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