Revision: 1336
http://stripes.svn.sourceforge.net/stripes/?rev=1336&view=rev
Author: bengunter
Date: 2010-11-12 16:25:10 +0000 (Fri, 12 Nov 2010)
Log Message:
-----------
Applied fix for STS-776, STS-773 and STS-775 from 1.5.x branch.
Modified Paths:
--------------
trunk/stripes/src/net/sourceforge/stripes/config/BootstrapPropertyResolver.java
trunk/stripes/src/net/sourceforge/stripes/util/ResolverUtil.java
Added Paths:
-----------
trunk/stripes/src/net/sourceforge/stripes/vfs/
trunk/stripes/src/net/sourceforge/stripes/vfs/DefaultVFS.java
trunk/stripes/src/net/sourceforge/stripes/vfs/JBoss6VFS.java
trunk/stripes/src/net/sourceforge/stripes/vfs/VFS.java
trunk/stripes/src/net/sourceforge/stripes/vfs/package.html
Modified:
trunk/stripes/src/net/sourceforge/stripes/config/BootstrapPropertyResolver.java
===================================================================
---
trunk/stripes/src/net/sourceforge/stripes/config/BootstrapPropertyResolver.java
2010-11-12 15:50:31 UTC (rev 1335)
+++
trunk/stripes/src/net/sourceforge/stripes/config/BootstrapPropertyResolver.java
2010-11-12 16:25:10 UTC (rev 1336)
@@ -28,6 +28,7 @@
import net.sourceforge.stripes.util.ReflectUtil;
import net.sourceforge.stripes.util.ResolverUtil;
import net.sourceforge.stripes.util.StringUtil;
+import net.sourceforge.stripes.vfs.VFS;
/**
* <p>Resolves configuration properties that are used to bootstrap the system.
Essentially this boils
@@ -50,12 +51,16 @@
private FilterConfig filterConfig;
+ /** The Configuration Key for looking up the comma separated list of VFS
classes. */
+ public static final String VFS_CLASSES = "VFS.Classes";
+
/** The Configuration Key for looking up the comma separated list of
extension packages. */
public static final String PACKAGES = "Extension.Packages";
/** Constructs a new BootstrapPropertyResolver with the given
ServletConfig. */
public BootstrapPropertyResolver(FilterConfig filterConfig) {
setFilterConfig(filterConfig);
+ initVFS();
}
/** Stores a reference to the filter's FilterConfig object. */
@@ -68,6 +73,18 @@
return this.filterConfig;
}
+ /** Add {...@link VFS} implementations that are specified in the filter
configuration. */
+ @SuppressWarnings("unchecked")
+ protected void initVFS() {
+ List<Class<?>> vfsImpls = getClassPropertyList(VFS_CLASSES);
+ for (Class<?> clazz : vfsImpls) {
+ if (!VFS.class.isAssignableFrom(clazz))
+ log.warn("Class ", clazz.getName(), " does not extend ",
VFS.class.getName());
+ else
+ VFS.addImplClass((Class<? extends VFS>) clazz);
+ }
+ }
+
/**
* Fetches a configuration property in the manner described in the class
level javadoc for
* this class.
Modified: trunk/stripes/src/net/sourceforge/stripes/util/ResolverUtil.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/util/ResolverUtil.java
2010-11-12 15:50:31 UTC (rev 1335)
+++ trunk/stripes/src/net/sourceforge/stripes/util/ResolverUtil.java
2010-11-12 16:25:10 UTC (rev 1336)
@@ -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: trunk/stripes/src/net/sourceforge/stripes/vfs/DefaultVFS.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/vfs/DefaultVFS.java
(rev 0)
+++ trunk/stripes/src/net/sourceforge/stripes/vfs/DefaultVFS.java
2010-11-12 16:25:10 UTC (rev 1336)
@@ -0,0 +1,306 @@
+/* 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 {
+ 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();
+ log.debug("Listing ", url);
+ 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);
+ log.debug("Listing ", url);
+ 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;
+ }
+ }
+
+ if (!lines.isEmpty()) {
+ log.debug("Listing ", url);
+ 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()) {
+ log.debug("Listing ", url);
+ 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: trunk/stripes/src/net/sourceforge/stripes/vfs/JBoss6VFS.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/vfs/JBoss6VFS.java
(rev 0)
+++ trunk/stripes/src/net/sourceforge/stripes/vfs/JBoss6VFS.java
2010-11-12 16:25:10 UTC (rev 1336)
@@ -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: trunk/stripes/src/net/sourceforge/stripes/vfs/VFS.java
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/vfs/VFS.java
(rev 0)
+++ trunk/stripes/src/net/sourceforge/stripes/vfs/VFS.java 2010-11-12
16:25:10 UTC (rev 1336)
@@ -0,0 +1,206 @@
+/* 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.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import net.sourceforge.stripes.exception.StripesRuntimeException;
+import net.sourceforge.stripes.util.Log;
+import net.sourceforge.stripes.util.ReflectUtil;
+
+/**
+ * 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);
+
+ /** The built-in implementations. */
+ public static final Class<?>[] IMPLEMENTATIONS = { JBoss6VFS.class,
DefaultVFS.class };
+
+ /** The list to which implementations are added by {...@link
#addImplClass(String)}. */
+ public static final List<Class<? extends VFS>> USER_IMPLEMENTATIONS = new
ArrayList<Class<? extends VFS>>();
+
+ /** Singleton instance. */
+ 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
+ */
+ @SuppressWarnings("unchecked")
+ public static VFS getInstance() {
+ if (instance != null)
+ return instance;
+
+ // Try the user implementations first, then the built-ins
+ List<Class<? extends VFS>> impls = new ArrayList<Class<? extends
VFS>>();
+ impls.addAll(USER_IMPLEMENTATIONS);
+ impls.addAll(Arrays.asList((Class<? extends VFS>[]) IMPLEMENTATIONS));
+
+ // Try each implementation class until a valid one is found
+ VFS vfs = null;
+ for (int i = 0; vfs == null || !vfs.isValid(); i++) {
+ Class<? extends VFS> impl = impls.get(i);
+ try {
+ vfs = impl.newInstance();
+ if (vfs == null || !vfs.isValid()) {
+ log.debug("VFS implementation ", impl.getName(),
+ " is not valid in this environment.");
+ }
+ }
+ catch (InstantiationException e) {
+ log.error(e, "Failed to instantiate ", impl);
+ return null;
+ }
+ catch (IllegalAccessException e) {
+ log.error(e, "Failed to instantiate ", impl);
+ return null;
+ }
+ }
+
+ log.info("Using VFS adapter ", vfs.getClass().getName());
+ return VFS.instance = vfs;
+ }
+
+ /**
+ * Adds the specified class to the list of {...@link VFS} implementations.
Classes added in this
+ * manner are tried in the order they are added and before any of the
built-in implementations.
+ *
+ * @param className The name of the {...@link VFS} implementation class to
add.
+ */
+ public static void addImplClass(Class<? extends VFS> clazz) {
+ if (clazz != null)
+ USER_IMPLEMENTATIONS.add(clazz);
+ }
+
+ /** Get a class by name. If the class is not found then return null. */
+ protected static Class<?> getClass(String className) {
+ try {
+ return ReflectUtil.findClass(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: trunk/stripes/src/net/sourceforge/stripes/vfs/package.html
===================================================================
--- trunk/stripes/src/net/sourceforge/stripes/vfs/package.html
(rev 0)
+++ trunk/stripes/src/net/sourceforge/stripes/vfs/package.html 2010-11-12
16:25:10 UTC (rev 1336)
@@ -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