Coverage Report - no.sesat.search.view.velocity.QuickResourceManagerImpl
 
Classes in this File Line Coverage Branch Coverage Complexity
QuickResourceManagerImpl
0%
0/11
0%
0/4
0
QuickResourceManagerImpl$1
N/A
N/A
0
QuickResourceManagerImpl$Loader
0%
0/49
0%
0/20
0
 
 1  
 /* Copyright (2005-2008) Schibsted Søk AS
 2  
  * This file is part of SESAT.
 3  
  *
 4  
  *   SESAT is free software: you can redistribute it and/or modify
 5  
  *   it under the terms of the GNU Affero General Public License as published by
 6  
  *   the Free Software Foundation, either version 3 of the License, or
 7  
  *   (at your option) any later version.
 8  
  *
 9  
  *   SESAT is distributed in the hope that it will be useful,
 10  
  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  
  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 12  
  *   GNU Affero General Public License for more details.
 13  
  *
 14  
  *   You should have received a copy of the GNU Affero General Public License
 15  
  *   along with SESAT.  If not, see <http://www.gnu.org/licenses/>.
 16  
  *
 17  
  * Created on May 11, 2007, 10:15:37 PM
 18  
  */
 19  
 package no.sesat.search.view.velocity;
 20  
 
 21  
 import org.apache.velocity.runtime.resource.ResourceManagerImpl;
 22  
 import org.apache.velocity.runtime.resource.Resource;
 23  
 import org.apache.velocity.exception.ResourceNotFoundException;
 24  
 import org.apache.velocity.exception.ParseErrorException;
 25  
 import org.apache.log4j.Logger;
 26  
 import org.apache.commons.lang.time.StopWatch;
 27  
 
 28  
 import java.util.concurrent.ExecutorService;
 29  
 import java.util.concurrent.Executors;
 30  
 import java.text.MessageFormat;
 31  
 import java.util.concurrent.ThreadPoolExecutor;
 32  
 
 33  
 /**
 34  
  * A quicker replacement for {@link org.apache.velocity.runtime.resource.ResourceManagerImpl} that avoids
 35  
  * doing resource loading and parsing while holding a global exclusive lock. This implementation creates new resource
 36  
  * instances instead of updating existing ones and loads and parses them in the background. This removes the need for
 37  
  * locking and the only synchronization done is whatever synchronization measures the cache is taking. Resources not yet
 38  
  * in the cache are loaded synchronously. The reduced locking is achieved at the expense of resources possibly getting
 39  
  * loaded several times during startup or reload.
 40  
  *
 41  
  *
 42  
  * @version $Id: QuickResourceManagerImpl.java 6646 2008-05-30 09:22:05Z sshafroi $
 43  
  */
 44  0
 public final class QuickResourceManagerImpl extends ResourceManagerImpl {
 45  
 
 46  0
     private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool();
 47  
 
 48  
     private static final String RESOURCE_NOT_FOUND = "ResourceManager :'{0}'' not found in any resource loader.";
 49  
     private static final String RESOURCE_PARSE_EXCEPTION = "ResourceManager.getResource() parse exception";
 50  
     private static final String RESOURCE_EXCEPTION = "ResourceManager.getResource() exception new";
 51  
     private static final String LOADED_VELOCITY_RESOURCE = "Loaded velocity resource {0} in {1}";
 52  
     private static final String CHECKED_MODIFICATION = "Checked modification of velocity resource {0} in {1}";
 53  
 
 54  0
     private static final Logger LOG = Logger.getLogger(QuickResourceManagerImpl.class);
 55  
     private static final String DEBUG_POOL_COUNT = "Pool size: ";
 56  
 
 57  
     /** {@inheritDoc}
 58  
      * Overridden to layer caching capabilities.
 59  
      */
 60  
     @Override
 61  
     public Resource getResource(final String name, final int type, final String encoding) throws Exception {
 62  
 
 63  0
         final Resource resource = globalCache.get(type + name);
 64  
 
 65  0
         if (resource != null) {
 66  
 
 67  
             // Use cached resource for this invocation but also start a thread to update cache with a brand new
 68  
             // resource instance.
 69  0
             if (resource.requiresChecking()) {
 70  
 
 71  
                 // Touch the resource so that a closely following caller won't trigger an update thread.
 72  
                 // Keeps updates of the same resource from piling up when traffic is high.
 73  0
                 resource.touch();
 74  0
                 EXECUTOR.submit(new Loader(resource, name, type, encoding));
 75  
             }
 76  
 
 77  0
             return resource;
 78  
 
 79  
         } else {
 80  
 
 81  0
             return new Loader(resource, name, type, encoding).load();
 82  
         }
 83  
     }
 84  
 
 85  
     /**
 86  
      * Class capable of loading a resource and updating the cache.
 87  
      */
 88  0
     private final class Loader implements Runnable {
 89  
 
 90  
         private final String name;
 91  
         private final int type;
 92  
         private final String enc;
 93  
         private final String key;
 94  
         private final Resource oldResource;
 95  0
         private boolean modified = false;
 96  
 
 97  
         /**
 98  
          * Creates a new resource loader. The supplied <tt>oldResource</tt> will not be refreshed but a new one will
 99  
          * replace it in the cache. This should be safe with regards to the rest of velocity since the default cache
 100  
          * implementation is backed by a LRUMap discarding resources at will.
 101  
          *
 102  
          * @param oldResource The resource being refreshed. Null if loaded for the first time.
 103  
          * @param name The name of the resource to load.
 104  
          * @param type The type of the resource to load.
 105  
          * @param enc The encoding of the resource.
 106  
          */
 107  0
         private Loader(final Resource oldResource, final String name, final int type, final String enc) {
 108  
 
 109  0
             this.oldResource = oldResource;
 110  0
             this.name = name;
 111  0
             this.type = type;
 112  0
             this.enc = enc;
 113  
 
 114  0
             key = type + name;
 115  
 
 116  0
             if(LOG.isDebugEnabled() && EXECUTOR instanceof ThreadPoolExecutor){
 117  0
                 final ThreadPoolExecutor tpe = (ThreadPoolExecutor)EXECUTOR;
 118  0
                 LOG.debug(DEBUG_POOL_COUNT + tpe.getActiveCount() + '/' + tpe.getPoolSize());
 119  
             }
 120  0
         }
 121  
 
 122  
         /**
 123  
          * Loads the resource if it has been modified since it was last loaded.
 124  
          *
 125  
          * @return the resource.
 126  
          * @throws ResourceNotFoundException if the resource wasn't found.
 127  
          * @throws ParseErrorException if the resource couldn't be parsed.
 128  
          * @throws Exception ?
 129  
          */
 130  
         private Resource load() throws Exception {
 131  
 
 132  0
             final StopWatch stopWatch = new StopWatch();
 133  
 
 134  
             try {
 135  0
                 stopWatch.start();
 136  0
                 modified = oldResource == null || oldResource.isSourceModified();
 137  0
                 stopWatch.split();
 138  
 
 139  0
                 if (modified) {
 140  0
                     final Resource newResource = loadResource(name, type, enc);
 141  
 
 142  0
                     if (newResource.getResourceLoader().isCachingOn()) {
 143  0
                         globalCache.put(key, newResource);
 144  
                     }
 145  0
                     return newResource;
 146  
                 } else {
 147  0
                     return oldResource;
 148  
                 }
 149  0
             } catch (ResourceNotFoundException rnfe) {
 150  0
                 log.error(MessageFormat.format(RESOURCE_NOT_FOUND, name));
 151  0
                 throw rnfe;
 152  0
             } catch (ParseErrorException pee) {
 153  0
                 log.error(RESOURCE_PARSE_EXCEPTION, pee);
 154  0
                 throw pee;
 155  0
             } catch (RuntimeException re) {
 156  0
                 throw re;
 157  0
             } catch (Exception e) {
 158  0
                 log.error(RESOURCE_EXCEPTION, e);
 159  0
                 throw e;
 160  
             } finally {
 161  
 
 162  0
                 stopWatch.stop();
 163  
 
 164  0
                 if (oldResource != null && LOG.isInfoEnabled()) {
 165  0
                     LOG.info(MessageFormat.format(CHECKED_MODIFICATION, key, stopWatch.toSplitString()));
 166  
                 }
 167  0
                 if (modified && LOG.isDebugEnabled()) {
 168  0
                     LOG.debug(MessageFormat.format(LOADED_VELOCITY_RESOURCE, key, stopWatch.toString()));
 169  
                 }
 170  
             }
 171  
         }
 172  
 
 173  
         /**
 174  
          * Loads resource if it has been modified since it was last loaded.
 175  
          */
 176  
         public void run() {
 177  
 
 178  
             try {
 179  0
                 load();
 180  
 
 181  0
             }  catch (ResourceNotFoundException rnfe) {
 182  0
                 log.error(MessageFormat.format(RESOURCE_NOT_FOUND, name));
 183  0
             } catch (ParseErrorException pee) {
 184  0
                 log.error(RESOURCE_PARSE_EXCEPTION, pee);
 185  0
             } catch (RuntimeException re) {
 186  0
                 throw re;
 187  0
             } catch (Exception e) {
 188  0
                 log.error(RESOURCE_EXCEPTION, e);
 189  0
             }
 190  0
         }
 191  
     }
 192  
 }