6881498: (file) Re-examine DirectoryStream exception handling

Reviewed-by: forax
This commit is contained in:
Alan Bateman 2010-09-10 16:36:48 +01:00
parent c73d500311
commit 0fb0f54897
12 changed files with 392 additions and 226 deletions

View file

@ -83,6 +83,7 @@ FILES_src = \
java/nio/file/ClosedFileSystemException.java \ java/nio/file/ClosedFileSystemException.java \
java/nio/file/ClosedWatchServiceException.java \ java/nio/file/ClosedWatchServiceException.java \
java/nio/file/CopyOption.java \ java/nio/file/CopyOption.java \
java/nio/file/DirectoryIteratorException.java \
java/nio/file/DirectoryNotEmptyException.java \ java/nio/file/DirectoryNotEmptyException.java \
java/nio/file/DirectoryStream.java \ java/nio/file/DirectoryStream.java \
java/nio/file/FileAlreadyExistsException.java \ java/nio/file/FileAlreadyExistsException.java \

View file

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

View file

@ -31,60 +31,84 @@ import java.io.IOException;
/** /**
* An object to iterate over the entries in a directory. A directory stream * 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> * <pre>
* Path dir = ... * Path dir = ...
* DirectoryStream&lt;Path&gt; stream = dir.newDirectoryStream(); * try (DirectoryStream&lt;Path&gt; stream = dir.newDirectoryStream()) {
* try {
* for (Path entry: stream) { * for (Path entry: stream) {
* .. * ...
* } * }
* } finally {
* stream.close();
* } * }
* </pre> * </pre>
* *
* <p><b> A {@code DirectoryStream} is not a general-purpose {@code Iterable}. * <p> Once a directory stream is closed, then further access to the directory,
* While this interface extends {@code Iterable}, the {@code iterator} method * using the {@code Iterator}, behaves as if the end of stream has been reached.
* may only be invoked once to obtain the iterator; a second, or subsequent, * Due to read-ahead, the {@code Iterator} may return one or more elements
* call to the {@code iterator} method throws {@code IllegalStateException}. </b> * after the directory stream has been closed. Once these buffered elements
* * have been read, then subsequent calls to the {@code hasNext} method returns
* <p> A {@code DirectoryStream} is opened upon creation and is closed by * {@code false}, and subsequent calls to the {@code next} method will throw
* invoking the {@link #close close} method. Closing the directory stream * {@code NoSuchElementException}.
* 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> A directory stream is not required to be <i>asynchronously closeable</i>. * <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 * 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 * directory, and another thread invokes the {@code close} method, then the
* second thread may block until the read operation is complete. * second thread may block until the read operation is complete.
* *
* <p> The {@link Iterator#hasNext() hasNext} and {@link Iterator#next() next} * <p> If an I/O error is encountered when accessing the directory then it
* methods can encounter an I/O error when iterating over the directory in which * causes the {@code Iterator}'s {@code hasNext} or {@code next} methods to
* case {@code ConcurrentModificationException} is thrown with cause * throw {@link DirectoryIteratorException} with the {@link IOException} as the
* {@link java.io.IOException}. The {@code hasNext} method is guaranteed to * cause. As stated above, the {@code hasNext} method is guaranteed to
* read-ahead by at least one element. This means that if the {@code hasNext} * read-ahead by at least one element. This means that if {@code hasNext} method
* method returns {@code true} and is followed by a call to the {@code next} * returns {@code true}, and is followed by a call to the {@code next} method,
* method then it is guaranteed not to fail with a {@code * then it is guaranteed that the {@code next} method will not fail with a
* ConcurrentModificationException}. * {@code DirectoryIteratorException}.
* *
* <p> The elements returned by the iterator are in no specific order. Some file * <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 * systems maintain special links to the directory itself and the directory's
* parent directory. Entries representing these links are not returned by the * parent directory. Entries representing these links are not returned by the
* iterator. * 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 * <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 * freeze the directory while iterating, so it may (or may not) reflect updates
* to the directory that occur after the {@code DirectoryStream} is created. * 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&lt;Path&gt; listSourceFiles(Path dir) throws IOException {
* List&lt;Path&gt; result = new ArrayList&lt;Path&gt;();
* try (DirectoryStream&lt;Path&gt; 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 * @param <T> The type of element returned by the iterator
* *
* @since 1.7 * @since 1.7

View file

@ -984,11 +984,11 @@ public abstract class Path
* directory. * directory.
* *
* <p> Where the filter terminates due to an uncaught error or runtime * <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 * hasNext} or {@link Iterator#next() next} method. Where an {@code
* IOException} is thrown, it is propogated as a {@link * IOException} is thrown, it results in the {@code hasNext} or {@code
* java.util.ConcurrentModificationException} with the {@code * next} method throwing a {@link DirectoryIteratorException} with the
* IOException} as the cause. * {@code IOException} as the cause.
* *
* <p> When an implementation supports operations on entries in the * <p> When an implementation supports operations on entries in the
* directory that execute in a race-free manner then the returned directory * directory that execute in a race-free manner then the returned directory

View file

@ -47,15 +47,6 @@ import java.io.IOException;
* newDirectoryStream} method will be a {@code SecureDirectoryStream} and must * newDirectoryStream} method will be a {@code SecureDirectoryStream} and must
* be cast to that type in order to invoke the methods defined by this interface. * 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 * <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 * provider}, and a security manager is set, then the permission checks are
* performed using the path obtained by resolving the given relative path * performed using the path obtained by resolving the given relative path

View file

@ -49,9 +49,9 @@ package java.util;
* <p>Note that fail-fast behavior cannot be guaranteed as it is, generally * <p>Note that fail-fast behavior cannot be guaranteed as it is, generally
* speaking, impossible to make any hard guarantees in the presence of * speaking, impossible to make any hard guarantees in the presence of
* unsynchronized concurrent modification. Fail-fast operations * 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 * 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> * should be used only to detect bugs.</i>
* *
* @author Josh Bloch * @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. * specified detail message.
* *
* @param message the detail message pertaining to this exception. * @param message the detail message pertaining to this exception.
@ -85,4 +85,39 @@ public class ConcurrentModificationException extends RuntimeException {
public ConcurrentModificationException(String message) { public ConcurrentModificationException(String message) {
super(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);
}
} }

View file

@ -27,7 +27,6 @@ package sun.nio.fs;
import java.nio.file.*; import java.nio.file.*;
import java.util.Iterator; import java.util.Iterator;
import java.util.ConcurrentModificationException;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.util.concurrent.locks.*; import java.util.concurrent.locks.*;
import java.io.IOException; import java.io.IOException;
@ -139,9 +138,6 @@ class UnixDirectoryStream
// next entry to return // next entry to return
private Path nextEntry; private Path nextEntry;
// previous entry returned by next method (needed by remove method)
private Path prevEntry;
UnixDirectoryIterator(DirectoryStream<Path> stream) { UnixDirectoryIterator(DirectoryStream<Path> stream) {
atEof = false; atEof = false;
this.stream = stream; this.stream = stream;
@ -168,24 +164,19 @@ class UnixDirectoryStream
// prevent close while reading // prevent close while reading
readLock().lock(); readLock().lock();
try { try {
if (isClosed) if (isOpen()) {
throwAsConcurrentModificationException(new
ClosedDirectoryStreamException());
try {
nameAsBytes = readdir(dp); nameAsBytes = readdir(dp);
}
} catch (UnixException x) { } catch (UnixException x) {
try { IOException ioe = x.asIOException(dir);
x.rethrowAsIOException(dir); throw new DirectoryIteratorException(ioe);
} catch (IOException ioe) {
throwAsConcurrentModificationException(ioe);
}
}
} finally { } finally {
readLock().unlock(); readLock().unlock();
} }
// EOF // EOF
if (nameAsBytes == null) { if (nameAsBytes == null) {
atEof = true;
return null; return null;
} }
@ -198,7 +189,7 @@ class UnixDirectoryStream
if (filter == null || filter.accept(entry)) if (filter == null || filter.accept(entry))
return entry; return entry;
} catch (IOException ioe) { } catch (IOException ioe) {
throwAsConcurrentModificationException(ioe); throw new DirectoryIteratorException(ioe);
} }
} }
} }
@ -206,66 +197,28 @@ class UnixDirectoryStream
@Override @Override
public synchronized boolean hasNext() { public synchronized boolean hasNext() {
if (nextEntry == null && !atEof) { if (nextEntry == null && !atEof)
nextEntry = readNextEntry(); nextEntry = readNextEntry();
// at EOF?
if (nextEntry == null)
atEof = true;
}
return nextEntry != null; return nextEntry != null;
} }
@Override @Override
public synchronized Path next() { public synchronized Path next() {
if (nextEntry == null) { Path result;
if (!atEof) { if (nextEntry == null && !atEof) {
nextEntry = readNextEntry(); result = readNextEntry();
} } else {
if (nextEntry == null) { result = nextEntry;
atEof = true;
throw new NoSuchElementException();
}
}
prevEntry = nextEntry;
nextEntry = null; nextEntry = null;
return prevEntry; }
if (result == null)
throw new NoSuchElementException();
return result;
} }
@Override @Override
public void remove() { public void remove() {
if (isClosed) { throw new UnsupportedOperationException();
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);
} }
} }
}
private static void throwAsConcurrentModificationException(Throwable t) {
ConcurrentModificationException cme = new ConcurrentModificationException();
cme.initCause(t);
throw cme;
}
} }

View file

@ -166,7 +166,7 @@ class UnixSecureDirectoryStream
* Deletes file/directory in this directory. Works in a race-free manner * Deletes file/directory in this directory. Works in a race-free manner
* when invoked with flags. * when invoked with flags.
*/ */
void implDelete(Path obj, boolean haveFlags, int flags) private void implDelete(Path obj, boolean haveFlags, int flags)
throws IOException throws IOException
{ {
UnixPath file = getName(obj); UnixPath file = getName(obj);

View file

@ -28,7 +28,6 @@ package sun.nio.fs;
import java.nio.file.*; import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.Iterator; import java.util.Iterator;
import java.util.ConcurrentModificationException;
import java.util.NoSuchElementException; import java.util.NoSuchElementException;
import java.io.IOException; 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 class WindowsDirectoryIterator implements Iterator<Path> {
private boolean atEof; private boolean atEof;
private String first; private String first;
private Path nextEntry; private Path nextEntry;
private Path prevEntry;
WindowsDirectoryIterator(String first) { WindowsDirectoryIterator(String first) {
atEof = false; atEof = false;
@ -156,7 +148,7 @@ class WindowsDirectoryStream
if (filter.accept(entry)) if (filter.accept(entry))
return entry; return entry;
} catch (IOException ioe) { } catch (IOException ioe) {
throwAsConcurrentModificationException(ioe); throw new DirectoryIteratorException(ioe);
} }
return null; return null;
} }
@ -177,21 +169,19 @@ class WindowsDirectoryStream
// synchronize on closeLock to prevent close while reading // synchronize on closeLock to prevent close while reading
synchronized (closeLock) { synchronized (closeLock) {
if (!isOpen)
throwAsConcurrentModificationException(new
ClosedDirectoryStreamException());
try { try {
if (isOpen) {
name = FindNextFile(handle, findDataBuffer.address()); name = FindNextFile(handle, findDataBuffer.address());
if (name == null) {
// NO_MORE_FILES
return null;
} }
} catch (WindowsException x) { } catch (WindowsException x) {
try { IOException ioe = x.asIOException(dir);
x.rethrowAsIOException(dir); throw new DirectoryIteratorException(ioe);
} catch (IOException ioe) {
throwAsConcurrentModificationException(ioe);
} }
// NO_MORE_FILES or stream closed
if (name == null) {
atEof = true;
return null;
} }
// grab the attributes from the WIN32_FIND_DATA structure // grab the attributes from the WIN32_FIND_DATA structure
@ -210,49 +200,28 @@ class WindowsDirectoryStream
@Override @Override
public synchronized boolean hasNext() { public synchronized boolean hasNext() {
if (nextEntry == null && !atEof) { if (nextEntry == null && !atEof)
nextEntry = readNextEntry(); nextEntry = readNextEntry();
atEof = (nextEntry == null);
}
return nextEntry != null; return nextEntry != null;
} }
@Override @Override
public synchronized Path next() { public synchronized Path next() {
if (nextEntry == null) { Path result = null;
if (!atEof) { if (nextEntry == null && !atEof) {
nextEntry = readNextEntry(); result = readNextEntry();
} } else {
if (nextEntry == null) { result = nextEntry;
atEof = true;
throw new NoSuchElementException();
}
}
prevEntry = nextEntry;
nextEntry = null; nextEntry = null;
return prevEntry; }
if (result == null)
throw new NoSuchElementException();
return result;
} }
@Override @Override
public void remove() { public void remove() {
if (!isOpen) { throw new UnsupportedOperationException();
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);
}
} }
} }
} }

View file

@ -104,20 +104,20 @@ public class Basic {
stream.close(); 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>() { filter = new DirectoryStream.Filter<Path>() {
public boolean accept(Path file) throws IOException { public boolean accept(Path file) throws IOException {
throw new IOException(); throw new java.util.zip.ZipException();
} }
}; };
stream = dir.newDirectoryStream(filter); stream = dir.newDirectoryStream(filter);
try { try {
stream.iterator().hasNext(); stream.iterator().hasNext();
throw new RuntimeException("ConcurrentModificationException expected"); throw new RuntimeException("DirectoryIteratorException expected");
} catch (ConcurrentModificationException x) { } catch (DirectoryIteratorException x) {
Throwable t = x.getCause(); IOException cause = x.getCause();
if (!(t instanceof IOException)) if (!(cause instanceof java.util.zip.ZipException))
throw new RuntimeException("Cause is not IOException as expected"); throw new RuntimeException("Expected IOException not propagated");
} finally { } finally {
stream.close(); stream.close();
} }
@ -142,58 +142,49 @@ public class Basic {
} catch (NotDirectoryException x) { } catch (NotDirectoryException x) {
} }
// test iterator remove method // test UnsupportedOperationException
stream = dir.newDirectoryStream(); stream = dir.newDirectoryStream();
Iterator<Path> i = stream.iterator(); Iterator<Path> i = stream.iterator();
while (i.hasNext()) { i.next();
Path entry = i.next(); try {
if (!entry.getName().equals(foo))
throw new RuntimeException("entry not expected");
i.remove(); i.remove();
throw new RuntimeException("UnsupportedOperationException expected");
} catch (UnsupportedOperationException uoe) {
} }
stream.close();
// test IllegalStateException // test IllegalStateException
dir.resolve(foo).createFile();
stream = dir.newDirectoryStream(); stream = dir.newDirectoryStream();
i = stream.iterator(); stream.iterator();
i.next();
try { try {
// attempt to obtain second iterator
stream.iterator(); stream.iterator();
throw new RuntimeException("IllegalStateException not thrown as expected"); throw new RuntimeException("IllegalStateException not thrown as expected");
} catch (IllegalStateException x) { } catch (IllegalStateException x) {
} }
stream.close(); stream.close();
stream = dir.newDirectoryStream();
stream.close();
try { try {
// attempt to obtain iterator after stream is closed
stream.iterator(); stream.iterator();
throw new RuntimeException("IllegalStateException not thrown as expected"); throw new RuntimeException("IllegalStateException not thrown as expected");
} catch (IllegalStateException x) { } 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 { public static void main(String[] args) throws IOException {

View file

@ -166,22 +166,6 @@ public class SecureDS {
stream.deleteDirectory(dirEntry); stream.deleteDirectory(dirEntry);
stream.deleteFile(fileEntry); 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 // clean-up
stream.close(); stream.close();
dir2.delete(); dir2.delete();

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