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.shared.resource;
20  
21  import java.util.logging.Level;
22  import java.util.logging.Logger;
23  
24  import javax.faces.application.ProjectStage;
25  import javax.faces.context.ExternalContext;
26  import javax.faces.context.FacesContext;
27  
28  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
29  import org.apache.myfaces.shared.util.ConcurrentLRUCache;
30  import org.apache.myfaces.shared.util.WebConfigParamUtils;
31  
32  public class ResourceHandlerCache
33  {
34      private static final Logger log = Logger
35              .getLogger(ResourceHandlerCache.class.getName());
36  
37      private Boolean _resourceCacheEnabled = null;
38      private volatile ConcurrentLRUCache<Object, ResourceValue> _resourceCacheMap = null;
39  
40      private volatile ConcurrentLRUCache<Object, ResourceValue> _viewResourceCacheMap = null;
41      
42      /**
43       * Controls the size of the cache used to check if a resource exists or not. 
44       * 
45       * <p>See org.apache.myfaces.RESOURCE_HANDLER_CACHE_ENABLED for details.</p>
46       */
47      @JSFWebConfigParam(defaultValue = "500", since = "2.0.2", group="resources", 
48              classType="java.lang.Integer", tags="performance")
49      private static final String RESOURCE_HANDLER_CACHE_SIZE_ATTRIBUTE = 
50          "org.apache.myfaces.RESOURCE_HANDLER_CACHE_SIZE";
51      private static final int RESOURCE_HANDLER_CACHE_DEFAULT_SIZE = 500;
52  
53      /**
54       * Enable or disable the cache used to "remember" if a resource handled by 
55       * the default ResourceHandler exists or not.
56       * 
57       */
58      @JSFWebConfigParam(defaultValue = "true", since = "2.0.2", group="resources", 
59              expectedValues="true,false", tags="performance")
60      private static final String RESOURCE_HANDLER_CACHE_ENABLED_ATTRIBUTE = 
61          "org.apache.myfaces.RESOURCE_HANDLER_CACHE_ENABLED";
62      private static final boolean RESOURCE_HANDLER_CACHE_ENABLED_DEFAULT = true;
63  
64      public ResourceValue getResource(String resourceName, String libraryName,
65              String contentType, String localePrefix)
66      {
67          return getResource(resourceName, libraryName, contentType, localePrefix, null);
68      }
69      
70      public ResourceValue getResource(String resourceName, String libraryName,
71              String contentType, String localePrefix, String contractName)
72      {
73          if (!isResourceCachingEnabled() || _resourceCacheMap == null)
74          {
75              return null;
76          }
77  
78          if (log.isLoggable(Level.FINE))
79          {
80              log.log(Level.FINE, "Attemping to get resource from cache for "
81                      + resourceName);
82          }
83  
84          ResourceKey key = new ResourceKey(resourceName, libraryName, contentType, localePrefix, contractName);
85  
86          return _resourceCacheMap.get(key);
87      }    
88  
89      public boolean containsResource(String resourceName, String libraryName, String contentType, String localePrefix)
90      {
91          return containsResource(resourceName, libraryName, contentType, localePrefix, null);
92      }
93      
94      public boolean containsResource(String resourceName, String libraryName, String contentType, 
95          String localePrefix, String contractName)
96      {
97          if (!isResourceCachingEnabled() || _resourceCacheMap == null)
98          {
99              return false;
100         }
101 
102         ResourceKey key = new ResourceKey(resourceName, libraryName, contentType, localePrefix);
103         return _resourceCacheMap.get(key) != null;
104     }
105 
106     public void putResource(String resourceName, String libraryName,
107             String contentType, String localePrefix, ResourceMeta resource, ResourceLoader loader)
108     {
109         putResource(resourceName, libraryName, contentType, localePrefix, null, resource, loader, null);
110     }
111     
112     public void putResource(String resourceName, String libraryName,
113             String contentType, String localePrefix, String contractName, ResourceMeta resource, ResourceLoader loader,
114             ResourceCachedInfo info)
115     {
116         if (!isResourceCachingEnabled())
117         {
118             return;
119         }
120 
121         if (log.isLoggable(Level.FINE))
122         {
123             log.log(Level.FINE, "Attemping to put resource to cache for "
124                     + resourceName);
125         }
126 
127         if (_resourceCacheMap == null)
128         {
129             if (log.isLoggable(Level.FINE))
130             {
131                 log.log(Level.FINE, "Initializing resource cache map");
132             }
133             int maxSize = getMaxSize();
134             _resourceCacheMap = new ConcurrentLRUCache<Object, ResourceValue>(
135                     (maxSize * 4 + 3) / 3, maxSize);
136         }
137 
138         _resourceCacheMap.put(new ResourceKey(resourceName, libraryName,
139                 contentType, localePrefix, contractName), new ResourceValue(resource, loader, info));
140     }
141     
142     public ResourceValue getResource(String resourceId)
143     {
144         if (!isResourceCachingEnabled() || _resourceCacheMap == null)
145         {
146             return null;
147         }
148 
149         if (log.isLoggable(Level.FINE))
150         {
151             log.log(Level.FINE, "Attemping to get resource from cache for "
152                     + resourceId);
153         }
154 
155         return _resourceCacheMap.get(resourceId);
156     }
157 
158     public ResourceValue getResource(String resourceId, String contractName)
159     {
160         if (!isResourceCachingEnabled() || _resourceCacheMap == null)
161         {
162             return null;
163         }
164 
165         if (log.isLoggable(Level.FINE))
166         {
167             log.log(Level.FINE, "Attemping to get resource from cache for "
168                     + resourceId);
169         }
170 
171         return _resourceCacheMap.get(contractName+':'+resourceId);
172     }
173     
174     public boolean containsResource(String resourceId, String contractName)
175     {
176         if (!isResourceCachingEnabled() || _resourceCacheMap == null)
177         {
178             return false;
179         }
180 
181         return _resourceCacheMap.get(contractName+':'+resourceId) != null;
182     }
183     
184     public boolean containsResource(String resourceId)
185     {
186         if (!isResourceCachingEnabled() || _resourceCacheMap == null)
187         {
188             return false;
189         }
190 
191         return _resourceCacheMap.get(resourceId) != null;
192     }
193 
194     public void putResource(String resourceId, ResourceMeta resource, ResourceLoader loader, 
195         ResourceCachedInfo info)
196     {
197         if (!isResourceCachingEnabled())
198         {
199             return;
200         }
201 
202         if (log.isLoggable(Level.FINE))
203         {
204             log.log(Level.FINE, "Attemping to put resource to cache for "
205                     + resourceId);
206         }
207 
208         if (_resourceCacheMap == null)
209         {
210             if (log.isLoggable(Level.FINE))
211             {
212                 log.log(Level.FINE, "Initializing resource cache map");
213             }
214             int maxSize = getMaxSize();
215             _resourceCacheMap = new ConcurrentLRUCache<Object, ResourceValue>(
216                     (maxSize * 4 + 3) / 3, maxSize);
217         }
218 
219         if (resource.getContractName() != null)
220         {
221             _resourceCacheMap.put(resource.getContractName()+':'+resourceId, 
222                 new ResourceValue(resource, loader));
223         }
224         else
225         {
226             _resourceCacheMap.put(resourceId, new ResourceValue(resource, loader, info));
227         }
228     }
229 
230     public boolean containsViewResource(
231         String resourceName, String contentType, String localePrefix)
232     {
233         return containsViewResource(resourceName, contentType, localePrefix, null);
234     }
235     
236     public boolean containsViewResource(String resourceName, String contentType, 
237         String localePrefix, String contractName)
238     {
239         if (!isResourceCachingEnabled() || _viewResourceCacheMap == null)
240         {
241             return false;
242         }
243 
244         ResourceKey key = new ResourceKey(resourceName, null, contentType, localePrefix, contractName);
245         return _viewResourceCacheMap.get(key) != null;
246     }
247     
248     public ResourceValue getViewResource(String resourceName,
249             String contentType, String localePrefix)
250     {
251         return getViewResource(resourceName, contentType, localePrefix, null);
252     }
253     
254     public ResourceValue getViewResource(String resourceName,
255             String contentType, String localePrefix, String contractName)
256     {
257         if (!isResourceCachingEnabled() || _viewResourceCacheMap == null)
258         {
259             return null;
260         }
261 
262         if (log.isLoggable(Level.FINE))
263         {
264             log.log(Level.FINE, "Attemping to get resource from cache for "
265                     + resourceName);
266         }
267 
268         ResourceKey key = new ResourceKey(resourceName, null, contentType, localePrefix, contractName);
269 
270         return _viewResourceCacheMap.get(key);
271     }
272     
273     public void putViewResource(String resourceName, String contentType, 
274         String localePrefix, ResourceMeta resource, ResourceLoader loader, ResourceCachedInfo info)
275     {
276         putViewResource(resourceName, contentType, localePrefix, null, resource, loader, info);
277     }
278     
279     public void putViewResource(String resourceName, String contentType, 
280         String localePrefix, String contractName, ResourceMeta resource, ResourceLoader loader,
281         ResourceCachedInfo info)
282     {
283         if (!isResourceCachingEnabled())
284         {
285             return;
286         }
287 
288         if (log.isLoggable(Level.FINE))
289         {
290             log.log(Level.FINE, "Attemping to put resource to cache for "
291                     + resourceName);
292         }
293 
294         if (_viewResourceCacheMap == null)
295         {
296             if (log.isLoggable(Level.FINE))
297             {
298                 log.log(Level.FINE, "Initializing resource cache map");
299             }
300             int maxSize = getMaxSize();
301             _viewResourceCacheMap = new ConcurrentLRUCache<Object, ResourceValue>(
302                     (maxSize * 4 + 3) / 3, maxSize);
303         }
304 
305         _viewResourceCacheMap.put(new ResourceKey(resourceName, null,
306                 contentType, localePrefix, contractName), new ResourceValue(resource, loader, info));
307     }
308 
309     private boolean isResourceCachingEnabled()
310     {
311         if (_resourceCacheEnabled == null)
312         {
313             FacesContext facesContext = FacesContext.getCurrentInstance();
314 
315             //first, check to make sure that ProjectStage is production, if not, skip caching
316             if (!facesContext.isProjectStage(ProjectStage.Production))
317             {
318                 _resourceCacheEnabled = Boolean.FALSE;
319                 return _resourceCacheEnabled;
320             }
321 
322             ExternalContext externalContext = facesContext.getExternalContext();
323             if (externalContext == null)
324             {
325                 return false; //don't cache right now, but don't disable it yet either
326             }
327 
328             //if in production, make sure that the cache is not explicitly disabled via context param
329             _resourceCacheEnabled = WebConfigParamUtils.getBooleanInitParameter(externalContext, 
330                     ResourceHandlerCache.RESOURCE_HANDLER_CACHE_ENABLED_ATTRIBUTE,
331                     ResourceHandlerCache.RESOURCE_HANDLER_CACHE_ENABLED_DEFAULT);
332 
333             if (log.isLoggable(Level.FINE))
334             {
335                 log.log(Level.FINE, "MyFaces Resource Caching Enabled="
336                         + _resourceCacheEnabled);
337             }
338         }
339         return _resourceCacheEnabled;
340     }
341 
342     private int getMaxSize()
343     {
344         
345         ExternalContext externalContext = FacesContext.getCurrentInstance()
346                 .getExternalContext();
347         return WebConfigParamUtils.getIntegerInitParameter(externalContext, 
348                 RESOURCE_HANDLER_CACHE_SIZE_ATTRIBUTE, RESOURCE_HANDLER_CACHE_DEFAULT_SIZE);
349     }
350 
351     public static class ResourceKey
352     {
353         private final String resourceName;
354         private final String libraryName;
355         private final String contentType;
356         private final String localePrefix;
357         private final String contractName;
358 
359         public ResourceKey(String resourceName, String libraryName,
360                 String contentType, String localePrefix)
361         {
362             this(resourceName, libraryName, contentType, localePrefix, null);
363         }
364         
365         public ResourceKey(String resourceName, String libraryName,
366                 String contentType, String localePrefix, String contractName)
367         {
368             this.resourceName = resourceName;
369             this.libraryName = libraryName;
370             this.contentType = contentType;
371             this.localePrefix = localePrefix;
372             this.contractName = contractName;
373         }
374 
375         @Override
376         public boolean equals(Object o)
377         {
378             if (this == o)
379             {
380                 return true;
381             }
382             if (o == null || getClass() != o.getClass())
383             {
384                 return false;
385             }
386 
387             ResourceKey that = (ResourceKey) o;
388 
389             if (contentType != null ? !contentType.equals(that.contentType) : that.contentType != null)
390             {
391                 return false;
392             }
393             if (libraryName != null ? !libraryName.equals(that.libraryName) : that.libraryName != null)
394             {
395                 return false;
396             }
397             if (localePrefix != null ? !localePrefix.equals(that.localePrefix) : that.localePrefix != null)
398             {
399                 return false;
400             }
401             if (resourceName != null ? !resourceName.equals(that.resourceName) : that.resourceName != null)
402             {
403                 return false;
404             }
405             if (contractName != null ? !contractName.equals(that.contractName) : that.contractName != null)
406             {
407                 return false;
408             }
409 
410             return true;
411         }
412 
413         @Override
414         public int hashCode()
415         {
416             int result = resourceName != null ? resourceName.hashCode() : 0;
417             result = 31 * result + (libraryName != null ? libraryName.hashCode() : 0);
418             result = 31 * result + (contentType != null ? contentType.hashCode() : 0);
419             result = 31 * result + (localePrefix != null ? localePrefix.hashCode() : 0);
420             result = 31 * result + (contractName != null ? contractName.hashCode() : 0);
421             return result;
422         }
423     }
424 
425     public static class ResourceValue
426     {
427         private final ResourceMeta resourceMeta;
428         
429         private final ResourceLoader resourceLoader;
430         
431         private final ResourceCachedInfo info;
432         
433         public ResourceValue(ResourceMeta resourceMeta,
434                 ResourceLoader resourceLoader)
435         {
436             this.resourceMeta = resourceMeta;
437             this.resourceLoader = resourceLoader;
438             this.info = null;
439         }
440         
441         public ResourceValue(ResourceMeta resourceMeta,
442                 ResourceLoader resourceLoader,
443                 ResourceCachedInfo info)
444         {
445             super();
446             this.resourceMeta = resourceMeta;
447             this.resourceLoader = resourceLoader;
448             this.info = info;
449         }
450 
451         public ResourceMeta getResourceMeta()
452         {
453             return resourceMeta;
454         }
455 
456         public ResourceLoader getResourceLoader()
457         {
458             return resourceLoader;
459         }
460         
461         public ResourceCachedInfo getCachedInfo()
462         {
463             return info;
464         }
465     }
466         
467 }