VFS.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.InvocationTargetException;
  19. import java.lang.reflect.Method;
  20. import java.net.URL;
  21. import java.util.ArrayList;
  22. import java.util.Arrays;
  23. import java.util.Collections;
  24. import java.util.List;

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

  27. /**
  28.  * Provides a very simple API for accessing resources within an application server.
  29.  *
  30.  * @author Ben Gunter
  31.  */
  32. public abstract class VFS {
  33.   private static final Log log = LogFactory.getLog(VFS.class);

  34.   /** The built-in implementations. */
  35.   private static final Class<?>[] IMPLEMENTATIONS = { JBoss6VFS.class, DefaultVFS.class };

  36.   /**
  37.    * The list to which implementations are added by {@link #addImplClass(Class)}.
  38.    */
  39.   private static final List<Class<? extends VFS>> USER_IMPLEMENTATIONS = new ArrayList<>();

  40.   /** Singleton instance holder. */
  41.   private static class VFSHolder {
  42.     static final VFS INSTANCE = createVFS();

  43.     @SuppressWarnings("unchecked")
  44.     static VFS createVFS() {
  45.       // Try the user implementations first, then the built-ins
  46.       List<Class<? extends VFS>> impls = new ArrayList<>(USER_IMPLEMENTATIONS);
  47.       impls.addAll(Arrays.asList((Class<? extends VFS>[]) IMPLEMENTATIONS));

  48.       // Try each implementation class until a valid one is found
  49.       VFS vfs = null;
  50.       for (int i = 0; vfs == null || !vfs.isValid(); i++) {
  51.         Class<? extends VFS> impl = impls.get(i);
  52.         try {
  53.           vfs = impl.getDeclaredConstructor().newInstance();
  54.           if (!vfs.isValid() && log.isDebugEnabled()) {
  55.             log.debug("VFS implementation " + impl.getName() + " is not valid in this environment.");
  56.           }
  57.         } catch (InstantiationException | IllegalAccessException | NoSuchMethodException
  58.             | InvocationTargetException e) {
  59.           log.error("Failed to instantiate " + impl, e);
  60.           return null;
  61.         }
  62.       }

  63.       if (log.isDebugEnabled()) {
  64.         log.debug("Using VFS adapter " + vfs.getClass().getName());
  65.       }

  66.       return vfs;
  67.     }

  68.     private VFSHolder() {
  69.     }
  70.   }

  71.   /**
  72.    * Get the singleton {@link VFS} instance. If no {@link VFS} implementation can be found for the current environment,
  73.    * then this method returns null.
  74.    *
  75.    * @return single instance of VFS
  76.    */
  77.   public static VFS getInstance() {
  78.     return VFSHolder.INSTANCE;
  79.   }

  80.   /**
  81.    * Adds the specified class to the list of {@link VFS} implementations. Classes added in this manner are tried in the
  82.    * order they are added and before any of the built-in implementations.
  83.    *
  84.    * @param clazz
  85.    *          The {@link VFS} implementation class to add.
  86.    */
  87.   public static void addImplClass(Class<? extends VFS> clazz) {
  88.     if (clazz != null) {
  89.       USER_IMPLEMENTATIONS.add(clazz);
  90.     }
  91.   }

  92.   /**
  93.    * Get a class by name. If the class is not found then return null.
  94.    *
  95.    * @param className
  96.    *          the class name
  97.    *
  98.    * @return the class
  99.    */
  100.   protected static Class<?> getClass(String className) {
  101.     try {
  102.       return Thread.currentThread().getContextClassLoader().loadClass(className);
  103.       // return ReflectUtil.findClass(className);
  104.     } catch (ClassNotFoundException e) {
  105.       if (log.isDebugEnabled()) {
  106.         log.debug("Class not found: " + className);
  107.       }
  108.       return null;
  109.     }
  110.   }

  111.   /**
  112.    * Get a method by name and parameter types. If the method is not found then return null.
  113.    *
  114.    * @param clazz
  115.    *          The class to which the method belongs.
  116.    * @param methodName
  117.    *          The name of the method.
  118.    * @param parameterTypes
  119.    *          The types of the parameters accepted by the method.
  120.    *
  121.    * @return the method
  122.    */
  123.   protected static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
  124.     if (clazz == null) {
  125.       return null;
  126.     }
  127.     try {
  128.       return clazz.getMethod(methodName, parameterTypes);
  129.     } catch (SecurityException e) {
  130.       log.error("Security exception looking for method " + clazz.getName() + "." + methodName + ".  Cause: " + e);
  131.       return null;
  132.     } catch (NoSuchMethodException e) {
  133.       log.error("Method not found " + clazz.getName() + "." + methodName + ".  Cause: " + e);
  134.       return null;
  135.     }
  136.   }

  137.   /**
  138.    * Invoke a method on an object and return whatever it returns.
  139.    *
  140.    * @param <T>
  141.    *          the generic type
  142.    * @param method
  143.    *          The method to invoke.
  144.    * @param object
  145.    *          The instance or class (for static methods) on which to invoke the method.
  146.    * @param parameters
  147.    *          The parameters to pass to the method.
  148.    *
  149.    * @return Whatever the method returns.
  150.    *
  151.    * @throws IOException
  152.    *           If I/O errors occur
  153.    * @throws RuntimeException
  154.    *           If anything else goes wrong
  155.    */
  156.   @SuppressWarnings("unchecked")
  157.   protected static <T> T invoke(Method method, Object object, Object... parameters)
  158.       throws IOException, RuntimeException {
  159.     try {
  160.       return (T) method.invoke(object, parameters);
  161.     } catch (IllegalArgumentException | IllegalAccessException e) {
  162.       throw new RuntimeException(e);
  163.     } catch (InvocationTargetException e) {
  164.       if (e.getTargetException() instanceof IOException) {
  165.         throw (IOException) e.getTargetException();
  166.       }
  167.       throw new RuntimeException(e);
  168.     }
  169.   }

  170.   /**
  171.    * Get a list of {@link URL}s from the context classloader for all the resources found at the specified path.
  172.    *
  173.    * @param path
  174.    *          The resource path.
  175.    *
  176.    * @return A list of {@link URL}s, as returned by {@link ClassLoader#getResources(String)}.
  177.    *
  178.    * @throws IOException
  179.    *           If I/O errors occur
  180.    */
  181.   protected static List<URL> getResources(String path) throws IOException {
  182.     return Collections.list(Thread.currentThread().getContextClassLoader().getResources(path));
  183.   }

  184.   /**
  185.    * Return true if the {@link VFS} implementation is valid for the current environment.
  186.    *
  187.    * @return true, if is valid
  188.    */
  189.   public abstract boolean isValid();

  190.   /**
  191.    * Recursively list the full resource path of all the resources that are children of the resource identified by a URL.
  192.    *
  193.    * @param url
  194.    *          The URL that identifies the resource to list.
  195.    * @param forPath
  196.    *          The path to the resource that is identified by the URL. Generally, this is the value passed to
  197.    *          {@link #getResources(String)} to get the resource URL.
  198.    *
  199.    * @return A list containing the names of the child resources.
  200.    *
  201.    * @throws IOException
  202.    *           If I/O errors occur
  203.    */
  204.   protected abstract List<String> list(URL url, String forPath) throws IOException;

  205.   /**
  206.    * Recursively list the full resource path of all the resources that are children of all the resources found at the
  207.    * specified path.
  208.    *
  209.    * @param path
  210.    *          The path of the resource(s) to list.
  211.    *
  212.    * @return A list containing the names of the child resources.
  213.    *
  214.    * @throws IOException
  215.    *           If I/O errors occur
  216.    */
  217.   public List<String> list(String path) throws IOException {
  218.     List<String> names = new ArrayList<>();
  219.     for (URL url : getResources(path)) {
  220.       names.addAll(list(url, path));
  221.     }
  222.     return names;
  223.   }
  224. }