1 /* |
|
2 * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved. |
|
3 * |
|
4 * Redistribution and use in source and binary forms, with or without |
|
5 * modification, are permitted provided that the following conditions |
|
6 * are met: |
|
7 * |
|
8 * - Redistributions of source code must retain the above copyright |
|
9 * notice, this list of conditions and the following disclaimer. |
|
10 * |
|
11 * - Redistributions in binary form must reproduce the above copyright |
|
12 * notice, this list of conditions and the following disclaimer in the |
|
13 * documentation and/or other materials provided with the distribution. |
|
14 * |
|
15 * - Neither the name of Oracle nor the names of its |
|
16 * contributors may be used to endorse or promote products derived |
|
17 * from this software without specific prior written permission. |
|
18 * |
|
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
|
20 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
|
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
|
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
|
27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
|
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
|
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
30 */ |
|
31 |
|
32 /* |
|
33 * This source code is provided to illustrate the usage of a given feature |
|
34 * or technique and has been deliberately simplified. Additional steps |
|
35 * required for a production-quality application, such as security checks, |
|
36 * input validation and proper error handling, might not be present in |
|
37 * this sample code. |
|
38 */ |
|
39 |
|
40 |
|
41 package com.sun.nio.zipfs; |
|
42 |
|
43 import java.io.*; |
|
44 import java.nio.channels.*; |
|
45 import java.nio.file.*; |
|
46 import java.nio.file.DirectoryStream.Filter; |
|
47 import java.nio.file.attribute.*; |
|
48 import java.nio.file.spi.FileSystemProvider; |
|
49 import java.net.URI; |
|
50 import java.net.URISyntaxException; |
|
51 import java.util.HashMap; |
|
52 import java.util.Map; |
|
53 import java.util.Set; |
|
54 import java.util.zip.ZipError; |
|
55 import java.util.concurrent.ExecutorService; |
|
56 |
|
57 /* |
|
58 * |
|
59 * @author Xueming Shen, Rajendra Gutupalli, Jaya Hangal |
|
60 */ |
|
61 |
|
62 public class ZipFileSystemProvider extends FileSystemProvider { |
|
63 |
|
64 |
|
65 private final Map<Path, ZipFileSystem> filesystems = new HashMap<>(); |
|
66 |
|
67 public ZipFileSystemProvider() {} |
|
68 |
|
69 @Override |
|
70 public String getScheme() { |
|
71 return "jar"; |
|
72 } |
|
73 |
|
74 protected Path uriToPath(URI uri) { |
|
75 String scheme = uri.getScheme(); |
|
76 if ((scheme == null) || !scheme.equalsIgnoreCase(getScheme())) { |
|
77 throw new IllegalArgumentException("URI scheme is not '" + getScheme() + "'"); |
|
78 } |
|
79 try { |
|
80 // only support legacy JAR URL syntax jar:{uri}!/{entry} for now |
|
81 String spec = uri.getRawSchemeSpecificPart(); |
|
82 int sep = spec.indexOf("!/"); |
|
83 if (sep != -1) |
|
84 spec = spec.substring(0, sep); |
|
85 return Paths.get(new URI(spec)).toAbsolutePath(); |
|
86 } catch (URISyntaxException e) { |
|
87 throw new IllegalArgumentException(e.getMessage(), e); |
|
88 } |
|
89 } |
|
90 |
|
91 private boolean ensureFile(Path path) { |
|
92 try { |
|
93 BasicFileAttributes attrs = |
|
94 Files.readAttributes(path, BasicFileAttributes.class); |
|
95 if (!attrs.isRegularFile()) |
|
96 throw new UnsupportedOperationException(); |
|
97 return true; |
|
98 } catch (IOException ioe) { |
|
99 return false; |
|
100 } |
|
101 } |
|
102 |
|
103 @Override |
|
104 public FileSystem newFileSystem(URI uri, Map<String, ?> env) |
|
105 throws IOException |
|
106 { |
|
107 Path path = uriToPath(uri); |
|
108 synchronized(filesystems) { |
|
109 Path realPath = null; |
|
110 if (ensureFile(path)) { |
|
111 realPath = path.toRealPath(); |
|
112 if (filesystems.containsKey(realPath)) |
|
113 throw new FileSystemAlreadyExistsException(); |
|
114 } |
|
115 ZipFileSystem zipfs = null; |
|
116 try { |
|
117 zipfs = new ZipFileSystem(this, path, env); |
|
118 } catch (ZipError ze) { |
|
119 String pname = path.toString(); |
|
120 if (pname.endsWith(".zip") || pname.endsWith(".jar")) |
|
121 throw ze; |
|
122 // assume NOT a zip/jar file |
|
123 throw new UnsupportedOperationException(); |
|
124 } |
|
125 filesystems.put(realPath, zipfs); |
|
126 return zipfs; |
|
127 } |
|
128 } |
|
129 |
|
130 @Override |
|
131 public FileSystem newFileSystem(Path path, Map<String, ?> env) |
|
132 throws IOException |
|
133 { |
|
134 if (path.getFileSystem() != FileSystems.getDefault()) { |
|
135 throw new UnsupportedOperationException(); |
|
136 } |
|
137 ensureFile(path); |
|
138 try { |
|
139 return new ZipFileSystem(this, path, env); |
|
140 } catch (ZipError ze) { |
|
141 String pname = path.toString(); |
|
142 if (pname.endsWith(".zip") || pname.endsWith(".jar")) |
|
143 throw ze; |
|
144 throw new UnsupportedOperationException(); |
|
145 } |
|
146 } |
|
147 |
|
148 @Override |
|
149 public Path getPath(URI uri) { |
|
150 |
|
151 String spec = uri.getSchemeSpecificPart(); |
|
152 int sep = spec.indexOf("!/"); |
|
153 if (sep == -1) |
|
154 throw new IllegalArgumentException("URI: " |
|
155 + uri |
|
156 + " does not contain path info ex. jar:file:/c:/foo.zip!/BAR"); |
|
157 return getFileSystem(uri).getPath(spec.substring(sep + 1)); |
|
158 } |
|
159 |
|
160 |
|
161 @Override |
|
162 public FileSystem getFileSystem(URI uri) { |
|
163 synchronized (filesystems) { |
|
164 ZipFileSystem zipfs = null; |
|
165 try { |
|
166 zipfs = filesystems.get(uriToPath(uri).toRealPath()); |
|
167 } catch (IOException x) { |
|
168 // ignore the ioe from toRealPath(), return FSNFE |
|
169 } |
|
170 if (zipfs == null) |
|
171 throw new FileSystemNotFoundException(); |
|
172 return zipfs; |
|
173 } |
|
174 } |
|
175 |
|
176 // Checks that the given file is a UnixPath |
|
177 static final ZipPath toZipPath(Path path) { |
|
178 if (path == null) |
|
179 throw new NullPointerException(); |
|
180 if (!(path instanceof ZipPath)) |
|
181 throw new ProviderMismatchException(); |
|
182 return (ZipPath)path; |
|
183 } |
|
184 |
|
185 @Override |
|
186 public void checkAccess(Path path, AccessMode... modes) throws IOException { |
|
187 toZipPath(path).checkAccess(modes); |
|
188 } |
|
189 |
|
190 @Override |
|
191 public void copy(Path src, Path target, CopyOption... options) |
|
192 throws IOException |
|
193 { |
|
194 toZipPath(src).copy(toZipPath(target), options); |
|
195 } |
|
196 |
|
197 @Override |
|
198 public void createDirectory(Path path, FileAttribute<?>... attrs) |
|
199 throws IOException |
|
200 { |
|
201 toZipPath(path).createDirectory(attrs); |
|
202 } |
|
203 |
|
204 @Override |
|
205 public final void delete(Path path) throws IOException { |
|
206 toZipPath(path).delete(); |
|
207 } |
|
208 |
|
209 @Override |
|
210 @SuppressWarnings("unchecked") |
|
211 public <V extends FileAttributeView> V |
|
212 getFileAttributeView(Path path, Class<V> type, LinkOption... options) |
|
213 { |
|
214 return ZipFileAttributeView.get(toZipPath(path), type); |
|
215 } |
|
216 |
|
217 @Override |
|
218 public FileStore getFileStore(Path path) throws IOException { |
|
219 return toZipPath(path).getFileStore(); |
|
220 } |
|
221 |
|
222 @Override |
|
223 public boolean isHidden(Path path) { |
|
224 return toZipPath(path).isHidden(); |
|
225 } |
|
226 |
|
227 @Override |
|
228 public boolean isSameFile(Path path, Path other) throws IOException { |
|
229 return toZipPath(path).isSameFile(other); |
|
230 } |
|
231 |
|
232 @Override |
|
233 public void move(Path src, Path target, CopyOption... options) |
|
234 throws IOException |
|
235 { |
|
236 toZipPath(src).move(toZipPath(target), options); |
|
237 } |
|
238 |
|
239 @Override |
|
240 public AsynchronousFileChannel newAsynchronousFileChannel(Path path, |
|
241 Set<? extends OpenOption> options, |
|
242 ExecutorService exec, |
|
243 FileAttribute<?>... attrs) |
|
244 throws IOException |
|
245 { |
|
246 throw new UnsupportedOperationException(); |
|
247 } |
|
248 |
|
249 @Override |
|
250 public SeekableByteChannel newByteChannel(Path path, |
|
251 Set<? extends OpenOption> options, |
|
252 FileAttribute<?>... attrs) |
|
253 throws IOException |
|
254 { |
|
255 return toZipPath(path).newByteChannel(options, attrs); |
|
256 } |
|
257 |
|
258 @Override |
|
259 public DirectoryStream<Path> newDirectoryStream( |
|
260 Path path, Filter<? super Path> filter) throws IOException |
|
261 { |
|
262 return toZipPath(path).newDirectoryStream(filter); |
|
263 } |
|
264 |
|
265 @Override |
|
266 public FileChannel newFileChannel(Path path, |
|
267 Set<? extends OpenOption> options, |
|
268 FileAttribute<?>... attrs) |
|
269 throws IOException |
|
270 { |
|
271 return toZipPath(path).newFileChannel(options, attrs); |
|
272 } |
|
273 |
|
274 @Override |
|
275 public InputStream newInputStream(Path path, OpenOption... options) |
|
276 throws IOException |
|
277 { |
|
278 return toZipPath(path).newInputStream(options); |
|
279 } |
|
280 |
|
281 @Override |
|
282 public OutputStream newOutputStream(Path path, OpenOption... options) |
|
283 throws IOException |
|
284 { |
|
285 return toZipPath(path).newOutputStream(options); |
|
286 } |
|
287 |
|
288 @Override |
|
289 public <A extends BasicFileAttributes> A |
|
290 readAttributes(Path path, Class<A> type, LinkOption... options) |
|
291 throws IOException |
|
292 { |
|
293 if (type == BasicFileAttributes.class || type == ZipFileAttributes.class) |
|
294 return (A)toZipPath(path).getAttributes(); |
|
295 return null; |
|
296 } |
|
297 |
|
298 @Override |
|
299 public Map<String, Object> |
|
300 readAttributes(Path path, String attribute, LinkOption... options) |
|
301 throws IOException |
|
302 { |
|
303 return toZipPath(path).readAttributes(attribute, options); |
|
304 } |
|
305 |
|
306 @Override |
|
307 public Path readSymbolicLink(Path link) throws IOException { |
|
308 throw new UnsupportedOperationException("Not supported."); |
|
309 } |
|
310 |
|
311 @Override |
|
312 public void setAttribute(Path path, String attribute, |
|
313 Object value, LinkOption... options) |
|
314 throws IOException |
|
315 { |
|
316 toZipPath(path).setAttribute(attribute, value, options); |
|
317 } |
|
318 |
|
319 ////////////////////////////////////////////////////////////// |
|
320 void removeFileSystem(Path zfpath, ZipFileSystem zfs) throws IOException { |
|
321 synchronized (filesystems) { |
|
322 zfpath = zfpath.toRealPath(); |
|
323 if (filesystems.get(zfpath) == zfs) |
|
324 filesystems.remove(zfpath); |
|
325 } |
|
326 } |
|
327 } |
|