Author: hlship
Date: Mon Apr 19 14:10:14 2010
New Revision: 935588
URL: http://svn.apache.org/viewvc?rev=935588&view=rev
Log:
Provide the missing asset request handler for the virtual "stack" folder.
Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java
(with props)
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestConstants.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StreamableResource.java
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java?rev=935588&r1=935587&r2=935588&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/InternalConstants.java
Mon Apr 19 14:10:14 2010
@@ -137,20 +137,24 @@ public final class InternalConstants
* @since 5.1.0.0
*/
public static final String GZIP_CONTENT_ENCODING = "gzip";
+
/**
* Identifies the start of an expansion inside a template.
*/
public static final String EXPANSION_START = "${";
+
/**
* Special prefix for parameters that are inherited from named parameters
of their container.
*/
public static final String INHERIT_BINDING_PREFIX = "inherit:";
+
public static final long TEN_YEARS = new
TimeInterval("10y").milliseconds();
public static final String[] EMPTY_STRING_ARRAY = new String[0];
/**
- * Name of the core {...@link JavascriptStack}.
+ * Name of the core {...@link JavascriptStack}, which supplies the basic
JavaScript infrastructure
+ * on the client.
*
* @since 5.2.0
*/
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestConstants.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestConstants.java?rev=935588&r1=935587&r2=935588&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestConstants.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/RequestConstants.java
Mon Apr 19 14:10:14 2010
@@ -38,14 +38,6 @@ public final class RequestConstants
public static final String CONTEXT_FOLDER = "ctx";
/**
- * Folder for virtual assets: combined JavaScript files. The file name is
actually a compressed bytestream
- * of the names of each file.
- *
- * @since 5.1.0.2
- */
- public static final String VIRTUAL_FOLDER = "virtual";
-
- /**
* Folder for combined {...@link JavascriptStack} JavaScript files. The
path consists of the locale (as a folder) and
* the name
* of the stack (suffixed with ".js").
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StreamableResource.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StreamableResource.java?rev=935588&r1=935587&r2=935588&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StreamableResource.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/StreamableResource.java
Mon Apr 19 14:10:14 2010
@@ -1,4 +1,4 @@
-// Copyright 2009 The Apache Software Foundation
+// Copyright 2009, 2010 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.
@@ -19,10 +19,11 @@ import java.io.InputStream;
/**
* Abstracts around a {...@link org.apache.tapestry5.ioc.Resource} to allow
access to the resource's content either
- * compressed on uncompressed. The advantage is that, for cmpressed streams,
the data is only compressed once, rather
+ * compressed on uncompressed. The advantage is that, for compressed streams,
the data is only compressed once, rather
* than for each request.
*
* @since 5.1.0.0
+ * @see ResourceCache
*/
public interface StreamableResource
{
Added:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java?rev=935588&view=auto
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java
(added)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java
Mon Apr 19 14:10:14 2010
@@ -0,0 +1,226 @@
+// Copyright 2010 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.internal.services.assets;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.zip.GZIPOutputStream;
+
+import org.apache.tapestry5.Asset;
+import org.apache.tapestry5.SymbolConstants;
+import org.apache.tapestry5.internal.InternalConstants;
+import org.apache.tapestry5.internal.TapestryInternalUtils;
+import org.apache.tapestry5.internal.services.ResourceCache;
+import org.apache.tapestry5.internal.services.StreamableResource;
+import org.apache.tapestry5.ioc.Resource;
+import org.apache.tapestry5.ioc.annotations.Symbol;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.json.JSONArray;
+import org.apache.tapestry5.services.InvalidationListener;
+import org.apache.tapestry5.services.LocalizationSetter;
+import org.apache.tapestry5.services.Request;
+import org.apache.tapestry5.services.Response;
+import org.apache.tapestry5.services.ResponseCompressionAnalyzer;
+import org.apache.tapestry5.services.assets.AssetRequestHandler;
+import org.apache.tapestry5.services.javascript.JavascriptStack;
+import org.apache.tapestry5.services.javascript.JavascriptStackSource;
+
+public class StackAssetRequestHandler implements AssetRequestHandler,
InvalidationListener
+{
+ private final ResourceCache resourceCache;
+
+ private final JavascriptStackSource javascriptStackSource;
+
+ private final LocalizationSetter localizationSetter;
+
+ private final ResponseCompressionAnalyzer compressionAnalyzer;
+
+ private final boolean productionMode;
+
+ private final Pattern pathPattern = Pattern.compile("^(.+)/(.+)\\.js$");
+
+ // Two caches, keyed on extra path. Both are accessed only from
synchronized blocks.
+ private final Map<String, ByteArrayOutputStream> uncompressedCache =
CollectionFactory.newCaseInsensitiveMap();
+
+ private final Map<String, ByteArrayOutputStream> compressedCache =
CollectionFactory.newCaseInsensitiveMap();
+
+ public StackAssetRequestHandler(ResourceCache resourceCache,
JavascriptStackSource javascriptStackSource,
+ LocalizationSetter localizationSetter, ResponseCompressionAnalyzer
compressionAnalyzer,
+
+ @Symbol(SymbolConstants.PRODUCTION_MODE)
+ boolean productionMode)
+ {
+ this.resourceCache = resourceCache;
+ this.javascriptStackSource = javascriptStackSource;
+ this.localizationSetter = localizationSetter;
+ this.compressionAnalyzer = compressionAnalyzer;
+ this.productionMode = productionMode;
+ }
+
+ public boolean handleAssetRequest(Request request, Response response,
String extraPath) throws IOException
+ {
+ boolean compress = compressionAnalyzer.isGZipSupported();
+
+ ByteArrayOutputStream stream = getStream(extraPath, compress);
+
+ // The whole point of this is to force the client to aggressively
cache the combined, virtual
+ // stack asset.
+
+ long lastModified = System.currentTimeMillis();
+ response.setDateHeader("Last-Modified", lastModified);
+
+ if (productionMode)
+ response.setDateHeader("Expires", lastModified +
InternalConstants.TEN_YEARS);
+
+ response.setContentLength(stream.size());
+
+ // Inform the upper layers that we are controlled compression here.
+ request.setAttribute(InternalConstants.SUPPRESS_COMPRESSION, true);
+
+ if (compress)
+ response.setHeader(InternalConstants.CONTENT_ENCODING_HEADER,
InternalConstants.GZIP_CONTENT_ENCODING);
+
+ // CSS support is problematic, because of relative URLs inside the CSS
files. For the
+ // moment, only JavaScript is supported.
+
+ OutputStream output = response.getOutputStream("text/javascript");
+
+ stream.writeTo(output);
+
+ output.close();
+
+ return true;
+ }
+
+ /** Notified by the {...@link ResourceCache} when resource files change;
the internal caches are cleared. */
+ public synchronized void objectWasInvalidated()
+ {
+ uncompressedCache.clear();
+ compressedCache.clear();
+ }
+
+ private ByteArrayOutputStream getStream(String extraPath, boolean
compressed) throws IOException
+ {
+ return compressed ? getCompressedStream(extraPath) :
getUncompressedStream(extraPath);
+ }
+
+ private synchronized ByteArrayOutputStream getCompressedStream(String
extraPath) throws IOException
+ {
+ ByteArrayOutputStream result = compressedCache.get(extraPath);
+
+ if (result == null)
+ {
+ ByteArrayOutputStream uncompressed =
getUncompressedStream(extraPath);
+ result = compressStream(uncompressed);
+ compressedCache.put(extraPath, result);
+ }
+
+ return result;
+ }
+
+ private synchronized ByteArrayOutputStream getUncompressedStream(String
extraPath) throws IOException
+ {
+ ByteArrayOutputStream result = uncompressedCache.get(extraPath);
+
+ if (result == null)
+ {
+ result = assembleStackContent(extraPath);
+ uncompressedCache.put(extraPath, result);
+ }
+
+ return result;
+ }
+
+ private ByteArrayOutputStream assembleStackContent(String extraPath)
throws IOException
+ {
+ Matcher matcher = pathPattern.matcher(extraPath);
+
+ if (!matcher.matches())
+ throw new RuntimeException("Invalid path for a stack asset
request.");
+
+ String localeName = matcher.group(1);
+ String stackName = matcher.group(2);
+
+ return assembleStackContent(localeName, stackName);
+ }
+
+ private ByteArrayOutputStream assembleStackContent(String localeName,
String stackName) throws IOException
+ {
+ localizationSetter.setNonPeristentLocaleFromLocaleName(localeName);
+
+ JavascriptStack stack = javascriptStackSource.getStack(stackName);
+ List<Asset> libraries = stack.getJavascriptLibraries();
+
+ return assembleStackContent(libraries);
+ }
+
+ private ByteArrayOutputStream assembleStackContent(List<Asset> libraries)
throws IOException
+ {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ OutputStreamWriter osw = new OutputStreamWriter(result, "UTF-8");
+ PrintWriter writer = new PrintWriter(osw, true);
+
+ JSONArray paths = new JSONArray();
+
+ for (Asset library : libraries)
+ {
+ String path = library.toClientURL();
+
+ paths.put(path);
+
+ writer.format("\n/* %s */;\n", path);
+
+ streamLibraryContent(library, result);
+ }
+
+ writer.format("\n;/**/\nTapestry.markScriptLibrariesLoaded(%s);\n",
paths);
+
+ writer.close();
+
+ return result;
+ }
+
+ private void streamLibraryContent(Asset library, OutputStream
outputStream) throws IOException
+ {
+ Resource resource = library.getResource();
+
+ StreamableResource streamable =
resourceCache.getStreamableResource(resource);
+
+ InputStream inputStream = streamable.getStream(false);
+
+ TapestryInternalUtils.copy(inputStream, outputStream);
+ }
+
+ private ByteArrayOutputStream compressStream(ByteArrayOutputStream
uncompressed) throws IOException
+ {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ OutputStream compressor = new GZIPOutputStream(result);
+
+ uncompressed.writeTo(compressor);
+
+ compressor.close();
+
+ return result;
+ }
+
+}
Propchange:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified:
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
URL:
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java?rev=935588&r1=935587&r2=935588&view=diff
==============================================================================
---
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
(original)
+++
tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/services/TapestryModule.java
Mon Apr 19 14:10:14 2010
@@ -80,6 +80,7 @@ import org.apache.tapestry5.internal.ser
import org.apache.tapestry5.internal.services.assets.AssetPathConstructorImpl;
import
org.apache.tapestry5.internal.services.assets.ClasspathAssetRequestHandler;
import
org.apache.tapestry5.internal.services.assets.ContextAssetRequestHandler;
+import org.apache.tapestry5.internal.services.assets.StackAssetRequestHandler;
import org.apache.tapestry5.internal.services.javascript.CoreJavascriptStack;
import
org.apache.tapestry5.internal.services.javascript.JavascriptStackPathConstructor;
import
org.apache.tapestry5.internal.services.javascript.JavascriptStackSourceImpl;
@@ -476,9 +477,16 @@ public final class TapestryModule
@ContextProvider
AssetFactory contextAssetFactory,
+ @Autobuild
+ StackAssetRequestHandler stackAssetRequestHandler,
+
+ ResourceCache resourceCache,
+
ClasspathAssetAliasManager classpathAssetAliasManager, ResourceStreamer
streamer,
AssetResourceLocator assetResourceLocator)
{
+ resourceCache.addInvalidationListener(stackAssetRequestHandler);
+
Map<String, String> mappings =
classpathAssetAliasManager.getMappings();
for (String folder : mappings.keySet())
@@ -490,6 +498,9 @@ public final class TapestryModule
configuration.add(RequestConstants.CONTEXT_FOLDER, new
ContextAssetRequestHandler(streamer, contextAssetFactory
.getRootResource()));
+
+ configuration.add(RequestConstants.STACK_FOLDER,
stackAssetRequestHandler);
+
}
private static String toPackagePath(String packageName)