All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ocpsoft.rewrite.annotation.scan.WebClassesFinder Maven / Gradle / Ivy

/*
 * Copyright 2010 Lincoln Baxter, III
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.ocpsoft.rewrite.annotation.scan;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.LinkedHashSet;
import java.util.Set;

import javax.servlet.ServletContext;

import org.ocpsoft.rewrite.annotation.api.ClassVisitor;
import org.ocpsoft.rewrite.annotation.spi.ClassFinder;

/**
 * Implementation of {@link ClassFinder} that searches for classes in the /WEB-INF/classes directory of a
 * web application. Please note that this class is stateful. It should be used only for one call to
 * {@link #findClasses(ClassVisitor)}.
 * 
 * @author Christian Kaltepoth
 */
public class WebClassesFinder extends AbstractClassFinder
{

   /**
    * The name of the classes directory
    */
   private final static String CLASSES_FOLDER = "/WEB-INF/classes/";

   /**
    * Manage a set of classes already processed
    */
   private final Set processedClasses = new LinkedHashSet();

   /**
    * Initialization
    */
   public WebClassesFinder(ServletContext servletContext, ClassLoader classLoader, PackageFilter packageFilter,
            ByteCodeFilter byteCodeFilter)
   {
      super(servletContext, classLoader, packageFilter, byteCodeFilter);
   }

   @Override
   public void findClasses(ClassVisitor visitor)
   {
      try
      {
         // get the absolute URL of the classes folder
         URL classesFolderUrl = servletContext.getResource(CLASSES_FOLDER);

         // abort if classes folder is missing
         if (classesFolderUrl == null)
         {
            log.warn("Cannot find classes folder: " + CLASSES_FOLDER);
            return;
         }

         // call recursive directory processing method
         processDirectory(classesFolderUrl, CLASSES_FOLDER, visitor);

      }
      catch (MalformedURLException e)
      {
         throw new IllegalStateException("Invalid URL: " + e.getMessage(), e);
      }
   }

   /**
    * Scan for classes in a single directory. This method will call itself recursively if it finds other directories and
    * call {@link #processClass(String, InputStream, ClassVisitor)} when it finds a file ending with ".class" and
    * that is accepted by the {@link PackageFilter}
    * 
    * @param absoluteUrl The absolute URL of the WEB-INF node to scan
    * @param relativePath The path of the node inside the WEB-INF
    * @param visitor The visitor class to call for classes found
    * @throws MalformedURLException for invalid URLs
    */
   protected void processDirectory(URL absoluteUrl, String relativePath, ClassVisitor visitor)
            throws MalformedURLException
   {

      // log directory name on trace level
      if (log.isTraceEnabled())
      {
         log.trace("Processing directory: " + relativePath);
      }

      // Convert url to string, this will result in a full path of a node in an exploded war archive
      // let it be "file://opt/server/application/abc/expoded/ear/war/WEB-INF/classes/com/"
      String urlAsString = absoluteUrl.toString();

      Set paths = servletContext.getResourcePaths(relativePath);
      if (paths == null || paths.isEmpty())
      {
         return;
      }

      // Process child nodes
      for (Object obj : paths)
      {
         // produces "/WEB-INF/classes/com/mycompany/"
         String childNodeName = obj.toString();

         // get last part of the node path (folder or class entry)
         // for example for childnode "/WEB-INF/classes/com/mycompany/" returns "mycompany/"
         String childNodeRelative = getChildNodeName(childNodeName);

         // get the folder of the node inside WEB-INF
         // for example for childnode "/WEB-INF/classes/com/mycompany/" returns "/WEB-INF/classes/com/"
         String webInfFolder = childNodeName.substring(0, childNodeName.length() - childNodeRelative.length());

         // Find relative base folder
         // produces "file://opt/server/application/abc/expoded/ear/war/"
         String urlBase = urlAsString.substring(0, urlAsString.length() - webInfFolder.length());

         // Create child node URL
         // produces "file://opt/server/application/abc/expoded/ear/war/WEB-INF/classes/com/mycompany/"
         URL childNodeUrl = new URL(urlBase + childNodeName);

         if (childNodeRelative.endsWith("/"))
         {
            // Recursive cal
            processDirectory(childNodeUrl, childNodeName, visitor);
         }
         if (childNodeRelative.endsWith(".class"))
         {
            handleClassEntry(childNodeName, visitor);
         }
      }
   }

   /**
    * Handles class entry in a WEB-INF.
    */
   private void handleClassEntry(String entryName, ClassVisitor visitor)
   {

      // build class name from relative name
      String className = getClassName(entryName.substring(CLASSES_FOLDER.length()));

      // check filter
      if (mustProcessClass(className) && !processedClasses.contains(className))
      {

         // mark this class as processed
         processedClasses.add(className);

         // the class file stream
         InputStream classFileStream = null;

         // close the stream in finally block
         try
         {

            /*
             * Try to open the .class file. if this isn't possible, we will scan it anyway.
             */
            classFileStream = servletContext.getResourceAsStream(entryName);

            if (classFileStream == null)
            {
               if (log.isDebugEnabled())
               {
                  log.debug("Could not obtain InputStream for class file: " + entryName);
               }
            }

            // analyze the class (with or without classFileStream)
            processClass(className, classFileStream, visitor);

         }
         finally
         {
            try
            {
               if (classFileStream != null)
               {
                  classFileStream.close();
               }
            }
            catch (IOException e)
            {
               if (log.isDebugEnabled())
               {
                  log.debug("Failed to close input stream: " + e.getMessage());
               }
            }
         }
      }

   }

   /**
    * @param path The path
    * @return last node in a a string representation of URL path. For example for "/a/b/c/d/" returns "d/", for
    *         "/a/b/c/d.class" returns "d.class"
    */
   private String getChildNodeName(String path)
   {
      String[] elements = path.split("/");
      int size = elements.length;
      String nodeName = elements[size - 1];
      return path.endsWith("/") ? nodeName + "/" : nodeName;
   }

   @Override
   public int priority()
   {
      return 0;
   }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy