8147460: Clean-up jrtfs implementation

Reviewed-by: alanb, jlaskey, sundar
This commit is contained in:
Xueming Shen 2016-04-15 13:05:52 -07:00
parent 965536262b
commit f4a9612da3
17 changed files with 1725 additions and 2512 deletions

View File

@ -136,7 +136,7 @@ public class ImageReader extends BasicImageReader {
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);
}

View File

@ -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();
}
}

View File

@ -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<String, ?> 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<? extends OpenOption> 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<Path> getRootDirectories() {
ArrayList<Path> 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<FileStore> getFileStores() {
ArrayList<FileStore> list = new ArrayList<>(1);
list.add(getFileStore(getRootPath()));
return list;
}
private static final Set<String> supportedFileAttributeViews
= Collections.unmodifiableSet(
new HashSet<String>(Arrays.asList("basic", "jrt")));
@Override
public final Set<String> 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<? extends OpenOption> 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<? extends OpenOption> 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<Path> iteratorOf(AbstractJrtPath jrtPath) throws IOException;
// returns the content of the file resource specified by the path
abstract byte[] getFileContent(AbstractJrtPath jrtPath) throws IOException;
}

View File

@ -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<Path> iterator() {
return new Iterator<Path>() {
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<Path> newDirectoryStream(Filter<? super Path> 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<String, Object> 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<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException {
return jrtfs.newByteChannel(this, options, attrs);
}
final FileChannel newFileChannel(Set<? extends OpenOption> 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;
}
}
}
}

View File

@ -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<String, PathNode> 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<Node> 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<Node> 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<Node> getChildren() {
if (!isDirectory())
throw new IllegalArgumentException("not a directory: " + getNameString());
if (children == null) {
List<Node> list = new ArrayList<>();
try (DirectoryStream<Path> 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/<package>/<module>/xyz
// For example /packages/java.lang/java.base/java/lang/
if (str.startsWith(PACKAGES)) {
// pkgEndIdx marks end of <package> part
int pkgEndIdx = str.indexOf('/', PACKAGES_LEN);
if (pkgEndIdx != -1) {
// modEndIdx marks end of <module> part
int modEndIdx = str.indexOf('/', pkgEndIdx + 1);
if (modEndIdx != -1) {
// make sure we have such module link!
// ie., /packages/<package>/<module> 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 "<JDK_MODULES_DIR>/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<String, List<String>> packageToModules = new HashMap<>();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(explodedModulesDir)) {
for (Path module : stream) {
if (Files.isDirectory(module)) {
String moduleName = module.getFileName().toString();
// make sure "/modules/<moduleName>" 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<String> 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/<foo> 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<Node> packagesChildren = new ArrayList<>(packageToModules.size());
for (Map.Entry<String, List<String>> entry : packageToModules.entrySet()) {
String pkgName = entry.getKey();
List<String> moduleNameList = entry.getValue();
List<Node> 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<Node> rootChildren = new ArrayList<>();
rootChildren.add(packagesDir);
rootChildren.add(modulesDir);
PathNode root = new PathNode("/", rootChildren);
nodes.put(root.getName(), root);
}
}

View File

@ -30,6 +30,7 @@ import java.nio.file.DirectoryIteratorException;
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 @@ import java.io.IOException;
*/
final class JrtDirectoryStream implements DirectoryStream<Path> {
private final AbstractJrtFileSystem jrtfs;
private final AbstractJrtPath dir;
private final JrtPath dir;
private final DirectoryStream.Filter<? super Path> filter;
private volatile boolean isClosed;
private volatile Iterator<Path> itr;
JrtDirectoryStream(AbstractJrtPath jrtPath,
JrtDirectoryStream(JrtPath dir,
DirectoryStream.Filter<? super java.nio.file.Path> 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<Path> 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<Path>() {
/*
* 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

View File

@ -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();
}
}

View File

@ -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<String, Node> nodes = Collections.synchronizedMap(new HashMap<>());
private final BasicFileAttributes modulesDirAttrs;
JrtExplodedFileSystem(JrtFileSystemProvider provider,
Map<String, ?> 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<Node> 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<Node> getChildren() throws IOException {
if (!isDirectory()) {
throw new NotDirectoryException(getName());
}
List<Node> children = new ArrayList<>();
try (DirectoryStream<Path> 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<Node> children;
DirNode(String name, List<Node> children) {
super(name);
this.children = children;
}
@Override
boolean isDirectory() {
return true;
}
@Override
List<Node> 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<Path> iteratorOf(AbstractJrtPath dir) throws IOException {
Node node = checkNode(dir).resolveLink(true);
if (!node.isDirectory()) {
throw new NotDirectoryException(getString(dir.getName()));
}
Function<Node, Path> 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/<package>/<module>/xyz
// For example /packages/java.lang/java.base/java/lang/
if (str.startsWith(PACKAGES)) {
// pkgEndIdx marks end of <package> part
int pkgEndIdx = str.indexOf('/', PACKAGES_LEN);
if (pkgEndIdx != -1) {
// modEndIdx marks end of <module> part
int modEndIdx = str.indexOf('/', pkgEndIdx + 1);
if (modEndIdx != -1) {
// make sure we have such module link!
// ie., /packages/<package>/<module> 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 "<JDK_MODULES_DIR>/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<String, List<String>> packageToModules = new HashMap<>();
try (DirectoryStream<Path> stream = Files.newDirectoryStream(SystemImages.explodedModulesDir())) {
for (Path module : stream) {
if (Files.isDirectory(module)) {
String moduleName = module.getFileName().toString();
// make sure "/modules/<moduleName>" 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<String> 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/<foo> 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<Node> packagesChildren = new ArrayList<>(packageToModules.size());
for (Map.Entry<String, List<String>> entry : packageToModules.entrySet()) {
String pkgName = entry.getKey();
List<String> moduleNameList = entry.getValue();
List<Node> 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<Node> rootChildren = new ArrayList<>();
rootChildren.add(modulesDir);
rootChildren.add(packagesDir);
DirNode root = new DirNode("/", rootChildren);
nodes.put(root.getName(), root);
}
}

View File

@ -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;
}
}

View File

@ -29,6 +29,7 @@ import java.nio.file.attribute.*;
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 @@ import java.util.Map;
final class JrtFileAttributeView implements BasicFileAttributeView {
private static enum AttrID {
size,
creationTime,
lastAccessTime,
@ -56,21 +56,19 @@ final class JrtFileAttributeView implements BasicFileAttributeView {
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 extends FileAttributeView> V get(AbstractJrtPath path, Class<V> type, LinkOption... options) {
if (type == null) {
throw new NullPointerException();
}
static <V extends FileAttributeView> V get(JrtPath path, Class<V> type, LinkOption... options) {
Objects.requireNonNull(type);
if (type == BasicFileAttributeView.class) {
return (V) new JrtFileAttributeView(path, false, options);
}
@ -80,10 +78,8 @@ final class JrtFileAttributeView implements BasicFileAttributeView {
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 @@ final class JrtFileAttributeView implements BasicFileAttributeView {
}
@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 {
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");
}
attribute = attribute.substring(colonPos);
}
try {
if (AttrID.valueOf(attribute) == AttrID.lastModifiedTime) {
setTimes((FileTime) value, null, null);
}
if (AttrID.valueOf(attribute) == AttrID.lastAccessTime) {
setTimes(null, (FileTime) value, null);
}
if (AttrID.valueOf(attribute) == AttrID.creationTime) {
setTimes(null, null, (FileTime) value);
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<String, Object> readAttributes(String attributes)
static Map<String, Object> 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<String, Object> 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();

View File

@ -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 @@ import jdk.internal.jimage.ImageReader.Node;
* 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 @@ final class JrtFileAttributes extends AbstractJrtFileAttributes {
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();
}
}

View File

@ -30,6 +30,7 @@ import java.nio.file.FileSystem;
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 @@ final class JrtFileStore extends FileStore {
protected final FileSystem jrtfs;
JrtFileStore(AbstractJrtPath jrtPath) {
JrtFileStore(JrtPath jrtPath) {
this.jrtfs = jrtPath.getFileSystem();
}
@ -71,9 +72,7 @@ final class JrtFileStore extends FileStore {
@Override
@SuppressWarnings("unchecked")
public <V extends FileStoreAttributeView> V getFileStoreAttributeView(Class<V> type) {
if (type == null) {
throw new NullPointerException();
}
Objects.requireNonNull(type, "type");
return (V) null;
}
@ -99,7 +98,7 @@ final class JrtFileStore extends FileStore {
@Override
public boolean supportsFileAttributeView(Class<? extends FileAttributeView> type) {
return (type == BasicFileAttributeView.class
|| type == JrtFileAttributeView.class);
return type == BasicFileAttributeView.class ||
type == JrtFileAttributeView.class;
}
}

View File

@ -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 static java.util.stream.Collectors.toList;
import jdk.internal.jimage.ImageReader;
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;
/**
* jrt file system implementation built on System jimage files.
@ -51,33 +75,21 @@ import jdk.internal.jimage.ImageReader.Node;
* 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<String, ?> 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<String, ?> 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 @@ class JrtFileSystem extends AbstractJrtFileSystem {
@Override
public void close() throws IOException {
if (!isClosable)
throw new UnsupportedOperationException();
cleanup();
}
@ -95,237 +109,397 @@ class JrtFileSystem extends AbstractJrtFileSystem {
protected void finalize() throws Throwable {
try {
cleanup();
} catch (IOException ignored) {
} catch (IOException ignored) {}
}
@Override
public FileSystemProvider provider() {
return provider;
}
@Override
public Iterable<Path> getRootDirectories() {
ArrayList<Path> dirs = new ArrayList<>();
dirs.add(getRootPath());
return dirs;
}
@Override
public JrtPath getPath(String first, String... more) {
if (more.length == 0) {
return new JrtPath(this, first);
}
super.finalize();
}
// AbstractJrtFileSystem method implementations
@Override
JrtPath getRootPath() {
return rootPath;
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
boolean isSameFile(AbstractJrtPath p1, AbstractJrtPath p2) throws IOException {
ensureOpen();
Node node1 = findNode(p1);
Node node2 = findNode(p2);
return node1.equals(node2);
public final boolean isReadOnly() {
return true;
}
@Override
boolean isLink(AbstractJrtPath jrtPath) throws IOException {
return checkNode(jrtPath).isLink();
public final UserPrincipalLookupService getUserPrincipalLookupService() {
throw new UnsupportedOperationException();
}
@Override
AbstractJrtPath resolveLink(AbstractJrtPath jrtPath) throws IOException {
Node node = checkNode(jrtPath);
public final WatchService newWatchService() {
throw new UnsupportedOperationException();
}
@Override
public final Iterable<FileStore> getFileStores() {
ArrayList<FileStore> list = new ArrayList<>(1);
list.add(getFileStore(getRootPath()));
return list;
}
private static final Set<String> supportedFileAttributeViews
= Collections.unmodifiableSet(
new HashSet<String>(Arrays.asList("basic", "jrt")));
@Override
public final Set<String> supportedFileAttributeViews() {
return supportedFileAttributeViews;
}
@Override
public final String toString() {
return "jrt:/";
}
@Override
public final String getSeparator() {
return "/";
}
@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")) {
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<Path> iteratorOf(JrtPath path, DirectoryStream.Filter<? super Path> 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<? extends OpenOption> 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<? extends OpenOption> 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<? extends OpenOption> 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<Path> 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(byte[] path) {
Node node = null;
private Node lookup(String path) {
try {
node = bootImage.findNode(getString(path));
return image.findNode(path);
} catch (RuntimeException re) {
throw new InvalidPathException(getString(path), re.toString());
throw new InvalidPathException(path, re.toString());
}
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);
}
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 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<Path> nodesToIterator(AbstractJrtPath dir, List<Node> childNodes) {
Function<Node, Path> nodeToPath =
child -> dir.resolve(
toJrtPath(child.getNameString()).getFileName());
return childNodes.stream().
map(nodeToPath).collect(toList()).
iterator();
}
private List<Node> rootChildren;
private synchronized void initRootChildren(AbstractJrtPath jrtPath) throws IOException {
if (rootChildren == null) {
rootChildren = new ArrayList<>();
rootChildren.addAll(findNode(jrtPath).getChildren());
}
}
private Iterator<Path> rootDirIterator(AbstractJrtPath jrtPath) throws IOException {
initRootChildren(jrtPath);
return nodesToIterator(jrtPath, rootChildren);
}
private List<Node> modulesChildren;
private synchronized void initModulesChildren(AbstractJrtPath jrtPath) throws IOException {
if (modulesChildren == null) {
modulesChildren = new ArrayList<>();
modulesChildren.addAll(findNode(jrtPath).getChildren());
}
}
private Iterator<Path> modulesDirIterator(AbstractJrtPath jrtPath) throws IOException {
initModulesChildren(jrtPath);
return nodesToIterator(jrtPath, modulesChildren);
}
private List<Node> packagesChildren;
private synchronized void initPackagesChildren(AbstractJrtPath jrtPath) throws IOException {
if (packagesChildren == null) {
packagesChildren = new ArrayList<>();
packagesChildren.addAll(findNode(jrtPath).getChildren());
}
}
private Iterator<Path> packagesDirIterator(AbstractJrtPath jrtPath) throws IOException {
initPackagesChildren(jrtPath);
return nodesToIterator(jrtPath, packagesChildren);
}
}

View File

@ -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 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
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 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
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 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
if (Files.notExists(jrtfs)) {
throw new IOException(jrtfs.toString() + " not exist");
}
Map<String,?> newEnv = new HashMap<>(env);
newEnv.remove("java.home");
ClassLoader cl = newJrtFsLoader(jrtfs);
@ -139,7 +136,6 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
JrtFsLoader(URL[] urls) {
super(urls);
}
@Override
protected Class<?> loadClass(String cn, boolean resolve)
throws ClassNotFoundException
@ -208,21 +204,7 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
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 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
}
// 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 extends FileAttributeView> V
getFileAttributeView(Path path, Class<V> 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 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
Set<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException {
return toAbstractJrtPath(path).newByteChannel(options, attrs);
return toJrtPath(path).newByteChannel(options, attrs);
}
@Override
public DirectoryStream<Path> newDirectoryStream(
Path path, Filter<? super Path> filter) throws IOException {
return toAbstractJrtPath(path).newDirectoryStream(filter);
return toJrtPath(path).newDirectoryStream(filter);
}
@Override
@ -333,19 +313,19 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
Set<? extends OpenOption> 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 @@ public final class JrtFileSystemProvider extends FileSystemProvider {
readAttributes(Path path, Class<A> 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 final class JrtFileSystemProvider extends FileSystemProvider {
public Map<String, Object>
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);
}
}

View File

@ -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 @@ package jdk.internal.jrtfs;
* 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 {
JrtPath(AbstractJrtFileSystem jrtfs, byte[] path) {
this(jrtfs, path, false);
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(AbstractJrtFileSystem jrtfs, byte[] path, boolean normalized) {
super(jrtfs, path, normalized);
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
protected JrtPath newJrtPath(byte[] path) {
return new JrtPath(jrtfs, path);
public final JrtPath getRoot() {
if (this.isAbsolute()) {
return jrtfs.getRootPath();
} else {
return null;
}
}
@Override
protected JrtPath newJrtPath(byte[] path, boolean normalized) {
return new JrtPath(jrtfs, path, normalized);
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;
}
@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;
}
@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
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
public final Iterator<Path> iterator() {
return new Iterator<Path>() {
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<Path> newDirectoryStream(Filter<? super Path> 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<String, Object> 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<? extends OpenOption> options,
FileAttribute<?>... attrs)
throws IOException
{
return jrtfs.newByteChannel(this, options, attrs);
}
final FileChannel newFileChannel(Set<? extends OpenOption> 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;
}
}
}
}

View File

@ -24,17 +24,22 @@
*/
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.
*
@ -42,20 +47,47 @@ import java.security.PrivilegedAction;
* 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() {}
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
private static final Path moduleImageFile;
final static Path moduleImageFile;
// "modules" jimage exists or not?
private static final boolean modulesImageExists;
final static boolean modulesImageExists;
// <JAVA_HOME>/modules directory Path
private static final Path explodedModulesDir;
static final Path explodedModulesDir;
static {
PrivilegedAction<String> pa = SystemImages::findHome;
PrivilegedAction<String> pa = SystemImage::findHome;
RUNTIME_HOME = AccessController.doPrivileged(pa);
FileSystem fs = FileSystems.getDefault();
@ -71,25 +103,13 @@ final class SystemImages {
});
}
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();
CodeSource cs = SystemImage.class.getProtectionDomain().getCodeSource();
if (cs == null)
return System.getProperty("java.home");

View File

@ -43,15 +43,15 @@ public class PathOps {
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 @@ public class PathOps {
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 @@ public class PathOps {
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 @@ public class PathOps {
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 @@ public class PathOps {
.root(null)
.parent(null)
.name("foo");
test("")
.root(null)
.parent(null)
.name("");
// startsWith
test("")
@ -261,6 +296,7 @@ public class PathOps {
.notStarts("/");
test("/")
.starts("/")
.notStarts("")
.notStarts("/foo");
test("/foo")
.starts("/")
@ -278,6 +314,7 @@ public class PathOps {
.notStarts("");
test("foo")
.starts("foo")
.notStarts("")
.notStarts("f");
test("foo/bar")
.starts("foo")
@ -293,12 +330,14 @@ public class PathOps {
.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 @@ public class PathOps {
.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 @@ public class PathOps {
// 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("/")