mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-21 19:44:41 +02:00
6881498: (file) Re-examine DirectoryStream exception handling
Reviewed-by: forax
This commit is contained in:
parent
c73d500311
commit
0fb0f54897
12 changed files with 392 additions and 226 deletions
|
@ -83,6 +83,7 @@ FILES_src = \
|
|||
java/nio/file/ClosedFileSystemException.java \
|
||||
java/nio/file/ClosedWatchServiceException.java \
|
||||
java/nio/file/CopyOption.java \
|
||||
java/nio/file/DirectoryIteratorException.java \
|
||||
java/nio/file/DirectoryNotEmptyException.java \
|
||||
java/nio/file/DirectoryStream.java \
|
||||
java/nio/file/FileAlreadyExistsException.java \
|
||||
|
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2010, 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 java.nio.file;
|
||||
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.Objects;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.InvalidObjectException;
|
||||
|
||||
/**
|
||||
* Runtime exception thrown if an I/O error is encountered when iterating over
|
||||
* the entries in a directory. The I/O error is retrieved as an {@link
|
||||
* IOException} using the {@link #getCause() getCause()} method.
|
||||
*
|
||||
* @since 1.7
|
||||
* @see DirectoryStream
|
||||
*/
|
||||
|
||||
public final class DirectoryIteratorException
|
||||
extends ConcurrentModificationException
|
||||
{
|
||||
private static final long serialVersionUID = -6012699886086212874L;
|
||||
|
||||
/**
|
||||
* Constructs an instance of this class.
|
||||
*
|
||||
* @param cause
|
||||
* the {@code IOException} that caused the directory iteration
|
||||
* to fail
|
||||
*
|
||||
* @throws NullPointerException
|
||||
* if the cause is {@code null}
|
||||
*/
|
||||
public DirectoryIteratorException(IOException cause) {
|
||||
super(Objects.nonNull(cause));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the cause of this exception.
|
||||
*
|
||||
* @return the cause
|
||||
*/
|
||||
@Override
|
||||
public IOException getCause() {
|
||||
return (IOException)super.getCause();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to read the object from a stream.
|
||||
*
|
||||
* @throws InvalidObjectException
|
||||
* if the object is invalid or has a cause that is not
|
||||
* an {@code IOException}
|
||||
*/
|
||||
private void readObject(ObjectInputStream s)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
s.defaultReadObject();
|
||||
Throwable cause = super.getCause();
|
||||
if (!(cause instanceof IOException))
|
||||
throw new InvalidObjectException("Cause must be an IOException");
|
||||
}
|
||||
}
|
|
@ -31,60 +31,84 @@ import java.io.IOException;
|
|||
|
||||
/**
|
||||
* An object to iterate over the entries in a directory. A directory stream
|
||||
* allows for convenient use of the for-each construct:
|
||||
* allows for the convenient use of the for-each construct to iterate over a
|
||||
* directory.
|
||||
*
|
||||
* <p> <b> While {@code DirectoryStream} extends {@code Iterable}, it is not a
|
||||
* general-purpose {@code Iterable} as it supports only a single {@code
|
||||
* Iterator}; invoking the {@link #iterator iterator} method to obtain a second
|
||||
* or subsequent iterator throws {@code IllegalStateException}. </b>
|
||||
*
|
||||
* <p> An important property of the directory stream's {@code Iterator} is that
|
||||
* its {@link Iterator#hasNext() hasNext} method is guaranteed to read-ahead by
|
||||
* at least one element. If {@code hasNext} method returns {@code true}, and is
|
||||
* followed by a call to the {@code next} method, it is guaranteed that the
|
||||
* {@code next} method will not throw an exception due to an I/O error, or
|
||||
* because the stream has been {@link #close closed}. The {@code Iterator} does
|
||||
* not support the {@link Iterator#remove remove} operation.
|
||||
*
|
||||
* <p> A {@code DirectoryStream} is opened upon creation and is closed by
|
||||
* invoking the {@code close} method. Closing a directory stream releases any
|
||||
* resources associated with the stream. Failure to close the stream may result
|
||||
* in a resource leak. The try-with-resources statement provides a useful
|
||||
* construct to ensure that the stream is closed:
|
||||
* <pre>
|
||||
* Path dir = ...
|
||||
* DirectoryStream<Path> stream = dir.newDirectoryStream();
|
||||
* try {
|
||||
* try (DirectoryStream<Path> stream = dir.newDirectoryStream()) {
|
||||
* for (Path entry: stream) {
|
||||
* ..
|
||||
* ...
|
||||
* }
|
||||
* } finally {
|
||||
* stream.close();
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* <p><b> A {@code DirectoryStream} is not a general-purpose {@code Iterable}.
|
||||
* While this interface extends {@code Iterable}, the {@code iterator} method
|
||||
* may only be invoked once to obtain the iterator; a second, or subsequent,
|
||||
* call to the {@code iterator} method throws {@code IllegalStateException}. </b>
|
||||
*
|
||||
* <p> A {@code DirectoryStream} is opened upon creation and is closed by
|
||||
* invoking the {@link #close close} method. Closing the directory stream
|
||||
* releases any resources associated with the stream. Once a directory stream
|
||||
* is closed, all further method invocations on the iterator throw {@link
|
||||
* java.util.ConcurrentModificationException} with cause {@link
|
||||
* ClosedDirectoryStreamException}.
|
||||
* <p> Once a directory stream is closed, then further access to the directory,
|
||||
* using the {@code Iterator}, behaves as if the end of stream has been reached.
|
||||
* Due to read-ahead, the {@code Iterator} may return one or more elements
|
||||
* after the directory stream has been closed. Once these buffered elements
|
||||
* have been read, then subsequent calls to the {@code hasNext} method returns
|
||||
* {@code false}, and subsequent calls to the {@code next} method will throw
|
||||
* {@code NoSuchElementException}.
|
||||
*
|
||||
* <p> A directory stream is not required to be <i>asynchronously closeable</i>.
|
||||
* If a thread is blocked on the directory stream's iterator reading from the
|
||||
* directory, and another thread invokes the {@code close} method, then the
|
||||
* second thread may block until the read operation is complete.
|
||||
*
|
||||
* <p> The {@link Iterator#hasNext() hasNext} and {@link Iterator#next() next}
|
||||
* methods can encounter an I/O error when iterating over the directory in which
|
||||
* case {@code ConcurrentModificationException} is thrown with cause
|
||||
* {@link java.io.IOException}. The {@code hasNext} method is guaranteed to
|
||||
* read-ahead by at least one element. This means that if the {@code hasNext}
|
||||
* method returns {@code true} and is followed by a call to the {@code next}
|
||||
* method then it is guaranteed not to fail with a {@code
|
||||
* ConcurrentModificationException}.
|
||||
* <p> If an I/O error is encountered when accessing the directory then it
|
||||
* causes the {@code Iterator}'s {@code hasNext} or {@code next} methods to
|
||||
* throw {@link DirectoryIteratorException} with the {@link IOException} as the
|
||||
* cause. As stated above, the {@code hasNext} method is guaranteed to
|
||||
* read-ahead by at least one element. This means that if {@code hasNext} method
|
||||
* returns {@code true}, and is followed by a call to the {@code next} method,
|
||||
* then it is guaranteed that the {@code next} method will not fail with a
|
||||
* {@code DirectoryIteratorException}.
|
||||
*
|
||||
* <p> The elements returned by the iterator are in no specific order. Some file
|
||||
* systems maintain special links to the directory itself and the directory's
|
||||
* parent directory. Entries representing these links are not returned by the
|
||||
* iterator.
|
||||
*
|
||||
* <p> The iterator's {@link Iterator#remove() remove} method removes the
|
||||
* directory entry for the last element returned by the iterator, as if by
|
||||
* invoking the {@link Path#delete delete} method. If an I/O error or
|
||||
* security exception occurs then {@code ConcurrentModificationException} is
|
||||
* thrown with the cause.
|
||||
*
|
||||
* <p> The iterator is <i>weakly consistent</i>. It is thread safe but does not
|
||||
* freeze the directory while iterating, so it may (or may not) reflect updates
|
||||
* to the directory that occur after the {@code DirectoryStream} is created.
|
||||
*
|
||||
* <p> <b>Usage Examples:</b>
|
||||
* Suppose we want a list of the source files in a directory. This example uses
|
||||
* both the for-each and try-with-resources constructs.
|
||||
* <pre>
|
||||
* List<Path> listSourceFiles(Path dir) throws IOException {
|
||||
* List<Path> result = new ArrayList<Path>();
|
||||
* try (DirectoryStream<Path> stream = dir.newDirectoryStream("*.{c,h,cpp,hpp,java}")) {
|
||||
* for (Path entry: stream) {
|
||||
* result.add(entry);
|
||||
* }
|
||||
* } catch (DirectoryIteratorException ex) {
|
||||
* // I/O error encounted during the iteration, the cause is an IOException
|
||||
* throw ex.getCause();
|
||||
* }
|
||||
* return result;
|
||||
* }
|
||||
* </pre>
|
||||
* @param <T> The type of element returned by the iterator
|
||||
*
|
||||
* @since 1.7
|
||||
|
|
|
@ -984,11 +984,11 @@ public abstract class Path
|
|||
* directory.
|
||||
*
|
||||
* <p> Where the filter terminates due to an uncaught error or runtime
|
||||
* exception then it is propogated to the iterator's {@link Iterator#hasNext()
|
||||
* exception then it is propagated to the {@link Iterator#hasNext()
|
||||
* hasNext} or {@link Iterator#next() next} method. Where an {@code
|
||||
* IOException} is thrown, it is propogated as a {@link
|
||||
* java.util.ConcurrentModificationException} with the {@code
|
||||
* IOException} as the cause.
|
||||
* IOException} is thrown, it results in the {@code hasNext} or {@code
|
||||
* next} method throwing a {@link DirectoryIteratorException} with the
|
||||
* {@code IOException} as the cause.
|
||||
*
|
||||
* <p> When an implementation supports operations on entries in the
|
||||
* directory that execute in a race-free manner then the returned directory
|
||||
|
|
|
@ -47,15 +47,6 @@ import java.io.IOException;
|
|||
* newDirectoryStream} method will be a {@code SecureDirectoryStream} and must
|
||||
* be cast to that type in order to invoke the methods defined by this interface.
|
||||
*
|
||||
* <p> As specified by {@code DirectoryStream}, the iterator's {@link
|
||||
* java.util.Iterator#remove() remove} method removes the directory entry for
|
||||
* the last element returned by the iterator. In the case of a {@code
|
||||
* SecureDirectoryStream} the {@code remove} method behaves as if by invoking
|
||||
* the {@link #deleteFile deleteFile} or {@link #deleteDirectory deleteDirectory}
|
||||
* methods defined by this interface. The {@code remove} may require to examine
|
||||
* the file to determine if the file is a directory, and consequently, it may
|
||||
* not be atomic with respect to other file system operations.
|
||||
*
|
||||
* <p> In the case of the default {@link java.nio.file.spi.FileSystemProvider
|
||||
* provider}, and a security manager is set, then the permission checks are
|
||||
* performed using the path obtained by resolving the given relative path
|
||||
|
|
|
@ -49,9 +49,9 @@ package java.util;
|
|||
* <p>Note that fail-fast behavior cannot be guaranteed as it is, generally
|
||||
* speaking, impossible to make any hard guarantees in the presence of
|
||||
* unsynchronized concurrent modification. Fail-fast operations
|
||||
* throw <tt>ConcurrentModificationException</tt> on a best-effort basis.
|
||||
* throw {@code ConcurrentModificationException} on a best-effort basis.
|
||||
* Therefore, it would be wrong to write a program that depended on this
|
||||
* exception for its correctness: <i><tt>ConcurrentModificationException</tt>
|
||||
* exception for its correctness: <i>{@code ConcurrentModificationException}
|
||||
* should be used only to detect bugs.</i>
|
||||
*
|
||||
* @author Josh Bloch
|
||||
|
@ -77,7 +77,7 @@ public class ConcurrentModificationException extends RuntimeException {
|
|||
}
|
||||
|
||||
/**
|
||||
* Constructs a <tt>ConcurrentModificationException</tt> with the
|
||||
* Constructs a {@code ConcurrentModificationException} with the
|
||||
* specified detail message.
|
||||
*
|
||||
* @param message the detail message pertaining to this exception.
|
||||
|
@ -85,4 +85,39 @@ public class ConcurrentModificationException extends RuntimeException {
|
|||
public ConcurrentModificationException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new exception with the specified cause and a detail
|
||||
* message of {@code (cause==null ? null : cause.toString())} (which
|
||||
* typically contains the class and detail message of {@code cause}.
|
||||
*
|
||||
* @param cause the cause (which is saved for later retrieval by the
|
||||
* {@link Throwable#getCause()} method). (A {@code null} value is
|
||||
* permitted, and indicates that the cause is nonexistent or
|
||||
* unknown.)
|
||||
* @since 1.7
|
||||
*/
|
||||
public ConcurrentModificationException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new exception with the specified detail message and
|
||||
* cause.
|
||||
*
|
||||
* <p>Note that the detail message associated with <code>cause</code> is
|
||||
* <i>not</i> automatically incorporated in this exception's detail
|
||||
* message.
|
||||
*
|
||||
* @param message the detail message (which is saved for later retrieval
|
||||
* by the {@link Throwable#getMessage()} method).
|
||||
* @param cause the cause (which is saved for later retrieval by the
|
||||
* {@link Throwable#getCause()} method). (A {@code null} value
|
||||
* is permitted, and indicates that the cause is nonexistent or
|
||||
* unknown.)
|
||||
* @since 1.7
|
||||
*/
|
||||
public ConcurrentModificationException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,6 @@ package sun.nio.fs;
|
|||
|
||||
import java.nio.file.*;
|
||||
import java.util.Iterator;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.concurrent.locks.*;
|
||||
import java.io.IOException;
|
||||
|
@ -139,9 +138,6 @@ class UnixDirectoryStream
|
|||
// next entry to return
|
||||
private Path nextEntry;
|
||||
|
||||
// previous entry returned by next method (needed by remove method)
|
||||
private Path prevEntry;
|
||||
|
||||
UnixDirectoryIterator(DirectoryStream<Path> stream) {
|
||||
atEof = false;
|
||||
this.stream = stream;
|
||||
|
@ -168,24 +164,19 @@ class UnixDirectoryStream
|
|||
// prevent close while reading
|
||||
readLock().lock();
|
||||
try {
|
||||
if (isClosed)
|
||||
throwAsConcurrentModificationException(new
|
||||
ClosedDirectoryStreamException());
|
||||
try {
|
||||
if (isOpen()) {
|
||||
nameAsBytes = readdir(dp);
|
||||
}
|
||||
} catch (UnixException x) {
|
||||
try {
|
||||
x.rethrowAsIOException(dir);
|
||||
} catch (IOException ioe) {
|
||||
throwAsConcurrentModificationException(ioe);
|
||||
}
|
||||
}
|
||||
IOException ioe = x.asIOException(dir);
|
||||
throw new DirectoryIteratorException(ioe);
|
||||
} finally {
|
||||
readLock().unlock();
|
||||
}
|
||||
|
||||
// EOF
|
||||
if (nameAsBytes == null) {
|
||||
atEof = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -198,7 +189,7 @@ class UnixDirectoryStream
|
|||
if (filter == null || filter.accept(entry))
|
||||
return entry;
|
||||
} catch (IOException ioe) {
|
||||
throwAsConcurrentModificationException(ioe);
|
||||
throw new DirectoryIteratorException(ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -206,66 +197,28 @@ class UnixDirectoryStream
|
|||
|
||||
@Override
|
||||
public synchronized boolean hasNext() {
|
||||
if (nextEntry == null && !atEof) {
|
||||
if (nextEntry == null && !atEof)
|
||||
nextEntry = readNextEntry();
|
||||
|
||||
// at EOF?
|
||||
if (nextEntry == null)
|
||||
atEof = true;
|
||||
}
|
||||
return nextEntry != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Path next() {
|
||||
if (nextEntry == null) {
|
||||
if (!atEof) {
|
||||
nextEntry = readNextEntry();
|
||||
}
|
||||
if (nextEntry == null) {
|
||||
atEof = true;
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
prevEntry = nextEntry;
|
||||
Path result;
|
||||
if (nextEntry == null && !atEof) {
|
||||
result = readNextEntry();
|
||||
} else {
|
||||
result = nextEntry;
|
||||
nextEntry = null;
|
||||
return prevEntry;
|
||||
}
|
||||
if (result == null)
|
||||
throw new NoSuchElementException();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (isClosed) {
|
||||
throwAsConcurrentModificationException(new
|
||||
ClosedDirectoryStreamException());
|
||||
}
|
||||
Path entry;
|
||||
synchronized (this) {
|
||||
if (prevEntry == null)
|
||||
throw new IllegalStateException("No previous entry to remove");
|
||||
entry = prevEntry;
|
||||
prevEntry = null;
|
||||
}
|
||||
|
||||
// use (race-free) unlinkat if available
|
||||
try {
|
||||
if (stream instanceof UnixSecureDirectoryStream) {
|
||||
((UnixSecureDirectoryStream)stream)
|
||||
.implDelete(entry.getName(), false, 0);
|
||||
} else {
|
||||
entry.delete();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throwAsConcurrentModificationException(ioe);
|
||||
} catch (SecurityException se) {
|
||||
throwAsConcurrentModificationException(se);
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void throwAsConcurrentModificationException(Throwable t) {
|
||||
ConcurrentModificationException cme = new ConcurrentModificationException();
|
||||
cme.initCause(t);
|
||||
throw cme;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -166,7 +166,7 @@ class UnixSecureDirectoryStream
|
|||
* Deletes file/directory in this directory. Works in a race-free manner
|
||||
* when invoked with flags.
|
||||
*/
|
||||
void implDelete(Path obj, boolean haveFlags, int flags)
|
||||
private void implDelete(Path obj, boolean haveFlags, int flags)
|
||||
throws IOException
|
||||
{
|
||||
UnixPath file = getName(obj);
|
||||
|
|
|
@ -28,7 +28,6 @@ package sun.nio.fs;
|
|||
import java.nio.file.*;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.util.Iterator;
|
||||
import java.util.ConcurrentModificationException;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -121,17 +120,10 @@ class WindowsDirectoryStream
|
|||
}
|
||||
}
|
||||
|
||||
private static void throwAsConcurrentModificationException(Throwable t) {
|
||||
ConcurrentModificationException cme = new ConcurrentModificationException();
|
||||
cme.initCause(t);
|
||||
throw cme;
|
||||
}
|
||||
|
||||
private class WindowsDirectoryIterator implements Iterator<Path> {
|
||||
private boolean atEof;
|
||||
private String first;
|
||||
private Path nextEntry;
|
||||
private Path prevEntry;
|
||||
|
||||
WindowsDirectoryIterator(String first) {
|
||||
atEof = false;
|
||||
|
@ -156,7 +148,7 @@ class WindowsDirectoryStream
|
|||
if (filter.accept(entry))
|
||||
return entry;
|
||||
} catch (IOException ioe) {
|
||||
throwAsConcurrentModificationException(ioe);
|
||||
throw new DirectoryIteratorException(ioe);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -177,21 +169,19 @@ class WindowsDirectoryStream
|
|||
|
||||
// synchronize on closeLock to prevent close while reading
|
||||
synchronized (closeLock) {
|
||||
if (!isOpen)
|
||||
throwAsConcurrentModificationException(new
|
||||
ClosedDirectoryStreamException());
|
||||
try {
|
||||
if (isOpen) {
|
||||
name = FindNextFile(handle, findDataBuffer.address());
|
||||
if (name == null) {
|
||||
// NO_MORE_FILES
|
||||
return null;
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
try {
|
||||
x.rethrowAsIOException(dir);
|
||||
} catch (IOException ioe) {
|
||||
throwAsConcurrentModificationException(ioe);
|
||||
IOException ioe = x.asIOException(dir);
|
||||
throw new DirectoryIteratorException(ioe);
|
||||
}
|
||||
|
||||
// NO_MORE_FILES or stream closed
|
||||
if (name == null) {
|
||||
atEof = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
// grab the attributes from the WIN32_FIND_DATA structure
|
||||
|
@ -210,49 +200,28 @@ class WindowsDirectoryStream
|
|||
|
||||
@Override
|
||||
public synchronized boolean hasNext() {
|
||||
if (nextEntry == null && !atEof) {
|
||||
if (nextEntry == null && !atEof)
|
||||
nextEntry = readNextEntry();
|
||||
atEof = (nextEntry == null);
|
||||
}
|
||||
return nextEntry != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Path next() {
|
||||
if (nextEntry == null) {
|
||||
if (!atEof) {
|
||||
nextEntry = readNextEntry();
|
||||
}
|
||||
if (nextEntry == null) {
|
||||
atEof = true;
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
}
|
||||
prevEntry = nextEntry;
|
||||
Path result = null;
|
||||
if (nextEntry == null && !atEof) {
|
||||
result = readNextEntry();
|
||||
} else {
|
||||
result = nextEntry;
|
||||
nextEntry = null;
|
||||
return prevEntry;
|
||||
}
|
||||
if (result == null)
|
||||
throw new NoSuchElementException();
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
if (!isOpen) {
|
||||
throwAsConcurrentModificationException(new
|
||||
ClosedDirectoryStreamException());
|
||||
}
|
||||
Path entry;
|
||||
synchronized (this) {
|
||||
if (prevEntry == null)
|
||||
throw new IllegalStateException("no last element");
|
||||
entry = prevEntry;
|
||||
prevEntry = null;
|
||||
}
|
||||
try {
|
||||
entry.delete();
|
||||
} catch (IOException ioe) {
|
||||
throwAsConcurrentModificationException(ioe);
|
||||
} catch (SecurityException se) {
|
||||
throwAsConcurrentModificationException(se);
|
||||
}
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,20 +104,20 @@ public class Basic {
|
|||
stream.close();
|
||||
}
|
||||
|
||||
// check that IOExceptions throws by filters are propagated
|
||||
// check that an IOException thrown by a filter is propagated
|
||||
filter = new DirectoryStream.Filter<Path>() {
|
||||
public boolean accept(Path file) throws IOException {
|
||||
throw new IOException();
|
||||
throw new java.util.zip.ZipException();
|
||||
}
|
||||
};
|
||||
stream = dir.newDirectoryStream(filter);
|
||||
try {
|
||||
stream.iterator().hasNext();
|
||||
throw new RuntimeException("ConcurrentModificationException expected");
|
||||
} catch (ConcurrentModificationException x) {
|
||||
Throwable t = x.getCause();
|
||||
if (!(t instanceof IOException))
|
||||
throw new RuntimeException("Cause is not IOException as expected");
|
||||
throw new RuntimeException("DirectoryIteratorException expected");
|
||||
} catch (DirectoryIteratorException x) {
|
||||
IOException cause = x.getCause();
|
||||
if (!(cause instanceof java.util.zip.ZipException))
|
||||
throw new RuntimeException("Expected IOException not propagated");
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
|
@ -142,58 +142,49 @@ public class Basic {
|
|||
} catch (NotDirectoryException x) {
|
||||
}
|
||||
|
||||
// test iterator remove method
|
||||
// test UnsupportedOperationException
|
||||
stream = dir.newDirectoryStream();
|
||||
Iterator<Path> i = stream.iterator();
|
||||
while (i.hasNext()) {
|
||||
Path entry = i.next();
|
||||
if (!entry.getName().equals(foo))
|
||||
throw new RuntimeException("entry not expected");
|
||||
i.next();
|
||||
try {
|
||||
i.remove();
|
||||
throw new RuntimeException("UnsupportedOperationException expected");
|
||||
} catch (UnsupportedOperationException uoe) {
|
||||
}
|
||||
stream.close();
|
||||
|
||||
// test IllegalStateException
|
||||
dir.resolve(foo).createFile();
|
||||
stream = dir.newDirectoryStream();
|
||||
i = stream.iterator();
|
||||
i.next();
|
||||
stream.iterator();
|
||||
try {
|
||||
// attempt to obtain second iterator
|
||||
stream.iterator();
|
||||
throw new RuntimeException("IllegalStateException not thrown as expected");
|
||||
} catch (IllegalStateException x) {
|
||||
}
|
||||
stream.close();
|
||||
|
||||
stream = dir.newDirectoryStream();
|
||||
stream.close();
|
||||
try {
|
||||
// attempt to obtain iterator after stream is closed
|
||||
stream.iterator();
|
||||
throw new RuntimeException("IllegalStateException not thrown as expected");
|
||||
} catch (IllegalStateException x) {
|
||||
}
|
||||
try {
|
||||
i.hasNext();
|
||||
throw new RuntimeException("ConcurrentModificationException not thrown as expected");
|
||||
} catch (ConcurrentModificationException x) {
|
||||
Throwable t = x.getCause();
|
||||
if (!(t instanceof ClosedDirectoryStreamException))
|
||||
throw new RuntimeException("Cause is not ClosedDirectoryStreamException as expected");
|
||||
}
|
||||
try {
|
||||
i.next();
|
||||
throw new RuntimeException("ConcurrentModificationException not thrown as expected");
|
||||
} catch (ConcurrentModificationException x) {
|
||||
Throwable t = x.getCause();
|
||||
if (!(t instanceof ClosedDirectoryStreamException))
|
||||
throw new RuntimeException("Cause is not ClosedDirectoryStreamException as expected");
|
||||
}
|
||||
try {
|
||||
i.remove();
|
||||
throw new RuntimeException("ConcurrentModificationException not thrown as expected");
|
||||
} catch (ConcurrentModificationException x) {
|
||||
Throwable t = x.getCause();
|
||||
if (!(t instanceof ClosedDirectoryStreamException))
|
||||
throw new RuntimeException("Cause is not ClosedDirectoryStreamException as expected");
|
||||
}
|
||||
|
||||
// test that iterator reads to end of stream when closed
|
||||
stream = dir.newDirectoryStream();
|
||||
i = stream.iterator();
|
||||
stream.close();
|
||||
while (i.hasNext())
|
||||
i.next();
|
||||
|
||||
stream = dir.newDirectoryStream();
|
||||
i = stream.iterator();
|
||||
stream.close();
|
||||
try {
|
||||
for (;;) i.next();
|
||||
} catch (NoSuchElementException expected) { }
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
|
|
|
@ -166,22 +166,6 @@ public class SecureDS {
|
|||
stream.deleteDirectory(dirEntry);
|
||||
stream.deleteFile(fileEntry);
|
||||
|
||||
// Test: remove
|
||||
// (requires resetting environment to get new iterator)
|
||||
stream.close();
|
||||
dir2.moveTo(dir1);
|
||||
dir1.resolve(fileEntry).createFile();
|
||||
stream = (SecureDirectoryStream<Path>)dir1.newDirectoryStream();
|
||||
dir1.moveTo(dir2);
|
||||
Iterator<Path> iter = stream.iterator();
|
||||
int removed = 0;
|
||||
while (iter.hasNext()) {
|
||||
iter.next();
|
||||
iter.remove();
|
||||
removed++;
|
||||
}
|
||||
assertTrue(removed == 1);
|
||||
|
||||
// clean-up
|
||||
stream.close();
|
||||
dir2.delete();
|
||||
|
|
131
jdk/test/java/nio/file/etc/Exceptions.java
Normal file
131
jdk/test/java/nio/file/etc/Exceptions.java
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright (c) 2010, 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @bug 4313887 6881498
|
||||
* @summary Miscellenous tests on exceptions in java.nio.file
|
||||
*/
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.io.*;
|
||||
import java.util.Objects;
|
||||
import java.lang.reflect.*;
|
||||
|
||||
public class Exceptions {
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
testFileSystemException();
|
||||
testDirectoryIteratorException();
|
||||
}
|
||||
|
||||
static void testFileSystemException() throws Exception {
|
||||
String thisFile = "source";
|
||||
String otherFile = "target";
|
||||
String reason = "Access denied";
|
||||
|
||||
// getFile/getOtherFile
|
||||
testFileSystemException(thisFile, otherFile, reason);
|
||||
testFileSystemException(null, otherFile, reason);
|
||||
testFileSystemException(thisFile, null, reason);
|
||||
testFileSystemException(thisFile, otherFile, null);
|
||||
|
||||
// serialization
|
||||
FileSystemException exc;
|
||||
exc = new FileSystemException(thisFile, otherFile, reason);
|
||||
exc = (FileSystemException)deserialize(serialize(exc));
|
||||
if (!exc.getFile().equals(thisFile) || !exc.getOtherFile().equals(otherFile))
|
||||
throw new RuntimeException("Exception not reconstituted completely");
|
||||
}
|
||||
|
||||
static void testFileSystemException(String thisFile,
|
||||
String otherFile,
|
||||
String reason)
|
||||
{
|
||||
FileSystemException exc = new FileSystemException(thisFile, otherFile, reason);
|
||||
if (!Objects.equals(thisFile, exc.getFile()))
|
||||
throw new RuntimeException("getFile returned unexpected result");
|
||||
if (!Objects.equals(otherFile, exc.getOtherFile()))
|
||||
throw new RuntimeException("getOtherFile returned unexpected result");
|
||||
if (!Objects.equals(reason, exc.getReason()))
|
||||
throw new RuntimeException("getReason returned unexpected result");
|
||||
}
|
||||
|
||||
static void testDirectoryIteratorException() throws Exception {
|
||||
// NullPointerException
|
||||
try {
|
||||
new DirectoryIteratorException(null);
|
||||
throw new RuntimeException("NullPointerException expected");
|
||||
} catch (NullPointerException expected) { }
|
||||
|
||||
// serialization
|
||||
DirectoryIteratorException exc;
|
||||
exc = new DirectoryIteratorException(new IOException());
|
||||
exc = (DirectoryIteratorException)deserialize(serialize(exc));
|
||||
IOException ioe = exc.getCause();
|
||||
if (ioe == null)
|
||||
throw new RuntimeException("Cause should not be null");
|
||||
|
||||
// when deserializing then the cause should be an IOException
|
||||
hackCause(exc, null);
|
||||
try {
|
||||
deserialize(serialize(exc));
|
||||
throw new RuntimeException("InvalidObjectException expected");
|
||||
} catch (InvalidObjectException expected) { }
|
||||
|
||||
hackCause(exc, new RuntimeException());
|
||||
try {
|
||||
deserialize(serialize(exc));
|
||||
throw new RuntimeException("InvalidObjectException expected");
|
||||
} catch (InvalidObjectException expected) { }
|
||||
}
|
||||
|
||||
|
||||
// Use reflection to set a Throwable's cause.
|
||||
static void hackCause(Throwable t, Throwable cause)
|
||||
throws NoSuchFieldException, IllegalAccessException
|
||||
{
|
||||
Field f = Throwable.class.getDeclaredField("cause");
|
||||
f.setAccessible(true);
|
||||
f.set(t, cause);
|
||||
}
|
||||
|
||||
// Serialize the given object to a byte[]
|
||||
static byte[] serialize(Object o) throws IOException {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ObjectOutputStream oos = new ObjectOutputStream(baos);
|
||||
oos.writeObject(o);
|
||||
oos.close();
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
// Read an object from its serialized form
|
||||
static Object deserialize(byte[] bytes)
|
||||
throws IOException, ClassNotFoundException
|
||||
{
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
|
||||
ObjectInputStream ois = new ObjectInputStream(in);
|
||||
Object result = ois.readObject();
|
||||
ois.close();
|
||||
return result;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue