On Tue, Aug 9, 2016 at 5:52 AM, Waldemar Kozaczuk <[email protected]> wrote:
> Therefore the changes that are part of this commit essentially allow to > bootstrap a JVM app in one of two modes: > - old one which provides classloader and JUL log manager isolation > between potential multiple apps and runjava code and apps that possibly > targets multi-apps on single JVM in OSv and requires full JRE > - new one which does NOT provide any classloader and JUL log manager > isolation and targets single main method apps on JVM in OSv and allows > using compact profile 1, 2 or 3 > > Following changes are part of this commit: > > * Added io.osv.isolated and io.osv.nonisolated packages under > java/runjava and refactored ContextIsolator to create JvmApp, > NonIsolatedJvmApp, RunNonIsolatedJvmApp, solatedJvmApp, NonIsolatedJvmApp > > * Changed java.cc and makefile to support producing two executable - > old java.so that can run multiple JVM apps in isolated fashion (class > loader, log manager, etc) and new java_non_isolated.so that can run single > JVM app without any isolation of the former one > > * Changed java/jvm/java.cc to print which java class it uses to > bootstrap - isolated or nonisolated one; fixed Makefile to properly handle > building of java_non_isolated.so > > * Added java_non_isolated.so to modules/java-tests/usr.manifest > > * Added java_non_isolated test to test.py; modified testing.py to > detect failure to start *.so > > * Added unit tests to test running non-isolated JVM app to > tests-isolates > > Fixes #497 > > Signed-off-by: Waldemar Kozaczuk <[email protected]> > --- > Makefile | 7 +- > java/jvm/java.cc | 13 +- > .../src/main/java/io/osv/ContextIsolator.java | 328 > --------------------- > java/runjava/src/main/java/io/osv/JvmApp.java | 187 ++++++++++++ > .../main/java/io/osv/{ => isolated}/Context.java | 2 +- > .../osv/{ => isolated}/ContextFailedException.java | 2 +- > .../main/java/io/osv/isolated/IsolatedJvmApp.java | 159 ++++++++++ > .../java/io/osv/{ => isolated}/MultiJarLoader.java | 4 +- > .../osv/{ => isolated}/OsvSystemClassLoader.java | 4 +- > .../RunIsolatedJvmApp.java} | 8 +- > .../main/java/io/osv/jul/IsolatingLogManager.java | 6 +- > .../main/java/io/osv/jul/LogManagerWrapper.java | 2 +- > .../AppThreadTerminatedWithUncaughtException.java} | 6 +- > .../java/io/osv/nonisolated/NonIsolatedJvmApp.java | 79 +++++ > .../RunNonIsolatedJvmApp.java} | 10 +- > .../src/main/java/tests/LoggingProcess.java | 4 +- > ...Process.java => NonIsolatedLoggingProcess.java} | 9 +- > .../src/main/java/tests/PropertyReader.java | 4 +- > .../src/main/java/tests/PropertySetter.java | 4 +- > .../src/main/java/tests/StaticFieldSetter.java | 4 +- > ...Tests.java => AllTestsThatTestIsolatedApp.java} | 2 +- > ...ts.java => AllTestsThatTestNonIsolatedApp.java} | 8 +- > .../main/java/io/osv/ClassLoaderIsolationTest.java | 5 +- > .../io/osv/ClassLoaderWithoutIsolationTest.java | 88 ++++++ > .../src/main/java/io/osv/LoggingIsolationTest.java | 4 +- > .../java/io/osv/LoggingWithoutIsolationTest.java | 54 ++++ > .../main/java/io/osv/PropertyIsolationTest.java | 4 +- > ...estIsolateLaunching.java => TestLaunching.java} | 23 +- > modules/java-tests/usr.manifest | 9 + > scripts/test.py | 6 +- > scripts/tests/testing.py | 1 + > 31 files changed, 666 insertions(+), 380 deletions(-) > delete mode 100644 java/runjava/src/main/java/io/osv/ContextIsolator.java > create mode 100644 java/runjava/src/main/java/io/osv/JvmApp.java > rename java/runjava/src/main/java/io/osv/{ => isolated}/Context.java > (98%) > copy java/runjava/src/main/java/io/osv/{ => > isolated}/ContextFailedException.java > (93%) > create mode 100644 java/runjava/src/main/java/io/ > osv/isolated/IsolatedJvmApp.java > rename java/runjava/src/main/java/io/osv/{ => > isolated}/MultiJarLoader.java (97%) > rename java/runjava/src/main/java/io/osv/{ => > isolated}/OsvSystemClassLoader.java > (98%) > copy java/runjava/src/main/java/io/osv/{RunJava.java => > isolated/RunIsolatedJvmApp.java} (90%) > rename java/runjava/src/main/java/io/osv/{ContextFailedException.java => > nonisolated/AppThreadTerminatedWithUncaughtException.java} (57%) > create mode 100644 java/runjava/src/main/java/io/osv/nonisolated/ > NonIsolatedJvmApp.java > rename java/runjava/src/main/java/io/osv/{RunJava.java => > nonisolated/RunNonIsolatedJvmApp.java} > (86%) > copy java/tests-isolates/src/main/java/tests/{LoggingProcess.java => > NonIsolatedLoggingProcess.java} (80%) > copy java/tests/src/main/java/io/osv/{AllTests.java => > AllTestsThatTestIsolatedApp.java} (91%) > rename java/tests/src/main/java/io/osv/{AllTests.java => > AllTestsThatTestNonIsolatedApp.java} (67%) > create mode 100644 java/tests/src/main/java/io/osv/ > ClassLoaderWithoutIsolationTest.java > create mode 100644 java/tests/src/main/java/io/osv/ > LoggingWithoutIsolationTest.java > rename java/tests/src/main/java/io/osv/{TestIsolateLaunching.java => > TestLaunching.java} (53%) > create mode 100644 modules/java-tests/usr.manifest > > diff --git a/Makefile b/Makefile > index abc7a8f..65f94ed 100644 > --- a/Makefile > +++ b/Makefile > @@ -136,7 +136,8 @@ very-quiet = $(if $V, $1, @$1) > ifeq ($(arch),aarch64) > java-targets := > else > -java-targets := $(out)/java/jvm/java.so $(out)/java/jni/balloon.so > $(out)/java/jni/elf-loader.so $(out)/java/jni/networking.so \ > +java-targets := $(out)/java/jvm/java.so $(out)/java/jvm/java_non_isolated.so > \ > + $(out)/java/jni/balloon.so $(out)/java/jni/elf-loader.so > $(out)/java/jni/networking.so \ > $(out)/java/jni/stty.so $(out)/java/jni/tracepoint.so > $(out)/java/jni/power.so $(out)/java/jni/monitor.so > endif > > @@ -376,6 +377,10 @@ $(out)/%.o: %.c | generated-headers > $(makedir) > $(call quiet, $(CC) $(CFLAGS) -c -o $@ $<, CC $*.c) > > +$(out)/java/jvm/java_non_isolated.o: java/jvm/java.cc | generated-headers > + $(makedir) > + $(call quiet, $(CXX) $(CXXFLAGS) -DRUN_JAVA_NON_ISOLATED -o $@ -c > java/jvm/java.cc, CXX $<) > + > $(out)/%.o: %.S > $(makedir) > $(call quiet, $(CXX) $(CXXFLAGS) $(ASFLAGS) -c -o $@ $<, AS $*.s) > diff --git a/java/jvm/java.cc b/java/jvm/java.cc > index 8c14462..b00d937 100644 > --- a/java/jvm/java.cc > +++ b/java/jvm/java.cc > @@ -30,7 +30,11 @@ extern size_t jvm_heap_size; > // parameters. > > #define JVM_PATH "/usr/lib/jvm/jre/lib/amd64/server/libjvm.so" > -#define RUNJAVA "io/osv/RunJava" // separated by slashes, not > dots > +#if defined(RUN_JAVA_NON_ISOLATED) > +#define RUNJAVA "io/osv/nonisolated/RunNonIsolatedJvmApp" // > separated by slashes, not dots > +#else > +#define RUNJAVA "io/osv/isolated/RunIsolatedJvmApp" // > separated by slashes, not dots > +#endif > > JavaVMOption mkoption(const char* s) > { > @@ -96,6 +100,8 @@ static void on_vm_stop(JNIEnv *env, jclass clz) { > > static int java_main(int argc, char **argv) > { > + std::cout << "java.so: Starting JVM app using: " << RUNJAVA << "\n"; > + > auto prog = elf::get_program(); > // The JVM library remains loaded as long as jvm_so is in scope. > auto jvm_so = prog->get_library(JVM_PATH); > @@ -108,8 +114,11 @@ static int java_main(int argc, char **argv) > > std::vector<JavaVMOption> options; > options.push_back(mkoption("-Djava.class.path=/dev/null")); > - options.push_back(mkoption("-Djava.system.class.loader=io. > osv.OsvSystemClassLoader")); > +#if !defined(RUN_JAVA_NON_ISOLATED) > + std::cout << "java.so: Setting Java system classloader and logging > manager to the isolated ones" << "\n"; > + options.push_back(mkoption("-Djava.system.class.loader=io. > osv.isolated.OsvSystemClassLoader")); > options.push_back(mkoption("-Djava.util.logging.manager=io. > osv.jul.IsolatingLogManager")); > +#endif > options.push_back(mkoption("-Dosv.version=" + osv::version())); > > { > diff --git a/java/runjava/src/main/java/io/osv/ContextIsolator.java > b/java/runjava/src/main/java/io/osv/ContextIsolator.java > deleted file mode 100644 > index bf4d443..0000000 > --- a/java/runjava/src/main/java/io/osv/ContextIsolator.java > +++ /dev/null > @@ -1,328 +0,0 @@ > -package io.osv; > - > -import io.osv.jul.IsolatingLogManager; > -import net.sf.cglib.proxy.Dispatcher; > -import net.sf.cglib.proxy.Enhancer; > - > -import java.io.File; > -import java.io.FileNotFoundException; > -import java.io.FilePermission; > -import java.lang.reflect.Field; > -import java.lang.reflect.InvocationTargetException; > -import java.lang.reflect.Method; > -import java.net.MalformedURLException; > -import java.net.URL; > -import java.net.URLClassLoader; > -import java.security.CodeSource; > -import java.security.PermissionCollection; > -import java.util.ArrayList; > -import java.util.List; > -import java.util.Properties; > -import java.util.jar.JarFile; > -import java.util.jar.Manifest; > -import java.util.logging.LogManager; > -import java.util.zip.ZipException; > - > -/* > - * Copyright (C) 2014 Cloudius Systems, Ltd. > - * > - * This work is open source software, licensed under the terms of the > - * BSD license as described in the LICENSE file in the top-level > directory. > - */ > -public class ContextIsolator { > - private static final ContextIsolator instance = new ContextIsolator(); > - > - static { > - verifyLogManagerIsInstalled(); > - } > - > - private final Context masterContext; > - private final Properties commonSystemProperties; > - > - private static void verifyLogManagerIsInstalled() { > - LogManager manager = LogManager.getLogManager(); > - if (!(manager instanceof IsolatingLogManager)) { > - throw new AssertionError("For isolation to work logging > manager must be " > - + IsolatingLogManager.class.getName() + " but is: " > + manager.getClass().getName()); > - } > - } > - > - private final InheritableThreadLocal<Context> currentContext = new > InheritableThreadLocal<Context>() { > - @Override > - protected Context initialValue() { > - return masterContext; > - } > - }; > - > - private final ClassLoader parentClassLoaderForIsolates; > - > - public static ContextIsolator getInstance() { > - return instance; > - } > - > - public ContextIsolator() { > - ClassLoader originalSystemClassLoader = > getOsvClassLoader().getParent(); > - commonSystemProperties = copyOf(System.getProperties()); > - masterContext = new Context(originalSystemClassLoader, > copyOf(commonSystemProperties)); > - > - parentClassLoaderForIsolates = originalSystemClassLoader; > - > - installSystemPropertiesProxy(); > - } > - > - private Properties copyOf(Properties properties) { > - Properties result = new Properties(); > - result.putAll(properties); > - return result; > - } > - > - private static void installSystemPropertiesProxy() { > - Enhancer enhancer = new Enhancer(); > - enhancer.setSuperclass(Properties.class); > - enhancer.setCallback(new Dispatcher() { > - @Override > - public Object loadObject() throws Exception { > - return instance.getContext().getProperties(); > - } > - }); > - Properties contextAwareProperties = (Properties) > enhancer.create(); > - > - try { > - Field props = System.class.getDeclaredField("props"); > - props.setAccessible(true); > - props.set(System.class, contextAwareProperties); > - } catch (NoSuchFieldException | IllegalAccessException e) { > - throw new AssertionError("Unable to override System.props", > e); > - } > - } > - > - public Context getContext() { > - return currentContext.get(); > - } > - > - private Context run(ClassLoader classLoader, final String classpath, > final String mainClass, > - final String[] args, final Properties properties) > { > - Properties contextProperties = new Properties(); > - contextProperties.putAll(commonSystemProperties); > - contextProperties.putAll(properties); > - > - final Context context = new Context(classLoader, > contextProperties); > - > - Thread thread = new Thread() { > - @Override > - public void run() { > - currentContext.set(context); > - context.setProperty("java.class.path", classpath); > - > - try { > - runMain(loadClass(mainClass), args); > - } catch (InterruptedException e) { > - Thread.currentThread().interrupt(); > - } catch (MainClassNotFoundException e) { > - context.setException(e); > - } catch (Throwable e) { > - getUncaughtExceptionHandler().uncaughtException(this, > e); > - } > - } > - }; > - > - context.setMainThread(thread); > - thread.setUncaughtExceptionHandler(new > Thread.UncaughtExceptionHandler() > { > - @Override > - public void uncaughtException(Thread t, Throwable e) { > - context.setException(e); > - } > - }); > - thread.setContextClassLoader(classLoader); > - thread.start(); > - return context; > - } > - > - public void runSync(String... args) throws Throwable { > - Context context = run(args); > - > - while (true) { > - try { > - context.join(); > - return; > - } catch (InterruptedException e) { > - context.interrupt(); > - } > - } > - } > - > - public Context run(String... args) throws Throwable { > - Properties properties = new Properties(); > - > - ArrayList<String> classpath = new ArrayList<>(); > - for (int i = 0; i < args.length; i++) { > - if (args[i].equals("-jar")) { > - if (i + 1 >= args.length) { > - throw new IllegalArgumentException("Missing jar name > after '-jar'."); > - } > - return runJar(args[i + 1], java.util.Arrays.copyOfRange(args, > i + 2, args.length), classpath, properties); > - } else if (args[i].equals("-classpath") || > args[i].equals("-cp")) { > - if (i + 1 >= args.length) { > - throw new IllegalArgumentException("Missing > parameter after '" + args[i] + "'"); > - } > - for (String c : expandClassPath(args[i + 1])) { > - classpath.add(c); > - } > - i++; > - } else if (args[i].startsWith("-D")) { > - int eq = args[i].indexOf('='); > - if (eq < 0) { > - /* -Dfoo is a special case for -Dfoo=true */ > - String key = args[i].substring(2); > - properties.put(key, "true"); > - } else { > - String key = args[i].substring(2, eq); > - String value = args[i].substring(eq + 1, > args[i].length()); > - properties.put(key, value); > - } > - } else if (!args[i].startsWith("-")) { > - return runClass(args[i], java.util.Arrays.copyOfRange(args, > i + 1, args.length), classpath, properties); > - } else { > - throw new IllegalArgumentException("Unknown parameter '" > + args[i] + "'"); > - } > - } > - throw new IllegalArgumentException("No jar or class specified to > run."); > - } > - > - private Context runJar(String jarName, String[] args, > ArrayList<String> classpath, Properties properties) throws Throwable { > - File jarFile = new File(jarName); > - try { > - JarFile jar = new JarFile(jarFile); > - Manifest mf = jar.getManifest(); > - jar.close(); > - String mainClass = mf.getMainAttributes(). > getValue("Main-Class"); > - if (mainClass == null) { > - throw new IllegalArgumentException("No 'Main-Class' > attribute in manifest of " + jarName); > - } > - classpath.add(jarName); > - return runClass(mainClass, args, classpath, properties); > - } catch (FileNotFoundException e) { > - throw new IllegalArgumentException("File not found: " + > jarName); > - } catch (ZipException e) { > - throw new IllegalArgumentException("File is not a jar: " + > jarName, e); > - } > - } > - > - private Context runClass(String mainClass, String[] args, > Iterable<String> classpath, Properties properties) throws > MalformedURLException { > - ClassLoader appClassLoader = getClassLoader(classpath, > parentClassLoaderForIsolates); > - return run(appClassLoader, joinClassPath(classpath), mainClass, > args, properties); > - } > - > - private static ClassLoader getClassLoader(Iterable<String> > classpath, ClassLoader parent) throws MalformedURLException { > - List<URL> urls = toUrls(classpath); > - URL[] urlArray = urls.toArray(new URL[urls.size()]); > - return new AppClassLoader(urlArray, parent); > - } > - > - private static List<URL> toUrls(Iterable<String> classpath) throws > MalformedURLException { > - ArrayList<URL> urls = new ArrayList<>(); > - for (String path : classpath) { > - urls.add(toUrl(path)); > - } > - return urls; > - } > - > - private static void runMain(Class<?> klass, String[] args) throws > Throwable { > - Method main = klass.getMethod("main", String[].class); > - try { > - main.invoke(null, new Object[]{args}); > - } catch (InvocationTargetException ex) { > - throw ex.getCause(); > - } > - } > - > - private static OsvSystemClassLoader getOsvClassLoader() { > - ClassLoader systemClassLoader = ClassLoader. > getSystemClassLoader(); > - if (!(systemClassLoader instanceof OsvSystemClassLoader)) { > - throw new AssertionError("System class loader should be an > instance of " > - + OsvSystemClassLoader.class.getName() + " but is " > - + systemClassLoader.getClass().getName()); > - } > - > - return (OsvSystemClassLoader) systemClassLoader; > - } > - > - private static String joinClassPath(Iterable<String> classpath) { > - StringBuilder sb = new StringBuilder(); > - boolean first = true; > - for (String path : classpath) { > - if (!first) { > - sb.append(":"); > - } > - first = false; > - sb.append(path); > - } > - return sb.toString(); > - } > - > - private static URL toUrl(String path) throws MalformedURLException { > - return new URL("file:///" + path + (isDirectory(path) ? "/" : > "")); > - } > - > - private static boolean isDirectory(String path) { > - return new File(path).isDirectory(); > - } > - > - private static Class<?> loadClass(String name) throws > MainClassNotFoundException { > - try { > - return Thread.currentThread().getContextClassLoader(). > loadClass(name); > - } catch (ClassNotFoundException ex) { > - throw new MainClassNotFoundException(name); > - } > - } > - > - // Expand classpath, as given in the "-classpath" option, to a list of > - // jars or directories. As in the traditional "java" command-line > - // launcher, components of the class path are separated by ":", and > - // we also support the traditional (but awkward) Java wildcard syntax, > - // where "dir/*" adds to the classpath all jar files in the given > - // directory. > - private static Iterable<String> expandClassPath(String classpath) { > - ArrayList<String> ret = new ArrayList<>(); > - for (String component : classpath.split(":")) { > - if (component.endsWith("/*")) { > - File dir = new File( > - component.substring(0, component.length() - 2)); > - if (dir.isDirectory()) { > - File[] files = dir.listFiles(); > - if (files == null) { > - continue; > - } > - for (File file : files) { > - String filename = file.getPath(); > - if (filename.endsWith(".jar")) { > - ret.add(filename); > - } > - } > - continue; // handled this path component > - } > - } > - ret.add(component); > - } > - return ret; > - } > - > - public Object receive() throws InterruptedException { > - return getContext().takeMessage(); > - } > - > - private static class AppClassLoader extends URLClassLoader { > - public AppClassLoader(URL[] urlArray, ClassLoader parent) { > - super(urlArray, parent); > - } > - > - @Override > - protected PermissionCollection getPermissions(CodeSource > codesource) { > - PermissionCollection permissions = super.getPermissions( > codesource); > - permissions.add(new > FilePermission("/usr/lib/jvm/jre/lib/ext/runjava.jar", > "read")); > - permissions.add(new RuntimePermission("exitVM")); > - return permissions; > - } > - } > - > -} > diff --git a/java/runjava/src/main/java/io/osv/JvmApp.java > b/java/runjava/src/main/java/io/osv/JvmApp.java > new file mode 100644 > index 0000000..e056fd9 > --- /dev/null > +++ b/java/runjava/src/main/java/io/osv/JvmApp.java > @@ -0,0 +1,187 @@ > +package io.osv; > + > +import java.io.File; > +import java.io.FileNotFoundException; > +import java.io.FilePermission; > +import java.lang.reflect.InvocationTargetException; > +import java.lang.reflect.Method; > +import java.net.MalformedURLException; > +import java.net.URL; > +import java.net.URLClassLoader; > +import java.security.CodeSource; > +import java.security.PermissionCollection; > +import java.util.ArrayList; > +import java.util.List; > +import java.util.Properties; > +import java.util.jar.JarFile; > +import java.util.jar.Manifest; > +import java.util.zip.ZipException; > + > +/** > + * Created by wkozaczuk on 7/17/16. > + */ > Avi/Nadav, do such comments conform with our coding style / licensing policy? -- You received this message because you are subscribed to the Google Groups "OSv Development" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. For more options, visit https://groups.google.com/d/optout.
