Coverage Report - no.sesat.search.view.velocity.VelocityEngineFactory
 
Classes in this File Line Coverage Branch Coverage Complexity
VelocityEngineFactory
0%
0/89
0%
0/18
0
VelocityEngineFactory$1
0%
0/5
N/A
0
VelocityEngineFactory$2
0%
0/6
N/A
0
VelocityEngineFactory$3
0%
0/3
N/A
0
VelocityEngineFactory$Context
N/A
N/A
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  
  * VelocityEngineFactory.java
 18  
  *
 19  
  * Created on 3 February 2006, 13:24
 20  
  *
 21  
  */
 22  
 
 23  
 package no.sesat.search.view.velocity;
 24  
 
 25  
 import java.io.IOException;
 26  
 import java.util.HashMap;
 27  
 import java.util.Map;
 28  
 import java.util.Properties;
 29  
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 30  
 
 31  
 import javax.xml.parsers.DocumentBuilder;
 32  
 
 33  
 import no.geodata.maputil.CoordHelper;
 34  
 import no.sesat.commons.ioc.BaseContext;
 35  
 import no.sesat.commons.ioc.ContextWrapper;
 36  
 import no.sesat.search.InfrastructureException;
 37  
 import no.sesat.search.result.Boomerang;
 38  
 import no.sesat.search.result.Decoder;
 39  
 import no.sesat.search.site.Site;
 40  
 import no.sesat.search.site.SiteContext;
 41  
 import no.sesat.search.site.SiteKeyedFactory;
 42  
 import no.sesat.search.site.config.BytecodeLoader;
 43  
 import no.sesat.search.site.config.DocumentLoader;
 44  
 import no.sesat.search.site.config.PropertiesLoader;
 45  
 import no.sesat.search.site.config.ResourceContext;
 46  
 import no.sesat.search.site.config.SiteClassLoaderFactory;
 47  
 import no.sesat.search.site.config.SiteConfiguration;
 48  
 import no.sesat.search.site.config.Spi;
 49  
 import no.sesat.search.site.config.UrlResourceLoader;
 50  
 
 51  
 import no.sesat.search.view.FindResource;
 52  
 import no.sesat.search.view.navigation.NavigationHelper;
 53  
 import org.apache.log4j.Level;
 54  
 import org.apache.log4j.Logger;
 55  
 import org.apache.velocity.Template;
 56  
 import org.apache.velocity.VelocityContext;
 57  
 import org.apache.velocity.app.Velocity;
 58  
 import org.apache.velocity.app.VelocityEngine;
 59  
 import org.apache.velocity.exception.ParseErrorException;
 60  
 import org.apache.velocity.exception.ResourceNotFoundException;
 61  
 import org.apache.velocity.runtime.RuntimeConstants;
 62  
 import org.apache.velocity.tools.generic.DateTool;
 63  
 import org.apache.velocity.tools.generic.MathTool;
 64  
 import org.apache.velocity.tools.generic.NumberTool;
 65  
 
 66  
 /** Custom Factory around Velocity Engines and Templates.
 67  
  * Each instance maps to an VelocityEngine instance.
 68  
  * All template operations (getting and merging) are done through this class
 69  
  *   rather than directly against Velocity's API.
 70  
  *
 71  
  * <b>Developer Aid</b><br/>
 72  
  * Ola-marius extended the engine so to run in debug mode that outlines (& titles) each rendered template.
 73  
  * See
 74  
  * <a href="http://sesat.no/debugging-velocity-templates.html">
 75  
  *  Debugging Velocity Templates
 76  
  * </a><br/>
 77  
  *
 78  
  * @version $Id: VelocityEngineFactory.java 7153 2009-01-20 13:30:03Z ssmiweve $
 79  
  *
 80  
  */
 81  
 public final class VelocityEngineFactory implements SiteKeyedFactory{
 82  
 
 83  
     /**
 84  
      * The context the AnalysisRules must work against.
 85  
      */
 86  
     public interface Context extends SiteContext, ResourceContext {
 87  
     }
 88  
 
 89  
     // Constants -----------------------------------------------------
 90  
 
 91  0
     private static final Logger LOG = Logger.getLogger(VelocityEngineFactory.class);
 92  
 
 93  
     private static final String INFO_TEMPLATE_NOT_FOUND = "Could not find template ";
 94  
     private static final String ERR_IN_TEMPLATE = "Error parsing template ";
 95  
     private static final String ERR_GETTING_TEMPLATE = "Error getting template ";
 96  
 
 97  
     private static final String VELOCITY_LOGGER = "org.apache.velocity";
 98  
 
 99  0
     private static final Map<Site,VelocityEngineFactory> INSTANCES = new HashMap<Site,VelocityEngineFactory>();
 100  0
     private static final ReentrantReadWriteLock INSTANCES_LOCK = new ReentrantReadWriteLock();
 101  
 
 102  
     private static final String LOGSYSTEM_CLASS = "org.apache.velocity.runtime.log.Log4JLogChute";
 103  
     private static final String LOG_NAME = "runtime.log.logsystem.log4j.logger";
 104  
 
 105  0
     private static final boolean VELOCITY_DEBUG = Boolean.getBoolean("VELOCITY_DEBUG");
 106  
 
 107  
     // Attributes ----------------------------------------------------
 108  
 
 109  
     private final VelocityEngine engine;
 110  
 
 111  
     // Static --------------------------------------------------------
 112  
 
 113  
 
 114  
     /** Find the appropriate velocity Template by its name against a given engine and site.
 115  
      * Will throw a ResourceNotFoundException if not found.
 116  
      *
 117  
      * @param engine the VelocityEngine appropriate for the current site.
 118  
      * @param site the current site.
 119  
      * @param templateName the name of the template. must not contain ".vm" suffix.
 120  
      * @return returns the template.
 121  
      * @throws org.apache.velocity.exception.ResourceNotFoundException if the template was not found.
 122  
      */
 123  
     public static Template getTemplate(
 124  
             final VelocityEngine engine,
 125  
             final Site site,
 126  
             final String templateName) throws ResourceNotFoundException{
 127  
 
 128  0
         final String templateUrl = site.getTemplateDir() + '/' + templateName + ".vm";
 129  
 
 130  
         try {
 131  0
             return engine.getTemplate(templateUrl);
 132  
 
 133  
 
 134  0
         } catch (ParseErrorException ex) {
 135  0
             LOG.error(ERR_IN_TEMPLATE + templateUrl, ex);
 136  0
             throw new InfrastructureException(ex);
 137  
 
 138  0
         } catch (ResourceNotFoundException ex) {
 139  
             // throw this so callers know we did not find the resource.
 140  0
             throw ex;
 141  
 
 142  0
         } catch (Exception ex) {
 143  0
             LOG.error(ERR_GETTING_TEMPLATE + templateUrl, ex);
 144  0
             throw new InfrastructureException(ex);
 145  
         }
 146  
     }
 147  
 
 148  
     public static VelocityContext newContextInstance(){
 149  
 
 150  0
         final VelocityContext context = new VelocityContext();
 151  
 
 152  
         // coord helper
 153  0
         context.put("coordHelper", new CoordHelper());
 154  
         // decoder
 155  0
         context.put("decoder", new Decoder());
 156  
         // math tool
 157  0
         context.put("math", new MathTool());
 158  
         // number tool
 159  0
         context.put("number", new NumberTool());
 160  
         // date tool
 161  0
         context.put("date", new DateTool());
 162  
         // navigation helper
 163  0
         context.put("navigationHelper", new NavigationHelper());
 164  
         // boomerang
 165  0
         context.put("boomerang", new Boomerang());
 166  
 
 167  0
         return context;
 168  
     }
 169  
 
 170  
 
 171  
     /** Main method to retrieve the correct VelocityEngine to further obtain
 172  
      * AnalysisRule.
 173  
      * @param cxt the contextual needs the VelocityEngine must use to operate.
 174  
      * @return VelocityEngine for this site.
 175  
      */
 176  
     public static VelocityEngineFactory instanceOf(final Context cxt) {
 177  
 
 178  0
         final Site site = cxt.getSite();
 179  
 
 180  0
         VelocityEngineFactory instance = null;
 181  
 
 182  
         try {
 183  0
             INSTANCES_LOCK.readLock().lock();
 184  0
             instance = INSTANCES.get(site);
 185  
         } finally {
 186  0
             INSTANCES_LOCK.readLock().unlock();
 187  0
         }
 188  
 
 189  0
         if(!VELOCITY_DEBUG) {
 190  0
             if (instance == null) {
 191  0
                 instance = new VelocityEngineFactory(cxt);
 192  
             }
 193  
         } else {
 194  0
             instance = new VelocityEngineFactory(cxt);
 195  
         }
 196  
 
 197  0
         return instance;
 198  
     }
 199  
 
 200  
     /**
 201  
      * Utility wrapper to the instanceOf(Context).
 202  
      * <b>Makes the presumption we will be using the UrlResourceLoader to load all resources.</b>
 203  
      * @param site the site the VelocityEngine will work for.
 204  
      * @return VelocityEngine for this site.
 205  
      */
 206  
     public static VelocityEngineFactory valueOf(final Site site) {
 207  
 
 208  
         // RegExpEvaluatorFactory.Context for this site & UrlResourceLoader.
 209  0
         final VelocityEngineFactory instance = VelocityEngineFactory.instanceOf(new VelocityEngineFactory.Context() {
 210  
             public Site getSite() {
 211  0
                 return site;
 212  
             }
 213  
             public PropertiesLoader newPropertiesLoader(
 214  
                     final SiteContext siteCxt,
 215  
                     final String resource,
 216  
                     final Properties properties) {
 217  
 
 218  0
                 return UrlResourceLoader.newPropertiesLoader(siteCxt, resource, properties);
 219  
             }
 220  
             public DocumentLoader newDocumentLoader(
 221  
                     final SiteContext siteCxt,
 222  
                     final String resource,
 223  
                     final DocumentBuilder builder) {
 224  
 
 225  0
                 return UrlResourceLoader.newDocumentLoader(siteCxt, resource, builder);
 226  
             }
 227  
             public BytecodeLoader newBytecodeLoader(final SiteContext site, final String name, final String jar) {
 228  0
                 return UrlResourceLoader.newBytecodeLoader(site, name, jar);
 229  
             }
 230  
 
 231  
         });
 232  0
         return instance;
 233  
     }
 234  
 
 235  
 
 236  
     // Constructors --------------------------------------------------
 237  
 
 238  
     /** Creates a new instance of VelocityEngineFactory */
 239  0
     private VelocityEngineFactory(final Context cxt) {
 240  
 
 241  
 
 242  
         try{
 243  0
             INSTANCES_LOCK.writeLock().lock();
 244  
 
 245  0
             final Site site = cxt.getSite();
 246  
 
 247  0
             final SiteConfiguration siteConf = SiteConfiguration.instanceOf(ContextWrapper.wrap(
 248  
                     SiteConfiguration.Context.class,
 249  
                     cxt));
 250  
 
 251  0
             final StringBuilder directives = new StringBuilder();
 252  
 
 253  0
             for(int i=0; i < 10; ++i){
 254  0
                 final String d = siteConf.getProperty("velocity.directives." + i);
 255  
 
 256  0
                 if(null != d && d.length() > 0){
 257  
 
 258  0
                     directives.append(d + ',');
 259  
                 }
 260  
             }
 261  
 
 262  
             // truncate last ','
 263  0
             directives.setLength(directives.length()-1);
 264  
 
 265  0
             final Logger logger = Logger.getLogger(VELOCITY_LOGGER);
 266  
 
 267  0
             engine = new VelocityEngine(){
 268  
                 /** We override this method to dampen the
 269  
                  * <ERROR velocity: ResourceManager : unable to find resource ...>
 270  
                  * error messages in sesam.error
 271  
                  **/
 272  
                 @Override
 273  
                 public Template getTemplate(final String name)
 274  
                         throws ResourceNotFoundException, ParseErrorException, Exception {
 275  
 
 276  0
                     final Level level = logger.getLevel();
 277  0
                     logger.setLevel(Level.FATAL);
 278  
 
 279  0
                     final Template retValue = super.getTemplate(name);
 280  
 
 281  0
                     logger.setLevel(level);
 282  0
                     return retValue;
 283  
                 }
 284  
 
 285  
             };
 286  
 
 287  
 
 288  0
             final ClassLoader origLoader = Thread.currentThread().getContextClassLoader();
 289  
             try{
 290  
 
 291  0
                 engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS, LOGSYSTEM_CLASS);
 292  0
                 engine.setProperty(LOG_NAME, logger.getName());
 293  0
                 engine.setProperty(Velocity.RESOURCE_LOADER, "url");
 294  0
                 engine.setProperty("url.resource.loader.class", URLVelocityTemplateLoader.class.getName());
 295  
 
 296  0
                 if(VELOCITY_DEBUG) {
 297  0
                     engine.setProperty("url.resource.loader.cache", "false");
 298  0
                     engine.setProperty("velocimacro.library.autoreload", "true");
 299  
                 } else {
 300  0
                     engine.setProperty("url.resource.loader.cache", "true");
 301  0
                     engine.setProperty("url.resource.loader.modificationCheckInterval", "60");
 302  0
                     engine.setProperty(Velocity.RESOURCE_MANAGER_CLASS, QuickResourceManagerImpl.class.getName());
 303  0
                     engine.setProperty(Velocity.RESOURCE_MANAGER_CACHE_CLASS, QuickResourceCacheImpl.class.getName());
 304  0
                     engine.setProperty(Velocity.RESOURCE_MANAGER_DEFAULTCACHE_SIZE, "0");
 305  
 
 306  
                     // Use custom unbound quicker resource cache.
 307  0
                     engine.setProperty(QuickResourceCacheImpl.INITIAL_SIZE_PROPERTY, 1000);
 308  
                 }
 309  
 
 310  0
                 engine.setProperty(Site.NAME_KEY, site);
 311  
 
 312  0
                 engine.setProperty("input.encoding", "UTF-8");
 313  0
                 engine.setProperty("output.encoding", "UTF-8");
 314  0
                 engine.setProperty("userdirective", directives.toString());
 315  0
                 engine.setProperty(
 316  
                         "velocimacro.library",
 317  
                         // sesat provides VM_sesat_library.vm -- should not be overridden
 318  
                         site.getTemplateDir() + "/VM_sesat_library.vm,"
 319  
                         // VM_global_library is optional library a skin can provide (and/or override)
 320  
                         + (isResourceAvailable(site, site.getTemplateDir() + "/VM_global_library.vm")
 321  
                         ? site.getTemplateDir() + "/VM_global_library.vm," : "")
 322  
                         // VM_site_library is secondary optional library a skin can provide (and/or override)
 323  
                         + (isResourceAvailable(site, site.getTemplateDir() + "/VM_site_library.vm")
 324  
                         ? site.getTemplateDir() + "/VM_site_library.vm," : "")
 325  
                         );
 326  
 
 327  0
                 final SiteClassLoaderFactory.Context classContext = ContextWrapper.wrap(
 328  
                         SiteClassLoaderFactory.Context.class,
 329  0
                         new BaseContext() {
 330  
                             public Site getSite(){
 331  0
                                 return site;
 332  
                             }
 333  
                             public Spi getSpi() {
 334  0
                                 return Spi.VELOCITY_DIRECTIVES;
 335  
                             }
 336  
                         },
 337  
                         cxt);
 338  
 
 339  0
                 final ClassLoader ctrlClassLoader = SiteClassLoaderFactory.instanceOf(classContext).getClassLoader();
 340  0
                 Thread.currentThread().setContextClassLoader(ctrlClassLoader);
 341  0
                 engine.init();
 342  
 
 343  0
             }catch (Exception e) {
 344  0
                 throw new InfrastructureException(e);
 345  
             }finally{
 346  0
                 Thread.currentThread().setContextClassLoader(origLoader);
 347  0
             }
 348  
 
 349  0
             INSTANCES.put(site, this);
 350  
         }finally{
 351  0
             INSTANCES_LOCK.writeLock().unlock();
 352  0
         }
 353  0
     }
 354  
 
 355  
     // Public --------------------------------------------------------
 356  
 
 357  
     public VelocityEngine getEngine() {
 358  0
         return engine;
 359  
     }
 360  
 
 361  
     public boolean remove(final Site site) {
 362  
 
 363  
         try{
 364  0
             INSTANCES_LOCK.writeLock().lock();
 365  0
             return null != INSTANCES.remove(site);
 366  
         }finally{
 367  0
             INSTANCES_LOCK.writeLock().unlock();
 368  
         }
 369  
     }
 370  
 
 371  
     // Package protected ---------------------------------------------
 372  
 
 373  
     // Protected -----------------------------------------------------
 374  
 
 375  
     // Private -------------------------------------------------------
 376  
 
 377  
     private boolean isResourceAvailable(final Site site, final String resource){
 378  
 
 379  
         try {
 380  0
             final URLResourceLoader loader = new URLResourceLoader(site);
 381  0
             loader.getResourceStream(resource).close();
 382  0
             return true;
 383  
 
 384  0
         } catch (Exception ex) {
 385  0
             LOG.error("Resource not available: " + resource, ex);
 386  0
             return false;
 387  
         }
 388  
     }
 389  
 
 390  
 
 391  
     // Inner classes -------------------------------------------------
 392  
 }