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
17package org.clearsilver;
18
19import java.lang.reflect.Constructor;
20import java.util.concurrent.locks.ReadWriteLock;
21import java.util.concurrent.locks.ReentrantReadWriteLock;
22import java.util.logging.Level;
23import 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 */
41public 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