You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tamaya.apache.org by an...@apache.org on 2015/01/16 15:06:38 UTC

incubator-tamaya git commit: TAMAYA-43: Added support for JBoss VFS.

Repository: incubator-tamaya
Updated Branches:
  refs/heads/master 987c5a1d9 -> af21ac43e


TAMAYA-43: Added support for JBoss VFS.


Project: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/commit/af21ac43
Tree: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/tree/af21ac43
Diff: http://git-wip-us.apache.org/repos/asf/incubator-tamaya/diff/af21ac43

Branch: refs/heads/master
Commit: af21ac43e7c00aeabc5a2ba617c5ca8737b1a290
Parents: 987c5a1
Author: anatole <an...@apache.org>
Authored: Fri Jan 16 15:06:13 2015 +0100
Committer: anatole <an...@apache.org>
Committed: Fri Jan 16 15:06:30 2015 +0100

----------------------------------------------------------------------
 .../resource/internal/ClasspathCollector.java   | 115 ++++++++-
 .../tamaya/resource/internal/VfsSupport.java    | 252 +++++++++++++++++++
 2 files changed, 365 insertions(+), 2 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/af21ac43/modules/resources/src/main/java/org/apache/tamaya/resource/internal/ClasspathCollector.java
----------------------------------------------------------------------
diff --git a/modules/resources/src/main/java/org/apache/tamaya/resource/internal/ClasspathCollector.java b/modules/resources/src/main/java/org/apache/tamaya/resource/internal/ClasspathCollector.java
index 8688ce8..c47bd10 100644
--- a/modules/resources/src/main/java/org/apache/tamaya/resource/internal/ClasspathCollector.java
+++ b/modules/resources/src/main/java/org/apache/tamaya/resource/internal/ClasspathCollector.java
@@ -20,6 +20,8 @@ package org.apache.tamaya.resource.internal;
 
 import java.io.File;
 import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
 import java.net.JarURLConnection;
 import java.net.MalformedURLException;
 import java.net.URISyntaxException;
@@ -28,6 +30,7 @@ import java.net.URLConnection;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Enumeration;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Objects;
 import java.util.jar.JarEntry;
@@ -121,6 +124,8 @@ public class ClasspathCollector {
                 try {
                     if (isJarFile(resource)) {
                         result.addAll(doFindPathMatchingJarResources(resource, locator.getSubPath()));
+                    } else if (resource.getProtocol().startsWith(PROTOCOL_VFSZIP)) {
+                        result.addAll(findMatchingVfsResources(resource, locator.getSubPath()));
                     } else {
                         result.addAll(FileCollector.traverseAndSelectFromChildren(getFile(resource),
                                 locator.getSubPathTokens(), 0));
@@ -194,7 +199,7 @@ public class ClasspathCollector {
                 String entryPath = entry.getName();
                 if (entryPath.startsWith(rootEntryPath)) {
                     String relativePath = entryPath.substring(rootEntryPath.length());
-                    if(relativePath.contains("/") && isFileExpression){
+                    if (relativePath.contains("/") && isFileExpression) {
                         continue;
                     }
                     if (relativePath.matches(subPattern)) {
@@ -245,7 +250,7 @@ public class ClasspathCollector {
                 PROTOCOL_ZIP.equals(protocol) ||
                 PROTOCOL_VFSZIP.equals(protocol) ||
                 PROTOCOL_WSJAR.equals(protocol) ||
-                (PROTOCOL_CODE_SOURCE.equals(protocol) && url.getPath().indexOf(JAR_URL_SEPARATOR) != -1));
+                (PROTOCOL_CODE_SOURCE.equals(protocol) && url.getPath().contains(JAR_URL_SEPARATOR)));
     }
 
     /**
@@ -268,4 +273,110 @@ public class ClasspathCollector {
         }
     }
 
+    /**
+     * Method that collects resources from a JBoss classloading system using Vfs.
+     * @param rootResource the root resource for evaluating its children.
+     * @param locationPattern the sub pattern that all children must mach, so they are selected.
+     * @return the resources found, never null.
+     * @throws IOException
+     */
+    private static Collection<URL> findMatchingVfsResources(
+            URL rootResource, String locationPattern) throws IOException {
+        Object root = VfsSupport.getRoot(rootResource);
+        PatternVfsVisitor visitor =
+                new PatternVfsVisitor(VfsSupport.getPath(root), locationPattern);
+        VfsSupport.visit(root, visitor);
+        return visitor.getResources();
+    }
+
+    /**
+     * Simple dynamic visitor implementation used for evaluating paths from a JBoss Vfs system.
+     */
+    private static class PatternVfsVisitor implements InvocationHandler {
+        /**
+         * The regex pattern to match agains all child resources of the root path against.
+         */
+        private final String subPattern;
+        /**
+         * The resource path before yny placeholders/whitespaces are occurring.
+         */
+        private final String rootPath;
+        /**
+         * THe resources found so far.
+         */
+        private final List<URL> resources = new LinkedList<>();
+
+        /**
+         * Creates a new visitor for cfs resources.
+         *
+         * @param rootPath   the root path, until any patterns are occurring.
+         * @param subPattern the sub pattern for looking for.
+         */
+        public PatternVfsVisitor(String rootPath, String subPattern) {
+            this.subPattern = subPattern;
+            this.rootPath = (rootPath.length() == 0 || rootPath.endsWith("/") ? rootPath : rootPath + "/");
+        }
+
+        /**
+         * Method called by visitor proxy.
+         *
+         * @param proxy  the proxy instance.
+         * @param method the method called.
+         * @param args   any arguments.
+         * @return the result.
+         * @throws Throwable in case something goes wrong.
+         */
+        @Override
+        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+            String methodName = method.getName();
+            if (Object.class.equals(method.getDeclaringClass())) {
+                if (methodName.equals("equals")) {
+                    // Only consider equal when proxies are identical.
+                    return (proxy == args[0]);
+                } else if (methodName.equals("hashCode")) {
+                    return System.identityHashCode(proxy);
+                }
+            } else if ("getAttributes".equals(methodName)) {
+                return VfsSupport.getVisitorAttributes();
+            } else if ("visit".equals(methodName)) {
+                visit(args[0]);
+                return null;
+            } else if ("toString".equals(methodName)) {
+                return toString();
+            }
+
+            throw new IllegalStateException("Unexpected method invocation: " + method);
+        }
+
+        /**
+         * Visitor method.
+         *
+         * @param vfsResource the vfsResource object.
+         */
+        public void visit(Object vfsResource) {
+            String subPath = VfsSupport.getPath(vfsResource).substring(this.rootPath.length());
+            if (subPath.matches(this.subPattern)) {
+                try {
+                    this.resources.add(VfsSupport.getURL(vfsResource));
+                } catch (Exception e) {
+                    LOG.log(Level.WARNING, "Failed to convert vfs resource to URL: " + vfsResource, e);
+                }
+            }
+        }
+
+        /**
+         * Access the resources found from Vfs during last visit.
+         *
+         * @return the resources found, not null.
+         */
+        public Collection<URL> getResources() {
+            return this.resources;
+        }
+
+        @Override
+        public String toString() {
+            return "sub-pattern: " + this.subPattern + ", resources: " + this.resources;
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-tamaya/blob/af21ac43/modules/resources/src/main/java/org/apache/tamaya/resource/internal/VfsSupport.java
----------------------------------------------------------------------
diff --git a/modules/resources/src/main/java/org/apache/tamaya/resource/internal/VfsSupport.java b/modules/resources/src/main/java/org/apache/tamaya/resource/internal/VfsSupport.java
new file mode 100644
index 0000000..22b4f52
--- /dev/null
+++ b/modules/resources/src/main/java/org/apache/tamaya/resource/internal/VfsSupport.java
@@ -0,0 +1,252 @@
+/*
+ * 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.tamaya.resource.internal;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Internal support class dealing with JBoss VFS in the classpath.
+ * <p>
+ * This code is compatible with JBoss AS 6+ and JBoss AS 7 and
+ * WildFly 8.
+ */
+class VfsSupport {
+
+    private static final String VFS3_PKG = "org.jboss.vfs.";
+    private static final String VFS_PROTOCOL = "VFS";
+
+    private static Method methodGetRootUrl = null;
+    private static Method methodToUrl;
+    private static Method methodGetPathName;
+    private static Class<?> fileVisitorInterface;
+    private static Method methodVisit;
+    private static Field visitorAttributesField = null;
+    private static Method methodGetPhysicalFile = null;
+
+    /**
+     * Private constructor.
+     */
+    private VfsSupport(){}
+
+    /**
+     * Initialize glue reflection code for communicating with VFS systems.
+     */
+    static {
+        ClassLoader loader = VfsSupport.class.getClassLoader();
+        try {
+            Class<?> vfsClass = loader.loadClass(VFS3_PKG + VFS_PROTOCOL);
+            methodGetRootUrl = findMethod(vfsClass, "getChild", URL.class);
+            Class<?> virtualFile = loader.loadClass(VFS3_PKG + "VirtualFile");
+            methodToUrl = findMethod(virtualFile, "toURL");
+            methodGetPathName = findMethod(virtualFile, "getPathName");
+            methodGetPhysicalFile = findMethod(virtualFile, "getPhysicalFile");
+            fileVisitorInterface = loader.loadClass(VFS3_PKG + "VirtualFileVisitor");
+            methodVisit = findMethod(virtualFile, "visit", fileVisitorInterface);
+            Class<?> visitorAttributesClass = loader.loadClass(VFS3_PKG + "VisitorAttributes");
+            visitorAttributesField = findField(visitorAttributesClass, "RECURSE");
+        } catch (ClassNotFoundException ex) {
+            throw new IllegalStateException("JBoss VFS not available.", ex);
+        }
+    }
+
+    /**
+     * Visit a VFS resource with the given visitor, modeled as dynamic {@link java.lang.reflect.InvocationHandler}.
+     *
+     * @param resource the resource
+     * @param visitor  the visitor.
+     * @throws IOException
+     */
+    static void visit(Object resource, InvocationHandler visitor) throws IOException {
+        Object visitorProxy = Proxy.newProxyInstance(
+                fileVisitorInterface.getClassLoader(),
+                new Class<?>[]{fileVisitorInterface}, visitor);
+        invokeVfsMethod(methodVisit, resource, visitorProxy);
+    }
+
+    /**
+     * Helper method to invoke an operation on VFS.
+     *
+     * @param method the method to invoke
+     * @param target the target instance
+     * @param args   any arguments
+     * @return the result
+     * @throws IOException if something fails.
+     */
+    private static Object invokeVfsMethod(Method method, Object target, Object... args) throws IOException {
+        try {
+            return method.invoke(target, args);
+        } catch (Exception ex) {
+            throw new IOException("Failed to evaluated method: " + method, ex);
+        }
+
+    }
+
+    /**
+     * Transform a VFS resource into an URL.
+     *
+     * @param vfsResource the cfw resource, not null
+     * @return the corresponding URL
+     * @throws IOException
+     */
+    static URL getURL(Object vfsResource) throws IOException {
+        return (URL) invokeVfsMethod(methodToUrl, vfsResource);
+    }
+
+    /**
+     * Get a to root VFS resource for the given URL.
+     *
+     * @param url the url
+     * @return the corresponding VFS resource.
+     * @throws IOException
+     */
+    static Object getRelative(URL url) throws IOException {
+        return invokeVfsMethod(methodGetRootUrl, null, url);
+    }
+
+    /**
+     * Transform the given VFS resource of a file.
+     *
+     * @param vfsResource the VFS resource
+     * @return the file.
+     * @throws IOException
+     */
+    static File getFile(Object vfsResource) throws IOException {
+        return (File) invokeVfsMethod(methodGetPhysicalFile, vfsResource);
+    }
+
+    /**
+     * Convert the given URL to the correspinoding root URL.
+     *
+     * @param url the url
+     * @return the root resource.
+     * @throws IOException
+     */
+    static Object getRoot(URL url) throws IOException {
+        return invokeVfsMethod(methodGetRootUrl, null, url);
+    }
+
+    /**
+     * Access the attributes from the current visitor context.
+     *
+     * @return the attributes.
+     */
+    static Object getVisitorAttributes() {
+        return readField(visitorAttributesField, null);
+    }
+
+    /**
+     * Access the corresponding path to the given VFS resource.
+     *
+     * @param resource the VFS resource
+     * @return the corresponding path.
+     */
+    static String getPath(Object resource) {
+        try {
+            return (String) methodGetPathName.invoke(resource);
+        } catch (Exception e) {
+            throw new IllegalStateException("Failed to get path name - " + resource, e);
+        }
+    }
+
+
+    /**
+     * Attempt to find a {@link Method} on the supplied class with the supplied name
+     * and parameter types. Searches all superclasses up to {@code Object}.
+     * <p>Returns {@code null} if no {@link Method} can be found.
+     *
+     * @param clazz      the class to introspect
+     * @param name       the name of the method
+     * @param paramTypes the parameter types of the method
+     *                   (may be {@code null} to indicate any signature)
+     * @return the Method object, or {@code null} if none found
+     */
+    private static Method findMethod(Class<?> clazz, String name, Class<?>... paramTypes) {
+        Objects.requireNonNull(clazz, "Class must not be null");
+        Objects.requireNonNull(name, "Method name must not be null");
+        Class<?> searchType = clazz;
+        while (searchType != null) {
+            Method[] methods = (searchType.isInterface() ? searchType.getMethods() : searchType.getDeclaredMethods());
+            for (Method method : methods) {
+                if (name.equals(method.getName()) &&
+                        (paramTypes == null || Arrays.equals(paramTypes, method.getParameterTypes()))) {
+                    return method;
+                }
+            }
+            searchType = searchType.getSuperclass();
+        }
+        return null;
+    }
+
+
+    /**
+     * Get the field represented by the supplied {@link Field field object} on the
+     * specified {@link Object target object}. In accordance with {@link Field#get(Object)}
+     * semantics, the returned value is automatically wrapped if the underlying field
+     * has a primitive type.
+     * <p>Thrown exceptions are rethrown as {@link IllegalStateException}.
+     *
+     * @param field  the field to get
+     * @param target the target object from which to get the field
+     * @return the field's current value
+     */
+    private static Object readField(Field field, Object target) {
+        try {
+            if (!field.isAccessible()) {
+                field.setAccessible(true);
+            }
+            return field.get(target);
+        } catch (Exception e) {
+            throw new IllegalStateException(
+                    "Failed to read field: " + field.toGenericString(), e);
+        }
+    }
+
+    /**
+     * Attempt to find a {@link Field field} on the supplied {@link Class} with the
+     * supplied {@code name}. Searches all superclasses up to {@link Object}.
+     *
+     * @param clazz the class to introspect
+     * @param name  the name of the field
+     * @return the corresponding Field object, or {@code null} if not found
+     */
+    private static Field findField(Class<?> clazz, String name) {
+        Objects.requireNonNull(clazz, "Class must not be null");
+        Objects.requireNonNull(name, "Name must not be null.");
+        Class<?> searchType = clazz;
+        while (!Object.class.equals(searchType) && searchType != null) {
+            Field[] fields = searchType.getDeclaredFields();
+            for (Field field : fields) {
+                if (name.equals(field.getName())) {
+                    return field;
+                }
+            }
+            searchType = searchType.getSuperclass();
+        }
+        return null;
+    }
+
+}