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