View Javadoc
1   /*
2    *    Copyright 2009-2023 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  
18  import java.io.IOException;
19  import java.lang.reflect.InvocationTargetException;
20  import java.lang.reflect.Method;
21  import java.net.URL;
22  import java.util.ArrayList;
23  import java.util.Arrays;
24  import java.util.Collections;
25  import java.util.List;
26  
27  import org.apache.ibatis.logging.Log;
28  import org.apache.ibatis.logging.LogFactory;
29  
30  /**
31   * Provides a very simple API for accessing resources within an application server.
32   *
33   * @author Ben Gunter
34   */
35  public abstract class VFS {
36    private static final Log log = LogFactory.getLog(VFS.class);
37  
38    /** The built-in implementations. */
39    public static final Class<?>[] IMPLEMENTATIONS = { JBoss6VFS.class, DefaultVFS.class };
40  
41    /**
42     * The list to which implementations are added by {@link #addImplClass(Class)}.
43     */
44    public static final List<Class<? extends VFS>> USER_IMPLEMENTATIONS = new ArrayList<>();
45  
46    /** Singleton instance holder. */
47    private static class VFSHolder {
48      static final VFS INSTANCE = createVFS();
49  
50      @SuppressWarnings("unchecked")
51      static VFS createVFS() {
52        // Try the user implementations first, then the built-ins
53        List<Class<? extends VFS>> impls = new ArrayList<>(USER_IMPLEMENTATIONS);
54        impls.addAll(Arrays.asList((Class<? extends VFS>[]) IMPLEMENTATIONS));
55  
56        // Try each implementation class until a valid one is found
57        VFS vfs = null;
58        for (int i = 0; vfs == null || !vfs.isValid(); i++) {
59          Class<? extends VFS> impl = impls.get(i);
60          try {
61            vfs = impl.getDeclaredConstructor().newInstance();
62            if (!vfs.isValid() && log.isDebugEnabled()) {
63              log.debug("VFS implementation " + impl.getName() + " is not valid in this environment.");
64            }
65          } catch (InstantiationException | IllegalAccessException | NoSuchMethodException
66              | InvocationTargetException e) {
67            log.error("Failed to instantiate " + impl, e);
68            return null;
69          }
70        }
71  
72        if (log.isDebugEnabled()) {
73          log.debug("Using VFS adapter " + vfs.getClass().getName());
74        }
75  
76        return vfs;
77      }
78  
79      private VFSHolder() {
80      }
81    }
82  
83    /**
84     * Get the singleton {@link VFS} instance. If no {@link VFS} implementation can be found for the current environment,
85     * then this method returns null.
86     *
87     * @return single instance of VFS
88     */
89    public static VFS getInstance() {
90      return VFSHolder.INSTANCE;
91    }
92  
93    /**
94     * Adds the specified class to the list of {@link VFS} implementations. Classes added in this manner are tried in the
95     * order they are added and before any of the built-in implementations.
96     *
97     * @param clazz
98     *          The {@link VFS} implementation class to add.
99     */
100   public static void addImplClass(Class<? extends VFS> clazz) {
101     if (clazz != null) {
102       USER_IMPLEMENTATIONS.add(clazz);
103     }
104   }
105 
106   /**
107    * Get a class by name. If the class is not found then return null.
108    *
109    * @param className
110    *          the class name
111    *
112    * @return the class
113    */
114   protected static Class<?> getClass(String className) {
115     try {
116       return Thread.currentThread().getContextClassLoader().loadClass(className);
117       // return ReflectUtil.findClass(className);
118     } catch (ClassNotFoundException e) {
119       if (log.isDebugEnabled()) {
120         log.debug("Class not found: " + className);
121       }
122       return null;
123     }
124   }
125 
126   /**
127    * Get a method by name and parameter types. If the method is not found then return null.
128    *
129    * @param clazz
130    *          The class to which the method belongs.
131    * @param methodName
132    *          The name of the method.
133    * @param parameterTypes
134    *          The types of the parameters accepted by the method.
135    *
136    * @return the method
137    */
138   protected static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
139     if (clazz == null) {
140       return null;
141     }
142     try {
143       return clazz.getMethod(methodName, parameterTypes);
144     } catch (SecurityException e) {
145       log.error("Security exception looking for method " + clazz.getName() + "." + methodName + ".  Cause: " + e);
146       return null;
147     } catch (NoSuchMethodException e) {
148       log.error("Method not found " + clazz.getName() + "." + methodName + "." + methodName + ".  Cause: " + e);
149       return null;
150     }
151   }
152 
153   /**
154    * Invoke a method on an object and return whatever it returns.
155    *
156    * @param <T>
157    *          the generic type
158    * @param method
159    *          The method to invoke.
160    * @param object
161    *          The instance or class (for static methods) on which to invoke the method.
162    * @param parameters
163    *          The parameters to pass to the method.
164    *
165    * @return Whatever the method returns.
166    *
167    * @throws IOException
168    *           If I/O errors occur
169    * @throws RuntimeException
170    *           If anything else goes wrong
171    */
172   @SuppressWarnings("unchecked")
173   protected static <T> T invoke(Method method, Object object, Object... parameters)
174       throws IOException, RuntimeException {
175     try {
176       return (T) method.invoke(object, parameters);
177     } catch (IllegalArgumentException | IllegalAccessException e) {
178       throw new RuntimeException(e);
179     } catch (InvocationTargetException e) {
180       if (e.getTargetException() instanceof IOException) {
181         throw (IOException) e.getTargetException();
182       }
183       throw new RuntimeException(e);
184     }
185   }
186 
187   /**
188    * Get a list of {@link URL}s from the context classloader for all the resources found at the specified path.
189    *
190    * @param path
191    *          The resource path.
192    *
193    * @return A list of {@link URL}s, as returned by {@link ClassLoader#getResources(String)}.
194    *
195    * @throws IOException
196    *           If I/O errors occur
197    */
198   protected static List<URL> getResources(String path) throws IOException {
199     return Collections.list(Thread.currentThread().getContextClassLoader().getResources(path));
200   }
201 
202   /**
203    * Return true if the {@link VFS} implementation is valid for the current environment.
204    *
205    * @return true, if is valid
206    */
207   public abstract boolean isValid();
208 
209   /**
210    * Recursively list the full resource path of all the resources that are children of the resource identified by a URL.
211    *
212    * @param url
213    *          The URL that identifies the resource to list.
214    * @param forPath
215    *          The path to the resource that is identified by the URL. Generally, this is the value passed to
216    *          {@link #getResources(String)} to get the resource URL.
217    *
218    * @return A list containing the names of the child resources.
219    *
220    * @throws IOException
221    *           If I/O errors occur
222    */
223   protected abstract List<String> list(URL url, String forPath) throws IOException;
224 
225   /**
226    * Recursively list the full resource path of all the resources that are children of all the resources found at the
227    * specified path.
228    *
229    * @param path
230    *          The path of the resource(s) to list.
231    *
232    * @return A list containing the names of the child resources.
233    *
234    * @throws IOException
235    *           If I/O errors occur
236    */
237   public List<String> list(String path) throws IOException {
238     List<String> names = new ArrayList<>();
239     for (URL url : getResources(path)) {
240       names.addAll(list(url, path));
241     }
242     return names;
243   }
244 }