mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-21 11:34:38 +02:00
6808647: (file) Paths.get("C:").newDirectoryStream() iterates over Path elements with additional slash [win]
6808648: (file) Files.walkFileTree should obtain file attributes during iteration [win] Reviewed-by: sherman
This commit is contained in:
parent
5efe4b020a
commit
6d59271ca9
10 changed files with 316 additions and 46 deletions
|
@ -252,6 +252,7 @@ FILES_src = \
|
|||
sun/nio/fs/AbstractUserDefinedFileAttributeView.java \
|
||||
sun/nio/fs/AbstractWatchKey.java \
|
||||
sun/nio/fs/AbstractWatchService.java \
|
||||
sun/nio/fs/BasicFileAttributesHolder.java \
|
||||
sun/nio/fs/Cancellable.java \
|
||||
sun/nio/fs/DefaultFileSystemProvider.java \
|
||||
sun/nio/fs/DefaultFileTypeDetector.java \
|
||||
|
|
|
@ -28,6 +28,7 @@ package java.nio.file;
|
|||
import java.nio.file.attribute.*;
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import sun.nio.fs.BasicFileAttributesHolder;
|
||||
|
||||
/**
|
||||
* Simple file tree walker that works in a similar manner to nftw(3C).
|
||||
|
@ -65,6 +66,10 @@ class FileTreeWalker {
|
|||
* Walk file tree starting at the given file
|
||||
*/
|
||||
void walk(Path start, int maxDepth) {
|
||||
// don't use attributes of starting file as they may be stale
|
||||
if (start instanceof BasicFileAttributesHolder) {
|
||||
((BasicFileAttributesHolder)start).invalidate();
|
||||
}
|
||||
FileVisitResult result = walk(start,
|
||||
maxDepth,
|
||||
new ArrayList<AncestorDirectory>());
|
||||
|
@ -75,11 +80,9 @@ class FileTreeWalker {
|
|||
|
||||
/**
|
||||
* @param file
|
||||
* The directory to visit
|
||||
* @param path
|
||||
* list of directories that is relative path from starting file
|
||||
* the directory to visit
|
||||
* @param depth
|
||||
* Depth remaining
|
||||
* depth remaining
|
||||
* @param ancestors
|
||||
* use when cycle detection is enabled
|
||||
*/
|
||||
|
@ -91,28 +94,36 @@ class FileTreeWalker {
|
|||
if (depth-- < 0)
|
||||
return FileVisitResult.CONTINUE;
|
||||
|
||||
// if attributes are cached then use them if possible
|
||||
BasicFileAttributes attrs = null;
|
||||
if (file instanceof BasicFileAttributesHolder) {
|
||||
BasicFileAttributes cached = ((BasicFileAttributesHolder)file).get();
|
||||
if (!followLinks || !cached.isSymbolicLink())
|
||||
attrs = cached;
|
||||
}
|
||||
IOException exc = null;
|
||||
|
||||
// attempt to get attributes of file. If fails and we are following
|
||||
// links then a link target might not exist so get attributes of link
|
||||
try {
|
||||
if (attrs == null) {
|
||||
try {
|
||||
attrs = Attributes.readBasicFileAttributes(file, linkOptions);
|
||||
} catch (IOException x1) {
|
||||
if (followLinks) {
|
||||
try {
|
||||
attrs = Attributes
|
||||
.readBasicFileAttributes(file, LinkOption.NOFOLLOW_LINKS);
|
||||
} catch (IOException x2) {
|
||||
exc = x2;
|
||||
try {
|
||||
attrs = Attributes.readBasicFileAttributes(file, linkOptions);
|
||||
} catch (IOException x1) {
|
||||
if (followLinks) {
|
||||
try {
|
||||
attrs = Attributes
|
||||
.readBasicFileAttributes(file, LinkOption.NOFOLLOW_LINKS);
|
||||
} catch (IOException x2) {
|
||||
exc = x2;
|
||||
}
|
||||
} else {
|
||||
exc = x1;
|
||||
}
|
||||
} else {
|
||||
exc = x1;
|
||||
}
|
||||
} catch (SecurityException x) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
} catch (SecurityException x) {
|
||||
return FileVisitResult.CONTINUE;
|
||||
}
|
||||
|
||||
// unable to get attributes of file
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright 2008-2009 Sun Microsystems, Inc. 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. Sun designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
package sun.nio.fs;
|
||||
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
|
||||
/**
|
||||
* Implemented by objects that may hold or cache the attributes of a file.
|
||||
*/
|
||||
|
||||
public interface BasicFileAttributesHolder {
|
||||
/**
|
||||
* Returns cached attributes (may be null). If file is a symbolic link then
|
||||
* the attributes are the link attributes and not the final target of the
|
||||
* file.
|
||||
*/
|
||||
BasicFileAttributes get();
|
||||
|
||||
/**
|
||||
* Invalidates cached attributes
|
||||
*/
|
||||
void invalidate();
|
||||
}
|
|
@ -26,6 +26,7 @@
|
|||
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;
|
||||
|
@ -49,6 +50,9 @@ class WindowsDirectoryStream
|
|||
// first entry in the directory
|
||||
private final String firstName;
|
||||
|
||||
// buffer for WIN32_FIND_DATA structure that receives information about file
|
||||
private final NativeBuffer findDataBuffer;
|
||||
|
||||
private final Object closeLock = new Object();
|
||||
|
||||
// need closeLock to access these
|
||||
|
@ -75,6 +79,7 @@ class WindowsDirectoryStream
|
|||
FirstFile first = FindFirstFile(search);
|
||||
this.handle = first.handle();
|
||||
this.firstName = first.name();
|
||||
this.findDataBuffer = WindowsFileAttributes.getBufferForFindData();
|
||||
} catch (WindowsException x) {
|
||||
if (x.lastError() == ERROR_DIRECTORY) {
|
||||
throw new NotDirectoryException(dir.getPathForExceptionMessage());
|
||||
|
@ -95,6 +100,7 @@ class WindowsDirectoryStream
|
|||
return;
|
||||
isOpen = false;
|
||||
}
|
||||
findDataBuffer.release();
|
||||
try {
|
||||
FindClose(handle);
|
||||
} catch (WindowsException x) {
|
||||
|
@ -133,11 +139,19 @@ class WindowsDirectoryStream
|
|||
}
|
||||
|
||||
// applies filter and also ignores "." and ".."
|
||||
private Path acceptEntry(String s) {
|
||||
private Path acceptEntry(String s, BasicFileAttributes attrs) {
|
||||
if (s.equals(".") || s.equals(".."))
|
||||
return null;
|
||||
if (dir.needsSlashWhenResolving()) {
|
||||
StringBuilder sb = new StringBuilder(dir.toString());
|
||||
sb.append('\\');
|
||||
sb.append(s);
|
||||
s = sb.toString();
|
||||
} else {
|
||||
s = dir + s;
|
||||
}
|
||||
Path entry = WindowsPath
|
||||
.createFromNormalizedPath(dir.getFileSystem(), dir + "\\" + s);
|
||||
.createFromNormalizedPath(dir.getFileSystem(), s, attrs);
|
||||
if (filter.accept(entry)) {
|
||||
return entry;
|
||||
} else {
|
||||
|
@ -149,21 +163,27 @@ class WindowsDirectoryStream
|
|||
private Path readNextEntry() {
|
||||
// handle first element returned by search
|
||||
if (first != null) {
|
||||
nextEntry = acceptEntry(first);
|
||||
nextEntry = acceptEntry(first, null);
|
||||
first = null;
|
||||
if (nextEntry != null)
|
||||
return nextEntry;
|
||||
}
|
||||
|
||||
String name = null;
|
||||
for (;;) {
|
||||
String name = null;
|
||||
WindowsFileAttributes attrs;
|
||||
|
||||
// synchronize on closeLock to prevent close while reading
|
||||
synchronized (closeLock) {
|
||||
if (!isOpen)
|
||||
throwAsConcurrentModificationException(new
|
||||
IllegalStateException("Directory stream is closed"));
|
||||
try {
|
||||
name = FindNextFile(handle);
|
||||
name = FindNextFile(handle, findDataBuffer.address());
|
||||
if (name == null) {
|
||||
// NO_MORE_FILES
|
||||
return null;
|
||||
}
|
||||
} catch (WindowsException x) {
|
||||
try {
|
||||
x.rethrowAsIOException(dir);
|
||||
|
@ -171,13 +191,16 @@ class WindowsDirectoryStream
|
|||
throwAsConcurrentModificationException(ioe);
|
||||
}
|
||||
}
|
||||
|
||||
// grab the attributes from the WIN32_FIND_DATA structure
|
||||
// (needs to be done while holding closeLock because close
|
||||
// will release the buffer)
|
||||
attrs = WindowsFileAttributes
|
||||
.fromFindData(findDataBuffer.address());
|
||||
}
|
||||
|
||||
// EOF
|
||||
if (name == null)
|
||||
return null;
|
||||
|
||||
Path entry = acceptEntry(name);
|
||||
// return entry if accepted by filter
|
||||
Path entry = acceptEntry(name, attrs);
|
||||
if (entry != null)
|
||||
return entry;
|
||||
}
|
||||
|
|
|
@ -87,6 +87,29 @@ class WindowsFileAttributes
|
|||
private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_SIZEHIGH = 28;
|
||||
private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_SIZELOW = 32;
|
||||
|
||||
/**
|
||||
* typedef struct _WIN32_FIND_DATA {
|
||||
* DWORD dwFileAttributes;
|
||||
* FILETIME ftCreationTime;
|
||||
* FILETIME ftLastAccessTime;
|
||||
* FILETIME ftLastWriteTime;
|
||||
* DWORD nFileSizeHigh;
|
||||
* DWORD nFileSizeLow;
|
||||
* DWORD dwReserved0;
|
||||
* DWORD dwReserved1;
|
||||
* TCHAR cFileName[MAX_PATH];
|
||||
* TCHAR cAlternateFileName[14];
|
||||
* } WIN32_FIND_DATA;
|
||||
*/
|
||||
private static final short SIZEOF_FIND_DATA = 592;
|
||||
private static final short OFFSETOF_FIND_DATA_ATTRIBUTES = 0;
|
||||
private static final short OFFSETOF_FIND_DATA_CREATETIME = 4;
|
||||
private static final short OFFSETOF_FIND_DATA_LASTACCESSTIME = 12;
|
||||
private static final short OFFSETOF_FIND_DATA_LASTWRITETIME = 20;
|
||||
private static final short OFFSETOF_FIND_DATA_SIZEHIGH = 28;
|
||||
private static final short OFFSETOF_FIND_DATA_SIZELOW = 32;
|
||||
private static final short OFFSETOF_FIND_DATA_RESERVED0 = 36;
|
||||
|
||||
// indicates if accurate metadata is required (interesting on NTFS only)
|
||||
private static final boolean ensureAccurateMetadata;
|
||||
static {
|
||||
|
@ -210,6 +233,41 @@ class WindowsFileAttributes
|
|||
0); // fileIndexLow
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Allocates a native buffer for a WIN32_FIND_DATA structure
|
||||
*/
|
||||
static NativeBuffer getBufferForFindData() {
|
||||
return NativeBuffers.getNativeBuffer(SIZEOF_FIND_DATA);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a WindowsFileAttributes from a WIN32_FIND_DATA structure
|
||||
*/
|
||||
static WindowsFileAttributes fromFindData(long address) {
|
||||
int fileAttrs = unsafe.getInt(address + OFFSETOF_FIND_DATA_ATTRIBUTES);
|
||||
long creationTime =
|
||||
toJavaTime(unsafe.getLong(address + OFFSETOF_FIND_DATA_CREATETIME));
|
||||
long lastAccessTime =
|
||||
toJavaTime(unsafe.getLong(address + OFFSETOF_FIND_DATA_LASTACCESSTIME));
|
||||
long lastWriteTime =
|
||||
toJavaTime(unsafe.getLong(address + OFFSETOF_FIND_DATA_LASTWRITETIME));
|
||||
long size = ((long)(unsafe.getInt(address + OFFSETOF_FIND_DATA_SIZEHIGH)) << 32)
|
||||
+ (unsafe.getInt(address + OFFSETOF_FIND_DATA_SIZELOW) & 0xFFFFFFFFL);
|
||||
int reparseTag = ((fileAttrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0) ?
|
||||
+ unsafe.getInt(address + OFFSETOF_FIND_DATA_RESERVED0) : 0;
|
||||
return new WindowsFileAttributes(fileAttrs,
|
||||
creationTime,
|
||||
lastAccessTime,
|
||||
lastWriteTime,
|
||||
size,
|
||||
reparseTag,
|
||||
1, // linkCount
|
||||
0, // volSerialNumber
|
||||
0, // fileIndexHigh
|
||||
0); // fileIndexLow
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the attributes of an open file
|
||||
*/
|
||||
|
|
|
@ -236,11 +236,9 @@ class WindowsFileSystem
|
|||
|
||||
@Override
|
||||
public Path getPath(String path) {
|
||||
WindowsPathParser.Result result = WindowsPathParser.parse(path);
|
||||
return new WindowsPath(this, result.type(), result.root(), result.path());
|
||||
return WindowsPath.parse(this, path);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public UserPrincipalLookupService getUserPrincipalLookupService() {
|
||||
return theLookupService;
|
||||
|
|
|
@ -211,9 +211,10 @@ class WindowsNativeDispatcher {
|
|||
* LPWIN32_FIND_DATA lpFindFileData
|
||||
* )
|
||||
*
|
||||
* @return lpFindFileData->cFileName
|
||||
* @return lpFindFileData->cFileName or null
|
||||
*/
|
||||
static native String FindNextFile(long handle) throws WindowsException;
|
||||
static native String FindNextFile(long handle, long address)
|
||||
throws WindowsException;
|
||||
|
||||
/**
|
||||
* HANDLE FindFirstStreamW(
|
||||
|
|
|
@ -83,10 +83,10 @@ class WindowsPath extends AbstractPath {
|
|||
/**
|
||||
* Initializes a new instance of this class.
|
||||
*/
|
||||
WindowsPath(WindowsFileSystem fs,
|
||||
WindowsPathType type,
|
||||
String root,
|
||||
String path)
|
||||
private WindowsPath(WindowsFileSystem fs,
|
||||
WindowsPathType type,
|
||||
String root,
|
||||
String path)
|
||||
{
|
||||
this.fs = fs;
|
||||
this.type = type;
|
||||
|
@ -95,7 +95,7 @@ class WindowsPath extends AbstractPath {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a WindowsPath by parsing the given path.
|
||||
* Creates a Path by parsing the given path.
|
||||
*/
|
||||
static WindowsPath parse(WindowsFileSystem fs, String path) {
|
||||
WindowsPathParser.Result result = WindowsPathParser.parse(path);
|
||||
|
@ -103,18 +103,71 @@ class WindowsPath extends AbstractPath {
|
|||
}
|
||||
|
||||
/**
|
||||
* Creates a WindowsPath from a given path that is known to be normalized.
|
||||
* Creates a Path from a given path that is known to be normalized.
|
||||
*/
|
||||
static WindowsPath createFromNormalizedPath(WindowsFileSystem fs, String path) {
|
||||
static WindowsPath createFromNormalizedPath(WindowsFileSystem fs,
|
||||
String path,
|
||||
BasicFileAttributes attrs)
|
||||
{
|
||||
try {
|
||||
WindowsPathParser.Result result =
|
||||
WindowsPathParser.parseNormalizedPath(path);
|
||||
return new WindowsPath(fs, result.type(), result.root(), result.path());
|
||||
if (attrs == null) {
|
||||
return new WindowsPath(fs,
|
||||
result.type(),
|
||||
result.root(),
|
||||
result.path());
|
||||
} else {
|
||||
return new WindowsPathWithAttributes(fs,
|
||||
result.type(),
|
||||
result.root(),
|
||||
result.path(),
|
||||
attrs);
|
||||
}
|
||||
} catch (InvalidPathException x) {
|
||||
throw new AssertionError(x.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a WindowsPath from a given path that is known to be normalized.
|
||||
*/
|
||||
static WindowsPath createFromNormalizedPath(WindowsFileSystem fs,
|
||||
String path)
|
||||
{
|
||||
return createFromNormalizedPath(fs, path, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Special implementation with attached/cached attributes (used to quicken
|
||||
* file tree traveral)
|
||||
*/
|
||||
private static class WindowsPathWithAttributes
|
||||
extends WindowsPath implements BasicFileAttributesHolder
|
||||
{
|
||||
final WeakReference<BasicFileAttributes> ref;
|
||||
|
||||
WindowsPathWithAttributes(WindowsFileSystem fs,
|
||||
WindowsPathType type,
|
||||
String root,
|
||||
String path,
|
||||
BasicFileAttributes attrs)
|
||||
{
|
||||
super(fs, type, root, path);
|
||||
ref = new WeakReference<BasicFileAttributes>(attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicFileAttributes get() {
|
||||
return ref.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidate() {
|
||||
ref.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// use this message when throwing exceptions
|
||||
String getPathForExceptionMessage() {
|
||||
return path;
|
||||
|
@ -290,6 +343,12 @@ class WindowsPath extends AbstractPath {
|
|||
return type == WindowsPathType.UNC;
|
||||
}
|
||||
|
||||
boolean needsSlashWhenResolving() {
|
||||
if (path.endsWith("\\"))
|
||||
return false;
|
||||
return path.length() > root.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAbsolute() {
|
||||
return type == WindowsPathType.ABSOLUTE || type == WindowsPathType.UNC;
|
||||
|
|
|
@ -392,16 +392,16 @@ Java_sun_nio_fs_WindowsNativeDispatcher_FindFirstFile1(JNIEnv* env, jclass this,
|
|||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_sun_nio_fs_WindowsNativeDispatcher_FindNextFile(JNIEnv* env, jclass this,
|
||||
jlong handle)
|
||||
jlong handle, jlong dataAddress)
|
||||
{
|
||||
WIN32_FIND_DATAW data;
|
||||
HANDLE h = (HANDLE)jlong_to_ptr(handle);
|
||||
WIN32_FIND_DATAW* data = (WIN32_FIND_DATAW*)jlong_to_ptr(dataAddress);
|
||||
|
||||
if (FindNextFileW(h, &data) != 0) {
|
||||
return (*env)->NewString(env, data.cFileName, wcslen(data.cFileName));
|
||||
if (FindNextFileW(h, data) != 0) {
|
||||
return (*env)->NewString(env, data->cFileName, wcslen(data->cFileName));
|
||||
} else {
|
||||
if (GetLastError() != ERROR_NO_MORE_FILES)
|
||||
throwWindowsException(env, GetLastError());
|
||||
if (GetLastError() != ERROR_NO_MORE_FILES)
|
||||
throwWindowsException(env, GetLastError());
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
|
73
jdk/test/java/nio/file/DirectoryStream/DriveLetter.java
Normal file
73
jdk/test/java/nio/file/DirectoryStream/DriveLetter.java
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2008-2009 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
|
||||
* CA 95054 USA or visit www.sun.com if you need additional information or
|
||||
* have any questions.
|
||||
*/
|
||||
|
||||
/* @test
|
||||
* @bug 6808647
|
||||
* @summary Checks that a DirectoryStream's iterator returns the expected
|
||||
* path when opening a directory by specifying only the drive letter.
|
||||
* @library ..
|
||||
*/
|
||||
|
||||
import java.nio.file.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
public class DriveLetter {
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
String os = System.getProperty("os.name");
|
||||
if (!os.startsWith("Windows")) {
|
||||
System.out.println("This is Windows specific test");
|
||||
return;
|
||||
}
|
||||
String here = System.getProperty("user.dir");
|
||||
if (here.length() < 2 || here.charAt(1) != ':')
|
||||
throw new RuntimeException("Unable to determine drive letter");
|
||||
|
||||
// create temporary file in current directory
|
||||
File tempFile = File.createTempFile("foo", "tmp", new File(here));
|
||||
try {
|
||||
// we should expect C:foo.tmp to be returned by iterator
|
||||
String drive = here.substring(0, 2);
|
||||
Path expected = Paths.get(drive).resolve(tempFile.getName());
|
||||
|
||||
boolean found = false;
|
||||
DirectoryStream<Path> stream = Paths.get(drive).newDirectoryStream();
|
||||
try {
|
||||
for (Path file : stream) {
|
||||
if (file.equals(expected)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
stream.close();
|
||||
}
|
||||
if (!found)
|
||||
throw new RuntimeException("Temporary file not found???");
|
||||
|
||||
} finally {
|
||||
tempFile.delete();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue