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.view.facelets.impl;
20  
21  import java.io.IOException;
22  import java.net.URL;
23  import java.net.URLConnection;
24  import java.util.Collections;
25  import java.util.HashSet;
26  import java.util.Map;
27  import java.util.Set;
28  import java.util.concurrent.ConcurrentHashMap;
29  
30  import javax.faces.view.facelets.FaceletContext;
31  import javax.faces.view.facelets.FaceletException;
32  
33  import org.apache.myfaces.shared.resource.ResourceLoaderUtils;
34  import org.apache.myfaces.view.facelets.AbstractFaceletCache;
35  import org.apache.myfaces.view.facelets.AbstractFaceletContext;
36  import org.apache.myfaces.view.facelets.util.ParameterCheck;
37  
38  /**
39   * Extended MyFaces specific FaceletCache implementation that recompile
40   * facelet instance when a template context param is found.
41   * 
42   * @author Leonardo Uribe
43   * @since 2.1.0
44   *
45   */
46  class CacheELFaceletCacheImpl extends AbstractFaceletCache<DefaultFacelet>
47  {
48  
49      private static final long INFINITE_DELAY = -1;
50      private static final long NO_CACHE_DELAY = 0;
51      
52      /**
53       * FaceletNode is necessary only here, because view metadata and 
54       * composite component metadata are special and does not allow use nested
55       * template tags. View metadata facelet trims everything outside f:metadata
56       * and composite component metadata only takes into account composite:xxx tags,
57       * ignoring ui:xxx tags.
58       */
59      private Map<String, FaceletNode> _facelets;
60      
61      private Map<String, DefaultFacelet> _viewMetadataFacelets;
62      
63      private Map<String, DefaultFacelet> _compositeComponentMetadataFacelets;
64      
65      private long _refreshPeriod;
66      
67      CacheELFaceletCacheImpl(long refreshPeriod)
68      {
69          _refreshPeriod = refreshPeriod < 0 ? INFINITE_DELAY : refreshPeriod * 1000;
70  
71          _facelets = new ConcurrentHashMap<String, FaceletNode>();
72          
73          _viewMetadataFacelets = new ConcurrentHashMap<String, DefaultFacelet>();
74          
75          _compositeComponentMetadataFacelets = new ConcurrentHashMap<String, DefaultFacelet>();
76      }
77  
78      @Override
79      public DefaultFacelet getFacelet(URL url) throws IOException
80      {
81          ParameterCheck.notNull("url", url);
82          
83          String key = url.toString();
84          
85          FaceletNode node = _facelets.get(key);
86          DefaultFacelet f = node != null ? node.getFacelet() : null;
87          
88          if (f == null || this.needsToBeRefreshed(f))
89          {
90              //f = this._createFacelet(url);
91              Set<String> paramsSet = null;
92              if (node != null)
93              {
94                  paramsSet = node.getParams();
95              }
96              f = getMemberFactory().newInstance(url);
97              if (_refreshPeriod != NO_CACHE_DELAY)
98              {
99                  //Map<String, FaceletNode> newLoc = new HashMap<String, FaceletNode>(_facelets);
100                 //newLoc.put(key, (paramsSet != null && !paramsSet.isEmpty()) ? 
101                 //        new FaceletNode(f, paramsSet) : new FaceletNode(f) );
102                 //_facelets = newLoc;
103                 _facelets.put(key, (paramsSet != null && !paramsSet.isEmpty()) ? 
104                         new FaceletNode(f, paramsSet) : new FaceletNode(f) );
105             }
106         }
107         
108         return f;
109     }
110 
111     @Override
112     public DefaultFacelet getFacelet(FaceletContext ctx, URL url) throws IOException
113     {
114         String key = url.toString();
115         
116         //1. Check that the current parameters on the template are known
117         //   for the template.
118         //2. If all current parameters are known return the template
119         //2. If some current parameter is not known, add the param(s) to the
120         //   template, register the known params in the template context and
121         //   recompile the facelet, to clean up al EL expressions at once.
122 
123         FaceletNode node = _facelets.get(key);
124         DefaultFacelet f = (node != null) ? node.getFacelet() : null;
125         
126         Set<String> paramsSet = Collections.emptySet();
127         paramsSet = (node != null) ? node.getParams() : paramsSet;
128 
129         AbstractFaceletContext actx = (AbstractFaceletContext) ctx;
130         Set<String> knownParameters = actx.getTemplateContext().isKnownParametersEmpty() ?
131             (Set) Collections.emptySet() : actx.getTemplateContext().getKnownParameters();
132         
133         boolean create = false;
134         for (String paramKey : knownParameters)
135         {
136             if (!paramsSet.contains(paramKey))
137             {
138                 create = true;
139                 break;
140             }
141         }
142         
143         if (f == null || this.needsToBeRefreshed(f) || create)
144         {
145             //f = this._createFacelet(url);
146             f = getMemberFactory().newInstance(url);
147             if (_refreshPeriod != NO_CACHE_DELAY)
148             {
149                 //Map<String, FaceletNode> newLoc = new HashMap<String, FaceletNode>(_facelets);
150                 if (!paramsSet.isEmpty()|| !knownParameters.isEmpty() )
151                 {
152                     paramsSet = new HashSet(paramsSet);
153                     paramsSet.addAll(knownParameters);
154                     //newLoc.put(key, new FaceletNode(f, paramsSet));
155                     _facelets.put(key, new FaceletNode(f, paramsSet));
156                 }
157                 else
158                 {
159                     //newLoc.put(key, new FaceletNode(f));
160                     _facelets.put(key, new FaceletNode(f));
161                 }
162                 //_facelets = newLoc;
163             }
164         }
165 
166         if (!paramsSet.isEmpty())
167         {
168             //actx.getTemplateContext().getKnownParameters().addAll(paramsSet);
169             for (String param : paramsSet)
170             {
171                 if (!actx.getTemplateContext().containsKnownParameter(param))
172                 {
173                     actx.getTemplateContext().addKnownParameters(param);
174                 }
175             }
176         }
177         
178         return f;
179     }
180     
181     @Override
182     public boolean isFaceletCached(URL url)
183     {
184         return _facelets.containsKey(url.toString());
185     }
186 
187     @Override
188     public DefaultFacelet getViewMetadataFacelet(URL url) throws IOException
189     {
190         ParameterCheck.notNull("url", url);
191         
192         String key = url.toString();
193         
194         DefaultFacelet f = _viewMetadataFacelets.get(key);
195         
196         if (f == null || this.needsToBeRefreshed(f))
197         {
198             //f = this._createViewMetadataFacelet(url);
199             f = getMetadataMemberFactory().newInstance(url);
200             if (_refreshPeriod != NO_CACHE_DELAY)
201             {
202                 //Map<String, DefaultFacelet> newLoc = new HashMap<String, DefaultFacelet>(_viewMetadataFacelets);
203                 //newLoc.put(key, f);
204                 //_viewMetadataFacelets = newLoc;
205                 _viewMetadataFacelets.put(key, f);
206             }
207         }
208         
209         return f;
210     }
211 
212     @Override
213     public boolean isViewMetadataFaceletCached(URL url)
214     {
215         return _viewMetadataFacelets.containsKey(url.toString());
216     }
217 
218     /**
219      * Template method for determining if the Facelet needs to be refreshed.
220      * 
221      * @param facelet
222      *            Facelet that could have expired
223      * @return true if it needs to be refreshed
224      */
225     protected boolean needsToBeRefreshed(DefaultFacelet facelet)
226     {
227         // if set to 0, constantly reload-- nocache
228         if (_refreshPeriod == NO_CACHE_DELAY)
229         {
230             return true;
231         }
232 
233         // if set to -1, never reload
234         if (_refreshPeriod == INFINITE_DELAY)
235         {
236             return false;
237         }
238 
239         long target = facelet.getCreateTime() + _refreshPeriod;
240         if (System.currentTimeMillis() > target)
241         {
242             // Should check for file modification
243 
244             try
245             {
246                 URLConnection conn = facelet.getSource().openConnection();
247                 long lastModified = ResourceLoaderUtils.getResourceLastModified(conn);
248 
249                 return lastModified == 0 || lastModified > target;
250             }
251             catch (IOException e)
252             {
253                 throw new FaceletException("Error Checking Last Modified for " + facelet.getAlias(), e);
254             }
255         }
256 
257         return false;
258     }
259 
260     @Override
261     public DefaultFacelet getCompositeComponentMetadataFacelet(URL url) throws IOException
262     {
263         ParameterCheck.notNull("url", url);
264 
265         String key = url.toString();
266 
267         DefaultFacelet f = _compositeComponentMetadataFacelets.get(key);
268 
269         if (f == null || this.needsToBeRefreshed(f))
270         {
271             f = getCompositeComponentMetadataMemberFactory().newInstance(url);
272             if (_refreshPeriod != NO_CACHE_DELAY)
273             {
274                 //Map<String, DefaultFacelet> newLoc
275                 //        = new HashMap<String, DefaultFacelet>(_compositeComponentMetadataFacelets);
276                 //newLoc.put(key, f);
277                 //_compositeComponentMetadataFacelets = newLoc;
278                 _compositeComponentMetadataFacelets.put(key, f);
279             }
280         }
281         return f;
282     }
283 
284     @Override
285     public boolean isCompositeComponentMetadataFaceletCached(URL url)
286     {
287         return _compositeComponentMetadataFacelets.containsKey(url.toString());
288     }
289     
290     private static class FaceletNode
291     {
292         private DefaultFacelet facelet;
293         private Set<String> params;
294 
295         public FaceletNode(DefaultFacelet facelet)
296         {
297             this.facelet = facelet;
298             this.params = Collections.emptySet();
299         }
300         
301         public FaceletNode(DefaultFacelet facelet, Set<String> params)
302         {
303             this.facelet = facelet;
304             this.params = params;
305         }
306         
307         /**
308          * @return the facelet
309          */
310         public DefaultFacelet getFacelet()
311         {
312             return facelet;
313         }
314 
315         /**
316          * @param facelet the facelet to set
317          */
318         public void setFacelet(DefaultFacelet facelet)
319         {
320             this.facelet = facelet;
321         }
322 
323         /**
324          * @return the params
325          */
326         public Set<String> getParams()
327         {
328             return params;
329         }
330 
331         /**
332          * @param params the params to set
333          */
334         public void setParams(Set<String> params)
335         {
336             this.params = params;
337         }
338         
339     }
340 }