View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    * 
10   *  http://www.apache.org/licenses/LICENSE-2.0
11   * 
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   */
19  package org.apache.myfaces.trinidad.resource;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.ByteArrayOutputStream;
23  import java.io.File;
24  import java.io.IOException;
25  
26  import java.io.InputStream;
27  import java.net.URL;
28  
29  import java.net.UnknownServiceException;
30  import java.net.URLConnection;
31  import java.net.URLStreamHandler;
32  import java.util.Map;
33  import java.util.HashMap;
34  
35  
36  
37  /**
38   * Base class for resource loaders.  Resource loaders can lookup resources
39   * as URLs from arbitrary locations, including JAR files.
40   *
41   */
42  public class CachingResourceLoader extends ResourceLoader
43  {
44    /**
45     * Constructs a new CachingResourceLoader.
46     *
47     * @param parent  the parent resource loader
48     */
49    public CachingResourceLoader(
50      ResourceLoader parent)
51    {
52      super(parent);
53  
54      _cache = new HashMap<String, URL>();
55    }
56  
57    /**
58     * Returns the cached resource url if previously requested.  Otherwise,
59     * fully reads the resource contents stores in the cache.
60     *
61     * @param path  the resource path
62     *
63     * @return the cached resource url
64     *
65     * @throws java.io.IOException  if an I/O error occurs
66     */
67    @Override
68    protected URL findResource(
69      String path
70      ) throws IOException
71    {
72      URL url = _cache.get(path);
73  
74      if (url == null)
75      {
76        url = getParent().getResource(path);
77  
78        if (url != null)
79        {
80          url = new URL("cache", null, -1, path, new URLStreamHandlerImpl(url));
81          _cache.put(path, url);
82        }
83      }
84  
85      return url;
86    }
87  
88    private final Map<String, URL> _cache;
89  
90    /**
91     * URLStreamHandler to cache URL contents and URLConnection headers.
92     */
93    static private class URLStreamHandlerImpl extends URLStreamHandler
94    {
95      public URLStreamHandlerImpl(
96        URL delegate)
97      {
98        _delegate = delegate;
99      }
100 
101     @Override
102     protected URLConnection openConnection(
103       URL url
104       ) throws IOException
105     {
106       return new URLConnectionImpl(url, _delegate.openConnection(), this);
107     }
108 
109     protected InputStream getInputStream(
110       URLConnection conn) throws IOException
111     {
112       long lastModified = _getLastModified(_delegate);
113 
114       if (_contents == null || _contentsModified < lastModified)
115       {
116         InputStream in = conn.getInputStream();
117         ByteArrayOutputStream out = new ByteArrayOutputStream();
118         try
119         {
120           byte[] buffer = new byte[2048];
121           int length;
122           while ((length = (in.read(buffer))) >= 0)
123           {
124             out.write(buffer, 0, length);
125           }
126         }
127         finally
128         {
129           in.close();
130         }
131         _contents = out.toByteArray();
132         _contentsModified = conn.getLastModified();
133       }
134 
135       return new ByteArrayInputStream(_contents);
136     }
137 
138     private final URL    _delegate;
139     private       byte[] _contents;
140     private       long   _contentsModified;
141   }
142 
143   /**
144    * URLConnection to cache URL contents and header fields.
145    */
146   static private class URLConnectionImpl extends URLConnection
147   {
148     /**
149      * Creates a new URLConnectionImpl.
150      *
151      * @param url      the cached url
152      * @param handler  the caching stream handler
153      */
154     public URLConnectionImpl(
155       URL                  url,
156       URLConnection        conn,
157       URLStreamHandlerImpl handler)
158     {
159       super(url);
160       _conn = conn;
161       _handler = handler;
162     }
163 
164     @Override
165     public void connect() throws IOException
166     {
167       // cache: no-op
168     }
169 
170     @Override
171     public String getContentType()
172     {
173       return _conn.getContentType();
174     }
175 
176     @Override
177     public int getContentLength()
178     {
179       return _conn.getContentLength();
180     }
181 
182     @Override
183     public long getLastModified()
184     {
185       return _conn.getLastModified();
186     }
187 
188     @Override
189     public String getHeaderField(
190       String name)
191     {
192       return _conn.getHeaderField(name);
193     }
194 
195     @Override
196     public InputStream getInputStream() throws IOException
197     {
198       return _handler.getInputStream(_conn);
199     }
200 
201     private final URLConnection        _conn;
202     private final URLStreamHandlerImpl _handler;
203   }
204 
205   static private long _getLastModified(URL url) throws IOException
206   {
207     if ("file".equals(url.getProtocol()))
208     {
209       String externalForm = url.toExternalForm();
210       // Remove the "file:"
211       File file = new File(externalForm.substring(5));
212 
213       return file.lastModified();
214     }
215     else
216     {
217       URLConnection connection = url.openConnection();
218       long modified = connection.getLastModified();
219       try
220       {
221         InputStream is = connection.getInputStream();
222         if (is != null)
223           is.close();
224       }
225       catch (UnknownServiceException use)
226       {
227       }
228 
229       return modified;
230     }
231   }
232 }