Package org.jboss.ejb3.embedded.impl.base.scanner

Source Code of org.jboss.ejb3.embedded.impl.base.scanner.ClassPathEjbJarScanner

/*
* 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.ejb3.embedded.impl.base.scanner;

import org.jboss.ejb3.embedded.impl.base.scanner.filter.BundleSymbolicNameExclusionFilter;
import org.jboss.ejb3.embedded.spi.scanner.filter.ExclusionFilter;
import org.jboss.logging.Logger;
import org.jboss.vfs.TempFileProvider;
import org.jboss.vfs.VFS;
import org.jboss.vfs.VirtualFile;

import javax.ejb.MessageDriven;
import javax.ejb.Singleton;
import javax.ejb.Stateful;
import javax.ejb.Stateless;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

/**
* Implements JVM ClassPath scanning for EJB JARs as defined
* by EJB 3.1 Final Draft 22.2.1.  This is a static utility
* class which is not to be instantiated.
*
* @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a>
* @version $Revision: $
*/
public class ClassPathEjbJarScanner
{

   //TODO
   /*
    * This is an intentionally naive implementation which essentially
    * amounts to junkware.  It gets us to the next phases of development,
    * but isn't intended to be the final solution.  For starters it's a static utility.
    *
    * Open issues:
    *
    * 1) Don't load all Classes to look for annotations.  Vie for ASM or Javassist (or
    * other bytecode analyzer).  Or pass through an isolated VDF Deployer chain and let the
    * deployers figure out what the eligible modules are
    * 2) Define a configurable ScheduledExecutorService to back the TempFileProvider
    * used to mount ZIP VFS roots.  If we go the deployer chain route as noted by 1) this
    * won't be necessary
    */

   //-------------------------------------------------------------------------------------||
   // Class Members ----------------------------------------------------------------------||
   //-------------------------------------------------------------------------------------||

   /**
    * Logger
    */
   private static final Logger log = Logger.getLogger(ClassPathEjbJarScanner.class);

   /**
    * System property key denoting the JVM ClassPath
    */
   private static final String SYS_PROP_KEY_CLASS_PATH = "java.class.path";

   /**
    * Dummy String array used in converting a {@link Collection} of {@link String} to a typed array
    */
   private static final String[] DUMMY = new String[]
   {};

   /**
    * Path of the EJB Descriptor, relative to the root of a deployment
    */
   private static final String PATH_EJB_JAR_XML = "META-INF/ejb-jar.xml";

   /**
    * .class Extension
    */
   private static final String EXTENSION_CLASS = ".class";

   /**
    * .jar Extension
    */
   private static final String EXTENSION_JAR = ".jar";

   /**
    * EJB Component-defining annotations
    */
   @SuppressWarnings("unchecked")
   private static final Class<? extends Annotation>[] EJB_COMPONENT_ANNOTATIONS = (Class<? extends Annotation>[]) new Class<?>[]
   {Stateless.class, Stateful.class, Singleton.class, MessageDriven.class};

   /**
    * {@link ScheduledExecutorService} to mount files to be scanned
    */
   @Deprecated
   //TODO Get some reusable, configurable real SES (as managed by the container) else we'll block on JVM shutdown;
   // this one is never shut down cleanly
   private static final ScheduledExecutorService ses = Executors.newScheduledThreadPool(Runtime.getRuntime()
         .availableProcessors());

   /**
    * Configured exclusion filters
    * TODO Shouldn't be hardcoded, but available via user configuration
    */
   private static final List<ExclusionFilter> exclusionFilters;
   static
   {
      exclusionFilters = new ArrayList<ExclusionFilter>();
      exclusionFilters.add(new BundleSymbolicNameExclusionFilter("org.eclipse", "org.junit"));
   }

   //-------------------------------------------------------------------------------------||
   // Constructor ------------------------------------------------------------------------||
   //-------------------------------------------------------------------------------------||

   /**
    * Internal Constructor, no instances permitted
    */
   private ClassPathEjbJarScanner()
   {
      throw new UnsupportedOperationException("No instances permitted");
   }

   //-------------------------------------------------------------------------------------||
   // Functional Methods -----------------------------------------------------------------||
   //-------------------------------------------------------------------------------------||

   /**
    * Obtains all EJB JAR entries from the ClassPath
    */
   public static String[] getEjbJars()
   {

      // Initialize
      final Collection<String> returnValue = new ArrayList<String>();

      // Get the full ClassPath
      final String classPath = SecurityActions.getSystemProperty(SYS_PROP_KEY_CLASS_PATH);
      if (log.isTraceEnabled())
      {
         log.tracef("Class Path: %s", classPath);
      }

      // Split by the path separator character
      final String[] classPathEntries = classPath.split(File.pathSeparator);

      // For each CP entry
      for (final String classPathEntry : classPathEntries)
      {
         // If this is an EJB JAR
         if (isEjbJar(classPathEntry))
         {
            // Add to be returned
            returnValue.add(classPathEntry);
         }
      }

      // Return
      if (log.isDebugEnabled())
      {
         log.debug("EJB Modules discovered on ClassPath: " + returnValue);
      }
      return returnValue.toArray(DUMMY);
   }

   //-------------------------------------------------------------------------------------||
   // Internal Helper Methods ------------------------------------------------------------||
   //-------------------------------------------------------------------------------------||

   /**
    * Determines whether this entry from the ClassPath is an EJB JAR
    */
   private static boolean isEjbJar(final String candidate)
   {

      /*
       * EJB 3.1 22.2.1:
       *
       * A classpath entry is considered a matching entry if it meets one of the following criteria:
       * - It is an ejb-jar according to the standard module-type identification rules defined by the Java
       *   EE platform specification
       * - It is a directory containing a META-INF/ejb-jar.xml file or at least one .class with an enterprise
       *   bean component-defining annotation
       */

      // Represent as VFS so we get a nice unified API
      final VirtualFile file = VFS.getChild(candidate);
      Closeable handle = null;
      TempFileProvider provider = null;

      try
      {

         // If the file exists
         if (file.exists())
         {

            // Mount Exploded dir
            if (file.isDirectory())
            {
               handle = VFS.mountReal(file.getPhysicalFile(), file);
            }
            // Mount EJB JAR
            else if (file.getName().endsWith(EXTENSION_JAR))
            {
               if (provider == null)
               {
                  provider = TempFileProvider.create("jbossejbmodulescanner", ses);
               }
               handle = VFS.mountZip(file.getPhysicalFile(), file, provider);
            }
            // No conditions met
            else
            {
               // So it's obvious if we've got something we didn't properly mount
               log.warn("Encountered unknown file type, skipping: " + file);
               return false;
            }

         }
         // Not a real file
         else
         {
            log.warn("File on ClassPath could not be found: " + file);
            return false;
         }

         /*
          * See if we've been configured to skip this file
          */
         for (final ExclusionFilter exclusionFilter : exclusionFilters)
         {
            // If we should exclude this
            if (exclusionFilter.exclude(file))
            {
               // Exclude from further processing
               if (log.isTraceEnabled())
               {
                  log.tracef("%s matched %s for exclusion; skipping", exclusionFilter, file);
               }
               return false;
            }
         }

         /*
          * Directories and real JARs are handled the same way in VFS, so just do
          * one check and skip logic to test isDirectory or not
          */

         // Look for META-INF/ejb-jar.xml
         final VirtualFile ejbJarXml = file.getChild(PATH_EJB_JAR_XML);
         if (ejbJarXml.exists())
         {
            if (log.isTraceEnabled())
            {
               log.tracef("Found descriptor %s in %s", ejbJarXml.getPathNameRelativeTo(file), file);
            }
            return true;
         }

         // Look for at least one .class with an EJB annotation
         if (containsEjbComponentClass(file))
         {
            return true;
         }

         // Return
         return false;

      }
      catch (final IOException e)
      {
         throw new RuntimeException("Could not mount file from ClassPath for EJB JAR module scanning", e);
      }
      finally
      {
         try
         {
            handle.close();
         }
         catch (final IOException e)
         {
            // Ignore
            log.warn("Could not close handle to mounted " + file, e);
         }
      }

   }

   /**
    * Determines if there is at least one .class in the given file
    * with an EJB component-defining annotation (Stateless, Stateful,
    * Singleton, MessageDriven)
    * @param file
    * @return
    * @deprecated Use a real implementation scanner
    */
   @Deprecated
   private static boolean containsEjbComponentClass(final VirtualFile file)
   {
      return containsEjbComponentClass(file, file);
   }

   /**
    * Determines if there is at least one .class in the given file
    * with an EJB component-defining annotation (Stateless, Stateful,
    * Singleton, MessageDriven).
    * @param root The original root from which we started the search
    * @param file
    * @return
    * @deprecated Use a real implementation scanner
    */
   @Deprecated
   private static boolean containsEjbComponentClass(final VirtualFile root, final VirtualFile file)
   {

      // Precondition check
      assert file != null : "File must be specified";

      // For all children
      for (final VirtualFile child : file.getChildren())
      {
         if (child.isDirectory())
         {
            // Determine if there's one in the child
            final boolean foundInChild = containsEjbComponentClass(root, child);
            if (foundInChild)
            {
               return true;
            }
         }

         // Get the Class for all .class files
         final String childName = child.getPathNameRelativeTo(root);
         if (childName.endsWith(EXTENSION_CLASS))
         {
            final String className = childName.substring(0, childName.length() - EXTENSION_CLASS.length()).replace('/',
                  '.');

            // Here's the naughty part; loading the Class (which we really don't need to do at all, just inspect for annotations)
            Class<?> clazz = null;
            try
            {
               clazz = Class.forName(className, false, SecurityActions.getTccl());
            }
            catch (final ClassNotFoundException cnfe)
            {
               throw new RuntimeException("Found .class on ClassPath which could not be found by the TCCL", cnfe);
            }
            catch (final NoClassDefFoundError ncdfe)
            {
               // Ugly hack used to identify the stuff for which we need to configure an exclusion filter
               log.warnf(
                     "Dev Hack Alert: Ignoring class on ClassPath which can't be loaded due to %s while loading %s; "
                           + "configure an exclusion filter so %s is not processed", ncdfe.toString(), className, root);
            }
            catch(final SecurityException e)
            {
               log.warnf("Can't load class %s (%s).", className, e.toString());
            }

            // Determine if we have a class with an EJB component annotation
            if (clazz != null)
            {
               for (final Class<? extends Annotation> annotationClass : EJB_COMPONENT_ANNOTATIONS)
               {
                  if (clazz.isAnnotationPresent(annotationClass))
                  {
                     if (log.isTraceEnabled())
                     {
                        log.tracef("Found %s on %s in %s", annotationClass, clazz, root);
                     }
                     return true;
                  }
               }
            }

         }

      }

      // No conditions met, so false
      return false;
   }
}
TOP

Related Classes of org.jboss.ejb3.embedded.impl.base.scanner.ClassPathEjbJarScanner

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.