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}