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