0001: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
0002: *
0003: * ***** BEGIN LICENSE BLOCK *****
0004: * Version: MPL 1.1/GPL 2.0
0005: *
0006: * The contents of this file are subject to the Mozilla Public License Version
0007: * 1.1 (the "License"); you may not use this file except in compliance with
0008: * the License. You may obtain a copy of the License at
0009: * http://www.mozilla.org/MPL/
0010: *
0011: * Software distributed under the License is distributed on an "AS IS" basis,
0012: * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
0013: * for the specific language governing rights and limitations under the
0014: * License.
0015: *
0016: * The Original Code is Rhino code, released
0017: * May 6, 1998.
0018: *
0019: * The Initial Developer of the Original Code is
0020: * Netscape Communications Corporation.
0021: * Portions created by the Initial Developer are Copyright (C) 1997-1999
0022: * the Initial Developer. All Rights Reserved.
0023: *
0024: * Contributor(s):
0025: * Patrick Beard
0026: * Igor Bukanov
0027: * Norris Boyd
0028: * Rob Ginda
0029: * Kurt Westerfeld
0030: * Matthias Radestock
0031: *
0032: * Alternatively, the contents of this file may be used under the terms of
0033: * the GNU General Public License Version 2 or later (the "GPL"), in which
0034: * case the provisions of the GPL are applicable instead of those above. If
0035: * you wish to allow use of your version of this file only under the terms of
0036: * the GPL and not to allow others to use your version of this file under the
0037: * MPL, indicate your decision by deleting the provisions above and replacing
0038: * them with the notice and other provisions required by the GPL. If you do
0039: * not delete the provisions above, a recipient may use your version of this
0040: * file under either the MPL or the GPL.
0041: *
0042: * ***** END LICENSE BLOCK ***** */
0043:
0044: package org.mozilla.javascript.tools.shell;
0045:
0046: import java.io.*;
0047: import java.net.*;
0048: import java.lang.reflect.*;
0049: import org.mozilla.javascript.*;
0050: import org.mozilla.javascript.tools.ToolErrorReporter;
0051: import org.mozilla.javascript.serialize.*;
0052:
0053: /**
0054: * This class provides for sharing functions across multiple threads.
0055: * This is of particular interest to server applications.
0056: *
0057: * @author Norris Boyd
0058: */
0059: public class Global extends ImporterTopLevel {
0060: static final long serialVersionUID = 4029130780977538005L;
0061:
0062: NativeArray history;
0063: private InputStream inStream;
0064: private PrintStream outStream;
0065: private PrintStream errStream;
0066: private boolean sealedStdLib = false;
0067: boolean initialized;
0068: private QuitAction quitAction;
0069: private String[] prompts = { "js> ", " > " };
0070:
0071: public Global() {
0072: }
0073:
0074: public Global(Context cx) {
0075: init(cx);
0076: }
0077:
0078: public boolean isInitialized() {
0079: return initialized;
0080: }
0081:
0082: /**
0083: * Set the action to call from quit().
0084: */
0085: public void initQuitAction(QuitAction quitAction) {
0086: if (quitAction == null)
0087: throw new IllegalArgumentException("quitAction is null");
0088: if (this .quitAction != null)
0089: throw new IllegalArgumentException(
0090: "The method is once-call.");
0091:
0092: this .quitAction = quitAction;
0093: }
0094:
0095: public void init(ContextFactory factory) {
0096: factory.call(new ContextAction() {
0097: public Object run(Context cx) {
0098: init(cx);
0099: return null;
0100: }
0101: });
0102: }
0103:
0104: public void init(Context cx) {
0105: // Define some global functions particular to the shell. Note
0106: // that these functions are not part of ECMA.
0107: initStandardObjects(cx, sealedStdLib);
0108: String[] names = { "defineClass", "deserialize", "gc", "help",
0109: "load", "loadClass", "print", "quit", "readFile",
0110: "readUrl", "runCommand", "seal", "serialize", "spawn",
0111: "sync", "toint32", "version", };
0112: defineFunctionProperties(names, Global.class,
0113: ScriptableObject.DONTENUM);
0114:
0115: // Set up "environment" in the global scope to provide access to the
0116: // System environment variables.
0117: Environment.defineClass(this );
0118: Environment environment = new Environment(this );
0119: defineProperty("environment", environment,
0120: ScriptableObject.DONTENUM);
0121:
0122: history = (NativeArray) cx.newArray(this , 0);
0123: defineProperty("history", history, ScriptableObject.DONTENUM);
0124: initialized = true;
0125: }
0126:
0127: /**
0128: * Print a help message.
0129: *
0130: * This method is defined as a JavaScript function.
0131: */
0132: public static void help(Context cx, Scriptable this Obj,
0133: Object[] args, Function funObj) {
0134: PrintStream out = getInstance(funObj).getOut();
0135: out.println(ToolErrorReporter.getMessage("msg.help"));
0136: }
0137:
0138: public static void gc(Context cx, Scriptable this Obj,
0139: Object[] args, Function funObj) {
0140: System.gc();
0141: }
0142:
0143: /**
0144: * Print the string values of its arguments.
0145: *
0146: * This method is defined as a JavaScript function.
0147: * Note that its arguments are of the "varargs" form, which
0148: * allows it to handle an arbitrary number of arguments
0149: * supplied to the JavaScript function.
0150: *
0151: */
0152: public static Object print(Context cx, Scriptable this Obj,
0153: Object[] args, Function funObj) {
0154: PrintStream out = getInstance(funObj).getOut();
0155: for (int i = 0; i < args.length; i++) {
0156: if (i > 0)
0157: out.print(" ");
0158:
0159: // Convert the arbitrary JavaScript value into a string form.
0160: String s = Context.toString(args[i]);
0161:
0162: out.print(s);
0163: }
0164: out.println();
0165: return Context.getUndefinedValue();
0166: }
0167:
0168: /**
0169: * Call embedding-specific quit action passing its argument as
0170: * int32 exit code.
0171: *
0172: * This method is defined as a JavaScript function.
0173: */
0174: public static void quit(Context cx, Scriptable this Obj,
0175: Object[] args, Function funObj) {
0176: Global global = getInstance(funObj);
0177: if (global.quitAction != null) {
0178: int exitCode = (args.length == 0 ? 0 : ScriptRuntime
0179: .toInt32(args[0]));
0180: global.quitAction.quit(cx, exitCode);
0181: }
0182: }
0183:
0184: /**
0185: * Get and set the language version.
0186: *
0187: * This method is defined as a JavaScript function.
0188: */
0189: public static double version(Context cx, Scriptable this Obj,
0190: Object[] args, Function funObj) {
0191: double result = cx.getLanguageVersion();
0192: if (args.length > 0) {
0193: double d = Context.toNumber(args[0]);
0194: cx.setLanguageVersion((int) d);
0195: }
0196: return result;
0197: }
0198:
0199: /**
0200: * Load and execute a set of JavaScript source files.
0201: *
0202: * This method is defined as a JavaScript function.
0203: *
0204: */
0205: public static void load(Context cx, Scriptable this Obj,
0206: Object[] args, Function funObj) {
0207: for (int i = 0; i < args.length; i++) {
0208: Main.processFile(cx, this Obj, Context.toString(args[i]));
0209: }
0210: }
0211:
0212: /**
0213: * Load a Java class that defines a JavaScript object using the
0214: * conventions outlined in ScriptableObject.defineClass.
0215: * <p>
0216: * This method is defined as a JavaScript function.
0217: * @exception IllegalAccessException if access is not available
0218: * to a reflected class member
0219: * @exception InstantiationException if unable to instantiate
0220: * the named class
0221: * @exception InvocationTargetException if an exception is thrown
0222: * during execution of methods of the named class
0223: * @see org.mozilla.javascript.ScriptableObject#defineClass(Scriptable,Class)
0224: */
0225: public static void defineClass(Context cx, Scriptable this Obj,
0226: Object[] args, Function funObj)
0227: throws IllegalAccessException, InstantiationException,
0228: InvocationTargetException {
0229: Class clazz = getClass(args);
0230: ScriptableObject.defineClass(this Obj, clazz);
0231: }
0232:
0233: /**
0234: * Load and execute a script compiled to a class file.
0235: * <p>
0236: * This method is defined as a JavaScript function.
0237: * When called as a JavaScript function, a single argument is
0238: * expected. This argument should be the name of a class that
0239: * implements the Script interface, as will any script
0240: * compiled by jsc.
0241: *
0242: * @exception IllegalAccessException if access is not available
0243: * to the class
0244: * @exception InstantiationException if unable to instantiate
0245: * the named class
0246: */
0247: public static void loadClass(Context cx, Scriptable this Obj,
0248: Object[] args, Function funObj)
0249: throws IllegalAccessException, InstantiationException {
0250: Class clazz = getClass(args);
0251: if (!Script.class.isAssignableFrom(clazz)) {
0252: throw reportRuntimeError("msg.must.implement.Script");
0253: }
0254: Script script = (Script) clazz.newInstance();
0255: script.exec(cx, this Obj);
0256: }
0257:
0258: private static Class getClass(Object[] args) {
0259: if (args.length == 0) {
0260: throw reportRuntimeError("msg.expected.string.arg");
0261: }
0262: Object arg0 = args[0];
0263: if (arg0 instanceof Wrapper) {
0264: Object wrapped = ((Wrapper) arg0).unwrap();
0265: if (wrapped instanceof Class)
0266: return (Class) wrapped;
0267: }
0268: String className = Context.toString(args[0]);
0269: try {
0270: return Class.forName(className);
0271: } catch (ClassNotFoundException cnfe) {
0272: throw reportRuntimeError("msg.class.not.found", className);
0273: }
0274: }
0275:
0276: public static void serialize(Context cx, Scriptable this Obj,
0277: Object[] args, Function funObj) throws IOException {
0278: if (args.length < 2) {
0279: throw Context
0280: .reportRuntimeError("Expected an object to serialize and a filename to write "
0281: + "the serialization to");
0282: }
0283: Object obj = args[0];
0284: String filename = Context.toString(args[1]);
0285: FileOutputStream fos = new FileOutputStream(filename);
0286: Scriptable scope = ScriptableObject.getTopLevelScope(this Obj);
0287: ScriptableOutputStream out = new ScriptableOutputStream(fos,
0288: scope);
0289: out.writeObject(obj);
0290: out.close();
0291: }
0292:
0293: public static Object deserialize(Context cx, Scriptable this Obj,
0294: Object[] args, Function funObj) throws IOException,
0295: ClassNotFoundException {
0296: if (args.length < 1) {
0297: throw Context
0298: .reportRuntimeError("Expected a filename to read the serialization from");
0299: }
0300: String filename = Context.toString(args[0]);
0301: FileInputStream fis = new FileInputStream(filename);
0302: Scriptable scope = ScriptableObject.getTopLevelScope(this Obj);
0303: ObjectInputStream in = new ScriptableInputStream(fis, scope);
0304: Object deserialized = in.readObject();
0305: in.close();
0306: return Context.toObject(deserialized, scope);
0307: }
0308:
0309: public String[] getPrompts(Context cx) {
0310: if (ScriptableObject.hasProperty(this , "prompts")) {
0311: Object promptsJS = ScriptableObject.getProperty(this ,
0312: "prompts");
0313: if (promptsJS instanceof Scriptable) {
0314: Scriptable s = (Scriptable) promptsJS;
0315: if (ScriptableObject.hasProperty(s, 0)
0316: && ScriptableObject.hasProperty(s, 1)) {
0317: Object elem0 = ScriptableObject.getProperty(s, 0);
0318: if (elem0 instanceof Function) {
0319: elem0 = ((Function) elem0).call(cx, this , s,
0320: new Object[0]);
0321: }
0322: prompts[0] = Context.toString(elem0);
0323: Object elem1 = ScriptableObject.getProperty(s, 1);
0324: if (elem1 instanceof Function) {
0325: elem1 = ((Function) elem1).call(cx, this , s,
0326: new Object[0]);
0327: }
0328: prompts[1] = Context.toString(elem1);
0329: }
0330: }
0331: }
0332: return prompts;
0333: }
0334:
0335: /**
0336: * The spawn function runs a given function or script in a different
0337: * thread.
0338: *
0339: * js> function g() { a = 7; }
0340: * js> a = 3;
0341: * 3
0342: * js> spawn(g)
0343: * Thread[Thread-1,5,main]
0344: * js> a
0345: * 3
0346: */
0347: public static Object spawn(Context cx, Scriptable this Obj,
0348: Object[] args, Function funObj) {
0349: Scriptable scope = funObj.getParentScope();
0350: Runner runner;
0351: if (args.length != 0 && args[0] instanceof Function) {
0352: Object[] newArgs = null;
0353: if (args.length > 1 && args[1] instanceof Scriptable) {
0354: newArgs = cx.getElements((Scriptable) args[1]);
0355: }
0356: if (newArgs == null) {
0357: newArgs = ScriptRuntime.emptyArgs;
0358: }
0359: runner = new Runner(scope, (Function) args[0], newArgs);
0360: } else if (args.length != 0 && args[0] instanceof Script) {
0361: runner = new Runner(scope, (Script) args[0]);
0362: } else {
0363: throw reportRuntimeError("msg.spawn.args");
0364: }
0365: runner.factory = cx.getFactory();
0366: Thread thread = new Thread(runner);
0367: thread.start();
0368: return thread;
0369: }
0370:
0371: /**
0372: * The sync function creates a synchronized function (in the sense
0373: * of a Java synchronized method) from an existing function. The
0374: * new function synchronizes on the <code>this</code> object of
0375: * its invocation.
0376: * js> var o = { f : sync(function(x) {
0377: * print("entry");
0378: * Packages.java.lang.Thread.sleep(x*1000);
0379: * print("exit");
0380: * })};
0381: * js> spawn(function() {o.f(5);});
0382: * Thread[Thread-0,5,main]
0383: * entry
0384: * js> spawn(function() {o.f(5);});
0385: * Thread[Thread-1,5,main]
0386: * js>
0387: * exit
0388: * entry
0389: * exit
0390: */
0391: public static Object sync(Context cx, Scriptable this Obj,
0392: Object[] args, Function funObj) {
0393: if (args.length == 1 && args[0] instanceof Function) {
0394: return new Synchronizer((Function) args[0]);
0395: } else {
0396: throw reportRuntimeError("msg.sync.args");
0397: }
0398: }
0399:
0400: /**
0401: * Execute the specified command with the given argument and options
0402: * as a separate process and return the exit status of the process.
0403: * <p>
0404: * Usage:
0405: * <pre>
0406: * runCommand(command)
0407: * runCommand(command, arg1, ..., argN)
0408: * runCommand(command, arg1, ..., argN, options)
0409: * </pre>
0410: * All except the last arguments to runCommand are converted to strings
0411: * and denote command name and its arguments. If the last argument is a
0412: * JavaScript object, it is an option object. Otherwise it is converted to
0413: * string denoting the last argument and options objects assumed to be
0414: * empty.
0415: * Te following properties of the option object are processed:
0416: * <ul>
0417: * <li><tt>args</tt> - provides an array of additional command arguments
0418: * <li><tt>env</tt> - explicit environment object. All its enumeratable
0419: * properties define the corresponding environment variable names.
0420: * <li><tt>input</tt> - the process input. If it is not
0421: * java.io.InputStream, it is converted to string and sent to the process
0422: * as its input. If not specified, no input is provided to the process.
0423: * <li><tt>output</tt> - the process output instead of
0424: * java.lang.System.out. If it is not instance of java.io.OutputStream,
0425: * the process output is read, converted to a string, appended to the
0426: * output property value converted to string and put as the new value of
0427: * the output property.
0428: * <li><tt>err</tt> - the process error output instead of
0429: * java.lang.System.err. If it is not instance of java.io.OutputStream,
0430: * the process error output is read, converted to a string, appended to
0431: * the err property value converted to string and put as the new
0432: * value of the err property.
0433: * </ul>
0434: */
0435: public static Object runCommand(Context cx, Scriptable this Obj,
0436: Object[] args, Function funObj) throws IOException {
0437: int L = args.length;
0438: if (L == 0 || (L == 1 && args[0] instanceof Scriptable)) {
0439: throw reportRuntimeError("msg.runCommand.bad.args");
0440: }
0441:
0442: InputStream in = null;
0443: OutputStream out = null, err = null;
0444: ByteArrayOutputStream outBytes = null, errBytes = null;
0445: Object outObj = null, errObj = null;
0446: String[] environment = null;
0447: Scriptable params = null;
0448: Object[] addArgs = null;
0449: if (args[L - 1] instanceof Scriptable) {
0450: params = (Scriptable) args[L - 1];
0451: --L;
0452: Object envObj = ScriptableObject.getProperty(params, "env");
0453: if (envObj != Scriptable.NOT_FOUND) {
0454: if (envObj == null) {
0455: environment = new String[0];
0456: } else {
0457: if (!(envObj instanceof Scriptable)) {
0458: throw reportRuntimeError("msg.runCommand.bad.env");
0459: }
0460: Scriptable envHash = (Scriptable) envObj;
0461: Object[] ids = ScriptableObject
0462: .getPropertyIds(envHash);
0463: environment = new String[ids.length];
0464: for (int i = 0; i != ids.length; ++i) {
0465: Object keyObj = ids[i], val;
0466: String key;
0467: if (keyObj instanceof String) {
0468: key = (String) keyObj;
0469: val = ScriptableObject.getProperty(envHash,
0470: key);
0471: } else {
0472: int ikey = ((Number) keyObj).intValue();
0473: key = Integer.toString(ikey);
0474: val = ScriptableObject.getProperty(envHash,
0475: ikey);
0476: }
0477: if (val == ScriptableObject.NOT_FOUND) {
0478: val = Undefined.instance;
0479: }
0480: environment[i] = key + '='
0481: + ScriptRuntime.toString(val);
0482: }
0483: }
0484: }
0485: Object inObj = ScriptableObject
0486: .getProperty(params, "input");
0487: if (inObj != Scriptable.NOT_FOUND) {
0488: in = toInputStream(inObj);
0489: }
0490: outObj = ScriptableObject.getProperty(params, "output");
0491: if (outObj != Scriptable.NOT_FOUND) {
0492: out = toOutputStream(outObj);
0493: if (out == null) {
0494: outBytes = new ByteArrayOutputStream();
0495: out = outBytes;
0496: }
0497: }
0498: errObj = ScriptableObject.getProperty(params, "err");
0499: if (errObj != Scriptable.NOT_FOUND) {
0500: err = toOutputStream(errObj);
0501: if (err == null) {
0502: errBytes = new ByteArrayOutputStream();
0503: err = errBytes;
0504: }
0505: }
0506: Object addArgsObj = ScriptableObject.getProperty(params,
0507: "args");
0508: if (addArgsObj != Scriptable.NOT_FOUND) {
0509: Scriptable s = Context.toObject(addArgsObj,
0510: getTopLevelScope(this Obj));
0511: addArgs = cx.getElements(s);
0512: }
0513: }
0514: Global global = getInstance(funObj);
0515: if (out == null) {
0516: out = (global != null) ? global.getOut() : System.out;
0517: }
0518: if (err == null) {
0519: err = (global != null) ? global.getErr() : System.err;
0520: }
0521: // If no explicit input stream, do not send any input to process,
0522: // in particular, do not use System.in to avoid deadlocks
0523: // when waiting for user input to send to process which is already
0524: // terminated as it is not always possible to interrupt read method.
0525:
0526: String[] cmd = new String[(addArgs == null) ? L : L
0527: + addArgs.length];
0528: for (int i = 0; i != L; ++i) {
0529: cmd[i] = ScriptRuntime.toString(args[i]);
0530: }
0531: if (addArgs != null) {
0532: for (int i = 0; i != addArgs.length; ++i) {
0533: cmd[L + i] = ScriptRuntime.toString(addArgs[i]);
0534: }
0535: }
0536:
0537: int exitCode = runProcess(cmd, environment, in, out, err);
0538: if (outBytes != null) {
0539: String s = ScriptRuntime.toString(outObj)
0540: + outBytes.toString();
0541: ScriptableObject.putProperty(params, "output", s);
0542: }
0543: if (errBytes != null) {
0544: String s = ScriptRuntime.toString(errObj)
0545: + errBytes.toString();
0546: ScriptableObject.putProperty(params, "err", s);
0547: }
0548:
0549: return new Integer(exitCode);
0550: }
0551:
0552: /**
0553: * The seal function seals all supplied arguments.
0554: */
0555: public static void seal(Context cx, Scriptable this Obj,
0556: Object[] args, Function funObj) {
0557: for (int i = 0; i != args.length; ++i) {
0558: Object arg = args[i];
0559: if (!(arg instanceof ScriptableObject)
0560: || arg == Undefined.instance) {
0561: if (!(arg instanceof Scriptable)
0562: || arg == Undefined.instance) {
0563: throw reportRuntimeError("msg.shell.seal.not.object");
0564: } else {
0565: throw reportRuntimeError("msg.shell.seal.not.scriptable");
0566: }
0567: }
0568: }
0569:
0570: for (int i = 0; i != args.length; ++i) {
0571: Object arg = args[i];
0572: ((ScriptableObject) arg).sealObject();
0573: }
0574: }
0575:
0576: /**
0577: * The readFile reads the given file content and convert it to a string
0578: * using the specified character coding or default character coding if
0579: * explicit coding argument is not given.
0580: * <p>
0581: * Usage:
0582: * <pre>
0583: * readFile(filePath)
0584: * readFile(filePath, charCoding)
0585: * </pre>
0586: * The first form converts file's context to string using the default
0587: * character coding.
0588: */
0589: public static Object readFile(Context cx, Scriptable this Obj,
0590: Object[] args, Function funObj) throws IOException {
0591: if (args.length == 0) {
0592: throw reportRuntimeError("msg.shell.readFile.bad.args");
0593: }
0594: String path = ScriptRuntime.toString(args[0]);
0595: String charCoding = null;
0596: if (args.length >= 2) {
0597: charCoding = ScriptRuntime.toString(args[1]);
0598: }
0599:
0600: return readUrl(path, charCoding, true);
0601: }
0602:
0603: /**
0604: * The readUrl opens connection to the given URL, read all its data
0605: * and converts them to a string
0606: * using the specified character coding or default character coding if
0607: * explicit coding argument is not given.
0608: * <p>
0609: * Usage:
0610: * <pre>
0611: * readUrl(url)
0612: * readUrl(url, charCoding)
0613: * </pre>
0614: * The first form converts file's context to string using the default
0615: * charCoding.
0616: */
0617: public static Object readUrl(Context cx, Scriptable this Obj,
0618: Object[] args, Function funObj) throws IOException {
0619: if (args.length == 0) {
0620: throw reportRuntimeError("msg.shell.readUrl.bad.args");
0621: }
0622: String url = ScriptRuntime.toString(args[0]);
0623: String charCoding = null;
0624: if (args.length >= 2) {
0625: charCoding = ScriptRuntime.toString(args[1]);
0626: }
0627:
0628: return readUrl(url, charCoding, false);
0629: }
0630:
0631: /**
0632: * Convert the argumnet to int32 number.
0633: */
0634: public static Object toint32(Context cx, Scriptable this Obj,
0635: Object[] args, Function funObj) {
0636: Object arg = (args.length != 0 ? args[0] : Undefined.instance);
0637: if (arg instanceof Integer)
0638: return arg;
0639: return ScriptRuntime.wrapInt(ScriptRuntime.toInt32(arg));
0640: }
0641:
0642: public InputStream getIn() {
0643: return inStream == null ? System.in : inStream;
0644: }
0645:
0646: public void setIn(InputStream in) {
0647: inStream = in;
0648: }
0649:
0650: public PrintStream getOut() {
0651: return outStream == null ? System.out : outStream;
0652: }
0653:
0654: public void setOut(PrintStream out) {
0655: outStream = out;
0656: }
0657:
0658: public PrintStream getErr() {
0659: return errStream == null ? System.err : errStream;
0660: }
0661:
0662: public void setErr(PrintStream err) {
0663: errStream = err;
0664: }
0665:
0666: public void setSealedStdLib(boolean value) {
0667: sealedStdLib = value;
0668: }
0669:
0670: private static Global getInstance(Function function) {
0671: Scriptable scope = function.getParentScope();
0672: if (!(scope instanceof Global))
0673: throw reportRuntimeError("msg.bad.shell.function.scope",
0674: String.valueOf(scope));
0675: return (Global) scope;
0676: }
0677:
0678: /**
0679: * Runs the given process using Runtime.exec().
0680: * If any of in, out, err is null, the corresponding process stream will
0681: * be closed immediately, otherwise it will be closed as soon as
0682: * all data will be read from/written to process
0683: *
0684: * @return Exit value of process.
0685: * @throws IOException If there was an error executing the process.
0686: */
0687: private static int runProcess(String[] cmd, String[] environment,
0688: InputStream in, OutputStream out, OutputStream err)
0689: throws IOException {
0690: Process p;
0691: if (environment == null) {
0692: p = Runtime.getRuntime().exec(cmd);
0693: } else {
0694: p = Runtime.getRuntime().exec(cmd, environment);
0695: }
0696:
0697: try {
0698: PipeThread inThread = null;
0699: if (in != null) {
0700: inThread = new PipeThread(false, in, p
0701: .getOutputStream());
0702: inThread.start();
0703: } else {
0704: p.getOutputStream().close();
0705: }
0706:
0707: PipeThread outThread = null;
0708: if (out != null) {
0709: outThread = new PipeThread(true, p.getInputStream(),
0710: out);
0711: outThread.start();
0712: } else {
0713: p.getInputStream().close();
0714: }
0715:
0716: PipeThread errThread = null;
0717: if (err != null) {
0718: errThread = new PipeThread(true, p.getErrorStream(),
0719: err);
0720: errThread.start();
0721: } else {
0722: p.getErrorStream().close();
0723: }
0724:
0725: // wait for process completion
0726: for (;;) {
0727: try {
0728: p.waitFor();
0729: if (outThread != null) {
0730: outThread.join();
0731: }
0732: if (inThread != null) {
0733: inThread.join();
0734: }
0735: if (errThread != null) {
0736: errThread.join();
0737: }
0738: break;
0739: } catch (InterruptedException ignore) {
0740: }
0741: }
0742:
0743: return p.exitValue();
0744: } finally {
0745: p.destroy();
0746: }
0747: }
0748:
0749: static void pipe(boolean fromProcess, InputStream from,
0750: OutputStream to) throws IOException {
0751: try {
0752: final int SIZE = 4096;
0753: byte[] buffer = new byte[SIZE];
0754: for (;;) {
0755: int n;
0756: if (!fromProcess) {
0757: n = from.read(buffer, 0, SIZE);
0758: } else {
0759: try {
0760: n = from.read(buffer, 0, SIZE);
0761: } catch (IOException ex) {
0762: // Ignore exception as it can be cause by closed pipe
0763: break;
0764: }
0765: }
0766: if (n < 0) {
0767: break;
0768: }
0769: if (fromProcess) {
0770: to.write(buffer, 0, n);
0771: to.flush();
0772: } else {
0773: try {
0774: to.write(buffer, 0, n);
0775: to.flush();
0776: } catch (IOException ex) {
0777: // Ignore exception as it can be cause by closed pipe
0778: break;
0779: }
0780: }
0781: }
0782: } finally {
0783: try {
0784: if (fromProcess) {
0785: from.close();
0786: } else {
0787: to.close();
0788: }
0789: } catch (IOException ex) {
0790: // Ignore errors on close. On Windows JVM may throw invalid
0791: // refrence exception if process terminates too fast.
0792: }
0793: }
0794: }
0795:
0796: private static InputStream toInputStream(Object value)
0797: throws IOException {
0798: InputStream is = null;
0799: String s = null;
0800: if (value instanceof Wrapper) {
0801: Object unwrapped = ((Wrapper) value).unwrap();
0802: if (unwrapped instanceof InputStream) {
0803: is = (InputStream) unwrapped;
0804: } else if (unwrapped instanceof byte[]) {
0805: is = new ByteArrayInputStream((byte[]) unwrapped);
0806: } else if (unwrapped instanceof Reader) {
0807: s = readReader((Reader) unwrapped);
0808: } else if (unwrapped instanceof char[]) {
0809: s = new String((char[]) unwrapped);
0810: }
0811: }
0812: if (is == null) {
0813: if (s == null) {
0814: s = ScriptRuntime.toString(value);
0815: }
0816: is = new ByteArrayInputStream(s.getBytes());
0817: }
0818: return is;
0819: }
0820:
0821: private static OutputStream toOutputStream(Object value) {
0822: OutputStream os = null;
0823: if (value instanceof Wrapper) {
0824: Object unwrapped = ((Wrapper) value).unwrap();
0825: if (unwrapped instanceof OutputStream) {
0826: os = (OutputStream) unwrapped;
0827: }
0828: }
0829: return os;
0830: }
0831:
0832: private static String readUrl(String filePath, String charCoding,
0833: boolean urlIsFile) throws IOException {
0834: int chunkLength;
0835: InputStream is = null;
0836: try {
0837: if (!urlIsFile) {
0838: URL urlObj = new URL(filePath);
0839: URLConnection uc = urlObj.openConnection();
0840: is = uc.getInputStream();
0841: chunkLength = uc.getContentLength();
0842: if (chunkLength <= 0)
0843: chunkLength = 1024;
0844: if (charCoding == null) {
0845: String type = uc.getContentType();
0846: if (type != null) {
0847: charCoding = getCharCodingFromType(type);
0848: }
0849: }
0850: } else {
0851: File f = new File(filePath);
0852:
0853: long length = f.length();
0854: chunkLength = (int) length;
0855: if (chunkLength != length)
0856: throw new IOException("Too big file size: "
0857: + length);
0858:
0859: if (chunkLength == 0) {
0860: return "";
0861: }
0862:
0863: is = new FileInputStream(f);
0864: }
0865:
0866: Reader r;
0867: if (charCoding == null) {
0868: r = new InputStreamReader(is);
0869: } else {
0870: r = new InputStreamReader(is, charCoding);
0871: }
0872: return readReader(r, chunkLength);
0873:
0874: } finally {
0875: if (is != null)
0876: is.close();
0877: }
0878: }
0879:
0880: private static String getCharCodingFromType(String type) {
0881: int i = type.indexOf(';');
0882: if (i >= 0) {
0883: int end = type.length();
0884: ++i;
0885: while (i != end && type.charAt(i) <= ' ') {
0886: ++i;
0887: }
0888: String charset = "charset";
0889: if (charset.regionMatches(true, 0, type, i, charset
0890: .length())) {
0891: i += charset.length();
0892: while (i != end && type.charAt(i) <= ' ') {
0893: ++i;
0894: }
0895: if (i != end && type.charAt(i) == '=') {
0896: ++i;
0897: while (i != end && type.charAt(i) <= ' ') {
0898: ++i;
0899: }
0900: if (i != end) {
0901: // i is at the start of non-empty
0902: // charCoding spec
0903: while (type.charAt(end - 1) <= ' ') {
0904: --end;
0905: }
0906: return type.substring(i, end);
0907: }
0908: }
0909: }
0910: }
0911: return null;
0912: }
0913:
0914: private static String readReader(Reader reader) throws IOException {
0915: return readReader(reader, 4096);
0916: }
0917:
0918: private static String readReader(Reader reader,
0919: int initialBufferSize) throws IOException {
0920: char[] buffer = new char[initialBufferSize];
0921: int offset = 0;
0922: for (;;) {
0923: int n = reader.read(buffer, offset, buffer.length - offset);
0924: if (n < 0) {
0925: break;
0926: }
0927: offset += n;
0928: if (offset == buffer.length) {
0929: char[] tmp = new char[buffer.length * 2];
0930: System.arraycopy(buffer, 0, tmp, 0, offset);
0931: buffer = tmp;
0932: }
0933: }
0934: return new String(buffer, 0, offset);
0935: }
0936:
0937: static RuntimeException reportRuntimeError(String msgId) {
0938: String message = ToolErrorReporter.getMessage(msgId);
0939: return Context.reportRuntimeError(message);
0940: }
0941:
0942: static RuntimeException reportRuntimeError(String msgId,
0943: String msgArg) {
0944: String message = ToolErrorReporter.getMessage(msgId, msgArg);
0945: return Context.reportRuntimeError(message);
0946: }
0947: }
0948:
0949: class Runner implements Runnable, ContextAction {
0950:
0951: Runner(Scriptable scope, Function func, Object[] args) {
0952: this .scope = scope;
0953: f = func;
0954: this .args = args;
0955: }
0956:
0957: Runner(Scriptable scope, Script script) {
0958: this .scope = scope;
0959: s = script;
0960: }
0961:
0962: public void run() {
0963: factory.call(this );
0964: }
0965:
0966: public Object run(Context cx) {
0967: if (f != null)
0968: return f.call(cx, scope, scope, args);
0969: else
0970: return s.exec(cx, scope);
0971: }
0972:
0973: ContextFactory factory;
0974: private Scriptable scope;
0975: private Function f;
0976: private Script s;
0977: private Object[] args;
0978: }
0979:
0980: class PipeThread extends Thread {
0981:
0982: PipeThread(boolean fromProcess, InputStream from, OutputStream to) {
0983: setDaemon(true);
0984: this .fromProcess = fromProcess;
0985: this .from = from;
0986: this .to = to;
0987: }
0988:
0989: public void run() {
0990: try {
0991: Global.pipe(fromProcess, from, to);
0992: } catch (IOException ex) {
0993: throw Context.throwAsScriptRuntimeEx(ex);
0994: }
0995: }
0996:
0997: private boolean fromProcess;
0998: private InputStream from;
0999: private OutputStream to;
1000: }
|