In order to run a JVM app using compact profile 1, 2 or 3 JRE the Java bootstrap code under runjava cannot depend on java.beans.* that is not part of any of the compact profiles. More specifically runjava depends on cglib and asm java libraries that manipulate bytecode and rely on java.beans*.
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 Jvm, NonIsolatedJvm, RunNonIsolatedJvmApp, IsolatedJvm, RunIsolatedJvmApp classes * 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 <jwkozac...@gmail.com> --- Makefile | 7 +- java/jvm/java.cc | 13 +- ... AppThreadTerminatedWithUncaughtException.java} | 6 +- .../java/io/osv/{ContextIsolator.java => Jvm.java} | 183 +++------------------ java/runjava/src/main/java/io/osv/RunJava.java | 48 ------ .../src/main/java/io/osv/RunJvmAppHelper.java | 44 +++++ .../main/java/io/osv/{ => isolated}/Context.java | 2 +- .../osv/{ => isolated}/ContextFailedException.java | 6 +- .../src/main/java/io/osv/isolated/IsolatedJvm.java | 160 ++++++++++++++++++ .../java/io/osv/{ => isolated}/MultiJarLoader.java | 4 +- .../osv/{ => isolated}/OsvSystemClassLoader.java | 4 +- .../java/io/osv/isolated/RunIsolatedJvmApp.java | 33 ++++ .../main/java/io/osv/jul/IsolatingLogManager.java | 6 +- .../main/java/io/osv/jul/LogManagerWrapper.java | 2 +- .../java/io/osv/nonisolated/NonIsolatedJvm.java | 86 ++++++++++ .../io/osv/nonisolated/RunNonIsolatedJvmApp.java | 33 ++++ .../src/main/java/tests/LoggingProcess.java | 4 +- ...Process.java => NonIsolatedLoggingProcess.java} | 10 +- .../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} | 9 +- .../main/java/io/osv/ClassLoaderIsolationTest.java | 5 +- .../io/osv/ClassLoaderWithoutIsolationTest.java | 89 ++++++++++ .../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 + 32 files changed, 615 insertions(+), 254 deletions(-) copy java/runjava/src/main/java/io/osv/{ContextFailedException.java => AppThreadTerminatedWithUncaughtException.java} (52%) rename java/runjava/src/main/java/io/osv/{ContextIsolator.java => Jvm.java} (51%) delete mode 100644 java/runjava/src/main/java/io/osv/RunJava.java create mode 100644 java/runjava/src/main/java/io/osv/RunJvmAppHelper.java rename java/runjava/src/main/java/io/osv/{ => isolated}/Context.java (98%) rename java/runjava/src/main/java/io/osv/{ => isolated}/ContextFailedException.java (62%) create mode 100644 java/runjava/src/main/java/io/osv/isolated/IsolatedJvm.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%) create mode 100644 java/runjava/src/main/java/io/osv/isolated/RunIsolatedJvmApp.java create mode 100644 java/runjava/src/main/java/io/osv/nonisolated/NonIsolatedJvm.java create mode 100644 java/runjava/src/main/java/io/osv/nonisolated/RunNonIsolatedJvmApp.java 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 650f667..e294a03 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/ContextFailedException.java b/java/runjava/src/main/java/io/osv/AppThreadTerminatedWithUncaughtException.java similarity index 52% copy from java/runjava/src/main/java/io/osv/ContextFailedException.java copy to java/runjava/src/main/java/io/osv/AppThreadTerminatedWithUncaughtException.java index 541115e..c4ec729 100644 --- a/java/runjava/src/main/java/io/osv/ContextFailedException.java +++ b/java/runjava/src/main/java/io/osv/AppThreadTerminatedWithUncaughtException.java @@ -1,13 +1,13 @@ package io.osv; /* - * Copyright (C) 2014 Cloudius Systems, Ltd. + * Copyright (C) 2016 Waldemar Kozaczuk * * 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 ContextFailedException extends Exception { - public ContextFailedException(Throwable cause) { +public class AppThreadTerminatedWithUncaughtException extends Exception { + public AppThreadTerminatedWithUncaughtException(Throwable cause) { super(cause); } } diff --git a/java/runjava/src/main/java/io/osv/ContextIsolator.java b/java/runjava/src/main/java/io/osv/Jvm.java similarity index 51% rename from java/runjava/src/main/java/io/osv/ContextIsolator.java rename to java/runjava/src/main/java/io/osv/Jvm.java index bf4d443..8008174 100644 --- a/java/runjava/src/main/java/io/osv/ContextIsolator.java +++ b/java/runjava/src/main/java/io/osv/Jvm.java @@ -1,13 +1,8 @@ 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; @@ -20,138 +15,19 @@ 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) 2016 Waldemar Kozaczuk * 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); +public abstract class Jvm<T> { + public abstract void runSync(String... args) throws Throwable; - 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 { + public T run(String... args) throws Throwable { Properties properties = new Properties(); ArrayList<String> classpath = new ArrayList<>(); @@ -189,7 +65,7 @@ public class ContextIsolator { throw new IllegalArgumentException("No jar or class specified to run."); } - private Context runJar(String jarName, String[] args, ArrayList<String> classpath, Properties properties) throws Throwable { + private T runJar(String jarName, String[] args, ArrayList<String> classpath, Properties properties) throws Throwable { File jarFile = new File(jarName); try { JarFile jar = new JarFile(jarFile); @@ -208,18 +84,23 @@ public class ContextIsolator { } } - private Context runClass(String mainClass, String[] args, Iterable<String> classpath, Properties properties) throws MalformedURLException { - ClassLoader appClassLoader = getClassLoader(classpath, parentClassLoaderForIsolates); + private T runClass(String mainClass, String[] args, Iterable<String> classpath, Properties properties) throws MalformedURLException { + ClassLoader appClassLoader = createAppClassLoader(classpath, getParentClassLoader()); return run(appClassLoader, joinClassPath(classpath), mainClass, args, properties); } - private static ClassLoader getClassLoader(Iterable<String> classpath, ClassLoader parent) throws MalformedURLException { + protected abstract T run(ClassLoader classLoader, final String classpath, final String mainClass, + final String[] args, final Properties properties); + + protected abstract ClassLoader getParentClassLoader(); + + private ClassLoader createAppClassLoader(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 { + private List<URL> toUrls(Iterable<String> classpath) throws MalformedURLException { ArrayList<URL> urls = new ArrayList<>(); for (String path : classpath) { urls.add(toUrl(path)); @@ -227,7 +108,7 @@ public class ContextIsolator { return urls; } - private static void runMain(Class<?> klass, String[] args) throws Throwable { + protected void runMain(Class<?> klass, String[] args) throws Throwable { Method main = klass.getMethod("main", String[].class); try { main.invoke(null, new Object[]{args}); @@ -236,18 +117,15 @@ public class ContextIsolator { } } - 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()); + protected Class<?> loadClass(String name) throws MainClassNotFoundException { + try { + return Thread.currentThread().getContextClassLoader().loadClass(name); + } catch (ClassNotFoundException ex) { + throw new MainClassNotFoundException(name); } - - return (OsvSystemClassLoader) systemClassLoader; } - private static String joinClassPath(Iterable<String> classpath) { + private String joinClassPath(Iterable<String> classpath) { StringBuilder sb = new StringBuilder(); boolean first = true; for (String path : classpath) { @@ -260,29 +138,21 @@ public class ContextIsolator { return sb.toString(); } - private static URL toUrl(String path) throws MalformedURLException { + private URL toUrl(String path) throws MalformedURLException { return new URL("file:///" + path + (isDirectory(path) ? "/" : "")); } - private static boolean isDirectory(String path) { + private 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) { + private Iterable<String> expandClassPath(String classpath) { ArrayList<String> ret = new ArrayList<>(); for (String component : classpath.split(":")) { if (component.endsWith("/*")) { @@ -307,10 +177,6 @@ public class ContextIsolator { 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); @@ -324,5 +190,4 @@ public class ContextIsolator { return permissions; } } - } diff --git a/java/runjava/src/main/java/io/osv/RunJava.java b/java/runjava/src/main/java/io/osv/RunJava.java deleted file mode 100644 index f7b8302..0000000 --- a/java/runjava/src/main/java/io/osv/RunJava.java +++ /dev/null @@ -1,48 +0,0 @@ -package io.osv; - -/* - * Copyright (C) 2013-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 RunJava { - private static native void onVMStop(); - - static { - Runtime.getRuntime().addShutdownHook(new Thread() { - public void run() { - onVMStop(); - } - }); - } - - public static void main(String[] args) { - if (args.length > 0 && args[0].equals("-version")) { - System.err.println("java version \"" + - System.getProperty("java.version") + "\""); - System.err.println(System.getProperty("java.runtime.name") + - " (" + System.getProperty("java.runtime.version") + - ")"); - System.err.println(System.getProperty("java.vm.name") + - " (build " + System.getProperty("java.vm.version") + - ", " + System.getProperty("java.vm.info") + ")"); - return; - } - - try { - ContextIsolator.getInstance().runSync(args); - } catch (IllegalArgumentException ex) { - System.err.println("RunJava: " + ex.getMessage()); - } catch (ContextFailedException ex) { - if (ex.getCause() instanceof MainClassNotFoundException) { - System.err.println("Error: Could not find or load main class " + ((MainClassNotFoundException) ex.getCause()).getClassName()); - } else { - ex.printStackTrace(); - } - } catch (Throwable ex) { - ex.printStackTrace(); - } - } -} diff --git a/java/runjava/src/main/java/io/osv/RunJvmAppHelper.java b/java/runjava/src/main/java/io/osv/RunJvmAppHelper.java new file mode 100644 index 0000000..6300999 --- /dev/null +++ b/java/runjava/src/main/java/io/osv/RunJvmAppHelper.java @@ -0,0 +1,44 @@ +package io.osv; + +/* + * Copyright (C) 2016 Waldemar Kozaczuk + * Copyright (C) 2013-2016 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 RunJvmAppHelper { + + public interface JvmFactory { + Jvm getJvm(); + } + + static public void runSync(JvmFactory jvmFactory, String[] args) { + + if (args.length > 0 && args[0].equals("-version")) { + System.err.println("java version \"" + + System.getProperty("java.version") + "\""); + System.err.println(System.getProperty("java.runtime.name") + + " (" + System.getProperty("java.runtime.version") + + ")"); + System.err.println(System.getProperty("java.vm.name") + + " (build " + System.getProperty("java.vm.version") + + ", " + System.getProperty("java.vm.info") + ")"); + return; + } + + try { + jvmFactory.getJvm().runSync(args); + } catch (IllegalArgumentException ex) { + System.err.println("RunJava: " + ex.getMessage()); + } catch (AppThreadTerminatedWithUncaughtException ex) { + if (ex.getCause() instanceof MainClassNotFoundException) { + System.err.println("Error: Could not find or load main class " + ((MainClassNotFoundException) ex.getCause()).getClassName()); + } else { + ex.printStackTrace(); + } + } catch (Throwable ex) { + ex.printStackTrace(); + } + } +} diff --git a/java/runjava/src/main/java/io/osv/Context.java b/java/runjava/src/main/java/io/osv/isolated/Context.java similarity index 98% rename from java/runjava/src/main/java/io/osv/Context.java rename to java/runjava/src/main/java/io/osv/isolated/Context.java index ad70504..73f86fd 100644 --- a/java/runjava/src/main/java/io/osv/Context.java +++ b/java/runjava/src/main/java/io/osv/isolated/Context.java @@ -1,4 +1,4 @@ -package io.osv; +package io.osv.isolated; import io.osv.jul.LogManagerWrapper; import io.osv.util.LazilyInitialized; diff --git a/java/runjava/src/main/java/io/osv/ContextFailedException.java b/java/runjava/src/main/java/io/osv/isolated/ContextFailedException.java similarity index 62% rename from java/runjava/src/main/java/io/osv/ContextFailedException.java rename to java/runjava/src/main/java/io/osv/isolated/ContextFailedException.java index 541115e..6623f59 100644 --- a/java/runjava/src/main/java/io/osv/ContextFailedException.java +++ b/java/runjava/src/main/java/io/osv/isolated/ContextFailedException.java @@ -1,4 +1,6 @@ -package io.osv; +package io.osv.isolated; + +import io.osv.AppThreadTerminatedWithUncaughtException; /* * Copyright (C) 2014 Cloudius Systems, Ltd. @@ -6,7 +8,7 @@ package io.osv; * 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 ContextFailedException extends Exception { +public class ContextFailedException extends AppThreadTerminatedWithUncaughtException { public ContextFailedException(Throwable cause) { super(cause); } diff --git a/java/runjava/src/main/java/io/osv/isolated/IsolatedJvm.java b/java/runjava/src/main/java/io/osv/isolated/IsolatedJvm.java new file mode 100644 index 0000000..6bc3768 --- /dev/null +++ b/java/runjava/src/main/java/io/osv/isolated/IsolatedJvm.java @@ -0,0 +1,160 @@ +package io.osv.isolated; + +import io.osv.MainClassNotFoundException; +import io.osv.Jvm; +import io.osv.jul.IsolatingLogManager; +import net.sf.cglib.proxy.Dispatcher; +import net.sf.cglib.proxy.Enhancer; + +import java.lang.reflect.Field; +import java.util.Properties; +import java.util.logging.LogManager; + +/* + * Copyright (C) 2016 Waldemar Kozaczuk + * 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 IsolatedJvm extends Jvm<Context> { + private static final IsolatedJvm instance = new IsolatedJvm(); + + 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 IsolatedJvm getInstance() { + return instance; + } + + private IsolatedJvm() { + 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 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(); + } + + protected 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; + } + + protected ClassLoader getParentClassLoader() { + return parentClassLoaderForIsolates; + } + + public void runSync(String... args) throws Throwable { + Context context = run(args); + + while (true) { + try { + context.join(); + return; + } catch (InterruptedException e) { + context.interrupt(); + } + } + } + + private 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; + } + + public Object receive() throws InterruptedException { + return getContext().takeMessage(); + } +} diff --git a/java/runjava/src/main/java/io/osv/MultiJarLoader.java b/java/runjava/src/main/java/io/osv/isolated/MultiJarLoader.java similarity index 97% rename from java/runjava/src/main/java/io/osv/MultiJarLoader.java rename to java/runjava/src/main/java/io/osv/isolated/MultiJarLoader.java index daf19ef..8902a89 100644 --- a/java/runjava/src/main/java/io/osv/MultiJarLoader.java +++ b/java/runjava/src/main/java/io/osv/isolated/MultiJarLoader.java @@ -1,4 +1,4 @@ -package io.osv; +package io.osv.isolated; import java.io.BufferedReader; import java.io.File; @@ -110,7 +110,7 @@ public class MultiJarLoader { @Override public void run() { try { - ContextIsolator.getInstance().runSync(args.split("\\s+")); + IsolatedJvm.getInstance().runSync(args.split("\\s+")); } catch (Throwable e) { System.err.println("Exception was caught while running " + args + " exception: " + e); diff --git a/java/runjava/src/main/java/io/osv/OsvSystemClassLoader.java b/java/runjava/src/main/java/io/osv/isolated/OsvSystemClassLoader.java similarity index 98% rename from java/runjava/src/main/java/io/osv/OsvSystemClassLoader.java rename to java/runjava/src/main/java/io/osv/isolated/OsvSystemClassLoader.java index dfa5b00..fee72cf 100644 --- a/java/runjava/src/main/java/io/osv/OsvSystemClassLoader.java +++ b/java/runjava/src/main/java/io/osv/isolated/OsvSystemClassLoader.java @@ -1,4 +1,4 @@ -package io.osv; +package io.osv.isolated; /* * Copyright (C) 2013 Cloudius Systems, Ltd. @@ -54,7 +54,7 @@ public class OsvSystemClassLoader extends ClassLoader { } private Context getContext() { - return ContextIsolator.getInstance().getContext(); + return IsolatedJvm.getInstance().getContext(); } private ClassLoader getDelegate() { diff --git a/java/runjava/src/main/java/io/osv/isolated/RunIsolatedJvmApp.java b/java/runjava/src/main/java/io/osv/isolated/RunIsolatedJvmApp.java new file mode 100644 index 0000000..29e3ccc --- /dev/null +++ b/java/runjava/src/main/java/io/osv/isolated/RunIsolatedJvmApp.java @@ -0,0 +1,33 @@ +package io.osv.isolated; + +/* + * Copyright (C) 2016 Waldemar Kozaczuk + * Copyright (C) 2013-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. + */ +import io.osv.Jvm; + +import static io.osv.RunJvmAppHelper.runSync; +import static io.osv.RunJvmAppHelper.JvmFactory; + +public class RunIsolatedJvmApp { + private static native void onVMStop(); + + static { + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + onVMStop(); + } + }); + } + + public static void main(String[] args) { + runSync(new JvmFactory() { + public Jvm getJvm() { + return IsolatedJvm.getInstance(); + } + },args); + } +} diff --git a/java/runjava/src/main/java/io/osv/jul/IsolatingLogManager.java b/java/runjava/src/main/java/io/osv/jul/IsolatingLogManager.java index 843bfc2..18b7d64 100644 --- a/java/runjava/src/main/java/io/osv/jul/IsolatingLogManager.java +++ b/java/runjava/src/main/java/io/osv/jul/IsolatingLogManager.java @@ -1,7 +1,7 @@ package io.osv.jul; -import io.osv.Context; -import io.osv.ContextIsolator; +import io.osv.isolated.Context; +import io.osv.isolated.IsolatedJvm; import java.beans.PropertyChangeListener; import java.io.IOException; @@ -19,7 +19,7 @@ import java.util.logging.Logger; @SuppressWarnings("UnusedDeclaration") public class IsolatingLogManager extends LogManager { private LogManager getDelegate() { - Context context = ContextIsolator.getInstance().getContext(); + Context context = IsolatedJvm.getInstance().getContext(); return context.getLogManagerWrapper().getManager(); } diff --git a/java/runjava/src/main/java/io/osv/jul/LogManagerWrapper.java b/java/runjava/src/main/java/io/osv/jul/LogManagerWrapper.java index e4d48d2..d00f014 100644 --- a/java/runjava/src/main/java/io/osv/jul/LogManagerWrapper.java +++ b/java/runjava/src/main/java/io/osv/jul/LogManagerWrapper.java @@ -1,6 +1,6 @@ package io.osv.jul; -import io.osv.Context; +import io.osv.isolated.Context; import io.osv.util.LazilyInitialized; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; diff --git a/java/runjava/src/main/java/io/osv/nonisolated/NonIsolatedJvm.java b/java/runjava/src/main/java/io/osv/nonisolated/NonIsolatedJvm.java new file mode 100644 index 0000000..5d19b77 --- /dev/null +++ b/java/runjava/src/main/java/io/osv/nonisolated/NonIsolatedJvm.java @@ -0,0 +1,86 @@ +package io.osv.nonisolated; + +import io.osv.AppThreadTerminatedWithUncaughtException; +import io.osv.Jvm; +import io.osv.MainClassNotFoundException; + +import java.util.Properties; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; + +/* + * Copyright (C) 2016 Waldemar Kozaczuk + * + * 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 NonIsolatedJvm extends Jvm<Thread> { + + private static final NonIsolatedJvm instance = new NonIsolatedJvm(); + + private AtomicReference<Throwable> thrownException = new AtomicReference<>(); + + public static NonIsolatedJvm getInstance() { + return instance; + } + + private NonIsolatedJvm() {} + + @Override + protected Thread run(ClassLoader classLoader, final String classpath, final String mainClass, final String[] args, final Properties properties) { + thrownException.set(null); + Thread thread = new Thread() { + @Override + public void run() { + System.setProperty("java.class.path", classpath); + + for(Map.Entry<?,?> property : properties.entrySet()) + System.setProperty(property.getKey().toString(),property.getValue().toString()); //TODO Check for null + + try { + runMain(loadClass(mainClass), args); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } catch (MainClassNotFoundException e) { + thrownException.set(e); + } catch (Throwable e) { + getUncaughtExceptionHandler().uncaughtException(this, e); + } + } + }; + + thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { + @Override + public void uncaughtException(Thread t, Throwable e) { + thrownException.set(e); + } + }); + thread.setContextClassLoader(classLoader); + thread.start(); + return thread; + } + + public void runSync(String... args) throws Throwable { + Thread thread = run(args); + + while (true) { + try { + thread.join(); + final Throwable exception = thrownException.get(); + if (null != exception) { + throw new AppThreadTerminatedWithUncaughtException(exception); + } + return; + } catch (InterruptedException e) { + thread.interrupt(); + } + } + } + + @Override + protected ClassLoader getParentClassLoader() { + return Thread.currentThread().getContextClassLoader(); + } + + public Throwable getThrownExceptionIfAny() { return thrownException.get(); } +} diff --git a/java/runjava/src/main/java/io/osv/nonisolated/RunNonIsolatedJvmApp.java b/java/runjava/src/main/java/io/osv/nonisolated/RunNonIsolatedJvmApp.java new file mode 100644 index 0000000..12f8800 --- /dev/null +++ b/java/runjava/src/main/java/io/osv/nonisolated/RunNonIsolatedJvmApp.java @@ -0,0 +1,33 @@ +package io.osv.nonisolated; + +/* + * Copyright (C) 2016 Waldemar Kozaczuk + * + * 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. + */ + +import io.osv.Jvm; + +import static io.osv.RunJvmAppHelper.runSync; +import static io.osv.RunJvmAppHelper.JvmFactory; + +public class RunNonIsolatedJvmApp { + private static native void onVMStop(); + + static { + Runtime.getRuntime().addShutdownHook(new Thread() { + public void run() { + onVMStop(); + } + }); + } + + public static void main(String[] args) { + runSync(new JvmFactory() { + public Jvm getJvm() { + return NonIsolatedJvm.getInstance(); + } + },args); + } +} diff --git a/java/tests-isolates/src/main/java/tests/LoggingProcess.java b/java/tests-isolates/src/main/java/tests/LoggingProcess.java index 161d4f6..c1d8385 100644 --- a/java/tests-isolates/src/main/java/tests/LoggingProcess.java +++ b/java/tests-isolates/src/main/java/tests/LoggingProcess.java @@ -1,6 +1,6 @@ package tests; -import io.osv.ContextIsolator; +import io.osv.isolated.IsolatedJvm; import java.util.concurrent.CyclicBarrier; import java.util.logging.FileHandler; @@ -16,7 +16,7 @@ import java.util.logging.SimpleFormatter; */ public class LoggingProcess { public static void main(String[] args) throws Throwable { - CyclicBarrier barrier = (CyclicBarrier) ContextIsolator.getInstance().receive(); + CyclicBarrier barrier = (CyclicBarrier) IsolatedJvm.getInstance().receive(); String logFileName = args[0]; String loggerName = args[1]; diff --git a/java/tests-isolates/src/main/java/tests/LoggingProcess.java b/java/tests-isolates/src/main/java/tests/NonIsolatedLoggingProcess.java similarity index 80% copy from java/tests-isolates/src/main/java/tests/LoggingProcess.java copy to java/tests-isolates/src/main/java/tests/NonIsolatedLoggingProcess.java index 161d4f6..e0755d3 100644 --- a/java/tests-isolates/src/main/java/tests/LoggingProcess.java +++ b/java/tests-isolates/src/main/java/tests/NonIsolatedLoggingProcess.java @@ -1,23 +1,19 @@ package tests; -import io.osv.ContextIsolator; - -import java.util.concurrent.CyclicBarrier; import java.util.logging.FileHandler; import java.util.logging.Level; import java.util.logging.Logger; import java.util.logging.SimpleFormatter; /* + * Copyright (C) 2016 Waldemar Kozaczuk * 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 LoggingProcess { +public class NonIsolatedLoggingProcess { public static void main(String[] args) throws Throwable { - CyclicBarrier barrier = (CyclicBarrier) ContextIsolator.getInstance().receive(); - String logFileName = args[0]; String loggerName = args[1]; Level level = Level.parse(args[2]); @@ -30,8 +26,6 @@ public class LoggingProcess { logger.setLevel(level); - barrier.await(); - logger.info(message); logger.warning(message); } diff --git a/java/tests-isolates/src/main/java/tests/PropertyReader.java b/java/tests-isolates/src/main/java/tests/PropertyReader.java index ae63402..5e51ade 100644 --- a/java/tests-isolates/src/main/java/tests/PropertyReader.java +++ b/java/tests-isolates/src/main/java/tests/PropertyReader.java @@ -1,6 +1,6 @@ package tests; -import io.osv.ContextIsolator; +import io.osv.isolated.IsolatedJvm; import java.util.concurrent.CyclicBarrier; @@ -17,7 +17,7 @@ public class PropertyReader { String property = args[0]; String expectedValue = args[1]; - CyclicBarrier barrier = (CyclicBarrier) ContextIsolator.getInstance().receive(); + CyclicBarrier barrier = (CyclicBarrier) IsolatedJvm.getInstance().receive(); barrier.await(); assertEquals(expectedValue, System.getProperty(property)); diff --git a/java/tests-isolates/src/main/java/tests/PropertySetter.java b/java/tests-isolates/src/main/java/tests/PropertySetter.java index 9ea83fa..8b4d72e 100644 --- a/java/tests-isolates/src/main/java/tests/PropertySetter.java +++ b/java/tests-isolates/src/main/java/tests/PropertySetter.java @@ -1,6 +1,6 @@ package tests; -import io.osv.ContextIsolator; +import io.osv.isolated.IsolatedJvm; import java.util.concurrent.CyclicBarrier; @@ -14,7 +14,7 @@ import static org.junit.Assert.assertEquals; */ public class PropertySetter { public static void main(String[] args) throws Exception { - CyclicBarrier barrier = (CyclicBarrier) ContextIsolator.getInstance().receive(); + CyclicBarrier barrier = (CyclicBarrier) IsolatedJvm.getInstance().receive(); String property = args[0]; String value = args[1]; diff --git a/java/tests-isolates/src/main/java/tests/StaticFieldSetter.java b/java/tests-isolates/src/main/java/tests/StaticFieldSetter.java index 6ef94e9..0103afc 100644 --- a/java/tests-isolates/src/main/java/tests/StaticFieldSetter.java +++ b/java/tests-isolates/src/main/java/tests/StaticFieldSetter.java @@ -1,6 +1,6 @@ package tests; -import io.osv.ContextIsolator; +import io.osv.isolated.IsolatedJvm; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; @@ -26,7 +26,7 @@ public class StaticFieldSetter { public static class Party { public static void main(String[] args) throws InterruptedException, BrokenBarrierException { - CyclicBarrier barrier = (CyclicBarrier) ContextIsolator.getInstance().receive(); + CyclicBarrier barrier = (CyclicBarrier) IsolatedJvm.getInstance().receive(); String value = args[0]; staticField = value; diff --git a/java/tests/src/main/java/io/osv/AllTests.java b/java/tests/src/main/java/io/osv/AllTestsThatTestIsolatedApp.java similarity index 91% copy from java/tests/src/main/java/io/osv/AllTests.java copy to java/tests/src/main/java/io/osv/AllTestsThatTestIsolatedApp.java index 4eca806..5cdb498 100644 --- a/java/tests/src/main/java/io/osv/AllTests.java +++ b/java/tests/src/main/java/io/osv/AllTestsThatTestIsolatedApp.java @@ -17,5 +17,5 @@ import org.junit.runners.Suite; PropertyIsolationTest.class, OsvApiTest.class }) -public class AllTests { +public class AllTestsThatTestIsolatedApp { } diff --git a/java/tests/src/main/java/io/osv/AllTests.java b/java/tests/src/main/java/io/osv/AllTestsThatTestNonIsolatedApp.java similarity index 67% rename from java/tests/src/main/java/io/osv/AllTests.java rename to java/tests/src/main/java/io/osv/AllTestsThatTestNonIsolatedApp.java index 4eca806..cfb78b5 100644 --- a/java/tests/src/main/java/io/osv/AllTests.java +++ b/java/tests/src/main/java/io/osv/AllTestsThatTestNonIsolatedApp.java @@ -1,21 +1,20 @@ package io.osv; /* + * Copyright (C) 2016 Waldemar Kozaczuk * 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. */ - import org.junit.runner.RunWith; import org.junit.runners.Suite; @RunWith(Suite.class) @Suite.SuiteClasses({ - LoggingIsolationTest.class, - ClassLoaderIsolationTest.class, - PropertyIsolationTest.class, + LoggingWithoutIsolationTest.class, + ClassLoaderWithoutIsolationTest.class, OsvApiTest.class }) -public class AllTests { +public class AllTestsThatTestNonIsolatedApp { } diff --git a/java/tests/src/main/java/io/osv/ClassLoaderIsolationTest.java b/java/tests/src/main/java/io/osv/ClassLoaderIsolationTest.java index f140e1b..5e920a9 100644 --- a/java/tests/src/main/java/io/osv/ClassLoaderIsolationTest.java +++ b/java/tests/src/main/java/io/osv/ClassLoaderIsolationTest.java @@ -5,7 +5,10 @@ import tests.*; import java.util.concurrent.CyclicBarrier; -import static io.osv.TestIsolateLaunching.runIsolate; +import io.osv.isolated.Context; +import io.osv.isolated.ContextFailedException; + +import static io.osv.TestLaunching.runIsolate; import static org.junit.Assert.*; /* diff --git a/java/tests/src/main/java/io/osv/ClassLoaderWithoutIsolationTest.java b/java/tests/src/main/java/io/osv/ClassLoaderWithoutIsolationTest.java new file mode 100644 index 0000000..ec20cf2 --- /dev/null +++ b/java/tests/src/main/java/io/osv/ClassLoaderWithoutIsolationTest.java @@ -0,0 +1,89 @@ +package io.osv; + +import io.osv.nonisolated.NonIsolatedJvm; +import org.junit.Test; +import tests.*; + +import static io.osv.TestLaunching.runWithoutIsolation; +import static org.junit.Assert.*; + +/* + * Copyright (C) 2016 Waldemar Kozaczuk + * 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 ClassLoaderWithoutIsolationTest { + private static final Class<?> THIS_CLASS = ClassLoaderWithoutIsolationTest.class; + + @Test + public void testParentContextSeesModificationsOfStaticFields() throws Throwable { + Thread thread = runWithoutIsolation(StaticFieldSetter.class); // This is where the class is loaded and available to child one + thread.join(); + + // + // Rethrow any exception that may have been raised and led to the thread terminating + final Throwable exception = NonIsolatedJvm.getInstance().getThrownExceptionIfAny(); + if( null != exception) + throw exception; + // + // There is one class instance of StaticFieldSetter loaded as there is no isolation + // between the parent app classloader (at the tests runner level) and the child app + // classloader level which is created when runWithoutIsolation is called + assertEquals(StaticFieldSetter.NEW_VALUE, StaticFieldSetter.staticField); + } + + @Test + public void testChildSeesSameVersionOfAClassDefinedInParentContext() throws Throwable { + String fieldName = "field_existing_only_in_isolate_context"; + + try { + ClassPresentInBothContexts.class.getDeclaredField(fieldName); + throw new AssertionError("The field should be absent in parent context"); + } catch (NoSuchFieldException e) { + // expected + } + + Thread thread = runWithoutIsolation(FieldTester.class, ClassPresentInBothContexts.class.getName(), fieldName); + thread.join(); + // + // Rethrow any exception that may have been raised and led to the thread terminating + final Throwable exception = NonIsolatedJvm.getInstance().getThrownExceptionIfAny(); + if( null != exception && exception instanceof NoSuchFieldException) { + // It is what is expected as there is no isolation between child and parent classloader the class loaded + // by parent classloader from tests.jar which is a first jar in the classpath + } + else if( null != exception) { + throw exception; + } + else { + throw new AssertionError("The field should be also absent in child context"); + } + } + + @Test + public void testClassesDefinedInParentContextAreVisibleToChild() throws Throwable { + Thread thread = runWithoutIsolation(ClassFinder.class, ClassPresentOnlyInParentContext.class.getName()); + thread.join(); + // + // Rethrow any exception that may have been raised and led to the thread terminating + final Throwable exception = NonIsolatedJvm.getInstance().getThrownExceptionIfAny(); + if( null != exception) + throw exception; + // + // As there is no isolation between child and parent classloader the class loaded + // by parent classloader ClassPresentOnlyInParentContext from tests.jar will + // actually be visible in the children + } + + @Test + public void testClassesFromExtensionDirectoryCanBeLoaded() throws Exception { + assertNotNull(SomeExtensionClass.class); + } + + @Test + public void testClassPutInRootDirectoryIsNotPickedUpByDefaultSystemClassLoader() throws Exception { + assertSame(ClassPutInRoot.class.getClassLoader(), THIS_CLASS.getClassLoader()); + } +} \ No newline at end of file diff --git a/java/tests/src/main/java/io/osv/LoggingIsolationTest.java b/java/tests/src/main/java/io/osv/LoggingIsolationTest.java index bb4637f..8154960 100644 --- a/java/tests/src/main/java/io/osv/LoggingIsolationTest.java +++ b/java/tests/src/main/java/io/osv/LoggingIsolationTest.java @@ -7,7 +7,9 @@ import java.io.File; import java.io.IOException; import java.util.concurrent.CyclicBarrier; -import static io.osv.TestIsolateLaunching.runIsolate; +import io.osv.isolated.Context; + +import static io.osv.TestLaunching.runIsolate; import static org.apache.commons.io.FileUtils.forceDeleteOnExit; import static org.apache.commons.io.FileUtils.readLines; import static org.fest.assertions.Assertions.assertThat; diff --git a/java/tests/src/main/java/io/osv/LoggingWithoutIsolationTest.java b/java/tests/src/main/java/io/osv/LoggingWithoutIsolationTest.java new file mode 100644 index 0000000..5f5a188 --- /dev/null +++ b/java/tests/src/main/java/io/osv/LoggingWithoutIsolationTest.java @@ -0,0 +1,54 @@ +package io.osv; + +import io.osv.nonisolated.NonIsolatedJvm; +import org.junit.Test; +import tests.NonIsolatedLoggingProcess; + +import java.io.File; +import java.io.IOException; +import java.util.List; + +import static io.osv.TestLaunching.runWithoutIsolation; +import static org.apache.commons.io.FileUtils.forceDeleteOnExit; +import static org.apache.commons.io.FileUtils.readLines; +import static org.fest.assertions.Assertions.assertThat; + +/* + * Copyright (C) 2016 Waldemar Kozaczuk + * 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 LoggingWithoutIsolationTest { + private static final String LOGGER_NAME = "test-logger"; + + @Test + public void testLogger() throws Throwable { + File log = newTemporaryFile(); + + Thread thread = runWithoutIsolation(NonIsolatedLoggingProcess.class, log.getAbsolutePath(), LOGGER_NAME, "INFO", "ctx"); + thread.join(); + + // + // Rethrow any exception that may have been raised and led to the thread terminating + final Throwable exception = NonIsolatedJvm.getInstance().getThrownExceptionIfAny(); + if( null != exception) + throw exception; + + final List<String> logLines = readLines(log); + for( String line : logLines) + System.out.println(line); + + assertThat(logLines) + .hasSize(4) + .contains("INFO: ctx") + .contains("WARNING: ctx"); + } + + private File newTemporaryFile() throws IOException { + File file = File.createTempFile("test", null); + forceDeleteOnExit(file); + return file; + } +} diff --git a/java/tests/src/main/java/io/osv/PropertyIsolationTest.java b/java/tests/src/main/java/io/osv/PropertyIsolationTest.java index 073d857..ad822ad 100644 --- a/java/tests/src/main/java/io/osv/PropertyIsolationTest.java +++ b/java/tests/src/main/java/io/osv/PropertyIsolationTest.java @@ -6,7 +6,9 @@ import tests.PropertySetter; import java.util.concurrent.CyclicBarrier; -import static io.osv.TestIsolateLaunching.runIsolate; +import io.osv.isolated.Context; + +import static io.osv.TestLaunching.runIsolate; import static java.util.Arrays.asList; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; diff --git a/java/tests/src/main/java/io/osv/TestIsolateLaunching.java b/java/tests/src/main/java/io/osv/TestLaunching.java similarity index 53% rename from java/tests/src/main/java/io/osv/TestIsolateLaunching.java rename to java/tests/src/main/java/io/osv/TestLaunching.java index c7e95f7..c8955c7 100644 --- a/java/tests/src/main/java/io/osv/TestIsolateLaunching.java +++ b/java/tests/src/main/java/io/osv/TestLaunching.java @@ -5,6 +5,10 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; +import io.osv.isolated.Context; +import io.osv.isolated.IsolatedJvm; +import io.osv.nonisolated.NonIsolatedJvm; + import static java.util.Arrays.asList; import static org.fest.assertions.Assertions.assertThat; @@ -14,13 +18,27 @@ import static org.fest.assertions.Assertions.assertThat; * 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 TestIsolateLaunching { +public class TestLaunching { public static Context runIsolate(Class<?> clazz, String... programArgs) throws Throwable { return runIsolate(clazz, Collections.<String>emptyList(), programArgs); } public static Context runIsolate(Class<?> clazz, List<String> args, String... programArgs) throws Throwable { + List<String> allArgs = testJarAndPrepareArgs(clazz, args, programArgs); + return IsolatedJvm.getInstance().run(allArgs.toArray(new String[allArgs.size()])); + } + + public static Thread runWithoutIsolation(Class<?> clazz, String... programArgs) throws Throwable { + return runWithoutIsolation(clazz, Collections.<String>emptyList(), programArgs); + } + + public static Thread runWithoutIsolation(Class<?> clazz, List<String> args, String... programArgs) throws Throwable { + List<String> allArgs = testJarAndPrepareArgs(clazz, args, programArgs); + return NonIsolatedJvm.getInstance().run(allArgs.toArray(new String[allArgs.size()])); + } + + private static List<String> testJarAndPrepareArgs(Class<?> clazz, List<String> args, String... programArgs) { String jarPath = System.getProperty("isolates.jar"); assertThat(jarPath).isNotEmpty(); assertThat(new File(jarPath)).exists(); @@ -32,7 +50,6 @@ public class TestIsolateLaunching { allArgs.add(clazz.getName()); allArgs.addAll(asList(programArgs)); - return ContextIsolator.getInstance().run(allArgs.toArray(new String[allArgs.size()])); + return allArgs; } - } diff --git a/modules/java-tests/usr.manifest b/modules/java-tests/usr.manifest new file mode 100644 index 0000000..0dca54e --- /dev/null +++ b/modules/java-tests/usr.manifest @@ -0,0 +1,9 @@ +# +# Copyright (C) 2013-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. +# + +[manifest] +/java_non_isolated.so: java/jvm/java_non_isolated.so diff --git a/scripts/test.py b/scripts/test.py index 5441149..605cb1a 100755 --- a/scripts/test.py +++ b/scripts/test.py @@ -22,8 +22,10 @@ blacklist= [ ] add_tests([ - SingleCommandTest('java', '/java.so -cp /tests/java/tests.jar:/tests/java/isolates.jar \ - -Disolates.jar=/tests/java/isolates.jar org.junit.runner.JUnitCore io.osv.AllTests'), + SingleCommandTest('java_isolated', '/java.so -cp /tests/java/tests.jar:/tests/java/isolates.jar \ + -Disolates.jar=/tests/java/isolates.jar org.junit.runner.JUnitCore io.osv.AllTestsThatTestIsolatedApp'), + SingleCommandTest('java_non_isolated', '/java_non_isolated.so -cp /tests/java/tests.jar:/tests/java/isolates.jar \ + -Disolates.jar=/tests/java/isolates.jar org.junit.runner.JUnitCore io.osv.AllTestsThatTestNonIsolatedApp'), SingleCommandTest('java-perms', '/java.so -cp /tests/java/tests.jar io.osv.TestDomainPermissions'), ]) diff --git a/scripts/tests/testing.py b/scripts/tests/testing.py index 89c8224..f71156d 100644 --- a/scripts/tests/testing.py +++ b/scripts/tests/testing.py @@ -73,6 +73,7 @@ def scan_errors(s): "at org.junit.runner.JUnitCore.main", "ContextFailedException", "\[backtrace\]", + "Failed to load object", ] for pattern in patterns: if re.findall(pattern, s): -- 1.9.1 -- 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 osv-dev+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.