Revision: 1331
http://stripes.svn.sourceforge.net/stripes/?rev=1331&view=rev
Author: bengunter
Date: 2010-11-11 22:03:55 +0000 (Thu, 11 Nov 2010)
Log Message:
-----------
Fixed STS-776, STS-773 and STS-775: class discovery does not work on JBoss 6.
Due to changes in the JBoss' URL handlers directory resources no longer can be
read to get the list of child resources. Having no other choice, I am now
invoking the JBoss 6 VFS API. To avoid a compile-time dependency, all calls are
made via reflection. I added an abstract VFS class that can support other
server-specific APIs as required in the future.
Modified Paths:
--------------
branches/1.5.x/stripes/src/net/sourceforge/stripes/util/ResolverUtil.java
Added Paths:
-----------
branches/1.5.x/stripes/src/net/sourceforge/stripes/vfs/
branches/1.5.x/stripes/src/net/sourceforge/stripes/vfs/DefaultVFS.java
branches/1.5.x/stripes/src/net/sourceforge/stripes/vfs/JBoss6VFS.java
branches/1.5.x/stripes/src/net/sourceforge/stripes/vfs/VFS.java
branches/1.5.x/stripes/src/net/sourceforge/stripes/vfs/package.html
Modified:
branches/1.5.x/stripes/src/net/sourceforge/stripes/util/ResolverUtil.java
===================================================================
--- branches/1.5.x/stripes/src/net/sourceforge/stripes/util/ResolverUtil.java
2010-11-11 13:38:41 UTC (rev 1330)
+++ branches/1.5.x/stripes/src/net/sourceforge/stripes/util/ResolverUtil.java
2010-11-11 22:03:55 UTC (rev 1331)
@@ -14,26 +14,14 @@
*/
package net.sourceforge.stripes.util;
-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.lang.annotation.Annotation;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-import java.util.jar.JarEntry;
-import java.util.jar.JarInputStream;
-import java.util.regex.Pattern;
+import net.sourceforge.stripes.vfs.VFS;
+
/**
* <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
@@ -70,13 +58,6 @@
/** An instance of Log to use for logging in this class. */
private static final Log log = Log.getInstance(ResolverUtil.class);
- /** The magic header that indicates a JAR (ZIP) file. */
- private static final byte[] JAR_MAGIC = { 'P', 'K', 3, 4 };
-
- /** Regular expression that matches a Java identifier. */
- private static final Pattern JAVA_IDENTIFIER_PATTERN = Pattern
-
.compile("\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*");
-
/**
* A simple interface that specifies how to test classes to determine if
they
* are to be included in the results produced by the ResolverUtil.
@@ -100,8 +81,7 @@
public IsA(Class<?> parentType) { this.parent = parentType; }
/** Returns true if type is assignable to the parent type supplied in
the constructor. */
- @SuppressWarnings("unchecked")
- public boolean matches(Class type) {
+ public boolean matches(Class<?> type) {
return type != null && parent.isAssignableFrom(type);
}
@@ -121,8 +101,7 @@
public AnnotatedWith(Class<? extends Annotation> annotation) {
this.annotation = annotation; }
/** Returns true if the type is annotated with the class provided to
the constructor. */
- @SuppressWarnings("unchecked")
- public boolean matches(Class type) {
+ public boolean matches(Class<?> type) {
return type != null && type.isAnnotationPresent(annotation);
}
@@ -220,12 +199,10 @@
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) {
+ List<String> children = VFS.getInstance().list(path);
+ for (String child : children) {
+ if (child.endsWith(".class"))
addIfMatching(test, child);
- }
}
}
catch (IOException ioe) {
@@ -236,218 +213,6 @@
}
/**
- * 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 {
- log.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) {
- is = jarUrl.openStream();
- resources = listClassResources(new JarInputStream(is), path);
- }
- else {
- List<String> children = new ArrayList<String>();
- try {
- if (isJar(url)) {
- // 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;) {
- log.trace("Jar entry: " + entry.getName());
- if (isRelevantResource(entry.getName())) {
- children.add(entry.getName());
- }
- }
- }
- else {
- // Some servlet containers allow reading from
"directory" resources like a
- // text file, listing the child resources one per line.
- is = url.openStream();
- BufferedReader reader = new BufferedReader(new
InputStreamReader(is));
- for (String line; (line = reader.readLine()) != null;)
{
- log.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());
- log.trace("Listing directory ",
file.getAbsolutePath());
- 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 rethrow it
- throw e;
- }
- }
-
- // 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.endsWith(".class")) {
- log.trace("Found class file: ", 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 file name
- if (name.endsWith(".class") && name.startsWith(path)) {
- log.trace("Found class file: ", name);
- 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 {
- log.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());
- log.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);
- log.trace("Extracted JAR URL: ", jarUrl);
- }
- else {
- log.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.
- log.trace("Not a JAR: ", jarUrl);
- jarUrl.replace(0, jarUrl.length(), testUrl.getFile());
- File file = new File(jarUrl.toString());
-
- // File name might be URL-encoded
- if (!file.exists()) {
- file = new File(StringUtil.urlDecode(jarUrl.toString()));
- }
-
- if (file.exists()) {
- log.trace("Trying real file: ", file.getAbsolutePath());
- testUrl = file.toURI().toURL();
- if (isJar(testUrl)) {
- return testUrl;
- }
- }
- }
- }
- catch (MalformedURLException e) {
- log.warn("Invalid JAR URL: ", jarUrl);
- }
-
- log.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)}.
*
@@ -458,60 +223,6 @@
}
/**
- * 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.endsWith(".class") || JAVA_IDENTIFIER_PATTERN
- .matcher(resourceName).matches());
- }
-
- /**
- * 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)) {
- log.debug("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.
*
@@ -525,7 +236,7 @@
ClassLoader loader = getClassLoader();
log.trace("Checking to see if class ", externalName, " matches
criteria [", test, "]");
- Class type = loader.loadClass(externalName);
+ Class<?> type = loader.loadClass(externalName);
if (test.matches(type) ) {
matches.add( (Class<T>) type);
}
Added: branches/1.5.x/stripes/src/net/sourceforge/stripes/vfs/DefaultVFS.java
===================================================================
--- branches/1.5.x/stripes/src/net/sourceforge/stripes/vfs/DefaultVFS.java
(rev 0)
+++ branches/1.5.x/stripes/src/net/sourceforge/stripes/vfs/DefaultVFS.java
2010-11-11 22:03:55 UTC (rev 1331)
@@ -0,0 +1,301 @@
+/* Copyright 2010 Ben Gunter
+ *
+ * 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 net.sourceforge.stripes.vfs;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+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.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarInputStream;
+
+import net.sourceforge.stripes.util.Log;
+import net.sourceforge.stripes.util.StringUtil;
+
+/**
+ * A default implementation of {...@link VFS} that works for most application
servers.
+ *
+ * @author Ben Gunter
+ */
+public class DefaultVFS extends VFS {
+ private Log log = Log.getInstance(DefaultVFS.class);
+
+ /** The magic header that indicates a JAR (ZIP) file. */
+ private static final byte[] JAR_MAGIC = { 'P', 'K', 3, 4 };
+
+ @Override
+ public boolean isValid() {
+ return true;
+ }
+
+ @Override
+ public List<String> list(URL url, String path) throws IOException {
+ log.debug("Listing ", 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);
+ if (jarUrl != null) {
+ is = jarUrl.openStream();
+ resources = listResources(new JarInputStream(is), path);
+ }
+ else {
+ List<String> children = new ArrayList<String>();
+ try {
+ if (isJar(url)) {
+ // 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;) {
+ log.trace("Jar entry: ", entry.getName());
+ children.add(entry.getName());
+ }
+ }
+ else {
+ /*
+ * Some servlet containers allow reading from
directory resources like a
+ * text file, listing the child resources one per
line. However, there is no
+ * way to differentiate between directory and file
resources just by reading
+ * them. To work around that, as each line is read,
try to look it up via
+ * the class loader as a child of the current
resource. If any line fails
+ * then we assume the current resource is not a
directory.
+ */
+ is = url.openStream();
+ BufferedReader reader = new BufferedReader(new
InputStreamReader(is));
+ List<String> lines = new ArrayList<String>();
+ for (String line; (line = reader.readLine()) != null;)
{
+ log.trace("Reader entry: ", line);
+ lines.add(line);
+ if (getResources(path + "/" + line).isEmpty()) {
+ lines.clear();
+ break;
+ }
+ }
+ children.addAll(lines);
+ }
+ }
+ 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());
+ log.trace("Listing directory ",
file.getAbsolutePath());
+ if (file.isDirectory()) {
+ children = Arrays.asList(file.list());
+ }
+ }
+ else {
+ // No idea where the exception came from so rethrow it
+ throw e;
+ }
+ }
+
+ // The URL prefix to use when recursively listing child
resources
+ String prefix = url.toExternalForm();
+ if (!prefix.endsWith("/"))
+ prefix = prefix + "/";
+
+ // Iterate over immediate children, adding files and recursing
into directories
+ for (String child : children) {
+ String resourcePath = path + "/" + child;
+ resources.add(resourcePath);
+ URL childUrl = new URL(prefix + child);
+ resources.addAll(list(childUrl, resourcePath));
+ }
+ }
+
+ return resources;
+ }
+ finally {
+ try {
+ if (is != null)
+ 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 If I/O errors occur
+ */
+ protected List<String> listResources(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 file name
+ if (name.startsWith(path)) {
+ log.trace("Found resource: ", name);
+ 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.
+ * @return The URL of the JAR file, if one is found. Null if not.
+ * @throws MalformedURLException
+ */
+ protected URL findJarForResource(URL url) throws MalformedURLException {
+ log.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());
+ log.trace("Inner URL: ", url);
+ }
+ }
+ catch (MalformedURLException e) {
+ // This will happen at some point and serves as 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);
+ log.trace("Extracted JAR URL: ", jarUrl);
+ }
+ else {
+ log.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.
+ log.trace("Not a JAR: ", jarUrl);
+ jarUrl.replace(0, jarUrl.length(), testUrl.getFile());
+ File file = new File(jarUrl.toString());
+
+ // File name might be URL-encoded
+ if (!file.exists()) {
+ file = new File(StringUtil.urlDecode(jarUrl.toString()));
+ }
+
+ if (file.exists()) {
+ log.trace("Trying real file: ", file.getAbsolutePath());
+ testUrl = file.toURI().toURL();
+ if (isJar(testUrl)) {
+ return testUrl;
+ }
+ }
+ }
+ }
+ catch (MalformedURLException e) {
+ log.warn("Invalid JAR URL: ", jarUrl);
+ }
+
+ log.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 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)) {
+ log.debug("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;
+ }
+}
Added: branches/1.5.x/stripes/src/net/sourceforge/stripes/vfs/JBoss6VFS.java
===================================================================
--- branches/1.5.x/stripes/src/net/sourceforge/stripes/vfs/JBoss6VFS.java
(rev 0)
+++ branches/1.5.x/stripes/src/net/sourceforge/stripes/vfs/JBoss6VFS.java
2010-11-11 22:03:55 UTC (rev 1331)
@@ -0,0 +1,169 @@
+/* Copyright 2010 Ben Gunter
+ *
+ * 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 net.sourceforge.stripes.vfs;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import net.sourceforge.stripes.util.Log;
+
+/**
+ * A {...@link VFS} implementation that works with the VFS API provided by
JBoss 6.
+ *
+ * @author Ben Gunter
+ */
+public class JBoss6VFS extends net.sourceforge.stripes.vfs.VFS {
+ private static final Log log = Log.getInstance(JBoss6VFS.class);
+
+ /** A class that mimics a tiny subset of the JBoss VirtualFile class. */
+ static class VirtualFile {
+ static Class<?> VirtualFile;
+ static Method getPathNameRelativeTo, getChildrenRecursively;
+
+ Object virtualFile;
+
+ VirtualFile(Object virtualFile) {
+ this.virtualFile = virtualFile;
+ }
+
+ String getPathNameRelativeTo(VirtualFile parent) {
+ try {
+ return invoke(getPathNameRelativeTo, virtualFile,
parent.virtualFile);
+ }
+ catch (IOException e) {
+ // This exception is not thrown by the called method
+ log.error("This should not be possible.
VirtualFile.getPathNameRelativeTo() threw IOException.");
+ return null;
+ }
+ }
+
+ List<VirtualFile> getChildren() throws IOException {
+ List<?> objects = invoke(getChildrenRecursively, virtualFile);
+ List<VirtualFile> children = new
ArrayList<VirtualFile>(objects.size());
+ for (Object object : objects) {
+ children.add(new VirtualFile(object));
+ }
+ return children;
+ }
+ }
+
+ /** A class that mimics a tiny subset of the JBoss VFS class. */
+ static class VFS {
+ static Class<?> VFS;
+ static Method getChild;
+
+ static VirtualFile getChild(URL url) throws IOException {
+ Object o = invoke(getChild, VFS, url);
+ return o == null ? null : new VirtualFile(o);
+ }
+ }
+
+ /** Flag that indicates if this VFS is valid for the current environment.
*/
+ private static Boolean valid;
+
+ /** Find all the classes and methods that are required to access the JBoss
6 VFS. */
+ protected static synchronized void initialize() {
+ if (valid == null) {
+ // Assume valid. It will get flipped later if something goes wrong.
+ valid = true;
+
+ // Look up and verify required classes
+ VFS.VFS = checkNotNull(getClass("org.jboss.vfs.VFS"));
+ VirtualFile.VirtualFile =
checkNotNull(getClass("org.jboss.vfs.VirtualFile"));
+
+ // Look up and verify required methods
+ VFS.getChild = checkNotNull(getMethod(VFS.VFS, "getChild",
URL.class));
+ VirtualFile.getChildrenRecursively =
checkNotNull(getMethod(VirtualFile.VirtualFile,
+ "getChildrenRecursively"));
+ VirtualFile.getPathNameRelativeTo =
checkNotNull(getMethod(VirtualFile.VirtualFile,
+ "getPathNameRelativeTo", VirtualFile.VirtualFile));
+
+ // Verify that the API has not changed
+ checkReturnType(VFS.getChild, VirtualFile.VirtualFile);
+ checkReturnType(VirtualFile.getChildrenRecursively, List.class);
+ checkReturnType(VirtualFile.getPathNameRelativeTo, String.class);
+ }
+ }
+
+ /**
+ * Verifies that the provided object reference is null. If it is null,
then this VFS is marked
+ * as invalid for the current environment.
+ *
+ * @param objects The object references to check for null.
+ */
+ protected static <T> T checkNotNull(T object) {
+ if (object == null)
+ setInvalid();
+ return object;
+ }
+
+ /**
+ * Verifies that the return type of a method is what it is expected to be.
If it is not, then
+ * this VFS is marked as invalid for the current environment.
+ *
+ * @param method The method whose return type is to be checked.
+ * @param expected A type to which the method's return type must be
assignable.
+ * @see Class#isAssignableFrom(Class)
+ */
+ protected static void checkReturnType(Method method, Class<?> expected) {
+ if (method != null &&
!expected.isAssignableFrom(method.getReturnType())) {
+ log.warn("Method ", method.getClass().getName(), ".",
method.getName(),
+ "(..) should return ", expected.getName(), " but returns
", //
+ method.getReturnType().getName(), " instead.");
+ setInvalid();
+ }
+ }
+
+ /** Mark this {...@link VFS} as invalid for the current environment. */
+ protected static void setInvalid() {
+ if (JBoss6VFS.valid != null && JBoss6VFS.valid) {
+ log.debug("JBoss 6 VFS API is not available in this environment.");
+ JBoss6VFS.valid = false;
+ }
+ }
+
+ static {
+ initialize();
+ }
+
+ @Override
+ public boolean isValid() {
+ return valid;
+ }
+
+ @Override
+ public List<String> list(URL url, String path) throws IOException {
+ VirtualFile directory;
+ directory = VFS.getChild(url);
+ if (directory == null)
+ return Collections.emptyList();
+
+ if (!path.endsWith("/"))
+ path += "/";
+
+ List<VirtualFile> children = directory.getChildren();
+ List<String> names = new ArrayList<String>(children.size());
+ for (VirtualFile vf : children) {
+ String relative = vf.getPathNameRelativeTo(directory);
+ names.add(path + relative);
+ }
+
+ return names;
+ }
+}
Added: branches/1.5.x/stripes/src/net/sourceforge/stripes/vfs/VFS.java
===================================================================
--- branches/1.5.x/stripes/src/net/sourceforge/stripes/vfs/VFS.java
(rev 0)
+++ branches/1.5.x/stripes/src/net/sourceforge/stripes/vfs/VFS.java
2010-11-11 22:03:55 UTC (rev 1331)
@@ -0,0 +1,165 @@
+/* Copyright 2010 Ben Gunter
+ *
+ * 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 net.sourceforge.stripes.vfs;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import net.sourceforge.stripes.exception.StripesRuntimeException;
+import net.sourceforge.stripes.util.Log;
+
+/**
+ * Provides a very simple API for accessing resources within an application
server.
+ *
+ * @author Ben Gunter
+ */
+public abstract class VFS {
+ private static final Log log = Log.getInstance(VFS.class);
+ private static VFS instance;
+
+ /**
+ * Get the singleton {...@link VFS} instance. If no {...@link VFS}
implementation can be found for the
+ * current environment, then this method returns null.
+ *
+ * @return
+ */
+ public static VFS getInstance() {
+ if (instance != null)
+ return instance;
+
+ // Try JBoss 6 first
+ VFS vfs = new JBoss6VFS();
+
+ // Fall back to default
+ if (!vfs.isValid())
+ vfs = new DefaultVFS();
+
+ log.info("Using VFS adapter ", vfs.getClass().getName());
+ return VFS.instance = vfs;
+ }
+
+ /** Get a class by name. If the class is not found then return null. */
+ protected static Class<?> getClass(String className) {
+ try {
+ return
Thread.currentThread().getContextClassLoader().loadClass(className);
+ }
+ catch (ClassNotFoundException e) {
+ log.debug("Class not found: ", className);
+ return null;
+ }
+ }
+
+ /**
+ * Get a method by name and parameter types. If the method is not found
then return null.
+ *
+ * @param clazz The class to which the method belongs.
+ * @param methodName The name of the method.
+ * @param parameterTypes The types of the parameters accepted by the
method.
+ */
+ protected static Method getMethod(Class<?> clazz, String methodName,
Class<?>... parameterTypes) {
+ try {
+ if (clazz == null)
+ return null;
+ else
+ return clazz.getMethod(methodName, parameterTypes);
+ }
+ catch (SecurityException e) {
+ log.debug(e, "Security exception looking for method ",
clazz.getName(), ".", methodName);
+ return null;
+ }
+ catch (NoSuchMethodException e) {
+ log.debug(e, "Method not found ", clazz.getName(), ".",
methodName);
+ return null;
+ }
+ }
+
+ /**
+ * Invoke a method on an object and return whatever it returns.
+ *
+ * @param method The method to invoke.
+ * @param object The instance or class (for static methods) on which to
invoke the method.
+ * @param parameters The parameters to pass to the method.
+ * @return Whatever the method returns.
+ * @throws IOException If I/O errors occur
+ * @throws StripesRuntimeException If anything else goes wrong
+ */
+ @SuppressWarnings("unchecked")
+ protected static <T> T invoke(Method method, Object object, Object...
parameters)
+ throws IOException, StripesRuntimeException {
+ try {
+ return (T) method.invoke(object, parameters);
+ }
+ catch (IllegalArgumentException e) {
+ throw new StripesRuntimeException(e);
+ }
+ catch (IllegalAccessException e) {
+ throw new StripesRuntimeException(e);
+ }
+ catch (InvocationTargetException e) {
+ if (e.getTargetException() instanceof IOException)
+ throw (IOException) e.getTargetException();
+ else
+ throw new StripesRuntimeException(e);
+ }
+ }
+
+ /**
+ * Get a list of {...@link URL}s from the context classloader for all the
resources found at the
+ * specified path.
+ *
+ * @param path The resource path.
+ * @return A list of {...@link URL}s, as returned by {...@link
ClassLoader#getResources(String)}.
+ * @throws IOException If I/O errors occur
+ */
+ protected static List<URL> getResources(String path) throws IOException {
+ return
Collections.list(Thread.currentThread().getContextClassLoader().getResources(path));
+ }
+
+ /** Return true if the {...@link VFS} implementation is valid for the
current environment. */
+ public abstract boolean isValid();
+
+ /**
+ * Recursively list the full resource path of all the resources that are
children of the
+ * resource identified by a URL.
+ *
+ * @param url The URL that identifies the resource to list.
+ * @param forPath The path to the resource that is identified by the URL.
Generally, this is the
+ * value passed to {...@link #getResources(String)} to get the
resource URL.
+ * @return A list containing the names of the child resources.
+ * @throws IOException If I/O errors occur
+ */
+ protected abstract List<String> list(URL url, String forPath) throws
IOException;
+
+ /**
+ * Recursively list the full resource path of all the resources that are
children of all the
+ * resources found at the specified path.
+ *
+ * @param path The path of the resource(s) to list.
+ * @return A list containing the names of the child resources.
+ * @throws IOException If I/O errors occur
+ */
+ public List<String> list(String path) throws IOException {
+ List<String> names = new ArrayList<String>();
+ for (URL url : getResources(path)) {
+ names.addAll(list(url, path));
+ }
+ return names;
+ }
+}
Added: branches/1.5.x/stripes/src/net/sourceforge/stripes/vfs/package.html
===================================================================
--- branches/1.5.x/stripes/src/net/sourceforge/stripes/vfs/package.html
(rev 0)
+++ branches/1.5.x/stripes/src/net/sourceforge/stripes/vfs/package.html
2010-11-11 22:03:55 UTC (rev 1331)
@@ -0,0 +1,3 @@
+<body>
+<p>Contains classes that facilitate discovery of resources via the class
loader.</p>
+</body>
\ No newline at end of file
This was sent by the SourceForge.net collaborative development platform, the
world's largest Open Source development site.
------------------------------------------------------------------------------
Centralized Desktop Delivery: Dell and VMware Reference Architecture
Simplifying enterprise desktop deployment and management using
Dell EqualLogic storage and VMware View: A highly scalable, end-to-end
client virtualization framework. Read more!
http://p.sf.net/sfu/dell-eql-dev2dev
_______________________________________________
Stripes-development mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/stripes-development