001: package com.mockrunner.ejb;
002:
003: import javax.ejb.EJBHome;
004: import javax.ejb.EJBLocalHome;
005: import javax.jms.ConnectionFactory;
006: import javax.jms.Destination;
007: import javax.jms.Topic;
008: import javax.naming.Context;
009: import javax.naming.NamingException;
010:
011: import org.apache.commons.beanutils.MethodUtils;
012: import org.mockejb.BasicEjbDescriptor;
013: import org.mockejb.EntityBeanDescriptor;
014: import org.mockejb.MDBDescriptor;
015: import org.mockejb.SessionBeanDescriptor;
016: import org.mockejb.TransactionManager;
017: import org.mockejb.TransactionPolicy;
018: import org.mockejb.interceptor.AspectSystemFactory;
019: import org.mockejb.interceptor.ClassPointcut;
020:
021: import com.mockrunner.base.NestedApplicationException;
022: import com.mockrunner.base.VerifyFailedException;
023: import com.mockrunner.mock.ejb.EJBMockObjectFactory;
024: import com.mockrunner.mock.ejb.MockUserTransaction;
025: import com.mockrunner.util.common.ClassUtil;
026:
027: /**
028: * Module for EJB tests.
029: */
030: public class EJBTestModule {
031: private EJBMockObjectFactory mockFactory;
032: private String impSuffix;
033: private String homeInterfaceSuffix;
034: private String businessInterfaceSuffix;
035: private String homeInterfacePackage;
036: private String businessInterfacePackage;
037:
038: public EJBTestModule(EJBMockObjectFactory mockFactory) {
039: this .mockFactory = mockFactory;
040: impSuffix = "Bean";
041: homeInterfaceSuffix = "Home";
042: businessInterfaceSuffix = "";
043: }
044:
045: /**
046: * Sets the suffix of the bean implementation class. The
047: * default is <i>"Bean"</i>, i.e. if the remote interface has
048: * the name <code>Test</code> the implementation class is
049: * <code>TestBean</code>.
050: * @param impSuffix the bean implementation suffix
051: */
052: public void setImplementationSuffix(String impSuffix) {
053: this .impSuffix = impSuffix;
054: }
055:
056: /**
057: * Sets the suffix of the remote (local respectively) interface. The
058: * default is an empty string, i.e. if the implementation class is
059: * <code>TestBean</code>, the remote interface is <code>Test</code>
060: * @param businessInterfaceSuffix the bean remote interface suffix
061: */
062: public void setBusinessInterfaceSuffix(
063: String businessInterfaceSuffix) {
064: this .businessInterfaceSuffix = businessInterfaceSuffix;
065: }
066:
067: /**
068: * Sets the suffix of the home (local home respectively) interface. The
069: * default is <i>"Home"</i>, i.e. if the implementation class is
070: * <code>TestBean</code>, the home interface is <code>TestHome</code>
071: * @param homeInterfaceSuffix the bean home interface suffix
072: */
073: public void setHomeInterfaceSuffix(String homeInterfaceSuffix) {
074: this .homeInterfaceSuffix = homeInterfaceSuffix;
075: }
076:
077: /**
078: * Sets the package for the bean home and remote interfaces. Per
079: * default, the framework expects that the interfaces are in the
080: * same package as the bean implementation classes.
081: * @param interfacePackage the package name for home and remote interfaces
082: */
083: public void setInterfacePackage(String interfacePackage) {
084: setHomeInterfacePackage(interfacePackage);
085: setBusinessInterfacePackage(interfacePackage);
086: }
087:
088: /**
089: * Sets the package for the bean home (local home respectively) interface. Per
090: * default, the framework expects that the interfaces are in the
091: * same package as the bean implementation classes.
092: * @param homeInterfacePackage the package name for home interface
093: */
094: public void setHomeInterfacePackage(String homeInterfacePackage) {
095: this .homeInterfacePackage = homeInterfacePackage;
096: }
097:
098: /**
099: * Sets the package for the bean remote (local respectively) interface. Per
100: * default, the framework expects that the interfaces are in the
101: * same package as the bean implementation classes.
102: * @param businessInterfacePackage the package name for remote interface
103: */
104: public void setBusinessInterfacePackage(
105: String businessInterfacePackage) {
106: this .businessInterfacePackage = businessInterfacePackage;
107: }
108:
109: /**
110: * Deploys a bean to the mock container using the specified
111: * descriptor. Sets the transaction policy <i>SUPPORTS</i>.
112: * Determines the type of bean (session, entity, message driven)
113: * using the descriptor.
114: * @param descriptor the descriptor
115: */
116: public void deploy(BasicEjbDescriptor descriptor) {
117: deploy(descriptor, TransactionPolicy.SUPPORTS);
118: }
119:
120: /**
121: * Deploys a bean to the mock container using the specified
122: * descriptor. Determines the type of bean (session, entity, message driven)
123: * using the descriptor.
124: * The specified transaction policy will be automatically set. If the
125: * specified transaction policy is <code>null</code>, no transaction policy
126: * will be set. This makes sense for BMT EJBs. Please note that the
127: * <code>deploy</code> methods of this class without a transaction policy
128: * argument automatically set the <i>SUPPORTS</i> policy, which also
129: * works fine for BMT EJBs.
130: * @param descriptor the descriptor
131: * @param policy the transaction policy
132: */
133: public void deploy(BasicEjbDescriptor descriptor,
134: TransactionPolicy policy) {
135: try {
136: if (descriptor instanceof SessionBeanDescriptor) {
137: mockFactory.getMockContainer().deploy(
138: (SessionBeanDescriptor) descriptor);
139: } else if (descriptor instanceof EntityBeanDescriptor) {
140: mockFactory.getMockContainer().deploy(
141: (EntityBeanDescriptor) descriptor);
142: } else if (descriptor instanceof MDBDescriptor) {
143: mockFactory.getMockContainer().deploy(
144: (MDBDescriptor) descriptor);
145: }
146: if (null != policy) {
147: AspectSystemFactory.getAspectSystem().add(
148: new ClassPointcut(descriptor.getIfaceClass(),
149: false), new TransactionManager(policy));
150: }
151: } catch (Exception exc) {
152: throw new NestedApplicationException(exc);
153: }
154: }
155:
156: /**
157: * Deploys a stateless session bean to the mock container. You have to specify
158: * the implementation class and the JNDI name. The frameworks
159: * determines the home and remote interfaces based on the
160: * information specified with the <code>setSuffix</code>
161: * and <code>setPackage</code> methods.
162: * Sets the transaction policy <i>SUPPORTS</i>.
163: * @param jndiName the JNDI name
164: * @param beanClass the bean implementation class
165: */
166: public void deploySessionBean(String jndiName, Class beanClass) {
167: deploySessionBean(jndiName, beanClass, false,
168: TransactionPolicy.SUPPORTS);
169: }
170:
171: /**
172: * Deploys a session bean to the mock container. You have to specify
173: * the implementation class and the JNDI name. The frameworks
174: * determines the home and remote interfaces based on the
175: * information specified with the <code>setSuffix</code>
176: * and <code>setPackage</code> methods.
177: * Sets the transaction policy <i>SUPPORTS</i>.
178: * @param jndiName the JNDI name
179: * @param beanClass the bean implementation class
180: * @param stateful is the bean stateful
181: */
182: public void deploySessionBean(String jndiName, Class beanClass,
183: boolean stateful) {
184: deploySessionBean(jndiName, beanClass, stateful,
185: TransactionPolicy.SUPPORTS);
186: }
187:
188: /**
189: * Deploys a stateless session bean to the mock container. You have to specify
190: * the implementation class and the JNDI name. The frameworks
191: * determines the home and remote interfaces based on the
192: * information specified with the <code>setSuffix</code>
193: * and <code>setPackage</code> methods.
194: * The specified transaction policy will be automatically set.
195: * @param jndiName the JNDI name
196: * @param beanClass the bean implementation class
197: * @param policy the transaction policy
198: */
199: public void deploySessionBean(String jndiName, Class beanClass,
200: TransactionPolicy policy) {
201: deploySessionBean(jndiName, beanClass, false, policy);
202: }
203:
204: /**
205: * Deploys a session bean to the mock container. You have to specify
206: * the implementation class and the JNDI name. The frameworks
207: * determines the home and remote interfaces based on the
208: * information specified with the <code>setSuffix</code>
209: * and <code>setPackage</code> methods.
210: * The specified transaction policy will be automatically set.
211: * @param jndiName the JNDI name
212: * @param beanClass the bean implementation class
213: * @param stateful is the bean stateful
214: * @param policy the transaction policy
215: */
216: public void deploySessionBean(String jndiName, Class beanClass,
217: boolean stateful, TransactionPolicy policy) {
218: SessionBeanDescriptor descriptor = new SessionBeanDescriptor(
219: jndiName, getHomeClass(beanClass),
220: getRemoteClass(beanClass), beanClass);
221: descriptor.setStateful(stateful);
222: deploy(descriptor, policy);
223: }
224:
225: /**
226: * Deploys a stateless session bean to the mock container. You have to specify
227: * the implementation class and the JNDI name. The frameworks
228: * determines the home and remote interfaces based on the
229: * information specified with the <code>setSuffix</code>
230: * and <code>setPackage</code> methods.
231: * Sets the transaction policy <i>SUPPORTS</i>.
232: * @param jndiName the JNDI name
233: * @param bean the bean implementation
234: */
235: public void deploySessionBean(String jndiName, Object bean) {
236: deploySessionBean(jndiName, bean, false,
237: TransactionPolicy.SUPPORTS);
238: }
239:
240: /**
241: * Deploys a session bean to the mock container. You have to specify
242: * the implementation class and the JNDI name. The frameworks
243: * determines the home and remote interfaces based on the
244: * information specified with the <code>setSuffix</code>
245: * and <code>setPackage</code> methods.
246: * Sets the transaction policy <i>SUPPORTS</i>.
247: * @param jndiName the JNDI name
248: * @param bean the bean implementation
249: * @param stateful is the bean stateful
250: */
251: public void deploySessionBean(String jndiName, Object bean,
252: boolean stateful) {
253: deploySessionBean(jndiName, bean, stateful,
254: TransactionPolicy.SUPPORTS);
255: }
256:
257: /**
258: * Deploys a stateless session bean to the mock container. You have to specify
259: * the implementation class and the JNDI name. The frameworks
260: * determines the home and remote interfaces based on the
261: * information specified with the <code>setSuffix</code>
262: * and <code>setPackage</code> methods.
263: * The specified transaction policy will be automatically set.
264: * @param jndiName the JNDI name
265: * @param bean the bean implementation
266: * @param policy the transaction policy
267: */
268: public void deploySessionBean(String jndiName, Object bean,
269: TransactionPolicy policy) {
270: deploySessionBean(jndiName, bean, false, policy);
271: }
272:
273: /**
274: * Deploys a session bean to the mock container. You have to specify
275: * the implementation class and the JNDI name. The frameworks
276: * determines the home and remote interfaces based on the
277: * information specified with the <code>setSuffix</code>
278: * and <code>setPackage</code> methods.
279: * The specified transaction policy will be automatically set.
280: * @param jndiName the JNDI name
281: * @param bean the bean implementation
282: * @param stateful is the bean stateful
283: * @param policy the transaction policy
284: */
285: public void deploySessionBean(String jndiName, Object bean,
286: boolean stateful, TransactionPolicy policy) {
287: SessionBeanDescriptor descriptor = new SessionBeanDescriptor(
288: jndiName, getHomeClass(bean.getClass()),
289: getRemoteClass(bean.getClass()), bean);
290: descriptor.setStateful(stateful);
291: deploy(descriptor, policy);
292: }
293:
294: /**
295: * Deploys an entity bean to the mock container. You have to specify
296: * the implementation class and the JNDI name. The frameworks
297: * determines the home and remote interfaces based on the
298: * information specified with the <code>setSuffix</code>
299: * and <code>setPackage</code> methods.
300: * Sets the transaction policy <i>SUPPORTS</i>.
301: * @param jndiName the JNDI name
302: * @param beanClass the bean implementation class
303: */
304: public void deployEntityBean(String jndiName, Class beanClass) {
305: deployEntityBean(jndiName, beanClass,
306: TransactionPolicy.SUPPORTS);
307: }
308:
309: /**
310: * Deploys an entity bean to the mock container. You have to specify
311: * the implementation class and the JNDI name. The frameworks
312: * determines the home and remote interfaces based on the
313: * information specified with the <code>setSuffix</code>
314: * and <code>setPackage</code> methods.
315: * The specified transaction policy will be automatically set.
316: * @param jndiName the JNDI name
317: * @param beanClass the bean implementation class
318: * @param policy the transaction policy
319: */
320: public void deployEntityBean(String jndiName, Class beanClass,
321: TransactionPolicy policy) {
322: EntityBeanDescriptor descriptor = new EntityBeanDescriptor(
323: jndiName, getHomeClass(beanClass),
324: getRemoteClass(beanClass), beanClass);
325: deploy(descriptor, policy);
326: }
327:
328: /**
329: * Deploys a message driven bean to the mock container.
330: * You have to specify JNDI names for connection factory and
331: * destination. For creating connection factory and destination
332: * objects you can use {@link com.mockrunner.mock.jms.JMSMockObjectFactory}
333: * and {@link com.mockrunner.jms.DestinationManager}.
334: * The specified objects are automatically bound to JNDI using
335: * the specified names. The mock container automatically creates
336: * a connection and session.
337: * Sets the transaction policy <i>NOT_SUPPORTED</i>.
338: * @param connectionFactoryJndiName the JNDI name of the connection factory
339: * @param destinationJndiName the JNDI name of the destination
340: * @param connectionFactory the connection factory
341: * @param destination the destination
342: * @param bean the message driven bean instance
343: */
344: public void deployMessageBean(String connectionFactoryJndiName,
345: String destinationJndiName,
346: ConnectionFactory connectionFactory,
347: Destination destination, Object bean) {
348: deployMessageBean(connectionFactoryJndiName,
349: destinationJndiName, connectionFactory, destination,
350: bean, TransactionPolicy.NOT_SUPPORTED);
351: }
352:
353: /**
354: * Deploys a message driven bean to the mock container.
355: * You have to specify JNDI names for connection factory and
356: * destination. For creating connection factory and destination
357: * objects you can use {@link com.mockrunner.mock.jms.JMSMockObjectFactory}
358: * and {@link com.mockrunner.jms.DestinationManager}.
359: * The specified objects are automatically bound to JNDI using
360: * the specified names. The mock container automatically creates
361: * a connection and session.
362: * The specified transaction policy will be automatically set.
363: * @param connectionFactoryJndiName the JNDI name of the connection factory
364: * @param destinationJndiName the JNDI name of the destination
365: * @param connectionFactory the connection factory
366: * @param destination the destination
367: * @param bean the message driven bean instance
368: * @param policy the transaction policy
369: */
370: public void deployMessageBean(String connectionFactoryJndiName,
371: String destinationJndiName,
372: ConnectionFactory connectionFactory,
373: Destination destination, Object bean,
374: TransactionPolicy policy) {
375: bindToContext(connectionFactoryJndiName, connectionFactory);
376: bindToContext(destinationJndiName, destination);
377: MDBDescriptor descriptor = new MDBDescriptor(
378: connectionFactoryJndiName, destinationJndiName, bean);
379: descriptor.setIsAlreadyBound(true);
380: descriptor.setIsTopic(destination instanceof Topic);
381: deploy(descriptor, policy);
382: }
383:
384: /**
385: * Adds an object to the mock context by calling <code>rebind</code>
386: * @param name JNDI name of the object
387: * @param object the object to add
388: */
389: public void bindToContext(String name, Object object) {
390: try {
391: Context context = mockFactory.getContext();
392: context.rebind(name, object);
393: } catch (NamingException exc) {
394: throw new RuntimeException("Object with name " + name
395: + " not found.");
396: }
397: }
398:
399: /**
400: * Lookup an object. If the object is not bound to the <code>InitialContext</code>,
401: * a <code>RuntimeException</code> will be thrown.
402: * @param name JNDI name of the object
403: * @return the object
404: * @throws RuntimeException if an object with the specified name cannot be found.
405: */
406: public Object lookup(String name) {
407: try {
408: Context context = mockFactory.getContext();
409: return context.lookup(name);
410: } catch (NamingException exc) {
411: throw new RuntimeException("Object with name " + name
412: + " not found.");
413: }
414: }
415:
416: /**
417: * @deprecated use {@link #createBean(String)}
418: */
419: public Object lookupBean(String name) {
420: return createBean(name);
421: }
422:
423: /**
424: * Create an EJB. The method looks up the home interface, calls
425: * the <code>create</code> method and returns the result, which
426: * you can cast to the remote interface. This method only works
427: * with <code>create</code> methods that have an empty parameter list.
428: * The <code>create</code> method must have the name <code>create</code>
429: * with no postfix.
430: * It works with the mock container but may fail with a real remote container.
431: * This method throws a <code>RuntimeException</code> if no object with the
432: * specified name can be found. If the found object is no EJB home interface,
433: * or if the corresponding <code>create</code> method cannot be found, this
434: * method returns <code>null</code>.
435: * @param name JNDI name of the bean
436: * @return the bean
437: * @throws RuntimeException in case of error
438: */
439: public Object createBean(String name) {
440: return createBean(name, new Object[0]);
441: }
442:
443: /**
444: * @deprecated use {@link #createBean(String, Object[])}
445: */
446: public Object lookupBean(String name, Object[] parameters) {
447: return createBean(name, parameters);
448: }
449:
450: /**
451: * Create an EJB. The method looks up the home interface, calls
452: * the <code>create</code> method with the specified parameters
453: * and returns the result, which you can cast to the remote interface.
454: * The <code>create</code> method must have the name <code>create</code>
455: * with no postfix.
456: * This method works with the mock container but may fail with
457: * a real remote container.
458: * This method throws a <code>RuntimeException</code> if no object with the
459: * specified name can be found. If the found object is no EJB home interface,
460: * or if the corresponding <code>create</code> method cannot be found, this
461: * method returns <code>null</code>.
462: * This method does not allow <code>null</code> as a parameter, because
463: * the type of the parameter cannot be determined in this case.
464: * @param name JNDI name of the bean
465: * @param parameters the parameters, <code>null</code> parameters are not allowed,
466: * primitive types are automatically unwrapped
467: * @return the bean
468: * @throws RuntimeException in case of error
469: */
470: public Object createBean(String name, Object[] parameters) {
471: return createBean(name, "create", parameters);
472: }
473:
474: /**
475: * @deprecated use {@link #createBean(String, String, Object[])}
476: */
477: public Object lookupBean(String name, String createMethod,
478: Object[] parameters) {
479: return createBean(name, createMethod, parameters);
480: }
481:
482: /**
483: * Create an EJB. The method looks up the home interface, calls
484: * the <code>create</code> method with the specified parameters
485: * and returns the result, which you can cast to the remote interface.
486: * This method works with the mock container but may fail with
487: * a real remote container.
488: * This method throws a <code>RuntimeException</code> if no object with the
489: * specified name can be found. If the found object is no EJB home interface,
490: * or if the corresponding <code>create</code> method cannot be found, this
491: * method returns <code>null</code>.
492: * This method does not allow <code>null</code> as a parameter, because
493: * the type of the parameter cannot be determined in this case.
494: * @param name JNDI name of the bean
495: * @param createMethod the name of the create method
496: * @param parameters the parameters, <code>null</code> parameters are not allowed,
497: * primitive types are automatically unwrapped
498: * @return the bean
499: * @throws RuntimeException in case of error
500: */
501: public Object createBean(String name, String createMethod,
502: Object[] parameters) {
503: Object home = lookupHome(name);
504: return invokeHomeMethod(home, createMethod, parameters, null);
505: }
506:
507: /**
508: * Create an EJB. The method looks up the home interface, calls
509: * the <code>create</code> method with the specified parameters
510: * and returns the result, which you can cast to the remote interface.
511: * This method works with the mock container but may fail with
512: * a real remote container.
513: * This method throws a <code>RuntimeException</code> if no object with the
514: * specified name can be found. If the found object is no EJB home interface,
515: * or if the corresponding <code>create</code> method cannot be found, this
516: * method returns <code>null</code>.
517: * This method does allow <code>null</code> as a parameter.
518: * @param name JNDI name of the bean
519: * @param createMethod the name of the create method
520: * @param parameters the parameters, <code>null</code> is allowed as a parameter
521: * @param parameterTypes the type of the specified parameters
522: * @return the bean
523: * @throws RuntimeException in case of error
524: */
525: public Object createBean(String name, String createMethod,
526: Object[] parameters, Class[] parameterTypes) {
527: Object home = lookupHome(name);
528: return invokeHomeMethod(home, createMethod, parameters,
529: parameterTypes);
530: }
531:
532: /**
533: * Create an entity EJB. The method looks up the home interface, calls
534: * the <code>create</code> method and returns the result, which
535: * you can cast to the remote interface. This method only works
536: * with <code>create</code> methods that have an empty parameter list.
537: * The <code>create</code> method must have the name <code>create</code>
538: * with no postfix.
539: * It works with the mock container but may fail with a real remote container.
540: * This method throws a <code>RuntimeException</code> if no object with the
541: * specified name can be found. If the found object is no EJB home interface,
542: * or if the corresponding <code>create</code> method cannot be found, this
543: * method returns <code>null</code>.
544: * The created entity EJB is added to the mock database automatically
545: * using the provided primary key.
546: * @param name JNDI name of the bean
547: * @param primaryKey the primary key
548: * @return the bean
549: * @throws RuntimeException in case of error
550: */
551: public Object createEntityBean(String name, Object primaryKey) {
552: return createEntityBean(name, new Object[0], primaryKey);
553: }
554:
555: /**
556: * Create an entity EJB. The method looks up the home interface, calls
557: * the <code>create</code> method with the specified parameters
558: * and returns the result, which you can cast to the remote interface.
559: * The <code>create</code> method must have the name <code>create</code>
560: * with no postfix.
561: * This method works with the mock container but may fail with
562: * a real remote container.
563: * This method throws a <code>RuntimeException</code> if no object with the
564: * specified name can be found. If the found object is no EJB home interface,
565: * or if the corresponding <code>create</code> method cannot be found, this
566: * method returns <code>null</code>.
567: * The created entity EJB is added to the mock database automatically
568: * using the provided primary key.
569: * This method does not allow <code>null</code> as a parameter, because
570: * the type of the parameter cannot be determined in this case.
571: * @param name JNDI name of the bean
572: * @param parameters the parameters, <code>null</code> parameters are not allowed,
573: * primitive types are automatically unwrapped
574: * @param primaryKey the primary key
575: * @return the bean
576: * @throws RuntimeException in case of error
577: */
578: public Object createEntityBean(String name, Object[] parameters,
579: Object primaryKey) {
580: return createEntityBean(name, "create", parameters, primaryKey);
581: }
582:
583: /**
584: * Create an entity EJB. The method looks up the home interface, calls
585: * the <code>create</code> method with the specified parameters
586: * and returns the result, which you can cast to the remote interface.
587: * This method works with the mock container but may fail with
588: * a real remote container.
589: * This method throws a <code>RuntimeException</code> if no object with the
590: * specified name can be found. If the found object is no EJB home interface,
591: * or if the corresponding <code>create</code> method cannot be found, this
592: * method returns <code>null</code>.
593: * The created entity EJB is added to the mock database automatically
594: * using the provided primary key.
595: * This method does not allow <code>null</code> as a parameter, because
596: * the type of the parameter cannot be determined in this case.
597: * @param name JNDI name of the bean
598: * @param createMethod the name of the create method
599: * @param parameters the parameters, <code>null</code> parameters are not allowed,
600: * primitive types are automatically unwrapped
601: * @param primaryKey the primary key
602: * @return the bean
603: * @throws RuntimeException in case of error
604: */
605: public Object createEntityBean(String name, String createMethod,
606: Object[] parameters, Object primaryKey) {
607: return createEntityBean(name, createMethod, parameters,
608: (Class[]) null, primaryKey);
609: }
610:
611: /**
612: * Create an entity EJB. The method looks up the home interface, calls
613: * the <code>create</code> method with the specified parameters
614: * and returns the result, which you can cast to the remote interface.
615: * This method works with the mock container but may fail with
616: * a real remote container.
617: * This method throws a <code>RuntimeException</code> if no object with the
618: * specified name can be found. If the found object is no EJB home interface,
619: * or if the corresponding <code>create</code> method cannot be found, this
620: * method returns <code>null</code>.
621: * The created entity EJB is added to the mock database automatically
622: * using the provided primary key.
623: * This method does allow <code>null</code> as a parameter.
624: * @param name JNDI name of the bean
625: * @param createMethod the name of the create method
626: * @param parameters the parameters, <code>null</code> is allowed as a parameter
627: * @param primaryKey the primary key
628: * @return the bean
629: * @throws RuntimeException in case of error
630: */
631: public Object createEntityBean(String name, String createMethod,
632: Object[] parameters, Class[] parameterTypes,
633: Object primaryKey) {
634: Object home = lookupHome(name);
635: Object remote = invokeHomeMethod(home, createMethod,
636: parameters, parameterTypes);
637: Class[] interfaces = home.getClass().getInterfaces();
638: Class homeInterface = getHomeInterfaceClass(interfaces);
639: if (null != homeInterface && null != remote) {
640: mockFactory.getMockContainer().getEntityDatabase().add(
641: homeInterface, primaryKey, remote);
642: }
643: return remote;
644: }
645:
646: /**
647: * Finds an entity EJB by its primary key. The method looks up the home interface,
648: * calls the <code>findByPrimaryKey</code> method and returns the result,
649: * which you can cast to the remote interface.
650: * This method works with the mock container but may fail with
651: * a real remote container.
652: * This method throws a <code>RuntimeException</code> if no object with the
653: * specified name can be found. If the found object is no EJB home interface,
654: * or if the <code>findByPrimaryKey</code> method cannot be found, this
655: * method returns <code>null</code>.
656: * If the mock container throws an exception because the primary key
657: * cannot be found in the entity database, this method returns <code>null</code>.
658: * @param name JNDI name of the bean
659: * @param primaryKey the primary key
660: * @return the bean
661: * @throws RuntimeException in case of error
662: */
663: public Object findByPrimaryKey(String name, Object primaryKey) {
664: Object home = lookupHome(name);
665: return invokeHomeMethod(home, "findByPrimaryKey",
666: new Object[] { primaryKey }, null);
667: }
668:
669: private Class getHomeInterfaceClass(Class[] interfaces) {
670: for (int ii = 0; ii < interfaces.length; ii++) {
671: Class current = interfaces[ii];
672: if (EJBHome.class.isAssignableFrom(current)
673: || EJBLocalHome.class.isAssignableFrom(current)) {
674: return current;
675: }
676: }
677: return null;
678: }
679:
680: private Object lookupHome(String name) {
681: Object object = lookup(name);
682: if (null == object)
683: return null;
684: if (!(object instanceof EJBHome || object instanceof EJBLocalHome))
685: return null;
686: return object;
687: }
688:
689: private Object invokeHomeMethod(Object home, String methodName,
690: Object[] parameters, Class[] parameterTypes) {
691: if (null == parameterTypes) {
692: checkNullParameters(methodName, parameters);
693: }
694: try {
695: if (null == parameterTypes) {
696: return MethodUtils.invokeMethod(home, methodName,
697: parameters);
698: } else {
699: return MethodUtils.invokeExactMethod(home, methodName,
700: parameters, parameterTypes);
701: }
702: } catch (Exception exc) {
703: return null;
704: }
705: }
706:
707: private void checkNullParameters(String createMethod,
708: Object[] parameters) {
709: for (int ii = 0; ii < parameters.length; ii++) {
710: if (null == parameters[ii]) {
711: String message = "Calling method " + createMethod
712: + " failed. ";
713: message += "Null is not allowed if the parameter types are not specified.";
714: throw new IllegalArgumentException(message);
715: }
716: }
717: }
718:
719: /**
720: * Resets the {@link com.mockrunner.mock.ejb.MockUserTransaction}.
721: * Note: If you do not use the {@link com.mockrunner.mock.ejb.MockUserTransaction}
722: * implementation, this method does nothing.
723: */
724: public void resetUserTransaction() {
725: MockUserTransaction transaction = mockFactory
726: .getMockUserTransaction();
727: if (null == transaction)
728: return;
729: transaction.reset();
730: }
731:
732: /**
733: * Verifies that the transaction was committed. If you are using
734: * container managed transactions, you have to set an appropriate
735: * transaction policy, e.g. <i>REQUIRED</i>. Otherwise the container
736: * will not commit the mock transaction.
737: * Note: If you do not use the {@link com.mockrunner.mock.ejb.MockUserTransaction}
738: * implementation, this method throws a <code>VerifyFailedException</code>.
739: * @throws VerifyFailedException if verification fails
740: */
741: public void verifyCommitted() {
742: MockUserTransaction transaction = mockFactory
743: .getMockUserTransaction();
744: if (null == transaction) {
745: throw new VerifyFailedException("MockTransaction is null.");
746: }
747: if (!transaction.wasCommitCalled()) {
748: throw new VerifyFailedException(
749: "Transaction was not committed.");
750: }
751: }
752:
753: /**
754: * Verifies that the transaction was not committed. If you are using
755: * container managed transactions, you have to set an appropriate
756: * transaction policy, e.g. <i>REQUIRED</i>.
757: * Note: If you do not use the {@link com.mockrunner.mock.ejb.MockUserTransaction}
758: * implementation, this method throws a <code>VerifyFailedException</code>.
759: * @throws VerifyFailedException if verification fails
760: */
761: public void verifyNotCommitted() {
762: MockUserTransaction transaction = mockFactory
763: .getMockUserTransaction();
764: if (null == transaction) {
765: throw new VerifyFailedException("MockTransaction is null.");
766: }
767: if (transaction.wasCommitCalled()) {
768: throw new VerifyFailedException(
769: "Transaction was committed.");
770: }
771: }
772:
773: /**
774: * Verifies that the transaction was rolled back. If you are using
775: * container managed transactions, you have to set an appropriate
776: * transaction policy, e.g. <i>REQUIRED</i>. Otherwise the container
777: * will not rollback the mock transaction.
778: * Note: If you do not use the {@link com.mockrunner.mock.ejb.MockUserTransaction}
779: * implementation, this method throws a <code>VerifyFailedException</code>.
780: * @throws VerifyFailedException if verification fails
781: */
782: public void verifyRolledBack() {
783: MockUserTransaction transaction = mockFactory
784: .getMockUserTransaction();
785: if (null == transaction) {
786: throw new VerifyFailedException("MockTransaction is null.");
787: }
788: if (!transaction.wasRollbackCalled()) {
789: throw new VerifyFailedException(
790: "Transaction was not rolled back");
791: }
792: }
793:
794: /**
795: * Verifies that the transaction was not rolled back. If you are using
796: * container managed transactions, you have to set an appropriate
797: * transaction policy, e.g. <i>REQUIRED</i>.
798: * Note: If you do not use the {@link com.mockrunner.mock.ejb.MockUserTransaction}
799: * implementation, this method throws a <code>VerifyFailedException</code>.
800: * @throws VerifyFailedException if verification fails
801: */
802: public void verifyNotRolledBack() {
803: MockUserTransaction transaction = mockFactory
804: .getMockUserTransaction();
805: if (null == transaction) {
806: throw new VerifyFailedException("MockTransaction is null.");
807: }
808: if (transaction.wasRollbackCalled()) {
809: throw new VerifyFailedException(
810: "Transaction was rolled back");
811: }
812: }
813:
814: /**
815: * Verifies that the transaction was marked for rollback using
816: * the method <code>setRollbackOnly()</code>.
817: * Note: If you do not use the {@link com.mockrunner.mock.ejb.MockUserTransaction}
818: * implementation, this method throws a <code>VerifyFailedException</code>.
819: * @throws VerifyFailedException if verification fails
820: */
821: public void verifyMarkedForRollback() {
822: MockUserTransaction transaction = mockFactory
823: .getMockUserTransaction();
824: if (null == transaction) {
825: throw new VerifyFailedException("MockTransaction is null.");
826: }
827: if (!transaction.wasRollbackOnlyCalled()) {
828: throw new VerifyFailedException(
829: "Transaction was not marked for rollback");
830: }
831: }
832:
833: /**
834: * Verifies that the transaction was not marked for rollback.
835: * Note: If you do not use the {@link com.mockrunner.mock.ejb.MockUserTransaction}
836: * implementation, this method throws a <code>VerifyFailedException</code>.
837: * @throws VerifyFailedException if verification fails
838: */
839: public void verifyNotMarkedForRollback() {
840: MockUserTransaction transaction = mockFactory
841: .getMockUserTransaction();
842: if (null == transaction) {
843: throw new VerifyFailedException("MockTransaction is null.");
844: }
845: if (transaction.wasRollbackOnlyCalled()) {
846: throw new VerifyFailedException(
847: "Transaction was marked for rollback");
848: }
849: }
850:
851: private Class getHomeClass(Class beanClass) {
852: String classPackage = ClassUtil.getPackageName(beanClass);
853: String className = ClassUtil.getClassName(beanClass);
854: className = truncateImplClassName(className);
855: if (null != homeInterfaceSuffix
856: && 0 != homeInterfaceSuffix.length()) {
857: className += homeInterfaceSuffix;
858: }
859: if (null != homeInterfacePackage
860: && 0 != homeInterfacePackage.length()) {
861: classPackage = homeInterfacePackage;
862: }
863: try {
864: return Class.forName(getClassName(classPackage, className),
865: true, beanClass.getClassLoader());
866: } catch (ClassNotFoundException exc) {
867: throw new RuntimeException("Home interface not found: "
868: + exc.getMessage());
869: }
870: }
871:
872: private Class getRemoteClass(Class beanClass) {
873: String classPackage = ClassUtil.getPackageName(beanClass);
874: String className = ClassUtil.getClassName(beanClass);
875: className = truncateImplClassName(className);
876: if (null != businessInterfaceSuffix
877: && 0 != businessInterfaceSuffix.length()) {
878: className += businessInterfaceSuffix;
879: }
880: if (null != businessInterfacePackage
881: && 0 != businessInterfacePackage.length()) {
882: classPackage = businessInterfacePackage;
883: }
884: try {
885: return Class.forName(getClassName(classPackage, className),
886: true, beanClass.getClassLoader());
887: } catch (ClassNotFoundException exc) {
888: throw new RuntimeException("Interface not found: "
889: + exc.getMessage());
890: }
891: }
892:
893: private String getClassName(String packageName, String className) {
894: if (null == packageName || packageName.length() == 0)
895: return className;
896: return packageName + "." + className;
897: }
898:
899: private String truncateImplClassName(String className) {
900: if (null != impSuffix && className.endsWith(impSuffix)) {
901: className = className.substring(0, className.length()
902: - impSuffix.length());
903: }
904: return className;
905: }
906: }
|