diff --git a/annotations-impl/src/main/java/org/ocpsoft/rewrite/annotation/scan/WebClassesFinder.java b/annotations-impl/src/main/java/org/ocpsoft/rewrite/annotation/scan/WebClassesFinder.java
index 4d5253434..107f0eb8a 100644
--- a/annotations-impl/src/main/java/org/ocpsoft/rewrite/annotation/scan/WebClassesFinder.java
+++ b/annotations-impl/src/main/java/org/ocpsoft/rewrite/annotation/scan/WebClassesFinder.java
@@ -21,226 +21,207 @@
import java.net.URL;
import java.util.LinkedHashSet;
import java.util.Set;
-
import javax.servlet.ServletContext;
-
import org.ocpsoft.rewrite.annotation.ClassVisitorImpl;
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
+ * 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(ClassVisitorImpl)}.
*
* @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
- {
- // we start the recursive scan in 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, classesFolderUrl, visitor);
-
- }
- catch (MalformedURLException e)
+ /**
+ * 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)
{
- 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, ClassVisitorImpl) when it finds a file ending with ".class" and
- * that is accepted by the {@link PackageFilter}
- *
- * @param directoryUrl The URL of the directory to scan
- * @param visitor The vistor class to call for classes found
- * @throws MalformedURLException for invalid URLs
- */
- protected void processDirectory(URL classesFolderUrl, URL directoryUrl, ClassVisitor visitor)
- throws MalformedURLException
- {
-
- // only the path of the classes folder URL is required in this method
- String classesFolderPath = classesFolderUrl.getPath();
-
- // log directory name on trace level
- if (log.isTraceEnabled())
- {
- log.trace("Processing directory: " + directoryUrl.toString());
+ log.warn("Cannot find classes folder: " + CLASSES_FOLDER);
+ return;
}
- // get the directory name relative to the '/WEB-INF/classes/' folder
- String relativeDirectoryName = getPathRelativeToClassesFolder(directoryUrl.getPath(), classesFolderPath);
-
- // call getResourcePaths to get directory entries
- Set> paths = servletContext.getResourcePaths(CLASSES_FOLDER + relativeDirectoryName);
-
- if (paths != null) {
-
- // loop over all entries of the directory
- for (Object relativePath : paths)
- {
-
- // get full URL for this entry
- URL entryUrl = servletContext.getResource(relativePath.toString());
-
- // Embedded Jetty bug?
- if (entryUrl == null) {
- log.warn("Unable to obtain URL for relative path: " + relativePath.toString());
- continue;
- }
-
- // we are using toString() instead of getPath() because AS7 doesn't handle # characters correctly
- String entryName = entryUrl.toString();
-
- // if this URL ends with .class it is a Java class
- if (entryName.endsWith(".class"))
- {
-
- // the name of the entry relative to the '/WEB-INF/classes/' folder
- String entryRelativeName = getPathRelativeToClassesFolder(entryName, classesFolderPath);
-
- // build class name from relative name
- String className = getClassName(entryRelativeName);
-
- // 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 an IOException is thrown,
- * we will scan it anyway.
- */
- try
- {
- classFileStream = entryUrl.openStream();
- }
- catch (IOException e)
- {
- if (log.isDebugEnabled())
- {
- log.debug("Cound not obtain InputStream for class file: " + entryUrl.toString(), e);
- }
- }
-
- // 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());
- }
- }
- }
- }
-
- }
-
- // if this URL ends with a slash, its a directory
- if (entryName.endsWith("/"))
- {
-
- // walk down the directory
- processDirectory(classesFolderUrl, entryUrl, visitor);
-
- }
- }
- }
- }
-
- /**
- * This method will create a path relative to the '/WEB-INF/classes/' folder for the given path. It will first try to
- * find the '/WEB-INF/classes/' suffix in the path to do so. If this suffix cannot be found (can happen when using
- * the jetty-maven-plugin with 'jetty:run' goal), the method will try to build the relative name by stripping the
- * path of the '/WEB-INF/classes/' folder which must be supplied to the method. The method will throw an
- * {@link IllegalArgumentException} if the relative path could not be build.
- *
- * @param path The path to build the relative path for
- * @param classesFolderPath the known path of the '/WEB-INF/classes/' folder.
- * @return the relative name of the path
- */
- private String getPathRelativeToClassesFolder(String path, String classesFolderPath)
- {
-
- // first try to find the '/WEB-INF/classes' suffix
- String result = stripKnownPrefix(path, CLASSES_FOLDER);
-
- // alternative: try to strip the full path of the '/WEB-INF/classes/' folder
- if (result == null)
+ // 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, ClassVisitorImpl) 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("/"))
{
- result = stripKnownPrefix(path, classesFolderPath);
+ // Recursive cal
+ processDirectory(childNodeUrl, childNodeName, visitor);
}
-
- // none of the two methods worked?
- if (result == null)
+ if (childNodeRelative.endsWith(".class"))
{
- throw new IllegalArgumentException("Unable to build path relative to '/WEB-INF/classes/' from: " + path);
+ handleClassEntry(childNodeUrl, childNodeName, visitor);
}
+ }
+ }
+
+ /**
+ * Handles class entry in a WEB-INF.
+ */
+ private void handleClassEntry(URL entryUrl, String entryName, ClassVisitor visitor)
+ {
+
+ // build class name from relative name
+ String className = getClassName(entryName.substring(CLASSES_FOLDER.length()));
- return result;
+ // check filter
+ if (mustProcessClass(className) && !processedClasses.contains(className))
+ {
- }
+ // mark this class as processed
+ processedClasses.add(className);
- @Override
- public int priority()
- {
- return 0;
- }
+ // the class file stream
+ InputStream classFileStream = null;
+
+ // close the stream in finally block
+ try
+ {
+
+ /*
+ * Try to open the .class file. If an IOException is thrown,
+ * we will scan it anyway.
+ */
+ try
+ {
+ classFileStream = entryUrl.openStream();
+ } catch (IOException e)
+ {
+ if (log.isDebugEnabled())
+ {
+ log.debug("Cound not obtain InputStream for class file: " + entryUrl.toString(), e);
+ }
+ }
+
+ // 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
+ * @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;
+ }
}