# HG changeset patch # User sherman # Date 1460750752 25200 # Node ID 9cc4eb4d749126c99fdc71b9d3be1109d9597f6b # Parent 80be215c8c517f5f8c3cdec916d9cb73f47aa4dd 8147460: Clean-up jrtfs implementation Reviewed-by: alanb, jlaskey, sundar diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java --- a/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java Fri Apr 15 10:14:57 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/jimage/ImageReader.java Fri Apr 15 13:05:52 2016 -0700 @@ -136,7 +136,7 @@ private final BasicFileAttributes fileAttrs; private boolean completed; - Node(String name, BasicFileAttributes fileAttrs) { + protected Node(String name, BasicFileAttributes fileAttrs) { this.name = Objects.requireNonNull(name); this.fileAttrs = Objects.requireNonNull(fileAttrs); } diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/src/java.base/share/classes/jdk/internal/jrtfs/AbstractJrtFileAttributes.java --- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/AbstractJrtFileAttributes.java Fri Apr 15 10:14:57 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.internal.jrtfs; - -import java.nio.file.attribute.BasicFileAttributes; -import java.util.Formatter; - -/** - * Base class for file attributes supported by jrt file systems. - * - * @implNote This class needs to maintain JDK 8 source compatibility. - * - * It is used internally in the JDK to implement jimage/jrtfs access, - * but also compiled and delivered as part of the jrtfs.jar to support access - * to the jimage file provided by the shipped JDK by tools running on JDK 8. - */ -public abstract class AbstractJrtFileAttributes implements BasicFileAttributes { - - // jrt fs specific attributes - /** - * Compressed resource file. If not available or not applicable, 0L is - * returned. - * - * @return the compressed resource size for compressed resources. - */ - public abstract long compressedSize(); - - /** - * "file" extension of a file resource. - * - * @return extension string for the file resource - */ - public abstract String extension(); - - @Override - public final String toString() { - StringBuilder sb = new StringBuilder(1024); - try (Formatter fm = new Formatter(sb)) { - if (creationTime() != null) { - fm.format(" creationTime : %tc%n", creationTime().toMillis()); - } else { - fm.format(" creationTime : null%n"); - } - - if (lastAccessTime() != null) { - fm.format(" lastAccessTime : %tc%n", lastAccessTime().toMillis()); - } else { - fm.format(" lastAccessTime : null%n"); - } - fm.format(" lastModifiedTime: %tc%n", lastModifiedTime().toMillis()); - fm.format(" isRegularFile : %b%n", isRegularFile()); - fm.format(" isDirectory : %b%n", isDirectory()); - fm.format(" isSymbolicLink : %b%n", isSymbolicLink()); - fm.format(" isOther : %b%n", isOther()); - fm.format(" fileKey : %s%n", fileKey()); - fm.format(" size : %d%n", size()); - fm.format(" compressedSize : %d%n", compressedSize()); - fm.format(" extension : %s%n", extension()); - } - return sb.toString(); - } -} diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/src/java.base/share/classes/jdk/internal/jrtfs/AbstractJrtFileSystem.java --- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/AbstractJrtFileSystem.java Fri Apr 15 10:14:57 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,372 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.internal.jrtfs; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.nio.ByteBuffer; -import java.nio.channels.Channels; -import java.nio.channels.FileChannel; -import java.nio.channels.NonWritableChannelException; -import java.nio.channels.ReadableByteChannel; -import java.nio.channels.SeekableByteChannel; -import java.nio.charset.Charset; -import java.nio.file.ClosedFileSystemException; -import java.nio.file.CopyOption; -import java.nio.file.FileStore; -import java.nio.file.FileSystem; -import java.nio.file.FileSystemNotFoundException; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.OpenOption; -import java.nio.file.Path; -import java.nio.file.PathMatcher; -import java.nio.file.ReadOnlyFileSystemException; -import java.nio.file.StandardOpenOption; -import java.nio.file.WatchService; -import java.nio.file.attribute.FileAttribute; -import java.nio.file.attribute.FileTime; -import java.nio.file.attribute.UserPrincipalLookupService; -import java.nio.file.spi.FileSystemProvider; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; -import java.util.regex.Pattern; - -/** - * Base class for jrt file systems. jrt filesystem implementations are currently - * available on top of .jimage file and on top "exploded" build directories. - * - * @implNote This class needs to maintain JDK 8 source compatibility. - * - * It is used internally in the JDK to implement jimage/jrtfs access, - * but also compiled and delivered as part of the jrtfs.jar to support access - * to the jimage file provided by the shipped JDK by tools running on JDK 8. - */ -abstract class AbstractJrtFileSystem extends FileSystem { - - private final JrtFileSystemProvider provider; - - AbstractJrtFileSystem(JrtFileSystemProvider provider, Map options) { - this.provider = provider; - } - - private static final Charset UTF_8 = Charset.forName("UTF-8"); - - // static utility methods - static ReadOnlyFileSystemException readOnly() { - return new ReadOnlyFileSystemException(); - } - - // if a Path does not exist, throw exception - static void checkExists(Path path) { - if (Files.notExists(path)) { - throw new FileSystemNotFoundException(path.toString()); - } - } - - static byte[] getBytes(String name) { - return name.getBytes(UTF_8); - } - - static String getString(byte[] name) { - return new String(name, UTF_8); - } - - // do the supplied options imply that we have to chase symlinks? - static boolean followLinks(LinkOption... options) { - if (options != null) { - for (LinkOption lo : options) { - if (lo == LinkOption.NOFOLLOW_LINKS) { - return false; - } else if (lo == null) { - throw new NullPointerException(); - } else { - throw new AssertionError("should not reach here"); - } - } - } - return true; - } - - // check that the options passed are supported by (read-only) jrt file system - static void checkOptions(Set options) { - // check for options of null type and option is an intance of StandardOpenOption - for (OpenOption option : options) { - if (option == null) { - throw new NullPointerException(); - } - if (!(option instanceof StandardOpenOption)) { - throw new IllegalArgumentException(); - } - } - - if (options.contains(StandardOpenOption.WRITE) - || options.contains(StandardOpenOption.APPEND)) { - throw readOnly(); - } - } - - // FileSystem method implementations - @Override - public FileSystemProvider provider() { - return provider; - } - - @Override - public Iterable getRootDirectories() { - ArrayList pathArr = new ArrayList<>(); - pathArr.add(getRootPath()); - return pathArr; - } - - @Override - public AbstractJrtPath getPath(String first, String... more) { - String path; - if (more.length == 0) { - path = first; - } else { - StringBuilder sb = new StringBuilder(); - sb.append(first); - for (String segment : more) { - if (segment.length() > 0) { - if (sb.length() > 0) { - sb.append('/'); - } - sb.append(segment); - } - } - path = sb.toString(); - } - return getRootPath().newJrtPath(getBytes(path)); - } - - @Override - public final boolean isReadOnly() { - return true; - } - - @Override - public final UserPrincipalLookupService getUserPrincipalLookupService() { - throw new UnsupportedOperationException(); - } - - @Override - public final WatchService newWatchService() { - throw new UnsupportedOperationException(); - } - - @Override - public final Iterable getFileStores() { - ArrayList list = new ArrayList<>(1); - list.add(getFileStore(getRootPath())); - return list; - } - - private static final Set supportedFileAttributeViews - = Collections.unmodifiableSet( - new HashSet(Arrays.asList("basic", "jrt"))); - - @Override - public final Set supportedFileAttributeViews() { - return supportedFileAttributeViews; - } - - @Override - public final String toString() { - return "jrt:/"; - } - - @Override - public final String getSeparator() { - return "/"; - } - - private static final String GLOB_SYNTAX = "glob"; - private static final String REGEX_SYNTAX = "regex"; - - @Override - public PathMatcher getPathMatcher(String syntaxAndInput) { - int pos = syntaxAndInput.indexOf(':'); - if (pos <= 0 || pos == syntaxAndInput.length()) { - throw new IllegalArgumentException(); - } - String syntax = syntaxAndInput.substring(0, pos); - String input = syntaxAndInput.substring(pos + 1); - String expr; - if (syntax.equalsIgnoreCase(GLOB_SYNTAX)) { - expr = JrtUtils.toRegexPattern(input); - } else { - if (syntax.equalsIgnoreCase(REGEX_SYNTAX)) { - expr = input; - } else { - throw new UnsupportedOperationException("Syntax '" + syntax - + "' not recognized"); - } - } - // return matcher - final Pattern pattern = Pattern.compile(expr); - return (Path path) -> pattern.matcher(path.toString()).matches(); - } - - // These methods throw read only file system exception - final void setTimes(AbstractJrtPath jrtPath, FileTime mtime, FileTime atime, FileTime ctime) - throws IOException { - throw readOnly(); - } - - final void createDirectory(AbstractJrtPath jrtPath, FileAttribute... attrs) throws IOException { - throw readOnly(); - } - - final void deleteFile(AbstractJrtPath jrtPath, boolean failIfNotExists) - throws IOException { - throw readOnly(); - } - - final OutputStream newOutputStream(AbstractJrtPath jrtPath, OpenOption... options) - throws IOException { - throw readOnly(); - } - - final void copyFile(boolean deletesrc, AbstractJrtPath srcPath, AbstractJrtPath dstPath, CopyOption... options) - throws IOException { - throw readOnly(); - } - - final FileChannel newFileChannel(AbstractJrtPath jrtPath, - Set options, - FileAttribute... attrs) - throws IOException { - throw new UnsupportedOperationException("newFileChannel"); - } - - final InputStream newInputStream(AbstractJrtPath jrtPath) throws IOException { - return new ByteArrayInputStream(getFileContent(jrtPath)); - } - - final SeekableByteChannel newByteChannel(AbstractJrtPath jrtPath, - Set options, - FileAttribute... attrs) - throws IOException { - checkOptions(options); - - byte[] buf = getFileContent(jrtPath); - final ReadableByteChannel rbc - = Channels.newChannel(new ByteArrayInputStream(buf)); - final long size = buf.length; - return new SeekableByteChannel() { - long read = 0; - - @Override - public boolean isOpen() { - return rbc.isOpen(); - } - - @Override - public long position() throws IOException { - return read; - } - - @Override - public SeekableByteChannel position(long pos) - throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public int read(ByteBuffer dst) throws IOException { - int n = rbc.read(dst); - if (n > 0) { - read += n; - } - return n; - } - - @Override - public SeekableByteChannel truncate(long size) - throws IOException { - throw new NonWritableChannelException(); - } - - @Override - public int write(ByteBuffer src) throws IOException { - throw new NonWritableChannelException(); - } - - @Override - public long size() throws IOException { - return size; - } - - @Override - public void close() throws IOException { - rbc.close(); - } - }; - } - - final JrtFileStore getFileStore(AbstractJrtPath jrtPath) { - return new JrtFileStore(jrtPath); - } - - final void ensureOpen() throws IOException { - if (!isOpen()) { - throw new ClosedFileSystemException(); - } - } - - // abstract methods to be implemented by a particular jrt file system - abstract AbstractJrtPath getRootPath(); - - abstract boolean isSameFile(AbstractJrtPath jrtPath1, AbstractJrtPath jrtPath2) throws IOException; - - abstract boolean isLink(AbstractJrtPath jrtPath) throws IOException; - - abstract AbstractJrtPath resolveLink(AbstractJrtPath jrtPath) throws IOException; - - abstract AbstractJrtFileAttributes getFileAttributes(AbstractJrtPath jrtPath, LinkOption... options) throws IOException; - - abstract boolean exists(AbstractJrtPath jrtPath) throws IOException; - - abstract boolean isDirectory(AbstractJrtPath jrtPath, boolean resolveLinks) throws IOException; - - /** - * returns the list of child paths of the given directory "path" - * - * @param path name of the directory whose content is listed - * @return iterator for child paths of the given directory path - */ - abstract Iterator iteratorOf(AbstractJrtPath jrtPath) throws IOException; - - // returns the content of the file resource specified by the path - abstract byte[] getFileContent(AbstractJrtPath jrtPath) throws IOException; -} diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/src/java.base/share/classes/jdk/internal/jrtfs/AbstractJrtPath.java --- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/AbstractJrtPath.java Fri Apr 15 10:14:57 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,935 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.internal.jrtfs; - -import java.io.*; -import java.net.URI; -import java.net.URISyntaxException; -import java.nio.channels.*; -import java.nio.file.*; -import java.nio.file.DirectoryStream.Filter; -import java.nio.file.attribute.*; -import java.util.*; -import static java.nio.file.StandardOpenOption.*; -import static java.nio.file.StandardCopyOption.*; - -/** - * Base class for Path implementation of jrt file systems. - * - * @implNote This class needs to maintain JDK 8 source compatibility. - * - * It is used internally in the JDK to implement jimage/jrtfs access, - * but also compiled and delivered as part of the jrtfs.jar to support access - * to the jimage file provided by the shipped JDK by tools running on JDK 8. - */ -abstract class AbstractJrtPath implements Path { - - protected final AbstractJrtFileSystem jrtfs; - private final byte[] path; - private volatile int[] offsets; - private int hashcode = 0; // cached hashcode (created lazily) - - AbstractJrtPath(AbstractJrtFileSystem jrtfs, byte[] path) { - this(jrtfs, path, false); - this.resolved = null; - } - - AbstractJrtPath(AbstractJrtFileSystem jrtfs, byte[] path, boolean normalized) { - this.resolved = null; - this.jrtfs = jrtfs; - if (normalized) { - this.path = path; - } else { - this.path = normalize(path); - } - } - - // factory methods to create subtypes of AbstractJrtPath - protected abstract AbstractJrtPath newJrtPath(byte[] path); - - protected abstract AbstractJrtPath newJrtPath(byte[] path, boolean normalized); - - final byte[] getName() { - return path; - } - - @Override - public final AbstractJrtPath getRoot() { - if (this.isAbsolute()) { - return jrtfs.getRootPath(); - } else { - return null; - } - } - - @Override - public final AbstractJrtPath getFileName() { - initOffsets(); - int count = offsets.length; - if (count == 0) { - return null; // no elements so no name - } - if (count == 1 && path[0] != '/') { - return this; - } - int lastOffset = offsets[count - 1]; - int len = path.length - lastOffset; - byte[] result = new byte[len]; - System.arraycopy(path, lastOffset, result, 0, len); - return newJrtPath(result); - } - - @Override - public final AbstractJrtPath getParent() { - initOffsets(); - int count = offsets.length; - if (count == 0) // no elements so no parent - { - return null; - } - int len = offsets[count - 1] - 1; - if (len <= 0) // parent is root only (may be null) - { - return getRoot(); - } - byte[] result = new byte[len]; - System.arraycopy(path, 0, result, 0, len); - return newJrtPath(result); - } - - @Override - public final int getNameCount() { - initOffsets(); - return offsets.length; - } - - @Override - public final AbstractJrtPath getName(int index) { - initOffsets(); - if (index < 0 || index >= offsets.length) { - throw new IllegalArgumentException(); - } - int begin = offsets[index]; - int len; - if (index == (offsets.length - 1)) { - len = path.length - begin; - } else { - len = offsets[index + 1] - begin - 1; - } - // construct result - byte[] result = new byte[len]; - System.arraycopy(path, begin, result, 0, len); - return newJrtPath(result); - } - - @Override - public final AbstractJrtPath subpath(int beginIndex, int endIndex) { - initOffsets(); - if (beginIndex < 0 - || beginIndex >= offsets.length - || endIndex > offsets.length - || beginIndex >= endIndex) { - throw new IllegalArgumentException(); - } - - // starting offset and length - int begin = offsets[beginIndex]; - int len; - if (endIndex == offsets.length) { - len = path.length - begin; - } else { - len = offsets[endIndex] - begin - 1; - } - // construct result - byte[] result = new byte[len]; - System.arraycopy(path, begin, result, 0, len); - return newJrtPath(result); - } - - @Override - public final AbstractJrtPath toRealPath(LinkOption... options) throws IOException { - AbstractJrtPath realPath = newJrtPath(getResolvedPath()).toAbsolutePath(); - realPath = JrtFileSystem.followLinks(options) ? jrtfs.resolveLink(this) : realPath; - realPath.checkAccess(); - return realPath; - } - - final AbstractJrtPath readSymbolicLink() throws IOException { - if (!jrtfs.isLink(this)) { - throw new IOException("not a symbolic link"); - } - - return jrtfs.resolveLink(this); - } - - final boolean isHidden() { - return false; - } - - @Override - public final AbstractJrtPath toAbsolutePath() { - if (isAbsolute()) { - return this; - } else { - //add / bofore the existing path - byte[] tmp = new byte[path.length + 1]; - tmp[0] = '/'; - System.arraycopy(path, 0, tmp, 1, path.length); - return newJrtPath(tmp).normalize(); - } - } - - @Override - public final URI toUri() { - try { - return new URI("jrt", - JrtFileSystem.getString(toAbsolutePath().path), - null); - } catch (URISyntaxException ex) { - throw new AssertionError(ex); - } - } - - private boolean equalsNameAt(AbstractJrtPath other, int index) { - int mbegin = offsets[index]; - int mlen; - if (index == (offsets.length - 1)) { - mlen = path.length - mbegin; - } else { - mlen = offsets[index + 1] - mbegin - 1; - } - int obegin = other.offsets[index]; - int olen; - if (index == (other.offsets.length - 1)) { - olen = other.path.length - obegin; - } else { - olen = other.offsets[index + 1] - obegin - 1; - } - if (mlen != olen) { - return false; - } - int n = 0; - while (n < mlen) { - if (path[mbegin + n] != other.path[obegin + n]) { - return false; - } - n++; - } - return true; - } - - @Override - public final AbstractJrtPath relativize(Path other) { - final AbstractJrtPath o = checkPath(other); - if (o.equals(this)) { - return newJrtPath(new byte[0], true); - } - if (/* this.getFileSystem() != o.getFileSystem() || */this.isAbsolute() != o.isAbsolute()) { - throw new IllegalArgumentException(); - } - int mc = this.getNameCount(); - int oc = o.getNameCount(); - int n = Math.min(mc, oc); - int i = 0; - while (i < n) { - if (!equalsNameAt(o, i)) { - break; - } - i++; - } - int dotdots = mc - i; - int len = dotdots * 3 - 1; - if (i < oc) { - len += (o.path.length - o.offsets[i] + 1); - } - byte[] result = new byte[len]; - - int pos = 0; - while (dotdots > 0) { - result[pos++] = (byte) '.'; - result[pos++] = (byte) '.'; - if (pos < len) // no tailing slash at the end - { - result[pos++] = (byte) '/'; - } - dotdots--; - } - if (i < oc) { - System.arraycopy(o.path, o.offsets[i], - result, pos, - o.path.length - o.offsets[i]); - } - return newJrtPath(result); - } - - @Override - public AbstractJrtFileSystem getFileSystem() { - return jrtfs; - } - - @Override - public final boolean isAbsolute() { - return (this.path.length > 0 && path[0] == '/'); - } - - @Override - public final AbstractJrtPath resolve(Path other) { - final AbstractJrtPath o = checkPath(other); - if (o.isAbsolute()) { - return o; - } - byte[] res; - if (this.path[path.length - 1] == '/') { - res = new byte[path.length + o.path.length]; - System.arraycopy(path, 0, res, 0, path.length); - System.arraycopy(o.path, 0, res, path.length, o.path.length); - } else { - res = new byte[path.length + 1 + o.path.length]; - System.arraycopy(path, 0, res, 0, path.length); - res[path.length] = '/'; - System.arraycopy(o.path, 0, res, path.length + 1, o.path.length); - } - return newJrtPath(res); - } - - @Override - public final Path resolveSibling(Path other) { - if (other == null) { - throw new NullPointerException(); - } - Path parent = getParent(); - return (parent == null) ? other : parent.resolve(other); - } - - @Override - public final boolean startsWith(Path other) { - final AbstractJrtPath o = checkPath(other); - if (o.isAbsolute() != this.isAbsolute() - || o.path.length > this.path.length) { - return false; - } - int olast = o.path.length; - for (int i = 0; i < olast; i++) { - if (o.path[i] != this.path[i]) { - return false; - } - } - olast--; - return o.path.length == this.path.length - || o.path[olast] == '/' - || this.path[olast + 1] == '/'; - } - - @Override - public final boolean endsWith(Path other) { - final AbstractJrtPath o = checkPath(other); - int olast = o.path.length - 1; - if (olast > 0 && o.path[olast] == '/') { - olast--; - } - int last = this.path.length - 1; - if (last > 0 && this.path[last] == '/') { - last--; - } - if (olast == -1) // o.path.length == 0 - { - return last == -1; - } - if ((o.isAbsolute() && (!this.isAbsolute() || olast != last)) - || (last < olast)) { - return false; - } - for (; olast >= 0; olast--, last--) { - if (o.path[olast] != this.path[last]) { - return false; - } - } - return o.path[olast + 1] == '/' - || last == -1 || this.path[last] == '/'; - } - - @Override - public final AbstractJrtPath resolve(String other) { - return resolve(getFileSystem().getPath(other)); - } - - @Override - public final Path resolveSibling(String other) { - return resolveSibling(getFileSystem().getPath(other)); - } - - @Override - public final boolean startsWith(String other) { - return startsWith(getFileSystem().getPath(other)); - } - - @Override - public final boolean endsWith(String other) { - return endsWith(getFileSystem().getPath(other)); - } - - @Override - public final AbstractJrtPath normalize() { - byte[] res = getResolved(); - if (res == path) // no change - { - return this; - } - return newJrtPath(res, true); - } - - private AbstractJrtPath checkPath(Path path) { - if (path == null) { - throw new NullPointerException(); - } - if (!(path instanceof AbstractJrtPath)) { - throw new ProviderMismatchException(); - } - return (AbstractJrtPath) path; - } - - // create offset list if not already created - private void initOffsets() { - if (offsets == null) { - int count, index; - // count names - count = 0; - index = 0; - while (index < path.length) { - byte c = path[index++]; - if (c != '/') { - count++; - while (index < path.length && path[index] != '/') { - index++; - } - } - } - // populate offsets - int[] result = new int[count]; - count = 0; - index = 0; - while (index < path.length) { - byte c = path[index]; - if (c == '/') { - index++; - } else { - result[count++] = index++; - while (index < path.length && path[index] != '/') { - index++; - } - } - } - synchronized (this) { - if (offsets == null) { - offsets = result; - } - } - } - } - - private volatile byte[] resolved; - - final byte[] getResolvedPath() { - byte[] r = resolved; - if (r == null) { - if (isAbsolute()) { - r = getResolved(); - } else { - r = toAbsolutePath().getResolvedPath(); - } - resolved = r; - } - return resolved; - } - - // removes redundant slashs, replace "\" to separator "/" - // and check for invalid characters - private static byte[] normalize(byte[] path) { - if (path.length == 0) { - return path; - } - byte prevC = 0; - for (int i = 0; i < path.length; i++) { - byte c = path[i]; - if (c == '\\') { - return normalize(path, i); - } - if (c == (byte) '/' && prevC == '/') { - return normalize(path, i - 1); - } - if (c == '\u0000') { - throw new InvalidPathException(JrtFileSystem.getString(path), - "Path: nul character not allowed"); - } - prevC = c; - } - - if (path.length > 1 && path[path.length - 1] == '/') { - return Arrays.copyOf(path, path.length - 1); - } - - return path; - } - - private static byte[] normalize(byte[] path, int off) { - byte[] to = new byte[path.length]; - int n = 0; - while (n < off) { - to[n] = path[n]; - n++; - } - int m = n; - byte prevC = 0; - while (n < path.length) { - byte c = path[n++]; - if (c == (byte) '\\') { - c = (byte) '/'; - } - if (c == (byte) '/' && prevC == (byte) '/') { - continue; - } - if (c == '\u0000') { - throw new InvalidPathException(JrtFileSystem.getString(path), - "Path: nul character not allowed"); - } - to[m++] = c; - prevC = c; - } - if (m > 1 && to[m - 1] == '/') { - m--; - } - return (m == to.length) ? to : Arrays.copyOf(to, m); - } - - // Remove DotSlash(./) and resolve DotDot (..) components - private byte[] getResolved() { - if (path.length == 0) { - return path; - } - for (int i = 0; i < path.length; i++) { - byte c = path[i]; - if (c == (byte) '.') { - return resolve0(); - } - } - - return path; - } - - // TBD: performance, avoid initOffsets - private byte[] resolve0() { - byte[] to = new byte[path.length]; - int nc = getNameCount(); - int[] lastM = new int[nc]; - int lastMOff = -1; - int m = 0; - for (int i = 0; i < nc; i++) { - int n = offsets[i]; - int len = (i == offsets.length - 1) - ? (path.length - n) : (offsets[i + 1] - n - 1); - if (len == 1 && path[n] == (byte) '.') { - if (m == 0 && path[0] == '/') // absolute path - { - to[m++] = '/'; - } - continue; - } - if (len == 2 && path[n] == '.' && path[n + 1] == '.') { - if (lastMOff >= 0) { - m = lastM[lastMOff--]; // retreat - continue; - } - if (path[0] == '/') { // "/../xyz" skip - if (m == 0) { - to[m++] = '/'; - } - } else { // "../xyz" -> "../xyz" - if (m != 0 && to[m - 1] != '/') { - to[m++] = '/'; - } - while (len-- > 0) { - to[m++] = path[n++]; - } - } - continue; - } - if (m == 0 && path[0] == '/' || // absolute path - m != 0 && to[m - 1] != '/') { // not the first name - to[m++] = '/'; - } - lastM[++lastMOff] = m; - while (len-- > 0) { - to[m++] = path[n++]; - } - } - if (m > 1 && to[m - 1] == '/') { - m--; - } - return (m == to.length) ? to : Arrays.copyOf(to, m); - } - - @Override - public final String toString() { - return JrtFileSystem.getString(path); - } - - @Override - public final int hashCode() { - int h = hashcode; - if (h == 0) { - hashcode = h = Arrays.hashCode(path); - } - return h; - } - - @Override - public final boolean equals(Object obj) { - return obj != null - && obj instanceof AbstractJrtPath - && this.jrtfs == ((AbstractJrtPath) obj).jrtfs - && compareTo((Path) obj) == 0; - } - - @Override - public final int compareTo(Path other) { - final AbstractJrtPath o = checkPath(other); - int len1 = this.path.length; - int len2 = o.path.length; - - int n = Math.min(len1, len2); - byte v1[] = this.path; - byte v2[] = o.path; - - int k = 0; - while (k < n) { - int c1 = v1[k] & 0xff; - int c2 = v2[k] & 0xff; - if (c1 != c2) { - return c1 - c2; - } - k++; - } - return len1 - len2; - } - - @Override - public final WatchKey register( - WatchService watcher, - WatchEvent.Kind[] events, - WatchEvent.Modifier... modifiers) { - if (watcher == null || events == null || modifiers == null) { - throw new NullPointerException(); - } - throw new UnsupportedOperationException(); - } - - @Override - public final WatchKey register(WatchService watcher, WatchEvent.Kind... events) { - return register(watcher, events, new WatchEvent.Modifier[0]); - } - - @Override - public final File toFile() { - throw new UnsupportedOperationException(); - } - - @Override - public final Iterator iterator() { - return new Iterator() { - private int i = 0; - - @Override - public boolean hasNext() { - return (i < getNameCount()); - } - - @Override - public Path next() { - if (i < getNameCount()) { - Path result = getName(i); - i++; - return result; - } else { - throw new NoSuchElementException(); - } - } - - @Override - public void remove() { - throw new ReadOnlyFileSystemException(); - } - }; - } - - ///////////////////////////////////////////////////////////////////// - // Helpers for JrtFileSystemProvider and JrtFileSystem - final int getPathLength() { - return path.length; - } - - final void createDirectory(FileAttribute... attrs) - throws IOException { - jrtfs.createDirectory(this, attrs); - } - - final InputStream newInputStream(OpenOption... options) throws IOException { - if (options.length > 0) { - for (OpenOption opt : options) { - if (opt != READ) { - throw new UnsupportedOperationException("'" + opt + "' not allowed"); - } - } - } - return jrtfs.newInputStream(this); - } - - final DirectoryStream newDirectoryStream(Filter filter) - throws IOException { - return new JrtDirectoryStream(this, filter); - } - - final void delete() throws IOException { - jrtfs.deleteFile(this, true); - } - - final void deleteIfExists() throws IOException { - jrtfs.deleteFile(this, false); - } - - final AbstractJrtFileAttributes getAttributes(LinkOption... options) throws IOException { - AbstractJrtFileAttributes zfas = jrtfs.getFileAttributes(this, options); - if (zfas == null) { - throw new NoSuchFileException(toString()); - } - return zfas; - } - - final void setAttribute(String attribute, Object value, LinkOption... options) - throws IOException { - String type; - String attr; - int colonPos = attribute.indexOf(':'); - if (colonPos == -1) { - type = "basic"; - attr = attribute; - } else { - type = attribute.substring(0, colonPos++); - attr = attribute.substring(colonPos); - } - JrtFileAttributeView view = JrtFileAttributeView.get(this, type, options); - if (view == null) { - throw new UnsupportedOperationException("view <" + view + "> is not supported"); - } - view.setAttribute(attr, value); - } - - final void setTimes(FileTime mtime, FileTime atime, FileTime ctime) - throws IOException { - jrtfs.setTimes(this, mtime, atime, ctime); - } - - final Map readAttributes(String attributes, LinkOption... options) - throws IOException { - String view; - String attrs; - int colonPos = attributes.indexOf(':'); - if (colonPos == -1) { - view = "basic"; - attrs = attributes; - } else { - view = attributes.substring(0, colonPos++); - attrs = attributes.substring(colonPos); - } - JrtFileAttributeView jrtfv = JrtFileAttributeView.get(this, view, options); - if (jrtfv == null) { - throw new UnsupportedOperationException("view not supported"); - } - return jrtfv.readAttributes(attrs); - } - - final FileStore getFileStore() throws IOException { - // each JrtFileSystem only has one root (as requested for now) - if (exists()) { - return jrtfs.getFileStore(this); - } - throw new NoSuchFileException(JrtFileSystem.getString(path)); - } - - final boolean isSameFile(Path other) throws IOException { - if (this.equals(other)) { - return true; - } - if (other == null - || this.getFileSystem() != other.getFileSystem()) { - return false; - } - this.checkAccess(); - AbstractJrtPath target = (AbstractJrtPath) other; - target.checkAccess(); - return Arrays.equals(this.getResolvedPath(), target.getResolvedPath()) - || jrtfs.isSameFile(this, target); - } - - final SeekableByteChannel newByteChannel(Set options, - FileAttribute... attrs) - throws IOException { - return jrtfs.newByteChannel(this, options, attrs); - } - - final FileChannel newFileChannel(Set options, - FileAttribute... attrs) - throws IOException { - return jrtfs.newFileChannel(this, options, attrs); - } - - final void checkAccess(AccessMode... modes) throws IOException { - boolean w = false; - boolean x = false; - for (AccessMode mode : modes) { - switch (mode) { - case READ: - break; - case WRITE: - w = true; - break; - case EXECUTE: - x = true; - break; - default: - throw new UnsupportedOperationException(); - } - } - - BasicFileAttributes attrs = jrtfs.getFileAttributes(this); - if (attrs == null && (path.length != 1 || path[0] != '/')) { - throw new NoSuchFileException(toString()); - } - if (w) { -// if (jrtfs.isReadOnly()) - throw new AccessDeniedException(toString()); - } - if (x) { - throw new AccessDeniedException(toString()); - } - } - - final boolean exists() { - try { - return jrtfs.exists(this); - } catch (IOException x) { - } - return false; - } - - final OutputStream newOutputStream(OpenOption... options) throws IOException { - if (options.length == 0) { - return jrtfs.newOutputStream(this, - CREATE_NEW, WRITE); - } - return jrtfs.newOutputStream(this, options); - } - - final void move(AbstractJrtPath target, CopyOption... options) - throws IOException { - if (this.jrtfs == target.jrtfs) { - jrtfs.copyFile(true, - this, target, - options); - } else { - copyToTarget(target, options); - delete(); - } - } - - final void copy(AbstractJrtPath target, CopyOption... options) - throws IOException { - if (this.jrtfs == target.jrtfs) { - jrtfs.copyFile(false, - this, target, - options); - } else { - copyToTarget(target, options); - } - } - - private void copyToTarget(AbstractJrtPath target, CopyOption... options) - throws IOException { - boolean replaceExisting = false; - boolean copyAttrs = false; - for (CopyOption opt : options) { - if (opt == REPLACE_EXISTING) { - replaceExisting = true; - } else if (opt == COPY_ATTRIBUTES) { - copyAttrs = true; - } - } - // attributes of source file - BasicFileAttributes jrtfas = getAttributes(); - // check if target exists - boolean exists; - if (replaceExisting) { - try { - target.deleteIfExists(); - exists = false; - } catch (DirectoryNotEmptyException x) { - exists = true; - } - } else { - exists = target.exists(); - } - if (exists) { - throw new FileAlreadyExistsException(target.toString()); - } - - if (jrtfas.isDirectory()) { - // create directory or file - target.createDirectory(); - } else { - try (InputStream is = jrtfs.newInputStream(this); OutputStream os = target.newOutputStream()) { - byte[] buf = new byte[8192]; - int n; - while ((n = is.read(buf)) != -1) { - os.write(buf, 0, n); - } - } - } - if (copyAttrs) { - BasicFileAttributeView view - = JrtFileAttributeView.get(target, BasicFileAttributeView.class); - try { - view.setTimes(jrtfas.lastModifiedTime(), - jrtfas.lastAccessTime(), - jrtfas.creationTime()); - } catch (IOException x) { - // rollback? - try { - target.delete(); - } catch (IOException ignore) { - } - throw x; - } - } - } -} diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/ExplodedImage.java Fri Apr 15 13:05:52 2016 -0700 @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.jrtfs; + +import java.io.IOException; +import java.nio.file.DirectoryStream; +import java.nio.file.FileSystem; +import java.nio.file.FileSystemException; +import java.nio.file.FileSystems; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import jdk.internal.jimage.ImageReader.Node; + +/** + * A jrt file system built on $JAVA_HOME/modules directory ('exploded modules + * build') + * + * @implNote This class needs to maintain JDK 8 source compatibility. + * + * It is used internally in the JDK to implement jimage/jrtfs access, + * but also compiled and delivered as part of the jrtfs.jar to support access + * to the jimage file provided by the shipped JDK by tools running on JDK 8. + */ +class ExplodedImage extends SystemImage { + + private static final String MODULES = "/modules/"; + private static final String PACKAGES = "/packages/"; + private static final int PACKAGES_LEN = PACKAGES.length(); + + private final FileSystem defaultFS; + private final String separator; + private final Map nodes = Collections.synchronizedMap(new HashMap<>()); + private final BasicFileAttributes modulesDirAttrs; + + ExplodedImage(Path modulesDir) throws IOException { + defaultFS = FileSystems.getDefault(); + String str = defaultFS.getSeparator(); + separator = str.equals("/") ? null : str; + modulesDirAttrs = Files.readAttributes(modulesDir, BasicFileAttributes.class); + initNodes(); + } + + // A Node that is backed by actual default file system Path + private final class PathNode extends Node { + + // Path in underlying default file system + private Path path; + private PathNode link; + private List children; + + PathNode(String name, Path path, BasicFileAttributes attrs) { // path + super(name, attrs); + this.path = path; + } + + PathNode(String name, Node link) { // link + super(name, link.getFileAttributes()); + this.link = (PathNode)link; + } + + PathNode(String name, List children) { // dir + super(name, modulesDirAttrs); + this.children = children; + } + + @Override + public boolean isDirectory() { + return children != null || + (link == null && getFileAttributes().isDirectory()); + } + + @Override + public boolean isLink() { + return link != null; + } + + @Override + public PathNode resolveLink(boolean recursive) { + if (link == null) + return this; + return recursive && link.isLink() ? link.resolveLink(true) : link; + } + + byte[] getContent() throws IOException { + if (!getFileAttributes().isRegularFile()) + throw new FileSystemException(getName() + " is not file"); + return Files.readAllBytes(path); + } + + @Override + public List getChildren() { + if (!isDirectory()) + throw new IllegalArgumentException("not a directory: " + getNameString()); + if (children == null) { + List list = new ArrayList<>(); + try (DirectoryStream stream = Files.newDirectoryStream(path)) { + for (Path p : stream) { + p = explodedModulesDir.relativize(p); + String pName = MODULES + nativeSlashToFrontSlash(p.toString()); + Node node = findNode(pName); + if (node != null) { // findNode may choose to hide certain files! + list.add(node); + } + } + } catch (IOException x) { + return null; + } + children = list; + } + return children; + } + } + + @Override + public void close() throws IOException { + nodes.clear(); + } + + @Override + public byte[] getResource(Node node) throws IOException { + return ((PathNode)node).getContent(); + } + + // find Node for the given Path + @Override + public synchronized Node findNode(String str) { + Node node = findModulesNode(str); + if (node != null) { + return node; + } + // lazily created for paths like /packages///xyz + // For example /packages/java.lang/java.base/java/lang/ + if (str.startsWith(PACKAGES)) { + // pkgEndIdx marks end of part + int pkgEndIdx = str.indexOf('/', PACKAGES_LEN); + if (pkgEndIdx != -1) { + // modEndIdx marks end of part + int modEndIdx = str.indexOf('/', pkgEndIdx + 1); + if (modEndIdx != -1) { + // make sure we have such module link! + // ie., /packages// is valid + Node linkNode = nodes.get(str.substring(0, modEndIdx)); + if (linkNode == null || !linkNode.isLink()) { + return null; + } + // map to "/modules/zyz" path and return that node + // For example, "/modules/java.base/java/lang" for + // "/packages/java.lang/java.base/java/lang". + String mod = MODULES + str.substring(pkgEndIdx + 1); + return findModulesNode(mod); + } + } + } + return null; + } + + // find a Node for a path that starts like "/modules/..." + Node findModulesNode(String str) { + PathNode node = nodes.get(str); + if (node != null) { + return node; + } + // lazily created "/modules/xyz/abc/" Node + // This is mapped to default file system path "/xyz/abc" + Path p = underlyingPath(str); + if (p != null) { + try { + BasicFileAttributes attrs = Files.readAttributes(p, BasicFileAttributes.class); + if (attrs.isRegularFile()) { + Path f = p.getFileName(); + if (f.toString().startsWith("_the.")) + return null; + } + node = new PathNode(str, p, attrs); + nodes.put(str, node); + return node; + } catch (IOException x) { + // does not exists or unable to determine + } + } + return null; + } + + Path underlyingPath(String str) { + if (str.startsWith(MODULES)) { + str = frontSlashToNativeSlash(str.substring("/modules".length())); + return defaultFS.getPath(explodedModulesDir.toString(), str); + } + return null; + } + + // convert "/" to platform path separator + private String frontSlashToNativeSlash(String str) { + return separator == null ? str : str.replace("/", separator); + } + + // convert platform path separator to "/" + private String nativeSlashToFrontSlash(String str) { + return separator == null ? str : str.replace(separator, "/"); + } + + // convert "/"s to "."s + private String slashesToDots(String str) { + return str.replace(separator != null ? separator : "/", "."); + } + + // initialize file system Nodes + private void initNodes() throws IOException { + // same package prefix may exist in mutliple modules. This Map + // is filled by walking "jdk modules" directory recursively! + Map> packageToModules = new HashMap<>(); + try (DirectoryStream stream = Files.newDirectoryStream(explodedModulesDir)) { + for (Path module : stream) { + if (Files.isDirectory(module)) { + String moduleName = module.getFileName().toString(); + // make sure "/modules/" is created + findModulesNode(MODULES + moduleName); + Files.walk(module).filter(Files::isDirectory).forEach((p) -> { + p = module.relativize(p); + String pkgName = slashesToDots(p.toString()); + // skip META-INFO and empty strings + if (!pkgName.isEmpty() && !pkgName.startsWith("META-INF")) { + List moduleNames = packageToModules.get(pkgName); + if (moduleNames == null) { + moduleNames = new ArrayList<>(); + packageToModules.put(pkgName, moduleNames); + } + moduleNames.add(moduleName); + } + }); + } + } + } + // create "/modules" directory + // "nodes" map contains only /modules/ nodes only so far and so add all as children of /modules + PathNode modulesDir = new PathNode("/modules", new ArrayList<>(nodes.values())); + nodes.put(modulesDir.getName(), modulesDir); + + // create children under "/packages" + List packagesChildren = new ArrayList<>(packageToModules.size()); + for (Map.Entry> entry : packageToModules.entrySet()) { + String pkgName = entry.getKey(); + List moduleNameList = entry.getValue(); + List moduleLinkNodes = new ArrayList<>(moduleNameList.size()); + for (String moduleName : moduleNameList) { + Node moduleNode = findModulesNode(MODULES + moduleName); + PathNode linkNode = new PathNode(PACKAGES + pkgName + "/" + moduleName, moduleNode); + nodes.put(linkNode.getName(), linkNode); + moduleLinkNodes.add(linkNode); + } + PathNode pkgDir = new PathNode(PACKAGES + pkgName, moduleLinkNodes); + nodes.put(pkgDir.getName(), pkgDir); + packagesChildren.add(pkgDir); + } + // "/packages" dir + PathNode packagesDir = new PathNode("/packages", packagesChildren); + nodes.put(packagesDir.getName(), packagesDir); + + // finally "/" dir! + List rootChildren = new ArrayList<>(); + rootChildren.add(packagesDir); + rootChildren.add(modulesDir); + PathNode root = new PathNode("/", rootChildren); + nodes.put(root.getName(), root); + } +} diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtDirectoryStream.java --- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtDirectoryStream.java Fri Apr 15 10:14:57 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtDirectoryStream.java Fri Apr 15 13:05:52 2016 -0700 @@ -30,6 +30,7 @@ import java.nio.file.NotDirectoryException; import java.nio.file.Path; import java.util.Iterator; +import java.util.Objects; import java.util.NoSuchElementException; import java.io.IOException; @@ -44,115 +45,47 @@ */ final class JrtDirectoryStream implements DirectoryStream { - private final AbstractJrtFileSystem jrtfs; - private final AbstractJrtPath dir; + private final JrtPath dir; private final DirectoryStream.Filter filter; private volatile boolean isClosed; private volatile Iterator itr; - JrtDirectoryStream(AbstractJrtPath jrtPath, + JrtDirectoryStream(JrtPath dir, DirectoryStream.Filter filter) - throws IOException { - this.jrtfs = jrtPath.getFileSystem(); - this.dir = jrtPath; - // sanity check - if (!jrtfs.isDirectory(dir, true)) { - throw new NotDirectoryException(jrtPath.toString()); + throws IOException + { + this.dir = dir; + if (!dir.jrtfs.isDirectory(dir, true)) { // sanity check + throw new NotDirectoryException(dir.toString()); } - this.filter = filter; } @Override public synchronized Iterator iterator() { - if (isClosed) { + if (isClosed) throw new ClosedDirectoryStreamException(); - } - if (itr != null) { + if (itr != null) throw new IllegalStateException("Iterator has already been returned"); - } - try { - itr = jrtfs.iteratorOf(dir); + itr = dir.jrtfs.iteratorOf(dir, filter); } catch (IOException e) { throw new IllegalStateException(e); } return new Iterator() { - /* - * next Path value to return from this iterator. - * null value means hasNext() not called yet - * or last hasNext() returned false or resulted - * in exception. If last hasNext() returned true, - * then this field has non-null value. - */ private Path next; - - // get-and-clear and set-next by these methods - private Path getAndClearNext() { - assert next != null; - Path result = this.next; - this.next = null; - return result; - } - - private void setNext(Path path) { - assert path != null; - this.next = path; - } - - // if hasNext() returns true, 'next' field has non-null Path @Override public synchronized boolean hasNext() { - if (next != null) { - return true; - } - - if (isClosed) { + if (isClosed) return false; - } - - if (filter == null) { - if (itr.hasNext()) { - setNext(itr.next()); - return true; - } else { - return false; - } - } else { - while (itr.hasNext()) { - Path tmpPath = itr.next(); - try { - if (filter.accept(tmpPath)) { - setNext(tmpPath); - return true; - } - } catch (IOException ioe) { - throw new DirectoryIteratorException(ioe); - } - } - - return false; - } + return itr.hasNext(); } @Override public synchronized Path next() { - if (next != null) { - return getAndClearNext(); - } - - if (isClosed) { + if (isClosed) throw new NoSuchElementException(); - } - - if (next == null && itr.hasNext()) { - // missing hasNext() between next() calls. - if (hasNext()) { - return getAndClearNext(); - } - } - - throw new NoSuchElementException(); + return itr.next(); } @Override diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtExplodedFileAttributes.java --- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtExplodedFileAttributes.java Fri Apr 15 10:14:57 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.internal.jrtfs; - -import java.io.IOException; -import java.nio.file.attribute.BasicFileAttributes; -import java.nio.file.attribute.FileTime; -import jdk.internal.jrtfs.JrtExplodedFileSystem.Node; - -/** - * jrt file system attributes implementation on top of 'exploded file system' - * Node. - * - * @implNote This class needs to maintain JDK 8 source compatibility. - * - * It is used internally in the JDK to implement jimage/jrtfs access, - * but also compiled and delivered as part of the jrtfs.jar to support access - * to the jimage file provided by the shipped JDK by tools running on JDK 8. - */ -final class JrtExplodedFileAttributes extends AbstractJrtFileAttributes { - - private final Node node; - private final BasicFileAttributes attrs; - - JrtExplodedFileAttributes(Node node) throws IOException { - this.node = node; - this.attrs = node.getBasicAttrs(); - } - - @Override - public FileTime creationTime() { - return attrs.creationTime(); - } - - @Override - public boolean isDirectory() { - return node.isDirectory(); - } - - @Override - public boolean isOther() { - return false; - } - - @Override - public boolean isRegularFile() { - return node.isFile(); - } - - @Override - public FileTime lastAccessTime() { - return attrs.lastAccessTime(); - } - - @Override - public FileTime lastModifiedTime() { - return attrs.lastModifiedTime(); - } - - @Override - public long size() { - return isRegularFile() ? attrs.size() : 0L; - } - - @Override - public boolean isSymbolicLink() { - return node.isLink(); - } - - @Override - public Object fileKey() { - return node.resolveLink(true); - } - - @Override - public long compressedSize() { - return 0L; - } - - @Override - public String extension() { - return node.getExtension(); - } -} diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtExplodedFileSystem.java --- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtExplodedFileSystem.java Fri Apr 15 10:14:57 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,528 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.internal.jrtfs; - -import java.io.IOException; -import java.nio.file.DirectoryStream; -import java.nio.file.FileSystem; -import java.nio.file.FileSystemException; -import java.nio.file.FileSystems; -import java.nio.file.Files; -import java.nio.file.LinkOption; -import java.nio.file.NoSuchFileException; -import java.nio.file.NotDirectoryException; -import java.nio.file.Path; -import java.nio.file.attribute.BasicFileAttributes; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.function.Function; -import static java.util.stream.Collectors.toList; -import static jdk.internal.jrtfs.AbstractJrtFileSystem.getString; - -/** - * A jrt file system built on $JAVA_HOME/modules directory ('exploded modules - * build') - * - * @implNote This class needs to maintain JDK 8 source compatibility. - * - * It is used internally in the JDK to implement jimage/jrtfs access, - * but also compiled and delivered as part of the jrtfs.jar to support access - * to the jimage file provided by the shipped JDK by tools running on JDK 8. - */ -class JrtExplodedFileSystem extends AbstractJrtFileSystem { - - private static final String MODULES = "/modules/"; - private static final String PACKAGES = "/packages/"; - private static final int PACKAGES_LEN = PACKAGES.length(); - - // root path - private final JrtExplodedPath rootPath; - private volatile boolean isOpen; - private final FileSystem defaultFS; - private final String separator; - private final Map nodes = Collections.synchronizedMap(new HashMap<>()); - private final BasicFileAttributes modulesDirAttrs; - - JrtExplodedFileSystem(JrtFileSystemProvider provider, - Map env) - throws IOException { - - super(provider, env); - checkExists(SystemImages.explodedModulesDir()); - byte[] root = new byte[]{'/'}; - rootPath = new JrtExplodedPath(this, root); - isOpen = true; - defaultFS = FileSystems.getDefault(); - String str = defaultFS.getSeparator(); - separator = str.equals(getSeparator()) ? null : str; - modulesDirAttrs = Files.readAttributes(SystemImages.explodedModulesDir(), BasicFileAttributes.class); - initNodes(); - } - - @Override - public void close() throws IOException { - cleanup(); - } - - @Override - public boolean isOpen() { - return isOpen; - } - - @Override - protected void finalize() throws Throwable { - cleanup(); - super.finalize(); - } - - private synchronized void cleanup() { - isOpen = false; - nodes.clear(); - } - - @Override - JrtExplodedPath getRootPath() { - return rootPath; - } - - // Base class for Nodes of this file system - abstract class Node { - - private final String name; - - Node(String name) { - this.name = name; - } - - final String getName() { - return name; - } - - final String getExtension() { - if (isFile()) { - final int index = name.lastIndexOf("."); - if (index != -1) { - return name.substring(index + 1); - } - } - - return null; - } - - BasicFileAttributes getBasicAttrs() throws IOException { - return modulesDirAttrs; - } - - boolean isLink() { - return false; - } - - boolean isDirectory() { - return false; - } - - boolean isFile() { - return false; - } - - byte[] getContent() throws IOException { - if (!isFile()) { - throw new FileSystemException(name + " is not file"); - } - - throw new AssertionError("ShouldNotReachHere"); - } - - List getChildren() throws IOException { - if (!isDirectory()) { - throw new NotDirectoryException(name); - } - - throw new AssertionError("ShouldNotReachHere"); - } - - final Node resolveLink() { - return resolveLink(false); - } - - Node resolveLink(boolean recursive) { - return this; - } - } - - // A Node that is backed by actual default file system Path - private final class PathNode extends Node { - - // Path in underlying default file system - private final Path path; - private final boolean file; - // lazily initialized, don't read attributes unless required! - private BasicFileAttributes attrs; - - PathNode(String name, Path path) { - super(name); - this.path = path; - this.file = Files.isRegularFile(path); - } - - @Override - synchronized BasicFileAttributes getBasicAttrs() throws IOException { - if (attrs == null) { - attrs = Files.readAttributes(path, BasicFileAttributes.class); - } - return attrs; - } - - @Override - boolean isDirectory() { - return !file; - } - - @Override - boolean isFile() { - return file; - } - - @Override - byte[] getContent() throws IOException { - if (!isFile()) { - throw new FileSystemException(getName() + " is not file"); - } - - return Files.readAllBytes(path); - } - - @Override - List getChildren() throws IOException { - if (!isDirectory()) { - throw new NotDirectoryException(getName()); - } - - List children = new ArrayList<>(); - try (DirectoryStream stream = Files.newDirectoryStream(path)) { - for (Path cp : stream) { - cp = SystemImages.explodedModulesDir().relativize(cp); - String cpName = MODULES + nativeSlashToFrontSlash(cp.toString()); - try { - children.add(findNode(cpName)); - } catch (NoSuchFileException nsfe) { - // findNode may choose to hide certain files! - } - } - } - - return children; - } - } - - // A Node that links to another Node - private final class LinkNode extends Node { - - // underlying linked Node - private final Node link; - - LinkNode(String name, Node link) { - super(name); - this.link = link; - } - - @Override - BasicFileAttributes getBasicAttrs() throws IOException { - return link.getBasicAttrs(); - } - - @Override - public boolean isLink() { - return true; - } - - @Override - Node resolveLink(boolean recursive) { - return recursive && (link instanceof LinkNode) ? ((LinkNode) link).resolveLink(true) : link; - } - } - - // A directory Node with it's children Nodes - private final class DirNode extends Node { - - // children Nodes of this Node. - private final List children; - - DirNode(String name, List children) { - super(name); - this.children = children; - } - - @Override - boolean isDirectory() { - return true; - } - - @Override - List getChildren() throws IOException { - return children; - } - } - - private JrtExplodedPath toJrtExplodedPath(String path) { - return toJrtExplodedPath(getBytes(path)); - } - - private JrtExplodedPath toJrtExplodedPath(byte[] path) { - return new JrtExplodedPath(this, path); - } - - @Override - boolean isSameFile(AbstractJrtPath jrtPath1, AbstractJrtPath jrtPath2) throws IOException { - Node n1 = checkNode(jrtPath1); - Node n2 = checkNode(jrtPath2); - return n1 == n2; - } - - @Override - boolean isLink(AbstractJrtPath jrtPath) throws IOException { - return checkNode(jrtPath).isLink(); - } - - @Override - AbstractJrtPath resolveLink(AbstractJrtPath jrtPath) throws IOException { - String name = checkNode(jrtPath).resolveLink().getName(); - return toJrtExplodedPath(name); - } - - @Override - AbstractJrtFileAttributes getFileAttributes(AbstractJrtPath jrtPath, LinkOption... options) throws IOException { - Node node = checkNode(jrtPath); - if (node.isLink() && followLinks(options)) { - node = node.resolveLink(true); - } - return new JrtExplodedFileAttributes(node); - } - - @Override - boolean exists(AbstractJrtPath jrtPath) throws IOException { - try { - checkNode(jrtPath); - return true; - } catch (NoSuchFileException nsfe) { - return false; - } - } - - @Override - boolean isDirectory(AbstractJrtPath jrtPath, boolean resolveLinks) throws IOException { - Node node = checkNode(jrtPath); - return resolveLinks && node.isLink() - ? node.resolveLink(true).isDirectory() - : node.isDirectory(); - } - - @Override - Iterator iteratorOf(AbstractJrtPath dir) throws IOException { - Node node = checkNode(dir).resolveLink(true); - if (!node.isDirectory()) { - throw new NotDirectoryException(getString(dir.getName())); - } - - Function nodeToPath = - child -> dir.resolve( - toJrtExplodedPath(child.getName()). - getFileName()); - - return node.getChildren().stream(). - map(nodeToPath).collect(toList()). - iterator(); - } - - @Override - byte[] getFileContent(AbstractJrtPath jrtPath) throws IOException { - return checkNode(jrtPath).getContent(); - } - - private Node checkNode(AbstractJrtPath jrtPath) throws IOException { - return checkNode(jrtPath.getResolvedPath()); - } - - private Node checkNode(byte[] path) throws IOException { - ensureOpen(); - return findNode(path); - } - - synchronized Node findNode(byte[] path) throws IOException { - return findNode(getString(path)); - } - - // find Node for the given Path - synchronized Node findNode(String str) throws IOException { - Node node = findModulesNode(str); - if (node != null) { - return node; - } - - // lazily created for paths like /packages///xyz - // For example /packages/java.lang/java.base/java/lang/ - if (str.startsWith(PACKAGES)) { - // pkgEndIdx marks end of part - int pkgEndIdx = str.indexOf('/', PACKAGES_LEN); - if (pkgEndIdx != -1) { - // modEndIdx marks end of part - int modEndIdx = str.indexOf('/', pkgEndIdx + 1); - if (modEndIdx != -1) { - // make sure we have such module link! - // ie., /packages// is valid - Node linkNode = nodes.get(str.substring(0, modEndIdx)); - if (linkNode == null || !linkNode.isLink()) { - throw new NoSuchFileException(str); - } - - // map to "/modules/zyz" path and return that node - // For example, "/modules/java.base/java/lang" for - // "/packages/java.lang/java.base/java/lang". - String mod = MODULES + str.substring(pkgEndIdx + 1); - return findNode(mod); - } - } - } - - throw new NoSuchFileException(str); - } - - // find a Node for a path that starts like "/modules/..." - synchronized Node findModulesNode(String str) throws IOException { - Node node = nodes.get(str); - if (node != null) { - return node; - } - - // lazily created "/modules/xyz/abc/" Node - // This is mapped to default file system path "/xyz/abc" - Path p = underlyingPath(str); - if (p != null) { - if (Files.isRegularFile(p)) { - Path file = p.getFileName(); - if (file.toString().startsWith("_the.")) { - return null; - } - } - node = new PathNode(str, p); - nodes.put(str, node); - return node; - } - - return null; - } - - Path underlyingPath(String str) { - if (str.startsWith(MODULES)) { - str = frontSlashToNativeSlash(str.substring("/modules".length())); - return defaultFS.getPath(SystemImages.explodedModulesDir().toString(), str); - } - return null; - } - - // convert "/" to platform path separator - private String frontSlashToNativeSlash(String str) { - return separator == null ? str : str.replace("/", separator); - } - - // convert platform path separator to "/" - private String nativeSlashToFrontSlash(String str) { - return separator == null ? str : str.replace(separator, "/"); - } - - // convert "/"s to "."s - private String slashesToDots(String str) { - return str.replace(separator != null ? separator : "/", "."); - } - - // initialize file system Nodes - private void initNodes() throws IOException { - // same package prefix may exist in mutliple modules. This Map - // is filled by walking "jdk modules" directory recursively! - Map> packageToModules = new HashMap<>(); - - try (DirectoryStream stream = Files.newDirectoryStream(SystemImages.explodedModulesDir())) { - for (Path module : stream) { - if (Files.isDirectory(module)) { - String moduleName = module.getFileName().toString(); - // make sure "/modules/" is created - findModulesNode(MODULES + moduleName); - - Files.walk(module).filter(Files::isDirectory).forEach((p) -> { - p = module.relativize(p); - String pkgName = slashesToDots(p.toString()); - // skip META-INFO and empty strings - if (!pkgName.isEmpty() && !pkgName.startsWith("META-INF")) { - List moduleNames = packageToModules.get(pkgName); - if (moduleNames == null) { - moduleNames = new ArrayList<>(); - packageToModules.put(pkgName, moduleNames); - } - moduleNames.add(moduleName); - } - }); - } - } - } - - // create "/modules" directory - // "nodes" map contains only /modules/ nodes only so far and so add all as children of /modules - DirNode modulesDir = new DirNode("/modules", new ArrayList<>(nodes.values())); - nodes.put(modulesDir.getName(), modulesDir); - - // create children under "/packages" - List packagesChildren = new ArrayList<>(packageToModules.size()); - for (Map.Entry> entry : packageToModules.entrySet()) { - String pkgName = entry.getKey(); - List moduleNameList = entry.getValue(); - List moduleLinkNodes = new ArrayList<>(moduleNameList.size()); - for (String moduleName : moduleNameList) { - Node moduleNode = findModulesNode(MODULES + moduleName); - LinkNode linkNode = new LinkNode(PACKAGES + pkgName + "/" + moduleName, moduleNode); - nodes.put(linkNode.getName(), linkNode); - moduleLinkNodes.add(linkNode); - } - - DirNode pkgDir = new DirNode(PACKAGES + pkgName, moduleLinkNodes); - nodes.put(pkgDir.getName(), pkgDir); - packagesChildren.add(pkgDir); - } - - // "/packages" dir - DirNode packagesDir = new DirNode("/packages", packagesChildren); - nodes.put(packagesDir.getName(), packagesDir); - - // finally "/" dir! - List rootChildren = new ArrayList<>(); - rootChildren.add(modulesDir); - rootChildren.add(packagesDir); - DirNode root = new DirNode("/", rootChildren); - nodes.put(root.getName(), root); - } -} diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtExplodedPath.java --- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtExplodedPath.java Fri Apr 15 10:14:57 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.internal.jrtfs; - -/** - * Path implementation for jrt file system on JDK exploded modules build. - * - * @implNote This class needs to maintain JDK 8 source compatibility. - * - * It is used internally in the JDK to implement jimage/jrtfs access, - * but also compiled and delivered as part of the jrtfs.jar to support access - * to the jimage file provided by the shipped JDK by tools running on JDK 8. - */ -final class JrtExplodedPath extends AbstractJrtPath { - - JrtExplodedPath(AbstractJrtFileSystem jrtfs, byte[] path) { - super(jrtfs, path); - } - - JrtExplodedPath(AbstractJrtFileSystem jrtfs, byte[] path, boolean normalized) { - super(jrtfs, path, normalized); - } - - @Override - protected AbstractJrtPath newJrtPath(byte[] path) { - return new JrtExplodedPath(jrtfs, path); - } - - @Override - protected AbstractJrtPath newJrtPath(byte[] path, boolean normalized) { - return new JrtExplodedPath(jrtfs, path, normalized); - } - - @Override - public JrtExplodedFileSystem getFileSystem() { - return (JrtExplodedFileSystem) jrtfs; - } -} diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributeView.java --- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributeView.java Fri Apr 15 10:14:57 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributeView.java Fri Apr 15 13:05:52 2016 -0700 @@ -29,6 +29,7 @@ import java.io.IOException; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Objects; /** * File attribute view for jrt file system. @@ -42,7 +43,6 @@ final class JrtFileAttributeView implements BasicFileAttributeView { private static enum AttrID { - size, creationTime, lastAccessTime, @@ -56,21 +56,19 @@ extension }; - private final AbstractJrtPath path; + private final JrtPath path; private final boolean isJrtView; private final LinkOption[] options; - private JrtFileAttributeView(AbstractJrtPath path, boolean isJrtView, LinkOption... options) { + private JrtFileAttributeView(JrtPath path, boolean isJrtView, LinkOption... options) { this.path = path; this.isJrtView = isJrtView; this.options = options; } @SuppressWarnings("unchecked") // Cast to V - static V get(AbstractJrtPath path, Class type, LinkOption... options) { - if (type == null) { - throw new NullPointerException(); - } + static V get(JrtPath path, Class type, LinkOption... options) { + Objects.requireNonNull(type); if (type == BasicFileAttributeView.class) { return (V) new JrtFileAttributeView(path, false, options); } @@ -80,10 +78,8 @@ return null; } - static JrtFileAttributeView get(AbstractJrtPath path, String type, LinkOption... options) { - if (type == null) { - throw new NullPointerException(); - } + static JrtFileAttributeView get(JrtPath path, String type, LinkOption... options) { + Objects.requireNonNull(type); if (type.equals("basic")) { return new JrtFileAttributeView(path, false, options); } @@ -99,61 +95,74 @@ } @Override - public AbstractJrtFileAttributes readAttributes() throws IOException { + public JrtFileAttributes readAttributes() throws IOException { return path.getAttributes(options); } @Override public void setTimes(FileTime lastModifiedTime, - FileTime lastAccessTime, - FileTime createTime) - throws IOException { + FileTime lastAccessTime, + FileTime createTime) throws IOException { path.setTimes(lastModifiedTime, lastAccessTime, createTime); } - void setAttribute(String attribute, Object value) + static void setAttribute(JrtPath path, String attribute, Object value) throws IOException { - try { - if (AttrID.valueOf(attribute) == AttrID.lastModifiedTime) { - setTimes((FileTime) value, null, null); + int colonPos = attribute.indexOf(':'); + if (colonPos != -1) { // type = "basic", if no ":" + String type = attribute.substring(0, colonPos++); + if (!type.equals("basic") && !type.equals("jrt")) { + throw new UnsupportedOperationException( + "view <" + type + "> is not supported"); } - if (AttrID.valueOf(attribute) == AttrID.lastAccessTime) { - setTimes(null, (FileTime) value, null); - } - if (AttrID.valueOf(attribute) == AttrID.creationTime) { - setTimes(null, null, (FileTime) value); + attribute = attribute.substring(colonPos); + } + try { + AttrID id = AttrID.valueOf(attribute); + if (id == AttrID.lastModifiedTime) { + path.setTimes((FileTime) value, null, null); + } else if (id == AttrID.lastAccessTime) { + path.setTimes(null, (FileTime) value, null); + } else if (id == AttrID.creationTime) { + path.setTimes(null, null, (FileTime) value); } return; - } catch (IllegalArgumentException x) { - } + } catch (IllegalArgumentException x) {} throw new UnsupportedOperationException("'" + attribute + "' is unknown or read-only attribute"); } - Map readAttributes(String attributes) + static Map readAttributes(JrtPath path, String attributes, + LinkOption... options) throws IOException { - AbstractJrtFileAttributes jrtfas = readAttributes(); + int colonPos = attributes.indexOf(':'); + boolean isJrtView = false; + if (colonPos != -1) { // type = "basic", if no ":" + String type = attributes.substring(0, colonPos++); + if (!type.equals("basic") && !type.equals("jrt")) { + throw new UnsupportedOperationException("view <" + type + + "> is not supported"); + } + isJrtView = true; + attributes = attributes.substring(colonPos); + } + JrtFileAttributes jrtfas = path.getAttributes(); LinkedHashMap map = new LinkedHashMap<>(); if ("*".equals(attributes)) { for (AttrID id : AttrID.values()) { - try { - map.put(id.name(), attribute(id, jrtfas)); - } catch (IllegalArgumentException x) { - } + map.put(id.name(), attribute(id, jrtfas, isJrtView)); } } else { String[] as = attributes.split(","); for (String a : as) { - try { - map.put(a, attribute(AttrID.valueOf(a), jrtfas)); - } catch (IllegalArgumentException x) { - } + //throw IllegalArgumentException + map.put(a, attribute(AttrID.valueOf(a), jrtfas, isJrtView)); } } return map; } - Object attribute(AttrID id, AbstractJrtFileAttributes jrtfas) { + static Object attribute(AttrID id, JrtFileAttributes jrtfas, boolean isJrtView) { switch (id) { case size: return jrtfas.size(); diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java --- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java Fri Apr 15 10:14:57 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileAttributes.java Fri Apr 15 13:05:52 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,7 +24,9 @@ */ package jdk.internal.jrtfs; +import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.FileTime; +import java.util.Formatter; import jdk.internal.jimage.ImageReader.Node; /** @@ -36,7 +38,7 @@ * but also compiled and delivered as part of the jrtfs.jar to support access * to the jimage file provided by the shipped JDK by tools running on JDK 8. */ -final class JrtFileAttributes extends AbstractJrtFileAttributes { +final class JrtFileAttributes implements BasicFileAttributes { private final Node node; @@ -90,14 +92,50 @@ return node.resolveLink(true); } - ///////// jrt entry attributes /////////// - @Override + ///////// jrtfs specific attributes /////////// + /** + * Compressed resource file. If not available or not applicable, 0L is + * returned. + * + * @return the compressed resource size for compressed resources. + */ public long compressedSize() { return node.compressedSize(); } - @Override + /** + * "file" extension of a file resource. + * + * @return extension string for the file resource + */ public String extension() { return node.extension(); } + + @Override + public final String toString() { + StringBuilder sb = new StringBuilder(1024); + try (Formatter fm = new Formatter(sb)) { + if (creationTime() != null) { + fm.format(" creationTime : %tc%n", creationTime().toMillis()); + } else { + fm.format(" creationTime : null%n"); + } + if (lastAccessTime() != null) { + fm.format(" lastAccessTime : %tc%n", lastAccessTime().toMillis()); + } else { + fm.format(" lastAccessTime : null%n"); + } + fm.format(" lastModifiedTime: %tc%n", lastModifiedTime().toMillis()); + fm.format(" isRegularFile : %b%n", isRegularFile()); + fm.format(" isDirectory : %b%n", isDirectory()); + fm.format(" isSymbolicLink : %b%n", isSymbolicLink()); + fm.format(" isOther : %b%n", isOther()); + fm.format(" fileKey : %s%n", fileKey()); + fm.format(" size : %d%n", size()); + fm.format(" compressedSize : %d%n", compressedSize()); + fm.format(" extension : %s%n", extension()); + } + return sb.toString(); + } } diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileStore.java --- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileStore.java Fri Apr 15 10:14:57 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileStore.java Fri Apr 15 13:05:52 2016 -0700 @@ -30,6 +30,7 @@ import java.nio.file.attribute.FileAttributeView; import java.nio.file.attribute.BasicFileAttributeView; import java.nio.file.attribute.FileStoreAttributeView; +import java.util.Objects; /** * File store implementation for jrt file systems. @@ -44,7 +45,7 @@ protected final FileSystem jrtfs; - JrtFileStore(AbstractJrtPath jrtPath) { + JrtFileStore(JrtPath jrtPath) { this.jrtfs = jrtPath.getFileSystem(); } @@ -71,9 +72,7 @@ @Override @SuppressWarnings("unchecked") public V getFileStoreAttributeView(Class type) { - if (type == null) { - throw new NullPointerException(); - } + Objects.requireNonNull(type, "type"); return (V) null; } @@ -99,7 +98,7 @@ @Override public boolean supportsFileAttributeView(Class type) { - return (type == BasicFileAttributeView.class - || type == JrtFileAttributeView.class); + return type == BasicFileAttributeView.class || + type == JrtFileAttributeView.class; } } diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java --- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java Fri Apr 15 10:14:57 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystem.java Fri Apr 15 13:05:52 2016 -0700 @@ -24,23 +24,47 @@ */ package jdk.internal.jrtfs; +import java.io.ByteArrayInputStream; import java.io.IOException; -import java.nio.file.LinkOption; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.nio.channels.Channels; +import java.nio.channels.FileChannel; +import java.nio.channels.NonWritableChannelException; +import java.nio.channels.ReadableByteChannel; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.ClosedFileSystemException; +import java.nio.file.CopyOption; +import java.nio.file.DirectoryStream; +import java.nio.file.FileStore; +import java.nio.file.FileSystem; import java.nio.file.FileSystemException; import java.nio.file.InvalidPathException; +import java.nio.file.LinkOption; import java.nio.file.NoSuchFileException; import java.nio.file.NotDirectoryException; +import java.nio.file.OpenOption; import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.ReadOnlyFileSystemException; +import java.nio.file.StandardOpenOption; +import java.nio.file.WatchService; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileTime; +import java.nio.file.attribute.UserPrincipalLookupService; +import java.nio.file.spi.FileSystemProvider; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; import java.util.Iterator; -import java.util.List; import java.util.Map; -import java.util.function.Function; +import java.util.Objects; +import java.util.Set; +import java.util.regex.Pattern; +import jdk.internal.jimage.ImageReader.Node; import static java.util.stream.Collectors.toList; -import jdk.internal.jimage.ImageReader; -import jdk.internal.jimage.ImageReader.Node; - /** * jrt file system implementation built on System jimage files. @@ -51,33 +75,21 @@ * but also compiled and delivered as part of the jrtfs.jar to support access * to the jimage file provided by the shipped JDK by tools running on JDK 8. */ -class JrtFileSystem extends AbstractJrtFileSystem { +class JrtFileSystem extends FileSystem { - // System image reader - private ImageReader bootImage; - // root path - private final JrtPath rootPath; + private final JrtFileSystemProvider provider; + private final JrtPath rootPath = new JrtPath(this, "/"); private volatile boolean isOpen; + private volatile boolean isClosable; + private SystemImage image; - // open a .jimage and build directory structure - private static ImageReader openImage(Path path) throws IOException { - ImageReader image = ImageReader.open(path); - image.getRootDirectory(); - return image; - } - - JrtFileSystem(JrtFileSystemProvider provider, - Map env) - throws IOException { - super(provider, env); - checkExists(SystemImages.moduleImageFile()); - - // open image file - this.bootImage = openImage(SystemImages.moduleImageFile()); - - byte[] root = new byte[]{'/'}; - rootPath = new JrtPath(this, root); - isOpen = true; + JrtFileSystem(JrtFileSystemProvider provider, Map env) + throws IOException + { + this.provider = provider; + this.image = SystemImage.open(); // open image file + this.isOpen = true; + this.isClosable = env != null; } // FileSystem method implementations @@ -88,6 +100,8 @@ @Override public void close() throws IOException { + if (!isClosable) + throw new UnsupportedOperationException(); cleanup(); } @@ -95,237 +109,397 @@ protected void finalize() throws Throwable { try { cleanup(); - } catch (IOException ignored) { - } - super.finalize(); + } catch (IOException ignored) {} } - // AbstractJrtFileSystem method implementations @Override - JrtPath getRootPath() { - return rootPath; + public FileSystemProvider provider() { + return provider; + } + + @Override + public Iterable getRootDirectories() { + ArrayList dirs = new ArrayList<>(); + dirs.add(getRootPath()); + return dirs; } @Override - boolean isSameFile(AbstractJrtPath p1, AbstractJrtPath p2) throws IOException { - ensureOpen(); - Node node1 = findNode(p1); - Node node2 = findNode(p2); - return node1.equals(node2); + public JrtPath getPath(String first, String... more) { + if (more.length == 0) { + return new JrtPath(this, first); + } + StringBuilder sb = new StringBuilder(); + sb.append(first); + for (String path : more) { + if (path.length() > 0) { + if (sb.length() > 0) { + sb.append('/'); + } + sb.append(path); + } + } + return new JrtPath(this, sb.toString()); + } + + @Override + public final boolean isReadOnly() { + return true; + } + + @Override + public final UserPrincipalLookupService getUserPrincipalLookupService() { + throw new UnsupportedOperationException(); + } + + @Override + public final WatchService newWatchService() { + throw new UnsupportedOperationException(); } @Override - boolean isLink(AbstractJrtPath jrtPath) throws IOException { - return checkNode(jrtPath).isLink(); + public final Iterable getFileStores() { + ArrayList list = new ArrayList<>(1); + list.add(getFileStore(getRootPath())); + return list; + } + + private static final Set supportedFileAttributeViews + = Collections.unmodifiableSet( + new HashSet(Arrays.asList("basic", "jrt"))); + + @Override + public final Set supportedFileAttributeViews() { + return supportedFileAttributeViews; + } + + @Override + public final String toString() { + return "jrt:/"; + } + + @Override + public final String getSeparator() { + return "/"; } @Override - AbstractJrtPath resolveLink(AbstractJrtPath jrtPath) throws IOException { - Node node = checkNode(jrtPath); + public PathMatcher getPathMatcher(String syntaxAndInput) { + int pos = syntaxAndInput.indexOf(':'); + if (pos <= 0 || pos == syntaxAndInput.length()) { + throw new IllegalArgumentException(); + } + String syntax = syntaxAndInput.substring(0, pos); + String input = syntaxAndInput.substring(pos + 1); + String expr; + if (syntax.equalsIgnoreCase("glob")) { + expr = JrtUtils.toRegexPattern(input); + } else if (syntax.equalsIgnoreCase("regex")) { + expr = input; + } else { + throw new UnsupportedOperationException("Syntax '" + syntax + + "' not recognized"); + } + // return matcher + final Pattern pattern = Pattern.compile(expr); + return (Path path) -> pattern.matcher(path.toString()).matches(); + } + + JrtPath resolveLink(JrtPath path) throws IOException { + Node node = checkNode(path); if (node.isLink()) { node = node.resolveLink(); - return toJrtPath(getBytes(node.getName())); + return new JrtPath(this, node.getName()); // TBD, normalized? } - - return jrtPath; + return path; } - @Override - JrtFileAttributes getFileAttributes(AbstractJrtPath jrtPath, LinkOption... options) + JrtFileAttributes getFileAttributes(JrtPath path, LinkOption... options) throws IOException { - Node node = checkNode(jrtPath); + Node node = checkNode(path); if (node.isLink() && followLinks(options)) { return new JrtFileAttributes(node.resolveLink(true)); } return new JrtFileAttributes(node); } - @Override - boolean exists(AbstractJrtPath jrtPath) throws IOException { + /** + * returns the list of child paths of the given directory "path" + * + * @param path name of the directory whose content is listed + * @return iterator for child paths of the given directory path + */ + Iterator iteratorOf(JrtPath path, DirectoryStream.Filter filter) + throws IOException { + Node node = checkNode(path).resolveLink(true); + if (!node.isDirectory()) { + throw new NotDirectoryException(path.getName()); + } + if (filter == null) { + return node.getChildren() + .stream() + .map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName()))) + .iterator(); + } + return node.getChildren() + .stream() + .map(child -> (Path)(path.resolve(new JrtPath(this, child.getNameString()).getFileName()))) + .filter(p -> { try { return filter.accept(p); + } catch (IOException x) {} + return false; + }) + .iterator(); + } + + // returns the content of the file resource specified by the path + byte[] getFileContent(JrtPath path) throws IOException { + Node node = checkNode(path); + if (node.isDirectory()) { + throw new FileSystemException(path + " is a directory"); + } + //assert node.isResource() : "resource node expected here"; + return image.getResource(node); + } + + /////////////// Implementation details below this point ////////// + + // static utility methods + static ReadOnlyFileSystemException readOnly() { + return new ReadOnlyFileSystemException(); + } + + // do the supplied options imply that we have to chase symlinks? + static boolean followLinks(LinkOption... options) { + if (options != null) { + for (LinkOption lo : options) { + Objects.requireNonNull(lo); + if (lo == LinkOption.NOFOLLOW_LINKS) { + return false; + } else { + throw new AssertionError("should not reach here"); + } + } + } + return true; + } + + // check that the options passed are supported by (read-only) jrt file system + static void checkOptions(Set options) { + // check for options of null type and option is an intance of StandardOpenOption + for (OpenOption option : options) { + Objects.requireNonNull(option); + if (!(option instanceof StandardOpenOption)) { + throw new IllegalArgumentException(); + } + } + if (options.contains(StandardOpenOption.WRITE) || + options.contains(StandardOpenOption.APPEND)) { + throw readOnly(); + } + } + + // clean up this file system - called from finalize and close + void cleanup() throws IOException { + if (!isOpen) { + return; + } + synchronized (this) { + isOpen = false; + // close image reader and null out + image.close(); + image = null; + } + } + + // These methods throw read only file system exception + final void setTimes(JrtPath jrtPath, FileTime mtime, FileTime atime, FileTime ctime) + throws IOException { + throw readOnly(); + } + + // These methods throw read only file system exception + final void createDirectory(JrtPath jrtPath, FileAttribute... attrs) throws IOException { + throw readOnly(); + } + + final void deleteFile(JrtPath jrtPath, boolean failIfNotExists) + throws IOException { + throw readOnly(); + } + + final OutputStream newOutputStream(JrtPath jrtPath, OpenOption... options) + throws IOException { + throw readOnly(); + } + + final void copyFile(boolean deletesrc, JrtPath srcPath, JrtPath dstPath, CopyOption... options) + throws IOException { + throw readOnly(); + } + + final FileChannel newFileChannel(JrtPath path, + Set options, + FileAttribute... attrs) + throws IOException { + throw new UnsupportedOperationException("newFileChannel"); + } + + final InputStream newInputStream(JrtPath path) throws IOException { + return new ByteArrayInputStream(getFileContent(path)); + } + + final SeekableByteChannel newByteChannel(JrtPath path, + Set options, + FileAttribute... attrs) + throws IOException { + checkOptions(options); + + byte[] buf = getFileContent(path); + final ReadableByteChannel rbc + = Channels.newChannel(new ByteArrayInputStream(buf)); + final long size = buf.length; + return new SeekableByteChannel() { + long read = 0; + + @Override + public boolean isOpen() { + return rbc.isOpen(); + } + + @Override + public long position() throws IOException { + return read; + } + + @Override + public SeekableByteChannel position(long pos) + throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public int read(ByteBuffer dst) throws IOException { + int n = rbc.read(dst); + if (n > 0) { + read += n; + } + return n; + } + + @Override + public SeekableByteChannel truncate(long size) + throws IOException { + throw new NonWritableChannelException(); + } + + @Override + public int write(ByteBuffer src) throws IOException { + throw new NonWritableChannelException(); + } + + @Override + public long size() throws IOException { + return size; + } + + @Override + public void close() throws IOException { + rbc.close(); + } + }; + } + + final JrtFileStore getFileStore(JrtPath path) { + return new JrtFileStore(path); + } + + final void ensureOpen() throws IOException { + if (!isOpen()) { + throw new ClosedFileSystemException(); + } + } + + final JrtPath getRootPath() { + return rootPath; + } + + boolean isSameFile(JrtPath path1, JrtPath path2) throws IOException { + return checkNode(path1) == checkNode(path2); + } + + boolean isLink(JrtPath path) throws IOException { + return checkNode(path).isLink(); + } + + boolean exists(JrtPath path) throws IOException { try { - checkNode(jrtPath); + checkNode(path); } catch (NoSuchFileException exp) { return false; } return true; } - @Override - boolean isDirectory(AbstractJrtPath jrtPath, boolean resolveLinks) + boolean isDirectory(JrtPath path, boolean resolveLinks) throws IOException { - Node node = checkNode(jrtPath); + Node node = checkNode(path); return resolveLinks && node.isLink() ? node.resolveLink(true).isDirectory() : node.isDirectory(); } - @Override - Iterator iteratorOf(AbstractJrtPath jrtPath) throws IOException { - Node node = checkNode(jrtPath).resolveLink(true); - if (!node.isDirectory()) { - throw new NotDirectoryException(getString(jrtPath.getName())); + JrtPath toRealPath(JrtPath path, LinkOption... options) + throws IOException { + Node node = checkNode(path); + if (followLinks(options) && node.isLink()) { + node = node.resolveLink(); } - - if (node.isRootDir()) { - return rootDirIterator(jrtPath); - } else if (node.isModulesDir()) { - return modulesDirIterator(jrtPath); - } else if (node.isPackagesDir()) { - return packagesDirIterator(jrtPath); - } - - return nodesToIterator(jrtPath, node.getChildren()); + // image node holds the real/absolute path name + return new JrtPath(this, node.getName(), true); } - @Override - byte[] getFileContent(AbstractJrtPath jrtPath) throws IOException { - final Node node = checkResource(jrtPath); - return bootImage.getResource(node); - } - - // Implementation details below this point - // clean up this file system - called from finalize and close - private void cleanup() throws IOException { - if (!isOpen) { - return; - } - - synchronized (this) { - isOpen = false; - - // close all image reader and null out - bootImage.close(); - bootImage = null; + private Node lookup(String path) { + try { + return image.findNode(path); + } catch (RuntimeException re) { + throw new InvalidPathException(path, re.toString()); } } - private Node lookup(byte[] path) { - Node node = null; - try { - node = bootImage.findNode(getString(path)); - } catch (RuntimeException re) { - throw new InvalidPathException(getString(path), re.toString()); + private Node lookupSymbolic(String path) { + int i = 1; + while (i < path.length()) { + i = path.indexOf('/', i); + if (i == -1) { + break; + } + String prefix = path.substring(0, i); + Node node = lookup(prefix); + if (node == null) { + break; + } + if (node.isLink()) { + Node link = node.resolveLink(true); + // resolved symbolic path concatenated to the rest of the path + String resPath = link.getName() + path.substring(i); + node = lookup(resPath); + return node != null ? node : lookupSymbolic(resPath); + } + i++; } - return node; - } - - private Node lookupSymbolic(byte[] path) { - for (int i = 1; i < path.length; i++) { - if (path[i] == (byte) '/') { - byte[] prefix = Arrays.copyOfRange(path, 0, i); - Node node = lookup(prefix); - if (node == null) { - break; - } - - if (node.isLink()) { - Node link = node.resolveLink(true); - // resolved symbolic path concatenated to the rest of the path - String resPath = link.getName() + getString(path).substring(i); - byte[] resPathBytes = getBytes(resPath); - node = lookup(resPathBytes); - return node != null ? node : lookupSymbolic(resPathBytes); - } - } - } - return null; } - private Node findNode(AbstractJrtPath jrtPath) throws IOException { - return findNode(jrtPath.getResolvedPath()); - } - - private Node findNode(byte[] path) throws IOException { - Node node = lookup(path); + Node checkNode(JrtPath path) throws IOException { + ensureOpen(); + String p = path.getResolvedPath(); + Node node = lookup(p); if (node == null) { - node = lookupSymbolic(path); + node = lookupSymbolic(p); if (node == null) { - throw new NoSuchFileException(getString(path)); + throw new NoSuchFileException(p); } } return node; } - - private Node checkNode(AbstractJrtPath jrtPath) throws IOException { - return checkNode(jrtPath.getResolvedPath()); - } - - private Node checkNode(byte[] path) throws IOException { - ensureOpen(); - return findNode(path); - } - - private Node checkResource(AbstractJrtPath jrtPath) throws IOException { - return checkResource(jrtPath.getResolvedPath()); - } - - private Node checkResource(byte[] path) throws IOException { - Node node = checkNode(path); - if (node.isDirectory()) { - throw new FileSystemException(getString(path) + " is a directory"); - } - - assert node.isResource() : "resource node expected here"; - return node; - } - - private JrtPath toJrtPath(String path) { - return toJrtPath(getBytes(path)); - } - - private JrtPath toJrtPath(byte[] path) { - return new JrtPath(this, path); - } - - private Iterator nodesToIterator(AbstractJrtPath dir, List childNodes) { - Function nodeToPath = - child -> dir.resolve( - toJrtPath(child.getNameString()).getFileName()); - return childNodes.stream(). - map(nodeToPath).collect(toList()). - iterator(); - } - - private List rootChildren; - - private synchronized void initRootChildren(AbstractJrtPath jrtPath) throws IOException { - if (rootChildren == null) { - rootChildren = new ArrayList<>(); - rootChildren.addAll(findNode(jrtPath).getChildren()); - } - } - - private Iterator rootDirIterator(AbstractJrtPath jrtPath) throws IOException { - initRootChildren(jrtPath); - return nodesToIterator(jrtPath, rootChildren); - } - - private List modulesChildren; - - private synchronized void initModulesChildren(AbstractJrtPath jrtPath) throws IOException { - if (modulesChildren == null) { - modulesChildren = new ArrayList<>(); - modulesChildren.addAll(findNode(jrtPath).getChildren()); - } - } - - private Iterator modulesDirIterator(AbstractJrtPath jrtPath) throws IOException { - initModulesChildren(jrtPath); - return nodesToIterator(jrtPath, modulesChildren); - } - - private List packagesChildren; - - private synchronized void initPackagesChildren(AbstractJrtPath jrtPath) throws IOException { - if (packagesChildren == null) { - packagesChildren = new ArrayList<>(); - packagesChildren.addAll(findNode(jrtPath).getChildren()); - } - } - - private Iterator packagesDirIterator(AbstractJrtPath jrtPath) throws IOException { - initPackagesChildren(jrtPath); - return nodesToIterator(jrtPath, packagesChildren); - } } diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java --- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java Fri Apr 15 10:14:57 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtFileSystemProvider.java Fri Apr 15 13:05:52 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -70,7 +70,7 @@ private void checkPermission() { SecurityManager sm = System.getSecurityManager(); if (sm != null) { - String home = SystemImages.RUNTIME_HOME; + String home = SystemImage.RUNTIME_HOME; FilePermission perm = new FilePermission(home + File.separator + "-", "read"); sm.checkPermission(perm); @@ -107,9 +107,7 @@ if (env != null && env.containsKey("java.home")) { return newFileSystem((String)env.get("java.home"), uri, env); } else { - return SystemImages.hasModulesImage() - ? new JrtFileSystem(this, env) - : new JrtExplodedFileSystem(this, env); + return new JrtFileSystem(this, env); } } @@ -121,7 +119,6 @@ if (Files.notExists(jrtfs)) { throw new IOException(jrtfs.toString() + " not exist"); } - Map newEnv = new HashMap<>(env); newEnv.remove("java.home"); ClassLoader cl = newJrtFsLoader(jrtfs); @@ -139,7 +136,6 @@ JrtFsLoader(URL[] urls) { super(urls); } - @Override protected Class loadClass(String cn, boolean resolve) throws ClassNotFoundException @@ -208,21 +204,7 @@ fs = this.theFileSystem; if (fs == null) { try { - if (SystemImages.hasModulesImage()) { - this.theFileSystem = fs = new JrtFileSystem(this, null) { - @Override - public void close() { - throw new UnsupportedOperationException(); - } - }; - } else { - this.theFileSystem = fs = new JrtExplodedFileSystem(this, null) { - @Override - public void close() { - throw new UnsupportedOperationException(); - } - }; - } + this.theFileSystem = fs = new JrtFileSystem(this, null); } catch (IOException ioe) { throw new InternalError(ioe); } @@ -240,69 +222,67 @@ } // Checks that the given file is a JrtPath - static final AbstractJrtPath toAbstractJrtPath(Path path) { - if (path == null) { - throw new NullPointerException(); - } - if (!(path instanceof AbstractJrtPath)) { + static final JrtPath toJrtPath(Path path) { + Objects.requireNonNull(path, "path"); + if (!(path instanceof JrtPath)) { throw new ProviderMismatchException(); } - return (AbstractJrtPath) path; + return (JrtPath) path; } @Override public void checkAccess(Path path, AccessMode... modes) throws IOException { - toAbstractJrtPath(path).checkAccess(modes); + toJrtPath(path).checkAccess(modes); } @Override public Path readSymbolicLink(Path link) throws IOException { - return toAbstractJrtPath(link).readSymbolicLink(); + return toJrtPath(link).readSymbolicLink(); } @Override public void copy(Path src, Path target, CopyOption... options) throws IOException { - toAbstractJrtPath(src).copy(toAbstractJrtPath(target), options); + toJrtPath(src).copy(toJrtPath(target), options); } @Override public void createDirectory(Path path, FileAttribute... attrs) throws IOException { - toAbstractJrtPath(path).createDirectory(attrs); + toJrtPath(path).createDirectory(attrs); } @Override public final void delete(Path path) throws IOException { - toAbstractJrtPath(path).delete(); + toJrtPath(path).delete(); } @Override @SuppressWarnings("unchecked") public V getFileAttributeView(Path path, Class type, LinkOption... options) { - return JrtFileAttributeView.get(toAbstractJrtPath(path), type, options); + return JrtFileAttributeView.get(toJrtPath(path), type, options); } @Override public FileStore getFileStore(Path path) throws IOException { - return toAbstractJrtPath(path).getFileStore(); + return toJrtPath(path).getFileStore(); } @Override public boolean isHidden(Path path) { - return toAbstractJrtPath(path).isHidden(); + return toJrtPath(path).isHidden(); } @Override public boolean isSameFile(Path path, Path other) throws IOException { - return toAbstractJrtPath(path).isSameFile(other); + return toJrtPath(path).isSameFile(other); } @Override public void move(Path src, Path target, CopyOption... options) throws IOException { - toAbstractJrtPath(src).move(toAbstractJrtPath(target), options); + toJrtPath(src).move(toJrtPath(target), options); } @Override @@ -319,13 +299,13 @@ Set options, FileAttribute... attrs) throws IOException { - return toAbstractJrtPath(path).newByteChannel(options, attrs); + return toJrtPath(path).newByteChannel(options, attrs); } @Override public DirectoryStream newDirectoryStream( Path path, Filter filter) throws IOException { - return toAbstractJrtPath(path).newDirectoryStream(filter); + return toJrtPath(path).newDirectoryStream(filter); } @Override @@ -333,19 +313,19 @@ Set options, FileAttribute... attrs) throws IOException { - return toAbstractJrtPath(path).newFileChannel(options, attrs); + return toJrtPath(path).newFileChannel(options, attrs); } @Override public InputStream newInputStream(Path path, OpenOption... options) throws IOException { - return toAbstractJrtPath(path).newInputStream(options); + return toJrtPath(path).newInputStream(options); } @Override public OutputStream newOutputStream(Path path, OpenOption... options) throws IOException { - return toAbstractJrtPath(path).newOutputStream(options); + return toJrtPath(path).newOutputStream(options); } @Override @@ -354,7 +334,7 @@ readAttributes(Path path, Class type, LinkOption... options) throws IOException { if (type == BasicFileAttributes.class || type == JrtFileAttributes.class) { - return (A) toAbstractJrtPath(path).getAttributes(options); + return (A) toJrtPath(path).getAttributes(options); } return null; } @@ -363,13 +343,13 @@ public Map readAttributes(Path path, String attribute, LinkOption... options) throws IOException { - return toAbstractJrtPath(path).readAttributes(attribute, options); + return toJrtPath(path).readAttributes(attribute, options); } @Override public void setAttribute(Path path, String attribute, Object value, LinkOption... options) throws IOException { - toAbstractJrtPath(path).setAttribute(attribute, value, options); + toJrtPath(path).setAttribute(attribute, value, options); } } diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtPath.java --- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtPath.java Fri Apr 15 10:14:57 2016 -0700 +++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/JrtPath.java Fri Apr 15 13:05:52 2016 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -24,8 +24,30 @@ */ package jdk.internal.jrtfs; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.channels.FileChannel; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.*; +import java.nio.file.DirectoryStream.Filter;; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.BasicFileAttributeView; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.attribute.FileTime; +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Set; +import static java.nio.file.StandardOpenOption.*; +import static java.nio.file.StandardCopyOption.*; + /** - * jrt Path implementation for jrt on .jimage files. + * Base class for Path implementation of jrt file systems. * * @implNote This class needs to maintain JDK 8 source compatibility. * @@ -33,23 +55,756 @@ * but also compiled and delivered as part of the jrtfs.jar to support access * to the jimage file provided by the shipped JDK by tools running on JDK 8. */ -final class JrtPath extends AbstractJrtPath { +final class JrtPath implements Path { + + final JrtFileSystem jrtfs; + private final String path; + private volatile int[] offsets; + + JrtPath(JrtFileSystem jrtfs, String path) { + this.jrtfs = jrtfs; + this.path = normalize(path); + this.resolved = null; + } + + JrtPath(JrtFileSystem jrtfs, String path, boolean normalized) { + this.jrtfs = jrtfs; + this.path = normalized ? path : normalize(path); + this.resolved = null; + } + + final String getName() { + return path; + } + + @Override + public final JrtPath getRoot() { + if (this.isAbsolute()) { + return jrtfs.getRootPath(); + } else { + return null; + } + } + + @Override + public final JrtPath getFileName() { + if (path.length() == 0) + return this; + if (path.length() == 1 && path.charAt(0) == '/') + return null; + int off = path.lastIndexOf('/'); + if (off == -1) + return this; + return new JrtPath(jrtfs, path.substring(off + 1), true); + } + + @Override + public final JrtPath getParent() { + initOffsets(); + int count = offsets.length; + if (count == 0) { // no elements so no parent + return null; + } + int off = offsets[count - 1] - 1; + if (off <= 0) { // parent is root only (may be null) + return getRoot(); + } + return new JrtPath(jrtfs, path.substring(0, off)); + } + + @Override + public final int getNameCount() { + initOffsets(); + return offsets.length; + } - JrtPath(AbstractJrtFileSystem jrtfs, byte[] path) { - this(jrtfs, path, false); + @Override + public final JrtPath getName(int index) { + initOffsets(); + if (index < 0 || index >= offsets.length) { + throw new IllegalArgumentException(); + } + int begin = offsets[index]; + int end; + if (index == (offsets.length - 1)) { + end = path.length(); + } else { + end = offsets[index + 1]; + } + return new JrtPath(jrtfs, path.substring(begin, end)); + } + + @Override + public final JrtPath subpath(int beginIndex, int endIndex) { + initOffsets(); + if (beginIndex < 0 || endIndex > offsets.length || + beginIndex >= endIndex) { + throw new IllegalArgumentException(); + } + // starting/ending offsets + int begin = offsets[beginIndex]; + int end; + if (endIndex == offsets.length) { + end = path.length(); + } else { + end = offsets[endIndex]; + } + return new JrtPath(jrtfs, path.substring(begin, end)); + } + + @Override + public final JrtPath toRealPath(LinkOption... options) throws IOException { + return jrtfs.toRealPath(this, options); + } + + @Override + public final JrtPath toAbsolutePath() { + if (isAbsolute()) + return this; + return new JrtPath(jrtfs, "/" + path, true); + } + + @Override + public final URI toUri() { + try { + return new URI("jrt", toAbsolutePath().path, null); + } catch (URISyntaxException ex) { + throw new AssertionError(ex); + } + } + + private boolean equalsNameAt(JrtPath other, int index) { + int mbegin = offsets[index]; + int mlen; + if (index == (offsets.length - 1)) { + mlen = path.length() - mbegin; + } else { + mlen = offsets[index + 1] - mbegin - 1; + } + int obegin = other.offsets[index]; + int olen; + if (index == (other.offsets.length - 1)) { + olen = other.path.length() - obegin; + } else { + olen = other.offsets[index + 1] - obegin - 1; + } + if (mlen != olen) { + return false; + } + int n = 0; + while (n < mlen) { + if (path.charAt(mbegin + n) != other.path.charAt(obegin + n)) { + return false; + } + n++; + } + return true; } - JrtPath(AbstractJrtFileSystem jrtfs, byte[] path, boolean normalized) { - super(jrtfs, path, normalized); + @Override + public final JrtPath relativize(Path other) { + final JrtPath o = checkPath(other); + if (o.equals(this)) { + return new JrtPath(jrtfs, "", true); + } + if (path.length() == 0) { + return o; + } + if (jrtfs != o.jrtfs || isAbsolute() != o.isAbsolute()) { + throw new IllegalArgumentException(); + } + final String tp = this.path; + final String op = o.path; + if (op.startsWith(tp)) { // fast path + int off = tp.length(); + if (op.charAt(off - 1) == '/') + return new JrtPath(jrtfs, op.substring(off), true); + if (op.charAt(off) == '/') + return new JrtPath(jrtfs, op.substring(off + 1), true); + } + int mc = this.getNameCount(); + int oc = o.getNameCount(); + int n = Math.min(mc, oc); + int i = 0; + while (i < n) { + if (!equalsNameAt(o, i)) { + break; + } + i++; + } + int dotdots = mc - i; + int len = dotdots * 3 - 1; + if (i < oc) { + len += (o.path.length() - o.offsets[i] + 1); + } + StringBuilder sb = new StringBuilder(len); + while (dotdots > 0) { + sb.append(".."); + if (sb.length() < len) { // no tailing slash at the end + sb.append('/'); + } + dotdots--; + } + if (i < oc) { + sb.append(o.path, o.offsets[i], o.path.length()); + } + return new JrtPath(jrtfs, sb.toString(), true); + } + + @Override + public JrtFileSystem getFileSystem() { + return jrtfs; + } + + @Override + public final boolean isAbsolute() { + return path.length() > 0 && path.charAt(0) == '/'; + } + + @Override + public final JrtPath resolve(Path other) { + final JrtPath o = checkPath(other); + if (this.path.length() == 0 || o.isAbsolute()) { + return o; + } + if (o.path.length() == 0) { + return this; + } + StringBuilder sb = new StringBuilder(path.length() + o.path.length()); + sb.append(path); + if (path.charAt(path.length() - 1) != '/') + sb.append('/'); + sb.append(o.path); + return new JrtPath(jrtfs, sb.toString(), true); + } + + @Override + public final Path resolveSibling(Path other) { + Objects.requireNonNull(other, "other"); + Path parent = getParent(); + return (parent == null) ? other : parent.resolve(other); + } + + @Override + public final boolean startsWith(Path other) { + if (!(Objects.requireNonNull(other) instanceof JrtPath)) + return false; + final JrtPath o = (JrtPath)other; + final String tp = this.path; + final String op = o.path; + if (isAbsolute() != o.isAbsolute() || !tp.startsWith(op)) { + return false; + } + int off = op.length(); + if (off == 0) { + return tp.length() == 0; + } + // check match is on name boundary + return tp.length() == off || tp.charAt(off) == '/' || + off == 0 || op.charAt(off - 1) == '/'; + } + + @Override + public final boolean endsWith(Path other) { + if (!(Objects.requireNonNull(other) instanceof JrtPath)) + return false; + final JrtPath o = (JrtPath)other; + final JrtPath t = this; + int olast = o.path.length() - 1; + if (olast > 0 && o.path.charAt(olast) == '/') { + olast--; + } + int last = t.path.length() - 1; + if (last > 0 && t.path.charAt(last) == '/') { + last--; + } + if (olast == -1) { // o.path.length == 0 + return last == -1; + } + if ((o.isAbsolute() && (!t.isAbsolute() || olast != last)) + || last < olast) { + return false; + } + for (; olast >= 0; olast--, last--) { + if (o.path.charAt(olast) != t.path.charAt(last)) { + return false; + } + } + return o.path.charAt(olast + 1) == '/' || + last == -1 || t.path.charAt(last) == '/'; + } + + @Override + public final JrtPath resolve(String other) { + return resolve(getFileSystem().getPath(other)); + } + + @Override + public final Path resolveSibling(String other) { + return resolveSibling(getFileSystem().getPath(other)); + } + + @Override + public final boolean startsWith(String other) { + return startsWith(getFileSystem().getPath(other)); + } + + @Override + public final boolean endsWith(String other) { + return endsWith(getFileSystem().getPath(other)); } @Override - protected JrtPath newJrtPath(byte[] path) { - return new JrtPath(jrtfs, path); + public final JrtPath normalize() { + String res = getResolved(); + if (res == path) { // no change + return this; + } + return new JrtPath(jrtfs, res, true); + } + + private JrtPath checkPath(Path path) { + Objects.requireNonNull(path); + if (!(path instanceof JrtPath)) + throw new ProviderMismatchException(); + return (JrtPath) path; + } + + // create offset list if not already created + private void initOffsets() { + if (this.offsets == null) { + int len = path.length(); + // count names + int count = 0; + int off = 0; + while (off < len) { + char c = path.charAt(off++); + if (c != '/') { + count++; + off = path.indexOf('/', off); + if (off == -1) + break; + } + } + // populate offsets + int[] offsets = new int[count]; + count = 0; + off = 0; + while (off < len) { + char c = path.charAt(off); + if (c == '/') { + off++; + } else { + offsets[count++] = off++; + off = path.indexOf('/', off); + if (off == -1) + break; + } + } + this.offsets = offsets; + } + } + + private volatile String resolved; + + final String getResolvedPath() { + String r = resolved; + if (r == null) { + if (isAbsolute()) { + r = getResolved(); + } else { + r = toAbsolutePath().getResolvedPath(); + } + resolved = r; + } + return r; + } + + // removes redundant slashs, replace "\" to separator "/" + // and check for invalid characters + private static String normalize(String path) { + int len = path.length(); + if (len == 0) { + return path; + } + char prevC = 0; + for (int i = 0; i < len; i++) { + char c = path.charAt(i); + if (c == '\\' || c == '\u0000') { + return normalize(path, i); + } + if (c == '/' && prevC == '/') { + return normalize(path, i - 1); + } + prevC = c; + } + if (prevC == '/' && len > 1) { + return path.substring(0, len - 1); + } + return path; + } + + private static String normalize(String path, int off) { + int len = path.length(); + StringBuilder to = new StringBuilder(len); + to.append(path, 0, off); + char prevC = 0; + while (off < len) { + char c = path.charAt(off++); + if (c == '\\') { + c = '/'; + } + if (c == '/' && prevC == '/') { + continue; + } + if (c == '\u0000') { + throw new InvalidPathException(path, + "Path: nul character not allowed"); + } + to.append(c); + prevC = c; + } + len = to.length(); + if (len > 1 && to.charAt(len - 1) == '/') { + to.deleteCharAt(len - 1); + } + return to.toString(); + } + + // Remove DotSlash(./) and resolve DotDot (..) components + private String getResolved() { + if (path.length() == 0) { + return path; + } + if (path.indexOf('.') == -1) { + return path; + } + int length = path.length(); + char[] to = new char[length]; + int nc = getNameCount(); + int[] lastM = new int[nc]; + int lastMOff = -1; + int m = 0; + for (int i = 0; i < nc; i++) { + int n = offsets[i]; + int len = (i == offsets.length - 1) ? length - n + : offsets[i + 1] - n - 1; + if (len == 1 && path.charAt(n) == '.') { + if (m == 0 && path.charAt(0) == '/') // absolute path + to[m++] = '/'; + continue; + } + if (len == 2 && path.charAt(n) == '.' && path.charAt(n + 1) == '.') { + if (lastMOff >= 0) { + m = lastM[lastMOff--]; // retreat + continue; + } + if (path.charAt(0) == '/') { // "/../xyz" skip + if (m == 0) + to[m++] = '/'; + } else { // "../xyz" -> "../xyz" + if (m != 0 && to[m-1] != '/') + to[m++] = '/'; + while (len-- > 0) + to[m++] = path.charAt(n++); + } + continue; + } + if (m == 0 && path.charAt(0) == '/' || // absolute path + m != 0 && to[m-1] != '/') { // not the first name + to[m++] = '/'; + } + lastM[++lastMOff] = m; + while (len-- > 0) + to[m++] = path.charAt(n++); + } + if (m > 1 && to[m - 1] == '/') + m--; + return (m == to.length) ? new String(to) : new String(to, 0, m); + } + + @Override + public final String toString() { + return path; + } + + @Override + public final int hashCode() { + return path.hashCode(); + } + + @Override + public final boolean equals(Object obj) { + return obj instanceof JrtPath && + this.path.equals(((JrtPath) obj).path); + } + + @Override + public final int compareTo(Path other) { + final JrtPath o = checkPath(other); + return path.compareTo(o.path); + } + + @Override + public final WatchKey register( + WatchService watcher, + WatchEvent.Kind[] events, + WatchEvent.Modifier... modifiers) { + Objects.requireNonNull(watcher, "watcher"); + Objects.requireNonNull(events, "events"); + Objects.requireNonNull(modifiers, "modifiers"); + throw new UnsupportedOperationException(); + } + + @Override + public final WatchKey register(WatchService watcher, WatchEvent.Kind... events) { + return register(watcher, events, new WatchEvent.Modifier[0]); + } + + @Override + public final File toFile() { + throw new UnsupportedOperationException(); } @Override - protected JrtPath newJrtPath(byte[] path, boolean normalized) { - return new JrtPath(jrtfs, path, normalized); + public final Iterator iterator() { + return new Iterator() { + private int i = 0; + + @Override + public boolean hasNext() { + return (i < getNameCount()); + } + + @Override + public Path next() { + if (i < getNameCount()) { + Path result = getName(i); + i++; + return result; + } else { + throw new NoSuchElementException(); + } + } + + @Override + public void remove() { + throw new ReadOnlyFileSystemException(); + } + }; + } + + // Helpers for JrtFileSystemProvider and JrtFileSystem + + final JrtPath readSymbolicLink() throws IOException { + if (!jrtfs.isLink(this)) { + throw new IOException("not a symbolic link"); + } + return jrtfs.resolveLink(this); + } + + final boolean isHidden() { + return false; + } + + final void createDirectory(FileAttribute... attrs) + throws IOException { + jrtfs.createDirectory(this, attrs); + } + + final InputStream newInputStream(OpenOption... options) throws IOException { + if (options.length > 0) { + for (OpenOption opt : options) { + if (opt != READ) { + throw new UnsupportedOperationException("'" + opt + "' not allowed"); + } + } + } + return jrtfs.newInputStream(this); + } + + final DirectoryStream newDirectoryStream(Filter filter) + throws IOException { + return new JrtDirectoryStream(this, filter); + } + + final void delete() throws IOException { + jrtfs.deleteFile(this, true); + } + + final void deleteIfExists() throws IOException { + jrtfs.deleteFile(this, false); + } + + final JrtFileAttributes getAttributes(LinkOption... options) throws IOException { + JrtFileAttributes zfas = jrtfs.getFileAttributes(this, options); + if (zfas == null) { + throw new NoSuchFileException(toString()); + } + return zfas; + } + + final void setAttribute(String attribute, Object value, LinkOption... options) + throws IOException { + JrtFileAttributeView.setAttribute(this, attribute, value); + } + + final Map readAttributes(String attributes, LinkOption... options) + throws IOException { + return JrtFileAttributeView.readAttributes(this, attributes, options); + } + + final void setTimes(FileTime mtime, FileTime atime, FileTime ctime) + throws IOException { + jrtfs.setTimes(this, mtime, atime, ctime); + } + + final FileStore getFileStore() throws IOException { + // each JrtFileSystem only has one root (as requested for now) + if (exists()) { + return jrtfs.getFileStore(this); + } + throw new NoSuchFileException(path); + } + + final boolean isSameFile(Path other) throws IOException { + if (this == other || this.equals(other)) { + return true; + } + if (other == null || this.getFileSystem() != other.getFileSystem()) { + return false; + } + this.checkAccess(); + JrtPath o = (JrtPath) other; + o.checkAccess(); + return this.getResolvedPath().equals(o.getResolvedPath()) || + jrtfs.isSameFile(this, o); + } + + final SeekableByteChannel newByteChannel(Set options, + FileAttribute... attrs) + throws IOException + { + return jrtfs.newByteChannel(this, options, attrs); + } + + final FileChannel newFileChannel(Set options, + FileAttribute... attrs) + throws IOException { + return jrtfs.newFileChannel(this, options, attrs); + } + + final void checkAccess(AccessMode... modes) throws IOException { + if (modes.length == 0) { // check if the path exists + jrtfs.checkNode(this); // no need to follow link. the "link" node + // is built from real node under "/module" + } else { + boolean w = false; + for (AccessMode mode : modes) { + switch (mode) { + case READ: + break; + case WRITE: + w = true; + break; + case EXECUTE: + throw new AccessDeniedException(toString()); + default: + throw new UnsupportedOperationException(); + } + } + jrtfs.checkNode(this); + if (w && jrtfs.isReadOnly()) { + throw new AccessDeniedException(toString()); + } + } + } + + final boolean exists() { + try { + return jrtfs.exists(this); + } catch (IOException x) {} + return false; + } + + final OutputStream newOutputStream(OpenOption... options) throws IOException { + if (options.length == 0) { + return jrtfs.newOutputStream(this, CREATE_NEW, WRITE); + } + return jrtfs.newOutputStream(this, options); + } + + final void move(JrtPath target, CopyOption... options) throws IOException { + if (this.jrtfs == target.jrtfs) { + jrtfs.copyFile(true, this, target, options); + } else { + copyToTarget(target, options); + delete(); + } + } + + final void copy(JrtPath target, CopyOption... options) throws IOException { + if (this.jrtfs == target.jrtfs) { + jrtfs.copyFile(false, this, target, options); + } else { + copyToTarget(target, options); + } + } + + private void copyToTarget(JrtPath target, CopyOption... options) + throws IOException { + boolean replaceExisting = false; + boolean copyAttrs = false; + for (CopyOption opt : options) { + if (opt == REPLACE_EXISTING) { + replaceExisting = true; + } else if (opt == COPY_ATTRIBUTES) { + copyAttrs = true; + } + } + // attributes of source file + BasicFileAttributes jrtfas = getAttributes(); + // check if target exists + boolean exists; + if (replaceExisting) { + try { + target.deleteIfExists(); + exists = false; + } catch (DirectoryNotEmptyException x) { + exists = true; + } + } else { + exists = target.exists(); + } + if (exists) { + throw new FileAlreadyExistsException(target.toString()); + } + if (jrtfas.isDirectory()) { + // create directory or file + target.createDirectory(); + } else { + try (InputStream is = jrtfs.newInputStream(this); + OutputStream os = target.newOutputStream()) { + byte[] buf = new byte[8192]; + int n; + while ((n = is.read(buf)) != -1) { + os.write(buf, 0, n); + } + } + } + if (copyAttrs) { + BasicFileAttributeView view = + Files.getFileAttributeView(target, BasicFileAttributeView.class); + try { + view.setTimes(jrtfas.lastModifiedTime(), + jrtfas.lastAccessTime(), + jrtfas.creationTime()); + } catch (IOException x) { + try { + target.delete(); // rollback? + } catch (IOException ignore) {} + throw x; + } + } } } diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/src/java.base/share/classes/jdk/internal/jrtfs/SystemImage.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/jdk/src/java.base/share/classes/jdk/internal/jrtfs/SystemImage.java Fri Apr 15 13:05:52 2016 -0700 @@ -0,0 +1,126 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package jdk.internal.jrtfs; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.file.Files; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.security.AccessController; +import java.security.CodeSource; +import java.security.PrivilegedAction; + +import jdk.internal.jimage.ImageReader; +import jdk.internal.jimage.ImageReader.Node; + +/** + * @implNote This class needs to maintain JDK 8 source compatibility. + * + * It is used internally in the JDK to implement jimage/jrtfs access, + * but also compiled and delivered as part of the jrtfs.jar to support access + * to the jimage file provided by the shipped JDK by tools running on JDK 8. + */ +abstract class SystemImage { + + abstract Node findNode(String path); + abstract byte[] getResource(Node node) throws IOException; + abstract void close() throws IOException; + + static SystemImage open() throws IOException { + if (modulesImageExists) { + // open a .jimage and build directory structure + final ImageReader image = ImageReader.open(moduleImageFile); + image.getRootDirectory(); + return new SystemImage() { + @Override + Node findNode(String path) { + return image.findNode(path); + } + @Override + byte[] getResource(Node node) throws IOException { + return image.getResource(node); + } + @Override + void close() throws IOException { + image.close(); + } + }; + } + if (Files.notExists(explodedModulesDir)) + throw new FileSystemNotFoundException(explodedModulesDir.toString()); + return new ExplodedImage(explodedModulesDir); + } + + static final String RUNTIME_HOME; + // "modules" jimage file Path + final static Path moduleImageFile; + // "modules" jimage exists or not? + final static boolean modulesImageExists; + // /modules directory Path + static final Path explodedModulesDir; + + static { + PrivilegedAction pa = SystemImage::findHome; + RUNTIME_HOME = AccessController.doPrivileged(pa); + + FileSystem fs = FileSystems.getDefault(); + moduleImageFile = fs.getPath(RUNTIME_HOME, "lib", "modules"); + explodedModulesDir = fs.getPath(RUNTIME_HOME, "modules"); + + modulesImageExists = AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public Boolean run() { + return Files.isRegularFile(moduleImageFile); + } + }); + } + + /** + * Returns the appropriate JDK home for this usage of the FileSystemProvider. + * When the CodeSource is null (null loader) then jrt:/ is the current runtime, + * otherwise the JDK home is located relative to jrt-fs.jar. + */ + private static String findHome() { + CodeSource cs = SystemImage.class.getProtectionDomain().getCodeSource(); + if (cs == null) + return System.getProperty("java.home"); + + // assume loaded from $TARGETJDK/jrt-fs.jar + URL url = cs.getLocation(); + if (!url.getProtocol().equalsIgnoreCase("file")) + throw new RuntimeException(url + " loaded in unexpected way"); + try { + return Paths.get(url.toURI()).getParent().toString(); + } catch (URISyntaxException e) { + throw new InternalError(e); + } + } +} diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/src/java.base/share/classes/jdk/internal/jrtfs/SystemImages.java --- a/jdk/src/java.base/share/classes/jdk/internal/jrtfs/SystemImages.java Fri Apr 15 10:14:57 2016 -0700 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,106 +0,0 @@ -/* - * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ -package jdk.internal.jrtfs; - -import java.net.URISyntaxException; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.FileSystem; -import java.nio.file.FileSystems; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.security.AccessController; -import java.security.CodeSource; -import java.security.PrivilegedAction; - -/** - * @implNote This class needs to maintain JDK 8 source compatibility. - * - * It is used internally in the JDK to implement jimage/jrtfs access, - * but also compiled and delivered as part of the jrtfs.jar to support access - * to the jimage file provided by the shipped JDK by tools running on JDK 8. - */ -final class SystemImages { - private SystemImages() {} - - - static final String RUNTIME_HOME; - // "modules" jimage file Path - private static final Path moduleImageFile; - // "modules" jimage exists or not? - private static final boolean modulesImageExists; - // /modules directory Path - private static final Path explodedModulesDir; - - static { - PrivilegedAction pa = SystemImages::findHome; - RUNTIME_HOME = AccessController.doPrivileged(pa); - - FileSystem fs = FileSystems.getDefault(); - moduleImageFile = fs.getPath(RUNTIME_HOME, "lib", "modules"); - explodedModulesDir = fs.getPath(RUNTIME_HOME, "modules"); - - modulesImageExists = AccessController.doPrivileged( - new PrivilegedAction() { - @Override - public Boolean run() { - return Files.isRegularFile(moduleImageFile); - } - }); - } - - static boolean hasModulesImage() { - return modulesImageExists; - } - - static Path moduleImageFile() { - return moduleImageFile; - } - - static Path explodedModulesDir() { - return explodedModulesDir; - } - - /** - * Returns the appropriate JDK home for this usage of the FileSystemProvider. - * When the CodeSource is null (null loader) then jrt:/ is the current runtime, - * otherwise the JDK home is located relative to jrt-fs.jar. - */ - private static String findHome() { - CodeSource cs = SystemImages.class.getProtectionDomain().getCodeSource(); - if (cs == null) - return System.getProperty("java.home"); - - // assume loaded from $TARGETJDK/jrt-fs.jar - URL url = cs.getLocation(); - if (!url.getProtocol().equalsIgnoreCase("file")) - throw new RuntimeException(url + " loaded in unexpected way"); - try { - return Paths.get(url.toURI()).getParent().toString(); - } catch (URISyntaxException e) { - throw new InternalError(e); - } - } -} diff -r 80be215c8c51 -r 9cc4eb4d7491 jdk/test/jdk/internal/jrtfs/PathOps.java --- a/jdk/test/jdk/internal/jrtfs/PathOps.java Fri Apr 15 10:14:57 2016 -0700 +++ b/jdk/test/jdk/internal/jrtfs/PathOps.java Fri Apr 15 13:05:52 2016 -0700 @@ -43,15 +43,15 @@ private Path path; private Exception exc; - private PathOps(String s) { + private PathOps(String first, String... more) { out.println(); - input = s; + input = first; try { - path = fs.getPath(s); - out.format("%s -> %s", s, path); + path = fs.getPath(first, more); + out.format("%s -> %s", first, path); } catch (Exception x) { exc = x; - out.format("%s -> %s", s, x); + out.format("%s -> %s", first, x); } out.println(); } @@ -175,6 +175,13 @@ return this; } + PathOps resolveSibling(String other, String expected) { + out.format("test resolveSibling %s\n", other); + checkPath(); + check(path.resolveSibling(other), expected); + return this; + } + PathOps relativize(String other, String expected) { out.format("test relativize %s\n", other); checkPath(); @@ -220,6 +227,10 @@ return new PathOps(s); } + static PathOps test(String first, String... more) { + return new PathOps(first, more); + } + // -- PathOpss -- static void header(String s) { @@ -231,6 +242,26 @@ static void doPathOpTests() { header("Path operations"); + // construction + test("/") + .string("/"); + test("/", "") + .string("/"); + test("/", "foo") + .string("/foo"); + test("/", "/foo") + .string("/foo"); + test("/", "foo/") + .string("/foo"); + test("foo", "bar", "gus") + .string("foo/bar/gus"); + test("") + .string(""); + test("", "/") + .string("/"); + test("", "foo", "", "bar", "", "/gus") + .string("foo/bar/gus"); + // all components test("/a/b/c") .root("/") @@ -254,6 +285,10 @@ .root(null) .parent(null) .name("foo"); + test("") + .root(null) + .parent(null) + .name(""); // startsWith test("") @@ -261,6 +296,7 @@ .notStarts("/"); test("/") .starts("/") + .notStarts("") .notStarts("/foo"); test("/foo") .starts("/") @@ -278,6 +314,7 @@ .notStarts(""); test("foo") .starts("foo") + .notStarts("") .notStarts("f"); test("foo/bar") .starts("foo") @@ -293,12 +330,14 @@ .notEnds("/"); test("/") .ends("/") + .notEnds("") .notEnds("foo") .notEnds("/foo"); test("/foo") .ends("foo") .ends("/foo") - .notEnds("/"); + .notEnds("/") + .notEnds("fool"); test("/foo/bar") .ends("bar") .ends("foo/bar") @@ -312,13 +351,31 @@ .ends("/foo/bar") .notEnds("/bar"); test("foo") - .ends("foo"); + .ends("foo") + .notEnds("") + .notEnds("oo") + .notEnds("oola"); test("foo/bar") .ends("bar") .ends("bar/") .ends("foo/bar/") - .ends("foo/bar"); - + .ends("foo/bar") + .notEnds("r") + .notEnds("barmaid") + .notEnds("/bar") + .notEnds("ar") + .notEnds("barack") + .notEnds("/bar") + .notEnds("o/bar"); + test("foo/bar/gus") + .ends("gus") + .ends("bar/gus") + .ends("foo/bar/gus") + .notEnds("g") + .notEnds("/gus") + .notEnds("r/gus") + .notEnds("barack/gus") + .notEnds("bar/gust"); // elements test("a/b/c") @@ -339,16 +396,54 @@ // resolve test("/tmp") .resolve("foo", "/tmp/foo") - .resolve("/foo", "/foo"); + .resolve("/foo", "/foo") + .resolve("", "/tmp"); test("tmp") .resolve("foo", "tmp/foo") + .resolve("/foo", "/foo") + .resolve("", "tmp"); + test("") + .resolve("", "") + .resolve("foo", "foo") .resolve("/foo", "/foo"); + // resolveSibling + test("foo") + .resolveSibling("bar", "bar") + .resolveSibling("/bar", "/bar") + .resolveSibling("", ""); + test("foo/bar") + .resolveSibling("gus", "foo/gus") + .resolveSibling("/gus", "/gus") + .resolveSibling("", "foo"); + test("/foo") + .resolveSibling("gus", "/gus") + .resolveSibling("/gus", "/gus") + .resolveSibling("", "/"); + test("/foo/bar") + .resolveSibling("gus", "/foo/gus") + .resolveSibling("/gus", "/gus") + .resolveSibling("", "/foo"); + test("") + .resolveSibling("foo", "foo") + .resolveSibling("/foo", "/foo") + .resolve("", ""); + // relativize test("/a/b/c") .relativize("/a/b/c", "") .relativize("/a/b/c/d/e", "d/e") - .relativize("/a/x", "../../x"); + .relativize("/a/x", "../../x") + .relativize("/x", "../../../x"); + test("a/b/c") + .relativize("a/b/c/d", "d") + .relativize("a/x", "../../x") + .relativize("x", "../../../x") + .relativize("", "../../.."); + test("") + .relativize("a", "a") + .relativize("a/b/c", "a/b/c") + .relativize("", ""); // normalize test("/")