Package io.osv

Source Code of io.osv.ContextIsolator$AppClassLoader

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;
        }
    }

}
TOP

Related Classes of io.osv.ContextIsolator$AppClassLoader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.