Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » deployment » [javadoc | source]
    1   /*
    2   * JBoss, Home of Professional Open Source
    3   * Copyright 2005, JBoss Inc., and individual contributors as indicated
    4   * by the @authors tag. See the copyright.txt in the distribution for a
    5   * full listing of individual contributors.
    6   *
    7   * This is free software; you can redistribute it and/or modify it
    8   * under the terms of the GNU Lesser General Public License as
    9   * published by the Free Software Foundation; either version 2.1 of
   10   * the License, or (at your option) any later version.
   11   *
   12   * This software is distributed in the hope that it will be useful,
   13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   15   * Lesser General Public License for more details.
   16   *
   17   * You should have received a copy of the GNU Lesser General Public
   18   * License along with this software; if not, write to the Free
   19   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
   20   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
   21   */
   22   package org.jboss.deployment;
   23   
   24   import java.io.File;
   25   import java.io.FileOutputStream;
   26   import java.io.IOException;
   27   import java.io.InputStream;
   28   import java.io.OutputStream;
   29   import java.net.MalformedURLException;
   30   import java.net.URL;
   31   import java.util.Enumeration;
   32   import java.util.jar.JarEntry;
   33   import java.util.jar.JarFile;
   34   
   35   import javax.management.Notification;
   36   
   37   import org.jboss.mx.util.MBeanProxyExt;
   38   import org.jboss.system.ServiceMBeanSupport;
   39   import org.jboss.system.server.ServerConfig;
   40   import org.jboss.system.server.ServerConfigLocator;
   41   import org.jboss.system.server.ServerConfigUtil;
   42   import org.jboss.util.file.JarUtils;
   43   import org.jboss.util.stream.Streams;
   44   
   45   /**
   46    * An abstract {@link SubDeployer}.
   47    *
   48    * Provides registration with {@link MainDeployer} as well as
   49    * implementations of init, create, start, stop and destroy that
   50    * generate JMX notifications on completion of the method.
   51    *
   52    * @version <tt>$Revision: 57108 $</tt>
   53    * @author  <a href="mailto:jason@planet57.com">Jason Dillon</a>
   54    * @author  <a href="mailto:scott.stark@jboss.org">Scott Stark</a>
   55    * @author  <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
   56    */
   57   public abstract class SubDeployerSupport extends ServiceMBeanSupport
   58      implements SubDeployerExt, SubDeployerExtMBean
   59   {
   60      /**
   61       * Holds the native library <em>suffix</em> for this system.
   62       * 
   63       * Determined by examining the result of System.mapLibraryName(specialToken).
   64       * The special token defaults to "XxX", but can be changed by setting the
   65       * system property: <tt>org.jboss.deployment.SubDeployerSupport.nativeLibToken</tt>.
   66       */
   67      protected static final String nativeSuffix;
   68   
   69      /**
   70       * Holds the native library <em>prefix</em> for this system.
   71       *
   72       * @see #nativeSuffix
   73       */
   74      protected static final String nativePrefix;
   75   
   76      /** A proxy to the MainDeployer. */
   77      protected MainDeployerMBean mainDeployer;
   78   
   79      /** The temporary directory into which deployments are unpacked */
   80      protected File tempDeployDir;
   81   
   82      /** The list of enhancedSuffixes for this subdeployer */
   83      protected String[] enhancedSuffixes;
   84      
   85      /** The suffixes of interest to this subdeployer */
   86      protected String[] suffixes;
   87      
   88      /** The relative order of this subdeployer - not really used */
   89      protected int relativeOrder = -1;
   90      
   91      /** The temporary directory where native libs are unpacked. */
   92      private File tempNativeDir;
   93   
   94      /** Whether to load native libraries */
   95      private boolean loadNative = false;
   96      
   97      /**
   98       * The <code>createService</code> method is one of the ServiceMBean lifecyle operations.
   99       * (no jmx tag needed from superinterface)
  100       * 
  101       * @exception Exception if an error occurs
  102       */
  103      protected void createService() throws Exception
  104      {
  105         // get the temporary directories to use
  106         ServerConfig config = ServerConfigLocator.locate();
  107         tempNativeDir = config.getServerNativeDir();
  108         tempDeployDir = config.getServerTempDeployDir();
  109         loadNative = ServerConfigUtil.isLoadNative();
  110   
  111         // Setup the proxy to mainDeployer
  112         mainDeployer = (MainDeployerMBean)
  113            MBeanProxyExt.create(MainDeployerMBean.class,
  114                              MainDeployerMBean.OBJECT_NAME,
  115                              server);
  116      }
  117   
  118      /**
  119       * Performs SubDeployer registration.
  120       */
  121      protected void startService() throws Exception
  122      {
  123         // Register with the main deployer
  124         mainDeployer.addDeployer(this);
  125      }
  126   
  127      /**
  128       * Performs SubDeployer deregistration.
  129       */
  130      protected void stopService() throws Exception
  131      {
  132         // Unregister with the main deployer
  133         mainDeployer.removeDeployer(this);
  134      }
  135   
  136      /**
  137       * Clean up.
  138       */
  139      protected void destroyService() throws Exception
  140      {
  141         // Help the GC
  142         mainDeployer = null;
  143         tempNativeDir = null;
  144      }
  145   
  146      /**
  147       * Set an array of suffixes of interest to this subdeployer.
  148       * No need to register twice suffixes that may refer to
  149       * unpacked deployments (e.g. .sar, .sar/).
  150       * 
  151       * @param suffixes array of suffix strings
  152       */
  153      protected void setSuffixes(String[] suffixes)
  154      {
  155         this.suffixes = suffixes;
  156      }
  157      
  158      /**
  159       * Set the relative order of the specified suffixes
  160       * all to the same value.
  161       * 
  162       * @param relativeOrder the relative order of the specified suffixes
  163       */
  164      protected void setRelativeOrder(int relativeOrder)
  165      {
  166         this.relativeOrder = relativeOrder;
  167      }
  168      
  169      /**
  170       * Set the enhanced suffixes list for this deployer,
  171       * causing also the supported suffixes list to be updated.
  172       * 
  173       * Each enhanced suffix entries has the form:
  174       * 
  175       *    [order:]suffix
  176       * 
  177       * No need to register twice suffixes that may refer to
  178       * unpacked deployments (e.g. .sar, .sar/).
  179       * 
  180       * @param enhancedSuffixes
  181       */
  182      public void setEnhancedSuffixes(String[] enhancedSuffixes)
  183      {
  184         if (enhancedSuffixes != null)
  185         {
  186            int len = enhancedSuffixes.length;
  187            suffixes = new String[len];
  188            
  189            for (int i = 0; i < len; i++)
  190            {
  191               // parse each enhancedSuffix
  192               SuffixOrderHelper.EnhancedSuffix e =
  193                  new SuffixOrderHelper.EnhancedSuffix(enhancedSuffixes[i]);
  194               
  195               suffixes[i] = e.suffix;
  196            }
  197         }
  198         this.enhancedSuffixes = enhancedSuffixes;
  199      }
  200      
  201      /**
  202       * Get an array of enhancedSuffixes
  203       * 
  204       * @return array of enhanced suffix strings
  205       */
  206      public String[] getEnhancedSuffixes()
  207      {
  208         return enhancedSuffixes;
  209      }
  210      
  211      /**
  212       * Get an array of suffixes of interest to this subdeployer
  213       * 
  214       * @return array of suffix strings
  215       */
  216      public String[] getSuffixes()
  217      {
  218         return suffixes;
  219      }
  220      
  221      /**
  222       * Get the relative order of the specified suffixes
  223       * 
  224       * @return the relative order of the specified suffixes
  225       */
  226      public int getRelativeOrder()
  227      {
  228         return relativeOrder;
  229      }
  230   
  231      /**
  232       * A default implementation that uses the suffixes registered
  233       * through either setSuffixes() or setEnhancedSuffixes(), to
  234       * decide if a module is deployable by this deployer.
  235       * 
  236       * If (according to DeploymentInfo) the deployment refers to
  237       * a directory, but not an xml or script deployment, then
  238       * the deployment suffix will be checked also against the
  239       * registered suffixes + "/".
  240       *
  241       * @param sdi the DeploymentInfo to check
  242       * @return whether the deployer can handle the deployment
  243       */
  244      public boolean accepts(DeploymentInfo sdi)
  245      {
  246         String[] acceptedSuffixes = getSuffixes();
  247         if (acceptedSuffixes == null)
  248         {
  249            return false;
  250         }
  251         else
  252         {
  253            String urlPath = sdi.url.getPath();
  254            String shortName = sdi.shortName;
  255            boolean checkDir = sdi.isDirectory && !(sdi.isXML || sdi.isScript);
  256            
  257            for (int i = 0; i < acceptedSuffixes.length; i++)
  258            {
  259               // First check the urlPath the might end in "/"
  260               // then check the shortName where "/" is removed
  261               if (urlPath.endsWith(acceptedSuffixes[i]) ||
  262                     (checkDir && shortName.endsWith(acceptedSuffixes[i])))
  263               {
  264                  return true;
  265               }
  266            }
  267            return false;
  268         }
  269      }
  270   
  271      /**
  272       * Sub-classes should override this method to provide
  273       * custom 'init' logic.
  274       *
  275       * <p>This method calls the processNestedDeployments(di) method and then
  276       * issues a JMX notification of type SubDeployer.INIT_NOTIFICATION.
  277       * This behaviour can overridden by concrete sub-classes.  If further
  278       * initialization needs to be done, and you wish to preserve the
  279       * functionality, be sure to call super.init(di) at the end of your
  280       * implementation.
  281       */
  282      public void init(DeploymentInfo di) throws DeploymentException
  283      {
  284         processNestedDeployments(di);
  285         
  286         emitNotification(SubDeployer.INIT_NOTIFICATION, di);
  287      }
  288   
  289      /**
  290       * Sub-classes should override this method to provide
  291       * custom 'create' logic.
  292       *
  293       * This method issues a JMX notification of type SubDeployer.CREATE_NOTIFICATION.
  294       */
  295      public void create(DeploymentInfo di) throws DeploymentException
  296      {
  297         emitNotification(SubDeployer.CREATE_NOTIFICATION, di);
  298      }
  299   
  300      /**
  301       * Sub-classes should override this method to provide
  302       * custom 'start' logic.
  303       *
  304       * This method issues a JMX notification of type SubDeployer.START_NOTIFICATION.
  305       */
  306      public void start(DeploymentInfo di) throws DeploymentException
  307      {
  308         emitNotification(SubDeployer.START_NOTIFICATION, di);
  309      }
  310   
  311      /**
  312       * Sub-classes should override this method to provide
  313       * custom 'stop' logic.
  314       *
  315       * This method issues a JMX notification of type SubDeployer.START_NOTIFICATION.
  316       */
  317      public void stop(DeploymentInfo di) throws DeploymentException
  318      {
  319         emitNotification(SubDeployer.STOP_NOTIFICATION, di);
  320      }
  321   
  322      /**
  323       * Sub-classes should override this method to provide
  324       * custom 'destroy' logic.
  325       *
  326       * This method issues a JMX notification of type SubDeployer.DESTROY_NOTIFICATION.
  327       */
  328      public void destroy(DeploymentInfo di) throws DeploymentException
  329      {
  330         emitNotification(SubDeployer.DESTROY_NOTIFICATION, di);
  331      }
  332   
  333      /**
  334       * Simple helper to emit a subdeployer notification containing DeploymentInfo
  335       */
  336      protected void emitNotification(String type, DeploymentInfo di)
  337      {
  338         Notification notification = new Notification(type, this, getNextNotificationSequenceNumber());
  339         notification.setUserData(di);
  340         sendNotification(notification);      
  341      }
  342      
  343      /**
  344       * The <code>processNestedDeployments</code> method searches for any nested and
  345       * deployable elements.  Only Directories and Zipped archives are processed,
  346       * and those are delegated to the addDeployableFiles and addDeployableJar
  347       * methods respectively.  This method can be overridden for alternate
  348       * behaviour.
  349       */
  350      protected void processNestedDeployments(DeploymentInfo di) throws DeploymentException
  351      {
  352         log.debug("looking for nested deployments in : " + di.url);
  353         if (di.isXML)
  354         {
  355            // no nested archives in an xml file
  356            return;
  357         }
  358   
  359         if (di.isDirectory)
  360         {
  361            File f = new File(di.url.getFile());
  362            if (!f.isDirectory())
  363            {
  364               // something is screwy
  365               throw new DeploymentException
  366                  ("Deploy file incorrectly reported as a directory: " + di.url);
  367            }
  368   
  369            addDeployableFiles(di, f);
  370         }
  371         else
  372         {
  373            try
  374            {
  375               // Obtain a jar url for the nested jar
  376               URL nestedURL = JarUtils.extractNestedJar(di.localUrl, this.tempDeployDir);
  377               JarFile jarFile = new JarFile(nestedURL.getFile());
  378               addDeployableJar(di, jarFile);
  379            }
  380            catch (Exception e)
  381            {
  382               log.warn("Failed to add deployable jar: " + di.localUrl, e);
  383   
  384               //
  385               // jason: should probably throw new DeploymentException
  386               //        ("Failed to add deployable jar: " + jarURLString, e);
  387               //        rather than make assumptions to what type of deployable
  388               //        file this was that failed...
  389               //
  390   
  391               return;
  392            }
  393         }
  394      }
  395   
  396      /**
  397       * This method returns true if the name is a recognized archive file.
  398       * 
  399       * It will query the MainDeployer that keeps a dynamically updated
  400       * list of known archive extensions. 
  401       *
  402       * @param name The "short-name" of the URL.  It will have any trailing '/'
  403       *        characters removed, and any directory structure has been removed.
  404       * @param url The full url.
  405       *
  406       * @return true iff the name ends in a known archive extension: .jar, .sar,
  407       *         .ear, .rar, .zip, .wsr, .war, or if the name matches the native
  408       *         library conventions.
  409       */
  410      protected boolean isDeployable(String name, URL url)
  411      {
  412         // any file under META-INF is not deployable; this method is called
  413         // also for zipped content, e.g. dir1/dir2.sar/META-INF/bla.xml 
  414         if (url.getPath().indexOf("META-INF") != -1)
  415         {
  416            return false;
  417         }
  418         String[] acceptedSuffixes = mainDeployer.getSuffixOrder();
  419         for (int i = 0; i < acceptedSuffixes.length; i++)
  420         {
  421            if (name.endsWith(acceptedSuffixes[i]))
  422            {
  423               return true;
  424            }
  425         }
  426         // this is probably obsolete
  427         return (name.endsWith(nativeSuffix) && name.startsWith(nativePrefix));
  428      }
  429   
  430      /**
  431       * This method recursively searches the directory structure for any files
  432       * that are deployable (@see isDeployable).  If a directory is found to
  433       * be deployable, then its subfiles and subdirectories are not searched.
  434       *
  435       * @param di the DeploymentInfo
  436       * @param dir The root directory to start searching.
  437       */
  438      protected void addDeployableFiles(DeploymentInfo di, File dir)
  439         throws DeploymentException
  440      {
  441         File[] files = dir.listFiles();
  442         for (int i = 0; i < files.length; i++)
  443         {
  444            File file = files[i];
  445            String name = file.getName();
  446            try
  447            {
  448               URL url = file.toURL();
  449               if (isDeployable(name, url))
  450               {
  451                  deployUrl(di, url, name);
  452                  // we don't want deployable units processed any further
  453                  continue;
  454               }
  455            }
  456            catch (MalformedURLException e)
  457            {
  458               log.warn("File name invalid; ignoring: " + file, e);
  459            }
  460            if (file.isDirectory())
  461            {
  462               addDeployableFiles(di, file);
  463            }
  464         }
  465      }
  466   
  467      /**
  468       * This method searches the entire jar file for any deployable files
  469       * (@see isDeployable).
  470       *
  471       * @param di the DeploymentInfo
  472       * @param jarFile the jar file to process.
  473       */
  474      protected void addDeployableJar(DeploymentInfo di, JarFile jarFile)
  475         throws DeploymentException
  476      {
  477         String urlPrefix = "jar:"+di.localUrl.toString()+"!/";
  478         for (Enumeration e = jarFile.entries(); e.hasMoreElements();)
  479         {
  480            JarEntry entry = (JarEntry)e.nextElement();
  481            String name = entry.getName();
  482            try
  483            {
  484               URL url = new URL(urlPrefix+name);
  485               if (isDeployable(name, url))
  486               {
  487                  // Obtain a jar url for the nested jar
  488                  URL nestedURL = JarUtils.extractNestedJar(url, this.tempDeployDir);
  489                  deployUrl(di, nestedURL, name);
  490               }
  491            }
  492            catch (MalformedURLException mue)
  493            {
  494               //
  495               // jason: why are we eating this exception?
  496               //
  497               log.warn("Jar entry invalid; ignoring: " + name, mue);
  498            }
  499            catch (IOException ex)
  500            {
  501               log.warn("Failed to extract nested jar; ignoring: " + name, ex);
  502            }
  503         }
  504      }
  505   
  506      protected void deployUrl(DeploymentInfo di, URL url, String name)
  507         throws DeploymentException
  508      {
  509         log.debug("nested deployment: " + url);
  510         try
  511         {
  512            //
  513            // jason: need better handling for os/arch specific libraries
  514            //        should be able to have multipule native libs in an archive
  515            //        one for each supported platform (os/arch), we only want to
  516            //        load the one for the current platform.
  517            //
  518            //        This probably means explitly listing the libraries in a
  519            //        deployment descriptor, which could probably also be used
  520            //        to explicitly map the files, as it might be possible to
  521            //        share a native lib between more than one version, no need
  522            //        to duplicate the file, metadata can be used to tell us
  523            //        what needs to be done.
  524            //
  525            //        Also need this mapping to get around the different values
  526            //        which are used by vm vendors for os.arch and such...
  527            //
  528   
  529            if (name.endsWith(nativeSuffix) && name.startsWith(nativePrefix))
  530            {
  531               File destFile = new File(tempNativeDir, name);
  532               log.info("Loading native library: " + destFile.toString());
  533   
  534               File parent = destFile.getParentFile();
  535               if (!parent.exists()) {
  536                  parent.mkdirs();
  537               }
  538   
  539               InputStream in = url.openStream();
  540               OutputStream out = new FileOutputStream(destFile);
  541               Streams.copyb(in, out);
  542   
  543               out.flush();
  544               out.close();
  545               in.close();
  546   
  547               if (loadNative)
  548                  System.load(destFile.toString());
  549            }
  550            else
  551            {
  552               new DeploymentInfo(url, di, getServer());
  553            }
  554         }
  555         catch (Exception ex)
  556         {
  557            throw new DeploymentException
  558               ("Could not deploy sub deployment "+name+" of deployment "+di.url, ex);
  559         }
  560      }
  561   
  562      /////////////////////////////////////////////////////////////////////////
  563      //                     Class Property Configuration                    //
  564      /////////////////////////////////////////////////////////////////////////
  565   
  566      /**
  567       * Static configuration properties for this class.  Allows easy access
  568       * to change defaults with system properties.
  569       */
  570      protected static class ClassConfiguration
  571         extends org.jboss.util.property.PropertyContainer
  572      {
  573         private String nativeLibToken = "XxX";
  574   
  575         public ClassConfiguration()
  576         {
  577            // properties will be settable under our enclosing classes group
  578            super(SubDeployerSupport.class);
  579   
  580            // bind the properties & the access methods
  581            bindMethod("nativeLibToken");
  582         }
  583   
  584         public void setNativeLibToken(final String token)
  585         {
  586            this.nativeLibToken = token;
  587         }
  588   
  589         public String getNativeLibToken()
  590         {
  591            return nativeLibToken;
  592         }
  593      }
  594   
  595      /** The singleton class configuration object for this class. */
  596      protected static final ClassConfiguration CONFIGURATION = new ClassConfiguration();
  597   
  598      //
  599      // jason: the following needs to be done after setting up the
  600      //        class config reference, so it is moved it down here.
  601      //
  602   
  603      /**
  604       * Determine the native library suffix and prefix.
  605       */
  606      static
  607      {
  608         // get the token to use from config, incase the default needs
  609         // to be changed to resolve problem with a specific platform
  610         String token = CONFIGURATION.getNativeLibToken();
  611   
  612         // then determine what the prefix and suffixes are for this platform
  613         String nativex = System.mapLibraryName(token);
  614         int xPos = nativex.indexOf(token);
  615         nativePrefix = nativex.substring(0, xPos);
  616         nativeSuffix = nativex.substring(xPos + 3);
  617      }
  618   }

Save This Page
Home » jboss-5.0.0.CR1-src » org » jboss » deployment » [javadoc | source]