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