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.commons.resourcehandler.resource;
20  
21  import java.util.Collections;
22  import java.util.LinkedHashMap;
23  import java.util.Map;
24  import java.util.logging.Level;
25  import java.util.logging.Logger;
26  
27  import javax.faces.application.ProjectStage;
28  import javax.faces.context.ExternalContext;
29  import javax.faces.context.FacesContext;
30  
31  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFWebConfigParam;
32  import org.apache.myfaces.commons.util.WebConfigParamUtils;
33  
34  public class ResourceHandlerCache
35  {
36      private static final Logger log = Logger
37              .getLogger(ResourceHandlerCache.class.getName());
38  
39      private Boolean _resourceCacheEnabled = null;
40      private Map<ResourceKey, ResourceValue> _resourceCacheMap = null;
41  
42      @JSFWebConfigParam(defaultValue = "500", since = "2.0.2")
43      private static final String RESOURCE_HANDLER_CACHE_SIZE_ATTRIBUTE = "org.apache.myfaces.RESOURCE_HANDLER_CACHE_SIZE";
44      private static final int RESOURCE_HANDLER_CACHE_DEFAULT_SIZE = 500;
45  
46      @JSFWebConfigParam(defaultValue = "true", since = "2.0.2")
47      private static final String RESOURCE_HANDLER_CACHE_ENABLED_ATTRIBUTE = "org.apache.myfaces.RESOURCE_HANDLER_CACHE_ENABLED";
48      private static final boolean RESOURCE_HANDLER_CACHE_ENABLED_DEFAULT = true;
49  
50      public ResourceValue getResource(String resourceName, String libraryName,
51              String contentType, String localePrefix)
52      {
53          if (!isResourceCachingEnabled() || _resourceCacheMap == null)
54              return null;
55  
56          if (log.isLoggable(Level.FINE))
57              log.log(Level.FINE, "Attemping to get resource from cache for "
58                      + resourceName);
59  
60          ResourceKey key = new ResourceKey(resourceName, libraryName, contentType, localePrefix);
61  
62          return _resourceCacheMap.get(key);
63      }
64      
65      public boolean containsResource(String resourceName, String libraryName, String contentType, String localePrefix)
66      {
67          if (!isResourceCachingEnabled() || _resourceCacheMap == null)
68              return false;
69  
70          ResourceKey key = new ResourceKey(resourceName, libraryName, contentType, localePrefix);
71          return _resourceCacheMap.containsKey(key);
72      }
73  
74      public void putResource(String resourceName, String libraryName,
75              String contentType, String localePrefix, ResourceMeta resource, ResourceLoader loader)
76      {
77          if (!isResourceCachingEnabled())
78              return;
79  
80          if (log.isLoggable(Level.FINE))
81              log.log(Level.FINE, "Attemping to put resource to cache for "
82                      + resourceName);
83  
84          if (_resourceCacheMap == null)
85          {
86              if (log.isLoggable(Level.FINE))
87                  log.log(Level.FINE, "Initializing resource cache map");
88              _resourceCacheMap = Collections
89                      .synchronizedMap(new _ResourceMap<ResourceKey, ResourceValue>(
90                              getMaxSize()));
91          }
92  
93          _resourceCacheMap.put(new ResourceKey(resourceName, libraryName,
94                  contentType, localePrefix), new ResourceValue(resource, loader));
95      }
96  
97      private boolean isResourceCachingEnabled()
98      {
99          if (_resourceCacheEnabled == null)
100         {
101             FacesContext facesContext = FacesContext.getCurrentInstance();
102 
103             //first, check to make sure that ProjectStage is production, if not, skip caching
104             if (!facesContext.isProjectStage(ProjectStage.Production))
105             {
106                 return _resourceCacheEnabled = Boolean.FALSE;
107             }
108 
109             ExternalContext externalContext = facesContext.getExternalContext();
110             if (externalContext == null)
111                 return false; //don't cache right now, but don't disable it yet either
112 
113             //if in production, make sure that the cache is not explicitly disabled via context param
114             _resourceCacheEnabled = WebConfigParamUtils.getBooleanInitParameter(externalContext, 
115                     ResourceHandlerCache.RESOURCE_HANDLER_CACHE_ENABLED_ATTRIBUTE,
116                     ResourceHandlerCache.RESOURCE_HANDLER_CACHE_ENABLED_DEFAULT);
117 
118             if (log.isLoggable(Level.FINE))
119             {
120                 log.log(Level.FINE, "MyFaces Resource Caching Enabled="
121                         + _resourceCacheEnabled);
122             }
123         }
124         return _resourceCacheEnabled;
125     }
126 
127     private int getMaxSize()
128     {
129         
130         ExternalContext externalContext = FacesContext.getCurrentInstance()
131                 .getExternalContext();
132         return WebConfigParamUtils.getIntegerInitParameter(externalContext, 
133                 RESOURCE_HANDLER_CACHE_SIZE_ATTRIBUTE, RESOURCE_HANDLER_CACHE_DEFAULT_SIZE);
134     }
135 
136     public static class ResourceKey
137     {
138         private String resourceName;
139         private String libraryName;
140         private String contentType;
141         private String localePrefix;
142 
143         public ResourceKey(String resourceName, String libraryName,
144                 String contentType, String localePrefix)
145         {
146             this.resourceName = resourceName;
147             this.libraryName = libraryName;
148             this.contentType = contentType;
149             this.localePrefix = localePrefix;
150         }
151 
152         @Override
153         public boolean equals(Object o)
154         {
155             if (this == o)
156             {
157                 return true;
158             }
159             if (o == null || getClass() != o.getClass())
160             {
161                 return false;
162             }
163 
164             ResourceKey that = (ResourceKey) o;
165 
166             if (contentType != null ? !contentType.equals(that.contentType) : that.contentType != null)
167             {
168                 return false;
169             }
170             if (libraryName != null ? !libraryName.equals(that.libraryName) : that.libraryName != null)
171             {
172                 return false;
173             }
174             if (localePrefix != null ? !localePrefix.equals(that.localePrefix) : that.localePrefix != null)
175             {
176                 return false;
177             }
178             if (resourceName != null ? !resourceName.equals(that.resourceName) : that.resourceName != null)
179             {
180                 return false;
181             }
182 
183             return true;
184         }
185 
186         @Override
187         public int hashCode()
188         {
189             int result = resourceName != null ? resourceName.hashCode() : 0;
190             result = 31 * result + (libraryName != null ? libraryName.hashCode() : 0);
191             result = 31 * result + (contentType != null ? contentType.hashCode() : 0);
192             result = 31 * result + (localePrefix != null ? localePrefix.hashCode() : 0);
193             return result;
194         }
195     }
196 
197     public static class ResourceValue
198     {
199         private ResourceMeta resourceMeta;
200         
201         private ResourceLoader resourceLoader;
202 
203         public ResourceValue(ResourceMeta resourceMeta,
204                 ResourceLoader resourceLoader)
205         {
206             super();
207             this.resourceMeta = resourceMeta;
208             this.resourceLoader = resourceLoader;
209         }
210 
211         public ResourceMeta getResourceMeta()
212         {
213             return resourceMeta;
214         }
215 
216         public ResourceLoader getResourceLoader()
217         {
218             return resourceLoader;
219         }
220     }
221 
222     private static class _ResourceMap<K, V> extends LinkedHashMap<K, V>
223     {
224         private static final long serialVersionUID = 1L;
225         private int maxCapacity;
226 
227         public _ResourceMap(int cacheSize)
228         {
229             // create map at max capacity and 1.1 load factor to avoid rehashing
230             super(cacheSize + 1, 1.1f, true);
231             maxCapacity = cacheSize;
232         }
233 
234         @Override
235         protected boolean removeEldestEntry(Map.Entry<K, V> eldest)
236         {
237             return size() > maxCapacity;
238         }
239     }
240 }