1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.apache.ibatis.io;
17
18 import java.io.BufferedReader;
19 import java.io.File;
20 import java.io.FileNotFoundException;
21 import java.io.IOException;
22 import java.io.InputStream;
23 import java.io.InputStreamReader;
24 import java.io.UnsupportedEncodingException;
25 import java.net.MalformedURLException;
26 import java.net.URL;
27 import java.net.URLEncoder;
28 import java.nio.file.InvalidPathException;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.List;
32 import java.util.jar.JarEntry;
33 import java.util.jar.JarInputStream;
34
35 import org.apache.ibatis.logging.Log;
36 import org.apache.ibatis.logging.LogFactory;
37
38
39
40
41
42
43 public class DefaultVFS extends VFS {
44 private static final Log log = LogFactory.getLog(DefaultVFS.class);
45
46
47 private static final byte[] JAR_MAGIC = { 'P', 'K', 3, 4 };
48
49 @Override
50 public boolean isValid() {
51 return true;
52 }
53
54 @Override
55 public List<String> list(URL url, String path) throws IOException {
56 InputStream is = null;
57 try {
58 List<String> resources = new ArrayList<>();
59
60
61
62 URL jarUrl = findJarForResource(url);
63 if (jarUrl != null) {
64 is = jarUrl.openStream();
65 if (log.isDebugEnabled()) {
66 log.debug("Listing " + url);
67 }
68 resources = listResources(new JarInputStream(is), path);
69 } else {
70 List<String> children = new ArrayList<>();
71 try {
72 if (isJar(url)) {
73
74
75 is = url.openStream();
76 try (JarInputStream jarInput = new JarInputStream(is)) {
77 if (log.isDebugEnabled()) {
78 log.debug("Listing " + url);
79 }
80 for (JarEntry entry; (entry = jarInput.getNextJarEntry()) != null; ) {
81 if (log.isDebugEnabled()) {
82 log.debug("Jar entry: " + entry.getName());
83 }
84 children.add(entry.getName());
85 }
86 }
87 } else {
88
89
90
91
92
93
94
95
96 is = url.openStream();
97 List<String> lines = new ArrayList<>();
98 try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
99 for (String line; (line = reader.readLine()) != null;) {
100 if (log.isDebugEnabled()) {
101 log.debug("Reader entry: " + line);
102 }
103 lines.add(line);
104 if (getResources(path + "/" + line).isEmpty()) {
105 lines.clear();
106 break;
107 }
108 }
109 } catch (InvalidPathException e) {
110
111 lines.clear();
112 }
113 if (!lines.isEmpty()) {
114 if (log.isDebugEnabled()) {
115 log.debug("Listing " + url);
116 }
117 children.addAll(lines);
118 }
119 }
120 } catch (FileNotFoundException e) {
121
122
123
124
125
126 if ("file".equals(url.getProtocol())) {
127 File file = new File(url.getFile());
128 if (log.isDebugEnabled()) {
129 log.debug("Listing directory " + file.getAbsolutePath());
130 }
131 if (file.isDirectory()) {
132 if (log.isDebugEnabled()) {
133 log.debug("Listing " + url);
134 }
135 children = Arrays.asList(file.list());
136 }
137 } else {
138
139 throw e;
140 }
141 }
142
143
144 String prefix = url.toExternalForm();
145 if (!prefix.endsWith("/")) {
146 prefix = prefix + "/";
147 }
148
149
150 for (String child : children) {
151 String resourcePath = path + "/" + child;
152 resources.add(resourcePath);
153 URL childUrl = new URL(prefix + child);
154 resources.addAll(list(childUrl, resourcePath));
155 }
156 }
157
158 return resources;
159 } finally {
160 if (is != null) {
161 try {
162 is.close();
163 } catch (Exception e) {
164
165 }
166 }
167 }
168 }
169
170
171
172
173
174
175
176
177
178
179 protected List<String> listResources(JarInputStream jar, String path) throws IOException {
180
181 if (!path.startsWith("/")) {
182 path = "/" + path;
183 }
184 if (!path.endsWith("/")) {
185 path = path + "/";
186 }
187
188
189 List<String> resources = new ArrayList<>();
190 for (JarEntry entry; (entry = jar.getNextJarEntry()) != null;) {
191 if (!entry.isDirectory()) {
192
193 StringBuilder name = new StringBuilder(entry.getName());
194 if (name.charAt(0) != '/') {
195 name.insert(0, '/');
196 }
197
198
199 if (name.indexOf(path) == 0) {
200 if (log.isDebugEnabled()) {
201 log.debug("Found resource: " + name);
202 }
203
204 resources.add(name.substring(1));
205 }
206 }
207 }
208 return resources;
209 }
210
211
212
213
214
215
216
217
218
219
220
221
222 protected URL findJarForResource(URL url) throws MalformedURLException {
223 if (log.isDebugEnabled()) {
224 log.debug("Find JAR URL: " + url);
225 }
226
227
228 boolean continueLoop = true;
229 while (continueLoop) {
230 try {
231 url = new URL(url.getFile());
232 if (log.isDebugEnabled()) {
233 log.debug("Inner URL: " + url);
234 }
235 } catch (MalformedURLException e) {
236
237 continueLoop = false;
238 }
239 }
240
241
242 StringBuilder jarUrl = new StringBuilder(url.toExternalForm());
243 int index = jarUrl.lastIndexOf(".jar");
244 if (index >= 0) {
245 jarUrl.setLength(index + 4);
246 if (log.isDebugEnabled()) {
247 log.debug("Extracted JAR URL: " + jarUrl);
248 }
249 } else {
250 if (log.isDebugEnabled()) {
251 log.debug("Not a JAR: " + jarUrl);
252 }
253 return null;
254 }
255
256
257 try {
258 URL testUrl = new URL(jarUrl.toString());
259 if (isJar(testUrl)) {
260 return testUrl;
261 } else {
262
263 if (log.isDebugEnabled()) {
264 log.debug("Not a JAR: " + jarUrl);
265 }
266 jarUrl.replace(0, jarUrl.length(), testUrl.getFile());
267 File file = new File(jarUrl.toString());
268
269
270 if (!file.exists()) {
271 try {
272 file = new File(URLEncoder.encode(jarUrl.toString(), "UTF-8"));
273 } catch (UnsupportedEncodingException e) {
274 throw new RuntimeException("Unsupported encoding? UTF-8? That's impossible.");
275 }
276 }
277
278 if (file.exists()) {
279 if (log.isDebugEnabled()) {
280 log.debug("Trying real file: " + file.getAbsolutePath());
281 }
282 testUrl = file.toURI().toURL();
283 if (isJar(testUrl)) {
284 return testUrl;
285 }
286 }
287 }
288 } catch (MalformedURLException e) {
289 log.warn("Invalid JAR URL: " + jarUrl);
290 }
291
292 if (log.isDebugEnabled()) {
293 log.debug("Not a JAR: " + jarUrl);
294 }
295 return null;
296 }
297
298
299
300
301
302
303
304
305
306 protected String getPackagePath(String packageName) {
307 return packageName == null ? null : packageName.replace('.', '/');
308 }
309
310
311
312
313
314
315
316
317 protected boolean isJar(URL url) {
318 return isJar(url, new byte[JAR_MAGIC.length]);
319 }
320
321
322
323
324
325
326
327
328
329
330
331 protected boolean isJar(URL url, byte[] buffer) {
332 try (InputStream is = url.openStream()) {
333 is.read(buffer, 0, JAR_MAGIC.length);
334 if (Arrays.equals(buffer, JAR_MAGIC)) {
335 if (log.isDebugEnabled()) {
336 log.debug("Found JAR: " + url);
337 }
338 return true;
339 }
340 } catch (Exception e) {
341
342 }
343
344 return false;
345 }
346 }