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