9 Replies Latest reply on Jan 26, 2010 3:06 PM by alrubinger

    Integration with OpenEJB

    alrubinger

      I've stalked David Blevins on Twitter to see if he'd like to bring ShrinkWrap support to OpenEJB (I think the aims of the projects are consistent).  This is my public response for us to sync up.


      ShrinkWrap archives can be integrated in at least 3 ways, depending upon your hooks:

       

      1) URL/File: We can export an archive in ZIP format to a File, and go into the container as any ol' JAR deployment.  Probably the lowest common denominator, involves encoding to ZIP and File I/O.  We do this currently in EmbeddedAS.

       

      2) byte[]/InputStream and String name: Similar to the above, only we don't flush to a file; if the container accepts bytes representing a JAR/EAR/WAR with some name this is a good option.

       

      3) Virtual Filesystem: If your container has a virtual filesystem of some type that we can extend/implement to understand how to traverse a ShrinkWrap archive, then we can deploy the archive natively and avoid a ZIP export entirely.  We'll be doing this in EmbeddedAS very soon.

       

      S,

      ALR

        • 1. Re: Integration with OpenEJB
          alrubinger

          Excuse my ugly prototyping code, but here's an example of Jetty integration using technique 1):

           

          /*
           * JBoss, Home of Professional Open Source.
           * Copyright 2010, 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.iface.jetty.impl.deployment;
          
          import java.io.File;
          import java.net.MalformedURLException;
          import java.net.URL;
          import java.security.AccessController;
          import java.security.PrivilegedAction;
          import java.util.logging.Logger;
          
          import org.jboss.iface.jetty.spi.deployment.JettyShrinkWrapDeployer;
          import org.jboss.iface.jetty.spi.service.JettyService;
          import org.jboss.iface.spi.deployment.DeploymentException;
          import org.jboss.shrinkwrap.api.exporter.ZipExporter;
          import org.jboss.shrinkwrap.api.spec.WebArchive;
          import org.mortbay.jetty.Server;
          import org.mortbay.jetty.servlet.Context;
          import org.mortbay.jetty.webapp.WebAppContext;
          
          /**
           * Deployment connector such that {@link JettyService} containers can 
           * support ShrinkWrap {@link WebArchive} types.
           * 
           * TODO: Undeployment 
           * 
           * TODO: PROTOTYPING IN PROGRESS
           *
           * @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a>
           */
          public class JettyShrinkWrapDeployerImpl implements JettyShrinkWrapDeployer
          {
          
             //-------------------------------------------------------------------------------------||
             // Class Members ----------------------------------------------------------------------||
             //-------------------------------------------------------------------------------------||
          
             /**
              * Logger
              */
             private static final Logger log = Logger.getLogger(JettyShrinkWrapDeployerImpl.class.getName());
          
             /**
              * System property denoting the name of the temp directory
              */
             private static final String SYSPROP_KEY_TMP_DIR = "java.io.tmpdir";
          
             /**
              * Temporary directory into which we'll extract the {@link WebArchive}s
              */
             private static final File TMP_DIR;
             static
             {
                TMP_DIR = new File(AccessController.doPrivileged(new PrivilegedAction<String>()
                {
          
                   @Override
                   public String run()
                   {
                      return System.getProperty(SYSPROP_KEY_TMP_DIR);
                   }
          
                }));
                // If the temp location doesn't exist or isn't a directory
                if (!TMP_DIR.exists() || !TMP_DIR.isDirectory())
                {
                   throw new IllegalStateException("Could not obtain temp directory \"" + TMP_DIR.getAbsolutePath() + "\"");
                }
             }
          
             //-------------------------------------------------------------------------------------||
             // Instance Members -------------------------------------------------------------------||
             //-------------------------------------------------------------------------------------||
          
             //-------------------------------------------------------------------------------------||
             // Constructor ------------------------------------------------------------------------||
             //-------------------------------------------------------------------------------------||
          
             //-------------------------------------------------------------------------------------||
             // Required Implementations -----------------------------------------------------------||
             //-------------------------------------------------------------------------------------||
          
             /**
              * {@inheritDoc}
              * @see org.jboss.iface.spi.deployment.Deployer#deploy(java.lang.Object, D[])
              */
             @Override
             public void deploy(final JettyService container, final WebArchive... deployables) throws DeploymentException,
                   IllegalArgumentException
             {
                // Precondition check
                assertDeployables(deployables);
          
                // Get the server
                final Server server = container.getServer();
          
                for (final WebArchive deployable : deployables)
                {
                   // Flush to file
                   final String name = deployable.getName();
                   final File exported = new File(TMP_DIR, deployable.getName());
                   deployable.as(ZipExporter.class).exportZip(exported);
          
                   // Mark to delete when we come down
                   exported.deleteOnExit();
          
                   // Add the context
                   final URL url;
                   try
                   {
                      url = exported.toURI().toURL();
                   }
                   catch (final MalformedURLException e)
                   {
                      throw new RuntimeException("Could not obtain URL of File " + exported.getAbsolutePath(), e);
                   }
                   log.info("Webapp location: " + url);
                   final Context context = new WebAppContext(url.toExternalForm(), "/" + name);
                   server.addHandler(context);
                   log.info("Deployed under context: " + name);
                }
          
                // Restart if necessary
                if (server.isRunning())
                {
                   try
                   {
                      container.stop();
                   }
                   catch (final Exception e)
                   {
                      log.warning("Problem in stopping the server cleanly: " + e);
                   }
          
                   try
                   {
                      container.start();
                   }
                   catch (final Exception e)
                   {
                      throw new RuntimeException(e);
                   }
          
                }
             }
          
             /**
              * {@inheritDoc}
              * @see org.jboss.iface.spi.deployment.Deployer#undeploy(java.lang.Object, D[])
              */
             @Override
             public void undeploy(JettyService container, WebArchive... deployables) throws DeploymentException,
                   IllegalArgumentException
             {
                // Precondition check
                assertDeployables(deployables);
          
                //TODO 
          
             }
          
             //-------------------------------------------------------------------------------------||
             // Internal Helper Methods ------------------------------------------------------------||
             //-------------------------------------------------------------------------------------||
          
             /**
              * Ensures deployables are specified
              */
             private static void assertDeployables(final WebArchive... deployables) throws IllegalArgumentException
             {
                if (deployables == null)
                {
                   throw new IllegalArgumentException("deployables must be specified");
                }
             }
          }
          
          
          
          • 2. Re: Integration with OpenEJB
            dblevins

            The file approach seems easiest.  Here's a test case that shows it:

             

              http://svn.apache.org/repos/asf/openejb/trunk/openejb3/container/openejb-core/src/test/java/org/apache/openejb/assembler/classic/RedeployTest.java

             

            We also have this other functionality and I don't know if this helps or not.  But you can basically build the app via the jaxb tree for the EJbJar and pass that to us:

             

              http://gist.github.com/283370

             

            There's a third version which basically takes the EjbJar and wraps it as a EjbModule and then you can specify the file path and classloader you'd like to use.  There's a AppModule version as well that allows you to put EjbModules and other types in it and deploy that as an ear.

             

            Not sure if any of that helps.  Maybe some sort of translation layer is possible?  At a minimum seems the java.io.File approach would work fine.

             

            -David

            • 3. Re: Integration with OpenEJB
              alrubinger

              Pointers to similar testcases are exactly the type of thing I was looking for.

               

              When I get some cycles next week I'll pull down source and see what I can hack up together.

               

              S,

              ALR

              • 4. Re: Integration with OpenEJB
                alrubinger

                David:

                 

                Attached is a lil patch that gets the job done.  Its test looks like:

                 

                      // Create archive to hold our test EJB
                      final String name = "echo.jar";
                      final JavaArchive archive = Archives.create(name, JavaArchive.class).addClasses(EchoBean.class,
                            EchoLocalBusiness.class);
                      logger.info("Created archive: " + archive.toString(true));
                
                      // These two objects pretty much encompass all the EJB Container
                      final ConfigurationFactory config = new ConfigurationFactory();
                      final Assembler assembler = new Assembler();
                      assembler.createTransactionManager(config.configureService(TransactionServiceInfo.class));
                      assembler.createSecurityService(config.configureService(SecurityServiceInfo.class));
                
                      // Deploy as an archive
                      final AppInfo appInfo = config.configureApplication(archive);
                      assembler.createApplication(appInfo);
                
                      // Get a JNDI Context
                      final Properties properties = new Properties();
                      properties.put(Context.INITIAL_CONTEXT_FACTORY, LocalInitialContextFactory.class.getName());
                      final Context ctx = new InitialContext(properties);
                
                      // Lookup
                      final EchoLocalBusiness bean = (EchoLocalBusiness) ctx.lookup(EchoBean.class.getSimpleName() + "Local");
                
                      // Invoke and test
                      final String request = "Word up";
                      final String response = bean.echo(request);
                      logger.info("Sent: \"" + request + "\"; got: \"" + response + "\"");
                      TestCase.assertEquals("Response from EJB invocation not expected", request, response);
                      TestCase.assertTrue("Response from local EJB invocation is equal by value but not by reference",
                            request == response);
                
                      // Undeploy the archive
                      assembler.destroyApplication(appInfo.jarPath);
                

                 

                Gives output:

                INFO - Created archive: echo.jar:
                /org/apache/openejb/assembler/classic/ejb/EchoBean.class
                /org/apache/openejb/assembler/classic/ejb/EchoLocalBusiness.class
                INFO - Configuring Service(id=Default Transaction Manager, type=TransactionManager, provider-id=Default Transaction Manager)
                INFO - Configuring Service(id=Default Security Service, type=SecurityService, provider-id=Default Security Service)
                INFO - Configuring enterprise application: /tmp/1aa8ca17-f7ee-4f90-a306-3105580cd19e/echo.jar
                INFO - Configuring Service(id=Default Stateless Container, type=Container, provider-id=Default Stateless Container)
                INFO - Auto-creating a container for bean EchoBean: Container(type=STATELESS, id=Default Stateless Container)
                INFO - Enterprise application "/tmp/1aa8ca17-f7ee-4f90-a306-3105580cd19e/echo.jar" loaded.
                INFO - Assembling app: /tmp/1aa8ca17-f7ee-4f90-a306-3105580cd19e/echo.jar
                INFO - Jndi(name=EchoBeanLocal) --> Ejb(deployment-id=EchoBean)
                INFO - Created Ejb(deployment-id=EchoBean, ejb-name=EchoBean, container=Default Stateless Container)
                INFO - Deployed Application(path=/tmp/1aa8ca17-f7ee-4f90-a306-3105580cd19e/echo.jar)
                INFO - Sent: "Word up"; got: "Word up"
                INFO - Undeploying app: /tmp/1aa8ca17-f7ee-4f90-a306-3105580cd19e/echo.jar
                

                 

                The underlying mechanism is using File serialization for the time being.  If we dig a little deeper and provide:

                 

                public AppModule load(String name, InputStream in) throws OpenEJBException;

                 

                ...within DeploymentLoader we can avoid the disk I/O.

                 

                S,

                ALR

                • 5. Re: Integration with OpenEJB
                  dblevins

                  This is great.  We should give this thing it's own module, maybe under container/, and move the ConfigurationFactory changes into a new subclass called ShrinkWrapConfigurationFactory.  Then we can really go nuts with changes.

                   

                  Can you reattach your patch here? (need the "Grant license to ASF for inclusion in ASF works" option checked before i can check it in)

                   

                    https://issues.apache.org/jira/browse/OPENEJB-1228

                   

                  -David

                  • 6. Re: Integration with OpenEJB
                    alrubinger

                    Nice; attached.

                     

                    Had you wanted us to do the moving, etc?

                     

                    S,

                    ALR

                    • 7. Re: Integration with OpenEJB
                      dblevins
                      If you want to do the subclass part, I can do the rest.
                      • 8. Re: Integration with OpenEJB
                        alrubinger
                        Deal; I'll ping back here when done.
                        • 9. Re: Integration with OpenEJB
                          alrubinger

                          Done and attached a new patch to the issue.

                           

                          S,

                          ALR