Author: craigmcc
Date: Thu Oct 19 19:59:37 2006
New Revision: 465957

URL: http://svn.apache.org/viewvc?view=rev&rev=465957
Log:
Add an optional optimization option for reducing startup time, by allowing
the developer to specify a set of Java packages and/or JAR files (in
WEB-INF/lib) that should be searched for Tiger annotations.

Thanks to Mario Ivankovits for the patch, which was applied with some
cosmetic changes.

SHALE-301

Added:
    
shale/framework/trunk/shale-tiger/src/main/java/org/apache/shale/tiger/view/faces/PackageInfo.java
   (with props)
Modified:
    
shale/framework/trunk/shale-tiger/src/main/java/org/apache/shale/tiger/view/faces/LifecycleListener2.java

Modified: 
shale/framework/trunk/shale-tiger/src/main/java/org/apache/shale/tiger/view/faces/LifecycleListener2.java
URL: 
http://svn.apache.org/viewvc/shale/framework/trunk/shale-tiger/src/main/java/org/apache/shale/tiger/view/faces/LifecycleListener2.java?view=diff&rev=465957&r1=465956&r2=465957
==============================================================================
--- 
shale/framework/trunk/shale-tiger/src/main/java/org/apache/shale/tiger/view/faces/LifecycleListener2.java
 (original)
+++ 
shale/framework/trunk/shale-tiger/src/main/java/org/apache/shale/tiger/view/faces/LifecycleListener2.java
 Thu Oct 19 19:59:37 2006
@@ -118,6 +118,14 @@
 
 
     /**
+     * <p> Servlet context init parameter which defines which packages to scan
+     * for beans.</p>
+     */
+    public static final String SCAN_PACKAGES =
+            "org.apache.shale.tiger.SCAN_PACKAGES";
+
+
+    /**
      * <p>Application scope attribute under which a configured
      * [EMAIL PROTECTED] FacesConfigConfig} bean will be stored, containing
      * information parsed from the relevant <code>faces-config.xml</code>
@@ -211,11 +219,29 @@
             ; // Null means it is not initialized yet
         }
 
-        // Scan the classes in /WEB-INF/classes for interesting annotations
-        List<Class> classes = null;
+        List<Class> classes;
+
+        String scanPackages = servletContext.getInitParameter(SCAN_PACKAGES);
+        if (scanPackages != null) {
+            // Scan the classes configured by the scan_packages context 
parameter
+            try {
+                classes = packageClasses(servletContext, scanPackages);
+            } catch (ClassNotFoundException e) {
+                throw new FacesException(e);
+            } catch (IOException e) {
+                throw new FacesException(e);
+            }
+        }
+        else {
+            // Scan the classes in /WEB-INF/classes for interesting annotations
+            try {
+                classes = webClasses(servletContext);
+            } catch (ClassNotFoundException e) {
+                throw new FacesException(e);
+            }
+        }
 
         try {
-            classes = webClasses(servletContext);
             for (Class clazz : classes) {
                 if (application != null) {
                     registerClass(clazz, application);
@@ -228,24 +254,25 @@
             throw new FacesException(e);
         }
 
-        // Scan the classes in /WEB-INF/lib for interesting annotations
-        List<JarFile> archives = null;
-
-        try {
-            archives = webArchives(servletContext);
-            for (JarFile archive : archives) {
-                classes = archiveClasses(servletContext, archive);
-                for (Class clazz : classes) {
-                    if (application != null) {
-                        registerClass(clazz, application);
-                    } else {
-                        queueClass(clazz);
+        if (scanPackages == null) {
+            // Scan the classes in /WEB-INF/lib for interesting annotations
+            List<JarFile> archives = null;
+            try {
+                archives = webArchives(servletContext);
+                for (JarFile archive : archives) {
+                    classes = archiveClasses(servletContext, archive);
+                    for (Class clazz : classes) {
+                        if (application != null) {
+                            registerClass(clazz, application);
+                        } else {
+                            queueClass(clazz);
+                        }
+                        scanClass(clazz, config);
                     }
-                    scanClass(clazz, config);
                 }
+            } catch (Exception e) {
+                throw new FacesException(e);
             }
-        } catch (Exception e) {
-            throw new FacesException(e);
         }
 
         // Create a parser instance used to parse faces-config.xml resources
@@ -295,6 +322,40 @@
 
     }
 
+    /**
+     * <p>Return a list of the classes defined within the given packages
+     * If there are no such classes, a zero-length list will be returned.</p>
+     *
+     * @param scanPackages the package configuration
+     *
+     * @exception ClassNotFoundException if a located class cannot be loaded
+     * @exception IOException if an input/output error occurs
+     */
+    private List<Class> packageClasses(ServletContext servletContext, String 
scanPackages)
+      throws ClassNotFoundException, IOException {
+
+        List<Class> list = new ArrayList<Class>();
+
+        String[] scanPackageTokens =  scanPackages.split(",");
+        for (String scanPackageToken : scanPackageTokens)
+        {
+            if (scanPackageToken.toLowerCase().endsWith(".jar"))
+            {
+                URL jarResource = servletContext.getResource(WEB_LIB_PREFIX + 
scanPackageToken);
+                String jarURLString = "jar:" + jarResource.toString() + "!/";
+                URL url = new URL(jarURLString);
+                JarFile jarFile = ((JarURLConnection) 
url.openConnection()).getJarFile();
+
+                list.addAll(archiveClasses(servletContext, jarFile));
+            }
+            else
+            {
+                PackageInfo.getInstance().getClasses(list, scanPackageToken);
+            }
+        }
+        return list;
+    }
+
 
     /**
      * <p>Respond to a context destroyed event.  Clean up our allocated
@@ -1112,7 +1173,7 @@
                 return map.get(annotation);
             }
 
-    
+
             // Construct and cache a new Map identifying the
             // methods of interest for these callbacks
             map = new HashMap<Class,Method>();

Added: 
shale/framework/trunk/shale-tiger/src/main/java/org/apache/shale/tiger/view/faces/PackageInfo.java
URL: 
http://svn.apache.org/viewvc/shale/framework/trunk/shale-tiger/src/main/java/org/apache/shale/tiger/view/faces/PackageInfo.java?view=auto&rev=465957
==============================================================================
--- 
shale/framework/trunk/shale-tiger/src/main/java/org/apache/shale/tiger/view/faces/PackageInfo.java
 (added)
+++ 
shale/framework/trunk/shale-tiger/src/main/java/org/apache/shale/tiger/view/faces/PackageInfo.java
 Thu Oct 19 19:59:37 2006
@@ -0,0 +1,243 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you 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.shale.tiger.view.faces;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.net.JarURLConnection;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * <p>Utility class with methods that support getting a recursive list of
+ * classes starting with a specific package name.</p>
+ */
+public final class PackageInfo {
+
+    /**
+     * <p>The <code>Log</code> instance we will be using.</p>
+     */
+    private transient Log log = null;
+
+
+    /**
+     * the singleton for this class
+     */
+    private final static PackageInfo INSTANCE = new PackageInfo();
+
+
+    /**
+     * <p>Get the singleton instance of this class.</p>
+     */
+    public final static PackageInfo getInstance() {
+
+        return INSTANCE;
+
+    }
+
+
+    /**
+     * <p>Return an array of all classes, visible to our application class 
loader,
+     * in the specified Java package.</p>
+     *
+     * @param classes List of matching classes being accumulated
+     * @param pckgname Package name used to select matching classes
+     *
+     * @throws ClassNotFoundException
+     */
+    public Class[] getClasses(final List<Class> classes, final String pckgname)
+      throws ClassNotFoundException {
+
+        Enumeration resources;
+        ClassLoader cld;
+        String path;
+        try {
+
+            // convert the package name to a path
+            path = pckgname.replace('.', '/');
+
+            cld = Thread.currentThread().getContextClassLoader();
+            if (cld == null) {
+                throw new ClassNotFoundException("Can't get class loader.");
+            }
+
+            // find the entry points to the classpath
+            resources = cld.getResources(path);
+            if (resources == null || !resources.hasMoreElements()) {
+                throw new ClassNotFoundException("No resource for " + path);
+            }
+
+        } catch (NullPointerException e) {
+            throw(ClassNotFoundException) new ClassNotFoundException(pckgname 
+ " (" + pckgname
+                    + ") does not appear to be a valid package", e);
+        } catch (IOException e) {
+            throw(ClassNotFoundException) new ClassNotFoundException(pckgname 
+ " (" + pckgname
+                    + ") does not appear to be a valid package", e);
+        }
+
+        // iterate through all resources containing the package in question
+        while (resources.hasMoreElements()) {
+            URL resource = (URL) resources.nextElement();
+            URLConnection connection = null;
+            try {
+                connection = resource.openConnection();
+            } catch (IOException e) {
+                throw(ClassNotFoundException) new 
ClassNotFoundException(pckgname + " (" + pckgname
+                        + ") does not appear to be a valid package", e);
+            }
+
+            if (connection instanceof JarURLConnection) {
+                // iterate trhough all the entries in the jar
+                JarURLConnection juc = (JarURLConnection) connection;
+                JarFile jarFile = null;
+                try {
+                    jarFile = juc.getJarFile();
+                } catch (IOException e) {
+                    throw(ClassNotFoundException) new 
ClassNotFoundException(pckgname + " (" + pckgname
+                            + ") does not appear to be a valid package", e);
+                }
+                Enumeration<JarEntry> entries = jarFile.entries();
+                while (entries.hasMoreElements()) {
+                    JarEntry jarEntry = entries.nextElement();
+                    String entryName = jarEntry.getName();
+                    if (!entryName.startsWith(path)) {
+                        continue;
+                    }
+                    if (!entryName.toLowerCase().endsWith(".class")) {
+                        continue;
+                    }
+                    String className = filenameToClassname(entryName);
+                    loadClass(classes, cld, className);
+                }
+            } else {
+                // iterate trhough all the children starting with the package 
name
+                File file;
+                try {
+                    file = new File(connection.getURL().toURI());
+                } catch (URISyntaxException e) {
+                    log().warn("error loading directory " + connection, e);
+                    continue;
+                }
+
+                listFilesRecursive(classes, file, cld, pckgname);
+            }
+        }
+
+        if (classes.size() < 1) {
+            throw new ClassNotFoundException(pckgname
+                    + " does not appear to be a valid package");
+        }
+
+        Class[] resolvedClasses = new Class[classes.size()];
+        classes.toArray(resolvedClasses);
+        return resolvedClasses;
+
+    }
+
+
+    /**
+     * <p>Convert a filename to a classname.</p>
+     *
+     * @param entryName Filename to be converted
+     */
+    protected String filenameToClassname(String entryName) {
+
+        return entryName.substring(0, entryName.length() - 6).replace('/', 
'.');
+
+    }
+
+
+    /**
+     * <p>Load the class <code>className</code> using the classloader
+     * <code>cld</code>, and add it to the list.</p>
+     *
+     * @param classes List of matching classes being accumulated
+     * @param cld ClassLoader from which to load the specified class
+     * @param className Name of the class to be loaded
+     */
+    protected void loadClass(List<Class> classes, ClassLoader cld, String 
className) {
+
+        try {
+            classes.add(cld.loadClass(className));
+        }
+        catch (NoClassDefFoundError e) {
+            log().warn("error loading class " + className, e);
+        } catch (ClassNotFoundException e) {
+            log().warn("error loading class " + className, e);
+        }
+
+    }
+
+
+    /**
+     * <p>Traverse a directory structure starting at <code>base</code>, adding
+     * matching files to the specified list.</p>
+     *
+     * @param classes List of matching classes being accumulated
+     * @param base Base file from which to recurse
+     * @param cld ClassLoader being searched for matching classes
+     * @param pckgname Package name used to select matching classes
+     */
+    protected void listFilesRecursive(final List<Class> classes, final File 
base,
+            final ClassLoader cld, final String pckgname) {
+
+        base.listFiles(new FileFilter() {
+
+            public boolean accept(File file) {
+                if (file.isDirectory()) {
+                    listFilesRecursive(classes, file, cld, pckgname + "." + 
file.getName());
+                    return false;
+                }
+                if (!file.getName().toLowerCase().endsWith(".class")) {
+                    return false;
+                }
+
+                String className = filenameToClassname(pckgname + "." + 
file.getName());
+                loadClass(classes, cld, className);
+
+                return false;
+            }
+
+        });
+
+    }
+
+
+    /**
+     * <p>Return the <code>Log</code> instance to be used for this class,
+     * instantiating a new one if necessary.</p>
+     */
+    private Log log() {
+
+        if (log == null) {
+            log = LogFactory.getLog(PackageInfo.class);
+        }
+        return log;
+
+    }
+
+
+}

Propchange: 
shale/framework/trunk/shale-tiger/src/main/java/org/apache/shale/tiger/view/faces/PackageInfo.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
shale/framework/trunk/shale-tiger/src/main/java/org/apache/shale/tiger/view/faces/PackageInfo.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL


Reply via email to