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.