Script.java Source code

Java tutorial

Introduction

Here is the source code for Script.java

Source

/*
This file is part of Eggshell.
Copyright 2013 George Magiros
    
Eggshell is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the
Free Software Foundation, either version 3 of the License, or (at
your option) any later version.
    
Eggshell 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 General Public
License for more details.
    
You should have received a copy of the GNU General Public License
along with Eggshell.  If not, see <http://www.gnu.org/licenses/>.
*/

import org.mozilla.javascript.*;

import java.io.IOException;
import java.io.FileNotFoundException;

// java stream reading classes
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;

// hadoop task context
import org.apache.hadoop.mapreduce.TaskInputOutputContext;

// hadoop filesystem classes
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.filecache.DistributedCache;

/** Class that starts the javascript and handles the evaluation of
 *  scripts including those serialized over the distributed cache
 */
class Script {
    /* private fields */

    /** The global scope object */
    private Scriptable globalScope;

    /** The Javascript interpreter's context object */
    private Context cx;

    /* constructors */

    /** Starts the javascript interpreter and defines the base classes
     *  used by eggshell in the global scope.
     *  @return             The new script object
     *  @thows IOException  Failed creating the Javascript environment
     */
    public Script() throws IOException {
        try {
            cx = Context.enter();
            cx.setLanguageVersion(Context.VERSION_1_7);
            globalScope = cx.initStandardObjects();
            ScriptableObject.defineClass(globalScope, EggGlobal.class);
            ScriptableObject.defineClass(globalScope, EggIterator.class);
            ScriptableObject.defineClass(globalScope, EggContext.class);
            ScriptableObject.defineClass(globalScope, Egg.class);
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

    /* public methods */

    /** Exit the javascript interpreter
     */
    public void exit() {
        cx.exit();
    }

    /** Set the global scope.
     *  @param scope    The new global scope
     */
    public void setGlobalScope(Scriptable scope) {
        globalScope = scope;
    }

    /** Create a Javascript array from a Java Object[] array.
     *  @param         The Java array
     *  @return        The new array
     */
    public Scriptable newArray(Object[] array) {
        return cx.newArray(globalScope, array);
    }

    /** Create a Javascript object from a class.  The constructor is
     *  searched for in the global scope.
     *  @param name       The name of the class
     *  @param args       The arguments to pass to the class's constructor
     *  @return           The new object
     */
    public Scriptable newObject(String name, Object[] args) {
        return cx.newObject(globalScope, name, args);
    }

    /** Return the Javascript global scope property referenced by the
     *  name.  If the property is not found then return null.
     *  @param name       The name of the property
     *  @return           The Javascript object or null
     */
    public Object getProperty(String name) {
        Object ret = globalScope.get(name, globalScope);
        return (ret != UniqueTag.NOT_FOUND) ? ret : null;
    }

    /** Return the Javascript object property referenced by the name.
     *  If the property is not found then return null.
     *  @param object     The object to look up the property in
     *  @param name       The name of the property
     *  @return           The Javascript object or null
     */
    public Object getProperty(String name, Scriptable object) {
        Object ret = object.get(name, object);
        return (ret != UniqueTag.NOT_FOUND) ? ret : null;
    }

    /** Add the property to the Javascript global scope.
     *  @param name       The name of the property
     *  @param name       The scriptable object
     */
    public void putProperty(String name, Object object) {
        globalScope.put(name, globalScope, object);
    }

    /** Call a Javascript function.  If a Javascript exception occurs
     *  return null as the result of the function call.
     *  @param func       The Javascript function
     *  @param thisObj    The 'this' object to pass to the function
     *  @param args       The arguments to pass to the function
     *  @return           The result of the Javascript function call
     */
    public Object callFunction(Function func, Scriptable thisObj, Object[] args) {
        if (thisObj == null)
            thisObj = globalScope;
        Object ret;
        try {
            ret = func.call(cx, thisObj.getParentScope(), thisObj, args);
            if (ret instanceof Undefined)
                ret = null;
        } catch (JavaScriptException e) {
            ret = null;
        }
        return ret;
    }

    /** Evaluates the Javascript expression contained within a string,
     *  performing the evaluation within the global scope.
     *  @param buf        String to evaluate
     *  @return           The result of the Javascript evaluation
     */
    public Object evalString(String buf) {
        return cx.evaluateString(globalScope, buf, this.getClass().toString(), 1, null);
    }

    /** Evaluates the Javascript expressions found in the eggshell
     *  Javascript library file.
     *  @return           The result of the Javascript evaluation
     */
    public Object evalLibrary() throws IOException {
        ClassLoader cl = this.getClass().getClassLoader();
        InputStream stream = cl.getResourceAsStream(Eggshell.LIBRARY_FILE);
        BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
        String line, buf = "";
        while ((line = reader.readLine()) != null)
            buf += line + "\n";
        reader.close();
        return evalString(buf);
    }

    /** Evaluates the Javascript expressions contained in a file.
     *  @param name       The name of the file
     *  @return           The result of the Javascript evaluation
     */
    public Object evalFile(String name) throws IOException {
        InputStream stream = new FileInputStream(name);
        BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
        String line, buf = "";
        while ((line = reader.readLine()) != null)
            buf += line + "\n";
        reader.close();
        return evalString(buf);
    }

    /** Evaluates the Javascript expressions contained in a
     *  DataInputStream serialized file and passed over the distributed
     *  cache.
     *  @param conf       The Hadoop configuration object
     *  @param pathString The path string of the cached file
     *  @param name       The name of the file added to the cache
     *  @return           The result of the Javascript evaluation
     */
    public Object evalCache(Configuration conf, String pathString, String name) throws IOException {
        FSDataInputStream in;
        FileSystem fs = FileSystem.getLocal(conf);
        try {
            Path path = new Path(pathString);
            in = fs.open(path);
        } catch (FileNotFoundException e) { // must be running in standalone mode
            Path path = new Path(Eggshell.SCRIPT_DIR + "/" + name);
            in = fs.open(path); // read it from the eggshell script directory instead
        }
        String buf = in.readUTF();
        in.close();
        return evalString(buf);
    }

    /** Call a map-reduce Javascript function, saving the result in
     *  key and value Hadoop custom writable 'Tuples'.
     *  @param func          The map-reduce Javascript function
     *  @param thisObj       The 'this' object to pass to the function
     *  @param args          The arguments to pass to the function
     *  @return              The result of the Javascript function call
     *  @throws IOException  The map-reduce function returned a bad result
     */
    public Object callMapReduce(Function func, Scriptable thisObj, Object[] args, Tuple key, Tuple value)
            throws IOException {
        Object ret = callFunction(func, thisObj, args);

        // handle generators
        if (ret == null || ret instanceof NativeGenerator)
            return ret;

        // handle a map-reduce result
        if (!(ret instanceof NativeArray))
            throw new IOException();
        NativeArray array = (NativeArray) ret;

        key.clear(); // get the key Tuple and fill it
        ret = array.get(0);
        if (!(ret instanceof NativeArray))
            key.add(ret);
        else {
            NativeArray subarray = (NativeArray) ret;
            for (int i = 0; i < subarray.size(); i++)
                key.add(subarray.get(i));
        }

        value.clear(); // get the value Tuple and fill with the rest
        for (int n = 1; n < array.size(); n++) {
            ret = array.get(n);
            if (!(ret instanceof NativeArray))
                value.add(ret);
            else {
                NativeArray subarray = (NativeArray) ret;
                for (int i = 0; i < subarray.size(); i++)
                    value.add(subarray.get(i));
            }
        }
        return array;
    }

    /** Call the map-reduce Javascript function with the given
     *  arguments.  Save the key-value result in the task's context
     *  @param context   Task context
     *  @param script    The Javascript interpreter
     *  @param f         The map-reduce javascript object
     *  @param args      The key-value arguments
     */
    @SuppressWarnings("unchecked")
    public void dispatchMapReduce(TaskInputOutputContext context, Function f, Scriptable thisObj, Object[] args,
            Tuple key, Tuple value) throws IOException, InterruptedException {
        Object ret = callMapReduce(f, thisObj, args, key, value);
        if (ret instanceof NativeGenerator) {
            NativeGenerator gen = (NativeGenerator) ret;
            Function next = (Function) gen.getProperty(gen, "next");
            while (callMapReduce(next, gen, null, key, value) != null)
                context.write(key, value);
        } else if (ret != null) {
            context.write(key, value);
        }
    }

    /** Serialize the Javascript object into a file on HDFS and then add
     *  the file to the distributed cache.
     *  @param conf       The Hadoop configuration object
     *  @param o          The Javascript object to serialize
     *  @param name       The name of file to save the serialized object to
     */
    public void serialize(Configuration conf, Object o, String name) throws IOException {
        FileSystem hdfs = FileSystem.get(conf);
        Path path = new Path(Eggshell.SCRIPT_DIR + "/" + name);
        FSDataOutputStream out = hdfs.create(path); // create the file
        String buf;
        if (!(o instanceof NativeObject)) {
            buf = cx.toString(o); // serialize
            if (o instanceof NativeArray)
                buf = "[" + buf + "]"; // if array
        } else {
            buf = "{";
            NativeObject obj = (NativeObject) o;
            Object[] propIds = obj.getPropertyIds(obj);
            for (Object propId : propIds) {
                String key = propId.toString();
                Object value = obj.getProperty(obj, key);
                buf += key + ":" + cx.toString(value) + ",";
            }
            buf += "}";
        }
        buf = "(" + buf + ")"; // force evaluation
        out.writeUTF(buf);
        out.close();
        DistributedCache.addCacheFile(path.toUri(), conf);
    }

    /** Deserialize the Javascript function from the distributed cache.
     *  @param conf    The Hadoop configuration
     *  @return        The Javascript function
     */
    public Object deserialize(Configuration conf, String file) throws IOException {
        Path[] cacheFiles = DistributedCache.getLocalCacheFiles(conf);
        if (null != cacheFiles && cacheFiles.length > 0) {
            for (Path path : cacheFiles) { // loop through cache files
                if (path.getName().equals(file)) { // find this file
                    return evalCache(conf, path.toString(), file);
                }
            }
        }
        return null;
    }
}