FileDocCategorySizeDatePackage
EJB3Deployer.javaAPI DocJBoss 4.2.117875Fri Jul 13 20:53:58 BST 2007org.jboss.ejb3

EJB3Deployer.java

/*
 * 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.ejb3;

import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedInputStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Properties;
import javax.management.ObjectName;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.LinkRef;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.StringRefAddr;

import org.jboss.deployment.DeploymentException;
import org.jboss.deployment.DeploymentInfo;
import org.jboss.deployment.SubDeployer;
import org.jboss.deployment.SubDeployerSupport;
import org.jboss.ejb3.naming.SimpleMultiplexer;
import org.jboss.logging.Logger;
import org.jboss.metadata.MetaData;
import org.jboss.metadata.XmlFileLoader;
import org.jboss.mx.loading.LoaderRepositoryFactory;
import org.jboss.mx.util.MBeanProxyExt;
import org.jboss.mx.util.ObjectNameConverter;
import org.jboss.naming.ENCFactory;
import org.jboss.system.ServiceControllerMBean;
import org.jboss.util.file.ArchiveBrowser;
import org.jboss.util.file.ClassFileFilter;
import org.w3c.dom.Element;

import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ClassFile;

/**
 * Deployer for Aspects
 *
 * @author <a href="mailto:bill@jboss.org">Bill Burke</a>
 * @version $Revision: 63169 $
 */
public class EJB3Deployer extends SubDeployerSupport
   implements SubDeployer, EJB3DeployerMBean
{
   private final static Logger log = Logger.getLogger(EJB3Deployer.class);
   
   private ServiceControllerMBean serviceController;

   /** A map of current deployments */
   private HashMap deployments = new HashMap();

   /** Hold a proxy reference to myself, used when registering to MainDeployer */
   private SubDeployer thisProxy;

   private Properties DefaultProperties;
   
   private boolean deployEjb3ExtensionOnly;

   private HashSet ignoredJarsSet;
   private HashMap<DeploymentInfo, String> jmxNames = new HashMap();

   /**
    * Default CTOR used to set default values to the Suffixes and RelativeOrder
    * attributes. Those are read at subdeployer registration time by the MainDeployer
    * to alter its SuffixOrder.
    */
   public EJB3Deployer()
   {
      setSuffixes(new String[]{".jar", ".ejb3", ".par"});
      setRelativeOrder(400); // before old EJB 2.1 deployer
   }

   public static boolean hasFile(DeploymentInfo di, String filePath)
   {
      String urlStr = di.url.getFile();
      try
      {
         URL dd = di.localCl.findResource(filePath);
         if (dd != null)
         {

            // If the DD url is not a subset of the urlStr then this is coming
            // from a jar referenced by the deployment jar manifest and the
            // this deployment jar it should not be treated as persistence
            if (di.localUrl != null)
            {
               urlStr = di.localUrl.toString();
            }

            String ddStr = dd.toString();
            if (ddStr.indexOf(urlStr) >= 0)
            {
               return true;
            }
         }
      }
      catch (Exception ignore)
      {
      }
      return false;
   }

   public static boolean hasPersistenceXml(DeploymentInfo di)
   {
      return hasFile(di, "META-INF/persistence.xml");
   }

   public static boolean has30EjbJarXml(DeploymentInfo di)
   {
      if (!hasFile(di, "META-INF/ejb-jar.xml")) return false;
      InputStream ddStream = di.localCl.getResourceAsStream("META-INF/ejb-jar.xml");

      return has30EjbJarXml(ddStream);
   }

   public static boolean has30EjbJarXml(InputStream ddStream)
   {
      try
      {
         // look for version="3.0" in the file
         byte[] stringToFind = "version=\"3.0\"".getBytes();
         InputStreamReader reader = new InputStreamReader(ddStream);
         try
         {
            int idx = 0;
            int len = stringToFind.length;
            while (reader.ready())
            {
               int read = reader.read();
               if (read == stringToFind[idx])
               {
                  idx++;
                  if (idx == len)
                  {
                     return true;
                  }
               }
               else
               {
                  idx = 0;
               }
            }

         }
         finally
         {
            try
            {
               reader.close();
               ddStream.close();
            }
            catch (IOException ignored)
            {
            }
         }
      }
      catch (Exception ignore)
      {
      }
      return false;
   }

   protected boolean hasOnlyJbossXml(DeploymentInfo di)
   {
      if (!hasFile(di, "META-INF/ejb-jar.xml")
         && hasFile(di, "META-INF/jboss.xml"))
      {
         return true;
      }
      return false;
   }

   public boolean hasEjbAnnotation(DeploymentInfo di)
   {
      Iterator it = ArchiveBrowser.getBrowser(di.localUrl, new ClassFileFilter());
      try
      {
         while (it.hasNext())
         {
            InputStream stream = (InputStream) it.next();
            DataInputStream dstream = new DataInputStream(new BufferedInputStream(stream));
            ClassFile cf = null;
            try
            {
               cf = new ClassFile(dstream);
               AnnotationsAttribute visible = (AnnotationsAttribute) cf.getAttribute(AnnotationsAttribute.visibleTag);
               if (visible != null)
               {
                  if (EJB3Util.isStateless(visible)) return true;
                  if (EJB3Util.isStatefulSession(visible)) return true;
                  if (EJB3Util.isMessageDriven(visible)) return true;
                  if (EJB3Util.isConsumer(visible)) return true;
                  if (EJB3Util.isService(visible)) return true;
               }
            }
            finally
            {
               dstream.close();
               stream.close();
            }
         }
      }
      catch (IOException e)
      {
         throw new RuntimeException(e);
      }
      return false;
   }

   /**
    * Returns true if this deployer can deploy the given DeploymentInfo.
    *
    * @return True if this deployer can deploy the given DeploymentInfo.
    * @jmx:managed-operation
    */
   public boolean accepts(DeploymentInfo di)
   {
      String urlStr = di.url.getFile();
      if (urlStr.endsWith(".ejb3") || urlStr.endsWith(".ejb3/") || urlStr.endsWith(".par") || urlStr.endsWith(".par/"))
      {
         return true;
      }
      
      // To be accepted the deployment's root name must end in jar
      if (!urlStr.endsWith(".jar") && !urlStr.endsWith(".jar/"))
      {
         return false;
      }

      if (ignoredJarsSet.contains(di.shortName))
      {
         return false;
      }
      
      if (has30EjbJarXml(di)) return true;
         
      if (!deployEjb3ExtensionOnly)
      {
         if (hasPersistenceXml(di)) return true;
         if (hasOnlyJbossXml(di)) return true;
         if (hasEjbAnnotation(di)) return true;
      }


      return false;
   }

   public Properties getDefaultProperties()
   {
      return DefaultProperties;
   }

   public void setJarsIgnoredForScanning(JarsIgnoredForScanningMBean mbean)
   {
      ignoredJarsSet = mbean.getIgnoredJarsSet();
   }
   
   public boolean getDeployEjb3ExtensionOnly()
   {
      return deployEjb3ExtensionOnly;
   }
   
   public void setDeployEjb3ExtensionOnly(boolean deployEjb3ExtensionOnly)
   {
      this.deployEjb3ExtensionOnly = deployEjb3ExtensionOnly;
   }

   /**
    * Overriden to set the hibernate.bytecode.provider from the
    * 
    * @throws Exception
    */
   protected void createService() throws Exception
   {
      URL propsUrl = this.getClass().getClassLoader().getResource("META-INF/persistence.properties");
      DefaultProperties = new Properties();
      DefaultProperties.load(propsUrl.openStream());
      log.debug("Default persistence.properties: " + DefaultProperties);
      /* Current hack to establish the hibernate bytecode provider from the
      externalized persistence.properties
      */
      String bcprovider = DefaultProperties.getProperty("hibernate.bytecode.provider", "javassist");
      System.setProperty("hibernate.bytecode.provider", bcprovider);
      super.createService();
   }

   /**
    * Get a reference to the ServiceController
    */
   protected void startService() throws Exception
   {
      serviceController = (ServiceControllerMBean)
              MBeanProxyExt.create(ServiceControllerMBean.class,
                                   ServiceControllerMBean.OBJECT_NAME, server);

      // make a proxy to myself, so that calls from the MainDeployer
      // can go through the MBeanServer, so interceptors can be added
      thisProxy = (SubDeployer)
              MBeanProxyExt.create(SubDeployer.class, super.getServiceName(), super.getServer());

      // register with the main deployer
      mainDeployer.addDeployer(thisProxy);

      // todo remove when we merge older model of ENC
      InitialContext iniCtx = new InitialContext();
      initializeJavaComp(iniCtx);
   }

   private static void multiplexJNDI(Context ctx) throws NamingException
   {
      log.info("Starting java:comp multiplexer");
      // rename to something SimpleMultiPlexer knows.
      // (doesn't work, because rename starts with a lookup and thus resolves
      // to a enc context bound to the current class loader).
      //ctx.rename("comp", "comp.original");
      {
         ctx.unbind("comp");
         // copied out of NamingBeanImpl
         RefAddr refAddr = new StringRefAddr("nns", "ENC");
         Reference envRef = new Reference("javax.namingMain.Context", refAddr, ENCFactory.class.getName(), null);
         ctx.bind("comp.original", envRef);
      }
      
      {
         RefAddr refAddr = new StringRefAddr("nns", "ENC-MULTIPLEXER");
         Reference ref = new Reference("javax.naming.Context", refAddr, SimpleMultiplexer.class.getName(), null);
         ctx.bind("comp", ref);
      }
   }
   
   public static void initializeJavaComp(InitialContext iniCtx)
           throws NamingException
   {
      RefAddr refAddr = new StringRefAddr("nns", "ENC-EJB3");
      Reference envRef = new Reference("javax.naming.Context", refAddr, ThreadLocalENCFactory.class.getName(), null);
      Context ctx = (Context) iniCtx.lookup("java:");
      ctx.rebind("comp.ejb3", envRef);
      
      multiplexJNDI(ctx);
   }

   /**
    * Implements the template method in superclass. This method stops all the
    * applications in this server.
    */
   protected void stopService() throws Exception
   {
      for (Iterator modules = deployments.values().iterator();
           modules.hasNext();)
      {
         DeploymentInfo di = (DeploymentInfo) modules.next();
         stop(di);
      }      // avoid concurrent modification exception
      for (Iterator modules = new ArrayList(deployments.values()).iterator();
           modules.hasNext();)
      {
         DeploymentInfo di = (DeploymentInfo) modules.next();
         destroy(di);
      }
      deployments.clear();

      // deregister with MainDeployer
      mainDeployer.removeDeployer(thisProxy);

      serviceController = null;
   }

   public void init(DeploymentInfo di) throws DeploymentException
   {     
      try
      {
         if( di.url.getProtocol().equalsIgnoreCase("file") )
         {
            File file = new File(di.url.getFile());

            if( !file.isDirectory() )
            {
               // If not directory we watch the package
               di.watch = di.url;
            }
            else
            {
               // If directory we watch the xml files
               di.watch = new URL(di.url, "META-INF/ejb-jar.xml");
            }
         }
         else
         {
            // We watch the top only, no directory support
            di.watch = di.url;
         }
         
         XmlFileLoader xfl = new XmlFileLoader();
         InputStream in = di.localCl.getResourceAsStream("META-INF/jboss.xml");
         if( in != null )
         {
            try
            {
               Element jboss = xfl.getDocument(in, "META-INF/jboss.xml").getDocumentElement();
               // Check for a ejb level class loading config
               Element loader = MetaData.getOptionalChild(jboss, "loader-repository");
               if( loader != null )
               {
                  LoaderRepositoryFactory.LoaderRepositoryConfig config =
                        LoaderRepositoryFactory.parseRepositoryConfig(loader);
                  di.setRepositoryInfo(config);
               }
               
               Element jmxNameElement = MetaData.getOptionalChild(jboss, "jmx-name");
               if (jmxNameElement != null)
               {
                  jmxNames.put(di, jmxNameElement.getChildNodes().item(0).getNodeValue());
               }
            }
            finally
            {
               in.close();
            }
         }
      }
      catch (Exception e)
      {
         if (e instanceof DeploymentException)
         {
            throw (DeploymentException) e;
         }
         throw new DeploymentException( "failed to initialize", e );
      }

      // invoke super-class initialization
      super.init(di);
   }

   public synchronized void create(DeploymentInfo di) throws DeploymentException
   {
      log.debug("create, " + di.shortName);

      try
      {
         // initialize the annotations loader
         URL loaderURL = (di.localUrl != null ? di.localUrl : di.url);
         di.annotationsCl = new URLClassLoader(new URL[]{loaderURL}, di.ucl);

         Ejb3Module ejbModule = new Ejb3Module(di);
         String name = jmxNames.get(di);
         if (name == null)
            name = Ejb3Module.BASE_EJB3_JMX_NAME + ",module=" + di.shortName;
         // Build an escaped JMX name including deployment shortname
         ObjectName ejbModuleName = ObjectNameConverter.convert(name);
         // Check that the name is not registered
         if (server.isRegistered(ejbModuleName) == true)
         {
            log.debug("The EJBModule name: " + ejbModuleName
                      + "is already registered, adding uid=" + System.identityHashCode(ejbModule));
            name = name + ",uid=" + System.identityHashCode(ejbModule);
            ejbModuleName = ObjectNameConverter.convert(name);
         }
         server.registerMBean(ejbModule, ejbModuleName);
         di.deployedObject = ejbModuleName;
         log.debug("Deploying: " + di.url);
         // Invoke the create life cycle method
         serviceController.create(di.deployedObject);
      }
      catch (Exception e)
      {
         throw new DeploymentException("Error during create of EjbModule: "
                                       + di.url, e);
      }
      super.create(di);
   }

   public synchronized void start(DeploymentInfo di)
           throws DeploymentException
   {
      try
      {
         // Start application
         log.debug("start application, deploymentInfo: " + di +
                   ", short name: " + di.shortName +
                   ", parent short name: " +
                   (di.parent == null ? "null" : di.parent.shortName));
         serviceController.start(di.deployedObject);
         log.info("Deployed: " + di.url);         // Register deployment. Use the application name in the hashtable
         // FIXME: this is obsolete!! (really?!)
         deployments.put(di.url, di);
      }
      catch (Exception e)
      {
         stop(di);
         destroy(di);
         throw new DeploymentException("Could not deploy " + di.url, e);
      }
      super.start(di);
   }

   public void stop(DeploymentInfo di)
           throws DeploymentException
   {
      log.debug("init, " + di.shortName);
      try
      {
         serviceController.stop(di.deployedObject);
      }
      catch (Exception e)
      {
         throw new DeploymentException("problem stopping ejb module: " +
                                       di.url, e);
      }
      
      super.stop(di);
   }

   public void destroy(DeploymentInfo di)
           throws DeploymentException
   {
      // FIXME: If the put() is obsolete above, this is obsolete, too
      deployments.remove(di.url);
      try
      {
         serviceController.destroy(di.deployedObject);
         serviceController.remove(di.deployedObject);
      }
      catch (Exception e)
      {
         throw new DeploymentException("problem destroying ejb module: " +
                                       di.url, e);
      }
      
      jmxNames.remove(di);
      
      super.destroy(di);
   }
}