|
Evaluating Software Design Patterns — the "Gang of Four" patterns implemented in Java 6 |
||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Object dk.rode.thesis.meta.reflect.proxy.ProxyFactory
public class ProxyFactory
A proxy factory creates dynamic proxies, which can also be used for duck typing.
The proxy objects are based on the
java.lang.reflect.Proxy
class, and must therefore adhere to the rules set forth by that
class. For example, an implication of this is that even if a
specific instance is supplied as the object
argument
to the getProxy(Object, InvocationHandler, Class...)
method, i.e. a specific class, the returned (proxy) object
cannot be cast back into that type (the specific type
information is "lost" as the class returned is an anonymous
Proxy
class).
Per default, the class loader used to load this class is used
as the class loader to define the created proxies, but a specific
class loader can be supplied at construction
time.
This class defines four specific proxy methods:
getLoggableObject(Object, Class...)
: returns a proxy
that will record and time all access to the proxied object
if and only if all access is through the proxy only.
getSynchronisedObject(Object, Object, Class...)
: returns
a proxy that guarantees thread-safe access to the proxied object
if and only if all access is through the proxy only.
getSharedObject(Object, String, Class[], String...)
: returns
a proxy of a given object that can be shared until a
mutator method is invoked, causing the original referenced
object to be copied (smart reference), if and only if
all access is through the proxy or copies of it only.
getNullObject(Class)
: returns a to this factory unique
null object of the type specified as the argument. Only a
single instance of the type in question will be created, and the
type may be either an interface or class. This can be used to
mask null where null is not allowed.
A proxy factory is open for sub-classing.
Please refer to the JavaDoc of the java.lang.reflect.Proxy
class for in-depth semantics regarding proxies. The JavaDoc for
the three methods described above contains examples on general
usage of Proxy
objects as well.
Implementation notes:
The nested classes defined in this class represent applications
of the Adapter and
Decorator patterns, while this
factory itself is an application of the Proxy
pattern.
Nested Class Summary | |
---|---|
private class |
ProxyFactory.Handler
A handler is used to decorate all invocation handlers used by proxies created by any proxy factory. |
private static class |
ProxyFactory.Key
A key adapts a proxy to ensure consistent hashCode()
and equals(Object) behaviour when proxies are stored
internally by proxy factories, because proxies
may override hashCode and equals(Object)
in unpredictable ways. |
private static interface |
ProxyFactory.Proxy
All proxies created by a proxy factory will be tagged with this adapter interface, allowing for later retrieval of the actual proxy factory that
created the proxy as well as the unique handler associated with it. |
Field Summary | |
---|---|
private ClassLoader |
classLoader
The class loader to use when creating the proxy objects. |
private static Method |
factoryMethod
Reference to the ProxyFactory.Proxy.getProxyFactory() method
defined in the ProxyFactory.Proxy interface. |
private static Method |
handlerMethod
Reference to the ProxyFactory.Proxy.getHandler() method
defined in the ProxyFactory.Proxy interface. |
private Map<Class<?>,Object> |
nulls
A map containing all unique null objects created by this factory. |
private Map<ProxyFactory.Key,WeakReference<Object>> |
proxies
A map containing the proxies created by this factory. |
Constructor Summary | |
---|---|
ProxyFactory()
No-arg constructor, which creates this proxy factory to use the class loader that loaded this factory. |
|
ProxyFactory(ClassLoader classLoader)
Constructor, which creates this factory to use the class loader supplied as classLoader when
creating new proxy objects. |
Method Summary | ||
---|---|---|
private
|
create(T object,
Set<Class<?>> interfaces,
InvocationHandler handler)
Factory method used to create a new proxy object implementing the interfaces supplied as interfaces , in order,
based on the object to proxy supplied as object , if any. |
|
boolean |
equals(Object object)
Returns true if object is the same
factory instance as this factory, false if not. |
|
protected static Set<Class<?>> |
getInterfaces(Object object,
Class<?>... interfaces)
Returns a set containing the interfaces to use for the proxied object supplied as object . |
|
|
getLoggableObject(T object,
Class<? super T>... interfaces)
Returns a (proxy) version of object that will record
all access to object when accessed (via the proxy) through
one of the interfaces supplied in interfaces . |
|
|
getNullObject(Class<T> type)
Returns a to this factory unique null object of the type supplied as type . |
|
Object |
getObject(Object proxy)
Returns the actual object proxied by proxy , or null
if no such proxy is handled by this proxy factory or if
garbage collected. |
|
List<?> |
getProxies(Object object)
Returns all proxy objects for the object supplied as object , if any. |
|
Object |
getProxy(InvocationHandler handler,
Class<?>... interfaces)
Creates a new proxy object implementing the interfaces supplied as interfaces , in order. |
|
|
getProxy(T object,
InvocationHandler handler,
Class<?>... interfaces)
Creates a new proxy object implementing the interfaces supplied as interfaces , in order. |
|
protected static ProxyFactory |
getProxyFactory(Object proxy)
Returns the proxy factory that created the proxy supplied as proxy , or null if proxy is not a
proxy created by a proxy factory. |
|
|
getSharedObject(T object,
String copyMethod,
Class<?>[] parameterTypes,
String... mutatorMethods)
Returns a (proxy) reference to object
that can be shared and copied by different contexts until a
given mutator method is invoked on it, forcing the original
object to be copied. |
|
|
getSynchronisedObject(T object,
Class<? super T>... interfaces)
Returns a synchronised (proxy) version of object that can
be viewed as any of the interfaces supplied in interfaces . |
|
|
getSynchronisedObject(T object,
Object lock,
Class<? super T>... interfaces)
Returns a synchronised (proxy) version of object that can
be viewed as any of the interfaces supplied in interfaces ,
synchronised on the object supplied as lock . |
|
int |
hashCode()
Returns the hash code of this factory. |
|
boolean |
isProxied(Object object)
Returns true if the object supplied as object
is proxied by a proxy created by this factory, false if not. |
|
boolean |
isProxy(Object object)
Returns true if object is a proxy created by this
proxy factory, false if not. |
|
protected void |
register(Object proxy,
Object object)
Registers the proxied object supplied as object
for the proxy supplied as proxy . |
|
String |
toString()
Return the string representation of this factory. |
Methods inherited from class java.lang.Object |
---|
clone, finalize, getClass, notify, notifyAll, wait, wait, wait |
Field Detail |
---|
private final ClassLoader classLoader
Never null.
private static final Method factoryMethod
ProxyFactory.Proxy.getProxyFactory()
method
defined in the ProxyFactory.Proxy
interface. Never null.
private static final Method handlerMethod
ProxyFactory.Proxy.getHandler()
method
defined in the ProxyFactory.Proxy
interface. Never null.
private Map<Class<?>,Object> nulls
A null object (value) is a unique object based on identity for its type (key), which can be used to mask null where null is not allowed. Hence, at most one instance of a given type will be created by this factory.
Will be null if no such null objects have been created.
private final Map<ProxyFactory.Key,WeakReference<Object>> proxies
The actual proxied objects are stored as weak references, so this factory will not keep a reference to them. Individual handlers may still keep references to the proxied objects, however.
Never null, but can be empty.
Implementation notes:
The keys, i.e. proxies, should be stored as weak references
as well, but this is tricky as weak references compare on
identity in equals(Object)
. A WeakHashMap
wraps any type of key as a weak reference, and hence creating new
keys
to look-up existing proxies will fail if
such a map is used!
Constructor Detail |
---|
public ProxyFactory()
public ProxyFactory(ClassLoader classLoader)
classLoader
when
creating new proxy objects.
classLoader
- The class loader; cannot be null.
NullPointerException
- If classLoader
is null.Method Detail |
---|
private <T> T create(T object, Set<Class<?>> interfaces, InvocationHandler handler)
interfaces
, in order,
based on the object to proxy supplied as object
, if any.
If object
is not null, the returned proxy will be registered
with object
as the associated proxied object using the
register(Object, Object)
method.
T
- The type of object
.object
- The object to proxy; can be null.interfaces
- The interfaces to implement by the returned
proxy; cannot be null, or contain null entries
or non-interface classes. Annotations are ignored.handler
- The invocation handler to use by the
proxy; cannot be null.
NullPointerException
- If interfaces
or handler
are null,
or if interfaces
contain a null entry.
IllegalArgumentException
- If interfaces
contain
a non-interface class.public final boolean equals(Object object)
object
is the same
factory instance as this factory, false if not.
equals
in class Object
object
- The object to test; can be null.
protected static final Set<Class<?>> getInterfaces(Object object, Class<?>... interfaces)
object
.
If interfaces
is not null, the contained interfaces
will be used in that order. If null, each interface implemented
by object
will be used, in the order implemented
by object
.
object
- The proxied object; cannot be null.interfaces
- The list of specified interfaces, if any;
can be empty.
NullPointerException
- If object
is null, or
interfaces
contains a null entry.public <T> T getLoggableObject(T object, Class<? super T>... interfaces)
object
that will record
all access to object
when accessed (via the proxy) through
one of the interfaces supplied in interfaces
. The access
is recorded to the log
associated with the original
class of object
.
All access to object
via the methods defined in one of the
types in interfaces
will be logged if and only if
object
is accessed through the returned proxy object
only. If object
is accessed directly, logging cannot
be guaranteed!
If no interfaces are supplied, all interfaces implemented by
object
, directly or indirectly (for example via a
super-class), will be used.
Since the returned object is a java.lang.reflect.Proxy
instance, the normal rules for Proxy
objects apply! An
implication of this is that even if a specific instance is supplied
as object
, i.e. a specific class, the returned (proxy)
object cannot be cast back into that type (the specific
type information is "lost").
Example:
// Interfaces... public interface Foo { public String foo(); } public interface Bar { public String bar(); } .. // Implementation... public class FooBar implements Foo, Bar { public String foo() { return "foo"; } public String bar() { return "bar"; } } .. FooBar foobar = new FooBar();Proxies can be supplied as theProxyFactory
factory = newProxyFactory()
; // No class casting needed, since we only use a single interface... Foo foo = factory.getLoggableObject
(foobar, Foo.class); // All access to the methods defined in Foo will now be // logged when accessing through "foo": String s = foobar.foo() // *not* logged s = foo.foo(); // logged // Will throw a class cast exception! FooBar goo = (FooBar)foo; .. // A class cast is now required, since more than one interface // is implemented by the returned proxy. A cast to either type // is valid... foo = (Foo)factory.getLoggableObject
(foobar, Foo.class, Bar.class); // All access to the methods defined in Foo *and* Bar will now // be logged accessing through "foo" and "bar": String s = foobar.foo(); // *not* logged s = foobar.bar(); // *not* logged s = foo.foo(); // logged // Must cast to access "bar"... Bar bar = (Bar)foo; s = bar.bar(); // logged // Will throw a class cast exception! FooBar goo = (FooBar)bar; // Default - all interfaces implemented by FooBar (including // those from super-classes) - no class casting is required, // but the returned proxy can simply be assigned to a proper type: foo = factory.getLoggableObject
(foobar); // Foo.class & Bar.class bar = factory.getLoggableObject
(foobar); // Foo.class & Bar.class
object
parameter, which
allows for nested logging.
T
- The type of object
.object
- The actual object to be used; cannot be null, and
must implement all the interfaces supplied
as interfaces
.interfaces
- The interfaces to supply the methods to be
logged; can be null, in which case all interfaces
implemented by the class of object
will be used.
interfaces
will be logged; never null.
NullPointerException
- If object
is null.
IllegalArgumentException
- If interfaces
contain
an non-interface type.getSynchronisedObject(Object, Class...)
,
getSynchronisedObject(Object, Object, Class...)
,
getSharedObject(Object, String, Class[], String...)
public <T> T getNullObject(Class<T> type) throws Exception
type
.
A null object is a unique object based on identity for its type, which can
be used to mask null where null is not allowed. Hence, at most one instance of
type
will be created by this factory.
Properties for returned null objects:
type
is java.lang.Object
, the returned null object
is Const.NULL
.
type
is an interface or abstract class, a unique proxy is
returned where all methods declaring a non-void return type will return null,
except java.lang.Object
methods and methods having a primitive return
type; the latter will return the default primitive value of the return type
in question.
type
is a primitive type or a primitive wrapper class, an
unique instance of the wrapper class is used with the default primitive
value.
type
is a neither a primitive type nor a primitive wrapper
class, a unique instance of the type is used, created using a public
no-arg constructor.
T
- The type of the null object.type
- The type as a class literal; cannot be null.
type
; never null.
Exception
- If type
represents a class, which is neither a primitive
type nor a primitive wrapper class, without a public no-arg constructor;
or if type
is an abstract class.public Object getObject(Object proxy)
proxy
, or null
if no such proxy is handled by this proxy factory or if
garbage collected.
proxy
- The proxy; cannot be null.
NullPointerException
- If proxy
is null.
IllegalArgumentException
- If proxy
does not
represent a proxy.public List<?> getProxies(Object object)
object
, if any.
object
- The object; cannot be null.
NullPointerException
- If object
is null.public Object getProxy(InvocationHandler handler, Class<?>... interfaces)
interfaces
, in order. The proxy object is not registered to any real object.
handler
- The invocation handler to use by the
proxy; cannot be null.interfaces
- The interfaces to implement by the returned
proxy; cannot be null, empty, or contain null
entries or non-interface classes.
NullPointerException
- If either argument is null, or if
interfaces
contains a null entry.
IllegalArgumentException
- If interfaces
contain
a non-interface class.
ClassCastException
- If the returned proxy is cast
to a non-interface class, or an interface not implemented
by the proxy.public <T> T getProxy(T object, InvocationHandler handler, Class<?>... interfaces)
interfaces
, in order.
The proxy object is registered as a proxy object for the
object supplied as object
, if any. Also, if interfaces
is empty, all interfaces implemented by object
will be used for the returned proxy.
Proxies can be supplied as the object
parameter, which
allows for nested proxies.
T
- The type of object
.object
- The object to proxy; can be null.handler
- The invocation handler to use by the
proxy; cannot be null.interfaces
- The interfaces to implement by the returned
proxy; cannot contain null entries
or non-interface classes.
NullPointerException
- If handler
or interfaces
are null,
or if interfaces
contains a null entry.
IllegalArgumentException
- If interfaces
contain
a non-interface class.
ClassCastException
- If the returned proxy is cast
to a non-interface class, or an interface not implemented
by the proxy.protected static final ProxyFactory getProxyFactory(Object proxy)
proxy
, or null if proxy
is not a
proxy created by a proxy factory.
proxy
- The proxy object; cannot be null.
NullPointerException
- If proxy
is null.
UnsupportedOperationException
- If this method
is invoked from a non sub-class context.public <T> T getSharedObject(T object, String copyMethod, Class<?>[] parameterTypes, String... mutatorMethods) throws Exception
reference
to object
that can be shared and copied by different contexts until a
given mutator method is invoked on it, forcing the original
object to be copied. Both copy and mutator methods
are supplied as arguments to this method.
All access to object
is via the interfaces the class of it
implements. All access to object
should be though the
returned proxy.
Since the returned object is a java.lang.reflect.Proxy
instance, the normal rules for Proxy
objects apply! An
implication of this is that even if a specific instance is supplied
as object
, i.e. a specific class, the returned (proxy)
object cannot be cast back into that type (the specific
type information is "lost").
Example:
// Interface of objects to be shared... public interface Foo { // Normal methods that can be shared... public int getFooCount(); public String toString(); .. // Mutator methods, which when invoked may cause a copy... public void setFooCount(int count); public void resetFooCount(); .. // Copy method to perform the actual copying... public Foo copy(boolean deep); } // Implementation... public class FooBar implements Foo { .. } .. // Specify copy method... String copyMethod = "copy"; // "boolean" formal parameter (primitive types are ignored)... Class<?> parameterTypes = {Boolean.class}; // or Boolean.TYPE // Mutator methods, based on name only... String[] mutators = {"getFooCount", "resetFooCount"}; // "foo" is the instance to be shared (originally)... Foo foo = new FooBar();ProxyFactory
factory = newProxyFactory()
; // No class casting needed... Foo foobar = factory.getSharedObject
(foo, copyMethod, parameterTypes, mutatorMethods); // Can also use varargs for mutators // Create a copy using the normal copy method. This will increase the // reference count to "foo"... Foo foofoo = foobar.copy(true); // Access to non-mutator methods will not cause a copy... System.out.println(foobar.getFooCount()); System.out.println(foofoo); // toString() .. // Access to a mutator method in any copy will force "foo" to // be copied because more than one reference. The value 42 will // be set in a copy of "foo" used by "foofoo", not in other // references (here, "foobar")... foofoo.setFooCount(42); // Only one reference, so if "foobar" accesses mutator methods, // no copy will be made (until copied again)... foobar.resetFooCount(); ..
T
- The type of object
.object
- The actual object to be shared; cannot be null.copyMethod
- The name of the copy method to use; cannot
be null. Must be declared in the class of
object
, but will be made accessible
if not already.parameterTypes
- The formal parameter types of copyMethod
,
if any, ignoring primitive types; can be null.mutatorMethods
- The names of the mutator methods that will
force a copy of object
before executed;
cannot be null or empty.
NullPointerException
- If object
or copyMethod
are null.
IllegalArgumentException
- If mutatorMethods
is empty, or
if the copy method is a vararg method.
Exception
- If no such copy method exists.Reference
,
getSynchronisedObject(Object, Class...)
,
getSynchronisedObject(Object, Object, Class...)
,
getLoggableObject(Object, Class...)
public <T> T getSynchronisedObject(T object, Class<? super T>... interfaces)
object
that can
be viewed as any of the interfaces supplied in interfaces
. The
synchronisation lock used is object
itself.
All access to object
via the methods defined in one of the
types in interfaces
will be synchronised on object
itself if and only if object
is accessed through the
returned proxy object only. If object
is accessed
directly, synchronisation cannot be guaranteed!
If no interfaces are supplied, all interfaces implemented by
object
, directly or indirectly (for example via a
super-class), will be used.
Since the returned object is a java.lang.reflect.Proxy
instance, the normal rules for Proxy
objects apply! An
implication of this is that even if a specific instance is supplied
as object
, i.e. a specific class, the returned (proxy)
object cannot be cast back into that type (the specific
type information is "lost" as the class returned is an anonymous
Proxy
class).
Example:
// Interfaces... public interface Foo { public String foo(); } public interface Bar { public String bar(); } .. // Implementation... public class FooBar implements Foo, Bar { public String foo() { return "foo"; } public String bar() { return "bar"; } } .. FooBar foobar = new FooBar();Proxies can be supplied as theProxyFactory
factory = newProxyFactory()
; // No class casting needed, since we only use a single interface... Foo foo = factory.getSynchronisedObject
(foobar, Foo.class); // All access to the methods defined in Foo will now be // synchronised on "foobar" when accessing through "foo": String s = foobar.foo() // *not* synchronised s = foo.foo(); // synchronised // Will throw a class cast exception! FooBar goo = (FooBar)foo; .. // A class cast is now required, since more than one interface // is implemented by the returned proxy. A cast to either type // is valid... foo = (Foo)factory.getSynchronisedObject
(foobar, Foo.class, Bar.class); // All access to the methods defined in Foo *and* Bar will now // be synchronised on "foobar" when accessing through "foo" and "bar": String s = foobar.foo(); // *not* synchronised s = foobar.bar(); // *not* synchronised s = foo.foo(); // synchronised // Must cast to access "bar"... Bar bar = (Bar)foo; s = bar.bar(); // synchronised // Will throw a class cast exception! FooBar goo = (FooBar)bar; // Default - all interfaces implemented by FooBar (including // those from super-classes) - no class casting is required, // but the returned proxy can simply be assigned to a proper type: foo = factory.getSynchronisedObject
(foobar); // Foo.class & Bar.class bar = factory.getSynchronisedObject
(foobar); // Foo.class & Bar.class
object
parameter, which
allows for nested synchronisation.
T
- The type of object
.object
- The actual object to be used; cannot be null, and
must implement all the interfaces supplied
as interfaces
.interfaces
- The interfaces to supply the methods to be
synchronised; can be null, in which case
all interfaces implemented by the class of object
will be used.
interfaces
; never null.
NullPointerException
- If object
is null.
IllegalArgumentException
- If interfaces
contain
an non-interface type.getSynchronisedObject(Object, Object, Class...)
,
getLoggableObject(Object, Class...)
,
getSharedObject(Object, String, Class[], String...)
public <T> T getSynchronisedObject(T object, Object lock, Class<? super T>... interfaces)
object
that can
be viewed as any of the interfaces supplied in interfaces
,
synchronised on the object supplied as lock
.
All access to object
via the methods defined in one of the
types in interfaces
will be synchronised on lock
if and only if object
is accessed through the
returned proxy object only. If object
is accessed
directly, synchronisation cannot be guaranteed!
If no interfaces are supplied, all interfaces implemented by
object
, directly or indirectly (for example via a
super-class), will be used.
Since the returned object is a java.lang.reflect.Proxy
instance, the normal rules for Proxy
objects apply! An
implication of this is that even if a specific instance is supplied
as object
, the returned (proxy) object cannot be
cast back into that type (the specific type information is "lost" as
the class returned is an anonymous Proxy
class).
Example:
// Interfaces... public interface Foo { public String foo(); } public interface Bar { public String bar(); } .. // Implementation... public class FooBar implements Foo, Bar { public String foo() { return "foo"; } public String bar() { return "bar"; } } .. Object lock = new Object(); FooBar foobar = new FooBar();Proxies can be supplied as theProxyFactory
factory = newProxyFactory()
; // No class casting needed, since we only use a single interface... Foo foo = factory.getSynchronisedObject
(foobar, lock, Foo.class); // All access to the methods defined in Foo will now be // synchronised on "lock" when accessing through "foo": String s = foobar.foo() // *not* synchronised s = foo.foo(); // synchronised // Will throw a class cast exception! FooBar goo = (FooBar)foo; .. // A class cast is now required, since more than one interface // is implemented by the returned proxy. A cast to either type // is valid... foo = (Foo)factory.getSynchronisedObject
(foobar, lock, Foo.class, Bar.class); // All access to the methods defined in Foo *and* Bar will now // be synchronised on "lock" when accessing through "foo" and "bar": String s = foobar.foo(); // *not* synchronised s = foobar.bar(); // *not* synchronised s = foo.foo(); // synchronised // Must cast to access "bar"... Bar bar = (Bar)foo; s = bar.bar(); // synchronised // Will throw a class cast exception! FooBar goo = (FooBar)bar; // Default - all interfaces implemented by FooBar (including // those from super-classes) - no class casting is required, // but the returned proxy can simply be assigned to a proper type: foo = factory.getSynchronisedObject
(foobar, lock); // Foo.class & Bar.class bar = factory.getSynchronisedObject
(foobar, lock); // Foo.class & Bar.class
object
parameter, which
allows nested synchronisation.
T
- The type of object
.object
- The actual object to be used; cannot be null, and
must implement all the interfaces supplied
as interfaces
.lock
- The object to synchronise on; cannot be null.interfaces
- The interfaces to supply the methods to be
synchronised; can be null, in which case
all interfaces implemented by the class of object
will be used.
interfaces
; never null.
NullPointerException
- If object
or lock
are null.
IllegalArgumentException
- If interfaces
contain an
non-interface type.getSynchronisedObject(Object, Object, Class...)
,
getLoggableObject(Object, Class...)
,
getSharedObject(Object, String, Class[], String...)
public final int hashCode()
hashCode
in class Object
public boolean isProxied(Object object)
object
is proxied by a proxy created by this factory, false if not.
object
- The object to test; cannot be null.
NullPointerException
- If object
is null.public boolean isProxy(Object object)
object
is a proxy created by this
proxy factory, false if not.
object
- The object to test; cannot be null.
NullPointerException
- If object
is null.protected void register(Object proxy, Object object)
object
for the proxy supplied as proxy
.
proxy
- The proxy; cannot be null.object
- The proxied object; cannot be null.
NullPointerException
- If either argument is null.
IllegalArgumentException
- If proxy
already
has an associated proxied object.
UnsupportedOperationException
- If this method
is invoked from a non sub-class context.public String toString()
toString
in class Object
|
Gunni Rode / rode.dk | ||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | ||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |