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.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.List;
31 import java.util.jar.JarEntry;
32 import java.util.jar.JarInputStream;
33
34 import org.apache.ibatis.logging.Log;
35 import org.apache.ibatis.logging.LogFactory;
36
37
38
39
40
41
42 public class DefaultVFS extends VFS {
43 private static final Log log = LogFactory.getLog(DefaultVFS.class);
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.isDebugEnabled()) {
65 log.debug("Listing " + url);
66 }
67 resources = listResources(new JarInputStream(is), path);
68 }
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 }
88 else {
89
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 }
111 if (!lines.isEmpty()) {
112 if (log.isDebugEnabled()) {
113 log.debug("Listing " + url);
114 }
115 children.addAll(lines);
116 }
117 }
118 } catch (FileNotFoundException e) {
119
120
121
122
123
124 if ("file".equals(url.getProtocol())) {
125 File file = new File(url.getFile());
126 if (log.isDebugEnabled()) {
127 log.debug("Listing directory " + file.getAbsolutePath());
128 }
129 if (file.isDirectory()) {
130 if (log.isDebugEnabled()) {
131 log.debug("Listing " + url);
132 }
133 children = Arrays.asList(file.list());
134 }
135 }
136 else {
137
138 throw e;
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 protected List<String> listResources(JarInputStream jar, String path) throws IOException {
179
180 if (!path.startsWith("/")) {
181 path = "/" + path;
182 }
183 if (!path.endsWith("/")) {
184 path = path + "/";
185 }
186
187
188 List<String> resources = new ArrayList<>();
189 for (JarEntry entry; (entry = jar.getNextJarEntry()) != null;) {
190 if (!entry.isDirectory()) {
191
192 StringBuilder name = new StringBuilder(entry.getName());
193 if (name.charAt(0) != '/') {
194 name.insert(0, '/');
195 }
196
197
198 if (name.indexOf(path) == 0) {
199 if (log.isDebugEnabled()) {
200 log.debug("Found resource: " + name);
201 }
202
203 resources.add(name.substring(1));
204 }
205 }
206 }
207 return resources;
208 }
209
210
211
212
213
214
215
216
217
218
219
220 protected URL findJarForResource(URL url) throws MalformedURLException {
221 if (log.isDebugEnabled()) {
222 log.debug("Find JAR URL: " + url);
223 }
224
225
226 boolean continueLoop = true;
227 while (continueLoop) {
228 try {
229 url = new URL(url.getFile());
230 if (log.isDebugEnabled()) {
231 log.debug("Inner URL: " + url);
232 }
233 } catch (MalformedURLException e) {
234
235 continueLoop = false;
236 }
237 }
238
239
240 StringBuilder jarUrl = new StringBuilder(url.toExternalForm());
241 int index = jarUrl.lastIndexOf(".jar");
242 if (index >= 0) {
243 jarUrl.setLength(index + 4);
244 if (log.isDebugEnabled()) {
245 log.debug("Extracted JAR URL: " + jarUrl);
246 }
247 }
248 else {
249 if (log.isDebugEnabled()) {
250 log.debug("Not a JAR: " + jarUrl);
251 }
252 return null;
253 }
254
255
256 try {
257 URL testUrl = new URL(jarUrl.toString());
258 if (isJar(testUrl)) {
259 return testUrl;
260 }
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 unpossible.");
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 protected String getPackagePath(String packageName) {
305 return packageName == null ? null : packageName.replace('.', '/');
306 }
307
308
309
310
311
312
313 protected boolean isJar(URL url) {
314 return isJar(url, new byte[JAR_MAGIC.length]);
315 }
316
317
318
319
320
321
322
323
324
325 protected boolean isJar(URL url, byte[] buffer) {
326 InputStream is = null;
327 try {
328 is = url.openStream();
329 is.read(buffer, 0, JAR_MAGIC.length);
330 if (Arrays.equals(buffer, JAR_MAGIC)) {
331 if (log.isDebugEnabled()) {
332 log.debug("Found JAR: " + url);
333 }
334 return true;
335 }
336 } catch (Exception e) {
337
338 } finally {
339 if (is != null) {
340 try {
341 is.close();
342 } catch (Exception e) {
343
344 }
345 }
346 }
347
348 return false;
349 }
350 }