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.io.InputStream;
23  import java.net.URL;
24  import java.net.URLConnection;
25  import java.util.HashMap;
26  import java.util.Map;
27  
28  import javax.faces.view.facelets.FaceletCache;
29  import javax.faces.view.facelets.FaceletException;
30  
31  import org.apache.myfaces.shared.resource.ResourceLoaderUtils;
32  import org.apache.myfaces.view.facelets.util.ParameterCheck;
33  
34  /**
35   * TODO: Note MyFaces core has another type of Facelet for read composite component
36   * metadata. The reason behind do this in MyFaces is to retrieve some information
37   * related to insertChildren/insertFacet that can be used as metadata and use that
38   * information later to do the hack for composite components using templates, instead
39   * rely on component relocation. This is not handled by FaceletCache included here 
40   * but in practice this should not be a problem, because the intention of this class
41   * is handle Facelet instances created using custom ResourceResolver stuff, and 
42   * usually those pages are for views, not for composite components. Even if that is
43   * true, composite component metadata Facelet is smaller (only add cc:xxx stuff) that
44   * the other ones used for views or the one used to apply the composite component
45   * itself.  
46   * 
47   * @author Leonardo Uribe
48   * @since 2.1.0
49   *
50   */
51  class FaceletCacheImpl extends FaceletCache<DefaultFacelet>
52  {
53  
54      private static final long INFINITE_DELAY = -1;
55      private static final long NO_CACHE_DELAY = 0;
56      
57      private Map<String, DefaultFacelet> _facelets;
58      
59      private Map<String, DefaultFacelet> _viewMetadataFacelets;
60  
61      private long _refreshPeriod;
62      
63      FaceletCacheImpl(long refreshPeriod)
64      {
65          _refreshPeriod = refreshPeriod < 0 ? INFINITE_DELAY : refreshPeriod * 1000;
66          
67          _facelets = new HashMap<String, DefaultFacelet>();
68          
69          _viewMetadataFacelets = new HashMap<String, DefaultFacelet>();
70      }
71  
72      @Override
73      public DefaultFacelet getFacelet(URL url) throws IOException
74      {
75          ParameterCheck.notNull("url", url);
76          
77          String key = url.toString();
78          
79          DefaultFacelet f = _facelets.get(key);
80          
81          if (f == null || this.needsToBeRefreshed(f))
82          {
83              //f = this._createFacelet(url);
84              f = getMemberFactory().newInstance(url);
85              if (_refreshPeriod != NO_CACHE_DELAY)
86              {
87                  Map<String, DefaultFacelet> newLoc = new HashMap<String, DefaultFacelet>(_facelets);
88                  newLoc.put(key, f);
89                  _facelets = newLoc;
90              }
91          }
92          
93          return f;
94      }
95      
96      @Override
97      public boolean isFaceletCached(URL url)
98      {
99          return _facelets.containsKey(url.toString());
100     }
101 
102     @Override
103     public DefaultFacelet getViewMetadataFacelet(URL url) throws IOException
104     {
105         ParameterCheck.notNull("url", url);
106         
107         String key = url.toString();
108         
109         DefaultFacelet f = _viewMetadataFacelets.get(key);
110         
111         if (f == null || this.needsToBeRefreshed(f))
112         {
113             //f = this._createViewMetadataFacelet(url);
114             f = getMetadataMemberFactory().newInstance(url);
115             if (_refreshPeriod != NO_CACHE_DELAY)
116             {
117                 Map<String, DefaultFacelet> newLoc = new HashMap<String, DefaultFacelet>(_viewMetadataFacelets);
118                 newLoc.put(key, f);
119                 _viewMetadataFacelets = newLoc;
120             }
121         }
122         
123         return f;
124     }
125 
126     @Override
127     public boolean isViewMetadataFaceletCached(URL url)
128     {
129         return _viewMetadataFacelets.containsKey(url.toString());
130     }
131 
132     /**
133      * Template method for determining if the Facelet needs to be refreshed.
134      * 
135      * @param facelet
136      *            Facelet that could have expired
137      * @return true if it needs to be refreshed
138      */
139     protected boolean needsToBeRefreshed(DefaultFacelet facelet)
140     {
141         // if set to 0, constantly reload-- nocache
142         if (_refreshPeriod == NO_CACHE_DELAY)
143         {
144             return true;
145         }
146 
147         // if set to -1, never reload
148         if (_refreshPeriod == INFINITE_DELAY)
149         {
150             return false;
151         }
152 
153         long target = facelet.getCreateTime() + _refreshPeriod;
154         if (System.currentTimeMillis() > target)
155         {
156             // Should check for file modification
157 
158             URLConnection conn = null;
159             try
160             {
161                 conn = facelet.getSource().openConnection();
162                 long lastModified = ResourceLoaderUtils.getResourceLastModified(conn);
163 
164                 return lastModified == 0 || lastModified > target;
165             }
166             catch (IOException e)
167             {
168                 throw new FaceletException("Error Checking Last Modified for " + facelet.getAlias(), e);
169             }
170             finally
171             {
172                 // finally close input stream when finished, if fails just continue.
173                 if (conn != null)
174                 {
175                     try 
176                     {
177                         InputStream is = conn.getInputStream();
178                         if (is != null)
179                         {
180                             is.close();
181                         }
182                     }
183                     catch (IOException e)
184                     {
185                         // Ignore 
186                     }
187                 }
188             }
189         }
190 
191         return false;
192     }
193 }