Home | History | Annotate | Download | only in clearsilver
      1 /*
      2  * Copyright (C) 2010 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package org.clearsilver;
     18 
     19 import java.lang.reflect.Constructor;
     20 import java.util.concurrent.locks.ReadWriteLock;
     21 import java.util.concurrent.locks.ReentrantReadWriteLock;
     22 import java.util.logging.Level;
     23 import java.util.logging.Logger;
     24 
     25 /**
     26  * This class holds static methods for getting and setting the CS and HDF
     27  * factory used throughout the Java Clearsilver Framework.
     28  * Clients are <strong>strongly encouraged</strong> to not use this class, and
     29  * instead directly inject {@link ClearsilverFactory} into the classes that
     30  * need to create {@link HDF} and {@link CS} instances.
     31  * For now, projects should set the {@link ClearsilverFactory} in FactoryLoader
     32  * and use the singleton accessor {@link #getClearsilverFactory()} if proper
     33  * dependency injection is not easy to implement.
     34  * <p>
     35  * Allows the default implementation to be the original JNI version without
     36  * requiring users that don't want to use the JNI version to have to link
     37  * it in.  The ClearsilverFactory object to use can be either passed into the
     38  * {@link #setClearsilverFactory} method or the class name can be specified
     39  * in the Java property {@code  org.clearsilver.defaultClearsilverFactory}.
     40  */
     41 public final class FactoryLoader {
     42   private static final Logger logger =
     43       Logger.getLogger(FactoryLoader.class.getName());
     44 
     45   private static final String DEFAULT_CS_FACTORY_CLASS_PROPERTY_NAME =
     46       "org.clearsilver.defaultClearsilverFactory";
     47   private static final String DEFAULT_CS_FACTORY_CLASS_NAME =
     48       "org.clearsilver.jni.JniClearsilverFactory";
     49 
     50   // ClearsilverFactory to be used when constructing objects.  Allows
     51   // applications to subclass the CS and HDF objects used in Java Clearsilver
     52   private static ClearsilverFactory clearsilverFactory = null;
     53 
     54   // Read/Write lock for global factory pointer.
     55   private static final ReadWriteLock factoryLock = new ReentrantReadWriteLock();
     56 
     57   // Getters and setters
     58   /**
     59    * Get the {@link org.clearsilver.ClearsilverFactory} object to be used by
     60    * disparate parts of the application.
     61    */
     62   public static ClearsilverFactory getClearsilverFactory() {
     63     factoryLock.readLock().lock();
     64     if (clearsilverFactory == null) {
     65       factoryLock.readLock().unlock();
     66       factoryLock.writeLock().lock();
     67       try {
     68         if (clearsilverFactory == null) {
     69           clearsilverFactory = newDefaultClearsilverFactory();
     70         }
     71         factoryLock.readLock().lock();
     72       } finally {
     73         factoryLock.writeLock().unlock();
     74       }
     75     }
     76     ClearsilverFactory returned = clearsilverFactory;
     77     factoryLock.readLock().unlock();
     78     return returned;
     79   }
     80 
     81   /**
     82    * Set the {@link org.clearsilver.ClearsilverFactory} to be used by
     83    * the application. If parameter is {@code null}, then the default factory
     84    * implementation will be used the next time {@link #getClearsilverFactory()}
     85    * is called.
     86    *
     87    * @return the previous factory (may return {@code null})
     88    */
     89   public static ClearsilverFactory setClearsilverFactory(
     90       ClearsilverFactory clearsilverFactory) {
     91     factoryLock.writeLock().lock();
     92     try {
     93       ClearsilverFactory previousFactory = FactoryLoader.clearsilverFactory;
     94       FactoryLoader.clearsilverFactory = clearsilverFactory;
     95       return previousFactory;
     96     } finally {
     97       factoryLock.writeLock().unlock();
     98     }
     99   }
    100 
    101   private static ClearsilverFactory newDefaultClearsilverFactory() {
    102     String factoryClassName =
    103         System.getProperty(DEFAULT_CS_FACTORY_CLASS_PROPERTY_NAME,
    104             DEFAULT_CS_FACTORY_CLASS_NAME);
    105     try {
    106       ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    107       Class<ClearsilverFactory> clazz =
    108           loadClass(factoryClassName, classLoader);
    109       Constructor<ClearsilverFactory> constructor = clazz.getConstructor();
    110       return constructor.newInstance();
    111     } catch (Exception e) {
    112       String errMsg = "Unable to load default ClearsilverFactory class: \"" +
    113           factoryClassName + "\"";
    114       logger.log(Level.SEVERE, errMsg, e);
    115       throw new RuntimeException(errMsg, e);
    116     }
    117   }
    118 
    119   private static Class<ClearsilverFactory> loadClass(String className,
    120       ClassLoader classLoader) throws ClassNotFoundException {
    121     return (Class<ClearsilverFactory>) Class.forName(className, true,
    122         classLoader);
    123   }
    124 }
    125