org.apache.sling.scripting.sightly.js.impl.JsEnvironment.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.sling.scripting.sightly.js.impl.JsEnvironment.java

Source

/*******************************************************************************
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 ******************************************************************************/
package org.apache.sling.scripting.sightly.js.impl;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
import javax.script.SimpleScriptContext;

import org.apache.commons.io.IOUtils;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.scripting.core.ScriptNameAwareReader;
import org.apache.sling.scripting.sightly.SightlyException;
import org.apache.sling.scripting.sightly.js.impl.async.AsyncContainer;
import org.apache.sling.scripting.sightly.js.impl.async.TimingBindingsValuesProvider;
import org.apache.sling.scripting.sightly.js.impl.async.UnaryCallback;
import org.apache.sling.scripting.sightly.js.impl.cjs.CommonJsModule;
import org.apache.sling.scripting.sightly.js.impl.loop.EventLoop;
import org.apache.sling.scripting.sightly.js.impl.loop.EventLoopInterop;
import org.apache.sling.scripting.sightly.js.impl.loop.Task;
import org.apache.sling.scripting.sightly.js.impl.use.DependencyResolver;
import org.apache.sling.scripting.sightly.js.impl.use.UseFunction;
import org.mozilla.javascript.Context;
import org.slf4j.LoggerFactory;

/**
 * Environment for running JS scripts
 */
public class JsEnvironment {

    private final ScriptEngine jsEngine;
    private final Bindings engineBindings;
    private EventLoop eventLoop;

    public JsEnvironment(ScriptEngine jsEngine) {
        this.jsEngine = jsEngine;
        engineBindings = new SimpleBindings();
        TimingBindingsValuesProvider.INSTANCE.addBindings(engineBindings);
    }

    public void initialize() {
        Context context = Context.enter();
        eventLoop = EventLoopInterop.obtainEventLoop(context);
    }

    public void cleanup() {
        Context context = Context.getCurrentContext();
        if (context == null) {
            throw new IllegalStateException("No current context");
        }
        EventLoopInterop.cleanupEventLoop(context);
        Context.exit();
    }

    public void runResource(Resource scriptResource, Bindings globalBindings, Bindings arguments,
            UnaryCallback callback) {
        ScriptContext scriptContext = new SimpleScriptContext();
        CommonJsModule module = new CommonJsModule();
        Bindings scriptBindings = buildBindings(scriptResource, globalBindings, arguments, module);
        scriptContext.setBindings(scriptBindings, ScriptContext.ENGINE_SCOPE);
        scriptContext.setAttribute(ScriptEngine.FILENAME, scriptResource.getPath(), ScriptContext.ENGINE_SCOPE);
        runScript(scriptResource, scriptContext, callback, module);
    }

    public AsyncContainer runResource(Resource scriptResource, Bindings globalBindings, Bindings arguments) {
        AsyncContainer asyncContainer = new AsyncContainer();
        runResource(scriptResource, globalBindings, arguments, asyncContainer.createCompletionCallback());
        return asyncContainer;
    }

    private Bindings buildBindings(Resource scriptResource, Bindings local, Bindings arguments,
            CommonJsModule commonJsModule) {
        Bindings bindings = new SimpleBindings();
        bindings.putAll(engineBindings);
        DependencyResolver dependencyResolver = new DependencyResolver(scriptResource, this, local);
        UseFunction useFunction = new UseFunction(dependencyResolver, arguments);
        bindings.put(Variables.JS_USE, useFunction);
        bindings.put(Variables.MODULE, commonJsModule);
        bindings.put(Variables.EXPORTS, commonJsModule.getExports());
        bindings.put(Variables.CONSOLE, new Console(LoggerFactory.getLogger(scriptResource.getName())));
        bindings.putAll(local);
        return bindings;
    }

    private void runScript(Resource scriptResource, ScriptContext scriptContext, UnaryCallback callback,
            CommonJsModule commonJsModule) {
        eventLoop.schedule(scriptTask(scriptResource, scriptContext, callback, commonJsModule));
    }

    private Task scriptTask(final Resource scriptResource, final ScriptContext scriptContext,
            final UnaryCallback callback, final CommonJsModule commonJsModule) {
        return new Task(new Runnable() {
            @Override
            public void run() {
                Reader reader = null;
                try {
                    Object result;
                    if (jsEngine instanceof Compilable) {
                        reader = new ScriptNameAwareReader(
                                new InputStreamReader(scriptResource.adaptTo(InputStream.class)),
                                scriptResource.getPath());
                        result = ((Compilable) jsEngine).compile(reader).eval(scriptContext);
                    } else {
                        reader = new InputStreamReader(scriptResource.adaptTo(InputStream.class));
                        result = jsEngine.eval(reader, scriptContext);
                    }
                    if (commonJsModule.isModified()) {
                        result = commonJsModule.getExports();
                    }
                    if (result instanceof AsyncContainer) {
                        ((AsyncContainer) result).addListener(callback);
                    } else {
                        callback.invoke(result);
                    }
                } catch (ScriptException e) {
                    throw new SightlyException(e);
                } finally {
                    IOUtils.closeQuietly(reader);
                }
            }
        });
    }

}