001: package org.mockejb;
002:
003: import java.io.Serializable;
004: import java.lang.reflect.*;
005: import java.rmi.RemoteException;
006:
007: import javax.ejb.*;
008:
009: import org.mockejb.interceptor.*;
010:
011: /**
012: * Serves as a proxy for all calls to the bean.
013: * Provided implementation of
014: * <code>javax.ejb.EJBObject</code> and <code>javax.ejb.EJBLocalObject</code> interfaces.
015: * Provides API for working with interceptors.
016: *
017: * @author Alexander Ananiev
018: */
019: public class MockEjbObject implements InvocationHandler,
020: EnterpriseBean, Serializable {
021:
022: private InterceptorInvoker interceptorInvoker = new InterceptorInvoker();
023:
024: private Object homeImpl;
025: private Object homeProxy;
026:
027: private Class ifaceClass;
028:
029: private Object bean;
030:
031: private Object proxy;
032:
033: private static MethodContainer standardMethods;
034:
035: /**
036: * Standard methods required by EJBObject and EJBLocalObject implemented by
037: * this class.
038: */
039: static {
040: standardMethods = new MethodContainer(MockEjbObject.class);
041: standardMethods.add("getEJBHome");
042: standardMethods.add("getEJBLocalHome");
043: standardMethods.add("getHandle");
044: standardMethods.add("getPrimaryKey");
045: standardMethods.add("remove");
046:
047: Class objectArg[] = { Object.class };
048: standardMethods.add("isIdentical", objectArg);
049:
050: // equals is a convenience method
051: standardMethods.add("equals", objectArg);
052: standardMethods.add("hashCode");
053: // another convenience method
054: standardMethods.add("toString");
055: // EJBBeanAccess
056: standardMethods.add("getBean");
057: standardMethods.add("getEjbContext");
058:
059: }
060:
061: MockEjbObject(Class ifaceClass) {
062: this .ifaceClass = ifaceClass;
063: }
064:
065: void setHomeImpl(final Object homeImpl) {
066: this .homeImpl = homeImpl;
067: }
068:
069: void setHomeProxy(final Object homeProxy) {
070: this .homeProxy = homeProxy;
071: }
072:
073: /**
074: * Adds the interceptor to the interceptor list for this bean.
075: * @param interceptor interceptor to add
076: * @deprecated Use AspectSystem and poincuts to add interceptors
077: */
078: public void addInterceptor(Interceptor interceptor) {
079:
080: AspectSystemFactory.getAspectSystem().add(
081: new ClassPointcut(ifaceClass, false), interceptor);
082: }
083:
084: /**
085: * Sets the transaction policy for the {@link TransactionManager}
086: * which is always part of the interceptor list.
087: * <br>The default policy is "Supports".
088: * @param policy transaction policy as defined by {@link TransactionPolicy}
089: * enumeration.
090: *
091: * @deprecated use TransactionManager with the AspectSystem to set transaction policies
092: */
093: public void setTransactionPolicy(TransactionPolicy policy) {
094:
095: interceptorInvoker.setContext(
096: TransactionManager.POLICY_CONTEXT_KEY, policy);
097:
098: }
099:
100: /** Creates a new instance */
101: Object createProxy(Object bean, MockEjbContext ejbContext) {
102:
103: this .bean = bean;
104:
105: proxy = Proxy.newProxyInstance(ifaceClass.getClassLoader(),
106: new Class[] { ifaceClass, EjbBeanAccess.class }, this );
107:
108: // Now we can provide proxy to the context
109: ejbContext.setEjbObjectProxy(proxy);
110: interceptorInvoker.setContext(MockEjbContext.class.getName(),
111: ejbContext);
112:
113: return proxy;
114:
115: }
116:
117: /**
118: * Invokes the target bean's method by delegating to the <code>InvocationContext</code>
119: * which calls interceptors and then the bean itself.
120: * If we're dealing with the standard EJB method, this object provides the implementation
121: * of these methods instead of the target bean. All interceptors are still invoked as before.
122: */
123: public Object invoke(Object proxy, Method ifaceMethod,
124: Object[] paramVals) throws Throwable {
125:
126: Method beanMethod = null;
127: Object returnValue = null;
128:
129: Object beanToInvoke = null;
130:
131: beanMethod = standardMethods.find(ifaceMethod);
132: if (beanMethod != null) {
133: beanToInvoke = this ;
134: } else {
135: beanToInvoke = this .bean;
136:
137: beanMethod = bean.getClass().getMethod(
138: ifaceMethod.getName(),
139: ifaceMethod.getParameterTypes());
140: }
141:
142: returnValue = interceptorInvoker.invoke(proxy, ifaceMethod,
143: beanToInvoke, beanMethod, paramVals);
144:
145: return returnValue;
146: }
147:
148: Object getHomeImpl() {
149: return this .homeImpl;
150: }
151:
152: /**
153: * Returns <code>MockEjbContext</code> object for the bean backed by this
154: * MockEjbObject. <code>MockEjbContext</code> implements SessionContext and MesageDrivenContext.
155: * Additionally it provides some convenience methods.
156: * @return MockEjbContext instance
157: */
158: public MockEjbContext getEjbContext() {
159:
160: if (interceptorInvoker.getContext(MockEjbContext.class
161: .getName()) == null)
162: throw new IllegalStateException(
163: "Context does not exist. Most likely this EJB has not been created yet");
164:
165: return (MockEjbContext) interceptorInvoker
166: .getContext(MockEjbContext.class.getName());
167: }
168:
169: // Implementation of the standard methods from Remote and Local ifaces
170:
171: /**
172: * Obtains the enterprise Bean's home interface.
173: * @return a reference to the enterprise Bean's home interface.
174: */
175: public EJBHome getEJBHome() {
176: if (homeProxy == null)
177: throw new IllegalStateException(
178: "Attempt to request a home for the Bean that does not have one, such as MDB");
179: return (EJBHome) homeProxy;
180: }
181:
182: /**
183: * Obtains the enterprise Bean's local home interface.
184: * @return a reference to the enterprise Bean's local home interface.
185: */
186: public EJBLocalHome getEJBLocalHome() {
187: if (homeProxy == null)
188: throw new IllegalStateException(
189: "Attempt to request a home for the Bean that does not have one, such as MDB");
190: return (EJBLocalHome) homeProxy;
191: }
192:
193: /**
194: * This method is not supported.
195: */
196: public Handle getHandle() throws RemoteException {
197: throwMethodNotImplemented("getHandle");
198: return null;
199: }
200:
201: public Object getPrimaryKey() {
202: return getEjbContext().getPrimaryKey();
203:
204: }
205:
206: /**
207: * Currently this method does not do anything
208: * TODO: should call ejbRemove for stateful session bean
209: * @see javax.ejb.EJBObject#remove()
210: */
211: public void remove() throws RemoveException {
212:
213: }
214:
215: /**
216: * Test if a given object is identical to the invoked object.
217: * Works for both EJBObject and EJBLocalObject interfaces.
218: * It uses address equality so the provided parameter does not
219: * have to be EJB-specific.
220: * @param object an object to test for identity with the invoked object.
221: * @return true if the given object is identical to this object
222: */
223: public boolean isIdentical(Object object) {
224: return equals(object);
225: }
226:
227: /**
228: * Tests if this object is equals to the given object.
229: * If the given object is a proxy to another bean, the pointer equality
230: * between proxies is used.
231: * Otherwise, <code>super.equals()</code> is called.
232: * In other words, this method can be used to compare MockEjbObject instances
233: * as well as bean dynamic proxies returned by Home <code>create()</code>.
234: *
235: * @param obj object to compare with
236: * @return <code>true</code> if this object or the dynamic proxy it holds equals
237: * to the given object.
238: *
239: */
240: public boolean equals(Object obj) {
241: // if the object is proxy
242: if (ifaceClass.isInstance(obj))
243: return (proxy == obj);
244: else
245: return super .equals(obj);
246: }
247:
248: public int hashCode() {
249: return ifaceClass.hashCode();
250: }
251:
252: // EjbBeanAccess
253: public Object getBean() {
254: return bean;
255: }
256:
257: /**
258: * Provides string representation of this <code>MockEjbObject</code> and
259: * the its bean implementation object.
260: * @see java.lang.Object#toString()
261: */
262: public String toString() {
263: return "EJB object: " + " BusinessIface: "
264: + ifaceClass.getName() + "\nBean: "
265: + bean.getClass().getName();
266: }
267:
268: /**
269: * Helper method to throw NotImplementedException for this class
270: * @param methodName
271: */
272: private void throwMethodNotImplemented(String methodName) {
273:
274: throw new MethodNotImplementedException(methodName, this
275: .getClass().getName());
276:
277: }
278:
279: }
|