Skip to content

Commit 785e57e

Browse files
wkozaczukavikivity
authored andcommitted
Provide alternative java.so to start jvm without classloader and log manager isolation in order to run JRE compact profiles.
In order to run a JVM app using compact profile 1, 2 or 3 JRE the Java bootstrap code under runjava cannot depend on java.beans.* that is not part of any of the compact profiles. More specifically runjava depends on cglib and asm java libraries that manipulate bytecode and rely on java.beans*. 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 Jvm, NonIsolatedJvm, RunNonIsolatedJvmApp, IsolatedJvm, RunIsolatedJvmApp classes * 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]> Message-Id: <[email protected]> Signed-off-by: Avi Kivity <[email protected]>
1 parent f53c0c3 commit 785e57e

32 files changed

+671
-238
lines changed

Makefile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,8 @@ very-quiet = $(if $V, $1, @$1)
136136
ifeq ($(arch),aarch64)
137137
java-targets :=
138138
else
139-
java-targets := $(out)/java/jvm/java.so $(out)/java/jni/balloon.so $(out)/java/jni/elf-loader.so $(out)/java/jni/networking.so \
139+
java-targets := $(out)/java/jvm/java.so $(out)/java/jvm/java_non_isolated.so \
140+
$(out)/java/jni/balloon.so $(out)/java/jni/elf-loader.so $(out)/java/jni/networking.so \
140141
$(out)/java/jni/stty.so $(out)/java/jni/tracepoint.so $(out)/java/jni/power.so $(out)/java/jni/monitor.so
141142
endif
142143

@@ -376,6 +377,10 @@ $(out)/%.o: %.c | generated-headers
376377
$(makedir)
377378
$(call quiet, $(CC) $(CFLAGS) -c -o $@ $<, CC $*.c)
378379

380+
$(out)/java/jvm/java_non_isolated.o: java/jvm/java.cc | generated-headers
381+
$(makedir)
382+
$(call quiet, $(CXX) $(CXXFLAGS) -DRUN_JAVA_NON_ISOLATED -o $@ -c java/jvm/java.cc, CXX $<)
383+
379384
$(out)/%.o: %.S
380385
$(makedir)
381386
$(call quiet, $(CXX) $(CXXFLAGS) $(ASFLAGS) -c -o $@ $<, AS $*.s)

java/jvm/java.cc

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,11 @@ extern size_t jvm_heap_size;
3030
// parameters.
3131

3232
#define JVM_PATH "/usr/lib/jvm/jre/lib/amd64/server/libjvm.so"
33-
#define RUNJAVA "io/osv/RunJava" // separated by slashes, not dots
33+
#if defined(RUN_JAVA_NON_ISOLATED)
34+
#define RUNJAVA "io/osv/nonisolated/RunNonIsolatedJvmApp" // separated by slashes, not dots
35+
#else
36+
#define RUNJAVA "io/osv/isolated/RunIsolatedJvmApp" // separated by slashes, not dots
37+
#endif
3438

3539
JavaVMOption mkoption(const char* s)
3640
{
@@ -96,6 +100,8 @@ static void on_vm_stop(JNIEnv *env, jclass clz) {
96100

97101
static int java_main(int argc, char **argv)
98102
{
103+
std::cout << "java.so: Starting JVM app using: " << RUNJAVA << "\n";
104+
99105
auto prog = elf::get_program();
100106
// The JVM library remains loaded as long as jvm_so is in scope.
101107
auto jvm_so = prog->get_library(JVM_PATH);
@@ -108,8 +114,11 @@ static int java_main(int argc, char **argv)
108114

109115
std::vector<JavaVMOption> options;
110116
options.push_back(mkoption("-Djava.class.path=/dev/null"));
111-
options.push_back(mkoption("-Djava.system.class.loader=io.osv.OsvSystemClassLoader"));
117+
#if !defined(RUN_JAVA_NON_ISOLATED)
118+
std::cout << "java.so: Setting Java system classloader and logging manager to the isolated ones" << "\n";
119+
options.push_back(mkoption("-Djava.system.class.loader=io.osv.isolated.OsvSystemClassLoader"));
112120
options.push_back(mkoption("-Djava.util.logging.manager=io.osv.jul.IsolatingLogManager"));
121+
#endif
113122
options.push_back(mkoption("-Dosv.version=" + osv::version()));
114123

115124
{
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.osv;
2+
3+
/*
4+
* Copyright (C) 2016 Waldemar Kozaczuk
5+
*
6+
* This work is open source software, licensed under the terms of the
7+
* BSD license as described in the LICENSE file in the top-level directory.
8+
*/
9+
public class AppThreadTerminatedWithUncaughtException extends Exception {
10+
public AppThreadTerminatedWithUncaughtException(Throwable cause) {
11+
super(cause);
12+
}
13+
}
Lines changed: 24 additions & 159 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,8 @@
11
package io.osv;
22

3-
import io.osv.jul.IsolatingLogManager;
4-
import net.sf.cglib.proxy.Dispatcher;
5-
import net.sf.cglib.proxy.Enhancer;
6-
73
import java.io.File;
84
import java.io.FileNotFoundException;
95
import java.io.FilePermission;
10-
import java.lang.reflect.Field;
116
import java.lang.reflect.InvocationTargetException;
127
import java.lang.reflect.Method;
138
import java.net.MalformedURLException;
@@ -20,138 +15,19 @@
2015
import java.util.Properties;
2116
import java.util.jar.JarFile;
2217
import java.util.jar.Manifest;
23-
import java.util.logging.LogManager;
2418
import java.util.zip.ZipException;
2519

2620
/*
21+
* Copyright (C) 2016 Waldemar Kozaczuk
2722
* Copyright (C) 2014 Cloudius Systems, Ltd.
2823
*
2924
* This work is open source software, licensed under the terms of the
3025
* BSD license as described in the LICENSE file in the top-level directory.
3126
*/
32-
public class ContextIsolator {
33-
private static final ContextIsolator instance = new ContextIsolator();
34-
35-
static {
36-
verifyLogManagerIsInstalled();
37-
}
38-
39-
private final Context masterContext;
40-
private final Properties commonSystemProperties;
41-
42-
private static void verifyLogManagerIsInstalled() {
43-
LogManager manager = LogManager.getLogManager();
44-
if (!(manager instanceof IsolatingLogManager)) {
45-
throw new AssertionError("For isolation to work logging manager must be "
46-
+ IsolatingLogManager.class.getName() + " but is: " + manager.getClass().getName());
47-
}
48-
}
49-
50-
private final InheritableThreadLocal<Context> currentContext = new InheritableThreadLocal<Context>() {
51-
@Override
52-
protected Context initialValue() {
53-
return masterContext;
54-
}
55-
};
56-
57-
private final ClassLoader parentClassLoaderForIsolates;
58-
59-
public static ContextIsolator getInstance() {
60-
return instance;
61-
}
62-
63-
public ContextIsolator() {
64-
ClassLoader originalSystemClassLoader = getOsvClassLoader().getParent();
65-
commonSystemProperties = copyOf(System.getProperties());
66-
masterContext = new Context(originalSystemClassLoader, copyOf(commonSystemProperties));
67-
68-
parentClassLoaderForIsolates = originalSystemClassLoader;
69-
70-
installSystemPropertiesProxy();
71-
}
72-
73-
private Properties copyOf(Properties properties) {
74-
Properties result = new Properties();
75-
result.putAll(properties);
76-
return result;
77-
}
78-
79-
private static void installSystemPropertiesProxy() {
80-
Enhancer enhancer = new Enhancer();
81-
enhancer.setSuperclass(Properties.class);
82-
enhancer.setCallback(new Dispatcher() {
83-
@Override
84-
public Object loadObject() throws Exception {
85-
return instance.getContext().getProperties();
86-
}
87-
});
88-
Properties contextAwareProperties = (Properties) enhancer.create();
89-
90-
try {
91-
Field props = System.class.getDeclaredField("props");
92-
props.setAccessible(true);
93-
props.set(System.class, contextAwareProperties);
94-
} catch (NoSuchFieldException | IllegalAccessException e) {
95-
throw new AssertionError("Unable to override System.props", e);
96-
}
97-
}
98-
99-
public Context getContext() {
100-
return currentContext.get();
101-
}
102-
103-
private Context run(ClassLoader classLoader, final String classpath, final String mainClass,
104-
final String[] args, final Properties properties) {
105-
Properties contextProperties = new Properties();
106-
contextProperties.putAll(commonSystemProperties);
107-
contextProperties.putAll(properties);
108-
109-
final Context context = new Context(classLoader, contextProperties);
27+
public abstract class Jvm<T> {
28+
public abstract void runSync(String... args) throws Throwable;
11029

111-
Thread thread = new Thread() {
112-
@Override
113-
public void run() {
114-
currentContext.set(context);
115-
context.setProperty("java.class.path", classpath);
116-
117-
try {
118-
runMain(loadClass(mainClass), args);
119-
} catch (InterruptedException e) {
120-
Thread.currentThread().interrupt();
121-
} catch (MainClassNotFoundException e) {
122-
context.setException(e);
123-
} catch (Throwable e) {
124-
getUncaughtExceptionHandler().uncaughtException(this, e);
125-
}
126-
}
127-
};
128-
129-
context.setMainThread(thread);
130-
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
131-
@Override
132-
public void uncaughtException(Thread t, Throwable e) {
133-
context.setException(e);
134-
}
135-
});
136-
thread.setContextClassLoader(classLoader);
137-
thread.start();
138-
return context;
139-
}
140-
141-
public void runSync(String... args) throws Throwable {
142-
Context context = run(args);
143-
144-
while (true) {
145-
try {
146-
context.join();
147-
return;
148-
} catch (InterruptedException e) {
149-
context.interrupt();
150-
}
151-
}
152-
}
153-
154-
public Context run(String... args) throws Throwable {
30+
public T run(String... args) throws Throwable {
15531
Properties properties = new Properties();
15632

15733
ArrayList<String> classpath = new ArrayList<>();
@@ -189,7 +65,7 @@ public Context run(String... args) throws Throwable {
18965
throw new IllegalArgumentException("No jar or class specified to run.");
19066
}
19167

192-
private Context runJar(String jarName, String[] args, ArrayList<String> classpath, Properties properties) throws Throwable {
68+
private T runJar(String jarName, String[] args, ArrayList<String> classpath, Properties properties) throws Throwable {
19369
File jarFile = new File(jarName);
19470
try {
19571
JarFile jar = new JarFile(jarFile);
@@ -208,26 +84,31 @@ private Context runJar(String jarName, String[] args, ArrayList<String> classpat
20884
}
20985
}
21086

211-
private Context runClass(String mainClass, String[] args, Iterable<String> classpath, Properties properties) throws MalformedURLException {
212-
ClassLoader appClassLoader = getClassLoader(classpath, parentClassLoaderForIsolates);
87+
private T runClass(String mainClass, String[] args, Iterable<String> classpath, Properties properties) throws MalformedURLException {
88+
ClassLoader appClassLoader = createAppClassLoader(classpath, getParentClassLoader());
21389
return run(appClassLoader, joinClassPath(classpath), mainClass, args, properties);
21490
}
21591

216-
private static ClassLoader getClassLoader(Iterable<String> classpath, ClassLoader parent) throws MalformedURLException {
92+
protected abstract T run(ClassLoader classLoader, final String classpath, final String mainClass,
93+
final String[] args, final Properties properties);
94+
95+
protected abstract ClassLoader getParentClassLoader();
96+
97+
private ClassLoader createAppClassLoader(Iterable<String> classpath, ClassLoader parent) throws MalformedURLException {
21798
List<URL> urls = toUrls(classpath);
21899
URL[] urlArray = urls.toArray(new URL[urls.size()]);
219100
return new AppClassLoader(urlArray, parent);
220101
}
221102

222-
private static List<URL> toUrls(Iterable<String> classpath) throws MalformedURLException {
103+
private List<URL> toUrls(Iterable<String> classpath) throws MalformedURLException {
223104
ArrayList<URL> urls = new ArrayList<>();
224105
for (String path : classpath) {
225106
urls.add(toUrl(path));
226107
}
227108
return urls;
228109
}
229110

230-
private static void runMain(Class<?> klass, String[] args) throws Throwable {
111+
protected void runMain(Class<?> klass, String[] args) throws Throwable {
231112
Method main = klass.getMethod("main", String[].class);
232113
try {
233114
main.invoke(null, new Object[]{args});
@@ -236,18 +117,15 @@ private static void runMain(Class<?> klass, String[] args) throws Throwable {
236117
}
237118
}
238119

239-
private static OsvSystemClassLoader getOsvClassLoader() {
240-
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
241-
if (!(systemClassLoader instanceof OsvSystemClassLoader)) {
242-
throw new AssertionError("System class loader should be an instance of "
243-
+ OsvSystemClassLoader.class.getName() + " but is "
244-
+ systemClassLoader.getClass().getName());
120+
protected Class<?> loadClass(String name) throws MainClassNotFoundException {
121+
try {
122+
return Thread.currentThread().getContextClassLoader().loadClass(name);
123+
} catch (ClassNotFoundException ex) {
124+
throw new MainClassNotFoundException(name);
245125
}
246-
247-
return (OsvSystemClassLoader) systemClassLoader;
248126
}
249127

250-
private static String joinClassPath(Iterable<String> classpath) {
128+
private String joinClassPath(Iterable<String> classpath) {
251129
StringBuilder sb = new StringBuilder();
252130
boolean first = true;
253131
for (String path : classpath) {
@@ -260,29 +138,21 @@ private static String joinClassPath(Iterable<String> classpath) {
260138
return sb.toString();
261139
}
262140

263-
private static URL toUrl(String path) throws MalformedURLException {
141+
private URL toUrl(String path) throws MalformedURLException {
264142
return new URL("file:///" + path + (isDirectory(path) ? "/" : ""));
265143
}
266144

267-
private static boolean isDirectory(String path) {
145+
private boolean isDirectory(String path) {
268146
return new File(path).isDirectory();
269147
}
270148

271-
private static Class<?> loadClass(String name) throws MainClassNotFoundException {
272-
try {
273-
return Thread.currentThread().getContextClassLoader().loadClass(name);
274-
} catch (ClassNotFoundException ex) {
275-
throw new MainClassNotFoundException(name);
276-
}
277-
}
278-
279149
// Expand classpath, as given in the "-classpath" option, to a list of
280150
// jars or directories. As in the traditional "java" command-line
281151
// launcher, components of the class path are separated by ":", and
282152
// we also support the traditional (but awkward) Java wildcard syntax,
283153
// where "dir/*" adds to the classpath all jar files in the given
284154
// directory.
285-
private static Iterable<String> expandClassPath(String classpath) {
155+
private Iterable<String> expandClassPath(String classpath) {
286156
ArrayList<String> ret = new ArrayList<>();
287157
for (String component : classpath.split(":")) {
288158
if (component.endsWith("/*")) {
@@ -307,10 +177,6 @@ private static Iterable<String> expandClassPath(String classpath) {
307177
return ret;
308178
}
309179

310-
public Object receive() throws InterruptedException {
311-
return getContext().takeMessage();
312-
}
313-
314180
private static class AppClassLoader extends URLClassLoader {
315181
public AppClassLoader(URL[] urlArray, ClassLoader parent) {
316182
super(urlArray, parent);
@@ -324,5 +190,4 @@ protected PermissionCollection getPermissions(CodeSource codesource) {
324190
return permissions;
325191
}
326192
}
327-
328193
}

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

Lines changed: 0 additions & 48 deletions
This file was deleted.

0 commit comments

Comments
 (0)