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 public RhinoExecutor invoke() 083 { 084 final Context context = contextFactory.enterContext(); 085 086 final ScriptableObject scope = context.initStandardObjects(); 087 088 try 089 { 090 context.setOptimizationLevel(-1); 091 092 for (Resource script : scripts) 093 { 094 loadScript(context, scope, script); 095 } 096 097 } finally 098 { 099 Context.exit(); 100 } 101 102 return new RhinoExecutor() 103 { 104 public ScriptableObject invokeFunction(String functionName, Object... arguments) 105 { 106 contextFactory.enterContext(context); 107 108 try 109 { 110 NativeFunction function = (NativeFunction) scope.get(functionName, scope); 111 112 return (ScriptableObject) function.call(context, scope, null, arguments); 113 } finally 114 { 115 Context.exit(); 116 } 117 } 118 119 public void discard() 120 { 121 put(this); 122 } 123 }; 124 } 125 }); 126 } 127 128 private void loadScript(final Context context, final ScriptableObject scope, final Resource script) 129 { 130 tracker.run(String.format("Loading script %s.", script), 131 new Runnable() 132 { 133 public void run() 134 { 135 InputStream in = null; 136 Reader r = null; 137 138 try 139 { 140 in = script.openStream(); 141 r = new InputStreamReader(in); 142 143 context.evaluateReader(scope, r, script.toString(), 1, null); 144 } catch (IOException ex) 145 { 146 throw new RuntimeException(String.format("Unable to read script %s: %s", 147 script, 148 ExceptionUtils.toMessage(ex) 149 ), ex); 150 } finally 151 { 152 InternalUtils.close(r); 153 InternalUtils.close(in); 154 } 155 } 156 }); 157 158 } 159 160 161}