// Copyright 2013-2014 The Apache Software Foundation // // Licensed 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.tapestry5.internal.webresources; import org.apache.tapestry5.ioc.Invokable; import org.apache.tapestry5.ioc.OperationTracker; import org.apache.tapestry5.ioc.Resource; import org.apache.tapestry5.ioc.internal.util.InternalUtils; import org.apache.tapestry5.ioc.util.ExceptionUtils; import org.mozilla.javascript.Context; import org.mozilla.javascript.ContextFactory; import org.mozilla.javascript.NativeFunction; import org.mozilla.javascript.ScriptableObject; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; /** * Manages a pool of initialized {@link RhinoExecutor} instances. The instances are initialized for a particular */ public class RhinoExecutorPool { private final OperationTracker tracker; private final List<Resource> scripts; private final Queue<RhinoExecutor> executors = new ConcurrentLinkedQueue<RhinoExecutor>(); private final ContextFactory contextFactory = new ContextFactory(); public RhinoExecutorPool(OperationTracker tracker, List<Resource> scripts) { this.tracker = tracker; this.scripts = scripts; } /** * Gets or creates an available executor. It is expected that {@link #put(RhinoExecutor)} will * be invoked after the executor completes. * * @return executor */ public RhinoExecutor get() { RhinoExecutor executor = executors.poll(); if (executor != null) { return executor; } return createExecutor(); } private void put(RhinoExecutor executor) { executors.add(executor); } private RhinoExecutor createExecutor() { return tracker.invoke(String.format("Creating Rhino executor for source(s) %s.", InternalUtils.join(scripts)), new Invokable<RhinoExecutor>() { @Override public RhinoExecutor invoke() { final Context context = contextFactory.enterContext(); final ScriptableObject scope = context.initStandardObjects(); try { context.setOptimizationLevel(-1); for (Resource script : scripts) { loadScript(context, scope, script); } } finally { Context.exit(); } return new RhinoExecutor() { @Override public ScriptableObject invokeFunction(String functionName, Object... arguments) { contextFactory.enterContext(context); try { NativeFunction function = (NativeFunction) scope.get(functionName, scope); return (ScriptableObject) function.call(context, scope, null, arguments); } finally { Context.exit(); } } @Override public void discard() { put(this); } }; } }); } private void loadScript(final Context context, final ScriptableObject scope, final Resource script) { tracker.run(String.format("Loading script %s.", script), new Runnable() { @Override public void run() { InputStream in = null; Reader r = null; try { in = script.openStream(); r = new InputStreamReader(in, StandardCharsets.UTF_8); context.evaluateReader(scope, r, script.toString(), 1, null); } catch (IOException ex) { throw new RuntimeException(String.format("Unable to read script %s: %s", script, ExceptionUtils.toMessage(ex) ), ex); } finally { InternalUtils.close(r); InternalUtils.close(in); } } }); } }