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.InternalUtils; 021import org.apache.tapestry5.ioc.util.ExceptionUtils; 022import org.mozilla.javascript.Context; 023import org.mozilla.javascript.ContextFactory; 024import org.mozilla.javascript.NativeFunction; 025import org.mozilla.javascript.ScriptableObject; 026 027import java.io.IOException; 028import java.io.InputStream; 029import java.io.InputStreamReader; 030import java.io.Reader; 031import java.util.List; 032import java.util.Queue; 033import java.util.concurrent.ConcurrentLinkedQueue; 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 Queue<RhinoExecutor> executors = new ConcurrentLinkedQueue<RhinoExecutor>(); 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 RhinoExecutor get() 061 { 062 063 RhinoExecutor executor = executors.poll(); 064 if (executor != null) 065 { 066 return executor; 067 } 068 069 return createExecutor(); 070 } 071 072 private void put(RhinoExecutor executor) 073 { 074 executors.add(executor); 075 } 076 077 private RhinoExecutor createExecutor() 078 { 079 return tracker.invoke(String.format("Creating Rhino executor for source(s) %s.", 080 InternalUtils.join(scripts)), 081 new Invokable<RhinoExecutor>() 082 { 083 @Override 084 public RhinoExecutor invoke() 085 { 086 final Context context = contextFactory.enterContext(); 087 088 final ScriptableObject scope = context.initStandardObjects(); 089 090 try 091 { 092 context.setOptimizationLevel(-1); 093 094 for (Resource script : scripts) 095 { 096 loadScript(context, scope, script); 097 } 098 099 } finally 100 { 101 Context.exit(); 102 } 103 104 return new RhinoExecutor() 105 { 106 @Override 107 public ScriptableObject invokeFunction(String functionName, Object... arguments) 108 { 109 contextFactory.enterContext(context); 110 111 try 112 { 113 NativeFunction function = (NativeFunction) scope.get(functionName, scope); 114 115 return (ScriptableObject) function.call(context, scope, null, arguments); 116 } finally 117 { 118 Context.exit(); 119 } 120 } 121 122 @Override 123 public void discard() 124 { 125 put(this); 126 } 127 }; 128 } 129 }); 130 } 131 132 private void loadScript(final Context context, final ScriptableObject scope, final Resource script) 133 { 134 tracker.run(String.format("Loading script %s.", script), 135 new Runnable() 136 { 137 @Override 138 public void run() 139 { 140 InputStream in = null; 141 Reader r = null; 142 143 try 144 { 145 in = script.openStream(); 146 r = new InputStreamReader(in); 147 148 context.evaluateReader(scope, r, script.toString(), 1, null); 149 } catch (IOException ex) 150 { 151 throw new RuntimeException(String.format("Unable to read script %s: %s", 152 script, 153 ExceptionUtils.toMessage(ex) 154 ), ex); 155 } finally 156 { 157 InternalUtils.close(r); 158 InternalUtils.close(in); 159 } 160 } 161 }); 162 163 } 164 165 166}