KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > mondrian > util > ObjectFactory


1 /*
2 // $Id: //open/mondrian/src/main/mondrian/util/ObjectFactory.java#9 $
3 // This software is subject to the terms of the Common Public License
4 // Agreement, available at the following URL:
5 // http://www.opensource.org/licenses/cpl.html.
6 // Copyright (C) 2007-2007 Julian Hyde and others
7 // All Rights Reserved.
8 // You must accept the terms of that agreement to use this software.
9 */

10 package mondrian.util;
11
12 import org.eigenbase.util.property.StringProperty;
13 import java.lang.reflect.Constructor JavaDoc;
14 import java.lang.reflect.InvocationHandler JavaDoc;
15 import java.lang.reflect.Proxy JavaDoc;
16 import java.util.Properties JavaDoc;
17
18 /**
19  * Concrete derived classes of the generic <code>ObjectFactory</code> class
20  * are used to produce an implementation of an interface (a
21  * normal interface implementation or a Proxy). In general, a
22  * factory should produce a default implementation for general application
23  * use as well as particular implementations used during testing.
24  * During testing of application code and during normal execution,
25  * the application code uses one of the <code>ObjectFactory</code>'s
26  * methods for producing implementation instances - the same method is
27  * used both for test and non-test modes. There are two ways of
28  * modifying the implementation returned to the application code.
29  * The first is for the application to use Properties.
30  * The <code>ObjectFactory</code> implementation looks for a given
31  * property (by default the name of the property is the class name
32  * of the interfaceClass object) and if found uses it as the classname
33  * to create.
34  * A second approach is to use a ThreadLocal; if the ThreadLocal
35  * is non-empty then use it as the classname.
36  * <p>
37  * When to use a Factory?
38  * <p>
39  * Everyone has an opinion. For me, there are two criteria: enabling
40  * unit testing and providing end-user/developer-customizer overriding.
41  * <p>
42  * If a method has side-effects, either its result depends upon
43  * a side-effect or calling it causes a side-effect, then the Object
44  * hosting the method is a candidate for having a factory. Why?
45  * Well, consider the case where a method returns the value of
46  * a System property and the System property is determined only once
47  * and set to a static final variable:
48  * <pre>
49  * class OneValue {
50  * private static final boolean propValue;
51  * static {
52  * propValue = Boolean.getBoolean("com.app.info.value");
53  * }
54  * .....
55  * public boolean hasInfo() {
56  * return propValue;
57  * }
58  * }
59  * </pre>
60  * <p>
61  * In this case, only one value is ever returned. If you have a
62  * module, a client of the above code, that uses the value returned
63  * by a call to the
64  * <code>hasInfo()</code> method, how do you write a unit test of
65  * your module that tests both possible return values?
66  * You can not, its value is based upon a side-effect, an external
67  * value that can not be controled by the unit test.
68  * If the <code>OneValue</code> class was an interface and there was a factory,
69  * then the unit test could arrange that its own version of the
70  * <code>OneValue</code>
71  * interface was returned and in one test arrange that <code>true</code>
72  * was returned and in a second test, arrange that <code>false</code>
73  * was returned.
74  * <p>
75  * The above is a trivial example of code that disallows clients of the
76  * code from being properly tested.
77  * <p>
78  * Another example might be a module that directly initializes a JMS
79  * queue and receives JMS message
80  * from the JMS queue. This code can not be tested without having a live
81  * JMS queue. On the other hand, if one defines an interface allowing
82  * one to wrap access to the JMS queue and accesses the implementation
83  * via a factory, then unit tests can be create that use a mock
84  * JMS queue.
85  * <p>
86  * With regards to providing end-user/developer-customizer overriding,
87  * its generally good to have a flexible application framework.
88  * Experimental or just different implementations can be developed and
89  * tested without having to touch a lot of the application code itself.
90  * <p>
91  * There is, of course, a trade-off between the use of a factory
92  * and the size or simplicity of the object being created.
93  * <p>
94  * What are the requirements for a template ObjectFactory?
95  * <p>
96  * First, every implementation must support the writing of unit tests.
97  * What this means it that test cases can override what the factory
98  * produces. The test cases can all use the same produced Object or
99  * each can request an Object targeted to its particular test. All this
100  * without changing the <code>default</code> behavior of the factory.
101  * <p>
102  * Next, it should be possible to create a factory from the template that
103  * is intended to deliver the same Object each time it is called, a
104  * different, new Object each time it is called, or, based on the
105  * calling environment (parameters, properties, <code>ThreadLocal</code>,
106  * etc.) one of a set of Objects. These are possible <code>default</code>
107  * behaviors, but, again, they can be overridden for test purposes.
108  * <p>
109  * While a factory has a <code>default</code> behavior in an
110  * application, it must be possible for every factory's behavior
111  * in that application to be globally overridden. What that means is
112  * if the application designer has dictated a <code>default</code>, the
113  * application user should be able to change the default. An example of
114  * this is overriding what Object is returned based upon a
115  * <code>System</code> property value.
116  * <p>
117  * Lastly, every factory is a singleton - if an interface with
118  * an implementation whose creation is mediated by a factory, then
119  * there is a single factory that does that creating.
120  * This does not mean that such a factory always return the same value,
121  * rather that there is only one instance of the factory itself.
122  * <p>
123  * The following is an example class that generates a factory
124  * singleton. In this case, the factory extends the
125  * <code>ObjectFactory</code>
126  * rather than the <code>ObjectFactory.Singleton</code>:
127  * <pre>
128  *
129  * public final class FooFactory extends ObjectFactory<Foo> {
130  * // The single instance of the factory
131  * private static final FooFactory factory;
132  * static {
133  * factory = new FooFactory();
134  * }
135  * public static FooFactory instance() {
136  * return factory;
137  * }
138  * ..........
139  * private FooFactory() {
140  * super(Foo.class);
141  * }
142  * ..........
143  * }
144  *
145  * </pre>
146  * <p>
147  * There are multiple ways of creating derived classes that have support
148  * for unit testing. A very simple way is to use <code>ThreadLocal</code>s.
149  *
150  * <pre>
151  *
152  * private static final ThreadLocal ClassName = new ThreadLocal();
153  * private static String getThreadLocalClassName() {
154  * return (String) ClassName.get();
155  * }
156  * public static void setThreadLocalClassName(String className) {
157  * ClassName.set(className);
158  * }
159  * public static void clearThreadLocalClassName() {
160  * ClassName.set(null);
161  * }
162  * ..........
163  * protected String getClassName() {
164  * return getThreadLocalClassName();
165  * }
166  *
167  * </pre>
168  * <p>
169  * Here, the unit test will call the <code>setThreadLocalClassName</code>
170  * method setting it with the class name of a specialized implemetation of
171  * the template interface. In the <code>finally</code> clause of the
172  * unit test, it is very important that there be a call to the
173  * <code>clearThreadLocalClassName</code> method so that other
174  * tests, etc. do not get an instance of the test-specific specialized
175  * implementation.
176  * <p>
177  * The following is an example unit test that uses the factory's
178  * <code>ThreadLocal</code> to override the implementation that is returned.
179  *
180  * <pre>
181  * interface Boo {
182  * boolean getValue();
183  * .......
184  * }
185  * class NormalBooImpl implements Boo {
186  * public boolean getValue() { ... }
187  * .......
188  * }
189  * class MyCode {
190  * private Boo boo;
191  * MyCode() {
192  * boo = BooFactory.instance().getObject();
193  * }
194  * .......
195  * int getValue() {
196  * if (boo.getValue()) {
197  * return 1;
198  * } else {
199  * return 0;
200  * }
201  *
202  * }
203  * }
204  *
205  * class MyCodeTest {
206  * private static boolean testValue;
207  * static class BooTest1 implements Boo {
208  * public boolean getValue() {
209  * return MyTest.testValue;
210  * }
211  * .....
212  * }
213  * static class BooTest2 implements
214  * java.lang.reflect.InvocationHandler {
215  * private final Boo boo;
216  * public BooTest2() {
217  * // remove test class name
218  * BooFactory.clearThreadLocalClassName();
219  * // get default Boo implementation
220  * this.boo = BooFactory.instance().getObject();
221  * }
222  * public Object invoke(Object proxy, Method method, Object[] args)
223  * throws Throwable {
224  * if (method.getName().equals("getValue")) [
225  * return new Boolean(MyTest.testValue);
226  * } else {
227  * return method.invoke(this.boo, args);
228  * }
229  * }
230  * }
231  * public void test1() {
232  * try {
233  * // Factory will creates test class
234  * BooFactory.setThreadLocalClassName("MyTest.BooTest1");
235  *
236  * MyTest.testValue = true;
237  * MyCode myCode = new MyCode();
238  * int value = myCode.getValue();
239  * assertTrue("Value not 1", (value == 1));
240  *
241  * MyTest.testValue = false;
242  * myCode = new MyCode();
243  * value = myCode.getValue();
244  * assertTrue("Value not 0", (value == 0));
245  * } finally {
246  * BooFactory.clearThreadLocalClassName();
247  * }
248  * }
249  * public void test2() {
250  * try {
251  * // Use InvocationHandler and Factory Proxy capability
252  * BooFactory.setThreadLocalClassName("MyTest.BooTest2");
253  *
254  * MyTest.testValue = true;
255  * MyCode myCode = new MyCode();
256  * int value = myCode.getValue();
257  * assertTrue("Value not 1", (value == 1));
258  *
259  * MyTest.testValue = false;
260  * myCode = new MyCode();
261  * value = myCode.getValue();
262  * assertTrue("Value not 0", (value == 0));
263  * } finally {
264  * BooFactory.clearThreadLocalClassName();
265  * }
266  * }
267  * }
268  *
269  * </pre>
270  * <p>
271  * While this is a very simple example, it shows how using such factories
272  * can aid in creating testable code. The MyCode method is a client of
273  * the Boo implementation. How to test the two different code branches the
274  * method can take? Because the Boo object is generated by a factory,
275  * one can override what object the factory returns.
276  *
277  * @author <a>Richard M. Emberson</a>
278  * @since Feb 01 2007
279  * @version $Id: //open/mondrian/src/main/mondrian/util/ObjectFactory.java#9 $
280  */

281 public abstract class ObjectFactory<V> {
282
283     private static final Class JavaDoc[] EMPTY_CLASS_ARRAY = new Class JavaDoc[0];
284     private static final Object JavaDoc[] EMPTY_OBJECT_ARRAY = new Object JavaDoc[0];
285
286     /**
287      * The type of the object to be generated.
288      */

289     private final Class JavaDoc<V> interfaceClass;
290
291     /**
292      * Creates a new factory object. The <code>interfaceClass</code> parameter
293      * is used to cast the object generated to type right type.
294      *
295      * @param interfaceClass the class object for the interface implemented
296      * by the objects returned by this factory
297      *
298      */

299     protected ObjectFactory(final Class JavaDoc<V> interfaceClass) {
300         this.interfaceClass = interfaceClass;
301     }
302
303     /**
304      * Constructs an object where the System Properties can be used
305      * to look up a class name.
306      * The constructor for the object takes no parameters.
307      *
308      * @return the newly created object
309      * @throws CreationException if unable to create the object
310      */

311     protected final V getObject() throws CreationException {
312         return getObject(System.getProperties());
313     }
314
315     /**
316      * Constructs an object where the <code>Properties</code> parameter can
317      * be used to look up a class name.
318      * The constructor for the object takes no parameters.
319      *
320      * @param props the property definitions to use to determine the
321      * implementation class
322      *
323      * @return the newly created object
324      * @throws CreationException if unable to create the object
325      */

326     protected final V getObject(final Properties JavaDoc props) throws CreationException {
327         return getObject(props, EMPTY_CLASS_ARRAY, EMPTY_OBJECT_ARRAY);
328     }
329
330     /**
331      * Constructs an object where the <code>parameterTypes</code> and
332      * <code>parameterValues</code> are constructor parameters and
333      * System Properties are used to look up a class name.
334      *
335      * @param parameterTypes the class parameters that define the signature
336      * of the constructor to use
337      * @param parameterValues the values to use to construct the current
338      * instance of the object
339      * @return the newly created object
340      * @throws CreationException if unable to create the object
341      */

342     protected final V getObject(final Class JavaDoc[] parameterTypes,
343                                 final Object JavaDoc[] parameterValues)
344             throws CreationException {
345         return getObject(System.getProperties(),
346                          parameterTypes,
347                          parameterValues);
348     }
349
350     /**
351      * Constructs an object where the <code>parameterTypes</code> and
352      * <code>parameterValues</code> are constructor parameters and
353      * Properties parameter is used to look up a class name.
354      * <p>
355      * This returns a new instance of the Object each time its
356      * called (assuming that if the method <code>getDefault</code>,
357      * which derived classes implement), if called, creates a new
358      * object each time.
359      *
360      * @param props the property definitions to use to determine the
361      * @param parameterTypes the class parameters that define the signature
362      * of the constructor to use
363      * @param parameterValues the values to use to construct the current
364      * instance of the object
365      * @return the newly created object
366      * @throws CreationException if unable to create the object
367      */

368     protected V getObject(final Properties JavaDoc props,
369                           final Class JavaDoc[] parameterTypes,
370                           final Object JavaDoc[] parameterValues)
371             throws CreationException {
372
373         // Unit test override
374
final String JavaDoc className = getClassName();
375         if (className != null) {
376             return getObject(className, parameterTypes, parameterValues);
377         }
378
379         final String JavaDoc propClassName = getClassName(props);
380         return (propClassName != null)
381                 // User overriding application default
382
? getObject(propClassName, parameterTypes, parameterValues)
383                 // Get application default
384
: getDefault(parameterTypes, parameterValues);
385     }
386
387     /**
388      * Creates an instance with the given <code>className</code>,
389      * <code>parameterTypes</code> and <code>parameterValues</code> or
390      * throw a <code>CreationException</code>. There are two different
391      * mechanims available. The first is to uses reflection
392      * to create the instance typing the generated Object based upon
393      * the <code>interfaceClass</code> factory instance object.
394      * With the second the <code>className</code> is an class that implements
395      * the <code>InvocationHandler</code> interface and in this case
396      * the <code>java.lang.reflect.Proxy</code> class is used to
397      * generate a proxy.
398      *
399      * @param className the class name used to create Object instance
400      * @param parameterTypes the class parameters that define the signature
401      * of the constructor to use
402      * @param parameterValues the values to use to construct the current
403      * instance of the object
404      * @return the newly created object
405      * @throws CreationException if unable to create the object
406      */

407     protected V getObject(final String JavaDoc className,
408                           final Class JavaDoc[] parameterTypes,
409                           final Object JavaDoc[] parameterValues)
410             throws CreationException {
411         try {
412             // As a place to begin google:
413
// org.apache.cxf.BusFactoryHelper.java
414
final ClassLoader JavaDoc loader =
415                 Thread.currentThread().getContextClassLoader();
416             final Class JavaDoc<?> genericClass =
417                 Class.forName(className, true, loader);
418
419             // Are we creating a Proxy or an instance?
420
if (InvocationHandler JavaDoc.class.isAssignableFrom(genericClass)) {
421                 final Constructor JavaDoc constructor =
422                     genericClass.getConstructor(parameterTypes);
423                 InvocationHandler JavaDoc handler = (InvocationHandler JavaDoc)
424                         constructor.newInstance(parameterValues);
425                 return (V) Proxy.newProxyInstance(
426                             loader,
427                             new Class JavaDoc[] { this.interfaceClass },
428                             handler);
429             } else {
430                 final Class JavaDoc<? extends V> specificClass =
431                     asSubclass(this.interfaceClass, genericClass);
432                 final Constructor JavaDoc<? extends V> constructor =
433                     specificClass.getConstructor(parameterTypes);
434
435                 return constructor.newInstance(parameterValues);
436             }
437
438         } catch (Exception JavaDoc exc) {
439             throw new CreationException("Error creating object of type \"" +
440                         this.interfaceClass.getName() + "\"" , exc);
441         }
442     }
443
444     /**
445      * This is a back port of a 1.5 version Class method.
446      *
447      * @param clazz the base class which the genericClass will be case
448      * @param genericClass the class to be cast to the base clazz
449      * @return this <tt>Class</tt> object, cast to represent a subclass of
450      * the specified class object.
451      * @throws ClassCastException if this <tt>Class</tt> object does
452      * not represent a subclass of the specified class (here "subclass"
453      * includes the class itself).
454      */

455     private static <V> Class JavaDoc<? extends V> asSubclass(final Class JavaDoc<V> clazz,
456                                                      final Class JavaDoc<?> genericClass) {
457         if (clazz.isAssignableFrom(genericClass)) {
458             return (Class JavaDoc<? extends V>) genericClass;
459         } else {
460             throw new ClassCastException JavaDoc(genericClass.toString());
461         }
462     }
463
464     /**
465      * Returns the name of a class to use to create an object.
466      * The default implementation returns null but derived
467      * classes can return a class name.
468      * <p>
469      * This method is the primary mechanism for supporting Unit testing.
470      * A derived class can have, as an example, this method return
471      * the value of a <code>ThreadLocal</code>. For testing it
472      * return a class name while for normal use it returns <code>null</code>.
473      *
474      * @return <code>null</code> or a class name
475      */

476     protected String JavaDoc getClassName() {
477         return null;
478     }
479
480     /**
481      * Returns the name of a class to use to create an object.
482      * The factory's <code>StringProperty</code> is gotten and
483      * if it has a non-null value, then that is returned. Otherwise,
484      * the <code>StringProperty</code>'s name (path) is used as the
485      * name to probe the <code>Properties</code> object for a value.
486      * This method is allowed to return null.
487      *
488      * @return <code>null</code> or a class name
489      */

490     protected String JavaDoc getClassName(final Properties JavaDoc props) {
491         final StringProperty stringProp = getStringProperty();
492         final String JavaDoc className = stringProp.get();
493         return (className != null)
494                 ? className
495                 : (props == null)
496                     ? null : props.getProperty(stringProp.getPath());
497     }
498
499     /**
500      * Return the <code>StringProperty</code> associated with this factory.
501      *
502      * @return the <code>StringProperty</code>
503      */

504     protected abstract StringProperty getStringProperty();
505
506     /**
507      * For most uses (other than testing) this is the method that derived
508      * classes implement that return the desired object.
509      *
510      * @param parameterTypes the class parameters that define the signature
511      * of the constructor to use
512      * @param parameterValues the values to use to construct the current
513      * instance of the object
514      * @return the newly created object
515      * @throws CreationException if unable to create the object
516      */

517     protected abstract V getDefault(Class JavaDoc[] parameterTypes,
518                                     Object JavaDoc[] parameterValues)
519         throws CreationException;
520
521     /**
522      * Factory method which creates an exception to be thrown
523      * if an object can not be created.
524      *
525      * @return
526      */

527     // REVIEW: jhyde, 2007/2/4: CreationException is superfluous, since it's
528
// unlikely that anyone will want to handle it. This code should wrap the
529
// error using Util.newError, just like elsewhere in mondrian.
530
protected CreationException defaultCreationException() {
531         return new CreationException("Error creating object of type \"" +
532                         this.interfaceClass.getName() + "\"");
533     }
534
535     /**
536      * Get the current override values in the opaque context object and
537      * clear those values within the Factory.
538      * <p>
539      * This is used in testing.
540      *
541      * @return the test <code>Context</code> object.
542      */

543     public Object JavaDoc removeContext() {
544         return null;
545     }
546
547     /**
548      * Restore the context object resetting override values.
549      * <p>
550      * This is used in testing.
551      *
552      * @param context the context object to be restored.
553      */

554     public void restoreContext(final Object JavaDoc context) {
555         // empty
556
}
557
558
559     /**
560      * Implementation of ObjectFactory
561      * that returns only a single instance of the Object.
562      */

563     public abstract static class Singleton<T> extends ObjectFactory<T> {
564
565         /**
566          * The single instance of the object created by the factory.
567          */

568         protected T singleInstance;
569
570         /**
571          * The test single instance of the object created by the factory.
572          * Creating this <code>testSingleInstance</code> does not change the
573          * current value of the <code>singleInstance</code> variable.
574          */

575         protected T testSingleInstance;
576
577         /**
578          * Creates a new singleton factory object. The
579          * <code>interfaceClass</code> parameter
580          * is used to cast the object generated to type right type.
581          *
582          * @param interfaceClass the class object for the interface implemented
583          * by the objects returned by this factory
584          */

585         protected Singleton(final Class JavaDoc<T> interfaceClass) {
586             super(interfaceClass);
587         }
588
589         /**
590          * Returns the singleton Object.
591          * The first time this is called, an object is created where
592          * the <code>parameterTypes</code> and
593          * <code>parameterValues</code> are constructor parameters and
594          * Properties parameter is used to look up a class name.
595          * <p>
596          * This returns a same instance of the Object each time its
597          * called except if the <code>getClassName</code> method
598          * returns a non-null class name which should only
599          * happen as needed for unit testing.
600          *
601          * @param props the property definitions to use to determine the
602          * @param parameterTypes the class parameters that define the signature
603          * of the constructor to use
604          * @param parameterValues the values to use to construct the current
605          * instance of the object
606          * @return the newly created object
607          * @throws CreationException if unable to create the object
608          */

609         protected T getObject(final Properties JavaDoc props,
610                               final Class JavaDoc[] parameterTypes,
611                               final Object JavaDoc[] parameterValues)
612                 throws CreationException {
613
614             // Unit test override, do not use application instance.
615
final String JavaDoc className = getClassName();
616             if (className != null) {
617                 if (this.testSingleInstance == null) {
618                     this.testSingleInstance = getTestObject(className,
619                                                     parameterTypes,
620                                                     parameterValues);
621                 }
622                 return this.testSingleInstance;
623             }
624
625             // NOTE: Should we distinguish between any Properties Object
626
// and that returned by System? When its the System's
627
// Properties Object (which is not a final instance variable
628
// within the System class), then its for sure the user
629
// providing a global override. If its not the System
630
// Properties object, then it may or may not be a global
631
// override so we may not want to set the singleInstance
632
// to it. For now I am ignoring the issue.
633
if (this.singleInstance == null) {
634                 final String JavaDoc propClassName = getClassName(props);
635
636                 this.singleInstance = (propClassName != null)
637                         // The user overriding application default
638
? getObject(propClassName, parameterTypes, parameterValues)
639                         // Get application default
640
: getDefault(parameterTypes, parameterValues);
641
642             }
643             return this.singleInstance;
644         }
645
646         /**
647          * Create an instance for test purposes.
648          *
649          * @param className the class name used to create Object instance
650          * @param parameterTypes the class parameters that define the signature
651          * of the constructor to use
652          * @param parameterValues the values to use to construct the current
653          * instance of the object
654          * @return the newly created object
655          * @throws CreationException if unable to create the object
656          */

657         protected T getTestObject(final String JavaDoc className,
658                                   final Class JavaDoc[] parameterTypes,
659                                   final Object JavaDoc[] parameterValues)
660                 throws CreationException {
661             return getObject(className, parameterTypes, parameterValues);
662         }
663     }
664
665     /**
666      * This is for testing only.
667      * <p>
668      * <code>Context</code> contain the Factory implementation specific
669      * non-default values and mechanism for overriding the default
670      * instance type returned by the Factory.
671      * Factory implementation can extend the <code>Context</code> interface
672      * to capture its specific override values.
673      * If, for example, a Factory implementation uses a <code>ThreadLocal</code>
674      * to override the default instance type for unit tests, then the
675      * <code>Context</code>
676      * will hold the current value of the <code>ThreadLocal</code>.
677      * Getting the Context, clears the <code>ThreadLocal</code> value.
678      * This allows the tester who wishes to create code that will provide
679      * a wrapper around the default instance type to register their
680      * wrapper class name with the <code>ThreadLocal</code>, and, within
681      * the wrapper constructor, get the <code>Context</code>, get
682      * a default instance, and then restore the <code>Context</code>.
683      */

684     public interface Context {
685     }
686 }
687
688 // End ObjectFactory.java
689
Popular Tags