mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-28 23:34:52 +02:00
8187443: Forest Consolidation: Move files to unified layout
Reviewed-by: darcy, ihse
This commit is contained in:
parent
270fe13182
commit
3789983e89
56923 changed files with 3 additions and 15727 deletions
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2011, 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 sun.nio.fs;
|
||||
|
||||
import java.nio.file.attribute.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Base implementation of AclFileAttributeView
|
||||
*/
|
||||
|
||||
abstract class AbstractAclFileAttributeView
|
||||
implements AclFileAttributeView, DynamicFileAttributeView
|
||||
{
|
||||
private static final String OWNER_NAME = "owner";
|
||||
private static final String ACL_NAME = "acl";
|
||||
|
||||
@Override
|
||||
public final String name() {
|
||||
return "acl";
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public final void setAttribute(String attribute, Object value)
|
||||
throws IOException
|
||||
{
|
||||
if (attribute.equals(OWNER_NAME)) {
|
||||
setOwner((UserPrincipal)value);
|
||||
return;
|
||||
}
|
||||
if (attribute.equals(ACL_NAME)) {
|
||||
setAcl((List<AclEntry>)value);
|
||||
return;
|
||||
}
|
||||
throw new IllegalArgumentException("'" + name() + ":" +
|
||||
attribute + "' not recognized");
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Map<String,Object> readAttributes(String[] attributes)
|
||||
throws IOException
|
||||
{
|
||||
boolean acl = false;
|
||||
boolean owner = false;
|
||||
for (String attribute: attributes) {
|
||||
if (attribute.equals("*")) {
|
||||
owner = true;
|
||||
acl = true;
|
||||
continue;
|
||||
}
|
||||
if (attribute.equals(ACL_NAME)) {
|
||||
acl = true;
|
||||
continue;
|
||||
}
|
||||
if (attribute.equals(OWNER_NAME)) {
|
||||
owner = true;
|
||||
continue;
|
||||
}
|
||||
throw new IllegalArgumentException("'" + name() + ":" +
|
||||
attribute + "' not recognized");
|
||||
}
|
||||
Map<String,Object> result = new HashMap<>(2);
|
||||
if (acl)
|
||||
result.put(ACL_NAME, getAcl());
|
||||
if (owner)
|
||||
result.put(OWNER_NAME, getOwner());
|
||||
return Collections.unmodifiableMap(result);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2011, 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 sun.nio.fs;
|
||||
|
||||
import java.nio.file.attribute.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Base implementation of BasicFileAttributeView
|
||||
*/
|
||||
|
||||
abstract class AbstractBasicFileAttributeView
|
||||
implements BasicFileAttributeView, DynamicFileAttributeView
|
||||
{
|
||||
private static final String SIZE_NAME = "size";
|
||||
private static final String CREATION_TIME_NAME = "creationTime";
|
||||
private static final String LAST_ACCESS_TIME_NAME = "lastAccessTime";
|
||||
private static final String LAST_MODIFIED_TIME_NAME = "lastModifiedTime";
|
||||
private static final String FILE_KEY_NAME = "fileKey";
|
||||
private static final String IS_DIRECTORY_NAME = "isDirectory";
|
||||
private static final String IS_REGULAR_FILE_NAME = "isRegularFile";
|
||||
private static final String IS_SYMBOLIC_LINK_NAME = "isSymbolicLink";
|
||||
private static final String IS_OTHER_NAME = "isOther";
|
||||
|
||||
// the names of the basic attributes
|
||||
static final Set<String> basicAttributeNames =
|
||||
Util.newSet(SIZE_NAME,
|
||||
CREATION_TIME_NAME,
|
||||
LAST_ACCESS_TIME_NAME,
|
||||
LAST_MODIFIED_TIME_NAME,
|
||||
FILE_KEY_NAME,
|
||||
IS_DIRECTORY_NAME,
|
||||
IS_REGULAR_FILE_NAME,
|
||||
IS_SYMBOLIC_LINK_NAME,
|
||||
IS_OTHER_NAME);
|
||||
|
||||
protected AbstractBasicFileAttributeView() { }
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "basic";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String attribute, Object value)
|
||||
throws IOException
|
||||
{
|
||||
if (attribute.equals(LAST_MODIFIED_TIME_NAME)) {
|
||||
setTimes((FileTime)value, null, null);
|
||||
return;
|
||||
}
|
||||
if (attribute.equals(LAST_ACCESS_TIME_NAME)) {
|
||||
setTimes(null, (FileTime)value, null);
|
||||
return;
|
||||
}
|
||||
if (attribute.equals(CREATION_TIME_NAME)) {
|
||||
setTimes(null, null, (FileTime)value);
|
||||
return;
|
||||
}
|
||||
throw new IllegalArgumentException("'" + name() + ":" +
|
||||
attribute + "' not recognized");
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to build a map of attribute name/values.
|
||||
*/
|
||||
static class AttributesBuilder {
|
||||
private Set<String> names = new HashSet<>();
|
||||
private Map<String,Object> map = new HashMap<>();
|
||||
private boolean copyAll;
|
||||
|
||||
private AttributesBuilder(Set<String> allowed, String[] requested) {
|
||||
for (String name: requested) {
|
||||
if (name.equals("*")) {
|
||||
copyAll = true;
|
||||
} else {
|
||||
if (!allowed.contains(name))
|
||||
throw new IllegalArgumentException("'" + name + "' not recognized");
|
||||
names.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates builder to build up a map of the matching attributes
|
||||
*/
|
||||
static AttributesBuilder create(Set<String> allowed, String[] requested) {
|
||||
return new AttributesBuilder(allowed, requested);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the attribute should be returned in the map
|
||||
*/
|
||||
boolean match(String name) {
|
||||
return copyAll || names.contains(name);
|
||||
}
|
||||
|
||||
void add(String name, Object value) {
|
||||
map.put(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the map. Discard all references to the AttributesBuilder
|
||||
* after invoking this method.
|
||||
*/
|
||||
Map<String,Object> unmodifiableMap() {
|
||||
return Collections.unmodifiableMap(map);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by readAttributes or sub-classes to add all matching basic
|
||||
* attributes to the builder
|
||||
*/
|
||||
final void addRequestedBasicAttributes(BasicFileAttributes attrs,
|
||||
AttributesBuilder builder)
|
||||
{
|
||||
if (builder.match(SIZE_NAME))
|
||||
builder.add(SIZE_NAME, attrs.size());
|
||||
if (builder.match(CREATION_TIME_NAME))
|
||||
builder.add(CREATION_TIME_NAME, attrs.creationTime());
|
||||
if (builder.match(LAST_ACCESS_TIME_NAME))
|
||||
builder.add(LAST_ACCESS_TIME_NAME, attrs.lastAccessTime());
|
||||
if (builder.match(LAST_MODIFIED_TIME_NAME))
|
||||
builder.add(LAST_MODIFIED_TIME_NAME, attrs.lastModifiedTime());
|
||||
if (builder.match(FILE_KEY_NAME))
|
||||
builder.add(FILE_KEY_NAME, attrs.fileKey());
|
||||
if (builder.match(IS_DIRECTORY_NAME))
|
||||
builder.add(IS_DIRECTORY_NAME, attrs.isDirectory());
|
||||
if (builder.match(IS_REGULAR_FILE_NAME))
|
||||
builder.add(IS_REGULAR_FILE_NAME, attrs.isRegularFile());
|
||||
if (builder.match(IS_SYMBOLIC_LINK_NAME))
|
||||
builder.add(IS_SYMBOLIC_LINK_NAME, attrs.isSymbolicLink());
|
||||
if (builder.match(IS_OTHER_NAME))
|
||||
builder.add(IS_OTHER_NAME, attrs.isOther());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String,Object> readAttributes(String[] requested)
|
||||
throws IOException
|
||||
{
|
||||
AttributesBuilder builder =
|
||||
AttributesBuilder.create(basicAttributeNames, requested);
|
||||
addRequestedBasicAttributes(readAttributes(), builder);
|
||||
return builder.unmodifiableMap();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (c) 2011, 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 sun.nio.fs;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.nio.file.spi.FileSystemProvider;
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Base implementation class of FileSystemProvider
|
||||
*/
|
||||
|
||||
public abstract class AbstractFileSystemProvider extends FileSystemProvider {
|
||||
protected AbstractFileSystemProvider() { }
|
||||
|
||||
/**
|
||||
* Splits the given attribute name into the name of an attribute view and
|
||||
* the attribute. If the attribute view is not identified then it assumed
|
||||
* to be "basic".
|
||||
*/
|
||||
private static String[] split(String attribute) {
|
||||
String[] s = new String[2];
|
||||
int pos = attribute.indexOf(':');
|
||||
if (pos == -1) {
|
||||
s[0] = "basic";
|
||||
s[1] = attribute;
|
||||
} else {
|
||||
s[0] = attribute.substring(0, pos++);
|
||||
s[1] = (pos == attribute.length()) ? "" : attribute.substring(pos);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a DynamicFileAttributeView by name. Returns {@code null} if the
|
||||
* view is not available.
|
||||
*/
|
||||
abstract DynamicFileAttributeView getFileAttributeView(Path file,
|
||||
String name,
|
||||
LinkOption... options);
|
||||
|
||||
@Override
|
||||
public final void setAttribute(Path file,
|
||||
String attribute,
|
||||
Object value,
|
||||
LinkOption... options)
|
||||
throws IOException
|
||||
{
|
||||
String[] s = split(attribute);
|
||||
if (s[0].length() == 0)
|
||||
throw new IllegalArgumentException(attribute);
|
||||
DynamicFileAttributeView view = getFileAttributeView(file, s[0], options);
|
||||
if (view == null)
|
||||
throw new UnsupportedOperationException("View '" + s[0] + "' not available");
|
||||
view.setAttribute(s[1], value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Map<String,Object> readAttributes(Path file, String attributes, LinkOption... options)
|
||||
throws IOException
|
||||
{
|
||||
String[] s = split(attributes);
|
||||
if (s[0].length() == 0)
|
||||
throw new IllegalArgumentException(attributes);
|
||||
DynamicFileAttributeView view = getFileAttributeView(file, s[0], options);
|
||||
if (view == null)
|
||||
throw new UnsupportedOperationException("View '" + s[0] + "' not available");
|
||||
return view.readAttributes(s[1].split(","));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a file. The {@code failIfNotExists} parameters determines if an
|
||||
* {@code IOException} is thrown when the file does not exist.
|
||||
*/
|
||||
abstract boolean implDelete(Path file, boolean failIfNotExists) throws IOException;
|
||||
|
||||
@Override
|
||||
public final void delete(Path file) throws IOException {
|
||||
implDelete(file, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean deleteIfExists(Path file) throws IOException {
|
||||
return implDelete(file, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether a file is a directory.
|
||||
*
|
||||
* @return {@code true} if the file is a directory; {@code false} if
|
||||
* the file does not exist, is not a directory, or it cannot
|
||||
* be determined if the file is a directory or not.
|
||||
*/
|
||||
public boolean isDirectory(Path file) {
|
||||
try {
|
||||
return readAttributes(file, BasicFileAttributes.class).isDirectory();
|
||||
} catch (IOException ioe) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether a file is a regular file with opaque content.
|
||||
*
|
||||
* @return {@code true} if the file is a regular file; {@code false} if
|
||||
* the file does not exist, is not a regular file, or it
|
||||
* cannot be determined if the file is a regular file or not.
|
||||
*/
|
||||
public boolean isRegularFile(Path file) {
|
||||
try {
|
||||
return readAttributes(file, BasicFileAttributes.class).isRegularFile();
|
||||
} catch (IOException ioe) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the existence of a file.
|
||||
*
|
||||
* @return {@code true} if the file exists; {@code false} if the file does
|
||||
* not exist or its existence cannot be determined.
|
||||
*/
|
||||
public boolean exists(Path file) {
|
||||
try {
|
||||
checkAccess(file);
|
||||
return true;
|
||||
} catch (IOException ioe) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 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
|
||||
* 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 sun.nio.fs;
|
||||
|
||||
import java.net.FileNameMap;
|
||||
import java.net.URLConnection;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.spi.FileTypeDetector;
|
||||
import java.util.Locale;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Base implementation of FileTypeDetector
|
||||
*/
|
||||
|
||||
public abstract class AbstractFileTypeDetector
|
||||
extends FileTypeDetector
|
||||
{
|
||||
protected AbstractFileTypeDetector() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the extension of a file name, specifically the portion of the
|
||||
* parameter string after the first dot. If the parameter is {@code null},
|
||||
* empty, does not contain a dot, or the dot is the last character, then an
|
||||
* empty string is returned, otherwise the characters after the dot are
|
||||
* returned.
|
||||
*
|
||||
* @param name A file name
|
||||
* @return The characters after the first dot or an empty string.
|
||||
*/
|
||||
protected final String getExtension(String name) {
|
||||
String ext = "";
|
||||
if (name != null && !name.isEmpty()) {
|
||||
int dot = name.indexOf('.');
|
||||
if ((dot >= 0) && (dot < name.length() - 1)) {
|
||||
ext = name.substring(dot + 1);
|
||||
}
|
||||
}
|
||||
return ext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the appropriate probe method to guess a file's content type,
|
||||
* and checks that the content type's syntax is valid.
|
||||
*/
|
||||
@Override
|
||||
public final String probeContentType(Path file) throws IOException {
|
||||
if (file == null)
|
||||
throw new NullPointerException("'file' is null");
|
||||
String result = implProbeContentType(file);
|
||||
|
||||
// Fall back to content types property.
|
||||
if (result == null) {
|
||||
Path fileName = file.getFileName();
|
||||
if (fileName != null) {
|
||||
FileNameMap fileNameMap = URLConnection.getFileNameMap();
|
||||
result = fileNameMap.getContentTypeFor(fileName.toString());
|
||||
}
|
||||
}
|
||||
|
||||
return (result == null) ? null : parse(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Probes the given file to guess its content type.
|
||||
*/
|
||||
protected abstract String implProbeContentType(Path file)
|
||||
throws IOException;
|
||||
|
||||
/**
|
||||
* Parses a candidate content type into its type and subtype, returning
|
||||
* null if either token is invalid.
|
||||
*/
|
||||
private static String parse(String s) {
|
||||
int slash = s.indexOf('/');
|
||||
int semicolon = s.indexOf(';');
|
||||
if (slash < 0)
|
||||
return null; // no subtype
|
||||
String type = s.substring(0, slash).trim().toLowerCase(Locale.ENGLISH);
|
||||
if (!isValidToken(type))
|
||||
return null; // invalid type
|
||||
String subtype = (semicolon < 0) ? s.substring(slash + 1) :
|
||||
s.substring(slash + 1, semicolon);
|
||||
subtype = subtype.trim().toLowerCase(Locale.ENGLISH);
|
||||
if (!isValidToken(subtype))
|
||||
return null; // invalid subtype
|
||||
StringBuilder sb = new StringBuilder(type.length() + subtype.length() + 1);
|
||||
sb.append(type);
|
||||
sb.append('/');
|
||||
sb.append(subtype);
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Special characters
|
||||
*/
|
||||
private static final String TSPECIALS = "()<>@,;:/[]?=\\\"";
|
||||
|
||||
/**
|
||||
* Returns true if the character is a valid token character.
|
||||
*/
|
||||
private static boolean isTokenChar(char c) {
|
||||
return (c > 040) && (c < 0177) && (TSPECIALS.indexOf(c) < 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given string is a legal type or subtype.
|
||||
*/
|
||||
private static boolean isValidToken(String s) {
|
||||
int len = s.length();
|
||||
if (len == 0)
|
||||
return false;
|
||||
for (int i = 0; i < len; i++) {
|
||||
if (!isTokenChar(s.charAt(i)))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
295
src/java.base/share/classes/sun/nio/fs/AbstractPoller.java
Normal file
295
src/java.base/share/classes/sun/nio/fs/AbstractPoller.java
Normal file
|
@ -0,0 +1,295 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2014, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Base implementation of background poller thread used in watch service
|
||||
* implementations. A poller thread waits on events from the file system and
|
||||
* also services "requests" from clients to register for new events or cancel
|
||||
* existing registrations.
|
||||
*/
|
||||
|
||||
abstract class AbstractPoller implements Runnable {
|
||||
|
||||
// list of requests pending to the poller thread
|
||||
private final LinkedList<Request> requestList;
|
||||
|
||||
// set to true when shutdown
|
||||
private boolean shutdown;
|
||||
|
||||
protected AbstractPoller() {
|
||||
this.requestList = new LinkedList<>();
|
||||
this.shutdown = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the poller thread
|
||||
*/
|
||||
public void start() {
|
||||
final Runnable thisRunnable = this;
|
||||
AccessController.doPrivileged(new PrivilegedAction<>() {
|
||||
@Override
|
||||
public Object run() {
|
||||
Thread thr = new Thread(null,
|
||||
thisRunnable,
|
||||
"FileSystemWatchService",
|
||||
0,
|
||||
false);
|
||||
thr.setDaemon(true);
|
||||
thr.start();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wakeup poller thread so that it can service pending requests
|
||||
*/
|
||||
abstract void wakeup() throws IOException;
|
||||
|
||||
/**
|
||||
* Executed by poller thread to register directory for changes
|
||||
*/
|
||||
abstract Object implRegister(Path path,
|
||||
Set<? extends WatchEvent.Kind<?>> events,
|
||||
WatchEvent.Modifier... modifiers);
|
||||
|
||||
/**
|
||||
* Executed by poller thread to cancel key
|
||||
*/
|
||||
abstract void implCancelKey(WatchKey key);
|
||||
|
||||
/**
|
||||
* Executed by poller thread to shutdown and cancel all keys
|
||||
*/
|
||||
abstract void implCloseAll();
|
||||
|
||||
/**
|
||||
* Requests, and waits on, poller thread to register given file.
|
||||
*/
|
||||
final WatchKey register(Path dir,
|
||||
WatchEvent.Kind<?>[] events,
|
||||
WatchEvent.Modifier... modifiers)
|
||||
throws IOException
|
||||
{
|
||||
// validate arguments before request to poller
|
||||
if (dir == null)
|
||||
throw new NullPointerException();
|
||||
Set<WatchEvent.Kind<?>> eventSet = new HashSet<>(events.length);
|
||||
for (WatchEvent.Kind<?> event: events) {
|
||||
// standard events
|
||||
if (event == StandardWatchEventKinds.ENTRY_CREATE ||
|
||||
event == StandardWatchEventKinds.ENTRY_MODIFY ||
|
||||
event == StandardWatchEventKinds.ENTRY_DELETE)
|
||||
{
|
||||
eventSet.add(event);
|
||||
continue;
|
||||
}
|
||||
|
||||
// OVERFLOW is ignored
|
||||
if (event == StandardWatchEventKinds.OVERFLOW)
|
||||
continue;
|
||||
|
||||
// null/unsupported
|
||||
if (event == null)
|
||||
throw new NullPointerException("An element in event set is 'null'");
|
||||
throw new UnsupportedOperationException(event.name());
|
||||
}
|
||||
if (eventSet.isEmpty())
|
||||
throw new IllegalArgumentException("No events to register");
|
||||
return (WatchKey)invoke(RequestType.REGISTER, dir, eventSet, modifiers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels, and waits on, poller thread to cancel given key.
|
||||
*/
|
||||
final void cancel(WatchKey key) {
|
||||
try {
|
||||
invoke(RequestType.CANCEL, key);
|
||||
} catch (IOException x) {
|
||||
// should not happen
|
||||
throw new AssertionError(x.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown poller thread
|
||||
*/
|
||||
final void close() throws IOException {
|
||||
invoke(RequestType.CLOSE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Types of request that the poller thread must handle
|
||||
*/
|
||||
private static enum RequestType {
|
||||
REGISTER,
|
||||
CANCEL,
|
||||
CLOSE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encapsulates a request (command) to the poller thread.
|
||||
*/
|
||||
private static class Request {
|
||||
private final RequestType type;
|
||||
private final Object[] params;
|
||||
|
||||
private boolean completed = false;
|
||||
private Object result = null;
|
||||
|
||||
Request(RequestType type, Object... params) {
|
||||
this.type = type;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
RequestType type() {
|
||||
return type;
|
||||
}
|
||||
|
||||
Object[] parameters() {
|
||||
return params;
|
||||
}
|
||||
|
||||
void release(Object result) {
|
||||
synchronized (this) {
|
||||
this.completed = true;
|
||||
this.result = result;
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Await completion of the request. The return value is the result of
|
||||
* the request.
|
||||
*/
|
||||
Object awaitResult() {
|
||||
boolean interrupted = false;
|
||||
synchronized (this) {
|
||||
while (!completed) {
|
||||
try {
|
||||
wait();
|
||||
} catch (InterruptedException x) {
|
||||
interrupted = true;
|
||||
}
|
||||
}
|
||||
if (interrupted)
|
||||
Thread.currentThread().interrupt();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues request to poller thread and waits for result
|
||||
*/
|
||||
private Object invoke(RequestType type, Object... params) throws IOException {
|
||||
// submit request
|
||||
Request req = new Request(type, params);
|
||||
synchronized (requestList) {
|
||||
if (shutdown) {
|
||||
throw new ClosedWatchServiceException();
|
||||
}
|
||||
requestList.add(req);
|
||||
|
||||
// wakeup thread
|
||||
wakeup();
|
||||
}
|
||||
|
||||
// wait for result
|
||||
Object result = req.awaitResult();
|
||||
|
||||
if (result instanceof RuntimeException)
|
||||
throw (RuntimeException)result;
|
||||
if (result instanceof IOException )
|
||||
throw (IOException)result;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked by poller thread to process all pending requests
|
||||
*
|
||||
* @return true if poller thread should shutdown
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
boolean processRequests() {
|
||||
synchronized (requestList) {
|
||||
Request req;
|
||||
while ((req = requestList.poll()) != null) {
|
||||
// if in process of shutdown then reject request
|
||||
if (shutdown) {
|
||||
req.release(new ClosedWatchServiceException());
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (req.type()) {
|
||||
/**
|
||||
* Register directory
|
||||
*/
|
||||
case REGISTER: {
|
||||
Object[] params = req.parameters();
|
||||
Path path = (Path)params[0];
|
||||
Set<? extends WatchEvent.Kind<?>> events =
|
||||
(Set<? extends WatchEvent.Kind<?>>)params[1];
|
||||
WatchEvent.Modifier[] modifiers =
|
||||
(WatchEvent.Modifier[])params[2];
|
||||
req.release(implRegister(path, events, modifiers));
|
||||
break;
|
||||
}
|
||||
/**
|
||||
* Cancel existing key
|
||||
*/
|
||||
case CANCEL : {
|
||||
Object[] params = req.parameters();
|
||||
WatchKey key = (WatchKey)params[0];
|
||||
implCancelKey(key);
|
||||
req.release(null);
|
||||
break;
|
||||
}
|
||||
/**
|
||||
* Close watch service
|
||||
*/
|
||||
case CLOSE: {
|
||||
implCloseAll();
|
||||
req.release(null);
|
||||
shutdown = true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
req.release(new IOException("request not recognized"));
|
||||
}
|
||||
}
|
||||
}
|
||||
return shutdown;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2011, 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 sun.nio.fs;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.file.attribute.UserDefinedFileAttributeView;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Base implementation of UserDefinedAttributeView
|
||||
*/
|
||||
|
||||
abstract class AbstractUserDefinedFileAttributeView
|
||||
implements UserDefinedFileAttributeView, DynamicFileAttributeView
|
||||
{
|
||||
protected AbstractUserDefinedFileAttributeView() { }
|
||||
|
||||
protected void checkAccess(String file,
|
||||
boolean checkRead,
|
||||
boolean checkWrite)
|
||||
{
|
||||
assert checkRead || checkWrite;
|
||||
SecurityManager sm = System.getSecurityManager();
|
||||
if (sm != null) {
|
||||
if (checkRead)
|
||||
sm.checkRead(file);
|
||||
if (checkWrite)
|
||||
sm.checkWrite(file);
|
||||
sm.checkPermission(new RuntimePermission("accessUserDefinedAttributes"));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String name() {
|
||||
return "user";
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setAttribute(String attribute, Object value)
|
||||
throws IOException
|
||||
{
|
||||
ByteBuffer bb;
|
||||
if (value instanceof byte[]) {
|
||||
bb = ByteBuffer.wrap((byte[])value);
|
||||
} else {
|
||||
bb = (ByteBuffer)value;
|
||||
}
|
||||
write(attribute, bb);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Map<String,Object> readAttributes(String[] attributes)
|
||||
throws IOException
|
||||
{
|
||||
// names of attributes to return
|
||||
List<String> names = new ArrayList<>();
|
||||
for (String name: attributes) {
|
||||
if (name.equals("*")) {
|
||||
names = list();
|
||||
break;
|
||||
} else {
|
||||
if (name.length() == 0)
|
||||
throw new IllegalArgumentException();
|
||||
names.add(name);
|
||||
}
|
||||
}
|
||||
|
||||
// read each value and return in map
|
||||
Map<String,Object> result = new HashMap<>();
|
||||
for (String name: names) {
|
||||
int size = size(name);
|
||||
byte[] buf = new byte[size];
|
||||
int n = read(name, ByteBuffer.wrap(buf));
|
||||
byte[] value = (n == size) ? buf : Arrays.copyOf(buf, n);
|
||||
result.put(name, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
223
src/java.base/share/classes/sun/nio/fs/AbstractWatchKey.java
Normal file
223
src/java.base/share/classes/sun/nio/fs/AbstractWatchKey.java
Normal file
|
@ -0,0 +1,223 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2011, 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 sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Base implementation class for watch keys.
|
||||
*/
|
||||
|
||||
abstract class AbstractWatchKey implements WatchKey {
|
||||
|
||||
/**
|
||||
* Maximum size of event list (in the future this may be tunable)
|
||||
*/
|
||||
static final int MAX_EVENT_LIST_SIZE = 512;
|
||||
|
||||
/**
|
||||
* Special event to signal overflow
|
||||
*/
|
||||
static final Event<Object> OVERFLOW_EVENT =
|
||||
new Event<Object>(StandardWatchEventKinds.OVERFLOW, null);
|
||||
|
||||
/**
|
||||
* Possible key states
|
||||
*/
|
||||
private static enum State { READY, SIGNALLED };
|
||||
|
||||
// reference to watcher
|
||||
private final AbstractWatchService watcher;
|
||||
|
||||
// reference to the original directory
|
||||
private final Path dir;
|
||||
|
||||
// key state
|
||||
private State state;
|
||||
|
||||
// pending events
|
||||
private List<WatchEvent<?>> events;
|
||||
|
||||
// maps a context to the last event for the context (iff the last queued
|
||||
// event for the context is an ENTRY_MODIFY event).
|
||||
private Map<Object,WatchEvent<?>> lastModifyEvents;
|
||||
|
||||
protected AbstractWatchKey(Path dir, AbstractWatchService watcher) {
|
||||
this.watcher = watcher;
|
||||
this.dir = dir;
|
||||
this.state = State.READY;
|
||||
this.events = new ArrayList<>();
|
||||
this.lastModifyEvents = new HashMap<>();
|
||||
}
|
||||
|
||||
final AbstractWatchService watcher() {
|
||||
return watcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the original watchable (Path)
|
||||
*/
|
||||
@Override
|
||||
public Path watchable() {
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues this key to the watch service
|
||||
*/
|
||||
final void signal() {
|
||||
synchronized (this) {
|
||||
if (state == State.READY) {
|
||||
state = State.SIGNALLED;
|
||||
watcher.enqueueKey(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the event to this key and signals it.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
final void signalEvent(WatchEvent.Kind<?> kind, Object context) {
|
||||
boolean isModify = (kind == StandardWatchEventKinds.ENTRY_MODIFY);
|
||||
synchronized (this) {
|
||||
int size = events.size();
|
||||
if (size > 0) {
|
||||
// if the previous event is an OVERFLOW event or this is a
|
||||
// repeated event then we simply increment the counter
|
||||
WatchEvent<?> prev = events.get(size-1);
|
||||
if ((prev.kind() == StandardWatchEventKinds.OVERFLOW) ||
|
||||
((kind == prev.kind() &&
|
||||
Objects.equals(context, prev.context()))))
|
||||
{
|
||||
((Event<?>)prev).increment();
|
||||
return;
|
||||
}
|
||||
|
||||
// if this is a modify event and the last entry for the context
|
||||
// is a modify event then we simply increment the count
|
||||
if (!lastModifyEvents.isEmpty()) {
|
||||
if (isModify) {
|
||||
WatchEvent<?> ev = lastModifyEvents.get(context);
|
||||
if (ev != null) {
|
||||
assert ev.kind() == StandardWatchEventKinds.ENTRY_MODIFY;
|
||||
((Event<?>)ev).increment();
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// not a modify event so remove from the map as the
|
||||
// last event will no longer be a modify event.
|
||||
lastModifyEvents.remove(context);
|
||||
}
|
||||
}
|
||||
|
||||
// if the list has reached the limit then drop pending events
|
||||
// and queue an OVERFLOW event
|
||||
if (size >= MAX_EVENT_LIST_SIZE) {
|
||||
kind = StandardWatchEventKinds.OVERFLOW;
|
||||
isModify = false;
|
||||
context = null;
|
||||
}
|
||||
}
|
||||
|
||||
// non-repeated event
|
||||
Event<Object> ev =
|
||||
new Event<>((WatchEvent.Kind<Object>)kind, context);
|
||||
if (isModify) {
|
||||
lastModifyEvents.put(context, ev);
|
||||
} else if (kind == StandardWatchEventKinds.OVERFLOW) {
|
||||
// drop all pending events
|
||||
events.clear();
|
||||
lastModifyEvents.clear();
|
||||
}
|
||||
events.add(ev);
|
||||
signal();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final List<WatchEvent<?>> pollEvents() {
|
||||
synchronized (this) {
|
||||
List<WatchEvent<?>> result = events;
|
||||
events = new ArrayList<>();
|
||||
lastModifyEvents.clear();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean reset() {
|
||||
synchronized (this) {
|
||||
if (state == State.SIGNALLED && isValid()) {
|
||||
if (events.isEmpty()) {
|
||||
state = State.READY;
|
||||
} else {
|
||||
// pending events so re-queue key
|
||||
watcher.enqueueKey(this);
|
||||
}
|
||||
}
|
||||
return isValid();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WatchEvent implementation
|
||||
*/
|
||||
private static class Event<T> implements WatchEvent<T> {
|
||||
private final WatchEvent.Kind<T> kind;
|
||||
private final T context;
|
||||
|
||||
// synchronize on watch key to access/increment count
|
||||
private int count;
|
||||
|
||||
Event(WatchEvent.Kind<T> type, T context) {
|
||||
this.kind = type;
|
||||
this.context = context;
|
||||
this.count = 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WatchEvent.Kind<T> kind() {
|
||||
return kind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T context() {
|
||||
return context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int count() {
|
||||
return count;
|
||||
}
|
||||
|
||||
// for repeated events
|
||||
void increment() {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
161
src/java.base/share/classes/sun/nio/fs/AbstractWatchService.java
Normal file
161
src/java.base/share/classes/sun/nio/fs/AbstractWatchService.java
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2011, 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 sun.nio.fs;
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.util.concurrent.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Base implementation class for watch services.
|
||||
*/
|
||||
|
||||
abstract class AbstractWatchService implements WatchService {
|
||||
|
||||
// signaled keys waiting to be dequeued
|
||||
private final LinkedBlockingDeque<WatchKey> pendingKeys =
|
||||
new LinkedBlockingDeque<WatchKey>();
|
||||
|
||||
// special key to indicate that watch service is closed
|
||||
private final WatchKey CLOSE_KEY =
|
||||
new AbstractWatchKey(null, null) {
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
};
|
||||
|
||||
// used when closing watch service
|
||||
private volatile boolean closed;
|
||||
private final Object closeLock = new Object();
|
||||
|
||||
protected AbstractWatchService() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given object with this watch service
|
||||
*/
|
||||
abstract WatchKey register(Path path,
|
||||
WatchEvent.Kind<?>[] events,
|
||||
WatchEvent.Modifier... modifers)
|
||||
throws IOException;
|
||||
|
||||
// used by AbstractWatchKey to enqueue key
|
||||
final void enqueueKey(WatchKey key) {
|
||||
pendingKeys.offer(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* Throws ClosedWatchServiceException if watch service is closed
|
||||
*/
|
||||
private void checkOpen() {
|
||||
if (closed)
|
||||
throw new ClosedWatchServiceException();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks the key isn't the special CLOSE_KEY used to unblock threads when
|
||||
* the watch service is closed.
|
||||
*/
|
||||
private void checkKey(WatchKey key) {
|
||||
if (key == CLOSE_KEY) {
|
||||
// re-queue in case there are other threads blocked in take/poll
|
||||
enqueueKey(key);
|
||||
}
|
||||
checkOpen();
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WatchKey poll() {
|
||||
checkOpen();
|
||||
WatchKey key = pendingKeys.poll();
|
||||
checkKey(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WatchKey poll(long timeout, TimeUnit unit)
|
||||
throws InterruptedException
|
||||
{
|
||||
checkOpen();
|
||||
WatchKey key = pendingKeys.poll(timeout, unit);
|
||||
checkKey(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WatchKey take()
|
||||
throws InterruptedException
|
||||
{
|
||||
checkOpen();
|
||||
WatchKey key = pendingKeys.take();
|
||||
checkKey(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells whether or not this watch service is open.
|
||||
*/
|
||||
final boolean isOpen() {
|
||||
return !closed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the object upon which the close method synchronizes.
|
||||
*/
|
||||
final Object closeLock() {
|
||||
return closeLock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this watch service. This method is invoked by the close
|
||||
* method to perform the actual work of closing the watch service.
|
||||
*/
|
||||
abstract void implClose() throws IOException;
|
||||
|
||||
@Override
|
||||
public final void close()
|
||||
throws IOException
|
||||
{
|
||||
synchronized (closeLock) {
|
||||
// nothing to do if already closed
|
||||
if (closed)
|
||||
return;
|
||||
closed = true;
|
||||
|
||||
implClose();
|
||||
|
||||
// clear pending keys and queue special key to ensure that any
|
||||
// threads blocked in take/poll wakeup
|
||||
pendingKeys.clear();
|
||||
pendingKeys.offer(CLOSE_KEY);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2009, 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 sun.nio.fs;
|
||||
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
/**
|
||||
* Implemented by objects that may hold or cache the attributes of a file.
|
||||
*/
|
||||
|
||||
public interface BasicFileAttributesHolder {
|
||||
/**
|
||||
* Returns cached attributes (may be null). If file is a symbolic link then
|
||||
* the attributes are the link attributes and not the final target of the
|
||||
* file.
|
||||
*/
|
||||
BasicFileAttributes get();
|
||||
|
||||
/**
|
||||
* Invalidates cached attributes
|
||||
*/
|
||||
void invalidate();
|
||||
}
|
137
src/java.base/share/classes/sun/nio/fs/Cancellable.java
Normal file
137
src/java.base/share/classes/sun/nio/fs/Cancellable.java
Normal file
|
@ -0,0 +1,137 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2009, 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 sun.nio.fs;
|
||||
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
/**
|
||||
* Base implementation of a task (typically native) that polls a memory location
|
||||
* during execution so that it may be aborted/cancelled before completion. The
|
||||
* task is executed by invoking the {@link runInterruptibly} method defined
|
||||
* here and cancelled by invoking Thread.interrupt.
|
||||
*/
|
||||
|
||||
abstract class Cancellable implements Runnable {
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
private final long pollingAddress;
|
||||
private final Object lock = new Object();
|
||||
|
||||
// the following require lock when examining or changing
|
||||
private boolean completed;
|
||||
private Throwable exception;
|
||||
|
||||
protected Cancellable() {
|
||||
pollingAddress = unsafe.allocateMemory(4);
|
||||
unsafe.putIntVolatile(null, pollingAddress, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the memory address of a 4-byte int that should be polled to
|
||||
* detect cancellation.
|
||||
*/
|
||||
protected long addressToPollForCancel() {
|
||||
return pollingAddress;
|
||||
}
|
||||
|
||||
/**
|
||||
* The value to write to the polled memory location to indicate that the
|
||||
* task has been cancelled. If this method is not overridden then it
|
||||
* defaults to MAX_VALUE.
|
||||
*/
|
||||
protected int cancelValue() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* "cancels" the task by writing bits into memory location that it polled
|
||||
* by the task.
|
||||
*/
|
||||
final void cancel() {
|
||||
synchronized (lock) {
|
||||
if (!completed) {
|
||||
unsafe.putIntVolatile(null, pollingAddress, cancelValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the exception thrown by the task or null if the task completed
|
||||
* successfully.
|
||||
*/
|
||||
private Throwable exception() {
|
||||
synchronized (lock) {
|
||||
return exception;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void run() {
|
||||
try {
|
||||
implRun();
|
||||
} catch (Throwable t) {
|
||||
synchronized (lock) {
|
||||
exception = t;
|
||||
}
|
||||
} finally {
|
||||
synchronized (lock) {
|
||||
completed = true;
|
||||
unsafe.freeMemory(pollingAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The task body. This should periodically poll the memory location
|
||||
* to check for cancellation.
|
||||
*/
|
||||
abstract void implRun() throws Throwable;
|
||||
|
||||
/**
|
||||
* Invokes the given task in its own thread. If this (meaning the current)
|
||||
* thread is interrupted then an attempt is make to cancel the background
|
||||
* thread by writing into the memory location that it polls cooperatively.
|
||||
*/
|
||||
static void runInterruptibly(Cancellable task) throws ExecutionException {
|
||||
Thread t = new Thread(null, task, "NIO-Task", 0, false);
|
||||
t.start();
|
||||
boolean cancelledByInterrupt = false;
|
||||
while (t.isAlive()) {
|
||||
try {
|
||||
t.join();
|
||||
} catch (InterruptedException e) {
|
||||
cancelledByInterrupt = true;
|
||||
task.cancel();
|
||||
}
|
||||
}
|
||||
if (cancelledByInterrupt)
|
||||
Thread.currentThread().interrupt();
|
||||
Throwable exc = task.exception();
|
||||
if (exc != null)
|
||||
throw new ExecutionException(exc);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2011, 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 sun.nio.fs;
|
||||
|
||||
import java.util.Map;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* Implemented by FileAttributeView implementations to support access to
|
||||
* attributes by names.
|
||||
*/
|
||||
|
||||
interface DynamicFileAttributeView {
|
||||
/**
|
||||
* Sets/updates the value of an attribute.
|
||||
*/
|
||||
void setAttribute(String attribute, Object value) throws IOException;
|
||||
|
||||
/**
|
||||
* Reads a set of file attributes as a bulk operation.
|
||||
*/
|
||||
Map<String,Object> readAttributes(String[] attributes) throws IOException;
|
||||
}
|
143
src/java.base/share/classes/sun/nio/fs/ExtendedOptions.java
Normal file
143
src/java.base/share/classes/sun/nio/fs/ExtendedOptions.java
Normal file
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* 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
|
||||
* 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 sun.nio.fs;
|
||||
|
||||
import java.nio.file.CopyOption;
|
||||
import java.nio.file.OpenOption;
|
||||
import java.nio.file.WatchEvent;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* Provides support for handling JDK-specific OpenOption, CopyOption and
|
||||
* WatchEvent.Modifier types.
|
||||
*/
|
||||
|
||||
public final class ExtendedOptions {
|
||||
|
||||
// maps InternalOption to ExternalOption
|
||||
private static final Map<InternalOption<?>, Wrapper<?>> internalToExternal
|
||||
= new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Wraps an option or modifier.
|
||||
*/
|
||||
private static final class Wrapper<T> {
|
||||
private final Object option;
|
||||
private final T param;
|
||||
|
||||
Wrapper(Object option, T param) {
|
||||
this.option = option;
|
||||
this.param = param;
|
||||
}
|
||||
|
||||
T parameter() {
|
||||
return param;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The internal version of a JDK-specific OpenOption, CopyOption or
|
||||
* WatchEvent.Modifier.
|
||||
*/
|
||||
public static final class InternalOption<T> {
|
||||
|
||||
InternalOption() { }
|
||||
|
||||
private void registerInternal(Object option, T param) {
|
||||
Wrapper<T> wrapper = new Wrapper<T>(option, param);
|
||||
internalToExternal.put(this, wrapper);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register this internal option as a OpenOption.
|
||||
*/
|
||||
public void register(OpenOption option) {
|
||||
registerInternal(option, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register this internal option as a CopyOption.
|
||||
*/
|
||||
public void register(CopyOption option) {
|
||||
registerInternal(option, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register this internal option as a WatchEvent.Modifier.
|
||||
*/
|
||||
public void register(WatchEvent.Modifier option) {
|
||||
registerInternal(option, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register this internal option as a WatchEvent.Modifier with the
|
||||
* given parameter.
|
||||
*/
|
||||
public void register(WatchEvent.Modifier option, T param) {
|
||||
registerInternal(option, param);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given option (or modifier) maps to this internal
|
||||
* option.
|
||||
*/
|
||||
public boolean matches(Object option) {
|
||||
Wrapper <?> wrapper = internalToExternal.get(this);
|
||||
if (wrapper == null)
|
||||
return false;
|
||||
else
|
||||
return option == wrapper.option;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parameter object associated with this internal option.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T parameter() {
|
||||
Wrapper<?> wrapper = internalToExternal.get(this);
|
||||
if (wrapper == null)
|
||||
return null;
|
||||
else
|
||||
return (T) wrapper.parameter();
|
||||
}
|
||||
}
|
||||
|
||||
// Internal equivalents of the options and modifiers defined in
|
||||
// package com.sun.nio.file
|
||||
|
||||
public static final InternalOption<Void> INTERRUPTIBLE = new InternalOption<>();
|
||||
|
||||
public static final InternalOption<Void> NOSHARE_READ = new InternalOption<>();
|
||||
public static final InternalOption<Void> NOSHARE_WRITE = new InternalOption<>();
|
||||
public static final InternalOption<Void> NOSHARE_DELETE = new InternalOption<>();
|
||||
|
||||
public static final InternalOption<Void> FILE_TREE = new InternalOption<>();
|
||||
|
||||
public static final InternalOption<Integer> SENSITIVITY_HIGH = new InternalOption<>();
|
||||
public static final InternalOption<Integer> SENSITIVITY_MEDIUM = new InternalOption<>();
|
||||
public static final InternalOption<Integer> SENSITIVITY_LOW = new InternalOption<>();
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2011, 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 sun.nio.fs;
|
||||
|
||||
import java.nio.file.attribute.*;
|
||||
import java.util.*;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* An implementation of FileOwnerAttributeView that delegates to a given
|
||||
* PosixFileAttributeView or AclFileAttributeView object.
|
||||
*/
|
||||
|
||||
final class FileOwnerAttributeViewImpl
|
||||
implements FileOwnerAttributeView, DynamicFileAttributeView
|
||||
{
|
||||
private static final String OWNER_NAME = "owner";
|
||||
|
||||
private final FileAttributeView view;
|
||||
private final boolean isPosixView;
|
||||
|
||||
FileOwnerAttributeViewImpl(PosixFileAttributeView view) {
|
||||
this.view = view;
|
||||
this.isPosixView = true;
|
||||
}
|
||||
|
||||
FileOwnerAttributeViewImpl(AclFileAttributeView view) {
|
||||
this.view = view;
|
||||
this.isPosixView = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "owner";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAttribute(String attribute, Object value)
|
||||
throws IOException
|
||||
{
|
||||
if (attribute.equals(OWNER_NAME)) {
|
||||
setOwner((UserPrincipal)value);
|
||||
} else {
|
||||
throw new IllegalArgumentException("'" + name() + ":" +
|
||||
attribute + "' not recognized");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String,Object> readAttributes(String[] attributes) throws IOException {
|
||||
Map<String,Object> result = new HashMap<>();
|
||||
for (String attribute: attributes) {
|
||||
if (attribute.equals("*") || attribute.equals(OWNER_NAME)) {
|
||||
result.put(OWNER_NAME, getOwner());
|
||||
} else {
|
||||
throw new IllegalArgumentException("'" + name() + ":" +
|
||||
attribute + "' not recognized");
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public UserPrincipal getOwner() throws IOException {
|
||||
if (isPosixView) {
|
||||
return ((PosixFileAttributeView)view).readAttributes().owner();
|
||||
} else {
|
||||
return ((AclFileAttributeView)view).getOwner();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOwner(UserPrincipal owner)
|
||||
throws IOException
|
||||
{
|
||||
if (isPosixView) {
|
||||
((PosixFileAttributeView)view).setOwner(owner);
|
||||
} else {
|
||||
((AclFileAttributeView)view).setOwner(owner);
|
||||
}
|
||||
}
|
||||
}
|
217
src/java.base/share/classes/sun/nio/fs/Globs.java
Normal file
217
src/java.base/share/classes/sun/nio/fs/Globs.java
Normal file
|
@ -0,0 +1,217 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2009, 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 sun.nio.fs;
|
||||
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
public class Globs {
|
||||
private Globs() { }
|
||||
|
||||
private static final String regexMetaChars = ".^$+{[]|()";
|
||||
private static final String globMetaChars = "\\*?[{";
|
||||
|
||||
private static boolean isRegexMeta(char c) {
|
||||
return regexMetaChars.indexOf(c) != -1;
|
||||
}
|
||||
|
||||
private static boolean isGlobMeta(char c) {
|
||||
return globMetaChars.indexOf(c) != -1;
|
||||
}
|
||||
private static char EOL = 0; //TBD
|
||||
|
||||
private static char next(String glob, int i) {
|
||||
if (i < glob.length()) {
|
||||
return glob.charAt(i);
|
||||
}
|
||||
return EOL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a regex pattern from the given glob expression.
|
||||
*
|
||||
* @throws PatternSyntaxException
|
||||
*/
|
||||
private static String toRegexPattern(String globPattern, boolean isDos) {
|
||||
boolean inGroup = false;
|
||||
StringBuilder regex = new StringBuilder("^");
|
||||
|
||||
int i = 0;
|
||||
while (i < globPattern.length()) {
|
||||
char c = globPattern.charAt(i++);
|
||||
switch (c) {
|
||||
case '\\':
|
||||
// escape special characters
|
||||
if (i == globPattern.length()) {
|
||||
throw new PatternSyntaxException("No character to escape",
|
||||
globPattern, i - 1);
|
||||
}
|
||||
char next = globPattern.charAt(i++);
|
||||
if (isGlobMeta(next) || isRegexMeta(next)) {
|
||||
regex.append('\\');
|
||||
}
|
||||
regex.append(next);
|
||||
break;
|
||||
case '/':
|
||||
if (isDos) {
|
||||
regex.append("\\\\");
|
||||
} else {
|
||||
regex.append(c);
|
||||
}
|
||||
break;
|
||||
case '[':
|
||||
// don't match name separator in class
|
||||
if (isDos) {
|
||||
regex.append("[[^\\\\]&&[");
|
||||
} else {
|
||||
regex.append("[[^/]&&[");
|
||||
}
|
||||
if (next(globPattern, i) == '^') {
|
||||
// escape the regex negation char if it appears
|
||||
regex.append("\\^");
|
||||
i++;
|
||||
} else {
|
||||
// negation
|
||||
if (next(globPattern, i) == '!') {
|
||||
regex.append('^');
|
||||
i++;
|
||||
}
|
||||
// hyphen allowed at start
|
||||
if (next(globPattern, i) == '-') {
|
||||
regex.append('-');
|
||||
i++;
|
||||
}
|
||||
}
|
||||
boolean hasRangeStart = false;
|
||||
char last = 0;
|
||||
while (i < globPattern.length()) {
|
||||
c = globPattern.charAt(i++);
|
||||
if (c == ']') {
|
||||
break;
|
||||
}
|
||||
if (c == '/' || (isDos && c == '\\')) {
|
||||
throw new PatternSyntaxException("Explicit 'name separator' in class",
|
||||
globPattern, i - 1);
|
||||
}
|
||||
// TBD: how to specify ']' in a class?
|
||||
if (c == '\\' || c == '[' ||
|
||||
c == '&' && next(globPattern, i) == '&') {
|
||||
// escape '\', '[' or "&&" for regex class
|
||||
regex.append('\\');
|
||||
}
|
||||
regex.append(c);
|
||||
|
||||
if (c == '-') {
|
||||
if (!hasRangeStart) {
|
||||
throw new PatternSyntaxException("Invalid range",
|
||||
globPattern, i - 1);
|
||||
}
|
||||
if ((c = next(globPattern, i++)) == EOL || c == ']') {
|
||||
break;
|
||||
}
|
||||
if (c < last) {
|
||||
throw new PatternSyntaxException("Invalid range",
|
||||
globPattern, i - 3);
|
||||
}
|
||||
regex.append(c);
|
||||
hasRangeStart = false;
|
||||
} else {
|
||||
hasRangeStart = true;
|
||||
last = c;
|
||||
}
|
||||
}
|
||||
if (c != ']') {
|
||||
throw new PatternSyntaxException("Missing ']", globPattern, i - 1);
|
||||
}
|
||||
regex.append("]]");
|
||||
break;
|
||||
case '{':
|
||||
if (inGroup) {
|
||||
throw new PatternSyntaxException("Cannot nest groups",
|
||||
globPattern, i - 1);
|
||||
}
|
||||
regex.append("(?:(?:");
|
||||
inGroup = true;
|
||||
break;
|
||||
case '}':
|
||||
if (inGroup) {
|
||||
regex.append("))");
|
||||
inGroup = false;
|
||||
} else {
|
||||
regex.append('}');
|
||||
}
|
||||
break;
|
||||
case ',':
|
||||
if (inGroup) {
|
||||
regex.append(")|(?:");
|
||||
} else {
|
||||
regex.append(',');
|
||||
}
|
||||
break;
|
||||
case '*':
|
||||
if (next(globPattern, i) == '*') {
|
||||
// crosses directory boundaries
|
||||
regex.append(".*");
|
||||
i++;
|
||||
} else {
|
||||
// within directory boundary
|
||||
if (isDos) {
|
||||
regex.append("[^\\\\]*");
|
||||
} else {
|
||||
regex.append("[^/]*");
|
||||
}
|
||||
}
|
||||
break;
|
||||
case '?':
|
||||
if (isDos) {
|
||||
regex.append("[^\\\\]");
|
||||
} else {
|
||||
regex.append("[^/]");
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if (isRegexMeta(c)) {
|
||||
regex.append('\\');
|
||||
}
|
||||
regex.append(c);
|
||||
}
|
||||
}
|
||||
|
||||
if (inGroup) {
|
||||
throw new PatternSyntaxException("Missing '}", globPattern, i - 1);
|
||||
}
|
||||
|
||||
return regex.append('$').toString();
|
||||
}
|
||||
|
||||
static String toUnixRegexPattern(String globPattern) {
|
||||
return toRegexPattern(globPattern, false);
|
||||
}
|
||||
|
||||
static String toWindowsRegexPattern(String globPattern) {
|
||||
return toRegexPattern(globPattern, true);
|
||||
}
|
||||
}
|
89
src/java.base/share/classes/sun/nio/fs/NativeBuffer.java
Normal file
89
src/java.base/share/classes/sun/nio/fs/NativeBuffer.java
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2009, 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 sun.nio.fs;
|
||||
|
||||
import java.lang.ref.Cleaner.Cleanable;
|
||||
import jdk.internal.misc.Unsafe;
|
||||
import jdk.internal.ref.CleanerFactory;
|
||||
|
||||
/**
|
||||
* A light-weight buffer in native memory.
|
||||
*/
|
||||
|
||||
class NativeBuffer {
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
private final long address;
|
||||
private final int size;
|
||||
private final Cleanable cleanable;
|
||||
|
||||
// optional "owner" to avoid copying
|
||||
// (only safe for use by thread-local caches)
|
||||
private Object owner;
|
||||
|
||||
private static class Deallocator implements Runnable {
|
||||
private final long address;
|
||||
Deallocator(long address) {
|
||||
this.address = address;
|
||||
}
|
||||
public void run() {
|
||||
unsafe.freeMemory(address);
|
||||
}
|
||||
}
|
||||
|
||||
NativeBuffer(int size) {
|
||||
this.address = unsafe.allocateMemory(size);
|
||||
this.size = size;
|
||||
this.cleanable = CleanerFactory.cleaner()
|
||||
.register(this, new Deallocator(address));
|
||||
}
|
||||
|
||||
void release() {
|
||||
NativeBuffers.releaseNativeBuffer(this);
|
||||
}
|
||||
|
||||
long address() {
|
||||
return address;
|
||||
}
|
||||
|
||||
int size() {
|
||||
return size;
|
||||
}
|
||||
|
||||
void free() {
|
||||
cleanable.clean();
|
||||
}
|
||||
|
||||
// not synchronized; only safe for use by thread-local caches
|
||||
void setOwner(Object owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
// not synchronized; only safe for use by thread-local caches
|
||||
Object owner() {
|
||||
return owner;
|
||||
}
|
||||
}
|
140
src/java.base/share/classes/sun/nio/fs/NativeBuffers.java
Normal file
140
src/java.base/share/classes/sun/nio/fs/NativeBuffers.java
Normal file
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2009, 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 sun.nio.fs;
|
||||
|
||||
import jdk.internal.misc.Unsafe;
|
||||
|
||||
/**
|
||||
* Factory for native buffers.
|
||||
*/
|
||||
|
||||
class NativeBuffers {
|
||||
private NativeBuffers() { }
|
||||
|
||||
private static final Unsafe unsafe = Unsafe.getUnsafe();
|
||||
|
||||
private static final int TEMP_BUF_POOL_SIZE = 3;
|
||||
private static ThreadLocal<NativeBuffer[]> threadLocal =
|
||||
new ThreadLocal<NativeBuffer[]>();
|
||||
|
||||
/**
|
||||
* Allocates a native buffer, of at least the given size, from the heap.
|
||||
*/
|
||||
static NativeBuffer allocNativeBuffer(int size) {
|
||||
// Make a new one of at least 2k
|
||||
if (size < 2048) size = 2048;
|
||||
return new NativeBuffer(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a native buffer, of at least the given size, from the thread
|
||||
* local cache.
|
||||
*/
|
||||
static NativeBuffer getNativeBufferFromCache(int size) {
|
||||
// return from cache if possible
|
||||
NativeBuffer[] buffers = threadLocal.get();
|
||||
if (buffers != null) {
|
||||
for (int i=0; i<TEMP_BUF_POOL_SIZE; i++) {
|
||||
NativeBuffer buffer = buffers[i];
|
||||
if (buffer != null && buffer.size() >= size) {
|
||||
buffers[i] = null;
|
||||
return buffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a native buffer, of at least the given size. The native buffer
|
||||
* is taken from the thread local cache if possible; otherwise it is
|
||||
* allocated from the heap.
|
||||
*/
|
||||
static NativeBuffer getNativeBuffer(int size) {
|
||||
NativeBuffer buffer = getNativeBufferFromCache(size);
|
||||
if (buffer != null) {
|
||||
buffer.setOwner(null);
|
||||
return buffer;
|
||||
} else {
|
||||
return allocNativeBuffer(size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the given buffer. If there is space in the thread local cache
|
||||
* then the buffer goes into the cache; otherwise the memory is deallocated.
|
||||
*/
|
||||
static void releaseNativeBuffer(NativeBuffer buffer) {
|
||||
// create cache if it doesn't exist
|
||||
NativeBuffer[] buffers = threadLocal.get();
|
||||
if (buffers == null) {
|
||||
buffers = new NativeBuffer[TEMP_BUF_POOL_SIZE];
|
||||
buffers[0] = buffer;
|
||||
threadLocal.set(buffers);
|
||||
return;
|
||||
}
|
||||
// Put it in an empty slot if such exists
|
||||
for (int i=0; i<TEMP_BUF_POOL_SIZE; i++) {
|
||||
if (buffers[i] == null) {
|
||||
buffers[i] = buffer;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Otherwise replace a smaller one in the cache if such exists
|
||||
for (int i=0; i<TEMP_BUF_POOL_SIZE; i++) {
|
||||
NativeBuffer existing = buffers[i];
|
||||
if (existing.size() < buffer.size()) {
|
||||
existing.free();
|
||||
buffers[i] = buffer;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// free it
|
||||
buffer.free();
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a byte array and zero terminator into a given native buffer.
|
||||
*/
|
||||
static void copyCStringToNativeBuffer(byte[] cstr, NativeBuffer buffer) {
|
||||
long offset = Unsafe.ARRAY_BYTE_BASE_OFFSET;
|
||||
long len = cstr.length;
|
||||
assert buffer.size() >= (len + 1);
|
||||
unsafe.copyMemory(cstr, offset, null, buffer.address(), len);
|
||||
unsafe.putByte(buffer.address() + len, (byte)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies a byte array and zero terminator into a native buffer, returning
|
||||
* the buffer.
|
||||
*/
|
||||
static NativeBuffer asNativeBuffer(byte[] cstr) {
|
||||
NativeBuffer buffer = getNativeBuffer(cstr.length+1);
|
||||
copyCStringToNativeBuffer(cstr, buffer);
|
||||
return buffer;
|
||||
}
|
||||
}
|
430
src/java.base/share/classes/sun/nio/fs/PollingWatchService.java
Normal file
430
src/java.base/share/classes/sun/nio/fs/PollingWatchService.java
Normal file
|
@ -0,0 +1,430 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 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
|
||||
* 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 sun.nio.fs;
|
||||
|
||||
import java.nio.file.ClosedWatchServiceException;
|
||||
import java.nio.file.DirectoryIteratorException;
|
||||
import java.nio.file.DirectoryStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.LinkOption;
|
||||
import java.nio.file.NotDirectoryException;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardWatchEventKinds;
|
||||
import java.nio.file.WatchEvent;
|
||||
import java.nio.file.WatchKey;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
import java.security.PrivilegedExceptionAction;
|
||||
import java.security.PrivilegedActionException;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Simple WatchService implementation that uses periodic tasks to poll
|
||||
* registered directories for changes. This implementation is for use on
|
||||
* operating systems that do not have native file change notification support.
|
||||
*/
|
||||
|
||||
class PollingWatchService
|
||||
extends AbstractWatchService
|
||||
{
|
||||
// map of registrations
|
||||
private final Map<Object, PollingWatchKey> map = new HashMap<>();
|
||||
|
||||
// used to execute the periodic tasks that poll for changes
|
||||
private final ScheduledExecutorService scheduledExecutor;
|
||||
|
||||
PollingWatchService() {
|
||||
// TBD: Make the number of threads configurable
|
||||
scheduledExecutor = Executors
|
||||
.newSingleThreadScheduledExecutor(new ThreadFactory() {
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(null, r, "FileSystemWatcher", 0, false);
|
||||
t.setDaemon(true);
|
||||
return t;
|
||||
}});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the given file with this watch service
|
||||
*/
|
||||
@Override
|
||||
WatchKey register(final Path path,
|
||||
WatchEvent.Kind<?>[] events,
|
||||
WatchEvent.Modifier... modifiers)
|
||||
throws IOException
|
||||
{
|
||||
// check events - CCE will be thrown if there are invalid elements
|
||||
final Set<WatchEvent.Kind<?>> eventSet = new HashSet<>(events.length);
|
||||
for (WatchEvent.Kind<?> event: events) {
|
||||
// standard events
|
||||
if (event == StandardWatchEventKinds.ENTRY_CREATE ||
|
||||
event == StandardWatchEventKinds.ENTRY_MODIFY ||
|
||||
event == StandardWatchEventKinds.ENTRY_DELETE)
|
||||
{
|
||||
eventSet.add(event);
|
||||
continue;
|
||||
}
|
||||
|
||||
// OVERFLOW is ignored
|
||||
if (event == StandardWatchEventKinds.OVERFLOW) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// null/unsupported
|
||||
if (event == null)
|
||||
throw new NullPointerException("An element in event set is 'null'");
|
||||
throw new UnsupportedOperationException(event.name());
|
||||
}
|
||||
if (eventSet.isEmpty())
|
||||
throw new IllegalArgumentException("No events to register");
|
||||
|
||||
// Extended modifiers may be used to specify the sensitivity level
|
||||
int sensitivity = 10;
|
||||
if (modifiers.length > 0) {
|
||||
for (WatchEvent.Modifier modifier: modifiers) {
|
||||
if (modifier == null)
|
||||
throw new NullPointerException();
|
||||
|
||||
if (ExtendedOptions.SENSITIVITY_HIGH.matches(modifier)) {
|
||||
sensitivity = ExtendedOptions.SENSITIVITY_HIGH.parameter();
|
||||
} else if (ExtendedOptions.SENSITIVITY_MEDIUM.matches(modifier)) {
|
||||
sensitivity = ExtendedOptions.SENSITIVITY_MEDIUM.parameter();
|
||||
} else if (ExtendedOptions.SENSITIVITY_LOW.matches(modifier)) {
|
||||
sensitivity = ExtendedOptions.SENSITIVITY_LOW.parameter();
|
||||
} else {
|
||||
throw new UnsupportedOperationException("Modifier not supported");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check if watch service is closed
|
||||
if (!isOpen())
|
||||
throw new ClosedWatchServiceException();
|
||||
|
||||
// registration is done in privileged block as it requires the
|
||||
// attributes of the entries in the directory.
|
||||
try {
|
||||
int value = sensitivity;
|
||||
return AccessController.doPrivileged(
|
||||
new PrivilegedExceptionAction<PollingWatchKey>() {
|
||||
@Override
|
||||
public PollingWatchKey run() throws IOException {
|
||||
return doPrivilegedRegister(path, eventSet, value);
|
||||
}
|
||||
});
|
||||
} catch (PrivilegedActionException pae) {
|
||||
Throwable cause = pae.getCause();
|
||||
if (cause != null && cause instanceof IOException)
|
||||
throw (IOException)cause;
|
||||
throw new AssertionError(pae);
|
||||
}
|
||||
}
|
||||
|
||||
// registers directory returning a new key if not already registered or
|
||||
// existing key if already registered
|
||||
private PollingWatchKey doPrivilegedRegister(Path path,
|
||||
Set<? extends WatchEvent.Kind<?>> events,
|
||||
int sensitivityInSeconds)
|
||||
throws IOException
|
||||
{
|
||||
// check file is a directory and get its file key if possible
|
||||
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
|
||||
if (!attrs.isDirectory()) {
|
||||
throw new NotDirectoryException(path.toString());
|
||||
}
|
||||
Object fileKey = attrs.fileKey();
|
||||
if (fileKey == null)
|
||||
throw new AssertionError("File keys must be supported");
|
||||
|
||||
// grab close lock to ensure that watch service cannot be closed
|
||||
synchronized (closeLock()) {
|
||||
if (!isOpen())
|
||||
throw new ClosedWatchServiceException();
|
||||
|
||||
PollingWatchKey watchKey;
|
||||
synchronized (map) {
|
||||
watchKey = map.get(fileKey);
|
||||
if (watchKey == null) {
|
||||
// new registration
|
||||
watchKey = new PollingWatchKey(path, this, fileKey);
|
||||
map.put(fileKey, watchKey);
|
||||
} else {
|
||||
// update to existing registration
|
||||
watchKey.disable();
|
||||
}
|
||||
}
|
||||
watchKey.enable(events, sensitivityInSeconds);
|
||||
return watchKey;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
void implClose() throws IOException {
|
||||
synchronized (map) {
|
||||
for (Map.Entry<Object, PollingWatchKey> entry: map.entrySet()) {
|
||||
PollingWatchKey watchKey = entry.getValue();
|
||||
watchKey.disable();
|
||||
watchKey.invalidate();
|
||||
}
|
||||
map.clear();
|
||||
}
|
||||
AccessController.doPrivileged(new PrivilegedAction<Void>() {
|
||||
@Override
|
||||
public Void run() {
|
||||
scheduledExecutor.shutdown();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Entry in directory cache to record file last-modified-time and tick-count
|
||||
*/
|
||||
private static class CacheEntry {
|
||||
private long lastModified;
|
||||
private int lastTickCount;
|
||||
|
||||
CacheEntry(long lastModified, int lastTickCount) {
|
||||
this.lastModified = lastModified;
|
||||
this.lastTickCount = lastTickCount;
|
||||
}
|
||||
|
||||
int lastTickCount() {
|
||||
return lastTickCount;
|
||||
}
|
||||
|
||||
long lastModified() {
|
||||
return lastModified;
|
||||
}
|
||||
|
||||
void update(long lastModified, int tickCount) {
|
||||
this.lastModified = lastModified;
|
||||
this.lastTickCount = tickCount;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WatchKey implementation that encapsulates a map of the entries of the
|
||||
* entries in the directory. Polling the key causes it to re-scan the
|
||||
* directory and queue keys when entries are added, modified, or deleted.
|
||||
*/
|
||||
private class PollingWatchKey extends AbstractWatchKey {
|
||||
private final Object fileKey;
|
||||
|
||||
// current event set
|
||||
private Set<? extends WatchEvent.Kind<?>> events;
|
||||
|
||||
// the result of the periodic task that causes this key to be polled
|
||||
private ScheduledFuture<?> poller;
|
||||
|
||||
// indicates if the key is valid
|
||||
private volatile boolean valid;
|
||||
|
||||
// used to detect files that have been deleted
|
||||
private int tickCount;
|
||||
|
||||
// map of entries in directory
|
||||
private Map<Path,CacheEntry> entries;
|
||||
|
||||
PollingWatchKey(Path dir, PollingWatchService watcher, Object fileKey)
|
||||
throws IOException
|
||||
{
|
||||
super(dir, watcher);
|
||||
this.fileKey = fileKey;
|
||||
this.valid = true;
|
||||
this.tickCount = 0;
|
||||
this.entries = new HashMap<Path,CacheEntry>();
|
||||
|
||||
// get the initial entries in the directory
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir)) {
|
||||
for (Path entry: stream) {
|
||||
// don't follow links
|
||||
long lastModified =
|
||||
Files.getLastModifiedTime(entry, LinkOption.NOFOLLOW_LINKS).toMillis();
|
||||
entries.put(entry.getFileName(), new CacheEntry(lastModified, tickCount));
|
||||
}
|
||||
} catch (DirectoryIteratorException e) {
|
||||
throw e.getCause();
|
||||
}
|
||||
}
|
||||
|
||||
Object fileKey() {
|
||||
return fileKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return valid;
|
||||
}
|
||||
|
||||
void invalidate() {
|
||||
valid = false;
|
||||
}
|
||||
|
||||
// enables periodic polling
|
||||
void enable(Set<? extends WatchEvent.Kind<?>> events, long period) {
|
||||
synchronized (this) {
|
||||
// update the events
|
||||
this.events = events;
|
||||
|
||||
// create the periodic task
|
||||
Runnable thunk = new Runnable() { public void run() { poll(); }};
|
||||
this.poller = scheduledExecutor
|
||||
.scheduleAtFixedRate(thunk, period, period, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
// disables periodic polling
|
||||
void disable() {
|
||||
synchronized (this) {
|
||||
if (poller != null)
|
||||
poller.cancel(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
valid = false;
|
||||
synchronized (map) {
|
||||
map.remove(fileKey());
|
||||
}
|
||||
disable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Polls the directory to detect for new files, modified files, or
|
||||
* deleted files.
|
||||
*/
|
||||
synchronized void poll() {
|
||||
if (!valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
// update tick
|
||||
tickCount++;
|
||||
|
||||
// open directory
|
||||
DirectoryStream<Path> stream = null;
|
||||
try {
|
||||
stream = Files.newDirectoryStream(watchable());
|
||||
} catch (IOException x) {
|
||||
// directory is no longer accessible so cancel key
|
||||
cancel();
|
||||
signal();
|
||||
return;
|
||||
}
|
||||
|
||||
// iterate over all entries in directory
|
||||
try {
|
||||
for (Path entry: stream) {
|
||||
long lastModified = 0L;
|
||||
try {
|
||||
lastModified =
|
||||
Files.getLastModifiedTime(entry, LinkOption.NOFOLLOW_LINKS).toMillis();
|
||||
} catch (IOException x) {
|
||||
// unable to get attributes of entry. If file has just
|
||||
// been deleted then we'll report it as deleted on the
|
||||
// next poll
|
||||
continue;
|
||||
}
|
||||
|
||||
// lookup cache
|
||||
CacheEntry e = entries.get(entry.getFileName());
|
||||
if (e == null) {
|
||||
// new file found
|
||||
entries.put(entry.getFileName(),
|
||||
new CacheEntry(lastModified, tickCount));
|
||||
|
||||
// queue ENTRY_CREATE if event enabled
|
||||
if (events.contains(StandardWatchEventKinds.ENTRY_CREATE)) {
|
||||
signalEvent(StandardWatchEventKinds.ENTRY_CREATE, entry.getFileName());
|
||||
continue;
|
||||
} else {
|
||||
// if ENTRY_CREATE is not enabled and ENTRY_MODIFY is
|
||||
// enabled then queue event to avoid missing out on
|
||||
// modifications to the file immediately after it is
|
||||
// created.
|
||||
if (events.contains(StandardWatchEventKinds.ENTRY_MODIFY)) {
|
||||
signalEvent(StandardWatchEventKinds.ENTRY_MODIFY, entry.getFileName());
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if file has changed
|
||||
if (e.lastModified != lastModified) {
|
||||
if (events.contains(StandardWatchEventKinds.ENTRY_MODIFY)) {
|
||||
signalEvent(StandardWatchEventKinds.ENTRY_MODIFY,
|
||||
entry.getFileName());
|
||||
}
|
||||
}
|
||||
// entry in cache so update poll time
|
||||
e.update(lastModified, tickCount);
|
||||
|
||||
}
|
||||
} catch (DirectoryIteratorException e) {
|
||||
// ignore for now; if the directory is no longer accessible
|
||||
// then the key will be cancelled on the next poll
|
||||
} finally {
|
||||
|
||||
// close directory stream
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException x) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
// iterate over cache to detect entries that have been deleted
|
||||
Iterator<Map.Entry<Path,CacheEntry>> i = entries.entrySet().iterator();
|
||||
while (i.hasNext()) {
|
||||
Map.Entry<Path,CacheEntry> mapEntry = i.next();
|
||||
CacheEntry entry = mapEntry.getValue();
|
||||
if (entry.lastTickCount() != tickCount) {
|
||||
Path name = mapEntry.getKey();
|
||||
// remove from map and queue delete event (if enabled)
|
||||
i.remove();
|
||||
if (events.contains(StandardWatchEventKinds.ENTRY_DELETE)) {
|
||||
signalEvent(StandardWatchEventKinds.ENTRY_DELETE, name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
63
src/java.base/share/classes/sun/nio/fs/Reflect.java
Normal file
63
src/java.base/share/classes/sun/nio/fs/Reflect.java
Normal file
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright (c) 2008, 2009, 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 sun.nio.fs;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.security.AccessController;
|
||||
import java.security.PrivilegedAction;
|
||||
|
||||
/**
|
||||
* Utility class for reflection.
|
||||
*/
|
||||
|
||||
class Reflect {
|
||||
private Reflect() {}
|
||||
|
||||
private static void setAccessible(final AccessibleObject ao) {
|
||||
AccessController.doPrivileged(new PrivilegedAction<Object>() {
|
||||
@Override
|
||||
public Object run() {
|
||||
ao.setAccessible(true);
|
||||
return null;
|
||||
}});
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup the field of a given class.
|
||||
*/
|
||||
static Field lookupField(String className, String fieldName) {
|
||||
try {
|
||||
Class<?> cl = Class.forName(className);
|
||||
Field f = cl.getDeclaredField(fieldName);
|
||||
setAccessible(f);
|
||||
return f;
|
||||
} catch (ClassNotFoundException x) {
|
||||
throw new AssertionError(x);
|
||||
} catch (NoSuchFieldException x) {
|
||||
throw new AssertionError(x);
|
||||
}
|
||||
}
|
||||
}
|
132
src/java.base/share/classes/sun/nio/fs/Util.java
Normal file
132
src/java.base/share/classes/sun/nio/fs/Util.java
Normal file
|
@ -0,0 +1,132 @@
|
|||
/*
|
||||
* Copyright (c) 2009, 2013, 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 sun.nio.fs;
|
||||
|
||||
import java.util.*;
|
||||
import java.nio.file.*;
|
||||
import java.nio.charset.Charset;
|
||||
import sun.security.action.GetPropertyAction;
|
||||
|
||||
/**
|
||||
* Utility methods
|
||||
*/
|
||||
|
||||
class Util {
|
||||
private Util() { }
|
||||
|
||||
private static final Charset jnuEncoding = Charset.forName(
|
||||
GetPropertyAction.privilegedGetProperty("sun.jnu.encoding"));
|
||||
|
||||
/**
|
||||
* Returns {@code Charset} corresponding to the sun.jnu.encoding property
|
||||
*/
|
||||
static Charset jnuEncoding() {
|
||||
return jnuEncoding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes the given String into a sequence of bytes using the {@code Charset}
|
||||
* specified by the sun.jnu.encoding property.
|
||||
*/
|
||||
static byte[] toBytes(String s) {
|
||||
return s.getBytes(jnuEncoding);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new String by decoding the specified array of bytes using the
|
||||
* {@code Charset} specified by the sun.jnu.encoding property.
|
||||
*/
|
||||
static String toString(byte[] bytes) {
|
||||
return new String(bytes, jnuEncoding);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Splits a string around the given character. The array returned by this
|
||||
* method contains each substring that is terminated by the character. Use
|
||||
* for simple string spilting cases when needing to avoid loading regex.
|
||||
*/
|
||||
static String[] split(String s, char c) {
|
||||
int count = 0;
|
||||
for (int i=0; i<s.length(); i++) {
|
||||
if (s.charAt(i) == c)
|
||||
count++;
|
||||
}
|
||||
String[] result = new String[count+1];
|
||||
int n = 0;
|
||||
int last = 0;
|
||||
for (int i=0; i<s.length(); i++) {
|
||||
if (s.charAt(i) == c) {
|
||||
result[n++] = s.substring(last, i);
|
||||
last = i + 1;
|
||||
}
|
||||
}
|
||||
result[n] = s.substring(last, s.length());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Set containing the given elements.
|
||||
*/
|
||||
@SafeVarargs
|
||||
static <E> Set<E> newSet(E... elements) {
|
||||
HashSet<E> set = new HashSet<>();
|
||||
for (E e: elements) {
|
||||
set.add(e);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a Set containing all the elements of the given Set plus
|
||||
* the given elements.
|
||||
*/
|
||||
@SafeVarargs
|
||||
static <E> Set<E> newSet(Set<E> other, E... elements) {
|
||||
HashSet<E> set = new HashSet<>(other);
|
||||
for (E e: elements) {
|
||||
set.add(e);
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if symbolic links should be followed
|
||||
*/
|
||||
static boolean followLinks(LinkOption... options) {
|
||||
boolean followLinks = true;
|
||||
for (LinkOption option: options) {
|
||||
if (option == LinkOption.NOFOLLOW_LINKS) {
|
||||
followLinks = false;
|
||||
} else if (option == null) {
|
||||
throw new NullPointerException();
|
||||
} else {
|
||||
throw new AssertionError("Should not get here");
|
||||
}
|
||||
}
|
||||
return followLinks;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue