1 /**
2 * Copyright 2009-2019 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 * http://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 /** The list to which implementations are added by {@link #addImplClass(Class)}. */
42 public static final List<Class<? extends VFS>> USER_IMPLEMENTATIONS = new ArrayList<>();
43
44 /** Singleton instance holder. */
45 private static class VFSHolder {
46 static final VFS INSTANCE = createVFS();
47
48 @SuppressWarnings("unchecked")
49 static VFS createVFS() {
50 // Try the user implementations first, then the built-ins
51 List<Class<? extends VFS>> impls = new ArrayList<>();
52 impls.addAll(USER_IMPLEMENTATIONS);
53 impls.addAll(Arrays.asList((Class<? extends VFS>[]) IMPLEMENTATIONS));
54
55 // Try each implementation class until a valid one is found
56 VFS vfs = null;
57 for (int i = 0; vfs == null || !vfs.isValid(); i++) {
58 Class<? extends VFS> impl = impls.get(i);
59 try {
60 vfs = impl.getDeclaredConstructor().newInstance();
61 if (!vfs.isValid()) {
62 if (log.isDebugEnabled()) {
63 log.debug("VFS implementation " + impl.getName() +
64 " is not valid in this environment.");
65 }
66 }
67 } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
68 log.error("Failed to instantiate " + impl, e);
69 return null;
70 }
71 }
72
73 if (log.isDebugEnabled()) {
74 log.debug("Using VFS adapter " + vfs.getClass().getName());
75 }
76
77 return vfs;
78 }
79 }
80
81 /**
82 * Get the singleton {@link VFS} instance. If no {@link VFS} implementation can be found for the
83 * current environment, then this method returns null.
84 */
85 public static VFS getInstance() {
86 return VFSHolder.INSTANCE;
87 }
88
89 /**
90 * Adds the specified class to the list of {@link VFS} implementations. Classes added in this
91 * manner are tried in the order they are added and before any of the built-in implementations.
92 *
93 * @param clazz The {@link VFS} implementation class to add.
94 */
95 public static void addImplClass(Class<? extends VFS> clazz) {
96 if (clazz != null) {
97 USER_IMPLEMENTATIONS.add(clazz);
98 }
99 }
100
101 /** Get a class by name. If the class is not found then return null. */
102 protected static Class<?> getClass(String className) {
103 try {
104 return Thread.currentThread().getContextClassLoader().loadClass(className);
105 // return ReflectUtil.findClass(className);
106 } catch (ClassNotFoundException e) {
107 if (log.isDebugEnabled()) {
108 log.debug("Class not found: " + className);
109 }
110 return null;
111 }
112 }
113
114 /**
115 * Get a method by name and parameter types. If the method is not found then return null.
116 *
117 * @param clazz The class to which the method belongs.
118 * @param methodName The name of the method.
119 * @param parameterTypes The types of the parameters accepted by the method.
120 */
121 protected static Method getMethod(Class<?> clazz, String methodName, Class<?>... parameterTypes) {
122 if (clazz == null) {
123 return null;
124 }
125 try {
126 return clazz.getMethod(methodName, parameterTypes);
127 } catch (SecurityException e) {
128 log.error("Security exception looking for method " + clazz.getName() + "." + methodName + ". Cause: " + e);
129 return null;
130 } catch (NoSuchMethodException e) {
131 log.error("Method not found " + clazz.getName() + "." + methodName + "." + methodName + ". Cause: " + e);
132 return null;
133 }
134 }
135
136 /**
137 * Invoke a method on an object and return whatever it returns.
138 *
139 * @param method The method to invoke.
140 * @param object The instance or class (for static methods) on which to invoke the method.
141 * @param parameters The parameters to pass to the method.
142 * @return Whatever the method returns.
143 * @throws IOException If I/O errors occur
144 * @throws RuntimeException If anything else goes wrong
145 */
146 @SuppressWarnings("unchecked")
147 protected static <T> T invoke(Method method, Object object, Object... parameters)
148 throws IOException, RuntimeException {
149 try {
150 return (T) method.invoke(object, parameters);
151 } catch (IllegalArgumentException | IllegalAccessException e) {
152 throw new RuntimeException(e);
153 } catch (InvocationTargetException e) {
154 if (e.getTargetException() instanceof IOException) {
155 throw (IOException) e.getTargetException();
156 } else {
157 throw new RuntimeException(e);
158 }
159 }
160 }
161
162 /**
163 * Get a list of {@link URL}s from the context classloader for all the resources found at the
164 * specified path.
165 *
166 * @param path The resource path.
167 * @return A list of {@link URL}s, as returned by {@link ClassLoader#getResources(String)}.
168 * @throws IOException If I/O errors occur
169 */
170 protected static List<URL> getResources(String path) throws IOException {
171 return Collections.list(Thread.currentThread().getContextClassLoader().getResources(path));
172 }
173
174 /** Return true if the {@link VFS} implementation is valid for the current environment. */
175 public abstract boolean isValid();
176
177 /**
178 * Recursively list the full resource path of all the resources that are children of the
179 * resource identified by a URL.
180 *
181 * @param url The URL that identifies the resource to list.
182 * @param forPath The path to the resource that is identified by the URL. Generally, this is the
183 * value passed to {@link #getResources(String)} to get the resource URL.
184 * @return A list containing the names of the child resources.
185 * @throws IOException If I/O errors occur
186 */
187 protected abstract List<String> list(URL url, String forPath) throws IOException;
188
189 /**
190 * Recursively list the full resource path of all the resources that are children of all the
191 * resources found at the specified path.
192 *
193 * @param path The path of the resource(s) to list.
194 * @return A list containing the names of the child resources.
195 * @throws IOException If I/O errors occur
196 */
197 public List<String> list(String path) throws IOException {
198 List<String> names = new ArrayList<>();
199 for (URL url : getResources(path)) {
200 names.addAll(list(url, path));
201 }
202 return names;
203 }
204 }