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.
+ */
+public abstract class JvmApp<T> {
+    public T 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 T 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 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);
+    }
+
+    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 List<URL> toUrls(Iterable<String> classpath) throws 
MalformedURLException {
+        ArrayList<URL> urls = new ArrayList<>();
+        for (String path : classpath) {
+            urls.add(toUrl(path));
+        }
+        return urls;
+    }
+
+    protected 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();
+        }
+    }
+
+    protected Class<?> loadClass(String name) throws 
MainClassNotFoundException {
+        try {
+            return 
Thread.currentThread().getContextClassLoader().loadClass(name);
+        } catch (ClassNotFoundException ex) {
+            throw new MainClassNotFoundException(name);
+        }
+    }
+
+    private 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 URL toUrl(String path) throws MalformedURLException {
+        return new URL("file:///" + path + (isDirectory(path) ? "/" : ""));
+    }
+
+    private boolean isDirectory(String path) {
+        return new File(path).isDirectory();
+    }
+
+    // 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 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;
+    }
+
+    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/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 93%
copy from java/runjava/src/main/java/io/osv/ContextFailedException.java
copy to java/runjava/src/main/java/io/osv/isolated/ContextFailedException.java
index 541115e..a3a5e0b 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,4 @@
-package io.osv;
+package io.osv.isolated;
 
 /*
  * Copyright (C) 2014 Cloudius Systems, Ltd.
diff --git a/java/runjava/src/main/java/io/osv/isolated/IsolatedJvmApp.java 
b/java/runjava/src/main/java/io/osv/isolated/IsolatedJvmApp.java
new file mode 100644
index 0000000..5f895bd
--- /dev/null
+++ b/java/runjava/src/main/java/io/osv/isolated/IsolatedJvmApp.java
@@ -0,0 +1,159 @@
+package io.osv.isolated;
+
+import io.osv.MainClassNotFoundException;
+import io.osv.JvmApp;
+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) 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 IsolatedJvmApp extends JvmApp<Context> {
+    private static final IsolatedJvmApp instance = new IsolatedJvmApp();
+
+    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 IsolatedJvmApp getInstance() {
+        return instance;
+    }
+
+    private IsolatedJvmApp() {
+        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..019bf02 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+"));
+                IsolatedJvmApp.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..14a35d2 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 IsolatedJvmApp.getInstance().getContext();
     }
 
     private ClassLoader getDelegate() {
diff --git a/java/runjava/src/main/java/io/osv/RunJava.java 
b/java/runjava/src/main/java/io/osv/isolated/RunIsolatedJvmApp.java
similarity index 90%
copy from java/runjava/src/main/java/io/osv/RunJava.java
copy to java/runjava/src/main/java/io/osv/isolated/RunIsolatedJvmApp.java
index f7b8302..1eaf3ef 100644
--- a/java/runjava/src/main/java/io/osv/RunJava.java
+++ b/java/runjava/src/main/java/io/osv/isolated/RunIsolatedJvmApp.java
@@ -1,4 +1,4 @@
-package io.osv;
+package io.osv.isolated;
 
 /*
  * Copyright (C) 2013-2014 Cloudius Systems, Ltd.
@@ -7,7 +7,9 @@ package io.osv;
  * BSD license as described in the LICENSE file in the top-level directory.
  */
 
-public class RunJava {
+import io.osv.MainClassNotFoundException;
+
+public class RunIsolatedJvmApp {
        private static native void onVMStop();
 
        static {
@@ -32,7 +34,7 @@ public class RunJava {
         }
 
         try {
-            ContextIsolator.getInstance().runSync(args);
+            IsolatedJvmApp.getInstance().runSync(args);
         } catch (IllegalArgumentException ex) {
             System.err.println("RunJava: " + ex.getMessage());
         } catch (ContextFailedException ex) {
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..fbee1ba 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.IsolatedJvmApp;
 
 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 = IsolatedJvmApp.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/ContextFailedException.java 
b/java/runjava/src/main/java/io/osv/nonisolated/AppThreadTerminatedWithUncaughtException.java
similarity index 57%
rename from java/runjava/src/main/java/io/osv/ContextFailedException.java
rename to 
java/runjava/src/main/java/io/osv/nonisolated/AppThreadTerminatedWithUncaughtException.java
index 541115e..39bb899 100644
--- a/java/runjava/src/main/java/io/osv/ContextFailedException.java
+++ 
b/java/runjava/src/main/java/io/osv/nonisolated/AppThreadTerminatedWithUncaughtException.java
@@ -1,4 +1,4 @@
-package io.osv;
+package io.osv.nonisolated;
 
 /*
  * Copyright (C) 2014 Cloudius Systems, Ltd.
@@ -6,8 +6,8 @@ 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 ContextFailedException(Throwable cause) {
+public class AppThreadTerminatedWithUncaughtException extends Exception {
+    public AppThreadTerminatedWithUncaughtException(Throwable cause) {
         super(cause);
     }
 }
diff --git 
a/java/runjava/src/main/java/io/osv/nonisolated/NonIsolatedJvmApp.java 
b/java/runjava/src/main/java/io/osv/nonisolated/NonIsolatedJvmApp.java
new file mode 100644
index 0000000..557a3bb
--- /dev/null
+++ b/java/runjava/src/main/java/io/osv/nonisolated/NonIsolatedJvmApp.java
@@ -0,0 +1,79 @@
+package io.osv.nonisolated;
+
+import io.osv.JvmApp;
+import io.osv.MainClassNotFoundException;
+
+import java.util.Properties;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class NonIsolatedJvmApp extends JvmApp<Thread> {
+
+    private static final NonIsolatedJvmApp instance = new NonIsolatedJvmApp();
+
+    private AtomicReference<Throwable> thrownException = new 
AtomicReference<>();
+
+    public static NonIsolatedJvmApp getInstance() {
+        return instance;
+    }
+
+    private NonIsolatedJvmApp() {}
+
+    @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/RunJava.java 
b/java/runjava/src/main/java/io/osv/nonisolated/RunNonIsolatedJvmApp.java
similarity index 86%
rename from java/runjava/src/main/java/io/osv/RunJava.java
rename to 
java/runjava/src/main/java/io/osv/nonisolated/RunNonIsolatedJvmApp.java
index f7b8302..660c900 100644
--- a/java/runjava/src/main/java/io/osv/RunJava.java
+++ b/java/runjava/src/main/java/io/osv/nonisolated/RunNonIsolatedJvmApp.java
@@ -1,4 +1,4 @@
-package io.osv;
+package io.osv.nonisolated;
 
 /*
  * Copyright (C) 2013-2014 Cloudius Systems, Ltd.
@@ -7,7 +7,9 @@ package io.osv;
  * BSD license as described in the LICENSE file in the top-level directory.
  */
 
-public class RunJava {
+import io.osv.MainClassNotFoundException;
+
+public class RunNonIsolatedJvmApp {
        private static native void onVMStop();
 
        static {
@@ -32,10 +34,10 @@ public class RunJava {
         }
 
         try {
-            ContextIsolator.getInstance().runSync(args);
+            NonIsolatedJvmApp.getInstance().runSync(args);
         } catch (IllegalArgumentException ex) {
             System.err.println("RunJava: " + ex.getMessage());
-        } catch (ContextFailedException ex) {
+        } catch (AppThreadTerminatedWithUncaughtException ex) {
             if (ex.getCause() instanceof MainClassNotFoundException) {
                 System.err.println("Error: Could not find or load main class " 
+ ((MainClassNotFoundException) ex.getCause()).getClassName());
             } else {
diff --git a/java/tests-isolates/src/main/java/tests/LoggingProcess.java 
b/java/tests-isolates/src/main/java/tests/LoggingProcess.java
index 161d4f6..5f495e0 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.IsolatedJvmApp;
 
 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) 
IsolatedJvmApp.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..8dab9d1 100644
--- a/java/tests-isolates/src/main/java/tests/LoggingProcess.java
+++ b/java/tests-isolates/src/main/java/tests/NonIsolatedLoggingProcess.java
@@ -1,8 +1,5 @@
 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;
@@ -14,10 +11,8 @@ import java.util.logging.SimpleFormatter;
  * 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 +25,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..3d96980 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.IsolatedJvmApp;
 
 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) 
IsolatedJvmApp.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..088fd80 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.IsolatedJvmApp;
 
 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) 
IsolatedJvmApp.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..6ddb970 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.IsolatedJvmApp;
 
 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) 
IsolatedJvmApp.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..dec806b 100644
--- a/java/tests/src/main/java/io/osv/AllTests.java
+++ b/java/tests/src/main/java/io/osv/AllTestsThatTestNonIsolatedApp.java
@@ -12,10 +12,10 @@ import org.junit.runners.Suite;
 
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
-        LoggingIsolationTest.class,
-        ClassLoaderIsolationTest.class,
-        PropertyIsolationTest.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..78b2e3c
--- /dev/null
+++ b/java/tests/src/main/java/io/osv/ClassLoaderWithoutIsolationTest.java
@@ -0,0 +1,88 @@
+package io.osv;
+
+import io.osv.nonisolated.NonIsolatedJvmApp;
+import org.junit.Test;
+import tests.*;
+
+import static io.osv.TestLaunching.runWithoutIsolation;
+import static org.junit.Assert.*;
+
+/*
+ * 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 = 
NonIsolatedJvmApp.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 = 
NonIsolatedJvmApp.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 = 
NonIsolatedJvmApp.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..867a6d4
--- /dev/null
+++ b/java/tests/src/main/java/io/osv/LoggingWithoutIsolationTest.java
@@ -0,0 +1,54 @@
+package io.osv;
+
+import io.osv.nonisolated.NonIsolatedJvmApp;
+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) 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 = 
NonIsolatedJvmApp.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..86e8959 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.IsolatedJvmApp;
+import io.osv.nonisolated.NonIsolatedJvmApp;
+
 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 IsolatedJvmApp.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 NonIsolatedJvmApp.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 [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to