|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Objectorg.jpu.patterns.proxy.JPUInvocationHandler
org.jpu.patterns.proxy.ProxyBean
Change-tracking invocation handler intended for use in implementing JavaBean's that are referenced through one or more interfaces. Automatically provides implementations of simple getter and setter methods, reducing the burden associated with maintaining large numbers of such methods.
While simple getters and setters are implemented automatically, more complex methods ("business" methods, for instance) can be explicitly
defined. When the caller calls a method on the interface, this class will first attempt to delegate the method call to "this
".
If no such method exists in this.getClass()
or any of its superclasses, then it will determine if the method is a
getter or setter and, if so, query or update an internal Map
of attributes; if not it will cause a
UnhandledMethodCallException
(unchecked) to be thrown.
If you wish to provide your own implementation of a method, add it to your ProxyBean
subclass (making sure it
has the same signature as the version in the interface). The proxy instance can be obtained via JPUInvocationHandler.getProxy()
and you are
free to call whichever methods on it you wish through the interface. To illustrate, suppose I have a class AccountImpl
and
interface Account
defined as follows:
public interface Account {
public String getId();
public void setId(String id);
public double getBalance();
public void setBalance(double bal);
public void deposit(double amt);
}
// ...
public class AccountImpl extends ProxyBean {
public void initializeProxy(Object newProxy) {
super.initializeProxy(newProxy);
Account acct = (Account)newProxy;
acct.setBalance(0.0);
}
public void deposit(double amt) {
Account acct = (Account)getProxy();
acct.setBalance( acct.getBalance() + amt );
}
}
Then the "id
" attribute will be maintained automatically (getters and setters implemented automatically) while
calls to deposit()
will be delegated to AccountImpl
's deposit()
method. Note the
initial balance is set in initializeProxy()
; without this the balance would start out null
.
The internal Map
that holds the attributes is made accessible to users of ProxyBean
both for query
and update. This facilitates convenient and efficient access to all the object's attributes for wholesale copying and
other sorts of manipulation.
This class can also maintain changed flags on each of the attributes. By default this feature is disabled but can be
enabled by overriding trackChanges()
and having it return true
. If changes are tracked, then
the public methods getChangedAttributes()
and isAttributeChanged(String)
can be used to query the flags and
setChangedFlag(String)
, clearChangedFlag(String)
, and clearChangedFlags()
to change them. If you wish for the subclass to be notified
of any changes, you may override the valueChanged(JPUBeanUtils.MethodDescriptor, Object, Object)
subclass hook. If you want to change the definition of
equality of objects (default is org.apache.commons.lang.ObjectUtils.equals()
), you can override areValuesEqual(JPUBeanUtils.MethodDescriptor , Object , Object )
.
This class inherits from JPUInvocationHandler
, so if you wish to add additional logic to the invocation
handler, override doInvoke(Object, Method, Object[], Object[])
and add the following lines at the top your implementation:
if ( super.doInvoke(proxy, method, args, result) ) {
return true;
}
Definitions of "getter" and "setter" used here are the same ones defined by the JavaBeans specification; that is, a method is considered a getter if it
starts with "get" and takes zero parameters; or if it starts with "is", takes zero parameters, and returns boolean
or Boolean
;
while a method is considered a setter if it starts with "set" and takes one parameter.
ProxyBean
can be quite useful in J2EE environments that use the
Transfer Object pattern to pass data between
tiers. The change-tracking feature can be used to determine which items have been altered by the presentation tier and
hence require persisting to the database. The JPU toolkit provides a class called ProxyTransferObject
which
is intended for this purpose; it extends ProxyBean
and adds serializability to the
invocation handler (which of course is necessary for passing such objects between tiers).
Nested Class Summary |
Nested classes inherited from class org.jpu.patterns.proxy.JPUInvocationHandler |
JPUInvocationHandler.DelegationCacheValue, JPUInvocationHandler.DelegationState |
Constructor Summary | |
ProxyBean()
|
Method Summary | |
boolean |
areAnyAttributesChanged()
Convenience alias for " ! getChangedAttributes().isEmpty() ". |
protected boolean |
areValuesEqual(JPUBeanUtils.MethodDescriptor method,
java.lang.Object oldValue,
java.lang.Object newValue)
Returns whether the two given values are considered equal. |
protected boolean |
autoDelegateToMap()
Returns whether implementations of getters and setters are automatically provided. |
void |
clearChangedFlag(java.lang.String attributeName)
Clears the changed flag for the named attribute. |
void |
clearChangedFlags()
Clears all changed flas. |
java.util.Map |
copyChangesTo(java.lang.Object dest)
Convenience alias for " copyChangesTo(dest, null) ". |
java.util.Map |
copyChangesTo(java.lang.Object dest,
CopyOptions options)
Copies all attributes from this object to dest using the given options. |
java.util.Map |
copyTo(java.lang.Object dest)
Convenience alias for " copyTo(dest,null) ". |
java.util.Map |
copyTo(java.lang.Object dest,
CopyOptions options)
Copies all attributes to " dest " (which needn't be backed by a ProxyBean )
with the given CopyOptions using JPUBeanUtils.copy(java.lang.Object, java.lang.Object) . |
protected boolean |
delegateToMap(java.lang.Object proxy,
java.lang.reflect.Method method,
java.lang.Object[] args,
java.lang.Object[] result)
|
protected boolean |
delegateToMap(java.lang.Object proxy,
java.lang.Object[] args,
java.lang.Object[] result)
Convenience alias for " delegateToMap(proxy, null, args, result) ". |
protected boolean |
doInvoke(java.lang.Object proxy,
java.lang.reflect.Method method,
java.lang.Object[] args,
java.lang.Object[] result)
Overrides the corresponding method of JPUInvocationHandler . |
protected java.lang.Object |
getAttribute(JPUBeanUtils.MethodDescriptor method)
Returns the value of the attribute associated with the given getter method. |
java.lang.Object |
getAttribute(java.lang.String attName)
Returns the value of the named attribute, or null
if that attribute is not present. |
java.util.Map |
getAttributes()
Returns a Map containing all the attributes. |
java.util.Set |
getChangedAttributes()
Returns the names of all attributes that have changed since the last call to clearChangedFlags() (or since
the object's creation). |
protected JPUBeanUtils.MethodDescriptor |
getDescriptor(java.lang.reflect.Method method)
|
boolean |
isAttributeChanged(java.lang.String attributeName)
Convenience alias for " getChangedAttributes().contains(attributeName) ". |
protected void |
setAttribute(JPUBeanUtils.MethodDescriptor method,
java.lang.Object[] args)
|
void |
setChangedFlag(java.lang.String attributeName)
Sets the changed flag for the named attribute. |
boolean |
trackChanges()
Returns whether changes are being tracked. |
protected void |
valueChanged(JPUBeanUtils.MethodDescriptor method,
java.lang.Object oldValue,
java.lang.Object newValue)
Hook that is invoked if change tracking is enabled and the value of an attribute has changed as a result of a setter invocation. |
Methods inherited from class java.lang.Object |
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait |
Constructor Detail |
public ProxyBean()
Method Detail |
public java.util.Map getAttributes()
Map
containing all the attributes.
The map is modifiable and the caller is free to change it as required.
public java.lang.Object getAttribute(java.lang.String attName)
null
if that attribute is not present.
public java.util.Set getChangedAttributes()
clearChangedFlags()
(or since
the object's creation). If trackChanges()
returns false
(the default), the returned
Set
will be empty.
The returned Set
is modifiable and the caller
is free to change it as required.
public boolean areAnyAttributesChanged()
! getChangedAttributes().isEmpty()
".
public boolean isAttributeChanged(java.lang.String attributeName)
getChangedAttributes().contains(attributeName)
".
public java.util.Map copyChangesTo(java.lang.Object dest) throws CopyException
copyChangesTo(dest, null)
".
CopyException
public java.util.Map copyChangesTo(java.lang.Object dest, CopyOptions options) throws CopyException
dest
using the given options.
The implementation uses JPUBeanUtils.copy(java.lang.Object, java.lang.Object)
.
CopyException
public java.util.Map copyTo(java.lang.Object dest) throws CopyException
copyTo(dest,null)
".
CopyException
public java.util.Map copyTo(java.lang.Object dest, CopyOptions options) throws CopyException
dest
" (which needn't be backed by a ProxyBean
)
with the given CopyOptions
using JPUBeanUtils.copy(java.lang.Object, java.lang.Object)
.
If "options.sourceClasses
" is null
, it is populated with an array containing all
interfaces implemented by the proxy prior to the call to JPUBeanUtils.copy(java.lang.Object, java.lang.Object)
.
CopyException
public void setChangedFlag(java.lang.String attributeName)
public void clearChangedFlag(java.lang.String attributeName)
public void clearChangedFlags()
protected boolean doInvoke(java.lang.Object proxy, java.lang.reflect.Method method, java.lang.Object[] args, java.lang.Object[] result) throws java.lang.Throwable
JPUInvocationHandler
.
Calls super.doInvoke()
and, if its return value is false
, calls
autoDelegateToMap()
. If that method returns true
, calls
delegateToMap(Object,Method,Object[],Object[])
and returns the result; else returns false
.
doInvoke
in class JPUInvocationHandler
java.lang.Throwable
protected boolean autoDelegateToMap()
true
, but subclasses are free to override. If false
is returned, the
subclass may still invoke the "Map delegation" feature by calling delegateToMap(Object,Method,Object[],Object[])
.
public boolean trackChanges()
false
, but subclasses are free to override.
protected boolean delegateToMap(java.lang.Object proxy, java.lang.Object[] args, java.lang.Object[] result) throws java.lang.Throwable
delegateToMap(proxy, null, args, result)
".
java.lang.Throwable
protected boolean delegateToMap(java.lang.Object proxy, java.lang.reflect.Method method, java.lang.Object[] args, java.lang.Object[] result) throws java.lang.Throwable
java.lang.Throwable
protected JPUBeanUtils.MethodDescriptor getDescriptor(java.lang.reflect.Method method)
protected void setAttribute(JPUBeanUtils.MethodDescriptor method, java.lang.Object[] args) throws java.lang.Throwable
java.lang.Throwable
protected void valueChanged(JPUBeanUtils.MethodDescriptor method, java.lang.Object oldValue, java.lang.Object newValue)
JPUBeanUtils.MethodDescriptor
.
protected boolean areValuesEqual(JPUBeanUtils.MethodDescriptor method, java.lang.Object oldValue, java.lang.Object newValue)
method
" is the JPUBeanUtils.MethodDescriptor
of the invoked setter.
protected java.lang.Object getAttribute(JPUBeanUtils.MethodDescriptor method) throws java.lang.Throwable
getAttributes().get(method.attributeName)
", but
subclasses are free to override.
java.lang.Throwable
|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |