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<ResourceKey, ResourceValue> _resourceCacheMap = null;
39  
40      /**
41       * Controls the size of the cache used to check if a resource exists or not. 
42       * 
43       * <p>See org.apache.myfaces.RESOURCE_HANDLER_CACHE_ENABLED for details.</p>
44       */
45      @JSFWebConfigParam(defaultValue = "500", since = "2.0.2", group="resources", 
46              classType="java.lang.Integer", tags="performance")
47      private static final String RESOURCE_HANDLER_CACHE_SIZE_ATTRIBUTE = 
48          "org.apache.myfaces.RESOURCE_HANDLER_CACHE_SIZE";
49      private static final int RESOURCE_HANDLER_CACHE_DEFAULT_SIZE = 500;
50  
51      /**
52       * Enable or disable the cache used to "remember" if a resource handled by 
53       * the default ResourceHandler exists or not.
54       * 
55       */
56      @JSFWebConfigParam(defaultValue = "true", since = "2.0.2", group="resources", 
57              expectedValues="true,false", tags="performance")
58      private static final String RESOURCE_HANDLER_CACHE_ENABLED_ATTRIBUTE = 
59          "org.apache.myfaces.RESOURCE_HANDLER_CACHE_ENABLED";
60      private static final boolean RESOURCE_HANDLER_CACHE_ENABLED_DEFAULT = true;
61  
62      public ResourceValue getResource(String resourceName, String libraryName,
63              String contentType, String localePrefix)
64      {
65          if (!isResourceCachingEnabled() || _resourceCacheMap == null)
66          {
67              return null;
68          }
69  
70          if (log.isLoggable(Level.FINE))
71          {
72              log.log(Level.FINE, "Attemping to get resource from cache for "
73                      + resourceName);
74          }
75  
76          ResourceKey key = new ResourceKey(resourceName, libraryName, contentType, localePrefix);
77  
78          return _resourceCacheMap.get(key);
79      }
80      
81      public boolean containsResource(String resourceName, String libraryName, String contentType, String localePrefix)
82      {
83          if (!isResourceCachingEnabled() || _resourceCacheMap == null)
84          {
85              return false;
86          }
87  
88          ResourceKey key = new ResourceKey(resourceName, libraryName, contentType, localePrefix);
89          return _resourceCacheMap.get(key) != null;
90      }
91  
92      public void putResource(String resourceName, String libraryName,
93              String contentType, String localePrefix, ResourceMeta resource, ResourceLoader loader)
94      {
95          if (!isResourceCachingEnabled())
96          {
97              return;
98          }
99  
100         if (log.isLoggable(Level.FINE))
101         {
102             log.log(Level.FINE, "Attemping to put resource to cache for "
103                     + resourceName);
104         }
105 
106         if (_resourceCacheMap == null)
107         {
108             if (log.isLoggable(Level.FINE))
109             {
110                 log.log(Level.FINE, "Initializing resource cache map");
111             }
112             int maxSize = getMaxSize();
113             _resourceCacheMap = new ConcurrentLRUCache<ResourceKey, ResourceValue>(
114                     (maxSize * 4 + 3) / 3, maxSize);
115         }
116 
117         _resourceCacheMap.put(new ResourceKey(resourceName, libraryName,
118                 contentType, localePrefix), new ResourceValue(resource, loader));
119     }
120 
121     private boolean isResourceCachingEnabled()
122     {
123         if (_resourceCacheEnabled == null)
124         {
125             FacesContext facesContext = FacesContext.getCurrentInstance();
126 
127             //first, check to make sure that ProjectStage is production, if not, skip caching
128             if (!facesContext.isProjectStage(ProjectStage.Production))
129             {
130                 _resourceCacheEnabled = Boolean.FALSE;
131                 return _resourceCacheEnabled;
132             }
133 
134             ExternalContext externalContext = facesContext.getExternalContext();
135             if (externalContext == null)
136             {
137                 return false; //don't cache right now, but don't disable it yet either
138             }
139 
140             //if in production, make sure that the cache is not explicitly disabled via context param
141             _resourceCacheEnabled = WebConfigParamUtils.getBooleanInitParameter(externalContext, 
142                     ResourceHandlerCache.RESOURCE_HANDLER_CACHE_ENABLED_ATTRIBUTE,
143                     ResourceHandlerCache.RESOURCE_HANDLER_CACHE_ENABLED_DEFAULT);
144 
145             if (log.isLoggable(Level.FINE))
146             {
147                 log.log(Level.FINE, "MyFaces Resource Caching Enabled="
148                         + _resourceCacheEnabled);
149             }
150         }
151         return _resourceCacheEnabled;
152     }
153 
154     private int getMaxSize()
155     {
156         
157         ExternalContext externalContext = FacesContext.getCurrentInstance()
158                 .getExternalContext();
159         return WebConfigParamUtils.getIntegerInitParameter(externalContext, 
160                 RESOURCE_HANDLER_CACHE_SIZE_ATTRIBUTE, RESOURCE_HANDLER_CACHE_DEFAULT_SIZE);
161     }
162 
163     public static class ResourceKey
164     {
165         private String resourceName;
166         private String libraryName;
167         private String contentType;
168         private String localePrefix;
169 
170         public ResourceKey(String resourceName, String libraryName,
171                 String contentType, String localePrefix)
172         {
173             this.resourceName = resourceName;
174             this.libraryName = libraryName;
175             this.contentType = contentType;
176             this.localePrefix = localePrefix;
177         }
178 
179         @Override
180         public boolean equals(Object o)
181         {
182             if (this == o)
183             {
184                 return true;
185             }
186             if (o == null || getClass() != o.getClass())
187             {
188                 return false;
189             }
190 
191             ResourceKey that = (ResourceKey) o;
192 
193             if (contentType != null ? !contentType.equals(that.contentType) : that.contentType != null)
194             {
195                 return false;
196             }
197             if (libraryName != null ? !libraryName.equals(that.libraryName) : that.libraryName != null)
198             {
199                 return false;
200             }
201             if (localePrefix != null ? !localePrefix.equals(that.localePrefix) : that.localePrefix != null)
202             {
203                 return false;
204             }
205             if (resourceName != null ? !resourceName.equals(that.resourceName) : that.resourceName != null)
206             {
207                 return false;
208             }
209 
210             return true;
211         }
212 
213         @Override
214         public int hashCode()
215         {
216             int result = resourceName != null ? resourceName.hashCode() : 0;
217             result = 31 * result + (libraryName != null ? libraryName.hashCode() : 0);
218             result = 31 * result + (contentType != null ? contentType.hashCode() : 0);
219             result = 31 * result + (localePrefix != null ? localePrefix.hashCode() : 0);
220             return result;
221         }
222     }
223 
224     public static class ResourceValue
225     {
226         private ResourceMeta resourceMeta;
227         
228         private ResourceLoader resourceLoader;
229 
230         public ResourceValue(ResourceMeta resourceMeta,
231                 ResourceLoader resourceLoader)
232         {
233             super();
234             this.resourceMeta = resourceMeta;
235             this.resourceLoader = resourceLoader;
236         }
237 
238         public ResourceMeta getResourceMeta()
239         {
240             return resourceMeta;
241         }
242 
243         public ResourceLoader getResourceLoader()
244         {
245             return resourceLoader;
246         }
247     }
248 
249 }