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