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