View Javadoc

1   /*
2    * Copyright (C) 2006  Tom Gibara
3    *
4    * This library is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Lesser General Public
6    * License as published by the Free Software Foundation; either
7    * version 2.1 of the License, or (at your option) any later version.
8    *
9    * This library 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 GNU
12   * Lesser General Public License for more details.
13   *
14   * You should have received a copy of the GNU Lesser General Public
15   * License along with this library; if not, write to the Free Software
16   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17   */
18  package com.tomgibara.pronto.util;
19  
20  import java.security.AccessController;
21  import java.security.PrivilegedAction;
22  
23  /**
24   * <p>
25   * This class can be extended to provide a factory implementation that:
26   * </p>
27   * 
28   * <ul>
29   * <li>Looks to a specified system property for the name of a singleton class
30   * to instantiate.</li>
31   * <li>Instantiates a default class if no property is present.</li>
32   * <li>Caches the resulting instance (or resulting exception) for every call to
33   * getInstance()</li>
34   * </ul>
35   * 
36   * <p>
37   * Note that instantiation of the instance is eager, but any exception is
38   * recorded and repeatedly rethrown on each call to getInstance. The eager
39   * instantiation means that this factory implementation is fast, requires no
40   * synchronization, and is thread-safe.
41   * </p>
42   * 
43   * <p>
44   * It is expected that this class will be used by extension, or delegation:
45   * supplying the appropriate strings to the constructor and wrapping the
46   * getInstance method to narrow the thrown Exception.
47   * </p>
48   * 
49   * @param <T>
50   *            the type of object produced by the factory
51   * @author Tom Gibara
52   * 
53   */
54  
55  public class FactoryHelper<T> {
56  
57      // fields
58  
59      /**
60       * An exception which was cached during the initialization of the instance
61       * field, or null if the instance was created successfully.
62       */
63      private final Exception exception;
64  
65      /**
66       * The instance which was created by this factory during its construction.
67       */
68      private final T instance;
69  
70      // constructors
71  
72      /**
73       * Constructs a new factory which will create an instance of the class
74       * specified by either the value of the specified system property, or the
75       * supplied class name - exactly one of which may be null. Any exception
76       * which arises from attempting to instantiate the class is cached and
77       * thrown on any subsequent call to getInstance(). If no class loader is
78       * supplied, then the context class loader is used; If the context class
79       * loader is null the class loader of this class is used.
80       * 
81       * @param className
82       *            the class to which this factory is to create, may be null
83       * @param classLoader
84       *            the class loader from which to load the class, may be null
85       * @param propertyName
86       *            a system property which overrides the specified class name
87       */
88  
89      @SuppressWarnings("unchecked")
90      public FactoryHelper(final String className, final ClassLoader classLoader, final String propertyName) {
91          if (className == null && propertyName == null) throw new IllegalArgumentException(
92                  "both className and propertyName are null");
93          ClassLoader cl = classLoader;
94          if (cl == null) cl = Thread.currentThread().getContextClassLoader();
95          if (cl == null) cl = getClass().getClassLoader();
96  
97          T i = null;
98          Exception e = null;
99          try {
100             String cn;
101             if (propertyName == null) {
102                 cn = className;
103             } else {
104                 cn = AccessController.doPrivileged(new PrivilegedAction<String>() {
105                     public String run() {
106                         return System.getProperty(propertyName, className);
107                     }
108                 });
109                 if (cn == null) throw new NullPointerException(String.format(
110                         "Unable to obtain a className from property: %s", propertyName));
111             }
112             Class<T> clss = (Class<T>) cl.loadClass(cn);
113             i = clss.newInstance();
114         } catch (Exception ex) {
115             e = ex;
116         } finally {
117             exception = e;
118             instance = i;
119         }
120     }
121 
122     // methods
123 
124     /**
125      * The object instance created by this factory. The same object is returned
126      * on each call to this method. Note that the exception thrown by this
127      * method is 'recycled'.
128      * 
129      * @return the instance created by this factory
130      * @throws Exception
131      *             the exception which occured trying to create the instance.
132      */
133 
134     public T getInstance() throws Exception {
135         if (exception != null) throw exception;
136         return instance;
137     }
138 
139 }