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.IOException;
24  import java.io.InputStream;
25  import java.net.URL;
26  import java.net.URLConnection;
27  import java.net.URLStreamHandler;
28  import java.util.concurrent.ConcurrentHashMap;
29  import java.util.concurrent.ConcurrentMap;
30  
31  import org.apache.myfaces.trinidad.util.URLUtils;
32  
33  /**
34   * Base class for resource loaders.  Resource loaders can lookup resources
35   * as URLs from arbitrary locations, including JAR files.
36   *
37   */
38  public class CachingResourceLoader extends ResourceLoader
39  {
40    /**
41     * Constructs a new CachingResourceLoader.
42     *
43     * @param parent  the parent resource loader
44     */
45    public CachingResourceLoader(
46      ResourceLoader parent)
47    {
48      super(parent);
49  
50      _cache = new ConcurrentHashMap<String, URL>();
51    }
52  
53    
54  
55    /**
56     * Returns the cached resource url if previously requested.  Otherwise,
57     * fully reads the resource contents stores in the cache.
58     *
59     * @param path  the resource path
60     *
61     * @return the cached resource url
62     *
63     * @throws java.io.IOException  if an I/O error occurs
64     */
65    @Override
66    protected URL findResource(
67      String path
68      ) throws IOException
69    {
70      URL url = _cache.get(path);
71  
72      if (url == null)
73      {
74        url = getParent().getResource(path);
75  
76        if (url != null)
77        {
78          url = new URL("cache", null, -1, path, new URLStreamHandlerImpl(url));
79          _cache.putIfAbsent(path, url);
80        }
81      }
82  
83      return url;
84    }
85  
86    private final ConcurrentMap<String, URL> _cache;
87  
88    @Override
89    public boolean isCachable()
90    {
91      return false;
92    }
93  
94    /**
95     * URLStreamHandler to cache URL contents and URLConnection headers.
96     */
97    static private class URLStreamHandlerImpl extends URLStreamHandler
98    {
99      public URLStreamHandlerImpl(
100       URL delegate)
101     {
102       _delegate = delegate;
103     }
104 
105     @Override
106     protected URLConnection openConnection(
107       URL url
108       ) throws IOException
109     {
110       return new URLConnectionImpl(url, _delegate.openConnection(), this);
111     }
112 
113     protected InputStream getInputStream(
114       URLConnection conn) throws IOException
115     {
116       long lastModified = URLUtils.getLastModified(_delegate);
117 
118       if (_contents == null || _contentsModified < lastModified)
119       {
120         InputStream in = conn.getInputStream();
121         ByteArrayOutputStream out = new ByteArrayOutputStream();
122         try
123         {
124           byte[] buffer = new byte[2048];
125           int length;
126           while ((length = (in.read(buffer))) >= 0)
127           {
128             out.write(buffer, 0, length);
129           }
130         }
131         finally
132         {
133           in.close();
134         }
135         _contents = out.toByteArray();
136         _contentsModified = URLUtils.getLastModified(conn);
137       }
138 
139       return new ByteArrayInputStream(_contents);
140     }
141 
142     private final URL    _delegate;
143     private       byte[] _contents;
144     private       long   _contentsModified;
145   }
146 
147   /**
148    * URLConnection to cache URL contents and header fields.
149    */
150   static private class URLConnectionImpl extends URLConnection
151   {
152     /**
153      * Creates a new URLConnectionImpl.
154      *
155      * @param url      the cached url
156      * @param handler  the caching stream handler
157      */
158     public URLConnectionImpl(
159       URL                  url,
160       URLConnection        conn,
161       URLStreamHandlerImpl handler)
162     {
163       super(url);
164       _conn = conn;
165       _handler = handler;
166     }
167 
168     @Override
169     public void connect() throws IOException
170     {
171       // cache: no-op
172     }
173 
174     @Override
175     public String getContentType()
176     {
177       return _conn.getContentType();
178     }
179 
180     @Override
181     public int getContentLength()
182     {
183       return _conn.getContentLength();
184     }
185 
186     @Override
187     public long getLastModified()
188     {
189       try
190       {
191         return URLUtils.getLastModified(_conn);
192       }
193       catch (IOException exception)
194       {
195         return -1;
196       }
197     }
198 
199     @Override
200     public String getHeaderField(
201       String name)
202     {
203       return _conn.getHeaderField(name);
204     }
205 
206     @Override
207     public InputStream getInputStream() throws IOException
208     {
209       return _handler.getInputStream(_conn);
210     }
211 
212     private final URLConnection        _conn;
213     private final URLStreamHandlerImpl _handler;
214   }
215 
216 }