/*
* $Id$
*
* Copyright (C) 2003-2014 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.vm.isolate;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javax.isolate.Isolate;
import javax.isolate.IsolateStartupException;
import javax.isolate.IsolateStatus;
import javax.isolate.Link;
import javax.naming.NameNotFoundException;
import org.jnode.annotation.MagicPermission;
import org.jnode.annotation.PrivilegedActionPragma;
import org.jnode.annotation.SharedStatics;
import org.jnode.naming.InitialNaming;
import org.jnode.plugin.PluginManager;
import org.jnode.vm.IOContext;
import org.jnode.vm.Unsafe;
import org.jnode.vm.VmIOContext;
import org.jnode.vm.VmMagic;
import org.jnode.vm.VmSystem;
import org.jnode.vm.classmgr.VmIsolatedStatics;
import org.jnode.vm.classmgr.VmType;
import org.jnode.vm.facade.ObjectVisitor;
import org.jnode.vm.facade.VmArchitecture;
import org.jnode.vm.facade.VmUtils;
import org.jnode.vm.objects.BootableHashMap;
import org.jnode.vm.scheduler.VmThread;
/**
* VM specific implementation of the Isolate class.
*
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
@MagicPermission
public final class VmIsolate {
/**
* The Isolate object that this object implements.
*/
private final Isolate isolate;
/**
* The classname of the main class.
*/
private final String mainClass;
/**
* The arguments to the main method.
*/
private final String[] args;
/**
* The isolate the started this isolate.
*/
private VmIsolate starter;
/**
* The isolate that created this isolate.
*/
private final VmIsolate creator;
/**
* The state of this isolate.
*/
private State state = State.CREATED;
private IsolateStatus.State isolateState;
private IsolateStatus.ExitReason exitReason;
private int exitCode;
/**
* The root of the threadgroups for this isolate.
*/
private ThreadGroup threadGroup;
/**
* IO bindings
*/
private final VmStreamBindings bindings;
/**
* Mapping between internal VmType and per isolate Class instance.
*/
private BootableHashMap<VmType<?>, Class<?>> classesMap = new BootableHashMap<VmType<?>, Class<?>>();
/**
* Isolated Statics table for this isolate
*/
private final VmIsolatedStatics isolatedStaticsTable;
/**
* System classloader for this isolate
*/
private ClassLoader systemClassLoader;
/**
* Links passed to the start of this isolate
*/
private VmLink[] dataLinks;
/**
* Status links created by newStatusLink()
*/
private LinkedList<VmLink> statusLinks = new LinkedList<VmLink>();
/**
* The isolate-specific default IO context
*/
private final IOContext vmIoContext = new VmIOContext();
/**
* The isolate-specific switchable IO context
*/
private IOContext ioContext = vmIoContext;
private Properties initProperties;
/**
* Unique identifier.
*/
private final int id;
/**
* Mapping between internal VmType and per isolate Class instance.
*/
private BootableHashMap<VmIsolateLocal<?>, ?> isolateLocalMap = new BootableHashMap<VmIsolateLocal<?>, Object>();
/**
* List of isolates started by this isolate.
*/
private final List<VmIsolate> children = new LinkedList<VmIsolate>();
/**
* Additional classpath for this isolate.
*/
private URL[] classpath;
/**
* Isolate states.
*
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
@SharedStatics
private enum State {
CREATED(IsolateStatus.State.UNKNOWN),
STARTING(IsolateStatus.State.STARTING),
STARTED(IsolateStatus.State.STARTED),
EXITING(IsolateStatus.State.EXITING),
EXITED(IsolateStatus.State.EXITED),
TERMINATING(IsolateStatus.State.EXITING),
TERMINATED(IsolateStatus.State.EXITED),
NEVERSTARTED(IsolateStatus.State.UNKNOWN),
UNKNOWN(IsolateStatus.State.UNKNOWN);
private final IsolateStatus.State isolateState;
private State(IsolateStatus.State isolateState) {
this.isolateState = isolateState;
}
IsolateStatus.State getIsolateState() {
return isolateState;
}
}
public static boolean walkIsolates(ObjectVisitor visitor) {
for (int i = 0; i < StaticData.isolates.size(); i++) {
VmIsolate isolate = StaticData.isolates.get(i);
if (!isolate.isolatedStaticsTable.walk(visitor))
return false;
}
return true;
}
public static Iterator<VmIsolatedStatics> staticsIterator() {
ArrayList<VmIsolatedStatics> l = new ArrayList<VmIsolatedStatics>();
VmIsolatedStatics ist = StaticData.rootIsolate.getIsolatedStaticsTable();
if (ist != null)
l.add(ist);
for (VmIsolate is : StaticData.isolates.toArray(new VmIsolate[StaticData.isolates.size()])) {
ist = is.getIsolatedStaticsTable();
if (ist != null)
l.add(ist);
}
return l.iterator();
}
/**
* Static data of the VMIsolate class.
*
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
@SharedStatics
private static final class StaticData {
/**
* The root (aka system) isolate.
*/
private static transient VmIsolate rootIsolate;
/**
* Non-root isolates.
*/
private static final List<VmIsolate> isolates = new LinkedList<VmIsolate>();
private static int nextId = 0;
static final VmIsolate getRoot() {
if (rootIsolate == null) {
rootIsolate = new VmIsolate(null/*Thread.currentThread().getVmThread().getIsolatedStatics()*/);
// org.jnode.vm.Unsafe.debug("getRoot() istatics: " + rootIsolate.isolatedStaticsTable + "\n");
// org.jnode.vm.Unsafe.debugStackTrace();
}
return rootIsolate;
}
static synchronized int nextId() {
return nextId++;
}
}
/**
* Isolate specific static data
*
* @author Ewout Prangsma (epr@users.sourceforge.net)
*/
private static class IsolatedStaticData {
/**
* The current isolate.
*/
static VmIsolate current;
/**
* Types of the arguments of the main(String[]) method
*/
static final Class[] mainTypes = new Class[]{String[].class};
}
/**
* Constructor for the root isolate.
*/
private VmIsolate(VmIsolatedStatics isolatedStatics) {
this.id = StaticData.nextId();
this.isolate = new Isolate(this);
this.mainClass = null;
this.args = null;
this.bindings = new VmStreamBindings();
this.state = State.STARTED;
this.threadGroup = getRootThreadGroup();
this.creator = null;
this.isolatedStaticsTable = isolatedStatics;
// Initialize currentHolder
IsolatedStaticData.current = this;
}
/**
* Initialize this instance.
*
* @param isolate
* @param mainClass
* @param args
* @param bindings
* @param properties
*/
public VmIsolate(Isolate isolate, VmStreamBindings bindings,
Properties properties, String mainClass, String[] args) {
this.id = StaticData.nextId();
this.initProperties = properties;
this.isolate = isolate;
this.mainClass = mainClass;
this.args = args;
this.bindings = bindings;
final VmArchitecture arch = VmUtils.getVm().getArch();
this.isolatedStaticsTable = new VmIsolatedStatics(VmMagic.currentProcessor().getIsolatedStatics(),
arch, new Unsafe.UnsafeObjectResolver());
this.creator = currentIsolate();
if (getRoot().executor == null && isRoot()) {
//initialize the root executor on the creation of the first child
getRoot().invokeAndWait(new Runnable() {
public void run() {
//org.jnode.vm.Unsafe.debug("Root executor ready\n");
}
});
}
StaticData.isolates.add(this);
}
/**
* Is the current thread running in the root isolate
*/
public static boolean isRoot() {
VmIsolate result = IsolatedStaticData.current;
if (result != null) {
return (result == StaticData.getRoot());
}
return true;
}
/**
* @return the root isolate
*/
public static VmIsolate getRoot() {
//todo security
return StaticData.getRoot();
}
/**
* @return an array of isolates without the root isolate
*/
public static VmIsolate[] getVmIsolates() {
//todo security
return StaticData.isolates.toArray(new VmIsolate[0]);
}
/**
* Gets the current isolate.
*
* @return the current isolate
*/
public static VmIsolate currentIsolate() {
VmIsolate result = IsolatedStaticData.current;
if (result == null) {
result = StaticData.getRoot();
}
return result;
}
/**
* Gets the isolate specific Class for the given type.
*/
@SuppressWarnings("unchecked")
public final <T> Class<T> getClassForType(VmType<T> type) {
Class<T> result = (Class<T>) classesMap.get(type);
if (result == null) {
synchronized (classesMap) {
result = (Class<T>) classesMap.get(type);
if (result == null) {
result = type.newClass();
classesMap.put(type, result);
}
}
}
return result;
}
/**
* Gets the isolate object that this object implements.
*
* @return the exposed Isolate object
*/
public final Isolate getIsolate() {
return isolate;
}
public final void isolateExit(int status) {
changeState(State.EXITING);
this.exitCode = status;
if (currentIsolate() == this) {
this.exitReason = IsolateStatus.ExitReason.SELF_EXIT;
} else {
this.exitReason = IsolateStatus.ExitReason.OTHER_EXIT;
}
disposeAppContext(this.exitReason == IsolateStatus.ExitReason.SELF_EXIT);
//stopAllThreads();
}
public final void isolateHalt(int status) {
changeState(State.EXITING);
this.exitCode = status;
if (currentIsolate() == this) {
this.exitReason = IsolateStatus.ExitReason.SELF_HALT;
} else {
this.exitReason = IsolateStatus.ExitReason.OTHER_HALT;
}
disposeAppContext(this.exitReason == IsolateStatus.ExitReason.SELF_HALT);
//stopAllThreads();
}
public final void systemExit(Isolate isolate, int status) {
//only this isolate may call this method
testIsolate(isolate);
//todo add similar checks to other exit modes too
synchronized (this) {
if (!this.state.equals(State.STARTED))
return;
}
changeState(State.EXITING);
this.exitReason = IsolateStatus.ExitReason.SELF_EXIT;
this.exitCode = status;
disposeAppContext(true);
//stopAllThreads();
}
public final void systemHalt(Isolate isolate, int status) {
//only this isolate may call this method
testIsolate(isolate);
changeState(State.EXITING);
this.exitReason = IsolateStatus.ExitReason.SELF_HALT;
this.exitCode = status;
disposeAppContext(true);
//stopAllThreads();
}
private void stopAllThreads() {
// TODO - investigate it
// TODO - this is probably unsafe because any of the threads being killed could
// be in the middle of updating a critical system data structure. I'm also
// unsure of the order in which we are killing the threads here. It might be
// better to kill the isolate's main thread first to give it the chance to
// do a graceful shutdown. (Stephen Crawley - 2008-11-08)
int ac = threadGroup.activeCount();
if (ac > 0) {
Thread[] ta = new Thread[ac];
int rc = threadGroup.enumerate(ta);
Thread current = Thread.currentThread();
boolean found = false;
for (int i = 0; i < rc; i++) {
Thread thread = ta[i];
if (current != thread) {
ThreadHelper.getVmThread(thread).stopForced(null);
} else {
found = true;
}
}
if (found) {
ThreadHelper.getVmThread(current).stop(new ThreadDeath());
} else {
doExit();
}
} else {
// TODO - analyze this case
doExit();
}
}
/**
* Request normal termination of this isolate.
*
* @param status
*/
public final void implicitExit(VmThread vmThread, int status) {
//on this isolate may call this method
testIsolate(currentIsolate().isolate);
// TODO - handle demon threads
if (threadGroup.activeCount() > 0 || threadGroup.activeGroupCount() > 0)
return;
if (exitReason == null) {
changeState(State.EXITING);
exitReason = IsolateStatus.ExitReason.IMPLICIT_EXIT;
this.exitCode = status;
}
if (vmThread.getName().contains("-AWT-stopper")) {
doExit();
} else {
disposeAppContext(true);
}
//doExit();
///stopAllThreads();
}
/**
* Request normal termination of this isolate.
*/
public final void uncaughtExceptionExit() {
//on this isolate may call this method
testIsolate(currentIsolate().isolate);
// TODO - handle demon threads
if (threadGroup.activeCount() > 0 || threadGroup.activeGroupCount() > 0)
return;
changeState(State.EXITING);
exitReason = IsolateStatus.ExitReason.UNCAUGHT_EXCEPTION;
this.exitCode = -1;
disposeAppContext(true);
//doExit();
//stopAllThreads();
}
/**
* Force termination of this isolate.
*
* @param status
* @deprecated
*/
@SuppressWarnings("deprecation")
public final void halt(int status) {
changeState(State.EXITING);
switch (state) {
case EXITING:
threadGroup.stop();
break;
}
if (currentIsolate() == this) {
this.exitReason = IsolateStatus.ExitReason.SELF_HALT;
} else {
this.exitReason = IsolateStatus.ExitReason.OTHER_HALT;
}
this.exitCode = status;
doExit();
}
private void doExit() {
try {
if (!threadGroup.isDestroyed())
threadGroup.destroy();
} catch (Throwable t) {
t.printStackTrace();
}
this.creator.removeChild(this);
StaticData.isolates.remove(this);
changeState(State.EXITED);
}
/**
* Has this isolate reached the exited state.
*
* @return {@code true} if the isolate has 'exited', otherwise {@code false}
*/
public final boolean hasExited() {
switch (state) {
case EXITED:
case TERMINATED:
return true;
default:
return false;
}
}
/**
* Has this isolate reached the terminated state.
*
* @return {@code true} if the isolate has 'terminated', otherwise {@code false}
*/
public final boolean hasTerminated() {
switch (state) {
case TERMINATED:
return true;
default:
return false;
}
}
/**
* Has this isolate reached the started state.
*
* @return {@code true} if the isolate has been 'started', otherwise {@code false}
*/
public final boolean hasStarted() {
switch (state) {
case STARTED:
case EXITED:
case TERMINATED:
return true;
default:
return false;
}
}
/**
* Gets the links passed to the start of the current isolate.
*/
public static Link[] getLinks() {
final VmLink[] vmLinks = currentIsolate().dataLinks;
if ((vmLinks == null) || (vmLinks.length == 0)) {
return new Link[0];
} else {
Link[] links = new Link[vmLinks.length];
int i = 0;
for (VmLink vmLink : vmLinks) {
links[i++] = vmLink.asLink();
}
return links;
}
}
/**
* Start this isolate.
*
* @param isolate the isolate to start
* @param links an array of links passed to the isolate on startup
* @throws IsolateStartupException on startup failure
*/
@PrivilegedActionPragma
public final void start(Isolate isolate, Link[] links)
throws IsolateStartupException {
testIsolate(isolate);
// The creator of this isolate must be the same as the current isolate
if (creator != currentIsolate()) {
throw new IllegalStateException(
"Creator is different from current isolate");
}
synchronized (this) {
// The state must be CREATED
if (state != State.CREATED) {
throw new IllegalStateException("Isolate has already been started");
}
changeState(State.STARTING);
}
// Save starter
this.starter = currentIsolate();
// Save links
this.dataLinks = null;
if (links != null) {
VmLink[] vmLinks = new VmLink[links.length];
int i = 0;
for (Link link : links) {
if (!link.isOpen()) {
throw new IsolateStartupException("Link is closed");
}
vmLinks[i] = VmLink.fromLink(link);
}
this.dataLinks = vmLinks;
}
// Create a new ThreadGroup
this.threadGroup = new ThreadGroup(StaticData.getRoot().threadGroup, mainClass);
// Find plugin manager
PluginManager piManager;
try {
piManager = InitialNaming.lookup(PluginManager.NAME);
} catch (NameNotFoundException ex) {
throw new IsolateStartupException("Cannot find PluginManager", ex);
}
// Create I/O channels
final PrintStream stdout;
final PrintStream stderr;
final InputStream stdin;
try {
stdout = bindings.createIsolatedOut();
stderr = bindings.createIsolatedErr();
stdin = bindings.createIsolatedIn();
} catch (IOException ex) {
throw new IsolateStartupException("Failed to create I/O streams",
ex);
}
// Create the main thread
final IsolateThread mainThread = new IsolateThread(threadGroup, this,
piManager, stdout, stderr, stdin);
// Start the main thread.
mainThread.start();
}
/*
private Vector<Runnable> taskList = new Vector<Runnable>();
private final Object taskSync = new Object();
private Thread executorThread;
*/
// public void invokeAndWait(final Runnable task) {
//TODO implement VmIsolate.invokeAndWait(Runnable)
/*
if(this == StaticData.rootIsolate){
task.run();
return;
}
synchronized(taskSync){
taskList.add(task);
taskSync.notifyAll();
}
synchronized(task){
while(taskList.contains(task)){
try {
task.wait();
}catch(InterruptedException e){
//
}
}
}
*/
// }
/*
private class TaskExecutor implements Runnable{
public void run() {
//while(!VmIsolate.this.hasTerminated()){
do {
Runnable task = null;
synchronized(taskSync){
try {
while(taskList.isEmpty()){
taskSync.wait();
}
try {
task = taskList.get(0);
task.run();
taskList.remove(0);
} catch(Throwable t){
System.err.println("Error during task execution, dropping task");
t.printStackTrace();
taskList.remove(0);
}
}catch(InterruptedException ie){
//
}
}
if(task != null)
synchronized(task){
task.notifyAll();
}
} while(!hasExited());
//} while(true);
}
}
*/
private ExecutorService executor;
/**
* Execute a task within this isolate and wait for completion.
*
* @param task the task as a Runnable object
*/
public synchronized void invokeAndWait(final Runnable task) {
if (executor == null) {
executor = AccessController.doPrivileged(new PrivilegedAction<ExecutorService>() {
public ExecutorService run() {
return Executors.newSingleThreadExecutor(new IsolateThreadFactory2(VmIsolate.this));
}
});
}
if (task == null)
return;
try {
if (executor.submit(task).get() != null) {
throw new RuntimeException("Execution failed!");
}
} catch (Exception x) {
throw new RuntimeException(x);
}
}
/**
* Execute a task asynchronously within this isolate.
*
* @param task the task as a Runnable object
*/
public synchronized void invokeLater(final Runnable task) {
org.jnode.vm.Unsafe.debug("invokeLater Called - 0\n");
if (executor == null) {
executor = java.util.concurrent.Executors.newSingleThreadExecutor(new IsolateThreadFactory(this));
org.jnode.vm.Unsafe.debug("invokeAndWait executor created - 0\n");
}
if (task == null)
return;
try {
org.jnode.vm.Unsafe.debug("invokeAndWait submitting task - 0\n");
executor.submit(task);
} catch (Exception x) {
throw new RuntimeException(x);
}
}
boolean isEDT() {
if (appContext == null)
return false;
try {
Object eq = appContext.getClass().getMethod("get", Object.class).
invoke(appContext, appContext.getClass().getField("EVENT_QUEUE_KEY").get(null));
if (eq == null)
return false;
org.jnode.vm.Unsafe.debug("isEDT - 1\n");
Object t = eq.getClass().getField("dispatchThread").get(eq);
if (t == null)
return false;
org.jnode.vm.Unsafe.debug("isEDT edt=" + t + '\n');
org.jnode.vm.Unsafe.debug("isEDT currenThread=" + Thread.currentThread() + '\n');
return t == Thread.currentThread();
} catch (Exception x) {
throw new RuntimeException(x);
}
/*
try {
return (Boolean) Class.forName("java.awt.EventQueue").
getMethod("isDispatchThread").invoke(null);
} catch (Exception x) {
throw new RuntimeException(x);
}
*/
// return false;
}
private Object appContext;
private void disposeAppContext(boolean intraIsolate) {
if (appSupport != null) {
appSupport.stop(intraIsolate);
} else {
stopAllThreads();
}
}
/**
* @param intraIsolate
* @deprecated
*/
private void disposeAppContext_old(boolean intraIsolate) {
final Object appContext;
final boolean is_edt;
synchronized (this) {
is_edt = isEDT();
appContext = this.appContext;
this.appContext = null;
}
org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 000\n");
org.jnode.vm.Unsafe.debugStackTrace();
org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 000 " + intraIsolate + '\n');
if (appContext != null) {
org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 0001\n");
org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 0002\n");
if (intraIsolate && is_edt) {
org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 0003\n");
Thread t = new Thread(new Runnable() {
public void run() {
org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 00\n");
getRoot().invokeAndWait(new Runnable() {
public void run() {
try {
org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 01\n");
appContext.getClass().getMethod("dispose").invoke(appContext);
org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 02\n");
} catch (Exception x) {
x.printStackTrace();
}
}
});
stopAllThreads();
doExit();
}
}, "isolate-" + getId() + "-AWT-stopper");
t.start();
} else {
org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 0004\n");
org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 0\n");
getRoot().invokeAndWait(new Runnable() {
public void run() {
try {
org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 1\n");
org.jnode.vm.Unsafe.debug("disposeAppContextCalled appcontext: " + appContext + '\n');
org.jnode.vm.Unsafe.debug(
"disposeAppContextCalled appcontext.getClass(): " + appContext.getClass() + '\n');
org.jnode.vm.Unsafe.debug("disposeAppContextCalled appcontext.getClass().dispose: " +
appContext.getClass().getMethod("dispose") + '\n');
appContext.getClass().getMethod("dispose").invoke(appContext);
org.jnode.vm.Unsafe.debug("disposeAppContextCalled - 2\n");
} catch (Exception x) {
x.printStackTrace();
}
}
});
stopAllThreads();
}
}
}
/**
* Run this isolate. This method is called from IsolateThread.
*/
@PrivilegedActionPragma
final void run(IsolateThread thread) {
try {
// Set current
IsolatedStaticData.current = VmIsolate.this;
// Set I/O
VmSystem.setOut(thread.getStdout());
VmSystem.setErr(thread.getStderr());
VmSystem.setIn(thread.getStdin());
// Set the isolate's properties to a copy of the initial properties passed
// when the isolate was created. (This needs to be done really early
// via the IOContext switch to avoid NPEs when the native compiler,
// class loader, security manager, etc call System.getProperty() too
// soon.)
Properties myProps = new Properties();
for (Object key : initProperties.keySet()) {
myProps.setProperty(
(String) key, (String) initProperties.getProperty((String) key));
}
System.setProperties(myProps);
// Set context classloader
final ClassLoader loader = getClassLoader(thread);
Thread.currentThread().setContextClassLoader(loader);
// Fire started events.
// TODO implement me
// Load the main class
final Class<?> cls = loader.loadClass(mainClass);
//start executor
//executorThread = new Thread(new TaskExecutor(), "isolate-executor");
//executorThread.start();
// Find main method
final Method mainMethod = cls.getMethod("main",
new Class[]{String[].class});
// IsolatedStaticData.mainTypes);
//create the appcontext for this isolate
// TODO - improve this
//appContext = Class.forName("sun.awt.SunToolkit").getMethod("createNewAppContext").invoke(null);
this.appSupport = new AppSupport(this);
this.appSupport.start();
// Update the state of this isolate.
changeState(State.STARTED);
//add to parent
this.creator.addChild(this);
mainMethod.setAccessible(true);
// Run main method.
mainMethod.invoke(null, new Object[]{args});
} catch (Throwable ex) {
try {
Unsafe.debug(" exception in isolate.run");
Unsafe.debug(ex.getMessage());
StackTraceElement[] trace = ex.getStackTrace();
if (trace != null) {
Unsafe.debug("getStackTrace() != null\n");
for (StackTraceElement element : trace) {
Unsafe.debug(element.getClassName());
Unsafe.debug('#');
Unsafe.debug(element.getMethodName());
Unsafe.debug(element.getLineNumber());
Unsafe.debug('\n');
}
} else {
Unsafe.debug("getStackTrace() == null\n");
}
ex.printStackTrace();
} catch (Throwable ex2) {
Unsafe.debug("Exception in catch block.. giving up: ");
Unsafe.debug(ex2.getMessage());
} finally {
systemHalt(isolate, -1);
}
}
}
private ClassLoader getClassLoader(IsolateThread thread) {
ClassLoader pluginsClassLoader = thread.getPluginManager().getRegistry().getPluginsClassLoader();
if (classpath == null) {
return pluginsClassLoader;
} else {
return new URLClassLoader(classpath, pluginsClassLoader);
}
}
/**
* Gets the root thread group of the current thread.
*
* @return
*/
private static final ThreadGroup getRootThreadGroup() {
ThreadGroup tg = Thread.currentThread().getThreadGroup();
while (tg.getParent() != null) {
tg = tg.getParent();
}
return tg;
}
/**
* @return the isolatedStaticsTable
*/
final VmIsolatedStatics getIsolatedStaticsTable() {
return isolatedStaticsTable;
}
/**
* Gets the classname of the main class.
*
* @return the main class name
*/
public final String getMainClassName() {
return mainClass;
}
/**
* Gets the system classloader for this isolate.
*/
public final ClassLoader getSystemClassLoader() {
return systemClassLoader;
}
/**
* Sets the system classloader for this isolate.
*/
public final void setSystemClassLoader(ClassLoader loader) {
if (this.systemClassLoader == null) {
this.systemClassLoader = loader;
}
}
private void testIsolate(Isolate isolate) {
if (this.isolate != isolate) {
throw new SecurityException("Method called by invalid isolate");
}
}
public IOContext getIOContext() {
return ioContext;
}
public void setIOContext(IOContext context) {
ioContext = context;
}
public void resetIOContext() {
ioContext = vmIoContext;
}
/**
* Returns the identifier of this isolate.
*
* @return the unique identifier
*/
public int getId() {
return id;
}
/**
* Returns the state of this isolate.
*
* @return the current state
*/
public State getState() {
return state;
}
/**
* Returns the VmIsolate instance which created this VmIsolate instance.
*
* @return the creator
*/
public VmIsolate getCreator() {
return creator;
}
/**
* Returns the map of isolate locals belonging to this isolate.
*
* @return the isolate local map
*/
BootableHashMap getIsolateLocalMap() {
return isolateLocalMap;
}
/**
* Returns the current state of this isolate.
* @return the state
*/
public IsolateStatus.State getIsolateState() {
return isolateState;
}
/**
* Set the classpath for this isolate to complement the default classpath.
*
* @param classpath an array of URLs holding the classpath entries as URLs
*/
public void setClasspath(URL[] classpath) {
this.classpath = classpath;
}
/**
* Create and return a new status link for this isolate and the supplied
* receiver isolate.
*
* @param receiver the receiver for the link.
* @return the link.
*/
public synchronized Link newStatusLink(VmIsolate receiver) {
Link link = VmLink.newLink(this, receiver);
VmLink vmLink = VmLink.fromLink(link);
statusLinks.add(vmLink);
if (isolateState != null && isolateState.equals(IsolateStatus.State.EXITED)) {
// The spec says that we should immediately send a link message
// if the isolate is already 'EXITED'.
sendStatus(vmLink, isolateState);
}
return link;
}
private synchronized boolean changeState(State newState) {
this.state = newState;
IsolateStatus.State newIsolateState = newState.getIsolateState();
if (isolateState != newIsolateState) {
this.isolateState = newIsolateState;
for (VmLink link : statusLinks) {
sendStatus(link, this.isolateState);
}
}
return true;
}
private void sendStatus(VmLink link, IsolateStatus.State state) {
if (state.equals(IsolateStatus.State.EXITED)) {
link.sendStatus(new StatusLinkMessage(new IsolateStatusImpl(exitReason, exitCode)));
} else {
link.sendStatus(new StatusLinkMessage(new IsolateStatusImpl(state)));
}
}
private void addChild(VmIsolate child) {
synchronized (children) {
children.add(child);
}
}
private void removeChild(VmIsolate child) {
synchronized (children) {
children.remove(child);
}
}
public VmIsolate[] getChildren() {
synchronized (children) {
return children.toArray(new VmIsolate[children.size()]);
}
}
public ThreadGroup getThreadGroup() {
if (threadGroup == null) {
throw new IllegalStateException("Isolate not available");
}
return threadGroup;
}
private AppSupport appSupport;
@SharedStatics
private static class AppSupport {
private static boolean awtSupport;
static {
try {
Class.forName("java.awt.Toolkit");
awtSupport = true;
} catch (ClassNotFoundException x) {
awtSupport = false;
}
}
private final VmIsolate vmIsolate;
private Object appContext;
AppSupport(VmIsolate vmIsolate) {
this.vmIsolate = vmIsolate;
}
boolean isAWTReady() {
if (!awtSupport)
return false;
try {
return Class.forName("java.awt.Toolkit").getField("toolkit").get(null) != null;
} catch (Exception x) {
return false;
}
}
void start() throws Exception {
if (isAWTReady()) {
synchronized (this) {
appContext = Class.forName("sun.awt.SunToolkit").getMethod("createNewAppContext").invoke(null);
}
}
}
void stop(boolean intraIsolate) {
boolean done = false;
if (awtSupport) {
synchronized (this) {
if (appContext != null) {
disposeAppContext(intraIsolate);
done = true;
}
}
}
if (!done) {
vmIsolate.stopAllThreads();
vmIsolate.doExit();
}
}
boolean isEDT() {
if (appContext == null)
return false;
try {
Object eq = appContext.getClass().getMethod("get", Object.class).
invoke(appContext, appContext.getClass().getField("EVENT_QUEUE_KEY").get(null));
if (eq == null)
return false;
Object t = eq.getClass().getField("dispatchThread").get(eq);
if (t == null)
return false;
return t == Thread.currentThread();
} catch (Exception x) {
throw new RuntimeException(x);
}
/*
try {
return (Boolean) Class.forName("java.awt.EventQueue").
getMethod("isDispatchThread").invoke(null);
} catch (Exception x) {
throw new RuntimeException(x);
}
*/
// return false;
}
private void disposeAppContext(boolean intraIsolate) {
final Object appContext;
final boolean is_edt;
synchronized (this) {
is_edt = isEDT();
appContext = this.appContext;
this.appContext = null;
}
if (appContext != null) {
if (intraIsolate && is_edt) {
Thread t = new Thread(new Runnable() {
public void run() {
getRoot().invokeAndWait(new Runnable() {
public void run() {
try {
appContext.getClass().getMethod("dispose").invoke(appContext);
} catch (Exception x) {
x.printStackTrace();
}
}
});
vmIsolate.stopAllThreads();
vmIsolate.doExit();
}
}, "isolate-" + vmIsolate.getId() + "-AWT-stopper");
t.start();
} else {
getRoot().invokeAndWait(new Runnable() {
public void run() {
try {
appContext.getClass().getMethod("dispose").invoke(appContext);
} catch (Exception x) {
x.printStackTrace();
}
}
});
vmIsolate.stopAllThreads();
}
}
}
}
}