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