    001// Copyright 2013-2014 The Apache Software Foundation
    002//
    003// Licensed under the Apache License, Version 2.0 (the "License");
    004// you may not use this file except in compliance with the License.
    005// You may obtain a copy of the License at
    006//
    007// http://www.apache.org/licenses/LICENSE-2.0
    008//
    009// Unless required by applicable law or agreed to in writing, software
    010// distributed under the License is distributed on an "AS IS" BASIS,
    011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012// See the License for the specific language governing permissions and
    013// limitations under the License.
    014
    015package org.apache.tapestry5.internal.webresources;
    016
    017import org.apache.tapestry5.ioc.Invokable;
    018import org.apache.tapestry5.ioc.OperationTracker;
    019import org.apache.tapestry5.ioc.Resource;
    020import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
    021import org.apache.tapestry5.ioc.internal.util.InternalUtils;
    022import org.apache.tapestry5.ioc.util.ExceptionUtils;
    023import org.apache.tapestry5.ioc.util.Stack;
    024import org.mozilla.javascript.Context;
    025import org.mozilla.javascript.ContextFactory;
    026import org.mozilla.javascript.NativeFunction;
    027import org.mozilla.javascript.ScriptableObject;
    028
    029import java.io.IOException;
    030import java.io.InputStream;
    031import java.io.InputStreamReader;
    032import java.io.Reader;
    033import java.util.List;
    034
    035/**
    036 * Manages a pool of initialized {@link RhinoExecutor} instances.  The instances are initialized for a particular
    037 */
    038public class RhinoExecutorPool
    039{
    040    private final OperationTracker tracker;
    041
    042    private final List<Resource> scripts;
    043
    044    private final Stack<RhinoExecutor> executors = CollectionFactory.newStack();
    045
    046    private final ContextFactory contextFactory = new ContextFactory();
    047
    048    public RhinoExecutorPool(OperationTracker tracker, List<Resource> scripts)
    049    {
    050        this.tracker = tracker;
    051        this.scripts = scripts;
    052    }
    053
    054    /**
    055     * Gets or creates an available executor. It is expected that {@link #put(RhinoExecutor)} will
    056     * be invoked after the executor completes.
    057     *
    058     * @return executor
    059     */
    060    public synchronized RhinoExecutor get()
    061    {
    062
    063        if (executors.isEmpty())
    064        {
    065            return createExecutor();
    066        }
    067
    068        return executors.pop();
    069    }
    070
    071    private synchronized void put(RhinoExecutor executor)
    072    {
    073        executors.push(executor);
    074    }
    075
    076    private RhinoExecutor createExecutor()
    077    {
    078        return tracker.invoke(String.format("Creating Rhino executor for source(s) %s.",
    079                InternalUtils.join(scripts)),
    080                new Invokable<RhinoExecutor>()
    081                {
    082                    @Override
    083                    public RhinoExecutor invoke()
    084                    {
    085                        final Context context = contextFactory.enterContext();
    086
    087                        final ScriptableObject scope = context.initStandardObjects();
    088
    089                        try
    090                        {
    091                            context.setOptimizationLevel(-1);
    092
    093                            for (Resource script : scripts)
    094                            {
    095                                loadScript(context, scope, script);
    096                            }
    097
    098                        } finally
    099                        {
    100                            Context.exit();
    101                        }
    102
    103                        return new RhinoExecutor()
    104                        {
    105                            @Override
    106                            public ScriptableObject invokeFunction(String functionName, Object... arguments)
    107                            {
    108                                contextFactory.enterContext(context);
    109
    110                                try
    111                                {
    112                                    NativeFunction function = (NativeFunction) scope.get(functionName, scope);
    113
    114                                    return (ScriptableObject) function.call(context, scope, null, arguments);
    115                                } finally
    116                                {
    117                                    Context.exit();
    118                                }
    119                            }
    120
    121                            @Override
    122                            public void discard()
    123                            {
    124                                put(this);
    125                            }
    126                        };
    127                    }
    128                });
    129    }
    130
    131    private void loadScript(final Context context, final ScriptableObject scope, final Resource script)
    132    {
    133        tracker.run(String.format("Loading script %s.", script),
    134                new Runnable()
    135                {
    136                    @Override
    137                    public void run()
    138                    {
    139                        InputStream in = null;
    140                        Reader r = null;
    141
    142                        try
    143                        {
    144                            in = script.openStream();
    145                            r = new InputStreamReader(in);
    146
    147                            context.evaluateReader(scope, r, script.toString(), 1, null);
    148                        } catch (IOException ex)
    149                        {
    150                            throw new RuntimeException(String.format("Unable to read script %s: %s",
    151                                    script,
    152                                    ExceptionUtils.toMessage(ex)
    153                            ), ex);
    154                        } finally
    155                        {
    156                            InternalUtils.close(r);
    157                            InternalUtils.close(in);
    158                        }
    159                    }
    160                });
    161
    162    }
    163
    164
    165}