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