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