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