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