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 }