EJB3EmbeddedPerformanceTips

Version 3

    Tips for improving performance with the Embedded EJB3 container.

     

    • Speedup JUnit Tests By Limiting bootup class scanning.

     

    When bootstrapping the EJB3 container the tutorial guides you to the following:

     

        EJB3StandaloneBootstrap.boot(null);
        EJB3StandaloneBootstrap.deployXmlResource("jboss-jms-beans.xml");
        EJB3StandaloneBootstrap.deployXmlResource("jboss-requestsvc-beans.xml");
        EJB3StandaloneBootstrap.scanClasspath();
    

     

    The last statement results in an exhaustive search of your classpath, which can take considerable time (the JRE rt.jar contains many classes!). On my machine (Dual Core, 3GHz, 2GB RAM, 10KRPM SATA disk) the exhaustive scan takes about 30 seconds.

     

    You can narrow the search by specifying a Deployer that will only scan archives that contain a particular resource. To do so, you can use the class below. To use the class you need to:

     

    1. Make sure that your compiled code contains the file META-INF/ejb-jar.xml. If you are using a project structured in the "maven" form, then put this file in the "[Project|project]/src/main/resources/META-INF" directory:

     

    <?xml version="1.0"?>
    <ejb-jar
      xmlns="http://java.sun.com/xml/ns/javaee"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"
      version="3.0">
      
      <!-- You don't need to list anything here, you can still use annotations. -->
      <!-- At the very least, however, have this blank file! -->
    </ejb-jar>
    

     

    Now make sure whatever build process you are using puts copies the META-INF directory to the same location as your compiled production classes (again, if you are following the maven structure this will be "[Project|project]/target/main/build" directory).

     

    1. Your test case should start out like this:

     

    public class MyEJBTestCase
        extends TestCase
    {
      public static Test suite()
          throws Exception
      {
        return EmbeddedJBossEJB3TestCaseHelper.embeddedEjb3TestSuite(
            MyEJBTestCase.class);
    //  Call this way if you need to load any extra resources
    //    return EmbeddedJBossEJB3TestCaseHelper.embeddedEjb3TestSuite(
    //        MyEJBTestCase.class, new String[] {"myresources-beans.xml"});
      }
    }
    

     

    1. Add this class to your test sources:

     

    import junit.extensions.TestSetup;
    import junit.framework.Test;
    import junit.framework.TestCase;
    import junit.framework.TestSuite;
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    import org.jboss.ejb3.embedded.EJB3StandaloneBootstrap;
    import org.jboss.ejb3.embedded.EJB3StandaloneDeployer;
    
    /**
     * Helper class to speed up / hide JBoss Embedded EJB3 Container. Compiled from
     * bits and pieces of the tutorial.
     *
     * @author Sam Wilson, Ecount, Inc.
     */
    public class EmbeddedJBossEJB3TestCaseHelper
    {
      private static final Log log =
          LogFactory.getLog(EmbeddedJBossEJB3TestCaseHelper.class);
    
      /**
       * Deployer used to load your EJBs.
       */
      private static EJB3StandaloneDeployer deployer;
    
      /**
       * Construct test case wrapper that bootstraps the EJB3 container.
       * 
       * @param clazz Your test class.
       * @return The wrapped test suite.
       * @throws Exception If anything goes wrong.
       */
      public static Test embeddedEjb3TestSuite(Class<? extends TestCase> clazz)
          throws Exception
      {
        return embeddedEjb3TestSuite(clazz, null);
      }
    
      /**
       * Construct test case wrapper that bootstraps the EJB3 container with
       * extra bean definition resources if necessary (data sources, etc.).
       * 
       * @param clazz Your test class.
       * @param extraResources Array of extra resource names.
       * @return The wrapped test suite.
       * @throws Exception If anything goes wrong.
       */
      public static Test embeddedEjb3TestSuite(Class<? extends TestCase> clazz,
                                               final String[] extraResources)
          throws Exception
      {
        TestSuite suite = new TestSuite();
        suite.addTestSuite(clazz);
    
        return new TestSetup(suite)
        {
          @Override
          protected void setUp()
              throws Exception
          {
            // These properties must be set to get the embedded JBoss EJB
            // implementation working. The docs had these being set as properties
            // passed to the new InitialContext() call. However, if you have legacy
            // code that does a JNDI lookup that can't be configured we have to set
            // these as system properties instead.
            System.setProperty(
                "java.naming.factory.initial",
                "org.jnp.interfaces.LocalOnlyContextFactory");
            System.setProperty(
                "java.naming.factory.url.pkgs",
                "org.jboss.naming:org.jnp.interfaces");
    
            startupEmbeddedJboss(extraResources);
          }
    
          @Override
          protected void tearDown()
              throws Exception
          {
            super.tearDown();
            shutdownEmbeddedJboss();
          }
        };
      }
    
      /**
       * Initialze the EJB3 container and load extra resources (connections, queues, 
       * etc.)
       * 
       * @param extraResources Array of extra resource names.
       */
      @SuppressWarnings("unchecked")
      public static void startupEmbeddedJboss(String[] extraResources)
      {
        log.info("Bootstrapping Embedded JBoss");
        long clock = System.currentTimeMillis();
    
        try {
          EJB3StandaloneBootstrap.ignoredJars.add("rt.jar");
          EJB3StandaloneBootstrap.boot(null);
          EJB3StandaloneBootstrap.deployXmlResource("jboss-jms-beans.xml");
    
          if(extraResources != null) {
            for(String resource : extraResources) {
              EJB3StandaloneBootstrap.deployXmlResource(resource);
            }
          }
    
          deployer = EJB3StandaloneBootstrap.createDeployer();
          deployer.getArchivesByResource().add("META-INF/ejb-jar.xml");
          deployer.create();
          deployer.start();
        }
        catch (RuntimeException ex) {
          log.fatal("Failure during bootstrap of Embedded JBoss", ex);
          throw ex;
        }
        catch (Exception ex) {
          log.fatal(ex);
          throw new RuntimeException(ex);
        }
        finally {
          long duration = System.currentTimeMillis() - clock;
          log.info(
              "Completed Bootstrapping of Embedded JBoss (" + duration + "ms)");
        }
      }
    
      /**
       * Shutdown the EJB3 container.
       */
      public static void shutdownEmbeddedJboss()
      {
        log.info("Shutting down Embedded JBoss");
    
        try {
          if (deployer != null) {
            deployer.stop();
            deployer.destroy();
          }
          EJB3StandaloneBootstrap.shutdown();
        }
        catch (RuntimeException ex) {
          log.fatal("Failure during shutdown of embedded JBoss", ex);
          throw ex;
        }
        catch (Exception ex) {
          log.fatal(ex);
          throw new RuntimeException(ex);
        }
        finally {
          log.info("Embedded JBoss Shutdown Complete");
        }
      }
    }