001: package org.mockejb;
002:
003: import java.lang.reflect.Method;
004: import java.lang.reflect.Modifier;
005: import java.util.*;
006:
007: import net.sf.cglib.proxy.*;
008:
009: import org.apache.commons.logging.*;
010:
011: import org.mockejb.interceptor.InterceptableProxy;
012: import org.mockejb.interceptor.InterceptorInvoker;
013:
014: /**
015: * Provides mock subclass of the CMP entity abstract class.
016: * The subclass gets created dynamically using CGLIB.
017: * The behavior is the following: CMP field getters return null if the corresponding
018: * set was not called, otherwise it returns the previously set value.
019: *
020: * Attempt to call ejbFind and ejbSelect will trigger exception. You need to intercept
021: * the appropriate methods of the Home interface and provide your own implementation.
022: *
023: * Intercept "create" method if you want to change the behavior.
024: *
025: * @author Alexander Ananiev
026: */
027: public class EntityBeanSubclass implements MethodInterceptor {
028:
029: // logger for this class
030: private static Log logger = LogFactory
031: .getLog(EntityBeanSubclass.class.getName());
032:
033: /**
034: * Creates an instance of EntityBeanSubclass. Current implementation creates
035: * a new instance evry time.
036: * This method does not create the actual sublcass, for that you need to call "create".
037: * @return an instance of EntityBeanSubclass
038: */
039: public static EntityBeanSubclass newInstance(
040: Class abstractEntityClass) {
041:
042: return (EntityBeanSubclass) InterceptableProxy.create(
043: EntityBeanSubclass.class, new EntityBeanSubclass(
044: abstractEntityClass));
045:
046: }
047:
048: private Class abstractEntityClass;
049:
050: /**
051: * Current values are stored here
052: */
053: private Map fieldVals = new HashMap();
054:
055: /**
056: * Fake constructor for CGLIB. This is needed in order to
057: * be able to re-use InterceptableProxy class
058: *
059: */
060: EntityBeanSubclass() {
061: }
062:
063: private EntityBeanSubclass(Class abstractEntityClass) {
064: this .abstractEntityClass = abstractEntityClass;
065: }
066:
067: /**
068: * Creates the subclass of the abstract entity class using CGLIB
069: * enhancer.
070: * @return an instance of the subclass implementing abstract methods of the
071: * provided entity bean class
072: */
073: public Object create() {
074: Enhancer e = new Enhancer();
075: e.setSuperclass(abstractEntityClass);
076: e.setCallback(this );
077:
078: return e.create();
079:
080: }
081:
082: public Object intercept(Object obj, Method method,
083: Object[] paramVals, MethodProxy cglibMethodProxy)
084: throws Throwable {
085:
086: String methodName = method.getName();
087: Object returnVal = null;
088:
089: if (isAbstractAndStartsWith(method, "set")) {
090:
091: String fieldName = deriveFieldName(methodName);
092: if (paramVals.length != 1)
093: throw new IllegalArgumentException(
094: "Attempt to call setter "
095: + method
096: + " with incorrect number of parameters");
097: if (!method.getReturnType().getName().equals("void")) {
098: throw new IllegalArgumentException(
099: "Attempt to call setter "
100: + method
101: + " which has the return type other than void");
102: }
103:
104: logger.debug("Calling setter for " + fieldName
105: + " with the value " + paramVals[0]);
106:
107: fieldVals.put(fieldName, paramVals[0]);
108: } else if (isAbstractAndStartsWith(method, "get")) {
109:
110: String fieldName = deriveFieldName(methodName);
111: if (paramVals.length > 0)
112: throw new IllegalArgumentException(
113: "Attempt to call getter method "
114: + method
115: + " with parameters. The method should not have any parameters");
116:
117: returnVal = fieldVals.get(fieldName);
118:
119: // check if it is CMR collection
120: Class returnClass = method.getReturnType();
121: if (java.util.Collection.class
122: .isAssignableFrom(returnClass)
123: && returnVal == null) {
124: returnVal = new ArrayList();
125: } else if (java.util.Set.class
126: .isAssignableFrom(returnClass)
127: && returnVal == null) {
128: returnVal = new HashSet();
129: }
130:
131: logger.debug("Calling getter for " + fieldName
132: + " with return value " + returnVal);
133:
134: }
135: // ejbSelect
136: else if (isAbstractAndStartsWith(method, "ejbSelect")) {
137: returnVal = invokeEjbSelect(obj, method, paramVals);
138: }
139: // business method or create or other method implemented in the abstract class
140: else {
141: returnVal = cglibMethodProxy.invokeSuper(obj, paramVals);
142: //cglibMethodProxy.invokeSuper(obj, paramVals);
143: }
144:
145: return returnVal;
146: }
147:
148: private boolean isAbstractAndStartsWith(Method method, String prefix) {
149:
150: return (method.getName().startsWith(prefix) && Modifier
151: .isAbstract(method.getModifiers()));
152: }
153:
154: private String deriveFieldName(String methodName) {
155:
156: return methodName.substring(3);
157:
158: }
159:
160: protected Object invokeEjbSelect(Object subclassObj,
161: Method ejbSelectMethod, Object[] paramVals)
162: throws Exception {
163:
164: Object returnObj = null;
165: DummyCMPBean dummyCmpBean = new DummyCMPBean();
166:
167: InterceptorInvoker interceptorInvoker = new InterceptorInvoker();
168:
169: try {
170: returnObj = interceptorInvoker.invoke(subclassObj,
171: ejbSelectMethod, dummyCmpBean, dummyCmpBean
172: .getTargetMethod(), paramVals);
173: } catch (MustBeInterceptedException mbie) {
174: // translate it into more meaningful error message
175: throw new MustBeInterceptedException(ejbSelectMethod);
176: }
177:
178: return returnObj;
179:
180: }
181:
182: }
|