alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide.execution;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.File;
import java.util.*;

import org.openide.ErrorManager;
import org.openide.filesystems.*;
import org.openide.loaders.DataObject;
import org.openide.cookies.ArgumentsCookie;
import org.openide.util.*;
import org.openide.windows.InputOutput;

/** Executes a class externally (in a separate process). Provides
* basic implementation that allows to specify the process to 
* execute, its parameters and also to substitute the content of repositorypath,
* classpath, bootclasspath and librarypath. This is done by inner class Format.
* 

* The behaviour described here can be overriden by subclasses to use different * format (extend the set of recognized tags), execute the * process with additional environment properties, etc. * * @author Ales Novak, Jaroslav Tulach */ public class ProcessExecutor extends Executor { /** generated Serialized Version UID */ static final long serialVersionUID = 1440216248312461457L; /** external process - like java.exe - property */ protected NbProcessDescriptor externalExecutor; /** class path settings or null */ private NbClassPath classPath; /** boot class path or null */ private NbClassPath bootClassPath; /** environment vars or null */ private String[] envp = null; /** if true, append/override env vars rather than replace */ private boolean addEnvs = false; /** working directory or null */ private File cwd = null; /** Create a new executor. */ public ProcessExecutor() { // externalExecutor = DEFAULT; } /** Set a new external execution command. * @param desc the settings for the new external executor */ public synchronized void setExternalExecutor (NbProcessDescriptor desc) { NbProcessDescriptor old = externalExecutor; externalExecutor = desc; firePropertyChange ("externalExecutor", old, desc); // NOI18N } public HelpCtx getHelpCtx () { return new HelpCtx (ProcessExecutor.class); } /** Get the current external execution command. * The default Java launcher associated with this VM's installation will be used, * and the user repository entries will be used for the class path if customized command is not set. *

Subclasses should use a different default process descriptor.

* @return the settings for the current external executor */ public synchronized NbProcessDescriptor getExternalExecutor() { if (externalExecutor == null) { externalExecutor = new NbProcessDescriptor ( "{" + Format.TAG_JAVAHOME + "}{" + Format.TAG_SEPARATOR + "}bin{" + Format.TAG_SEPARATOR + "}java", // /usr/local/bin/java // NOI18N "-cp {" + Format.TAG_REPOSITORY + "}" + // -cp {REPOSITORY}:{CLASSPATH} {CLASSNAME} {ARGUMENTS} // NOI18N "{" + Format.TAG_PATHSEPARATOR + "}" + "{" + Format.TAG_CLASSPATH + "}" + // NOI18N "{" + Format.TAG_PATHSEPARATOR + "}" + "{" + Format.TAG_LIBRARY + "} " + // NOI18N "{" + Format.TAG_CLASSNAME + "} " + // NOI18N "{" + Format.TAG_ARGUMENTS + '}', // NOI18N NbBundle.getBundle(ProcessExecutor.class).getString ("MSG_ExecutorHint") ); } return externalExecutor; } /** Getter for class path associated with this executor. * @deprecated Java-specific, do not use. */ public NbClassPath getClassPath () { return classPath == null ? NbClassPath.createClassPath () : classPath; } /** Setter for class path for this executor. * @deprecated Java-specific, do not use. */ public synchronized void setClassPath (NbClassPath path) { NbClassPath old = classPath; classPath = path; firePropertyChange ("classPath", old, path); // NOI18N } /** Getter for boot class path associated with this executor. * @deprecated Java-specific, do not use. */ public NbClassPath getBootClassPath () { return bootClassPath == null ? NbClassPath.createBootClassPath () : bootClassPath; } /** Setter for boot class path for this executor. * @deprecated Java-specific, do not use. */ public synchronized void setBootClassPath (NbClassPath path) { NbClassPath old = bootClassPath; bootClassPath = path; firePropertyChange ("bootClassPath", old, path); // NOI18N } /** Getter for repository path. It is immutable reflecting * NbClassPath.createRepositoryPath (). Is here only to be displayed in property sheet. * @deprecated Java-specific, do not use. */ public NbClassPath getRepositoryPath () { Thread.dumpStack();//XXX return new NbClassPath(new String[0]); /* return NbClassPath.createRepositoryPath (FileSystemCapability.EXECUTE); */ } /** Getter for repository path. It is immutable reflecting * NbClassPath.createLibraryPath (). Is here only to be displayed in property sheet. * @deprecated Java-specific, do not use. */ public NbClassPath getLibraryPath () { return NbClassPath.createLibraryPath (); } /** Get environment variables. * @return the NAME=VALUE pairs, or null (typically, inherit that of parent) */ public String[] getEnvironmentVariables () { return envp; } /** Set environment variables. * @param nue the new variables * @see #getEnvironmentVariables */ public synchronized void setEnvironmentVariables (String[] nue) { String[] old = envp; envp = nue; firePropertyChange ("environmentVariables", old, nue); // NOI18N } /** Check if environment variables will be appended or replaced. * If false (the default), when environment variables are * {@link #getEnvironmentVariables specified}, these are taken as is * for the process' environment, matching the standard Java platform * behavior. If true, any specified environment variables append to * or replace those found in the IDE's own environment, permitting * incremental changes. * @return true if variables should be incremental * @since 1.15 */ public boolean getAppendEnvironmentVariables () { return addEnvs; } /** Set whether environment variables should be appended and replaced. * @param nue true if so * @see #getAppendEnvironmentVariables * @since 1.15 */ public synchronized void setAppendEnvironmentVariables (boolean nue) { boolean old = addEnvs; addEnvs = nue; firePropertyChange ("appendEnvironmentVariables", // NOI18N old ? Boolean.TRUE : Boolean.FALSE, nue ? Boolean.TRUE : Boolean.FALSE); } /** Get the working directory. * @return the working directory to use, or null (use that of parent) */ public File getWorkingDirectory () { return cwd; } /** Set the working directory. * @param nue the new directory * @see #getWorkingDirectory */ public synchronized void setWorkingDirectory (File nue) { File old = cwd; cwd = nue; firePropertyChange ("workingDirectory", old, nue); // NOI18N } /** Called to create the java.lang.Process for given exec info. * Current implementation scans creates new Format with provided * exec info and asks the current executor to start with that * format. *

* Subclasses can override this to achieve the right behaviour, add * system properties, own format, etc. * * @param info exec info * @return the executed process * @exception IOException if the action fails * @deprecated Do not use; override {@link #createProcess(DataObject)} instead. */ protected Process createProcess (ExecInfo info) throws IOException { return getExternalExecutor ().exec (new Format ( info, getClassPath (), getBootClassPath (), getRepositoryPath (), getLibraryPath () ), envp, addEnvs, cwd); } // lazily loads a class private final Class getKlass(String name) { try { return Class.forName(name, false, getClass().getClassLoader()); } catch (ClassNotFoundException e) { throw new NoClassDefFoundError(e.getLocalizedMessage()); } } private final static Set warnedClasses = new WeakSet(); // Set /** Called to create the java.lang.Process for given data object. *

* Subclasses must override this to achive the right behaviour, add * system properties, own format, etc. * Treat this method as abstract. The default implementation * must not be used. *

*
*

Here is an example implementation:

*
     * protected Process createProcess(DataObject d) throws IOException {
     *     Format f = new MyFormat(d, getParam1(), getParam2(), ...);
     *     return getExternalExecutor().exec(f,
     *         getEnvironmentVariables(),
     *         getAppendEnvironmentVariables(),
     *         getWorkingDirectory());
     * }
     * private static final class MyFormat extends MapFormat {
     *     public MyFormat(DataObject d, String param1, String param2, ...)
     *             throws IOException {
     *         super(new HashMap());
     *         Map m = getMap();
     *         // Whatever you need to subst, e.g. {/}, {path}, {param1}, ...
     *         // File separator if that is useful:
     *         map.put("/", File.separator);
     *         // Typically you are passing a file path, so:
     *         File f = FileUtil.toFile(d.getPrimaryFile());
     *         if (f == null) {
     *             throw new IOException("File " + d.getPrimaryFile() +
     *                                   " not a disk file");
     *         }
     *         map.put("path", f.getAbsolutePath());
     *         // Some params configured in the executor's property sheet:
     *         map.put("param1", param1);
     *         map.put("param2", param2);
     *         // ...
     *         // If you pay attention to arguments then also:
     *         ArgumentsCookie c =
     *             (ArgumentsCookie)d.getCookie(ArgumentsCookie.class);
     *         if (c != null) {
     *             StringBuffer b = new StringBuffer();
     *             String[] args = c.getArguments();
     *             for (int i = 0; i < args.length; i++) {
     *                 b.append('"');
     *                 b.append(args[i]);
     *                 b.append("\" ");
     *             }
     *             map.put("args", b.toString());
     *         } else {
     *             map.put("args", "");
     *         }
     *     }
     * }
     * 
*
* @param obj data object to execute * @return the executed process * @exception IOException if the action fails */ protected Process createProcess (DataObject obj) throws IOException { throw new IOException("No longer works!"); // NOI18N /* Class c = getClass(); synchronized (warnedClasses) { if (warnedClasses.add(c)) { ErrorManager.getDefault().log(ErrorManager.WARNING, "Warning - " + c.getName() + " should have overridden createProcess(DataObject); falling back on deprecated ExecInfo usage"); } } String[] params; ArgumentsCookie ac = (ArgumentsCookie) obj.getCookie(getKlass("org.openide.cookies.ArgumentsCookie")); if (ac != null) { params = ac.getArguments(); } else { params = new String[0]; } return createProcess (new ExecInfo(obj.getPrimaryFile().getPackageName ('.'), params)); */ } /* Executes given class by creating new process in underlting operating system. * @param ctx used to write to the Output Window * @param info information about the class to be executed * @deprecated Use {@link #execute(DataObject)} instead. */ public ExecutorTask execute(ExecInfo info) throws IOException { PERunnable run = new PERunnable(info); return handleExec (run, info.getClassName()); } /** Executes given dataobject by creating new process in underlying operating system. * This method in turn calls the createProcess (DataObject) method. * * @param obj object to execute * @return the executed process * @exception IOException if the action fails */ public ExecutorTask execute(DataObject obj) throws IOException { PERunnable run = new PERunnable (obj); return handleExec (run, obj.getName ()); // JST: we can change the name if necessary } /** Method that executes given runnable. * @param run runnable to start * @param name name to assign to the I/O tab * @return execution task describing the execution */ private ExecutorTask handleExec (PERunnable run, String name) throws IOException { synchronized (run) { InputOutput inout = (needsIO() ? null : InputOutput.NULL); ExecutorTask et = ExecutionEngine.getDefault().execute( name, run, inout ); run.setExecutorTask(et); try { run.wait(); Throwable t = run.getException(); if (t != null) { if (t instanceof RuntimeException) { throw (RuntimeException) t; } else if (t instanceof Error) { throw (Error) t; } else if (t instanceof IOException) { throw (IOException) t; } else { IOException ex = new IOException (t.getMessage ()); ErrorManager.getDefault ().copyAnnotation(ex, t); throw ex; } } return run.getExecutorTask(); } catch (InterruptedException e) { IOException ex = new IOException (e.getMessage()); ErrorManager.getDefault().copyAnnotation(ex, e); throw ex; } } } /** the executing runnable */ private class PERunnable implements Runnable, TaskListener { /** data object to execute or null */ private DataObject obj; /** exec info to execute or null */ private ExecInfo info; private ExecutorTask fromEngine; ExecutorTask fromMe; private Throwable t; PERunnable(ExecInfo info) { this.info = info; } PERunnable(DataObject obj) { this.obj = obj; } public synchronized void run() { try { String className; Process process; if (info != null) { // use info to create the process className = info.getClassName (); process = createProcess (info); } else { // use data object to create the process className = obj.getName (); process = createProcess (obj); } Thread[] copyMakers = new Thread[3]; (copyMakers[0] = new CopyMaker(fromEngine.getInputOutput().getIn(), new OutputStreamWriter(process.getOutputStream()), true, className)).start(); (copyMakers[1] = new CopyMaker(new InputStreamReader(process.getInputStream()), fromEngine.getInputOutput().getOut(), false, className)).start(); (copyMakers[2] = new CopyMaker(new InputStreamReader(process.getErrorStream()), fromEngine.getInputOutput().getErr(), false, className)).start(); fromMe = new ExternalExecutorTask(this, fromEngine, process, copyMakers); } catch (Exception e) { t = e; } finally { this.notifyAll(); } } public void setExecutorTask(ExecutorTask fromEngine) { this.fromEngine = fromEngine; fromEngine.addTaskListener(this); } public ExecutorTask getExecutorTask() { return fromMe; } public Throwable getException() { return t; } /** Implementation of task listener, called when the task provided * by execution engine finishes. */ public void taskFinished(Task t) { if (fromMe != null) { fromMe.stop(); } } } /** Default format that can format tags related to execution. These include settings of classpath * (can be composed from repository, class path, boot class path and libraries), putting somewhere * the name of executed class and its arguments. * @deprecated Very Java-specific. Just use your own MapFormat subclass directly if you like. * @see ProcessExecutor#createProcess(DataObject) */ public static class Format extends MapFormat { /** Tag replaced with ProcessExecutors.getClassPath () */ public static final String TAG_CLASSPATH = "classpath"; // NOI18N /** Tag replaced with ProcessExecutors.getBootClassPath () */ public static final String TAG_BOOTCLASSPATH = "bootclasspath"; // NOI18N /** Tag replaced with ProcessExecutors.getRepositoryPath () */ public static final String TAG_REPOSITORY = "filesystems"; // NOI18N /** Tag replaced with ProcessExecutors.getLibraryPath () */ public static final String TAG_LIBRARY = "library"; // NOI18N /** Tag replaced with name of executed class */ public static final String TAG_CLASSNAME = "classname"; // NOI18N /** Tag replaced with arguments of the program */ public static final String TAG_ARGUMENTS = "arguments"; // NOI18N /** Tag replaced with install directory of JDK */ public static final String TAG_JAVAHOME = "java.home"; // NOI18N /** Tag replaced with install directory of JDK */ public static final String TAG_JDKHOME = "jdk.home"; // NOI18N /** Tag replaced with separator between filename components */ public static final String TAG_SEPARATOR = "/"; // NOI18N /** Tag replaced with separator between path components */ public static final String TAG_PATHSEPARATOR = ":"; // NOI18N static final long serialVersionUID =1105067849363827986L; /** All values for the paths takes from NbClassPath.createXXX methods. * @param info exec info about class to execute */ public Format (ExecInfo info) { this ( info, NbClassPath.createClassPath (), NbClassPath.createBootClassPath (), new NbClassPath(new String[0]), /* NbClassPath.createRepositoryPath (FileSystemCapability.EXECUTE), */ NbClassPath.createLibraryPath () ); Thread.dumpStack();//XXX } /** @param info exec info about class to execute * @param classPath to substitute instead of CLASSPATH * @param bootClassPath boot class path * @param repository repository path * @param library library path */ public Format ( ExecInfo info, NbClassPath classPath, NbClassPath bootClassPath, NbClassPath repository, NbClassPath library ) { super (new java.util.HashMap (7)); java.util.Map map = getMap (); map.put (TAG_CLASSPATH, classPath.getClassPath ()); map.put (TAG_BOOTCLASSPATH, bootClassPath.getClassPath ()); map.put (TAG_REPOSITORY, repository.getClassPath ()); map.put (TAG_LIBRARY, library.getClassPath ()); map.put (TAG_CLASSNAME, info.getClassName ()); map.put (TAG_JAVAHOME, System.getProperty("java.home")); map.put (TAG_JDKHOME, System.getProperty("jdk.home")); map.put (TAG_SEPARATOR, java.io.File.separator); map.put (TAG_PATHSEPARATOR, java.io.File.pathSeparator); // JST: // it is not too nice that we have to create string from string[] // and the string will be later parsed again, but hopefully it // will work StringBuffer sb = new StringBuffer (); String[] args = info.getArguments (); for (int i = 0; i < args.length; i++) { sb.append ('\"'); sb.append (args[i]); sb.append ('\"'); sb.append (' '); } map.put (TAG_ARGUMENTS, sb.toString ()); } } /** This thread simply reads from given Reader and writes read chars to given Writer. */ private static class CopyMaker extends Thread { final Writer os; final Reader is; /** while set to false at streams that writes to the OutputWindow it must be * true for a stream that reads from the window. */ final boolean autoflush; final String permName; private boolean done = false; CopyMaker(Reader is, Writer os, boolean b, String className) { this.os = os; this.is = is; autoflush = b; permName = className; } /* Makes copy. */ public void run() { int read; char[] buff = new char [256]; try { while ((read = read(is, buff, 0, 256)) > 0x0) { os.write(buff,0,read); if (autoflush) os.flush(); } } catch (IOException ex) { } catch (InterruptedException e) { } } public void interrupt() { super.interrupt(); done = true; } private int read(Reader is, char[] buff, int start, int count) throws InterruptedException, IOException { // XXX (anovak) IBM JDK 1.3.x on OS/2 is broken // is.ready()/available() returns false/0 until // at least one byte from the stream is read. // Then it works as advertised. // isao 2001-11-12: ditto for JDK 1.3 on OpenVMS // XXX is this true for any 1.4 port? -jglick if (Utilities.getOperatingSystem() != Utilities.OS_OS2) { while (!is.ready() && !done) sleep(100); } return is.read(buff, start, count); } } // end of CopyMaker /** SysProcess that describes the external process. */ static class ExternalExecutorTask extends ExecutorTask { Process proc; Thread[] copyMakers; ExecutorTask foreign; ExternalExecutorTask(Runnable run, ExecutorTask etask, Process proc, Thread[] copyMakers) { super(run); this.proc = proc; this.copyMakers = copyMakers; foreign = etask; TaskListener tl = new TaskListener() { public void taskFinished(Task t) { stop(); } }; etask.addTaskListener(tl); new Thread() { public void run() { result(); } }.start(); } public void stop() { try { copyMakers[0].interrupt(); copyMakers[1].interrupt(); copyMakers[2].interrupt(); } finally { proc.destroy(); } } public int result() { try { int ret = proc.waitFor(); Thread.sleep(2000); // time for copymakers return ret; } catch (InterruptedException e) { return 1; // 0 is success } finally { stop(); notifyFinished(); } } public InputOutput getInputOutput() { return foreign.getInputOutput(); } public void run() { } } }
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.