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 }