Skip to content

Commit

Permalink
MONDRIAN
Browse files Browse the repository at this point in the history
   Refactored ObjectFactory adding ability to return a
   java.lang.reflect.Proxy. In addition, the ObjectFactory
   has two additional test methods (removeContext and restoreContext)
   that allow test code to remove and restore any override
   information for the particular factory (allowing test code
   to get to the default implementation) - this enables the
   creation of test implementations that wrap the default implementation.

[git-p4: depot-paths = "//open/mondrian/": change = 8720]
  • Loading branch information
Richard Emberson committed Feb 14, 2007
1 parent 1f004dd commit 1c6854d
Show file tree
Hide file tree
Showing 5 changed files with 143 additions and 31 deletions.
2 changes: 1 addition & 1 deletion src/main/mondrian/rolap/RolapConnection.java
Expand Up @@ -397,7 +397,7 @@ public void memoryUsageNotification(long used, long max) {
}
}
Listener listener = new Listener(query);
MemoryMonitor mm = MemoryMonitorFactory.instance().getObject();
MemoryMonitor mm = MemoryMonitorFactory.getMemoryMonitor();
try {
mm.addListener(listener);
// Check to see if we must punt
Expand Down
5 changes: 4 additions & 1 deletion src/main/mondrian/util/CreationException.java
Expand Up @@ -9,8 +9,11 @@
*/
package mondrian.util;

public class CreationException extends RuntimeException {
import mondrian.olap.MondrianException;

public class CreationException extends MondrianException {
public CreationException() {
super();
}
public CreationException(String s) {
super(s);
Expand Down
8 changes: 4 additions & 4 deletions src/main/mondrian/util/MemoryMonitorFactory.java
Expand Up @@ -53,10 +53,10 @@ public final class MemoryMonitorFactory
/**
* Access the <code>MemoryMonitorFactory</code> instance.
*
* @return the <code>MemoryMonitorFactory</code>.
* @return the <code>MemoryMonitor</code>.
*/
public static MemoryMonitorFactory instance() {
return factory;
public static MemoryMonitor getMemoryMonitor() {
return factory.getObject();
}

/**
Expand All @@ -79,7 +79,7 @@ private static String getThreadLocalClassName() {
/**
* Sets the class name of a <code>MemoryMonitor</code> implementation.
* This should be called (obviously) before calling the
* <code>MemoryMonitorFactory</code> <code>getObject</code>
* <code>MemoryMonitorFactory</code> <code>getMemoryMonitor</code>
* method to get the <code>MemoryMonitor</code> implementation.
* Generally, this is only used for testing.
*
Expand Down
153 changes: 131 additions & 22 deletions src/main/mondrian/util/ObjectFactory.java
Expand Up @@ -10,11 +10,14 @@
package mondrian.util;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Properties;

/**
* Concrete derived classes of the generic <code>ObjectFactory</code> class
* are used to produce an implementation of an interface. In general, a
* are used to produce an implementation of an interface (a
* normal interface implementation or a Proxy). In general, a
* factory should produce a default implementation for general application
* use as well as particular implementations used during testing.
* During testing of application code and during normal execution,
Expand Down Expand Up @@ -178,6 +181,10 @@
* boolean getValue();
* .......
* }
* class NormalBooImpl implements Boo {
* public boolean getValue() { ... }
* .......
* }
* class MyCode {
* private Boo boo;
* MyCode() {
Expand All @@ -196,14 +203,52 @@
*
* class MyCodeTest {
* private static boolean testValue;
* static class BooTest implements Boo {
* static class BooTest1 implements Boo {
* public boolean getValue() {
* return MyTest.testValue;
* }
* .....
* }
* static class BooTest2 implements
* java.lang.reflect.InvocationHandler {
* private final Boo boo;
* public BooTest2() {
* // remove test class name
* BooFactory.clearThreadLocalClassName();
* // get default Boo implementation
* this.boo = BooFactory.instance().getObject();
* }
* public Object invoke(Object proxy, Method method, Object[] args)
* throws Throwable {
* if (method.getName().equals("getValue")) [
* return new Boolean(MyTest.testValue);
* } else {
* return method.invoke(this.boo, args);
* }
* }
* }
* public void test1() {
* try {
* // Factory will creates test class
* BooFactory.setThreadLocalClassName("MyTest.BooTest1");
*
* MyTest.testValue = true;
* MyCode myCode = new MyCode();
* int value = myCode.getValue();
* assertTrue("Value not 1", (value == 1));
*
* MyTest.testValue = false;
* myCode = new MyCode();
* value = myCode.getValue();
* assertTrue("Value not 0", (value == 0));
* } finally {
* BooFactory.clearThreadLocalClassName();
* }
* }
* public void test() {
* public void test2() {
* try {
* BooFactory.setThreadLocalClassName("MyTest.BooTest");
* // Use InvocationHandler and Factory Proxy capability
* BooFactory.setThreadLocalClassName("MyTest.BooTest2");
*
* MyTest.testValue = true;
* MyCode myCode = new MyCode();
Expand Down Expand Up @@ -262,7 +307,7 @@ protected ObjectFactory(final Class<V> interfaceClass) {
* @return the newly created object
* @throws CreationException if unable to create the object
*/
public final V getObject() throws CreationException {
protected final V getObject() throws CreationException {
return getObject(System.getProperties());
}

Expand All @@ -277,7 +322,7 @@ public final V getObject() throws CreationException {
* @return the newly created object
* @throws CreationException if unable to create the object
*/
public final V getObject(final Properties props) throws CreationException {
protected final V getObject(final Properties props) throws CreationException {
return getObject(props, EMPTY_CLASS_ARRAY, EMPTY_OBJECT_ARRAY);
}

Expand All @@ -293,8 +338,8 @@ public final V getObject(final Properties props) throws CreationException {
* @return the newly created object
* @throws CreationException if unable to create the object
*/
public final V getObject(final Class[] parameterTypes,
final Object[] parameterValues)
protected final V getObject(final Class[] parameterTypes,
final Object[] parameterValues)
throws CreationException {
return getObject(System.getProperties(),
parameterTypes,
Expand All @@ -319,9 +364,9 @@ public final V getObject(final Class[] parameterTypes,
* @return the newly created object
* @throws CreationException if unable to create the object
*/
public V getObject(final Properties props,
final Class[] parameterTypes,
final Object[] parameterValues)
protected V getObject(final Properties props,
final Class[] parameterTypes,
final Object[] parameterValues)
throws CreationException {

// Unit test override
Expand All @@ -341,9 +386,14 @@ public V getObject(final Properties props,
/**
* Creates an instance with the given <code>className</code>,
* <code>parameterTypes</code> and <code>parameterValues</code> or
* throw a <code>CreationException</code>. This uses reflection
* throw a <code>CreationException</code>. There are two different
* mechanims available. The first is to uses reflection
* to create the instance typing the generated Object based upon
* the <code>interfaceClass</code> factory instance object.
* With the second the <code>className</code> is an class that implements
* the <code>InvocationHandler</code> interface and in this case
* the <code>java.lang.reflect.Proxy</code> class is used to
* generate a proxy.
*
* @param className the class name used to create Object instance
* @param parameterTypes the class parameters that define the signature
Expand All @@ -358,18 +408,31 @@ protected V getObject(final String className,
final Object[] parameterValues)
throws CreationException {
try {
// As reference google the source for:
// As a place to begin google:
// org.apache.cxf.BusFactoryHelper.java
final ClassLoader loader =
Thread.currentThread().getContextClassLoader();
final Class<?> genericClass =
Class.forName(className, true, loader);
final Class<? extends V> specificClass =
asSubclass(this.interfaceClass, genericClass);
final Constructor<? extends V> constructor =
specificClass.getConstructor(parameterTypes);

return constructor.newInstance(parameterValues);
// Are we creating a Proxy or an instance?
if (InvocationHandler.class.isAssignableFrom(genericClass)) {
final Constructor constructor =
genericClass.getConstructor(parameterTypes);
InvocationHandler handler = (InvocationHandler)
constructor.newInstance(parameterValues);
return (V) Proxy.newProxyInstance(
loader,
new Class[] { this.interfaceClass },
handler);
} else {
final Class<? extends V> specificClass =
asSubclass(this.interfaceClass, genericClass);
final Constructor<? extends V> constructor =
specificClass.getConstructor(parameterTypes);

return constructor.newInstance(parameterValues);
}

} catch (Exception exc) {
throw new CreationException("Error creating object of type \"" +
Expand All @@ -389,7 +452,7 @@ protected V getObject(final String className,
* includes the class itself).
*/
private static <V> Class<? extends V> asSubclass(final Class<V> clazz,
final Class<?> genericClass) {
final Class<?> genericClass) {
if (clazz.isAssignableFrom(genericClass)) {
return (Class<? extends V>) genericClass;
} else {
Expand Down Expand Up @@ -455,6 +518,30 @@ protected CreationException defaultCreationException() {
this.interfaceClass.getName() + "\"");
}

/**
* Get the current override values in the opaque context object and
* clear those values within the Factory.
* <p>
* This is used in testing.
*
* @return the test <code>Context</code> object.
*/
public Object removeContext() {
return null;
}

/**
* Restore the context object resetting override values.
* <p>
* This is used in testing.
*
* @param context the context object to be restored.
*/
public void restoreContext(final Object context) {
// empty
}


/**
* Implementation of ObjectFactory
* that returns only a single instance of the Object.
Expand Down Expand Up @@ -505,9 +592,9 @@ protected Singleton(final Class<T> interfaceClass) {
* @return the newly created object
* @throws CreationException if unable to create the object
*/
public T getObject(final Properties props,
final Class[] parameterTypes,
final Object[] parameterValues)
protected T getObject(final Properties props,
final Class[] parameterTypes,
final Object[] parameterValues)
throws CreationException {

// Unit test override, do not use application instance.
Expand Down Expand Up @@ -560,6 +647,28 @@ protected T getTestObject(final String className,
return getObject(className, parameterTypes, parameterValues);
}
}

/**
* This is for testing only.
* <p>
* <code>Context</code> contain the Factory implementation specific
* non-default values and mechanism for overriding the default
* instance type returned by the Factory.
* Factory implementation can extend the <code>Context</code> interface
* to capture its specific override values.
* If, for example, a Factory implementation uses a <code>ThreadLocal</code>
* to override the default instance type for unit tests, then the
* <code>Context</code>
* will hold the current value of the <code>ThreadLocal</code>.
* Getting the Context, clears the <code>ThreadLocal</code> value.
* This allows the tester who wishes to create code that will provide
* a wrapper around the default instance type to register their
* wrapper class name with the <code>ThreadLocal</code>, and, within
* the wrapper constructor, get the <code>Context</code>, get
* a default instance, and then restore the <code>Context</code>.
*/
public interface Context {
}
}

// End ObjectFactory.java
6 changes: 3 additions & 3 deletions testsrc/main/mondrian/util/MemoryMonitorTest.java
Expand Up @@ -111,7 +111,7 @@ public void memoryUsageNotification(long used, long max) {
}
}
Listener listener = new Listener();
MemoryMonitor mm = MemoryMonitorFactory.instance().getObject();
MemoryMonitor mm = MemoryMonitorFactory.getMemoryMonitor();
try {
// We use a percentage of '0' because we know that value is
// less than or equal to the lowest JVM memory usage.
Expand All @@ -137,7 +137,7 @@ public void memoryUsageNotification(long used, long max) {
}
}
Listener listener = new Listener();
MemoryMonitor mm = MemoryMonitorFactory.instance().getObject();
MemoryMonitor mm = MemoryMonitorFactory.getMemoryMonitor();
// we will set a percentage slightly above the current
// used level, and then allocate some objects that will
// force a notification.
Expand Down Expand Up @@ -264,7 +264,7 @@ public void memoryUsageNotification(long used, long max) {
MemoryMonitor mm = null;
try {
MemoryMonitorFactory.setThreadLocalClassName(TestMM.class.getName());
mm = MemoryMonitorFactory.instance().getObject();
mm = MemoryMonitorFactory.getMemoryMonitor();
boolean b = causeGC(mm);
//System.out.println("causeGC="+b);
long neededMemory = 5000000;
Expand Down

0 comments on commit 1c6854d

Please sign in to comment.