Author: mgrigorov
Date: Thu Dec  2 20:04:35 2010
New Revision: 1041559

URL: http://svn.apache.org/viewvc?rev=1041559&view=rev
Log:
WICKET-3176 URLResourceStream loads target content twice.

Make URLResourceStream lazy - it will make the connection to the resource on 
first read and will cache the results.
A new connection is being made only for refreshing the last modification time.

Modified:
    
wicket/trunk/wicket/src/main/java/org/apache/wicket/util/resource/UrlResourceStream.java
    
wicket/trunk/wicket/src/test/java/org/apache/wicket/util/resource/UrlResourceStreamTest.java

Modified: 
wicket/trunk/wicket/src/main/java/org/apache/wicket/util/resource/UrlResourceStream.java
URL: 
http://svn.apache.org/viewvc/wicket/trunk/wicket/src/main/java/org/apache/wicket/util/resource/UrlResourceStream.java?rev=1041559&r1=1041558&r2=1041559&view=diff
==============================================================================
--- 
wicket/trunk/wicket/src/main/java/org/apache/wicket/util/resource/UrlResourceStream.java
 (original)
+++ 
wicket/trunk/wicket/src/main/java/org/apache/wicket/util/resource/UrlResourceStream.java
 Thu Dec  2 20:04:35 2010
@@ -49,8 +49,10 @@ public class UrlResourceStream extends A
        /** Logging. */
        private static final Logger log = 
LoggerFactory.getLogger(UrlResourceStream.class);
 
-       /** Resource stream. */
-       private transient InputStream inputStream;
+       /**
+        * The meta data for this stream. Lazy loaded on demand.
+        */
+       private transient StreamData streamData;
 
        /** The URL to this resource. */
        private final URL url;
@@ -58,14 +60,23 @@ public class UrlResourceStream extends A
        /** the handle to the file if it is a file resource */
        private File file;
 
-       /** Length of stream. */
-       private Bytes contentLength;
+       /**
+        * Meta data class for the stream attributes
+        */
+       private static class StreamData
+       {
+               private URLConnection connection;
+
+               /** Length of stream. */
+               private long contentLength;
 
-       /** Content type for stream. */
-       private String contentType;
+               /** Content type for stream. */
+               private String contentType;
 
-       /** Last known time the stream was last modified. */
-       private long lastModified;
+               /** Last known time the stream was last modified. */
+               private long lastModified;
+
+       }
 
        /**
         * Construct.
@@ -78,23 +89,6 @@ public class UrlResourceStream extends A
                // save the url
                this.url = url;
 
-               // retrieve the content type and length
-               URLConnection connection = null;
-               try
-               {
-                       connection = url.openConnection();
-                       contentLength = 
Bytes.bytes(connection.getContentLength());
-                       contentType = connection.getContentType();
-               }
-               catch (IOException ex)
-               {
-                       throw new IllegalArgumentException("Invalid URL 
parameter " + url, ex);
-               }
-               finally
-               {
-                       Connections.closeQuietly(connection);
-               }
-
                try
                {
                        file = new File(new URI(url.toExternalForm()));
@@ -113,16 +107,69 @@ public class UrlResourceStream extends A
        }
 
        /**
+        * Lazy loads the stream settings on demand
+        * 
+        * @param initialize
+        *            a flag indicating whether to load the settings
+        * @return the meta data with the stream settings
+        */
+       private StreamData getData(boolean initialize)
+       {
+               if (streamData == null && initialize)
+               {
+                       streamData = new StreamData();
+
+                       try
+                       {
+                               streamData.connection = url.openConnection();
+                               streamData.contentLength = 
streamData.connection.getContentLength();
+                               streamData.contentType = 
streamData.connection.getContentType();
+
+                               if (streamData.contentType == null ||
+                                       
streamData.contentType.indexOf("unknown") != -1)
+                               {
+                                       if (Application.exists() && 
Application.get() instanceof WebApplication)
+                                       {
+                                               // TODO Post 1.2: General: For 
non webapplication another method
+                                               // should be implemented 
(getMimeType on application?)
+                                               streamData.contentType = 
WebApplication.get()
+                                                       .getServletContext()
+                                                       
.getMimeType(url.getFile());
+                                               if (streamData.contentType == 
null)
+                                               {
+                                                       streamData.contentType 
= URLConnection.getFileNameMap()
+                                                               
.getContentTypeFor(url.getFile());
+                                               }
+                                       }
+                                       else
+                                       {
+                                               streamData.contentType = 
URLConnection.getFileNameMap().getContentTypeFor(
+                                                       url.getFile());
+                                       }
+                               }
+                       }
+                       catch (IOException ex)
+                       {
+                               throw new IllegalArgumentException("Invalid URL 
parameter " + url, ex);
+                       }
+               }
+
+               return streamData;
+       }
+
+       /**
         * Closes this resource.
         * 
         * @throws IOException
         */
        public void close() throws IOException
        {
-               if (inputStream != null)
+               StreamData data = getData(false);
+
+               if (data != null)
                {
-                       inputStream.close();
-                       inputStream = null;
+                       Connections.closeQuietly(data.connection);
+                       data.connection = null;
                }
        }
 
@@ -132,35 +179,7 @@ public class UrlResourceStream extends A
        @Override
        public String getContentType()
        {
-               testContentType();
-               return contentType;
-       }
-
-       /**
-        * Method to test the content type on null or unknown. if this is the 
case the content type is
-        * tried to be resolved throw the servlet context
-        */
-       private void testContentType()
-       {
-               if (contentType == null || contentType.contains("unknown"))
-               {
-                       Application application = Application.get();
-                       if (application instanceof WebApplication)
-                       {
-                               // TODO Post 1.2: General: For non 
webapplication another method
-                               // should be implemented (getMimeType on 
application?)
-                               contentType = 
((WebApplication)application).getServletContext().getMimeType(
-                                       url.getFile());
-                               if (contentType == null)
-                               {
-                                       contentType = 
URLConnection.getFileNameMap().getContentTypeFor(url.getFile());
-                               }
-                       }
-                       else
-                       {
-                               contentType = 
URLConnection.getFileNameMap().getContentTypeFor(url.getFile());
-                       }
-               }
+               return getData(true).contentType;
        }
 
        /**
@@ -169,19 +188,15 @@ public class UrlResourceStream extends A
         */
        public InputStream getInputStream() throws 
ResourceStreamNotFoundException
        {
-               if (inputStream == null)
+               InputStream inputStream;
+               try
                {
-                       try
-                       {
-                               inputStream = url.openStream();
-                       }
-                       catch (IOException e)
-                       {
-                               throw new 
ResourceStreamNotFoundException("Resource " + url +
-                                       " could not be opened", e);
-                       }
+                       inputStream = getData(true).connection.getInputStream();
+               }
+               catch (IOException e)
+               {
+                       throw new ResourceStreamNotFoundException("Resource " + 
url + " could not be opened", e);
                }
-
                return inputStream;
        }
 
@@ -202,6 +217,8 @@ public class UrlResourceStream extends A
        {
                try
                {
+                       StreamData data = getData(true);
+
                        if (file != null)
                        {
                                // in case the file has been removed by now
@@ -213,9 +230,9 @@ public class UrlResourceStream extends A
                                long lastModified = file.lastModified();
 
                                // if last modified changed update content 
length and last modified date
-                               if (lastModified != this.lastModified)
+                               if (lastModified != data.lastModified)
                                {
-                                       this.lastModified = lastModified;
+                                       data.lastModified = lastModified;
                                        setContentLength();
                                }
                        }
@@ -224,14 +241,14 @@ public class UrlResourceStream extends A
                                long lastModified = 
Connections.getLastModified(url);
 
                                // if last modified changed update content 
length and last modified date
-                               if (lastModified != this.lastModified)
+                               if (lastModified != data.lastModified)
                                {
-                                       this.lastModified = lastModified;
+                                       data.lastModified = lastModified;
 
                                        setContentLength();
                                }
                        }
-                       return Time.milliseconds(lastModified);
+                       return Time.milliseconds(data.lastModified);
                }
                catch (IOException e)
                {
@@ -254,8 +271,9 @@ public class UrlResourceStream extends A
 
        private void setContentLength() throws IOException
        {
+               StreamData data = getData(true);
                URLConnection connection = url.openConnection();
-               contentLength = Bytes.bytes(connection.getContentLength());
+               data.contentLength = connection.getContentLength();
                Connections.close(connection);
        }
 
@@ -274,7 +292,8 @@ public class UrlResourceStream extends A
        @Override
        public Bytes length()
        {
-               return contentLength;
+               long contentLength = getData(true).contentLength;
+               return Bytes.bytes(contentLength);
        }
 
        /**

Modified: 
wicket/trunk/wicket/src/test/java/org/apache/wicket/util/resource/UrlResourceStreamTest.java
URL: 
http://svn.apache.org/viewvc/wicket/trunk/wicket/src/test/java/org/apache/wicket/util/resource/UrlResourceStreamTest.java?rev=1041559&r1=1041558&r2=1041559&view=diff
==============================================================================
--- 
wicket/trunk/wicket/src/test/java/org/apache/wicket/util/resource/UrlResourceStreamTest.java
 (original)
+++ 
wicket/trunk/wicket/src/test/java/org/apache/wicket/util/resource/UrlResourceStreamTest.java
 Thu Dec  2 20:04:35 2010
@@ -18,6 +18,9 @@ package org.apache.wicket.util.resource;
 
 import java.io.IOException;
 import java.net.URL;
+import java.net.URLConnection;
+import java.net.URLStreamHandler;
+import java.util.concurrent.atomic.AtomicInteger;
 
 import junit.framework.TestCase;
 
@@ -45,4 +48,59 @@ public class UrlResourceStreamTest exten
                stream.close();
        }
 
+       /**
+        * https://issues.apache.org/jira/browse/WICKET-3176
+        * 
+        * @throws IOException
+        * @throws ResourceStreamNotFoundException
+        */
+       public void testLoadJustOnce() throws IOException, 
ResourceStreamNotFoundException
+       {
+               String anyClassInJarFile = "/java/lang/String.class";
+               URL realURL = getClass().getResource(anyClassInJarFile);
+
+               final AtomicInteger counter = new AtomicInteger(0);
+               URL url = new URL(null, "test://anything", new 
CountingURLStreamHandler(realURL, counter));
+
+               UrlResourceStream countingStream = new UrlResourceStream(url);
+               // assert the call is not made yet
+               assertEquals(0, counter.get());
+               countingStream.length();
+               // assert the connection is loaded lazily
+               assertEquals(1, counter.get());
+
+               // assert the following calls do not make new connections
+               countingStream.getInputStream();
+               assertEquals(1, counter.get());
+               countingStream.getContentType();
+               assertEquals(1, counter.get());
+               countingStream.getInputStream();
+               assertEquals(1, counter.get());
+               countingStream.close();
+               assertEquals(1, counter.get());
+       }
+
+       /**
+        * {...@link URLStreamHandler} that counts the calls to {...@link 
URL#openConnection()}
+        */
+       private static final class CountingURLStreamHandler extends 
URLStreamHandler
+       {
+               private final AtomicInteger counter;
+
+               private final URL realURL;
+
+               public CountingURLStreamHandler(URL realURL, AtomicInteger 
counter)
+               {
+                       this.counter = counter;
+                       this.realURL = realURL;
+               }
+
+               @Override
+               protected URLConnection openConnection(URL u) throws IOException
+               {
+                       counter.getAndIncrement();
+                       return realURL.openConnection();
+               }
+
+       }
 }


Reply via email to