View Javadoc
1   /*
2    * Copyright (c) 2002-2014, Mairie de Paris
3    * All rights reserved.
4    *
5    * Redistribution and use in source and binary forms, with or without
6    * modification, are permitted provided that the following conditions
7    * are met:
8    *
9    *  1. Redistributions of source code must retain the above copyright notice
10   *     and the following disclaimer.
11   *
12   *  2. Redistributions in binary form must reproduce the above copyright notice
13   *     and the following disclaimer in the documentation and/or other materials
14   *     provided with the distribution.
15   *
16   *  3. Neither the name of 'Mairie de Paris' nor 'Lutece' nor the names of its
17   *     contributors may be used to endorse or promote products derived from
18   *     this software without specific prior written permission.
19   *
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23   * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30   * POSSIBILITY OF SUCH DAMAGE.
31   *
32   * License 1.0
33   */
34  package fr.paris.lutece.portal.service.cache;
35  
36  import fr.paris.lutece.portal.service.page.PageEvent;
37  import fr.paris.lutece.portal.service.page.PageEventListener;
38  import fr.paris.lutece.portal.service.page.PageService;
39  
40  import net.sf.ehcache.Cache;
41  import net.sf.ehcache.CacheException;
42  import net.sf.ehcache.CacheManager;
43  import net.sf.ehcache.Ehcache;
44  import net.sf.ehcache.constructs.blocking.BlockingCache;
45  import net.sf.ehcache.constructs.blocking.LockTimeoutException;
46  import net.sf.ehcache.constructs.web.AlreadyCommittedException;
47  import net.sf.ehcache.constructs.web.AlreadyGzippedException;
48  import net.sf.ehcache.constructs.web.filter.FilterNonReentrantException;
49  import net.sf.ehcache.constructs.web.filter.SimpleCachingHeadersPageCachingFilter;
50  
51  import org.apache.log4j.Logger;
52  
53  import java.util.List;
54  
55  import javax.servlet.FilterChain;
56  import javax.servlet.FilterConfig;
57  import javax.servlet.http.HttpServletRequest;
58  import javax.servlet.http.HttpServletResponse;
59  
60  
61  /**
62   * Headers Page Caching Filter
63   * based on EHCACHE WEB
64   */
65  public class HeadersPageCachingFilter extends SimpleCachingHeadersPageCachingFilter implements CacheableService,
66      PageEventListener
67  {
68      private static final String BLOCKING_TIMEOUT_MILLIS = "blockingTimeoutMillis";
69      private static final String INIT_PARAM_CACHE_NAME = "cacheName";
70      private Cache _cache;
71      private Logger _logger = Logger.getLogger( "lutece.cache" );
72      private boolean _bInit;
73      private boolean _bEnable = true;
74      private String _strCacheName;
75  
76      /**
77       * {@inheritDoc }
78       */
79      @Override
80      public void doInit( FilterConfig filterConfig ) throws CacheException
81      {
82          // Override to inhibate the startup call made too early
83          // The original treatment is done at the first doFilter call
84          // through the init method below
85      }
86  
87      /**
88       * Initialization of the filter
89       */
90      private void init(  )
91      {
92          // Execute the doInit
93          synchronized ( HeadersPageCachingFilter.class )
94          {
95              if ( blockingCache == null )
96              {
97                  _strCacheName = filterConfig.getInitParameter( INIT_PARAM_CACHE_NAME );
98                  CacheService.getInstance(  ).createCache( _strCacheName );
99                  _cache = CacheManager.getInstance(  ).getCache( _strCacheName );
100                 CacheService.registerCacheableService( this );
101                 _logger.debug( "Initializing cache : " + _strCacheName );
102 
103                 setCacheNameIfAnyConfigured( filterConfig );
104 
105                 final String localCacheName = getCacheName(  );
106                 Ehcache cache = getCacheManager(  ).getEhcache( localCacheName );
107 
108                 if ( cache == null )
109                 {
110                     throw new CacheException( "cache '" + localCacheName + "' not found in configuration" );
111                 }
112 
113                 if ( !( cache instanceof BlockingCache ) )
114                 {
115                     // decorate and substitute
116                     BlockingCache newBlockingCache = new BlockingCache( cache );
117                     getCacheManager(  ).replaceCacheWithDecoratedCache( cache, newBlockingCache );
118                 }
119 
120                 blockingCache = (BlockingCache) getCacheManager(  ).getEhcache( localCacheName );
121 
122                 Integer blockingTimeoutMillis = parseBlockingCacheTimeoutMillis( filterConfig );
123 
124                 if ( ( blockingTimeoutMillis != null ) && ( blockingTimeoutMillis > 0 ) )
125                 {
126                     blockingCache.setTimeoutMillis( blockingTimeoutMillis );
127                 }
128             }
129 
130             PageService.addPageEventListener( this );
131         }
132 
133         _bInit = true;
134     }
135 
136     /**
137      * Reads the filterConfig for the parameter "blockingTimeoutMillis", and if
138      * found, set the blocking timeout. If there is a parsing exception, no
139      * timeout is set.
140      *
141      * @param filterConfig The filter config
142      * @return The timeout value
143      */
144     private Integer parseBlockingCacheTimeoutMillis( FilterConfig filterConfig )
145     {
146         String timeout = filterConfig.getInitParameter( BLOCKING_TIMEOUT_MILLIS );
147 
148         try
149         {
150             return Integer.parseInt( timeout );
151         }
152         catch ( NumberFormatException e )
153         {
154             return null;
155         }
156     }
157 
158     /**
159      * {@inheritDoc } This method is overriden to provide the cache name
160      */
161     @Override
162     protected String getCacheName(  )
163     {
164         return _strCacheName;
165     }
166 
167     /**
168      * {@inheritDoc }
169      */
170     @Override
171     protected void doFilter( HttpServletRequest request, HttpServletResponse response, FilterChain chain )
172         throws AlreadyGzippedException, AlreadyCommittedException, FilterNonReentrantException, LockTimeoutException,
173             Exception
174     {
175         if ( !_bInit )
176         {
177             init(  );
178         }
179 
180         if ( _bEnable )
181         {
182             super.doFilter( request, response, chain );
183             _logger.debug( "URI served from cache : " + request.getRequestURI(  ) );
184         }
185         else
186         {
187             chain.doFilter( request, response );
188         }
189     }
190 
191     // Cacheable Service implementation
192     /**
193      * {@inheritDoc }
194      */
195     @Override
196     public boolean isCacheEnable(  )
197     {
198         return _bEnable;
199     }
200 
201     /**
202      * {@inheritDoc }
203      */
204     @Override
205     public int getCacheSize(  )
206     {
207         return _cache.getSize(  );
208     }
209 
210     /**
211      * {@inheritDoc }
212      */
213     @Override
214     public void resetCache(  )
215     {
216         _cache.removeAll(  );
217     }
218 
219     /**
220      * {@inheritDoc }
221      */
222     @Override
223     public String getName(  )
224     {
225         return _strCacheName;
226     }
227 
228     /**
229      * {@inheritDoc }
230      */
231     @Override
232     public void enableCache( boolean bEnable )
233     {
234         _bEnable = bEnable;
235 
236         if ( ( !_bEnable ) && ( _cache != null ) )
237         {
238             _cache.removeAll(  );
239         }
240 
241         CacheService.updateCacheStatus( this );
242     }
243 
244     /**
245      * {@inheritDoc }
246      */
247     @Override
248     public List<String> getKeys(  )
249     {
250         return _cache.getKeys(  );
251     }
252 
253     /**
254      * {@inheritDoc }
255      */
256     @Override
257     public int getMaxElements(  )
258     {
259         return _cache.getCacheConfiguration(  ).getMaxElementsInMemory(  );
260     }
261 
262     /**
263      * {@inheritDoc }
264      */
265     @Override
266     public long getTimeToLive(  )
267     {
268         return _cache.getCacheConfiguration(  ).getTimeToLiveSeconds(  );
269     }
270 
271     /**
272      * {@inheritDoc }
273      */
274     @Override
275     public long getMemorySize(  )
276     {
277         return _cache.calculateInMemorySize(  );
278     }
279 
280     /**
281      * {@inheritDoc }
282      */
283     @Override
284     public String getInfos(  )
285     {
286         return CacheService.getInfos( _cache );
287     }
288 
289     /**
290      * {@inheritDoc }
291      */
292     @Override
293     public void processPageEvent( PageEvent event )
294     {
295         String strPattern = "page_id=" + event.getPage(  ).getId(  );
296 
297         for ( String strKey : (List<String>) blockingCache.getKeys(  ) )
298         {
299             if ( strKey.contains( strPattern ) && ( event.getEventType(  ) != PageEvent.PAGE_CREATED ) )
300             {
301                 blockingCache.remove( strKey );
302             }
303         }
304     }
305 }