PageRenderTime 29ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/java/runjava/src/main/java/io/osv/ContextIsolator.java

https://gitlab.com/jforge/osv
Java | 328 lines | 274 code | 41 blank | 13 comment | 30 complexity | 742f739499088706546ca35a5b3f6dd0 MD5 | raw file
Possible License(s): BSD-3-Clause, 0BSD, MPL-2.0-no-copyleft-exception
  1. package io.osv;
  2. import io.osv.jul.IsolatingLogManager;
  3. import net.sf.cglib.proxy.Dispatcher;
  4. import net.sf.cglib.proxy.Enhancer;
  5. import java.io.File;
  6. import java.io.FileNotFoundException;
  7. import java.io.FilePermission;
  8. import java.lang.reflect.Field;
  9. import java.lang.reflect.InvocationTargetException;
  10. import java.lang.reflect.Method;
  11. import java.net.MalformedURLException;
  12. import java.net.URL;
  13. import java.net.URLClassLoader;
  14. import java.security.CodeSource;
  15. import java.security.PermissionCollection;
  16. import java.util.ArrayList;
  17. import java.util.List;
  18. import java.util.Properties;
  19. import java.util.jar.JarFile;
  20. import java.util.jar.Manifest;
  21. import java.util.logging.LogManager;
  22. import java.util.zip.ZipException;
  23. /*
  24. * Copyright (C) 2014 Cloudius Systems, Ltd.
  25. *
  26. * This work is open source software, licensed under the terms of the
  27. * BSD license as described in the LICENSE file in the top-level directory.
  28. */
  29. public class ContextIsolator {
  30. private static final ContextIsolator instance = new ContextIsolator();
  31. static {
  32. verifyLogManagerIsInstalled();
  33. }
  34. private final Context masterContext;
  35. private final Properties commonSystemProperties;
  36. private static void verifyLogManagerIsInstalled() {
  37. LogManager manager = LogManager.getLogManager();
  38. if (!(manager instanceof IsolatingLogManager)) {
  39. throw new AssertionError("For isolation to work logging manager must be "
  40. + IsolatingLogManager.class.getName() + " but is: " + manager.getClass().getName());
  41. }
  42. }
  43. private final InheritableThreadLocal<Context> currentContext = new InheritableThreadLocal<Context>() {
  44. @Override
  45. protected Context initialValue() {
  46. return masterContext;
  47. }
  48. };
  49. private final ClassLoader parentClassLoaderForIsolates;
  50. public static ContextIsolator getInstance() {
  51. return instance;
  52. }
  53. public ContextIsolator() {
  54. ClassLoader originalSystemClassLoader = getOsvClassLoader().getParent();
  55. commonSystemProperties = copyOf(System.getProperties());
  56. masterContext = new Context(originalSystemClassLoader, copyOf(commonSystemProperties));
  57. parentClassLoaderForIsolates = originalSystemClassLoader;
  58. installSystemPropertiesProxy();
  59. }
  60. private Properties copyOf(Properties properties) {
  61. Properties result = new Properties();
  62. result.putAll(properties);
  63. return result;
  64. }
  65. private static void installSystemPropertiesProxy() {
  66. Enhancer enhancer = new Enhancer();
  67. enhancer.setSuperclass(Properties.class);
  68. enhancer.setCallback(new Dispatcher() {
  69. @Override
  70. public Object loadObject() throws Exception {
  71. return instance.getContext().getProperties();
  72. }
  73. });
  74. Properties contextAwareProperties = (Properties) enhancer.create();
  75. try {
  76. Field props = System.class.getDeclaredField("props");
  77. props.setAccessible(true);
  78. props.set(System.class, contextAwareProperties);
  79. } catch (NoSuchFieldException | IllegalAccessException e) {
  80. throw new AssertionError("Unable to override System.props", e);
  81. }
  82. }
  83. public Context getContext() {
  84. return currentContext.get();
  85. }
  86. private Context run(ClassLoader classLoader, final String classpath, final String mainClass,
  87. final String[] args, final Properties properties) {
  88. Properties contextProperties = new Properties();
  89. contextProperties.putAll(commonSystemProperties);
  90. contextProperties.putAll(properties);
  91. final Context context = new Context(classLoader, contextProperties);
  92. Thread thread = new Thread() {
  93. @Override
  94. public void run() {
  95. currentContext.set(context);
  96. context.setProperty("java.class.path", classpath);
  97. try {
  98. runMain(loadClass(mainClass), args);
  99. } catch (InterruptedException e) {
  100. Thread.currentThread().interrupt();
  101. } catch (MainClassNotFoundException e) {
  102. context.setException(e);
  103. } catch (Throwable e) {
  104. getUncaughtExceptionHandler().uncaughtException(this, e);
  105. }
  106. }
  107. };
  108. context.setMainThread(thread);
  109. thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
  110. @Override
  111. public void uncaughtException(Thread t, Throwable e) {
  112. context.setException(e);
  113. }
  114. });
  115. thread.setContextClassLoader(classLoader);
  116. thread.start();
  117. return context;
  118. }
  119. public void runSync(String... args) throws Throwable {
  120. Context context = run(args);
  121. while (true) {
  122. try {
  123. context.join();
  124. return;
  125. } catch (InterruptedException e) {
  126. context.interrupt();
  127. }
  128. }
  129. }
  130. public Context run(String... args) throws Throwable {
  131. Properties properties = new Properties();
  132. ArrayList<String> classpath = new ArrayList<>();
  133. for (int i = 0; i < args.length; i++) {
  134. if (args[i].equals("-jar")) {
  135. if (i + 1 >= args.length) {
  136. throw new IllegalArgumentException("Missing jar name after '-jar'.");
  137. }
  138. return runJar(args[i + 1], java.util.Arrays.copyOfRange(args, i + 2, args.length), classpath, properties);
  139. } else if (args[i].equals("-classpath") || args[i].equals("-cp")) {
  140. if (i + 1 >= args.length) {
  141. throw new IllegalArgumentException("Missing parameter after '" + args[i] + "'");
  142. }
  143. for (String c : expandClassPath(args[i + 1])) {
  144. classpath.add(c);
  145. }
  146. i++;
  147. } else if (args[i].startsWith("-D")) {
  148. int eq = args[i].indexOf('=');
  149. if (eq < 0) {
  150. /* -Dfoo is a special case for -Dfoo=true */
  151. String key = args[i].substring(2);
  152. properties.put(key, "true");
  153. } else {
  154. String key = args[i].substring(2, eq);
  155. String value = args[i].substring(eq + 1, args[i].length());
  156. properties.put(key, value);
  157. }
  158. } else if (!args[i].startsWith("-")) {
  159. return runClass(args[i], java.util.Arrays.copyOfRange(args, i + 1, args.length), classpath, properties);
  160. } else {
  161. throw new IllegalArgumentException("Unknown parameter '" + args[i] + "'");
  162. }
  163. }
  164. throw new IllegalArgumentException("No jar or class specified to run.");
  165. }
  166. private Context runJar(String jarName, String[] args, ArrayList<String> classpath, Properties properties) throws Throwable {
  167. File jarFile = new File(jarName);
  168. try {
  169. JarFile jar = new JarFile(jarFile);
  170. Manifest mf = jar.getManifest();
  171. jar.close();
  172. String mainClass = mf.getMainAttributes().getValue("Main-Class");
  173. if (mainClass == null) {
  174. throw new IllegalArgumentException("No 'Main-Class' attribute in manifest of " + jarName);
  175. }
  176. classpath.add(jarName);
  177. return runClass(mainClass, args, classpath, properties);
  178. } catch (FileNotFoundException e) {
  179. throw new IllegalArgumentException("File not found: " + jarName);
  180. } catch (ZipException e) {
  181. throw new IllegalArgumentException("File is not a jar: " + jarName, e);
  182. }
  183. }
  184. private Context runClass(String mainClass, String[] args, Iterable<String> classpath, Properties properties) throws MalformedURLException {
  185. ClassLoader appClassLoader = getClassLoader(classpath, parentClassLoaderForIsolates);
  186. return run(appClassLoader, joinClassPath(classpath), mainClass, args, properties);
  187. }
  188. private static ClassLoader getClassLoader(Iterable<String> classpath, ClassLoader parent) throws MalformedURLException {
  189. List<URL> urls = toUrls(classpath);
  190. URL[] urlArray = urls.toArray(new URL[urls.size()]);
  191. return new AppClassLoader(urlArray, parent);
  192. }
  193. private static List<URL> toUrls(Iterable<String> classpath) throws MalformedURLException {
  194. ArrayList<URL> urls = new ArrayList<>();
  195. for (String path : classpath) {
  196. urls.add(toUrl(path));
  197. }
  198. return urls;
  199. }
  200. private static void runMain(Class<?> klass, String[] args) throws Throwable {
  201. Method main = klass.getMethod("main", String[].class);
  202. try {
  203. main.invoke(null, new Object[]{args});
  204. } catch (InvocationTargetException ex) {
  205. throw ex.getCause();
  206. }
  207. }
  208. private static OsvSystemClassLoader getOsvClassLoader() {
  209. ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
  210. if (!(systemClassLoader instanceof OsvSystemClassLoader)) {
  211. throw new AssertionError("System class loader should be an instance of "
  212. + OsvSystemClassLoader.class.getName() + " but is "
  213. + systemClassLoader.getClass().getName());
  214. }
  215. return (OsvSystemClassLoader) systemClassLoader;
  216. }
  217. private static String joinClassPath(Iterable<String> classpath) {
  218. StringBuilder sb = new StringBuilder();
  219. boolean first = true;
  220. for (String path : classpath) {
  221. if (!first) {
  222. sb.append(":");
  223. }
  224. first = false;
  225. sb.append(path);
  226. }
  227. return sb.toString();
  228. }
  229. private static URL toUrl(String path) throws MalformedURLException {
  230. return new URL("file:///" + path + (isDirectory(path) ? "/" : ""));
  231. }
  232. private static boolean isDirectory(String path) {
  233. return new File(path).isDirectory();
  234. }
  235. private static Class<?> loadClass(String name) throws MainClassNotFoundException {
  236. try {
  237. return Thread.currentThread().getContextClassLoader().loadClass(name);
  238. } catch (ClassNotFoundException ex) {
  239. throw new MainClassNotFoundException(name);
  240. }
  241. }
  242. // Expand classpath, as given in the "-classpath" option, to a list of
  243. // jars or directories. As in the traditional "java" command-line
  244. // launcher, components of the class path are separated by ":", and
  245. // we also support the traditional (but awkward) Java wildcard syntax,
  246. // where "dir/*" adds to the classpath all jar files in the given
  247. // directory.
  248. private static Iterable<String> expandClassPath(String classpath) {
  249. ArrayList<String> ret = new ArrayList<>();
  250. for (String component : classpath.split(":")) {
  251. if (component.endsWith("/*")) {
  252. File dir = new File(
  253. component.substring(0, component.length() - 2));
  254. if (dir.isDirectory()) {
  255. File[] files = dir.listFiles();
  256. if (files == null) {
  257. continue;
  258. }
  259. for (File file : files) {
  260. String filename = file.getPath();
  261. if (filename.endsWith(".jar")) {
  262. ret.add(filename);
  263. }
  264. }
  265. continue; // handled this path component
  266. }
  267. }
  268. ret.add(component);
  269. }
  270. return ret;
  271. }
  272. public Object receive() throws InterruptedException {
  273. return getContext().takeMessage();
  274. }
  275. private static class AppClassLoader extends URLClassLoader {
  276. public AppClassLoader(URL[] urlArray, ClassLoader parent) {
  277. super(urlArray, parent);
  278. }
  279. @Override
  280. protected PermissionCollection getPermissions(CodeSource codesource) {
  281. PermissionCollection permissions = super.getPermissions(codesource);
  282. permissions.add(new FilePermission("/usr/lib/jvm/jre/lib/ext/runjava.jar", "read"));
  283. permissions.add(new RuntimePermission("exitVM"));
  284. return permissions;
  285. }
  286. }
  287. }