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 public class DefaultVFS extends VFS {
42 private static final Logger log = Logger.getLogger(DefaultVFS.class.getName());
43
44
45 private static final byte[] JAR_MAGIC = { 'P', 'K', 3, 4 };
46
47 @Override
48 public boolean isValid() {
49 return true;
50 }
51
52 @Override
53 public List<String> list(URL url, String path) throws IOException {
54 InputStream is = null;
55 try {
56 List<String> resources = new ArrayList<>();
57
58
59
60 URL jarUrl = findJarForResource(url);
61 if (jarUrl != null) {
62 is = jarUrl.openStream();
63 if (log.isLoggable(Level.FINER)) {
64 log.log(Level.FINER, "Listing " + url);
65 }
66 resources = listResources(new JarInputStream(is), path);
67 } else {
68 List<String> children = new ArrayList<>();
69 try {
70 if (isJar(url)) {
71
72
73 is = url.openStream();
74 try (JarInputStream jarInput = new JarInputStream(is)) {
75 if (log.isLoggable(Level.FINER)) {
76 log.log(Level.FINER, "Listing " + url);
77 }
78 for (JarEntry entry; (entry = jarInput.getNextJarEntry()) != null;) {
79 if (log.isLoggable(Level.FINER)) {
80 log.log(Level.FINER, "Jar entry: " + entry.getName());
81 }
82 children.add(entry.getName());
83 }
84 }
85 } else {
86
87
88
89
90
91
92
93 is = url.openStream();
94 List<String> lines = new ArrayList<>();
95 try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
96 for (String line; (line = reader.readLine()) != null;) {
97 if (log.isLoggable(Level.FINER)) {
98 log.log(Level.FINER, "Reader entry: " + line);
99 }
100 lines.add(line);
101 if (getResources(path + "/" + line).isEmpty()) {
102 lines.clear();
103 break;
104 }
105 }
106 } catch (InvalidPathException e) {
107
108 lines.clear();
109 }
110 if (!lines.isEmpty()) {
111 if (log.isLoggable(Level.FINER)) {
112 log.log(Level.FINER, "Listing " + url);
113 }
114 children.addAll(lines);
115 }
116 }
117 } catch (FileNotFoundException e) {
118
119
120
121
122 if (!"file".equals(url.getProtocol())) {
123
124 throw e;
125 }
126 File file = Path.of(url.getFile()).toFile();
127 if (log.isLoggable(Level.FINER)) {
128 log.log(Level.FINER, "Listing directory " + file.getAbsolutePath());
129 }
130 if (file.isDirectory()) {
131 if (log.isLoggable(Level.FINER)) {
132 log.log(Level.FINER, "Listing " + url);
133 }
134 children = Arrays.asList(file.list());
135 }
136 }
137
138
139 String prefix = url.toExternalForm();
140 if (!prefix.endsWith("/")) {
141 prefix = prefix + "/";
142 }
143
144
145 for (String child : children) {
146 String resourcePath = path + "/" + child;
147 resources.add(resourcePath);
148 URL childUrl = new URL(prefix + child);
149 resources.addAll(list(childUrl, resourcePath));
150 }
151 }
152
153 return resources;
154 } finally {
155 if (is != null) {
156 try {
157 is.close();
158 } catch (Exception e) {
159
160 }
161 }
162 }
163 }
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.isLoggable(Level.FINER)) {
201 log.log(Level.FINER, "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
223
224 protected URL findJarForResource(URL url) throws MalformedURLException {
225 if (log.isLoggable(Level.FINER)) {
226 log.log(Level.FINER, "Find JAR URL: " + url);
227 }
228
229
230 boolean continueLoop = true;
231 while (continueLoop) {
232 try {
233 url = new URL(url.getFile());
234 if (log.isLoggable(Level.FINER)) {
235 log.log(Level.FINER, "Inner URL: " + url);
236 }
237 } catch (MalformedURLException e) {
238
239 continueLoop = false;
240 }
241 }
242
243
244 StringBuilder jarUrl = new StringBuilder(url.toExternalForm());
245 int index = jarUrl.lastIndexOf(".jar");
246 if (index < 0) {
247 if (log.isLoggable(Level.FINER)) {
248 log.log(Level.FINER, "Not a JAR: " + jarUrl);
249 }
250 return null;
251 }
252 jarUrl.setLength(index + 4);
253 if (log.isLoggable(Level.FINER)) {
254 log.log(Level.FINER, "Extracted JAR URL: " + jarUrl);
255 }
256
257
258 try {
259 URL testUrl = new URL(jarUrl.toString());
260 if (isJar(testUrl)) {
261 return testUrl;
262 }
263
264 if (log.isLoggable(Level.FINER)) {
265 log.log(Level.FINER, "Not a JAR: " + jarUrl);
266 }
267 jarUrl.replace(0, jarUrl.length(), testUrl.getFile());
268 File file = Path.of(jarUrl.toString()).toFile();
269
270
271 if (!file.exists()) {
272 file = Path.of(URLEncoder.encode(jarUrl.toString(), StandardCharsets.UTF_8)).toFile();
273 }
274
275 if (file.exists()) {
276 if (log.isLoggable(Level.FINER)) {
277 log.log(Level.FINER, "Trying real file: " + file.getAbsolutePath());
278 }
279 testUrl = file.toURI().toURL();
280 if (isJar(testUrl)) {
281 return testUrl;
282 }
283 }
284 } catch (MalformedURLException e) {
285 log.log(Level.WARNING, "Invalid JAR URL: " + jarUrl);
286 }
287
288 if (log.isLoggable(Level.FINER)) {
289 log.log(Level.FINER, "Not a JAR: " + jarUrl);
290 }
291 return null;
292 }
293
294
295
296
297
298
299
300
301
302
303 protected String getPackagePath(String packageName) {
304 return packageName == null ? null : packageName.replace('.', '/');
305 }
306
307
308
309
310
311
312
313
314
315 protected boolean isJar(URL url) {
316 return isJar(url, new byte[JAR_MAGIC.length]);
317 }
318
319
320
321
322
323
324
325
326
327
328
329
330 protected boolean isJar(URL url, byte[] buffer) {
331 try (InputStream is = url.openStream()) {
332 is.read(buffer, 0, JAR_MAGIC.length);
333 if (Arrays.equals(buffer, JAR_MAGIC)) {
334 if (log.isLoggable(Level.FINER)) {
335 log.log(Level.FINER, "Found JAR: " + url);
336 }
337 return true;
338 }
339 } catch (Exception e) {
340
341 }
342
343 return false;
344 }
345 }