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 }