/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.test.util.ejb; import java.lang.reflect.Constructor; import java.util.Properties; import javax.ejb.EJBException; import javax.ejb.SessionBean; import javax.ejb.SessionContext; import javax.naming.Binding; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.naming.NamingEnumeration; import javax.transaction.Status; import javax.transaction.SystemException; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import org.jboss.logging.Logger; import org.jboss.tm.TransactionManagerLocator; /** * Implementation of the ejb test runner. * * @see EJBTestRunner * * @author <a href="mailto:dain@daingroup.com">Dain Sundstrom</a> * @author Scott.Stark@jboss.org * @version $Revision: 65582 $ */ public class EJBTestRunnerBean implements SessionBean { private static final Logger log = Logger.getLogger(EJBTestRunnerBean.class); transient private SessionContext ctx; private String runnerJndiName; /** Run the specified test method on the given class name using a Properties * map built from all java:comp/env entries. * * @param className the name of the test class * @param methodName the name of the test method * @throws RemoteTestException If any throwable is thrown during * execution of the method, it is wrapped with a RemoteTestException and * rethrown. */ public void run(String className, String methodName) throws RemoteTestException { Properties props = new Properties(); try { InitialContext ctx = new InitialContext(); NamingEnumeration bindings = ctx.listBindings("java:comp/env"); while( bindings.hasMore() ) { Binding binding = (Binding) bindings.next(); String name = binding.getName(); String value = binding.getObject().toString(); props.setProperty(name, value); } } catch(NamingException e) { throw new RemoteTestException(e); } run(className, methodName, props); } /** Run the specified test method on the given class name * * @param className the name of the test class * @param methodName the name of the test method * @param props * @throws RemoteTestException If any throwable is thrown during * execution of the method, it is wrapped with a RemoteTestException and * rethrown. */ public void run(String className, String methodName, Properties props) throws RemoteTestException { EJBTestCase testCase = getTestInstance(className, methodName); setUpEJB(testCase, props); RemoteTestException exception = null; try { runTestCase(testCase); } catch (RemoteTestException e) { exception = e; } finally { try { tearDownEJB(testCase, props); } catch (RemoteTestException e) { // favor the run exception if one was thrown if (exception != null) { exception = e; } } if (exception != null) { throw exception; } } } private static boolean wantUserTransaction(Properties props) { return props == null || props.get("NO_USER_TRANSACTION") == null; } /** * Runs the setUpEJB method on the specified test case * @param testCase the actual test case that will be run * @throws RemoteTestException If any throwable is thrown during execution * of the method, it is wrapped with a RemoteTestException and rethrown. */ private void setUpEJB(EJBTestCase testCase, Properties props) throws RemoteTestException { boolean wantUserTransaction = wantUserTransaction(props); try { if (wantUserTransaction) ctx.getUserTransaction().begin(); try { testCase.setUpEJB(props); } catch (Throwable e) { throw new RemoteTestException(e); } if (wantUserTransaction && ctx.getUserTransaction().getStatus() == Status.STATUS_ACTIVE) { ctx.getUserTransaction().commit(); } } catch (Throwable e) { try { if (wantUserTransaction) ctx.getUserTransaction().rollback(); } catch (SystemException unused) { // eat the exception we are exceptioning out anyway } if (e instanceof RemoteTestException) { throw (RemoteTestException) e; } throw new RemoteTestException(e); } } /** * Runs the test method on the specified test case * @param testCase the actual test case that will be run * @throws RemoteTestException If any throwable is thrown during execution * of the method, it is wrapped with a RemoteTestException and rethrown. */ private void runTestCase(EJBTestCase testCase) throws RemoteTestException { try { boolean wantUserTransaction = wantUserTransaction(testCase.getProps()); try { if (wantUserTransaction) ctx.getUserTransaction().begin(); try { testCase.runBare(); } catch (Throwable e) { throw new RemoteTestException(e); } if (wantUserTransaction && ctx.getUserTransaction().getStatus() == Status.STATUS_ACTIVE) { ctx.getUserTransaction().commit(); } } catch (Throwable e) { try { if (wantUserTransaction) ctx.getUserTransaction().rollback(); } catch (SystemException unused) { // eat the exception we are exceptioning out anyway } if (e instanceof RemoteTestException) { throw (RemoteTestException) e; } throw new RemoteTestException(e); } } finally { Transaction tx = null; TransactionManager tm = TransactionManagerLocator.getInstance().locate(); try { tx = tm.getTransaction(); if (tx != null) { try { tx.rollback(); } finally { tm.suspend(); } } } catch (Exception e) { log.error("Error rolling back incomplete transaction: " + tx, e); } } } /** * Runs the tearDownEJB method on the specified test case * @param testCase the actual test case that will be run * @throws RemoteTestException If any throwable is thrown during execution * of the method, it is wrapped with a RemoteTestException and rethrown. */ private void tearDownEJB(EJBTestCase testCase, Properties props) throws RemoteTestException { boolean wantUserTransaction = wantUserTransaction(props); try { if (wantUserTransaction) ctx.getUserTransaction().begin(); try { testCase.tearDownEJB(props); } catch (Throwable e) { throw new RemoteTestException(e); } if (wantUserTransaction && ctx.getUserTransaction().getStatus() == Status.STATUS_ACTIVE) { ctx.getUserTransaction().commit(); } } catch (Throwable e) { try { if (wantUserTransaction) ctx.getUserTransaction().rollback(); } catch (SystemException unused) { // eat the exception we are exceptioning out anyway } if (e instanceof RemoteTestException) { throw (RemoteTestException) e; } throw new RemoteTestException(e); } } /** * Gets a instance of the test class with the specified class name and * initialized to execute the specified method. * * @param className the name of the test class * @param methodName the name of the test method * @return a new instance of the test class with the specified class name and * initialized to execute the specified method. */ private EJBTestCase getTestInstance(String className, String methodName) { Class testClass = null; try { ClassLoader loader = Thread.currentThread().getContextClassLoader(); testClass = loader.loadClass(className); } catch (ClassNotFoundException e) { throw new EJBException("Test class not found : " + className); } Constructor constructor = null; try { constructor = testClass.getConstructor(new Class[]{String.class}); } catch (Exception e) { throw new EJBException("Test class does not have a constructor " + "which has a single String argument.", e); } try { EJBTestCase testCase = (EJBTestCase) constructor.newInstance(new Object[]{methodName}); testCase.setServerSide(true); return testCase; } catch (Exception e) { throw new EJBException("Cannot instantiate test class: " + testClass.getName(), e); } } public void ejbCreate() { } public void ejbRemove() { } public void ejbActivate() { } public void ejbPassivate() { } public void setSessionContext(SessionContext ctx) { this.ctx = ctx; } }