On Tue, Aug 9, 2016 at 5:52 AM, Waldemar Kozaczuk <[email protected]>
wrote:

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

Avi/Nadav, do such comments conform with our coding style / licensing
policy?

-- 
You received this message because you are subscribed to the Google Groups "OSv 
Development" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to