JBoss6VFS.java

  1. /*
  2.  *    Copyright 2009-2024 the original author or authors.
  3.  *
  4.  *    Licensed under the Apache License, Version 2.0 (the "License");
  5.  *    you may not use this file except in compliance with the License.
  6.  *    You may obtain a copy of the License at
  7.  *
  8.  *       https://www.apache.org/licenses/LICENSE-2.0
  9.  *
  10.  *    Unless required by applicable law or agreed to in writing, software
  11.  *    distributed under the License is distributed on an "AS IS" BASIS,
  12.  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13.  *    See the License for the specific language governing permissions and
  14.  *    limitations under the License.
  15.  */
  16. package org.apache.ibatis.io;

  17. import java.io.IOException;
  18. import java.lang.reflect.Method;
  19. import java.net.URL;
  20. import java.util.ArrayList;
  21. import java.util.Collections;
  22. import java.util.List;
  23. import java.util.concurrent.locks.ReentrantLock;

  24. import org.apache.ibatis.logging.Log;
  25. import org.apache.ibatis.logging.LogFactory;

  26. /**
  27.  * A {@link VFS} implementation that works with the VFS API provided by JBoss 6.
  28.  *
  29.  * @author Ben Gunter
  30.  */
  31. public class JBoss6VFS extends VFS {
  32.   private static final Log log = LogFactory.getLog(JBoss6VFS.class);
  33.   private static final ReentrantLock lock = new ReentrantLock();

  34.   /** A class that mimics a tiny subset of the JBoss VirtualFile class. */
  35.   static class VirtualFile {
  36.     static Class<?> VirtualFile;
  37.     static Method getPathNameRelativeTo;
  38.     static Method getChildrenRecursively;

  39.     Object virtualFile;

  40.     VirtualFile(Object virtualFile) {
  41.       this.virtualFile = virtualFile;
  42.     }

  43.     String getPathNameRelativeTo(VirtualFile parent) {
  44.       try {
  45.         return invoke(getPathNameRelativeTo, virtualFile, parent.virtualFile);
  46.       } catch (IOException e) {
  47.         // This exception is not thrown by the called method
  48.         log.error("This should not be possible. VirtualFile.getPathNameRelativeTo() threw IOException.");
  49.         return null;
  50.       }
  51.     }

  52.     List<VirtualFile> getChildren() throws IOException {
  53.       List<?> objects = invoke(getChildrenRecursively, virtualFile);
  54.       List<VirtualFile> children = new ArrayList<>(objects.size());
  55.       for (Object object : objects) {
  56.         children.add(new VirtualFile(object));
  57.       }
  58.       return children;
  59.     }
  60.   }

  61.   /** A class that mimics a tiny subset of the JBoss VFS class. */
  62.   static class VFS {
  63.     static Class<?> VFS;
  64.     static Method getChild;

  65.     private VFS() {
  66.       // Prevent Instantiation
  67.     }

  68.     static VirtualFile getChild(URL url) throws IOException {
  69.       Object o = invoke(getChild, VFS, url);
  70.       return o == null ? null : new VirtualFile(o);
  71.     }
  72.   }

  73.   /** Flag that indicates if this VFS is valid for the current environment. */
  74.   private static Boolean valid;

  75.   /** Find all the classes and methods that are required to access the JBoss 6 VFS. */
  76.   protected static void initialize() {
  77.     lock.lock();
  78.     try {
  79.       if (valid == null) {
  80.         // Assume valid. It will get flipped later if something goes wrong.
  81.         valid = Boolean.TRUE;

  82.         // Look up and verify required classes
  83.         VFS.VFS = checkNotNull(getClass("org.jboss.vfs.VFS"));
  84.         VirtualFile.VirtualFile = checkNotNull(getClass("org.jboss.vfs.VirtualFile"));

  85.         // Look up and verify required methods
  86.         VFS.getChild = checkNotNull(getMethod(VFS.VFS, "getChild", URL.class));
  87.         VirtualFile.getChildrenRecursively = checkNotNull(getMethod(VirtualFile.VirtualFile, "getChildrenRecursively"));
  88.         VirtualFile.getPathNameRelativeTo = checkNotNull(
  89.             getMethod(VirtualFile.VirtualFile, "getPathNameRelativeTo", VirtualFile.VirtualFile));

  90.         // Verify that the API has not changed
  91.         checkReturnType(VFS.getChild, VirtualFile.VirtualFile);
  92.         checkReturnType(VirtualFile.getChildrenRecursively, List.class);
  93.         checkReturnType(VirtualFile.getPathNameRelativeTo, String.class);
  94.       }
  95.     } finally {
  96.       lock.unlock();
  97.     }
  98.   }

  99.   /**
  100.    * Verifies that the provided object reference is null. If it is null, then this VFS is marked as invalid for the
  101.    * current environment.
  102.    *
  103.    * @param <T>
  104.    *          the generic type
  105.    * @param object
  106.    *          The object reference to check for null.
  107.    *
  108.    * @return the t
  109.    */
  110.   protected static <T> T checkNotNull(T object) {
  111.     if (object == null) {
  112.       setInvalid();
  113.     }
  114.     return object;
  115.   }

  116.   /**
  117.    * Verifies that the return type of method is what it is expected to be. If it is not, then this VFS is marked as
  118.    * invalid for the current environment.
  119.    *
  120.    * @param method
  121.    *          The method whose return type is to be checked.
  122.    * @param expected
  123.    *          A type to which the method's return type must be assignable.
  124.    *
  125.    * @see Class#isAssignableFrom(Class)
  126.    */
  127.   protected static void checkReturnType(Method method, Class<?> expected) {
  128.     if (method != null && !expected.isAssignableFrom(method.getReturnType())) {
  129.       log.error("Method " + method.getClass().getName() + "." + method.getName() + "(..) should return "
  130.           + expected.getName() + " but returns " + method.getReturnType().getName() + " instead.");
  131.       setInvalid();
  132.     }
  133.   }

  134.   /**
  135.    * Mark this {@link VFS} as invalid for the current environment.
  136.    */
  137.   protected static void setInvalid() {
  138.     if (JBoss6VFS.valid.booleanValue()) {
  139.       log.debug("JBoss 6 VFS API is not available in this environment.");
  140.       JBoss6VFS.valid = Boolean.FALSE;
  141.     }
  142.   }

  143.   static {
  144.     initialize();
  145.   }

  146.   @Override
  147.   public boolean isValid() {
  148.     return valid;
  149.   }

  150.   @Override
  151.   public List<String> list(URL url, String path) throws IOException {
  152.     VirtualFile directory;
  153.     directory = VFS.getChild(url);
  154.     if (directory == null) {
  155.       return Collections.emptyList();
  156.     }

  157.     if (!path.endsWith("/")) {
  158.       path += "/";
  159.     }

  160.     List<VirtualFile> children = directory.getChildren();
  161.     List<String> names = new ArrayList<>(children.size());
  162.     for (VirtualFile vf : children) {
  163.       names.add(path + vf.getPathNameRelativeTo(directory));
  164.     }

  165.     return names;
  166.   }
  167. }