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