| MockEjbObject.java |
package org.mockejb;
import java.io.Serializable;
import java.lang.reflect.*;
import java.rmi.RemoteException;
import javax.ejb.*;
import org.mockejb.interceptor.*;
/**
* Serves as a proxy for all calls to the bean.
* Provided implementation of
* <code>javax.ejb.EJBObject</code> and <code>javax.ejb.EJBLocalObject</code> interfaces.
* Provides API for working with interceptors.
*
* @author Alexander Ananiev
*/
public class MockEjbObject implements InvocationHandler, EnterpriseBean, Serializable {
private InterceptorInvoker interceptorInvoker= new InterceptorInvoker();
private Object homeImpl;
private Object homeProxy;
private Class ifaceClass;
private Object bean;
private Object proxy;
private static MethodContainer standardMethods;
/**
* Standard methods required by EJBObject and EJBLocalObject implemented by
* this class.
*/
static {
standardMethods = new MethodContainer( MockEjbObject.class );
standardMethods.add("getEJBHome");
standardMethods.add("getEJBLocalHome");
standardMethods.add("getHandle");
standardMethods.add("getPrimaryKey");
standardMethods.add("remove");
Class objectArg[] = {Object.class};
standardMethods.add("isIdentical", objectArg );
// equals is a convenience method
standardMethods.add("equals", objectArg );
standardMethods.add("hashCode" );
// another convenience method
standardMethods.add("toString" );
// EJBBeanAccess
standardMethods.add("getBean");
standardMethods.add("getEjbContext");
}
MockEjbObject( Class ifaceClass ) {
this.ifaceClass = ifaceClass;
}
void setHomeImpl( final Object homeImpl ){
this.homeImpl = homeImpl;
}
void setHomeProxy ( final Object homeProxy ){
this.homeProxy = homeProxy;
}
/**
* Adds the interceptor to the interceptor list for this bean.
* @param interceptor interceptor to add
* @deprecated Use AspectSystem and poincuts to add interceptors
*/
public void addInterceptor( Interceptor interceptor ){
AspectSystemFactory.getAspectSystem().add(
new ClassPointcut( ifaceClass, false), interceptor);
}
/**
* Sets the transaction policy for the {@link TransactionManager}
* which is always part of the interceptor list.
* <br>The default policy is "Supports".
* @param policy transaction policy as defined by {@link TransactionPolicy}
* enumeration.
*
* @deprecated use TransactionManager with the AspectSystem to set transaction policies
*/
public void setTransactionPolicy( TransactionPolicy policy ){
interceptorInvoker.setContext( TransactionManager.POLICY_CONTEXT_KEY, policy );
}
/** Creates a new instance */
Object createProxy( Object bean, MockEjbContext ejbContext ) {
this.bean = bean;
proxy = Proxy.newProxyInstance( ifaceClass.getClassLoader(),
new Class[] { ifaceClass, EjbBeanAccess.class }, this );
// Now we can provide proxy to the context
ejbContext.setEjbObjectProxy( proxy );
interceptorInvoker.setContext( MockEjbContext.class.getName(), ejbContext );
return proxy;
}
/**
* Invokes the target bean's method by delegating to the <code>InvocationContext</code>
* which calls interceptors and then the bean itself.
* If we're dealing with the standard EJB method, this object provides the implementation
* of these methods instead of the target bean. All interceptors are still invoked as before.
*/
public Object invoke( Object proxy, Method ifaceMethod, Object[] paramVals )
throws Throwable {
Method beanMethod = null;
Object returnValue = null;
Object beanToInvoke = null;
beanMethod = standardMethods.find( ifaceMethod );
if ( beanMethod != null ) {
beanToInvoke= this;
}
else {
beanToInvoke= this.bean;
beanMethod = bean.getClass().getMethod( ifaceMethod.getName(),
ifaceMethod.getParameterTypes() );
}
returnValue = interceptorInvoker.invoke( proxy, ifaceMethod, beanToInvoke, beanMethod, paramVals );
return returnValue;
}
Object getHomeImpl() {
return this.homeImpl;
}
/**
* Returns <code>MockEjbContext</code> object for the bean backed by this
* MockEjbObject. <code>MockEjbContext</code> implements SessionContext and MesageDrivenContext.
* Additionally it provides some convenience methods.
* @return MockEjbContext instance
*/
public MockEjbContext getEjbContext(){
if ( interceptorInvoker.getContext( MockEjbContext.class.getName())== null )
throw new IllegalStateException( "Context does not exist. Most likely this EJB has not been created yet");
return (MockEjbContext) interceptorInvoker.getContext( MockEjbContext.class.getName());
}
// Implementation of the standard methods from Remote and Local ifaces
/**
* Obtains the enterprise Bean's home interface.
* @return a reference to the enterprise Bean's home interface.
*/
public EJBHome getEJBHome() {
if ( homeProxy == null )
throw new IllegalStateException(
"Attempt to request a home for the Bean that does not have one, such as MDB");
return (EJBHome) homeProxy;
}
/**
* Obtains the enterprise Bean's local home interface.
* @return a reference to the enterprise Bean's local home interface.
*/
public EJBLocalHome getEJBLocalHome() {
if ( homeProxy == null )
throw new IllegalStateException(
"Attempt to request a home for the Bean that does not have one, such as MDB");
return (EJBLocalHome) homeProxy;
}
/**
* This method is not supported.
*/
public Handle getHandle() throws RemoteException {
throwMethodNotImplemented( "getHandle");
return null;
}
public Object getPrimaryKey() {
return getEjbContext().getPrimaryKey();
}
/**
* Currently this method does not do anything
* TODO: should call ejbRemove for stateful session bean
* @see javax.ejb.EJBObject#remove()
*/
public void remove() throws RemoveException {
}
/**
* Test if a given object is identical to the invoked object.
* Works for both EJBObject and EJBLocalObject interfaces.
* It uses address equality so the provided parameter does not
* have to be EJB-specific.
* @param object an object to test for identity with the invoked object.
* @return true if the given object is identical to this object
*/
public boolean isIdentical(Object object) {
return equals(object);
}
/**
* Tests if this object is equals to the given object.
* If the given object is a proxy to another bean, the pointer equality
* between proxies is used.
* Otherwise, <code>super.equals()</code> is called.
* In other words, this method can be used to compare MockEjbObject instances
* as well as bean dynamic proxies returned by Home <code>create()</code>.
*
* @param obj object to compare with
* @return <code>true</code> if this object or the dynamic proxy it holds equals
* to the given object.
*
*/
public boolean equals( Object obj ){
// if the object is proxy
if (ifaceClass.isInstance( obj ) )
return (proxy==obj);
else
return super.equals( obj );
}
public int hashCode() {
return ifaceClass.hashCode();
}
// EjbBeanAccess
public Object getBean(){
return bean;
}
/**
* Provides string representation of this <code>MockEjbObject</code> and
* the its bean implementation object.
* @see java.lang.Object#toString()
*/
public String toString(){
return "EJB object: " +
" BusinessIface: "+ifaceClass.getName() +"\nBean: "+bean.getClass().getName();
}
/**
* Helper method to throw NotImplementedException for this class
* @param methodName
*/
private void throwMethodNotImplemented( String methodName ){
throw new MethodNotImplementedException( methodName,
this.getClass().getName() );
}
}