All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.eclipsesource.v8.NodeJS Maven / Gradle / Ivy

There is a newer version: 4.6.0
Show newest version
/*******************************************************************************
 * Copyright (c) 2016 EclipseSource and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    EclipseSource - initial API and implementation
 ******************************************************************************/
package com.eclipsesource.v8;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * An isolate NodeJS runtime.
 *
 * This class is only available on some platforms. In particular any methods
 * on this class, on an Android device, will lead to an UnsupportedOperationException.
 */
public class NodeJS {

    private static final String TMP_JS_EXT          = ".js.tmp";
    private static final String NEXT_TICK           = "nextTick";
    private static final String PROCESS             = "process";
    private static final String GLOBAL              = "global";
    private static final String STARTUP_CALLBACK    = "__run";
    private static final String STARTUP_SCRIPT      = "global." + STARTUP_CALLBACK + "(require, exports, module, __filename, __dirname);";
    private static final String STARTUP_SCRIPT_NAME = "startup";

    private V8         v8;
    private V8Function require;

    /**
     * Creates a NodeJS Runtime
     *
     * @return The NodeJS runtime.
     *
     * May throw an UnsupportedOperationException if node.js integration has not
     * been compiled for your platform.
     */
    public static NodeJS createNodeJS() {
        return createNodeJS(null);
    }

    /**
     * Creates a NodeJS runtime and executes a JS Script
     *
     * @param file The JavaScript to execute or null for no script.
     * @return The NodeJS runtime.
     *
     * May throw an UnsupportedOperationException if node.js integration has not
     * been compiled for your platform.
     */
    public static NodeJS createNodeJS(final File file) {
        V8 v8 = V8.createV8Runtime(GLOBAL);
        final NodeJS node = new NodeJS(v8);
        v8.registerJavaMethod(new JavaVoidCallback() {

            @Override
            public void invoke(final V8Object receiver, final V8Array parameters) {
                V8Function require = (V8Function) parameters.get(0);
                try {
                    node.init(require.twin());
                } finally {
                    require.release();
                }
            }
        }, STARTUP_CALLBACK);
        try {
            File startupScript = createTemporaryScriptFile(STARTUP_SCRIPT, STARTUP_SCRIPT_NAME);
            try {
                v8.createNodeRuntime(startupScript.getAbsolutePath());
            } finally {
                startupScript.delete();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        if (file != null) {
            node.exec(file);
        }
        return node;
    }

    /**
     * Returns the V8 runtime being used for this NodeJS instance.
     *
     * @return The V8 Runtime.
     */
    public V8 getRuntime() {
        return v8;
    }

    /**
     * Handles the next message in the message loop. Returns True
     * if there are more messages to handle, false otherwise.
     *
     * @return True if there are more messages to handle, false otherwise.
     */
    public boolean handleMessage() {
        v8.checkThread();
        return v8.pumpMessageLoop();
    }

    /**
     * Releases the NodeJS runtime.
     */
    public void release() {
        v8.checkThread();
        if (!require.isReleased()) {
            require.release();
        }
        if (!v8.isReleased()) {
            v8.release();
        }
    }

    /**
     * Returns true if there are more messages to process, false otherwise.
     *
     * @return True if there are more messages to process, false otherwise.
     */
    public boolean isRunning() {
        v8.checkThread();
        return v8.isRunning();
    }

    /**
     * Invokes NodeJS require() on the specified file. This will load the module, execute
     * it and return the exports object to the caller. The exports object must be released.
     *
     * @param file The module to load.
     * @return The exports object.
     */
    public V8Object require(final File file) {
        v8.checkThread();
        V8Array requireParams = new V8Array(v8);
        try {
            requireParams.push(file.getAbsolutePath());
            return (V8Object) require.call(null, requireParams);
        } finally {
            requireParams.release();
        }
    }

    /**
     * Execute a NodeJS script. This will load the script and execute it on the
     * next tick. This is the same as how NodeJS executes scripts at startup. Since
     * the script won't actually run until the next tick, this method does not return
     * a result.
     *
     * @param file The script to execute.
     */
    public void exec(final File file) {
        V8Function scriptExecution = createScriptExecutionCallback(file);
        V8Object process = null;
        V8Array parameters = null;
        try {
            process = v8.getObject(PROCESS);
            parameters = new V8Array(v8);
            parameters.push(scriptExecution);
            process.executeObjectFunction(NEXT_TICK, parameters);
        } finally {
            safeRelease(process);
            safeRelease(parameters);
            safeRelease(scriptExecution);
        }
    }

    private V8Function createScriptExecutionCallback(final File file) {
        V8Function v8Function = new V8Function(v8, new JavaCallback() {
            @Override
            public Object invoke(final V8Object receiver, final V8Array parameters) {
                V8Array requireParams = new V8Array(v8);
                try {
                    requireParams.push(file.getAbsolutePath());
                    return require.call(null, requireParams);
                } finally {
                    requireParams.release();
                }
            }
        });
        return v8Function;
    }

    private void safeRelease(final Releasable releasable) {
        if (releasable != null) {
            releasable.release();
        }
    }

    private NodeJS(final V8 v8) {
        this.v8 = v8;
    }

    private void init(final V8Function require) {
        this.require = require;
    }

    private static File createTemporaryScriptFile(final String script, final String name) throws IOException {
        File tempFile = File.createTempFile(name, TMP_JS_EXT);
        PrintWriter writer = new PrintWriter(tempFile, "UTF-8");
        try {
            writer.print(script);
        } finally {
            writer.close();
        }
        return tempFile;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy