diff --git a/jdk/make/java/nio/FILES_java.gmk b/jdk/make/java/nio/FILES_java.gmk index 4d89db04a49..e49d5267a47 100644 --- a/jdk/make/java/nio/FILES_java.gmk +++ b/jdk/make/java/nio/FILES_java.gmk @@ -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 \ diff --git a/jdk/src/share/classes/java/nio/file/DirectoryIteratorException.java b/jdk/src/share/classes/java/nio/file/DirectoryIteratorException.java new file mode 100644 index 00000000000..729c84a77cd --- /dev/null +++ b/jdk/src/share/classes/java/nio/file/DirectoryIteratorException.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"); + } +} diff --git a/jdk/src/share/classes/java/nio/file/DirectoryStream.java b/jdk/src/share/classes/java/nio/file/DirectoryStream.java index 61fa52cec4d..fd7bf85c939 100644 --- a/jdk/src/share/classes/java/nio/file/DirectoryStream.java +++ b/jdk/src/share/classes/java/nio/file/DirectoryStream.java @@ -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. + * + *

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}. + * + *

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. + * + *

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: *

  *   Path dir = ...
- *   DirectoryStream<Path> stream = dir.newDirectoryStream();
- *   try {
+ *   try (DirectoryStream<Path> stream = dir.newDirectoryStream()) {
  *       for (Path entry: stream) {
- *         ..
+ *           ...
  *       }
- *   } finally {
- *       stream.close();
  *   }
  * 
* - *

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}. - * - *

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}. + *

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}. * *

A directory stream is not required to be asynchronously closeable. * 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. * - *

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}. + *

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}. * *

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. * - *

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. - * *

The iterator is weakly consistent. 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. * + *

Usage Examples: + * Suppose we want a list of the source files in a directory. This example uses + * both the for-each and try-with-resources constructs. + *

+ *   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;
+ *   }
+ * 
* @param The type of element returned by the iterator * * @since 1.7 diff --git a/jdk/src/share/classes/java/nio/file/Path.java b/jdk/src/share/classes/java/nio/file/Path.java index 029feee58a0..abdc77750cc 100644 --- a/jdk/src/share/classes/java/nio/file/Path.java +++ b/jdk/src/share/classes/java/nio/file/Path.java @@ -984,11 +984,11 @@ public abstract class Path * directory. * *

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. * *

When an implementation supports operations on entries in the * directory that execute in a race-free manner then the returned directory diff --git a/jdk/src/share/classes/java/nio/file/SecureDirectoryStream.java b/jdk/src/share/classes/java/nio/file/SecureDirectoryStream.java index 362d1f547ea..d4b00e1158c 100644 --- a/jdk/src/share/classes/java/nio/file/SecureDirectoryStream.java +++ b/jdk/src/share/classes/java/nio/file/SecureDirectoryStream.java @@ -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. * - *

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. - * *

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 diff --git a/jdk/src/share/classes/java/util/ConcurrentModificationException.java b/jdk/src/share/classes/java/util/ConcurrentModificationException.java index b96b451aab9..956fbdfaf62 100644 --- a/jdk/src/share/classes/java/util/ConcurrentModificationException.java +++ b/jdk/src/share/classes/java/util/ConcurrentModificationException.java @@ -49,9 +49,9 @@ package java.util; *

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 ConcurrentModificationException 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: ConcurrentModificationException + * exception for its correctness: {@code ConcurrentModificationException} * should be used only to detect bugs. * * @author Josh Bloch @@ -77,7 +77,7 @@ public class ConcurrentModificationException extends RuntimeException { } /** - * Constructs a ConcurrentModificationException 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. + * + *

Note that the detail message associated with cause is + * not 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); + } } diff --git a/jdk/src/solaris/classes/sun/nio/fs/UnixDirectoryStream.java b/jdk/src/solaris/classes/sun/nio/fs/UnixDirectoryStream.java index 51ebcbb1fe3..2328c6266d3 100644 --- a/jdk/src/solaris/classes/sun/nio/fs/UnixDirectoryStream.java +++ b/jdk/src/solaris/classes/sun/nio/fs/UnixDirectoryStream.java @@ -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 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); - } } + } catch (UnixException x) { + 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(); - } + Path result; + if (nextEntry == null && !atEof) { + result = readNextEntry(); + } else { + result = nextEntry; + nextEntry = null; } - prevEntry = 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; - } - } diff --git a/jdk/src/solaris/classes/sun/nio/fs/UnixSecureDirectoryStream.java b/jdk/src/solaris/classes/sun/nio/fs/UnixSecureDirectoryStream.java index 1c10e1ed34f..33eecd14bec 100644 --- a/jdk/src/solaris/classes/sun/nio/fs/UnixSecureDirectoryStream.java +++ b/jdk/src/solaris/classes/sun/nio/fs/UnixSecureDirectoryStream.java @@ -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); diff --git a/jdk/src/windows/classes/sun/nio/fs/WindowsDirectoryStream.java b/jdk/src/windows/classes/sun/nio/fs/WindowsDirectoryStream.java index 108b2e3f592..8b57034383e 100644 --- a/jdk/src/windows/classes/sun/nio/fs/WindowsDirectoryStream.java +++ b/jdk/src/windows/classes/sun/nio/fs/WindowsDirectoryStream.java @@ -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 { 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 { - name = FindNextFile(handle, findDataBuffer.address()); - if (name == null) { - // NO_MORE_FILES - return null; + if (isOpen) { + name = FindNextFile(handle, findDataBuffer.address()); } } 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(); - } + Path result = null; + if (nextEntry == null && !atEof) { + result = readNextEntry(); + } else { + result = nextEntry; + nextEntry = null; } - prevEntry = 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(); } } } diff --git a/jdk/test/java/nio/file/DirectoryStream/Basic.java b/jdk/test/java/nio/file/DirectoryStream/Basic.java index 69fe7b1faba..f5aef3630ea 100644 --- a/jdk/test/java/nio/file/DirectoryStream/Basic.java +++ b/jdk/test/java/nio/file/DirectoryStream/Basic.java @@ -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() { 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 - stream = dir.newDirectoryStream(); - Iterator i = stream.iterator(); - while (i.hasNext()) { - Path entry = i.next(); - if (!entry.getName().equals(foo)) - throw new RuntimeException("entry not expected"); - i.remove(); - } - stream.close(); - - // test IllegalStateException - dir.resolve(foo).createFile(); + // test UnsupportedOperationException stream = dir.newDirectoryStream(); - i = stream.iterator(); + Iterator i = stream.iterator(); i.next(); try { + i.remove(); + throw new RuntimeException("UnsupportedOperationException expected"); + } catch (UnsupportedOperationException uoe) { + } + + // test IllegalStateException + stream = dir.newDirectoryStream(); + 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 { diff --git a/jdk/test/java/nio/file/DirectoryStream/SecureDS.java b/jdk/test/java/nio/file/DirectoryStream/SecureDS.java index b307d8456dc..173d770602d 100644 --- a/jdk/test/java/nio/file/DirectoryStream/SecureDS.java +++ b/jdk/test/java/nio/file/DirectoryStream/SecureDS.java @@ -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)dir1.newDirectoryStream(); - dir1.moveTo(dir2); - Iterator iter = stream.iterator(); - int removed = 0; - while (iter.hasNext()) { - iter.next(); - iter.remove(); - removed++; - } - assertTrue(removed == 1); - // clean-up stream.close(); dir2.delete(); diff --git a/jdk/test/java/nio/file/etc/Exceptions.java b/jdk/test/java/nio/file/etc/Exceptions.java new file mode 100644 index 00000000000..28d9459a93d --- /dev/null +++ b/jdk/test/java/nio/file/etc/Exceptions.java @@ -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; + } +}