View Javadoc
1   /*
2    *    Copyright 2010-2026 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.Method;
20  import java.net.URL;
21  import java.util.ArrayList;
22  import java.util.List;
23  import java.util.logging.Level;
24  import java.util.logging.Logger;
25  
26  /**
27   * A {@link VFS} implementation that works with the VFS API provided by JBoss 6.
28   */
29  public class JBoss6VFS extends VFS {
30    private static final Logger log = Logger.getLogger(JBoss6VFS.class.getName());
31  
32    /** A class that mimics a tiny subset of the JBoss VirtualFile class. */
33    static class VirtualFile {
34      static Class<?> VirtualFile;
35      static Method getPathNameRelativeTo;
36      static Method getChildrenRecursively;
37  
38      Object virtualFile;
39  
40      VirtualFile(Object virtualFile) {
41        this.virtualFile = virtualFile;
42      }
43  
44      String getPathNameRelativeTo(VirtualFile parent) {
45        try {
46          return invoke(getPathNameRelativeTo, virtualFile, parent.virtualFile);
47        } catch (IOException e) {
48          // This exception is not thrown by the called method
49          log.log(Level.SEVERE, "This should not be possible. VirtualFile.getPathNameRelativeTo() threw IOException.");
50          return null;
51        }
52      }
53  
54      List<VirtualFile> getChildren() throws IOException {
55        List<?> objects = invoke(getChildrenRecursively, virtualFile);
56        List<VirtualFile> children = new ArrayList<>(objects.size());
57        for (Object object : objects) {
58          children.add(new VirtualFile(object));
59        }
60        return children;
61      }
62    }
63  
64    /** A class that mimics a tiny subset of the JBoss VFS class. */
65    static class VFS {
66      static Class<?> VFS;
67      static Method getChild;
68  
69      private VFS() {
70        // Prevent Instantiation
71      }
72  
73      static VirtualFile getChild(URL url) throws IOException {
74        Object o = invoke(getChild, VFS, url);
75        return o == null ? null : new VirtualFile(o);
76      }
77    }
78  
79    /** Flag that indicates if this VFS is valid for the current environment. */
80    private static Boolean valid;
81  
82    /** Find all the classes and methods that are required to access the JBoss 6 VFS. */
83    protected static synchronized void initialize() {
84      if (valid == null) {
85        // Assume valid. It will get flipped later if something goes wrong.
86        valid = Boolean.TRUE;
87  
88        // Look up and verify required classes
89        VFS.VFS = checkNotNull(getClass("org.jboss.vfs.VFS"));
90        VirtualFile.VirtualFile = checkNotNull(getClass("org.jboss.vfs.VirtualFile"));
91  
92        // Look up and verify required methods
93        VFS.getChild = checkNotNull(getMethod(VFS.VFS, "getChild", URL.class));
94        VirtualFile.getChildrenRecursively = checkNotNull(getMethod(VirtualFile.VirtualFile, "getChildrenRecursively"));
95        VirtualFile.getPathNameRelativeTo = checkNotNull(
96            getMethod(VirtualFile.VirtualFile, "getPathNameRelativeTo", VirtualFile.VirtualFile));
97  
98        // Verify that the API has not changed
99        checkReturnType(VFS.getChild, VirtualFile.VirtualFile);
100       checkReturnType(VirtualFile.getChildrenRecursively, List.class);
101       checkReturnType(VirtualFile.getPathNameRelativeTo, String.class);
102     }
103   }
104 
105   /**
106    * Verifies that the provided object reference is null. If it is null, then this VFS is marked as invalid for the
107    * current environment.
108    *
109    * @param <T>
110    *          the generic type
111    * @param object
112    *          The object reference to check for null.
113    *
114    * @return the t
115    */
116   protected static <T> T checkNotNull(T object) {
117     if (object == null) {
118       setInvalid();
119     }
120     return object;
121   }
122 
123   /**
124    * Verifies that the return type of method is what it is expected to be. If it is not, then this VFS is marked as
125    * invalid for the current environment.
126    *
127    * @param method
128    *          The method whose return type is to be checked.
129    * @param expected
130    *          A type to which the method's return type must be assignable.
131    *
132    * @see Class#isAssignableFrom(Class)
133    */
134   protected static void checkReturnType(Method method, Class<?> expected) {
135     if (method != null && !expected.isAssignableFrom(method.getReturnType())) {
136       log.log(Level.SEVERE, "Method " + method.getClass().getName() + "." + method.getName() + "(..) should return "
137           + expected.getName() + " but returns " + method.getReturnType().getName() + " instead.");
138       setInvalid();
139     }
140   }
141 
142   /**
143    * Mark this {@link VFS} as invalid for the current environment.
144    */
145   protected static void setInvalid() {
146     if (JBoss6VFS.valid.booleanValue()) {
147       log.log(Level.FINER, "JBoss 6 VFS API is not available in this environment.");
148       JBoss6VFS.valid = Boolean.FALSE;
149     }
150   }
151 
152   static {
153     initialize();
154   }
155 
156   @Override
157   public boolean isValid() {
158     return valid;
159   }
160 
161   @Override
162   public List<String> list(URL url, String path) throws IOException {
163     VirtualFile directory;
164     directory = VFS.getChild(url);
165     if (directory == null) {
166       return List.of();
167     }
168 
169     if (!path.endsWith("/")) {
170       path += "/";
171     }
172 
173     List<VirtualFile> children = directory.getChildren();
174     List<String> names = new ArrayList<>(children.size());
175     for (VirtualFile vf : children) {
176       names.add(path + vf.getPathNameRelativeTo(directory));
177     }
178 
179     return names;
180   }
181 }