You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2010/04/28 04:44:18 UTC

svn commit: r938755 - in /tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets: BytestreamCache.java StackAssetRequestHandler.java

Author: hlship
Date: Wed Apr 28 02:44:18 2010
New Revision: 938755

URL: http://svn.apache.org/viewvc?rev=938755&view=rev
Log:
TAP5-1116: Live lock when using compressed and virtual assets due to ByteArrayOutputStream being synchronized

Added:
    tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/BytestreamCache.java   (with props)
Modified:
    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/BytestreamCache.java
URL: http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/BytestreamCache.java?rev=938755&view=auto
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/BytestreamCache.java (added)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/BytestreamCache.java Wed Apr 28 02:44:18 2010
@@ -0,0 +1,51 @@
+// 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.OutputStream;
+
+/**
+ * A wrapper around a byte-stream, represented internally as a byte array. Part of the fix
+ * to TAP5-1116, avoiding a live lock due to ByteArrayOutputStream.writeTo() being a synchronized
+ * method.
+ * 
+ * @since 5.2.0
+ */
+public class BytestreamCache
+{
+    private final byte[] streamData;
+
+    public BytestreamCache(byte[] streamData)
+    {
+        this.streamData = streamData;
+    }
+
+    public BytestreamCache(ByteArrayOutputStream os)
+    {
+        this(os.toByteArray());
+    }
+
+    public void writeTo(OutputStream os) throws IOException
+    {
+        os.write(streamData, 0, streamData.length);
+    }
+
+    public int size()
+    {
+        return streamData.length;
+    }
+}

Propchange: tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/BytestreamCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 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=938755&r1=938754&r2=938755&view=diff
==============================================================================
--- tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java (original)
+++ tapestry/tapestry5/trunk/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/assets/StackAssetRequestHandler.java Wed Apr 28 02:44:18 2010
@@ -60,9 +60,9 @@ public class StackAssetRequestHandler im
     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, BytestreamCache> uncompressedCache = CollectionFactory.newCaseInsensitiveMap();
 
-    private final Map<String, ByteArrayOutputStream> compressedCache = CollectionFactory.newCaseInsensitiveMap();
+    private final Map<String, BytestreamCache> compressedCache = CollectionFactory.newCaseInsensitiveMap();
 
     public StackAssetRequestHandler(ResourceCache resourceCache, JavascriptStackSource javascriptStackSource,
             LocalizationSetter localizationSetter, ResponseCompressionAnalyzer compressionAnalyzer,
@@ -81,7 +81,7 @@ public class StackAssetRequestHandler im
     {
         boolean compress = compressionAnalyzer.isGZipSupported();
 
-        ByteArrayOutputStream stream = getStream(extraPath, compress);
+        BytestreamCache cachedStream = getStream(extraPath, compress);
 
         // The whole point of this is to force the client to aggressively cache the combined, virtual
         // stack asset.
@@ -92,7 +92,7 @@ public class StackAssetRequestHandler im
         if (productionMode)
             response.setDateHeader("Expires", lastModified + InternalConstants.TEN_YEARS);
 
-        response.setContentLength(stream.size());
+        response.setContentLength(cachedStream.size());
 
         // Inform the upper layers that we are controlled compression here.
         request.setAttribute(InternalConstants.SUPPRESS_COMPRESSION, true);
@@ -105,7 +105,7 @@ public class StackAssetRequestHandler im
 
         OutputStream output = response.getOutputStream("text/javascript");
 
-        stream.writeTo(output);
+        cachedStream.writeTo(output);
 
         output.close();
 
@@ -119,18 +119,18 @@ public class StackAssetRequestHandler im
         compressedCache.clear();
     }
 
-    private ByteArrayOutputStream getStream(String extraPath, boolean compressed) throws IOException
+    private BytestreamCache getStream(String extraPath, boolean compressed) throws IOException
     {
         return compressed ? getCompressedStream(extraPath) : getUncompressedStream(extraPath);
     }
 
-    private synchronized ByteArrayOutputStream getCompressedStream(String extraPath) throws IOException
+    private synchronized BytestreamCache getCompressedStream(String extraPath) throws IOException
     {
-        ByteArrayOutputStream result = compressedCache.get(extraPath);
+        BytestreamCache result = compressedCache.get(extraPath);
 
         if (result == null)
         {
-            ByteArrayOutputStream uncompressed = getUncompressedStream(extraPath);
+            BytestreamCache uncompressed = getUncompressedStream(extraPath);
             result = compressStream(uncompressed);
             compressedCache.put(extraPath, result);
         }
@@ -138,9 +138,9 @@ public class StackAssetRequestHandler im
         return result;
     }
 
-    private synchronized ByteArrayOutputStream getUncompressedStream(String extraPath) throws IOException
+    private synchronized BytestreamCache getUncompressedStream(String extraPath) throws IOException
     {
-        ByteArrayOutputStream result = uncompressedCache.get(extraPath);
+        BytestreamCache result = uncompressedCache.get(extraPath);
 
         if (result == null)
         {
@@ -151,7 +151,7 @@ public class StackAssetRequestHandler im
         return result;
     }
 
-    private ByteArrayOutputStream assembleStackContent(String extraPath) throws IOException
+    private BytestreamCache assembleStackContent(String extraPath) throws IOException
     {
         Matcher matcher = pathPattern.matcher(extraPath);
 
@@ -164,7 +164,7 @@ public class StackAssetRequestHandler im
         return assembleStackContent(localeName, stackName);
     }
 
-    private ByteArrayOutputStream assembleStackContent(String localeName, String stackName) throws IOException
+    private BytestreamCache assembleStackContent(String localeName, String stackName) throws IOException
     {
         localizationSetter.setNonPeristentLocaleFromLocaleName(localeName);
 
@@ -174,10 +174,10 @@ public class StackAssetRequestHandler im
         return assembleStackContent(libraries);
     }
 
-    private ByteArrayOutputStream assembleStackContent(List<Asset> libraries) throws IOException
+    private BytestreamCache assembleStackContent(List<Asset> libraries) throws IOException
     {
-        ByteArrayOutputStream result = new ByteArrayOutputStream();
-        OutputStreamWriter osw = new OutputStreamWriter(result, "UTF-8");
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        OutputStreamWriter osw = new OutputStreamWriter(stream, "UTF-8");
         PrintWriter writer = new PrintWriter(osw, true);
 
         JSONArray paths = new JSONArray();
@@ -190,14 +190,14 @@ public class StackAssetRequestHandler im
 
             writer.format("\n/* %s */;\n", path);
 
-            streamLibraryContent(library, result);
+            streamLibraryContent(library, stream);
         }
 
         writer.format("\n;/**/\nTapestry.markScriptLibrariesLoaded(%s);\n", paths);
 
         writer.close();
 
-        return result;
+        return new BytestreamCache(stream);
     }
 
     private void streamLibraryContent(Asset library, OutputStream outputStream) throws IOException
@@ -211,16 +211,16 @@ public class StackAssetRequestHandler im
         TapestryInternalUtils.copy(inputStream, outputStream);
     }
 
-    private ByteArrayOutputStream compressStream(ByteArrayOutputStream uncompressed) throws IOException
+    private BytestreamCache compressStream(BytestreamCache uncompressed) throws IOException
     {
-        ByteArrayOutputStream result = new ByteArrayOutputStream();
-        OutputStream compressor = new GZIPOutputStream(result);
+        ByteArrayOutputStream compressed = new ByteArrayOutputStream();
+        OutputStream compressor = new GZIPOutputStream(compressed);
 
         uncompressed.writeTo(compressor);
 
         compressor.close();
 
-        return result;
+        return new BytestreamCache(compressed);
     }
 
 }