/* * $Id$ * * Copyright (C) 2003-2015 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.shell.proclet; import gnu.java.security.action.GetPropertiesAction; import java.io.Closeable; import java.security.AccessController; import java.util.Map; import java.util.Properties; import org.jnode.shell.CommandRunnable; import org.jnode.shell.CommandThread; import org.jnode.shell.CommandThreadImpl; import org.jnode.util.ProxyStream; import org.jnode.vm.VmExit; import org.jnode.vm.VmIOContext; import org.jnode.vm.VmSystem; import org.jnode.vm.isolate.VmIsolate; /** * This class implements the proclet-specific state used in the JNode proclet * mechanism. * <p> * A 'proclet' is group of threads that has its own version of the system * properties, streams and environment and superficially behaves as if it was a * self-contained application. * * @author crawley@jnode.org */ public class Proclet extends ThreadGroup { public static final int NO_SUCH_PID = 0; private Properties properties; private Map<String, String> environment; private int threadCount; private final int pid; private int exitStatus = 0; private Throwable uncaughtException; private static int nextPid = 1; private Proclet(ThreadGroup parent, Properties properties, Map<String, String> environment, Object[] streams) throws ProcletException { super(parent, nextProcletName()); Proclet parentContext = getParentContext(parent); if (streams == null) { streams = new Object[] { // FIXME ... the deproxy calls are probably redundant deproxy(System.in), deproxy(System.out), deproxy(System.err) }; } if (properties == null) { if (parentContext != null) { properties = parentContext.properties; } else { // FIXME ... temporary properties = AccessController.doPrivileged(new GetPropertiesAction()); } properties = (Properties) properties.clone(); } if (environment == null) { if (parentContext != null) { environment = parentContext.environment; } else { environment = VmIOContext.getGlobalEnv(); } } this.environment = environment; this.properties = properties; this.pid = extractPid(getName()); ((ProcletIOContext) VmIsolate.getRoot().getIOContext()).setStreamsForNewProclet(pid, streams); setDaemon(true); } private Closeable deproxy(Closeable stream) { if (stream instanceof ProxyStream) { return ((ProxyStream<?>) stream).getProxiedStream(); } else { return stream; } } /** * Get the proclet's unique PID. This value uniquely identifies the proclet. * * @return the pid */ public int getPid() { return pid; } public synchronized Map<String, String> getEnvironment() { return environment; } public synchronized Properties getProperties() { return properties; } public synchronized void setProperties(Properties properties) { this.properties = properties; } public synchronized void setEnvironment(Map<String, String> environment) { this.environment = environment; } /** * Get the ProcletContext for the current thread. * * @return the context, or <code>null</code> if the current thread is not * a member of a proclet. */ public static Proclet currentProcletContext() { if (!VmSystem.hasVmIOContext()) { return getParentContext(Thread.currentThread().getThreadGroup()); } else { return null; } } /** * Get the ProcletContext for a thread group. This is the thread group * itself, or the innermost enclosing parent thread group that is a * ProcletContect instance. * * @param threadGroup the starting thread group * @return the context, or <code>null</code> if the thread group does not * have an ancestor that is a ProcletContex. */ private static Proclet getParentContext(ThreadGroup threadGroup) { while (threadGroup != null) { if (threadGroup instanceof Proclet) { return (Proclet) threadGroup; } threadGroup = threadGroup.getParent(); } return null; } /** * Create a new Thread as the initial thread for a new proclet. The proclet * context will be initialised from the current thread's proclet context (if * it exists) or from {@link java.lang.System#in}, * {@link java.lang.System#out}, {@link java.lang.System#err}, and * {@link java.lang.System#getProperties()}. * * @param target the new Thread's Runnable object. * @return the new Thread */ public static CommandThread createProclet(CommandRunnable target) { return createProclet(target, null, null, null, null, 0); } /** * Create a new Thread as the initial thread for a new proclet, using a * supplied set of system properties, and environment and streams vector. If * any of these is null, the corresponding proclet context will be * initialised from the current thread's proclet context (if it exists) or * from {@link java.lang.System#in}, {@link java.lang.System#out}, * {@link java.lang.System#err}, and * {@link java.lang.System#getProperties()}. * * @param properties the proclet's system properties, or <code>null</code>. * @param environment the proclet's environment, or <code>null</code>. * @param streams the proclet's streams vector, or <code>null</code>. * @param target the new Thread's Runnable object. * @return the new Thread */ public static CommandThread createProclet(CommandRunnable target, Properties properties, Map<String, String> environment, Object[] streams) { return createProclet(target, properties, environment, streams, null, 0); } /** * Create a new Thread as the initial thread for a new proclet, using a * supplied set of system properties, and environment and streams vector. If * any of these is null, the corresponding proclet context will be * initialised from the current thread's proclet context (if it exists) or * from {@link java.lang.System#in}, {@link java.lang.System#out}, * {@link java.lang.System#err}, and * {@link java.lang.System#getProperties()}. This overload also supplies an * optional thread name. * * @param properties the proclet's system properties, or <code>null</code>. * @param environment the proclet's environment, or <code>null</code>. * @param streams the proclet's streams vector, or <code>null</code>. * @param target the new Thread's Runnable object. * @param name an optional Thread name. * @return the new Thread */ public static CommandThreadImpl createProclet(CommandRunnable target, Properties properties, Map<String, String> environment, Object[] streams, String name) { return createProclet(target, properties, environment, streams, name, 0); } /** * Create a new Thread as the initial thread for a new proclet, using a * supplied set of system properties, and environment and streams vector. If * any of these is null, the corresponding proclet context will be * initialised from the current thread's proclet context (if it exists) or * from {@link java.lang.System#in}, {@link java.lang.System#out}, * {@link java.lang.System#err}, and * {@link java.lang.System#getProperties()}. This overload also allows the * caller to specify a thread stack size. * * @param properties the proclet's system properties, or <code>null</code>. * @param environment the proclet's environment, or <code>null</code>. * @param streams the proclet's streams vector, or <code>null</code>. * @param target the new Thread's Runnable object. * @param name the new Thread's name. * @param size the new Thread's stack size; zero denotes the default thread * stack size. * @return the new Thread */ public static CommandThreadImpl createProclet(CommandRunnable target, Properties properties, Map<String, String> environment, Object[] streams, String name, long size) { Proclet procletContext = new Proclet(Thread .currentThread().getThreadGroup(), properties, environment, streams); if (name == null) { name = procletContext.autoThreadName(); } return new CommandThreadImpl(procletContext, target, name, size); } /** * Generate a "unique" default name for a Proclet thread. */ private synchronized String autoThreadName() { return getName() + "-" + (++threadCount); } private static synchronized String nextProcletName() { return "Proclet-" + nextPid++; } private static int extractPid(String name) { return Integer.parseInt(name.substring("Proclet-".length())); } @Override public void uncaughtException(Thread thread, Throwable t) { if (t instanceof VmExit) { exitStatus = ((VmExit) t).getStatus(); } else { uncaughtException = t; } super.uncaughtException(thread, t); } /** * Retrieves a proclet's exit status. * * @return the status value set in System.exit(int), or zero. */ public int getExitStatus() { return exitStatus; } /** * If the proclet dies with an uncaught exception, this method returns that * exception. * * @return the exception, or <code>null</code>. */ public Throwable getUncaughtException() { return uncaughtException; } /** * Return a human-readable String representing this ProcletContext. * * @return a human-readable String representing this ProcletContext */ public String toString() { return getClass().getName() + "[name=" + getName() + ",maxpri=" + getMaxPriority() + ",pid=" + getPid() + ']'; } }