http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6e42b37c/tapestry-webresources/src/main/java/org/apache/tapestry5/internal/webresources/RhinoExecutorPool.java ---------------------------------------------------------------------- diff --git a/tapestry-webresources/src/main/java/org/apache/tapestry5/internal/webresources/RhinoExecutorPool.java b/tapestry-webresources/src/main/java/org/apache/tapestry5/internal/webresources/RhinoExecutorPool.java new file mode 100644 index 0000000..efc9de8 --- /dev/null +++ b/tapestry-webresources/src/main/java/org/apache/tapestry5/internal/webresources/RhinoExecutorPool.java @@ -0,0 +1,147 @@ +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.CollectionFactory; +import org.apache.tapestry5.ioc.internal.util.InternalUtils; +import org.apache.tapestry5.ioc.util.ExceptionUtils; +import org.apache.tapestry5.ioc.util.Stack; +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.util.List; + +/** + * 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 Stack<RhinoExecutor> executors = CollectionFactory.newStack(); + + 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 synchronized RhinoExecutor get() + { + + if (executors.isEmpty()) + { + return createExecutor(); + } + + return executors.pop(); + } + + private synchronized void put(RhinoExecutor executor) + { + executors.push(executor); + } + + private RhinoExecutor createExecutor() + { + return tracker.invoke(String.format("Creating Rhino executor for source(s) %s.", + InternalUtils.join(scripts)), + new Invokable<RhinoExecutor>() + { + 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() + { + 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(); + } + } + + 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() + { + public void run() + { + InputStream in = null; + Reader r = null; + + try + { + in = script.openStream(); + r = new InputStreamReader(in); + + 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); + } + } + }); + + } + + +}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6e42b37c/tapestry-webresources/src/main/java/org/apache/tapestry5/webresources/WebResourcesSymbols.java ---------------------------------------------------------------------- diff --git a/tapestry-webresources/src/main/java/org/apache/tapestry5/webresources/WebResourcesSymbols.java b/tapestry-webresources/src/main/java/org/apache/tapestry5/webresources/WebResourcesSymbols.java new file mode 100644 index 0000000..82bd018 --- /dev/null +++ b/tapestry-webresources/src/main/java/org/apache/tapestry5/webresources/WebResourcesSymbols.java @@ -0,0 +1,25 @@ +// Copyright 2013 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.webresources; + +public class WebResourcesSymbols +{ + /** + * Directory that stores cached copies of compiled CoffeeScript files. The directory will be created + * as necessary. This allows compilation (e.g., CoffeeScript to JavaScript) to be avoided after a restart. + * The default is from the {@code java.io.tmpdir} system property (which is not necessarily stable between executions). + */ + public static final String CACHE_DIR = "tapestry.compiled-asset-cache-dir"; +} http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/6e42b37c/tapestry-webresources/src/main/java/org/apache/tapestry5/webresources/modules/WebResourcesModule.java ---------------------------------------------------------------------- diff --git a/tapestry-webresources/src/main/java/org/apache/tapestry5/webresources/modules/WebResourcesModule.java b/tapestry-webresources/src/main/java/org/apache/tapestry5/webresources/modules/WebResourcesModule.java new file mode 100644 index 0000000..1948873 --- /dev/null +++ b/tapestry-webresources/src/main/java/org/apache/tapestry5/webresources/modules/WebResourcesModule.java @@ -0,0 +1,126 @@ +// Copyright 2013 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.webresources.modules; + +import com.github.sommeri.less4j.LessCompiler; +import com.github.sommeri.less4j.core.parser.AntlrException; +import org.apache.tapestry5.MarkupWriter; +import org.apache.tapestry5.internal.webresources.*; +import org.apache.tapestry5.ioc.MappedConfiguration; +import org.apache.tapestry5.ioc.ServiceBinder; +import org.apache.tapestry5.ioc.annotations.Autobuild; +import org.apache.tapestry5.ioc.annotations.Contribute; +import org.apache.tapestry5.ioc.annotations.Primary; +import org.apache.tapestry5.ioc.internal.util.CollectionFactory; +import org.apache.tapestry5.ioc.internal.util.InternalUtils; +import org.apache.tapestry5.ioc.services.FactoryDefaults; +import org.apache.tapestry5.ioc.services.SymbolProvider; +import org.apache.tapestry5.services.ObjectRenderer; +import org.apache.tapestry5.services.assets.ResourceMinimizer; +import org.apache.tapestry5.services.assets.ResourceTransformer; +import org.apache.tapestry5.services.assets.StreamableResourceSource; +import org.apache.tapestry5.webresources.WebResourcesSymbols; + +import java.util.List; + +/** + * Configures use of various transformers and mimimizers to support: + * <ul> + * <li>Less to CSS</li> + * <li>CoffeeScript to JavaScript</li> + * <li>CSS minimization via YUI Compressor</li> + * <li>JavaScript minimization via Google Closure</li> + * </ul> + * + * @since 5.4 + */ +public class WebResourcesModule +{ + public static void bind(ServiceBinder binder) + { + binder.bind(ResourceTransformerFactory.class, ResourceTransformerFactoryImpl.class); + } + + @Contribute(SymbolProvider.class) + @FactoryDefaults + public static void setupDefaultCacheDirectory(MappedConfiguration<String, Object> configuration) + { + configuration.add(WebResourcesSymbols.CACHE_DIR, "${java.io.tmpdir}"); + } + + + @Contribute(StreamableResourceSource.class) + public static void provideCompilers(MappedConfiguration<String, ResourceTransformer> configuration, ResourceTransformerFactory factory, + @Autobuild CoffeeScriptCompiler coffeeScriptCompiler) + { + // contribution ids are file extensions: + + configuration.add("coffee", + factory.createCompiler("text/javascript", "CoffeeScript", "JavaScript", + coffeeScriptCompiler, + CacheMode.SINGLE_FILE)); + + configuration.add("less", + factory.createCompiler("text/css", "Less", "CSS", new LessResourceTransformer(), + CacheMode.MULTIPLE_FILE)); + } + + @Contribute(ResourceMinimizer.class) + @Primary + public static void setupDefaultResourceMinimizers(MappedConfiguration<String, ResourceMinimizer> configuration) + { + configuration.addInstance("text/css", CSSMinimizer.class); + configuration.addInstance("text/javascript", GoogleClosureMinimizer.class); + } + + /** + * Alas {@link AntlrException}s do not have a useful toString() which makes them useless in the exception report; + * here we provide an {@link ObjectRenderer} that breaks them apart into useful strings. Eventually we may be + * able to synthesize a {@link org.apache.tapestry5.ioc.Location} from them as well and show some of the source .less file. + */ + @Contribute(ObjectRenderer.class) + @Primary + public static void provideLessCompilerProblemRenderer(MappedConfiguration<Class, ObjectRenderer> configuration) + { + configuration.add(LessCompiler.Problem.class, new ObjectRenderer<LessCompiler.Problem>() + { + public void render(LessCompiler.Problem problem, MarkupWriter writer) + { + List<String> strings = CollectionFactory.newList(); + + if (InternalUtils.isNonBlank(problem.getMessage())) + { + strings.add(problem.getMessage()); + } + + // Inside WRO4J we see that the LessSource is a StringSource with no useful toString(), so + // it is omitted. We may need to create our own processors, stripping away a couple of layers of + // WRO4J to get proper exception reporting! + + if (problem.getLine() > 0) + { + strings.add("line " + problem.getLine()); + } + + if (problem.getCharacter() > 0) + { + strings.add("position " + problem.getCharacter()); + } + + writer.write(InternalUtils.join(strings, " - ")); + } + }); + } +}
