click-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From med...@apache.org
Subject svn commit: r893062 - /incubator/click/trunk/click/framework/src/org/apache/click/service/DeployUtils.java
Date Tue, 22 Dec 2009 02:58:43 GMT
Author: medgar
Date: Tue Dec 22 02:58:42 2009
New Revision: 893062

URL: http://svn.apache.org/viewvc?rev=893062&view=rev
Log:
checkstyle updates

Modified:
    incubator/click/trunk/click/framework/src/org/apache/click/service/DeployUtils.java

Modified: incubator/click/trunk/click/framework/src/org/apache/click/service/DeployUtils.java
URL: http://svn.apache.org/viewvc/incubator/click/trunk/click/framework/src/org/apache/click/service/DeployUtils.java?rev=893062&r1=893061&r2=893062&view=diff
==============================================================================
--- incubator/click/trunk/click/framework/src/org/apache/click/service/DeployUtils.java (original)
+++ incubator/click/trunk/click/framework/src/org/apache/click/service/DeployUtils.java Tue
Dec 22 02:58:42 2009
@@ -1,552 +1,554 @@
-/* Copyright 2005-2006 Tim Fennell
- *
- * 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.apache.click.service;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FilenameFilter;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-import java.util.jar.JarEntry;
-import java.util.jar.JarInputStream;
-import org.apache.click.util.ClickUtils;
-
-/**
- * <p>ResolverUtil is used to locate classes that are available in the/a class path
and meet
- * arbitrary conditions. The two most common conditions are that a class implements/extends
- * another class, or that is it annotated with a specific annotation. However, through the
use
- * of the {@link Test} class it is possible to search using arbitrary conditions.</p>
- *
- * <p>A ClassLoader is used to locate all locations (directories and jar files) in
the class
- * path that contain classes within certain packages, and then to load those classes and
- * check them. By default the ClassLoader returned by
- *  {@code Thread.currentThread().getContextClassLoader()} is used, but this can be overridden
- * by calling {@link #setClassLoader(ClassLoader)} prior to invoking any of the {@code find()}
- * methods.</p>
- *
- * <p>General searches are initiated by calling the
- * {@link #find(net.sourceforge.stripes.util.ResolverUtil.Test, String)} ()} method and supplying
- * a package name and a Test instance. This will cause the named package <b>and all
sub-packages</b>
- * to be scanned for classes that meet the test. There are also utility methods for the common
- * use cases of scanning multiple packages for extensions of particular classes, or classes
- * annotated with a specific annotation.</p>
- *
- * <p>The standard usage pattern for the ResolverUtil class is as follows:</p>
- *
- *<pre>
- *ResolverUtil&lt;ActionBean&gt; resolver = new ResolverUtil&lt;ActionBean&gt;();
- *resolver.findImplementation(ActionBean.class, pkg1, pkg2);
- *resolver.find(new CustomTest(), pkg1);
- *resolver.find(new CustomTest(), pkg2);
- *Collection&lt;ActionBean&gt; beans = resolver.getClasses();
- *</pre>
- *
- * This class was copied and adpated from the Stripes Framework -
- * <a href="http://www.stripesframework.org">Stripes</a>.
- *
- * @author Tim Fennell
- */
-class DeployUtils<T> {
-
-    // -------------------------------------------------------------- Constants
-
-    /** The magic header that indicates a JAR (ZIP) file. */
-    private static final byte[] JAR_MAGIC = { 'P', 'K', 3, 4 };
-
-    // -------------------------------------------------------------- Variables
-
-    /** The set of matches being accumulated. */
-    private List<String> matches = new ArrayList<String>();
-
-    /** The log service to log output to. */
-    private LogService logService;
-
-    /**
-     * The ClassLoader to use when looking for classes. If null then the ClassLoader
-     * returned by Thread.currentThread().getContextClassLoader() will be used.
-     */
-    private ClassLoader classloader;
-
-    // ----------------------------------------------------------- Constructors
-
-    /**
-     * Create a new DeployUtils instance.
-     *
-     * @param logService the logService to log output to
-     */
-    public DeployUtils(LogService logService) {
-        this.logService = logService;
-    }
-
-    // --------------------------------------------------------- Public Methods
-
-    /**
-     * Provides access to the resources discovered so far. If no calls have been
-     * made to any of the {@code find()} methods, this list will be empty.
-     *
-     * @return the list of resources that have been discovered.
-     */
-    public List<String> getResources() {
-        return matches;
-    }
-
-    /**
-     * Returns the classloader that will be used for scanning for classes. If no explicit
-     * ClassLoader has been set by the calling, the context class loader will be used.
-     *
-     * @return the ClassLoader that will be used to scan for classes
-     */
-    public ClassLoader getClassLoader() {
-        return classloader == null ? Thread.currentThread().getContextClassLoader() : classloader;
-    }
-
-    /**
-     * Sets an explicit ClassLoader that should be used when scanning for classes. If none
-     * is set then the context classloader will be used.
-     *
-     * @param classloader a ClassLoader to use when scanning for classes
-     */
-    public void setClassLoader(ClassLoader classloader) {
-        this.classloader = classloader;
-    }
-
-    /**
-     * Attemp to discover resources inside the given directory. Accumulated
-     * resources can be accessed by calling {@link #getResources()}.
-     *
-     * @param dirs one or more directories to scan (including sub-directories)
-     * for resources
-     * @return instance of DeployUtils allows for chaining calls
-     */
-    public DeployUtils<T> findResources(String... dirs) {
-        if (dirs == null) {
-            return this;
-        }
-
-        Test test = new IsDeployable();
-        for (String dir : dirs) {
-            find(test, dir);
-        }
-
-        return this;
-    }
-
-    /**
-     * Scans for classes starting at the package provided and descending into subpackages.
-     * Each class is offered up to the Test as it is discovered, and if the Test returns
-     * true the class is retained.  Accumulated classes can be fetched by calling
-     * {@link #getClasses()}.
-     *
-     * @param test an instance of {@link Test} that will be used to filter classes
-     * @param packageName the name of the package from which to start scanning for
-     *        classes, e.g. {@code net.sourceforge.stripes}
-     */
-    public DeployUtils<T> find(Test test, String packageName) {
-        String path = getPackagePath(packageName);
-
-        try {
-            List<URL> urls = Collections.list(getClassLoader().getResources(path));
-            for (URL url : urls) {
-                List<String> children = listClassResources(url, path);
-                for (String child : children) {
-                    addIfMatching(test, child);
-                }
-            }
-        }
-        catch (IOException ioe) {
-            logService.error("could not read package: " + packageName + " -- " + ioe);
-        }
-
-        return this;
-    }
-
-    // ------------------------------------------------------ Protected Methods
-
-    /**
-     * Recursively list all resources under the given URL that appear to define a Java class.
-     * Matching resources will have a name that ends in ".class" and have a relative path
such that
-     * each segment of the path is a valid Java identifier. The resource paths returned will
be
-     * relative to the URL and begin with the specified path.
-     *
-     * @param url The URL of the parent resource to search.
-     * @param path The path with which each matching resource path must begin, relative to
the URL.
-     * @return A list of matching resources. The list may be empty.
-     * @throws IOException
-     */
-    protected List<String> listClassResources(URL url, String path) throws IOException
{
-        if (logService.isDebugEnabled()) {
-            logService.debug("listing classes in " + url);
-        }
-
-        InputStream is = null;
-        try {
-            List<String> resources = new ArrayList<String>();
-
-            // First, try to find the URL of a JAR file containing the requested
-            // resource. If a JAR file is found, then we'll list child resources
-            // by reading the JAR.
-            URL jarUrl = findJarForResource(url, path);
-            if (jarUrl != null) {
-                // example jarUrl : jar:c:/dev/mylib.jar
-                is = jarUrl.openStream();
-                resources = listClassResources(new JarInputStream(is), path);
-
-            } else {
-                List<String> children = new ArrayList<String>();
-                try {
-                    if (isJar(url)) {
-                        // example url : jar:c:/dev/mylib.jar/META-INF/resources
-
-                        // Some versions of JBoss VFS might give a JAR stream even
-                        // if the resource referenced by the URL isn't actually a JAR
-                        is = url.openStream();
-                        JarInputStream jarInput = new JarInputStream(is);
-                        for (JarEntry entry; (entry = jarInput.getNextJarEntry()) != null;)
{
-                            if (logService.isTraceEnabled()) {
-                                logService.trace("jar entry: " + entry.getName());
-                            }
-
-                            if (isRelevantResource(entry.getName())) {
-                                children.add(entry.getName());
-                            }
-                        }
-                    } else {
-                        // Some servlet containers allow reading from "directory"
-                        // resources like text file, listing the child resources
-                        // one per line.
-                        is = url.openStream();
-
-                        // There is the possibility that a file doesn't have an
-                        // extension and would be seen as a directory. Guard against
-                        // that by checking that the url is a file and adding it
-                        // to the list of resources
-                        File file = new File(url.getFile());
-                        if (file.isFile()) {
-                            if (isRelevantResource(file.getName())) {
-                                resources.add(path);
-                            }
-                        } else {
-                            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
-                            for (String line; (line = reader.readLine()) != null;) {
-                                if (logService.isTraceEnabled()) {
-                                    logService.trace("reader entry: " + line);
-                                }
-                                if (isRelevantResource(line)) {
-                                    children.add(line);
-                                }
-                            }
-                        }
-                    }
-                } catch (FileNotFoundException e) {
-                    /*
-                     * For file URLs the openStream() call might fail, depending on the servlet
-                     * container, because directories can't be opened for reading. If that
happens,
-                     * then list the directory directly instead.
-                     */
-                    if ("file".equals(url.getProtocol())) {
-                        File file = new File(url.getFile());
-                        if (file.isDirectory()) {
-                            children = Arrays.asList(file.list(new FilenameFilter() {
-                                public boolean accept(File dir, String name) {
-                                    return isRelevantResource(name);
-                                }
-                            }));
-                        }
-                    } else {
-                        // No idea where the exception came from so log it
-                        logService.error("could not deploy the resources from"
-                            + " the url '" + url + "'. You will need to"
-                            + " manually included resources from this url in"
-                            + " your application.");
-                    }
-                }
-
-                // The URL prefix to use when recursively listing child resources
-                String prefix = url.toExternalForm();
-                if (!prefix.endsWith("/"))
-                    prefix = prefix + "/";
-
-                // Iterate over each immediate child, adding classes and recursing into directories
-                for (String child : children) {
-                    String resourcePath = path + "/" + child;
-                    if (child.indexOf(".") != -1) {
-                        if (logService.isTraceEnabled()) {
-                            logService.trace("found deployable resource: " + resourcePath);
-                        }
-                        resources.add(resourcePath);
-                    }
-                    else {
-                        URL childUrl = new URL(prefix + child);
-                        resources.addAll(listClassResources(childUrl, resourcePath));
-                    }
-                }
-            }
-
-            return resources;
-        }
-        finally {
-            try {
-                is.close();
-            }
-            catch (Exception e) {
-            }
-        }
-    }
-
-    /**
-     * List the names of the entries in the given {@link JarInputStream} that begin with
the
-     * specified {@code path}. Entries will match with or without a leading slash.
-     *
-     * @param jar The JAR input stream
-     * @param path The leading path to match
-     * @return The names of all the matching entries
-     * @throws IOException
-     */
-    protected List<String> listClassResources(JarInputStream jar, String path) throws
IOException {
-        // Include the leading and trailing slash when matching names
-        if (!path.startsWith("/"))
-            path = "/" + path;
-        if (!path.endsWith("/"))
-            path = path + "/";
-
-        // Iterate over the entries and collect those that begin with the requested path
-        List<String> resources = new ArrayList<String>();
-        for (JarEntry entry; (entry = jar.getNextJarEntry()) != null;) {
-            if (!entry.isDirectory()) {
-                // Add leading slash if it's missing
-                String name = entry.getName();
-                if (!name.startsWith("/"))
-                    name = "/" + name;
-
-                // Check resource name
-                if (name.startsWith(path)) {
-                    resources.add(name.substring(1)); // Trim leading slash
-                }
-            }
-        }
-        return resources;
-    }
-
-    /**
-     * Attempts to deconstruct the given URL to find a JAR file containing the resource referenced
-     * by the URL. That is, assuming the URL references a JAR entry, this method will return
a URL
-     * that references the JAR file containing the entry. If the JAR cannot be located, then
this
-     * method returns null.
-     *
-     * @param url The URL of the JAR entry.
-     * @param path The path by which the URL was requested from the class loader.
-     * @return The URL of the JAR file, if one is found. Null if not.
-     * @throws MalformedURLException
-     */
-    protected URL findJarForResource(URL url, String path) throws MalformedURLException {
-        if (logService.isTraceEnabled()) {
-            logService.trace("find jar url: " + url);
-        }
-
-        // If the file part of the URL is itself a URL, then that URL probably points to
the JAR
-        try {
-            for (;;) {
-                url = new URL(url.getFile());
-                if (logService.isTraceEnabled()) {
-                    logService.trace("inner url: " + url);
-                }
-            }
-        } catch (MalformedURLException e) {
-            // This will happen at some point and serves a break in the loop
-        }
-
-        // Look for the .jar extension and chop off everything after that
-        StringBuilder jarUrl = new StringBuilder(url.toExternalForm());
-        int index = jarUrl.lastIndexOf(".jar");
-        if (index >= 0) {
-            jarUrl.setLength(index + 4);
-            if (logService.isTraceEnabled()) {
-                logService.trace("extracted jar url: " + jarUrl);
-            }
-        } else {
-            if (logService.isTraceEnabled()) {
-                logService.trace("not a jar: " + jarUrl);
-            }
-            return null;
-        }
-
-        // Try to open and test it
-        try {
-            URL testUrl = new URL(jarUrl.toString());
-            if (isJar(testUrl)) {
-                return testUrl;
-            } else {
-                // WebLogic fix: check if the URL's file exists in the filesystem.
-                if (logService.isTraceEnabled()) {
-                    logService.trace("not a jar: " + jarUrl);
-                }
-
-                jarUrl.replace(0, jarUrl.length(), testUrl.getFile());
-
-                // File name might be URL-encoded
-                File file = new File(ClickUtils.decodeURL(jarUrl.toString()));
-                if (file.exists()) {
-                    if (logService.isTraceEnabled()) {
-                        logService.trace("trying real file: " + file.getAbsolutePath());
-                    }
-                    testUrl =  file.toURI().toURL();
-                    if (isJar(testUrl)) {
-                        return testUrl;
-                    }
-                }
-            }
-        }
-        catch (MalformedURLException e) {
-            logService.warn("invalid jar url: " + e.getMessage());
-        }
-
-        if (logService.isTraceEnabled()) {
-            logService.trace("not a jar: " + jarUrl);
-        }
-        return null;
-    }
-
-    /**
-     * Converts a Java package name to a path that can be looked up with a call to
-     * {@link ClassLoader#getResources(String)}.
-     *
-     * @param packageName The Java package name to convert to a path
-     */
-    protected String getPackagePath(String packageName) {
-        return packageName == null ? null : packageName.replace('.', '/');
-    }
-
-    /**
-     * Returns true if the name of a resource (file or directory) is one that matters in
the search
-     * for classes. Relevant resources would be class files themselves (file names that end
with
-     * ".class") and directories that might be a Java package name segment (java identifiers).
-     *
-     * @param resourceName The resource name, without path information
-     */
-    protected boolean isRelevantResource(String resourceName) {
-        return resourceName != null && !resourceName.equals("");
-    }
-
-    /**
-     * Returns true if the resource located at the given URL is a JAR file.
-     *
-     * @param url The URL of the resource to test.
-     */
-    protected boolean isJar(URL url) {
-        return isJar(url, new byte[JAR_MAGIC.length]);
-    }
-
-    /**
-     * Returns true if the resource located at the given URL is a JAR file.
-     *
-     * @param url The URL of the resource to test.
-     * @param buffer A buffer into which the first few bytes of the resource are read. The
buffer
-     *            must be at least the size of {@link #JAR_MAGIC}. (The same buffer may be
reused
-     *            for multiple calls as an optimization.)
-     */
-    protected boolean isJar(URL url, byte[] buffer) {
-        InputStream is = null;
-        try {
-            is = url.openStream();
-            is.read(buffer, 0, JAR_MAGIC.length);
-            if (Arrays.equals(buffer, JAR_MAGIC)) {
-                if (logService.isInfoEnabled()) {
-                    logService.info("found jar: " + url);
-                }
-                return true;
-            }
-        }
-        catch (Exception e) {
-            // Failure to read the stream means this is not a JAR
-        }
-        finally {
-            try {
-                is.close();
-            }
-            catch (Exception e) {
-            }
-        }
-
-        return false;
-    }
-
-    /**
-     * Add the class designated by the fully qualified class name provided to the set of
-     * resolved classes if and only if it is approved by the Test supplied.
-     *
-     * @param test the test used to determine if the class matches
-     * @param fqn the fully qualified name of a class
-     */
-    @SuppressWarnings("unchecked")
-    protected void addIfMatching(Test test, String fqn) {
-        try {
-            if (test.matches(fqn) ) {
-                matches.add(fqn);
-            }
-        }
-        catch (Throwable t) {
-            logService.error("could not examine class '" + fqn + "'" + " due to a "
-                + t.getClass().getName() + " with message: " + t.getMessage());
-        }
-    }
-
-    // ---------------------------------------------------------- Inner classes
-
-    /**
-     * A simple interface that specifies how to test classes to determine if they
-     * are to be included in the results produced by the ResolverUtil.
-     */
-    static interface Test {
-        /**
-         * Will be called repeatedly with candidate classes. Must return True if a class
-         * is to be included in the results, false otherwise.
-         */
-        boolean matches(String resource);
-    }
-
-    /**
-     * This test matches deployable resources.
-     */
-    static class IsDeployable implements Test {
-
-        /**
-         * Default constructor.
-         */
-        IsDeployable() {
-        }
-
-        /**
-         * Return true if the given resource should be included in the results,
-         * false otherwise.
-         *
-         * @param resource the resource to be included in the results
-         * @return true if the resource should be included in the results, false
-         * otherwise
-         */
-        @SuppressWarnings("unchecked")
-        public boolean matches(String resource) {
-            // If a resource is found, it must be deployed
-            return true;
-        }
-    }
-}
+/* Copyright 2005-2006 Tim Fennell
+ *
+ * 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.apache.click.service;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+import org.apache.click.util.ClickUtils;
+
+/**
+ * <p>ResolverUtil is used to locate classes that are available in the/a class path
and meet
+ * arbitrary conditions. The two most common conditions are that a class implements/extends
+ * another class, or that is it annotated with a specific annotation. However, through the
use
+ * of the {@link Test} class it is possible to search using arbitrary conditions.</p>
+ *
+ * <p>A ClassLoader is used to locate all locations (directories and jar files) in
the class
+ * path that contain classes within certain packages, and then to load those classes and
+ * check them. By default the ClassLoader returned by
+ *  {@code Thread.currentThread().getContextClassLoader()} is used, but this can be overridden
+ * by calling {@link #setClassLoader(ClassLoader)} prior to invoking any of the {@code find()}
+ * methods.</p>
+ *
+ * <p>General searches are initiated by calling the
+ * {@link #find(net.sourceforge.stripes.util.ResolverUtil.Test, String)} ()} method and supplying
+ * a package name and a Test instance. This will cause the named package <b>and all
sub-packages</b>
+ * to be scanned for classes that meet the test. There are also utility methods for the common
+ * use cases of scanning multiple packages for extensions of particular classes, or classes
+ * annotated with a specific annotation.</p>
+ *
+ * <p>The standard usage pattern for the ResolverUtil class is as follows:</p>
+ *
+ *<pre>
+ *ResolverUtil&lt;ActionBean&gt; resolver = new ResolverUtil&lt;ActionBean&gt;();
+ *resolver.findImplementation(ActionBean.class, pkg1, pkg2);
+ *resolver.find(new CustomTest(), pkg1);
+ *resolver.find(new CustomTest(), pkg2);
+ *Collection&lt;ActionBean&gt; beans = resolver.getClasses();
+ *</pre>
+ *
+ * This class was copied and adpated from the Stripes Framework -
+ * <a href="http://www.stripesframework.org">Stripes</a>.
+ *
+ * @author Tim Fennell
+ */
+class DeployUtils<T> {
+
+    // -------------------------------------------------------------- Constants
+
+    /** The magic header that indicates a JAR (ZIP) file. */
+    private static final byte[] JAR_MAGIC = { 'P', 'K', 3, 4 };
+
+    // -------------------------------------------------------------- Variables
+
+    /** The set of matches being accumulated. */
+    private List<String> matches = new ArrayList<String>();
+
+    /** The log service to log output to. */
+    private LogService logService;
+
+    /**
+     * The ClassLoader to use when looking for classes. If null then the ClassLoader
+     * returned by Thread.currentThread().getContextClassLoader() will be used.
+     */
+    private ClassLoader classloader;
+
+    // ----------------------------------------------------------- Constructors
+
+    /**
+     * Create a new DeployUtils instance.
+     *
+     * @param logService the logService to log output to
+     */
+    public DeployUtils(LogService logService) {
+        this.logService = logService;
+    }
+
+    // --------------------------------------------------------- Public Methods
+
+    /**
+     * Provides access to the resources discovered so far. If no calls have been
+     * made to any of the {@code find()} methods, this list will be empty.
+     *
+     * @return the list of resources that have been discovered.
+     */
+    public List<String> getResources() {
+        return matches;
+    }
+
+    /**
+     * Returns the classloader that will be used for scanning for classes. If no explicit
+     * ClassLoader has been set by the calling, the context class loader will be used.
+     *
+     * @return the ClassLoader that will be used to scan for classes
+     */
+    public ClassLoader getClassLoader() {
+        return classloader == null ? Thread.currentThread().getContextClassLoader() : classloader;
+    }
+
+    /**
+     * Sets an explicit ClassLoader that should be used when scanning for classes. If none
+     * is set then the context classloader will be used.
+     *
+     * @param classloader a ClassLoader to use when scanning for classes
+     */
+    public void setClassLoader(ClassLoader classloader) {
+        this.classloader = classloader;
+    }
+
+    /**
+     * Attemp to discover resources inside the given directory. Accumulated
+     * resources can be accessed by calling {@link #getResources()}.
+     *
+     * @param dirs one or more directories to scan (including sub-directories)
+     * for resources
+     * @return instance of DeployUtils allows for chaining calls
+     */
+    public DeployUtils<T> findResources(String... dirs) {
+        if (dirs == null) {
+            return this;
+        }
+
+        Test test = new IsDeployable();
+        for (String dir : dirs) {
+            find(test, dir);
+        }
+
+        return this;
+    }
+
+    /**
+     * Scans for classes starting at the package provided and descending into subpackages.
+     * Each class is offered up to the Test as it is discovered, and if the Test returns
+     * true the class is retained.  Accumulated classes can be fetched by calling
+     * {@link #getClasses()}.
+     *
+     * @param test an instance of {@link Test} that will be used to filter classes
+     * @param packageName the name of the package from which to start scanning for
+     *        classes, e.g. {@code net.sourceforge.stripes}
+     */
+    public DeployUtils<T> find(Test test, String packageName) {
+        String path = getPackagePath(packageName);
+
+        try {
+            List<URL> urls = Collections.list(getClassLoader().getResources(path));
+            for (URL url : urls) {
+                List<String> children = listClassResources(url, path);
+                for (String child : children) {
+                    addIfMatching(test, child);
+                }
+            }
+
+        } catch (IOException ioe) {
+            logService.error("could not read package: " + packageName + " -- " + ioe);
+        }
+
+        return this;
+    }
+
+    // ------------------------------------------------------ Protected Methods
+
+    /**
+     * Recursively list all resources under the given URL that appear to define a Java class.
+     * Matching resources will have a name that ends in ".class" and have a relative path
such that
+     * each segment of the path is a valid Java identifier. The resource paths returned will
be
+     * relative to the URL and begin with the specified path.
+     *
+     * @param url The URL of the parent resource to search.
+     * @param path The path with which each matching resource path must begin, relative to
the URL.
+     * @return A list of matching resources. The list may be empty.
+     * @throws IOException
+     */
+    protected List<String> listClassResources(URL url, String path) throws IOException
{
+        if (logService.isDebugEnabled()) {
+            logService.debug("listing classes in " + url);
+        }
+
+        InputStream is = null;
+        try {
+            List<String> resources = new ArrayList<String>();
+
+            // First, try to find the URL of a JAR file containing the requested
+            // resource. If a JAR file is found, then we'll list child resources
+            // by reading the JAR.
+            URL jarUrl = findJarForResource(url, path);
+            if (jarUrl != null) {
+                // example jarUrl : jar:c:/dev/mylib.jar
+                is = jarUrl.openStream();
+                resources = listClassResources(new JarInputStream(is), path);
+
+            } else {
+                List<String> children = new ArrayList<String>();
+                try {
+                    if (isJar(url)) {
+                        // example url : jar:c:/dev/mylib.jar/META-INF/resources
+
+                        // Some versions of JBoss VFS might give a JAR stream even
+                        // if the resource referenced by the URL isn't actually a JAR
+                        is = url.openStream();
+                        JarInputStream jarInput = new JarInputStream(is);
+                        for (JarEntry entry; (entry = jarInput.getNextJarEntry()) != null;)
{
+                            if (logService.isTraceEnabled()) {
+                                logService.trace("jar entry: " + entry.getName());
+                            }
+
+                            if (isRelevantResource(entry.getName())) {
+                                children.add(entry.getName());
+                            }
+                        }
+                    } else {
+                        // Some servlet containers allow reading from "directory"
+                        // resources like text file, listing the child resources
+                        // one per line.
+                        is = url.openStream();
+
+                        // There is the possibility that a file doesn't have an
+                        // extension and would be seen as a directory. Guard against
+                        // that by checking that the url is a file and adding it
+                        // to the list of resources
+                        File file = new File(url.getFile());
+                        if (file.isFile()) {
+                            if (isRelevantResource(file.getName())) {
+                                resources.add(path);
+                            }
+                        } else {
+                            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+                            for (String line; (line = reader.readLine()) != null;) {
+                                if (logService.isTraceEnabled()) {
+                                    logService.trace("reader entry: " + line);
+                                }
+                                if (isRelevantResource(line)) {
+                                    children.add(line);
+                                }
+                            }
+                        }
+                    }
+
+                } catch (FileNotFoundException e) {
+                    /*
+                     * For file URLs the openStream() call might fail, depending on the servlet
+                     * container, because directories can't be opened for reading. If that
happens,
+                     * then list the directory directly instead.
+                     */
+                    if ("file".equals(url.getProtocol())) {
+                        File file = new File(url.getFile());
+                        if (file.isDirectory()) {
+                            children = Arrays.asList(file.list(new FilenameFilter() {
+                                public boolean accept(File dir, String name) {
+                                    return isRelevantResource(name);
+                                }
+                            }));
+                        }
+                    } else {
+                        // No idea where the exception came from so log it
+                        logService.error("could not deploy the resources from"
+                            + " the url '" + url + "'. You will need to"
+                            + " manually included resources from this url in"
+                            + " your application.");
+                    }
+                }
+
+                // The URL prefix to use when recursively listing child resources
+                String prefix = url.toExternalForm();
+                if (!prefix.endsWith("/")) {
+                    prefix = prefix + "/";
+                }
+
+                // Iterate over each immediate child, adding classes and recursing into directories
+                for (String child : children) {
+                    String resourcePath = path + "/" + child;
+                    if (child.indexOf(".") != -1) {
+                        if (logService.isTraceEnabled()) {
+                            logService.trace("found deployable resource: " + resourcePath);
+                        }
+                        resources.add(resourcePath);
+
+                    } else {
+                        URL childUrl = new URL(prefix + child);
+                        resources.addAll(listClassResources(childUrl, resourcePath));
+                    }
+                }
+            }
+
+            return resources;
+
+        } finally {
+            ClickUtils.close(is);
+        }
+    }
+
+    /**
+     * List the names of the entries in the given {@link JarInputStream} that begin with
the
+     * specified {@code path}. Entries will match with or without a leading slash.
+     *
+     * @param jar The JAR input stream
+     * @param path The leading path to match
+     * @return The names of all the matching entries
+     * @throws IOException
+     */
+    protected List<String> listClassResources(JarInputStream jar, String path) throws
IOException {
+        // Include the leading and trailing slash when matching names
+        if (!path.startsWith("/")) {
+            path = "/" + path;
+        }
+        if (!path.endsWith("/")) {
+            path = path + "/";
+        }
+
+        // Iterate over the entries and collect those that begin with the requested path
+        List<String> resources = new ArrayList<String>();
+        for (JarEntry entry; (entry = jar.getNextJarEntry()) != null;) {
+            if (!entry.isDirectory()) {
+                // Add leading slash if it's missing
+                String name = entry.getName();
+                if (!name.startsWith("/")) {
+                    name = "/" + name;
+                }
+
+                // Check resource name
+                if (name.startsWith(path)) {
+                    resources.add(name.substring(1)); // Trim leading slash
+                }
+            }
+        }
+
+        return resources;
+    }
+
+    /**
+     * Attempts to deconstruct the given URL to find a JAR file containing the resource referenced
+     * by the URL. That is, assuming the URL references a JAR entry, this method will return
a URL
+     * that references the JAR file containing the entry. If the JAR cannot be located, then
this
+     * method returns null.
+     *
+     * @param url The URL of the JAR entry.
+     * @param path The path by which the URL was requested from the class loader.
+     * @return The URL of the JAR file, if one is found. Null if not.
+     * @throws MalformedURLException
+     */
+    protected URL findJarForResource(URL url, String path) throws MalformedURLException {
+        if (logService.isTraceEnabled()) {
+            logService.trace("find jar url: " + url);
+        }
+
+        // If the file part of the URL is itself a URL, then that URL probably points to
the JAR
+        try {
+            for (;;) {
+                url = new URL(url.getFile());
+                if (logService.isTraceEnabled()) {
+                    logService.trace("inner url: " + url);
+                }
+            }
+
+        } catch (MalformedURLException e) {
+            // This will happen at some point and serves a break in the loop
+        }
+
+        // Look for the .jar extension and chop off everything after that
+        StringBuilder jarUrl = new StringBuilder(url.toExternalForm());
+        int index = jarUrl.lastIndexOf(".jar");
+        if (index >= 0) {
+            jarUrl.setLength(index + 4);
+            if (logService.isTraceEnabled()) {
+                logService.trace("extracted jar url: " + jarUrl);
+            }
+        } else {
+            if (logService.isTraceEnabled()) {
+                logService.trace("not a jar: " + jarUrl);
+            }
+            return null;
+        }
+
+        // Try to open and test it
+        try {
+            URL testUrl = new URL(jarUrl.toString());
+            if (isJar(testUrl)) {
+                return testUrl;
+
+            } else {
+                // WebLogic fix: check if the URL's file exists in the filesystem.
+                if (logService.isTraceEnabled()) {
+                    logService.trace("not a jar: " + jarUrl);
+                }
+
+                jarUrl.replace(0, jarUrl.length(), testUrl.getFile());
+
+                // File name might be URL-encoded
+                File file = new File(ClickUtils.decodeURL(jarUrl.toString()));
+                if (file.exists()) {
+                    if (logService.isTraceEnabled()) {
+                        logService.trace("trying real file: " + file.getAbsolutePath());
+                    }
+                    testUrl =  file.toURI().toURL();
+                    if (isJar(testUrl)) {
+                        return testUrl;
+                    }
+                }
+            }
+
+        } catch (MalformedURLException e) {
+            logService.warn("invalid jar url: " + e.getMessage());
+        }
+
+        if (logService.isTraceEnabled()) {
+            logService.trace("not a jar: " + jarUrl);
+        }
+        return null;
+    }
+
+    /**
+     * Converts a Java package name to a path that can be looked up with a call to
+     * {@link ClassLoader#getResources(String)}.
+     *
+     * @param packageName The Java package name to convert to a path
+     */
+    protected String getPackagePath(String packageName) {
+        return packageName == null ? null : packageName.replace('.', '/');
+    }
+
+    /**
+     * Returns true if the name of a resource (file or directory) is one that matters in
the search
+     * for classes. Relevant resources would be class files themselves (file names that end
with
+     * ".class") and directories that might be a Java package name segment (java identifiers).
+     *
+     * @param resourceName The resource name, without path information
+     */
+    protected boolean isRelevantResource(String resourceName) {
+        return resourceName != null && !resourceName.equals("");
+    }
+
+    /**
+     * Returns true if the resource located at the given URL is a JAR file.
+     *
+     * @param url The URL of the resource to test.
+     */
+    protected boolean isJar(URL url) {
+        return isJar(url, new byte[JAR_MAGIC.length]);
+    }
+
+    /**
+     * Returns true if the resource located at the given URL is a JAR file.
+     *
+     * @param url The URL of the resource to test.
+     * @param buffer A buffer into which the first few bytes of the resource are read. The
buffer
+     *            must be at least the size of {@link #JAR_MAGIC}. (The same buffer may be
reused
+     *            for multiple calls as an optimization.)
+     */
+    protected boolean isJar(URL url, byte[] buffer) {
+        InputStream is = null;
+        try {
+            is = url.openStream();
+            is.read(buffer, 0, JAR_MAGIC.length);
+            if (Arrays.equals(buffer, JAR_MAGIC)) {
+                if (logService.isInfoEnabled()) {
+                    logService.info("found jar: " + url);
+                }
+                return true;
+            }
+
+        } catch (Exception e) {
+            // Failure to read the stream means this is not a JAR
+
+        } finally {
+            try {
+                is.close();
+            } catch (Exception e) {
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Add the class designated by the fully qualified class name provided to the set of
+     * resolved classes if and only if it is approved by the Test supplied.
+     *
+     * @param test the test used to determine if the class matches
+     * @param fqn the fully qualified name of a class
+     */
+    @SuppressWarnings("unchecked")
+    protected void addIfMatching(Test test, String fqn) {
+        try {
+            if (test.matches(fqn)) {
+                matches.add(fqn);
+            }
+        } catch (Throwable t) {
+            logService.error("could not examine class '" + fqn + "'" + " due to a "
+                + t.getClass().getName() + " with message: " + t.getMessage());
+        }
+    }
+
+    // ---------------------------------------------------------- Inner classes
+
+    /**
+     * A simple interface that specifies how to test classes to determine if they
+     * are to be included in the results produced by the ResolverUtil.
+     */
+    static interface Test {
+        /**
+         * Will be called repeatedly with candidate classes. Must return True if a class
+         * is to be included in the results, false otherwise.
+         */
+        boolean matches(String resource);
+    }
+
+    /**
+     * This test matches deployable resources.
+     */
+    static class IsDeployable implements Test {
+
+        /**
+         * Default constructor.
+         */
+        IsDeployable() {
+        }
+
+        /**
+         * Return true if the given resource should be included in the results,
+         * false otherwise.
+         *
+         * @param resource the resource to be included in the results
+         * @return true if the resource should be included in the results, false
+         * otherwise
+         */
+        @SuppressWarnings("unchecked")
+        public boolean matches(String resource) {
+            // If a resource is found, it must be deployed
+            return true;
+        }
+    }
+}



Mime
View raw message