From 9e04234870e65dec1599465350fc2fedecd5b910 Mon Sep 17 00:00:00 2001 From: alanb Date: Sun, 15 Feb 2009 12:25:54 +0000 Subject: [PATCH] 6781363: New I/O: Update socket-channel API to jsr203/nio2-b99 4313887: New I/O: Improved filesystem interface 4607272: New I/O: Support asynchronous I/O Reviewed-by: sherman, chegar --- make/docs/CORE_PKGS.gmk | 5 +- make/docs/NON_CORE_PKGS.gmk | 5 +- make/java/nio/Exportedfiles.gmk | 4 +- make/java/nio/FILES_c.gmk | 4 +- make/java/nio/FILES_java.gmk | 137 +- make/java/nio/Makefile | 214 ++- make/java/nio/mapfile-linux | 113 +- make/java/nio/mapfile-solaris | 101 +- make/mksample/nio/Makefile | 4 +- make/mksample/nio/file/Makefile | 56 + .../com/sun/nio/file/ExtendedCopyOption.java | 43 + .../com/sun/nio/file/ExtendedOpenOption.java | 50 + .../nio/file/ExtendedWatchEventModifier.java | 43 + .../file/SensitivityWatchEventModifier.java | 62 + src/share/classes/java/io/File.java | 347 +++- src/share/classes/java/io/FilePermission.java | 58 +- .../java/net/StandardProtocolFamily.java | 4 +- .../java/net/StandardSocketOption.java | 39 +- .../nio/channels/AsynchronousByteChannel.java | 205 +++ .../nio/channels/AsynchronousChannel.java | 116 ++ .../channels/AsynchronousChannelGroup.java | 344 ++++ .../channels/AsynchronousDatagramChannel.java | 718 ++++++++ .../nio/channels/AsynchronousFileChannel.java | 774 ++++++++ .../AsynchronousServerSocketChannel.java | 303 ++++ .../channels/AsynchronousSocketChannel.java | 670 +++++++ .../classes/java/nio/channels/Channels.java | 157 +- .../java/nio/channels/CompletionHandler.java | 77 + .../java/nio/channels/DatagramChannel.java | 18 +- .../java/nio/channels/FileChannel.java | 252 ++- .../classes/java/nio/channels/FileLock.java | 77 +- .../java/nio/channels/MembershipKey.java | 22 +- .../java/nio/channels/MulticastChannel.java | 27 +- .../java/nio/channels/NetworkChannel.java | 16 +- .../nio/channels/SeekableByteChannel.java | 168 ++ .../nio/channels/ServerSocketChannel.java | 6 +- .../java/nio/channels/SocketChannel.java | 16 +- .../classes/java/nio/channels/exceptions | 37 +- .../java/nio/channels/package-info.java | 64 +- .../spi/AsynchronousChannelProvider.java | 264 +++ .../nio/channels/spi/SelectorProvider.java | 6 +- .../java/nio/channels/spi/package.html | 6 +- .../java/nio/file/AccessDeniedException.java | 68 + .../classes/java/nio/file/AccessMode.java | 49 + .../file/AtomicMoveNotSupportedException.java | 56 + .../file/ClosedDirectoryStreamException.java | 45 + .../nio/file/ClosedFileSystemException.java | 43 + .../nio/file/ClosedWatchServiceException.java | 43 + .../classes/java/nio/file/CopyOption.java | 41 + .../nio/file/DirectoryNotEmptyException.java | 49 + .../java/nio/file/DirectoryStream.java | 138 ++ .../java/nio/file/DirectoryStreamFilters.java | 210 +++ .../classes/java/nio/file/FileAction.java | 64 + .../nio/file/FileAlreadyExistsException.java | 63 + src/share/classes/java/nio/file/FileRef.java | 424 +++++ .../classes/java/nio/file/FileStore.java | 169 ++ .../classes/java/nio/file/FileSystem.java | 453 +++++ .../FileSystemAlreadyExistsException.java | 53 + .../java/nio/file/FileSystemException.java | 125 ++ .../nio/file/FileSystemNotFoundException.java | 52 + .../classes/java/nio/file/FileSystems.java | 413 +++++ .../classes/java/nio/file/FileTreeWalker.java | 244 +++ .../java/nio/file/FileVisitOption.java | 45 + .../java/nio/file/FileVisitResult.java | 62 + .../classes/java/nio/file/FileVisitor.java | 175 ++ src/share/classes/java/nio/file/Files.java | 406 +++++ .../java/nio/file/InvalidPathException.java | 130 ++ .../classes/java/nio/file/LinkOption.java | 43 + .../classes/java/nio/file/LinkPermission.java | 107 ++ .../java/nio/file/NoSuchFileException.java | 63 + .../java/nio/file/NotDirectoryException.java | 49 + .../java/nio/file/NotLinkException.java | 63 + .../classes/java/nio/file/OpenOption.java | 45 + src/share/classes/java/nio/file/Path.java | 1612 +++++++++++++++++ .../classes/java/nio/file/PathMatcher.java | 49 + src/share/classes/java/nio/file/Paths.java | 123 ++ .../nio/file/ProviderMismatchException.java | 53 + .../nio/file/ProviderNotFoundException.java | 52 + .../nio/file/ReadOnlyFileSystemException.java | 43 + .../java/nio/file/SecureDirectoryStream.java | 324 ++++ .../java/nio/file/SimpleFileVisitor.java | 121 ++ .../java/nio/file/StandardCopyOption.java | 47 + .../java/nio/file/StandardOpenOption.java | 125 ++ .../java/nio/file/StandardWatchEventKind.java | 94 + .../classes/java/nio/file/WatchEvent.java | 116 ++ src/share/classes/java/nio/file/WatchKey.java | 138 ++ .../classes/java/nio/file/WatchService.java | 178 ++ .../classes/java/nio/file/Watchable.java | 127 ++ .../java/nio/file/attribute/AclEntry.java | 394 ++++ .../java/nio/file/attribute/AclEntryFlag.java | 65 + .../file/attribute/AclEntryPermission.java | 130 ++ .../java/nio/file/attribute/AclEntryType.java | 56 + .../file/attribute/AclFileAttributeView.java | 211 +++ .../nio/file/attribute/AttributeView.java | 118 ++ .../java/nio/file/attribute/Attributes.java | 703 +++++++ .../attribute/BasicFileAttributeView.java | 186 ++ .../file/attribute/BasicFileAttributes.java | 163 ++ .../file/attribute/DosFileAttributeView.java | 179 ++ .../nio/file/attribute/DosFileAttributes.java | 84 + .../nio/file/attribute/FileAttribute.java | 50 + .../nio/file/attribute/FileAttributeView.java | 43 + .../attribute/FileOwnerAttributeView.java | 101 ++ .../attribute/FileStoreAttributeView.java | 38 + .../FileStoreSpaceAttributeView.java | 85 + .../attribute/FileStoreSpaceAttributes.java | 66 + .../nio/file/attribute/GroupPrincipal.java | 42 + .../attribute/PosixFileAttributeView.java | 196 ++ .../file/attribute/PosixFileAttributes.java | 77 + .../file/attribute/PosixFilePermission.java | 86 + .../file/attribute/PosixFilePermissions.java | 180 ++ .../UserDefinedFileAttributeView.java | 232 +++ .../nio/file/attribute/UserPrincipal.java | 54 + .../attribute/UserPrincipalLookupService.java | 104 ++ .../UserPrincipalNotFoundException.java | 64 + .../java/nio/file/attribute/package-info.java | 119 ++ .../classes/java/nio/file/package-info.java | 116 ++ .../java/nio/file/spi/AbstractPath.java | 568 ++++++ .../java/nio/file/spi/FileSystemProvider.java | 434 +++++ .../java/nio/file/spi/FileTypeDetector.java | 106 ++ .../java/nio/file/spi/package-info.java | 39 + src/share/classes/java/util/Scanner.java | 46 +- .../classes/sun/nio/ch/AbstractFuture.java | 63 + .../nio/ch/AsynchronousChannelGroupImpl.java | 341 ++++ .../nio/ch/AsynchronousFileChannelImpl.java | 164 ++ .../AsynchronousServerSocketChannelImpl.java | 219 +++ .../nio/ch/AsynchronousSocketChannelImpl.java | 542 ++++++ src/share/classes/sun/nio/ch/Cancellable.java | 39 + .../classes/sun/nio/ch/CompletedFuture.java | 113 ++ .../sun/nio/ch/DatagramChannelImpl.java | 102 +- .../sun/nio/ch/ExtendedSocketOption.java | 2 +- .../classes/sun/nio/ch/FileChannelImpl.java | 399 +--- .../classes/sun/nio/ch/FileDispatcher.java | 48 + .../classes/sun/nio/ch/FileLockImpl.java | 21 +- .../classes/sun/nio/ch/FileLockTable.java | 282 +++ src/share/classes/sun/nio/ch/Groupable.java | 35 + src/share/classes/sun/nio/ch/IOUtil.java | 243 +-- src/share/classes/sun/nio/ch/Invoker.java | 261 +++ .../classes/sun/nio/ch/MembershipKeyImpl.java | 20 +- .../sun/nio/ch/MembershipRegistry.java | 22 +- .../classes/sun/nio/ch/NativeThreadSet.java | 14 +- src/share/classes/sun/nio/ch/Net.java | 6 +- src/share/classes/sun/nio/ch/OptionKey.java | 2 +- .../classes/sun/nio/ch/PendingFuture.java | 257 +++ src/share/classes/sun/nio/ch/Reflect.java | 6 +- .../sun/nio/ch/ServerSocketChannelImpl.java | 23 +- ...SimpleAsynchronousDatagramChannelImpl.java | 612 +++++++ .../ch/SimpleAsynchronousFileChannelImpl.java | 426 +++++ .../classes/sun/nio/ch/SocketChannelImpl.java | 24 +- src/share/classes/sun/nio/ch/ThreadPool.java | 176 ++ src/share/classes/sun/nio/ch/Util.java | 6 +- .../nio/fs/AbstractAclFileAttributeView.java | 110 ++ .../fs/AbstractBasicFileAttributeView.java | 208 +++ .../AbstractFileStoreSpaceAttributeView.java | 119 ++ .../sun/nio/fs/AbstractFileTypeDetector.java | 69 + .../classes/sun/nio/fs/AbstractPoller.java | 290 +++ .../AbstractUserDefinedFileAttributeView.java | 124 ++ .../classes/sun/nio/fs/AbstractWatchKey.java | 180 ++ .../sun/nio/fs/AbstractWatchService.java | 161 ++ src/share/classes/sun/nio/fs/Cancellable.java | 137 ++ .../nio/fs/FileOwnerAttributeViewImpl.java | 111 ++ src/share/classes/sun/nio/fs/Globs.java | 217 +++ src/share/classes/sun/nio/fs/MimeType.java | 73 + .../classes/sun/nio/fs/NativeBuffer.java | 87 + .../classes/sun/nio/fs/NativeBuffers.java | 140 ++ .../sun/nio/fs/PollingWatchService.java | 429 +++++ src/share/classes/sun/nio/fs/Reflect.java | 63 + .../sun/security/util/SecurityConstants.java | 3 +- .../sun/nio/ch/genSocketOptionRegistry.c | 8 +- src/share/sample/nio/file/AclEdit.java | 296 +++ src/share/sample/nio/file/Chmod.java | 347 ++++ src/share/sample/nio/file/Copy.java | 217 +++ src/share/sample/nio/file/DiskUsage.java | 74 + src/share/sample/nio/file/FileType.java | 57 + src/share/sample/nio/file/WatchDir.java | 196 ++ src/share/sample/nio/file/Xdd.java | 112 ++ .../sun/nio/ch/DatagramDispatcher.java | 6 +- .../DefaultAsynchronousChannelProvider.java | 56 + .../sun/nio/ch/DevPollArrayWrapper.java | 4 +- .../sun/nio/ch/DevPollSelectorImpl.java | 6 +- src/solaris/classes/sun/nio/ch/EPoll.java | 121 ++ .../classes/sun/nio/ch/EPollArrayWrapper.java | 4 +- src/solaris/classes/sun/nio/ch/EPollPort.java | 322 ++++ .../classes/sun/nio/ch/EPollSelectorImpl.java | 6 +- ...ispatcher.java => FileDispatcherImpl.java} | 45 +- .../ch/LinuxAsynchronousChannelProvider.java | 99 + .../classes/sun/nio/ch/PollSelectorImpl.java | 6 +- src/solaris/classes/sun/nio/ch/Port.java | 168 ++ .../classes/sun/nio/ch/SinkChannelImpl.java | 4 +- .../classes/sun/nio/ch/SocketDispatcher.java | 14 +- .../SolarisAsynchronousChannelProvider.java | 102 ++ .../classes/sun/nio/ch/SolarisEventPort.java | 244 +++ .../classes/sun/nio/ch/SourceChannelImpl.java | 4 +- ...ixAsynchronousServerSocketChannelImpl.java | 327 ++++ .../ch/UnixAsynchronousSocketChannelImpl.java | 673 +++++++ .../sun/nio/fs/DefaultFileSystemProvider.java | 73 + .../sun/nio/fs/DefaultFileTypeDetector.java | 36 + .../sun/nio/fs/GnomeFileTypeDetector.java | 101 ++ .../sun/nio/fs/LinuxDosFileAttributeView.java | 297 +++ .../classes/sun/nio/fs/LinuxFileStore.java | 152 ++ .../classes/sun/nio/fs/LinuxFileSystem.java | 175 ++ .../sun/nio/fs/LinuxFileSystemProvider.java | 41 + .../sun/nio/fs/LinuxNativeDispatcher.java | 126 ++ .../fs/LinuxUserDefinedFileAttributeView.java | 350 ++++ .../classes/sun/nio/fs/LinuxWatchService.java | 466 +++++ .../nio/fs/SolarisAclFileAttributeView.java | 408 +++++ .../classes/sun/nio/fs/SolarisFileStore.java | 103 ++ .../classes/sun/nio/fs/SolarisFileSystem.java | 164 ++ .../sun/nio/fs/SolarisFileSystemProvider.java | 41 + .../sun/nio/fs/SolarisNativeDispatcher.java | 56 + .../SolarisUserDefinedFileAttributeView.java | 293 +++ .../sun/nio/fs/SolarisWatchService.java | 770 ++++++++ .../sun/nio/fs/UnixChannelFactory.java | 286 +++ .../classes/sun/nio/fs/UnixCopyFile.java | 608 +++++++ .../sun/nio/fs/UnixDirectoryStream.java | 267 +++ .../classes/sun/nio/fs/UnixException.java | 113 ++ .../sun/nio/fs/UnixFileAttributeViews.java | 392 ++++ .../sun/nio/fs/UnixFileAttributes.java | 307 ++++ .../classes/sun/nio/fs/UnixFileKey.java | 56 + .../sun/nio/fs/UnixFileModeAttribute.java | 84 + .../classes/sun/nio/fs/UnixFileStore.java | 290 +++ .../sun/nio/fs/UnixFileStoreAttributes.java | 59 + .../classes/sun/nio/fs/UnixFileSystem.java | 380 ++++ .../sun/nio/fs/UnixFileSystemProvider.java | 139 ++ .../classes/sun/nio/fs/UnixMountEntry.java | 85 + .../sun/nio/fs/UnixNativeDispatcher.java | 556 ++++++ src/solaris/classes/sun/nio/fs/UnixPath.java | 1228 +++++++++++++ .../sun/nio/fs/UnixSecureDirectoryStream.java | 643 +++++++ .../classes/sun/nio/fs/UnixUriUtils.java | 216 +++ .../sun/nio/fs/UnixUserPrincipals.java | 175 ++ src/solaris/native/sun/nio/ch/EPoll.c | 151 ++ src/solaris/native/sun/nio/ch/EPollPort.c | 76 + .../native/sun/nio/ch/FileChannelImpl.c | 102 +- ...{FileDispatcher.c => FileDispatcherImpl.c} | 133 +- .../native/sun/nio/ch/SocketDispatcher.c | 3 +- .../native/sun/nio/ch/SolarisEventPort.c | 120 ++ .../UnixAsynchronousServerSocketChannelImpl.c | 47 + .../ch/UnixAsynchronousSocketChannelImpl.c | 53 + .../native/sun/nio/fs/GnomeFileTypeDetector.c | 205 +++ .../native/sun/nio/fs/LinuxNativeDispatcher.c | 160 ++ .../native/sun/nio/fs/LinuxWatchService.c | 195 ++ .../sun/nio/fs/SolarisNativeDispatcher.c | 61 + .../native/sun/nio/fs/SolarisWatchService.c | 104 ++ src/solaris/native/sun/nio/fs/UnixCopyFile.c | 85 + .../native/sun/nio/fs/UnixNativeDispatcher.c | 1080 +++++++++++ .../native/sun/nio/fs/genSolarisConstants.c | 105 ++ .../native/sun/nio/fs/genUnixConstants.c | 125 ++ .../DefaultAsynchronousChannelProvider.java | 43 + ...ispatcher.java => FileDispatcherImpl.java} | 46 +- src/windows/classes/sun/nio/ch/Iocp.java | 437 +++++ .../classes/sun/nio/ch/PendingIoCache.java | 161 ++ .../WindowsAsynchronousChannelProvider.java | 101 ++ .../WindowsAsynchronousFileChannelImpl.java | 741 ++++++++ ...wsAsynchronousServerSocketChannelImpl.java | 367 ++++ .../WindowsAsynchronousSocketChannelImpl.java | 911 ++++++++++ .../sun/nio/fs/DefaultFileSystemProvider.java | 38 + .../sun/nio/fs/DefaultFileTypeDetector.java | 36 + .../sun/nio/fs/RegistryFileTypeDetector.java | 82 + .../nio/fs/WindowsAclFileAttributeView.java | 226 +++ .../sun/nio/fs/WindowsChannelFactory.java | 341 ++++ .../classes/sun/nio/fs/WindowsConstants.java | 192 ++ .../sun/nio/fs/WindowsDirectoryStream.java | 232 +++ .../classes/sun/nio/fs/WindowsException.java | 109 ++ .../sun/nio/fs/WindowsFileAttributeViews.java | 296 +++ .../sun/nio/fs/WindowsFileAttributes.java | 403 +++++ .../classes/sun/nio/fs/WindowsFileCopy.java | 519 ++++++ .../classes/sun/nio/fs/WindowsFileStore.java | 337 ++++ .../classes/sun/nio/fs/WindowsFileSystem.java | 319 ++++ .../sun/nio/fs/WindowsFileSystemProvider.java | 146 ++ .../sun/nio/fs/WindowsLinkSupport.java | 446 +++++ .../sun/nio/fs/WindowsNativeDispatcher.java | 1133 ++++++++++++ .../classes/sun/nio/fs/WindowsPath.java | 1316 ++++++++++++++ .../classes/sun/nio/fs/WindowsPathParser.java | 225 +++ .../classes/sun/nio/fs/WindowsPathType.java | 38 + .../classes/sun/nio/fs/WindowsSecurity.java | 123 ++ .../sun/nio/fs/WindowsSecurityDescriptor.java | 392 ++++ .../classes/sun/nio/fs/WindowsUriSupport.java | 167 ++ .../WindowsUserDefinedFileAttributeView.java | 342 ++++ .../sun/nio/fs/WindowsUserPrincipals.java | 169 ++ .../sun/nio/fs/WindowsWatchService.java | 582 ++++++ .../native/sun/nio/ch/FileChannelImpl.c | 175 +- ...{FileDispatcher.c => FileDispatcherImpl.c} | 146 +- src/windows/native/sun/nio/ch/Iocp.c | 147 ++ .../ch/WindowsAsynchronousFileChannelImpl.c | 132 ++ ...ndowsAsynchronousServerSocketChannelImpl.c | 142 ++ .../ch/WindowsAsynchronousSocketChannelImpl.c | 222 +++ .../sun/nio/fs/RegistryFileTypeDetector.c | 62 + .../sun/nio/fs/WindowsNativeDispatcher.c | 1345 ++++++++++++++ .../AsynchronousChannelGroup/AsExecutor.java | 81 + .../AsynchronousChannelGroup/Attack.java | 63 + .../BadProperties.java | 41 + .../AsynchronousChannelGroup/Basic.java | 259 +++ .../AsynchronousChannelGroup/GroupOfOne.java | 140 ++ .../AsynchronousChannelGroup/Identity.java | 166 ++ .../PrivilegedThreadFactory.java | 50 + .../AsynchronousChannelGroup/Restart.java | 133 ++ .../AsynchronousChannelGroup/Unbounded.java | 120 ++ .../AsynchronousChannelGroup/run_any_task.sh | 52 + .../AsynchronousDatagramChannel/Basic.java | 448 +++++ .../AsynchronousFileChannel/Basic.java | 585 ++++++ .../CustomThreadPool.java | 67 + .../AsynchronousFileChannel/Lock.java | 340 ++++ .../MyThreadFactory.java | 49 + .../Basic.java | 136 ++ .../WithSecurityManager.java | 76 + .../java.policy.allow | 3 + .../java.policy.deny | 3 + .../AsynchronousSocketChannel/Basic.java | 805 ++++++++ .../AsynchronousSocketChannel/Leaky.java | 104 ++ test/java/nio/channels/Channels/Basic2.java | 172 ++ .../DatagramChannel/BasicMulticastTests.java | 14 +- .../DatagramChannel/SocketOptionTests.java | 4 +- .../SocketOptionTests.java | 4 +- .../SocketChannel/SocketOptionTests.java | 4 +- .../nio/channels/etc/NetworkChannelTests.java | 25 +- .../CheckProvider.java | 38 + ...o.channels.spi.AsynchronousChannelProvider | 1 + .../Provider1.java | 69 + .../Provider2.java | 69 + .../custom_provider.sh | 71 + test/java/nio/file/DirectoryStream/Basic.java | 168 ++ .../nio/file/DirectoryStream/Filters.java | 241 +++ .../nio/file/DirectoryStream/SecureDS.java | 370 ++++ test/java/nio/file/FileStore/Basic.java | 87 + test/java/nio/file/FileSystem/Basic.java | 82 + test/java/nio/file/Files/ContentType.java | 91 + test/java/nio/file/Files/CreateFileTree.java | 96 + test/java/nio/file/Files/ForceLoad.java | 38 + .../java.nio.file.spi.FileTypeDetector | 1 + test/java/nio/file/Files/Misc.java | 126 ++ test/java/nio/file/Files/PrintFileTree.java | 78 + .../file/Files/SimpleFileTypeDetector.java | 47 + test/java/nio/file/Files/SkipSiblings.java | 85 + test/java/nio/file/Files/TerminateWalk.java | 70 + test/java/nio/file/Files/content_type.sh | 71 + test/java/nio/file/Files/walk_file_tree.sh | 86 + test/java/nio/file/Path/CopyAndMove.java | 983 ++++++++++ test/java/nio/file/Path/DeleteOnClose.java | 77 + test/java/nio/file/Path/InterruptCopy.java | 119 ++ test/java/nio/file/Path/Links.java | 147 ++ test/java/nio/file/Path/Misc.java | 389 ++++ test/java/nio/file/Path/PathOps.java | 752 ++++++++ test/java/nio/file/Path/SBC.java | 468 +++++ test/java/nio/file/Path/TemporaryFiles.java | 76 + test/java/nio/file/Path/UriImportExport.java | 80 + test/java/nio/file/Path/delete_on_close.sh | 61 + test/java/nio/file/Path/temporary_files.sh | 65 + test/java/nio/file/PathMatcher/Basic.java | 174 ++ test/java/nio/file/TestUtil.java | 125 ++ test/java/nio/file/WatchService/Basic.java | 493 +++++ .../file/WatchService/FileTreeModifier.java | 148 ++ .../WatchService/SensitivityModifier.java | 122 ++ .../WatchService/WithSecurityManager.java | 83 + .../java/nio/file/WatchService/denyAll.policy | 3 + .../WatchService/grantDirAndOneLevel.policy | 5 + .../file/WatchService/grantDirAndTree.policy | 5 + .../nio/file/WatchService/grantDirOnly.policy | 4 + .../attribute/AclFileAttributeView/Basic.java | 166 ++ .../nio/file/attribute/Attributes/Basic.java | 254 +++ .../BasicFileAttributeView/Basic.java | 150 ++ .../attribute/DosFileAttributeView/Basic.java | 155 ++ .../FileStoreAttributeView/Basic.java | 171 ++ .../PosixFileAttributeView/Basic.java | 398 ++++ .../UserDefinedFileAttributeView/Basic.java | 273 +++ .../java/nio/file/spi/SetDefaultProvider.java | 44 + test/java/nio/file/spi/TestProvider.java | 128 ++ 364 files changed, 65088 insertions(+), 1153 deletions(-) create mode 100644 make/mksample/nio/file/Makefile create mode 100644 src/share/classes/com/sun/nio/file/ExtendedCopyOption.java create mode 100644 src/share/classes/com/sun/nio/file/ExtendedOpenOption.java create mode 100644 src/share/classes/com/sun/nio/file/ExtendedWatchEventModifier.java create mode 100644 src/share/classes/com/sun/nio/file/SensitivityWatchEventModifier.java create mode 100644 src/share/classes/java/nio/channels/AsynchronousByteChannel.java create mode 100644 src/share/classes/java/nio/channels/AsynchronousChannel.java create mode 100644 src/share/classes/java/nio/channels/AsynchronousChannelGroup.java create mode 100644 src/share/classes/java/nio/channels/AsynchronousDatagramChannel.java create mode 100644 src/share/classes/java/nio/channels/AsynchronousFileChannel.java create mode 100644 src/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java create mode 100644 src/share/classes/java/nio/channels/AsynchronousSocketChannel.java create mode 100644 src/share/classes/java/nio/channels/CompletionHandler.java create mode 100644 src/share/classes/java/nio/channels/SeekableByteChannel.java create mode 100644 src/share/classes/java/nio/channels/spi/AsynchronousChannelProvider.java create mode 100644 src/share/classes/java/nio/file/AccessDeniedException.java create mode 100644 src/share/classes/java/nio/file/AccessMode.java create mode 100644 src/share/classes/java/nio/file/AtomicMoveNotSupportedException.java create mode 100644 src/share/classes/java/nio/file/ClosedDirectoryStreamException.java create mode 100644 src/share/classes/java/nio/file/ClosedFileSystemException.java create mode 100644 src/share/classes/java/nio/file/ClosedWatchServiceException.java create mode 100644 src/share/classes/java/nio/file/CopyOption.java create mode 100644 src/share/classes/java/nio/file/DirectoryNotEmptyException.java create mode 100644 src/share/classes/java/nio/file/DirectoryStream.java create mode 100644 src/share/classes/java/nio/file/DirectoryStreamFilters.java create mode 100644 src/share/classes/java/nio/file/FileAction.java create mode 100644 src/share/classes/java/nio/file/FileAlreadyExistsException.java create mode 100644 src/share/classes/java/nio/file/FileRef.java create mode 100644 src/share/classes/java/nio/file/FileStore.java create mode 100644 src/share/classes/java/nio/file/FileSystem.java create mode 100644 src/share/classes/java/nio/file/FileSystemAlreadyExistsException.java create mode 100644 src/share/classes/java/nio/file/FileSystemException.java create mode 100644 src/share/classes/java/nio/file/FileSystemNotFoundException.java create mode 100644 src/share/classes/java/nio/file/FileSystems.java create mode 100644 src/share/classes/java/nio/file/FileTreeWalker.java create mode 100644 src/share/classes/java/nio/file/FileVisitOption.java create mode 100644 src/share/classes/java/nio/file/FileVisitResult.java create mode 100644 src/share/classes/java/nio/file/FileVisitor.java create mode 100644 src/share/classes/java/nio/file/Files.java create mode 100644 src/share/classes/java/nio/file/InvalidPathException.java create mode 100644 src/share/classes/java/nio/file/LinkOption.java create mode 100644 src/share/classes/java/nio/file/LinkPermission.java create mode 100644 src/share/classes/java/nio/file/NoSuchFileException.java create mode 100644 src/share/classes/java/nio/file/NotDirectoryException.java create mode 100644 src/share/classes/java/nio/file/NotLinkException.java create mode 100644 src/share/classes/java/nio/file/OpenOption.java create mode 100644 src/share/classes/java/nio/file/Path.java create mode 100644 src/share/classes/java/nio/file/PathMatcher.java create mode 100644 src/share/classes/java/nio/file/Paths.java create mode 100644 src/share/classes/java/nio/file/ProviderMismatchException.java create mode 100644 src/share/classes/java/nio/file/ProviderNotFoundException.java create mode 100644 src/share/classes/java/nio/file/ReadOnlyFileSystemException.java create mode 100644 src/share/classes/java/nio/file/SecureDirectoryStream.java create mode 100644 src/share/classes/java/nio/file/SimpleFileVisitor.java create mode 100644 src/share/classes/java/nio/file/StandardCopyOption.java create mode 100644 src/share/classes/java/nio/file/StandardOpenOption.java create mode 100644 src/share/classes/java/nio/file/StandardWatchEventKind.java create mode 100644 src/share/classes/java/nio/file/WatchEvent.java create mode 100644 src/share/classes/java/nio/file/WatchKey.java create mode 100644 src/share/classes/java/nio/file/WatchService.java create mode 100644 src/share/classes/java/nio/file/Watchable.java create mode 100644 src/share/classes/java/nio/file/attribute/AclEntry.java create mode 100644 src/share/classes/java/nio/file/attribute/AclEntryFlag.java create mode 100644 src/share/classes/java/nio/file/attribute/AclEntryPermission.java create mode 100644 src/share/classes/java/nio/file/attribute/AclEntryType.java create mode 100644 src/share/classes/java/nio/file/attribute/AclFileAttributeView.java create mode 100644 src/share/classes/java/nio/file/attribute/AttributeView.java create mode 100644 src/share/classes/java/nio/file/attribute/Attributes.java create mode 100644 src/share/classes/java/nio/file/attribute/BasicFileAttributeView.java create mode 100644 src/share/classes/java/nio/file/attribute/BasicFileAttributes.java create mode 100644 src/share/classes/java/nio/file/attribute/DosFileAttributeView.java create mode 100644 src/share/classes/java/nio/file/attribute/DosFileAttributes.java create mode 100644 src/share/classes/java/nio/file/attribute/FileAttribute.java create mode 100644 src/share/classes/java/nio/file/attribute/FileAttributeView.java create mode 100644 src/share/classes/java/nio/file/attribute/FileOwnerAttributeView.java create mode 100644 src/share/classes/java/nio/file/attribute/FileStoreAttributeView.java create mode 100644 src/share/classes/java/nio/file/attribute/FileStoreSpaceAttributeView.java create mode 100644 src/share/classes/java/nio/file/attribute/FileStoreSpaceAttributes.java create mode 100644 src/share/classes/java/nio/file/attribute/GroupPrincipal.java create mode 100644 src/share/classes/java/nio/file/attribute/PosixFileAttributeView.java create mode 100644 src/share/classes/java/nio/file/attribute/PosixFileAttributes.java create mode 100644 src/share/classes/java/nio/file/attribute/PosixFilePermission.java create mode 100644 src/share/classes/java/nio/file/attribute/PosixFilePermissions.java create mode 100644 src/share/classes/java/nio/file/attribute/UserDefinedFileAttributeView.java create mode 100644 src/share/classes/java/nio/file/attribute/UserPrincipal.java create mode 100644 src/share/classes/java/nio/file/attribute/UserPrincipalLookupService.java create mode 100644 src/share/classes/java/nio/file/attribute/UserPrincipalNotFoundException.java create mode 100644 src/share/classes/java/nio/file/attribute/package-info.java create mode 100644 src/share/classes/java/nio/file/package-info.java create mode 100644 src/share/classes/java/nio/file/spi/AbstractPath.java create mode 100644 src/share/classes/java/nio/file/spi/FileSystemProvider.java create mode 100644 src/share/classes/java/nio/file/spi/FileTypeDetector.java create mode 100644 src/share/classes/java/nio/file/spi/package-info.java create mode 100644 src/share/classes/sun/nio/ch/AbstractFuture.java create mode 100644 src/share/classes/sun/nio/ch/AsynchronousChannelGroupImpl.java create mode 100644 src/share/classes/sun/nio/ch/AsynchronousFileChannelImpl.java create mode 100644 src/share/classes/sun/nio/ch/AsynchronousServerSocketChannelImpl.java create mode 100644 src/share/classes/sun/nio/ch/AsynchronousSocketChannelImpl.java create mode 100644 src/share/classes/sun/nio/ch/Cancellable.java create mode 100644 src/share/classes/sun/nio/ch/CompletedFuture.java create mode 100644 src/share/classes/sun/nio/ch/FileDispatcher.java create mode 100644 src/share/classes/sun/nio/ch/FileLockTable.java create mode 100644 src/share/classes/sun/nio/ch/Groupable.java create mode 100644 src/share/classes/sun/nio/ch/Invoker.java create mode 100644 src/share/classes/sun/nio/ch/PendingFuture.java create mode 100644 src/share/classes/sun/nio/ch/SimpleAsynchronousDatagramChannelImpl.java create mode 100644 src/share/classes/sun/nio/ch/SimpleAsynchronousFileChannelImpl.java create mode 100644 src/share/classes/sun/nio/ch/ThreadPool.java create mode 100644 src/share/classes/sun/nio/fs/AbstractAclFileAttributeView.java create mode 100644 src/share/classes/sun/nio/fs/AbstractBasicFileAttributeView.java create mode 100644 src/share/classes/sun/nio/fs/AbstractFileStoreSpaceAttributeView.java create mode 100644 src/share/classes/sun/nio/fs/AbstractFileTypeDetector.java create mode 100644 src/share/classes/sun/nio/fs/AbstractPoller.java create mode 100644 src/share/classes/sun/nio/fs/AbstractUserDefinedFileAttributeView.java create mode 100644 src/share/classes/sun/nio/fs/AbstractWatchKey.java create mode 100644 src/share/classes/sun/nio/fs/AbstractWatchService.java create mode 100644 src/share/classes/sun/nio/fs/Cancellable.java create mode 100644 src/share/classes/sun/nio/fs/FileOwnerAttributeViewImpl.java create mode 100644 src/share/classes/sun/nio/fs/Globs.java create mode 100644 src/share/classes/sun/nio/fs/MimeType.java create mode 100644 src/share/classes/sun/nio/fs/NativeBuffer.java create mode 100644 src/share/classes/sun/nio/fs/NativeBuffers.java create mode 100644 src/share/classes/sun/nio/fs/PollingWatchService.java create mode 100644 src/share/classes/sun/nio/fs/Reflect.java create mode 100644 src/share/sample/nio/file/AclEdit.java create mode 100644 src/share/sample/nio/file/Chmod.java create mode 100644 src/share/sample/nio/file/Copy.java create mode 100644 src/share/sample/nio/file/DiskUsage.java create mode 100644 src/share/sample/nio/file/FileType.java create mode 100644 src/share/sample/nio/file/WatchDir.java create mode 100644 src/share/sample/nio/file/Xdd.java create mode 100644 src/solaris/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java create mode 100644 src/solaris/classes/sun/nio/ch/EPoll.java create mode 100644 src/solaris/classes/sun/nio/ch/EPollPort.java rename src/solaris/classes/sun/nio/ch/{FileDispatcher.java => FileDispatcherImpl.java} (72%) create mode 100644 src/solaris/classes/sun/nio/ch/LinuxAsynchronousChannelProvider.java create mode 100644 src/solaris/classes/sun/nio/ch/Port.java create mode 100644 src/solaris/classes/sun/nio/ch/SolarisAsynchronousChannelProvider.java create mode 100644 src/solaris/classes/sun/nio/ch/SolarisEventPort.java create mode 100644 src/solaris/classes/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java create mode 100644 src/solaris/classes/sun/nio/ch/UnixAsynchronousSocketChannelImpl.java create mode 100644 src/solaris/classes/sun/nio/fs/DefaultFileSystemProvider.java create mode 100644 src/solaris/classes/sun/nio/fs/DefaultFileTypeDetector.java create mode 100644 src/solaris/classes/sun/nio/fs/GnomeFileTypeDetector.java create mode 100644 src/solaris/classes/sun/nio/fs/LinuxDosFileAttributeView.java create mode 100644 src/solaris/classes/sun/nio/fs/LinuxFileStore.java create mode 100644 src/solaris/classes/sun/nio/fs/LinuxFileSystem.java create mode 100644 src/solaris/classes/sun/nio/fs/LinuxFileSystemProvider.java create mode 100644 src/solaris/classes/sun/nio/fs/LinuxNativeDispatcher.java create mode 100644 src/solaris/classes/sun/nio/fs/LinuxUserDefinedFileAttributeView.java create mode 100644 src/solaris/classes/sun/nio/fs/LinuxWatchService.java create mode 100644 src/solaris/classes/sun/nio/fs/SolarisAclFileAttributeView.java create mode 100644 src/solaris/classes/sun/nio/fs/SolarisFileStore.java create mode 100644 src/solaris/classes/sun/nio/fs/SolarisFileSystem.java create mode 100644 src/solaris/classes/sun/nio/fs/SolarisFileSystemProvider.java create mode 100644 src/solaris/classes/sun/nio/fs/SolarisNativeDispatcher.java create mode 100644 src/solaris/classes/sun/nio/fs/SolarisUserDefinedFileAttributeView.java create mode 100644 src/solaris/classes/sun/nio/fs/SolarisWatchService.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixChannelFactory.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixCopyFile.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixDirectoryStream.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixException.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixFileAttributeViews.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixFileAttributes.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixFileKey.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixFileModeAttribute.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixFileStore.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixFileStoreAttributes.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixFileSystem.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixFileSystemProvider.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixMountEntry.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixNativeDispatcher.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixPath.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixSecureDirectoryStream.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixUriUtils.java create mode 100644 src/solaris/classes/sun/nio/fs/UnixUserPrincipals.java create mode 100644 src/solaris/native/sun/nio/ch/EPoll.c create mode 100644 src/solaris/native/sun/nio/ch/EPollPort.c rename src/solaris/native/sun/nio/ch/{FileDispatcher.c => FileDispatcherImpl.c} (50%) create mode 100644 src/solaris/native/sun/nio/ch/SolarisEventPort.c create mode 100644 src/solaris/native/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.c create mode 100644 src/solaris/native/sun/nio/ch/UnixAsynchronousSocketChannelImpl.c create mode 100644 src/solaris/native/sun/nio/fs/GnomeFileTypeDetector.c create mode 100644 src/solaris/native/sun/nio/fs/LinuxNativeDispatcher.c create mode 100644 src/solaris/native/sun/nio/fs/LinuxWatchService.c create mode 100644 src/solaris/native/sun/nio/fs/SolarisNativeDispatcher.c create mode 100644 src/solaris/native/sun/nio/fs/SolarisWatchService.c create mode 100644 src/solaris/native/sun/nio/fs/UnixCopyFile.c create mode 100644 src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c create mode 100644 src/solaris/native/sun/nio/fs/genSolarisConstants.c create mode 100644 src/solaris/native/sun/nio/fs/genUnixConstants.c create mode 100644 src/windows/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java rename src/windows/classes/sun/nio/ch/{FileDispatcher.java => FileDispatcherImpl.java} (71%) create mode 100644 src/windows/classes/sun/nio/ch/Iocp.java create mode 100644 src/windows/classes/sun/nio/ch/PendingIoCache.java create mode 100644 src/windows/classes/sun/nio/ch/WindowsAsynchronousChannelProvider.java create mode 100644 src/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java create mode 100644 src/windows/classes/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java create mode 100644 src/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java create mode 100644 src/windows/classes/sun/nio/fs/DefaultFileSystemProvider.java create mode 100644 src/windows/classes/sun/nio/fs/DefaultFileTypeDetector.java create mode 100644 src/windows/classes/sun/nio/fs/RegistryFileTypeDetector.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsAclFileAttributeView.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsChannelFactory.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsConstants.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsDirectoryStream.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsException.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsFileAttributes.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsFileCopy.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsFileStore.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsFileSystem.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsFileSystemProvider.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsLinkSupport.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsPath.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsPathParser.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsPathType.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsSecurity.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsSecurityDescriptor.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsUriSupport.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsUserDefinedFileAttributeView.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsUserPrincipals.java create mode 100644 src/windows/classes/sun/nio/fs/WindowsWatchService.java rename src/windows/native/sun/nio/ch/{FileDispatcher.c => FileDispatcherImpl.c} (67%) create mode 100644 src/windows/native/sun/nio/ch/Iocp.c create mode 100644 src/windows/native/sun/nio/ch/WindowsAsynchronousFileChannelImpl.c create mode 100644 src/windows/native/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.c create mode 100644 src/windows/native/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.c create mode 100644 src/windows/native/sun/nio/fs/RegistryFileTypeDetector.c create mode 100644 src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c create mode 100644 test/java/nio/channels/AsynchronousChannelGroup/AsExecutor.java create mode 100644 test/java/nio/channels/AsynchronousChannelGroup/Attack.java create mode 100644 test/java/nio/channels/AsynchronousChannelGroup/BadProperties.java create mode 100644 test/java/nio/channels/AsynchronousChannelGroup/Basic.java create mode 100644 test/java/nio/channels/AsynchronousChannelGroup/GroupOfOne.java create mode 100644 test/java/nio/channels/AsynchronousChannelGroup/Identity.java create mode 100644 test/java/nio/channels/AsynchronousChannelGroup/PrivilegedThreadFactory.java create mode 100644 test/java/nio/channels/AsynchronousChannelGroup/Restart.java create mode 100644 test/java/nio/channels/AsynchronousChannelGroup/Unbounded.java create mode 100644 test/java/nio/channels/AsynchronousChannelGroup/run_any_task.sh create mode 100644 test/java/nio/channels/AsynchronousDatagramChannel/Basic.java create mode 100644 test/java/nio/channels/AsynchronousFileChannel/Basic.java create mode 100644 test/java/nio/channels/AsynchronousFileChannel/CustomThreadPool.java create mode 100644 test/java/nio/channels/AsynchronousFileChannel/Lock.java create mode 100644 test/java/nio/channels/AsynchronousFileChannel/MyThreadFactory.java create mode 100644 test/java/nio/channels/AsynchronousServerSocketChannel/Basic.java create mode 100644 test/java/nio/channels/AsynchronousServerSocketChannel/WithSecurityManager.java create mode 100644 test/java/nio/channels/AsynchronousServerSocketChannel/java.policy.allow create mode 100644 test/java/nio/channels/AsynchronousServerSocketChannel/java.policy.deny create mode 100644 test/java/nio/channels/AsynchronousSocketChannel/Basic.java create mode 100644 test/java/nio/channels/AsynchronousSocketChannel/Leaky.java create mode 100644 test/java/nio/channels/Channels/Basic2.java create mode 100644 test/java/nio/channels/spi/AsynchronousChannelProvider/CheckProvider.java create mode 100644 test/java/nio/channels/spi/AsynchronousChannelProvider/META-INF/services/java.nio.channels.spi.AsynchronousChannelProvider create mode 100644 test/java/nio/channels/spi/AsynchronousChannelProvider/Provider1.java create mode 100644 test/java/nio/channels/spi/AsynchronousChannelProvider/Provider2.java create mode 100644 test/java/nio/channels/spi/AsynchronousChannelProvider/custom_provider.sh create mode 100644 test/java/nio/file/DirectoryStream/Basic.java create mode 100644 test/java/nio/file/DirectoryStream/Filters.java create mode 100644 test/java/nio/file/DirectoryStream/SecureDS.java create mode 100644 test/java/nio/file/FileStore/Basic.java create mode 100644 test/java/nio/file/FileSystem/Basic.java create mode 100644 test/java/nio/file/Files/ContentType.java create mode 100644 test/java/nio/file/Files/CreateFileTree.java create mode 100644 test/java/nio/file/Files/ForceLoad.java create mode 100644 test/java/nio/file/Files/META-INF/services/java.nio.file.spi.FileTypeDetector create mode 100644 test/java/nio/file/Files/Misc.java create mode 100644 test/java/nio/file/Files/PrintFileTree.java create mode 100644 test/java/nio/file/Files/SimpleFileTypeDetector.java create mode 100644 test/java/nio/file/Files/SkipSiblings.java create mode 100644 test/java/nio/file/Files/TerminateWalk.java create mode 100644 test/java/nio/file/Files/content_type.sh create mode 100644 test/java/nio/file/Files/walk_file_tree.sh create mode 100644 test/java/nio/file/Path/CopyAndMove.java create mode 100644 test/java/nio/file/Path/DeleteOnClose.java create mode 100644 test/java/nio/file/Path/InterruptCopy.java create mode 100644 test/java/nio/file/Path/Links.java create mode 100644 test/java/nio/file/Path/Misc.java create mode 100644 test/java/nio/file/Path/PathOps.java create mode 100644 test/java/nio/file/Path/SBC.java create mode 100644 test/java/nio/file/Path/TemporaryFiles.java create mode 100644 test/java/nio/file/Path/UriImportExport.java create mode 100644 test/java/nio/file/Path/delete_on_close.sh create mode 100644 test/java/nio/file/Path/temporary_files.sh create mode 100644 test/java/nio/file/PathMatcher/Basic.java create mode 100644 test/java/nio/file/TestUtil.java create mode 100644 test/java/nio/file/WatchService/Basic.java create mode 100644 test/java/nio/file/WatchService/FileTreeModifier.java create mode 100644 test/java/nio/file/WatchService/SensitivityModifier.java create mode 100644 test/java/nio/file/WatchService/WithSecurityManager.java create mode 100644 test/java/nio/file/WatchService/denyAll.policy create mode 100644 test/java/nio/file/WatchService/grantDirAndOneLevel.policy create mode 100644 test/java/nio/file/WatchService/grantDirAndTree.policy create mode 100644 test/java/nio/file/WatchService/grantDirOnly.policy create mode 100644 test/java/nio/file/attribute/AclFileAttributeView/Basic.java create mode 100644 test/java/nio/file/attribute/Attributes/Basic.java create mode 100644 test/java/nio/file/attribute/BasicFileAttributeView/Basic.java create mode 100644 test/java/nio/file/attribute/DosFileAttributeView/Basic.java create mode 100644 test/java/nio/file/attribute/FileStoreAttributeView/Basic.java create mode 100644 test/java/nio/file/attribute/PosixFileAttributeView/Basic.java create mode 100644 test/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java create mode 100644 test/java/nio/file/spi/SetDefaultProvider.java create mode 100644 test/java/nio/file/spi/TestProvider.java diff --git a/make/docs/CORE_PKGS.gmk b/make/docs/CORE_PKGS.gmk index 4a41a2005..f9b9ee59c 100644 --- a/make/docs/CORE_PKGS.gmk +++ b/make/docs/CORE_PKGS.gmk @@ -1,5 +1,5 @@ # -# Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2001-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 @@ -110,6 +110,9 @@ CORE_PKGS = \ java.nio.channels.spi \ java.nio.charset \ java.nio.charset.spi \ + java.nio.file \ + java.nio.file.attribute \ + java.nio.file.spi \ java.rmi \ java.rmi.activation \ java.rmi.dgc \ diff --git a/make/docs/NON_CORE_PKGS.gmk b/make/docs/NON_CORE_PKGS.gmk index ccf7a0f79..6028a85da 100644 --- a/make/docs/NON_CORE_PKGS.gmk +++ b/make/docs/NON_CORE_PKGS.gmk @@ -1,5 +1,5 @@ # -# Copyright 2002-2008 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2002-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 @@ -65,6 +65,8 @@ OLD_JSSE_PKGS = com.sun.net.ssl HTTPSERVER_PKGS = com.sun.net.httpserver \ com.sun.net.httpserver.spi +NIO_PKGS = com.sun.nio.file + DOCLETAPI_PKGS = com.sun.javadoc TAGLETAPI_FILE = com/sun/tools/doclets/Taglet.java @@ -92,6 +94,7 @@ NON_CORE_PKGS = $(DOMAPI_PKGS) \ $(MGMT_PKGS) \ $(JAAS_PKGS) \ $(JGSS_PKGS) \ + $(NIO_PKGS) \ $(OLD_JSSE_PKGS) \ $(HTTPSERVER_PKGS) \ $(SMARTCARDIO_PKGS) \ diff --git a/make/java/nio/Exportedfiles.gmk b/make/java/nio/Exportedfiles.gmk index 49c4e24ac..fd8b73f64 100644 --- a/make/java/nio/Exportedfiles.gmk +++ b/make/java/nio/Exportedfiles.gmk @@ -1,5 +1,5 @@ # -# Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2000-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 @@ -33,7 +33,7 @@ FILES_export = \ sun/nio/ch/DatagramChannelImpl.java \ sun/nio/ch/DatagramDispatcher.java \ sun/nio/ch/FileChannelImpl.java \ - sun/nio/ch/FileDispatcher.java \ + sun/nio/ch/FileDispatcherImpl.java \ sun/nio/ch/FileKey.java \ sun/nio/ch/FileLockImpl.java \ sun/nio/ch/IOStatus.java \ diff --git a/make/java/nio/FILES_c.gmk b/make/java/nio/FILES_c.gmk index 6f7c3ff3f..b1670c20d 100644 --- a/make/java/nio/FILES_c.gmk +++ b/make/java/nio/FILES_c.gmk @@ -1,5 +1,5 @@ # -# Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2000-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 @@ -27,7 +27,7 @@ FILES_c = \ DatagramChannelImpl.c \ DatagramDispatcher.c \ FileChannelImpl.c \ - FileDispatcher.c \ + FileDispatcherImpl.c \ FileKey.c \ IOUtil.c \ MappedByteBuffer.c \ diff --git a/make/java/nio/FILES_java.gmk b/make/java/nio/FILES_java.gmk index 29f1f8f42..01bfbfcf4 100644 --- a/make/java/nio/FILES_java.gmk +++ b/make/java/nio/FILES_java.gmk @@ -1,5 +1,5 @@ # -# Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2000-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 @@ -31,19 +31,29 @@ FILES_src = \ java/nio/MappedByteBuffer.java \ java/nio/StringCharBuffer.java \ \ + java/nio/channels/AsynchronousByteChannel.java \ + java/nio/channels/AsynchronousChannel.java \ + java/nio/channels/AsynchronousChannelGroup.java \ + java/nio/channels/AsynchronousDatagramChannel.java \ + java/nio/channels/AsynchronousFileChannel.java \ + java/nio/channels/AsynchronousServerSocketChannel.java \ + java/nio/channels/AsynchronousSocketChannel.java \ java/nio/channels/ByteChannel.java \ java/nio/channels/Channel.java \ java/nio/channels/Channels.java \ + java/nio/channels/CompletionHandler.java \ java/nio/channels/DatagramChannel.java \ java/nio/channels/FileChannel.java \ java/nio/channels/FileLock.java \ java/nio/channels/GatheringByteChannel.java \ java/nio/channels/InterruptibleChannel.java \ + java/nio/channels/Pipe.java \ java/nio/channels/MembershipKey.java \ java/nio/channels/MulticastChannel.java \ java/nio/channels/NetworkChannel.java \ java/nio/channels/ReadableByteChannel.java \ java/nio/channels/ScatteringByteChannel.java \ + java/nio/channels/SeekableByteChannel.java \ java/nio/channels/SelectableChannel.java \ java/nio/channels/Selector.java \ java/nio/channels/SelectionKey.java \ @@ -55,6 +65,7 @@ FILES_src = \ java/nio/channels/spi/AbstractSelectableChannel.java \ java/nio/channels/spi/AbstractSelectionKey.java \ java/nio/channels/spi/AbstractSelector.java \ + java/nio/channels/spi/AsynchronousChannelProvider.java \ java/nio/channels/spi/SelectorProvider.java \ \ java/nio/charset/Charset.java \ @@ -66,21 +77,117 @@ FILES_src = \ \ java/nio/charset/spi/CharsetProvider.java \ \ + java/nio/file/AccessDeniedException.java \ + java/nio/file/AccessMode.java \ + java/nio/file/AtomicMoveNotSupportedException.java \ + java/nio/file/ClosedDirectoryStreamException.java \ + java/nio/file/ClosedFileSystemException.java \ + java/nio/file/ClosedWatchServiceException.java \ + java/nio/file/CopyOption.java \ + java/nio/file/DirectoryNotEmptyException.java \ + java/nio/file/DirectoryStream.java \ + java/nio/file/DirectoryStreamFilters.java \ + java/nio/file/FileAction.java \ + java/nio/file/FileAlreadyExistsException.java \ + java/nio/file/FileRef.java \ + java/nio/file/FileStore.java \ + java/nio/file/FileSystem.java \ + java/nio/file/FileSystemAlreadyExistsException.java \ + java/nio/file/FileSystemException.java \ + java/nio/file/FileSystemNotFoundException.java \ + java/nio/file/FileSystems.java \ + java/nio/file/FileTreeWalker.java \ + java/nio/file/FileVisitOption.java \ + java/nio/file/FileVisitResult.java \ + java/nio/file/FileVisitor.java \ + java/nio/file/Files.java \ + java/nio/file/InvalidPathException.java \ + java/nio/file/LinkOption.java \ + java/nio/file/LinkPermission.java \ + java/nio/file/NoSuchFileException.java \ + java/nio/file/NotDirectoryException.java \ + java/nio/file/NotLinkException.java \ + java/nio/file/OpenOption.java \ + java/nio/file/Path.java \ + java/nio/file/PathMatcher.java \ + java/nio/file/Paths.java \ + java/nio/file/ProviderMismatchException.java \ + java/nio/file/ProviderNotFoundException.java \ + java/nio/file/ReadOnlyFileSystemException.java \ + java/nio/file/SecureDirectoryStream.java \ + java/nio/file/SimpleFileVisitor.java \ + java/nio/file/StandardCopyOption.java \ + java/nio/file/StandardOpenOption.java \ + java/nio/file/StandardWatchEventKind.java \ + java/nio/file/WatchEvent.java \ + java/nio/file/WatchKey.java \ + java/nio/file/WatchService.java \ + java/nio/file/Watchable.java \ + \ + java/nio/file/attribute/AclEntry.java \ + java/nio/file/attribute/AclEntryFlag.java \ + java/nio/file/attribute/AclEntryPermission.java \ + java/nio/file/attribute/AclEntryType.java \ + java/nio/file/attribute/AclFileAttributeView.java \ + java/nio/file/attribute/AttributeView.java \ + java/nio/file/attribute/Attributes.java \ + java/nio/file/attribute/BasicFileAttributeView.java \ + java/nio/file/attribute/BasicFileAttributes.java \ + java/nio/file/attribute/DosFileAttributeView.java \ + java/nio/file/attribute/DosFileAttributes.java \ + java/nio/file/attribute/FileAttribute.java \ + java/nio/file/attribute/FileAttributeView.java \ + java/nio/file/attribute/FileOwnerAttributeView.java \ + java/nio/file/attribute/FileStoreAttributeView.java \ + java/nio/file/attribute/FileStoreSpaceAttributeView.java \ + java/nio/file/attribute/FileStoreSpaceAttributes.java \ + java/nio/file/attribute/GroupPrincipal.java \ + java/nio/file/attribute/UserDefinedFileAttributeView.java \ + java/nio/file/attribute/PosixFileAttributeView.java \ + java/nio/file/attribute/PosixFileAttributes.java \ + java/nio/file/attribute/PosixFilePermission.java \ + java/nio/file/attribute/PosixFilePermissions.java \ + java/nio/file/attribute/UserPrincipal.java \ + java/nio/file/attribute/UserPrincipalLookupService.java \ + java/nio/file/attribute/UserPrincipalNotFoundException.java \ + \ + java/nio/file/spi/AbstractPath.java \ + java/nio/file/spi/FileSystemProvider.java \ + java/nio/file/spi/FileTypeDetector.java \ + \ + com/sun/nio/file/ExtendedCopyOption.java \ + com/sun/nio/file/ExtendedOpenOption.java \ + com/sun/nio/file/ExtendedWatchEventModifier.java \ + com/sun/nio/file/SensitivityWatchEventModifier.java \ + \ sun/nio/ByteBuffered.java \ \ + sun/nio/ch/AbstractFuture.java \ sun/nio/ch/AbstractPollArrayWrapper.java \ sun/nio/ch/AllocatedNativeObject.java \ + sun/nio/ch/AsynchronousChannelGroupImpl.java \ + sun/nio/ch/AsynchronousFileChannelImpl.java \ + sun/nio/ch/AsynchronousServerSocketChannelImpl.java \ + sun/nio/ch/AsynchronousSocketChannelImpl.java \ + sun/nio/ch/Cancellable.java \ sun/nio/ch/ChannelInputStream.java \ + sun/nio/ch/CompletedFuture.java \ sun/nio/ch/DatagramChannelImpl.java \ sun/nio/ch/DatagramDispatcher.java \ sun/nio/ch/DatagramSocketAdaptor.java \ + sun/nio/ch/DefaultAsynchronousChannelProvider.java \ sun/nio/ch/DefaultSelectorProvider.java \ sun/nio/ch/DirectBuffer.java \ sun/nio/ch/ExtendedSocketOption.java \ sun/nio/ch/FileChannelImpl.java \ sun/nio/ch/FileDispatcher.java \ + sun/nio/ch/FileDispatcherImpl.java \ sun/nio/ch/FileKey.java \ + sun/nio/ch/FileLockImpl.java \ + sun/nio/ch/FileLockTable.java \ + sun/nio/ch/Groupable.java \ sun/nio/ch/Interruptible.java \ + sun/nio/ch/Invoker.java \ sun/nio/ch/IOUtil.java \ sun/nio/ch/IOStatus.java \ sun/nio/ch/IOVecWrapper.java \ @@ -92,6 +199,7 @@ FILES_src = \ sun/nio/ch/NativeThreadSet.java \ sun/nio/ch/Net.java \ sun/nio/ch/OptionKey.java \ + sun/nio/ch/PendingFuture.java \ sun/nio/ch/PipeImpl.java \ sun/nio/ch/PollArrayWrapper.java \ sun/nio/ch/Reflect.java \ @@ -101,12 +209,14 @@ FILES_src = \ sun/nio/ch/SelChImpl.java \ sun/nio/ch/ServerSocketAdaptor.java \ sun/nio/ch/ServerSocketChannelImpl.java \ + sun/nio/ch/SimpleAsynchronousDatagramChannelImpl.java \ sun/nio/ch/SinkChannelImpl.java \ sun/nio/ch/SocketAdaptor.java \ sun/nio/ch/SocketChannelImpl.java \ sun/nio/ch/SocketDispatcher.java \ sun/nio/ch/SocketOptionRegistry.java \ sun/nio/ch/SourceChannelImpl.java \ + sun/nio/ch/ThreadPool.java \ sun/nio/ch/Util.java \ \ sun/nio/cs/AbstractCharsetProvider.java \ @@ -134,6 +244,24 @@ FILES_src = \ sun/nio/cs/UTF_32LE_BOM.java \ sun/nio/cs/UTF_32Coder.java \ \ + sun/nio/fs/AbstractAclFileAttributeView.java \ + sun/nio/fs/AbstractBasicFileAttributeView.java \ + sun/nio/fs/AbstractFileStoreSpaceAttributeView.java \ + sun/nio/fs/AbstractFileTypeDetector.java \ + sun/nio/fs/AbstractPoller.java \ + sun/nio/fs/AbstractUserDefinedFileAttributeView.java \ + sun/nio/fs/AbstractWatchKey.java \ + sun/nio/fs/AbstractWatchService.java \ + sun/nio/fs/Cancellable.java \ + sun/nio/fs/DefaultFileSystemProvider.java \ + sun/nio/fs/DefaultFileTypeDetector.java \ + sun/nio/fs/FileOwnerAttributeViewImpl.java \ + sun/nio/fs/Globs.java \ + sun/nio/fs/MimeType.java \ + sun/nio/fs/NativeBuffer.java \ + sun/nio/fs/NativeBuffers.java \ + sun/nio/fs/Reflect.java \ + \ java/net/DatagramSocket.java \ java/net/DatagramSocketImpl.java \ java/net/PlainSocketImpl.java \ @@ -244,24 +372,31 @@ FILES_gen_ex = \ java/nio/InvalidMarkException.java \ java/nio/ReadOnlyBufferException.java \ \ + java/nio/channels/AcceptPendingException.java \ java/nio/channels/AlreadyBoundException.java \ java/nio/channels/AlreadyConnectedException.java \ java/nio/channels/AsynchronousCloseException.java \ + java/nio/channels/CancelledKeyException.java \ java/nio/channels/ClosedByInterruptException.java \ java/nio/channels/ClosedChannelException.java \ java/nio/channels/ClosedSelectorException.java \ java/nio/channels/ConnectionPendingException.java \ java/nio/channels/FileLockInterruptionException.java \ java/nio/channels/IllegalBlockingModeException.java \ + java/nio/channels/IllegalChannelGroupException.java \ java/nio/channels/IllegalSelectorException.java \ + java/nio/channels/InterruptedByTimeoutException.java \ java/nio/channels/NoConnectionPendingException.java \ java/nio/channels/NonReadableChannelException.java \ java/nio/channels/NonWritableChannelException.java \ java/nio/channels/NotYetBoundException.java \ java/nio/channels/NotYetConnectedException.java \ java/nio/channels/OverlappingFileLockException.java \ + java/nio/channels/ReadPendingException.java \ + java/nio/channels/ShutdownChannelGroupException.java \ java/nio/channels/UnresolvedAddressException.java \ java/nio/channels/UnsupportedAddressTypeException.java \ + java/nio/channels/WritePendingException.java \ \ java/nio/charset/CharacterCodingException.java \ java/nio/charset/IllegalCharsetNameException.java \ diff --git a/make/java/nio/Makefile b/make/java/nio/Makefile index bf7bc2e02..f85cc69e5 100644 --- a/make/java/nio/Makefile +++ b/make/java/nio/Makefile @@ -1,5 +1,5 @@ # -# Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2000-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 @@ -56,56 +56,214 @@ FILES_java += \ sun/nio/ch/DevPollSelectorProvider.java \ sun/nio/ch/InheritedChannel.java \ sun/nio/ch/PollSelectorProvider.java \ - sun/nio/ch/PollSelectorImpl.java + sun/nio/ch/PollSelectorImpl.java \ + sun/nio/ch/Port.java \ + sun/nio/ch/SimpleAsynchronousFileChannelImpl.java \ + sun/nio/ch/SolarisAsynchronousChannelProvider.java \ + sun/nio/ch/SolarisEventPort.java \ + sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java \ + sun/nio/ch/UnixAsynchronousSocketChannelImpl.java \ + \ + sun/nio/fs/GnomeFileTypeDetector.java \ + sun/nio/fs/PollingWatchService.java \ + sun/nio/fs/SolarisAclFileAttributeView.java \ + sun/nio/fs/SolarisFileStore.java \ + sun/nio/fs/SolarisFileSystem.java \ + sun/nio/fs/SolarisFileSystemProvider.java \ + sun/nio/fs/SolarisUserDefinedFileAttributeView.java \ + sun/nio/fs/SolarisNativeDispatcher.java \ + sun/nio/fs/SolarisWatchService.java \ + sun/nio/fs/UnixChannelFactory.java \ + sun/nio/fs/UnixCopyFile.java \ + sun/nio/fs/UnixDirectoryStream.java \ + sun/nio/fs/UnixException.java \ + sun/nio/fs/UnixFileAttributeViews.java \ + sun/nio/fs/UnixFileAttributes.java \ + sun/nio/fs/UnixFileKey.java \ + sun/nio/fs/UnixFileModeAttribute.java \ + sun/nio/fs/UnixFileStore.java \ + sun/nio/fs/UnixFileStoreAttributes.java \ + sun/nio/fs/UnixFileSystem.java \ + sun/nio/fs/UnixFileSystemProvider.java \ + sun/nio/fs/UnixMountEntry.java \ + sun/nio/fs/UnixNativeDispatcher.java \ + sun/nio/fs/UnixPath.java \ + sun/nio/fs/UnixSecureDirectoryStream.java \ + sun/nio/fs/UnixUriUtils.java \ + sun/nio/fs/UnixUserPrincipals.java FILES_c += \ DevPollArrayWrapper.c \ InheritedChannel.c \ NativeThread.c \ - PollArrayWrapper.c + PollArrayWrapper.c \ + SolarisEventPort.c \ + UnixAsynchronousServerSocketChannelImpl.c \ + UnixAsynchronousSocketChannelImpl.c \ + \ + GnomeFileTypeDetector.c \ + SolarisNativeDispatcher.c \ + SolarisWatchService.c \ + UnixCopyFile.c \ + UnixNativeDispatcher.c FILES_export += \ sun/nio/ch/DevPollArrayWrapper.java \ sun/nio/ch/InheritedChannel.java \ - sun/nio/ch/NativeThread.java + sun/nio/ch/NativeThread.java \ + sun/nio/ch/SolarisEventPort.java \ + sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java \ + sun/nio/ch/UnixAsynchronousSocketChannelImpl.java \ + \ + sun/nio/fs/GnomeFileTypeDetector.java \ + sun/nio/fs/SolarisNativeDispatcher.java \ + sun/nio/fs/SolarisWatchService.java \ + sun/nio/fs/UnixCopyFile.java \ + sun/nio/fs/UnixNativeDispatcher.java + +FILES_gen += \ + sun/nio/fs/SolarisConstants.java \ + sun/nio/fs/UnixConstants.java endif # PLATFORM = solaris ifeq ($(PLATFORM), windows) FILES_java += \ + sun/nio/ch/Iocp.java \ + sun/nio/ch/PendingIoCache.java \ + sun/nio/ch/WindowsAsynchronousChannelProvider.java \ + sun/nio/ch/WindowsAsynchronousFileChannelImpl.java \ + sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java \ + sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java \ sun/nio/ch/WindowsSelectorImpl.java \ - sun/nio/ch/WindowsSelectorProvider.java + sun/nio/ch/WindowsSelectorProvider.java \ + \ + sun/nio/fs/RegistryFileTypeDetector.java \ + sun/nio/fs/WindowsAclFileAttributeView.java \ + sun/nio/fs/WindowsChannelFactory.java \ + sun/nio/fs/WindowsConstants.java \ + sun/nio/fs/WindowsDirectoryStream.java \ + sun/nio/fs/WindowsException.java \ + sun/nio/fs/WindowsFileAttributeViews.java \ + sun/nio/fs/WindowsFileAttributes.java \ + sun/nio/fs/WindowsFileCopy.java \ + sun/nio/fs/WindowsFileStore.java \ + sun/nio/fs/WindowsFileSystem.java \ + sun/nio/fs/WindowsFileSystemProvider.java \ + sun/nio/fs/WindowsLinkSupport.java \ + sun/nio/fs/WindowsUserDefinedFileAttributeView.java \ + sun/nio/fs/WindowsNativeDispatcher.java \ + sun/nio/fs/WindowsPath.java \ + sun/nio/fs/WindowsPathParser.java \ + sun/nio/fs/WindowsPathType.java \ + sun/nio/fs/WindowsSecurity.java \ + sun/nio/fs/WindowsSecurityDescriptor.java \ + sun/nio/fs/WindowsUriSupport.java \ + sun/nio/fs/WindowsUserPrincipals.java \ + sun/nio/fs/WindowsWatchService.java FILES_c += \ + Iocp.c \ + RegistryFileTypeDetector.c \ + WindowsAsynchronousFileChannelImpl.c \ + WindowsAsynchronousServerSocketChannelImpl.c \ + WindowsAsynchronousSocketChannelImpl.c \ + WindowsNativeDispatcher.c \ WindowsSelectorImpl.c FILES_export += \ - sun/nio/ch/WindowsSelectorImpl.java + sun/nio/ch/Iocp.java \ + sun/nio/ch/WindowsAsynchronousFileChannelImpl.java \ + sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java \ + sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java \ + sun/nio/ch/WindowsSelectorImpl.java \ + sun/nio/fs/WindowsNativeDispatcher.java \ + sun/nio/fs/RegistryFileTypeDetector.java endif # PLATFORM = windows ifeq ($(PLATFORM), linux) FILES_java += \ sun/nio/ch/AbstractPollSelectorImpl.java \ + sun/nio/ch/EPoll.java \ sun/nio/ch/EPollArrayWrapper.java \ + sun/nio/ch/EPollPort.java \ sun/nio/ch/EPollSelectorProvider.java \ sun/nio/ch/EPollSelectorImpl.java \ sun/nio/ch/InheritedChannel.java \ + sun/nio/ch/LinuxAsynchronousChannelProvider.java \ sun/nio/ch/PollSelectorProvider.java \ - sun/nio/ch/PollSelectorImpl.java + sun/nio/ch/PollSelectorImpl.java \ + sun/nio/ch/Port.java \ + sun/nio/ch/SimpleAsynchronousFileChannelImpl.java \ + sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java \ + sun/nio/ch/UnixAsynchronousSocketChannelImpl.java \ + \ + sun/nio/fs/GnomeFileTypeDetector.java \ + sun/nio/fs/LinuxDosFileAttributeView.java \ + sun/nio/fs/LinuxFileStore.java \ + sun/nio/fs/LinuxFileSystem.java \ + sun/nio/fs/LinuxFileSystemProvider.java \ + sun/nio/fs/LinuxUserDefinedFileAttributeView.java \ + sun/nio/fs/LinuxNativeDispatcher.java \ + sun/nio/fs/LinuxWatchService.java \ + sun/nio/fs/PollingWatchService.java \ + sun/nio/fs/UnixChannelFactory.java \ + sun/nio/fs/UnixCopyFile.java \ + sun/nio/fs/UnixDirectoryStream.java \ + sun/nio/fs/UnixException.java \ + sun/nio/fs/UnixFileAttributeViews.java \ + sun/nio/fs/UnixFileAttributes.java \ + sun/nio/fs/UnixFileKey.java \ + sun/nio/fs/UnixFileModeAttribute.java \ + sun/nio/fs/UnixFileStore.java \ + sun/nio/fs/UnixFileStoreAttributes.java \ + sun/nio/fs/UnixFileSystem.java \ + sun/nio/fs/UnixFileSystemProvider.java \ + sun/nio/fs/UnixMountEntry.java \ + sun/nio/fs/UnixNativeDispatcher.java \ + sun/nio/fs/UnixPath.java \ + sun/nio/fs/UnixSecureDirectoryStream.java \ + sun/nio/fs/UnixUriUtils.java \ + sun/nio/fs/UnixUserPrincipals.java FILES_c += \ + EPoll.c \ EPollArrayWrapper.c \ + EPollPort.c \ InheritedChannel.c \ NativeThread.c \ - PollArrayWrapper.c + PollArrayWrapper.c \ + UnixAsynchronousServerSocketChannelImpl.c \ + UnixAsynchronousSocketChannelImpl.c \ + \ + GnomeFileTypeDetector.c \ + LinuxNativeDispatcher.c \ + LinuxWatchService.c \ + UnixCopyFile.c \ + UnixNativeDispatcher.c FILES_export += \ + sun/nio/ch/EPoll.java \ sun/nio/ch/EPollArrayWrapper.java \ + sun/nio/ch/EPollPort.java \ sun/nio/ch/InheritedChannel.java \ - sun/nio/ch/NativeThread.java + sun/nio/ch/NativeThread.java \ + sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.java \ + sun/nio/ch/UnixAsynchronousSocketChannelImpl.java \ + \ + sun/nio/fs/GnomeFileTypeDetector.java \ + sun/nio/fs/LinuxNativeDispatcher.java \ + sun/nio/fs/LinuxWatchService.java \ + sun/nio/fs/UnixCopyFile.java \ + sun/nio/fs/UnixNativeDispatcher.java + +FILES_gen += \ + sun/nio/fs/UnixConstants.java endif # PLATFORM = linux +# # Find platform-specific C source files # +vpath %.c $(PLATFORM_SRC)/native/sun/nio/fs vpath %.c $(PLATFORM_SRC)/native/sun/nio/ch vpath %.c $(SHARE_SRC)/native/sun/nio/ch @@ -175,12 +333,14 @@ CH_SRC=$(NIO_SRC)/channels CS_SRC=$(NIO_SRC)/charset SCH_SRC=$(SNIO_SRC)/ch SCS_SRC=$(SNIO_SRC)/cs +SFS_SRC=$(SNIO_SRC)/fs BUF_GEN=$(NIO_GEN) CH_GEN=$(NIO_GEN)/channels CS_GEN=$(NIO_GEN)/charset SCH_GEN=$(SNIO_GEN)/ch SCS_GEN=$(SNIO_GEN)/cs +SFS_GEN=$(SNIO_GEN)/fs FILES_gensbcs_out = $(FILES_gen_sbcs:%.java=$(GENSRCDIR)/%.java) @@ -670,4 +830,40 @@ $(FILES_gensbcs_out): $(GENCSSRC)/SingleByte-X.java $(GENCSSRC)/sbcs $(BOOT_JAVA_CMD) -cp $(CHARSETMAPPING_JARFILE) build.tools.charsetmapping.GenerateSBCS \ $(GENCSSRC) $(SCS_GEN) sbcs +# +# Generated file system implementation classes (Unix only) +# + +GENUC_SRC = $(PLATFORM_SRC)/native/sun/nio/fs/genUnixConstants.c + +GENUC_EXE = $(TEMPDIR)/genUnixConstants + +GENUC_COPYRIGHT_YEARS = $(shell $(CAT) $(GENUC_SRC) | \ + $(NAWK) '/^.*Copyright.*Sun/ { print $$3 }') + +$(GENUC_EXE) : $(GENUC_SRC) + $(prep-target) + $(CC) $(CPPFLAGS) -o $@ $(GENUC_SRC) + +$(SFS_GEN)/UnixConstants.java: $(GENUC_EXE) + $(prep-target) + NAWK="$(NAWK)" SH="$(SH)" $(SH) -e addNotices.sh $(GENUC_COPYRIGHT_YEARS) > $@ + $(GENUC_EXE) >> $@ + +GENSC_SRC = $(PLATFORM_SRC)/native/sun/nio/fs/genSolarisConstants.c + +GENSC_EXE = $(TEMPDIR)/genSolarisConstants + +GENSC_COPYRIGHT_YEARS = $(shell $(CAT) $(GENSC_SRC) | \ + $(NAWK) '/^.*Copyright.*Sun/ { print $$3 }') + +$(GENSC_EXE) : $(GENSC_SRC) + $(prep-target) + $(CC) $(CPPFLAGS) -o $@ $(GENSC_SRC) + +$(SFS_GEN)/SolarisConstants.java: $(GENSC_EXE) + $(prep-target) + NAWK="$(NAWK)" SH="$(SH)" $(SH) -e addNotices.sh $(GENSC_COPYRIGHT_YEARS) > $@ + $(GENSC_EXE) >> $@ + .PHONY: sources diff --git a/make/java/nio/mapfile-linux b/make/java/nio/mapfile-linux index 3fb47b0eb..0bd240811 100644 --- a/make/java/nio/mapfile-linux +++ b/make/java/nio/mapfile-linux @@ -1,5 +1,5 @@ # -# Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2001-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 @@ -44,27 +44,38 @@ SUNWprivate_1.1 { Java_sun_nio_ch_EPollArrayWrapper_interrupt; Java_sun_nio_ch_EPollArrayWrapper_offsetofData; Java_sun_nio_ch_EPollArrayWrapper_sizeofEPollEvent; + Java_sun_nio_ch_EPoll_init; + Java_sun_nio_ch_EPoll_eventSize; + Java_sun_nio_ch_EPoll_eventsOffset; + Java_sun_nio_ch_EPoll_dataOffset; + Java_sun_nio_ch_EPoll_epollCreate; + Java_sun_nio_ch_EPoll_epollCtl; + Java_sun_nio_ch_EPoll_epollWait; + Java_sun_nio_ch_EPollPort_close0; + Java_sun_nio_ch_EPollPort_drain1; + Java_sun_nio_ch_EPollPort_interrupt; + Java_sun_nio_ch_EPollPort_socketpair; Java_sun_nio_ch_FileChannelImpl_close0; - Java_sun_nio_ch_FileChannelImpl_force0; Java_sun_nio_ch_FileChannelImpl_initIDs; - Java_sun_nio_ch_FileChannelImpl_lock0; Java_sun_nio_ch_FileChannelImpl_map0; Java_sun_nio_ch_FileChannelImpl_position0; - Java_sun_nio_ch_FileChannelImpl_release0; - Java_sun_nio_ch_FileChannelImpl_size0; Java_sun_nio_ch_FileChannelImpl_transferTo0; - Java_sun_nio_ch_FileChannelImpl_truncate0; Java_sun_nio_ch_FileChannelImpl_unmap0; - Java_sun_nio_ch_FileDispatcher_close0; - Java_sun_nio_ch_FileDispatcher_closeIntFD; - Java_sun_nio_ch_FileDispatcher_init; - Java_sun_nio_ch_FileDispatcher_preClose0; - Java_sun_nio_ch_FileDispatcher_pread0; - Java_sun_nio_ch_FileDispatcher_pwrite0; - Java_sun_nio_ch_FileDispatcher_read0; - Java_sun_nio_ch_FileDispatcher_readv0; - Java_sun_nio_ch_FileDispatcher_write0; - Java_sun_nio_ch_FileDispatcher_writev0; + Java_sun_nio_ch_FileDispatcherImpl_close0; + Java_sun_nio_ch_FileDispatcherImpl_closeIntFD; + Java_sun_nio_ch_FileDispatcherImpl_force0; + Java_sun_nio_ch_FileDispatcherImpl_init; + Java_sun_nio_ch_FileDispatcherImpl_lock0; + Java_sun_nio_ch_FileDispatcherImpl_preClose0; + Java_sun_nio_ch_FileDispatcherImpl_pread0; + Java_sun_nio_ch_FileDispatcherImpl_pwrite0; + Java_sun_nio_ch_FileDispatcherImpl_read0; + Java_sun_nio_ch_FileDispatcherImpl_readv0; + Java_sun_nio_ch_FileDispatcherImpl_release0; + Java_sun_nio_ch_FileDispatcherImpl_size0; + Java_sun_nio_ch_FileDispatcherImpl_truncate0; + Java_sun_nio_ch_FileDispatcherImpl_write0; + Java_sun_nio_ch_FileDispatcherImpl_writev0; Java_sun_nio_ch_FileKey_init; Java_sun_nio_ch_FileKey_initIDs; Java_sun_nio_ch_InheritedChannel_close0; @@ -108,6 +119,76 @@ SUNWprivate_1.1 { Java_sun_nio_ch_ServerSocketChannelImpl_accept0; Java_sun_nio_ch_ServerSocketChannelImpl_initIDs; Java_sun_nio_ch_SocketChannelImpl_checkConnect; + Java_sun_nio_ch_UnixAsynchronousServerSocketChannelImpl_accept0; + Java_sun_nio_ch_UnixAsynchronousServerSocketChannelImpl_initIDs; + Java_sun_nio_ch_UnixAsynchronousSocketChannelImpl_checkConnect; + Java_sun_nio_fs_GnomeFileTypeDetector_initializeGio; + Java_sun_nio_fs_GnomeFileTypeDetector_probeUsingGio; + Java_sun_nio_fs_GnomeFileTypeDetector_initializeGnomeVfs; + Java_sun_nio_fs_GnomeFileTypeDetector_probeUsingGnomeVfs; + Java_sun_nio_fs_LinuxWatchService_init; + Java_sun_nio_fs_LinuxWatchService_eventSize; + Java_sun_nio_fs_LinuxWatchService_eventOffsets; + Java_sun_nio_fs_LinuxWatchService_inotifyInit; + Java_sun_nio_fs_LinuxWatchService_inotifyAddWatch; + Java_sun_nio_fs_LinuxWatchService_inotifyRmWatch; + Java_sun_nio_fs_LinuxWatchService_configureBlocking; + Java_sun_nio_fs_LinuxWatchService_socketpair; + Java_sun_nio_fs_LinuxWatchService_poll; + Java_sun_nio_fs_LinuxNativeDispatcher_init; + Java_sun_nio_fs_LinuxNativeDispatcher_fgetxattr0; + Java_sun_nio_fs_LinuxNativeDispatcher_flistxattr; + Java_sun_nio_fs_LinuxNativeDispatcher_fsetxattr0; + Java_sun_nio_fs_LinuxNativeDispatcher_fremovexattr0; + Java_sun_nio_fs_LinuxNativeDispatcher_setmntent0; + Java_sun_nio_fs_LinuxNativeDispatcher_endmntent; + Java_sun_nio_fs_UnixNativeDispatcher_initIDs; + Java_sun_nio_fs_UnixNativeDispatcher_getcwd; + Java_sun_nio_fs_UnixNativeDispatcher_strerror; + Java_sun_nio_fs_UnixNativeDispatcher_dup; + Java_sun_nio_fs_UnixNativeDispatcher_access0; + Java_sun_nio_fs_UnixNativeDispatcher_stat0; + Java_sun_nio_fs_UnixNativeDispatcher_lstat0; + Java_sun_nio_fs_UnixNativeDispatcher_fstat; + Java_sun_nio_fs_UnixNativeDispatcher_fstatat0; + Java_sun_nio_fs_UnixNativeDispatcher_chmod0; + Java_sun_nio_fs_UnixNativeDispatcher_fchmod; + Java_sun_nio_fs_UnixNativeDispatcher_chown0; + Java_sun_nio_fs_UnixNativeDispatcher_lchown0; + Java_sun_nio_fs_UnixNativeDispatcher_fchown; + Java_sun_nio_fs_UnixNativeDispatcher_utimes0; + Java_sun_nio_fs_UnixNativeDispatcher_futimes; + Java_sun_nio_fs_UnixNativeDispatcher_open0; + Java_sun_nio_fs_UnixNativeDispatcher_openat0; + Java_sun_nio_fs_UnixNativeDispatcher_close; + Java_sun_nio_fs_UnixNativeDispatcher_read; + Java_sun_nio_fs_UnixNativeDispatcher_write; + Java_sun_nio_fs_UnixNativeDispatcher_fopen0; + Java_sun_nio_fs_UnixNativeDispatcher_fclose; + Java_sun_nio_fs_UnixNativeDispatcher_opendir0; + Java_sun_nio_fs_UnixNativeDispatcher_fdopendir; + Java_sun_nio_fs_UnixNativeDispatcher_readdir; + Java_sun_nio_fs_UnixNativeDispatcher_closedir; + Java_sun_nio_fs_UnixNativeDispatcher_link0; + Java_sun_nio_fs_UnixNativeDispatcher_unlink0; + Java_sun_nio_fs_UnixNativeDispatcher_unlinkat0; + Java_sun_nio_fs_UnixNativeDispatcher_rename0; + Java_sun_nio_fs_UnixNativeDispatcher_renameat0; + Java_sun_nio_fs_UnixNativeDispatcher_mkdir0; + Java_sun_nio_fs_UnixNativeDispatcher_rmdir0; + Java_sun_nio_fs_UnixNativeDispatcher_symlink0; + Java_sun_nio_fs_UnixNativeDispatcher_readlink0; + Java_sun_nio_fs_UnixNativeDispatcher_realpath0; + Java_sun_nio_fs_UnixNativeDispatcher_statvfs0; + Java_sun_nio_fs_UnixNativeDispatcher_pathconf0; + Java_sun_nio_fs_UnixNativeDispatcher_fpathconf; + Java_sun_nio_fs_UnixNativeDispatcher_mknod0; + Java_sun_nio_fs_UnixNativeDispatcher_getpwuid; + Java_sun_nio_fs_UnixNativeDispatcher_getgrgid; + Java_sun_nio_fs_UnixNativeDispatcher_getpwnam0; + Java_sun_nio_fs_UnixNativeDispatcher_getgrnam0; + Java_sun_nio_fs_UnixNativeDispatcher_getextmntent; + Java_sun_nio_fs_UnixCopyFile_transfer; local: *; diff --git a/make/java/nio/mapfile-solaris b/make/java/nio/mapfile-solaris index 6e109e2fa..2192a5a77 100644 --- a/make/java/nio/mapfile-solaris +++ b/make/java/nio/mapfile-solaris @@ -1,5 +1,5 @@ # -# Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2001-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 @@ -43,26 +43,26 @@ SUNWprivate_1.1 { Java_sun_nio_ch_DevPollArrayWrapper_register; Java_sun_nio_ch_DevPollArrayWrapper_registerMultiple; Java_sun_nio_ch_FileChannelImpl_close0; - Java_sun_nio_ch_FileChannelImpl_force0; Java_sun_nio_ch_FileChannelImpl_initIDs; - Java_sun_nio_ch_FileChannelImpl_lock0; Java_sun_nio_ch_FileChannelImpl_map0; Java_sun_nio_ch_FileChannelImpl_position0; - Java_sun_nio_ch_FileChannelImpl_release0; - Java_sun_nio_ch_FileChannelImpl_size0; Java_sun_nio_ch_FileChannelImpl_transferTo0; - Java_sun_nio_ch_FileChannelImpl_truncate0; Java_sun_nio_ch_FileChannelImpl_unmap0; - Java_sun_nio_ch_FileDispatcher_close0; - Java_sun_nio_ch_FileDispatcher_closeIntFD; - Java_sun_nio_ch_FileDispatcher_init; - Java_sun_nio_ch_FileDispatcher_preClose0; - Java_sun_nio_ch_FileDispatcher_pread0; - Java_sun_nio_ch_FileDispatcher_pwrite0; - Java_sun_nio_ch_FileDispatcher_read0; - Java_sun_nio_ch_FileDispatcher_readv0; - Java_sun_nio_ch_FileDispatcher_write0; - Java_sun_nio_ch_FileDispatcher_writev0; + Java_sun_nio_ch_FileDispatcherImpl_close0; + Java_sun_nio_ch_FileDispatcherImpl_closeIntFD; + Java_sun_nio_ch_FileDispatcherImpl_force0; + Java_sun_nio_ch_FileDispatcherImpl_init; + Java_sun_nio_ch_FileDispatcherImpl_lock0; + Java_sun_nio_ch_FileDispatcherImpl_preClose0; + Java_sun_nio_ch_FileDispatcherImpl_pread0; + Java_sun_nio_ch_FileDispatcherImpl_pwrite0; + Java_sun_nio_ch_FileDispatcherImpl_read0; + Java_sun_nio_ch_FileDispatcherImpl_readv0; + Java_sun_nio_ch_FileDispatcherImpl_release0; + Java_sun_nio_ch_FileDispatcherImpl_size0; + Java_sun_nio_ch_FileDispatcherImpl_truncate0; + Java_sun_nio_ch_FileDispatcherImpl_write0; + Java_sun_nio_ch_FileDispatcherImpl_writev0; Java_sun_nio_ch_FileKey_init; Java_sun_nio_ch_FileKey_initIDs; Java_sun_nio_ch_InheritedChannel_close0; @@ -106,6 +106,75 @@ SUNWprivate_1.1 { Java_sun_nio_ch_ServerSocketChannelImpl_accept0; Java_sun_nio_ch_ServerSocketChannelImpl_initIDs; Java_sun_nio_ch_SocketChannelImpl_checkConnect; + Java_sun_nio_ch_UnixAsynchronousServerSocketChannelImpl_accept0; + Java_sun_nio_ch_UnixAsynchronousServerSocketChannelImpl_initIDs; + Java_sun_nio_ch_UnixAsynchronousSocketChannelImpl_checkConnect; + Java_sun_nio_ch_SolarisEventPort_init; + Java_sun_nio_ch_SolarisEventPort_portCreate; + Java_sun_nio_ch_SolarisEventPort_portClose; + Java_sun_nio_ch_SolarisEventPort_portAssociate; + Java_sun_nio_ch_SolarisEventPort_portGet; + Java_sun_nio_ch_SolarisEventPort_portGetn; + Java_sun_nio_ch_SolarisEventPort_portSend; + Java_sun_nio_fs_GnomeFileTypeDetector_initializeGio; + Java_sun_nio_fs_GnomeFileTypeDetector_probeUsingGio; + Java_sun_nio_fs_GnomeFileTypeDetector_initializeGnomeVfs; + Java_sun_nio_fs_GnomeFileTypeDetector_probeUsingGnomeVfs; + Java_sun_nio_fs_UnixNativeDispatcher_initIDs; + Java_sun_nio_fs_UnixNativeDispatcher_getcwd; + Java_sun_nio_fs_UnixNativeDispatcher_strerror; + Java_sun_nio_fs_UnixNativeDispatcher_dup; + Java_sun_nio_fs_UnixNativeDispatcher_access0; + Java_sun_nio_fs_UnixNativeDispatcher_stat0; + Java_sun_nio_fs_UnixNativeDispatcher_lstat0; + Java_sun_nio_fs_UnixNativeDispatcher_fstat; + Java_sun_nio_fs_UnixNativeDispatcher_fstatat0; + Java_sun_nio_fs_UnixNativeDispatcher_chmod0; + Java_sun_nio_fs_UnixNativeDispatcher_fchmod; + Java_sun_nio_fs_UnixNativeDispatcher_chown0; + Java_sun_nio_fs_UnixNativeDispatcher_lchown0; + Java_sun_nio_fs_UnixNativeDispatcher_fchown; + Java_sun_nio_fs_UnixNativeDispatcher_utimes0; + Java_sun_nio_fs_UnixNativeDispatcher_futimes; + Java_sun_nio_fs_UnixNativeDispatcher_open0; + Java_sun_nio_fs_UnixNativeDispatcher_openat0; + Java_sun_nio_fs_UnixNativeDispatcher_close; + Java_sun_nio_fs_UnixNativeDispatcher_read; + Java_sun_nio_fs_UnixNativeDispatcher_write; + Java_sun_nio_fs_UnixNativeDispatcher_fopen0; + Java_sun_nio_fs_UnixNativeDispatcher_fclose; + Java_sun_nio_fs_UnixNativeDispatcher_opendir0; + Java_sun_nio_fs_UnixNativeDispatcher_fdopendir; + Java_sun_nio_fs_UnixNativeDispatcher_readdir; + Java_sun_nio_fs_UnixNativeDispatcher_closedir; + Java_sun_nio_fs_UnixNativeDispatcher_link0; + Java_sun_nio_fs_UnixNativeDispatcher_unlink0; + Java_sun_nio_fs_UnixNativeDispatcher_unlinkat0; + Java_sun_nio_fs_UnixNativeDispatcher_rename0; + Java_sun_nio_fs_UnixNativeDispatcher_renameat0; + Java_sun_nio_fs_UnixNativeDispatcher_mkdir0; + Java_sun_nio_fs_UnixNativeDispatcher_rmdir0; + Java_sun_nio_fs_UnixNativeDispatcher_symlink0; + Java_sun_nio_fs_UnixNativeDispatcher_readlink0; + Java_sun_nio_fs_UnixNativeDispatcher_realpath0; + Java_sun_nio_fs_UnixNativeDispatcher_statvfs0; + Java_sun_nio_fs_UnixNativeDispatcher_pathconf0; + Java_sun_nio_fs_UnixNativeDispatcher_fpathconf; + Java_sun_nio_fs_UnixNativeDispatcher_mknod0; + Java_sun_nio_fs_UnixNativeDispatcher_getpwuid; + Java_sun_nio_fs_UnixNativeDispatcher_getgrgid; + Java_sun_nio_fs_UnixNativeDispatcher_getpwnam0; + Java_sun_nio_fs_UnixNativeDispatcher_getgrnam0; + Java_sun_nio_fs_UnixNativeDispatcher_getextmntent; + Java_sun_nio_fs_UnixCopyFile_transfer; + Java_sun_nio_fs_SolarisNativeDispatcher_init; + Java_sun_nio_fs_SolarisNativeDispatcher_facl; + Java_sun_nio_fs_SolarisWatchService_init; + Java_sun_nio_fs_SolarisWatchService_portCreate; + Java_sun_nio_fs_SolarisWatchService_portAssociate; + Java_sun_nio_fs_SolarisWatchService_portDissociate; + Java_sun_nio_fs_SolarisWatchService_portSend; + Java_sun_nio_fs_SolarisWatchService_portGetn; local: *; diff --git a/make/mksample/nio/Makefile b/make/mksample/nio/Makefile index e05106293..5fcfd03a7 100644 --- a/make/mksample/nio/Makefile +++ b/make/mksample/nio/Makefile @@ -1,5 +1,5 @@ # -# Copyright 2004-2008 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2004-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 @@ -31,7 +31,7 @@ BUILDDIR = ../.. PRODUCT = java include $(BUILDDIR)/common/Defs.gmk -SUBDIRS = multicast server +SUBDIRS = file multicast server all build clean clobber:: $(SUBDIRS-loop) diff --git a/make/mksample/nio/file/Makefile b/make/mksample/nio/file/Makefile new file mode 100644 index 000000000..f7159de83 --- /dev/null +++ b/make/mksample/nio/file/Makefile @@ -0,0 +1,56 @@ +# +# 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. +# + +# +# Makefile for the nio/file sample code +# + +BUILDDIR = ../../.. + +PRODUCT = java + +include $(BUILDDIR)/common/Defs.gmk + +SAMPLE_SRC_DIR = $(SHARE_SRC)/sample/nio/file +SAMPLE_DST_DIR = $(SAMPLEDIR)/nio/file + +SAMPLE_FILES = \ + $(SAMPLE_DST_DIR)/AclEdit.java \ + $(SAMPLE_DST_DIR)/Chmod.java \ + $(SAMPLE_DST_DIR)/Copy.java \ + $(SAMPLE_DST_DIR)/DiskUsage.java \ + $(SAMPLE_DST_DIR)/FileType.java \ + $(SAMPLE_DST_DIR)/WatchDir.java \ + $(SAMPLE_DST_DIR)/Xdd.java + +all build: $(SAMPLE_FILES) + +$(SAMPLE_DST_DIR)/%: $(SAMPLE_SRC_DIR)/% + $(install-file) + +clean clobber: + $(RM) -r $(SAMPLE_DST_DIR) + +.PHONY: all build clean clobber diff --git a/src/share/classes/com/sun/nio/file/ExtendedCopyOption.java b/src/share/classes/com/sun/nio/file/ExtendedCopyOption.java new file mode 100644 index 000000000..b612c0e8d --- /dev/null +++ b/src/share/classes/com/sun/nio/file/ExtendedCopyOption.java @@ -0,0 +1,43 @@ +/* + * Copyright 2007-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 com.sun.nio.file; + +import java.nio.file.CopyOption; + +/** + * Defines extended copy options supported on some platforms + * by Sun's provider implementation. + * + * @since 1.7 + */ + +public enum ExtendedCopyOption implements CopyOption { + /** + * The copy may be interrupted by the {@link Thread#interrupt interrupt} + * method. + */ + INTERRUPTIBLE, +} diff --git a/src/share/classes/com/sun/nio/file/ExtendedOpenOption.java b/src/share/classes/com/sun/nio/file/ExtendedOpenOption.java new file mode 100644 index 000000000..25208d812 --- /dev/null +++ b/src/share/classes/com/sun/nio/file/ExtendedOpenOption.java @@ -0,0 +1,50 @@ +/* + * Copyright 2007-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 com.sun.nio.file; + +import java.nio.file.OpenOption; + +/** + * Defines extended open options supported on some platforms + * by Sun's provider implementation. + * + * @since 1.7 + */ + +public enum ExtendedOpenOption implements OpenOption { + /** + * Prevent operations on the file that request read access. + */ + NOSHARE_READ, + /** + * Prevent operations on the file that request write access. + */ + NOSHARE_WRITE, + /** + * Prevent operations on the file that request delete access. + */ + NOSHARE_DELETE; +} diff --git a/src/share/classes/com/sun/nio/file/ExtendedWatchEventModifier.java b/src/share/classes/com/sun/nio/file/ExtendedWatchEventModifier.java new file mode 100644 index 000000000..0f6ddc327 --- /dev/null +++ b/src/share/classes/com/sun/nio/file/ExtendedWatchEventModifier.java @@ -0,0 +1,43 @@ +/* + * Copyright 2007-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 com.sun.nio.file; + +import java.nio.file.WatchEvent.Modifier; + +/** + * Defines extended watch event modifiers supported on some platforms + * by Sun's provider implementation. + * + * @since 1.7 + */ + +public enum ExtendedWatchEventModifier implements Modifier { + + /** + * Register a file tree instead of a single directory. + */ + FILE_TREE, +} diff --git a/src/share/classes/com/sun/nio/file/SensitivityWatchEventModifier.java b/src/share/classes/com/sun/nio/file/SensitivityWatchEventModifier.java new file mode 100644 index 000000000..57ab111b0 --- /dev/null +++ b/src/share/classes/com/sun/nio/file/SensitivityWatchEventModifier.java @@ -0,0 +1,62 @@ +/* + * Copyright 2007-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 com.sun.nio.file; + +import java.nio.file.WatchEvent.Modifier; + +/** + * Defines the sensitivity levels when registering objects with a + * watch service implementation that polls the file system. + * + * @since 1.7 + */ + +public enum SensitivityWatchEventModifier implements Modifier { + /** + * High sensitivity. + */ + HIGH(2), + /** + * Medium sensitivity. + */ + MEDIUM(10), + /** + * Low sensitivity. + */ + LOW(30); + + /** + * Returns the sensitivity in seconds. + */ + public int sensitivityValueInSeconds() { + return sensitivity; + } + + private final int sensitivity; + private SensitivityWatchEventModifier(int sensitivity) { + this.sensitivity = sensitivity; + } +} diff --git a/src/share/classes/java/io/File.java b/src/share/classes/java/io/File.java index d573b89f4..6a0415101 100644 --- a/src/share/classes/java/io/File.java +++ b/src/share/classes/java/io/File.java @@ -1,5 +1,5 @@ /* - * Copyright 1994-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1994-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 @@ -30,12 +30,12 @@ import java.net.URI; import java.net.URL; import java.net.MalformedURLException; import java.net.URISyntaxException; -import java.util.ArrayList; -import java.util.Map; -import java.util.Hashtable; -import java.util.Random; +import java.util.*; +import java.nio.file.*; +import java.nio.file.attribute.*; import java.security.AccessController; -import java.security.AccessControlException; +import java.security.PrivilegedAction; +import java.security.SecureRandom; import sun.security.action.GetPropertyAction; @@ -131,6 +131,18 @@ import sun.security.action.GetPropertyAction; * created, the abstract pathname represented by a File object * will never change. * + *

Interoperability with {@code java.nio.file} package

+ * + *

The {@code java.nio.file} + * package defines interfaces and classes for the Java virtual machine to access + * files, file attributes, and file systems. This API may be used to overcome + * many of the limitations of the {@code java.io.File} class. + * The {@link #toPath toPath} method may be used to obtain a {@link + * Path} that uses the abstract path represented by a {@code File} object to + * locate a file. The resulting {@code Path} provides more efficient and + * extensive access to file attributes, additional file operations, and I/O + * exceptions to help diagnose errors when an operation on a file fails. + * * @author unascribed * @since JDK1.0 */ @@ -573,6 +585,7 @@ public class File * read access to the file * * @since JDK1.1 + * @see Path#toRealPath */ public String getCanonicalPath() throws IOException { return fs.canonicalize(fs.resolve(this)); @@ -597,6 +610,7 @@ public class File * read access to the file * * @since 1.2 + * @see Path#toRealPath */ public File getCanonicalFile() throws IOException { String canonPath = getCanonicalPath(); @@ -663,6 +677,14 @@ public class File * system is converted into an abstract pathname in a virtual machine on a * different operating system. * + *

Note that when this abstract pathname represents a UNC pathname then + * all components of the UNC (including the server name component) are encoded + * in the {@code URI} path. The authority component is undefined, meaning + * that it is represented as {@code null}. The {@link Path} class defines the + * {@link Path#toUri toUri} method to encode the server name in the authority + * component of the resulting {@code URI}. The {@link #toPath toPath} method + * may be used to obtain a {@code Path} representing this abstract pathname. + * * @return An absolute, hierarchical URI with a scheme equal to * "file", a path representing this abstract pathname, * and undefined authority, query, and fragment components @@ -764,6 +786,8 @@ public class File * If a security manager exists and its {@link * java.lang.SecurityManager#checkRead(java.lang.String)} * method denies read access to the file + * + * @see Attributes#readBasicFileAttributes */ public boolean isDirectory() { SecurityManager security = System.getSecurityManager(); @@ -788,6 +812,8 @@ public class File * If a security manager exists and its {@link * java.lang.SecurityManager#checkRead(java.lang.String)} * method denies read access to the file + * + * @see Attributes#readBasicFileAttributes */ public boolean isFile() { SecurityManager security = System.getSecurityManager(); @@ -836,6 +862,8 @@ public class File * If a security manager exists and its {@link * java.lang.SecurityManager#checkRead(java.lang.String)} * method denies read access to the file + * + * @see Attributes#readBasicFileAttributes */ public long lastModified() { SecurityManager security = System.getSecurityManager(); @@ -858,6 +886,8 @@ public class File * If a security manager exists and its {@link * java.lang.SecurityManager#checkRead(java.lang.String)} * method denies read access to the file + * + * @see Attributes#readBasicFileAttributes */ public long length() { SecurityManager security = System.getSecurityManager(); @@ -907,6 +937,12 @@ public class File * this pathname denotes a directory, then the directory must be empty in * order to be deleted. * + *

Note that the {@link Path} class defines the {@link Path#delete + * delete} method to throw an {@link IOException} when a file cannot be + * deleted. This is useful for error reporting and to diagnose why a file + * cannot be deleted. The {@link #toPath toPath} method may be used to + * obtain a {@code Path} representing this abstract pathname. + * * @return true if and only if the file or directory is * successfully deleted; false otherwise * @@ -973,6 +1009,13 @@ public class File * will appear in any specific order; they are not, in particular, * guaranteed to appear in alphabetical order. * + *

Note that the {@link Path} class defines the {@link + * Path#newDirectoryStream newDirectoryStream} method to open a directory + * and iterate over the names of the files in the directory. This may use + * less resources when working with very large directories. The {@link + * #toPath toPath} method may be used to obtain a {@code Path} representing + * this abstract pathname. + * * @return An array of strings naming the files and directories in the * directory denoted by this abstract pathname. The array will be * empty if the directory is empty. Returns {@code null} if @@ -1024,13 +1067,13 @@ public class File if ((names == null) || (filter == null)) { return names; } - ArrayList v = new ArrayList(); + List v = new ArrayList(); for (int i = 0 ; i < names.length ; i++) { if (filter.accept(this, names[i])) { v.add(names[i]); } } - return (String[])(v.toArray(new String[v.size()])); + return v.toArray(new String[v.size()]); } /** @@ -1052,6 +1095,13 @@ public class File * will appear in any specific order; they are not, in particular, * guaranteed to appear in alphabetical order. * + *

Note that the {@link Path} class defines the {@link + * Path#newDirectoryStream newDirectoryStream} method to open a directory + * and iterate over the names of the files in the directory. This may use + * less resources when working with very large directories. The {@link + * #toPath toPath} method may be used to obtain a {@code Path} representing + * this abstract pathname. + * * @return An array of abstract pathnames denoting the files and * directories in the directory denoted by this abstract pathname. * The array will be empty if the directory is empty. Returns @@ -1157,6 +1207,12 @@ public class File /** * Creates the directory named by this abstract pathname. * + *

Note that the {@link Path} class defines the {@link Path#createDirectory + * createDirectory} method to throw an {@link IOException} when a directory + * cannot be created. This is useful for error reporting and to diagnose why + * a directory cannot be created. The {@link #toPath toPath} method may be + * used to obtain a {@code Path} representing this abstract pathname. + * * @return true if and only if the directory was * created; false otherwise * @@ -1222,6 +1278,11 @@ public class File * already exists. The return value should always be checked to make sure * that the rename operation was successful. * + *

Note that the {@link Path} class defines the {@link Path#moveTo + * moveTo} method to move or rename a file in a platform independent manner. + * The {@link #toPath toPath} method may be used to obtain a {@code Path} + * representing this abstract pathname. + * * @param dest The new abstract pathname for the named file * * @return true if and only if the renaming succeeded; @@ -1304,10 +1365,14 @@ public class File return fs.setReadOnly(this); } - /** + /** * Sets the owner's or everybody's write permission for this abstract * pathname. * + *

The {@link Attributes Attributes} class defines methods that operate + * on file attributes including file permissions. This may be used when + * finer manipulation of file permissions is required. + * * @param writable * If true, sets the access permission to allow write * operations; if false to disallow write operations @@ -1371,6 +1436,10 @@ public class File * Sets the owner's or everybody's read permission for this abstract * pathname. * + *

The {@link Attributes Attributes} class defines methods that operate + * on file attributes including file permissions. This may be used when + * finer manipulation of file permissions is required. + * * @param readable * If true, sets the access permission to allow read * operations; if false to disallow read operations @@ -1440,6 +1509,10 @@ public class File * Sets the owner's or everybody's execute permission for this abstract * pathname. * + *

The {@link Attributes Attributes} class defines methods that operate + * on file attributes including file permissions. This may be used when + * finer manipulation of file permissions is required. + * * @param executable * If true, sets the access permission to allow execute * operations; if false to disallow execute operations @@ -1678,44 +1751,44 @@ public class File /* -- Temporary files -- */ - private static final Object tmpFileLock = new Object(); + private static class TemporaryDirectory { + private TemporaryDirectory() { } - private static int counter = -1; /* Protected by tmpFileLock */ + static final String valueAsString = fs.normalize( + AccessController.doPrivileged(new GetPropertyAction("java.io.tmpdir"))); + static final File valueAsFile = + new File(valueAsString, fs.prefixLength(valueAsString)); - private static File generateFile(String prefix, String suffix, File dir) - throws IOException - { - if (counter == -1) { - counter = new Random().nextInt() & 0xffff; + // file name generation + private static final SecureRandom random = new SecureRandom(); + static File generateFile(String prefix, String suffix, File dir) { + long n = random.nextLong(); + if (n == Long.MIN_VALUE) { + n = 0; // corner case + } else { + n = Math.abs(n); + } + return new File(dir, prefix + Long.toString(n) + suffix); } - counter++; - return new File(dir, prefix + Integer.toString(counter) + suffix); - } - private static String tmpdir; /* Protected by tmpFileLock */ - - private static String getTempDir() { - if (tmpdir == null) - tmpdir = fs.normalize( - AccessController.doPrivileged( - new GetPropertyAction("java.io.tmpdir"))); - return tmpdir; - } - - private static boolean checkAndCreate(String filename, SecurityManager sm) - throws IOException - { - if (sm != null) { - try { - sm.checkWrite(filename); - } catch (AccessControlException x) { - /* Throwing the original AccessControlException could disclose - the location of the default temporary directory, so we - re-throw a more innocuous SecurityException */ - throw new SecurityException("Unable to create temporary file"); - } + // default file permissions + static final FileAttribute> defaultPosixFilePermissions = + PosixFilePermissions.asFileAttribute(EnumSet + .of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE)); + static final boolean isPosix = isPosix(); + static boolean isPosix() { + return AccessController.doPrivileged( + new PrivilegedAction() { + public Boolean run() { + try { + return FileSystems.getDefault().getPath(valueAsString) + .getFileStore().supportsFileAttributeView("posix"); + } catch (IOException e) { + throw new IOError(e); + } + } + }); } - return fs.createFileExclusively(filename); } /** @@ -1791,22 +1864,29 @@ public class File File directory) throws IOException { - if (prefix == null) throw new NullPointerException(); if (prefix.length() < 3) throw new IllegalArgumentException("Prefix string too short"); - String s = (suffix == null) ? ".tmp" : suffix; - synchronized (tmpFileLock) { - if (directory == null) { - String tmpDir = getTempDir(); - directory = new File(tmpDir, fs.prefixLength(tmpDir)); + if (suffix == null) + suffix = ".tmp"; + + File tmpdir = (directory != null) ? + directory : TemporaryDirectory.valueAsFile; + SecurityManager sm = System.getSecurityManager(); + File f; + do { + f = TemporaryDirectory.generateFile(prefix, suffix, tmpdir); + if (sm != null) { + try { + sm.checkWrite(f.getPath()); + } catch (SecurityException se) { + // don't reveal temporary directory location + if (directory == null) + throw new SecurityException("Unable to create temporary file"); + throw se; + } } - SecurityManager sm = System.getSecurityManager(); - File f; - do { - f = generateFile(prefix, s, directory); - } while (!checkAndCreate(f.getPath(), sm)); - return f; - } + } while (!fs.createFileExclusively(f.getPath())); + return f; } /** @@ -1844,6 +1924,122 @@ public class File return createTempFile(prefix, suffix, null); } + /** + * Creates an empty file in the default temporary-file directory, using + * the given prefix and suffix to generate its name. This method is + * equivalent to invoking the {@link #createTempFile(String,String) + * createTempFile(prefix, suffix)} method with the addition that the + * resulting pathname may be requested to be deleted when the Java virtual + * machine terminates, and the initial file attributes to set atomically + * when creating the file may be specified. + * + *

When the value of the {@code deleteOnExit} method is {@code true} + * then the resulting file is requested to be deleted when the Java virtual + * machine terminates as if by invoking the {@link #deleteOnExit deleteOnExit} + * method. + * + *

The {@code attrs} parameter is an optional array of {@link FileAttribute + * attributes} to set atomically when creating the file. Each attribute is + * identified by its {@link FileAttribute#name name}. If more than one attribute + * of the same name is included in the array then all but the last occurrence + * is ignored. + * + * @param prefix + * The prefix string to be used in generating the file's + * name; must be at least three characters long + * @param suffix + * The suffix string to be used in generating the file's + * name; may be {@code null}, in which case the suffix + * {@code ".tmp"} will be used + * @param deleteOnExit + * {@code true} if the file denoted by resulting pathname be + * deleted when the Java virtual machine terminates + * @param attrs + * An optional list of file attributes to set atomically when creating + * the file + * + * @return An abstract pathname denoting a newly-created empty file + * + * @throws IllegalArgumentException + * If the prefix argument contains fewer than three + * characters + * @throws UnsupportedOperationException + * If the array contains an attribute that cannot be set atomically + * when creating the file + * @throws IOException + * If a file could not be created + * @throws SecurityException + * If a security manager exists and its {@link + * java.lang.SecurityManager#checkWrite(java.lang.String)} + * method does not allow a file to be created. When the {@code + * deleteOnExit} parameter has the value {@code true} then the + * security manager's {@link + * java.lang.SecurityManager#checkDelete(java.lang.String)} is + * invoked to check delete access to the file. + * @since 1.7 + */ + public static File createTempFile(String prefix, + String suffix, + boolean deleteOnExit, + FileAttribute... attrs) + throws IOException + { + if (prefix.length() < 3) + throw new IllegalArgumentException("Prefix string too short"); + suffix = (suffix == null) ? ".tmp" : suffix; + + // special case POSIX environments so that 0600 is used as the file mode + if (TemporaryDirectory.isPosix) { + if (attrs.length == 0) { + // no attributes so use default permissions + attrs = new FileAttribute[1]; + attrs[0] = TemporaryDirectory.defaultPosixFilePermissions; + } else { + // check if posix permissions given; if not use default + boolean hasPermissions = false; + for (int i=0; i[] copy = new FileAttribute[attrs.length+1]; + System.arraycopy(attrs, 0, copy, 0, attrs.length); + attrs = copy; + attrs[attrs.length-1] = + TemporaryDirectory.defaultPosixFilePermissions; + } + } + } + + // use Path#createFile to create file + SecurityManager sm = System.getSecurityManager(); + for (;;) { + File f = TemporaryDirectory + .generateFile(prefix, suffix, TemporaryDirectory.valueAsFile); + if (sm != null && deleteOnExit) + sm.checkDelete(f.getPath()); + try { + f.toPath().createFile(attrs); + if (deleteOnExit) + DeleteOnExitHook.add(f.getPath()); + return f; + } catch (InvalidPathException e) { + // don't reveal temporary directory location + if (sm != null) + throw new IllegalArgumentException("Invalid prefix or suffix"); + throw e; + } catch (SecurityException e) { + // don't reveal temporary directory location + if (sm != null) + throw new SecurityException("Unable to create temporary file"); + throw e; + } catch (FileAlreadyExistsException e) { + // ignore + } + } + } /* -- Basic infrastructure -- */ @@ -1963,5 +2159,46 @@ public class File ); } + // -- Integration with java.nio.file -- + + private volatile transient Path filePath; + /** + * Returns a {@link Path java.nio.file.Path} object constructed from the + * this abstract path. The first invocation of this method works as if + * invoking it were equivalent to evaluating the expression: + *

+     * {@link FileSystems#getDefault FileSystems.getDefault}().{@link FileSystem#getPath getPath}(this.{@link #getPath getPath}());
+     * 
+ * Subsequent invocations of this method return the same {@code Path}. + * + *

If this abstract pathname is the empty abstract pathname then this + * method returns a {@code Path} that may be used to access to the current + * user directory. + * + * @return A {@code Path} constructed from this abstract path. The resulting + * {@code Path} is associated with the {@link FileSystems#getDefault + * default-filesystem}. + * + * @throws InvalidPathException + * If a {@code Path} object cannot be constructed from the abstract + * path (see {@link java.nio.file.FileSystem#getPath FileSystem.getPath}) + * + * @since 1.7 + */ + public Path toPath() { + if (filePath == null) { + synchronized (this) { + if (filePath == null) { + if (path.length() == 0) { + // assume default file system treats "." as current directory + filePath = Paths.get("."); + } else { + filePath = Paths.get(path); + } + } + } + } + return filePath; + } } diff --git a/src/share/classes/java/io/FilePermission.java b/src/share/classes/java/io/FilePermission.java index 9758e35de..88c98fbdd 100644 --- a/src/share/classes/java/io/FilePermission.java +++ b/src/share/classes/java/io/FilePermission.java @@ -1,5 +1,5 @@ /* - * Copyright 1997-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-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 @@ -29,7 +29,6 @@ import java.security.*; import java.util.Enumeration; import java.util.List; import java.util.ArrayList; -import java.util.StringTokenizer; import java.util.Vector; import java.util.Collections; import java.io.ObjectStreamField; @@ -58,7 +57,8 @@ import sun.security.util.SecurityConstants; *

* The actions to be granted are passed to the constructor in a string containing * a list of one or more comma-separated keywords. The possible keywords are - * "read", "write", "execute", and "delete". Their meaning is defined as follows: + * "read", "write", "execute", "delete", and "readlink". Their meaning is + * defined as follows: *

*

*
read
read permission @@ -69,6 +69,11 @@ import sun.security.util.SecurityConstants; *
delete *
delete permission. Allows File.delete to * be called. Corresponds to SecurityManager.checkDelete. + *
readlink + *
read link permission. Allows the target of a + * symbolic link + * to be read by invoking the {@link java.nio.file.Path#readSymbolicLink + * readSymbolicLink } method. *
*

* The actions string is converted to lowercase before processing. @@ -114,11 +119,15 @@ public final class FilePermission extends Permission implements Serializable { * Delete action. */ private final static int DELETE = 0x8; + /** + * Read link action. + */ + private final static int READLINK = 0x10; /** - * All actions (read,write,execute,delete) + * All actions (read,write,execute,delete,readlink) */ - private final static int ALL = READ|WRITE|EXECUTE|DELETE; + private final static int ALL = READ|WRITE|EXECUTE|DELETE|READLINK; /** * No actions. */ @@ -235,7 +244,7 @@ public final class FilePermission extends Permission implements Serializable { * path is the pathname of a file or directory, and actions * contains a comma-separated list of the desired actions granted on the * file or directory. Possible actions are - * "read", "write", "execute", and "delete". + * "read", "write", "execute", "delete", and "readlink". * *

A pathname that ends in "/*" (where "/" is * the file separator character, File.separatorChar) @@ -425,6 +434,8 @@ public final class FilePermission extends Permission implements Serializable { return EXECUTE; } else if (actions == SecurityConstants.FILE_DELETE_ACTION) { return DELETE; + } else if (actions == SecurityConstants.FILE_READLINK_ACTION) { + return READLINK; } char[] a = actions.toCharArray(); @@ -485,6 +496,18 @@ public final class FilePermission extends Permission implements Serializable { matchlen = 6; mask |= DELETE; + } else if (i >= 7 && (a[i-7] == 'r' || a[i-7] == 'R') && + (a[i-6] == 'e' || a[i-6] == 'E') && + (a[i-5] == 'a' || a[i-5] == 'A') && + (a[i-4] == 'd' || a[i-4] == 'D') && + (a[i-3] == 'l' || a[i-3] == 'L') && + (a[i-2] == 'i' || a[i-2] == 'I') && + (a[i-1] == 'n' || a[i-1] == 'N') && + (a[i] == 'k' || a[i] == 'K')) + { + matchlen = 8; + mask |= READLINK; + } else { // parse error throw new IllegalArgumentException( @@ -529,7 +552,7 @@ public final class FilePermission extends Permission implements Serializable { /** * Return the canonical string representation of the actions. * Always returns present actions in the following order: - * read, write, execute, delete. + * read, write, execute, delete, readlink. * * @return the canonical string representation of the actions. */ @@ -561,14 +584,20 @@ public final class FilePermission extends Permission implements Serializable { sb.append("delete"); } + if ((mask & READLINK) == READLINK) { + if (comma) sb.append(','); + else comma = true; + sb.append("readlink"); + } + return sb.toString(); } /** * Returns the "canonical string representation" of the actions. * That is, this method always returns present actions in the following order: - * read, write, execute, delete. For example, if this FilePermission object - * allows both write and read actions, a call to getActions + * read, write, execute, delete, readlink. For example, if this FilePermission + * object allows both write and read actions, a call to getActions * will return the string "read,write". * * @return the canonical string representation of the actions. @@ -678,7 +707,7 @@ final class FilePermissionCollection extends PermissionCollection implements Serializable { // Not serialized; see serialization section at end of class - private transient List perms; + private transient List perms; /** * Create an empty FilePermissions object. @@ -686,7 +715,7 @@ implements Serializable { */ public FilePermissionCollection() { - perms = new ArrayList(); + perms = new ArrayList(); } /** @@ -791,7 +820,7 @@ implements Serializable { // Don't call out.defaultWriteObject() // Write out Vector - Vector permissions = new Vector(perms.size()); + Vector permissions = new Vector(perms.size()); synchronized (this) { permissions.addAll(perms); } @@ -804,6 +833,7 @@ implements Serializable { /* * Reads in a Vector of FilePermissions and saves them in the perms field. */ + @SuppressWarnings("unchecked") private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { // Don't call defaultReadObject() @@ -812,8 +842,8 @@ implements Serializable { ObjectInputStream.GetField gfields = in.readFields(); // Get the one we want - Vector permissions = (Vector)gfields.get("permissions", null); - perms = new ArrayList(permissions.size()); + Vector permissions = (Vector)gfields.get("permissions", null); + perms = new ArrayList(permissions.size()); perms.addAll(permissions); } } diff --git a/src/share/classes/java/net/StandardProtocolFamily.java b/src/share/classes/java/net/StandardProtocolFamily.java index 7c11b32f5..d4b03ed88 100644 --- a/src/share/classes/java/net/StandardProtocolFamily.java +++ b/src/share/classes/java/net/StandardProtocolFamily.java @@ -1,5 +1,5 @@ /* - * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007-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 @@ -26,7 +26,7 @@ package java.net; /** - * Defines the standard family of communication protocols. + * Defines the standard families of communication protocols. * * @since 1.7 */ diff --git a/src/share/classes/java/net/StandardSocketOption.java b/src/share/classes/java/net/StandardSocketOption.java index 405038cc1..dba8ff22d 100644 --- a/src/share/classes/java/net/StandardSocketOption.java +++ b/src/share/classes/java/net/StandardSocketOption.java @@ -1,5 +1,5 @@ /* - * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007-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 @@ -59,6 +59,7 @@ public final class StandardSocketOption { * * @see RFC 929: * Broadcasting Internet Datagrams + * @see DatagramSocket#setBroadcast */ public static final SocketOption SO_BROADCAST = new StdSocketOption("SO_BROADCAST", Boolean.class); @@ -78,6 +79,7 @@ public final class StandardSocketOption { * * @see RFC 1122 * Requirements for Internet Hosts -- Communication Layers + * @see Socket#setKeepAlive */ public static final SocketOption SO_KEEPALIVE = new StdSocketOption("SO_KEEPALIVE", Boolean.class); @@ -107,6 +109,8 @@ public final class StandardSocketOption { * socket is bound or connected. Whether an implementation allows the * socket send buffer to be changed after the socket is bound is system * dependent. + * + * @see Socket#setSendBufferSize */ public static final SocketOption SO_SNDBUF = new StdSocketOption("SO_SNDBUF", Integer.class); @@ -145,6 +149,8 @@ public final class StandardSocketOption { * * @see RFC 1323: TCP * Extensions for High Performance + * @see Socket#setReceiveBufferSize + * @see ServerSocket#setReceiveBufferSize */ public static final SocketOption SO_RCVBUF = new StdSocketOption("SO_RCVBUF", Integer.class); @@ -175,6 +181,7 @@ public final class StandardSocketOption { * * @see RFC 793: Transmission * Control Protocol + * @see ServerSocket#setReuseAddress */ public static final SocketOption SO_REUSEADDR = new StdSocketOption("SO_REUSEADDR", Boolean.class); @@ -205,6 +212,8 @@ public final class StandardSocketOption { * is system dependent. Setting the linger interval to a value that is * greater than its maximum value causes the linger interval to be set to * its maximum value. + * + * @see Socket#setSoLinger */ public static final SocketOption SO_LINGER = new StdSocketOption("SO_LINGER", Integer.class); @@ -215,15 +224,15 @@ public final class StandardSocketOption { /** * The Type of Service (ToS) octet in the Internet Protocol (IP) header. * - *

The value of this socket option is an {@code Integer}, the least - * significant 8 bits of which represents the value of the ToS octet in IP - * packets sent by sockets to an {@link StandardProtocolFamily#INET IPv4} - * socket. The interpretation of the ToS octet is network specific and - * is not defined by this class. Further information on the ToS octet can be - * found in RFC 1349 - * and RFC 2474. The - * value of the socket option is a hint. An implementation may - * ignore the value, or ignore specific values. + *

The value of this socket option is an {@code Integer} representing + * the value of the ToS octet in IP packets sent by sockets to an {@link + * StandardProtocolFamily#INET IPv4} socket. The interpretation of the ToS + * octet is network specific and is not defined by this class. Further + * information on the ToS octet can be found in RFC 1349 and RFC 2474. The value + * of the socket option is a hint. An implementation may ignore the + * value, or ignore specific values. * *

The initial/default value of the TOS field in the ToS octet is * implementation specific but will typically be {@code 0}. For @@ -235,6 +244,8 @@ public final class StandardSocketOption { *

The behavior of this socket option on a stream-oriented socket, or an * {@link StandardProtocolFamily#INET6 IPv6} socket, is not defined in this * release. + * + * @see DatagramSocket#setTrafficClass */ public static final SocketOption IP_TOS = new StdSocketOption("IP_TOS", Integer.class); @@ -257,6 +268,7 @@ public final class StandardSocketOption { * is system dependent. * * @see java.nio.channels.MulticastChannel + * @see MulticastSocket#setInterface */ public static final SocketOption IP_MULTICAST_IF = new StdSocketOption("IP_MULTICAST_IF", NetworkInterface.class); @@ -283,6 +295,7 @@ public final class StandardSocketOption { * prior to binding the socket is system dependent. * * @see java.nio.channels.MulticastChannel + * @see MulticastSocket#setTimeToLive */ public static final SocketOption IP_MULTICAST_TTL = new StdSocketOption("IP_MULTICAST_TTL", Integer.class); @@ -307,6 +320,7 @@ public final class StandardSocketOption { * binding the socket is system dependent. * * @see java.nio.channels.MulticastChannel + * @see MulticastSocket#setLoopbackMode */ public static final SocketOption IP_MULTICAST_LOOP = new StdSocketOption("IP_MULTICAST_LOOP", Boolean.class); @@ -328,11 +342,12 @@ public final class StandardSocketOption { * coalescing impacts performance. The socket option may be enabled at any * time. In other words, the Nagle Algorithm can be disabled. Once the option * is enabled, it is system dependent whether it can be subsequently - * disabled. In that case, invoking the {@code setOption} method to disable - * the option has no effect. + * disabled. If it cannot, then invoking the {@code setOption} method to + * disable the option has no effect. * * @see RFC 1122: * Requirements for Internet Hosts -- Communication Layers + * @see Socket#setTcpNoDelay */ public static final SocketOption TCP_NODELAY = new StdSocketOption("TCP_NODELAY", Boolean.class); diff --git a/src/share/classes/java/nio/channels/AsynchronousByteChannel.java b/src/share/classes/java/nio/channels/AsynchronousByteChannel.java new file mode 100644 index 000000000..7bc433574 --- /dev/null +++ b/src/share/classes/java/nio/channels/AsynchronousByteChannel.java @@ -0,0 +1,205 @@ +/* + * Copyright 2007-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 java.nio.channels; + +import java.nio.ByteBuffer; +import java.util.concurrent.Future; + +/** + * An asynchronous channel that can read and write bytes. + * + *

Some channels may not allow more than one read or write to be outstanding + * at any given time. If a thread invokes a read method before a previous read + * operation has completed then a {@link ReadPendingException} will be thrown. + * Similarly, if a write method is invoked before a previous write has completed + * then {@link WritePendingException} is thrown. Whether or not other kinds of + * I/O operations may proceed concurrently with a read operation depends upon + * the type of the channel. + * + *

Note that {@link java.nio.ByteBuffer ByteBuffers} are not safe for use by + * multiple concurrent threads. When a read or write operation is initiated then + * care must be taken to ensure that the buffer is not accessed until the + * operation completes. + * + * @see Channels#newInputStream(AsynchronousByteChannel) + * @see Channels#newOutputStream(AsynchronousByteChannel) + * + * @since 1.7 + */ + +public interface AsynchronousByteChannel + extends AsynchronousChannel +{ + /** + * Reads a sequence of bytes from this channel into the given buffer. + * + *

This method initiates an operation to read a sequence of bytes from + * this channel into the given buffer. The method returns a {@link Future} + * representing the pending result of the operation. The result of the + * operation, obtained by invoking the {@code Future} 's {@link + * Future#get() get} method, is the number of bytes read or {@code -1} if + * all bytes have been read and the channel has reached end-of-stream. + * + *

This method initiates a read operation to read up to r bytes + * from the channel, where r is the number of bytes remaining in the + * buffer, that is, {@code dst.remaining()} at the time that the read is + * attempted. Where r is 0, the read operation completes immediately + * with a result of {@code 0} without initiating an I/O operation. + * + *

Suppose that a byte sequence of length n is read, where + * 0 < n <= r. + * This byte sequence will be transferred into the buffer so that the first + * byte in the sequence is at index p and the last byte is at index + * p + n - 1, + * where p is the buffer's position at the moment the read is + * performed. Upon completion the buffer's position will be equal to + * p + n; its limit will not have changed. + * + *

Buffers are not safe for use by multiple concurrent threads so care + * should be taken to not to access the buffer until the operaton has completed. + * + *

This method may be invoked at any time. Some channel types may not + * allow more than one read to be outstanding at any given time. If a thread + * initiates a read operation before a previous read operation has + * completed then a {@link ReadPendingException} will be thrown. + * + *

The handler parameter is used to specify a {@link + * CompletionHandler}. When the read operation completes the handler's + * {@link CompletionHandler#completed completed} method is executed. + * + * + * @param dst + * The buffer into which bytes are to be transferred + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The completion handler object; can be {@code null} + * + * @return A Future representing the result of the operation + * + * @throws IllegalArgumentException + * If the buffer is read-only + * @throws ReadPendingException + * If the channel does not allow more than one read to be outstanding + * and a previous read has not completed + */ + Future read(ByteBuffer dst, + A attachment, + CompletionHandler handler); + + /** + * Reads a sequence of bytes from this channel into the given buffer. + * + *

An invocation of this method of the form c.read(dst) + * behaves in exactly the same manner as the invocation + *

+     * c.read(dst, null, null);
+ * + * @param dst + * The buffer into which bytes are to be transferred + * + * @return A Future representing the result of the operation + * + * @throws IllegalArgumentException + * If the buffer is read-only + * @throws ReadPendingException + * If the channel does not allow more than one read to be outstanding + * and a previous read has not completed + */ + Future read(ByteBuffer dst); + + /** + * Writes a sequence of bytes to this channel from the given buffer. + * + *

This method initiates an operation to write a sequence of bytes to + * this channel from the given buffer. This method returns a {@link + * Future} representing the pending result of the operation. The result + * of the operation, obtained by invoking the Future's {@link + * Future#get() get} method, is the number of bytes written, possibly zero. + * + *

This method initiates a write operation to write up to r bytes + * to the channel, where r is the number of bytes remaining in the + * buffer, that is, {@code src.remaining()} at the moment the write is + * attempted. Where r is 0, the write operation completes immediately + * with a result of {@code 0} without initiating an I/O operation. + * + *

Suppose that a byte sequence of length n is written, where + * 0 < n <= r. + * This byte sequence will be transferred from the buffer starting at index + * p, where p is the buffer's position at the moment the + * write is performed; the index of the last byte written will be + * p + n - 1. + * Upon completion the buffer's position will be equal to + * p + n; its limit will not have changed. + * + *

Buffers are not safe for use by multiple concurrent threads so care + * should be taken to not to access the buffer until the operaton has completed. + * + *

This method may be invoked at any time. Some channel types may not + * allow more than one write to be outstanding at any given time. If a thread + * initiates a write operation before a previous write operation has + * completed then a {@link WritePendingException} will be thrown. + * + *

The handler parameter is used to specify a {@link + * CompletionHandler}. When the write operation completes the handler's + * {@link CompletionHandler#completed completed} method is executed. + * + * @param src + * The buffer from which bytes are to be retrieved + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The completion handler object; can be {@code null} + * + * @return A Future representing the result of the operation + * + * @throws WritePendingException + * If the channel does not allow more than one write to be outstanding + * and a previous write has not completed + */ + Future write(ByteBuffer src, + A attachment, + CompletionHandler handler); + + /** + * Writes a sequence of bytes to this channel from the given buffer. + * + *

An invocation of this method of the form c.write(src) + * behaves in exactly the same manner as the invocation + *

+     * c.write(src, null, null);
+ * + * @param src + * The buffer from which bytes are to be retrieved + * + * @return A Future representing the result of the operation + * + * @throws WritePendingException + * If the channel does not allow more than one write to be outstanding + * and a previous write has not completed + */ + Future write(ByteBuffer src); +} diff --git a/src/share/classes/java/nio/channels/AsynchronousChannel.java b/src/share/classes/java/nio/channels/AsynchronousChannel.java new file mode 100644 index 000000000..f3e4ffe4e --- /dev/null +++ b/src/share/classes/java/nio/channels/AsynchronousChannel.java @@ -0,0 +1,116 @@ +/* + * Copyright 2007-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 java.nio.channels; + +import java.io.IOException; +import java.util.concurrent.Future; // javadoc + +/** + * A channel that supports asynchronous I/O operations. Asynchronous I/O + * operations will usually take one of two forms: + * + *
    + *
  1. {@link Future}<V> operation(...)
  2. + *
  3. Future<V> operation(... A attachment, {@link CompletionHandler}<V,? super A> handler)
  4. + *
+ * + * where operation is the name of the I/O operation (read or write for + * example), V is the result type of the I/O operation, and A is + * the type of an object attached to the I/O operation to provide context when + * consuming the result. The attachment is important for cases where a + * state-less {@code CompletionHandler} is used to consume the result + * of many I/O operations. + * + *

In the first form, the methods defined by the {@link Future Future} + * interface may be used to check if the operation has completed, wait for its + * completion, and to retrieve the result. In the second form, a {@link + * CompletionHandler} is invoked to consume the result of the I/O operation when + * it completes, fails, or is cancelled. + * + *

A channel that implements this interface is asynchronously + * closeable: If an I/O operation is outstanding on the channel and the + * channel's {@link #close close} method is invoked, then the I/O operation + * fails with the exception {@link AsynchronousCloseException}. + * + *

Asynchronous channels are safe for use by multiple concurrent threads. + * Some channel implementations may support concurrent reading and writing, but + * may not allow more than one read and one write operation to be outstanding at + * any given time. + * + *

Cancellation

+ * + *

The {@code Future} interface defines the {@link Future#cancel cancel} + * method to cancel execution of a task. + * + *

Where the {@code cancel} method is invoked with the {@code + * mayInterruptIfRunning} parameter set to {@code true} then the I/O operation + * may be interrupted by closing the channel. This will cause any other I/O + * operations outstanding on the channel to complete with the exception {@link + * AsynchronousCloseException}. + * + *

If a {@code CompletionHandler} is specified when initiating an I/O + * operation, and the {@code cancel} method is invoked to cancel the I/O + * operation before it completes, then the {@code CompletionHandler}'s {@link + * CompletionHandler#cancelled cancelled} method is invoked. + * + *

If an implementation of this interface supports a means to cancel I/O + * operations, and where cancellation may leave the channel, or the entity to + * which it is connected, in an inconsistent state, then the channel is put into + * an implementation specific error state that prevents further + * attempts to initiate I/O operations on the channel. For example, if a read + * operation is cancelled but the implementation cannot guarantee that bytes + * have not been read from the channel then it puts the channel into error state + * state; further attempts to initiate a {@code read} operation causes an + * unspecified runtime exception to be thrown. + * + *

Where the {@code cancel} method is invoked to cancel read or write + * operations then it recommended that all buffers used in the I/O operations be + * discarded or care taken to ensure that the buffers are not accessed while the + * channel remains open. + * + * @since 1.7 + */ + +public interface AsynchronousChannel + extends Channel +{ + /** + * Closes this channel. + * + *

Any outstanding asynchronous operations upon this channel will + * complete with the exception {@link AsynchronousCloseException}. After a + * channel is closed then further attempts to initiate asynchronous I/O + * operations complete immediately with cause {@link ClosedChannelException}. + * + *

This method otherwise behaves exactly as specified by the {@link + * Channel} interface. + * + * @throws IOException + * If an I/O error occurs + */ + @Override + void close() throws IOException; +} diff --git a/src/share/classes/java/nio/channels/AsynchronousChannelGroup.java b/src/share/classes/java/nio/channels/AsynchronousChannelGroup.java new file mode 100644 index 000000000..1199e16b5 --- /dev/null +++ b/src/share/classes/java/nio/channels/AsynchronousChannelGroup.java @@ -0,0 +1,344 @@ +/* + * Copyright 2007-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 java.nio.channels; + +import java.nio.channels.spi.AsynchronousChannelProvider; +import java.io.IOException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +/** + * A grouping of asynchronous channels for the purpose of resource sharing. + * + *

An asynchronous channel group encapsulates the mechanics required to + * handle the completion of I/O operations initiated by {@link AsynchronousChannel + * asynchronous channels} that are bound to the group. A group has an associated + * thread pool to which tasks are submitted to handle I/O events and dispatch to + * {@link CompletionHandler completion-handlers} that consume the result of + * asynchronous operations performed on channels in the group. In addition to + * handling I/O events, the pooled threads may also execute other tasks required + * to support the execution of asynchronous I/O operations. + * + *

An asynchronous channel group is created by invoking the {@link + * #withFixedThreadPool withFixedThreadPool} or {@link #withCachedThreadPool + * withCachedThreadPool} methods defined here. Channels are bound to a group by + * specifying the group when constructing the channel. The associated thread + * pool is owned by the group; termination of the group results in the + * shutdown of the associated thread pool. + * + *

In addition to groups created explicitly, the Java virtual machine + * maintains a system-wide default group that is constructed + * automatically. Asynchronous channels that do not specify a group at + * construction time are bound to the default group. The default group has an + * associated thread pool that creates new threads as needed. The default group + * may be configured by means of system properties defined in the table below. + * Where the {@link java.util.concurrent.ThreadFactory ThreadFactory} for the + * default group is not configured then the pooled threads of the default group + * are {@link Thread#isDaemon daemon} threads. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
System propertyDescription
{@code java.nio.channels.DefaultThreadPool.threadFactory} The value of this property is taken to be the fully-qualified name + * of a concrete {@link java.util.concurrent.ThreadFactory ThreadFactory} + * class. The class is loaded using the system class loader and instantiated. + * The factory's {@link java.util.concurrent.ThreadFactory#newThread + * newThread} method is invoked to create each thread for the default + * group's thread pool. If the process to load and instantiate the value + * of the property fails then an unspecified error is thrown during the + * construction of the default group.
{@code java.nio.channels.DefaultThreadPool.initialSize} The value of the {@code initialSize} parameter for the default + * group (see {@link #withCachedThreadPool withCachedThreadPool}). + * The value of the property is taken to be the {@code String} + * representation of an {@code Integer} that is the initial size parameter. + * If the value cannot be parsed as an {@code Integer} it causes an + * unspecified error to be thrown during the construction of the default + * group.
+ * + *

Threading

+ * + *

The completion handler for an I/O operation initiated on a channel bound + * to a group is guaranteed to be invoked by one of the pooled threads in the + * group. This ensures that the completion handler is run by a thread with the + * expected identity. + * + *

Where an I/O operation completes immediately, and the initiating thread + * is one of the pooled threads in the group then the completion handler may + * be invoked directly by the initiating thread. To avoid stack overflow, an + * implementation may impose a limit as to the number of activations on the + * thread stack. Some I/O operations may prohibit invoking the completion + * handler directly by the initiating thread (see {@link + * AsynchronousServerSocketChannel#accept(Object,CompletionHandler) accept}). + * + *

Shutdown and Termination

+ * + *

The {@link #shutdown() shutdown} method is used to initiate an orderly + * shutdown of a group. An orderly shutdown marks the group as shutdown; + * further attempts to construct a channel that binds to the group will throw + * {@link ShutdownChannelGroupException}. Whether or not a group is shutdown can + * be tested using the {@link #isShutdown() isShutdown} method. Once shutdown, + * the group terminates when all asynchronous channels that are bound to + * the group are closed, all actively executing completion handlers have run to + * completion, and resources used by the group are released. No attempt is made + * to stop or interrupt threads that are executing completion handlers. The + * {@link #isTerminated() isTerminated} method is used to test if the group has + * terminated, and the {@link #awaitTermination awaitTermination} method can be + * used to block until the group has terminated. + * + *

The {@link #shutdownNow() shutdownNow} method can be used to initiate a + * forceful shutdown of the group. In addition to the actions performed + * by an orderly shutdown, the {@code shutdownNow} method closes all open channels + * in the group as if by invoking the {@link AsynchronousChannel#close close} + * method. + * + * @since 1.7 + * + * @see AsynchronousSocketChannel#open(AsynchronousChannelGroup) + * @see AsynchronousServerSocketChannel#open(AsynchronousChannelGroup) + */ + +public abstract class AsynchronousChannelGroup { + private final AsynchronousChannelProvider provider; + + /** + * Initialize a new instance of this class. + * + * @param provider + * The asynchronous channel provider for this group + */ + protected AsynchronousChannelGroup(AsynchronousChannelProvider provider) { + this.provider = provider; + } + + /** + * Returns the provider that created this channel group. + * + * @return The provider that created this channel group + */ + public final AsynchronousChannelProvider provider() { + return provider; + } + + /** + * Creates an asynchronous channel group with a fixed thread pool. + * + *

The resulting asynchronous channel group reuses a fixed number of + * threads. At any point, at most {@code nThreads} threads will be active + * processing tasks that are submitted to handle I/O events and dispatch + * completion results for operations initiated on asynchronous channels in + * the group. + * + *

The group is created by invoking the {@link + * AsynchronousChannelProvider#openAsynchronousChannelGroup(int,ThreadFactory) + * openAsynchronousChannelGroup(int,ThreadFactory)} method of the system-wide + * default {@link AsynchronousChannelProvider} object. + * + * @param nThreads + * The number of threads in the pool + * @param threadFactory + * The factory to use when creating new threads + * + * @return A new asynchronous channel group + * + * @throws IllegalArgumentException + * If {@code nThreads <= 0} + * @throws IOException + * If an I/O error occurs + */ + public static AsynchronousChannelGroup withFixedThreadPool(int nThreads, + ThreadFactory threadFactory) + throws IOException + { + return AsynchronousChannelProvider.provider() + .openAsynchronousChannelGroup(nThreads, threadFactory); + } + + /** + * Creates an asynchronous channel group with a given thread pool that + * creates new threads as needed. + * + *

The {@code executor} parameter is an {@code ExecutorService} that + * creates new threads as needed to execute tasks that are submitted to + * handle I/O events and dispatch completion results for operations initiated + * on asynchronous channels in the group. It may reuse previously constructed + * threads when they are available. + * + *

The {@code initialSize} parameter may be used by the implementation + * as a hint as to the initial number of tasks it may submit. For + * example, it may be used to indictae the initial number of threads that + * wait on I/O events. + * + *

The executor is intended to be used exclusively by the resulting + * asynchronous channel group. Termination of the group results in the + * orderly {@link ExecutorService#shutdown shutdown} of the executor + * service. Shutting down the executor service by other means results in + * unspecified behavior. + * + *

The group is created by invoking the {@link + * AsynchronousChannelProvider#openAsynchronousChannelGroup(ExecutorService,int) + * openAsynchronousChannelGroup(ExecutorService,int)} method of the system-wide + * default {@link AsynchronousChannelProvider} object. + * + * @param executor + * The thread pool for the resulting group + * @param initialSize + * A value {@code >=0} or a negative value for implementation + * specific default + * + * @return A new asynchronous channel group + * + * @throws IOException + * If an I/O error occurs + * + * @see java.util.concurrent.Executors#newCachedThreadPool + */ + public static AsynchronousChannelGroup withCachedThreadPool(ExecutorService executor, + int initialSize) + throws IOException + { + return AsynchronousChannelProvider.provider() + .openAsynchronousChannelGroup(executor, initialSize); + } + + /** + * Creates an asynchronous channel group with a given thread pool. + * + *

The {@code executor} parameter is an {@code ExecutorService} that + * executes tasks submitted to dispatch completion results for operations + * initiated on asynchronous channels in the group. + * + *

Care should be taken when configuring the executor service. It + * should support direct handoff or unbounded queuing of + * submitted tasks, and the thread that invokes the {@link + * ExecutorService#execute execute} method should never invoke the task + * directly. An implementation may mandate additional constraints. + * + *

The executor is intended to be used exclusively by the resulting + * asynchronous channel group. Termination of the group results in the + * orderly {@link ExecutorService#shutdown shutdown} of the executor + * service. Shutting down the executor service by other means results in + * unspecified behavior. + * + *

The group is created by invoking the {@link + * AsynchronousChannelProvider#openAsynchronousChannelGroup(ExecutorService,int) + * openAsynchronousChannelGroup(ExecutorService,int)} method of the system-wide + * default {@link AsynchronousChannelProvider} object with an {@code + * initialSize} of {@code 0}. + * + * @param executor + * The thread pool for the resulting group + * + * @return A new asynchronous channel group + * + * @throws IOException + * If an I/O error occurs + */ + public static AsynchronousChannelGroup withThreadPool(ExecutorService executor) + throws IOException + { + return AsynchronousChannelProvider.provider() + .openAsynchronousChannelGroup(executor, 0); + } + + /** + * Tells whether or not this asynchronous channel group is shutdown. + * + * @return {@code true} if this asynchronous channel group is shutdown or + * has been marked for shutdown. + */ + public abstract boolean isShutdown(); + + /** + * Tells whether or not this group has terminated. + * + *

Where this method returns {@code true}, then the associated thread + * pool has also {@link ExecutorService#isTerminated terminated}. + * + * @return {@code true} if this group has terminated + */ + public abstract boolean isTerminated(); + + /** + * Initiates an orderly shutdown of the group. + * + *

This method marks the group as shutdown. Further attempts to construct + * channel that binds to this group will throw {@link ShutdownChannelGroupException}. + * The group terminates when all asynchronous channels in the group are + * closed, all actively executing completion handlers have run to completion, + * and all resources have been released. This method has no effect if the + * group is already shutdown. + */ + public abstract void shutdown(); + + /** + * Shuts down the group and closes all open channels in the group. + * + *

In addition to the actions performed by the {@link #shutdown() shutdown} + * method, this method invokes the {@link AsynchronousChannel#close close} + * method on all open channels in the group. This method does not attempt to + * stop or interrupt threads that are executing completion handlers. The + * group terminates when all actively executing completion handlers have run + * to completion and all resources have been released. This method may be + * invoked at any time. If some other thread has already invoked it, then + * another invocation will block until the first invocation is complete, + * after which it will return without effect. + * + * @throws IOException + * If an I/O error occurs + */ + public abstract void shutdownNow() throws IOException; + + /** + * Awaits termination of the group. + + *

This method blocks until the group has terminated, or the timeout + * occurs, or the current thread is interrupted, whichever happens first. + * + * @param timeout + * The maximum time to wait, or zero or less to not wait + * @param unit + * The time unit of the timeout argument + * + * @return {@code true} if the group has terminated; {@code false} if the + * timeout elapsed before termination + * + * @throws InterruptedException + * If interrupted while waiting + */ + public abstract boolean awaitTermination(long timeout, TimeUnit unit) + throws InterruptedException; +} diff --git a/src/share/classes/java/nio/channels/AsynchronousDatagramChannel.java b/src/share/classes/java/nio/channels/AsynchronousDatagramChannel.java new file mode 100644 index 000000000..6a9d9f097 --- /dev/null +++ b/src/share/classes/java/nio/channels/AsynchronousDatagramChannel.java @@ -0,0 +1,718 @@ +/* + * Copyright 2007-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 java.nio.channels; + +import java.nio.channels.spi.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.Future; +import java.io.IOException; +import java.net.SocketOption; +import java.net.SocketAddress; +import java.net.ProtocolFamily; +import java.nio.ByteBuffer; + +/** + * An asynchronous channel for datagram-oriented sockets. + * + *

An asynchronous datagram channel is created by invoking one of the {@link + * #open open} methods defined by this class. It is not possible to create a channel + * for an arbitrary, pre-existing datagram socket. A newly-created asynchronous + * datagram channel is open but not connected. It need not be connected in order + * for the {@link #send send} and {@link #receive receive} methods to be used. + * A datagram channel may be connected, by invoking its {@link #connect connect} + * method, in order to avoid the overhead of the security checks that are otherwise + * performed as part of every send and receive operation when a security manager + * is set. The channel must be connected in order to use the {@link #read read} + * and {@link #write write} methods, since those methods do not accept or return + * socket addresses. Once connected, an asynchronous datagram channel remains + * connected until it is disconnected or closed. + * + *

Socket options are configured using the {@link #setOption(SocketOption,Object) + * setOption} method. An asynchronous datagram channel to an Internet Protocol + * (IP) socket supports the following options: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Option NameDescription
{@link java.net.StandardSocketOption#SO_SNDBUF SO_SNDBUF} The size of the socket send buffer
{@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} The size of the socket receive buffer
{@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} Re-use address
{@link java.net.StandardSocketOption#SO_BROADCAST SO_BROADCAST} Allow transmission of broadcast datagrams
{@link java.net.StandardSocketOption#IP_TOS IP_TOS} The Type of Service (ToS) octet in the Internet Protocol (IP) header
{@link java.net.StandardSocketOption#IP_MULTICAST_IF IP_MULTICAST_IF} The network interface for Internet Protocol (IP) multicast datagrams
{@link java.net.StandardSocketOption#IP_MULTICAST_TTL + * IP_MULTICAST_TTL} The time-to-live for Internet Protocol (IP) multicast + * datagrams
{@link java.net.StandardSocketOption#IP_MULTICAST_LOOP + * IP_MULTICAST_LOOP} Loopback for Internet Protocol (IP) multicast datagrams
+ *
+ * Additional (implementation specific) options may also be supported. + * + *

Asynchronous datagram channels allow more than one read/receive and + * write/send to be oustanding at any given time. + * + *

Usage Example: + *

+ *  final AsynchronousDatagramChannel dc = AsynchronousDatagramChannel.open()
+ *      .bind(new InetSocketAddress(4000));
+ *
+ *  // print the source address of all packets that we receive
+ *  dc.receive(buffer, buffer, new CompletionHandler<SocketAddress,ByteBuffer>() {
+ *      public void completed(SocketAddress sa, ByteBuffer buffer) {
+ *          try {
+ *               System.out.println(sa);
+ *
+ *               buffer.clear();
+ *               dc.receive(buffer, buffer, this);
+ *           } catch (...) { ... }
+ *      }
+ *      public void failed(Throwable exc, ByteBuffer buffer) {
+ *          ...
+ *      }
+ *      public void cancelled(ByteBuffer buffer) {
+ *          ...
+ *      }
+ *  });
+ * 
+ * + * @since 1.7 + */ + +public abstract class AsynchronousDatagramChannel + implements AsynchronousByteChannel, MulticastChannel +{ + private final AsynchronousChannelProvider provider; + + /** + * Initializes a new instance of this class. + */ + protected AsynchronousDatagramChannel(AsynchronousChannelProvider provider) { + this.provider = provider; + } + + /** + * Returns the provider that created this channel. + */ + public final AsynchronousChannelProvider provider() { + return provider; + } + + /** + * Opens an asynchronous datagram channel. + * + *

The new channel is created by invoking the {@link + * java.nio.channels.spi.AsynchronousChannelProvider#openAsynchronousDatagramChannel + * openAsynchronousDatagramChannel} method on the {@link + * java.nio.channels.spi.AsynchronousChannelProvider} object that created + * the given group (or the default provider where {@code group} is {@code + * null}). + * + *

The {@code family} parameter is used to specify the {@link ProtocolFamily}. + * If the datagram channel is to be used for Internet Protocol {@link + * MulticastChannel multicasting} then this parameter should correspond to + * the address type of the multicast groups that this channel will join. + * + * @param family + * The protocol family, or {@code null} to use the default protocol + * family + * @param group + * The group to which the newly constructed channel should be bound, + * or {@code null} for the default group + * + * @return A new asynchronous datagram channel + * + * @throws UnsupportedOperationException + * If the specified protocol family is not supported. For example, + * suppose the parameter is specified as {@link + * java.net.StandardProtocolFamily#INET6 INET6} but IPv6 is not + * enabled on the platform. + * @throws ShutdownChannelGroupException + * The specified group is shutdown + * @throws IOException + * If an I/O error occurs + */ + public static AsynchronousDatagramChannel open(ProtocolFamily family, + AsynchronousChannelGroup group) + throws IOException + { + AsynchronousChannelProvider provider = (group == null) ? + AsynchronousChannelProvider.provider() : group.provider(); + return provider.openAsynchronousDatagramChannel(family, group); + } + + /** + * Opens an asynchronous datagram channel. + * + *

This method returns an asynchronous datagram channel that is + * bound to the default group. This method is equivalent to evaluating + * the expression: + *

+     * open((ProtocolFamily)null, (AsynchronousChannelGroup)null);
+     * 
+ * + * @return A new asynchronous datagram channel + * + * @throws IOException + * If an I/O error occurs + */ + public static AsynchronousDatagramChannel open() + throws IOException + { + return open(null, null); + } + + // -- Socket-specific operations -- + + /** + * @throws AlreadyBoundException {@inheritDoc} + * @throws UnsupportedAddressTypeException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + * @throws SecurityException + * If a security manager has been installed and its {@link + * SecurityManager#checkListen checkListen} method denies the + * operation + */ + @Override + public abstract AsynchronousDatagramChannel bind(SocketAddress local) + throws IOException; + + /** + * @throws IllegalArgumentException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public abstract AsynchronousDatagramChannel setOption(SocketOption name, T value) + throws IOException; + + /** + * Returns the remote address to which this channel is connected. + * + *

Where the channel is connected to an Internet Protocol socket address + * then the return value from this method is of type {@link + * java.net.InetSocketAddress}. + * + * @return The remote address; {@code null} if the channel's socket is not + * connected + * + * @throws ClosedChannelException + * If the channel is closed + * @throws IOException + * If an I/O error occurs + */ + public abstract SocketAddress getRemoteAddress() throws IOException; + + /** + * Connects this channel's socket. + * + *

The channel's socket is configured so that it only receives + * datagrams from, and sends datagrams to, the given remote peer + * address. Once connected, datagrams may not be received from or sent to + * any other address. A datagram socket remains connected until it is + * explicitly disconnected or until it is closed. + * + *

This method performs exactly the same security checks as the {@link + * java.net.DatagramSocket#connect connect} method of the {@link + * java.net.DatagramSocket} class. That is, if a security manager has been + * installed then this method verifies that its {@link + * java.lang.SecurityManager#checkAccept checkAccept} and {@link + * java.lang.SecurityManager#checkConnect checkConnect} methods permit + * datagrams to be received from and sent to, respectively, the given + * remote address. + * + *

This method may be invoked at any time. Whether it has any effect + * on outstanding read or write operations is implementation specific and + * therefore not specified. + * + * @param remote + * The remote address to which this channel is to be connected + * + * @return This datagram channel + * + * @throws ClosedChannelException + * If this channel is closed + * + * @throws SecurityException + * If a security manager has been installed + * and it does not permit access to the given remote address + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract AsynchronousDatagramChannel connect(SocketAddress remote) + throws IOException; + + /** + * Disconnects this channel's socket. + * + *

The channel's socket is configured so that it can receive datagrams + * from, and sends datagrams to, any remote address so long as the security + * manager, if installed, permits it. + * + *

This method may be invoked at any time. Whether it has any effect + * on outstanding read or write operations is implementation specific and + * therefore not specified. + * + * @return This datagram channel + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract AsynchronousDatagramChannel disconnect() throws IOException; + + /** + * Receives a datagram via this channel. + * + *

This method initiates the receiving of a datagram, returning a + * {@code Future} representing the pending result of the operation. + * The {@code Future}'s {@link Future#get() get} method returns + * the source address of the datagram upon successful completion. + * + *

The datagram is transferred into the given byte buffer starting at + * its current position, as if by a regular {@link AsynchronousByteChannel#read + * read} operation. If there are fewer bytes remaining in the buffer + * than are required to hold the datagram then the remainder of the datagram + * is silently discarded. + * + *

If a timeout is specified and the timeout elapses before the operation + * completes then the operation completes with the exception {@link + * InterruptedByTimeoutException}. When a timeout elapses then the state of + * the {@link ByteBuffer} is not defined. The buffers should be discarded or + * at least care must be taken to ensure that the buffer is not accessed + * while the channel remains open. + * + *

When a security manager has been installed and the channel is not + * connected, then it verifies that the source's address and port number are + * permitted by the security manager's {@link SecurityManager#checkAccept + * checkAccept} method. The permission check is performed with privileges that + * are restricted by the calling context of this method. If the permission + * check fails then the operation completes with a {@link SecurityException}. + * The overhead of this security check can be avoided by first connecting the + * socket via the {@link #connect connect} method. + * + * @param dst + * The buffer into which the datagram is to be transferred + * @param timeout + * The timeout, or {@code 0L} for no timeout + * @param unit + * The time unit of the {@code timeout} argument + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The handler for consuming the result; can be {@code null} + * + * @return a {@code Future} object representing the pending result + * + * @throws IllegalArgumentException + * If the timeout is negative or the buffer is read-only + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + */ + public abstract Future receive(ByteBuffer dst, + long timeout, + TimeUnit unit, + A attachment, + CompletionHandler handler); + + /** + * Receives a datagram via this channel. + * + *

This method initiates the receiving of a datagram, returning a + * {@code Future} representing the pending result of the operation. + * The {@code Future}'s {@link Future#get() get} method returns + * the source address of the datagram upon successful completion. + * + *

This method is equivalent to invoking {@link + * #receive(ByteBuffer,long,TimeUnit,Object,CompletionHandler)} with a + * timeout of {@code 0L}. + * + * @param dst + * The buffer into which the datagram is to be transferred + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The handler for consuming the result; can be {@code null} + * + * @return a {@code Future} object representing the pending result + * + * @throws IllegalArgumentException + * If the buffer is read-only + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + */ + public final Future receive(ByteBuffer dst, + A attachment, + CompletionHandler handler) + { + return receive(dst, 0L, TimeUnit.MILLISECONDS, attachment, handler); + } + + /** + * Receives a datagram via this channel. + * + *

This method initiates the receiving of a datagram, returning a + * {@code Future} representing the pending result of the operation. + * The {@code Future}'s {@link Future#get() get} method returns + * the source address of the datagram upon successful completion. + * + *

This method is equivalent to invoking {@link + * #receive(ByteBuffer,long,TimeUnit,Object,CompletionHandler)} with a + * timeout of {@code 0L}, and an attachment and completion handler + * of {@code null}. + * + * @param dst + * The buffer into which the datagram is to be transferred + * + * @return a {@code Future} object representing the pending result + * + * @throws IllegalArgumentException + * If the buffer is read-only + */ + public final Future receive(ByteBuffer dst) { + return receive(dst, 0L, TimeUnit.MILLISECONDS, null, null); + } + + /** + * Sends a datagram via this channel. + * + *

This method initiates sending of a datagram, returning a + * {@code Future} representing the pending result of the operation. + * The operation sends the remaining bytes in the given buffer as a single + * datagram to the given target address. The result of the operation, obtained + * by invoking the {@code Future}'s {@link Future#get() get} + * method, is the number of bytes sent. + * + *

The datagram is transferred from the byte buffer as if by a regular + * {@link AsynchronousByteChannel#write write} operation. + * + *

If a timeout is specified and the timeout elapses before the operation + * completes then the operation completes with the exception {@link + * InterruptedByTimeoutException}. When a timeout elapses then the state of + * the {@link ByteBuffer} is not defined. The buffers should be discarded or + * at least care must be taken to ensure that the buffer is not accessed + * while the channel remains open. + * + *

If there is a security manager installed and the the channel is not + * connected then this method verifies that the target address and port number + * are permitted by the security manager's {@link SecurityManager#checkConnect + * checkConnect} method. The overhead of this security check can be avoided + * by first connecting the socket via the {@link #connect connect} method. + * + * @param src + * The buffer containing the datagram to be sent + * @param target + * The address to which the datagram is to be sent + * @param timeout + * The timeout, or {@code 0L} for no timeout + * @param unit + * The time unit of the {@code timeout} argument + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The handler for consuming the result; can be {@code null} + * + * @return a {@code Future} object representing the pending result + * + * @throws UnresolvedAddressException + * If the given remote address is not fully resolved + * @throws UnsupportedAddressTypeException + * If the type of the given remote address is not supported + * @throws IllegalArgumentException + * If the timeout is negative, or if the channel's socket is + * connected to an address that is not equal to {@code target} + * @throws SecurityException + * If a security manager has been installed and it does not permit + * datagrams to be sent to the given address + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + */ + public abstract Future send(ByteBuffer src, + SocketAddress target, + long timeout, + TimeUnit unit, + A attachment, + CompletionHandler handler); + + /** + * Sends a datagram via this channel. + * + *

This method initiates sending of a datagram, returning a + * {@code Future} representing the pending result of the operation. + * The operation sends the remaining bytes in the given buffer as a single + * datagram to the given target address. The result of the operation, obtained + * by invoking the {@code Future}'s {@link Future#get() get} + * method, is the number of bytes sent. + * + *

This method is equivalent to invoking {@link + * #send(ByteBuffer,SocketAddress,long,TimeUnit,Object,CompletionHandler)} + * with a timeout of {@code 0L}. + * + * @param src + * The buffer containing the datagram to be sent + * @param target + * The address to which the datagram is to be sent + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The handler for consuming the result; can be {@code null} + * + * @return a {@code Future} object representing the pending result + * + * @throws UnresolvedAddressException + * If the given remote address is not fully resolved + * @throws UnsupportedAddressTypeException + * If the type of the given remote address is not supported + * @throws IllegalArgumentException + * If the channel's socket is connected and is connected to an + * address that is not equal to {@code target} + * @throws SecurityException + * If a security manager has been installed and it does not permit + * datagrams to be sent to the given address + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + */ + public final Future send(ByteBuffer src, + SocketAddress target, + A attachment, + CompletionHandler handler) + { + return send(src, target, 0L, TimeUnit.MILLISECONDS, attachment, handler); + } + + /** + * Sends a datagram via this channel. + * + *

This method initiates sending of a datagram, returning a + * {@code Future} representing the pending result of the operation. + * The operation sends the remaining bytes in the given buffer as a single + * datagram to the given target address. The result of the operation, obtained + * by invoking the {@code Future}'s {@link Future#get() get} + * method, is the number of bytes sent. + * + *

This method is equivalent to invoking {@link + * #send(ByteBuffer,SocketAddress,long,TimeUnit,Object,CompletionHandler)} + * with a timeout of {@code 0L} and an attachment and completion handler + * of {@code null}. + * + * @param src + * The buffer containing the datagram to be sent + * @param target + * The address to which the datagram is to be sent + * + * @return a {@code Future} object representing the pending result + * + * @throws UnresolvedAddressException + * If the given remote address is not fully resolved + * @throws UnsupportedAddressTypeException + * If the type of the given remote address is not supported + * @throws IllegalArgumentException + * If the channel's socket is connected and is connected to an + * address that is not equal to {@code target} + * @throws SecurityException + * If a security manager has been installed and it does not permit + * datagrams to be sent to the given address + */ + public final Future send(ByteBuffer src, SocketAddress target) { + return send(src, target, 0L, TimeUnit.MILLISECONDS, null, null); + } + + /** + * Receives a datagram via this channel. + * + *

This method initiates the receiving of a datagram, returning a + * {@code Future} representing the pending result of the operation. + * The {@code Future}'s {@link Future#get() get} method returns + * the number of bytes transferred upon successful completion. + * + *

This method may only be invoked if this channel is connected, and it + * only accepts datagrams from the peer that the channel is connected too. + * The datagram is transferred into the given byte buffer starting at + * its current position and exactly as specified in the {@link + * AsynchronousByteChannel} interface. If there are fewer bytes + * remaining in the buffer than are required to hold the datagram then the + * remainder of the datagram is silently discarded. + * + *

If a timeout is specified and the timeout elapses before the operation + * completes then the operation completes with the exception {@link + * InterruptedByTimeoutException}. When a timeout elapses then the state of + * the {@link ByteBuffer} is not defined. The buffers should be discarded or + * at least care must be taken to ensure that the buffer is not accessed + * while the channel remains open. + * + * @param dst + * The buffer into which the datagram is to be transferred + * @param timeout + * The timeout, or {@code 0L} for no timeout + * @param unit + * The time unit of the {@code timeout} argument + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The handler for consuming the result; can be {@code null} + * + * @return a {@code Future} object representing the pending result + * + * @throws IllegalArgumentException + * If the timeout is negative or buffer is read-only + * @throws NotYetConnectedException + * If this channel is not connected + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + */ + public abstract Future read(ByteBuffer dst, + long timeout, + TimeUnit unit, + A attachment, + CompletionHandler handler); + + /** + * @throws NotYetConnectedException + * If this channel is not connected + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + */ + @Override + public final Future read(ByteBuffer dst, + A attachment, + CompletionHandler handler) + { + return read(dst, 0L, TimeUnit.MILLISECONDS, attachment, handler); + } + + /** + * @throws NotYetConnectedException + * If this channel is not connected + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + */ + @Override + public final Future read(ByteBuffer dst) { + return read(dst, 0L, TimeUnit.MILLISECONDS, null, null); + } + + /** + * Writes a datagram to this channel. + * + *

This method initiates sending of a datagram, returning a + * {@code Future} representing the pending result of the operation. + * The operation sends the remaining bytes in the given buffer as a single + * datagram. The result of the operation, obtained by invoking the + * {@code Future}'s {@link Future#get() get} method, is the + * number of bytes sent. + * + *

The datagram is transferred from the byte buffer as if by a regular + * {@link AsynchronousByteChannel#write write} operation. + * + *

This method may only be invoked if this channel is connected, + * in which case it sends datagrams directly to the socket's peer. Otherwise + * it behaves exactly as specified in the {@link + * AsynchronousByteChannel} interface. + * + *

If a timeout is specified and the timeout elapses before the operation + * completes then the operation completes with the exception {@link + * InterruptedByTimeoutException}. When a timeout elapses then the state of + * the {@link ByteBuffer} is not defined. The buffers should be discarded or + * at least care must be taken to ensure that the buffer is not accessed + * while the channel remains open. + * + * @param src + * The buffer containing the datagram to be sent + * @param timeout + * The timeout, or {@code 0L} for no timeout + * @param unit + * The time unit of the {@code timeout} argument + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The handler for consuming the result; can be {@code null} + * + * @return a {@code Future} object representing the pending result + * + * @throws IllegalArgumentException + * If the timeout is negative + * @throws NotYetConnectedException + * If this channel is not connected + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + */ + public abstract Future write(ByteBuffer src, + long timeout, + TimeUnit unit, + A attachment, + CompletionHandler handler); + /** + * @throws NotYetConnectedException + * If this channel is not connected + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + */ + @Override + public final Future write(ByteBuffer src, + A attachment, + CompletionHandler handler) + { + return write(src, 0L, TimeUnit.MILLISECONDS, attachment, handler); + } + + /** + * @throws NotYetConnectedException + * If this channel is not connected + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + */ + @Override + public final Future write(ByteBuffer src) { + return write(src, 0L, TimeUnit.MILLISECONDS, null, null); + } +} diff --git a/src/share/classes/java/nio/channels/AsynchronousFileChannel.java b/src/share/classes/java/nio/channels/AsynchronousFileChannel.java new file mode 100644 index 000000000..a9bff5f16 --- /dev/null +++ b/src/share/classes/java/nio/channels/AsynchronousFileChannel.java @@ -0,0 +1,774 @@ +/* + * Copyright 2007-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 java.nio.channels; + +import java.nio.file.*; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.spi.*; +import java.nio.ByteBuffer; +import java.io.IOException; +import java.util.concurrent.Future; +import java.util.concurrent.ExecutorService; +import java.util.Set; +import java.util.HashSet; +import java.util.Collections; + +/** + * An asynchronous channel for reading, writing, and manipulating a file. + * + *

An asynchronous file channel is created when a file is opened by invoking + * one of the {@link #open open} methods defined by this class. The file contains + * a variable-length sequence of bytes that can be read and written and whose + * current size can be {@link #size() queried}. The size of the file increases + * when bytes are written beyond its current size; the size of the file decreases + * when it is {@link #truncate truncated}. + * + *

An asynchronous file channel does not have a current position + * within the file. Instead, the file position is specified to each read and + * write operation. + * + *

In addition to read and write operations, this class defines the + * following operations:

+ * + *
    + * + *
  • Updates made to a file may be {@link #force forced + * out} to the underlying storage device, ensuring that data are not + * lost in the event of a system crash.

  • + * + *
  • A region of a file may be {@link FileLock locked} + * against access by other programs.

  • + * + *
+ * + *

The {@link #read read}, {@link #write write}, and {@link #lock lock} + * methods defined by this class are asynchronous and return a {@link Future} + * to represent the pending result of the operation. This may be used to check + * if the operation has completed, to wait for its completion, and to retrieve + * the result. These method may optionally specify a {@link CompletionHandler} + * that is invoked to consume the result of the I/O operation when it completes. + * + *

An {@code AsynchronousFileChannel} is associated with a thread pool to + * which tasks are submitted to handle I/O events and dispatch to completion + * handlers that consume the results of I/O operations on the channel. The + * completion handler for an I/O operation initiated on a channel is guaranteed + * to be invoked by one threads in the thread pool (This ensures that the + * completion handler is run by a thread with the expected identity). + * Where an I/O operation completes immediately, and the initiating thread is + * itself a thread in the thread pool, then the completion handler may be invoked + * directly by the initiating thread. When an {@code AsynchronousFileChannel} is + * created without specifying a thread pool then the channel is associated with + * a system-dependent and default thread pool that may be shared with other + * channels. The default thread pool is configured by the system properties + * defined by the {@link AsynchronousChannelGroup} class. + * + *

Channels of this type are safe for use by multiple concurrent threads. The + * {@link Channel#close close} method may be invoked at any time, as specified + * by the {@link Channel} interface. This causes all outstanding asynchronous + * operations on the channel to complete with the exception {@link + * AsynchronousCloseException}. Multiple read and write operations may be + * outstanding at the same time. When multiple read and write operations are + * outstanding then the ordering of the I/O operations, and the order that the + * completion handlers are invoked, is not specified; they are not, in particular, + * guaranteed to execute in the order that the operations were initiated. The + * {@link java.nio.ByteBuffer ByteBuffers} used when reading or writing are not + * safe for use by multiple concurrent I/O operations. Furthermore, after an I/O + * operation is initiated then care should be taken to ensure that the buffer is + * not accessed until after the operation has completed. + * + *

As with {@link FileChannel}, the view of a file provided by an instance of + * this class is guaranteed to be consistent with other views of the same file + * provided by other instances in the same program. The view provided by an + * instance of this class may or may not, however, be consistent with the views + * seen by other concurrently-running programs due to caching performed by the + * underlying operating system and delays induced by network-filesystem protocols. + * This is true regardless of the language in which these other programs are + * written, and whether they are running on the same machine or on some other + * machine. The exact nature of any such inconsistencies are system-dependent + * and are therefore unspecified. + * + * @since 1.7 + */ + +public abstract class AsynchronousFileChannel + implements AsynchronousChannel +{ + /** + * Initializes a new instance of this class. + */ + protected AsynchronousFileChannel() { + } + + /** + * Closes this channel. + * + *

If this channel is associated with its own thread pool then closing + * the channel causes the thread pool to shutdown after all actively + * executing completion handlers have completed. No attempt is made to stop + * or interrupt actively completion handlers. + * + *

This method otherwise behaves exactly as specified by the {@link + * AsynchronousChannel} interface. + * + * @throws IOException {@inheritDoc} + */ + @Override + public abstract void close() throws IOException; + + /** + * Opens or creates a file for reading and/or writing, returning an + * asynchronous file channel to access the file. + * + *

The {@code options} parameter determines how the file is opened. + * The {@link StandardOpenOption#READ READ} and {@link StandardOpenOption#WRITE + * WRITE} options determines if the file should be opened for reading and/or + * writing. If neither option is contained in the array then an existing file + * is opened for reading. + * + *

In addition to {@code READ} and {@code WRITE}, the following options + * may be present: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Option Description
{@link StandardOpenOption#TRUNCATE_EXISTING TRUNCATE_EXISTING} When opening an existing file, the file is first truncated to a + * size of 0 bytes. This option is ignored when the file is opened only + * for reading.
{@link StandardOpenOption#CREATE_NEW CREATE_NEW} If this option is present then a new file is created, failing if + * the file already exists. When creating a file the check for the + * existence of the file and the creation of the file if it does not exist + * is atomic with respect to other file system operations. This option is + * ignored when the file is opened only for reading.
{@link StandardOpenOption#CREATE CREATE} If this option is present then an existing file is opened if it + * exists, otherwise a new file is created. When creating a file the check + * for the existence of the file and the creation of the file if it does + * not exist is atomic with respect to other file system operations. This + * option is ignored if the {@code CREATE_NEW} option is also present or + * the file is opened only for reading.
{@link StandardOpenOption#DELETE_ON_CLOSE DELETE_ON_CLOSE} When this option is present then the implementation makes a + * best effort attempt to delete the file when closed by the + * the {@link #close close} method. If the {@code close} method is not + * invoked then a best effort attempt is made to delete the file + * when the Java virtual machine terminates.
{@link StandardOpenOption#SPARSE SPARSE} When creating a new file this option is a hint that the + * new file will be sparse. This option is ignored when not creating + * a new file.
{@link StandardOpenOption#SYNC SYNC} Requires that every update to the file's content or metadata be + * written synchronously to the underlying storage device. (see Synchronized I/O file + * integrity).
{@link StandardOpenOption#DSYNC DSYNC} Requires that every update to the file's content be written + * synchronously to the underlying storage device. (see Synchronized I/O file + * integrity).
+ * + *

An implementation may also support additional options. + * + *

The {@code executor} parameter is the {@link ExecutorService} to + * which tasks are submitted to handle I/O events and dispatch completion + * results for operations initiated on resulting channel. + * The nature of these tasks is highly implementation specific and so care + * should be taken when configuring the {@code Executor}. Minimally it + * should support an unbounded work queue and should not run tasks on the + * caller thread of the {@link ExecutorService#execute execute} method. + * {@link #close Closing} the channel results in the orderly {@link + * ExecutorService#shutdown shutdown} of the executor service. Shutting down + * the executor service by other means results in unspecified behavior. + * + *

The {@code attrs} parameter is an optional array of file {@link + * FileAttribute file-attributes} to set atomically when creating the file. + * + *

The new channel is created by invoking the {@link + * FileSystemProvider#newFileChannel newFileChannel} method on the + * provider that created the {@code Path}. + * + * @param file + * The path of the file to open or create + * @param options + * Options specifying how the file is opened + * @param executor + * The thread pool or {@code null} to associate the channel with + * the default thread pool + * @param attrs + * An optional list of file attributes to set atomically when + * creating the file + * + * @return A new asynchronous file channel + * + * @throws IllegalArgumentException + * If the set contains an invalid combination of options + * @throws UnsupportedOperationException + * If the {@code file} is associated with a provider that does not + * support creating asynchronous file channels, or an unsupported + * open option is specified, or the array contains an attribute that + * cannot be set atomically when creating the file + * @throws IOException + * If an I/O error occurs + * @throws SecurityException + * If a security manager is installed and it denies an + * unspecified permission required by the implementation. + * In the case of the default provider, the {@link + * SecurityManager#checkRead(String)} method is invoked to check + * read access if the file is opened for reading. The {@link + * SecurityManager#checkWrite(String)} method is invoked to check + * write access if the file is opened for writing + */ + public static AsynchronousFileChannel open(Path file, + Set options, + ExecutorService executor, + FileAttribute... attrs) + throws IOException + { + FileSystemProvider provider = file.getFileSystem().provider(); + return provider.newAsynchronousFileChannel(file, options, executor, attrs); + } + + private static final FileAttribute[] NO_ATTRIBUTES = new FileAttribute[0]; + + /** + * Opens or creates a file for reading and/or writing, returning an + * asynchronous file channel to access the file. + * + *

An invocation of this method behaves in exactly the same way as the + * invocation + *

+     *     ch.{@link #open(Path,Set,ExecutorService,FileAttribute[]) open}(file, opts, null, new FileAttribute<?>[0]);
+     * 
+ * where {@code opts} is a {@code Set} containing the options specified to + * this method. + * + *

The resulting channel is associated with default thread pool to which + * tasks are submitted to handle I/O events and dispatch to completion + * handlers that consume the result of asynchronous operations performed on + * the resulting channel. + * + * @param file + * The path of the file to open or create + * @param options + * Options specifying how the file is opened + * + * @return A new asynchronous file channel + * + * @throws IllegalArgumentException + * If the set contains an invalid combination of options + * @throws UnsupportedOperationException + * If the {@code file} is associated with a provider that does not + * support creating file channels, or an unsupported open option is + * specified + * @throws IOException + * If an I/O error occurs + * @throws SecurityException + * If a security manager is installed and it denies an + * unspecified permission required by the implementation. + * In the case of the default provider, the {@link + * SecurityManager#checkRead(String)} method is invoked to check + * read access if the file is opened for reading. The {@link + * SecurityManager#checkWrite(String)} method is invoked to check + * write access if the file is opened for writing + */ + public static AsynchronousFileChannel open(Path file, OpenOption... options) + throws IOException + { + Set set = new HashSet(options.length); + Collections.addAll(set, options); + return open(file, set, null, NO_ATTRIBUTES); + } + + /** + * Returns the current size of this channel's file. + * + * @return The current size of this channel's file, measured in bytes + * + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If some other I/O error occurs + */ + public abstract long size() throws IOException; + + /** + * Truncates this channel's file to the given size. + * + *

If the given size is less than the file's current size then the file + * is truncated, discarding any bytes beyond the new end of the file. If + * the given size is greater than or equal to the file's current size then + * the file is not modified.

+ * + * @param size + * The new size, a non-negative byte count + * + * @return This file channel + * + * @throws NonWritableChannelException + * If this channel was not opened for writing + * + * @throws ClosedChannelException + * If this channel is closed + * + * @throws IllegalArgumentException + * If the new size is negative + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract AsynchronousFileChannel truncate(long size) throws IOException; + + /** + * Forces any updates to this channel's file to be written to the storage + * device that contains it. + * + *

If this channel's file resides on a local storage device then when + * this method returns it is guaranteed that all changes made to the file + * since this channel was created, or since this method was last invoked, + * will have been written to that device. This is useful for ensuring that + * critical information is not lost in the event of a system crash. + * + *

If the file does not reside on a local device then no such guarantee + * is made. + * + *

The {@code metaData} parameter can be used to limit the number of + * I/O operations that this method is required to perform. Passing + * {@code false} for this parameter indicates that only updates to the + * file's content need be written to storage; passing {@code true} + * indicates that updates to both the file's content and metadata must be + * written, which generally requires at least one more I/O operation. + * Whether this parameter actually has any effect is dependent upon the + * underlying operating system and is therefore unspecified. + * + *

Invoking this method may cause an I/O operation to occur even if the + * channel was only opened for reading. Some operating systems, for + * example, maintain a last-access time as part of a file's metadata, and + * this time is updated whenever the file is read. Whether or not this is + * actually done is system-dependent and is therefore unspecified. + * + *

This method is only guaranteed to force changes that were made to + * this channel's file via the methods defined in this class. + * + * @param metaData + * If {@code true} then this method is required to force changes + * to both the file's content and metadata to be written to + * storage; otherwise, it need only force content changes to be + * written + * + * @throws ClosedChannelException + * If this channel is closed + * + * @throws IOException + * If some other I/O error occurs + */ + public abstract void force(boolean metaData) throws IOException; + + /** + * Acquires a lock on the given region of this channel's file. + * + *

This method initiates an operation to acquire a lock on the given region + * of this channel's file. The method returns a {@code Future} representing + * the pending result of the operation. Its {@link Future#get() get} + * method returns the {@link FileLock} on successful completion. + * + *

The region specified by the {@code position} and {@code size} + * parameters need not be contained within, or even overlap, the actual + * underlying file. Lock regions are fixed in size; if a locked region + * initially contains the end of the file and the file grows beyond the + * region then the new portion of the file will not be covered by the lock. + * If a file is expected to grow in size and a lock on the entire file is + * required then a region starting at zero, and no smaller than the + * expected maximum size of the file, should be locked. The two-argument + * {@link #lock(Object,CompletionHandler)} method simply locks a region + * of size {@link Long#MAX_VALUE}. If a lock that overlaps the requested + * region is already held by this Java virtual machine, or this method has + * been invoked to lock an overlapping region and that operation has not + * completed, then this method throws {@link OverlappingFileLockException}. + * + *

Some operating systems do not support a mechanism to acquire a file + * lock in an asynchronous manner. Consequently an implementation may + * acquire the file lock in a background thread or from a task executed by + * a thread in the associated thread pool. If there are many lock operations + * outstanding then it may consume threads in the Java virtual machine for + * indefinite periods. + * + *

Some operating systems do not support shared locks, in which case a + * request for a shared lock is automatically converted into a request for + * an exclusive lock. Whether the newly-acquired lock is shared or + * exclusive may be tested by invoking the resulting lock object's {@link + * FileLock#isShared() isShared} method. + * + *

File locks are held on behalf of the entire Java virtual machine. + * They are not suitable for controlling access to a file by multiple + * threads within the same virtual machine. + * + * @param position + * The position at which the locked region is to start; must be + * non-negative + * @param size + * The size of the locked region; must be non-negative, and the sum + * {@code position} + {@code size} must be non-negative + * @param shared + * {@code true} to request a shared lock, in which case this + * channel must be open for reading (and possibly writing); + * {@code false} to request an exclusive lock, in which case this + * channel must be open for writing (and possibly reading) + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The handler for consuming the result; can be {@code null} + * + * @return a {@code Future} object representing the pending result + * + * @throws OverlappingFileLockException + * If a lock that overlaps the requested region is already held by + * this Java virtual machine, or there is already a pending attempt + * to lock an overlapping region + * @throws IllegalArgumentException + * If the preconditions on the parameters do not hold + * @throws NonReadableChannelException + * If {@code shared} is true this channel but was not opened for reading + * @throws NonWritableChannelException + * If {@code shared} is false but this channel was not opened for writing + * @throws ShutdownChannelGroupException + * If a handler is specified, the channel is closed, and the channel + * was originally created with its own thread pool + */ + public abstract Future lock(long position, + long size, + boolean shared, + A attachment, + CompletionHandler handler); + + /** + * Acquires an exclusive lock on this channel's file. + * + *

This method initiates an operation to acquire an exclusive lock on this + * channel's file. The method returns a {@code Future} representing + * the pending result of the operation. Its {@link Future#get() get} + * method returns the {@link FileLock} on successful completion. + * + *

An invocation of this method of the form {@code ch.lock(att,handler)} + * behaves in exactly the same way as the invocation + *

+     *     ch.{@link #lock(long,long,boolean,Object,CompletionHandler) lock}(0L, Long.MAX_VALUE, false, att, handler)
+     * 
+ * + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The handler for consuming the result; can be {@code null} + * + * @return a {@code Future} object representing the pending result + * + * @throws OverlappingFileLockException + * If a lock is already held by this Java virtual machine, or there + * is already a pending attempt to lock a region + * @throws NonWritableChannelException + * If this channel was not opened for writing + * @throws ShutdownChannelGroupException + * If a handler is specified, the channel is closed, and the channel + * was originally created with its own thread pool + */ + public final Future lock(A attachment, + CompletionHandler handler) + { + return lock(0L, Long.MAX_VALUE, false, attachment, handler); + } + + /** + * Acquires an exclusive lock on this channel's file. + * + *

This method initiates an operation to acquire an exclusive lock on this + * channel's file. The method returns a {@code Future} representing the + * pending result of the operation. Its {@link Future#get() get} method + * returns the {@link FileLock} on successful completion. + * + *

An invocation of this method behaves in exactly the same way as the + * invocation + *

+     *     ch.{@link #lock(long,long,boolean,Object,CompletionHandler) lock}(0L, Long.MAX_VALUE, false, null, null)
+     * 
+ * + * @return A {@code Future} object representing the pending result + * + * @throws OverlappingFileLockException + * If a lock is already held by this Java virtual machine, or there + * is already a pending attempt to lock a region + * @throws NonWritableChannelException + * If this channel was not opened for writing + */ + public final Future lock() { + return lock(0L, Long.MAX_VALUE, false, null, null); + } + + /** + * Attempts to acquire a lock on the given region of this channel's file. + * + *

This method does not block. An invocation always returns immediately, + * either having acquired a lock on the requested region or having failed to + * do so. If it fails to acquire a lock because an overlapping lock is held + * by another program then it returns {@code null}. If it fails to acquire + * a lock for any other reason then an appropriate exception is thrown. + * + * @param position + * The position at which the locked region is to start; must be + * non-negative + * + * @param size + * The size of the locked region; must be non-negative, and the sum + * {@code position} + {@code size} must be non-negative + * + * @param shared + * {@code true} to request a shared lock, + * {@code false} to request an exclusive lock + * + * @return A lock object representing the newly-acquired lock, + * or {@code null} if the lock could not be acquired + * because another program holds an overlapping lock + * + * @throws IllegalArgumentException + * If the preconditions on the parameters do not hold + * @throws ClosedChannelException + * If this channel is closed + * @throws OverlappingFileLockException + * If a lock that overlaps the requested region is already held by + * this Java virtual machine, or if another thread is already + * blocked in this method and is attempting to lock an overlapping + * region of the same file + * @throws NonReadableChannelException + * If {@code shared} is true this channel but was not opened for reading + * @throws NonWritableChannelException + * If {@code shared} is false but this channel was not opened for writing + * + * @throws IOException + * If some other I/O error occurs + * + * @see #lock(Object,CompletionHandler) + * @see #lock(long,long,boolean,Object,CompletionHandler) + * @see #tryLock() + */ + public abstract FileLock tryLock(long position, long size, boolean shared) + throws IOException; + + /** + * Attempts to acquire an exclusive lock on this channel's file. + * + *

An invocation of this method of the form {@code ch.tryLock()} + * behaves in exactly the same way as the invocation + * + *

+     *     ch.{@link #tryLock(long,long,boolean) tryLock}(0L, Long.MAX_VALUE, false) 
+ * + * @return A lock object representing the newly-acquired lock, + * or {@code null} if the lock could not be acquired + * because another program holds an overlapping lock + * + * @throws ClosedChannelException + * If this channel is closed + * @throws OverlappingFileLockException + * If a lock that overlaps the requested region is already held by + * this Java virtual machine, or if another thread is already + * blocked in this method and is attempting to lock an overlapping + * region + * @throws NonWritableChannelException + * If {@code shared} is false but this channel was not opened for writing + * + * @throws IOException + * If some other I/O error occurs + * + * @see #lock(Object,CompletionHandler) + * @see #lock(long,long,boolean,Object,CompletionHandler) + * @see #tryLock(long,long,boolean) + */ + public final FileLock tryLock() throws IOException { + return tryLock(0L, Long.MAX_VALUE, false); + } + + /** + * Reads a sequence of bytes from this channel into the given buffer, + * starting at the given file position. + * + *

This method initiates the reading of a sequence of bytes from this + * channel into the given buffer, starting at the given file position. This + * method returns a {@code Future} representing the pending result of the + * operation. The Future's {@link Future#get() get} method returns the + * number of bytes read or {@code -1} if the given position is greater than + * or equal to the file's size at the time that the read is attempted. + * + *

This method works in the same manner as the {@link + * AsynchronousByteChannel#read(ByteBuffer,Object,CompletionHandler)} + * method, except that bytes are read starting at the given file position. + * If the given file position is greater than the file's size at the time + * that the read is attempted then no bytes are read. + * + * @param dst + * The buffer into which bytes are to be transferred + * @param position + * The file position at which the transfer is to begin; + * must be non-negative + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The handler for consuming the result; can be {@code null} + * + * @return A {@code Future} object representing the pending result + * + * @throws IllegalArgumentException + * If the position is negative or the buffer is read-only + * @throws NonReadableChannelException + * If this channel was not opened for reading + * @throws ShutdownChannelGroupException + * If a handler is specified, the channel is closed, and the channel + * was originally created with its own thread pool + */ + public abstract Future read(ByteBuffer dst, + long position, + A attachment, + CompletionHandler handler); + + /** + * Reads a sequence of bytes from this channel into the given buffer, + * starting at the given file position. + * + *

This method initiates the reading of a sequence of bytes from this + * channel into the given buffer, starting at the given file position. This + * method returns a {@code Future} representing the pending result of the + * operation. The Future's {@link Future#get() get} method returns the + * number of bytes read or {@code -1} if the given position is greater + * than or equal to the file's size at the time that the read is attempted. + * + *

This method is equivalent to invoking {@link + * #read(ByteBuffer,long,Object,CompletionHandler)} with the {@code attachment} + * and handler parameters set to {@code null}. + * + * @param dst + * The buffer into which bytes are to be transferred + * @param position + * The file position at which the transfer is to begin; + * must be non-negative + * + * @return A {@code Future} object representing the pending result + * + * @throws IllegalArgumentException + * If the position is negative or the buffer is read-only + * @throws NonReadableChannelException + * If this channel was not opened for reading + */ + public final Future read(ByteBuffer dst, long position) { + return read(dst, position, null, null); + } + + /** + * Writes a sequence of bytes to this channel from the given buffer, starting + * at the given file position. + * + *

This method initiates the writing of a sequence of bytes to this channel + * from the given buffer, starting at the given file position. The method + * returns a {@code Future} representing the pending result of the write + * operation. The Future's {@link Future#get() get} method returns the + * number of bytes written. + * + *

This method works in the same manner as the {@link + * AsynchronousByteChannel#write(ByteBuffer,Object,CompletionHandler)} + * method, except that bytes are written starting at the given file position. + * If the given position is greater than the file's size, at the time that + * the write is attempted, then the file will be grown to accommodate the new + * bytes; the values of any bytes between the previous end-of-file and the + * newly-written bytes are unspecified. + * + * @param src + * The buffer from which bytes are to be transferred + * @param position + * The file position at which the transfer is to begin; + * must be non-negative + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The handler for consuming the result; can be {@code null} + * + * @return A {@code Future} object representing the pending result + * + * @throws IllegalArgumentException + * If the position is negative + * @throws NonWritableChannelException + * If this channel was not opened for writing + * @throws ShutdownChannelGroupException + * If a handler is specified, the channel is closed, and the channel + * was originally created with its own thread pool + */ + public abstract Future write(ByteBuffer src, + long position, + A attachment, + CompletionHandler handler); + + /** + * Writes a sequence of bytes to this channel from the given buffer, starting + * at the given file position. + * + *

This method initiates the writing of a sequence of bytes to this channel + * from the given buffer, starting at the given file position. The method + * returns a {@code Future} representing the pending result of the write + * operation. The Future's {@link Future#get() get} method returns the + * number of bytes written. + * + *

This method is equivalent to invoking {@link + * #write(ByteBuffer,long,Object,CompletionHandler)} with the {@code attachment} + * and handler parameters set to {@code null}. + * + * @param src + * The buffer from which bytes are to be transferred + * @param position + * The file position at which the transfer is to begin; + * must be non-negative + * + * @return A {@code Future} object representing the pending result + * + * @throws IllegalArgumentException + * If the position is negative + * @throws NonWritableChannelException + * If this channel was not opened for writing + */ + public final Future write(ByteBuffer src, long position) { + return write(src, position, null, null); + } +} diff --git a/src/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java b/src/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java new file mode 100644 index 000000000..99c56fa59 --- /dev/null +++ b/src/share/classes/java/nio/channels/AsynchronousServerSocketChannel.java @@ -0,0 +1,303 @@ +/* + * Copyright 2007-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 java.nio.channels; + +import java.nio.channels.spi.*; +import java.net.SocketOption; +import java.net.SocketAddress; +import java.util.concurrent.Future; +import java.io.IOException; + +/** + * An asynchronous channel for stream-oriented listening sockets. + * + *

An asynchronous server-socket channel is created by invoking the + * {@link #open open} method of this class. + * A newly-created asynchronous server-socket channel is open but not yet bound. + * It can be bound to a local address and configured to listen for connections + * by invoking the {@link #bind(SocketAddress,int) bind} method. Once bound, + * the {@link #accept(Object,CompletionHandler) accept} method + * is used to initiate the accepting of connections to the channel's socket. + * An attempt to invoke the accept method on an unbound channel will + * cause a {@link NotYetBoundException} to be thrown. + * + *

Channels of this type are safe for use by multiple concurrent threads + * though at most one accept operation can be outstanding at any time. + * If a thread initiates an accept operation before a previous accept operation + * has completed then an {@link AcceptPendingException} will be thrown. + * + *

Socket options are configured using the {@link #setOption(SocketOption,Object) + * setOption} method. Channels of this type support the following options: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + *
Option NameDescription
{@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} The size of the socket receive buffer
{@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} Re-use address
+ *
+ * Additional (implementation specific) options may also be supported. + * + *

Usage Example: + *

+ *  final AsynchronousServerSocketChannel listener =
+ *      AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(5000));
+ *
+ *  listener.accept(null, new CompletionHandler<AsynchronousSocketChannel,Void>() {
+ *      public void completed(AsynchronousSocketChannel ch, Void att) {
+ *          // accept the next connection
+ *          listener.accept(null, this);
+ *
+ *          // handle this connection
+ *          handle(ch);
+ *      }
+ *      public void failed(Throwable exc, Void att) {
+ *          ...
+ *      }
+ *      public void cancelled(Void att) {
+ *          ...
+ *      }
+ *  });
+ * 
+ * + * @since 1.7 + */ + +public abstract class AsynchronousServerSocketChannel + implements AsynchronousChannel, NetworkChannel +{ + private final AsynchronousChannelProvider provider; + + /** + * Initializes a new instance of this class. + */ + protected AsynchronousServerSocketChannel(AsynchronousChannelProvider provider) { + this.provider = provider; + } + + /** + * Returns the provider that created this channel. + */ + public final AsynchronousChannelProvider provider() { + return provider; + } + + /** + * Opens an asynchronous server-socket channel. + * + *

The new channel is created by invoking the {@link + * java.nio.channels.spi.AsynchronousChannelProvider#openAsynchronousServerSocketChannel + * openAsynchronousServerSocketChannel} method on the {@link + * java.nio.channels.spi.AsynchronousChannelProvider} object that created + * the given group. If the group parameter is null then the + * resulting channel is created by the system-wide default provider, and + * bound to the default group. + * + * @param group + * The group to which the newly constructed channel should be bound, + * or null for the default group + * + * @return A new asynchronous server socket channel + * + * @throws ShutdownChannelGroupException + * If the channel group is shutdown + * @throws IOException + * If an I/O error occurs + */ + public static AsynchronousServerSocketChannel open(AsynchronousChannelGroup group) + throws IOException + { + AsynchronousChannelProvider provider = (group == null) ? + AsynchronousChannelProvider.provider() : group.provider(); + return provider.openAsynchronousServerSocketChannel(group); + } + + /** + * Opens an asynchronous server-socket channel. + * + *

This method returns an asynchronous server socket channel that is + * bound to the default group. This method is equivalent to evaluating + * the expression: + *

+     * open((AsynchronousChannelGroup)null);
+     * 
+ * + * @return A new asynchronous server socket channel + * + * @throws IOException + * If an I/O error occurs + */ + public static AsynchronousServerSocketChannel open() + throws IOException + { + return open(null); + } + + /** + * Binds the channel's socket to a local address and configures the socket to + * listen for connections. + * + *

An invocation of this method is equivalent to the following: + *

+     * bind(local, 0);
+     * 
+ * + * @param local + * The local address to bind the socket, or null to bind + * to an automatically assigned socket address + * + * @return This channel + * + * @throws AlreadyBoundException {@inheritDoc} + * @throws UnsupportedAddressTypeException {@inheritDoc} + * @throws SecurityException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + public final AsynchronousServerSocketChannel bind(SocketAddress local) + throws IOException + { + return bind(local, 0); + } + + /** + * Binds the channel's socket to a local address and configures the socket to + * listen for connections. + * + *

This method is used to establish an association between the socket and + * a local address. Once an association is established then the socket remains + * bound until the associated channel is closed. + * + *

The {@code backlog} parameter is the maximum number of pending + * connections on the socket. Its exact semantics are implementation specific. + * In particular, an implementation may impose a maximum length or may choose + * to ignore the parameter altogther. If the {@code backlog} parameter has + * the value {@code 0}, or a negative value, then an implementation specific + * default is used. + * + * @param local + * The local address to bind the socket, or {@code null} to bind + * to an automatically assigned socket address + * @param backlog + * The maximum number of pending connections + * + * @return This channel + * + * @throws AlreadyBoundException + * If the socket is already bound + * @throws UnsupportedAddressTypeException + * If the type of the given address is not supported + * @throws SecurityException + * If a security manager has been installed and its {@link + * SecurityManager#checkListen checkListen} method denies the operation + * @throws ClosedChannelException + * If the channel is closed + * @throws IOException + * If some other I/O error occurs + */ + public abstract AsynchronousServerSocketChannel bind(SocketAddress local, int backlog) + throws IOException; + + /** + * @throws IllegalArgumentException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + public abstract AsynchronousServerSocketChannel setOption(SocketOption name, T value) + throws IOException; + + /** + * Accepts a connection. + * + *

This method initiates accepting a connection made to this channel's + * socket, returning a {@link Future} representing the pending result + * of the operation. The {@code Future}'s {@link Future#get() get} + * method will return the {@link AsynchronousSocketChannel} for the new + * connection on successful completion. + * + *

When a new connection is accepted then the resulting {@code + * AsynchronousSocketChannel} will be bound to the same {@link + * AsynchronousChannelGroup} as this channel. If the group is {@link + * AsynchronousChannelGroup#isShutdown shutdown} and a connection is accepted, + * then the connection is closed, and the operation completes with an {@code + * IOException} and cause {@link ShutdownChannelGroupException}. + * + *

To allow for concurrent handling of new connections, the completion + * handler is not invoked directly by the initiating thread when a new + * connection is accepted immediately (see Threading). + * + *

If a security manager has been installed then it verifies that the + * address and port number of the connection's remote endpoint are permitted + * by the security manager's {@link SecurityManager#checkAccept checkAccept} + * method. The permission check is performed with privileges that are restricted + * by the calling context of this method. If the permission check fails then + * the connection is closed and the operation completes with a {@link + * SecurityException}. + * + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The handler for consuming the result; can be {@code null} + * + * @return an Future object representing the pending result + * + * @throws AcceptPendingException + * If an accept operation is already in progress on this channel + * @throws NotYetBoundException + * If this channel's socket has not yet been bound + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + */ + public abstract Future + accept(A attachment, CompletionHandler handler); + + /** + * Accepts a connection. + * + *

This method is equivalent to invoking {@link + * #accept(Object,CompletionHandler)} with the {@code attachment} + * and {@code handler} parameters set to {@code null}. + * + * @return an Future object representing the pending result + * + * @throws AcceptPendingException + * If an accept operation is already in progress on this channel + * @throws NotYetBoundException + * If this channel's socket has not yet been bound + */ + public final Future accept() { + return accept(null, null); + } +} diff --git a/src/share/classes/java/nio/channels/AsynchronousSocketChannel.java b/src/share/classes/java/nio/channels/AsynchronousSocketChannel.java new file mode 100644 index 000000000..b6a5da810 --- /dev/null +++ b/src/share/classes/java/nio/channels/AsynchronousSocketChannel.java @@ -0,0 +1,670 @@ +/* + * Copyright 2007-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 java.nio.channels; + +import java.nio.channels.spi.*; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.Future; +import java.io.IOException; +import java.net.SocketOption; +import java.net.SocketAddress; +import java.nio.ByteBuffer; + +/** + * An asynchronous channel for stream-oriented connecting sockets. + * + *

Asynchronous socket channels are created in one of two ways. A newly-created + * {@code AsynchronousSocketChannel} is created by invoking one of the {@link + * #open open} methods defined by this class. A newly-created channel is open but + * not yet connected. A connected {@code AsynchronousSocketChannel} is created + * when a connection is made to the socket of an {@link AsynchronousServerSocketChannel}. + * It is not possible to create an asynchronous socket channel for an arbitrary, + * pre-existing {@link java.net.Socket socket}. + * + *

A newly-created channel is connected by invoking its {@link #connect connect} + * method; once connected, a channel remains connected until it is closed. Whether + * or not a socket channel is connected may be determined by invoking its {@link + * #getRemoteAddress getRemoteAddress} method. An attempt to invoke an I/O + * operation upon an unconnected channel will cause a {@link NotYetConnectedException} + * to be thrown. + * + *

Channels of this type are safe for use by multiple concurrent threads. + * They support concurrent reading and writing, though at most one read operation + * and one write operation can be outstanding at any time. + * If a thread initiates a read operation before a previous read operation has + * completed then a {@link ReadPendingException} will be thrown. Similarly, an + * attempt to initiate a write operation before a previous write has completed + * will throw a {@link WritePendingException}. + * + *

Socket options are configured using the {@link #setOption(SocketOption,Object) + * setOption} method. Asynchronous socket channels support the following options: + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Option NameDescription
{@link java.net.StandardSocketOption#SO_SNDBUF SO_SNDBUF} The size of the socket send buffer
{@link java.net.StandardSocketOption#SO_RCVBUF SO_RCVBUF} The size of the socket receive buffer
{@link java.net.StandardSocketOption#SO_KEEPALIVE SO_KEEPALIVE} Keep connection alive
{@link java.net.StandardSocketOption#SO_REUSEADDR SO_REUSEADDR} Re-use address
{@link java.net.StandardSocketOption#TCP_NODELAY TCP_NODELAY} Disable the Nagle algorithm
+ *
+ * Additional (implementation specific) options may also be supported. + * + *

Timeouts

+ * + *

The {@link #read(ByteBuffer,long,TimeUnit,Object,CompletionHandler) read} + * and {@link #write(ByteBuffer,long,TimeUnit,Object,CompletionHandler) write} + * methods defined by this class allow a timeout to be specified when initiating + * a read or write operation. If the timeout elapses before an operation completes + * then the operation completes with the exception {@link + * InterruptedByTimeoutException}. A timeout may leave the channel, or the + * underlying connection, in an inconsistent state. Where the implementation + * cannot guarantee that bytes have not been read from the channel then it puts + * the channel into an implementation specific error state. A subsequent + * attempt to initiate a {@code read} operation causes an unspecified runtime + * exception to be thrown. Similarly if a {@code write} operation times out and + * the implementation cannot guarantee bytes have not been written to the + * channel then further attempts to {@code write} to the channel cause an + * unspecified runtime exception to be thrown. When a timeout elapses then the + * state of the {@link ByteBuffer}, or the sequence of buffers, for the I/O + * operation is not defined. Buffers should be discarded or at least care must + * be taken to ensure that the buffers are not accessed while the channel remains + * open. + * + * @since 1.7 + */ + +public abstract class AsynchronousSocketChannel + implements AsynchronousByteChannel, NetworkChannel +{ + private final AsynchronousChannelProvider provider; + + /** + * Initializes a new instance of this class. + */ + protected AsynchronousSocketChannel(AsynchronousChannelProvider provider) { + this.provider = provider; + } + + /** + * Returns the provider that created this channel. + */ + public final AsynchronousChannelProvider provider() { + return provider; + } + + /** + * Opens an asynchronous socket channel. + * + *

The new channel is created by invoking the {@link + * AsynchronousChannelProvider#openAsynchronousSocketChannel + * openAsynchronousSocketChannel} method on the {@link + * AsynchronousChannelProvider} that created the group. If the group parameter + * is {@code null} then the resulting channel is created by the system-wide + * default provider, and bound to the default group. + * + * @param group + * The group to which the newly constructed channel should be bound, + * or {@code null} for the default group + * + * @return A new asynchronous socket channel + * + * @throws ShutdownChannelGroupException + * If the channel group is shutdown + * @throws IOException + * If an I/O error occurs + */ + public static AsynchronousSocketChannel open(AsynchronousChannelGroup group) + throws IOException + { + AsynchronousChannelProvider provider = (group == null) ? + AsynchronousChannelProvider.provider() : group.provider(); + return provider.openAsynchronousSocketChannel(group); + } + + /** + * Opens an asynchronous socket channel. + * + *

This method returns an asynchronous socket channel that is bound to + * the default group.This method is equivalent to evaluating the + * expression: + *

+     * open((AsynchronousChannelGroup)null);
+     * 
+ * + * @return A new asynchronous socket channel + * + * @throws IOException + * If an I/O error occurs + */ + public static AsynchronousSocketChannel open() + throws IOException + { + return open(null); + } + + + // -- socket options and related -- + + /** + * @throws ConnectionPendingException + * If a connection operation is already in progress on this channel + * @throws AlreadyBoundException {@inheritDoc} + * @throws UnsupportedAddressTypeException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public abstract AsynchronousSocketChannel bind(SocketAddress local) + throws IOException; + + /** + * @throws IllegalArgumentException {@inheritDoc} + * @throws ClosedChannelException {@inheritDoc} + * @throws IOException {@inheritDoc} + */ + @Override + public abstract AsynchronousSocketChannel setOption(SocketOption name, T value) + throws IOException; + + /** + * Shutdown the connection for reading without closing the channel. + * + *

Once shutdown for reading then further reads on the channel will + * return {@code -1}, the end-of-stream indication. If the input side of the + * connection is already shutdown then invoking this method has no effect. + * The effect on an outstanding read operation is system dependent and + * therefore not specified. The effect, if any, when there is data in the + * socket receive buffer that has not been read, or data arrives subsequently, + * is also system dependent. + * + * @return The channel + * + * @throws NotYetConnectedException + * If this channel is not yet connected + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If some other I/O error occurs + */ + public abstract AsynchronousSocketChannel shutdownInput() throws IOException; + + /** + * Shutdown the connection for writing without closing the channel. + * + *

Once shutdown for writing then further attempts to write to the + * channel will throw {@link ClosedChannelException}. If the output side of + * the connection is already shutdown then invoking this method has no + * effect. The effect on an outstanding write operation is system dependent + * and therefore not specified. + * + * @return The channel + * + * @throws NotYetConnectedException + * If this channel is not yet connected + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If some other I/O error occurs + */ + public abstract AsynchronousSocketChannel shutdownOutput() throws IOException; + + // -- state -- + + /** + * Returns the remote address to which this channel's socket is connected. + * + *

Where the channel is bound and connected to an Internet Protocol + * socket address then the return value from this method is of type {@link + * java.net.InetSocketAddress}. + * + * @return The remote address; {@code null} if the channel's socket is not + * connected + * + * @throws ClosedChannelException + * If the channel is closed + * @throws IOException + * If an I/O error occurs + */ + public abstract SocketAddress getRemoteAddress() throws IOException; + + // -- asynchronous operations -- + + /** + * Connects this channel. + * + *

This method initiates an operation to connect this channel, returning + * a {@code Future} representing the pending result of the operation. If + * the connection is successfully established then the {@code Future}'s + * {@link Future#get() get} method will return {@code null}. If the + * connection cannot be established then the channel is closed. In that case, + * invoking the {@code get} method throws {@link + * java.util.concurrent.ExecutionException} with an {@code IOException} as + * the cause. + * + *

This method performs exactly the same security checks as the {@link + * java.net.Socket} class. That is, if a security manager has been + * installed then this method verifies that its {@link + * java.lang.SecurityManager#checkConnect checkConnect} method permits + * connecting to the address and port number of the given remote endpoint. + * + * @param remote + * The remote address to which this channel is to be connected + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The handler for consuming the result; can be {@code null} + * + * @return A {@code Future} object representing the pending result + * + * @throws UnresolvedAddressException + * If the given remote address is not fully resolved + * @throws UnsupportedAddressTypeException + * If the type of the given remote address is not supported + * @throws AlreadyConnectedException + * If this channel is already connected + * @throws ConnectionPendingException + * If a connection operation is already in progress on this channel + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + * @throws SecurityException + * If a security manager has been installed + * and it does not permit access to the given remote endpoint + * + * @see #getRemoteAddress + */ + public abstract Future connect(SocketAddress remote, + A attachment, + CompletionHandler handler); + + /** + * Connects this channel. + * + *

This method is equivalent to invoking {@link + * #connect(SocketAddress,Object,CompletionHandler)} with the {@code attachment} + * and handler parameters set to {@code null}. + * + * @param remote + * The remote address to which this channel is to be connected + * + * @return A {@code Future} object representing the pending result + * + * @throws UnresolvedAddressException + * If the given remote address is not fully resolved + * @throws UnsupportedAddressTypeException + * If the type of the given remote address is not supported + * @throws AlreadyConnectedException + * If this channel is already connected + * @throws ConnectionPendingException + * If a connection operation is already in progress on this channel + * @throws SecurityException + * If a security manager has been installed + * and it does not permit access to the given remote endpoint + */ + public final Future connect(SocketAddress remote) { + return connect(remote, null, null); + } + + /** + * Reads a sequence of bytes from this channel into the given buffer. + * + *

This method initiates the reading of a sequence of bytes from this + * channel into the given buffer, returning a {@code Future} representing + * the pending result of the operation. The {@code Future}'s {@link + * Future#get() get} method returns the number of bytes read or {@code -1} + * if all bytes have been read and channel has reached end-of-stream. + * + *

If a timeout is specified and the timeout elapses before the operation + * completes then the operation completes with the exception {@link + * InterruptedByTimeoutException}. Where a timeout occurs, and the + * implementation cannot guarantee that bytes have not been read, or will not + * be read from the channel into the given buffer, then further attempts to + * read from the channel will cause an unspecific runtime exception to be + * thrown. + * + *

Otherwise this method works in the same manner as the {@link + * AsynchronousByteChannel#read(ByteBuffer,Object,CompletionHandler)} + * method. + * + * @param dst + * The buffer into which bytes are to be transferred + * @param timeout + * The timeout, or {@code 0L} for no timeout + * @param unit + * The time unit of the {@code timeout} argument + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The handler for consuming the result; can be {@code null} + * + * @return A {@code Future} object representing the pending result + * + * @throws IllegalArgumentException + * If the {@code timeout} parameter is negative or the buffer is + * read-only + * @throws ReadPendingException + * If a read operation is already in progress on this channel + * @throws NotYetConnectedException + * If this channel is not yet connected + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + */ + public abstract Future read(ByteBuffer dst, + long timeout, + TimeUnit unit, + A attachment, + CompletionHandler handler); + + /** + * @throws IllegalArgumentException {@inheritDoc} + * @throws ReadPendingException {@inheritDoc} + * @throws NotYetConnectedException + * If this channel is not yet connected + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + */ + @Override + public final Future read(ByteBuffer dst, + A attachment, + CompletionHandler handler) + { + return read(dst, 0L, TimeUnit.MILLISECONDS, attachment, handler); + } + + /** + * @throws IllegalArgumentException {@inheritDoc} + * @throws ReadPendingException {@inheritDoc} + * @throws NotYetConnectedException + * If this channel is not yet connected + */ + @Override + public final Future read(ByteBuffer dst) { + return read(dst, 0L, TimeUnit.MILLISECONDS, null, null); + } + + /** + * Reads a sequence of bytes from this channel into a subsequence of the + * given buffers. This operation, sometimes called a scattering read, + * is often useful when implementing network protocols that group data into + * segments consisting of one or more fixed-length headers followed by a + * variable-length body. + * + *

This method initiates a read of up to r bytes from this channel, + * where r is the total number of bytes remaining in the specified + * subsequence of the given buffer array, that is, + * + *

+     * dsts[offset].remaining()
+     *     + dsts[offset+1].remaining()
+     *     + ... + dsts[offset+length-1].remaining()
+ * + * at the moment that the read is attempted. + * + *

Suppose that a byte sequence of length n is read, where + * 0 < n <= r. + * Up to the first dsts[offset].remaining() bytes of this sequence + * are transferred into buffer dsts[offset], up to the next + * dsts[offset+1].remaining() bytes are transferred into buffer + * dsts[offset+1], and so forth, until the entire byte sequence + * is transferred into the given buffers. As many bytes as possible are + * transferred into each buffer, hence the final position of each updated + * buffer, except the last updated buffer, is guaranteed to be equal to + * that buffer's limit. The underlying operating system may impose a limit + * on the number of buffers that may be used in an I/O operation. Where the + * number of buffers (with bytes remaining), exceeds this limit, then the + * I/O operation is performed with the maximum number of buffers allowed by + * the operating system. + * + *

The return value from this method is a {@code Future} representing + * the pending result of the operation. The {@code Future}'s {@link + * Future#get() get} method returns the number of bytes read or {@code -1L} + * if all bytes have been read and the channel has reached end-of-stream. + * + *

If a timeout is specified and the timeout elapses before the operation + * completes then it completes with the exception {@link + * InterruptedByTimeoutException}. Where a timeout occurs, and the + * implementation cannot guarantee that bytes have not been read, or will not + * be read from the channel into the given buffers, then further attempts to + * read from the channel will cause an unspecific runtime exception to be + * thrown. + * + * @param dsts + * The buffers into which bytes are to be transferred + * @param offset + * The offset within the buffer array of the first buffer into which + * bytes are to be transferred; must be non-negative and no larger than + * {@code dsts.length} + * @param length + * The maximum number of buffers to be accessed; must be non-negative + * and no larger than {@code dsts.length - offset} + * @param timeout + * The timeout, or {@code 0L} for no timeout + * @param unit + * The time unit of the {@code timeout} argument + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The handler for consuming the result; can be {@code null} + * + * @return A {@code Future} object representing the pending result + * + * @throws IndexOutOfBoundsException + * If the pre-conditions for the {@code offset} and {@code length} + * parameter aren't met + * @throws IllegalArgumentException + * If the {@code timeout} parameter is negative, or a buffer is + * read-only + * @throws ReadPendingException + * If a read operation is already in progress on this channel + * @throws NotYetConnectedException + * If this channel is not yet connected + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + */ + public abstract Future read(ByteBuffer[] dsts, + int offset, + int length, + long timeout, + TimeUnit unit, + A attachment, + CompletionHandler handler); + + /** + * Writes a sequence of bytes to this channel from the given buffer. + * + *

This method initiates the writing of a sequence of bytes to this channel + * from the given buffer, returning a {@code Future} representing the + * pending result of the operation. The {@code Future}'s {@link Future#get() + * get} method will return the number of bytes written. + * + *

If a timeout is specified and the timeout elapses before the operation + * completes then it completes with the exception {@link + * InterruptedByTimeoutException}. Where a timeout occurs, and the + * implementation cannot guarantee that bytes have not been written, or will + * not be written to the channel from the given buffer, then further attempts + * to write to the channel will cause an unspecific runtime exception to be + * thrown. + * + *

Otherwise this method works in the same manner as the {@link + * AsynchronousByteChannel#write(ByteBuffer,Object,CompletionHandler)} + * method. + * + * @param src + * The buffer from which bytes are to be retrieved + * @param timeout + * The timeout, or {@code 0L} for no timeout + * @param unit + * The time unit of the {@code timeout} argument + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The handler for consuming the result; can be {@code null} + * + * @return A {@code Future} object representing the pending result + * + * @throws IllegalArgumentException + * If the {@code timeout} parameter is negative + * @throws WritePendingException + * If a write operation is already in progress on this channel + * @throws NotYetConnectedException + * If this channel is not yet connected + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + */ + public abstract Future write(ByteBuffer src, + long timeout, + TimeUnit unit, + A attachment, + CompletionHandler handler); + + /** + * @throws WritePendingException {@inheritDoc} + * @throws NotYetConnectedException + * If this channel is not yet connected + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + */ + @Override + public final Future write(ByteBuffer src, + A attachment, + CompletionHandler handler) + + { + return write(src, 0L, TimeUnit.MILLISECONDS, attachment, handler); + } + + /** + * @throws WritePendingException {@inheritDoc} + * @throws NotYetConnectedException + * If this channel is not yet connected + */ + @Override + public final Future write(ByteBuffer src) { + return write(src, 0L, TimeUnit.MILLISECONDS, null, null); + } + + /** + * Writes a sequence of bytes to this channel from a subsequence of the given + * buffers. This operation, sometimes called a gathering write, is + * often useful when implementing network protocols that group data into + * segments consisting of one or more fixed-length headers followed by a + * variable-length body. + * + *

This method initiates a write of up to r bytes to this channel, + * where r is the total number of bytes remaining in the specified + * subsequence of the given buffer array, that is, + * + *

+     * srcs[offset].remaining()
+     *     + srcs[offset+1].remaining()
+     *     + ... + srcs[offset+length-1].remaining()
+ * + * at the moment that the write is attempted. + * + *

Suppose that a byte sequence of length n is written, where + * 0 < n <= r. + * Up to the first srcs[offset].remaining() bytes of this sequence + * are written from buffer srcs[offset], up to the next + * srcs[offset+1].remaining() bytes are written from buffer + * srcs[offset+1], and so forth, until the entire byte sequence is + * written. As many bytes as possible are written from each buffer, hence + * the final position of each updated buffer, except the last updated + * buffer, is guaranteed to be equal to that buffer's limit. The underlying + * operating system may impose a limit on the number of buffers that may be + * used in an I/O operation. Where the number of buffers (with bytes + * remaining), exceeds this limit, then the I/O operation is performed with + * the maximum number of buffers allowed by the operating system. + * + *

The return value from this method is a {@code Future} representing + * the pending result of the operation. The {@code Future}'s {@link + * Future#get() get} method will return the number of bytes written. + * + *

If a timeout is specified and the timeout elapses before the operation + * completes then it completes with the exception {@link + * InterruptedByTimeoutException}. Where a timeout occurs, and the + * implementation cannot guarantee that bytes have not been written, or will + * not be written to the channel from the given buffers, then further attempts + * to write to the channel will cause an unspecific runtime exception to be + * thrown. + * + * @param srcs + * The buffers from which bytes are to be retrieved + * @param offset + * The offset within the buffer array of the first buffer from which + * bytes are to be retrieved; must be non-negative and no larger + * than {@code srcs.length} + * @param length + * The maximum number of buffers to be accessed; must be non-negative + * and no larger than {@code srcs.length - offset} + * @param timeout + * The timeout, or {@code 0L} for no timeout + * @param unit + * The time unit of the {@code timeout} argument + * @param attachment + * The object to attach to the I/O operation; can be {@code null} + * @param handler + * The handler for consuming the result; can be {@code null} + * + * @return A {@code Future} object representing the pending result + * + * @throws IndexOutOfBoundsException + * If the pre-conditions for the {@code offset} and {@code length} + * parameter aren't met + * @throws IllegalArgumentException + * If the {@code timeout} parameter is negative + * @throws WritePendingException + * If a write operation is already in progress on this channel + * @throws NotYetConnectedException + * If this channel is not yet connected + * @throws ShutdownChannelGroupException + * If a handler is specified, and the channel group is shutdown + */ + public abstract Future write(ByteBuffer[] srcs, + int offset, + int length, + long timeout, + TimeUnit unit, + A attachment, + CompletionHandler handler); +} diff --git a/src/share/classes/java/nio/channels/Channels.java b/src/share/classes/java/nio/channels/Channels.java index cab960486..4fdcef8ab 100644 --- a/src/share/classes/java/nio/channels/Channels.java +++ b/src/share/classes/java/nio/channels/Channels.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-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 @@ -33,15 +33,12 @@ import java.io.Reader; import java.io.Writer; import java.io.IOException; import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.BufferOverflowException; -import java.nio.BufferUnderflowException; import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; -import java.nio.charset.CoderResult; import java.nio.charset.UnsupportedCharsetException; import java.nio.channels.spi.AbstractInterruptibleChannel; +import java.util.concurrent.ExecutionException; import sun.nio.ch.ChannelInputStream; import sun.nio.cs.StreamDecoder; import sun.nio.cs.StreamEncoder; @@ -184,6 +181,155 @@ public final class Channels { }; } + /** + * {@note new} + * Constructs a stream that reads bytes from the given channel. + * + *

The stream will not be buffered, and it will not support the {@link + * InputStream#mark mark} or {@link InputStream#reset reset} methods. The + * stream will be safe for access by multiple concurrent threads. Closing + * the stream will in turn cause the channel to be closed.

+ * + * @param ch + * The channel from which bytes will be read + * + * @return A new input stream + * + * @since 1.7 + */ + public static InputStream newInputStream(final AsynchronousByteChannel ch) { + checkNotNull(ch, "ch"); + return new InputStream() { + + private ByteBuffer bb = null; + private byte[] bs = null; // Invoker's previous array + private byte[] b1 = null; + + @Override + public synchronized int read() throws IOException { + if (b1 == null) + b1 = new byte[1]; + int n = this.read(b1); + if (n == 1) + return b1[0] & 0xff; + return -1; + } + + @Override + public synchronized int read(byte[] bs, int off, int len) + throws IOException + { + if ((off < 0) || (off > bs.length) || (len < 0) || + ((off + len) > bs.length) || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) + return 0; + + ByteBuffer bb = ((this.bs == bs) + ? this.bb + : ByteBuffer.wrap(bs)); + bb.position(off); + bb.limit(Math.min(off + len, bb.capacity())); + this.bb = bb; + this.bs = bs; + + boolean interrupted = false; + try { + for (;;) { + try { + return ch.read(bb).get(); + } catch (ExecutionException ee) { + throw new IOException(ee.getCause()); + } catch (InterruptedException ie) { + interrupted = true; + } + } + } finally { + if (interrupted) + Thread.currentThread().interrupt(); + } + } + + @Override + public void close() throws IOException { + ch.close(); + } + }; + } + + /** + * {@note new} + * Constructs a stream that writes bytes to the given channel. + * + *

The stream will not be buffered. The stream will be safe for access + * by multiple concurrent threads. Closing the stream will in turn cause + * the channel to be closed.

+ * + * @param ch + * The channel to which bytes will be written + * + * @return A new output stream + * + * @since 1.7 + */ + public static OutputStream newOutputStream(final AsynchronousByteChannel ch) { + checkNotNull(ch, "ch"); + return new OutputStream() { + + private ByteBuffer bb = null; + private byte[] bs = null; // Invoker's previous array + private byte[] b1 = null; + + @Override + public synchronized void write(int b) throws IOException { + if (b1 == null) + b1 = new byte[1]; + b1[0] = (byte)b; + this.write(b1); + } + + @Override + public synchronized void write(byte[] bs, int off, int len) + throws IOException + { + if ((off < 0) || (off > bs.length) || (len < 0) || + ((off + len) > bs.length) || ((off + len) < 0)) { + throw new IndexOutOfBoundsException(); + } else if (len == 0) { + return; + } + ByteBuffer bb = ((this.bs == bs) + ? this.bb + : ByteBuffer.wrap(bs)); + bb.limit(Math.min(off + len, bb.capacity())); + bb.position(off); + this.bb = bb; + this.bs = bs; + + boolean interrupted = false; + try { + while (bb.remaining() > 0) { + try { + ch.write(bb).get(); + } catch (ExecutionException ee) { + throw new IOException(ee.getCause()); + } catch (InterruptedException ie) { + interrupted = true; + } + } + } finally { + if (interrupted) + Thread.currentThread().interrupt(); + } + } + + @Override + public void close() throws IOException { + ch.close(); + } + }; + } + // -- Channels from streams -- @@ -468,5 +614,4 @@ public final class Channels { checkNotNull(csName, "csName"); return newWriter(ch, Charset.forName(csName).newEncoder(), -1); } - } diff --git a/src/share/classes/java/nio/channels/CompletionHandler.java b/src/share/classes/java/nio/channels/CompletionHandler.java new file mode 100644 index 000000000..c4d4add8f --- /dev/null +++ b/src/share/classes/java/nio/channels/CompletionHandler.java @@ -0,0 +1,77 @@ +/* + * Copyright 2007-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 java.nio.channels; + +/** + * A handler for consuming the result of an asynchronous I/O operation. + * + *

The asynchronous channels defined in this package allow a completion + * handler to be specified to consume the result of an asynchronous operation. + * The {@link #completed completed} method is invoked when the I/O operation + * completes successfully. The {@link #failed failed} method is invoked if the + * I/O operations fails. The {@link #cancelled cancelled} method is invoked when + * the I/O operation is cancelled by invoking the {@link + * java.util.concurrent.Future#cancel cancel} method. The implementations of + * these methods should complete in a timely manner so as to avoid keeping the + * invoking thread from dispatching to other completion handlers. + * + * @param The result type of the I/O operation + * @param The type of the object attached to the I/O operation + * + * @since 1.7 + */ + +public interface CompletionHandler { + + /** + * Invoked when an operation has completed. + * + * @param result + * The result of the I/O operation. + * @param attachment + * The object attached to the I/O operation when it was initiated. + */ + void completed(V result, A attachment); + + /** + * Invoked when an operation fails. + * + * @param exc + * The exception to indicate why the I/O operation failed + * @param attachment + * The object attached to the I/O operation when it was initiated. + */ + void failed(Throwable exc, A attachment); + + /** + * Invoked when an operation is cancelled by invoking the {@link + * java.util.concurrent.Future#cancel cancel} method. + * + * @param attachment + * The object attached to the I/O operation when it was initiated. + */ + void cancelled(A attachment); +} diff --git a/src/share/classes/java/nio/channels/DatagramChannel.java b/src/share/classes/java/nio/channels/DatagramChannel.java index b8697fa1d..c7bd3df8b 100644 --- a/src/share/classes/java/nio/channels/DatagramChannel.java +++ b/src/share/classes/java/nio/channels/DatagramChannel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-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 @@ -31,7 +31,8 @@ import java.net.DatagramSocket; import java.net.SocketOption; import java.net.SocketAddress; import java.nio.ByteBuffer; -import java.nio.channels.spi.*; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.SelectorProvider; /** * A selectable channel for datagram-oriented sockets. @@ -53,7 +54,8 @@ import java.nio.channels.spi.*; * be determined by invoking its {@link #isConnected isConnected} method. * *

Socket options are configured using the {@link #setOption(SocketOption,Object) - * setOption} method. Datagram channels support the following options: + * setOption} method. A datagram channel to an Internet Protocol socket supports + * the following options: *

* * @@ -211,6 +213,7 @@ public abstract class DatagramChannel throws IOException; /** + * @throws UnsupportedOperationException {@inheritDoc} * @throws IllegalArgumentException {@inheritDoc} * @throws ClosedChannelException {@inheritDoc} * @throws IOException {@inheritDoc} @@ -220,7 +223,6 @@ public abstract class DatagramChannel public abstract DatagramChannel setOption(SocketOption name, T value) throws IOException; - /** * Retrieves a datagram socket associated with this channel. * @@ -313,15 +315,17 @@ public abstract class DatagramChannel /** * Returns the remote address to which this channel's socket is connected. * - * @return The remote address; {@code null} if the channel is not {@link - * #isOpen open} or the channel's socket is not connected + * @return The remote address; {@code null} if the channel's socket is not + * connected * + * @throws ClosedChannelException + * If the channel is closed * @throws IOException * If an I/O error occurs * * @since 1.7 */ - public abstract SocketAddress getConnectedAddress() throws IOException; + public abstract SocketAddress getRemoteAddress() throws IOException; /** * Receives a datagram via this channel. diff --git a/src/share/classes/java/nio/channels/FileChannel.java b/src/share/classes/java/nio/channels/FileChannel.java index e3b5f5bcb..ab780a520 100644 --- a/src/share/classes/java/nio/channels/FileChannel.java +++ b/src/share/classes/java/nio/channels/FileChannel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2005 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-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 @@ -29,16 +29,23 @@ import java.io.*; import java.nio.ByteBuffer; import java.nio.MappedByteBuffer; import java.nio.channels.spi.AbstractInterruptibleChannel; - +import java.nio.file.*; +import java.nio.file.attribute.FileAttribute; +import java.nio.file.spi.*; +import java.util.Set; +import java.util.HashSet; +import java.util.Collections; /** * A channel for reading, writing, mapping, and manipulating a file. * - *

A file channel has a current position within its file which can - * be both {@link #position() queried} and {@link #position(long) - * modified}. The file itself contains a variable-length sequence + *

{@note revised} + * A file channel is a {@link SeekableByteChannel} that is connected to + * a file. It has a current position within its file which can + * be both {@link #position() queried} and {@link #position(long) + * modified}. The file itself contains a variable-length sequence * of bytes that can be read and written and whose current {@link #size - * size} can be queried. The size of the file increases + * size} can be queried. The size of the file increases * when bytes are written beyond its current size; the size of the file * decreases when it is {@link #truncate truncated}. The * file may also have some associated metadata such as access @@ -50,27 +57,27 @@ import java.nio.channels.spi.AbstractInterruptibleChannel; * *

    * - *
  • Bytes may be {@link #read(ByteBuffer, long) read} or - * {@link #write(ByteBuffer, long) written} at an absolute + *

  • Bytes may be {@link #read(ByteBuffer, long) read} or + * {@link #write(ByteBuffer, long) written} at an absolute * position in a file in a way that does not affect the channel's current * position.

  • * - *
  • A region of a file may be {@link #map mapped} + *

  • A region of a file may be {@link #map mapped} * directly into memory; for large files this is often much more efficient * than invoking the usual read or write methods. *

  • * - *
  • Updates made to a file may be {@link #force forced - * out} to the underlying storage device, ensuring that data are not + *

  • Updates made to a file may be {@link #force forced + * out} to the underlying storage device, ensuring that data are not * lost in the event of a system crash.

  • * - *
  • Bytes can be transferred from a file {@link #transferTo to - * some other channel}, and {@link #transferFrom vice - * versa}, in a way that can be optimized by many operating systems + *

  • Bytes can be transferred from a file {@link #transferTo to + * some other channel}, and {@link #transferFrom vice + * versa}, in a way that can be optimized by many operating systems * into a very fast transfer directly to or from the filesystem cache. *

  • * - *
  • A region of a file may be {@link FileLock locked} + *

  • A region of a file may be {@link FileLock locked} * against access by other programs.

  • * *
@@ -96,25 +103,23 @@ import java.nio.channels.spi.AbstractInterruptibleChannel; * machine. The exact nature of any such inconsistencies are system-dependent * and are therefore unspecified. * - *

This class does not define methods for opening existing files or for - * creating new ones; such methods may be added in a future release. In this - * release a file channel can be obtained from an existing {@link - * java.io.FileInputStream#getChannel FileInputStream}, {@link + *

A file channel is created by invoking one of the {@link #open open} + * methods defined by this class. A file channel can also be obtained from an + * existing {@link java.io.FileInputStream#getChannel FileInputStream}, {@link * java.io.FileOutputStream#getChannel FileOutputStream}, or {@link * java.io.RandomAccessFile#getChannel RandomAccessFile} object by invoking * that object's getChannel method, which returns a file channel that - * is connected to the same underlying file. - * - *

The state of a file channel is intimately connected to that of the - * object whose getChannel method returned the channel. Changing the - * channel's position, whether explicitly or by reading or writing bytes, will - * change the file position of the originating object, and vice versa. - * Changing the file's length via the file channel will change the length seen - * via the originating object, and vice versa. Changing the file's content by - * writing bytes will change the content seen by the originating object, and - * vice versa. + * is connected to the same underlying file. Where the file channel is obtained + * from an existing stream or random access file then the state of the file + * channel is intimately connected to that of the object whose getChannel + * method returned the channel. Changing the channel's position, whether + * explicitly or by reading or writing bytes, will change the file position of + * the originating object, and vice versa. Changing the file's length via the + * file channel will change the length seen via the originating object, and vice + * versa. Changing the file's content by writing bytes will change the content + * seen by the originating object, and vice versa. * - *

At various points this class specifies that an + *

At various points this class specifies that an * instance that is "open for reading," "open for writing," or "open for * reading and writing" is required. A channel obtained via the {@link * java.io.FileInputStream#getChannel getChannel} method of a {@link @@ -127,7 +132,7 @@ import java.nio.channels.spi.AbstractInterruptibleChannel; * was created with mode "r" and will be open for reading and writing * if the instance was created with mode "rw". * - *

A file channel that is open for writing may be in + *

A file channel that is open for writing may be in * append mode, for example if it was obtained from a file-output stream * that was created by invoking the {@link * java.io.FileOutputStream#FileOutputStream(java.io.File,boolean) @@ -138,7 +143,6 @@ import java.nio.channels.spi.AbstractInterruptibleChannel; * of the data are done in a single atomic operation is system-dependent and * therefore unspecified. * - * * @see java.io.FileInputStream#getChannel() * @see java.io.FileOutputStream#getChannel() * @see java.io.RandomAccessFile#getChannel() @@ -147,18 +151,190 @@ import java.nio.channels.spi.AbstractInterruptibleChannel; * @author Mike McCloskey * @author JSR-51 Expert Group * @since 1.4 + * @updated 1.7 */ public abstract class FileChannel extends AbstractInterruptibleChannel - implements ByteChannel, GatheringByteChannel, ScatteringByteChannel + implements SeekableByteChannel, GatheringByteChannel, ScatteringByteChannel { - /** * Initializes a new instance of this class. */ protected FileChannel() { } + /** + * {@note new} + * Opens or creates a file, returning a file channel to access the file. + * + *

The {@code options} parameter determines how the file is opened. + * The {@link StandardOpenOption#READ READ} and {@link StandardOpenOption#WRITE + * WRITE} options determine if the file should be opened for reading and/or + * writing. If neither option (or the {@link StandardOpenOption#APPEND APPEND} + * option) is contained in the array then the file is opened for reading. + * By default reading or writing commences at the beginning of the file. + * + *

In the addition to {@code READ} and {@code WRITE}, the following + * options may be present: + * + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Option Description
{@link StandardOpenOption#APPEND APPEND} If this option is present then the file is opened for writing and + * each invocation of the channel's {@code write} method first advances + * the position to the end of the file and then writes the requested + * data. Whether the advancement of the position and the writing of the + * data are done in a single atomic operation is system-dependent and + * therefore unspecified. This option may not be used in conjunction + * with the {@code READ} or {@code TRUNCATE_EXISTING} options.
{@link StandardOpenOption#TRUNCATE_EXISTING TRUNCATE_EXISTING} If this option is present then the existing file is truncated to + * a size of 0 bytes. This option is ignored when the file is opened only + * for reading.
{@link StandardOpenOption#CREATE_NEW CREATE_NEW} If this option is present then a new file is created, failing if + * the file already exists. When creating a file the check for the + * existence of the file and the creation of the file if it does not exist + * is atomic with respect to other file system operations. This option is + * ignored when the file is opened only for reading.
{@link StandardOpenOption#CREATE CREATE} If this option is present then an existing file is opened if it + * exists, otherwise a new file is created. When creating a file the check + * for the existence of the file and the creation of the file if it does + * not exist is atomic with respect to other file system operations. This + * option is ignored if the {@code CREATE_NEW} option is also present or + * the file is opened only for reading.
{@link StandardOpenOption#DELETE_ON_CLOSE DELETE_ON_CLOSE} When this option is present then the implementation makes a + * best effort attempt to delete the file when closed by the + * the {@link #close close} method. If the {@code close} method is not + * invoked then a best effort attempt is made to delete the file + * when the Java virtual machine terminates.
{@link StandardOpenOption#SPARSE SPARSE} When creating a new file this option is a hint that the + * new file will be sparse. This option is ignored when not creating + * a new file.
{@link StandardOpenOption#SYNC SYNC} Requires that every update to the file's content or metadata be + * written synchronously to the underlying storage device. (see Synchronized I/O file + * integrity).
{@link StandardOpenOption#DSYNC DSYNC} Requires that every update to the file's content be written + * synchronously to the underlying storage device. (see Synchronized I/O file + * integrity).
+ * + *

An implementation may also support additional options. + * + *

The {@code attrs} parameter is an optional array of file {@link + * FileAttribute file-attributes} to set atomically when creating the file. + * + *

The new channel is created by invoking the {@link + * FileSystemProvider#newFileChannel newFileChannel} method on the + * provider that created the {@code Path}. + * + * @param file + * The path of the file to open or create + * @param options + * Options specifying how the file is opened + * @param attrs + * An optional list of file attributes to set atomically when + * creating the file + * + * @return A new file channel + * + * @throws IllegalArgumentException + * If the set contains an invalid combination of options + * @throws UnsupportedOperationException + * If the {@code file} is associated with a provider that does not + * support creating file channels, or an unsupported open option is + * specified, or the array contains an attribute that cannot be set + * atomically when creating the file + * @throws IOException + * If an I/O error occurs + * @throws SecurityException + * If a security manager is installed and it denies an + * unspecified permission required by the implementation. + * In the case of the default provider, the {@link + * SecurityManager#checkRead(String)} method is invoked to check + * read access if the file is opened for reading. The {@link + * SecurityManager#checkWrite(String)} method is invoked to check + * write access if the file is opened for writing + * + * @since 1.7 + */ + public static FileChannel open(Path file, + Set options, + FileAttribute... attrs) + throws IOException + { + FileSystemProvider provider = file.getFileSystem().provider(); + return provider.newFileChannel(file, options, attrs); + } + + private static final FileAttribute[] NO_ATTRIBUTES = new FileAttribute[0]; + + /** + * {@note new} + * Opens or creates a file, returning a file channel to access the file. + * + *

An invocation of this method behaves in exactly the same way as the + * invocation + *

+     *     fc.{@link #open(Path,Set,FileAttribute[]) open}(file, options, new FileAttribute<?>[0]);
+     * 
+ * + * @param file + * The path of the file to open or create + * @param options + * Options specifying how the file is opened + * + * @return A new file channel + * + * @throws IllegalArgumentException + * If the set contains an invalid combination of options + * @throws UnsupportedOperationException + * If the {@code file} is associated with a provider that does not + * support creating file channels, or an unsupported open option is + * specified + * @throws IOException + * If an I/O error occurs + * @throws SecurityException + * If a security manager is installed and it denies an + * unspecified permission required by the implementation. + * In the case of the default provider, the {@link + * SecurityManager#checkRead(String)} method is invoked to check + * read access if the file is opened for reading. The {@link + * SecurityManager#checkWrite(String)} method is invoked to check + * write access if the file is opened for writing + * + * @since 1.7 + */ + public static FileChannel open(Path file, OpenOption... options) + throws IOException + { + Set set = new HashSet(options.length); + Collections.addAll(set, options); + return open(file, set, NO_ATTRIBUTES); + } // -- Channel operations -- @@ -286,7 +462,7 @@ public abstract class FileChannel public abstract FileChannel position(long newPosition) throws IOException; /** - * Returns the current size of this channel's file.

+ * Returns the current size of this channel's file.

* * @return The current size of this channel's file, * measured in bytes @@ -359,7 +535,7 @@ public abstract class FileChannel *

This method is only guaranteed to force changes that were made to * this channel's file via the methods defined in this class. It may or * may not force changes that were made by modifying the content of a - * {@link MappedByteBuffer mapped byte buffer} obtained by + * {@link MappedByteBuffer mapped byte buffer} obtained by * invoking the {@link #map map} method. Invoking the {@link * MappedByteBuffer#force force} method of the mapped byte buffer will * force changes made to the buffer's content to be written.

@@ -678,7 +854,7 @@ public abstract class FileChannel * reading; for a read/write or private mapping, this channel must have * been opened for both reading and writing. * - *

The {@link MappedByteBuffer mapped byte buffer} + *

The {@link MappedByteBuffer mapped byte buffer} * returned by this method will have a position of zero and a limit and * capacity of size; its mark will be undefined. The buffer and * the mapping that it represents will remain valid until the buffer itself @@ -717,6 +893,8 @@ public abstract class FileChannel * The size of the region to be mapped; must be non-negative and * no greater than {@link java.lang.Integer#MAX_VALUE} * + * @return The mapped byte buffer + * * @throws NonReadableChannelException * If the mode is {@link MapMode#READ_ONLY READ_ONLY} but * this channel was not opened for reading diff --git a/src/share/classes/java/nio/channels/FileLock.java b/src/share/classes/java/nio/channels/FileLock.java index 921922242..b0ec37f1b 100644 --- a/src/share/classes/java/nio/channels/FileLock.java +++ b/src/share/classes/java/nio/channels/FileLock.java @@ -1,5 +1,5 @@ /* - * Copyright 2001 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-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 @@ -27,14 +27,16 @@ package java.nio.channels; import java.io.IOException; - /** * A token representing a lock on a region of a file. * *

A file-lock object is created each time a lock is acquired on a file via * one of the {@link FileChannel#lock(long,long,boolean) lock} or {@link - * FileChannel#tryLock(long,long,boolean) tryLock} methods of the {@link - * FileChannel} class. + * FileChannel#tryLock(long,long,boolean) tryLock} methods of the + * {@link FileChannel} class, or the {@link + * AsynchronousFileChannel#lock(long,long,boolean,Object,CompletionHandler) lock} + * or {@link AsynchronousFileChannel#tryLock(long,long,boolean) tryLock} + * methods of the {@link AsynchronousFileChannel} class. * *

A file-lock object is initially valid. It remains valid until the lock * is released by invoking the {@link #release release} method, by closing the @@ -70,8 +72,7 @@ import java.io.IOException; *

File-lock objects are safe for use by multiple concurrent threads. * * - * - *

Platform dependencies

+ *

Platform dependencies

* *

This file-locking API is intended to map directly to the native locking * facility of the underlying operating system. Thus the locks held on a file @@ -93,7 +94,7 @@ import java.io.IOException; * *

On some systems, acquiring a mandatory lock on a region of a file * prevents that region from being {@link java.nio.channels.FileChannel#map - * mapped into memory}, and vice versa. Programs that combine + * mapped into memory}, and vice versa. Programs that combine * locking and mapping should be prepared for this combination to fail. * *

On some systems, closing a channel releases all locks held by the Java @@ -113,11 +114,12 @@ import java.io.IOException; * @author Mark Reinhold * @author JSR-51 Expert Group * @since 1.4 + * @updated 1.7 */ public abstract class FileLock { - private final FileChannel channel; + private final Channel channel; private final long position; private final long size; private final boolean shared; @@ -159,11 +161,66 @@ public abstract class FileLock { } /** - * Returns the file channel upon whose file this lock is held.

+ * {@note new} Initializes a new instance of this class. + * + * @param channel + * The channel upon whose file this lock is held + * + * @param position + * The position within the file at which the locked region starts; + * must be non-negative + * + * @param size + * The size of the locked region; must be non-negative, and the sum + * position + size must be non-negative + * + * @param shared + * true if this lock is shared, + * false if it is exclusive + * + * @throws IllegalArgumentException + * If the preconditions on the parameters do not hold + * + * @since 1.7 + */ + protected FileLock(AsynchronousFileChannel channel, + long position, long size, boolean shared) + { + if (position < 0) + throw new IllegalArgumentException("Negative position"); + if (size < 0) + throw new IllegalArgumentException("Negative size"); + if (position + size < 0) + throw new IllegalArgumentException("Negative position + size"); + this.channel = channel; + this.position = position; + this.size = size; + this.shared = shared; + } + + /** + * {@note revised} + * Returns the file channel upon whose file this lock was acquired. * - * @return The file channel + *

This method has been superseded by the {@link #acquiredBy acquiredBy} + * method. + * + * @return The file channel, or {@code null} if the file lock was not + * acquired by a file channel. */ public final FileChannel channel() { + return (channel instanceof FileChannel) ? (FileChannel)channel : null; + } + + /** + * {@note new} + * Returns the channel upon whose file this lock was acquired. + * + * @return The channel upon whose file this lock was acquired. + * + * @since 1.7 + */ + public Channel acquiredBy() { return channel; } diff --git a/src/share/classes/java/nio/channels/MembershipKey.java b/src/share/classes/java/nio/channels/MembershipKey.java index 0d2fc52bc..804e6724a 100644 --- a/src/share/classes/java/nio/channels/MembershipKey.java +++ b/src/share/classes/java/nio/channels/MembershipKey.java @@ -1,5 +1,5 @@ /* - * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007-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 @@ -28,7 +28,6 @@ package java.nio.channels; import java.net.InetAddress; import java.net.NetworkInterface; import java.io.IOException; -import java.util.List; /** * A token representing the membership of an Internet Protocol (IP) multicast @@ -38,7 +37,7 @@ import java.util.List; * to the group, or it may be source-specific, meaning that it * represents a membership that receives only datagrams from a specific source * address. Whether or not a membership key is source-specific may be determined - * by invoking its {@link #getSourceAddress() getSourceAddress} method. + * by invoking its {@link #sourceAddress() sourceAddress} method. * *

A membership key is valid upon creation and remains valid until the * membership is dropped by invoking the {@link #drop() drop} method, or @@ -93,11 +92,8 @@ public abstract class MembershipKey { * If the multicast group membership is already invalid then invoking this * method has no effect. Once a multicast group membership is invalid, * it remains invalid forever. - * - * @throws IOException - * If an I/O error occurs */ - public abstract void drop() throws IOException; + public abstract void drop(); /** * Block multicast datagrams from the given source address. @@ -140,10 +136,8 @@ public abstract class MembershipKey { * @throws IllegalStateException * If the given source address is not currently blocked or the * membership key is no longer valid - * @throws IOException - * If an I/O error occurs */ - public abstract MembershipKey unblock(InetAddress source) throws IOException; + public abstract MembershipKey unblock(InetAddress source); /** * Returns the channel for which this membership key was created. This @@ -152,7 +146,7 @@ public abstract class MembershipKey { * * @return the channel */ - public abstract MulticastChannel getChannel(); + public abstract MulticastChannel channel(); /** * Returns the multicast group for which this membership key was created. @@ -161,7 +155,7 @@ public abstract class MembershipKey { * * @return the multicast group */ - public abstract InetAddress getGroup(); + public abstract InetAddress group(); /** * Returns the network interface for which this membership key was created. @@ -170,7 +164,7 @@ public abstract class MembershipKey { * * @return the network interface */ - public abstract NetworkInterface getNetworkInterface(); + public abstract NetworkInterface networkInterface(); /** * Returns the source address if this membership key is source-specific, @@ -179,5 +173,5 @@ public abstract class MembershipKey { * @return The source address if this membership key is source-specific, * otherwise {@code null} */ - public abstract InetAddress getSourceAddress(); + public abstract InetAddress sourceAddress(); } diff --git a/src/share/classes/java/nio/channels/MulticastChannel.java b/src/share/classes/java/nio/channels/MulticastChannel.java index 440dd2a8b..1cacf98e6 100644 --- a/src/share/classes/java/nio/channels/MulticastChannel.java +++ b/src/share/classes/java/nio/channels/MulticastChannel.java @@ -1,5 +1,5 @@ /* - * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007-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 @@ -122,6 +122,22 @@ import java.net.StandardSocketOption; // javadoc public interface MulticastChannel extends NetworkChannel { + /** + * Closes this channel. + * + *

If the channel is a member of a multicast group then the membership + * is {@link MembershipKey#drop dropped}. Upon return, the {@link + * MembershipKey membership-key} will be {@link MembershipKey#isValid + * invalid}. + * + *

This method otherwise behaves exactly as specified by the {@link + * Channel} interface. + * + * @throws IOException + * If an I/O error occurs + */ + @Override void close() throws IOException; + /** * Joins a multicast group to begin receiving all datagrams sent to the group, * returning a membership key. @@ -130,7 +146,7 @@ public interface MulticastChannel * interface to receive all datagrams then the membership key, representing * that membership, is returned. Otherwise this channel joins the group and * the resulting new membership key is returned. The resulting membership key - * is not {@link MembershipKey#getSourceAddress source-specific}. + * is not {@link MembershipKey#sourceAddress source-specific}. * *

A multicast channel may join several multicast groups, including * the same group on more than one interface. An implementation may impose a @@ -150,6 +166,8 @@ public interface MulticastChannel * @throws IllegalStateException * If the channel already has source-specific membership of the * group on the interface + * @throws UnsupportedOperationException + * If the channel's socket is not an Internet Protocol socket * @throws ClosedChannelException * If this channel is closed * @throws IOException @@ -170,7 +188,7 @@ public interface MulticastChannel * interface to receive datagrams from the given source address then the * membership key, representing that membership, is returned. Otherwise this * channel joins the group and the resulting new membership key is returned. - * The resulting membership key is {@link MembershipKey#getSourceAddress + * The resulting membership key is {@link MembershipKey#sourceAddress * source-specific}. * *

Membership is cumulative and this method may be invoked @@ -196,7 +214,8 @@ public interface MulticastChannel * If the channel is currently a member of the group on the given * interface to receive all datagrams * @throws UnsupportedOperationException - * If the underlying operation system does not support source filtering + * If the channel's socket is not an Internet Protocol socket or + * source filtering is not supported * @throws ClosedChannelException * If this channel is closed * @throws IOException diff --git a/src/share/classes/java/nio/channels/NetworkChannel.java b/src/share/classes/java/nio/channels/NetworkChannel.java index fae642fcb..103427759 100644 --- a/src/share/classes/java/nio/channels/NetworkChannel.java +++ b/src/share/classes/java/nio/channels/NetworkChannel.java @@ -1,5 +1,5 @@ /* - * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007-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 @@ -95,9 +95,10 @@ public interface NetworkChannel * java.net.InetSocketAddress}. * * @return The socket address that the socket is bound to, or {@code null} - * if the channel is not {@link #isOpen open} or the channel's socket - * is not bound + * if the channel's socket is not bound * + * @throws ClosedChannelException + * If the channel is closed * @throws IOException * If an I/O error occurs */ @@ -114,9 +115,10 @@ public interface NetworkChannel * * @return This channel * + * @throws UnsupportedOperationException + * If the socket option is not supported by this channel * @throws IllegalArgumentException - * If the socket option is not supported by this channel, or - * the value is not a valid value for this socket option + * If the value is not a valid value for this socket option * @throws ClosedChannelException * If this channel is closed * @throws IOException @@ -135,7 +137,7 @@ public interface NetworkChannel * @return The value of the socket option. A value of {@code null} may be * a valid value for some socket options. * - * @throws IllegalArgumentException + * @throws UnsupportedOperationException * If the socket option is not supported by this channel * @throws ClosedChannelException * If this channel is closed @@ -154,5 +156,5 @@ public interface NetworkChannel * * @return A set of the socket options supported by this channel */ - Set> options(); + Set> supportedOptions(); } diff --git a/src/share/classes/java/nio/channels/SeekableByteChannel.java b/src/share/classes/java/nio/channels/SeekableByteChannel.java new file mode 100644 index 000000000..33efc2488 --- /dev/null +++ b/src/share/classes/java/nio/channels/SeekableByteChannel.java @@ -0,0 +1,168 @@ +/* + * Copyright 2007-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 java.nio.channels; + +import java.nio.ByteBuffer; +import java.io.IOException; + +/** + * A byte channel that maintains a current position and allows the + * position to be changed. + * + *

A seekable byte channel is connected to an entity, typically a file, + * that contains a variable-length sequence of bytes that can be read and + * written. The current position can be {@link #position() queried} and + * {@link #position(long) modified}. The channel also provides access to + * the current size of the entity to which the channel is connected. The + * size increases when bytes are written beyond its current size; the size + * decreases when it is {@link #truncate truncated}. + * + *

The {@link #position(long) position} and {@link #truncate truncate} methods + * which do not otherwise have a value to return are specified to return the + * channel upon which they are invoked. This allows method invocations to be + * chained. Implementations of this interface should specialize the return type + * so that method invocations on the implementation class can be chained. + * + * @since 1.7 + * @see java.nio.file.FileRef#newByteChannel + */ + +public interface SeekableByteChannel + extends ByteChannel +{ + /** + * Reads a sequence of bytes from this channel into the given buffer. + * + *

Bytes are read starting at this channel's current position, and + * then the position is updated with the number of bytes actually read. + * Otherwise this method behaves exactly as specified in the {@link + * ReadableByteChannel} interface. + */ + @Override + int read(ByteBuffer dst) throws IOException; + + /** + * Writes a sequence of bytes to this channel from the given buffer. + * + *

Bytes are written starting at this channel's current position, unless + * the channel is connected to an entity such as a file that is opened with + * the {@link java.nio.file.StandardOpenOption#APPEND APPEND} option, in + * which case the position is first advanced to the end. The entity to which + * the channel is connected is grown, if necessary, to accommodate the + * written bytes, and then the position is updated with the number of bytes + * actually written. Otherwise this method behaves exactly as specified by + * the {@link WritableByteChannel} interface. + */ + @Override + int write(ByteBuffer src) throws IOException; + + /** + * Returns this channel's position. + * + * @return This channel's position, + * a non-negative integer counting the number of bytes + * from the beginning of the entity to the current position + * + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If some other I/O error occurs + */ + long position() throws IOException; + + /** + * Sets this channel's position. + * + *

Setting the position to a value that is greater than the current size + * is legal but does not change the size of the entity. A later attempt to + * read bytes at such a position will immediately return an end-of-file + * indication. A later attempt to write bytes at such a position will cause + * the entity to grow to accommodate the new bytes; the values of any bytes + * between the previous end-of-file and the newly-written bytes are + * unspecified. + * + *

Setting the channel's position is not recommended when connected to + * an entity, typically a file, that is opened with the {@link + * java.nio.file.StandardOpenOption#APPEND APPEND} option. When opened for + * append, the position is first advanced to the end before writing. + * + * @param newPosition + * The new position, a non-negative integer counting + * the number of bytes from the beginning of the entity + * + * @return This channel + * + * @throws ClosedChannelException + * If this channel is closed + * @throws IllegalArgumentException + * If the new position is negative + * @throws IOException + * If some other I/O error occurs + */ + SeekableByteChannel position(long newPosition) throws IOException; + + /** + * Returns the current size of entity to which this channel is connected. + * + * @return The current size, measured in bytes + * + * @throws ClosedChannelException + * If this channel is closed + * @throws IOException + * If some other I/O error occurs + */ + long size() throws IOException; + + /** + * Truncates the entity, to which this channel is connected, to the given + * size. + * + *

If the given size is less than the current size then the entity is + * truncated, discarding any bytes beyond the new end. If the given size is + * greater than or equal to the current size then the entity is not modified. + * In either case, if the current position is greater than the given size + * then it is set to that size. + * + *

An implementation of this interface may prohibit truncation when + * connected to an entity, typically a file, opened with the {@link + * java.nio.file.StandardOpenOption#APPEND APPEND} option. + * + * @param size + * The new size, a non-negative byte count + * + * @return This channel + * + * @throws NonWritableChannelException + * If this channel was not opened for writing + * @throws ClosedChannelException + * If this channel is closed + * @throws IllegalArgumentException + * If the new size is negative + * @throws IOException + * If some other I/O error occurs + */ + SeekableByteChannel truncate(long size) throws IOException; +} diff --git a/src/share/classes/java/nio/channels/ServerSocketChannel.java b/src/share/classes/java/nio/channels/ServerSocketChannel.java index 84ea062c9..5be9bc7cb 100644 --- a/src/share/classes/java/nio/channels/ServerSocketChannel.java +++ b/src/share/classes/java/nio/channels/ServerSocketChannel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-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 @@ -29,7 +29,8 @@ import java.io.IOException; import java.net.ServerSocket; import java.net.SocketOption; import java.net.SocketAddress; -import java.nio.channels.spi.*; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.SelectorProvider; /** * A selectable channel for stream-oriented listening sockets. @@ -195,6 +196,7 @@ public abstract class ServerSocketChannel throws IOException; /** + * @throws UnsupportedOperationException {@inheritDoc} * @throws IllegalArgumentException {@inheritDoc} * @throws ClosedChannelException {@inheritDoc} * @throws IOException {@inheritDoc} diff --git a/src/share/classes/java/nio/channels/SocketChannel.java b/src/share/classes/java/nio/channels/SocketChannel.java index 2e96bd2e4..975048df0 100644 --- a/src/share/classes/java/nio/channels/SocketChannel.java +++ b/src/share/classes/java/nio/channels/SocketChannel.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-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 @@ -30,7 +30,8 @@ import java.net.Socket; import java.net.SocketOption; import java.net.SocketAddress; import java.nio.ByteBuffer; -import java.nio.channels.spi.*; +import java.nio.channels.spi.AbstractSelectableChannel; +import java.nio.channels.spi.SelectorProvider; /** * A selectable channel for stream-oriented connecting sockets. @@ -212,7 +213,7 @@ public abstract class SocketChannel /** * @throws ConnectionPendingException - * If a non-blocking connection operation is already in progress on + * If a non-blocking connect operation is already in progress on * this channel * @throws AlreadyBoundException {@inheritDoc} * @throws UnsupportedAddressTypeException {@inheritDoc} @@ -226,6 +227,7 @@ public abstract class SocketChannel throws IOException; /** + * @throws UnsupportedOperationException {@inheritDoc} * @throws IllegalArgumentException {@inheritDoc} * @throws ClosedChannelException {@inheritDoc} * @throws IOException {@inheritDoc} @@ -432,15 +434,17 @@ public abstract class SocketChannel * socket address then the return value from this method is of type {@link * java.net.InetSocketAddress}. * - * @return The remote address; {@code null} if the channel is not {@link - * #isOpen open} or the channel's socket is not connected + * @return The remote address; {@code null} if the channel's socket is not + * connected * + * @throws ClosedChannelException + * If the channel is closed * @throws IOException * If an I/O error occurs * * @since 1.7 */ - public abstract SocketAddress getConnectedAddress() throws IOException; + public abstract SocketAddress getRemoteAddress() throws IOException; // -- ByteChannel operations -- diff --git a/src/share/classes/java/nio/channels/exceptions b/src/share/classes/java/nio/channels/exceptions index 04cfbe03e..fed9f72ab 100644 --- a/src/share/classes/java/nio/channels/exceptions +++ b/src/share/classes/java/nio/channels/exceptions @@ -1,5 +1,5 @@ # -# Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. +# Copyright 2000-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 @@ -150,6 +150,21 @@ gen OverlappingFileLockException " SINCE=1.7 +SUPER=java.io.IOException + +gen InterruptedByTimeoutException " + * Checked exception received by a thread when a timeout elapses before an + * asynchronous operation completes." \ + -4268008601014042947L + +SUPER=IllegalArgumentException + +gen IllegalChannelGroupException " + * Unchecked exception thrown when an attempt is made to open a channel + * in a group that was not created by the same provider. " \ + -2495041211157744253L + + SUPER=IllegalStateException gen AlreadyBoundException " @@ -157,3 +172,23 @@ gen AlreadyBoundException " * network oriented channel that is already bound." \ 6796072983322737592L +gen AcceptPendingException " + * Unchecked exception thrown when an attempt is made to initiate an accept + * operation on a channel and a previous accept operation has not completed." \ + 2721339977965416421L + +gen ReadPendingException " + * Unchecked exception thrown when an attempt is made to read from an + * asynchronous socket channel and a previous read has not completed." \ + 1986315242191227217L + +gen WritePendingException " + * Unchecked exception thrown when an attempt is made to write to an + * asynchronous socket channel and a previous write has not completed." \ + 7031871839266032276L + +gen ShutdownChannelGroupException " + * Unchecked exception thrown when an attempt is made to construct a channel in + * a group that is shutdown or the completion handler for an I/O operation + * cannot be invoked because the channel group is shutdown." \ + -3903801676350154157L diff --git a/src/share/classes/java/nio/channels/package-info.java b/src/share/classes/java/nio/channels/package-info.java index e8c2a929d..47a1cb5f9 100644 --- a/src/share/classes/java/nio/channels/package-info.java +++ b/src/share/classes/java/nio/channels/package-info.java @@ -1,5 +1,5 @@ /* - * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2001-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 @@ -46,6 +46,10 @@ * Can read/write to/from a buffer *     {@link java.nio.channels.SeekableByteChannel} * A {@code ByteChannel} connected to an entity that contains a variable-length sequence of bytes + *   {@link java.nio.channels.AsynchronousChannel} + * Supports asynchronous I/O operations. + *     {@link java.nio.channels.AsynchronousByteChannel} + * Can read and write bytes asynchronously *   {@link java.nio.channels.NetworkChannel} * A channel to a network socket *     {@link java.nio.channels.MulticastChannel} @@ -218,12 +222,70 @@ * directly; custom channel classes should extend the appropriate {@link * java.nio.channels.SelectableChannel} subclasses defined in this package. * + * + * + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + *

Asynchronous I/O

Description

{@link java.nio.channels.AsynchronousFileChannel}An asynchronous channel for reading, writing, and manipulating a file
{@link java.nio.channels.AsynchronousSocketChannel}An asynchronous channel to a stream-oriented connecting socket
{@link java.nio.channels.AsynchronousServerSocketChannel}  An asynchronous channel to a stream-oriented listening socket
{@link java.nio.channels.AsynchronousDatagramChannel}An asynchronous channel to a datagram-oriented socket
{@link java.nio.channels.CompletionHandler}A handler for consuming the result of an asynchronous operation
{@link java.nio.channels.AsynchronousChannelGroup}A grouping of asynchronous channels for the purpose of resource sharing
+ * + *

{@link java.nio.channels.AsynchronousChannel Asynchronous channels} are a + * special type of channel capable of asynchronous I/O operations. Asynchronous + * channels are non-blocking and define methods to initiate asynchronous + * operations, returning a {@link java.util.concurrent.Future} representing the + * pending result of each operation. The {@code Future} can be used to poll or + * wait for the result of the operation. Asynchronous I/O operations can also + * specify a {@link java.nio.channels.CompletionHandler} to invoke when the + * operation completes. A completion handler is user provided code that is executed + * to consume the result of I/O operation. + * + *

This package defines asynchronous-channel classes that are connected to + * a stream-oriented connecting or listening socket, or a datagram-oriented socket. + * It also defines the {@link java.nio.channels.AsynchronousFileChannel} class + * for asynchronous reading, writing, and manipulating a file. As with the {@link + * java.nio.channels.FileChannel} it supports operations to truncate the file + * to a specific size, force updates to the file to be written to the storage + * device, or acquire locks on the whole file or on a specific region of the file. + * Unlike the {@code FileChannel} it does not define methods for mapping a + * region of the file directly into memory. Where memory mapped I/O is required, + * then a {@code FileChannel} can be used. + * + *

Asynchronous channels are bound to an asynchronous channel group for the + * purpose of resource sharing. A group has an associated {@link + * java.util.concurrent.ExecutorService} to which tasks are submitted to handle + * I/O events and dispatch to completion handlers that consume the result of + * asynchronous operations performed on channels in the group. The group can + * optionally be specified when creating the channel or the channel can be bound + * to a default group. Sophisticated users may wish to create their + * own asynchronous channel groups or configure the {@code ExecutorService} + * that will be used for the default group. + * + *

As with selectors, the implementatin of asynchronous channels can be + * replaced by "plugging in" an alternative definition or instance of the {@link + * java.nio.channels.spi.AsynchronousChannelProvider} class defined in the + * {@link java.nio.channels.spi} package. It is not expected that many + * developers will actually make use of this facility; it is provided primarily + * so that sophisticated users can take advantage of operating-system-specific + * asynchronous I/O mechanisms when very high performance is required. + * *


*

Unless otherwise noted, passing a null argument to a constructor * or method in any class or interface in this package will cause a {@link * java.lang.NullPointerException NullPointerException} to be thrown. * * @since 1.4 + * @updated 1.7 * @author Mark Reinhold * @author JSR-51 Expert Group */ diff --git a/src/share/classes/java/nio/channels/spi/AsynchronousChannelProvider.java b/src/share/classes/java/nio/channels/spi/AsynchronousChannelProvider.java new file mode 100644 index 000000000..941364876 --- /dev/null +++ b/src/share/classes/java/nio/channels/spi/AsynchronousChannelProvider.java @@ -0,0 +1,264 @@ +/* + * Copyright 2007-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 java.nio.channels.spi; + +import java.nio.channels.*; +import java.net.ProtocolFamily; +import java.io.IOException; +import java.util.Iterator; +import java.util.ServiceLoader; +import java.util.ServiceConfigurationError; +import java.util.concurrent.*; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * Service-provider class for asynchronous channels. + * + *

An asynchronous channel provider is a concrete subclass of this class that + * has a zero-argument constructor and implements the abstract methods specified + * below. A given invocation of the Java virtual machine maintains a single + * system-wide default provider instance, which is returned by the {@link + * #provider() provider} method. The first invocation of that method will locate + * the default provider as specified below. + * + *

All of the methods in this class are safe for use by multiple concurrent + * threads.

+ * + * @since 1.7 + */ + +public abstract class AsynchronousChannelProvider { + private static Void checkPermission() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new RuntimePermission("asynchronousChannelProvider")); + return null; + } + private AsynchronousChannelProvider(Void ignore) { } + + /** + * Initializes a new instance of this class. + * + * @throws SecurityException + * If a security manager has been installed and it denies + * {@link RuntimePermission}("asynchronousChannelProvider") + */ + protected AsynchronousChannelProvider() { + this(checkPermission()); + } + + // lazy initialization of default provider + private static class ProviderHolder { + static final AsynchronousChannelProvider provider = load(); + + private static AsynchronousChannelProvider load() { + return AccessController + .doPrivileged(new PrivilegedAction() { + public AsynchronousChannelProvider run() { + AsynchronousChannelProvider p; + p = loadProviderFromProperty(); + if (p != null) + return p; + p = loadProviderAsService(); + if (p != null) + return p; + return sun.nio.ch.DefaultAsynchronousChannelProvider.create(); + }}); + } + + private static AsynchronousChannelProvider loadProviderFromProperty() { + String cn = System.getProperty("java.nio.channels.spi.AsynchronousChannelProvider"); + if (cn == null) + return null; + try { + Class c = Class.forName(cn, true, + ClassLoader.getSystemClassLoader()); + return (AsynchronousChannelProvider)c.newInstance(); + } catch (ClassNotFoundException x) { + throw new ServiceConfigurationError(null, x); + } catch (IllegalAccessException x) { + throw new ServiceConfigurationError(null, x); + } catch (InstantiationException x) { + throw new ServiceConfigurationError(null, x); + } catch (SecurityException x) { + throw new ServiceConfigurationError(null, x); + } + } + + private static AsynchronousChannelProvider loadProviderAsService() { + ServiceLoader sl = + ServiceLoader.load(AsynchronousChannelProvider.class, + ClassLoader.getSystemClassLoader()); + Iterator i = sl.iterator(); + for (;;) { + try { + return (i.hasNext()) ? i.next() : null; + } catch (ServiceConfigurationError sce) { + if (sce.getCause() instanceof SecurityException) { + // Ignore the security exception, try the next provider + continue; + } + throw sce; + } + } + } + } + + /** + * Returns the system-wide default asynchronous channel provider for this + * invocation of the Java virtual machine. + * + *

The first invocation of this method locates the default provider + * object as follows:

+ * + *
    + * + *
  1. If the system property + * java.nio.channels.spi.AsynchronousChannelProvider is defined + * then it is taken to be the fully-qualified name of a concrete provider class. + * The class is loaded and instantiated; if this process fails then an + * unspecified error is thrown.

  2. + * + *
  3. If a provider class has been installed in a jar file that is + * visible to the system class loader, and that jar file contains a + * provider-configuration file named + * java.nio.channels.spi.AsynchronousChannelProvider in the resource + * directory META-INF/services, then the first class name + * specified in that file is taken. The class is loaded and + * instantiated; if this process fails then an unspecified error is + * thrown.

  4. + * + *
  5. Finally, if no provider has been specified by any of the above + * means then the system-default provider class is instantiated and the + * result is returned.

  6. + * + *
+ * + *

Subsequent invocations of this method return the provider that was + * returned by the first invocation.

+ * + * @return The system-wide default AsynchronousChannel provider + */ + public static AsynchronousChannelProvider provider() { + return ProviderHolder.provider; + } + + /** + * Constructs a new asynchronous channel group with a fixed thread pool. + * + * @param nThreads + * The number of threads in the pool + * @param threadFactory + * The factory to use when creating new threads + * + * @throws IllegalArgumentException + * If {@code nThreads <= 0} + * @throws IOException + * If an I/O error occurs + * + * @see AsynchronousChannelGroup#withFixedThreadPool + */ + public abstract AsynchronousChannelGroup + openAsynchronousChannelGroup(int nThreads, ThreadFactory threadFactory) throws IOException; + + /** + * Constructs a new asynchronous channel group with the given thread pool. + * + * @param executor + * The thread pool + * @param initialSize + * A value {@code >=0} or a negative value for implementation + * specific default + * + * @throws IOException + * If an I/O error occurs + * + * @see AsynchronousChannelGroup#withCachedThreadPool + */ + public abstract AsynchronousChannelGroup + openAsynchronousChannelGroup(ExecutorService executor, int initialSize) throws IOException; + + /** + * Opens an asynchronous server-socket channel. + * + * @param group + * The group to which the channel is bound, or {@code null} to + * bind to the default group + * + * @return The new channel + * + * @throws IllegalChannelGroupException + * If the provider that created the group differs from this provider + * @throws ShutdownChannelGroupException + * The group is shutdown + * @throws IOException + * If an I/O error occurs + */ + public abstract AsynchronousServerSocketChannel openAsynchronousServerSocketChannel + (AsynchronousChannelGroup group) throws IOException; + + /** + * Opens an asynchronous socket channel. + * + * @param group + * The group to which the channel is bound, or {@code null} to + * bind to the default group + * + * @return The new channel + * + * @throws IllegalChannelGroupException + * If the provider that created the group differs from this provider + * @throws ShutdownChannelGroupException + * The group is shutdown + * @throws IOException + * If an I/O error occurs + */ + public abstract AsynchronousSocketChannel openAsynchronousSocketChannel + (AsynchronousChannelGroup group) throws IOException; + + /** + * Opens an asynchronous datagram channel. + * + * @param family + * The protocol family, or {@code null} for the default protocol + * family + * @param group + * The group to which the channel is bound, or {@code null} to + * bind to the default group + * + * @return The new channel + * + * @throws IllegalChannelGroupException + * If the provider that created the group differs from this provider + * @throws ShutdownChannelGroupException + * The group is shutdown + * @throws IOException + * If an I/O error occurs + */ + public abstract AsynchronousDatagramChannel openAsynchronousDatagramChannel + (ProtocolFamily family, AsynchronousChannelGroup group) throws IOException; +} diff --git a/src/share/classes/java/nio/channels/spi/SelectorProvider.java b/src/share/classes/java/nio/channels/spi/SelectorProvider.java index dc61c9c27..d03800e22 100644 --- a/src/share/classes/java/nio/channels/spi/SelectorProvider.java +++ b/src/share/classes/java/nio/channels/spi/SelectorProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-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 @@ -89,8 +89,8 @@ public abstract class SelectorProvider { if (cn == null) return false; try { - Class c = Class.forName(cn, true, - ClassLoader.getSystemClassLoader()); + Class c = Class.forName(cn, true, + ClassLoader.getSystemClassLoader()); provider = (SelectorProvider)c.newInstance(); return true; } catch (ClassNotFoundException x) { diff --git a/src/share/classes/java/nio/channels/spi/package.html b/src/share/classes/java/nio/channels/spi/package.html index 5b3ddd299..5960da38e 100644 --- a/src/share/classes/java/nio/channels/spi/package.html +++ b/src/share/classes/java/nio/channels/spi/package.html @@ -1,5 +1,5 @@ URI conversion + */ + +class UnixUriUtils { + private UnixUriUtils() { } + + /** + * Converts URI to Path + */ + static UnixPath fromUri(UnixFileSystem fs, URI uri) { + if (!uri.isAbsolute()) + throw new IllegalArgumentException("URI is not absolute"); + if (uri.isOpaque()) + throw new IllegalArgumentException("URI is not hierarchical"); + String scheme = uri.getScheme(); + if ((scheme == null) || !scheme.equalsIgnoreCase("file")) + throw new IllegalArgumentException("URI scheme is not \"file\""); + if (uri.getAuthority() != null) + throw new IllegalArgumentException("URI has an authority component"); + if (uri.getFragment() != null) + throw new IllegalArgumentException("URI has a fragment component"); + if (uri.getQuery() != null) + throw new IllegalArgumentException("URI has a query component"); + + String path = uri.getPath(); + if (path.equals("")) + throw new IllegalArgumentException("URI path component is empty"); + if (path.endsWith("/") && (path.length() > 1)) { + // "/foo/" --> "/foo", but "/" --> "/" + path = path.substring(0, path.length() - 1); + } + + // preserve bytes + byte[] result = new byte[path.length()]; + for (int i=0; i> 4) & 0x0f]); + sb.append(hexDigits[(c >> 0) & 0x0f]); + } + } + + // trailing slash if directory + if (sb.charAt(sb.length()-1) != '/') { + try { + if (UnixFileAttributes.get(up, true).isDirectory()) + sb.append('/'); + } catch (UnixException x) { + // ignore + } + } + + try { + return new URI(sb.toString()); + } catch (URISyntaxException x) { + throw new AssertionError(x); // should not happen + } + } + + // The following is copied from java.net.URI + + // Compute the low-order mask for the characters in the given string + private static long lowMask(String chars) { + int n = chars.length(); + long m = 0; + for (int i = 0; i < n; i++) { + char c = chars.charAt(i); + if (c < 64) + m |= (1L << c); + } + return m; + } + + // Compute the high-order mask for the characters in the given string + private static long highMask(String chars) { + int n = chars.length(); + long m = 0; + for (int i = 0; i < n; i++) { + char c = chars.charAt(i); + if ((c >= 64) && (c < 128)) + m |= (1L << (c - 64)); + } + return m; + } + + // Compute a low-order mask for the characters + // between first and last, inclusive + private static long lowMask(char first, char last) { + long m = 0; + int f = Math.max(Math.min(first, 63), 0); + int l = Math.max(Math.min(last, 63), 0); + for (int i = f; i <= l; i++) + m |= 1L << i; + return m; + } + + // Compute a high-order mask for the characters + // between first and last, inclusive + private static long highMask(char first, char last) { + long m = 0; + int f = Math.max(Math.min(first, 127), 64) - 64; + int l = Math.max(Math.min(last, 127), 64) - 64; + for (int i = f; i <= l; i++) + m |= 1L << i; + return m; + } + + // Tell whether the given character is permitted by the given mask pair + private static boolean match(char c, long lowMask, long highMask) { + if (c < 64) + return ((1L << c) & lowMask) != 0; + if (c < 128) + return ((1L << (c - 64)) & highMask) != 0; + return false; + } + + // digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | + // "8" | "9" + private static final long L_DIGIT = lowMask('0', '9'); + private static final long H_DIGIT = 0L; + + // upalpha = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | + // "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | + // "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" + private static final long L_UPALPHA = 0L; + private static final long H_UPALPHA = highMask('A', 'Z'); + + // lowalpha = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | + // "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | + // "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" + private static final long L_LOWALPHA = 0L; + private static final long H_LOWALPHA = highMask('a', 'z'); + + // alpha = lowalpha | upalpha + private static final long L_ALPHA = L_LOWALPHA | L_UPALPHA; + private static final long H_ALPHA = H_LOWALPHA | H_UPALPHA; + + // alphanum = alpha | digit + private static final long L_ALPHANUM = L_DIGIT | L_ALPHA; + private static final long H_ALPHANUM = H_DIGIT | H_ALPHA; + + // mark = "-" | "_" | "." | "!" | "~" | "*" | "'" | + // "(" | ")" + private static final long L_MARK = lowMask("-_.!~*'()"); + private static final long H_MARK = highMask("-_.!~*'()"); + + // unreserved = alphanum | mark + private static final long L_UNRESERVED = L_ALPHANUM | L_MARK; + private static final long H_UNRESERVED = H_ALPHANUM | H_MARK; + + // pchar = unreserved | escaped | + // ":" | "@" | "&" | "=" | "+" | "$" | "," + private static final long L_PCHAR + = L_UNRESERVED | lowMask(":@&=+$,"); + private static final long H_PCHAR + = H_UNRESERVED | highMask(":@&=+$,"); + + // All valid path characters + private static final long L_PATH = L_PCHAR | lowMask(";/"); + private static final long H_PATH = H_PCHAR | highMask(";/"); + + private final static char[] hexDigits = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; +} diff --git a/src/solaris/classes/sun/nio/fs/UnixUserPrincipals.java b/src/solaris/classes/sun/nio/fs/UnixUserPrincipals.java new file mode 100644 index 000000000..88dfe9c4d --- /dev/null +++ b/src/solaris/classes/sun/nio/fs/UnixUserPrincipals.java @@ -0,0 +1,175 @@ +/* + * 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.*; +import java.io.IOException; +import static sun.nio.fs.UnixNativeDispatcher.*; + +/** + * Unix implementation of java.nio.file.attribute.UserPrincipal + */ + +class UnixUserPrincipals { + private static User createSpecial(String name) { return new User(-1, name); } + + static final User SPECIAL_OWNER = createSpecial("OWNER@"); + static final User SPECIAL_GROUP = createSpecial("GROUP@"); + static final User SPECIAL_EVERYONE = createSpecial("EVERYONE@"); + + static class User implements UserPrincipal { + private final int id; // uid or gid + private final boolean isGroup; + private final String name; + + private User(int id, boolean isGroup, String name) { + this.id = id; + this.isGroup = isGroup; + this.name = name; + } + + User(int id, String name) { + this(id, false, name); + } + + int uid() { + if (isGroup) + throw new AssertionError(); + return id; + } + + int gid() { + if (isGroup) + return id; + throw new AssertionError(); + } + + boolean isSpecial() { + return id == -1; + } + + @Override + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + if (!(obj instanceof User)) + return false; + User other = (User)obj; + if ((this.id != other.id) || + (this.isGroup != other.isGroup)) { + return false; + } + // specials + if (this.id == -1 && other.id == -1) + return this.name.equals(other.name); + + return true; + } + + @Override + public int hashCode() { + return (id != -1) ? id : name.hashCode(); + } + } + + static class Group extends User implements GroupPrincipal { + Group(int id, String name) { + super(id, true, name); + } + } + + // return UserPrincipal representing given uid + static User fromUid(int uid) { + String name = null; + try { + name = new String(getpwuid(uid)); + } catch (UnixException x) { + name = Integer.toString(uid); + } + return new User(uid, name); + } + + // return GroupPrincipal representing given gid + static Group fromGid(int gid) { + String name = null; + try { + name = new String(getgrgid(gid)); + } catch (UnixException x) { + name = Integer.toString(gid); + } + return new Group(gid, name); + } + + // lookup user or group name + private static int lookupName(String name, boolean isGroup) + throws IOException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new RuntimePermission("lookupUserInformation")); + } + int id = -1; + try { + id = (isGroup) ? getgrnam(name) : getpwnam(name); + } catch (UnixException x) { + throw new IOException(name + ": " + x.errorString()); + } + if (id == -1) + throw new UserPrincipalNotFoundException(name); + return id; + + } + + // lookup user name + static UserPrincipal lookupUser(String name) throws IOException { + if (name.equals(SPECIAL_OWNER.getName())) + return SPECIAL_OWNER; + if (name.equals(SPECIAL_GROUP.getName())) + return SPECIAL_GROUP; + if (name.equals(SPECIAL_EVERYONE.getName())) + return SPECIAL_EVERYONE; + int uid = lookupName(name, false); + return new User(uid, name); + } + + // lookup group name + static GroupPrincipal lookupGroup(String group) + throws IOException + { + int gid = lookupName(group, true); + return new Group(gid, group); + } +} diff --git a/src/solaris/native/sun/nio/ch/EPoll.c b/src/solaris/native/sun/nio/ch/EPoll.c new file mode 100644 index 000000000..fc9cab72c --- /dev/null +++ b/src/solaris/native/sun/nio/ch/EPoll.c @@ -0,0 +1,151 @@ +/* + * 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. + */ + +#include "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "jlong.h" +#include "nio_util.h" + +#include "sun_nio_ch_EPoll.h" + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* epoll_wait(2) man page */ + +typedef union epoll_data { + void *ptr; + int fd; + __uint32_t u32; + __uint64_t u64; +} epoll_data_t; + +struct epoll_event { + __uint32_t events; /* Epoll events */ + epoll_data_t data; /* User data variable */ +} __attribute__ ((__packed__)); + +#ifdef __cplusplus +} +#endif + +/* + * epoll event notification is new in 2.6 kernel. As the offical build + * platform for the JDK is on a 2.4-based distribution then we must + * obtain the addresses of the epoll functions dynamically. + */ +typedef int (*epoll_create_t)(int size); +typedef int (*epoll_ctl_t) (int epfd, int op, int fd, struct epoll_event *event); +typedef int (*epoll_wait_t) (int epfd, struct epoll_event *events, int maxevents, int timeout); + +static epoll_create_t epoll_create_func; +static epoll_ctl_t epoll_ctl_func; +static epoll_wait_t epoll_wait_func; + + +JNIEXPORT void JNICALL +Java_sun_nio_ch_EPoll_init(JNIEnv *env, jclass this) +{ + epoll_create_func = (epoll_create_t) dlsym(RTLD_DEFAULT, "epoll_create"); + epoll_ctl_func = (epoll_ctl_t) dlsym(RTLD_DEFAULT, "epoll_ctl"); + epoll_wait_func = (epoll_wait_t) dlsym(RTLD_DEFAULT, "epoll_wait"); + + if ((epoll_create_func == NULL) || (epoll_ctl_func == NULL) || + (epoll_wait_func == NULL)) { + JNU_ThrowInternalError(env, "unable to get address of epoll functions, pre-2.6 kernel?"); + } +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_EPoll_eventSize(JNIEnv* env, jclass this) +{ + return sizeof(struct epoll_event); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_EPoll_eventsOffset(JNIEnv* env, jclass this) +{ + return offsetof(struct epoll_event, events); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_EPoll_dataOffset(JNIEnv* env, jclass this) +{ + return offsetof(struct epoll_event, data); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_EPoll_epollCreate(JNIEnv *env, jclass c) { + /* + * epoll_create expects a size as a hint to the kernel about how to + * dimension internal structures. We can't predict the size in advance. + */ + int epfd = (*epoll_create_func)(256); + if (epfd < 0) { + JNU_ThrowIOExceptionWithLastError(env, "epoll_create failed"); + } + return epfd; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_EPoll_epollCtl(JNIEnv *env, jclass c, jint epfd, + jint opcode, jint fd, jint events) +{ + struct epoll_event event; + int res; + + event.events = events; + event.data.fd = fd; + + RESTARTABLE((*epoll_ctl_func)(epfd, (int)opcode, (int)fd, &event), res); + + return (res == 0) ? 0 : errno; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_EPoll_epollWait(JNIEnv *env, jclass c, + jint epfd, jlong address, jint numfds) +{ + struct epoll_event *events = jlong_to_ptr(address); + int res; + + RESTARTABLE((*epoll_wait_func)(epfd, events, numfds, -1), res); + if (res < 0) { + JNU_ThrowIOExceptionWithLastError(env, "epoll_wait failed"); + } + return res; +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_EPoll_close0(JNIEnv *env, jclass c, jint epfd) { + int res; + RESTARTABLE(close(epfd), res); +} diff --git a/src/solaris/native/sun/nio/ch/EPollPort.c b/src/solaris/native/sun/nio/ch/EPollPort.c new file mode 100644 index 000000000..8d34dd8dd --- /dev/null +++ b/src/solaris/native/sun/nio/ch/EPollPort.c @@ -0,0 +1,76 @@ +/* + * 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. + */ + +#include "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "jlong.h" +#include "nio_util.h" + +#include "sun_nio_ch_EPollPort.h" + +#include +#include +#include + +JNIEXPORT void JNICALL +Java_sun_nio_ch_EPollPort_socketpair(JNIEnv* env, jclass clazz, jintArray sv) { + int sp[2]; + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) == -1) { + JNU_ThrowIOExceptionWithLastError(env, "socketpair failed"); + } else { + jint res[2]; + res[0] = (jint)sp[0]; + res[1] = (jint)sp[1]; + (*env)->SetIntArrayRegion(env, sv, 0, 2, &res[0]); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_EPollPort_interrupt(JNIEnv *env, jclass c, jint fd) { + int res; + int buf[1]; + buf[0] = 1; + RESTARTABLE(write(fd, buf, 1), res); + if (res < 0) { + JNU_ThrowIOExceptionWithLastError(env, "write failed"); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_EPollPort_drain1(JNIEnv *env, jclass cl, jint fd) { + int res; + char buf[1]; + RESTARTABLE(read(fd, buf, 1), res); + if (res < 0) { + JNU_ThrowIOExceptionWithLastError(env, "drain1 failed"); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_EPollPort_close0(JNIEnv *env, jclass c, jint fd) { + int res; + RESTARTABLE(close(fd), res); +} diff --git a/src/solaris/native/sun/nio/ch/FileChannelImpl.c b/src/solaris/native/sun/nio/ch/FileChannelImpl.c index b42c899e5..7b37e6168 100644 --- a/src/solaris/native/sun/nio/ch/FileChannelImpl.c +++ b/src/solaris/native/sun/nio/ch/FileChannelImpl.c @@ -1,5 +1,5 @@ /* - * Copyright 2000-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-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 @@ -31,7 +31,6 @@ #include #include "sun_nio_ch_FileChannelImpl.h" #include "java_lang_Integer.h" -#include "java_lang_Long.h" #include "nio.h" #include "nio_util.h" #include @@ -145,32 +144,6 @@ Java_sun_nio_ch_FileChannelImpl_unmap0(JNIEnv *env, jobject this, } -JNIEXPORT jint JNICALL -Java_sun_nio_ch_FileChannelImpl_truncate0(JNIEnv *env, jobject this, - jobject fdo, jlong size) -{ - return handle(env, - ftruncate64(fdval(env, fdo), size), - "Truncation failed"); -} - - -JNIEXPORT jint JNICALL -Java_sun_nio_ch_FileChannelImpl_force0(JNIEnv *env, jobject this, - jobject fdo, jboolean md) -{ - jint fd = fdval(env, fdo); - int result = 0; - - if (md == JNI_FALSE) { - result = fdatasync(fd); - } else { - result = fsync(fd); - } - return handle(env, result, "Force failed"); -} - - JNIEXPORT jlong JNICALL Java_sun_nio_ch_FileChannelImpl_position0(JNIEnv *env, jobject this, jobject fdo, jlong offset) @@ -187,17 +160,6 @@ Java_sun_nio_ch_FileChannelImpl_position0(JNIEnv *env, jobject this, } -JNIEXPORT jlong JNICALL -Java_sun_nio_ch_FileChannelImpl_size0(JNIEnv *env, jobject this, jobject fdo) -{ - struct stat64 fbuf; - - if (fstat64(fdval(env, fdo), &fbuf) < 0) - return handle(env, -1, "Size failed"); - return fbuf.st_size; -} - - JNIEXPORT void JNICALL Java_sun_nio_ch_FileChannelImpl_close0(JNIEnv *env, jobject this, jobject fdo) { @@ -280,65 +242,3 @@ Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this, } #endif } - -JNIEXPORT jint JNICALL -Java_sun_nio_ch_FileChannelImpl_lock0(JNIEnv *env, jobject this, jobject fdo, - jboolean block, jlong pos, jlong size, - jboolean shared) -{ - jint fd = fdval(env, fdo); - jint lockResult = 0; - int cmd = 0; - struct flock64 fl; - - fl.l_whence = SEEK_SET; - if (size == (jlong)java_lang_Long_MAX_VALUE) { - fl.l_len = (off64_t)0; - } else { - fl.l_len = (off64_t)size; - } - fl.l_start = (off64_t)pos; - if (shared == JNI_TRUE) { - fl.l_type = F_RDLCK; - } else { - fl.l_type = F_WRLCK; - } - if (block == JNI_TRUE) { - cmd = F_SETLKW64; - } else { - cmd = F_SETLK64; - } - lockResult = fcntl(fd, cmd, &fl); - if (lockResult < 0) { - if ((cmd == F_SETLK64) && (errno == EAGAIN)) - return sun_nio_ch_FileChannelImpl_NO_LOCK; - if (errno == EINTR) - return sun_nio_ch_FileChannelImpl_INTERRUPTED; - JNU_ThrowIOExceptionWithLastError(env, "Lock failed"); - } - return 0; -} - - -JNIEXPORT void JNICALL -Java_sun_nio_ch_FileChannelImpl_release0(JNIEnv *env, jobject this, - jobject fdo, jlong pos, jlong size) -{ - jint fd = fdval(env, fdo); - jint lockResult = 0; - struct flock64 fl; - int cmd = F_SETLK64; - - fl.l_whence = SEEK_SET; - if (size == (jlong)java_lang_Long_MAX_VALUE) { - fl.l_len = (off64_t)0; - } else { - fl.l_len = (off64_t)size; - } - fl.l_start = (off64_t)pos; - fl.l_type = F_UNLCK; - lockResult = fcntl(fd, cmd, &fl); - if (lockResult < 0) { - JNU_ThrowIOExceptionWithLastError(env, "Release failed"); - } -} diff --git a/src/solaris/native/sun/nio/ch/FileDispatcher.c b/src/solaris/native/sun/nio/ch/FileDispatcherImpl.c similarity index 50% rename from src/solaris/native/sun/nio/ch/FileDispatcher.c rename to src/solaris/native/sun/nio/ch/FileDispatcherImpl.c index fd3c0e3d6..ed6588933 100644 --- a/src/solaris/native/sun/nio/ch/FileDispatcher.c +++ b/src/solaris/native/sun/nio/ch/FileDispatcherImpl.c @@ -1,5 +1,5 @@ /* - * Copyright 2000-2002 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-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 @@ -27,11 +27,13 @@ #include "jni_util.h" #include "jvm.h" #include "jlong.h" -#include "sun_nio_ch_FileDispatcher.h" +#include "sun_nio_ch_FileDispatcherImpl.h" +#include "java_lang_Long.h" #include #include #include #include +#include "nio.h" #include "nio_util.h" @@ -40,7 +42,7 @@ static int preCloseFD = -1; /* File descriptor to which we dup other fd's JNIEXPORT void JNICALL -Java_sun_nio_ch_FileDispatcher_init(JNIEnv *env, jclass cl) +Java_sun_nio_ch_FileDispatcherImpl_init(JNIEnv *env, jclass cl) { int sp[2]; if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) < 0) { @@ -52,7 +54,7 @@ Java_sun_nio_ch_FileDispatcher_init(JNIEnv *env, jclass cl) } JNIEXPORT jint JNICALL -Java_sun_nio_ch_FileDispatcher_read0(JNIEnv *env, jclass clazz, +Java_sun_nio_ch_FileDispatcherImpl_read0(JNIEnv *env, jclass clazz, jobject fdo, jlong address, jint len) { jint fd = fdval(env, fdo); @@ -62,7 +64,7 @@ Java_sun_nio_ch_FileDispatcher_read0(JNIEnv *env, jclass clazz, } JNIEXPORT jint JNICALL -Java_sun_nio_ch_FileDispatcher_pread0(JNIEnv *env, jclass clazz, jobject fdo, +Java_sun_nio_ch_FileDispatcherImpl_pread0(JNIEnv *env, jclass clazz, jobject fdo, jlong address, jint len, jlong offset) { jint fd = fdval(env, fdo); @@ -72,7 +74,7 @@ Java_sun_nio_ch_FileDispatcher_pread0(JNIEnv *env, jclass clazz, jobject fdo, } JNIEXPORT jlong JNICALL -Java_sun_nio_ch_FileDispatcher_readv0(JNIEnv *env, jclass clazz, +Java_sun_nio_ch_FileDispatcherImpl_readv0(JNIEnv *env, jclass clazz, jobject fdo, jlong address, jint len) { jint fd = fdval(env, fdo); @@ -84,7 +86,7 @@ Java_sun_nio_ch_FileDispatcher_readv0(JNIEnv *env, jclass clazz, } JNIEXPORT jint JNICALL -Java_sun_nio_ch_FileDispatcher_write0(JNIEnv *env, jclass clazz, +Java_sun_nio_ch_FileDispatcherImpl_write0(JNIEnv *env, jclass clazz, jobject fdo, jlong address, jint len) { jint fd = fdval(env, fdo); @@ -94,7 +96,7 @@ Java_sun_nio_ch_FileDispatcher_write0(JNIEnv *env, jclass clazz, } JNIEXPORT jint JNICALL -Java_sun_nio_ch_FileDispatcher_pwrite0(JNIEnv *env, jclass clazz, jobject fdo, +Java_sun_nio_ch_FileDispatcherImpl_pwrite0(JNIEnv *env, jclass clazz, jobject fdo, jlong address, jint len, jlong offset) { jint fd = fdval(env, fdo); @@ -104,7 +106,7 @@ Java_sun_nio_ch_FileDispatcher_pwrite0(JNIEnv *env, jclass clazz, jobject fdo, } JNIEXPORT jlong JNICALL -Java_sun_nio_ch_FileDispatcher_writev0(JNIEnv *env, jclass clazz, +Java_sun_nio_ch_FileDispatcherImpl_writev0(JNIEnv *env, jclass clazz, jobject fdo, jlong address, jint len) { jint fd = fdval(env, fdo); @@ -115,6 +117,113 @@ Java_sun_nio_ch_FileDispatcher_writev0(JNIEnv *env, jclass clazz, return convertLongReturnVal(env, writev(fd, iov, len), JNI_FALSE); } +static jlong +handle(JNIEnv *env, jlong rv, char *msg) +{ + if (rv >= 0) + return rv; + if (errno == EINTR) + return IOS_INTERRUPTED; + JNU_ThrowIOExceptionWithLastError(env, msg); + return IOS_THROWN; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_FileDispatcherImpl_force0(JNIEnv *env, jobject this, + jobject fdo, jboolean md) +{ + jint fd = fdval(env, fdo); + int result = 0; + + if (md == JNI_FALSE) { + result = fdatasync(fd); + } else { + result = fsync(fd); + } + return handle(env, result, "Force failed"); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_FileDispatcherImpl_truncate0(JNIEnv *env, jobject this, + jobject fdo, jlong size) +{ + return handle(env, + ftruncate64(fdval(env, fdo), size), + "Truncation failed"); +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_ch_FileDispatcherImpl_size0(JNIEnv *env, jobject this, jobject fdo) +{ + struct stat64 fbuf; + + if (fstat64(fdval(env, fdo), &fbuf) < 0) + return handle(env, -1, "Size failed"); + return fbuf.st_size; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_FileDispatcherImpl_lock0(JNIEnv *env, jobject this, jobject fdo, + jboolean block, jlong pos, jlong size, + jboolean shared) +{ + jint fd = fdval(env, fdo); + jint lockResult = 0; + int cmd = 0; + struct flock64 fl; + + fl.l_whence = SEEK_SET; + if (size == (jlong)java_lang_Long_MAX_VALUE) { + fl.l_len = (off64_t)0; + } else { + fl.l_len = (off64_t)size; + } + fl.l_start = (off64_t)pos; + if (shared == JNI_TRUE) { + fl.l_type = F_RDLCK; + } else { + fl.l_type = F_WRLCK; + } + if (block == JNI_TRUE) { + cmd = F_SETLKW64; + } else { + cmd = F_SETLK64; + } + lockResult = fcntl(fd, cmd, &fl); + if (lockResult < 0) { + if ((cmd == F_SETLK64) && (errno == EAGAIN)) + return sun_nio_ch_FileDispatcherImpl_NO_LOCK; + if (errno == EINTR) + return sun_nio_ch_FileDispatcherImpl_INTERRUPTED; + JNU_ThrowIOExceptionWithLastError(env, "Lock failed"); + } + return 0; +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_FileDispatcherImpl_release0(JNIEnv *env, jobject this, + jobject fdo, jlong pos, jlong size) +{ + jint fd = fdval(env, fdo); + jint lockResult = 0; + struct flock64 fl; + int cmd = F_SETLK64; + + fl.l_whence = SEEK_SET; + if (size == (jlong)java_lang_Long_MAX_VALUE) { + fl.l_len = (off64_t)0; + } else { + fl.l_len = (off64_t)size; + } + fl.l_start = (off64_t)pos; + fl.l_type = F_UNLCK; + lockResult = fcntl(fd, cmd, &fl); + if (lockResult < 0) { + JNU_ThrowIOExceptionWithLastError(env, "Release failed"); + } +} + + static void closeFileDescriptor(JNIEnv *env, int fd) { if (fd != -1) { int result = close(fd); @@ -124,14 +233,14 @@ static void closeFileDescriptor(JNIEnv *env, int fd) { } JNIEXPORT void JNICALL -Java_sun_nio_ch_FileDispatcher_close0(JNIEnv *env, jclass clazz, jobject fdo) +Java_sun_nio_ch_FileDispatcherImpl_close0(JNIEnv *env, jclass clazz, jobject fdo) { jint fd = fdval(env, fdo); closeFileDescriptor(env, fd); } JNIEXPORT void JNICALL -Java_sun_nio_ch_FileDispatcher_preClose0(JNIEnv *env, jclass clazz, jobject fdo) +Java_sun_nio_ch_FileDispatcherImpl_preClose0(JNIEnv *env, jclass clazz, jobject fdo) { jint fd = fdval(env, fdo); if (preCloseFD >= 0) { @@ -141,7 +250,7 @@ Java_sun_nio_ch_FileDispatcher_preClose0(JNIEnv *env, jclass clazz, jobject fdo) } JNIEXPORT void JNICALL -Java_sun_nio_ch_FileDispatcher_closeIntFD(JNIEnv *env, jclass clazz, jint fd) +Java_sun_nio_ch_FileDispatcherImpl_closeIntFD(JNIEnv *env, jclass clazz, jint fd) { closeFileDescriptor(env, fd); } diff --git a/src/solaris/native/sun/nio/ch/SocketDispatcher.c b/src/solaris/native/sun/nio/ch/SocketDispatcher.c index 1524326b6..812ff4bed 100644 --- a/src/solaris/native/sun/nio/ch/SocketDispatcher.c +++ b/src/solaris/native/sun/nio/ch/SocketDispatcher.c @@ -1,5 +1,5 @@ /* - * Copyright 2000-2001 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-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 @@ -27,7 +27,6 @@ #include "jni_util.h" #include "jvm.h" #include "jlong.h" -#include "sun_nio_ch_FileDispatcher.h" /* this is a fake c file to make the build happy since there is no real SocketDispatcher.c file on Solaris but there is on windows. */ diff --git a/src/solaris/native/sun/nio/ch/SolarisEventPort.c b/src/solaris/native/sun/nio/ch/SolarisEventPort.c new file mode 100644 index 000000000..649475946 --- /dev/null +++ b/src/solaris/native/sun/nio/ch/SolarisEventPort.c @@ -0,0 +1,120 @@ +/* + * 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. + */ + +#include "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "jlong.h" +#include "nio_util.h" + +#include +#include +#include +#include // Solaris 10 + +#include "sun_nio_ch_SolarisEventPort.h" + +JNIEXPORT void JNICALL +Java_sun_nio_ch_SolarisEventPort_init(JNIEnv *env, jclass clazz) +{ +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_SolarisEventPort_portCreate + (JNIEnv* env, jclass clazz) +{ + int port = port_create(); + if (port == -1) { + JNU_ThrowIOExceptionWithLastError(env, "port_create"); + } + return (jint)port; +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_SolarisEventPort_portClose + (JNIEnv* env, jclass clazz, jint port) +{ + int res; + RESTARTABLE(close(port), res); +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_SolarisEventPort_portAssociate + (JNIEnv* env, jclass clazz, jint port, jint source, jlong objectAddress, jint events) +{ + uintptr_t object = (uintptr_t)jlong_to_ptr(objectAddress); + + if (port_associate((int)port, (int)source, object, (int)events, NULL) == -1) { + JNU_ThrowIOExceptionWithLastError(env, "port_associate"); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_SolarisEventPort_portDissociate + (JNIEnv* env, jclass clazz, jint port, jint source, jlong objectAddress) +{ + uintptr_t object = (uintptr_t)jlong_to_ptr(objectAddress); + + if (port_dissociate((int)port, (int)source, object) == -1) { + JNU_ThrowIOExceptionWithLastError(env, "port_dissociate"); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_SolarisEventPort_portSend(JNIEnv* env, jclass clazz, + jint port, jint events) +{ + if (port_send((int)port, (int)events, NULL) == -1) { + JNU_ThrowIOExceptionWithLastError(env, "port_send"); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_SolarisEventPort_portGet(JNIEnv* env, jclass clazz, + jint port, jlong eventAddress) +{ + int res; + port_event_t* ev = (port_event_t*)jlong_to_ptr(eventAddress); + + RESTARTABLE(port_get((int)port, ev, NULL), res); + if (res == -1) { + JNU_ThrowIOExceptionWithLastError(env, "port_get"); + } +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_SolarisEventPort_portGetn(JNIEnv* env, jclass clazz, + jint port, jlong arrayAddress, jint max) +{ + int res; + uint_t n = 1; + port_event_t* list = (port_event_t*)jlong_to_ptr(arrayAddress); + + RESTARTABLE(port_getn((int)port, list, (uint_t)max, &n, NULL), res); + if (res == -1) { + JNU_ThrowIOExceptionWithLastError(env, "port_getn"); + } + return (jint)n; +} diff --git a/src/solaris/native/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.c b/src/solaris/native/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.c new file mode 100644 index 000000000..9c92cc2ad --- /dev/null +++ b/src/solaris/native/sun/nio/ch/UnixAsynchronousServerSocketChannelImpl.c @@ -0,0 +1,47 @@ +/* + * 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. + */ + +#include "sun_nio_ch_UnixAsynchronousServerSocketChannelImpl.h" + +extern void Java_sun_nio_ch_ServerSocketChannelImpl_initIDs(JNIEnv* env, + jclass c); + +extern jint Java_sun_nio_ch_ServerSocketChannelImpl_accept0(JNIEnv* env, + jobject this, jobject ssfdo, jobject newfdo, jobjectArray isaa); + +JNIEXPORT void JNICALL +Java_sun_nio_ch_UnixAsynchronousServerSocketChannelImpl_initIDs(JNIEnv* env, + jclass c) +{ + Java_sun_nio_ch_ServerSocketChannelImpl_initIDs(env, c); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_UnixAsynchronousServerSocketChannelImpl_accept0(JNIEnv* env, + jobject this, jobject ssfdo, jobject newfdo, jobjectArray isaa) +{ + return Java_sun_nio_ch_ServerSocketChannelImpl_accept0(env, this, + ssfdo, newfdo, isaa); +} diff --git a/src/solaris/native/sun/nio/ch/UnixAsynchronousSocketChannelImpl.c b/src/solaris/native/sun/nio/ch/UnixAsynchronousSocketChannelImpl.c new file mode 100644 index 000000000..461d97f40 --- /dev/null +++ b/src/solaris/native/sun/nio/ch/UnixAsynchronousSocketChannelImpl.c @@ -0,0 +1,53 @@ +/* + * 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. + */ + +#include +#include + +#include "jni.h" +#include "jni_util.h" +#include "net_util.h" +#include "jlong.h" +#include "sun_nio_ch_UnixAsynchronousSocketChannelImpl.h" +#include "nio_util.h" +#include "nio.h" + + +JNIEXPORT void JNICALL +Java_sun_nio_ch_UnixAsynchronousSocketChannelImpl_checkConnect(JNIEnv *env, + jobject this, int fd) +{ + int error = 0; + int n = sizeof(error); + int result; + + result = getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &n); + if (result < 0) { + JNU_ThrowIOExceptionWithLastError(env, "getsockopt"); + } else { + if (error) + handleSocketError(env, error); + } +} diff --git a/src/solaris/native/sun/nio/fs/GnomeFileTypeDetector.c b/src/solaris/native/sun/nio/fs/GnomeFileTypeDetector.c new file mode 100644 index 000000000..0a169a94d --- /dev/null +++ b/src/solaris/native/sun/nio/fs/GnomeFileTypeDetector.c @@ -0,0 +1,205 @@ +/* + * 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. + */ + +#include "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "jlong.h" + +#include +#include +#include + +#ifdef __solaris__ +#include +#endif + +#ifdef __linux__ +#include +#endif + +/* Definitions for GIO */ + +#define G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE "standard::content-type" + +typedef void* gpointer; +typedef struct _GFile GFile; +typedef struct _GFileInfo GFileInfo; +typedef struct _GCancellable GCancellable; +typedef struct _GError GError; + +typedef enum { + G_FILE_QUERY_INFO_NONE = 0 +} GFileQueryInfoFlags; + +typedef void (*g_type_init_func)(void); +typedef void (*g_object_unref_func)(gpointer object); +typedef GFile* (*g_file_new_for_path_func)(const char* path); +typedef GFileInfo* (*g_file_query_info_func)(GFile *file, + const char *attributes, GFileQueryInfoFlags flags, + GCancellable *cancellable, GError **error); +typedef char* (*g_file_info_get_content_type_func)(GFileInfo *info); + +static g_type_init_func g_type_init; +static g_object_unref_func g_object_unref; +static g_file_new_for_path_func g_file_new_for_path; +static g_file_query_info_func g_file_query_info; +static g_file_info_get_content_type_func g_file_info_get_content_type; + + +/* Definitions for GNOME VFS */ + +typedef int gboolean; + +typedef gboolean (*gnome_vfs_init_function)(void); +typedef const char* (*gnome_vfs_mime_type_from_name_function) + (const char* filename); + +static gnome_vfs_init_function gnome_vfs_init; +static gnome_vfs_mime_type_from_name_function gnome_vfs_mime_type_from_name; + + +#include "sun_nio_fs_GnomeFileTypeDetector.h" + + +JNIEXPORT jboolean JNICALL +Java_sun_nio_fs_GnomeFileTypeDetector_initializeGio + (JNIEnv* env, jclass this) +{ + void* gio_handle; + + gio_handle = dlopen("libgio-2.0.so", RTLD_LAZY); + if (gio_handle == NULL) { + gio_handle = dlopen("libgio-2.0.so.0", RTLD_LAZY); + if (gio_handle == NULL) { + return JNI_FALSE; + } + } + + g_type_init = (g_type_init_func)dlsym(gio_handle, "g_type_init"); + (*g_type_init)(); + + g_object_unref = (g_object_unref_func)dlsym(gio_handle, "g_object_unref"); + + g_file_new_for_path = + (g_file_new_for_path_func)dlsym(gio_handle, "g_file_new_for_path"); + + g_file_query_info = + (g_file_query_info_func)dlsym(gio_handle, "g_file_query_info"); + + g_file_info_get_content_type = (g_file_info_get_content_type_func) + dlsym(gio_handle, "g_file_info_get_content_type"); + + + if (g_type_init == NULL || + g_object_unref == NULL || + g_file_new_for_path == NULL || + g_file_query_info == NULL || + g_file_info_get_content_type == NULL) + { + dlclose(gio_handle); + return JNI_FALSE; + } + + (*g_type_init)(); + return JNI_TRUE; +} + +JNIEXPORT jbyteArray JNICALL +Java_sun_nio_fs_GnomeFileTypeDetector_probeUsingGio + (JNIEnv* env, jclass this, jlong pathAddress) +{ + char* path = (char*)jlong_to_ptr(pathAddress); + GFile* gfile; + GFileInfo* gfileinfo; + jbyteArray result = NULL; + + gfile = (*g_file_new_for_path)(path); + gfileinfo = (*g_file_query_info)(gfile, G_FILE_ATTRIBUTE_STANDARD_CONTENT_TYPE, + G_FILE_QUERY_INFO_NONE, NULL, NULL); + if (gfileinfo != NULL) { + const char* mime = (*g_file_info_get_content_type)(gfileinfo); + if (mime != NULL) { + jsize len = strlen(mime); + result = (*env)->NewByteArray(env, len); + if (result != NULL) { + (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)mime); + } + } + (*g_object_unref)(gfileinfo); + } + (*g_object_unref)(gfile); + + return result; +} + +JNIEXPORT jboolean JNICALL +Java_sun_nio_fs_GnomeFileTypeDetector_initializeGnomeVfs + (JNIEnv* env, jclass this) +{ + void* vfs_handle; + + vfs_handle = dlopen("libgnomevfs-2.so", RTLD_LAZY); + if (vfs_handle == NULL) { + vfs_handle = dlopen("libgnomevfs-2.so.0", RTLD_LAZY); + } + if (vfs_handle == NULL) { + return JNI_FALSE; + } + + gnome_vfs_init = (gnome_vfs_init_function)dlsym(vfs_handle, "gnome_vfs_init"); + gnome_vfs_mime_type_from_name = (gnome_vfs_mime_type_from_name_function) + dlsym(vfs_handle, "gnome_vfs_mime_type_from_name"); + + if (gnome_vfs_init == NULL || + gnome_vfs_mime_type_from_name == NULL) + { + dlclose(vfs_handle); + return JNI_FALSE; + } + + (*gnome_vfs_init)(); + return JNI_TRUE; +} + +JNIEXPORT jbyteArray JNICALL +Java_sun_nio_fs_GnomeFileTypeDetector_probeUsingGnomeVfs + (JNIEnv* env, jclass this, jlong pathAddress) +{ + char* path = (char*)jlong_to_ptr(pathAddress); + const char* mime = (*gnome_vfs_mime_type_from_name)(path); + + if (mime == NULL) { + return NULL; + } else { + jbyteArray result; + jsize len = strlen(mime); + result = (*env)->NewByteArray(env, len); + if (result != NULL) { + (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)mime); + } + return result; + } +} diff --git a/src/solaris/native/sun/nio/fs/LinuxNativeDispatcher.c b/src/solaris/native/sun/nio/fs/LinuxNativeDispatcher.c new file mode 100644 index 000000000..8e4fa6696 --- /dev/null +++ b/src/solaris/native/sun/nio/fs/LinuxNativeDispatcher.c @@ -0,0 +1,160 @@ +/* + * 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. + */ + +#include "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "jlong.h" + +#include +#include +#include +#include + +#include "sun_nio_fs_LinuxNativeDispatcher.h" + +typedef size_t fgetxattr_func(int fd, const char* name, void* value, size_t size); +typedef int fsetxattr_func(int fd, const char* name, void* value, size_t size, int flags); +typedef int fremovexattr_func(int fd, const char* name); +typedef int flistxattr_func(int fd, char* list, size_t size); + +fgetxattr_func* my_fgetxattr_func = NULL; +fsetxattr_func* my_fsetxattr_func = NULL; +fremovexattr_func* my_fremovexattr_func = NULL; +flistxattr_func* my_flistxattr_func = NULL; + +static void throwUnixException(JNIEnv* env, int errnum) { + jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException", + "(I)V", errnum); + if (x != NULL) { + (*env)->Throw(env, x); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_LinuxNativeDispatcher_init(JNIEnv *env, jclass clazz) +{ + my_fgetxattr_func = (fgetxattr_func*)dlsym(RTLD_DEFAULT, "fgetxattr"); + my_fsetxattr_func = (fsetxattr_func*)dlsym(RTLD_DEFAULT, "fsetxattr"); + my_fremovexattr_func = (fremovexattr_func*)dlsym(RTLD_DEFAULT, "fremovexattr"); + my_flistxattr_func = (flistxattr_func*)dlsym(RTLD_DEFAULT, "flistxattr"); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_LinuxNativeDispatcher_fgetxattr0(JNIEnv* env, jclass clazz, + jint fd, jlong nameAddress, jlong valueAddress, jint valueLen) +{ + size_t res = -1; + const char* name = jlong_to_ptr(nameAddress); + void* value = jlong_to_ptr(valueAddress); + + if (my_fgetxattr_func == NULL) { + errno = ENOTSUP; + } else { + /* EINTR not documented */ + res = (*my_fgetxattr_func)(fd, name, value, valueLen); + } + if (res == (size_t)-1) + throwUnixException(env, errno); + return (jint)res; +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_LinuxNativeDispatcher_fsetxattr0(JNIEnv* env, jclass clazz, + jint fd, jlong nameAddress, jlong valueAddress, jint valueLen) +{ + int res = -1; + const char* name = jlong_to_ptr(nameAddress); + void* value = jlong_to_ptr(valueAddress); + + if (my_fsetxattr_func == NULL) { + errno = ENOTSUP; + } else { + /* EINTR not documented */ + res = (*my_fsetxattr_func)(fd, name, value, valueLen, 0); + } + if (res == -1) + throwUnixException(env, errno); +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_LinuxNativeDispatcher_fremovexattr0(JNIEnv* env, jclass clazz, + jint fd, jlong nameAddress) +{ + int res = -1; + const char* name = jlong_to_ptr(nameAddress); + + if (my_fremovexattr_func == NULL) { + errno = ENOTSUP; + } else { + /* EINTR not documented */ + res = (*my_fremovexattr_func)(fd, name); + } + if (res == -1) + throwUnixException(env, errno); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_LinuxNativeDispatcher_flistxattr(JNIEnv* env, jclass clazz, + jint fd, jlong listAddress, jint size) +{ + size_t res = -1; + char* list = jlong_to_ptr(listAddress); + + if (my_flistxattr_func == NULL) { + errno = ENOTSUP; + } else { + /* EINTR not documented */ + res = (*my_flistxattr_func)(fd, list, (size_t)size); + } + if (res == (size_t)-1) + throwUnixException(env, errno); + return (jint)res; +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_LinuxNativeDispatcher_setmntent0(JNIEnv* env, jclass this, jlong pathAddress, + jlong modeAddress) +{ + FILE* fp = NULL; + const char* path = (const char*)jlong_to_ptr(pathAddress); + const char* mode = (const char*)jlong_to_ptr(modeAddress); + + do { + fp = setmntent(path, mode); + } while (fp == NULL && errno == EINTR); + if (fp == NULL) { + throwUnixException(env, errno); + } + return ptr_to_jlong(fp); +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_LinuxNativeDispatcher_endmntent(JNIEnv* env, jclass this, jlong stream) +{ + FILE* fp = jlong_to_ptr(stream); + /* FIXME - man page doesn't explain how errors are returned */ + endmntent(fp); +} diff --git a/src/solaris/native/sun/nio/fs/LinuxWatchService.c b/src/solaris/native/sun/nio/fs/LinuxWatchService.c new file mode 100644 index 000000000..74a154079 --- /dev/null +++ b/src/solaris/native/sun/nio/fs/LinuxWatchService.c @@ -0,0 +1,195 @@ +/* + * 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. + */ + +#include "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "jlong.h" + +#include +#include +#include +#include +#include + +#include "sun_nio_fs_LinuxWatchService.h" + +/* inotify.h may not be available at build time */ +#ifdef __cplusplus +extern "C" { +#endif +struct inotify_event +{ + int wd; + uint32_t mask; + uint32_t cookie; + uint32_t len; + char name __flexarr; +}; +#ifdef __cplusplus +} +#endif + +typedef int inotify_init_func(void); +typedef int inotify_add_watch_func(int fd, const char* path, uint32_t mask); +typedef int inotify_rm_watch_func(int fd, uint32_t wd); + +inotify_init_func* my_inotify_init_func = NULL; +inotify_add_watch_func* my_inotify_add_watch_func = NULL; +inotify_rm_watch_func* my_inotify_rm_watch_func = NULL; + +static void throwUnixException(JNIEnv* env, int errnum) { + jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException", + "(I)V", errnum); + if (x != NULL) { + (*env)->Throw(env, x); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_LinuxWatchService_init(JNIEnv *env, jclass clazz) +{ + my_inotify_init_func = (inotify_init_func*) + dlsym(RTLD_DEFAULT, "inotify_init"); + my_inotify_add_watch_func = + (inotify_add_watch_func*) dlsym(RTLD_DEFAULT, "inotify_add_watch"); + my_inotify_rm_watch_func = + (inotify_rm_watch_func*) dlsym(RTLD_DEFAULT, "inotify_rm_watch"); + + if ((my_inotify_init_func == NULL) || (my_inotify_add_watch_func == NULL) || + (my_inotify_rm_watch_func == NULL)) { + JNU_ThrowInternalError(env, "unable to get address of inotify functions"); + } +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_LinuxWatchService_eventSize(JNIEnv *env, jclass clazz) +{ + return (jint)sizeof(struct inotify_event); +} + +JNIEXPORT jintArray JNICALL +Java_sun_nio_fs_LinuxWatchService_eventOffsets(JNIEnv *env, jclass clazz) +{ + jintArray result = (*env)->NewIntArray(env, 5); + if (result != NULL) { + jint arr[5]; + arr[0] = (jint)offsetof(struct inotify_event, wd); + arr[1] = (jint)offsetof(struct inotify_event, mask); + arr[2] = (jint)offsetof(struct inotify_event, cookie); + arr[3] = (jint)offsetof(struct inotify_event, len); + arr[4] = (jint)offsetof(struct inotify_event, name); + (*env)->SetIntArrayRegion(env, result, 0, 5, arr); + } + return result; +} + + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_LinuxWatchService_inotifyInit + (JNIEnv* env, jclass clazz) +{ + int ifd = (*my_inotify_init_func)(); + if (ifd == -1) { + throwUnixException(env, errno); + } + return (jint)ifd; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_LinuxWatchService_inotifyAddWatch + (JNIEnv* env, jclass clazz, jint fd, jlong address, jint mask) +{ + int wfd = -1; + const char* path = (const char*)jlong_to_ptr(address); + + wfd = (*my_inotify_add_watch_func)((int)fd, path, mask); + if (wfd == -1) { + throwUnixException(env, errno); + } + return (jint)wfd; +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_LinuxWatchService_inotifyRmWatch + (JNIEnv* env, jclass clazz, jint fd, jint wd) +{ + int err = (*my_inotify_rm_watch_func)((int)fd, (int)wd); + if (err == -1) + throwUnixException(env, errno); +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_LinuxWatchService_configureBlocking + (JNIEnv* env, jclass clazz, jint fd, jboolean blocking) +{ + int flags = fcntl(fd, F_GETFL); + + if ((blocking == JNI_FALSE) && !(flags & O_NONBLOCK)) + fcntl(fd, F_SETFL, flags | O_NONBLOCK); + else if ((blocking == JNI_TRUE) && (flags & O_NONBLOCK)) + fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_LinuxWatchService_socketpair + (JNIEnv* env, jclass clazz, jintArray sv) +{ + int sp[2]; + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sp) == -1) { + throwUnixException(env, errno); + } else { + jint res[2]; + res[0] = (jint)sp[0]; + res[1] = (jint)sp[1]; + (*env)->SetIntArrayRegion(env, sv, 0, 2, &res[0]); + } + +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_LinuxWatchService_poll + (JNIEnv* env, jclass clazz, jint fd1, jint fd2) +{ + struct pollfd ufds[2]; + int n; + + ufds[0].fd = fd1; + ufds[0].events = POLLIN; + ufds[1].fd = fd2; + ufds[1].events = POLLIN; + + n = poll(&ufds[0], 2, -1); + if (n == -1) { + if (errno == EINTR) { + n = 0; + } else { + throwUnixException(env, errno); + } + } + return (jint)n; + + +} diff --git a/src/solaris/native/sun/nio/fs/SolarisNativeDispatcher.c b/src/solaris/native/sun/nio/fs/SolarisNativeDispatcher.c new file mode 100644 index 000000000..a57009c67 --- /dev/null +++ b/src/solaris/native/sun/nio/fs/SolarisNativeDispatcher.c @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#include "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "jlong.h" + +#include +#include +#include + +#include "sun_nio_fs_SolarisNativeDispatcher.h" + +static void throwUnixException(JNIEnv* env, int errnum) { + jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException", + "(I)V", errnum); + if (x != NULL) { + (*env)->Throw(env, x); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_SolarisNativeDispatcher_init(JNIEnv *env, jclass clazz) { +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_SolarisNativeDispatcher_facl(JNIEnv* env, jclass this, jint fd, + jint cmd, jint nentries, jlong address) +{ + void* aclbufp = jlong_to_ptr(address); + int n = -1; + + n = facl((int)fd, (int)cmd, (int)nentries, aclbufp); + if (n == -1) { + throwUnixException(env, errno); + } + return (jint)n; +} diff --git a/src/solaris/native/sun/nio/fs/SolarisWatchService.c b/src/solaris/native/sun/nio/fs/SolarisWatchService.c new file mode 100644 index 000000000..776227f99 --- /dev/null +++ b/src/solaris/native/sun/nio/fs/SolarisWatchService.c @@ -0,0 +1,104 @@ +/* + * 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. + */ + +#include "jni.h" +#include "jni_util.h" +#include "jvm.h" +#include "jlong.h" + +#include +#include +#include +#include // Solaris 10 + +#include "sun_nio_fs_SolarisWatchService.h" + +static void throwUnixException(JNIEnv* env, int errnum) { + jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException", + "(I)V", errnum); + if (x != NULL) { + (*env)->Throw(env, x); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_SolarisWatchService_init(JNIEnv *env, jclass clazz) +{ +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_SolarisWatchService_portCreate + (JNIEnv* env, jclass clazz) +{ + int port = port_create(); + if (port == -1) { + throwUnixException(env, errno); + } + return (jint)port; +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_SolarisWatchService_portAssociate + (JNIEnv* env, jclass clazz, jint port, jint source, jlong objectAddress, jint events) +{ + uintptr_t object = (uintptr_t)jlong_to_ptr(objectAddress); + + if (port_associate((int)port, (int)source, object, (int)events, NULL) == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_SolarisWatchService_portDissociate + (JNIEnv* env, jclass clazz, jint port, jint source, jlong objectAddress) +{ + uintptr_t object = (uintptr_t)jlong_to_ptr(objectAddress); + + if (port_dissociate((int)port, (int)source, object) == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_SolarisWatchService_portSend(JNIEnv* env, jclass clazz, + jint port, jint events) +{ + if (port_send((int)port, (int)events, NULL) == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_SolarisWatchService_portGetn(JNIEnv* env, jclass clazz, + jint port, jlong arrayAddress, jint max) +{ + uint_t n = 1; + port_event_t* list = (port_event_t*)jlong_to_ptr(arrayAddress); + + if (port_getn((int)port, list, (uint_t)max, &n, NULL) == -1) { + throwUnixException(env, errno); + } + return (jint)n; +} diff --git a/src/solaris/native/sun/nio/fs/UnixCopyFile.c b/src/solaris/native/sun/nio/fs/UnixCopyFile.c new file mode 100644 index 000000000..526ccba96 --- /dev/null +++ b/src/solaris/native/sun/nio/fs/UnixCopyFile.c @@ -0,0 +1,85 @@ +/* + * 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. + */ + +#include "jni.h" +#include "jni_util.h" +#include "jlong.h" + +#include +#include + +#include "sun_nio_fs_UnixCopyFile.h" + +#define RESTARTABLE(_cmd, _result) do { \ + do { \ + _result = _cmd; \ + } while((_result == -1) && (errno == EINTR)); \ +} while(0) + +static void throwUnixException(JNIEnv* env, int errnum) { + jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException", + "(I)V", errnum); + if (x != NULL) { + (*env)->Throw(env, x); + } +} + +/** + * Transfer all bytes from src to dst via user-space buffers + */ +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixCopyFile_transfer + (JNIEnv* env, jclass this, jint dst, jint src, jlong cancelAddress) +{ + char buf[8192]; + volatile jint* cancel = (jint*)jlong_to_ptr(cancelAddress); + + for (;;) { + ssize_t n, pos, len; + RESTARTABLE(read((int)src, &buf, sizeof(buf)), n); + if (n <= 0) { + if (n < 0) + throwUnixException(env, errno); + return; + } + if (cancel != NULL && *cancel != 0) { + throwUnixException(env, ECANCELED); + return; + } + pos = 0; + len = n; + do { + char* bufp = buf; + bufp += pos; + RESTARTABLE(write((int)dst, bufp, len), n); + if (n == -1) { + throwUnixException(env, errno); + return; + } + pos += n; + len -= n; + } while (len > 0); + } +} diff --git a/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c b/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c new file mode 100644 index 000000000..13a1a3a21 --- /dev/null +++ b/src/solaris/native/sun/nio/fs/UnixNativeDispatcher.c @@ -0,0 +1,1080 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __solaris__ +#include +#include +#include +#endif + +#ifdef __linux__ +#include +#include +#endif + +#include "jni.h" +#include "jni_util.h" +#include "jlong.h" + +#include "sun_nio_fs_UnixNativeDispatcher.h" + +#define RESTARTABLE(_cmd, _result) do { \ + do { \ + _result = _cmd; \ + } while((_result == -1) && (errno == EINTR)); \ +} while(0) + +static jfieldID attrs_st_mode; +static jfieldID attrs_st_ino; +static jfieldID attrs_st_dev; +static jfieldID attrs_st_rdev; +static jfieldID attrs_st_nlink; +static jfieldID attrs_st_uid; +static jfieldID attrs_st_gid; +static jfieldID attrs_st_size; +static jfieldID attrs_st_atime; +static jfieldID attrs_st_mtime; +static jfieldID attrs_st_ctime; + +static jfieldID attrs_f_frsize; +static jfieldID attrs_f_blocks; +static jfieldID attrs_f_bfree; +static jfieldID attrs_f_bavail; + +static jfieldID entry_name; +static jfieldID entry_dir; +static jfieldID entry_fstype; +static jfieldID entry_options; +static jfieldID entry_dev; + +/** + * System calls that may not be available at build time. + */ +typedef int openat64_func(int, const char *, int, ...); +typedef int fstatat64_func(int, const char *, struct stat64 *, int); +typedef int unlinkat_func(int, const char*, int); +typedef int renameat_func(int, const char*, int, const char*); +typedef int futimesat_func(int, const char *, const struct timeval *); +typedef DIR* fdopendir_func(int); + +static openat64_func* my_openat64_func = NULL; +static fstatat64_func* my_fstatat64_func = NULL; +static unlinkat_func* my_unlinkat_func = NULL; +static renameat_func* my_renameat_func = NULL; +static futimesat_func* my_futimesat_func = NULL; +static fdopendir_func* my_fdopendir_func = NULL; + +/** + * fstatat missing from glibc on Linux. Temporary workaround + * for x86/x64. + */ +#if defined(__linux__) && defined(__i386) +#define FSTATAT64_SYSCALL_AVAILABLE +static int fstatat64_wrapper(int dfd, const char *path, + struct stat64 *statbuf, int flag) +{ + #ifndef __NR_fstatat64 + #define __NR_fstatat64 300 + #endif + return syscall(__NR_fstatat64, dfd, path, statbuf, flag); +} +#endif + +#if defined(__linux__) && defined(__x86_64__) +#define FSTATAT64_SYSCALL_AVAILABLE +static int fstatat64_wrapper(int dfd, const char *path, + struct stat64 *statbuf, int flag) +{ + #ifndef __NR_newfstatat + #define __NR_newfstatat 262 + #endif + return syscall(__NR_newfstatat, dfd, path, statbuf, flag); +} +#endif + +/** + * Call this to throw an internal UnixException when a system/library + * call fails + */ +static void throwUnixException(JNIEnv* env, int errnum) { + jobject x = JNU_NewObjectByName(env, "sun/nio/fs/UnixException", + "(I)V", errnum); + if (x != NULL) { + (*env)->Throw(env, x); + } +} + +/** + * Initialize jfieldIDs + */ +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_initIDs(JNIEnv* env, jclass this) +{ + jclass clazz; + + clazz = (*env)->FindClass(env, "sun/nio/fs/UnixFileAttributes"); + if (clazz == NULL) { + return; + } + attrs_st_mode = (*env)->GetFieldID(env, clazz, "st_mode", "I"); + attrs_st_ino = (*env)->GetFieldID(env, clazz, "st_ino", "J"); + attrs_st_dev = (*env)->GetFieldID(env, clazz, "st_dev", "J"); + attrs_st_rdev = (*env)->GetFieldID(env, clazz, "st_rdev", "J"); + attrs_st_nlink = (*env)->GetFieldID(env, clazz, "st_nlink", "I"); + attrs_st_uid = (*env)->GetFieldID(env, clazz, "st_uid", "I"); + attrs_st_gid = (*env)->GetFieldID(env, clazz, "st_gid", "I"); + attrs_st_size = (*env)->GetFieldID(env, clazz, "st_size", "J"); + attrs_st_atime = (*env)->GetFieldID(env, clazz, "st_atime", "J"); + attrs_st_mtime = (*env)->GetFieldID(env, clazz, "st_mtime", "J"); + attrs_st_ctime = (*env)->GetFieldID(env, clazz, "st_ctime", "J"); + + clazz = (*env)->FindClass(env, "sun/nio/fs/UnixFileStoreAttributes"); + if (clazz == NULL) { + return; + } + attrs_f_frsize = (*env)->GetFieldID(env, clazz, "f_frsize", "J"); + attrs_f_blocks = (*env)->GetFieldID(env, clazz, "f_blocks", "J"); + attrs_f_bfree = (*env)->GetFieldID(env, clazz, "f_bfree", "J"); + attrs_f_bavail = (*env)->GetFieldID(env, clazz, "f_bavail", "J"); + + clazz = (*env)->FindClass(env, "sun/nio/fs/UnixMountEntry"); + if (clazz == NULL) { + return; + } + entry_name = (*env)->GetFieldID(env, clazz, "name", "[B"); + entry_dir = (*env)->GetFieldID(env, clazz, "dir", "[B"); + entry_fstype = (*env)->GetFieldID(env, clazz, "fstype", "[B"); + entry_options = (*env)->GetFieldID(env, clazz, "opts", "[B"); + entry_dev = (*env)->GetFieldID(env, clazz, "dev", "J"); + + /* system calls that might not be available at build time */ + +#if defined(__solaris__) && defined(_LP64) + /* Solaris 64-bit does not have openat64/fstatat64 */ + my_openat64_func = (openat64_func*)dlsym(RTLD_DEFAULT, "openat"); + my_fstatat64_func = (fstatat64_func*)dlsym(RTLD_DEFAULT, "fstatat"); +#else + my_openat64_func = (openat64_func*) dlsym(RTLD_DEFAULT, "openat64"); + my_fstatat64_func = (fstatat64_func*) dlsym(RTLD_DEFAULT, "fstatat64"); +#endif + my_unlinkat_func = (unlinkat_func*) dlsym(RTLD_DEFAULT, "unlinkat"); + my_renameat_func = (renameat_func*) dlsym(RTLD_DEFAULT, "renameat"); + my_futimesat_func = (futimesat_func*) dlsym(RTLD_DEFAULT, "futimesat"); + my_fdopendir_func = (fdopendir_func*) dlsym(RTLD_DEFAULT, "fdopendir"); + +#if defined(FSTATAT64_SYSCALL_AVAILABLE) + /* fstatat64 missing from glibc */ + if (my_fstatat64_func == NULL) + my_fstatat64_func = (fstatat64_func*)&fstatat64_wrapper; +#endif +} + +JNIEXPORT jbyteArray JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_getcwd(JNIEnv* env, jclass this) { + jbyteArray result = NULL; + char buf[PATH_MAX+1]; + + /* EINTR not listed as a possible error */ + char* cwd = getcwd(buf, sizeof(buf)); + if (cwd == NULL) { + throwUnixException(env, errno); + } else { + jsize len = (jsize)strlen(buf); + result = (*env)->NewByteArray(env, len); + if (result != NULL) { + (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)buf); + } + } + return result; +} + +JNIEXPORT jbyteArray +Java_sun_nio_fs_UnixNativeDispatcher_strerror(JNIEnv* env, jclass this, jint error) +{ + char* msg; + jsize len; + jbyteArray bytes; + + msg = strerror((int)error); + len = strlen(msg); + bytes = (*env)->NewByteArray(env, len); + if (bytes != NULL) { + (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)msg); + } + return bytes; +} + +JNIEXPORT jint +Java_sun_nio_fs_UnixNativeDispatcher_dup(JNIEnv* env, jclass this, jint fd) { + + int res = -1; + + RESTARTABLE(dup((int)fd), res); + if (fd == -1) { + throwUnixException(env, errno); + } + return (jint)res; +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_fopen0(JNIEnv* env, jclass this, + jlong pathAddress, jlong modeAddress) +{ + FILE* fp = NULL; + const char* path = (const char*)jlong_to_ptr(pathAddress); + const char* mode = (const char*)jlong_to_ptr(modeAddress); + + do { + fp = fopen(path, mode); + } while (fp == NULL && errno == EINTR); + + if (fp == NULL) { + throwUnixException(env, errno); + } + + return ptr_to_jlong(fp); +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_fclose(JNIEnv* env, jclass this, jlong stream) +{ + int res; + FILE* fp = jlong_to_ptr(stream); + + do { + res = fclose(fp); + } while (res == EOF && errno == EINTR); + if (res == EOF) { + throwUnixException(env, errno); + } +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_open0(JNIEnv* env, jclass this, + jlong pathAddress, jint oflags, jint mode) +{ + jint fd; + const char* path = (const char*)jlong_to_ptr(pathAddress); + + RESTARTABLE(open64(path, (int)oflags, (mode_t)mode), fd); + if (fd == -1) { + throwUnixException(env, errno); + } + return fd; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_openat0(JNIEnv* env, jclass this, jint dfd, + jlong pathAddress, jint oflags, jint mode) +{ + jint fd; + const char* path = (const char*)jlong_to_ptr(pathAddress); + + if (my_openat64_func == NULL) { + JNU_ThrowInternalError(env, "should not reach here"); + return -1; + } + + RESTARTABLE((*my_openat64_func)(dfd, path, (int)oflags, (mode_t)mode), fd); + if (fd == -1) { + throwUnixException(env, errno); + } + return fd; +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_close(JNIEnv* env, jclass this, jint fd) { + int err; + /* TDB - need to decide if EIO and other errors should cause exception */ + RESTARTABLE(close((int)fd), err); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_read(JNIEnv* env, jclass this, jint fd, + jlong address, jint nbytes) +{ + ssize_t n; + void* bufp = jlong_to_ptr(address); + RESTARTABLE(read((int)fd, bufp, (size_t)nbytes), n); + if (n == -1) { + throwUnixException(env, errno); + } + return (jint)n; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_write(JNIEnv* env, jclass this, jint fd, + jlong address, jint nbytes) +{ + ssize_t n; + void* bufp = jlong_to_ptr(address); + RESTARTABLE(write((int)fd, bufp, (size_t)nbytes), n); + if (n == -1) { + throwUnixException(env, errno); + } + return (jint)n; +} + +/** + * Copy stat64 members into sun.nio.fs.UnixFileAttributes + */ +static void prepAttributes(JNIEnv* env, struct stat64* buf, jobject attrs) { + (*env)->SetIntField(env, attrs, attrs_st_mode, (jint)buf->st_mode); + (*env)->SetLongField(env, attrs, attrs_st_ino, (jlong)buf->st_ino); + (*env)->SetLongField(env, attrs, attrs_st_dev, (jlong)buf->st_dev); + (*env)->SetLongField(env, attrs, attrs_st_rdev, (jlong)buf->st_rdev); + (*env)->SetIntField(env, attrs, attrs_st_nlink, (jint)buf->st_nlink); + (*env)->SetIntField(env, attrs, attrs_st_uid, (jint)buf->st_uid); + (*env)->SetIntField(env, attrs, attrs_st_gid, (jint)buf->st_gid); + (*env)->SetLongField(env, attrs, attrs_st_size, (jlong)buf->st_size); + (*env)->SetLongField(env, attrs, attrs_st_atime, (jlong)buf->st_atime * 1000); + (*env)->SetLongField(env, attrs, attrs_st_mtime, (jlong)buf->st_mtime * 1000); + (*env)->SetLongField(env, attrs, attrs_st_ctime, (jlong)buf->st_ctime * 1000); +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_stat0(JNIEnv* env, jclass this, + jlong pathAddress, jobject attrs) +{ + int err; + struct stat64 buf; + const char* path = (const char*)jlong_to_ptr(pathAddress); + + RESTARTABLE(stat64(path, &buf), err); + if (err == -1) { + throwUnixException(env, errno); + } else { + prepAttributes(env, &buf, attrs); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_lstat0(JNIEnv* env, jclass this, + jlong pathAddress, jobject attrs) +{ + int err; + struct stat64 buf; + const char* path = (const char*)jlong_to_ptr(pathAddress); + + RESTARTABLE(lstat64(path, &buf), err); + if (err == -1) { + throwUnixException(env, errno); + } else { + prepAttributes(env, &buf, attrs); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_fstat(JNIEnv* env, jclass this, jint fd, + jobject attrs) +{ + int err; + struct stat64 buf; + + RESTARTABLE(fstat64((int)fd, &buf), err); + if (err == -1) { + throwUnixException(env, errno); + } else { + prepAttributes(env, &buf, attrs); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_fstatat0(JNIEnv* env, jclass this, jint dfd, + jlong pathAddress, jint flag, jobject attrs) +{ + int err; + struct stat64 buf; + const char* path = (const char*)jlong_to_ptr(pathAddress); + + if (my_fstatat64_func == NULL) { + JNU_ThrowInternalError(env, "should not reach here"); + return; + } + RESTARTABLE((*my_fstatat64_func)((int)dfd, path, &buf, (int)flag), err); + if (err == -1) { + throwUnixException(env, errno); + } else { + prepAttributes(env, &buf, attrs); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_chmod0(JNIEnv* env, jclass this, + jlong pathAddress, jint mode) +{ + int err; + const char* path = (const char*)jlong_to_ptr(pathAddress); + + RESTARTABLE(chmod(path, (mode_t)mode), err); + if (err == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_fchmod(JNIEnv* env, jclass this, jint filedes, + jint mode) +{ + int err; + + RESTARTABLE(fchmod((int)filedes, (mode_t)mode), err); + if (err == -1) { + throwUnixException(env, errno); + } +} + + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_chown0(JNIEnv* env, jclass this, + jlong pathAddress, jint uid, jint gid) +{ + int err; + const char* path = (const char*)jlong_to_ptr(pathAddress); + + RESTARTABLE(chown(path, (uid_t)uid, (gid_t)gid), err); + if (err == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_lchown0(JNIEnv* env, jclass this, jlong pathAddress, jint uid, jint gid) +{ + int err; + const char* path = (const char*)jlong_to_ptr(pathAddress); + + RESTARTABLE(lchown(path, (uid_t)uid, (gid_t)gid), err); + if (err == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_fchown(JNIEnv* env, jclass this, jint filedes, jint uid, jint gid) +{ + int err; + + RESTARTABLE(fchown(filedes, (uid_t)uid, (gid_t)gid), err); + if (err == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_utimes0(JNIEnv* env, jclass this, + jlong pathAddress, jlong accessTime, jlong modificationTime) +{ + int err; + struct timeval times[2]; + const char* path = (const char*)jlong_to_ptr(pathAddress); + + times[0].tv_sec = accessTime / 1000; + times[0].tv_usec = (accessTime % 1000) * 1000; + + times[1].tv_sec = modificationTime / 1000; + times[1].tv_usec = (modificationTime % 1000) * 1000; + + RESTARTABLE(utimes(path, ×[0]), err); + if (err == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_futimes(JNIEnv* env, jclass this, jint filedes, + jlong accessTime, jlong modificationTime) +{ + struct timeval times[2]; + int err = 0; + + times[0].tv_sec = accessTime / 1000; + times[0].tv_usec = (accessTime % 1000) * 1000; + + times[1].tv_sec = modificationTime / 1000; + times[1].tv_usec = (modificationTime % 1000) * 1000; + + if (my_futimesat_func != NULL) { + RESTARTABLE((*my_futimesat_func)(filedes, NULL, ×[0]), err); + if (err == -1) { + throwUnixException(env, errno); + } + } +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_opendir0(JNIEnv* env, jclass this, + jlong pathAddress) +{ + DIR* dir; + const char* path = (const char*)jlong_to_ptr(pathAddress); + + /* EINTR not listed as a possible error */ + dir = opendir(path); + if (dir == NULL) { + throwUnixException(env, errno); + } + return ptr_to_jlong(dir); +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_fdopendir(JNIEnv* env, jclass this, int dfd) { + DIR* dir; + + if (my_fdopendir_func == NULL) { + JNU_ThrowInternalError(env, "should not reach here"); + return (jlong)-1; + } + + /* EINTR not listed as a possible error */ + dir = (*my_fdopendir_func)((int)dfd); + if (dir == NULL) { + throwUnixException(env, errno); + } + return ptr_to_jlong(dir); +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_closedir(JNIEnv* env, jclass this, jlong dir) { + int err; + DIR* dirp = jlong_to_ptr(dir); + + RESTARTABLE(closedir(dirp), err); + if (errno == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT jbyteArray JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_readdir(JNIEnv* env, jclass this, jlong value) { + char entry[sizeof(struct dirent64) + PATH_MAX + 1]; + struct dirent64* ptr = (struct dirent64*)&entry; + struct dirent64* result; + int res; + DIR* dirp = jlong_to_ptr(value); + + /* EINTR not listed as a possible error */ + /* TDB: reentrant version probably not required here */ + res = readdir64_r(dirp, ptr, &result); + if (res != 0) { + throwUnixException(env, res); + return NULL; + } else { + if (result == NULL) { + return NULL; + } else { + jsize len = strlen(ptr->d_name); + jbyteArray bytes = (*env)->NewByteArray(env, len); + if (bytes != NULL) { + (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)(ptr->d_name)); + } + return bytes; + } + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_mkdir0(JNIEnv* env, jclass this, + jlong pathAddress, jint mode) +{ + const char* path = (const char*)jlong_to_ptr(pathAddress); + + /* EINTR not listed as a possible error */ + if (mkdir(path, (mode_t)mode) == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_rmdir0(JNIEnv* env, jclass this, + jlong pathAddress) +{ + const char* path = (const char*)jlong_to_ptr(pathAddress); + + /* EINTR not listed as a possible error */ + if (rmdir(path) == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_link0(JNIEnv* env, jclass this, + jlong existingAddress, jlong newAddress) +{ + int err; + const char* existing = (const char*)jlong_to_ptr(existingAddress); + const char* newname = (const char*)jlong_to_ptr(newAddress); + + RESTARTABLE(link(existing, newname), err); + if (err == -1) { + throwUnixException(env, errno); + } +} + + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_unlink0(JNIEnv* env, jclass this, + jlong pathAddress) +{ + const char* path = (const char*)jlong_to_ptr(pathAddress); + + /* EINTR not listed as a possible error */ + if (unlink(path) == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_unlinkat0(JNIEnv* env, jclass this, jint dfd, + jlong pathAddress, jint flags) +{ + const char* path = (const char*)jlong_to_ptr(pathAddress); + + if (my_unlinkat_func == NULL) { + JNU_ThrowInternalError(env, "should not reach here"); + return; + } + + /* EINTR not listed as a possible error */ + if ((*my_unlinkat_func)((int)dfd, path, (int)flags) == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_rename0(JNIEnv* env, jclass this, + jlong fromAddress, jlong toAddress) +{ + const char* from = (const char*)jlong_to_ptr(fromAddress); + const char* to = (const char*)jlong_to_ptr(toAddress); + + /* EINTR not listed as a possible error */ + if (rename(from, to) == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_renameat0(JNIEnv* env, jclass this, + jint fromfd, jlong fromAddress, jint tofd, jlong toAddress) +{ + const char* from = (const char*)jlong_to_ptr(fromAddress); + const char* to = (const char*)jlong_to_ptr(toAddress); + + if (my_renameat_func == NULL) { + JNU_ThrowInternalError(env, "should not reach here"); + return; + } + + /* EINTR not listed as a possible error */ + if ((*my_renameat_func)((int)fromfd, from, (int)tofd, to) == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_symlink0(JNIEnv* env, jclass this, + jlong targetAddress, jlong linkAddress) +{ + const char* target = (const char*)jlong_to_ptr(targetAddress); + const char* link = (const char*)jlong_to_ptr(linkAddress); + + /* EINTR not listed as a possible error */ + if (symlink(target, link) == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT jbyteArray JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_readlink0(JNIEnv* env, jclass this, + jlong pathAddress) +{ + jbyteArray result = NULL; + char target[PATH_MAX+1]; + const char* path = (const char*)jlong_to_ptr(pathAddress); + + /* EINTR not listed as a possible error */ + int n = readlink(path, target, sizeof(target)); + if (n == -1) { + throwUnixException(env, errno); + } else { + jsize len; + if (n == sizeof(target)) { + n--; + } + target[n] = '\0'; + len = (jsize)strlen(target); + result = (*env)->NewByteArray(env, len); + if (result != NULL) { + (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)target); + } + } + return result; +} + +JNIEXPORT jbyteArray JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_realpath0(JNIEnv* env, jclass this, + jlong pathAddress) +{ + jbyteArray result = NULL; + char resolved[PATH_MAX+1]; + const char* path = (const char*)jlong_to_ptr(pathAddress); + + /* EINTR not listed as a possible error */ + if (realpath(path, resolved) == NULL) { + throwUnixException(env, errno); + } else { + jsize len = (jsize)strlen(resolved); + result = (*env)->NewByteArray(env, len); + if (result != NULL) { + (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)resolved); + } + } + return result; +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_access0(JNIEnv* env, jclass this, + jlong pathAddress, jint amode) +{ + int err; + const char* path = (const char*)jlong_to_ptr(pathAddress); + + RESTARTABLE(access(path, (int)amode), err); + if (err == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_statvfs0(JNIEnv* env, jclass this, + jlong pathAddress, jobject attrs) +{ + int err; + struct statvfs64 buf; + const char* path = (const char*)jlong_to_ptr(pathAddress); + + + RESTARTABLE(statvfs64(path, &buf), err); + if (err == -1) { + throwUnixException(env, errno); + } else { + (*env)->SetLongField(env, attrs, attrs_f_frsize, long_to_jlong(buf.f_frsize)); + (*env)->SetLongField(env, attrs, attrs_f_blocks, long_to_jlong(buf.f_blocks)); + (*env)->SetLongField(env, attrs, attrs_f_bfree, long_to_jlong(buf.f_bfree)); + (*env)->SetLongField(env, attrs, attrs_f_bavail, long_to_jlong(buf.f_bavail)); + } +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_pathconf0(JNIEnv* env, jclass this, + jlong pathAddress, jint name) +{ + long err; + const char* path = (const char*)jlong_to_ptr(pathAddress); + + err = pathconf(path, (int)name); + if (err == -1) { + throwUnixException(env, errno); + } + return (jlong)err; +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_fpathconf(JNIEnv* env, jclass this, + jint fd, jint name) +{ + long err; + + err = fpathconf((int)fd, (int)name); + if (err == -1) { + throwUnixException(env, errno); + } + return (jlong)err; +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_mknod0(JNIEnv* env, jclass this, + jlong pathAddress, jint mode, jlong dev) +{ + int err; + const char* path = (const char*)jlong_to_ptr(pathAddress); + + RESTARTABLE(mknod(path, (mode_t)mode, (dev_t)dev), err); + if (err == -1) { + throwUnixException(env, errno); + } +} + +JNIEXPORT jbyteArray JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_getpwuid(JNIEnv* env, jclass this, jint uid) +{ + jbyteArray result = NULL; + int buflen; + + buflen = (int)sysconf(_SC_GETPW_R_SIZE_MAX); + if (buflen == -1) { + throwUnixException(env, errno); + } else { + char* pwbuf = (char*)malloc(buflen); + if (pwbuf == NULL) { + JNU_ThrowOutOfMemoryError(env, "native heap"); + } else { + struct passwd pwent; + struct passwd* p; + int res = 0; + +#ifdef __solaris__ + p = getpwuid_r((uid_t)uid, &pwent, pwbuf, (size_t)buflen); +#else + res = getpwuid_r((uid_t)uid, &pwent, pwbuf, (size_t)buflen, &p); +#endif + + if (res != 0 || p == NULL || p->pw_name == NULL || *(p->pw_name) == '\0') { + throwUnixException(env, errno); + } else { + jsize len = strlen(p->pw_name); + result = (*env)->NewByteArray(env, len); + if (result != NULL) { + (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)(p->pw_name)); + } + } + free(pwbuf); + } + } + return result; +} + + +JNIEXPORT jbyteArray JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_getgrgid(JNIEnv* env, jclass this, jint gid) +{ + jbyteArray result = NULL; + int buflen; + + buflen = (int)sysconf(_SC_GETGR_R_SIZE_MAX); + if (buflen == -1) { + throwUnixException(env, errno); + } else { + char* grbuf = (char*)malloc(buflen); + if (grbuf == NULL) { + JNU_ThrowOutOfMemoryError(env, "native heap"); + } else { + struct group grent; + struct group* g; + int res = 0; + +#ifdef __solaris__ + g = getgrgid_r((gid_t)gid, &grent, grbuf, (size_t)buflen); +#else + res = getgrgid_r((gid_t)gid, &grent, grbuf, (size_t)buflen, &g); +#endif + if (res != 0 || g == NULL || g->gr_name == NULL || *(g->gr_name) == '\0') { + throwUnixException(env, errno); + } else { + jsize len = strlen(g->gr_name); + result = (*env)->NewByteArray(env, len); + if (result != NULL) { + (*env)->SetByteArrayRegion(env, result, 0, len, (jbyte*)(g->gr_name)); + } + } + free(grbuf); + } + } + return result; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_getpwnam0(JNIEnv* env, jclass this, + jlong nameAddress) +{ + jint uid = -1; + int buflen; + char* pwbuf; + struct passwd pwent; + struct passwd* p; + int res = 0; + const char* name = (const char*)jlong_to_ptr(nameAddress); + + buflen = (int)sysconf(_SC_GETPW_R_SIZE_MAX); + if (buflen == -1) { + throwUnixException(env, errno); + return -1; + } + pwbuf = (char*)malloc(buflen); + if (pwbuf == NULL) { + JNU_ThrowOutOfMemoryError(env, "native heap"); + return -1; + } + +#ifdef __solaris__ + p = getpwnam_r(name, &pwent, pwbuf, (size_t)buflen); +#else + res = getpwnam_r(name, &pwent, pwbuf, (size_t)buflen, &p); +#endif + + if (res != 0 || p == NULL || p->pw_name == NULL || *(p->pw_name) == '\0') { + /* not found or error */ + } else { + uid = p->pw_uid; + } + + free(pwbuf); + + return uid; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_getgrnam0(JNIEnv* env, jclass this, + jlong nameAddress) +{ + jint gid = -1; + int buflen; + char* grbuf; + struct group grent; + struct group* g; + int res = 0; + const char* name = (const char*)jlong_to_ptr(nameAddress); + + buflen = (int)sysconf(_SC_GETGR_R_SIZE_MAX); + if (buflen == -1) { + throwUnixException(env, errno); + return -1; + } + grbuf = (char*)malloc(buflen); + if (grbuf == NULL) { + JNU_ThrowOutOfMemoryError(env, "native heap"); + return -1; + } + +#ifdef __solaris__ + g = getgrnam_r(name, &grent, grbuf, (size_t)buflen); +#else + res = getgrnam_r(name, &grent, grbuf, (size_t)buflen, &g); +#endif + + if (res != 0 || g == NULL || g->gr_name == NULL || *(g->gr_name) == '\0') { + /* not found or error */ + } else { + gid = g->gr_gid; + } + free(grbuf); + + return gid; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_UnixNativeDispatcher_getextmntent(JNIEnv* env, jclass this, + jlong value, jobject entry) +{ +#ifdef __solaris__ + struct extmnttab ent; +#else + struct mntent ent; + char buf[1024]; + int buflen = sizeof(buf); + struct mntent* m; +#endif + FILE* fp = jlong_to_ptr(value); + jsize len; + jbyteArray bytes; + char* name; + char* dir; + char* fstype; + char* options; + dev_t dev; + +#ifdef __solaris__ + if (getextmntent(fp, &ent, 0)) + return -1; + name = ent.mnt_special; + dir = ent.mnt_mountp; + fstype = ent.mnt_fstype; + options = ent.mnt_mntopts; + dev = makedev(ent.mnt_major, ent.mnt_minor); + if (dev == NODEV) { + /* possible bug on Solaris 8 and 9 */ + throwUnixException(env, errno); + return -1; + } +#else + m = getmntent_r(fp, &ent, (char*)&buf, buflen); + if (m == NULL) + return -1; + name = m->mnt_fsname; + dir = m->mnt_dir; + fstype = m->mnt_type; + options = m->mnt_opts; + dev = 0; +#endif + + len = strlen(name); + bytes = (*env)->NewByteArray(env, len); + if (bytes == NULL) + return -1; + (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)name); + (*env)->SetObjectField(env, entry, entry_name, bytes); + + len = strlen(dir); + bytes = (*env)->NewByteArray(env, len); + if (bytes == NULL) + return -1; + (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)dir); + (*env)->SetObjectField(env, entry, entry_dir, bytes); + + len = strlen(fstype); + bytes = (*env)->NewByteArray(env, len); + if (bytes == NULL) + return -1; + (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)fstype); + (*env)->SetObjectField(env, entry, entry_fstype, bytes); + + len = strlen(options); + bytes = (*env)->NewByteArray(env, len); + if (bytes == NULL) + return -1; + (*env)->SetByteArrayRegion(env, bytes, 0, len, (jbyte*)options); + (*env)->SetObjectField(env, entry, entry_options, bytes); + + if (dev != 0) + (*env)->SetLongField(env, entry, entry_dev, (jlong)dev); + + return 0; +} diff --git a/src/solaris/native/sun/nio/fs/genSolarisConstants.c b/src/solaris/native/sun/nio/fs/genSolarisConstants.c new file mode 100644 index 000000000..cb9831f9b --- /dev/null +++ b/src/solaris/native/sun/nio/fs/genSolarisConstants.c @@ -0,0 +1,105 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include +#include + +/** + * Generates sun.nio.fs.SolarisConstants + */ + +static void out(char* s) { + printf("%s\n", s); +} + +static void emit(char* name, int value) { + printf(" static final int %s = %d;\n", name, value); +} + +static void emitX(char* name, int value) { + printf(" static final int %s = 0x%x;\n", name, value); +} + +#define DEF(X) emit(#X, X); +#define DEFX(X) emitX(#X, X); + +int main(int argc, const char* argv[]) { + out("// AUTOMATICALLY GENERATED FILE - DO NOT EDIT "); + out("package sun.nio.fs; "); + out("class SolarisConstants { "); + out(" private SolarisConstants() { } "); + + // extended attributes + DEFX(O_XATTR); + DEF(_PC_XATTR_ENABLED); + + // ACL configuration + DEF(_PC_ACL_ENABLED); + DEFX(_ACL_ACE_ENABLED); + + // ACL commands + DEFX(ACE_GETACL); + DEFX(ACE_SETACL); + + // ACL mask/flags/types + DEFX(ACE_ACCESS_ALLOWED_ACE_TYPE); + DEFX(ACE_ACCESS_DENIED_ACE_TYPE); + DEFX(ACE_SYSTEM_AUDIT_ACE_TYPE); + DEFX(ACE_SYSTEM_ALARM_ACE_TYPE); + DEFX(ACE_READ_DATA); + DEFX(ACE_LIST_DIRECTORY); + DEFX(ACE_WRITE_DATA); + DEFX(ACE_ADD_FILE); + DEFX(ACE_APPEND_DATA); + DEFX(ACE_ADD_SUBDIRECTORY); + DEFX(ACE_READ_NAMED_ATTRS); + DEFX(ACE_WRITE_NAMED_ATTRS); + DEFX(ACE_EXECUTE); + DEFX(ACE_DELETE_CHILD); + DEFX(ACE_READ_ATTRIBUTES); + DEFX(ACE_WRITE_ATTRIBUTES); + DEFX(ACE_DELETE); + DEFX(ACE_READ_ACL); + DEFX(ACE_WRITE_ACL); + DEFX(ACE_WRITE_OWNER); + DEFX(ACE_SYNCHRONIZE); + DEFX(ACE_FILE_INHERIT_ACE); + DEFX(ACE_DIRECTORY_INHERIT_ACE); + DEFX(ACE_NO_PROPAGATE_INHERIT_ACE); + DEFX(ACE_INHERIT_ONLY_ACE); + DEFX(ACE_SUCCESSFUL_ACCESS_ACE_FLAG); + DEFX(ACE_FAILED_ACCESS_ACE_FLAG); + DEFX(ACE_IDENTIFIER_GROUP); + DEFX(ACE_OWNER); + DEFX(ACE_GROUP); + DEFX(ACE_EVERYONE); + + out("} "); + return 0; +} diff --git a/src/solaris/native/sun/nio/fs/genUnixConstants.c b/src/solaris/native/sun/nio/fs/genUnixConstants.c new file mode 100644 index 000000000..c01f641a6 --- /dev/null +++ b/src/solaris/native/sun/nio/fs/genUnixConstants.c @@ -0,0 +1,125 @@ +/* + * 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. + */ + +#include +#include +#include +#include +#include + +/** + * Generates sun.nio.fs.UnixConstants + */ + +static void out(char* s) { + printf("%s\n", s); +} + +static void emit(char* name, int value) { + printf(" static final int %s = %d;\n", name, value); +} + +static void emitX(char* name, int value) { + printf(" static final int %s = 0x%x;\n", name, value); +} + +#define DEF(X) emit(#X, X); +#define DEFX(X) emitX(#X, X); + +int main(int argc, const char* argv[]) { + out("// AUTOMATICALLY GENERATED FILE - DO NOT EDIT "); + out("package sun.nio.fs; "); + out("class UnixConstants { "); + out(" private UnixConstants() { } "); + + // open flags + DEF(O_RDONLY); + DEF(O_WRONLY); + DEF(O_RDWR); + DEFX(O_APPEND); + DEFX(O_CREAT); + DEFX(O_EXCL); + DEFX(O_TRUNC); + DEFX(O_SYNC); + DEFX(O_DSYNC); + DEFX(O_NOFOLLOW); + + // flags used with openat/unlinkat/etc. +#ifdef __solaris__ + DEFX(AT_SYMLINK_NOFOLLOW); + DEFX(AT_REMOVEDIR); +#endif +#ifdef __linux__ + emitX("AT_SYMLINK_NOFOLLOW", 0x100); // since 2.6.16 + emitX("AT_REMOVEDIR", 0x200); +#endif + + // mode masks + emitX("S_IAMB", + (S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IWOTH|S_IXOTH)); + DEF(S_IRUSR); + DEF(S_IWUSR); + DEF(S_IXUSR); + DEF(S_IRGRP); + DEF(S_IWGRP); + DEF(S_IXGRP); + DEF(S_IROTH); + DEF(S_IWOTH); + DEF(S_IXOTH); + DEFX(S_IFMT); + DEFX(S_IFREG); + DEFX(S_IFDIR); + DEFX(S_IFLNK); + DEFX(S_IFCHR); + DEFX(S_IFBLK); + DEFX(S_IFIFO); + + // access modes + DEF(R_OK); + DEF(W_OK); + DEF(X_OK); + DEF(F_OK); + + // errors + DEF(ENOENT); + DEF(EACCES); + DEF(EEXIST); + DEF(ENOTDIR); + DEF(EINVAL); + DEF(EXDEV); + DEF(EISDIR); + DEF(ENOTEMPTY); + DEF(ENOSPC); + DEF(EAGAIN); + DEF(ENOSYS); + DEF(ELOOP); + DEF(EROFS); + DEF(ENODATA); + DEF(ERANGE); + + out("} "); + + return 0; +} diff --git a/src/windows/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java b/src/windows/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java new file mode 100644 index 000000000..6187aa318 --- /dev/null +++ b/src/windows/classes/sun/nio/ch/DefaultAsynchronousChannelProvider.java @@ -0,0 +1,43 @@ +/* + * 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.ch; + +import java.nio.channels.spi.AsynchronousChannelProvider; + +/** + * Creates this platform's default asynchronous channel provider + */ + +public class DefaultAsynchronousChannelProvider { + private DefaultAsynchronousChannelProvider() { } + + /** + * Returns the default AsynchronousChannelProvider. + */ + public static AsynchronousChannelProvider create() { + return new WindowsAsynchronousChannelProvider(); + } +} diff --git a/src/windows/classes/sun/nio/ch/FileDispatcher.java b/src/windows/classes/sun/nio/ch/FileDispatcherImpl.java similarity index 71% rename from src/windows/classes/sun/nio/ch/FileDispatcher.java rename to src/windows/classes/sun/nio/ch/FileDispatcherImpl.java index 692844966..30390f7a6 100644 --- a/src/windows/classes/sun/nio/ch/FileDispatcher.java +++ b/src/windows/classes/sun/nio/ch/FileDispatcherImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-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 @@ -27,13 +27,7 @@ package sun.nio.ch; import java.io.*; - -/** - * Allows different platforms to call different native methods - * for read and write operations. - */ - -class FileDispatcher extends NativeDispatcher +class FileDispatcherImpl extends FileDispatcher { static { @@ -74,6 +68,28 @@ class FileDispatcher extends NativeDispatcher return writev0(fd, address, len); } + int force(FileDescriptor fd, boolean metaData) throws IOException { + return force0(fd, metaData); + } + + int truncate(FileDescriptor fd, long size) throws IOException { + return truncate0(fd, size); + } + + long size(FileDescriptor fd) throws IOException { + return size0(fd); + } + + int lock(FileDescriptor fd, boolean blocking, long pos, long size, + boolean shared) throws IOException + { + return lock0(fd, blocking, pos, size, shared); + } + + void release(FileDescriptor fd, long pos, long size) throws IOException { + release0(fd, pos, size); + } + void close(FileDescriptor fd) throws IOException { close0(fd); } @@ -98,6 +114,20 @@ class FileDispatcher extends NativeDispatcher static native long writev0(FileDescriptor fd, long address, int len) throws IOException; + static native int force0(FileDescriptor fd, boolean metaData) + throws IOException; + + static native int truncate0(FileDescriptor fd, long size) + throws IOException; + + static native long size0(FileDescriptor fd) throws IOException; + + static native int lock0(FileDescriptor fd, boolean blocking, long pos, + long size, boolean shared) throws IOException; + + static native void release0(FileDescriptor fd, long pos, long size) + throws IOException; + static native void close0(FileDescriptor fd) throws IOException; static native void closeByHandle(long fd) throws IOException; diff --git a/src/windows/classes/sun/nio/ch/Iocp.java b/src/windows/classes/sun/nio/ch/Iocp.java new file mode 100644 index 000000000..b4c2cdef6 --- /dev/null +++ b/src/windows/classes/sun/nio/ch/Iocp.java @@ -0,0 +1,437 @@ +/* + * 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.ch; + +import java.nio.channels.*; +import java.nio.channels.spi.AsynchronousChannelProvider; +import java.io.Closeable; +import java.io.IOException; +import java.io.FileDescriptor; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import sun.misc.Unsafe; + +/** + * Windows implementation of AsynchronousChannelGroup encapsulating an I/O + * completion port. + */ + +class Iocp extends AsynchronousChannelGroupImpl { + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final long INVALID_HANDLE_VALUE = -1L; + + // maps completion key to channel + private final ReadWriteLock keyToChannelLock = new ReentrantReadWriteLock(); + private final Map keyToChannel = + new HashMap(); + private int nextCompletionKey; + + // handle to completion port + private final long port; + + // true if port has been closed + private boolean closed; + + // the set of "stale" OVERLAPPED structures. These OVERLAPPED structures + // relate to I/O operations where the completion notification was not + // received in a timely manner after the channel is closed. + private final Set staleIoSet = new HashSet(); + + Iocp(AsynchronousChannelProvider provider, ThreadPool pool) + throws IOException + { + super(provider, pool); + this.port = + createIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, fixedThreadCount()); + this.nextCompletionKey = 1; + } + + Iocp start() { + startThreads(new EventHandlerTask()); + return this; + } + + /* + * Channels implements this interface support overlapped I/O and can be + * associated with a completion port. + */ + static interface OverlappedChannel extends Closeable { + /** + * Returns a reference to the pending I/O result. + */ + PendingFuture getByOverlapped(long overlapped); + } + + // release all resources + void implClose() { + synchronized (this) { + if (closed) + return; + closed = true; + } + close0(port); + synchronized (staleIoSet) { + for (Long ov: staleIoSet) { + unsafe.freeMemory(ov); + } + staleIoSet.clear(); + } + } + + @Override + boolean isEmpty() { + keyToChannelLock.writeLock().lock(); + try { + return keyToChannel.isEmpty(); + } finally { + keyToChannelLock.writeLock().unlock(); + } + } + + @Override + final Object attachForeignChannel(final Channel channel, FileDescriptor fdObj) + throws IOException + { + int key = associate(new OverlappedChannel() { + public PendingFuture getByOverlapped(long overlapped) { + return null; + } + public void close() throws IOException { + channel.close(); + } + }, 0L); + return Integer.valueOf(key); + } + + @Override + final void detachForeignChannel(Object key) { + disassociate((Integer)key); + } + + @Override + void closeAllChannels() { + /** + * On Windows the close operation will close the socket/file handle + * and then wait until all outstanding I/O operations have aborted. + * This is necessary as each channel's cache of OVERLAPPED structures + * can only be freed once all I/O operations have completed. As I/O + * completion requires a lookup of the keyToChannel then we must close + * the channels when not holding the write lock. + */ + final int MAX_BATCH_SIZE = 32; + OverlappedChannel channels[] = new OverlappedChannel[MAX_BATCH_SIZE]; + int count; + do { + // grab a batch of up to 32 channels + keyToChannelLock.writeLock().lock(); + count = 0; + try { + for (Integer key: keyToChannel.keySet()) { + channels[count++] = keyToChannel.get(key); + if (count >= MAX_BATCH_SIZE) + break; + } + } finally { + keyToChannelLock.writeLock().unlock(); + } + + // close them + for (int i=0; i 0); + } + + private void wakeup() { + try { + postQueuedCompletionStatus(port, 0); + } catch (IOException e) { + // should not happen + throw new AssertionError(e); + } + } + + @Override + void executeOnHandlerTask(Runnable task) { + synchronized (this) { + if (closed) + throw new RejectedExecutionException(); + offerTask(task); + wakeup(); + } + + } + + @Override + void shutdownHandlerTasks() { + // shutdown all handler threads + int nThreads = threadCount(); + while (nThreads-- > 0) { + wakeup(); + } + } + + /** + * Associate the given handle with this group + */ + int associate(OverlappedChannel ch, long handle) throws IOException { + keyToChannelLock.writeLock().lock(); + + // generate a completion key (if not shutdown) + int key; + try { + if (isShutdown()) + throw new ShutdownChannelGroupException(); + + // generate unique key + do { + key = nextCompletionKey++; + } while ((key == 0) || keyToChannel.containsKey(key)); + + // associate with I/O completion port + if (handle != 0L) + createIoCompletionPort(handle, port, key, 0); + + // setup mapping + keyToChannel.put(key, ch); + } finally { + keyToChannelLock.writeLock().unlock(); + } + return key; + } + + /** + * Disassociate channel from the group. + */ + void disassociate(int key) { + boolean checkForShutdown = false; + + keyToChannelLock.writeLock().lock(); + try { + keyToChannel.remove(key); + + // last key to be removed so check if group is shutdown + if (keyToChannel.isEmpty()) + checkForShutdown = true; + + } finally { + keyToChannelLock.writeLock().unlock(); + } + + // continue shutdown + if (checkForShutdown && isShutdown()) { + try { + shutdownNow(); + } catch (IOException ignore) { } + } + } + + /** + * Invoked when a channel associated with this port is closed before + * notifications for all outstanding I/O operations have been received. + */ + void makeStale(Long overlapped) { + synchronized (staleIoSet) { + staleIoSet.add(overlapped); + } + } + + /** + * Checks if the given OVERLAPPED is stale and if so, releases it. + */ + private void checkIfStale(long ov) { + synchronized (staleIoSet) { + boolean removed = staleIoSet.remove(ov); + if (removed) { + unsafe.freeMemory(ov); + } + } + } + + /** + * The handler for consuming the result of an asynchronous I/O operation. + */ + static interface ResultHandler { + /** + * Invoked if the I/O operation completes successfully. + */ + public void completed(int bytesTransferred); + + /** + * Invoked if the I/O operation fails. + */ + public void failed(int error, IOException ioe); + } + + // Creates IOException for the given I/O error. + private static IOException translateErrorToIOException(int error) { + String msg = getErrorMessage(error); + if (msg == null) + msg = "Unknown error: 0x0" + Integer.toHexString(error); + return new IOException(msg); + } + + /** + * Long-running task servicing system-wide or per-file completion port + */ + private class EventHandlerTask implements Runnable { + public void run() { + Invoker.GroupAndInvokeCount myGroupAndInvokeCount = + Invoker.getGroupAndInvokeCount(); + CompletionStatus ioResult = new CompletionStatus(); + boolean replaceMe = false; + + try { + for (;;) { + // reset invoke count + if (myGroupAndInvokeCount != null) + myGroupAndInvokeCount.resetInvokeCount(); + + // wait for I/O completion event + // A error here is fatal (thread will not be replaced) + replaceMe = false; + try { + getQueuedCompletionStatus(port, ioResult); + } catch (IOException x) { + // should not happen + x.printStackTrace(); + return; + } + + // handle wakeup to execute task or shutdown + if (ioResult.completionKey() == 0 && + ioResult.overlapped() == 0L) + { + Runnable task = pollTask(); + if (task == null) { + // shutdown request + return; + } + + // run task + // (if error/exception then replace thread) + replaceMe = true; + task.run(); + continue; + } + + // map key to channel + OverlappedChannel ch = null; + keyToChannelLock.readLock().lock(); + try { + ch = keyToChannel.get(ioResult.completionKey()); + if (ch == null) { + checkIfStale(ioResult.overlapped()); + continue; + } + } finally { + keyToChannelLock.readLock().unlock(); + } + + // lookup I/O request + PendingFuture result = ch.getByOverlapped(ioResult.overlapped()); + if (result == null) { + // we get here if the OVERLAPPED structure is associated + // with an I/O operation on a channel that was closed + // but the I/O operation event wasn't read in a timely + // manner. Alternatively, it may be related to a + // tryLock operation as the OVERLAPPED structures for + // these operations are not in the I/O cache. + checkIfStale(ioResult.overlapped()); + continue; + } + + // synchronize on result in case I/O completed immediately + // and was handled by initiator + synchronized (result) { + if (result.isDone()) { + continue; + } + // not handled by initiator + } + + // invoke I/O result handler + int error = ioResult.error(); + ResultHandler rh = (ResultHandler)result.getContext(); + replaceMe = true; // (if error/exception then replace thread) + if (error == 0) { + rh.completed(ioResult.bytesTransferred()); + } else { + rh.failed(error, translateErrorToIOException(error)); + } + } + } finally { + // last thread to exit when shutdown releases resources + int remaining = threadExit(this, replaceMe); + if (remaining == 0 && isShutdown()) { + implClose(); + } + } + } + } + + /** + * Container for data returned by GetQueuedCompletionStatus + */ + private static class CompletionStatus { + private int error; + private int bytesTransferred; + private int completionKey; + private long overlapped; + + private CompletionStatus() { } + int error() { return error; } + int bytesTransferred() { return bytesTransferred; } + int completionKey() { return completionKey; } + long overlapped() { return overlapped; } + } + + // -- native methods -- + + private static native void initIDs(); + + private static native long createIoCompletionPort(long handle, + long existingPort, int completionKey, int concurrency) throws IOException; + + private static native void close0(long handle); + + private static native void getQueuedCompletionStatus(long completionPort, + CompletionStatus status) throws IOException; + + private static native void postQueuedCompletionStatus(long completionPort, + int completionKey) throws IOException; + + private static native String getErrorMessage(int error); + + static { + Util.load(); + initIDs(); + } +} diff --git a/src/windows/classes/sun/nio/ch/PendingIoCache.java b/src/windows/classes/sun/nio/ch/PendingIoCache.java new file mode 100644 index 000000000..2e3d50385 --- /dev/null +++ b/src/windows/classes/sun/nio/ch/PendingIoCache.java @@ -0,0 +1,161 @@ +/* + * 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.ch; + +import java.nio.channels.*; +import java.util.*; +import sun.misc.Unsafe; + +/** + * Maintains a mapping of pending I/O requests (identified by the address of + * an OVERLAPPED structure) to Futures. + */ + +class PendingIoCache { + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static final int addressSize = unsafe.addressSize(); + + private static int dependsArch(int value32, int value64) { + return (addressSize == 4) ? value32 : value64; + } + + /* + * typedef struct _OVERLAPPED { + * DWORD Internal; + * DWORD InternalHigh; + * DWORD Offset; + * DWORD OffsetHigh; + * HANDLE hEvent; + * } OVERLAPPED; + */ + private static final int SIZEOF_OVERLAPPED = dependsArch(20, 32); + + // set to true when closed + private boolean closed; + + // set to true when thread is waiting for all I/O operations to complete + private boolean closePending; + + // maps OVERLAPPED to PendingFuture + private final Map pendingIoMap = + new HashMap(); + + // per-channel cache of OVERLAPPED structures + private long[] overlappedCache = new long[4]; + private int overlappedCacheCount = 0; + + PendingIoCache() { + } + + long add(PendingFuture result) { + synchronized (this) { + if (closed) + throw new AssertionError("Should not get here"); + long ov; + if (overlappedCacheCount > 0) { + ov = overlappedCache[--overlappedCacheCount]; + } else { + ov = unsafe.allocateMemory(SIZEOF_OVERLAPPED); + } + pendingIoMap.put(ov, result); + return ov; + } + } + + @SuppressWarnings("unchecked") + PendingFuture remove(long overlapped) { + synchronized (this) { + PendingFuture res = pendingIoMap.remove(overlapped); + if (res != null) { + if (overlappedCacheCount < overlappedCache.length) { + overlappedCache[overlappedCacheCount++] = overlapped; + } else { + // cache full or channel closing + unsafe.freeMemory(overlapped); + } + // notify closing thread. + if (closePending) { + this.notifyAll(); + } + } + return res; + } + } + + void close() { + synchronized (this) { + if (closed) + return; + + // handle the case that where there are I/O operations that have + // not completed. + if (!pendingIoMap.isEmpty()) + clearPendingIoMap(); + + // release memory for any cached OVERLAPPED structures + while (overlappedCacheCount > 0) { + unsafe.freeMemory( overlappedCache[--overlappedCacheCount] ); + } + + // done + closed = true; + } + } + + private void clearPendingIoMap() { + assert Thread.holdsLock(this); + + // wait up to 50ms for the I/O operations to complete + closePending = true; + try { + this.wait(50); + } catch (InterruptedException x) { } + closePending = false; + if (pendingIoMap.isEmpty()) + return; + + // cause all pending I/O operations to fail + // simulate the failure of all pending I/O operations. + for (Long ov: pendingIoMap.keySet()) { + PendingFuture result = pendingIoMap.get(ov); + assert !result.isDone(); + + // make I/O port aware of the stale OVERLAPPED structure + Iocp iocp = (Iocp)((Groupable)result.channel()).group(); + iocp.makeStale(ov); + + // execute a task that invokes the result handler's failed method + final Iocp.ResultHandler rh = (Iocp.ResultHandler)result.getContext(); + Runnable task = new Runnable() { + public void run() { + rh.failed(-1, new AsynchronousCloseException()); + } + }; + iocp.executeOnPooledThread(task); + } + pendingIoMap.clear(); + } +} diff --git a/src/windows/classes/sun/nio/ch/WindowsAsynchronousChannelProvider.java b/src/windows/classes/sun/nio/ch/WindowsAsynchronousChannelProvider.java new file mode 100644 index 000000000..435b5297b --- /dev/null +++ b/src/windows/classes/sun/nio/ch/WindowsAsynchronousChannelProvider.java @@ -0,0 +1,101 @@ +/* + * 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.ch; + +import java.nio.channels.*; +import java.nio.channels.spi.AsynchronousChannelProvider; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; +import java.net.ProtocolFamily; +import java.io.IOException; + +public class WindowsAsynchronousChannelProvider + extends AsynchronousChannelProvider +{ + private static volatile Iocp defaultIocp; + + public WindowsAsynchronousChannelProvider() { + // nothing to do + } + + private Iocp defaultIocp() throws IOException { + if (defaultIocp == null) { + synchronized (WindowsAsynchronousChannelProvider.class) { + if (defaultIocp == null) { + // default thread pool may be shared with AsynchronousFileChannels + defaultIocp = new Iocp(this, ThreadPool.getDefault()).start(); + } + } + } + return defaultIocp; + } + + @Override + public AsynchronousChannelGroup openAsynchronousChannelGroup(int nThreads, ThreadFactory factory) + throws IOException + { + return new Iocp(this, ThreadPool.create(nThreads, factory)).start(); + } + + @Override + public AsynchronousChannelGroup openAsynchronousChannelGroup(ExecutorService executor, int initialSize) + throws IOException + { + return new Iocp(this, ThreadPool.wrap(executor, initialSize)).start(); + } + + private Iocp toIocp(AsynchronousChannelGroup group) throws IOException { + if (group == null) { + return defaultIocp(); + } else { + if (!(group instanceof Iocp)) + throw new IllegalChannelGroupException(); + return (Iocp)group; + } + } + + @Override + public AsynchronousServerSocketChannel openAsynchronousServerSocketChannel(AsynchronousChannelGroup group) + throws IOException + { + return new WindowsAsynchronousServerSocketChannelImpl(toIocp(group)); + } + + @Override + public AsynchronousSocketChannel openAsynchronousSocketChannel(AsynchronousChannelGroup group) + throws IOException + { + return new WindowsAsynchronousSocketChannelImpl(toIocp(group)); + } + + @Override + public AsynchronousDatagramChannel openAsynchronousDatagramChannel(ProtocolFamily family, + AsynchronousChannelGroup group) + throws IOException + { + return new SimpleAsynchronousDatagramChannelImpl(family, toIocp(group)); + } +} diff --git a/src/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java b/src/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java new file mode 100644 index 000000000..ef668648d --- /dev/null +++ b/src/windows/classes/sun/nio/ch/WindowsAsynchronousFileChannelImpl.java @@ -0,0 +1,741 @@ +/* + * 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.ch; + +import java.nio.channels.*; +import java.util.concurrent.*; +import java.nio.ByteBuffer; +import java.nio.BufferOverflowException; +import java.io.IOException; +import java.io.FileDescriptor; +import sun.misc.SharedSecrets; +import sun.misc.JavaIOFileDescriptorAccess; + +/** + * Windows implementation of AsynchronousFileChannel using overlapped I/O. + */ + +public class WindowsAsynchronousFileChannelImpl + extends AsynchronousFileChannelImpl + implements Iocp.OverlappedChannel, Groupable +{ + private static final JavaIOFileDescriptorAccess fdAccess = + SharedSecrets.getJavaIOFileDescriptorAccess(); + + // error when EOF is detected asynchronously. + private static final int ERROR_HANDLE_EOF = 38; + + // Lazy initialization of default I/O completion port + private static class DefaultIocpHolder { + static final Iocp defaultIocp = defaultIocp(); + private static Iocp defaultIocp() { + try { + return new Iocp(null, ThreadPool.createDefault()).start(); + } catch (IOException ioe) { + InternalError e = new InternalError(); + e.initCause(ioe); + throw e; + } + } + } + + // Used for force/truncate/size methods + private static final FileDispatcher nd = new FileDispatcherImpl(); + + // The handle is extracted for use in native methods invoked from this class. + private final long handle; + + // The key that identifies the channel's association with the I/O port + private final int completionKey; + + // I/O completion port (group) + private final Iocp iocp; + + private final boolean isDefaultIocp; + + // Caches OVERLAPPED structure for each outstanding I/O operation + private final PendingIoCache ioCache; + + + private WindowsAsynchronousFileChannelImpl(FileDescriptor fdObj, + boolean reading, + boolean writing, + Iocp iocp, + boolean isDefaultIocp) + throws IOException + { + super(fdObj, reading, writing, iocp.executor()); + this.handle = fdAccess.getHandle(fdObj); + this.iocp = iocp; + this.isDefaultIocp = isDefaultIocp; + this.ioCache = new PendingIoCache(); + this.completionKey = iocp.associate(this, handle); + } + + public static AsynchronousFileChannel open(FileDescriptor fdo, + boolean reading, + boolean writing, + ThreadPool pool) + throws IOException + { + Iocp iocp; + boolean isDefaultIocp; + if (pool == null) { + iocp = DefaultIocpHolder.defaultIocp; + isDefaultIocp = true; + } else { + iocp = new Iocp(null, pool).start(); + isDefaultIocp = false; + } + try { + return new + WindowsAsynchronousFileChannelImpl(fdo, reading, writing, iocp, isDefaultIocp); + } catch (IOException x) { + // error binding to port so need to close it (if created for this channel) + if (!isDefaultIocp) + iocp.implClose(); + throw x; + } + } + + @Override + public PendingFuture getByOverlapped(long overlapped) { + return ioCache.remove(overlapped); + } + + @Override + public void close() throws IOException { + closeLock.writeLock().lock(); + try { + if (closed) + return; // already closed + closed = true; + } finally { + closeLock.writeLock().unlock(); + } + + // invalidate all locks held for this channel + invalidateAllLocks(); + + // close the file + close0(handle); + + // waits until all I/O operations have completed + ioCache.close(); + + // disassociate from port and shutdown thread pool if not default + iocp.disassociate(completionKey); + if (!isDefaultIocp) + iocp.shutdown(); + } + + @Override + public AsynchronousChannelGroupImpl group() { + return iocp; + } + + /** + * Translates Throwable to IOException + */ + private static IOException toIOException(Throwable x) { + if (x instanceof IOException) { + if (x instanceof ClosedChannelException) + x = new AsynchronousCloseException(); + return (IOException)x; + } + return new IOException(x); + } + + @Override + public long size() throws IOException { + try { + begin(); + return nd.size(fdObj); + } finally { + end(); + } + } + + @Override + public AsynchronousFileChannel truncate(long size) throws IOException { + if (size < 0) + throw new IllegalArgumentException("Negative size"); + if (!writing) + throw new NonWritableChannelException(); + try { + begin(); + if (size > nd.size(fdObj)) + return this; + nd.truncate(fdObj, size); + } finally { + end(); + } + return this; + } + + @Override + public void force(boolean metaData) throws IOException { + try { + begin(); + nd.force(fdObj, metaData); + } finally { + end(); + } + } + + // -- file locking -- + + /** + * Task that initiates locking operation and handles completion result. + */ + private class LockTask implements Runnable, Iocp.ResultHandler { + private final long position; + private final FileLockImpl fli; + private final PendingFuture result; + + LockTask(long position, + FileLockImpl fli, + PendingFuture result) + { + this.position = position; + this.fli = fli; + this.result = result; + } + + @Override + public void run() { + long overlapped = 0L; + try { + begin(); + + // allocate OVERLAPPED structure + overlapped = ioCache.add(result); + + // synchronize on result to avoid race with handler thread + // when lock is acquired immediately. + synchronized (result) { + int n = lockFile(handle, position, fli.size(), fli.isShared(), + overlapped); + if (n == IOStatus.UNAVAILABLE) { + // I/O is pending + return; + } + // acquired lock immediately + result.setResult(fli); + } + + } catch (Throwable x) { + // lock failed or channel closed + removeFromFileLockTable(fli); + if (overlapped != 0L) + ioCache.remove(overlapped); + result.setFailure(toIOException(x)); + } finally { + end(); + } + + // invoke completion handler + Invoker.invoke(result.handler(), result); + } + + @Override + public void completed(int bytesTransferred) { + // release waiters and invoke completion handler + result.setResult(fli); + Invoker.invoke(result.handler(), result); + } + + @Override + public void failed(int error, IOException x) { + // lock not acquired so remove from lock table + removeFromFileLockTable(fli); + + // release waiters + if (isOpen()) { + result.setFailure(x); + } else { + result.setFailure(new AsynchronousCloseException()); + } + Invoker.invoke(result.handler(), result); + } + } + + @Override + public Future lock(long position, + long size, + boolean shared, + A attachment, + CompletionHandler handler) + { + if (shared && !reading) + throw new NonReadableChannelException(); + if (!shared && !writing) + throw new NonWritableChannelException(); + + // add to lock table + FileLockImpl fli = addToFileLockTable(position, size, shared); + if (fli == null) { + CompletedFuture result = CompletedFuture + .withFailure(this, new ClosedChannelException(), attachment); + Invoker.invoke(handler, result); + return result; + } + + // create Future and task that will be invoked to acquire lock + PendingFuture result = + new PendingFuture(this, handler, attachment); + LockTask lockTask = new LockTask(position, fli, result); + result.setContext(lockTask); + + // initiate I/O (can only be done from thread in thread pool) + try { + Invoker.invokeOnThreadInThreadPool(this, lockTask); + } catch (ShutdownChannelGroupException e) { + // rollback + removeFromFileLockTable(fli); + throw e; + } + return result; + } + + static final int NO_LOCK = -1; // Failed to lock + static final int LOCKED = 0; // Obtained requested lock + + @Override + public FileLock tryLock(long position, long size, boolean shared) + throws IOException + { + if (shared && !reading) + throw new NonReadableChannelException(); + if (!shared && !writing) + throw new NonWritableChannelException(); + + // add to lock table + final FileLockImpl fli = addToFileLockTable(position, size, shared); + if (fli == null) + throw new ClosedChannelException(); + + boolean gotLock = false; + try { + begin(); + // try to acquire the lock + int res = nd.lock(fdObj, false, position, size, shared); + if (res == NO_LOCK) + return null; + gotLock = true; + return fli; + } finally { + if (!gotLock) + removeFromFileLockTable(fli); + end(); + } + } + + // invoke by FileFileImpl to release lock + @Override + void release(FileLockImpl fli) throws IOException { + try { + begin(); + nd.release(fdObj, fli.position(), fli.size()); + removeFromFileLockTable(fli); + } finally { + end(); + } + } + + /** + * Task that initiates read operation and handles completion result. + */ + private class ReadTask implements Runnable, Iocp.ResultHandler { + private final ByteBuffer dst; + private final int pos, rem; // buffer position/remaining + private final long position; // file position + private final PendingFuture result; + + // set to dst if direct; otherwise set to substituted direct buffer + private volatile ByteBuffer buf; + + ReadTask(ByteBuffer dst, + int pos, + int rem, + long position, + PendingFuture result) + { + this.dst = dst; + this.pos = pos; + this.rem = rem; + this.position = position; + this.result = result; + } + + void releaseBufferIfSubstituted() { + if (buf != dst) + Util.releaseTemporaryDirectBuffer(buf); + } + + void updatePosition(int bytesTransferred) { + // if the I/O succeeded then adjust buffer position + if (bytesTransferred > 0) { + if (buf == dst) { + try { + dst.position(pos + bytesTransferred); + } catch (IllegalArgumentException x) { + // someone has changed the position; ignore + } + } else { + // had to substitute direct buffer + buf.position(bytesTransferred).flip(); + try { + dst.put(buf); + } catch (BufferOverflowException x) { + // someone has changed the position; ignore + } + } + } + } + + @Override + public void run() { + int n = -1; + long overlapped = 0L; + long address; + + // Substitute a native buffer if not direct + if (dst instanceof DirectBuffer) { + buf = dst; + address = ((DirectBuffer)dst).address() + pos; + } else { + buf = Util.getTemporaryDirectBuffer(rem); + address = ((DirectBuffer)buf).address(); + } + + try { + begin(); + + // allocate OVERLAPPED + overlapped = ioCache.add(result); + + // synchronize on result to allow this thread handle the case + // where the read completes immediately. + synchronized (result) { + n = readFile(handle, address, rem, position, overlapped); + if (n == IOStatus.UNAVAILABLE) { + // I/O is pending + return; + } + // read completed immediately: + // 1. update buffer position + // 2. release waiters + updatePosition(n); + result.setResult(n); + } + } catch (Throwable x) { + // failed to initiate read + result.setFailure(toIOException(x)); + } finally { + end(); + } + + // read failed or EOF so completion port will not be notified + if (n < 0 && overlapped != 0L) { + ioCache.remove(overlapped); + } + + // return direct buffer to cache if substituted + releaseBufferIfSubstituted(); + + // invoke completion handler + Invoker.invoke(result.handler(), result); + } + + /** + * Executed when the I/O has completed + */ + @Override + public void completed(int bytesTransferred) { + updatePosition(bytesTransferred); + + // return direct buffer to cache if substituted + releaseBufferIfSubstituted(); + + // release waiters and invoke completion handler + result.setResult(bytesTransferred); + Invoker.invoke(result.handler(), result); + } + + @Override + public void failed(int error, IOException x) { + // if EOF detected asynchronously then it is reported as error + if (error == ERROR_HANDLE_EOF) { + completed(-1); + } else { + // return direct buffer to cache if substituted + releaseBufferIfSubstituted(); + + // release waiters + if (isOpen()) { + result.setFailure(x); + } else { + result.setFailure(new AsynchronousCloseException()); + } + Invoker.invoke(result.handler(), result); + } + } + } + + @Override + public Future read(ByteBuffer dst, + long position, + A attachment, + CompletionHandler handler) + { + if (!reading) + throw new NonReadableChannelException(); + if (position < 0) + throw new IllegalArgumentException("Negative position"); + if (dst.isReadOnly()) + throw new IllegalArgumentException("Read-only buffer"); + + // check if channel is closed + if (!isOpen()) { + CompletedFuture result = CompletedFuture + .withFailure(this, new ClosedChannelException(), attachment); + Invoker.invoke(handler, result); + return result; + } + + int pos = dst.position(); + int lim = dst.limit(); + assert (pos <= lim); + int rem = (pos <= lim ? lim - pos : 0); + + // no space remaining + if (rem == 0) { + CompletedFuture result = + CompletedFuture.withResult(this, 0, attachment); + Invoker.invoke(handler, result); + return result; + } + + // create Future and task that initiates read + PendingFuture result = + new PendingFuture(this, handler, attachment); + ReadTask readTask = new ReadTask(dst, pos, rem, position, result); + result.setContext(readTask); + + // initiate I/O (can only be done from thread in thread pool) + Invoker.invokeOnThreadInThreadPool(this, readTask); + return result; + } + + /** + * Task that initiates write operation and handles completion result. + */ + private class WriteTask implements Runnable, Iocp.ResultHandler { + private final ByteBuffer src; + private final int pos, rem; // buffer position/remaining + private final long position; // file position + private final PendingFuture result; + + // set to src if direct; otherwise set to substituted direct buffer + private volatile ByteBuffer buf; + + WriteTask(ByteBuffer src, + int pos, + int rem, + long position, + PendingFuture result) + { + this.src = src; + this.pos = pos; + this.rem = rem; + this.position = position; + this.result = result; + } + + void releaseBufferIfSubstituted() { + if (buf != src) + Util.releaseTemporaryDirectBuffer(buf); + } + + void updatePosition(int bytesTransferred) { + // if the I/O succeeded then adjust buffer position + if (bytesTransferred > 0) { + try { + src.position(pos + bytesTransferred); + } catch (IllegalArgumentException x) { + // someone has changed the position + } + } + } + + @Override + public void run() { + int n = -1; + long overlapped = 0L; + long address; + + // Substitute a native buffer if not direct + if (src instanceof DirectBuffer) { + buf = src; + address = ((DirectBuffer)src).address() + pos; + } else { + buf = Util.getTemporaryDirectBuffer(rem); + buf.put(src); + buf.flip(); + // temporarily restore position as we don't know how many bytes + // will be written + src.position(pos); + address = ((DirectBuffer)buf).address(); + } + + try { + begin(); + + // allocate an OVERLAPPED structure + overlapped = ioCache.add(result); + + // synchronize on result to allow this thread handle the case + // where the read completes immediately. + synchronized (result) { + n = writeFile(handle, address, rem, position, overlapped); + if (n == IOStatus.UNAVAILABLE) { + // I/O is pending + return; + } + // read completed immediately: + // 1. update buffer position + // 2. release waiters + updatePosition(n); + result.setResult(n); + } + } catch (Throwable x) { + // failed to initiate read: + result.setFailure(toIOException(x)); + + // release resources + if (overlapped != 0L) + ioCache.remove(overlapped); + releaseBufferIfSubstituted(); + + } finally { + end(); + } + + // invoke completion handler + Invoker.invoke(result.handler(), result); + } + + /** + * Executed when the I/O has completed + */ + @Override + public void completed(int bytesTransferred) { + updatePosition(bytesTransferred); + + // return direct buffer to cache if substituted + releaseBufferIfSubstituted(); + + // release waiters and invoke completion handler + result.setResult(bytesTransferred); + Invoker.invoke(result.handler(), result); + } + + @Override + public void failed(int error, IOException x) { + // return direct buffer to cache if substituted + releaseBufferIfSubstituted(); + + // release waiters and invoker completion handler + if (isOpen()) { + result.setFailure(x); + } else { + result.setFailure(new AsynchronousCloseException()); + } + Invoker.invoke(result.handler(), result); + } + } + + @Override + public Future write(ByteBuffer src, + long position, + A attachment, + CompletionHandler handler) + { + if (!writing) + throw new NonWritableChannelException(); + if (position < 0) + throw new IllegalArgumentException("Negative position"); + + // check if channel is closed + if (!isOpen()) { + CompletedFuture result = CompletedFuture + .withFailure(this, new ClosedChannelException(), attachment); + Invoker.invoke(handler, result); + return result; + } + + int pos = src.position(); + int lim = src.limit(); + assert (pos <= lim); + int rem = (pos <= lim ? lim - pos : 0); + + // nothing to write + if (rem == 0) { + CompletedFuture result = + CompletedFuture.withResult(this, 0, attachment); + Invoker.invoke(handler, result); + return result; + } + + // create Future and task to initiate write + PendingFuture result = + new PendingFuture(this, handler, attachment); + WriteTask writeTask = new WriteTask(src, pos, rem, position, result); + result.setContext(writeTask); + + // initiate I/O (can only be done from thread in thread pool) + Invoker.invokeOnThreadInThreadPool(this, writeTask); + return result; + } + + // -- Native methods -- + + private static native int readFile(long handle, long address, int len, + long offset, long overlapped) throws IOException; + + private static native int writeFile(long handle, long address, int len, + long offset, long overlapped) throws IOException; + + private static native int lockFile(long handle, long position, long size, + boolean shared, long overlapped) throws IOException; + + private static native void close0(long handle); + + static { + Util.load(); + } +} diff --git a/src/windows/classes/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java b/src/windows/classes/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java new file mode 100644 index 000000000..8efb10d75 --- /dev/null +++ b/src/windows/classes/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.java @@ -0,0 +1,367 @@ +/* + * 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.ch; + +import java.nio.channels.*; +import java.net.InetSocketAddress; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; +import java.io.IOException; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedAction; +import sun.misc.Unsafe; + +/** + * Windows implementation of AsynchronousServerSocketChannel using overlapped I/O. + */ + +class WindowsAsynchronousServerSocketChannelImpl + extends AsynchronousServerSocketChannelImpl implements Iocp.OverlappedChannel +{ + private static final Unsafe unsafe = Unsafe.getUnsafe(); + + // 2 * (sizeof(SOCKET_ADDRESS) + 16) + private static final int DATA_BUFFER_SIZE = 88; + + private final long handle; + private final int completionKey; + private final Iocp iocp; + + // typically there will be zero, or one I/O operations pending. In rare + // cases there may be more. These rare cases arise when a sequence of accept + // operations complete immediately and handled by the initiating thread. + // The corresponding OVERLAPPED cannot be reused/released until the completion + // event has been posted. + private final PendingIoCache ioCache; + + // the data buffer to receive the local/remote socket address + private final long dataBuffer; + + // flag to indicate that an accept operation is outstanding + private AtomicBoolean accepting = new AtomicBoolean(); + + + WindowsAsynchronousServerSocketChannelImpl(Iocp iocp) throws IOException { + super(iocp); + + // associate socket with given completion port + long h = IOUtil.fdVal(fd); + int key; + try { + key = iocp.associate(this, h); + } catch (IOException x) { + closesocket0(h); // prevent leak + throw x; + } + + this.handle = h; + this.completionKey = key; + this.iocp = iocp; + this.ioCache = new PendingIoCache(); + this.dataBuffer = unsafe.allocateMemory(DATA_BUFFER_SIZE); + } + + @Override + public PendingFuture getByOverlapped(long overlapped) { + return ioCache.remove(overlapped); + } + + @Override + void implClose() throws IOException { + // close socket (which may cause outstanding accept to be aborted). + closesocket0(handle); + + // waits until the accept operations have completed + ioCache.close(); + + // finally disassociate from the completion port + iocp.disassociate(completionKey); + + // release other resources + unsafe.freeMemory(dataBuffer); + } + + @Override + public AsynchronousChannelGroupImpl group() { + return iocp; + } + + /** + * Task to initiate accept operation and to handle result. + */ + private class AcceptTask implements Runnable, Iocp.ResultHandler { + private final WindowsAsynchronousSocketChannelImpl channel; + private final AccessControlContext acc; + private final PendingFuture result; + + AcceptTask(WindowsAsynchronousSocketChannelImpl channel, + AccessControlContext acc, + PendingFuture result) + { + this.channel = channel; + this.acc = acc; + this.result = result; + } + + void enableAccept() { + accepting.set(false); + } + + void closeChildChannel() { + try { + channel.close(); + } catch (IOException ignore) { } + } + + // caller must have acquired read lock for the listener and child channel. + void finishAccept() throws IOException { + /** + * Set local/remote addresses. This is currently very inefficient + * in that it requires 2 calls to getsockname and 2 calls to getpeername. + * (should change this to use GetAcceptExSockaddrs) + */ + updateAcceptContext(handle, channel.handle()); + + InetSocketAddress local = Net.localAddress(channel.fd); + final InetSocketAddress remote = Net.remoteAddress(channel.fd); + channel.setConnected(local, remote); + + // permission check (in context of initiating thread) + if (acc != null) { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + SecurityManager sm = System.getSecurityManager(); + sm.checkAccept(remote.getAddress().getHostAddress(), + remote.getPort()); + return null; + } + }, acc); + } + } + + /** + * Initiates the accept operation. + */ + @Override + public void run() { + long overlapped = 0L; + + try { + // begin usage of listener socket + begin(); + try { + // begin usage of child socket (as it is registered with + // completion port and so may be closed in the event that + // the group is forcefully closed). + channel.begin(); + + synchronized (result) { + overlapped = ioCache.add(result); + + int n = accept0(handle, channel.handle(), overlapped, dataBuffer); + if (n == IOStatus.UNAVAILABLE) { + return; + } + + // connection accepted immediately + finishAccept(); + + // allow another accept before the result is set + enableAccept(); + result.setResult(channel); + } + } finally { + // end usage on child socket + channel.end(); + } + } catch (Throwable x) { + // failed to initiate accept so release resources + if (overlapped != 0L) + ioCache.remove(overlapped); + closeChildChannel(); + if (x instanceof ClosedChannelException) + x = new AsynchronousCloseException(); + if (!(x instanceof IOException) && !(x instanceof SecurityException)) + x = new IOException(x); + enableAccept(); + result.setFailure(x); + } finally { + // end of usage of listener socket + end(); + } + + // accept completed immediately but may not have executed on + // initiating thread in which case the operation may have been + // cancelled. + if (result.isCancelled()) { + closeChildChannel(); + } + + // invoke completion handler + Invoker.invokeIndirectly(result.handler(), result); + } + + /** + * Executed when the I/O has completed + */ + @Override + public void completed(int bytesTransferred) { + try { + // connection accept after group has shutdown + if (iocp.isShutdown()) { + throw new IOException(new ShutdownChannelGroupException()); + } + + // finish the accept + try { + begin(); + try { + channel.begin(); + finishAccept(); + } finally { + channel.end(); + } + } finally { + end(); + } + + // allow another accept before the result is set + enableAccept(); + result.setResult(channel); + } catch (Throwable x) { + enableAccept(); + closeChildChannel(); + if (x instanceof ClosedChannelException) + x = new AsynchronousCloseException(); + if (!(x instanceof IOException) && !(x instanceof SecurityException)) + x = new IOException(x); + result.setFailure(x); + } + + // if an async cancel has already cancelled the operation then + // close the new channel so as to free resources + if (result.isCancelled()) { + closeChildChannel(); + } + + // invoke handler (but not directly) + Invoker.invokeIndirectly(result.handler(), result); + } + + @Override + public void failed(int error, IOException x) { + enableAccept(); + closeChildChannel(); + + // release waiters + if (isOpen()) { + result.setFailure(x); + } else { + result.setFailure(new AsynchronousCloseException()); + } + Invoker.invokeIndirectly(result.handler(), result); + } + } + + @Override + public Future accept(A attachment, + final CompletionHandler handler) + { + if (!isOpen()) { + CompletedFuture result = CompletedFuture + .withFailure(this, new ClosedChannelException(), attachment); + Invoker.invokeIndirectly(handler, result); + return result; + } + if (isAcceptKilled()) + throw new RuntimeException("Accept not allowed due to cancellation"); + + // ensure channel is bound to local address + if (localAddress == null) + throw new NotYetBoundException(); + + // create the socket that will be accepted. The creation of the socket + // is enclosed by a begin/end for the listener socket to ensure that + // we check that the listener is open and also to prevent the I/O + // port from being closed as the new socket is registered. + WindowsAsynchronousSocketChannelImpl ch = null; + IOException ioe = null; + try { + begin(); + ch = new WindowsAsynchronousSocketChannelImpl(iocp, false); + } catch (IOException x) { + ioe = x; + } finally { + end(); + } + if (ioe != null) { + CompletedFuture result = + CompletedFuture.withFailure(this, ioe, attachment); + Invoker.invokeIndirectly(handler, result); + return result; + } + + // need calling context when there is security manager as + // permission check may be done in a different thread without + // any application call frames on the stack + AccessControlContext acc = (System.getSecurityManager() == null) ? + null : AccessController.getContext(); + + PendingFuture result = + new PendingFuture(this, handler, attachment); + AcceptTask task = new AcceptTask(ch, acc, result); + result.setContext(task); + + // check and set flag to prevent concurrent accepting + if (!accepting.compareAndSet(false, true)) + throw new AcceptPendingException(); + + // initiate accept. As I/O operations are tied to the initiating thread + // then it will only be invoked direcly if this thread is in the thread + // pool. If this thread is not in the thread pool when a task is + // submitted to initiate the accept. + Invoker.invokeOnThreadInThreadPool(this, task); + return result; + } + + // -- Native methods -- + + private static native void initIDs(); + + private static native int accept0(long listenSocket, long acceptSocket, + long overlapped, long dataBuffer) throws IOException; + + private static native void updateAcceptContext(long listenSocket, + long acceptSocket) throws IOException; + + private static native void closesocket0(long socket) throws IOException; + + static { + Util.load(); + initIDs(); + } +} diff --git a/src/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java b/src/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java new file mode 100644 index 000000000..fe9920c15 --- /dev/null +++ b/src/windows/classes/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.java @@ -0,0 +1,911 @@ +/* + * 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 conne02110-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.ch; + +import java.nio.channels.*; +import java.nio.ByteBuffer; +import java.nio.BufferOverflowException; +import java.net.*; +import java.util.concurrent.*; +import java.io.IOException; +import sun.misc.Unsafe; + +/** + * Windows implementation of AsynchronousSocketChannel using overlapped I/O. + */ + +class WindowsAsynchronousSocketChannelImpl + extends AsynchronousSocketChannelImpl implements Iocp.OverlappedChannel +{ + private static final Unsafe unsafe = Unsafe.getUnsafe(); + private static int addressSize = unsafe.addressSize(); + + private static int dependsArch(int value32, int value64) { + return (addressSize == 4) ? value32 : value64; + } + + /* + * typedef struct _WSABUF { + * u_long len; + * char FAR * buf; + * } WSABUF; + */ + private static final int SIZEOF_WSABUF = dependsArch(8, 16); + private static final int OFFSETOF_LEN = 0; + private static final int OFFSETOF_BUF = dependsArch(4, 8); + + // maximum vector size for scatter/gather I/O + private static final int MAX_WSABUF = 16; + + private static final int SIZEOF_WSABUFARRAY = MAX_WSABUF * SIZEOF_WSABUF; + + + // socket handle. Use begin()/end() around each usage of this handle. + final long handle; + + // I/O completion port that the socket is associated with + private final Iocp iocp; + + // completion key to identify channel when I/O completes + private final int completionKey; + + // Pending I/O operations are tied to an OVERLAPPED structure that can only + // be released when the I/O completion event is posted to the completion + // port. Where I/O operations complete immediately then it is possible + // there may be more than two OVERLAPPED structures in use. + private final PendingIoCache ioCache; + + // per-channel arrays of WSABUF structures + private final long readBufferArray; + private final long writeBufferArray; + + + WindowsAsynchronousSocketChannelImpl(Iocp iocp, boolean failIfGroupShutdown) + throws IOException + { + super(iocp); + + // associate socket with default completion port + long h = IOUtil.fdVal(fd); + int key = 0; + try { + key = iocp.associate(this, h); + } catch (ShutdownChannelGroupException x) { + if (failIfGroupShutdown) { + closesocket0(h); + throw x; + } + } catch (IOException x) { + closesocket0(h); + throw x; + } + + this.handle = h; + this.iocp = iocp; + this.completionKey = key; + this.ioCache = new PendingIoCache(); + + // allocate WSABUF arrays + this.readBufferArray = unsafe.allocateMemory(SIZEOF_WSABUFARRAY); + this.writeBufferArray = unsafe.allocateMemory(SIZEOF_WSABUFARRAY); + } + + WindowsAsynchronousSocketChannelImpl(Iocp iocp) throws IOException { + this(iocp, true); + } + + @Override + public AsynchronousChannelGroupImpl group() { + return iocp; + } + + /** + * Invoked by Iocp when an I/O operation competes. + */ + @Override + public PendingFuture getByOverlapped(long overlapped) { + return ioCache.remove(overlapped); + } + + // invoked by WindowsAsynchronousServerSocketChannelImpl + long handle() { + return handle; + } + + // invoked by WindowsAsynchronousServerSocketChannelImpl when new connection + // accept + void setConnected(SocketAddress localAddress, SocketAddress remoteAddress) { + synchronized (stateLock) { + state = ST_CONNECTED; + this.localAddress = localAddress; + this.remoteAddress = remoteAddress; + } + } + + @Override + void implClose() throws IOException { + // close socket (may cause outstanding async I/O operations to fail). + closesocket0(handle); + + // waits until all I/O operations have completed + ioCache.close(); + + // release arrays of WSABUF structures + unsafe.freeMemory(readBufferArray); + unsafe.freeMemory(writeBufferArray); + + // finally disassociate from the completion port (key can be 0 if + // channel created when group is shutdown) + if (completionKey != 0) + iocp.disassociate(completionKey); + } + + @Override + public void onCancel(PendingFuture task) { + if (task.getContext() instanceof ConnectTask) + killConnect(); + if (task.getContext() instanceof ReadTask) + killReading(); + if (task.getContext() instanceof WriteTask) + killWriting(); + } + + /** + * Implements the task to initiate a connection and the handler to + * consume the result when the connection is established (or fails). + */ + private class ConnectTask implements Runnable, Iocp.ResultHandler { + private final InetSocketAddress remote; + private final PendingFuture result; + + ConnectTask(InetSocketAddress remote, PendingFuture result) { + this.remote = remote; + this.result = result; + } + + private void closeChannel() { + try { + close(); + } catch (IOException ignore) { } + } + + private IOException toIOException(Throwable x) { + if (x instanceof IOException) { + if (x instanceof ClosedChannelException) + x = new AsynchronousCloseException(); + return (IOException)x; + } + return new IOException(x); + } + + /** + * Invoke after a connection is successfully established. + */ + private void afterConnect() throws IOException { + updateConnectContext(handle); + synchronized (stateLock) { + state = ST_CONNECTED; + remoteAddress = remote; + } + } + + /** + * Task to initiate a connection. + */ + @Override + public void run() { + long overlapped = 0L; + Throwable exc = null; + try { + begin(); + + // synchronize on result to allow this thread handle the case + // where the connection is established immediately. + synchronized (result) { + overlapped = ioCache.add(result); + // initiate the connection + int n = connect0(handle, Net.isIPv6Available(), remote.getAddress(), + remote.getPort(), overlapped); + if (n == IOStatus.UNAVAILABLE) { + // connection is pending + return; + } + + // connection established immediately + afterConnect(); + result.setResult(null); + } + } catch (Throwable x) { + exc = x; + } finally { + end(); + } + + if (exc != null) { + if (overlapped != 0L) + ioCache.remove(overlapped); + closeChannel(); + result.setFailure(toIOException(exc)); + } + Invoker.invoke(result.handler(), result); + } + + /** + * Invoked by handler thread when connection established. + */ + @Override + public void completed(int bytesTransferred) { + Throwable exc = null; + try { + begin(); + afterConnect(); + result.setResult(null); + } catch (Throwable x) { + // channel is closed or unable to finish connect + exc = x; + } finally { + end(); + } + + // can't close channel while in begin/end block + if (exc != null) { + closeChannel(); + result.setFailure(toIOException(exc)); + } + + Invoker.invoke(result.handler(), result); + } + + /** + * Invoked by handler thread when failed to establish connection. + */ + @Override + public void failed(int error, IOException x) { + if (isOpen()) { + closeChannel(); + result.setFailure(x); + } else { + result.setFailure(new AsynchronousCloseException()); + } + Invoker.invoke(result.handler(), result); + } + } + + @Override + public Future connect(SocketAddress remote, + A attachment, + CompletionHandler handler) + { + if (!isOpen()) { + CompletedFuture result = CompletedFuture + .withFailure(this, new ClosedChannelException(), attachment); + Invoker.invoke(handler, result); + return result; + } + + InetSocketAddress isa = Net.checkAddress(remote); + + // permission check + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkConnect(isa.getAddress().getHostAddress(), isa.getPort()); + + // check and update state + // ConnectEx requires the socket to be bound to a local address + IOException bindException = null; + synchronized (stateLock) { + if (state == ST_CONNECTED) + throw new AlreadyConnectedException(); + if (state == ST_PENDING) + throw new ConnectionPendingException(); + if (localAddress == null) { + try { + bind(new InetSocketAddress(0)); + } catch (IOException x) { + bindException = x; + } + } + if (bindException == null) + state = ST_PENDING; + } + + // handle bind failure + if (bindException != null) { + try { + close(); + } catch (IOException ignore) { } + CompletedFuture result = CompletedFuture + .withFailure(this, bindException, attachment); + Invoker.invoke(handler, result); + return result; + } + + // setup task + PendingFuture result = + new PendingFuture(this, handler, attachment); + ConnectTask task = new ConnectTask(isa, result); + result.setContext(task); + + // initiate I/O (can only be done from thread in thread pool) + Invoker.invokeOnThreadInThreadPool(this, task); + return result; + } + + /** + * Implements the task to initiate a read and the handler to consume the + * result when the read completes. + */ + private class ReadTask implements Runnable, Iocp.ResultHandler { + private final ByteBuffer[] bufs; + private final int numBufs; + private final boolean scatteringRead; + private final PendingFuture result; + + // set by run method + private ByteBuffer[] shadow; + + ReadTask(ByteBuffer[] bufs, + boolean scatteringRead, + PendingFuture result) + { + this.bufs = bufs; + this.numBufs = (bufs.length > MAX_WSABUF) ? MAX_WSABUF : bufs.length; + this.scatteringRead = scatteringRead; + this.result = result; + } + + /** + * Invoked prior to read to prepare the WSABUF array. Where necessary, + * it substitutes non-direct buffers with direct buffers. + */ + void prepareBuffers() { + shadow = new ByteBuffer[numBufs]; + long address = readBufferArray; + for (int i=0; i= len) { + bytesRead -= len; + int newPosition = pos + len; + try { + nextBuffer.position(newPosition); + } catch (IllegalArgumentException x) { + // position changed by another + } + } else { // Buffers not completely filled + if (bytesRead > 0) { + assert(pos + bytesRead < (long)Integer.MAX_VALUE); + int newPosition = pos + bytesRead; + try { + nextBuffer.position(newPosition); + } catch (IllegalArgumentException x) { + // position changed by another + } + } + break; + } + } + + // Put results from shadow into the slow buffers + for (int i=0; i Future readImpl(ByteBuffer[] bufs, + boolean scatteringRead, + long timeout, + TimeUnit unit, + A attachment, + CompletionHandler handler) + { + // setup task + PendingFuture result = + new PendingFuture(this, handler, attachment); + final ReadTask readTask = new ReadTask(bufs, scatteringRead, result); + result.setContext(readTask); + + // schedule timeout + if (timeout > 0L) { + Future timeoutTask = iocp.schedule(new Runnable() { + public void run() { + readTask.timeout(); + } + }, timeout, unit); + result.setTimeoutTask(timeoutTask); + } + + // initiate I/O (can only be done from thread in thread pool) + Invoker.invokeOnThreadInThreadPool(this, readTask); + return result; + } + + /** + * Implements the task to initiate a write and the handler to consume the + * result when the write completes. + */ + private class WriteTask implements Runnable, Iocp.ResultHandler { + private final ByteBuffer[] bufs; + private final int numBufs; + private final boolean gatheringWrite; + private final PendingFuture result; + + // set by run method + private ByteBuffer[] shadow; + + WriteTask(ByteBuffer[] bufs, + boolean gatheringWrite, + PendingFuture result) + { + this.bufs = bufs; + this.numBufs = (bufs.length > MAX_WSABUF) ? MAX_WSABUF : bufs.length; + this.gatheringWrite = gatheringWrite; + this.result = result; + } + + /** + * Invoked prior to write to prepare the WSABUF array. Where necessary, + * it substitutes non-direct buffers with direct buffers. + */ + void prepareBuffers() { + shadow = new ByteBuffer[numBufs]; + long address = writeBufferArray; + for (int i=0; i= len) { + bytesWritten -= len; + int newPosition = pos + len; + try { + nextBuffer.position(newPosition); + } catch (IllegalArgumentException x) { + // position changed by someone else + } + } else { // Buffers not completely filled + if (bytesWritten > 0) { + assert(pos + bytesWritten < (long)Integer.MAX_VALUE); + int newPosition = pos + bytesWritten; + try { + nextBuffer.position(newPosition); + } catch (IllegalArgumentException x) { + // position changed by someone else + } + } + break; + } + } + } + + void releaseBuffers() { + for (int i=0; i Future writeImpl(ByteBuffer[] bufs, + boolean gatheringWrite, + long timeout, + TimeUnit unit, + A attachment, + CompletionHandler handler) + { + // setup task + PendingFuture result = + new PendingFuture(this, handler, attachment); + final WriteTask writeTask = new WriteTask(bufs, gatheringWrite, result); + result.setContext(writeTask); + + // schedule timeout + if (timeout > 0L) { + Future timeoutTask = iocp.schedule(new Runnable() { + public void run() { + writeTask.timeout(); + } + }, timeout, unit); + result.setTimeoutTask(timeoutTask); + } + + // initiate I/O (can only be done from thread in thread pool) + Invoker.invokeOnThreadInThreadPool(this, writeTask); + return result; + } + + // -- Native methods -- + + private static native void initIDs(); + + private static native int connect0(long socket, boolean preferIPv6, + InetAddress remote, int remotePort, long overlapped) throws IOException; + + private static native void updateConnectContext(long socket) throws IOException; + + private static native int read0(long socket, int count, long addres, long overlapped) + throws IOException; + + private static native int write0(long socket, int count, long address, + long overlapped) throws IOException; + + private static native void shutdown0(long socket, int how) throws IOException; + + private static native void closesocket0(long socket) throws IOException; + + static { + Util.load(); + initIDs(); + } +} diff --git a/src/windows/classes/sun/nio/fs/DefaultFileSystemProvider.java b/src/windows/classes/sun/nio/fs/DefaultFileSystemProvider.java new file mode 100644 index 000000000..93923103e --- /dev/null +++ b/src/windows/classes/sun/nio/fs/DefaultFileSystemProvider.java @@ -0,0 +1,38 @@ +/* + * 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.spi.FileSystemProvider; + +/** + * Creates default provider on Windows + */ +public class DefaultFileSystemProvider { + private DefaultFileSystemProvider() { } + public static FileSystemProvider create() { + return new WindowsFileSystemProvider(); + } +} diff --git a/src/windows/classes/sun/nio/fs/DefaultFileTypeDetector.java b/src/windows/classes/sun/nio/fs/DefaultFileTypeDetector.java new file mode 100644 index 000000000..1e775aee8 --- /dev/null +++ b/src/windows/classes/sun/nio/fs/DefaultFileTypeDetector.java @@ -0,0 +1,36 @@ +/* + * 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.spi.FileTypeDetector; + +public class DefaultFileTypeDetector { + private DefaultFileTypeDetector() { } + + public static FileTypeDetector create() { + return new RegistryFileTypeDetector(); + } +} diff --git a/src/windows/classes/sun/nio/fs/RegistryFileTypeDetector.java b/src/windows/classes/sun/nio/fs/RegistryFileTypeDetector.java new file mode 100644 index 000000000..dc4b9c0e2 --- /dev/null +++ b/src/windows/classes/sun/nio/fs/RegistryFileTypeDetector.java @@ -0,0 +1,82 @@ +/* + * 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.*; +import java.io.IOException; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * File type detector that does lookup of file extension using Windows Registry. + */ + +public class RegistryFileTypeDetector + extends AbstractFileTypeDetector +{ + public RegistryFileTypeDetector() { + super(); + } + + @Override + public String implProbeContentType(FileRef file) throws IOException { + if (!(file instanceof Path)) + return null; + + // get file extension + Path name = ((Path)file).getName(); + if (name == null) + return null; + String filename = name.toString(); + int dot = filename.lastIndexOf('.'); + if ((dot < 0) || (dot == (filename.length()-1))) + return null; + + // query HKEY_CLASSES_ROOT\ + String key = filename.substring(dot); + NativeBuffer keyBuffer = WindowsNativeDispatcher.asNativeBuffer(key); + NativeBuffer nameBuffer = WindowsNativeDispatcher.asNativeBuffer("Content Type"); + try { + return queryStringValue(keyBuffer.address(), nameBuffer.address()); + } finally { + nameBuffer.release(); + keyBuffer.release(); + } + } + + private static native String queryStringValue(long subKey, long name); + + static { + AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Void run() { + // nio.dll has dependency on net.dll + System.loadLibrary("net"); + System.loadLibrary("nio"); + return null; + }}); + } +} diff --git a/src/windows/classes/sun/nio/fs/WindowsAclFileAttributeView.java b/src/windows/classes/sun/nio/fs/WindowsAclFileAttributeView.java new file mode 100644 index 000000000..937aedd85 --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsAclFileAttributeView.java @@ -0,0 +1,226 @@ +/* + * 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.ProviderMismatchException; +import java.nio.file.attribute.*; +import java.util.*; +import java.io.IOException; + +import static sun.nio.fs.WindowsNativeDispatcher.*; +import static sun.nio.fs.WindowsConstants.*; + +/** + * Windows implementation of AclFileAttributeView. + */ + +class WindowsAclFileAttributeView + extends AbstractAclFileAttributeView +{ + /** + * typedef struct _SECURITY_DESCRIPTOR { + * BYTE Revision; + * BYTE Sbz1; + * SECURITY_DESCRIPTOR_CONTROL Control; + * PSID Owner; + * PSID Group; + * PACL Sacl; + * PACL Dacl; + * } SECURITY_DESCRIPTOR; + */ + private static final short SIZEOF_SECURITY_DESCRIPTOR = 20; + + private final WindowsPath file; + private final boolean followLinks; + + WindowsAclFileAttributeView(WindowsPath file, boolean followLinks) { + this.file = file; + this.followLinks = followLinks; + } + + // permision check + private void checkAccess(WindowsPath file, + boolean checkRead, + boolean checkWrite) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + if (checkRead) + sm.checkRead(file.getPathForPermissionCheck()); + if (checkWrite) + sm.checkWrite(file.getPathForPermissionCheck()); + sm.checkPermission(new RuntimePermission("accessUserInformation")); + } + } + + // invokes GetFileSecurity to get requested security information + static NativeBuffer getFileSecurity(String path, int request) + throws IOException + { + // invoke get to buffer size + int size = 0; + try { + size = GetFileSecurity(path, request, 0L, 0); + } catch (WindowsException x) { + x.rethrowAsIOException(path); + } + assert size > 0; + + // allocate buffer and re-invoke to get security information + NativeBuffer buffer = NativeBuffers.getNativeBuffer(size); + try { + for (;;) { + int newSize = GetFileSecurity(path, request, buffer.address(), size); + if (newSize <= size) + return buffer; + + // buffer was insufficient + buffer.release(); + buffer = NativeBuffers.getNativeBuffer(newSize); + size = newSize; + } + } catch (WindowsException x) { + buffer.release(); + x.rethrowAsIOException(path); + return null; + } + } + + @Override + public UserPrincipal getOwner() + throws IOException + { + checkAccess(file, true, false); + + // GetFileSecurity does not follow links so when following links we + // need the final target + String path = WindowsLinkSupport.getFinalPath(file, followLinks); + NativeBuffer buffer = getFileSecurity(path, OWNER_SECURITY_INFORMATION); + try { + // get the address of the SID + long sidAddress = GetSecurityDescriptorOwner(buffer.address()); + if (sidAddress == 0L) + throw new IOException("no owner"); + return WindowsUserPrincipals.fromSid(sidAddress); + } catch (WindowsException x) { + x.rethrowAsIOException(file); + return null; + } finally { + buffer.release(); + } + } + + @Override + public List getAcl() + throws IOException + { + checkAccess(file, true, false); + + // GetFileSecurity does not follow links so when following links we + // need the final target + String path = WindowsLinkSupport.getFinalPath(file, followLinks); + + // ALLOW and DENY entries in DACL; + // AUDIT entries in SACL (ignore for now as it requires privileges) + NativeBuffer buffer = getFileSecurity(path, DACL_SECURITY_INFORMATION); + try { + return WindowsSecurityDescriptor.getAcl(buffer.address()); + } finally { + buffer.release(); + } + } + + @Override + public void setOwner(UserPrincipal obj) + throws IOException + { + if (obj == null) + throw new NullPointerException("'owner' is null"); + if (!(obj instanceof WindowsUserPrincipals.User)) + throw new ProviderMismatchException(); + WindowsUserPrincipals.User owner = (WindowsUserPrincipals.User)obj; + + // permission check + checkAccess(file, false, true); + + // SetFileSecurity does not follow links so when following links we + // need the final target + String path = WindowsLinkSupport.getFinalPath(file, followLinks); + + // ConvertStringSidToSid allocates memory for SID so must invoke + // LocalFree to free it when we are done + long pOwner = 0L; + try { + pOwner = ConvertStringSidToSid(owner.sidString()); + } catch (WindowsException x) { + throw new IOException("Failed to get SID for " + owner.getName() + + ": " + x.errorString()); + } + + // Allocate buffer for security descriptor, initialize it, set + // owner information and update the file. + try { + NativeBuffer buffer = NativeBuffers.getNativeBuffer(SIZEOF_SECURITY_DESCRIPTOR); + try { + InitializeSecurityDescriptor(buffer.address()); + SetSecurityDescriptorOwner(buffer.address(), pOwner); + // may need SeRestorePrivilege to set the owner + WindowsSecurity.Privilege priv = + WindowsSecurity.enablePrivilege("SeRestorePrivilege"); + try { + SetFileSecurity(path, + OWNER_SECURITY_INFORMATION, + buffer.address()); + } finally { + priv.drop(); + } + } catch (WindowsException x) { + x.rethrowAsIOException(file); + } finally { + buffer.release(); + } + } finally { + LocalFree(pOwner); + } + } + + @Override + public void setAcl(List acl) throws IOException { + checkAccess(file, false, true); + + // SetFileSecurity does not follow links so when following links we + // need the final target + String path = WindowsLinkSupport.getFinalPath(file, followLinks); + WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.create(acl); + try { + SetFileSecurity(path, DACL_SECURITY_INFORMATION, sd.address()); + } catch (WindowsException x) { + x.rethrowAsIOException(file); + } finally { + sd.release(); + } + } +} diff --git a/src/windows/classes/sun/nio/fs/WindowsChannelFactory.java b/src/windows/classes/sun/nio/fs/WindowsChannelFactory.java new file mode 100644 index 000000000..f559166bb --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsChannelFactory.java @@ -0,0 +1,341 @@ +/* + * 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.*; +import java.nio.channels.*; +import java.io.FileDescriptor; +import java.io.IOException; +import java.util.*; + +import com.sun.nio.file.ExtendedOpenOption; + +import sun.nio.ch.FileChannelImpl; +import sun.nio.ch.ThreadPool; +import sun.nio.ch.WindowsAsynchronousFileChannelImpl; +import sun.misc.SharedSecrets; +import sun.misc.JavaIOFileDescriptorAccess; + +import static sun.nio.fs.WindowsNativeDispatcher.*; +import static sun.nio.fs.WindowsConstants.*; + +/** + * Factory to create FileChannels and AsynchronousFileChannels. + */ + +class WindowsChannelFactory { + private static final JavaIOFileDescriptorAccess fdAccess = + SharedSecrets.getJavaIOFileDescriptorAccess(); + + private WindowsChannelFactory() { } + + /** + * Do not follow reparse points when opening an existing file. Do not fail + * if the file is a reparse point. + */ + static final OpenOption OPEN_REPARSE_POINT = new OpenOption() { }; + + /** + * Represents the flags from a user-supplied set of open options. + */ + private static class Flags { + boolean read; + boolean write; + boolean append; + boolean truncateExisting; + boolean create; + boolean createNew; + boolean deleteOnClose; + boolean sparse; + boolean overlapped; + boolean sync; + boolean dsync; + + // non-standard + boolean shareRead = true; + boolean shareWrite = true; + boolean shareDelete = true; + boolean noFollowLinks; + boolean openReparsePoint; + + static Flags toFlags(Set options) { + Flags flags = new Flags(); + for (OpenOption option: options) { + if (option instanceof StandardOpenOption) { + switch ((StandardOpenOption)option) { + case READ : flags.read = true; break; + case WRITE : flags.write = true; break; + case APPEND : flags.append = true; break; + case TRUNCATE_EXISTING : flags.truncateExisting = true; break; + case CREATE : flags.create = true; break; + case CREATE_NEW : flags.createNew = true; break; + case DELETE_ON_CLOSE : flags.deleteOnClose = true; break; + case SPARSE : flags.sparse = true; break; + case SYNC : flags.sync = true; break; + case DSYNC : flags.dsync = true; break; + default: throw new UnsupportedOperationException(); + } + continue; + } + if (option instanceof ExtendedOpenOption) { + switch ((ExtendedOpenOption)option) { + case NOSHARE_READ : flags.shareRead = false; break; + case NOSHARE_WRITE : flags.shareWrite = false; break; + case NOSHARE_DELETE : flags.shareDelete = false; break; + default: throw new UnsupportedOperationException(); + } + continue; + } + if (option == LinkOption.NOFOLLOW_LINKS) { + flags.noFollowLinks = true; + continue; + } + if (option == OPEN_REPARSE_POINT) { + flags.openReparsePoint = true; + continue; + } + if (option == null) + throw new NullPointerException(); + throw new UnsupportedOperationException(); + } + return flags; + } + } + + /** + * Open/creates file, returning FileChannel to access the file + * + * @param pathForWindows + * The path of the file to open/create + * @param pathToCheck + * The path used for permission checks (if security manager) + */ + static FileChannel newFileChannel(String pathForWindows, + String pathToCheck, + Set options, + long pSecurityDescriptor) + throws WindowsException + { + Flags flags = Flags.toFlags(options); + + // default is reading; append => writing + if (!flags.read && !flags.write) { + if (flags.append) { + flags.write = true; + } else { + flags.read = true; + } + } + + // validation + if (flags.read && flags.append) + throw new IllegalArgumentException("READ + APPEND not allowed"); + if (flags.append && flags.truncateExisting) + throw new IllegalArgumentException("APPEND + TRUNCATE_EXISTING not allowed"); + + FileDescriptor fdObj = open(pathForWindows, pathToCheck, flags, pSecurityDescriptor); + return FileChannelImpl.open(fdObj, flags.read, flags.write, null); + } + + /** + * Open/creates file, returning AsynchronousFileChannel to access the file + * + * @param pathForWindows + * The path of the file to open/create + * @param pathToCheck + * The path used for permission checks (if security manager) + * @param pool + * The thread pool that the channel is associated with + */ + static AsynchronousFileChannel newAsynchronousFileChannel(String pathForWindows, + String pathToCheck, + Set options, + long pSecurityDescriptor, + ThreadPool pool) + throws IOException + { + Flags flags = Flags.toFlags(options); + + // Overlapped I/O required + flags.overlapped = true; + + // default is reading + if (!flags.read && !flags.write) { + flags.read = true; + } + + // validation + if (flags.append) + throw new UnsupportedOperationException("APPEND not allowed"); + + // open file for overlapped I/O + FileDescriptor fdObj; + try { + fdObj = open(pathForWindows, pathToCheck, flags, pSecurityDescriptor); + } catch (WindowsException x) { + x.rethrowAsIOException(pathForWindows); + return null; + } + + // create the AsynchronousFileChannel + try { + return WindowsAsynchronousFileChannelImpl.open(fdObj, flags.read, flags.write, pool); + } catch (IOException x) { + // IOException is thrown if the file handle cannot be associated + // with the completion port. All we can do is close the file. + long handle = fdAccess.getHandle(fdObj); + CloseHandle(handle); + throw x; + } + } + + /** + * Opens file based on parameters and options, returning a FileDescriptor + * encapsulating the handle to the open file. + */ + private static FileDescriptor open(String pathForWindows, + String pathToCheck, + Flags flags, + long pSecurityDescriptor) + throws WindowsException + { + // set to true if file must be truncated after open + boolean truncateAfterOpen = false; + + // map options + int dwDesiredAccess = 0; + if (flags.read) + dwDesiredAccess |= GENERIC_READ; + if (flags.write) + dwDesiredAccess |= (flags.append) ? FILE_APPEND_DATA : GENERIC_WRITE; + + int dwShareMode = 0; + if (flags.shareRead) + dwShareMode |= FILE_SHARE_READ; + if (flags.shareWrite) + dwShareMode |= FILE_SHARE_WRITE; + if (flags.shareDelete) + dwShareMode |= FILE_SHARE_DELETE; + + int dwFlagsAndAttributes = FILE_ATTRIBUTE_NORMAL; + int dwCreationDisposition = OPEN_EXISTING; + if (flags.write) { + if (flags.createNew) { + dwCreationDisposition = CREATE_NEW; + // force create to fail if file is orphaned reparse point + dwFlagsAndAttributes |= FILE_FLAG_OPEN_REPARSE_POINT; + } else { + if (flags.create) + dwCreationDisposition = OPEN_ALWAYS; + if (flags.truncateExisting) { + // Windows doesn't have a creation disposition that exactly + // corresponds to CREATE + TRUNCATE_EXISTING so we use + // the OPEN_ALWAYS mode and then truncate the file. + if (dwCreationDisposition == OPEN_ALWAYS) { + truncateAfterOpen = true; + } else { + dwCreationDisposition = TRUNCATE_EXISTING; + } + } + } + } + + if (flags.dsync || flags.sync) + dwFlagsAndAttributes |= FILE_FLAG_WRITE_THROUGH; + if (flags.overlapped) + dwFlagsAndAttributes |= FILE_FLAG_OVERLAPPED; + if (flags.deleteOnClose) + dwFlagsAndAttributes |= FILE_FLAG_DELETE_ON_CLOSE; + + // NOFOLLOW_LINKS and NOFOLLOW_REPARSEPOINT mean open reparse point + boolean okayToFollowLinks = true; + if (dwCreationDisposition != CREATE_NEW && + (flags.noFollowLinks || + flags.openReparsePoint || + flags.deleteOnClose)) + { + if (flags.noFollowLinks || flags.deleteOnClose) + okayToFollowLinks = false; + dwFlagsAndAttributes |= FILE_FLAG_OPEN_REPARSE_POINT; + } + + // permission check + if (pathToCheck != null) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + if (flags.read) + sm.checkRead(pathToCheck); + if (flags.write) + sm.checkWrite(pathToCheck); + if (flags.deleteOnClose) + sm.checkDelete(pathToCheck); + } + } + + // open file + long handle = CreateFile(pathForWindows, + dwDesiredAccess, + dwShareMode, + pSecurityDescriptor, + dwCreationDisposition, + dwFlagsAndAttributes); + + // make sure this isn't a symbolic link. + if (!okayToFollowLinks) { + try { + if (WindowsFileAttributes.readAttributes(handle).isSymbolicLink()) + throw new WindowsException("File is symbolic link"); + } catch (WindowsException x) { + CloseHandle(handle); + throw x; + } + } + + // truncate file (for CREATE + TRUNCATE_EXISTING case) + if (truncateAfterOpen) { + try { + SetEndOfFile(handle); + } catch (WindowsException x) { + CloseHandle(handle); + throw x; + } + } + + // make the file sparse if needed + if (dwCreationDisposition == CREATE_NEW && flags.sparse) { + try { + DeviceIoControlSetSparse(handle); + } catch (WindowsException x) { + // ignore as sparse option is hint + } + } + + // create FileDescriptor and return + FileDescriptor fdObj = new FileDescriptor(); + fdAccess.setHandle(fdObj, handle); + return fdObj; + } +} diff --git a/src/windows/classes/sun/nio/fs/WindowsConstants.java b/src/windows/classes/sun/nio/fs/WindowsConstants.java new file mode 100644 index 000000000..f2619ac80 --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsConstants.java @@ -0,0 +1,192 @@ +/* + * 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; + +/** + * Win32 APIs constants. + */ + +class WindowsConstants { + private WindowsConstants() { } + + // general + public static final long INVALID_HANDLE_VALUE = -1L; + + // generic rights + public static final int GENERIC_READ = 0x80000000; + public static final int GENERIC_WRITE = 0x40000000; + + // share modes + public static final int FILE_SHARE_READ = 0x00000001; + public static final int FILE_SHARE_WRITE = 0x00000002; + public static final int FILE_SHARE_DELETE = 0x00000004; + + // creation modes + public static final int CREATE_NEW = 1; + public static final int CREATE_ALWAYS = 2; + public static final int OPEN_EXISTING = 3; + public static final int OPEN_ALWAYS = 4; + public static final int TRUNCATE_EXISTING = 5; + + // attributes and flags + public static final int FILE_ATTRIBUTE_READONLY = 0x00000001; + public static final int FILE_ATTRIBUTE_HIDDEN = 0x00000002; + public static final int FILE_ATTRIBUTE_SYSTEM = 0x00000004; + public static final int FILE_ATTRIBUTE_DIRECTORY = 0x00000010; + public static final int FILE_ATTRIBUTE_ARCHIVE = 0x00000020; + public static final int FILE_ATTRIBUTE_DEVICE = 0x00000040; + public static final int FILE_ATTRIBUTE_NORMAL = 0x00000080; + public static final int FILE_ATTRIBUTE_REPARSE_POINT = 0x400; + public static final int FILE_FLAG_NO_BUFFERING = 0x20000000; + public static final int FILE_FLAG_OVERLAPPED = 0x40000000; + public static final int FILE_FLAG_WRITE_THROUGH = 0x80000000; + public static final int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; + public static final int FILE_FLAG_DELETE_ON_CLOSE = 0x04000000; + public static final int FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000; + + // stream ids + public static final int BACKUP_ALTERNATE_DATA = 0x00000004; + public static final int BACKUP_SPARSE_BLOCK = 0x00000009; + + // reparse point/symbolic link related constants + public static final int IO_REPARSE_TAG_SYMLINK = 0xA000000C; + public static final int MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024; + public static final int SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1; + + // volume flags + public static final int FILE_CASE_SENSITIVE_SEARCH = 0x00000001; + public static final int FILE_CASE_PRESERVED_NAMES = 0x00000002; + public static final int FILE_PERSISTENT_ACLS = 0x00000008; + public static final int FILE_VOLUME_IS_COMPRESSED = 0x00008000; + public static final int FILE_NAMED_STREAMS = 0x00040000; + public static final int FILE_READ_ONLY_VOLUME = 0x00080000; + + // error codes + public static final int ERROR_FILE_NOT_FOUND = 2; + public static final int ERROR_PATH_NOT_FOUND = 3; + public static final int ERROR_ACCESS_DENIED = 5; + public static final int ERROR_INVALID_HANDLE = 6; + public static final int ERROR_INVALID_DATA = 13; + public static final int ERROR_NOT_SAME_DEVICE = 17; + public static final int ERROR_NOT_READY = 21; + public static final int ERROR_FILE_EXISTS = 80; + public static final int ERROR_DISK_FULL = 112; + public static final int ERROR_INSUFFICIENT_BUFFER = 122; + public static final int ERROR_INVALID_LEVEL = 124; + public static final int ERROR_DIR_NOT_EMPTY = 145; + public static final int ERROR_ALREADY_EXISTS = 183; + public static final int ERROR_DIRECTORY = 267; + public static final int ERROR_NOTIFY_ENUM_DIR = 1022; + public static final int ERROR_NONE_MAPPED = 1332; + public static final int ERROR_NOT_A_REPARSE_POINT = 4390; + public static final int ERROR_INVALID_REPARSE_DATA = 4392; + + // notify filters + public static final int FILE_NOTIFY_CHANGE_FILE_NAME = 0x00000001; + public static final int FILE_NOTIFY_CHANGE_DIR_NAME = 0x00000002; + public static final int FILE_NOTIFY_CHANGE_ATTRIBUTES = 0x00000004; + public static final int FILE_NOTIFY_CHANGE_SIZE = 0x00000008; + public static final int FILE_NOTIFY_CHANGE_LAST_WRITE = 0x00000010; + public static final int FILE_NOTIFY_CHANGE_LAST_ACCESS = 0x00000020; + public static final int FILE_NOTIFY_CHANGE_CREATION = 0x00000040; + public static final int FILE_NOTIFY_CHANGE_SECURITY = 0x00000100; + + // notify actions + public final static int FILE_ACTION_ADDED = 0x00000001; + public final static int FILE_ACTION_REMOVED = 0x00000002; + public final static int FILE_ACTION_MODIFIED = 0x00000003; + public final static int FILE_ACTION_RENAMED_OLD_NAME = 0x00000004; + public final static int FILE_ACTION_RENAMED_NEW_NAME = 0x00000005; + + // copy flags + public static final int COPY_FILE_FAIL_IF_EXISTS = 0x00000001; + public static final int COPY_FILE_COPY_SYMLINK = 0x00000800; + + // move flags + public static final int MOVEFILE_REPLACE_EXISTING = 0x00000001; + public static final int MOVEFILE_COPY_ALLOWED = 0x00000002; + + // drive types + public static final int DRIVE_UNKNOWN = 0; + public static final int DRIVE_NO_ROOT_DIR = 1; + public static final int DRIVE_REMOVABLE = 2; + public static final int DRIVE_FIXED = 3; + public static final int DRIVE_REMOTE = 4; + public static final int DRIVE_CDROM = 5; + public static final int DRIVE_RAMDISK = 6; + + // file security + public static final int OWNER_SECURITY_INFORMATION = 0x00000001; + public static final int GROUP_SECURITY_INFORMATION = 0x00000002; + public static final int DACL_SECURITY_INFORMATION = 0x00000004; + public static final int SACL_SECURITY_INFORMATION = 0x00000008; + + public static final int SidTypeUser = 1; + public static final int SidTypeGroup = 2; + public static final int SidTypeDomain = 3; + public static final int SidTypeAlias = 4; + public static final int SidTypeWellKnownGroup = 5; + public static final int SidTypeDeletedAccount = 6; + public static final int SidTypeInvalid = 7; + public static final int SidTypeUnknown = 8; + public static final int SidTypeComputer= 9; + + public static final byte ACCESS_ALLOWED_ACE_TYPE = 0x0; + public static final byte ACCESS_DENIED_ACE_TYPE = 0x1; + + public static final byte OBJECT_INHERIT_ACE = 0x1; + public static final byte CONTAINER_INHERIT_ACE = 0x2; + public static final byte NO_PROPAGATE_INHERIT_ACE = 0x4; + public static final byte INHERIT_ONLY_ACE = 0x8; + + public static final int DELETE = 0x00010000; + public static final int READ_CONTROL = 0x00020000; + public static final int WRITE_DAC = 0x00040000; + public static final int WRITE_OWNER = 0x00080000; + public static final int SYNCHRONIZE = 0x00100000; + + public static final int FILE_LIST_DIRECTORY = 0x0001; + public static final int FILE_READ_DATA = 0x0001; + public static final int FILE_WRITE_DATA = 0x0002; + public static final int FILE_APPEND_DATA = 0x0004; + public static final int FILE_READ_EA = 0x0008; + public static final int FILE_WRITE_EA = 0x0010; + public static final int FILE_EXECUTE = 0x0020; + public static final int FILE_DELETE_CHILD = 0x0040; + public static final int FILE_READ_ATTRIBUTES = 0x0080; + public static final int FILE_WRITE_ATTRIBUTES = 0x0100; + + // operating system security + public static final int TOKEN_DUPLICATE = 0x0002; + public static final int TOKEN_IMPERSONATE = 0x0004; + public static final int TOKEN_QUERY = 0x0008; + public static final int TOKEN_ADJUST_PRIVILEGES = 0x0020; + + public static final int SE_PRIVILEGE_ENABLED = 0x00000002; + + public static final int TokenUser = 1; + public static final int PROCESS_QUERY_INFORMATION = 0x0400; +} diff --git a/src/windows/classes/sun/nio/fs/WindowsDirectoryStream.java b/src/windows/classes/sun/nio/fs/WindowsDirectoryStream.java new file mode 100644 index 000000000..dc03cf0c4 --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsDirectoryStream.java @@ -0,0 +1,232 @@ +/* + * 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.*; +import java.util.Iterator; +import java.util.ConcurrentModificationException; +import java.util.NoSuchElementException; +import java.io.IOException; + +import static sun.nio.fs.WindowsNativeDispatcher.*; +import static sun.nio.fs.WindowsConstants.*; + +/** + * Windows implementation of DirectoryStream + */ + +class WindowsDirectoryStream + implements DirectoryStream +{ + private final WindowsPath dir; + private final DirectoryStream.Filter filter; + + // handle to directory + private final long handle; + // first entry in the directory + private final String firstName; + + private final Object closeLock = new Object(); + + // need closeLock to access these + private boolean isOpen = true; + private Iterator iterator; + + + WindowsDirectoryStream(WindowsPath dir, DirectoryStream.Filter filter) + throws IOException + { + this.dir = dir; + this.filter = filter; + + try { + // Need to append * or \* to match entries in directory. + String search = dir.getPathForWin32Calls(); + char last = search.charAt(search.length() -1); + if (last == ':' || last == '\\') { + search += "*"; + } else { + search += "\\*"; + } + + FirstFile first = FindFirstFile(search); + this.handle = first.handle(); + this.firstName = first.name(); + } catch (WindowsException x) { + if (x.lastError() == ERROR_DIRECTORY) { + throw new NotDirectoryException(dir.getPathForExceptionMessage()); + } + x.rethrowAsIOException(dir); + + // keep compiler happy + throw new AssertionError(); + } + } + + @Override + public void close() + throws IOException + { + synchronized (closeLock) { + if (!isOpen) + return; + isOpen = false; + } + try { + FindClose(handle); + } catch (WindowsException x) { + x.rethrowAsIOException(dir); + } + } + + @Override + public Iterator iterator() { + if (!isOpen) { + throw new IllegalStateException("Directory stream is closed"); + } + synchronized (this) { + if (iterator != null) + throw new IllegalStateException("Iterator already obtained"); + iterator = new WindowsDirectoryIterator(firstName); + return iterator; + } + } + + 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; + this.first = first; + } + + // applies filter and also ignores "." and ".." + private Path acceptEntry(String s) { + if (s.equals(".") || s.equals("..")) + return null; + Path entry = WindowsPath + .createFromNormalizedPath(dir.getFileSystem(), dir + "\\" + s); + if (filter.accept(entry)) { + return entry; + } else { + return null; + } + } + + // reads next directory entry + private Path readNextEntry() { + // handle first element returned by search + if (first != null) { + nextEntry = acceptEntry(first); + first = null; + if (nextEntry != null) + return nextEntry; + } + + String name = null; + for (;;) { + // synchronize on closeLock to prevent close while reading + synchronized (closeLock) { + if (!isOpen) + throwAsConcurrentModificationException(new + IllegalStateException("Directory stream is closed")); + try { + name = FindNextFile(handle); + } catch (WindowsException x) { + try { + x.rethrowAsIOException(dir); + } catch (IOException ioe) { + throwAsConcurrentModificationException(ioe); + } + } + } + + // EOF + if (name == null) + return null; + + Path entry = acceptEntry(name); + if (entry != null) + return entry; + } + } + + @Override + public synchronized boolean hasNext() { + 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; + nextEntry = null; + return prevEntry; + } + + @Override + public void remove() { + if (!isOpen) { + throw new IllegalStateException("Directory stream is closed"); + } + Path entry; + synchronized (this) { + if (prevEntry == null) + throw new IllegalStateException("no last element"); + entry = prevEntry; + prevEntry = null; + } + try { + entry.delete(true); + } catch (IOException ioe) { + throwAsConcurrentModificationException(ioe); + } catch (SecurityException se) { + throwAsConcurrentModificationException(se); + } + } + } +} diff --git a/src/windows/classes/sun/nio/fs/WindowsException.java b/src/windows/classes/sun/nio/fs/WindowsException.java new file mode 100644 index 000000000..7da722076 --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsException.java @@ -0,0 +1,109 @@ +/* + * 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.*; +import java.io.IOException; + +import static sun.nio.fs.WindowsConstants.*; + +/** + * Internal exception thrown when a Win32 calls fails. + */ + +class WindowsException extends Exception { + static final long serialVersionUID = 2765039493083748820L; + + private int lastError; + private String msg; + + WindowsException(int lastError) { + this.lastError = lastError; + this.msg = null; + } + + WindowsException(String msg) { + this.lastError = 0; + this.msg = msg; + } + + int lastError() { + return lastError; + } + + String errorString() { + if (msg == null) { + msg = WindowsNativeDispatcher.FormatMessage(lastError); + if (msg == null) { + msg = "Unknown error: 0x" + Integer.toHexString(lastError); + } + } + return msg; + } + + @Override + public String getMessage() { + return errorString(); + } + + private IOException translateToIOException(String file, String other) { + // not created with last error + if (lastError() == 0) + return new IOException(errorString()); + + // handle specific cases + if (lastError() == ERROR_FILE_NOT_FOUND || lastError() == ERROR_PATH_NOT_FOUND) + return new NoSuchFileException(file, other, null); + if (lastError() == ERROR_FILE_EXISTS || lastError() == ERROR_ALREADY_EXISTS) + return new FileAlreadyExistsException(file, other, null); + if (lastError() == ERROR_ACCESS_DENIED) + return new AccessDeniedException(file, other, null); + + // fallback to the more general exception + return new FileSystemException(file, other, errorString()); + } + + void rethrowAsIOException(String file) throws IOException { + IOException x = translateToIOException(file, null); + throw x; + } + + void rethrowAsIOException(WindowsPath file, WindowsPath other) throws IOException { + String a = (file == null) ? null : file.getPathForExceptionMessage(); + String b = (other == null) ? null : other.getPathForExceptionMessage(); + IOException x = translateToIOException(a, b); + throw x; + } + + void rethrowAsIOException(WindowsPath file) throws IOException { + rethrowAsIOException(file, null); + } + + IOException asIOException(WindowsPath file) { + return translateToIOException(file.getPathForExceptionMessage(), null); + } + +} diff --git a/src/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java b/src/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java new file mode 100644 index 000000000..39c34a10e --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsFileAttributeViews.java @@ -0,0 +1,296 @@ +/* + * 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.*; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.io.IOException; + +import static sun.nio.fs.WindowsNativeDispatcher.*; +import static sun.nio.fs.WindowsConstants.*; + +class WindowsFileAttributeViews { + + private static class Basic extends AbstractBasicFileAttributeView { + final WindowsPath file; + final boolean followLinks; + + Basic(WindowsPath file, boolean followLinks) { + this.file = file; + this.followLinks = followLinks; + } + + @Override + public WindowsFileAttributes readAttributes() throws IOException { + try { + return WindowsFileAttributes.get(file, followLinks); + } catch (WindowsException x) { + x.rethrowAsIOException(file); + return null; // keep compiler happy + } + } + + /** + * Parameter values in Windows times. + */ + void setFileTimes(long createTime, long lastAccessTime, long lastWriteTime) + throws IOException + { + long handle = -1L; + try { + int flags = FILE_FLAG_BACKUP_SEMANTICS; + if (!followLinks && file.getFileSystem().supportsLinks()) + flags |= FILE_FLAG_OPEN_REPARSE_POINT; + + handle = CreateFile(file.getPathForWin32Calls(), + FILE_WRITE_ATTRIBUTES, + (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), + OPEN_EXISTING, + flags); + } catch (WindowsException x) { + x.rethrowAsIOException(file); + } + + // update attributes + try { + SetFileTime(handle, createTime, lastAccessTime, lastWriteTime); + } catch (WindowsException x) { + x.rethrowAsIOException(file); + } finally { + CloseHandle(handle); + } + } + + @Override + public void setTimes(Long lastModifiedTime, + Long lastAccessTime, + Long createTime, + TimeUnit unit) throws IOException + { + file.checkWrite(); + + // if all null then do nothing + if (lastModifiedTime == null && lastAccessTime == null && + createTime == null) + { + // no effect + return; + } + + // null => no change + // -1 => change to current time + long now = System.currentTimeMillis(); + long modTime = 0L, accTime = 0L, crTime = 0L; + if (lastModifiedTime != null) { + if (lastModifiedTime < 0L) { + if (lastModifiedTime != -1L) + throw new IllegalArgumentException(); + modTime = now; + } else { + modTime = TimeUnit.MILLISECONDS.convert(lastModifiedTime, unit); + } + modTime = WindowsFileAttributes.toWindowsTime(modTime); + } + if (lastAccessTime != null) { + if (lastAccessTime < 0L) { + if (lastAccessTime != -1L) + throw new IllegalArgumentException(); + accTime = now; + } else { + accTime = TimeUnit.MILLISECONDS.convert(lastAccessTime, unit); + } + accTime = WindowsFileAttributes.toWindowsTime(accTime); + } + if (createTime != null) { + if (createTime < 0L) { + if (createTime != -1L) + throw new IllegalArgumentException(); + crTime = now; + } else { + crTime = TimeUnit.MILLISECONDS.convert(createTime, unit); + } + crTime = WindowsFileAttributes.toWindowsTime(crTime); + } + + setFileTimes(crTime, accTime, modTime); + } + } + + static class Dos extends Basic implements DosFileAttributeView { + private static final String READONLY_NAME = "readonly"; + private static final String ARCHIVE_NAME = "archive"; + private static final String SYSTEM_NAME = "system"; + private static final String HIDDEN_NAME = "hidden"; + private static final String ATTRIBUTES_NAME = "attributes"; + + Dos(WindowsPath file, boolean followLinks) { + super(file, followLinks); + } + + @Override + public String name() { + return "dos"; + } + + @Override + public Object getAttribute(String attribute) throws IOException { + if (attribute.equals(READONLY_NAME)) + return readAttributes().isReadOnly(); + if (attribute.equals(ARCHIVE_NAME)) + return readAttributes().isArchive(); + if (attribute.equals(SYSTEM_NAME)) + return readAttributes().isSystem(); + if (attribute.equals(HIDDEN_NAME)) + return readAttributes().isHidden(); + // implementation specific + if (attribute.equals(ATTRIBUTES_NAME)) + return readAttributes().attributes(); + return super.getAttribute(attribute); + } + + @Override + public void setAttribute(String attribute, Object value) + throws IOException + { + if (attribute.equals(READONLY_NAME)) { + setReadOnly((Boolean)value); + return; + } + if (attribute.equals(ARCHIVE_NAME)) { + setArchive((Boolean)value); + return; + } + if (attribute.equals(SYSTEM_NAME)) { + setSystem((Boolean)value); + return; + } + if (attribute.equals(HIDDEN_NAME)) { + setHidden((Boolean)value); + return; + } + super.setAttribute(attribute, value); + } + + @Override + public Map readAttributes(String first, String[] rest) + throws IOException + { + AttributesBuilder builder = AttributesBuilder.create(first, rest); + WindowsFileAttributes attrs = readAttributes(); + addBasicAttributesToBuilder(attrs, builder); + if (builder.match(READONLY_NAME)) + builder.add(READONLY_NAME, attrs.isReadOnly()); + if (builder.match(ARCHIVE_NAME)) + builder.add(ARCHIVE_NAME, attrs.isArchive()); + if (builder.match(SYSTEM_NAME)) + builder.add(SYSTEM_NAME, attrs.isSystem()); + if (builder.match(HIDDEN_NAME)) + builder.add(HIDDEN_NAME, attrs.isHidden()); + if (builder.match(ATTRIBUTES_NAME)) + builder.add(ATTRIBUTES_NAME, attrs.attributes()); + return builder.unmodifiableMap(); + } + + /** + * Update DOS attributes + */ + private void updateAttributes(int flag, boolean enable) + throws IOException + { + file.checkWrite(); + + // GetFileAttribtues & SetFileAttributes do not follow links so when + // following links we need the final target + String path = WindowsLinkSupport.getFinalPath(file, followLinks); + try { + int oldValue = GetFileAttributes(path); + int newValue = oldValue; + if (enable) { + newValue |= flag; + } else { + newValue &= ~flag; + } + if (newValue != oldValue) { + SetFileAttributes(path, newValue); + } + } catch (WindowsException x) { + // don't reveal target in exception + x.rethrowAsIOException(file); + } + } + + @Override + public void setReadOnly(boolean value) throws IOException { + updateAttributes(FILE_ATTRIBUTE_READONLY, value); + } + + @Override + public void setHidden(boolean value) throws IOException { + updateAttributes(FILE_ATTRIBUTE_HIDDEN, value); + } + + @Override + public void setArchive(boolean value) throws IOException { + updateAttributes(FILE_ATTRIBUTE_ARCHIVE, value); + } + + @Override + public void setSystem(boolean value) throws IOException { + updateAttributes(FILE_ATTRIBUTE_SYSTEM, value); + } + + // package-private + // Copy given attributes to the file. + void setAttributes(WindowsFileAttributes attrs) + throws IOException + { + // copy DOS attributes to target + int flags = 0; + if (attrs.isReadOnly()) flags |= FILE_ATTRIBUTE_READONLY; + if (attrs.isHidden()) flags |= FILE_ATTRIBUTE_HIDDEN; + if (attrs.isArchive()) flags |= FILE_ATTRIBUTE_ARCHIVE; + if (attrs.isSystem()) flags |= FILE_ATTRIBUTE_SYSTEM; + updateAttributes(flags, true); + + // copy file times to target - must be done after updating FAT attributes + // as otherwise the last modified time may be wrong. + setFileTimes( + WindowsFileAttributes.toWindowsTime(attrs.creationTime()), + WindowsFileAttributes.toWindowsTime(attrs.lastModifiedTime()), + WindowsFileAttributes.toWindowsTime(attrs.lastAccessTime())); + } + } + + static BasicFileAttributeView createBasicView(WindowsPath file, boolean followLinks) { + return new Basic(file, followLinks); + } + + static WindowsFileAttributeViews.Dos createDosView(WindowsPath file, boolean followLinks) { + return new Dos(file, followLinks); + } +} diff --git a/src/windows/classes/sun/nio/fs/WindowsFileAttributes.java b/src/windows/classes/sun/nio/fs/WindowsFileAttributes.java new file mode 100644 index 000000000..a0a403682 --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsFileAttributes.java @@ -0,0 +1,403 @@ +/* + * 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.*; +import java.util.concurrent.TimeUnit; +import java.security.AccessController; +import sun.misc.Unsafe; +import sun.security.action.GetPropertyAction; + +import static sun.nio.fs.WindowsNativeDispatcher.*; +import static sun.nio.fs.WindowsConstants.*; + +/** + * Windows implementation of DosFileAttributes/BasicFileAttributes + */ + +class WindowsFileAttributes + implements DosFileAttributes +{ + private static final Unsafe unsafe = Unsafe.getUnsafe(); + + /* + * typedef struct _BY_HANDLE_FILE_INFORMATION { + * DWORD dwFileAttributes; + * FILETIME ftCreationTime; + * FILETIME ftLastAccessTime; + * FILETIME ftLastWriteTime; + * DWORD dwVolumeSerialNumber; + * DWORD nFileSizeHigh; + * DWORD nFileSizeLow; + * DWORD nNumberOfLinks; + * DWORD nFileIndexHigh; + * DWORD nFileIndexLow; + * } BY_HANDLE_FILE_INFORMATION; + */ + private static final short SIZEOF_FILE_INFORMATION = 52; + private static final short OFFSETOF_FILE_INFORMATION_ATTRIBUTES = 0; + private static final short OFFSETOF_FILE_INFORMATION_CREATETIME = 4; + private static final short OFFSETOF_FILE_INFORMATION_LASTACCESSTIME = 12; + private static final short OFFSETOF_FILE_INFORMATION_LASTWRITETIME = 20; + private static final short OFFSETOF_FILE_INFORMATION_VOLSERIALNUM = 28; + private static final short OFFSETOF_FILE_INFORMATION_SIZEHIGH = 32; + private static final short OFFSETOF_FILE_INFORMATION_SIZELOW = 36; + private static final short OFFSETOF_FILE_INFORMATION_NUMLINKS = 40; + private static final short OFFSETOF_FILE_INFORMATION_INDEXHIGH = 44; + private static final short OFFSETOF_FILE_INFORMATION_INDEXLOW = 48; + + /* + * typedef struct _WIN32_FILE_ATTRIBUTE_DATA { + * DWORD dwFileAttributes; + * FILETIME ftCreationTime; + * FILETIME ftLastAccessTime; + * FILETIME ftLastWriteTime; + * DWORD nFileSizeHigh; + * DWORD nFileSizeLow; + * } WIN32_FILE_ATTRIBUTE_DATA; + */ + private static final short SIZEOF_FILE_ATTRIBUTE_DATA = 36; + private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES = 0; + private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_CREATETIME = 4; + private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_LASTACCESSTIME = 12; + private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_LASTWRITETIME = 20; + private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_SIZEHIGH = 28; + private static final short OFFSETOF_FILE_ATTRIBUTE_DATA_SIZELOW = 32; + + // indicates if accurate metadata is required (interesting on NTFS only) + private static final boolean ensureAccurateMetadata; + static { + String propValue = AccessController.doPrivileged( + new GetPropertyAction("sun.nio.fs.ensureAccurateMetadata", "false")); + ensureAccurateMetadata = (propValue.length() == 0) ? + true : Boolean.valueOf(propValue); + } + + // attributes + private final int fileAttrs; + private final long creationTime; + private final long lastAccessTime; + private final long lastWriteTime; + private final long size; + private final int reparseTag; + + // additional attributes when using GetFileInformationByHandle + private final int linkCount; + private final int volSerialNumber; + private final int fileIndexHigh; + private final int fileIndexLow; + + /** + * Convert 64-bit value representing the number of 100-nanosecond intervals + * since January 1, 1601 to java time. + */ + private static long toJavaTime(long time) { + time /= 10000L; + time -= 11644473600000L; + return time; + } + + /** + * Convert java time to 64-bit value representing the number of 100-nanosecond + * intervals since January 1, 1601. + */ + static long toWindowsTime(long time) { + time += 11644473600000L; + time *= 10000L; + return time; + } + + /** + * Initialize a new instance of this class + */ + private WindowsFileAttributes(int fileAttrs, + long creationTime, + long lastAccessTime, + long lastWriteTime, + long size, + int reparseTag, + int linkCount, + int volSerialNumber, + int fileIndexHigh, + int fileIndexLow) + { + this.fileAttrs = fileAttrs; + this.creationTime = creationTime; + this.lastAccessTime = lastAccessTime; + this.lastWriteTime = lastWriteTime; + this.size = size; + this.reparseTag = reparseTag; + this.linkCount = linkCount; + this.volSerialNumber = volSerialNumber; + this.fileIndexHigh = fileIndexHigh; + this.fileIndexLow = fileIndexLow; + } + + /** + * Create a WindowsFileAttributes from a BY_HANDLE_FILE_INFORMATION structure + */ + private static WindowsFileAttributes fromFileInformation(long address, int reparseTag) { + int fileAttrs = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_ATTRIBUTES); + long creationTime = + toJavaTime(unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_CREATETIME)); + long lastAccessTime = + toJavaTime(unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_LASTACCESSTIME)); + long lastWriteTime = + toJavaTime(unsafe.getLong(address + OFFSETOF_FILE_INFORMATION_LASTWRITETIME)); + long size = ((long)(unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_SIZEHIGH)) << 32) + + (unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_SIZELOW) & 0xFFFFFFFFL); + int linkCount = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_NUMLINKS); + int volSerialNumber = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_VOLSERIALNUM); + int fileIndexHigh = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_INDEXHIGH); + int fileIndexLow = unsafe.getInt(address + OFFSETOF_FILE_INFORMATION_INDEXLOW); + return new WindowsFileAttributes(fileAttrs, + creationTime, + lastAccessTime, + lastWriteTime, + size, + reparseTag, + linkCount, + volSerialNumber, + fileIndexHigh, + fileIndexLow); + } + + /** + * Create a WindowsFileAttributes from a WIN32_FILE_ATTRIBUTE_DATA structure + */ + private static WindowsFileAttributes fromFileAttributeData(long address, int reparseTag) { + int fileAttrs = unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES); + long creationTime = + toJavaTime(unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_CREATETIME)); + long lastAccessTime = + toJavaTime(unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_LASTACCESSTIME)); + long lastWriteTime = + toJavaTime(unsafe.getLong(address + OFFSETOF_FILE_ATTRIBUTE_DATA_LASTWRITETIME)); + long size = ((long)(unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_SIZEHIGH)) << 32) + + (unsafe.getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_SIZELOW) & 0xFFFFFFFFL); + return new WindowsFileAttributes(fileAttrs, + creationTime, + lastAccessTime, + lastWriteTime, + size, + reparseTag, + 1, // linkCount + 0, // volSerialNumber + 0, // fileIndexHigh + 0); // fileIndexLow + } + + /** + * Reads the attributes of an open file + */ + static WindowsFileAttributes readAttributes(long handle) + throws WindowsException + { + NativeBuffer buffer = NativeBuffers + .getNativeBuffer(SIZEOF_FILE_INFORMATION); + try { + long address = buffer.address(); + GetFileInformationByHandle(handle, address); + + // if file is a reparse point then read the tag + int reparseTag = 0; + int fileAttrs = unsafe + .getInt(address + OFFSETOF_FILE_INFORMATION_ATTRIBUTES); + if ((fileAttrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0) { + int size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE; + NativeBuffer reparseBuffer = NativeBuffers.getNativeBuffer(size); + try { + DeviceIoControlGetReparsePoint(handle, reparseBuffer.address(), size); + reparseTag = (int)unsafe.getLong(reparseBuffer.address()); + } finally { + reparseBuffer.release(); + } + } + + return fromFileInformation(address, reparseTag); + } finally { + buffer.release(); + } + } + + /** + * Returns attributes of given file. + */ + static WindowsFileAttributes get(WindowsPath path, boolean followLinks) + throws WindowsException + { + if (!ensureAccurateMetadata) { + NativeBuffer buffer = + NativeBuffers.getNativeBuffer(SIZEOF_FILE_ATTRIBUTE_DATA); + try { + long address = buffer.address(); + GetFileAttributesEx(path.getPathForWin32Calls(), address); + // if reparse point then file may be a sym link; otherwise + // just return the attributes + int fileAttrs = unsafe + .getInt(address + OFFSETOF_FILE_ATTRIBUTE_DATA_ATTRIBUTES); + if ((fileAttrs & FILE_ATTRIBUTE_REPARSE_POINT) == 0) + return fromFileAttributeData(address, 0); + } finally { + buffer.release(); + } + } + + // file is reparse point so need to open file to get attributes + long handle = path.openForReadAttributeAccess(followLinks); + try { + return readAttributes(handle); + } finally { + CloseHandle(handle); + } + } + + /** + * Returns true if the attribtues are of the same file - both files must + * be open. + */ + static boolean isSameFile(WindowsFileAttributes attrs1, + WindowsFileAttributes attrs2) + { + // volume serial number and file index must be the same + return (attrs1.volSerialNumber == attrs2.volSerialNumber) && + (attrs1.fileIndexHigh == attrs2.fileIndexHigh) && + (attrs1.fileIndexLow == attrs2.fileIndexLow); + } + + // package-private + int attributes() { + return fileAttrs; + } + + int volSerialNumber() { + if (volSerialNumber == 0) + throw new AssertionError("Should not get here"); + return volSerialNumber; + } + + int fileIndexHigh() { + if (volSerialNumber == 0) + throw new AssertionError("Should not get here"); + return fileIndexHigh; + } + + int fileIndexLow() { + if (volSerialNumber == 0) + throw new AssertionError("Should not get here"); + return fileIndexLow; + } + + @Override + public long size() { + return size; + } + + @Override + public long lastModifiedTime() { + return (lastWriteTime >= 0L) ? lastWriteTime : 0L; + } + + @Override + public long lastAccessTime() { + return (lastAccessTime >= 0L) ? lastAccessTime : 0L; + } + + @Override + public long creationTime() { + return (creationTime >= 0L) ? creationTime : 0L; + } + + @Override + public TimeUnit resolution() { + return TimeUnit.MILLISECONDS; + } + + @Override + public int linkCount() { + return linkCount; + } + + @Override + public Object fileKey() { + return null; + } + + // package private + boolean isReparsePoint() { + return (fileAttrs & FILE_ATTRIBUTE_REPARSE_POINT) != 0; + } + + boolean isDirectoryLink() { + return isSymbolicLink() && ((fileAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0); + } + + @Override + public boolean isSymbolicLink() { + return reparseTag == IO_REPARSE_TAG_SYMLINK; + } + + @Override + public boolean isDirectory() { + // ignore FILE_ATTRIBUTE_DIRECTORY attribute if file is a sym link + if (isSymbolicLink()) + return false; + return ((fileAttrs & FILE_ATTRIBUTE_DIRECTORY) != 0); + } + + @Override + public boolean isOther() { + if (isSymbolicLink()) + return false; + // return true if device or reparse point + return ((fileAttrs & (FILE_ATTRIBUTE_DEVICE | FILE_ATTRIBUTE_REPARSE_POINT)) != 0); + } + + @Override + public boolean isRegularFile() { + return !isSymbolicLink() && !isDirectory() && !isOther(); + } + + @Override + public boolean isReadOnly() { + return (fileAttrs & FILE_ATTRIBUTE_READONLY) != 0; + } + + @Override + public boolean isHidden() { + return (fileAttrs & FILE_ATTRIBUTE_HIDDEN) != 0; + } + + @Override + public boolean isArchive() { + return (fileAttrs & FILE_ATTRIBUTE_ARCHIVE) != 0; + } + + @Override + public boolean isSystem() { + return (fileAttrs & FILE_ATTRIBUTE_SYSTEM) != 0; + } +} diff --git a/src/windows/classes/sun/nio/fs/WindowsFileCopy.java b/src/windows/classes/sun/nio/fs/WindowsFileCopy.java new file mode 100644 index 000000000..69a41262a --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsFileCopy.java @@ -0,0 +1,519 @@ +/* + * 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.*; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import com.sun.nio.file.ExtendedCopyOption; + +import static sun.nio.fs.WindowsNativeDispatcher.*; +import static sun.nio.fs.WindowsConstants.*; + +/** + * Utility methods for copying and moving files. + */ + +class WindowsFileCopy { + private WindowsFileCopy() { + } + + /** + * Copy file from source to target + */ + static void copy(final WindowsPath source, + final WindowsPath target, + CopyOption... options) + throws IOException + { + // map options + boolean replaceExisting = false; + boolean copyAttributes = false; + boolean followLinks = true; + boolean interruptible = false; + for (CopyOption option: options) { + if (option == StandardCopyOption.REPLACE_EXISTING) { + replaceExisting = true; + continue; + } + if (option == LinkOption.NOFOLLOW_LINKS) { + followLinks = false; + continue; + } + if (option == StandardCopyOption.COPY_ATTRIBUTES) { + copyAttributes = true; + continue; + } + if (option == ExtendedCopyOption.INTERRUPTIBLE) { + interruptible = true; + continue; + } + if (option == null) + throw new NullPointerException(); + throw new UnsupportedOperationException("Unsupported copy option"); + } + + // check permissions. If the source file is a symbolic link then + // later we must also check LinkPermission + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + source.checkRead(); + target.checkWrite(); + } + + // get attributes of source file + // attempt to get attributes of target file + // if both files are the same there is nothing to do + // if target exists and !replace then throw exception + + WindowsFileAttributes sourceAttrs = null; + WindowsFileAttributes targetAttrs = null; + + long sourceHandle = 0L; + try { + sourceHandle = source.openForReadAttributeAccess(followLinks); + } catch (WindowsException x) { + x.rethrowAsIOException(source); + } + try { + // source attributes + try { + sourceAttrs = WindowsFileAttributes.readAttributes(sourceHandle); + } catch (WindowsException x) { + x.rethrowAsIOException(source); + } + + // open target (don't follow links) + long targetHandle = 0L; + try { + targetHandle = target.openForReadAttributeAccess(false); + try { + targetAttrs = WindowsFileAttributes.readAttributes(targetHandle); + + // if both files are the same then nothing to do + if (WindowsFileAttributes.isSameFile(sourceAttrs, targetAttrs)) { + return; + } + + // can't replace file + if (!replaceExisting) { + throw new FileAlreadyExistsException( + target.getPathForExceptionMessage()); + } + + } finally { + CloseHandle(targetHandle); + } + } catch (WindowsException x) { + // ignore + } + + } finally { + CloseHandle(sourceHandle); + } + + // if source file is a symbolic link then we must check for LinkPermission + if (sm != null && sourceAttrs.isSymbolicLink()) { + sm.checkPermission(new LinkPermission("symbolic")); + } + + final String sourcePath = asWin32Path(source); + final String targetPath = asWin32Path(target); + + // if target exists then delete it. + if (targetAttrs != null) { + try { + if (targetAttrs.isDirectory() || targetAttrs.isDirectoryLink()) { + RemoveDirectory(targetPath); + } else { + DeleteFile(targetPath); + } + } catch (WindowsException x) { + if (targetAttrs.isDirectory()) { + // ERROR_ALREADY_EXISTS is returned when attempting to delete + // non-empty directory on SAMBA servers. + if (x.lastError() == ERROR_DIR_NOT_EMPTY || + x.lastError() == ERROR_ALREADY_EXISTS) + { + throw new FileAlreadyExistsException( + target.getPathForExceptionMessage()); + } + } + x.rethrowAsIOException(target); + } + } + + // Use CopyFileEx if the file is not a directory or junction + if (!sourceAttrs.isDirectory() && !sourceAttrs.isDirectoryLink()) { + final int flags = + (source.getFileSystem().supportsLinks() && !followLinks) ? + COPY_FILE_COPY_SYMLINK : 0; + + if (interruptible) { + // interruptible copy + Cancellable copyTask = new Cancellable() { + @Override + public int cancelValue() { + return 1; // TRUE + } + @Override + public void implRun() throws IOException { + try { + CopyFileEx(sourcePath, targetPath, flags, + addressToPollForCancel()); + } catch (WindowsException x) { + x.rethrowAsIOException(source, target); + } + } + }; + try { + Cancellable.runInterruptibly(copyTask); + } catch (ExecutionException e) { + Throwable t = e.getCause(); + if (t instanceof IOException) + throw (IOException)t; + throw new IOException(t); + } + } else { + // non-interruptible copy + try { + CopyFileEx(sourcePath, targetPath, flags, 0L); + } catch (WindowsException x) { + x.rethrowAsIOException(source, target); + } + } + if (copyAttributes) { + // CopyFileEx does not copy security attributes + try { + copySecurityAttributes(source, target, followLinks); + } catch (IOException x) { + // ignore + } + } + return; + } + + // copy directory or directory junction + try { + if (sourceAttrs.isDirectory()) { + CreateDirectory(targetPath, 0L); + } else { + String linkTarget = WindowsLinkSupport.readLink(source); + int flags = SYMBOLIC_LINK_FLAG_DIRECTORY; + CreateSymbolicLink(targetPath, + addPrefixIfNeeded(linkTarget), + flags); + } + } catch (WindowsException x) { + x.rethrowAsIOException(target); + } + if (copyAttributes) { + // copy DOS/timestamps attributes + WindowsFileAttributeViews.Dos view = + WindowsFileAttributeViews.createDosView(target, false); + try { + view.setAttributes(sourceAttrs); + } catch (IOException x) { + if (sourceAttrs.isDirectory()) { + try { + RemoveDirectory(targetPath); + } catch (WindowsException ignore) { } + } + } + + // copy security attributes. If this fail it doesn't cause the move + // to fail. + try { + copySecurityAttributes(source, target, followLinks); + } catch (IOException ignore) { } + } + } + + /** + * Move file from source to target + */ + static void move(WindowsPath source, WindowsPath target, CopyOption... options) + throws IOException + { + // map options + boolean atomicMove = false; + boolean replaceExisting = false; + for (CopyOption option: options) { + if (option == StandardCopyOption.ATOMIC_MOVE) { + atomicMove = true; + continue; + } + if (option == StandardCopyOption.REPLACE_EXISTING) { + replaceExisting = true; + continue; + } + if (option == LinkOption.NOFOLLOW_LINKS) { + // ignore + continue; + } + if (option == null) throw new NullPointerException(); + throw new UnsupportedOperationException("Unsupported copy option"); + } + + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + source.checkWrite(); + target.checkWrite(); + } + + final String sourcePath = asWin32Path(source); + final String targetPath = asWin32Path(target); + + // atomic case + if (atomicMove) { + try { + MoveFileEx(sourcePath, targetPath, MOVEFILE_REPLACE_EXISTING); + } catch (WindowsException x) { + if (x.lastError() == ERROR_NOT_SAME_DEVICE) { + throw new AtomicMoveNotSupportedException( + source.getPathForExceptionMessage(), + target.getPathForExceptionMessage(), + x.errorString()); + } + x.rethrowAsIOException(source, target); + } + return; + } + + // get attributes of source file + // attempt to get attributes of target file + // if both files are the same there is nothing to do + // if target exists and !replace then throw exception + + WindowsFileAttributes sourceAttrs = null; + WindowsFileAttributes targetAttrs = null; + + long sourceHandle = 0L; + try { + sourceHandle = source.openForReadAttributeAccess(false); + } catch (WindowsException x) { + x.rethrowAsIOException(source); + } + try { + // source attributes + try { + sourceAttrs = WindowsFileAttributes.readAttributes(sourceHandle); + } catch (WindowsException x) { + x.rethrowAsIOException(source); + } + + // open target (don't follow links) + long targetHandle = 0L; + try { + targetHandle = target.openForReadAttributeAccess(false); + try { + targetAttrs = WindowsFileAttributes.readAttributes(targetHandle); + + // if both files are the same then nothing to do + if (WindowsFileAttributes.isSameFile(sourceAttrs, targetAttrs)) { + return; + } + + // can't replace file + if (!replaceExisting) { + throw new FileAlreadyExistsException( + target.getPathForExceptionMessage()); + } + + } finally { + CloseHandle(targetHandle); + } + } catch (WindowsException x) { + // ignore + } + + } finally { + CloseHandle(sourceHandle); + } + + // if target exists then delete it. + if (targetAttrs != null) { + try { + if (targetAttrs.isDirectory() || targetAttrs.isDirectoryLink()) { + RemoveDirectory(targetPath); + } else { + DeleteFile(targetPath); + } + } catch (WindowsException x) { + if (targetAttrs.isDirectory()) { + // ERROR_ALREADY_EXISTS is returned when attempting to delete + // non-empty directory on SAMBA servers. + if (x.lastError() == ERROR_DIR_NOT_EMPTY || + x.lastError() == ERROR_ALREADY_EXISTS) + { + throw new FileAlreadyExistsException( + target.getPathForExceptionMessage()); + } + } + x.rethrowAsIOException(target); + } + } + + // first try MoveFileEx (no options). If target is on same volume then + // all attributes (including security attributes) are preserved. + try { + MoveFileEx(sourcePath, targetPath, 0); + return; + } catch (WindowsException x) { + if (x.lastError() != ERROR_NOT_SAME_DEVICE) + x.rethrowAsIOException(source, target); + } + + // target is on different volume so use MoveFileEx with copy option + if (!sourceAttrs.isDirectory() && !sourceAttrs.isDirectoryLink()) { + try { + MoveFileEx(sourcePath, targetPath, MOVEFILE_COPY_ALLOWED); + } catch (WindowsException x) { + x.rethrowAsIOException(source, target); + } + // MoveFileEx does not copy security attributes when moving + // across volumes. + try { + copySecurityAttributes(source, target, false); + } catch (IOException x) { + // ignore + } + return; + } + + // moving directory or directory-link to another file system + assert sourceAttrs.isDirectory() || sourceAttrs.isDirectoryLink(); + + // create new directory or directory junction + try { + if (sourceAttrs.isDirectory()) { + CreateDirectory(targetPath, 0L); + } else { + String linkTarget = WindowsLinkSupport.readLink(source); + CreateSymbolicLink(targetPath, + addPrefixIfNeeded(linkTarget), + SYMBOLIC_LINK_FLAG_DIRECTORY); + } + } catch (WindowsException x) { + x.rethrowAsIOException(target); + } + + // copy timestamps/DOS attributes + WindowsFileAttributeViews.Dos view = + WindowsFileAttributeViews.createDosView(target, false); + try { + view.setAttributes(sourceAttrs); + } catch (IOException x) { + // rollback + try { + RemoveDirectory(targetPath); + } catch (WindowsException ignore) { } + throw x; + } + + // copy security attributes. If this fails it doesn't cause the move + // to fail. + try { + copySecurityAttributes(source, target, false); + } catch (IOException ignore) { } + + // delete source + try { + RemoveDirectory(sourcePath); + } catch (WindowsException x) { + // rollback + try { + RemoveDirectory(targetPath); + } catch (WindowsException ignore) { } + // ERROR_ALREADY_EXISTS is returned when attempting to delete + // non-empty directory on SAMBA servers. + if (x.lastError() == ERROR_DIR_NOT_EMPTY || + x.lastError() == ERROR_ALREADY_EXISTS) + { + throw new DirectoryNotEmptyException( + target.getPathForExceptionMessage()); + } + x.rethrowAsIOException(source); + } + } + + + private static String asWin32Path(WindowsPath path) throws IOException { + try { + return path.getPathForWin32Calls(); + } catch (WindowsException x) { + x.rethrowAsIOException(path); + return null; + } + } + + /** + * Copy DACL/owner/group from source to target + */ + private static void copySecurityAttributes(WindowsPath source, + WindowsPath target, + boolean followLinks) + throws IOException + { + String path = WindowsLinkSupport.getFinalPath(source, followLinks); + + // may need SeRestorePrivilege to set file owner + WindowsSecurity.Privilege priv = + WindowsSecurity.enablePrivilege("SeRestorePrivilege"); + try { + int request = (DACL_SECURITY_INFORMATION | + OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION); + NativeBuffer buffer = + WindowsAclFileAttributeView.getFileSecurity(path, request); + try { + try { + SetFileSecurity(target.getPathForWin32Calls(), request, + buffer.address()); + } catch (WindowsException x) { + x.rethrowAsIOException(target); + } + } finally { + buffer.release(); + } + } finally { + priv.drop(); + } + } + + /** + * Add long path prefix to path if required + */ + private static String addPrefixIfNeeded(String path) { + if (path.length() > 248) { + if (path.startsWith("\\\\")) { + path = "\\\\?\\UNC" + path.substring(1, path.length()); + } else { + path = "\\\\?\\" + path; + } + } + return path; + } +} diff --git a/src/windows/classes/sun/nio/fs/WindowsFileStore.java b/src/windows/classes/sun/nio/fs/WindowsFileStore.java new file mode 100644 index 000000000..5d3a0af25 --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsFileStore.java @@ -0,0 +1,337 @@ +/* + * 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.*; +import java.nio.file.attribute.*; +import java.util.*; +import java.io.IOException; + +import static sun.nio.fs.WindowsConstants.*; +import static sun.nio.fs.WindowsNativeDispatcher.*; + +/** + * Windows implementation of FileStore. + */ + +class WindowsFileStore + extends FileStore +{ + private final String root; + private final VolumeInformation volInfo; + private final int volType; + private final String displayName; // returned by toString + + private WindowsFileStore(String root) throws WindowsException { + assert root.charAt(root.length()-1) == '\\'; + this.root = root; + this.volInfo = GetVolumeInformation(root); + this.volType = GetDriveType(root); + + // file store "display name" is the volume name if available + String vol = volInfo.volumeName(); + if (vol.length() > 0) { + this.displayName = vol; + } else { + // TBD - should we map all types? Does this need to be localized? + this.displayName = (volType == DRIVE_REMOVABLE) ? "Removable Disk" : ""; + } + } + + static WindowsFileStore create(String root, boolean ignoreNotReady) + throws IOException + { + try { + return new WindowsFileStore(root); + } catch (WindowsException x) { + if (ignoreNotReady && x.lastError() == ERROR_NOT_READY) + return null; + x.rethrowAsIOException(root); + return null; // keep compiler happy + } + } + + static WindowsFileStore create(WindowsPath file) throws IOException { + try { + // if the file is a link then GetVolumePathName returns the + // volume that the link is on so we need to call it with the + // final target + String target; + if (file.getFileSystem().supportsLinks()) { + target = WindowsLinkSupport.getFinalPath(file, true); + } else { + // file must exist + WindowsFileAttributes.get(file, true); + target = file.getPathForWin32Calls(); + } + String root = GetVolumePathName(target); + return new WindowsFileStore(root); + } catch (WindowsException x) { + x.rethrowAsIOException(file); + return null; // keep compiler happy + } + } + + VolumeInformation volumeInformation() { + return volInfo; + } + + int volumeType() { + return volType; + } + + @Override + public String name() { + return volInfo.volumeName(); // "SYSTEM", "DVD-RW", ... + } + + @Override + public String type() { + return volInfo.fileSystemName(); // "FAT", "NTFS", ... + } + + @Override + public boolean isReadOnly() { + return ((volInfo.flags() & FILE_READ_ONLY_VOLUME) != 0); + } + + @Override + @SuppressWarnings("unchecked") + public V getFileStoreAttributeView(Class view) { + if (view == FileStoreSpaceAttributeView.class) + return (V) new WindowsFileStoreAttributeView(this); + return (V) null; + } + + @Override + public FileStoreAttributeView getFileStoreAttributeView(String name) { + if (name.equals("space")) + return new WindowsFileStoreAttributeView(this); + if (name.equals("volume")) + return new VolumeFileStoreAttributeView(this); + return null; + } + + @Override + public boolean supportsFileAttributeView(Class type) { + if (type == BasicFileAttributeView.class) + return true; + if (type == AclFileAttributeView.class || type == FileOwnerAttributeView.class) + return ((volInfo.flags() & FILE_PERSISTENT_ACLS) != 0); + if (type == UserDefinedFileAttributeView.class) + return ((volInfo.flags() & FILE_NAMED_STREAMS) != 0); + return false; + } + + @Override + public boolean supportsFileAttributeView(String name) { + if (name.equals("basic") || name.equals("dos")) + return true; + if (name.equals("acl")) + return supportsFileAttributeView(AclFileAttributeView.class); + if (name.equals("owner")) + return supportsFileAttributeView(FileOwnerAttributeView.class); + if (name.equals("xattr")) + return supportsFileAttributeView(UserDefinedFileAttributeView.class); + return false; + } + + @Override + public boolean equals(Object ob) { + if (ob == this) + return true; + if (!(ob instanceof WindowsFileStore)) + return false; + WindowsFileStore other = (WindowsFileStore)ob; + return this.volInfo.volumeSerialNumber() == other.volInfo.volumeSerialNumber(); + } + + @Override + public int hashCode() { + // reveals VSN without permission check - okay? + return volInfo.volumeSerialNumber(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(displayName); + if (sb.length() > 0) + sb.append(" "); + sb.append("("); + // drop trailing slash + sb.append(root.subSequence(0, root.length()-1)); + sb.append(")"); + return sb.toString(); + } + + static class WindowsFileStoreAttributeView + extends AbstractFileStoreSpaceAttributeView + { + private final WindowsFileStore fs; + + WindowsFileStoreAttributeView(WindowsFileStore fs) { + this.fs = fs; + } + + @Override + public FileStoreSpaceAttributes readAttributes() + throws IOException + { + // read the free space info + DiskFreeSpace info = null; + try { + info = GetDiskFreeSpaceEx(fs.root); + } catch (WindowsException x) { + x.rethrowAsIOException(fs.root); + } + + final DiskFreeSpace result = info; + return new FileStoreSpaceAttributes() { + @Override + public long totalSpace() { + return result.totalNumberOfBytes(); + } + @Override + public long usableSpace() { + return result.freeBytesAvailable(); + } + @Override + public long unallocatedSpace() { + return result.totalNumberOfFreeBytes(); + } + }; + } + } + + /** + * Windows-specific attribute view to allow access to volume information. + */ + static class VolumeFileStoreAttributeView + implements FileStoreAttributeView + { + private static final String VSN_NAME = "vsn"; + private static final String COMPRESSED_NAME = "compressed"; + private static final String REMOVABLE_NAME = "removable"; + private static final String CDROM_NAME = "cdrom"; + + private final WindowsFileStore fs; + + VolumeFileStoreAttributeView(WindowsFileStore fs) { + this.fs = fs; + } + + @Override + public String name() { + return "volume"; + } + + private int vsn() { + return fs.volumeInformation().volumeSerialNumber(); + } + + private boolean isCompressed() { + return (fs.volumeInformation().flags() & + FILE_VOLUME_IS_COMPRESSED) > 0; + } + + private boolean isRemovable() { + return fs.volumeType() == DRIVE_REMOVABLE; + } + + private boolean isCdrom() { + return fs.volumeType() == DRIVE_CDROM; + } + + @Override + public Object getAttribute(String attribute) throws IOException { + if (attribute.equals(VSN_NAME)) + return vsn(); + if (attribute.equals(COMPRESSED_NAME)) + return isCompressed(); + if (attribute.equals(REMOVABLE_NAME)) + return isRemovable(); + if (attribute.equals(CDROM_NAME)) + return isCdrom(); + return null; + } + + @Override + public void setAttribute(String attribute, Object value) + throws IOException + { + throw new UnsupportedOperationException(); + } + + @Override + public Map readAttributes(String first, String... rest) + throws IOException + { + boolean all = false; + boolean vsn = false; + boolean compressed = false; + boolean removable = false; + boolean cdrom = false; + + if (first.equals(VSN_NAME)) vsn = true; + else if (first.equals(COMPRESSED_NAME)) compressed = true; + else if (first.equals(REMOVABLE_NAME)) removable = true; + else if (first.equals(CDROM_NAME)) cdrom = true; + else if (first.equals("*")) all = true; + + if (!all) { + for (String attribute: rest) { + if (attribute.equals("*")) { + all = true; + break; + } + if (attribute.equals(VSN_NAME)) { + vsn = true; + continue; + } + if (attribute.equals(COMPRESSED_NAME)) { + compressed = true; + continue; + } + if (attribute.equals(REMOVABLE_NAME)) { + removable = true; + continue; + } + } + } + + Map result = new HashMap(); + if (all || vsn) + result.put(VSN_NAME, vsn()); + if (all || compressed) + result.put(COMPRESSED_NAME, isCompressed()); + if (all || removable) + result.put(REMOVABLE_NAME, isRemovable()); + if (all || cdrom) + result.put(CDROM_NAME, isCdrom()); + return result; + } + } +} diff --git a/src/windows/classes/sun/nio/fs/WindowsFileSystem.java b/src/windows/classes/sun/nio/fs/WindowsFileSystem.java new file mode 100644 index 000000000..624a1df5c --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsFileSystem.java @@ -0,0 +1,319 @@ +/* + * 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.*; +import java.nio.file.attribute.*; +import java.nio.file.spi.*; +import java.util.*; +import java.util.regex.Pattern; +import java.io.IOException; +import java.security.AccessController; +import java.security.PrivilegedAction; +import sun.security.action.GetPropertyAction; + +class WindowsFileSystem + extends FileSystem +{ + private final WindowsFileSystemProvider provider; + + // default directory (is absolute), and default root + private final String defaultDirectory; + private final String defaultRoot; + + private final boolean supportsLinks; + private final boolean supportsStreamEnumeration; + + // package-private + WindowsFileSystem(WindowsFileSystemProvider provider, + String dir) + { + this.provider = provider; + + // parse default directory and check it is absolute + WindowsPathParser.Result result = WindowsPathParser.parse(dir); + + if (result.type() != WindowsPathType.ABSOLUTE) + throw new AssertionError("Default directory must be absolute/non-UNC"); + this.defaultDirectory = result.path(); + this.defaultRoot = result.root(); + + PrivilegedAction pa = new GetPropertyAction("os.version"); + String osversion = AccessController.doPrivileged(pa); + String[] vers = osversion.split("\\.", 0); + int major = Integer.parseInt(vers[0]); + int minor = Integer.parseInt(vers[1]); + + // symbolic links available on Vista and newer + supportsLinks = (major >= 6); + + // enumeration of data streams available on Windows Server 2003 and newer + supportsStreamEnumeration = (major >= 6) || (major == 5 && minor >= 2); + } + + // package-private + String defaultDirectory() { + return defaultDirectory; + } + + String defaultRoot() { + return defaultRoot; + } + + boolean supportsLinks() { + return supportsLinks; + } + + boolean supportsStreamEnumeration() { + return supportsStreamEnumeration; + } + + @Override + public FileSystemProvider provider() { + return provider; + } + + @Override + public String getSeparator() { + return "\\"; + } + + @Override + public boolean isOpen() { + return true; + } + + @Override + public boolean isReadOnly() { + return false; + } + + @Override + public void close() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public Iterable getRootDirectories() { + int drives = 0; + try { + drives = WindowsNativeDispatcher.GetLogicalDrives(); + } catch (WindowsException x) { + // shouldn't happen + throw new AssertionError(x.getMessage()); + } + + // iterate over roots, ignoring those that the security manager denies + ArrayList result = new ArrayList(); + SecurityManager sm = System.getSecurityManager(); + for (int i = 0; i <= 25; i++) { // 0->A, 1->B, 2->C... + if ((drives & (1 << i)) != 0) { + StringBuilder sb = new StringBuilder(3); + sb.append((char)('A' + i)); + sb.append(":\\"); + String root = sb.toString(); + if (sm != null) { + try { + sm.checkRead(root); + } catch (SecurityException x) { + continue; + } + } + result.add(WindowsPath.createFromNormalizedPath(this, root)); + } + } + return Collections.unmodifiableList(result); + } + + /** + * Iterator returned by getFileStores method. + */ + private class FileStoreIterator implements Iterator { + private final Iterator roots; + private FileStore next; + + FileStoreIterator() { + this.roots = getRootDirectories().iterator(); + } + + private FileStore readNext() { + assert Thread.holdsLock(this); + for (;;) { + if (!roots.hasNext()) + return null; + WindowsPath root = (WindowsPath)roots.next(); + // ignore if security manager denies access + try { + root.checkRead(); + } catch (SecurityException x) { + continue; + } + try { + FileStore fs = WindowsFileStore.create(root.toString(), true); + if (fs != null) + return fs; + } catch (IOException ioe) { + // skip it + } + } + } + + @Override + public synchronized boolean hasNext() { + if (next != null) + return true; + next = readNext(); + return next != null; + } + + @Override + public synchronized FileStore next() { + if (next == null) + next = readNext(); + if (next == null) { + throw new NoSuchElementException(); + } else { + FileStore result = next; + next = null; + return result; + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + + @Override + public Iterable getFileStores() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + try { + sm.checkPermission(new RuntimePermission("getFileStoreAttributes")); + } catch (SecurityException se) { + return Collections.emptyList(); + } + } + return new Iterable() { + public Iterator iterator() { + return new FileStoreIterator(); + } + }; + } + + // supported views + private static final Set supportedFileAttributeViews = Collections + .unmodifiableSet(new HashSet(Arrays.asList("basic", "dos", "acl", "owner", "xattr"))); + + @Override + public Set supportedFileAttributeViews() { + return supportedFileAttributeViews; + } + + @Override + public Path getPath(String path) { + WindowsPathParser.Result result = WindowsPathParser.parse(path); + return new WindowsPath(this, result.type(), result.root(), result.path()); + } + + + @Override + public UserPrincipalLookupService getUserPrincipalLookupService() { + return theLookupService; + } + + private static final UserPrincipalLookupService theLookupService = + new UserPrincipalLookupService() { + @Override + public UserPrincipal lookupPrincipalByName(String name) + throws IOException + { + return WindowsUserPrincipals.lookup(name); + } + @Override + public GroupPrincipal lookupPrincipalByGroupName(String group) + throws IOException + { + UserPrincipal user = WindowsUserPrincipals.lookup(group); + if (!(user instanceof GroupPrincipal)) + throw new UserPrincipalNotFoundException(group); + return (GroupPrincipal)user; + } + }; + + @Override + public PathMatcher getPathMatcher(String syntaxAndInput) { + int pos = syntaxAndInput.indexOf(':'); + if (pos <= 0 || pos == syntaxAndInput.length()) + throw new IllegalArgumentException(); + String syntax = syntaxAndInput.substring(0, pos); + String input = syntaxAndInput.substring(pos+1); + + String expr; + if (syntax.equals(GLOB_SYNTAX)) { + expr = Globs.toWindowsRegexPattern(input); + } else { + if (syntax.equals(REGEX_SYNTAX)) { + expr = input; + } else { + throw new UnsupportedOperationException("Syntax '" + syntax + + "' not recognized"); + } + } + + // match in uppercase + StringBuilder sb = new StringBuilder(expr.length()); + for (int i=0; i env) + throws IOException + { + checkUri(uri); + throw new FileSystemAlreadyExistsException(); + } + + @Override + public final FileSystem getFileSystem(URI uri) { + checkUri(uri); + return theFileSystem; + } + + @Override + public Path getPath(URI uri) { + return WindowsUriSupport.fromUri(theFileSystem, uri); + } + + @Override + public FileChannel newFileChannel(Path path, + Set options, + FileAttribute... attrs) + throws IOException + { + if (path == null) + throw new NullPointerException(); + if (!(path instanceof WindowsPath)) + throw new ProviderMismatchException(); + WindowsPath file = (WindowsPath)path; + + WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.fromAttribute(attrs); + try { + return WindowsChannelFactory + .newFileChannel(file.getPathForWin32Calls(), + file.getPathForPermissionCheck(), + options, + sd.address()); + } catch (WindowsException x) { + x.rethrowAsIOException(file); + return null; + } finally { + if (sd != null) + sd.release(); + } + } + + @Override + public AsynchronousFileChannel newAsynchronousFileChannel(Path path, + Set options, + ExecutorService executor, + FileAttribute... attrs) + throws IOException + { + if (path == null) + throw new NullPointerException(); + if (!(path instanceof WindowsPath)) + throw new ProviderMismatchException(); + WindowsPath file = (WindowsPath)path; + ThreadPool pool = (executor == null) ? null : ThreadPool.wrap(executor, 0); + WindowsSecurityDescriptor sd = + WindowsSecurityDescriptor.fromAttribute(attrs); + try { + return WindowsChannelFactory + .newAsynchronousFileChannel(file.getPathForWin32Calls(), + file.getPathForPermissionCheck(), + options, + sd.address(), + pool); + } catch (WindowsException x) { + x.rethrowAsIOException(file); + return null; + } finally { + if (sd != null) + sd.release(); + } + } +} diff --git a/src/windows/classes/sun/nio/fs/WindowsLinkSupport.java b/src/windows/classes/sun/nio/fs/WindowsLinkSupport.java new file mode 100644 index 000000000..516275dfe --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsLinkSupport.java @@ -0,0 +1,446 @@ +/* + * 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.*; +import java.io.IOException; +import java.io.IOError; +import java.security.AccessController; +import java.security.PrivilegedAction; +import sun.misc.Unsafe; + +import static sun.nio.fs.WindowsNativeDispatcher.*; +import static sun.nio.fs.WindowsConstants.*; + +/** + * Utility methods for symbolic link support on Windows Vista and newer. + */ + +class WindowsLinkSupport { + private static final Unsafe unsafe = Unsafe.getUnsafe(); + + private WindowsLinkSupport() { + } + + /** + * Returns the target of a symbolic link + */ + static String readLink(WindowsPath path) throws IOException { + long handle = 0L; + try { + handle = path.openForReadAttributeAccess(false); // don't follow links + } catch (WindowsException x) { + x.rethrowAsIOException(path); + } + try { + return readLinkImpl(handle); + } finally { + CloseHandle(handle); + } + } + + /** + * Returns the final path of a given path as a String. This should be used + * prior to calling Win32 system calls that do not follow links. + */ + static String getFinalPath(WindowsPath input, boolean followLinks) + throws IOException + { + WindowsFileSystem fs = input.getFileSystem(); + + try { + // if not following links then don't need final path + if (!followLinks || !fs.supportsLinks()) + return input.getPathForWin32Calls(); + + // if file is a sym link then don't need final path + if (!WindowsFileAttributes.get(input, false).isSymbolicLink()) { + return input.getPathForWin32Calls(); + } + } catch (WindowsException x) { + x.rethrowAsIOException(input); + } + + // The file is a symbolic link so we open it and try to get the + // normalized path. This should succeed on NTFS but may fail if there + // is a link to a non-NFTS file system. + long h = 0; + try { + h = input.openForReadAttributeAccess(true); + } catch (WindowsException x) { + x.rethrowAsIOException(input); + } + try { + return stripPrefix(GetFinalPathNameByHandle(h)); + } catch (WindowsException x) { + // ERROR_INVALID_LEVEL is the error returned when not supported by + // the file system + if (x.lastError() != ERROR_INVALID_LEVEL) + x.rethrowAsIOException(input); + } finally { + CloseHandle(h); + } + + // Fallback: read target of link, resolve against parent, and repeat + // until file is not a link. + WindowsPath target = input; + int linkCount = 0; + do { + try { + WindowsFileAttributes attrs = + WindowsFileAttributes.get(target, false); + // non a link so we are done + if (!attrs.isSymbolicLink()) { + return target.getPathForWin32Calls(); + } + } catch (WindowsException x) { + x.rethrowAsIOException(target); + } + WindowsPath link = WindowsPath + .createFromNormalizedPath(fs, readLink(target)); + WindowsPath parent = target.getParent(); + if (parent == null) { + // no parent so use parent of absolute path + final WindowsPath t = target; + target = AccessController + .doPrivileged(new PrivilegedAction() { + @Override + public WindowsPath run() { + return t.toAbsolutePath(); + }}); + parent = target.getParent(); + } + target = parent.resolve(link); + + } while (++linkCount < 32); + + throw new FileSystemException(input.getPathForExceptionMessage(), null, + "Too many links"); + } + + /** + * Returns the actual path of a file, optionally resolving all symbolic + * links. + */ + static String getRealPath(WindowsPath input, boolean resolveLinks) + throws IOException + { + WindowsFileSystem fs = input.getFileSystem(); + if (!fs.supportsLinks()) + resolveLinks = false; + + // On Vista use GetFinalPathNameByHandle. This should succeed on NTFS + // but may fail if there is a link to a non-NFTS file system. + if (resolveLinks) { + long h = 0; + try { + h = input.openForReadAttributeAccess(true); + } catch (WindowsException x) { + x.rethrowAsIOException(input); + } + try { + return stripPrefix(GetFinalPathNameByHandle(h)); + } catch (WindowsException x) { + if (x.lastError() != ERROR_INVALID_LEVEL) + x.rethrowAsIOException(input); + } finally { + CloseHandle(h); + } + } + + // Not resolving links or we are on Windows Vista (or newer) with a + // link to non-NFTS file system. + + // Start with absolute path + String path = null; + try { + path = input.toAbsolutePath().toString(); + } catch (IOError x) { + throw (IOException)(x.getCause()); + } + + // Collapse "." and ".." + try { + path = GetFullPathName(path); + } catch (WindowsException x) { + x.rethrowAsIOException(input); + } + + // eliminate all symbolic links + if (resolveLinks) { + path = resolveAllLinks(WindowsPath.createFromNormalizedPath(fs, path)); + } + + // string builder to build up components of path + StringBuilder sb = new StringBuilder(path.length()); + + // Copy root component + int start; + char c0 = path.charAt(0); + char c1 = path.charAt(1); + if ((c0 <= 'z' && c0 >= 'a' || c0 <= 'Z' && c0 >= 'A') && + c1 == ':' && path.charAt(2) == '\\') { + // Driver specifier + sb.append(Character.toUpperCase(c0)); + sb.append(":\\"); + start = 3; + } else if (c0 == '\\' && c1 == '\\') { + // UNC pathname, begins with "\\\\host\\share" + int last = path.length() - 1; + int pos = path.indexOf('\\', 2); + // skip both server and share names + if (pos == -1 || (pos == last)) { + // The UNC does not have a share name (collapsed by GetFullPathName) + throw new FileSystemException(input.getPathForExceptionMessage(), + null, "UNC has invalid share"); + } + pos = path.indexOf('\\', pos+1); + if (pos < 0) { + pos = last; + sb.append(path).append("\\"); + } else { + sb.append(path, 0, pos+1); + } + start = pos + 1; + } else { + throw new AssertionError("path type not recognized"); + } + + // check root directory exists + try { + FirstFile fileData = FindFirstFile(sb.toString() + "*"); + FindClose(fileData.handle()); + } catch (WindowsException x) { + x.rethrowAsIOException(path); + } + + // iterate through each component to get its actual name in the + // directory + int curr = start; + while (curr < path.length()) { + int next = path.indexOf('\\', curr); + int end = (next == -1) ? path.length() : next; + String search = sb.toString() + path.substring(curr, end); + try { + FirstFile fileData = FindFirstFile(addLongPathPrefixIfNeeded(search)); + try { + sb.append(fileData.name()); + if (next != -1) { + sb.append('\\'); + } + } finally { + FindClose(fileData.handle()); + } + } catch (WindowsException e) { + e.rethrowAsIOException(path); + } + curr = end + 1; + } + + return sb.toString(); + } + + /** + * Returns target of a symbolic link given the handle of an open file + * (that should be a link). + */ + private static String readLinkImpl(long handle) throws IOException { + int size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE; + NativeBuffer buffer = NativeBuffers.getNativeBuffer(size); + try { + try { + DeviceIoControlGetReparsePoint(handle, buffer.address(), size); + } catch (WindowsException x) { + // FIXME: exception doesn't have file name + if (x.lastError() == ERROR_NOT_A_REPARSE_POINT) + throw new NotLinkException(null, null, x.errorString()); + x.rethrowAsIOException((String)null); + } + + /* + * typedef struct _REPARSE_DATA_BUFFER { + * ULONG ReparseTag; + * USHORT ReparseDataLength; + * USHORT Reserved; + * union { + * struct { + * USHORT SubstituteNameOffset; + * USHORT SubstituteNameLength; + * USHORT PrintNameOffset; + * USHORT PrintNameLength; + * WCHAR PathBuffer[1]; + * } SymbolicLinkReparseBuffer; + * struct { + * USHORT SubstituteNameOffset; + * USHORT SubstituteNameLength; + * USHORT PrintNameOffset; + * USHORT PrintNameLength; + * WCHAR PathBuffer[1]; + * } MountPointReparseBuffer; + * struct { + * UCHAR DataBuffer[1]; + * } GenericReparseBuffer; + * }; + * } REPARSE_DATA_BUFFER + */ + final short OFFSETOF_REPARSETAG = 0; + final short OFFSETOF_PATHOFFSET = 8; + final short OFFSETOF_PATHLENGTH = 10; + final short OFFSETOF_PATHBUFFER = 16 + 4; // check this + + int tag = (int)unsafe.getLong(buffer.address() + OFFSETOF_REPARSETAG); + if (tag != IO_REPARSE_TAG_SYMLINK) { + // FIXME: exception doesn't have file name + throw new NotLinkException(null, null, "Reparse point is not a symbolic link"); + } + + // get offset and length of target + short nameOffset = unsafe.getShort(buffer.address() + OFFSETOF_PATHOFFSET); + short nameLengthInBytes = unsafe.getShort(buffer.address() + OFFSETOF_PATHLENGTH); + if ((nameLengthInBytes % 2) != 0) + throw new FileSystemException(null, null, "Symbolic link corrupted"); + + // copy into char array + char[] name = new char[nameLengthInBytes/2]; + unsafe.copyMemory(null, buffer.address() + OFFSETOF_PATHBUFFER + nameOffset, + name, Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes); + + // remove special prefix + String target = stripPrefix(new String(name)); + if (target.length() == 0) { + throw new IOException("Symbolic link target is invalid"); + } + return target; + } finally { + buffer.release(); + } + } + + /** + * Resolve all symbolic-links in a given absolute and normalized path + */ + private static String resolveAllLinks(WindowsPath path) + throws IOException + { + assert path.isAbsolute(); + WindowsFileSystem fs = path.getFileSystem(); + + // iterate through each name element of the path, resolving links as + // we go. + int linkCount = 0; + int elem = 0; + while (elem < path.getNameCount()) { + WindowsPath current = path.getRoot().resolve(path.subpath(0, elem+1)); + + WindowsFileAttributes attrs = null; + try { + attrs = WindowsFileAttributes.get(current, false); + } catch (WindowsException x) { + x.rethrowAsIOException(current); + } + + /** + * If a symbolic link then we resolve it against the parent + * of the current name element. We then resolve any remaining + * part of the path against the result. The target of the link + * may have "." and ".." components so re-normalize and restart + * the process from the first element. + */ + if (attrs.isSymbolicLink()) { + linkCount++; + if (linkCount > 32) + throw new IOException("Too many links"); + WindowsPath target = WindowsPath + .createFromNormalizedPath(fs, readLink(current)); + WindowsPath remainder = null; + int count = path.getNameCount(); + if ((elem+1) < count) { + remainder = path.subpath(elem+1, count); + } + path = current.getParent().resolve(target); + try { + String full = GetFullPathName(path.toString()); + if (!full.equals(path.toString())) { + path = WindowsPath.createFromNormalizedPath(fs, full); + } + } catch (WindowsException x) { + x.rethrowAsIOException(path); + } + if (remainder != null) { + path = path.resolve(remainder); + } + + // reset + elem = 0; + } else { + // not a link + elem++; + } + } + + return path.toString(); + } + + /** + * Add long path prefix to path if required. + */ + private static String addLongPathPrefixIfNeeded(String path) { + if (path.length() > 248) { + if (path.startsWith("\\\\")) { + path = "\\\\?\\UNC" + path.substring(1, path.length()); + } else { + path = "\\\\?\\" + path; + } + } + return path; + } + + /** + * Strip long path or symbolic link prefix from path + */ + private static String stripPrefix(String path) { + // prefix for resolved/long path + if (path.startsWith("\\\\?\\")) { + if (path.startsWith("\\\\?\\UNC\\")) { + path = "\\" + path.substring(7); + } else { + path = path.substring(4); + } + return path; + } + + // prefix for target of symbolic link + if (path.startsWith("\\??\\")) { + if (path.startsWith("\\??\\UNC\\")) { + path = "\\" + path.substring(7); + } else { + path = path.substring(4); + } + return path; + } + return path; + } +} diff --git a/src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java b/src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java new file mode 100644 index 000000000..d7cb674c6 --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsNativeDispatcher.java @@ -0,0 +1,1133 @@ +/* + * 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.security.AccessController; +import java.security.PrivilegedAction; +import sun.misc.Unsafe; + +/** + * Win32 and library calls. + */ + +class WindowsNativeDispatcher { + private WindowsNativeDispatcher() { } + + /** + * HANDLE CreateFile( + * LPCTSTR lpFileName, + * DWORD dwDesiredAccess, + * DWORD dwShareMode, + * LPSECURITY_ATTRIBUTES lpSecurityAttributes, + * DWORD dwCreationDisposition, + * DWORD dwFlagsAndAttributes, + * HANDLE hTemplateFile + * ) + */ + static long CreateFile(String path, + int dwDesiredAccess, + int dwShareMode, + long lpSecurityAttributes, + int dwCreationDisposition, + int dwFlagsAndAttributes) + throws WindowsException + { + NativeBuffer buffer = asNativeBuffer(path); + try { + return CreateFile0(buffer.address(), + dwDesiredAccess, + dwShareMode, + lpSecurityAttributes, + dwCreationDisposition, + dwFlagsAndAttributes); + } finally { + buffer.release(); + } + } + static long CreateFile(String path, + int dwDesiredAccess, + int dwShareMode, + int dwCreationDisposition, + int dwFlagsAndAttributes) + throws WindowsException + { + return CreateFile(path, dwDesiredAccess, dwShareMode, 0L, + dwCreationDisposition, dwFlagsAndAttributes); + } + private static native long CreateFile0(long lpFileName, + int dwDesiredAccess, + int dwShareMode, + long lpSecurityAttributes, + int dwCreationDisposition, + int dwFlagsAndAttributes) + throws WindowsException; + + /** + * CloseHandle( + * HANDLE hObject + * ) + */ + static native void CloseHandle(long handle); + + /** + * DeleteFile( + * LPCTSTR lpFileName + * ) + */ + static void DeleteFile(String path) throws WindowsException { + NativeBuffer buffer = asNativeBuffer(path); + try { + DeleteFile0(buffer.address()); + } finally { + buffer.release(); + } + } + private static native void DeleteFile0(long lpFileName) + throws WindowsException; + + /** + * CreateDirectory( + * LPCTSTR lpPathName, + * LPSECURITY_ATTRIBUTES lpSecurityAttributes + * ) + */ + static void CreateDirectory(String path, long lpSecurityAttributes) throws WindowsException { + NativeBuffer buffer = asNativeBuffer(path); + try { + CreateDirectory0(buffer.address(), lpSecurityAttributes); + } finally { + buffer.release(); + } + } + private static native void CreateDirectory0(long lpFileName, long lpSecurityAttributes) + throws WindowsException; + + /** + * RemoveDirectory( + * LPCTSTR lpPathName + * ) + */ + static void RemoveDirectory(String path) throws WindowsException { + NativeBuffer buffer = asNativeBuffer(path); + try { + RemoveDirectory0(buffer.address()); + } finally { + buffer.release(); + } + } + private static native void RemoveDirectory0(long lpFileName) + throws WindowsException; + + /** + * Marks a file as a sparse file. + * + * DeviceIoControl( + * FSCTL_SET_SPARSE + * ) + */ + static native void DeviceIoControlSetSparse(long handle) + throws WindowsException; + + /** + * Retrieves the reparse point data associated with the file or directory. + * + * DeviceIoControl( + * FSCTL_GET_REPARSE_POINT + * ) + */ + static native void DeviceIoControlGetReparsePoint(long handle, + long bufferAddress, int bufferSize) throws WindowsException; + + /** + * HANDLE FindFirstFile( + * LPCTSTR lpFileName, + * LPWIN32_FIND_DATA lpFindFileData + * ) + */ + static FirstFile FindFirstFile(String path) throws WindowsException { + NativeBuffer buffer = asNativeBuffer(path); + try { + FirstFile data = new FirstFile(); + FindFirstFile0(buffer.address(), data); + return data; + } finally { + buffer.release(); + } + } + static class FirstFile { + private long handle; + private String name; + + private FirstFile() { } + public long handle() { return handle; } + public String name() { return name; } + } + private static native void FindFirstFile0(long lpFileName, FirstFile obj) + throws WindowsException; + + /** + * HANDLE FindFirstFile( + * LPCTSTR lpFileName, + * LPWIN32_FIND_DATA lpFindFileData + * ) + */ + static long FindFirstFile(String path, long address) throws WindowsException { + NativeBuffer buffer = asNativeBuffer(path); + try { + return FindFirstFile1(buffer.address(), address); + } finally { + buffer.release(); + } + } + private static native long FindFirstFile1(long lpFileName, long address) + throws WindowsException; + + /** + * FindNextFile( + * HANDLE hFindFile, + * LPWIN32_FIND_DATA lpFindFileData + * ) + * + * @return lpFindFileData->cFileName + */ + static native String FindNextFile(long handle) throws WindowsException; + + /** + * HANDLE FindFirstStreamW( + * LPCWSTR lpFileName, + * STREAM_INFO_LEVELS InfoLevel, + * LPVOID lpFindStreamData, + * DWORD dwFlags + * ) + */ + static FirstStream FindFirstStream(String path) throws WindowsException { + NativeBuffer buffer = asNativeBuffer(path); + try { + FirstStream data = new FirstStream(); + FindFirstStream0(buffer.address(), data); + if (data.handle() == WindowsConstants.INVALID_HANDLE_VALUE) + return null; + return data; + } finally { + buffer.release(); + } + } + static class FirstStream { + private long handle; + private String name; + + private FirstStream() { } + public long handle() { return handle; } + public String name() { return name; } + } + private static native void FindFirstStream0(long lpFileName, FirstStream obj) + throws WindowsException; + + /* + * FindNextStreamW( + * HANDLE hFindStream, + * LPVOID lpFindStreamData + * ) + */ + static native String FindNextStream(long handle) throws WindowsException; + + /** + * FindClose( + * HANDLE hFindFile + * ) + */ + static native void FindClose(long handle) throws WindowsException; + + /** + * GetFileInformationByHandle( + * HANDLE hFile, + * LPBY_HANDLE_FILE_INFORMATION lpFileInformation + * ) + */ + static native void GetFileInformationByHandle(long handle, long address) + throws WindowsException; + + /** + * CopyFileEx( + * LPCWSTR lpExistingFileName + * LPCWSTR lpNewFileName, + * LPPROGRESS_ROUTINE lpProgressRoutine + * LPVOID lpData, + * LPBOOL pbCancel, + * DWORD dwCopyFlags + * ) + */ + static void CopyFileEx(String source, String target, int flags, + long addressToPollForCancel) + throws WindowsException + { + NativeBuffer sourceBuffer = asNativeBuffer(source); + NativeBuffer targetBuffer = asNativeBuffer(target); + try { + CopyFileEx0(sourceBuffer.address(), targetBuffer.address(), flags, + addressToPollForCancel); + } finally { + targetBuffer.release(); + sourceBuffer.release(); + } + } + private static native void CopyFileEx0(long existingAddress, long newAddress, + int flags, long addressToPollForCancel) throws WindowsException; + + /** + * MoveFileEx( + * LPCTSTR lpExistingFileName, + * LPCTSTR lpNewFileName, + * DWORD dwFlags + * ) + */ + static void MoveFileEx(String source, String target, int flags) + throws WindowsException + { + NativeBuffer sourceBuffer = asNativeBuffer(source); + NativeBuffer targetBuffer = asNativeBuffer(target); + try { + MoveFileEx0(sourceBuffer.address(), targetBuffer.address(), flags); + } finally { + targetBuffer.release(); + sourceBuffer.release(); + } + } + private static native void MoveFileEx0(long existingAddress, long newAddress, + int flags) throws WindowsException; + + /** + * DWORD GetFileAttributes( + * LPCTSTR lpFileName + * ) + */ + static int GetFileAttributes(String path) throws WindowsException { + NativeBuffer buffer = asNativeBuffer(path); + try { + return GetFileAttributes0(buffer.address()); + } finally { + buffer.release(); + } + } + private static native int GetFileAttributes0(long lpFileName) + throws WindowsException; + + /** + * SetFileAttributes( + * LPCTSTR lpFileName, + * DWORD dwFileAttributes + */ + static void SetFileAttributes(String path, int dwFileAttributes) + throws WindowsException + { + NativeBuffer buffer = asNativeBuffer(path); + try { + SetFileAttributes0(buffer.address(), dwFileAttributes); + } finally { + buffer.release(); + } + } + private static native void SetFileAttributes0(long lpFileName, + int dwFileAttributes) throws WindowsException; + + /** + * GetFileAttributesEx( + * LPCTSTR lpFileName, + * GET_FILEEX_INFO_LEVELS fInfoLevelId, + * LPVOID lpFileInformation + * ); + */ + static void GetFileAttributesEx(String path, long address) throws WindowsException { + NativeBuffer buffer = asNativeBuffer(path); + try { + GetFileAttributesEx0(buffer.address(), address); + } finally { + buffer.release(); + } + } + private static native void GetFileAttributesEx0(long lpFileName, long address) + throws WindowsException; + /** + * SetFileTime( + * HANDLE hFile, + * CONST FILETIME *lpCreationTime, + * CONST FILETIME *lpLastAccessTime, + * CONST FILETIME *lpLastWriteTime + * ) + */ + static native void SetFileTime(long handle, long createTime, + long lastAccessTime, long lastWriteTime) throws WindowsException; + + /** + * SetEndOfFile( + * HANDLE hFile + * ) + */ + static native void SetEndOfFile(long handle) throws WindowsException; + + /** + * DWORD GetLogicalDrives(VOID) + */ + static native int GetLogicalDrives() throws WindowsException; + + /** + * GetVolumeInformation( + * LPCTSTR lpRootPathName, + * LPTSTR lpVolumeNameBuffer, + * DWORD nVolumeNameSize, + * LPDWORD lpVolumeSerialNumber, + * LPDWORD lpMaximumComponentLength, + * LPDWORD lpFileSystemFlags, + * LPTSTR lpFileSystemNameBuffer, + * DWORD nFileSystemNameSize + * ) + */ + static VolumeInformation GetVolumeInformation(String root) + throws WindowsException + { + NativeBuffer buffer = asNativeBuffer(root); + try { + VolumeInformation info = new VolumeInformation(); + GetVolumeInformation0(buffer.address(), info); + return info; + } finally { + buffer.release(); + } + } + static class VolumeInformation { + private String fileSystemName; + private String volumeName; + private int volumeSerialNumber; + private int flags; + private VolumeInformation() { } + + public String fileSystemName() { return fileSystemName; } + public String volumeName() { return volumeName; } + public int volumeSerialNumber() { return volumeSerialNumber; } + public int flags() { return flags; } + } + private static native void GetVolumeInformation0(long lpRoot, + VolumeInformation obj) + throws WindowsException; + + /** + * UINT GetDriveType( + * LPCTSTR lpRootPathName + * ) + */ + static int GetDriveType(String root) throws WindowsException { + NativeBuffer buffer = asNativeBuffer(root); + try { + return GetDriveType0(buffer.address()); + } finally { + buffer.release(); + } + } + private static native int GetDriveType0(long lpRoot) throws WindowsException; + + /** + * GetDiskFreeSpaceEx( + * LPCTSTR lpDirectoryName, + * PULARGE_INTEGER lpFreeBytesAvailableToCaller, + * PULARGE_INTEGER lpTotalNumberOfBytes, + * PULARGE_INTEGER lpTotalNumberOfFreeBytes + * ) + */ + static DiskFreeSpace GetDiskFreeSpaceEx(String path) + throws WindowsException + { + NativeBuffer buffer = asNativeBuffer(path); + try { + DiskFreeSpace space = new DiskFreeSpace(); + GetDiskFreeSpaceEx0(buffer.address(), space); + return space; + } finally { + buffer.release(); + } + } + static class DiskFreeSpace { + private long freeBytesAvailable; + private long totalNumberOfBytes; + private long totalNumberOfFreeBytes; + private DiskFreeSpace() { } + + public long freeBytesAvailable() { return freeBytesAvailable; } + public long totalNumberOfBytes() { return totalNumberOfBytes; } + public long totalNumberOfFreeBytes() { return totalNumberOfFreeBytes; } + } + private static native void GetDiskFreeSpaceEx0(long lpDirectoryName, + DiskFreeSpace obj) + throws WindowsException; + + + /** + * GetVolumePathName( + * LPCTSTR lpszFileName, + * LPTSTR lpszVolumePathName, + * DWORD cchBufferLength + * ) + * + * @return lpFileName + */ + static String GetVolumePathName(String path) throws WindowsException { + NativeBuffer buffer = asNativeBuffer(path); + try { + return GetVolumePathName0(buffer.address()); + } finally { + buffer.release(); + } + } + private static native String GetVolumePathName0(long lpFileName) + throws WindowsException; + + + /** + * InitializeSecurityDescriptor( + * PSECURITY_DESCRIPTOR pSecurityDescriptor, + * DWORD dwRevision + * ) + */ + static native void InitializeSecurityDescriptor(long sdAddress) + throws WindowsException; + + /** + * InitializeAcl( + * PACL pAcl, + * DWORD nAclLength, + * DWORD dwAclRevision + * ) + */ + static native void InitializeAcl(long aclAddress, int size) + throws WindowsException; + + /** + * GetFileSecurity( + * LPCTSTR lpFileName, + * SECURITY_INFORMATION RequestedInformation, + * PSECURITY_DESCRIPTOR pSecurityDescriptor, + * DWORD nLength, + * LPDWORD lpnLengthNeeded + * ) + */ + static int GetFileSecurity(String path, + int requestedInformation, + long pSecurityDescriptor, + int nLength) throws WindowsException + { + NativeBuffer buffer = asNativeBuffer(path); + try { + return GetFileSecurity0(buffer.address(), requestedInformation, + pSecurityDescriptor, nLength); + } finally { + buffer.release(); + } + } + private static native int GetFileSecurity0(long lpFileName, + int requestedInformation, + long pSecurityDescriptor, + int nLength) throws WindowsException; + + /** + * SetFileSecurity( + * LPCTSTR lpFileName, + * SECURITY_INFORMATION SecurityInformation, + * PSECURITY_DESCRIPTOR pSecurityDescriptor + * ) + */ + static void SetFileSecurity(String path, + int securityInformation, + long pSecurityDescriptor) + throws WindowsException + { + NativeBuffer buffer = asNativeBuffer(path); + try { + SetFileSecurity0(buffer.address(), securityInformation, + pSecurityDescriptor); + } finally { + buffer.release(); + } + } + static native void SetFileSecurity0(long lpFileName, int securityInformation, + long pSecurityDescriptor) throws WindowsException; + + /** + * GetSecurityDescriptorOwner( + * PSECURITY_DESCRIPTOR pSecurityDescriptor + * PSID *pOwner, + * LPBOOL lpbOwnerDefaulted + * ) + * + * @return pOwner + */ + static native long GetSecurityDescriptorOwner(long pSecurityDescriptor) + throws WindowsException; + + /** + * SetSecurityDescriptorOwner( + * PSECURITY_DESCRIPTOR pSecurityDescriptor, + * PSID pOwner, + * BOOL bOwnerDefaulted + * ) + */ + static native void SetSecurityDescriptorOwner(long pSecurityDescriptor, + long pOwner) + throws WindowsException; + + /** + * GetSecurityDescriptorDacl( + * PSECURITY_DESCRIPTOR pSecurityDescriptor, + * LPBOOL lpbDaclPresent, + * PACL *pDacl, + * LPBOOL lpbDaclDefaulted + * ) + */ + static native long GetSecurityDescriptorDacl(long pSecurityDescriptor); + + /** + * SetSecurityDescriptorDacl( + * PSECURITY_DESCRIPTOR pSecurityDescriptor, + * BOOL bDaclPresent, + * PACL pDacl, + * BOOL bDaclDefaulted + * ) + */ + static native void SetSecurityDescriptorDacl(long pSecurityDescriptor, long pAcl) + throws WindowsException; + + + /** + * GetAclInformation( + * PACL pAcl, + * LPVOID pAclInformation, + * DWORD nAclInformationLength, + * ACL_INFORMATION_CLASS dwAclInformationClass + * ) + */ + static AclInformation GetAclInformation(long aclAddress) { + AclInformation info = new AclInformation(); + GetAclInformation0(aclAddress, info); + return info; + } + static class AclInformation { + private int aceCount; + private AclInformation() { } + + public int aceCount() { return aceCount; } + } + private static native void GetAclInformation0(long aclAddress, + AclInformation obj); + + /** + * GetAce( + * PACL pAcl, + * DWORD dwAceIndex, + * LPVOID *pAce + * ) + */ + static native long GetAce(long aclAddress, int aceIndex); + + /** + * AddAccessAllowedAceEx( + * PACL pAcl, + * DWORD dwAceRevision, + * DWORD AceFlags, + * DWORD AccessMask, + * PSID pSid + * ) + */ + static native void AddAccessAllowedAceEx(long aclAddress, int flags, + int mask, long sidAddress) throws WindowsException; + + /** + * AddAccessDeniedAceEx( + * PACL pAcl, + * DWORD dwAceRevision, + * DWORD AceFlags, + * DWORD AccessMask, + * PSID pSid + * ) + */ + static native void AddAccessDeniedAceEx(long aclAddress, int flags, + int mask, long sidAddress) throws WindowsException; + + /** + * LookupAccountSid( + * LPCTSTR lpSystemName, + * PSID Sid, + * LPTSTR Name, + * LPDWORD cbName, + * LPTSTR ReferencedDomainName, + * LPDWORD cbReferencedDomainName, + * PSID_NAME_USE peUse + * ) + */ + static Account LookupAccountSid(long sidAddress) throws WindowsException { + Account acc = new Account(); + LookupAccountSid0(sidAddress, acc); + return acc; + } + static class Account { + private String domain; + private String name; + private int use; + private Account() { } + + public String domain() { return domain; } + public String name() { return name; } + public int use() { return use; } + } + private static native void LookupAccountSid0(long sidAddress, Account obj) + throws WindowsException; + + /** + * LookupAccountName( + * LPCTSTR lpSystemName, + * LPCTSTR lpAccountName, + * PSID Sid, + * LPDWORD cbSid, + * LPTSTR ReferencedDomainName, + * LPDWORD cbReferencedDomainName, + * PSID_NAME_USE peUse + * ) + * + * @return cbSid + */ + static int LookupAccountName(String accountName, + long pSid, + int cbSid) throws WindowsException + { + NativeBuffer buffer = asNativeBuffer(accountName); + try { + return LookupAccountName0(buffer.address(), pSid, cbSid); + } finally { + buffer.release(); + } + } + private static native int LookupAccountName0(long lpAccountName, long pSid, + int cbSid) throws WindowsException; + + /** + * DWORD GetLengthSid( + * PSID pSid + * ) + */ + static native int GetLengthSid(long sidAddress); + + /** + * ConvertSidToStringSid( + * PSID Sid, + * LPTSTR* StringSid + * ) + * + * @return StringSid + */ + static native String ConvertSidToStringSid(long sidAddress) + throws WindowsException; + + /** + * ConvertStringSidToSid( + * LPCTSTR StringSid, + * PSID* pSid + * ) + * + * @return pSid + */ + static long ConvertStringSidToSid(String sidString) + throws WindowsException + { + NativeBuffer buffer = asNativeBuffer(sidString); + try { + return ConvertStringSidToSid0(buffer.address()); + } finally { + buffer.release(); + } + } + private static native long ConvertStringSidToSid0(long lpStringSid) + throws WindowsException; + + /** + * HANDLE GetCurrentProcess(VOID) + */ + static native long GetCurrentProcess(); + + /** + * HANDLE GetCurrentThread(VOID) + */ + static native long GetCurrentThread(); + + /** + * OpenProcessToken( + * HANDLE ProcessHandle, + * DWORD DesiredAccess, + * PHANDLE TokenHandle + * ) + */ + static native long OpenProcessToken(long hProcess, int desiredAccess) + throws WindowsException; + + /** + * OpenThreadToken( + * HANDLE ThreadHandle, + * DWORD DesiredAccess, + * BOOL OpenAsSelf, + * PHANDLE TokenHandle + * ) + */ + static native long OpenThreadToken(long hThread, int desiredAccess, + boolean openAsSelf) throws WindowsException; + + /** + */ + static native long DuplicateTokenEx(long hThread, int desiredAccess) + throws WindowsException; + + /** + * SetThreadToken( + * PHANDLE Thread, + * HANDLE Token + * ) + */ + static native void SetThreadToken(long thread, long hToken) + throws WindowsException; + + /** + * GetTokenInformation( + * HANDLE TokenHandle, + * TOKEN_INFORMATION_CLASS TokenInformationClass, + * LPVOID TokenInformation, + * DWORD TokenInformationLength, + * PDWORD ReturnLength + * ) + */ + static native int GetTokenInformation(long token, int tokenInfoClass, + long pTokenInfo, int tokenInfoLength) throws WindowsException; + + /** + * AdjustTokenPrivileges( + * HANDLE TokenHandle, + * BOOL DisableAllPrivileges + * PTOKEN_PRIVILEGES NewState + * DWORD BufferLength + * PTOKEN_PRIVILEGES + * PDWORD ReturnLength + * ) + */ + static native void AdjustTokenPrivileges(long token, long luid, int attributes) + throws WindowsException; + + /** + */ + static long LookupPrivilegeValue(String name) throws WindowsException { + NativeBuffer buffer = asNativeBuffer(name); + try { + return LookupPrivilegeValue0(buffer.address()); + } finally { + buffer.release(); + } + } + private static native long LookupPrivilegeValue0(long lpName) + throws WindowsException; + + /** + * BuildTrusteeWithSid( + * PTRUSTEE pTrustee, + * PSID pSid + * ) + * + * @return pTrustee + */ + static native long BuildTrusteeWithSid(long pSid); + + /** + * GetEffectiveRightsFromAcl( + * PACL pacl, + * PTRUSTEE pTrustee, + * PACCESS_MASK pAccessRights + * ) + * + * @return AccessRights + */ + static native int GetEffectiveRightsFromAcl(long pAcl, long pTrustee) + throws WindowsException; + + /** + * CreateSymbolicLink( + * LPCWSTR lpSymlinkFileName, + * LPCWSTR lpTargetFileName, + * DWORD dwFlags + * ) + */ + static void CreateSymbolicLink(String link, String target, int flags) + throws WindowsException + { + NativeBuffer linkBuffer = asNativeBuffer(link); + NativeBuffer targetBuffer = asNativeBuffer(target); + try { + CreateSymbolicLink0(linkBuffer.address(), targetBuffer.address(), + flags); + } finally { + targetBuffer.release(); + linkBuffer.release(); + } + } + private static native void CreateSymbolicLink0(long linkAddress, + long targetAddress, int flags) throws WindowsException; + + /** + * CreateHardLink( + * LPCTSTR lpFileName, + * LPCTSTR lpExistingFileName, + * LPSECURITY_ATTRIBUTES lpSecurityAttributes + * ) + */ + static void CreateHardLink(String newFile, String existingFile) + throws WindowsException + { + NativeBuffer newFileBuffer = asNativeBuffer(newFile); + NativeBuffer existingFileBuffer = asNativeBuffer(existingFile); + try { + CreateHardLink0(newFileBuffer.address(), existingFileBuffer.address()); + } finally { + existingFileBuffer.release(); + newFileBuffer.release(); + } + } + private static native void CreateHardLink0(long newFileBuffer, + long existingFiletBuffer) throws WindowsException; + + /** + * GetFullPathName( + * LPCTSTR lpFileName, + * DWORD nBufferLength, + * LPTSTR lpBuffer, + * LPTSTR *lpFilePart + * ) + */ + static String GetFullPathName(String path) throws WindowsException { + NativeBuffer buffer = asNativeBuffer(path); + try { + return GetFullPathName0(buffer.address()); + } finally { + buffer.release(); + } + } + private static native String GetFullPathName0(long pathAddress) + throws WindowsException; + + /** + * GetFinalPathNameByHandle( + * HANDLE hFile, + * LPTSTR lpszFilePath, + * DWORD cchFilePath, + * DWORD dwFlags + * ) + */ + static native String GetFinalPathNameByHandle(long handle) + throws WindowsException; + + /** + * FormatMessage( + * DWORD dwFlags, + * LPCVOID lpSource, + * DWORD dwMessageId, + * DWORD dwLanguageId, + * LPTSTR lpBuffer, + * DWORD nSize, + * va_list *Arguments + * ) + */ + static native String FormatMessage(int errorCode); + + /** + * LocalFree( + * HLOCAL hMem + * ) + */ + static native void LocalFree(long address); + + /** + * HANDLE CreateIoCompletionPort ( + * HANDLE FileHandle, + * HANDLE ExistingCompletionPort, + * DWORD CompletionKey, + * DWORD NumberOfConcurrentThreads + * ) + */ + static native long CreateIoCompletionPort(long fileHandle, long existingPort, + int completionKey) throws WindowsException; + + + /** + * GetQueuedCompletionStatus( + * HANDLE CompletionPort, + * LPDWORD lpNumberOfBytesTransferred, + * LPDWORD lpCompletionKey, + * LPOVERLAPPED *lpOverlapped, + * DWORD dwMilliseconds + */ + static CompletionStatus GetQueuedCompletionStatus(long completionPort) + throws WindowsException + { + CompletionStatus status = new CompletionStatus(); + GetQueuedCompletionStatus0(completionPort, status); + return status; + } + static class CompletionStatus { + private int error; + private int bytesTransferred; + private int completionKey; + private CompletionStatus() { } + + int error() { return error; } + int bytesTransferred() { return bytesTransferred; } + int completionKey() { return completionKey; } + } + private static native void GetQueuedCompletionStatus0(long completionPort, + CompletionStatus status) throws WindowsException; + + /** + * PostQueuedCompletionStatus( + * HANDLE CompletionPort, + * DWORD dwNumberOfBytesTransferred, + * DWORD dwCompletionKey, + * LPOVERLAPPED lpOverlapped + * ) + */ + static native void PostQueuedCompletionStatus(long completionPort, + int completionKey) throws WindowsException; + + /** + * ReadDirectoryChangesW( + * HANDLE hDirectory, + * LPVOID lpBuffer, + * DWORD nBufferLength, + * BOOL bWatchSubtree, + * DWORD dwNotifyFilter, + * LPDWORD lpBytesReturned, + * LPOVERLAPPED lpOverlapped, + * LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine + * ) + */ + static native void ReadDirectoryChangesW(long hDirectory, + long bufferAddress, + int bufferLength, + boolean watchSubTree, + int filter, + long bytesReturnedAddress, + long pOverlapped) + throws WindowsException; + + /** + * BackupRead( + * HANDLE hFile, + * LPBYTE lpBuffer, + * DWORD nNumberOfBytesToRead, + * LPDWORD lpNumberOfBytesRead, + * BOOL bAbort, + * BOOL bProcessSecurity, + * LPVOID* lpContext + * ) + */ + static BackupResult BackupRead(long hFile, + long bufferAddress, + int bufferSize, + boolean abort, + long context) + throws WindowsException + { + BackupResult result = new BackupResult(); + BackupRead0(hFile, bufferAddress, bufferSize, abort, context, result); + return result; + } + static class BackupResult { + private int bytesTransferred; + private long context; + private BackupResult() { } + + int bytesTransferred() { return bytesTransferred; } + long context() { return context; } + } + private static native void BackupRead0(long hFile, long bufferAddress, + int bufferSize, boolean abort, long context, BackupResult result) + throws WindowsException; + + /** + * BackupSeek( + * HANDLE hFile, + * DWORD dwLowBytesToSeek, + * DWORD dwHighBytesToSeek, + * LPDWORD lpdwLowByteSeeked, + * LPDWORD lpdwHighByteSeeked, + * LPVOID* lpContext + * ) + */ + static native void BackupSeek(long hFile, long bytesToSeek, long context) + throws WindowsException; + + + // -- support for copying String with a NativeBuffer -- + + private static final Unsafe unsafe = Unsafe.getUnsafe(); + + static NativeBuffer asNativeBuffer(String s) { + int stringLengthInBytes = s.length() << 1; + int sizeInBytes = stringLengthInBytes + 2; // char terminator + + // get a native buffer of sufficient size + NativeBuffer buffer = NativeBuffers.getNativeBufferFromCache(sizeInBytes); + if (buffer == null) { + buffer = NativeBuffers.allocNativeBuffer(sizeInBytes); + } else { + // buffer already contains the string contents + if (buffer.owner() == s) + return buffer; + } + + // copy into buffer and zero terminate + char[] chars = s.toCharArray(); + unsafe.copyMemory(chars, Unsafe.ARRAY_CHAR_BASE_OFFSET, null, + buffer.address(), (long)stringLengthInBytes); + unsafe.putChar(buffer.address() + stringLengthInBytes, (char)0); + buffer.setOwner(s); + return buffer; + } + + // -- native library initialization -- + + private static native void initIDs(); + + static { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + // nio.dll has dependency on net.dll + System.loadLibrary("net"); + System.loadLibrary("nio"); + return null; + }}); + initIDs(); + } + +} diff --git a/src/windows/classes/sun/nio/fs/WindowsPath.java b/src/windows/classes/sun/nio/fs/WindowsPath.java new file mode 100644 index 000000000..e57a0caff --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsPath.java @@ -0,0 +1,1316 @@ +/* + * 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.*; +import java.nio.file.attribute.*; +import java.nio.file.spi.AbstractPath; +import java.nio.channels.*; +import java.io.*; +import java.net.URI; +import java.security.AccessController; +import java.util.*; +import java.lang.ref.WeakReference; + +import com.sun.nio.file.ExtendedWatchEventModifier; + +import sun.security.util.SecurityConstants; +import sun.misc.Unsafe; + +import static sun.nio.fs.WindowsNativeDispatcher.*; +import static sun.nio.fs.WindowsConstants.*; + +/** + * Windows implementation of Path + */ + +class WindowsPath extends AbstractPath { + private static final Unsafe unsafe = Unsafe.getUnsafe(); + + // The maximum path that does not require long path prefix. On Windows + // the maximum path is 260 minus 1 (NUL) but for directories it is 260 + // minus 12 minus 1 (to allow for the creation of a 8.3 file in the + // directory). + private static final int MAX_PATH = 247; + + // Maximum extended-length path + private static final int MAX_LONG_PATH = 32000; + + // FIXME - eliminate this reference to reduce space + private final WindowsFileSystem fs; + + // path type + private final WindowsPathType type; + // root component (may be empty) + private final String root; + // normalized path + private final String path; + + // the path to use in Win32 calls. This differs from path for relative + // paths and has a long path prefix for all paths longer than MAX_PATH. + private volatile WeakReference pathForWin32Calls; + + // offsets into name components (computed lazily) + private volatile Integer[] offsets; + + // computed hash code (computed lazily, no need to be volatile) + private int hash; + + + /** + * Initializes a new instance of this class. + */ + WindowsPath(WindowsFileSystem fs, + WindowsPathType type, + String root, + String path) + { + this.fs = fs; + this.type = type; + this.root = root; + this.path = path; + } + + /** + * Creates a WindowsPath by parsing the given path. + */ + static WindowsPath parse(WindowsFileSystem fs, String path) { + WindowsPathParser.Result result = WindowsPathParser.parse(path); + return new WindowsPath(fs, result.type(), result.root(), result.path()); + } + + /** + * Creates a WindowsPath from a given path that is known to be normalized. + */ + static WindowsPath createFromNormalizedPath(WindowsFileSystem fs, String path) { + try { + WindowsPathParser.Result result = + WindowsPathParser.parseNormalizedPath(path); + return new WindowsPath(fs, result.type(), result.root(), result.path()); + } catch (InvalidPathException x) { + throw new AssertionError(x.getMessage()); + } + } + + // use this message when throwing exceptions + String getPathForExceptionMessage() { + return path; + } + + // use this path for permission checks + String getPathForPermissionCheck() { + return path; + } + + // use this path for Win32 calls + // This method will prefix long paths with \\?\ or \\?\UNC as required. + String getPathForWin32Calls() throws WindowsException { + // short absolute paths can be used directly + if (isAbsolute() && path.length() <= MAX_PATH) + return path; + + // return cached values if available + WeakReference ref = pathForWin32Calls; + String resolved = (ref != null) ? ref.get() : null; + if (resolved != null) { + // Win32 path already available + return resolved; + } + + // resolve against default directory + resolved = getAbsolutePath(); + + // Long paths need to have "." and ".." removed and be prefixed with + // "\\?\". Note that it is okay to remove ".." even when it follows + // a link - for example, it is okay for foo/link/../bar to be changed + // to foo/bar. The reason is that Win32 APIs to access foo/link/../bar + // will access foo/bar anyway (which differs to Unix systems) + if (resolved.length() > MAX_PATH) { + if (resolved.length() > MAX_LONG_PATH) { + throw new WindowsException("Cannot access file with path exceeding " + + MAX_LONG_PATH + " characters"); + } + resolved = addPrefixIfNeeded(GetFullPathName(resolved)); + } + + // cache the resolved path (except drive relative paths as the working + // directory on removal media devices can change during the lifetime + // of the VM) + if (type != WindowsPathType.DRIVE_RELATIVE) { + synchronized (path) { + pathForWin32Calls = new WeakReference(resolved); + } + } + return resolved; + } + + // return this path resolved against the file system's default directory + private String getAbsolutePath() throws WindowsException { + if (isAbsolute()) + return path; + + // Relative path ("foo" for example) + if (type == WindowsPathType.RELATIVE) { + String defaultDirectory = getFileSystem().defaultDirectory(); + if (defaultDirectory.endsWith("\\")) { + return defaultDirectory + path; + } else { + StringBuilder sb = + new StringBuilder(defaultDirectory.length() + path.length() + 1); + return sb.append(defaultDirectory).append('\\').append(path).toString(); + } + } + + // Directory relative path ("\foo" for example) + if (type == WindowsPathType.DIRECTORY_RELATIVE) { + String defaultRoot = getFileSystem().defaultRoot(); + return defaultRoot + path.substring(1); + } + + // Drive relative path ("C:foo" for example). + if (isSameDrive(root, getFileSystem().defaultRoot())) { + // relative to default directory + String remaining = path.substring(root.length()); + String defaultDirectory = getFileSystem().defaultDirectory(); + String result; + if (defaultDirectory.endsWith("\\")) { + result = defaultDirectory + remaining; + } else { + result = defaultDirectory + "\\" + remaining; + } + return result; + } else { + // relative to some other drive + String wd; + try { + int dt = GetDriveType(root + "\\"); + if (dt == DRIVE_UNKNOWN || dt == DRIVE_NO_ROOT_DIR) + throw new WindowsException(""); + wd = GetFullPathName(root + "."); + } catch (WindowsException x) { + throw new WindowsException("Unable to get working directory of drive '" + + Character.toUpperCase(root.charAt(0)) + "'"); + } + String result = wd; + if (wd.endsWith("\\")) { + result += path.substring(root.length()); + } else { + if (path.length() > root.length()) + result += "\\" + path.substring(root.length()); + } + return result; + } + } + + // returns true if same drive letter + private static boolean isSameDrive(String root1, String root2) { + return Character.toUpperCase(root1.charAt(0)) == + Character.toUpperCase(root2.charAt(0)); + } + + // Add long path prefix to path if required + private static String addPrefixIfNeeded(String path) { + if (path.length() > 248) { + if (path.startsWith("\\\\")) { + path = "\\\\?\\UNC" + path.substring(1, path.length()); + } else { + path = "\\\\?\\" + path; + } + } + return path; + } + + @Override + public WindowsFileSystem getFileSystem() { + return fs; + } + + // -- Path operations -- + + @Override + public Path getName() { + // represents root component only + if (root.length() == path.length()) + return null; + int off = path.lastIndexOf('\\'); + if (off < root.length()) + off = root.length(); + else + off++; + return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", path.substring(off)); + } + + @Override + public WindowsPath getParent() { + // represents root component only + if (root.length() == path.length()) + return null; + int off = path.lastIndexOf('\\'); + if (off < root.length()) + return getRoot(); + else + return new WindowsPath(getFileSystem(), + type, + root, + path.substring(0, off)); + } + + @Override + public WindowsPath getRoot() { + if (root.length() == 0) + return null; + return new WindowsPath(getFileSystem(), type, root, root); + } + + // package-private + boolean isUnc() { + return type == WindowsPathType.UNC; + } + + @Override + public boolean isAbsolute() { + return type == WindowsPathType.ABSOLUTE || type == WindowsPathType.UNC; + } + + private WindowsPath checkPath(FileRef path) { + if (path == null) + throw new NullPointerException(); + if (!(path instanceof WindowsPath)) { + throw new ProviderMismatchException(); + } + return (WindowsPath)path; + } + + @Override + public WindowsPath relativize(Path obj) { + WindowsPath other = checkPath(obj); + if (this.equals(other)) + return null; + + // can only relativize paths of the same type + if (this.type != other.type) + throw new IllegalArgumentException("'other' is different type of Path"); + + // can only relativize paths if root component matches + if (!this.root.equalsIgnoreCase(other.root)) + throw new IllegalArgumentException("'other' has different root"); + + int bn = this.getNameCount(); + int cn = other.getNameCount(); + + // skip matching names + int n = (bn > cn) ? cn : bn; + int i = 0; + while (i < n) { + if (!this.getName(i).equals(other.getName(i))) + break; + i++; + } + + // append ..\ for remaining names in the base + StringBuilder result = new StringBuilder(); + for (int j=i; j ignore name + int remaining = count; // number of names remaining + + // multiple passes to eliminate all occurences of "." and "name/.." + int prevRemaining; + do { + prevRemaining = remaining; + int prevName = -1; + for (int i=0; i 2) { + prevName = i; + continue; + } + + // "." or something else + if (name.length() == 1) { + // ignore "." + if (name.charAt(0) == '.') { + ignore[i] = true; + remaining--; + } else { + prevName = i; + } + continue; + } + + // not ".." + if (name.charAt(0) != '.' || name.charAt(1) != '.') { + prevName = i; + continue; + } + + // ".." found + if (prevName >= 0) { + // name//.. found so mark name and ".." to be + // ignored + ignore[prevName] = true; + ignore[i] = true; + remaining = remaining - 2; + prevName = -1; + } else { + // Cases: + // C:\\.. + // \\server\\share\\.. + // \.. + if (isAbsolute() || type == WindowsPathType.DIRECTORY_RELATIVE) { + boolean hasPrevious = false; + for (int j=0; j remaining); + + // no redundant names + if (remaining == count) + return this; + + // corner case - all names removed + if (remaining == 0) { + return getRoot(); + } + + // re-constitute the path from the remaining names. + StringBuilder result = new StringBuilder(); + if (root != null) + result.append(root); + for (int i=0; i list = new ArrayList(); + int start = root.length(); + int off = root.length(); + while (off < path.length()) { + if (path.charAt(off) != '\\') { + off++; + } else { + list.add(start); + start = ++off; + } + } + if (start != off) + list.add(start); + synchronized (this) { + if (offsets == null) + offsets = list.toArray(new Integer[list.size()]); + } + } + } + + @Override + public int getNameCount() { + initOffsets(); + return offsets.length; + } + + private String elementAsString(int i) { + initOffsets(); + if (i == (offsets.length-1)) + return path.substring(offsets[i]); + return path.substring(offsets[i], offsets[i+1]-1); + } + + @Override + public WindowsPath getName(int index) { + initOffsets(); + if (index < 0 || index >= offsets.length) + throw new IllegalArgumentException(); + return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", elementAsString(index)); + } + + @Override + public WindowsPath subpath(int beginIndex, int endIndex) { + initOffsets(); + if (beginIndex < 0) + throw new IllegalArgumentException(); + if (beginIndex >= offsets.length) + throw new IllegalArgumentException(); + if (endIndex > offsets.length) + throw new IllegalArgumentException(); + if (beginIndex >= endIndex) + throw new IllegalArgumentException(); + + StringBuilder sb = new StringBuilder(); + Integer[] nelems = new Integer[endIndex - beginIndex]; + for (int i = beginIndex; i < endIndex; i++) { + nelems[i-beginIndex] = sb.length(); + sb.append(elementAsString(i)); + if (i != (endIndex-1)) + sb.append("\\"); + } + return new WindowsPath(getFileSystem(), WindowsPathType.RELATIVE, "", sb.toString()); + } + + @Override + public boolean startsWith(Path obj) { + WindowsPath other = checkPath(obj); + + // if this path has a root component the given path's root must match + if (!this.root.equalsIgnoreCase(other.root)) + return false; + + // roots match so compare elements + int thisCount = getNameCount(); + int otherCount = other.getNameCount(); + if (otherCount <= thisCount) { + while (--otherCount >= 0) { + String thisElement = this.elementAsString(otherCount); + String otherElement = other.elementAsString(otherCount); + // FIXME: should compare in uppercase + if (!thisElement.equalsIgnoreCase(otherElement)) + return false; + } + return true; + } + return false; + } + + @Override + public boolean endsWith(Path obj) { + WindowsPath other = checkPath(obj); + + // other path is longer + if (other.path.length() > path.length()) { + return false; + } + + int thisCount = this.getNameCount(); + int otherCount = other.getNameCount(); + + // given path has more elements that this path + if (otherCount > thisCount) { + return false; + } + + // compare roots + if (other.root.length() > 0) { + if (otherCount < thisCount) + return false; + // FIXME: should compare in uppercase + if (!this.root.equalsIgnoreCase(other.root)) + return false; + } + + // match last 'otherCount' elements + int off = thisCount - otherCount; + while (--otherCount >= 0) { + String thisElement = this.elementAsString(off + otherCount); + String otherElement = other.elementAsString(otherCount); + // FIXME: should compare in uppercase + if (!thisElement.equalsIgnoreCase(otherElement)) + return false; + } + return true; + } + + @Override + public int compareTo(Path obj) { + if (obj == null) + throw new NullPointerException(); + String s1 = path; + String s2 = ((WindowsPath)obj).path; + int n1 = s1.length(); + int n2 = s2.length(); + int min = Math.min(n1, n2); + for (int i = 0; i < min; i++) { + char c1 = s1.charAt(i); + char c2 = s2.charAt(i); + if (c1 != c2) { + c1 = Character.toUpperCase(c1); + c2 = Character.toUpperCase(c2); + if (c1 != c2) { + return c1 - c2; + } + } + } + return n1 - n2; + } + + @Override + public boolean equals(Object obj) { + if ((obj != null) && (obj instanceof WindowsPath)) { + return compareTo((Path)obj) == 0; + } + return false; + } + + @Override + public int hashCode() { + // OK if two or more threads compute hash + int h = hash; + if (h == 0) { + for (int i = 0; i< path.length(); i++) { + h = 31*h + Character.toUpperCase(path.charAt(i)); + } + hash = h; + } + return h; + } + + @Override + public String toString() { + return path; + } + + @Override + public Iterator iterator() { + return new Iterator() { + private int i = 0; + @Override + public boolean hasNext() { + return (i < getNameCount()); + } + @Override + public Path next() { + if (i < getNameCount()) { + Path result = getName(i); + i++; + return result; + } else { + throw new NoSuchElementException(); + } + } + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + + // -- file operations -- + + // package-private + long openForReadAttributeAccess(boolean followLinks) + throws WindowsException + { + int flags = FILE_FLAG_BACKUP_SEMANTICS; + if (!followLinks && getFileSystem().supportsLinks()) + flags |= FILE_FLAG_OPEN_REPARSE_POINT; + return CreateFile(getPathForWin32Calls(), + FILE_READ_ATTRIBUTES, + (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), + 0L, + OPEN_EXISTING, + flags); + } + + void checkRead() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkRead(getPathForPermissionCheck()); + } + } + + void checkWrite() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkWrite(getPathForPermissionCheck()); + } + } + + void checkDelete() { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkDelete(getPathForPermissionCheck()); + } + } + + @Override + public FileStore getFileStore() + throws IOException + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new RuntimePermission("getFileStoreAttributes")); + checkRead(); + } + return WindowsFileStore.create(this); + } + + /** + * Returns buffer with SID_AND_ATTRIBUTES structure representing the user + * associated with the current thread access token. + * FIXME - this should be cached. + */ + private NativeBuffer getUserInfo() throws IOException { + try { + long hToken = WindowsSecurity.processTokenWithQueryAccess; + int size = GetTokenInformation(hToken, TokenUser, 0L, 0); + assert size > 0; + + NativeBuffer buffer = NativeBuffers.getNativeBuffer(size); + try { + int newsize = GetTokenInformation(hToken, TokenUser, + buffer.address(), size); + if (newsize != size) + throw new AssertionError(); + return buffer; + } catch (WindowsException x) { + buffer.release(); + throw x; + } + } catch (WindowsException x) { + throw new IOException(x.getMessage()); + } + } + + /** + * Reads the file ACL and return the effective access as ACCESS_MASK + */ + private int getEffectiveAccess() throws IOException { + // read security descriptor continaing ACL (symlinks are followed) + String target = WindowsLinkSupport.getFinalPath(this, true); + NativeBuffer aclBuffer = WindowsAclFileAttributeView + .getFileSecurity(target, DACL_SECURITY_INFORMATION); + + // retrieves DACL from security descriptor + long pAcl = GetSecurityDescriptorDacl(aclBuffer.address()); + + // Use GetEffectiveRightsFromAcl to get effective access to file + try { + NativeBuffer userBuffer = getUserInfo(); + try { + try { + // SID_AND_ATTRIBUTES->pSid + long pSid = unsafe.getAddress(userBuffer.address()); + long pTrustee = BuildTrusteeWithSid(pSid); + try { + return GetEffectiveRightsFromAcl(pAcl, pTrustee); + } finally { + LocalFree(pTrustee); + } + } catch (WindowsException x) { + throw new IOException("Unable to get effective rights from ACL: " + + x.getMessage()); + } + } finally { + userBuffer.release(); + } + } finally { + aclBuffer.release(); + } + } + + @Override + public void checkAccess(AccessMode... modes) throws IOException { + // if no access modes then simply file attributes + if (modes.length == 0) { + checkRead(); + try { + WindowsFileAttributes.get(this, true); + } catch (WindowsException exc) { + exc.rethrowAsIOException(this); + } + return; + } + + boolean r = false; + boolean w = false; + boolean x = false; + for (AccessMode mode: modes) { + switch (mode) { + case READ : r = true; break; + case WRITE : w = true; break; + case EXECUTE : x = true; break; + default: throw new AssertionError("Should not get here"); + } + } + + int mask = 0; + if (r) { + checkRead(); + mask |= FILE_READ_DATA; + } + if (w) { + checkWrite(); + mask |= FILE_WRITE_DATA; + } + if (x) { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkExec(getPathForPermissionCheck()); + mask |= FILE_EXECUTE; + } + + if ((getEffectiveAccess() & mask) == 0) + throw new AccessDeniedException( + this.getPathForExceptionMessage(), null, + "Effective permissions does not allow requested access"); + + // for write access we neeed to check if the DOS readonly attribute + // and if the volume is read-only + if (w) { + try { + WindowsFileAttributes attrs = WindowsFileAttributes.get(this, true); + if (!attrs.isDirectory() && attrs.isReadOnly()) + throw new AccessDeniedException( + this.getPathForExceptionMessage(), null, + "DOS readonly attribute is set"); + } catch (WindowsException exc) { + exc.rethrowAsIOException(this); + } + + if (WindowsFileStore.create(this).isReadOnly()) { + throw new AccessDeniedException( + this.getPathForExceptionMessage(), null, "Read-only file system"); + } + return; + } + } + + @Override + public void delete(boolean failIfNotExists) throws IOException { + checkDelete(); + + WindowsFileAttributes attrs = null; + try { + // need to know if file is a directory or junction + attrs = WindowsFileAttributes.get(this, false); + if (attrs.isDirectory() || attrs.isDirectoryLink()) { + RemoveDirectory(getPathForWin32Calls()); + } else { + DeleteFile(getPathForWin32Calls()); + } + } catch (WindowsException x) { + + // no-op if file does not exist + if (!failIfNotExists && + (x.lastError() == ERROR_FILE_NOT_FOUND || + x.lastError() == ERROR_PATH_NOT_FOUND)) return; + + if (attrs != null && attrs.isDirectory()) { + // ERROR_ALREADY_EXISTS is returned when attempting to delete + // non-empty directory on SAMBA servers. + if (x.lastError() == ERROR_DIR_NOT_EMPTY || + x.lastError() == ERROR_ALREADY_EXISTS) + { + throw new DirectoryNotEmptyException( + getPathForExceptionMessage()); + } + } + x.rethrowAsIOException(this); + } + } + + @Override + public DirectoryStream newDirectoryStream(DirectoryStream.Filter filter) + throws IOException + { + checkRead(); + if (filter == null) + throw new NullPointerException(); + return new WindowsDirectoryStream(this, filter); + } + + @Override + public void implCopyTo(Path obj, CopyOption... options) throws IOException { + WindowsPath target = (WindowsPath)obj; + WindowsFileCopy.copy(this, target, options); + } + + @Override + public void implMoveTo(Path obj, CopyOption... options) throws IOException { + WindowsPath target = (WindowsPath)obj; + WindowsFileCopy.move(this, target, options); + } + + private boolean followLinks(LinkOption... options) { + boolean followLinks = true; + for (LinkOption option: options) { + if (option == LinkOption.NOFOLLOW_LINKS) { + followLinks = false; + continue; + } + if (option == null) + throw new NullPointerException(); + throw new AssertionError("Should not get here"); + } + return followLinks; + } + + @Override + @SuppressWarnings("unchecked") + public V + getFileAttributeView(Class view, LinkOption... options) + { + if (view == null) + throw new NullPointerException(); + boolean followLinks = followLinks(options); + if (view == BasicFileAttributeView.class) + return (V) WindowsFileAttributeViews.createBasicView(this, followLinks); + if (view == DosFileAttributeView.class) + return (V) WindowsFileAttributeViews.createDosView(this, followLinks); + if (view == AclFileAttributeView.class) + return (V) new WindowsAclFileAttributeView(this, followLinks); + if (view == FileOwnerAttributeView.class) + return (V) new FileOwnerAttributeViewImpl( + new WindowsAclFileAttributeView(this, followLinks)); + if (view == UserDefinedFileAttributeView.class) + return (V) new WindowsUserDefinedFileAttributeView(this, followLinks); + return (V) null; + } + + @Override + public FileAttributeView getFileAttributeView(String name, LinkOption... options) { + boolean followLinks = followLinks(options); + if (name.equals("basic")) + return WindowsFileAttributeViews.createBasicView(this, followLinks); + if (name.equals("dos")) + return WindowsFileAttributeViews.createDosView(this, followLinks); + if (name.equals("acl")) + return new WindowsAclFileAttributeView(this, followLinks); + if (name.equals("owner")) + return new FileOwnerAttributeViewImpl( + new WindowsAclFileAttributeView(this, followLinks)); + if (name.equals("xattr")) + return new WindowsUserDefinedFileAttributeView(this, followLinks); + return null; + } + + @Override + public WindowsPath createDirectory(FileAttribute... attrs) + throws IOException + { + checkWrite(); + WindowsSecurityDescriptor sd = WindowsSecurityDescriptor.fromAttribute(attrs); + try { + CreateDirectory(getPathForWin32Calls(), sd.address()); + } catch (WindowsException x) { + x.rethrowAsIOException(this); + } finally { + sd.release(); + } + return this; + } + + @Override + public InputStream newInputStream()throws IOException { + try { + Set options = Collections.emptySet(); + FileChannel fc = WindowsChannelFactory + .newFileChannel(getPathForWin32Calls(), + getPathForPermissionCheck(), + options, + 0L); + return Channels.newInputStream(fc); + } catch (WindowsException x) { + x.rethrowAsIOException(this); + return null; // keep compiler happy + } + } + + @Override + public SeekableByteChannel newByteChannel(Set options, + FileAttribute... attrs) + throws IOException + { + WindowsSecurityDescriptor sd = + WindowsSecurityDescriptor.fromAttribute(attrs); + try { + return WindowsChannelFactory + .newFileChannel(getPathForWin32Calls(), + getPathForPermissionCheck(), + options, + sd.address()); + } catch (WindowsException x) { + x.rethrowAsIOException(this); + return null; // keep compiler happy + } finally { + sd.release(); + } + } + + @Override + public OutputStream newOutputStream(Set options, + FileAttribute... attrs) + throws IOException + { + // need to copy options to add WRITE + Set opts = new HashSet(options); + if (opts.contains(StandardOpenOption.READ)) + throw new IllegalArgumentException("READ not allowed"); + opts.add(StandardOpenOption.WRITE); + + WindowsSecurityDescriptor sd = + WindowsSecurityDescriptor.fromAttribute(attrs); + FileChannel fc; + try { + fc = WindowsChannelFactory + .newFileChannel(getPathForWin32Calls(), + getPathForPermissionCheck(), + opts, + sd.address()); + return Channels.newOutputStream(fc); + } catch (WindowsException x) { + x.rethrowAsIOException(this); + return null; // keep compiler happy + } finally { + sd.release(); + } + } + + @Override + public boolean isSameFile(FileRef obj) throws IOException { + if (this.equals(obj)) + return true; + if (!(obj instanceof WindowsPath)) // includes null check + return false; + WindowsPath other = (WindowsPath)obj; + + // check security manager access to both files + this.checkRead(); + other.checkRead(); + + // open both files and see if they are the same + long h1 = 0L; + try { + h1 = this.openForReadAttributeAccess(true); + } catch (WindowsException x) { + x.rethrowAsIOException(this); + } + try { + WindowsFileAttributes attrs1 = null; + try { + attrs1 = WindowsFileAttributes.readAttributes(h1); + } catch (WindowsException x) { + x.rethrowAsIOException(this); + } + long h2 = 0L; + try { + h2 = other.openForReadAttributeAccess(true); + } catch (WindowsException x) { + x.rethrowAsIOException(other); + } + try { + WindowsFileAttributes attrs2 = null; + try { + attrs2 = WindowsFileAttributes.readAttributes(h2); + } catch (WindowsException x) { + x.rethrowAsIOException(other); + } + return WindowsFileAttributes.isSameFile(attrs1, attrs2); + } finally { + CloseHandle(h2); + } + } finally { + CloseHandle(h1); + } + } + + @Override + public WindowsPath createSymbolicLink(Path obj, FileAttribute... attrs) + throws IOException + { + if (!getFileSystem().supportsLinks()) { + throw new UnsupportedOperationException("Symbolic links not supported " + + "on this operating system"); + } + + WindowsPath target = checkPath(obj); + + // no attributes allowed + if (attrs.length > 0) { + WindowsSecurityDescriptor.fromAttribute(attrs); // may throw NPE or UOE + throw new UnsupportedOperationException("Initial file attributes" + + "not supported when creating symbolic link"); + } + + // permission check + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new LinkPermission("symbolic")); + this.checkWrite(); + } + + /** + * Throw I/O exception for the drive-relative case because Windows + * creates a link with the resolved target for this case. + */ + if (target.type == WindowsPathType.DRIVE_RELATIVE) { + throw new IOException("Cannot create symbolic link to drive-relative target"); + } + + /* + * Windows treates symbolic links to directories differently than it + * does to other file types. For that reason we check if the exists and + * is a directory. + */ + int flags = 0; + WindowsPath resolvedTarget = + WindowsPath.createFromNormalizedPath(getFileSystem(), resolve(target).path); + try { + if (WindowsFileAttributes.get(resolvedTarget, true).isDirectory()) + flags |= SYMBOLIC_LINK_FLAG_DIRECTORY; + } catch (WindowsException x) { + // unable to access target so assume target is not a directory + } + + // create the link + try { + CreateSymbolicLink(getPathForWin32Calls(), + addPrefixIfNeeded(target.toString()), + flags); + } catch (WindowsException x) { + if (x.lastError() == ERROR_INVALID_REPARSE_DATA) { + x.rethrowAsIOException(this, target); + } else { + x.rethrowAsIOException(this); + } + } + return this; + } + + @Override + public Path createLink(Path obj) throws IOException { + WindowsPath existing = checkPath(obj); + + // permission check + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new LinkPermission("hard")); + this.checkWrite(); + existing.checkWrite(); + } + + // create hard link + try { + CreateHardLink(this.getPathForWin32Calls(), + existing.getPathForWin32Calls()); + } catch (WindowsException x) { + x.rethrowAsIOException(this, existing); + } + + return this; + } + + @Override + public WindowsPath readSymbolicLink() throws IOException { + if (!getFileSystem().supportsLinks()) { + throw new UnsupportedOperationException("symbolic links not supported"); + } + + // permission check + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + FilePermission perm = new FilePermission(getPathForPermissionCheck(), + SecurityConstants.FILE_READLINK_ACTION); + AccessController.checkPermission(perm); + } + + String target = WindowsLinkSupport.readLink(this); + return createFromNormalizedPath(getFileSystem(), target); + } + + @Override + public URI toUri() { + return WindowsUriSupport.toUri(this); + } + + @Override + public WindowsPath toAbsolutePath() { + if (isAbsolute()) + return this; + + // permission check as per spec + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPropertyAccess("user.dir"); + } + + try { + return createFromNormalizedPath(getFileSystem(), getAbsolutePath()); + } catch (WindowsException x) { + throw new IOError(new IOException(x.getMessage())); + } + } + + @Override + public WindowsPath toRealPath(boolean resolveLinks) throws IOException { + checkRead(); + String rp = WindowsLinkSupport.getRealPath(this, resolveLinks); + return createFromNormalizedPath(getFileSystem(), rp); + } + + @Override + public boolean isHidden() throws IOException { + checkRead(); + WindowsFileAttributes attrs = null; + try { + attrs = WindowsFileAttributes.get(this, true); + } catch (WindowsException x) { + x.rethrowAsIOException(this); + } + // DOS hidden attribute not meaningful when set on directories + if (attrs.isDirectory()) + return false; + return attrs.isHidden(); + } + + @Override + public WatchKey register(WatchService watcher, + WatchEvent.Kind[] events, + WatchEvent.Modifier... modifiers) + throws IOException + { + if (watcher == null) + throw new NullPointerException(); + if (!(watcher instanceof WindowsWatchService)) + throw new ProviderMismatchException(); + + // When a security manager is set then we need to make a defensive + // copy of the modifiers and check for the Windows specific FILE_TREE + // modifier. When the modifier is present then check that permission + // has been granted recursively. + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + boolean watchSubtree = false; + final int ml = modifiers.length; + if (ml > 0) { + modifiers = Arrays.copyOf(modifiers, ml); + int i=0; + while (i < ml) { + if (modifiers[i++] == ExtendedWatchEventModifier.FILE_TREE) { + watchSubtree = true; + break; + } + } + } + String s = getPathForPermissionCheck(); + sm.checkRead(s); + if (watchSubtree) + sm.checkRead(s + "\\-"); + } + + return ((WindowsWatchService)watcher).register(this, events, modifiers); + } +} diff --git a/src/windows/classes/sun/nio/fs/WindowsPathParser.java b/src/windows/classes/sun/nio/fs/WindowsPathParser.java new file mode 100644 index 000000000..411b91b1c --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsPathParser.java @@ -0,0 +1,225 @@ +/* + * 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.InvalidPathException; + +/** + * A parser of Windows path strings + */ + +class WindowsPathParser { + private WindowsPathParser() { } + + /** + * The result of a parse operation + */ + static class Result { + private final WindowsPathType type; + private final String root; + private final String path; + + Result(WindowsPathType type, String root, String path) { + this.type = type; + this.root = root; + this.path = path; + } + + /** + * The path type + */ + WindowsPathType type() { + return type; + } + + /** + * The root component + */ + String root() { + return root; + } + + /** + * The normalized path (includes root) + */ + String path() { + return path; + } + } + + /** + * Parses the given input as a Windows path + */ + static Result parse(String input) { + if (input == null || input.length() == 0) + throw new InvalidPathException(input, "Empty or null path"); + return parse(input, true); + } + + /** + * Parses the given input as a Windows path where it is known that the + * path is already normalized. + */ + static Result parseNormalizedPath(String input) { + return parse(input, false); + } + + /** + * Parses the given input as a Windows path. + * + * @param requireToNormalize + * Indicates if the path requires to be normalized + */ + private static Result parse(String input, boolean requireToNormalize) { + String root = ""; + WindowsPathType type = null; + + int len = input.length(); + int off = 0; + if (len > 1) { + char c0 = input.charAt(0); + char c1 = input.charAt(1); + char c = 0; + int next = 2; + if (isSlash(c0) && isSlash(c1)) { + // UNC: We keep the first two slash, collapse all the + // following, then take the hostname and share name out, + // meanwhile collapsing all the redundant slashes. + type = WindowsPathType.UNC; + off = nextNonSlash(input, next, len); + next = nextSlash(input, off, len); + if (off == next) + throw new InvalidPathException(input, "UNC path is missing hostname"); + String host = input.substring(off, next); //host + off = nextNonSlash(input, next, len); + next = nextSlash(input, off, len); + if (off == next) + throw new InvalidPathException(input, "UNC path is missing sharename"); + root = "\\\\" + host + "\\" + input.substring(off, next) + "\\"; + off = next; + } else { + if (isLetter(c0) && c1 == ':') { + root = input.substring(0, 2); + if (len > 2 && isSlash(input.charAt(2))) { + off = 3; + root += "\\"; + type = WindowsPathType.ABSOLUTE; + } else { + off = 2; + type = WindowsPathType.DRIVE_RELATIVE; + } + } + } + } + if (off == 0) { + if (isSlash(input.charAt(0))) { + type = WindowsPathType.DIRECTORY_RELATIVE; + root = "\\"; + } else { + type = WindowsPathType.RELATIVE; + } + } + + if (requireToNormalize) { + StringBuilder sb = new StringBuilder(input.length()); + sb.append(root); + return new Result(type, root, normalize(sb, input, off)); + } else { + return new Result(type, root, input); + } + } + + /** + * Remove redundant slashes from the rest of the path, forcing all slashes + * into the preferred slash. + */ + private static String normalize(StringBuilder sb, String path, int off) { + int len = path.length(); + off = nextNonSlash(path, off, len); + int start = off; + char lastC = 0; + while (off < len) { + char c = path.charAt(off); + if (isSlash(c)) { + if (lastC == ' ') + throw new InvalidPathException(path, + "Trailing char <" + lastC + ">", + off - 1); + sb.append(path, start, off); + off = nextNonSlash(path, off, len); + if (off != len) //no slash at the end of normalized path + sb.append('\\'); + start = off; + } else { + if (isInvalidPathChar(c)) + throw new InvalidPathException(path, + "Illegal char <" + c + ">", + off); + lastC = c; + off++; + } + } + if (start != off) { + if (lastC == ' ') + throw new InvalidPathException(path, + "Trailing char <" + lastC + ">", + off - 1); + sb.append(path, start, off); + } + return sb.toString(); + } + + private static final boolean isSlash(char c) { + return (c == '\\') || (c == '/'); + } + + private static final int nextNonSlash(String path, int off, int end) { + while (off < end && isSlash(path.charAt(off))) { off++; } + return off; + } + + private static final int nextSlash(String path, int off, int end) { + char c; + while (off < end && !isSlash(c=path.charAt(off))) { + if (isInvalidPathChar(c)) + throw new InvalidPathException(path, + "Illegal character [" + c + "] in path", + off); + off++; + } + return off; + } + + private static final boolean isLetter(char c) { + return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')); + } + + // Reserved characters for window path name + private static final String reservedChars = "<>:\"|?*"; + private static final boolean isInvalidPathChar(char ch) { + return ch < '\u0020' || reservedChars.indexOf(ch) != -1; + } +} diff --git a/src/windows/classes/sun/nio/fs/WindowsPathType.java b/src/windows/classes/sun/nio/fs/WindowsPathType.java new file mode 100644 index 000000000..ae3651f1f --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsPathType.java @@ -0,0 +1,38 @@ +/* + * 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; + +/** + * A type safe enum of Windows path types. + */ + +enum WindowsPathType { + ABSOLUTE, // C:\foo + UNC, // \\server\share\foo + RELATIVE, // foo + DIRECTORY_RELATIVE, // \foo + DRIVE_RELATIVE // C:foo +} diff --git a/src/windows/classes/sun/nio/fs/WindowsSecurity.java b/src/windows/classes/sun/nio/fs/WindowsSecurity.java new file mode 100644 index 000000000..9d44257b5 --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsSecurity.java @@ -0,0 +1,123 @@ +/* + * 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 static sun.nio.fs.WindowsNativeDispatcher.*; +import static sun.nio.fs.WindowsConstants.*; + +/** + * Security related utility methods. + */ + +class WindowsSecurity { + private WindowsSecurity() { } + + // opens process token for given access + private static long openProcessToken(int access) { + try { + return OpenProcessToken(GetCurrentProcess(), access); + } catch (WindowsException x) { + return 0L; + } + } + + /** + * Returns the access token for this process with TOKEN_DUPLICATE access + */ + static final long processTokenWithDuplicateAccess = + openProcessToken(TOKEN_DUPLICATE); + + /** + * Returns the access token for this process with TOKEN_QUERY access + */ + static final long processTokenWithQueryAccess = + openProcessToken(TOKEN_QUERY); + + /** + * Returned by enablePrivilege when code may require a given privilege. + * The drop method should be invoked after the operation completes so as + * to revert the privilege. + */ + static interface Privilege { + void drop(); + } + + /** + * Attempts to enable the given privilege for this method. + */ + static Privilege enablePrivilege(String priv) { + final long pLuid; + try { + pLuid = LookupPrivilegeValue(priv); + } catch (WindowsException x) { + // indicates bug in caller + throw new AssertionError(x); + } + + long hToken = 0L; + boolean impersontating = false; + boolean elevated = false; + try { + hToken = OpenThreadToken(GetCurrentThread(), + TOKEN_ADJUST_PRIVILEGES, false); + if (hToken == 0L && processTokenWithDuplicateAccess != 0L) { + hToken = DuplicateTokenEx(processTokenWithDuplicateAccess, + (TOKEN_ADJUST_PRIVILEGES|TOKEN_IMPERSONATE)); + SetThreadToken(0L, hToken); + impersontating = true; + } + + if (hToken != 0L) { + AdjustTokenPrivileges(hToken, pLuid, SE_PRIVILEGE_ENABLED); + elevated = true; + } + } catch (WindowsException x) { + // nothing to do, privilege not enabled + } + + final long token = hToken; + final boolean stopImpersontating = impersontating; + final boolean needToRevert = elevated; + + return new Privilege() { + @Override + public void drop() { + try { + if (stopImpersontating) { + SetThreadToken(0L, 0L); + } else { + if (needToRevert) { + AdjustTokenPrivileges(token, pLuid, 0); + } + } + } catch (WindowsException x) { + // should not happen + throw new AssertionError(x); + } + } + }; + } +} diff --git a/src/windows/classes/sun/nio/fs/WindowsSecurityDescriptor.java b/src/windows/classes/sun/nio/fs/WindowsSecurityDescriptor.java new file mode 100644 index 000000000..0fa0725c5 --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsSecurityDescriptor.java @@ -0,0 +1,392 @@ +/* + * 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.ProviderMismatchException; +import java.nio.file.attribute.*; +import java.util.*; +import java.io.IOException; +import sun.misc.Unsafe; + +import static sun.nio.fs.WindowsNativeDispatcher.*; +import static sun.nio.fs.WindowsConstants.*; + +/** + * A SecurityDescriptor for use when setting a file's ACL or creating a file + * with an initial ACL. + */ + +class WindowsSecurityDescriptor { + private static final Unsafe unsafe = Unsafe.getUnsafe(); + + /** + * typedef struct _ACL { + * BYTE AclRevision; + * BYTE Sbz1; + * WORD AclSize; + * WORD AceCount; + * WORD Sbz2; + * } ACL; + * + * typedef struct _ACE_HEADER { + * BYTE AceType; + * BYTE AceFlags; + * WORD AceSize; + * } ACE_HEADER; + * + * typedef struct _ACCESS_ALLOWED_ACE { + * ACE_HEADER Header; + * ACCESS_MASK Mask; + * DWORD SidStart; + * } ACCESS_ALLOWED_ACE; + * + * typedef struct _ACCESS_DENIED_ACE { + * ACE_HEADER Header; + * ACCESS_MASK Mask; + * DWORD SidStart; + * } ACCESS_DENIED_ACE; + * + * typedef struct _SECURITY_DESCRIPTOR { + * BYTE Revision; + * BYTE Sbz1; + * SECURITY_DESCRIPTOR_CONTROL Control; + * PSID Owner; + * PSID Group; + * PACL Sacl; + * PACL Dacl; + * } SECURITY_DESCRIPTOR; + */ + private static final short SIZEOF_ACL = 8; + private static final short SIZEOF_ACCESS_ALLOWED_ACE = 12; + private static final short SIZEOF_ACCESS_DENIED_ACE = 12; + private static final short SIZEOF_SECURITY_DESCRIPTOR = 20; + + private static final short OFFSETOF_TYPE = 0; + private static final short OFFSETOF_FLAGS = 1; + private static final short OFFSETOF_ACCESS_MASK = 4; + private static final short OFFSETOF_SID = 8; + + // null security descriptor + private static final WindowsSecurityDescriptor NULL_DESCRIPTOR = + new WindowsSecurityDescriptor(); + + // native resources + private final List sidList; + private final NativeBuffer aclBuffer, sdBuffer; + + /** + * Creates the "null" SecurityDescriptor + */ + private WindowsSecurityDescriptor() { + this.sidList = null; + this.aclBuffer = null; + this.sdBuffer = null; + } + + /** + * Creates a SecurityDescriptor from the given ACL + */ + private WindowsSecurityDescriptor(List acl) throws IOException { + boolean initialized = false; + + // SECURITY: need to copy list in case size changes during processing + acl = new ArrayList(acl); + + // list of SIDs + sidList = new ArrayList(acl.size()); + try { + // initial size of ACL + int size = SIZEOF_ACL; + + // get the SID for each entry + for (AclEntry entry: acl) { + UserPrincipal user = entry.principal(); + if (!(user instanceof WindowsUserPrincipals.User)) + throw new ProviderMismatchException(); + String sidString = ((WindowsUserPrincipals.User)user).sidString(); + try { + long pSid = ConvertStringSidToSid(sidString); + sidList.add(pSid); + + // increase size to allow for entry + size += GetLengthSid(pSid) + + Math.max(SIZEOF_ACCESS_ALLOWED_ACE, SIZEOF_ACCESS_DENIED_ACE); + + } catch (WindowsException x) { + throw new IOException("Failed to get SID for " + user.getName() + + ": " + x.errorString()); + } + } + + // allocate memory for the ACL + aclBuffer = NativeBuffers.getNativeBuffer(size); + sdBuffer = NativeBuffers.getNativeBuffer(SIZEOF_SECURITY_DESCRIPTOR); + + InitializeAcl(aclBuffer.address(), size); + + // Add entry ACE to the ACL + int i = 0; + while (i < acl.size()) { + AclEntry entry = acl.get(i); + long pSid = sidList.get(i); + try { + encode(entry, pSid, aclBuffer.address()); + } catch (WindowsException x) { + throw new IOException("Failed to encode ACE: " + + x.errorString()); + } + i++; + } + + // initialize security descriptor and set DACL + InitializeSecurityDescriptor(sdBuffer.address()); + SetSecurityDescriptorDacl(sdBuffer.address(), aclBuffer.address()); + initialized = true; + } catch (WindowsException x) { + throw new IOException(x.getMessage()); + } finally { + // release resources if not completely initialized + if (!initialized) + release(); + } + } + + /** + * Releases memory associated with SecurityDescriptor + */ + void release() { + if (sdBuffer != null) + sdBuffer.release(); + if (aclBuffer != null) + aclBuffer.release(); + if (sidList != null) { + // release memory for SIDs + for (Long sid: sidList) { + LocalFree(sid); + } + } + } + + /** + * Returns address of SecurityDescriptor + */ + long address() { + return (sdBuffer == null) ? 0L : sdBuffer.address(); + } + + // decode Windows ACE to NFSv4 AclEntry + private static AclEntry decode(long aceAddress) + throws IOException + { + // map type + byte aceType = unsafe.getByte(aceAddress + OFFSETOF_TYPE); + if (aceType != ACCESS_ALLOWED_ACE_TYPE && aceType != ACCESS_DENIED_ACE_TYPE) + return null; + AclEntryType type; + if (aceType == ACCESS_ALLOWED_ACE_TYPE) { + type = AclEntryType.ALLOW; + } else { + type = AclEntryType.DENY; + } + + // map flags + byte aceFlags = unsafe.getByte(aceAddress + OFFSETOF_FLAGS); + Set flags = new HashSet(); + if ((aceFlags & OBJECT_INHERIT_ACE) != 0) + flags.add(AclEntryFlag.FILE_INHERIT); + if ((aceFlags & CONTAINER_INHERIT_ACE) != 0) + flags.add(AclEntryFlag.DIRECTORY_INHERIT); + if ((aceFlags & NO_PROPAGATE_INHERIT_ACE) != 0) + flags.add(AclEntryFlag.NO_PROPAGATE_INHERIT); + if ((aceFlags & INHERIT_ONLY_ACE) != 0) + flags.add(AclEntryFlag.INHERIT_ONLY); + + // map access mask + int mask = unsafe.getInt(aceAddress + OFFSETOF_ACCESS_MASK); + Set perms = new HashSet(); + if ((mask & FILE_READ_DATA) > 0) + perms.add(AclEntryPermission.READ_DATA); + if ((mask & FILE_WRITE_DATA) > 0) + perms.add(AclEntryPermission.WRITE_DATA); + if ((mask & FILE_APPEND_DATA ) > 0) + perms.add(AclEntryPermission.APPEND_DATA); + if ((mask & FILE_READ_EA) > 0) + perms.add(AclEntryPermission.READ_NAMED_ATTRS); + if ((mask & FILE_WRITE_EA) > 0) + perms.add(AclEntryPermission.WRITE_NAMED_ATTRS); + if ((mask & FILE_EXECUTE) > 0) + perms.add(AclEntryPermission.EXECUTE); + if ((mask & FILE_DELETE_CHILD ) > 0) + perms.add(AclEntryPermission.DELETE_CHILD); + if ((mask & FILE_READ_ATTRIBUTES) > 0) + perms.add(AclEntryPermission.READ_ATTRIBUTES); + if ((mask & FILE_WRITE_ATTRIBUTES) > 0) + perms.add(AclEntryPermission.WRITE_ATTRIBUTES); + if ((mask & DELETE) > 0) + perms.add(AclEntryPermission.DELETE); + if ((mask & READ_CONTROL) > 0) + perms.add(AclEntryPermission.READ_ACL); + if ((mask & WRITE_DAC) > 0) + perms.add(AclEntryPermission.WRITE_ACL); + if ((mask & WRITE_OWNER) > 0) + perms.add(AclEntryPermission.WRITE_OWNER); + if ((mask & SYNCHRONIZE) > 0) + perms.add(AclEntryPermission.SYNCHRONIZE); + + // lookup SID to create UserPrincipal + long sidAddress = aceAddress + OFFSETOF_SID; + UserPrincipal user = WindowsUserPrincipals.fromSid(sidAddress); + + return AclEntry.newBuilder() + .setType(type) + .setPrincipal(user) + .setFlags(flags).setPermissions(perms).build(); + } + + // encode NFSv4 AclEntry as Windows ACE to given ACL + private static void encode(AclEntry ace, long sidAddress, long aclAddress) + throws WindowsException + { + // ignore non-allow/deny entries for now + if (ace.type() != AclEntryType.ALLOW && ace.type() != AclEntryType.DENY) + return; + boolean allow = (ace.type() == AclEntryType.ALLOW); + + // map access mask + Set aceMask = ace.permissions(); + int mask = 0; + if (aceMask.contains(AclEntryPermission.READ_DATA)) + mask |= FILE_READ_DATA; + if (aceMask.contains(AclEntryPermission.WRITE_DATA)) + mask |= FILE_WRITE_DATA; + if (aceMask.contains(AclEntryPermission.APPEND_DATA)) + mask |= FILE_APPEND_DATA; + if (aceMask.contains(AclEntryPermission.READ_NAMED_ATTRS)) + mask |= FILE_READ_EA; + if (aceMask.contains(AclEntryPermission.WRITE_NAMED_ATTRS)) + mask |= FILE_WRITE_EA; + if (aceMask.contains(AclEntryPermission.EXECUTE)) + mask |= FILE_EXECUTE; + if (aceMask.contains(AclEntryPermission.DELETE_CHILD)) + mask |= FILE_DELETE_CHILD; + if (aceMask.contains(AclEntryPermission.READ_ATTRIBUTES)) + mask |= FILE_READ_ATTRIBUTES; + if (aceMask.contains(AclEntryPermission.WRITE_ATTRIBUTES)) + mask |= FILE_WRITE_ATTRIBUTES; + if (aceMask.contains(AclEntryPermission.DELETE)) + mask |= DELETE; + if (aceMask.contains(AclEntryPermission.READ_ACL)) + mask |= READ_CONTROL; + if (aceMask.contains(AclEntryPermission.WRITE_ACL)) + mask |= WRITE_DAC; + if (aceMask.contains(AclEntryPermission.WRITE_OWNER)) + mask |= WRITE_OWNER; + if (aceMask.contains(AclEntryPermission.SYNCHRONIZE)) + mask |= SYNCHRONIZE; + + // map flags + Set aceFlags = ace.flags(); + byte flags = 0; + if (aceFlags.contains(AclEntryFlag.FILE_INHERIT)) + flags |= OBJECT_INHERIT_ACE; + if (aceFlags.contains(AclEntryFlag.DIRECTORY_INHERIT)) + flags |= CONTAINER_INHERIT_ACE; + if (aceFlags.contains(AclEntryFlag.NO_PROPAGATE_INHERIT)) + flags |= NO_PROPAGATE_INHERIT_ACE; + if (aceFlags.contains(AclEntryFlag.INHERIT_ONLY)) + flags |= INHERIT_ONLY_ACE; + + if (allow) { + AddAccessAllowedAceEx(aclAddress, flags, mask, sidAddress); + } else { + AddAccessDeniedAceEx(aclAddress, flags, mask, sidAddress); + } + } + + /** + * Creates a security descriptor with a DACL representing the given ACL. + */ + static WindowsSecurityDescriptor create(List acl) + throws IOException + { + return new WindowsSecurityDescriptor(acl); + } + + /** + * Processes the array of attributes looking for the attribute "acl:acl". + * Returns security descriptor representing the ACL or the "null" security + * descriptor if the attribute is not in the array. + */ + @SuppressWarnings("unchecked") + static WindowsSecurityDescriptor fromAttribute(FileAttribute... attrs) + throws IOException + { + WindowsSecurityDescriptor sd = NULL_DESCRIPTOR; + for (FileAttribute attr: attrs) { + // if more than one ACL specified then last one wins + if (sd != NULL_DESCRIPTOR) + sd.release(); + if (attr == null) + throw new NullPointerException(); + if (attr.name().equals("acl:acl")) { + List acl = (List)attr.value(); + sd = new WindowsSecurityDescriptor(acl); + } else { + throw new UnsupportedOperationException("'" + attr.name() + + "' not supported as initial attribute"); + } + } + return sd; + } + + /** + * Extracts DACL from security descriptor. + */ + static List getAcl(long pSecurityDescriptor) throws IOException { + // get address of DACL + long aclAddress = GetSecurityDescriptorDacl(pSecurityDescriptor); + + // get ACE count + int aceCount = 0; + if (aclAddress == 0L) { + // no ACEs + aceCount = 0; + } else { + AclInformation aclInfo = GetAclInformation(aclAddress); + aceCount = aclInfo.aceCount(); + } + ArrayList result = new ArrayList(aceCount); + + // decode each of the ACEs to AclEntry objects + for (int i=0; i 2) && (path.charAt(2) == ':')) { + // "/c:/foo" --> "c:/foo" + path = path.substring(1); + } + } + return WindowsPath.parse(fs, path); + } +} diff --git a/src/windows/classes/sun/nio/fs/WindowsUserDefinedFileAttributeView.java b/src/windows/classes/sun/nio/fs/WindowsUserDefinedFileAttributeView.java new file mode 100644 index 000000000..db361c97e --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsUserDefinedFileAttributeView.java @@ -0,0 +1,342 @@ +/* + * 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.*; +import static java.nio.file.StandardOpenOption.*; +import java.nio.ByteBuffer; +import java.nio.channels.FileChannel; +import java.io.IOException; +import java.util.*; +import sun.misc.Unsafe; + +import static sun.nio.fs.WindowsNativeDispatcher.*; +import static sun.nio.fs.WindowsConstants.*; + +/** + * Windows emulation of NamedAttributeView using Alternative Data Streams + */ + +class WindowsUserDefinedFileAttributeView + extends AbstractUserDefinedFileAttributeView +{ + private static final Unsafe unsafe = Unsafe.getUnsafe(); + + // syntax to address named streams + private String join(String file, String name) { + if (name == null) + throw new NullPointerException("'name' is null"); + return file + ":" + name; + } + private String join(WindowsPath file, String name) throws WindowsException { + return join(file.getPathForWin32Calls(), name); + } + + private final WindowsPath file; + private final boolean followLinks; + + WindowsUserDefinedFileAttributeView(WindowsPath file, boolean followLinks) { + this.file = file; + this.followLinks = followLinks; + } + + // enumerates the file streams using FindFirstStream/FindNextStream APIs. + private List listUsingStreamEnumeration() throws IOException { + List list = new ArrayList(); + try { + FirstStream first = FindFirstStream(file.getPathForWin32Calls()); + if (first != null) { + long handle = first.handle(); + try { + // first stream is always ::$DATA for files + String name = first.name(); + if (!name.equals("::$DATA")) { + String[] segs = name.split(":"); + list.add(segs[1]); + } + while ((name = FindNextStream(handle)) != null) { + String[] segs = name.split(":"); + list.add(segs[1]); + } + } finally { + FindClose(handle); + } + } + } catch (WindowsException x) { + x.rethrowAsIOException(file); + } + return Collections.unmodifiableList(list); + } + + // enumerates the file streams by reading the stream headers using + // BackupRead + private List listUsingBackupRead() throws IOException { + long handle = -1L; + try { + int flags = FILE_FLAG_BACKUP_SEMANTICS; + if (!followLinks && file.getFileSystem().supportsLinks()) + flags |= FILE_FLAG_OPEN_REPARSE_POINT; + + handle = CreateFile(file.getPathForWin32Calls(), + GENERIC_READ, + FILE_SHARE_READ, // no write as we depend on file size + OPEN_EXISTING, + flags); + } catch (WindowsException x) { + x.rethrowAsIOException(file); + } + + // buffer to read stream header and stream name. + final int BUFFER_SIZE = 4096; + NativeBuffer buffer = null; + + // result with names of alternative data streams + final List list = new ArrayList(); + + try { + buffer = NativeBuffers.getNativeBuffer(BUFFER_SIZE); + long address = buffer.address(); + + /** + * typedef struct _WIN32_STREAM_ID { + * DWORD dwStreamId; + * DWORD dwStreamAttributes; + * LARGE_INTEGER Size; + * DWORD dwStreamNameSize; + * WCHAR cStreamName[ANYSIZE_ARRAY]; + * } WIN32_STREAM_ID; + */ + final int SIZEOF_STREAM_HEADER = 20; + final int OFFSETOF_STREAM_ID = 0; + final int OFFSETOF_STREAM_SIZE = 8; + final int OFFSETOF_STREAM_NAME_SIZE = 16; + + long context = 0L; + try { + for (;;) { + // read stream header + BackupResult result = BackupRead(handle, address, + SIZEOF_STREAM_HEADER, false, context); + context = result.context(); + if (result.bytesTransferred() == 0) + break; + + int streamId = unsafe.getInt(address + OFFSETOF_STREAM_ID); + long streamSize = unsafe.getLong(address + OFFSETOF_STREAM_SIZE); + int nameSize = unsafe.getInt(address + OFFSETOF_STREAM_NAME_SIZE); + + // read stream name + if (nameSize > 0) { + result = BackupRead(handle, address, nameSize, false, context); + if (result.bytesTransferred() != nameSize) + break; + } + + // check for alternative data stream + if (streamId == BACKUP_ALTERNATE_DATA) { + char[] nameAsArray = new char[nameSize/2]; + unsafe.copyMemory(null, address, nameAsArray, + Unsafe.ARRAY_CHAR_BASE_OFFSET, nameSize); + + String[] segs = new String(nameAsArray).split(":"); + if (segs.length == 3) + list.add(segs[1]); + } + + // sparse blocks not currently handled as documentation + // is not sufficient on how the spase block can be skipped. + if (streamId == BACKUP_SPARSE_BLOCK) { + throw new IOException("Spare blocks not handled"); + } + + // seek to end of stream + if (streamSize > 0L) { + BackupSeek(handle, streamSize, context); + } + } + } catch (WindowsException x) { + // failed to read or seek + throw new IOException(x.errorString()); + } finally { + // release context + if (context != 0L) { + try { + BackupRead(handle, 0L, 0, true, context); + } catch (WindowsException ignore) { } + } + } + } finally { + if (buffer != null) + buffer.release(); + CloseHandle(handle); + } + return Collections.unmodifiableList(list); + } + + @Override + public List list() throws IOException { + if (System.getSecurityManager() != null) + checkAccess(file.getPathForPermissionCheck(), true, false); + // use stream APIs on Windwos Server 2003 and newer + if (file.getFileSystem().supportsStreamEnumeration()) { + return listUsingStreamEnumeration(); + } else { + return listUsingBackupRead(); + } + } + + @Override + public int size(String name) throws IOException { + if (System.getSecurityManager() != null) + checkAccess(file.getPathForPermissionCheck(), true, false); + + // wrap with channel + FileChannel fc = null; + try { + Set opts = new HashSet(); + opts.add(READ); + if (!followLinks) + opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT); + fc = WindowsChannelFactory + .newFileChannel(join(file, name), null, opts, 0L); + } catch (WindowsException x) { + x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name)); + } + try { + long size = fc.size(); + if (size > Integer.MAX_VALUE) + throw new ArithmeticException("Stream too large"); + return (int)size; + } finally { + fc.close(); + } + } + + @Override + public int read(String name, ByteBuffer dst) throws IOException { + if (System.getSecurityManager() != null) + checkAccess(file.getPathForPermissionCheck(), true, false); + + // wrap with channel + FileChannel fc = null; + try { + Set opts = new HashSet(); + opts.add(READ); + if (!followLinks) + opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT); + fc = WindowsChannelFactory + .newFileChannel(join(file, name), null, opts, 0L); + } catch (WindowsException x) { + x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name)); + } + + // read to EOF (nothing we can do if I/O error occurs) + try { + if (fc.size() > dst.remaining()) + throw new IOException("Stream too large"); + int total = 0; + while (dst.hasRemaining()) { + int n = fc.read(dst); + if (n < 0) + break; + total += n; + } + return total; + } finally { + fc.close(); + } + } + + @Override + public int write(String name, ByteBuffer src) throws IOException { + if (System.getSecurityManager() != null) + checkAccess(file.getPathForPermissionCheck(), false, true); + + /** + * Creating a named stream will cause the unnamed stream to be created + * if it doesn't already exist. To avoid this we open the unnamed stream + * for reading and hope it isn't deleted/moved while we create or + * replace the named stream. Opening the file without sharing options + * may cause sharing violations with other programs that are accessing + * the unnamed stream. + */ + long handle = -1L; + try { + int flags = FILE_FLAG_BACKUP_SEMANTICS; + if (!followLinks) + flags |= FILE_FLAG_OPEN_REPARSE_POINT; + + handle = CreateFile(file.getPathForWin32Calls(), + GENERIC_READ, + (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), + OPEN_EXISTING, + flags); + } catch (WindowsException x) { + x.rethrowAsIOException(file); + } + try { + Set opts = new HashSet(); + if (!followLinks) + opts.add(WindowsChannelFactory.OPEN_REPARSE_POINT); + opts.add(CREATE); + opts.add(WRITE); + opts.add(StandardOpenOption.TRUNCATE_EXISTING); + FileChannel named = null; + try { + named = WindowsChannelFactory + .newFileChannel(join(file, name), null, opts, 0L); + } catch (WindowsException x) { + x.rethrowAsIOException(join(file.getPathForPermissionCheck(), name)); + } + // write value (nothing we can do if I/O error occurs) + try { + int rem = src.remaining(); + while (src.hasRemaining()) { + named.write(src); + } + return rem; + } finally { + named.close(); + } + } finally { + CloseHandle(handle); + } + } + + @Override + public void delete(String name) throws IOException { + if (System.getSecurityManager() != null) + checkAccess(file.getPathForPermissionCheck(), false, true); + + String path = WindowsLinkSupport.getFinalPath(file, followLinks); + String toDelete = join(path, name); + try { + DeleteFile(toDelete); + } catch (WindowsException x) { + x.rethrowAsIOException(toDelete); + } + } +} diff --git a/src/windows/classes/sun/nio/fs/WindowsUserPrincipals.java b/src/windows/classes/sun/nio/fs/WindowsUserPrincipals.java new file mode 100644 index 000000000..caf36f17c --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsUserPrincipals.java @@ -0,0 +1,169 @@ +/* + * 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.*; +import java.io.IOException; + +import static sun.nio.fs.WindowsConstants.*; +import static sun.nio.fs.WindowsNativeDispatcher.*; + +class WindowsUserPrincipals { + private WindowsUserPrincipals() { } + + static class User implements UserPrincipal { + // String representation of SID + private final String sidString; + + // SID type + private final int sidType; + + // Account name (if available) or SID + private final String accountName; + + User(String sidString, int sidType, String accountName) { + this.sidString = sidString; + this.sidType = sidType; + this.accountName = accountName; + } + + // package-private + String sidString() { + return sidString; + } + + @Override + public String getName() { + return accountName; + } + + @Override + public String toString() { + String type; + switch (sidType) { + case SidTypeUser : type = "User"; break; + case SidTypeGroup : type = "Group"; break; + case SidTypeDomain : type = "Domain"; break; + case SidTypeAlias : type = "Alias"; break; + case SidTypeWellKnownGroup : type = "Well-known group"; break; + case SidTypeDeletedAccount : type = "Deleted"; break; + case SidTypeInvalid : type = "Invalid"; break; + case SidTypeComputer : type = "Computer"; break; + default: type = "Unknown"; + } + return accountName + " (" + type + ")"; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + if (!(obj instanceof WindowsUserPrincipals.User)) + return false; + WindowsUserPrincipals.User other = (WindowsUserPrincipals.User)obj; + return this.sidString.equals(other.sidString); + } + + @Override + public int hashCode() { + return sidString.hashCode(); + } + } + + static class Group extends User implements GroupPrincipal { + Group(String sidString, int sidType, String accountName) { + super(sidString, sidType, accountName); + } + } + + static UserPrincipal fromSid(long sidAddress) throws IOException { + String sidString; + try { + sidString = ConvertSidToStringSid(sidAddress); + if (sidString == null) { + // pre-Windows XP system? + throw new AssertionError(); + } + } catch (WindowsException x) { + throw new IOException("Unable to convert SID to String: " + + x.errorString()); + } + + // lookup account; if not available then use the SID as the name + Account account = null; + String name; + try { + account = LookupAccountSid(sidAddress); + name = account.domain() + "\\" + account.name(); + } catch (WindowsException x) { + name = sidString; + } + + int sidType = (account == null) ? SidTypeUnknown : account.use(); + if ((sidType == SidTypeGroup) || + (sidType == SidTypeWellKnownGroup) || + (sidType == SidTypeAlias)) // alias for local group + { + return new Group(sidString, sidType, name); + } else { + return new User(sidString, sidType, name); + } + } + + static UserPrincipal lookup(String name) throws IOException { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new RuntimePermission("lookupUserInformation")); + } + + // invoke LookupAccountName to get buffer size needed for SID + int size = 0; + try { + size = LookupAccountName(name, 0L, 0); + } catch (WindowsException x) { + if (x.lastError() == ERROR_NONE_MAPPED) + throw new UserPrincipalNotFoundException(name); + throw new IOException(name + ": " + x.errorString()); + } + assert size > 0; + + // allocate buffer and re-invoke LookupAccountName get SID + NativeBuffer sidBuffer = NativeBuffers.getNativeBuffer(size); + try { + int newSize = LookupAccountName(name, sidBuffer.address(), size); + if (newSize != size) { + // can this happen? + throw new AssertionError("SID change during lookup"); + } + + // return user principal + return fromSid(sidBuffer.address()); + } catch (WindowsException x) { + throw new IOException(name + ": " + x.errorString()); + } finally { + sidBuffer.release(); + } + } +} diff --git a/src/windows/classes/sun/nio/fs/WindowsWatchService.java b/src/windows/classes/sun/nio/fs/WindowsWatchService.java new file mode 100644 index 000000000..6a5190784 --- /dev/null +++ b/src/windows/classes/sun/nio/fs/WindowsWatchService.java @@ -0,0 +1,582 @@ +/* + * 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.*; +import java.io.IOException; +import java.util.*; +import com.sun.nio.file.ExtendedWatchEventModifier; +import sun.misc.Unsafe; + +import static sun.nio.fs.WindowsNativeDispatcher.*; +import static sun.nio.fs.WindowsConstants.*; + +/* + * Win32 implementation of WatchService based on ReadDirectoryChangesW. + */ + +class WindowsWatchService + extends AbstractWatchService +{ + private final Unsafe unsafe = Unsafe.getUnsafe(); + + // background thread to service I/O completion port + private final Poller poller; + + /** + * Creates an I/O completion port and a daemon thread to service it + */ + WindowsWatchService(WindowsFileSystem fs) throws IOException { + // create I/O completion port + long port = 0L; + try { + port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0); + } catch (WindowsException x) { + throw new IOException(x.getMessage()); + } + + this.poller = new Poller(fs, this, port); + this.poller.start(); + } + + @Override + WatchKey register(Path path, + WatchEvent.Kind[] events, + WatchEvent.Modifier... modifiers) + throws IOException + { + // delegate to poller + return poller.register(path, events, modifiers); + } + + @Override + void implClose() throws IOException { + // delegate to poller + poller.close(); + } + + /** + * Windows implementation of WatchKey. + */ + private class WindowsWatchKey extends AbstractWatchKey { + // file key (used to detect existing registrations) + private FileKey fileKey; + + // handle to directory + private volatile long handle = INVALID_HANDLE_VALUE; + + // interest events + private Set> events; + + // subtree + private boolean watchSubtree; + + // buffer for change events + private NativeBuffer buffer; + + // pointer to bytes returned (in buffer) + private long countAddress; + + // pointer to overlapped structure (in buffer) + private long overlappedAddress; + + // completion key (used to map I/O completion to WatchKey) + private int completionKey; + + WindowsWatchKey(AbstractWatchService watcher, FileKey fileKey) { + super(watcher); + this.fileKey = fileKey; + } + + WindowsWatchKey init(long handle, + Set> events, + boolean watchSubtree, + NativeBuffer buffer, + long countAddress, + long overlappedAddress, + int completionKey) + { + this.handle = handle; + this.events = events; + this.watchSubtree = watchSubtree; + this.buffer = buffer; + this.countAddress = countAddress; + this.overlappedAddress = overlappedAddress; + this.completionKey = completionKey; + return this; + } + + long handle() { + return handle; + } + + Set> events() { + return events; + } + + void setEvents(Set> events) { + this.events = events; + } + + boolean watchSubtree() { + return watchSubtree; + } + + NativeBuffer buffer() { + return buffer; + } + + long countAddress() { + return countAddress; + } + + long overlappedAddress() { + return overlappedAddress; + } + + FileKey fileKey() { + return fileKey; + } + + int completionKey() { + return completionKey; + } + + // close directory and release buffer + void releaseResources() { + CloseHandle(handle); + buffer.cleaner().clean(); + } + + // Invalidate key by closing directory and releasing buffer + void invalidate() { + releaseResources(); + handle = INVALID_HANDLE_VALUE; + buffer = null; + countAddress = 0; + overlappedAddress = 0; + } + + @Override + public boolean isValid() { + return handle != INVALID_HANDLE_VALUE; + } + + @Override + public void cancel() { + if (isValid()) { + // delegate to poller + poller.cancel(this); + } + } + } + + // file key to unique identify (open) directory + private static class FileKey { + private final int volSerialNumber; + private final int fileIndexHigh; + private final int fileIndexLow; + + FileKey(int volSerialNumber, int fileIndexHigh, int fileIndexLow) { + this.volSerialNumber = volSerialNumber; + this.fileIndexHigh = fileIndexHigh; + this.fileIndexLow = fileIndexLow; + } + + @Override + public int hashCode() { + return volSerialNumber ^ fileIndexHigh ^ fileIndexLow; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) + return true; + if (!(obj instanceof FileKey)) + return false; + FileKey other = (FileKey)obj; + if (this.volSerialNumber != other.volSerialNumber) return false; + if (this.fileIndexHigh != other.fileIndexHigh) return false; + if (this.fileIndexLow != other.fileIndexLow) return false; + return true; + } + } + + // all change events + private static final int ALL_FILE_NOTIFY_EVENTS = + FILE_NOTIFY_CHANGE_FILE_NAME | + FILE_NOTIFY_CHANGE_DIR_NAME | + FILE_NOTIFY_CHANGE_ATTRIBUTES | + FILE_NOTIFY_CHANGE_SIZE | + FILE_NOTIFY_CHANGE_LAST_WRITE | + FILE_NOTIFY_CHANGE_CREATION | + FILE_NOTIFY_CHANGE_SECURITY; + + /** + * Background thread to service I/O completion port. + */ + private class Poller extends AbstractPoller { + /* + * typedef struct _OVERLAPPED { + * DWORD Internal; + * DWORD InternalHigh; + * DWORD Offset; + * DWORD OffsetHigh; + * HANDLE hEvent; + * } OVERLAPPED; + */ + private static final short SIZEOF_DWORD = 4; + private static final short SIZEOF_OVERLAPPED = 32; // 20 on 32-bit + + /* + * typedef struct _FILE_NOTIFY_INFORMATION { + * DWORD NextEntryOffset; + * DWORD Action; + * DWORD FileNameLength; + * WCHAR FileName[1]; + * } FileNameLength; + */ + private static final short OFFSETOF_NEXTENTRYOFFSET = 0; + private static final short OFFSETOF_ACTION = 4; + private static final short OFFSETOF_FILENAMELENGTH = 8; + private static final short OFFSETOF_FILENAME = 12; + + // size of per-directory buffer for events (FIXME - make this configurable) + private static final int CHANGES_BUFFER_SIZE = 16 * 1024; + + private final WindowsFileSystem fs; + private final WindowsWatchService watcher; + private final long port; + + // maps completion key to WatchKey + private final Map int2key; + + // maps file key to WatchKey + private final Map fk2key; + + // unique completion key for each directory + private int lastCompletionKey; + + Poller(WindowsFileSystem fs, WindowsWatchService watcher, long port) { + this.fs = fs; + this.watcher = watcher; + this.port = port; + this.int2key = new HashMap(); + this.fk2key = new HashMap(); + this.lastCompletionKey = 0; + } + + @Override + void wakeup() throws IOException { + try { + PostQueuedCompletionStatus(port, 0); + } catch (WindowsException x) { + throw new IOException(x.getMessage()); + } + } + + /** + * Register a directory for changes as follows: + * + * 1. Open directory + * 2. Read its attributes (and check it really is a directory) + * 3. Assign completion key and associated handle with completion port + * 4. Call ReadDirectoryChangesW to start (async) read of changes + * 5. Create or return existing key representing registration + */ + @Override + Object implRegister(Path obj, + Set> events, + WatchEvent.Modifier... modifiers) + { + WindowsPath dir = (WindowsPath)obj; + boolean watchSubtree = false; + + // FILE_TREE modifier allowed + for (WatchEvent.Modifier modifier: modifiers) { + if (modifier == ExtendedWatchEventModifier.FILE_TREE) { + watchSubtree = true; + continue; + } else { + if (modifier == null) + return new NullPointerException(); + if (modifier instanceof com.sun.nio.file.SensitivityWatchEventModifier) + continue; // ignore + return new UnsupportedOperationException("Modifier not supported"); + } + } + + // open directory + long handle = -1L; + try { + handle = CreateFile(dir.getPathForWin32Calls(), + FILE_LIST_DIRECTORY, + (FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE), + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED); + } catch (WindowsException x) { + return x.asIOException(dir); + } + + boolean registered = false; + try { + // read attributes and check file is a directory + WindowsFileAttributes attrs = null; + try { + attrs = WindowsFileAttributes.readAttributes(handle); + } catch (WindowsException x) { + return x.asIOException(dir); + } + if (!attrs.isDirectory()) { + return new NotDirectoryException(dir.getPathForExceptionMessage()); + } + + // check if this directory is already registered + FileKey fk = new FileKey(attrs.volSerialNumber(), + attrs.fileIndexHigh(), + attrs.fileIndexLow()); + WindowsWatchKey existing = fk2key.get(fk); + + // if already registered and we're not changing the subtree + // modifier then simply update the event and return the key. + if (existing != null && watchSubtree == existing.watchSubtree()) { + existing.setEvents(events); + return existing; + } + + // unique completion key (skip 0) + int completionKey = ++lastCompletionKey; + if (completionKey == 0) + completionKey = ++lastCompletionKey; + + // associate handle with completion port + try { + CreateIoCompletionPort(handle, port, completionKey); + } catch (WindowsException x) { + return new IOException(x.getMessage()); + } + + // allocate memory for events, including space for other structures + // needed to do overlapped I/O + int size = CHANGES_BUFFER_SIZE + SIZEOF_DWORD + SIZEOF_OVERLAPPED; + NativeBuffer buffer = NativeBuffers.getNativeBuffer(size); + + long bufferAddress = buffer.address(); + long overlappedAddress = bufferAddress + size - SIZEOF_OVERLAPPED; + long countAddress = overlappedAddress - SIZEOF_DWORD; + + // start async read of changes to directory + try { + ReadDirectoryChangesW(handle, + bufferAddress, + CHANGES_BUFFER_SIZE, + watchSubtree, + ALL_FILE_NOTIFY_EVENTS, + countAddress, + overlappedAddress); + } catch (WindowsException x) { + buffer.release(); + return new IOException(x.getMessage()); + } + + WindowsWatchKey watchKey; + if (existing == null) { + // not registered so create new watch key + watchKey = new WindowsWatchKey(watcher, fk) + .init(handle, events, watchSubtree, buffer, countAddress, + overlappedAddress, completionKey); + // map file key to watch key + fk2key.put(fk, watchKey); + } else { + // directory already registered so need to: + // 1. remove mapping from old completion key to existing watch key + // 2. release existing key's resources (handle/buffer) + // 3. re-initialize key with new handle/buffer + int2key.remove(existing.completionKey()); + existing.releaseResources(); + watchKey = existing.init(handle, events, watchSubtree, buffer, + countAddress, overlappedAddress, completionKey); + } + // map completion map to watch key + int2key.put(completionKey, watchKey); + + registered = true; + return watchKey; + + } finally { + if (!registered) CloseHandle(handle); + } + } + + // cancel single key + @Override + void implCancelKey(WatchKey obj) { + WindowsWatchKey key = (WindowsWatchKey)obj; + if (key.isValid()) { + fk2key.remove(key.fileKey()); + int2key.remove(key.completionKey()); + key.invalidate(); + } + } + + // close watch service + @Override + void implCloseAll() { + // cancel all keys + for (Map.Entry entry: int2key.entrySet()) { + entry.getValue().invalidate(); + } + fk2key.clear(); + int2key.clear(); + + // close I/O completion port + CloseHandle(port); + } + + // Translate file change action into watch event + private WatchEvent.Kind translateActionToEvent(int action) + { + switch (action) { + case FILE_ACTION_MODIFIED : + return StandardWatchEventKind.ENTRY_MODIFY; + + case FILE_ACTION_ADDED : + case FILE_ACTION_RENAMED_NEW_NAME : + return StandardWatchEventKind.ENTRY_CREATE; + + case FILE_ACTION_REMOVED : + case FILE_ACTION_RENAMED_OLD_NAME : + return StandardWatchEventKind.ENTRY_DELETE; + + default : + return null; // action not recognized + } + } + + // process events (list of FILE_NOTIFY_INFORMATION structures) + private void processEvents(WindowsWatchKey key, int size) { + long address = key.buffer().address(); + + int nextOffset; + do { + int action = unsafe.getInt(address + OFFSETOF_ACTION); + + // map action to event + WatchEvent.Kind kind = translateActionToEvent(action); + if (key.events().contains(kind)) { + // copy the name + int nameLengthInBytes = unsafe.getInt(address + OFFSETOF_FILENAMELENGTH); + if ((nameLengthInBytes % 2) != 0) { + throw new AssertionError("FileNameLength.FileNameLength is not a multiple of 2"); + } + char[] nameAsArray = new char[nameLengthInBytes/2]; + unsafe.copyMemory(null, address + OFFSETOF_FILENAME, nameAsArray, + Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes); + + // create FileName and queue event + WindowsPath name = WindowsPath + .createFromNormalizedPath(fs, new String(nameAsArray)); + key.signalEvent(kind, name); + } + + // next event + nextOffset = unsafe.getInt(address + OFFSETOF_NEXTENTRYOFFSET); + address += (long)nextOffset; + } while (nextOffset != 0); + } + + /** + * Poller main loop + */ + @Override + public void run() { + for (;;) { + CompletionStatus info = null; + try { + info = GetQueuedCompletionStatus(port); + } catch (WindowsException x) { + // this should not happen + x.printStackTrace(); + return; + } + + // wakeup + if (info.completionKey() == 0) { + boolean shutdown = processRequests(); + if (shutdown) { + return; + } + continue; + } + + // map completionKey to get WatchKey + WindowsWatchKey key = int2key.get(info.completionKey()); + if (key == null) { + // We get here when a registration is changed. In that case + // the directory is closed which causes an event with the + // old completion key. + continue; + } + + // ReadDirectoryChangesW failed + if (info.error() != 0) { + // buffer overflow + if (info.error() == ERROR_NOTIFY_ENUM_DIR) { + key.signalEvent(StandardWatchEventKind.OVERFLOW, null); + } else { + // other error so cancel key + implCancelKey(key); + key.signal(); + } + continue; + } + + // process the events + if (info.bytesTransferred() > 0) { + processEvents(key, info.bytesTransferred()); + } else { + // insufficient buffer size + key.signalEvent(StandardWatchEventKind.OVERFLOW, null); + } + + // start read for next batch of changes + try { + ReadDirectoryChangesW(key.handle(), + key.buffer().address(), + CHANGES_BUFFER_SIZE, + key.watchSubtree(), + ALL_FILE_NOTIFY_EVENTS, + key.countAddress(), + key.overlappedAddress()); + } catch (WindowsException x) { + // no choice but to cancel key + implCancelKey(key); + key.signal(); + } + } + } + } +} diff --git a/src/windows/native/sun/nio/ch/FileChannelImpl.c b/src/windows/native/sun/nio/ch/FileChannelImpl.c index da96a109a..d1056b883 100644 --- a/src/windows/native/sun/nio/ch/FileChannelImpl.c +++ b/src/windows/native/sun/nio/ch/FileChannelImpl.c @@ -1,5 +1,5 @@ /* - * Copyright 2000-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-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 @@ -34,10 +34,6 @@ static jfieldID chan_fd; /* id for jobject 'fd' in java.io.FileChannel */ - -/* false for 95/98/ME, true for NT/W2K */ -static jboolean onNT = JNI_FALSE; - /************************************************************** * static method to store field ID's in initializers * and retrieve the allocation granularity @@ -47,15 +43,9 @@ Java_sun_nio_ch_FileChannelImpl_initIDs(JNIEnv *env, jclass clazz) { SYSTEM_INFO si; jint align; - OSVERSIONINFO ver; GetSystemInfo(&si); align = si.dwAllocationGranularity; chan_fd = (*env)->GetFieldID(env, clazz, "fd", "Ljava/io/FileDescriptor;"); - ver.dwOSVersionInfoSize = sizeof(ver); - GetVersionEx(&ver); - if (ver.dwPlatformId == VER_PLATFORM_WIN32_NT) { - onNT = JNI_TRUE; - } return align; } @@ -146,56 +136,6 @@ Java_sun_nio_ch_FileChannelImpl_unmap0(JNIEnv *env, jobject this, return 0; } -JNIEXPORT jint JNICALL -Java_sun_nio_ch_FileChannelImpl_truncate0(JNIEnv *env, jobject this, - jobject fdo, jlong size) -{ - DWORD lowPos = 0; - long highPos = 0; - BOOL result = 0; - HANDLE h = (HANDLE)(handleval(env, fdo)); - - lowPos = (DWORD)size; - highPos = (long)(size >> 32); - lowPos = SetFilePointer(h, lowPos, &highPos, FILE_BEGIN); - if (lowPos == ((DWORD)-1)) { - if (GetLastError() != ERROR_SUCCESS) { - JNU_ThrowIOExceptionWithLastError(env, "Truncation failed"); - return IOS_THROWN; - } - } - result = SetEndOfFile(h); - if (result == 0) { - JNU_ThrowIOExceptionWithLastError(env, "Truncation failed"); - return IOS_THROWN; - } - return 0; -} - - -JNIEXPORT jint JNICALL -Java_sun_nio_ch_FileChannelImpl_force0(JNIEnv *env, jobject this, - jobject fdo, jboolean md) -{ - int result = 0; - HANDLE h = (HANDLE)(handleval(env, fdo)); - - if (h != INVALID_HANDLE_VALUE) { - result = FlushFileBuffers(h); - if (result == 0) { - int error = GetLastError(); - if (error != ERROR_ACCESS_DENIED) { - JNU_ThrowIOExceptionWithLastError(env, "Force failed"); - return IOS_THROWN; - } - } - } else { - JNU_ThrowIOExceptionWithLastError(env, "Force failed"); - return IOS_THROWN; - } - return 0; -} - JNIEXPORT jlong JNICALL Java_sun_nio_ch_FileChannelImpl_position0(JNIEnv *env, jobject this, jobject fdo, jlong offset) @@ -220,23 +160,6 @@ Java_sun_nio_ch_FileChannelImpl_position0(JNIEnv *env, jobject this, return (((jlong)highPos) << 32) | lowPos; } -JNIEXPORT jlong JNICALL -Java_sun_nio_ch_FileChannelImpl_size0(JNIEnv *env, jobject this, jobject fdo) -{ - DWORD sizeLow = 0; - DWORD sizeHigh = 0; - HANDLE h = (HANDLE)(handleval(env, fdo)); - - sizeLow = GetFileSize(h, &sizeHigh); - if (sizeLow == ((DWORD)-1)) { - if (GetLastError() != ERROR_SUCCESS) { - JNU_ThrowIOExceptionWithLastError(env, "Size failed"); - return IOS_THROWN; - } - } - return (((jlong)sizeHigh) << 32) | sizeLow; -} - JNIEXPORT void JNICALL Java_sun_nio_ch_FileChannelImpl_close0(JNIEnv *env, jobject this, jobject fdo) { @@ -257,99 +180,3 @@ Java_sun_nio_ch_FileChannelImpl_transferTo0(JNIEnv *env, jobject this, { return IOS_UNSUPPORTED; } - -JNIEXPORT jint JNICALL -Java_sun_nio_ch_FileChannelImpl_lock0(JNIEnv *env, jobject this, jobject fdo, - jboolean block, jlong pos, jlong size, - jboolean shared) -{ - HANDLE h = (HANDLE)(handleval(env, fdo)); - DWORD lowPos = (DWORD)pos; - long highPos = (long)(pos >> 32); - DWORD lowNumBytes = (DWORD)size; - DWORD highNumBytes = (DWORD)(size >> 32); - jint result = 0; - if (onNT) { - DWORD flags = 0; - OVERLAPPED o; - o.hEvent = 0; - o.Offset = lowPos; - o.OffsetHigh = highPos; - if (block == JNI_FALSE) { - flags |= LOCKFILE_FAIL_IMMEDIATELY; - } - if (shared == JNI_FALSE) { - flags |= LOCKFILE_EXCLUSIVE_LOCK; - } - result = LockFileEx(h, flags, 0, lowNumBytes, highNumBytes, &o); - if (result == 0) { - int error = GetLastError(); - if (error != ERROR_LOCK_VIOLATION) { - JNU_ThrowIOExceptionWithLastError(env, "Lock failed"); - return sun_nio_ch_FileChannelImpl_NO_LOCK; - } - if (flags & LOCKFILE_FAIL_IMMEDIATELY) { - return sun_nio_ch_FileChannelImpl_NO_LOCK; - } - JNU_ThrowIOExceptionWithLastError(env, "Lock failed"); - return sun_nio_ch_FileChannelImpl_NO_LOCK; - } - return sun_nio_ch_FileChannelImpl_LOCKED; - } else { - for(;;) { - if (size > 0x7fffffff) { - size = 0x7fffffff; - } - lowNumBytes = (DWORD)size; - highNumBytes = 0; - result = LockFile(h, lowPos, highPos, lowNumBytes, highNumBytes); - if (result != 0) { - if (shared == JNI_TRUE) { - return sun_nio_ch_FileChannelImpl_RET_EX_LOCK; - } else { - return sun_nio_ch_FileChannelImpl_LOCKED; - } - } else { - int error = GetLastError(); - if (error != ERROR_LOCK_VIOLATION) { - JNU_ThrowIOExceptionWithLastError(env, "Lock failed"); - return sun_nio_ch_FileChannelImpl_NO_LOCK; - } - if (block == JNI_FALSE) { - return sun_nio_ch_FileChannelImpl_NO_LOCK; - } - } - Sleep(100); - } - } - return sun_nio_ch_FileChannelImpl_NO_LOCK; -} - -JNIEXPORT void JNICALL -Java_sun_nio_ch_FileChannelImpl_release0(JNIEnv *env, jobject this, - jobject fdo, jlong pos, jlong size) -{ - HANDLE h = (HANDLE)(handleval(env, fdo)); - DWORD lowPos = (DWORD)pos; - long highPos = (long)(pos >> 32); - DWORD lowNumBytes = (DWORD)size; - DWORD highNumBytes = (DWORD)(size >> 32); - jint result = 0; - if (onNT) { - OVERLAPPED o; - o.hEvent = 0; - o.Offset = lowPos; - o.OffsetHigh = highPos; - result = UnlockFileEx(h, 0, lowNumBytes, highNumBytes, &o); - } else { - if (size > 0x7fffffff) { - size = 0x7fffffff; - } - lowNumBytes = (DWORD)size; - highNumBytes = 0; - result = UnlockFile(h, lowPos, highPos, lowNumBytes, highNumBytes); - } - if (result == 0) { - JNU_ThrowIOExceptionWithLastError(env, "Release failed"); - } -} diff --git a/src/windows/native/sun/nio/ch/FileDispatcher.c b/src/windows/native/sun/nio/ch/FileDispatcherImpl.c similarity index 67% rename from src/windows/native/sun/nio/ch/FileDispatcher.c rename to src/windows/native/sun/nio/ch/FileDispatcherImpl.c index a3c3985e7..a65ad90e2 100644 --- a/src/windows/native/sun/nio/ch/FileDispatcher.c +++ b/src/windows/native/sun/nio/ch/FileDispatcherImpl.c @@ -1,5 +1,5 @@ /* - * Copyright 2000-2003 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2000-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 @@ -28,18 +28,18 @@ #include "jni_util.h" #include "jvm.h" #include "jlong.h" -#include "sun_nio_ch_FileDispatcher.h" +#include "sun_nio_ch_FileDispatcherImpl.h" #include #include "nio.h" #include "nio_util.h" /************************************************************** - * FileDispatcher.c + * FileDispatcherImpl.c */ JNIEXPORT jint JNICALL -Java_sun_nio_ch_FileDispatcher_read0(JNIEnv *env, jclass clazz, jobject fdo, +Java_sun_nio_ch_FileDispatcherImpl_read0(JNIEnv *env, jclass clazz, jobject fdo, jlong address, jint len) { DWORD read = 0; @@ -70,7 +70,7 @@ Java_sun_nio_ch_FileDispatcher_read0(JNIEnv *env, jclass clazz, jobject fdo, } JNIEXPORT jlong JNICALL -Java_sun_nio_ch_FileDispatcher_readv0(JNIEnv *env, jclass clazz, jobject fdo, +Java_sun_nio_ch_FileDispatcherImpl_readv0(JNIEnv *env, jclass clazz, jobject fdo, jlong address, jint len) { DWORD read = 0; @@ -119,7 +119,7 @@ Java_sun_nio_ch_FileDispatcher_readv0(JNIEnv *env, jclass clazz, jobject fdo, } JNIEXPORT jint JNICALL -Java_sun_nio_ch_FileDispatcher_pread0(JNIEnv *env, jclass clazz, jobject fdo, +Java_sun_nio_ch_FileDispatcherImpl_pread0(JNIEnv *env, jclass clazz, jobject fdo, jlong address, jint len, jlong offset) { DWORD read = 0; @@ -182,7 +182,7 @@ Java_sun_nio_ch_FileDispatcher_pread0(JNIEnv *env, jclass clazz, jobject fdo, } JNIEXPORT jint JNICALL -Java_sun_nio_ch_FileDispatcher_write0(JNIEnv *env, jclass clazz, jobject fdo, +Java_sun_nio_ch_FileDispatcherImpl_write0(JNIEnv *env, jclass clazz, jobject fdo, jlong address, jint len) { BOOL result = 0; @@ -205,7 +205,7 @@ Java_sun_nio_ch_FileDispatcher_write0(JNIEnv *env, jclass clazz, jobject fdo, } JNIEXPORT jlong JNICALL -Java_sun_nio_ch_FileDispatcher_writev0(JNIEnv *env, jclass clazz, jobject fdo, +Java_sun_nio_ch_FileDispatcherImpl_writev0(JNIEnv *env, jclass clazz, jobject fdo, jlong address, jint len) { BOOL result = 0; @@ -244,7 +244,7 @@ Java_sun_nio_ch_FileDispatcher_writev0(JNIEnv *env, jclass clazz, jobject fdo, } JNIEXPORT jint JNICALL -Java_sun_nio_ch_FileDispatcher_pwrite0(JNIEnv *env, jclass clazz, jobject fdo, +Java_sun_nio_ch_FileDispatcherImpl_pwrite0(JNIEnv *env, jclass clazz, jobject fdo, jlong address, jint len, jlong offset) { BOOL result = 0; @@ -295,6 +295,130 @@ Java_sun_nio_ch_FileDispatcher_pwrite0(JNIEnv *env, jclass clazz, jobject fdo, return convertReturnVal(env, (jint)written, JNI_FALSE); } +JNIEXPORT jint JNICALL +Java_sun_nio_ch_FileDispatcherImpl_force0(JNIEnv *env, jobject this, + jobject fdo, jboolean md) +{ + int result = 0; + HANDLE h = (HANDLE)(handleval(env, fdo)); + + if (h != INVALID_HANDLE_VALUE) { + result = FlushFileBuffers(h); + if (result == 0) { + int error = GetLastError(); + if (error != ERROR_ACCESS_DENIED) { + JNU_ThrowIOExceptionWithLastError(env, "Force failed"); + return IOS_THROWN; + } + } + } else { + JNU_ThrowIOExceptionWithLastError(env, "Force failed"); + return IOS_THROWN; + } + return 0; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_FileDispatcherImpl_truncate0(JNIEnv *env, jobject this, + jobject fdo, jlong size) +{ + DWORD lowPos = 0; + long highPos = 0; + BOOL result = 0; + HANDLE h = (HANDLE)(handleval(env, fdo)); + + lowPos = (DWORD)size; + highPos = (long)(size >> 32); + lowPos = SetFilePointer(h, lowPos, &highPos, FILE_BEGIN); + if (lowPos == ((DWORD)-1)) { + if (GetLastError() != ERROR_SUCCESS) { + JNU_ThrowIOExceptionWithLastError(env, "Truncation failed"); + return IOS_THROWN; + } + } + result = SetEndOfFile(h); + if (result == 0) { + JNU_ThrowIOExceptionWithLastError(env, "Truncation failed"); + return IOS_THROWN; + } + return 0; +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_ch_FileDispatcherImpl_size0(JNIEnv *env, jobject this, jobject fdo) +{ + DWORD sizeLow = 0; + DWORD sizeHigh = 0; + HANDLE h = (HANDLE)(handleval(env, fdo)); + + sizeLow = GetFileSize(h, &sizeHigh); + if (sizeLow == ((DWORD)-1)) { + if (GetLastError() != ERROR_SUCCESS) { + JNU_ThrowIOExceptionWithLastError(env, "Size failed"); + return IOS_THROWN; + } + } + return (((jlong)sizeHigh) << 32) | sizeLow; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_FileDispatcherImpl_lock0(JNIEnv *env, jobject this, jobject fdo, + jboolean block, jlong pos, jlong size, + jboolean shared) +{ + HANDLE h = (HANDLE)(handleval(env, fdo)); + DWORD lowPos = (DWORD)pos; + long highPos = (long)(pos >> 32); + DWORD lowNumBytes = (DWORD)size; + DWORD highNumBytes = (DWORD)(size >> 32); + BOOL result; + DWORD flags = 0; + OVERLAPPED o; + o.hEvent = 0; + o.Offset = lowPos; + o.OffsetHigh = highPos; + if (block == JNI_FALSE) { + flags |= LOCKFILE_FAIL_IMMEDIATELY; + } + if (shared == JNI_FALSE) { + flags |= LOCKFILE_EXCLUSIVE_LOCK; + } + result = LockFileEx(h, flags, 0, lowNumBytes, highNumBytes, &o); + if (result == 0) { + int error = GetLastError(); + if (error != ERROR_LOCK_VIOLATION) { + JNU_ThrowIOExceptionWithLastError(env, "Lock failed"); + return sun_nio_ch_FileDispatcherImpl_NO_LOCK; + } + if (flags & LOCKFILE_FAIL_IMMEDIATELY) { + return sun_nio_ch_FileDispatcherImpl_NO_LOCK; + } + JNU_ThrowIOExceptionWithLastError(env, "Lock failed"); + return sun_nio_ch_FileDispatcherImpl_NO_LOCK; + } + return sun_nio_ch_FileDispatcherImpl_LOCKED; +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_FileDispatcherImpl_release0(JNIEnv *env, jobject this, + jobject fdo, jlong pos, jlong size) +{ + HANDLE h = (HANDLE)(handleval(env, fdo)); + DWORD lowPos = (DWORD)pos; + long highPos = (long)(pos >> 32); + DWORD lowNumBytes = (DWORD)size; + DWORD highNumBytes = (DWORD)(size >> 32); + jint result = 0; + OVERLAPPED o; + o.hEvent = 0; + o.Offset = lowPos; + o.OffsetHigh = highPos; + result = UnlockFileEx(h, 0, lowNumBytes, highNumBytes, &o); + if (result == 0) { + JNU_ThrowIOExceptionWithLastError(env, "Release failed"); + } +} + static void closeFile(JNIEnv *env, jlong fd) { HANDLE h = (HANDLE)fd; if (h != INVALID_HANDLE_VALUE) { @@ -305,14 +429,14 @@ static void closeFile(JNIEnv *env, jlong fd) { } JNIEXPORT void JNICALL -Java_sun_nio_ch_FileDispatcher_close0(JNIEnv *env, jclass clazz, jobject fdo) +Java_sun_nio_ch_FileDispatcherImpl_close0(JNIEnv *env, jclass clazz, jobject fdo) { jlong fd = handleval(env, fdo); closeFile(env, fd); } JNIEXPORT void JNICALL -Java_sun_nio_ch_FileDispatcher_closeByHandle(JNIEnv *env, jclass clazz, +Java_sun_nio_ch_FileDispatcherImpl_closeByHandle(JNIEnv *env, jclass clazz, jlong fd) { closeFile(env, fd); diff --git a/src/windows/native/sun/nio/ch/Iocp.c b/src/windows/native/sun/nio/ch/Iocp.c new file mode 100644 index 000000000..9568189ee --- /dev/null +++ b/src/windows/native/sun/nio/ch/Iocp.c @@ -0,0 +1,147 @@ +/* + * 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. + */ + +#include + +#include "jni.h" +#include "jni_util.h" +#include "jlong.h" +#include "nio.h" +#include "nio_util.h" + +#include "sun_nio_ch_Iocp.h" + + +static jfieldID completionStatus_error; +static jfieldID completionStatus_bytesTransferred; +static jfieldID completionStatus_completionKey; +static jfieldID completionStatus_overlapped; + + +JNIEXPORT void JNICALL +Java_sun_nio_ch_Iocp_initIDs(JNIEnv* env, jclass this) +{ + jclass clazz; + + clazz = (*env)->FindClass(env, "sun/nio/ch/Iocp$CompletionStatus"); + if (clazz == NULL) { + return; + } + completionStatus_error = (*env)->GetFieldID(env, clazz, "error", "I"); + if (completionStatus_error == NULL) return; + completionStatus_bytesTransferred = (*env)->GetFieldID(env, clazz, "bytesTransferred", "I"); + if (completionStatus_bytesTransferred == NULL) return; + completionStatus_completionKey = (*env)->GetFieldID(env, clazz, "completionKey", "I"); + if (completionStatus_completionKey == NULL) return; + completionStatus_overlapped = (*env)->GetFieldID(env, clazz, "overlapped", "J"); +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_ch_Iocp_createIoCompletionPort(JNIEnv* env, jclass this, + jlong handle, jlong existingPort, jint completionKey, jint concurrency) +{ + HANDLE port = CreateIoCompletionPort((HANDLE)jlong_to_ptr(handle), + (HANDLE)jlong_to_ptr(existingPort), + (DWORD)completionKey, + (DWORD)concurrency); + if (port == NULL) { + JNU_ThrowIOExceptionWithLastError(env, "CreateIoCompletionPort failed"); + } + return ptr_to_jlong(port); +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_Iocp_close0(JNIEnv* env, jclass this, + jlong handle) +{ + HANDLE h = (HANDLE)jlong_to_ptr(handle); + CloseHandle(h); +} + + +JNIEXPORT void JNICALL +Java_sun_nio_ch_Iocp_getQueuedCompletionStatus(JNIEnv* env, jclass this, + jlong completionPort, jobject obj) +{ + DWORD bytesTransferred; + DWORD completionKey; + OVERLAPPED *lpOverlapped; + BOOL res; + + res = GetQueuedCompletionStatus((HANDLE)jlong_to_ptr(completionPort), + &bytesTransferred, + &completionKey, + &lpOverlapped, + INFINITE); + if (res == 0 && lpOverlapped == NULL) { + JNU_ThrowIOExceptionWithLastError(env, "GetQueuedCompletionStatus failed"); + } else { + DWORD ioResult = (res == 0) ? GetLastError() : 0; + (*env)->SetIntField(env, obj, completionStatus_error, ioResult); + (*env)->SetIntField(env, obj, completionStatus_bytesTransferred, + (jint)bytesTransferred); + (*env)->SetIntField(env, obj, completionStatus_completionKey, + (jint)completionKey); + (*env)->SetLongField(env, obj, completionStatus_overlapped, + ptr_to_jlong(lpOverlapped)); + + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_Iocp_postQueuedCompletionStatus(JNIEnv* env, jclass this, + jlong completionPort, jint completionKey) +{ + BOOL res; + + res = PostQueuedCompletionStatus((HANDLE)jlong_to_ptr(completionPort), + (DWORD)0, + (DWORD)completionKey, + NULL); + if (res == 0) { + JNU_ThrowIOExceptionWithLastError(env, "PostQueuedCompletionStatus"); + } +} + +JNIEXPORT jstring JNICALL +Java_sun_nio_ch_Iocp_getErrorMessage(JNIEnv* env, jclass this, jint errorCode) +{ + WCHAR message[255]; + + DWORD len = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + (DWORD)errorCode, + 0, + &message[0], + 255, + NULL); + + + if (len == 0) { + return NULL; + } else { + return (*env)->NewString(env, (const jchar *)message, (jsize)wcslen(message)); + } +} diff --git a/src/windows/native/sun/nio/ch/WindowsAsynchronousFileChannelImpl.c b/src/windows/native/sun/nio/ch/WindowsAsynchronousFileChannelImpl.c new file mode 100644 index 000000000..d8346ba3e --- /dev/null +++ b/src/windows/native/sun/nio/ch/WindowsAsynchronousFileChannelImpl.c @@ -0,0 +1,132 @@ +/* + * 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. + */ + +#include + +#include "jni.h" +#include "jni_util.h" +#include "jlong.h" +#include "nio.h" +#include "nio_util.h" + +#include "sun_nio_ch_WindowsAsynchronousFileChannelImpl.h" + + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_WindowsAsynchronousFileChannelImpl_readFile(JNIEnv* env, jclass this, + jlong handle, jlong address, jint len, jlong offset, jlong ov) +{ + BOOL res; + DWORD nread = 0; + + OVERLAPPED* lpOverlapped = (OVERLAPPED*)jlong_to_ptr(ov); + lpOverlapped->Offset = (DWORD)offset; + lpOverlapped->OffsetHigh = (DWORD)((long)(offset >> 32)); + lpOverlapped->hEvent = NULL; + + res = ReadFile((HANDLE) jlong_to_ptr(handle), + (LPVOID) jlong_to_ptr(address), + (DWORD)len, + &nread, + lpOverlapped); + + if (res == 0) { + int error = GetLastError(); + if (error == ERROR_IO_PENDING) + return IOS_UNAVAILABLE; + if (error == ERROR_HANDLE_EOF) + return IOS_EOF; + JNU_ThrowIOExceptionWithLastError(env, "ReadFile failed"); + return IOS_THROWN; + } + + return (jint)nread; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_WindowsAsynchronousFileChannelImpl_writeFile(JNIEnv* env, jclass this, + jlong handle, jlong address, jint len, jlong offset, jlong ov) +{ + BOOL res; + DWORD nwritten = 0; + + OVERLAPPED* lpOverlapped = (OVERLAPPED*)jlong_to_ptr(ov); + lpOverlapped->Offset = (DWORD)offset; + lpOverlapped->OffsetHigh = (DWORD)((long)(offset >> 32)); + lpOverlapped->hEvent = NULL; + + res = WriteFile((HANDLE)jlong_to_ptr(handle), + (LPVOID) jlong_to_ptr(address), + (DWORD)len, + &nwritten, + lpOverlapped); + + if (res == 0) { + int error = GetLastError(); + if (error == ERROR_IO_PENDING) { + return IOS_UNAVAILABLE; + } + JNU_ThrowIOExceptionWithLastError(env, "WriteFile failed"); + return IOS_THROWN; + } + return (jint)nwritten; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_WindowsAsynchronousFileChannelImpl_lockFile(JNIEnv *env, jobject this, jlong handle, + jlong pos, jlong size, jboolean shared, jlong ov) +{ + BOOL res; + HANDLE h = jlong_to_ptr(handle); + DWORD lowPos = (DWORD)pos; + long highPos = (long)(pos >> 32); + DWORD lowNumBytes = (DWORD)size; + DWORD highNumBytes = (DWORD)(size >> 32); + DWORD flags = (shared == JNI_TRUE) ? 0 : LOCKFILE_EXCLUSIVE_LOCK; + OVERLAPPED* lpOverlapped = (OVERLAPPED*)jlong_to_ptr(ov); + + lpOverlapped->Offset = lowPos; + lpOverlapped->OffsetHigh = highPos; + lpOverlapped->hEvent = NULL; + + res = LockFileEx(h, flags, 0, lowNumBytes, highNumBytes, lpOverlapped); + if (res == 0) { + int error = GetLastError(); + if (error == ERROR_IO_PENDING) { + return IOS_UNAVAILABLE; + } + JNU_ThrowIOExceptionWithLastError(env, "WriteFile failed"); + return IOS_THROWN; + } + return 0; +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_WindowsAsynchronousFileChannelImpl_close0(JNIEnv* env, jclass this, + jlong handle) +{ + HANDLE h = (HANDLE)jlong_to_ptr(handle); + CloseHandle(h); +} diff --git a/src/windows/native/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.c b/src/windows/native/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.c new file mode 100644 index 000000000..ba706a4d8 --- /dev/null +++ b/src/windows/native/sun/nio/ch/WindowsAsynchronousServerSocketChannelImpl.c @@ -0,0 +1,142 @@ +/* + * 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. + */ + +#include +#include + +#include "jni.h" +#include "jni_util.h" +#include "jlong.h" +#include "nio.h" +#include "nio_util.h" +#include "net_util.h" + +#include "sun_nio_ch_WindowsAsynchronousServerSocketChannelImpl.h" + + +#ifndef WSAID_ACCEPTEX +#define WSAID_ACCEPTEX {0xb5367df1,0xcbac,0x11cf,{0x95,0xca,0x00,0x80,0x5f,0x48,0xa1,0x92}} +#endif + +#ifndef SO_UPDATE_ACCEPT_CONTEXT +#define SO_UPDATE_ACCEPT_CONTEXT 0x700B +#endif + + +typedef BOOL (*AcceptEx_t) +( + SOCKET sListenSocket, + SOCKET sAcceptSocket, + PVOID lpOutputBuffer, + DWORD dwReceiveDataLength, + DWORD dwLocalAddressLength, + DWORD dwRemoteAddressLength, + LPDWORD lpdwBytesReceived, + LPOVERLAPPED lpOverlapped +); + + +static AcceptEx_t AcceptEx_func; + + +JNIEXPORT void JNICALL +Java_sun_nio_ch_WindowsAsynchronousServerSocketChannelImpl_initIDs(JNIEnv* env, jclass this) { + GUID GuidAcceptEx = WSAID_ACCEPTEX; + SOCKET s; + int rv; + DWORD dwBytes; + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) { + JNU_ThrowIOExceptionWithLastError(env, "socket failed"); + return; + } + rv = WSAIoctl(s, + SIO_GET_EXTENSION_FUNCTION_POINTER, + (LPVOID)&GuidAcceptEx, + sizeof(GuidAcceptEx), + &AcceptEx_func, + sizeof(AcceptEx_func), + &dwBytes, + NULL, + NULL); + if (rv != 0) + JNU_ThrowIOExceptionWithLastError(env, "WSAIoctl failed"); + closesocket(s); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_WindowsAsynchronousServerSocketChannelImpl_accept0(JNIEnv* env, jclass this, + jlong listenSocket, jlong acceptSocket, jlong ov, jlong buf) +{ + BOOL res; + SOCKET s1 = (SOCKET)jlong_to_ptr(listenSocket); + SOCKET s2 = (SOCKET)jlong_to_ptr(acceptSocket); + PVOID outputBuffer = (PVOID)jlong_to_ptr(buf); + + DWORD nread = 0; + OVERLAPPED* lpOverlapped = (OVERLAPPED*)jlong_to_ptr(ov); + ZeroMemory((PVOID)lpOverlapped, sizeof(OVERLAPPED)); + + res = (*AcceptEx_func)(s1, + s2, + outputBuffer, + 0, + sizeof(SOCKETADDRESS)+16, + sizeof(SOCKETADDRESS)+16, + &nread, + lpOverlapped); + if (res == 0) { + int error = WSAGetLastError(); + if (error == ERROR_IO_PENDING) { + return IOS_UNAVAILABLE; + } + JNU_ThrowIOExceptionWithLastError(env, "AcceptEx failed"); + return IOS_THROWN; + } + + return 0; +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_WindowsAsynchronousServerSocketChannelImpl_updateAcceptContext(JNIEnv* env, jclass this, + jlong listenSocket, jlong acceptSocket) +{ + SOCKET s1 = (SOCKET)jlong_to_ptr(listenSocket); + SOCKET s2 = (SOCKET)jlong_to_ptr(acceptSocket); + + setsockopt(s2, SOL_SOCKET, SO_UPDATE_ACCEPT_CONTEXT, (char *)&s1, sizeof(s1)); +} + + +JNIEXPORT void JNICALL +Java_sun_nio_ch_WindowsAsynchronousServerSocketChannelImpl_closesocket0(JNIEnv* env, jclass this, + jlong socket) +{ + SOCKET s = (SOCKET)jlong_to_ptr(socket); + + if (closesocket(s) == SOCKET_ERROR) + JNU_ThrowIOExceptionWithLastError(env, "closesocket failed"); +} diff --git a/src/windows/native/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.c b/src/windows/native/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.c new file mode 100644 index 000000000..97c49f60a --- /dev/null +++ b/src/windows/native/sun/nio/ch/WindowsAsynchronousSocketChannelImpl.c @@ -0,0 +1,222 @@ +/* + * 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. + */ + +#include +#include +#include + +#include "jni.h" +#include "jni_util.h" +#include "jlong.h" +#include "nio.h" +#include "nio_util.h" +#include "net_util.h" + +#include "sun_nio_ch_WindowsAsynchronousSocketChannelImpl.h" + +#ifndef WSAID_CONNECTEX +#define WSAID_CONNECTEX {0x25a207b9,0xddf3,0x4660,{0x8e,0xe9,0x76,0xe5,0x8c,0x74,0x06,0x3e}} +#endif + +#ifndef SO_UPDATE_CONNECT_CONTEXT +#define SO_UPDATE_CONNECT_CONTEXT 0x7010 +#endif + +typedef BOOL (*ConnectEx_t) +( + SOCKET s, + const struct sockaddr* name, + int namelen, + PVOID lpSendBuffer, + DWORD dwSendDataLength, + LPDWORD lpdwBytesSent, + LPOVERLAPPED lpOverlapped +); + +static ConnectEx_t ConnectEx_func; + + +JNIEXPORT void JNICALL +Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_initIDs(JNIEnv* env, jclass this) { + GUID GuidConnectEx = WSAID_CONNECTEX; + SOCKET s; + int rv; + DWORD dwBytes; + + s = socket(AF_INET, SOCK_STREAM, 0); + if (s == INVALID_SOCKET) { + JNU_ThrowIOExceptionWithLastError(env, "socket failed"); + return; + } + rv = WSAIoctl(s, + SIO_GET_EXTENSION_FUNCTION_POINTER, + (LPVOID)&GuidConnectEx, + sizeof(GuidConnectEx), + &ConnectEx_func, + sizeof(ConnectEx_func), + &dwBytes, + NULL, + NULL); + if (rv != 0) + JNU_ThrowIOExceptionWithLastError(env, "WSAIoctl failed"); + closesocket(s); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_connect0(JNIEnv* env, jclass this, + jlong socket, jboolean preferIPv6, jobject iao, jint port, jlong ov) +{ + SOCKET s = (SOCKET) jlong_to_ptr(socket); + OVERLAPPED* lpOverlapped = (OVERLAPPED*) jlong_to_ptr(ov); + + SOCKETADDRESS sa; + int sa_len; + BOOL res; + + if (NET_InetAddressToSockaddr(env, iao, port, (struct sockaddr *)&sa, &sa_len, preferIPv6) != 0) { + return IOS_THROWN; + } + + ZeroMemory((PVOID)lpOverlapped, sizeof(OVERLAPPED)); + + res = (*ConnectEx_func)(s, + (struct sockaddr *)&sa, + sa_len, + NULL, + 0, + NULL, + lpOverlapped); + if (res == 0) { + int error = GetLastError(); + if (error == ERROR_IO_PENDING) { + return IOS_UNAVAILABLE; + } + JNU_ThrowIOExceptionWithLastError(env, "ConnectEx failed"); + return IOS_THROWN; + } + return 0; +} + +JNIEXPORT void JNICALL +Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_updateConnectContext(JNIEnv* env, jclass this, + jlong socket) +{ + SOCKET s = (SOCKET)jlong_to_ptr(socket); + setsockopt(s, SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, NULL, 0); +} + + +JNIEXPORT void JNICALL +Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_shutdown0(JNIEnv *env, jclass cl, + jlong socket, jint how) +{ + SOCKET s =(SOCKET) jlong_to_ptr(socket); + if (shutdown(s, how) == SOCKET_ERROR) { + JNU_ThrowIOExceptionWithLastError(env, "shutdown failed"); + } +} + + +JNIEXPORT void JNICALL +Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_closesocket0(JNIEnv* env, jclass this, + jlong socket) +{ + SOCKET s = (SOCKET)jlong_to_ptr(socket); + if (closesocket(s) == SOCKET_ERROR) + JNU_ThrowIOExceptionWithLastError(env, "closesocket failed"); +} + + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_read0(JNIEnv* env, jclass this, + jlong socket, jint count, jlong address, jlong ov) +{ + SOCKET s = (SOCKET) jlong_to_ptr(socket); + WSABUF* lpWsaBuf = (WSABUF*) jlong_to_ptr(address); + OVERLAPPED* lpOverlapped = (OVERLAPPED*) jlong_to_ptr(ov); + BOOL res; + DWORD nread = 0; + DWORD flags = 0; + + ZeroMemory((PVOID)lpOverlapped, sizeof(OVERLAPPED)); + res = WSARecv(s, + lpWsaBuf, + (DWORD)count, + &nread, + &flags, + lpOverlapped, + NULL); + + if (res == SOCKET_ERROR) { + int error = WSAGetLastError(); + if (error == WSA_IO_PENDING) { + return IOS_UNAVAILABLE; + } + if (error == WSAESHUTDOWN) { + return 0; // input shutdown + } + JNU_ThrowIOExceptionWithLastError(env, "WSARecv failed"); + return IOS_THROWN; + } + if (nread == 0) { + // Handle graceful close or bytes not yet available cases + // via completion port notification. + return IOS_UNAVAILABLE; + } + return (jint)nread; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_ch_WindowsAsynchronousSocketChannelImpl_write0(JNIEnv* env, jclass this, + jlong socket, jint count, jlong address, jlong ov) +{ + SOCKET s = (SOCKET) jlong_to_ptr(socket); + WSABUF* lpWsaBuf = (WSABUF*) jlong_to_ptr(address); + OVERLAPPED* lpOverlapped = (OVERLAPPED*) jlong_to_ptr(ov); + BOOL res; + DWORD nwritten; + + ZeroMemory((PVOID)lpOverlapped, sizeof(OVERLAPPED)); + res = WSASend(s, + lpWsaBuf, + (DWORD)count, + &nwritten, + 0, + lpOverlapped, + NULL); + + if (res == SOCKET_ERROR) { + int error = WSAGetLastError(); + if (error == WSA_IO_PENDING) { + return IOS_UNAVAILABLE; + } + if (error == WSAESHUTDOWN) { + return IOS_EOF; // output shutdown + } + JNU_ThrowIOExceptionWithLastError(env, "WSASend failed"); + return IOS_THROWN; + } + return (jint)nwritten; +} diff --git a/src/windows/native/sun/nio/fs/RegistryFileTypeDetector.c b/src/windows/native/sun/nio/fs/RegistryFileTypeDetector.c new file mode 100644 index 000000000..14c7f6af9 --- /dev/null +++ b/src/windows/native/sun/nio/fs/RegistryFileTypeDetector.c @@ -0,0 +1,62 @@ +/* + * 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. + */ + +#include + +#include "jni.h" +#include "jni_util.h" +#include "jlong.h" + +#include "sun_nio_fs_RegistryFileTypeDetector.h" + + +JNIEXPORT jstring JNICALL +Java_sun_nio_fs_RegistryFileTypeDetector_queryStringValue(JNIEnv* env, jclass this, + jlong keyAddress, jlong nameAddress) +{ + LPCWSTR lpSubKey= (LPCWSTR)jlong_to_ptr(keyAddress); + LPWSTR lpValueName = (LPWSTR)jlong_to_ptr(nameAddress); + LONG res; + HKEY hKey; + jstring result = NULL; + + res = RegOpenKeyExW(HKEY_CLASSES_ROOT, lpSubKey, 0, KEY_READ, &hKey); + if (res == ERROR_SUCCESS) { + DWORD type; + BYTE data[255]; + DWORD size = sizeof(data); + + res = RegQueryValueExW(hKey, lpValueName, NULL, &type, (LPBYTE)&data, &size); + if (res == ERROR_SUCCESS) { + if (type == REG_SZ) { + jsize len = wcslen((WCHAR*)data); + result = (*env)->NewString(env, (const jchar*)&data, len); + } + } + + RegCloseKey(hKey); + } + return result; +} diff --git a/src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c b/src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c new file mode 100644 index 000000000..fd30891b4 --- /dev/null +++ b/src/windows/native/sun/nio/fs/WindowsNativeDispatcher.c @@ -0,0 +1,1345 @@ +/* + * 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. + */ + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0500 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "jni.h" +#include "jni_util.h" +#include "jlong.h" + +#include "sun_nio_fs_WindowsNativeDispatcher.h" + +/** + * jfieldIDs + */ +static jfieldID findFirst_handle; +static jfieldID findFirst_name; + +static jfieldID findStream_handle; +static jfieldID findStream_name; + +static jfieldID volumeInfo_fsName; +static jfieldID volumeInfo_volName; +static jfieldID volumeInfo_volSN; +static jfieldID volumeInfo_flags; + +static jfieldID diskSpace_bytesAvailable; +static jfieldID diskSpace_totalBytes; +static jfieldID diskSpace_totalFree; + +static jfieldID account_domain; +static jfieldID account_name; +static jfieldID account_use; + +static jfieldID aclInfo_aceCount; + +static jfieldID completionStatus_error; +static jfieldID completionStatus_bytesTransferred; +static jfieldID completionStatus_completionKey; + +static jfieldID backupResult_bytesTransferred; +static jfieldID backupResult_context; + + +/** + * Win32 APIs not defined in Visual Studio 2003 header files + */ + +typedef enum { + FindStreamInfoStandard +} MY_STREAM_INFO_LEVELS; + +typedef struct _MY_WIN32_FIND_STREAM_DATA { + LARGE_INTEGER StreamSize; + WCHAR cStreamName[MAX_PATH + 36]; +} MY_WIN32_FIND_STREAM_DATA; + +typedef HANDLE (WINAPI* FindFirstStream_Proc)(LPCWSTR, MY_STREAM_INFO_LEVELS, LPVOID, DWORD); +typedef BOOL (WINAPI* FindNextStream_Proc)(HANDLE, LPVOID); + +typedef BOOLEAN (WINAPI* CreateSymbolicLinkProc) (LPCWSTR, LPCWSTR, DWORD); +typedef BOOL (WINAPI* CreateHardLinkProc) (LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES); +typedef BOOL (WINAPI* GetFinalPathNameByHandleProc) (HANDLE, LPWSTR, DWORD, DWORD); + +typedef BOOL (WINAPI* ConvertSidToStringSidProc) (PSID, LPWSTR*); +typedef BOOL (WINAPI* ConvertStringSidToSidProc) (LPWSTR, PSID*); +typedef DWORD (WINAPI* GetLengthSidProc) (PSID); + +static FindFirstStream_Proc FindFirstStream_func; +static FindNextStream_Proc FindNextStream_func; + +static CreateSymbolicLinkProc CreateSymbolicLink_func; +static CreateHardLinkProc CreateHardLink_func; +static GetFinalPathNameByHandleProc GetFinalPathNameByHandle_func; + +static ConvertSidToStringSidProc ConvertSidToStringSid_func; +static ConvertStringSidToSidProc ConvertStringSidToSid_func; +static GetLengthSidProc GetLengthSid_func; + +static void throwWindowsException(JNIEnv* env, DWORD lastError) { + jobject x = JNU_NewObjectByName(env, "sun/nio/fs/WindowsException", + "(I)V", lastError); + if (x != NULL) { + (*env)->Throw(env, x); + } +} + +/** + * Initializes jfieldIDs and get address of Win32 calls that are located + * at runtime. + */ +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_initIDs(JNIEnv* env, jclass this) +{ + jclass clazz; + HMODULE h; + + clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$FirstFile"); + if (clazz == NULL) { + return; + } + findFirst_handle = (*env)->GetFieldID(env, clazz, "handle", "J"); + findFirst_name = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;"); + + clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$FirstStream"); + if (clazz == NULL) { + return; + } + findStream_handle = (*env)->GetFieldID(env, clazz, "handle", "J"); + findStream_name = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;"); + + clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$VolumeInformation"); + if (clazz == NULL) { + return; + } + volumeInfo_fsName = (*env)->GetFieldID(env, clazz, "fileSystemName", "Ljava/lang/String;"); + volumeInfo_volName = (*env)->GetFieldID(env, clazz, "volumeName", "Ljava/lang/String;"); + volumeInfo_volSN = (*env)->GetFieldID(env, clazz, "volumeSerialNumber", "I"); + volumeInfo_flags = (*env)->GetFieldID(env, clazz, "flags", "I"); + + clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$DiskFreeSpace"); + if (clazz == NULL) { + return; + } + diskSpace_bytesAvailable = (*env)->GetFieldID(env, clazz, "freeBytesAvailable", "J"); + diskSpace_totalBytes = (*env)->GetFieldID(env, clazz, "totalNumberOfBytes", "J"); + diskSpace_totalFree = (*env)->GetFieldID(env, clazz, "totalNumberOfFreeBytes", "J"); + + clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$Account"); + if (clazz == NULL) { + return; + } + account_domain = (*env)->GetFieldID(env, clazz, "domain", "Ljava/lang/String;"); + account_name = (*env)->GetFieldID(env, clazz, "name", "Ljava/lang/String;"); + account_use = (*env)->GetFieldID(env, clazz, "use", "I"); + + clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$AclInformation"); + if (clazz == NULL) { + return; + } + aclInfo_aceCount = (*env)->GetFieldID(env, clazz, "aceCount", "I"); + + clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$CompletionStatus"); + if (clazz == NULL) { + return; + } + completionStatus_error = (*env)->GetFieldID(env, clazz, "error", "I"); + completionStatus_bytesTransferred = (*env)->GetFieldID(env, clazz, "bytesTransferred", "I"); + completionStatus_completionKey = (*env)->GetFieldID(env, clazz, "completionKey", "I"); + + clazz = (*env)->FindClass(env, "sun/nio/fs/WindowsNativeDispatcher$BackupResult"); + if (clazz == NULL) { + return; + } + backupResult_bytesTransferred = (*env)->GetFieldID(env, clazz, "bytesTransferred", "I"); + backupResult_context = (*env)->GetFieldID(env, clazz, "context", "J"); + + + h = LoadLibrary("kernel32"); + if (h != INVALID_HANDLE_VALUE) { + FindFirstStream_func = + (FindFirstStream_Proc)GetProcAddress(h, "FindFirstStreamW"); + FindNextStream_func = + (FindNextStream_Proc)GetProcAddress(h, "FindNextStreamW"); + CreateSymbolicLink_func = + (CreateSymbolicLinkProc)GetProcAddress(h, "CreateSymbolicLinkW"); + CreateHardLink_func = + (CreateHardLinkProc)GetProcAddress(h, "CreateHardLinkW"); + GetFinalPathNameByHandle_func = + (GetFinalPathNameByHandleProc)GetProcAddress(h, "GetFinalPathNameByHandleW"); + FreeLibrary(h); + } + + h = LoadLibrary("advapi32"); + if (h != INVALID_HANDLE_VALUE) { + ConvertSidToStringSid_func = + (ConvertSidToStringSidProc)GetProcAddress(h, "ConvertSidToStringSidW"); + ConvertStringSidToSid_func = + (ConvertStringSidToSidProc)GetProcAddress(h, "ConvertStringSidToSidW"); + GetLengthSid_func = + (GetLengthSidProc)GetProcAddress(h, "GetLengthSid"); + FreeLibrary(h); + } + +} + +JNIEXPORT jstring JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_FormatMessage(JNIEnv* env, jclass this, jint errorCode) { + WCHAR message[255]; + + DWORD len = FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + (DWORD)errorCode, + 0, + &message[0], + 255, + NULL); + + + if (len == 0) { + return NULL; + } else { + return (*env)->NewString(env, (const jchar *)message, (jsize)wcslen(message)); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_LocalFree(JNIEnv* env, jclass this, jlong address) +{ + HLOCAL hMem = (HLOCAL)jlong_to_ptr(address); + LocalFree(hMem); +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_CreateFile0(JNIEnv* env, jclass this, + jlong address, jint dwDesiredAccess, jint dwShareMode, jlong sdAddress, + jint dwCreationDisposition, jint dwFlagsAndAttributes) +{ + HANDLE handle; + LPCWSTR lpFileName = jlong_to_ptr(address); + + SECURITY_ATTRIBUTES securityAttributes; + LPSECURITY_ATTRIBUTES lpSecurityAttributes; + PSECURITY_DESCRIPTOR lpSecurityDescriptor = jlong_to_ptr(sdAddress); + + + if (lpSecurityDescriptor == NULL) { + lpSecurityAttributes = NULL; + } else { + securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); + securityAttributes.lpSecurityDescriptor = lpSecurityDescriptor; + securityAttributes.bInheritHandle = FALSE; + lpSecurityAttributes = &securityAttributes; + } + + handle = CreateFileW(lpFileName, + (DWORD)dwDesiredAccess, + (DWORD)dwShareMode, + lpSecurityAttributes, + (DWORD)dwCreationDisposition, + (DWORD)dwFlagsAndAttributes, + NULL); + if (handle == INVALID_HANDLE_VALUE) { + throwWindowsException(env, GetLastError()); + } + return ptr_to_jlong(handle); +} + + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_DeviceIoControlSetSparse(JNIEnv* env, jclass this, + jlong handle) +{ + DWORD bytesReturned; + HANDLE h = (HANDLE)jlong_to_ptr(handle); + if (DeviceIoControl(h, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &bytesReturned, NULL) == 0) { + throwWindowsException(env, GetLastError()); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_DeviceIoControlGetReparsePoint(JNIEnv* env, jclass this, + jlong handle, jlong bufferAddress, jint bufferSize) +{ + DWORD bytesReturned; + HANDLE h = (HANDLE)jlong_to_ptr(handle); + LPVOID outBuffer = (LPVOID)jlong_to_ptr(bufferAddress); + + if (DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, outBuffer, (DWORD)bufferSize, + &bytesReturned, NULL) == 0) + { + throwWindowsException(env, GetLastError()); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_DeleteFile0(JNIEnv* env, jclass this, jlong address) +{ + LPCWSTR lpFileName = jlong_to_ptr(address); + if (DeleteFileW(lpFileName) == 0) { + throwWindowsException(env, GetLastError()); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_CreateDirectory0(JNIEnv* env, jclass this, + jlong address, jlong sdAddress) +{ + LPCWSTR lpFileName = jlong_to_ptr(address); + + SECURITY_ATTRIBUTES securityAttributes; + LPSECURITY_ATTRIBUTES lpSecurityAttributes; + PSECURITY_DESCRIPTOR lpSecurityDescriptor = jlong_to_ptr(sdAddress); + + + if (lpSecurityDescriptor == NULL) { + lpSecurityAttributes = NULL; + } else { + securityAttributes.nLength = sizeof(SECURITY_ATTRIBUTES); + securityAttributes.lpSecurityDescriptor = lpSecurityDescriptor; + securityAttributes.bInheritHandle = FALSE; + lpSecurityAttributes = &securityAttributes; + } + + if (CreateDirectoryW(lpFileName, lpSecurityAttributes) == 0) { + throwWindowsException(env, GetLastError()); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_RemoveDirectory0(JNIEnv* env, jclass this, jlong address) +{ + LPCWSTR lpFileName = jlong_to_ptr(address); + if (RemoveDirectoryW(lpFileName) == 0) { + throwWindowsException(env, GetLastError()); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_CloseHandle(JNIEnv* env, jclass this, + jlong handle) +{ + HANDLE h = (HANDLE)jlong_to_ptr(handle); + CloseHandle(h); +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_FindFirstFile0(JNIEnv* env, jclass this, + jlong address, jobject obj) +{ + WIN32_FIND_DATAW data; + LPCWSTR lpFileName = jlong_to_ptr(address); + + HANDLE handle = FindFirstFileW(lpFileName, &data); + if (handle != INVALID_HANDLE_VALUE) { + jstring name = (*env)->NewString(env, data.cFileName, wcslen(data.cFileName)); + if (name == NULL) + return; + (*env)->SetLongField(env, obj, findFirst_handle, ptr_to_jlong(handle)); + (*env)->SetObjectField(env, obj, findFirst_name, name); + } else { + throwWindowsException(env, GetLastError()); + } +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_FindFirstFile1(JNIEnv* env, jclass this, + jlong pathAddress, jlong dataAddress) +{ + LPCWSTR lpFileName = jlong_to_ptr(pathAddress); + WIN32_FIND_DATAW* data = (WIN32_FIND_DATAW*)jlong_to_ptr(dataAddress); + + HANDLE handle = FindFirstFileW(lpFileName, data); + if (handle == INVALID_HANDLE_VALUE) { + throwWindowsException(env, GetLastError()); + } + return ptr_to_jlong(handle); +} + +JNIEXPORT jstring JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_FindNextFile(JNIEnv* env, jclass this, + jlong handle) +{ + WIN32_FIND_DATAW data; + HANDLE h = (HANDLE)jlong_to_ptr(handle); + + if (FindNextFileW(h, &data) != 0) { + return (*env)->NewString(env, data.cFileName, wcslen(data.cFileName)); + } else { + if (GetLastError() != ERROR_NO_MORE_FILES) + throwWindowsException(env, GetLastError()); + return NULL; + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_FindFirstStream0(JNIEnv* env, jclass this, + jlong address, jobject obj) +{ + MY_WIN32_FIND_STREAM_DATA data; + LPCWSTR lpFileName = jlong_to_ptr(address); + HANDLE handle; + + if (FindFirstStream_func == NULL) { + JNU_ThrowInternalError(env, "Should not get here"); + return; + } + + handle = (*FindFirstStream_func)(lpFileName, FindStreamInfoStandard, &data, 0); + if (handle != INVALID_HANDLE_VALUE) { + jstring name = (*env)->NewString(env, data.cStreamName, wcslen(data.cStreamName)); + if (name == NULL) + return; + (*env)->SetLongField(env, obj, findStream_handle, ptr_to_jlong(handle)); + (*env)->SetObjectField(env, obj, findStream_name, name); + } else { + if (GetLastError() == ERROR_HANDLE_EOF) { + (*env)->SetLongField(env, obj, findStream_handle, ptr_to_jlong(handle)); + } else { + throwWindowsException(env, GetLastError()); + } + } + +} + +JNIEXPORT jstring JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_FindNextStream(JNIEnv* env, jclass this, + jlong handle) +{ + MY_WIN32_FIND_STREAM_DATA data; + HANDLE h = (HANDLE)jlong_to_ptr(handle); + + if (FindNextStream_func == NULL) { + JNU_ThrowInternalError(env, "Should not get here"); + return NULL; + } + + if ((*FindNextStream_func)(h, &data) != 0) { + return (*env)->NewString(env, data.cStreamName, wcslen(data.cStreamName)); + } else { + if (GetLastError() != ERROR_HANDLE_EOF) + throwWindowsException(env, GetLastError()); + return NULL; + } +} + + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_FindClose(JNIEnv* env, jclass this, + jlong handle) +{ + HANDLE h = (HANDLE)jlong_to_ptr(handle); + if (FindClose(h) == 0) { + throwWindowsException(env, GetLastError()); + } +} + + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetFileInformationByHandle(JNIEnv* env, jclass this, + jlong handle, jlong address) +{ + HANDLE h = (HANDLE)jlong_to_ptr(handle); + BY_HANDLE_FILE_INFORMATION* info = + (BY_HANDLE_FILE_INFORMATION*)jlong_to_ptr(address); + if (GetFileInformationByHandle(h, info) == 0) { + throwWindowsException(env, GetLastError()); + } +} + + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_CopyFileEx0(JNIEnv* env, jclass this, + jlong existingAddress, jlong newAddress, jint flags, jlong cancelAddress) +{ + LPCWSTR lpExistingFileName = jlong_to_ptr(existingAddress); + LPCWSTR lpNewFileName = jlong_to_ptr(newAddress); + LPBOOL cancel = (LPBOOL)jlong_to_ptr(cancelAddress); + if (CopyFileExW(lpExistingFileName, lpNewFileName, NULL, NULL, cancel, + (DWORD)flags) == 0) + { + throwWindowsException(env, GetLastError()); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_MoveFileEx0(JNIEnv* env, jclass this, + jlong existingAddress, jlong newAddress, jint flags) +{ + LPCWSTR lpExistingFileName = jlong_to_ptr(existingAddress); + LPCWSTR lpNewFileName = jlong_to_ptr(newAddress); + if (MoveFileExW(lpExistingFileName, lpNewFileName, (DWORD)flags) == 0) { + throwWindowsException(env, GetLastError()); + } +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetLogicalDrives(JNIEnv* env, jclass this) +{ + DWORD res = GetLogicalDrives(); + if (res == 0) { + throwWindowsException(env, GetLastError()); + } + return (jint)res; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetFileAttributes0(JNIEnv* env, jclass this, + jlong address) +{ + LPCWSTR lpFileName = jlong_to_ptr(address); + DWORD value = GetFileAttributesW(lpFileName); + + if (value == INVALID_FILE_ATTRIBUTES) { + throwWindowsException(env, GetLastError()); + } + return (jint)value; +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_SetFileAttributes0(JNIEnv* env, jclass this, + jlong address, jint value) +{ + LPCWSTR lpFileName = jlong_to_ptr(address); + if (SetFileAttributesW(lpFileName, (DWORD)value) == 0) { + throwWindowsException(env, GetLastError()); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetFileAttributesEx0(JNIEnv* env, jclass this, + jlong pathAddress, jlong dataAddress) +{ + LPCWSTR lpFileName = jlong_to_ptr(pathAddress); + WIN32_FILE_ATTRIBUTE_DATA* data = (WIN32_FILE_ATTRIBUTE_DATA*)jlong_to_ptr(dataAddress); + + BOOL res = GetFileAttributesExW(lpFileName, GetFileExInfoStandard, (LPVOID)data); + if (res == 0) + throwWindowsException(env, GetLastError()); +} + + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_SetFileTime(JNIEnv* env, jclass this, + jlong handle, jlong createTime, jlong lastAccessTime, jlong lastWriteTime) +{ + HANDLE h = (HANDLE)jlong_to_ptr(handle); + + if (SetFileTime(h, + (createTime == (jlong)0) ? NULL : (CONST FILETIME *)&createTime, + (lastAccessTime == (jlong)0) ? NULL : (CONST FILETIME *)&lastAccessTime, + (lastWriteTime == (jlong)0) ? NULL : (CONST FILETIME *)&lastWriteTime) == 0) + { + throwWindowsException(env, GetLastError()); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_SetEndOfFile(JNIEnv* env, jclass this, + jlong handle) +{ + HANDLE h = (HANDLE)jlong_to_ptr(handle); + + if (SetEndOfFile(h) == 0) + throwWindowsException(env, GetLastError()); +} + + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetVolumeInformation0(JNIEnv* env, jclass this, + jlong address, jobject obj) +{ + WCHAR volumeName[MAX_PATH+1]; + DWORD volumeSerialNumber; + DWORD maxComponentLength; + DWORD flags; + WCHAR fileSystemName[MAX_PATH+1]; + LPCWSTR lpFileName = jlong_to_ptr(address); + jstring str; + + BOOL res = GetVolumeInformationW(lpFileName, + &volumeName[0], + MAX_PATH+1, + &volumeSerialNumber, + &maxComponentLength, + &flags, + &fileSystemName[0], + MAX_PATH+1); + if (res == 0) { + throwWindowsException(env, GetLastError()); + return; + } + + str = (*env)->NewString(env, (const jchar *)fileSystemName, (jsize)wcslen(fileSystemName)); + if (str == NULL) return; + (*env)->SetObjectField(env, obj, volumeInfo_fsName, str); + + str = (*env)->NewString(env, (const jchar *)volumeName, (jsize)wcslen(volumeName)); + if (str == NULL) return; + (*env)->SetObjectField(env, obj, volumeInfo_volName, str); + + (*env)->SetIntField(env, obj, volumeInfo_volSN, (jint)volumeSerialNumber); + (*env)->SetIntField(env, obj, volumeInfo_flags, (jint)flags); +} + + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetDriveType0(JNIEnv* env, jclass this, jlong address) { + LPCWSTR lpRootPathName = jlong_to_ptr(address); + return (jint)GetDriveTypeW(lpRootPathName); +} + + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetDiskFreeSpaceEx0(JNIEnv* env, jclass this, + jlong address, jobject obj) +{ + ULARGE_INTEGER freeBytesAvailable; + ULARGE_INTEGER totalNumberOfBytes; + ULARGE_INTEGER totalNumberOfFreeBytes; + LPCWSTR lpDirName = jlong_to_ptr(address); + + + BOOL res = GetDiskFreeSpaceExW(lpDirName, + &freeBytesAvailable, + &totalNumberOfBytes, + &totalNumberOfFreeBytes); + if (res == 0) { + throwWindowsException(env, GetLastError()); + return; + } + + (*env)->SetLongField(env, obj, diskSpace_bytesAvailable, + long_to_jlong(freeBytesAvailable.QuadPart)); + (*env)->SetLongField(env, obj, diskSpace_totalBytes, + long_to_jlong(totalNumberOfBytes.QuadPart)); + (*env)->SetLongField(env, obj, diskSpace_totalFree, + long_to_jlong(totalNumberOfFreeBytes.QuadPart)); +} + + +JNIEXPORT jstring JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetVolumePathName0(JNIEnv* env, jclass this, + jlong address) +{ + WCHAR volumeName[MAX_PATH+1]; + LPCWSTR lpFileName = jlong_to_ptr(address); + + + BOOL res = GetVolumePathNameW(lpFileName, + &volumeName[0], + MAX_PATH+1); + if (res == 0) { + throwWindowsException(env, GetLastError()); + return NULL; + } else { + return (*env)->NewString(env, (const jchar *)volumeName, (jsize)wcslen(volumeName)); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_InitializeSecurityDescriptor(JNIEnv* env, jclass this, + jlong address) +{ + PSECURITY_DESCRIPTOR pSecurityDescriptor = + (PSECURITY_DESCRIPTOR)jlong_to_ptr(address); + + if (InitializeSecurityDescriptor(pSecurityDescriptor, SECURITY_DESCRIPTOR_REVISION) == 0) { + throwWindowsException(env, GetLastError()); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_InitializeAcl(JNIEnv* env, jclass this, + jlong address, jint size) +{ + PACL pAcl = (PACL)jlong_to_ptr(address); + + if (InitializeAcl(pAcl, (DWORD)size, ACL_REVISION) == 0) { + throwWindowsException(env, GetLastError()); + } +} + + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_SetFileSecurity0(JNIEnv* env, jclass this, + jlong pathAddress, jint requestedInformation, jlong descAddress) +{ + LPCWSTR lpFileName = jlong_to_ptr(pathAddress); + PSECURITY_DESCRIPTOR pSecurityDescriptor = jlong_to_ptr(descAddress); + DWORD lengthNeeded = 0; + + BOOL res = SetFileSecurityW(lpFileName, + (SECURITY_INFORMATION)requestedInformation, + pSecurityDescriptor); + + if (res == 0) { + throwWindowsException(env, GetLastError()); + } +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetFileSecurity0(JNIEnv* env, jclass this, + jlong pathAddress, jint requestedInformation, jlong descAddress, jint nLength) +{ + LPCWSTR lpFileName = jlong_to_ptr(pathAddress); + PSECURITY_DESCRIPTOR pSecurityDescriptor = jlong_to_ptr(descAddress); + DWORD lengthNeeded = 0; + + BOOL res = GetFileSecurityW(lpFileName, + (SECURITY_INFORMATION)requestedInformation, + pSecurityDescriptor, + (DWORD)nLength, + &lengthNeeded); + + if (res == 0) { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + return (jint)lengthNeeded; + } else { + throwWindowsException(env, GetLastError()); + return 0; + } + } else { + return (jint)nLength; + } +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetSecurityDescriptorOwner(JNIEnv* env, + jclass this, jlong address) +{ + PSECURITY_DESCRIPTOR pSecurityDescriptor = jlong_to_ptr(address); + PSID pOwner; + BOOL bOwnerDefaulted; + + + if (GetSecurityDescriptorOwner(pSecurityDescriptor, &pOwner, &bOwnerDefaulted) == 0) { + throwWindowsException(env, GetLastError()); + } + return ptr_to_jlong(pOwner); +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_SetSecurityDescriptorOwner(JNIEnv* env, + jclass this, jlong descAddress, jlong ownerAddress) +{ + PSECURITY_DESCRIPTOR pSecurityDescriptor = jlong_to_ptr(descAddress); + PSID pOwner = jlong_to_ptr(ownerAddress); + + if (SetSecurityDescriptorOwner(pSecurityDescriptor, pOwner, FALSE) == 0) { + throwWindowsException(env, GetLastError()); + } +} + + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetSecurityDescriptorDacl(JNIEnv* env, + jclass this, jlong address) +{ + PSECURITY_DESCRIPTOR pSecurityDescriptor = jlong_to_ptr(address); + BOOL bDaclPresent; + PACL pDacl; + BOOL bDaclDefaulted; + + if (GetSecurityDescriptorDacl(pSecurityDescriptor, &bDaclPresent, &pDacl, &bDaclDefaulted) == 0) { + throwWindowsException(env, GetLastError()); + return (jlong)0; + } else { + return (bDaclPresent) ? ptr_to_jlong(pDacl) : (jlong)0; + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_SetSecurityDescriptorDacl(JNIEnv* env, + jclass this, jlong descAddress, jlong aclAddress) +{ + PSECURITY_DESCRIPTOR pSecurityDescriptor = (PSECURITY_DESCRIPTOR)jlong_to_ptr(descAddress); + PACL pAcl = (PACL)jlong_to_ptr(aclAddress); + + if (SetSecurityDescriptorDacl(pSecurityDescriptor, TRUE, pAcl, FALSE) == 0) { + throwWindowsException(env, GetLastError()); + } +} + + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetAclInformation0(JNIEnv* env, + jclass this, jlong address, jobject obj) +{ + PACL pAcl = (PACL)jlong_to_ptr(address); + ACL_SIZE_INFORMATION acl_size_info; + + if (GetAclInformation(pAcl, (void *) &acl_size_info, sizeof(acl_size_info), AclSizeInformation) == 0) { + throwWindowsException(env, GetLastError()); + } else { + (*env)->SetIntField(env, obj, aclInfo_aceCount, (jint)acl_size_info.AceCount); + } +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetAce(JNIEnv* env, jclass this, jlong address, + jint aceIndex) +{ + PACL pAcl = (PACL)jlong_to_ptr(address); + LPVOID pAce; + + if (GetAce(pAcl, (DWORD)aceIndex, &pAce) == 0) { + throwWindowsException(env, GetLastError()); + return (jlong)0; + } else { + return ptr_to_jlong(pAce); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_AddAccessAllowedAceEx(JNIEnv* env, + jclass this, jlong aclAddress, jint flags, jint mask, jlong sidAddress) +{ + PACL pAcl = (PACL)jlong_to_ptr(aclAddress); + PSID pSid = (PSID)jlong_to_ptr(sidAddress); + + if (AddAccessAllowedAceEx(pAcl, ACL_REVISION, (DWORD)flags, (DWORD)mask, pSid) == 0) { + throwWindowsException(env, GetLastError()); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_AddAccessDeniedAceEx(JNIEnv* env, + jclass this, jlong aclAddress, jint flags, jint mask, jlong sidAddress) +{ + PACL pAcl = (PACL)jlong_to_ptr(aclAddress); + PSID pSid = (PSID)jlong_to_ptr(sidAddress); + + if (AddAccessDeniedAceEx(pAcl, ACL_REVISION, (DWORD)flags, (DWORD)mask, pSid) == 0) { + throwWindowsException(env, GetLastError()); + } +} + + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_LookupAccountSid0(JNIEnv* env, + jclass this, jlong address, jobject obj) +{ + WCHAR domain[255]; + WCHAR name[255]; + DWORD domainLen = sizeof(domain); + DWORD nameLen = sizeof(name); + SID_NAME_USE use; + PSID sid = jlong_to_ptr(address); + jstring s; + + if (LookupAccountSidW(NULL, sid, &name[0], &nameLen, &domain[0], &domainLen, &use) == 0) { + throwWindowsException(env, GetLastError()); + return; + } + + s = (*env)->NewString(env, (const jchar *)domain, (jsize)wcslen(domain)); + if (s == NULL) + return; + (*env)->SetObjectField(env, obj, account_domain, s); + + s = (*env)->NewString(env, (const jchar *)name, (jsize)wcslen(name)); + if (s == NULL) + return; + (*env)->SetObjectField(env, obj, account_name, s); + (*env)->SetIntField(env, obj, account_use, (jint)use); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_LookupAccountName0(JNIEnv* env, + jclass this, jlong nameAddress, jlong sidAddress, jint cbSid) +{ + + LPCWSTR accountName = jlong_to_ptr(nameAddress); + PSID sid = jlong_to_ptr(sidAddress); + WCHAR domain[255]; + DWORD domainLen = sizeof(domain); + SID_NAME_USE use; + + if (LookupAccountNameW(NULL, accountName, sid, (LPDWORD)&cbSid, + &domain[0], &domainLen, &use) == 0) + { + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + throwWindowsException(env, GetLastError()); + } + } + + return cbSid; +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetLengthSid(JNIEnv* env, + jclass this, jlong address) +{ + PSID sid = jlong_to_ptr(address); + + if (GetLengthSid_func == NULL) { + JNU_ThrowInternalError(env, "Should not get here"); + return 0; + } + return (jint)(*GetLengthSid_func)(sid); +} + + +JNIEXPORT jstring JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_ConvertSidToStringSid(JNIEnv* env, + jclass this, jlong address) +{ + PSID sid = jlong_to_ptr(address); + LPWSTR string; + + if (ConvertSidToStringSid_func == NULL) { + JNU_ThrowInternalError(env, "Should not get here"); + return NULL; + } + + if ((*ConvertSidToStringSid_func)(sid, &string) == 0) { + throwWindowsException(env, GetLastError()); + return NULL; + } else { + jstring s = (*env)->NewString(env, (const jchar *)string, + (jsize)wcslen(string)); + LocalFree(string); + return s; + } +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_ConvertStringSidToSid0(JNIEnv* env, + jclass this, jlong address) +{ + LPWSTR lpStringSid = jlong_to_ptr(address); + PSID pSid; + + if (ConvertStringSidToSid_func == NULL) { + JNU_ThrowInternalError(env, "Should not get here"); + return (jlong)0; + } + + if ((*ConvertStringSidToSid_func)(lpStringSid, &pSid) == 0) + throwWindowsException(env, GetLastError()); + + return ptr_to_jlong(pSid); +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetCurrentProcess(JNIEnv* env, jclass this) { + HANDLE hProcess = GetCurrentProcess(); + return ptr_to_jlong(hProcess); +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetCurrentThread(JNIEnv* env, jclass this) { + HANDLE hThread = GetCurrentThread(); + return ptr_to_jlong(hThread); +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_OpenProcessToken(JNIEnv* env, + jclass this, jlong process, jint desiredAccess) +{ + HANDLE hProcess = (HANDLE)jlong_to_ptr(process); + HANDLE hToken; + + if (OpenProcessToken(hProcess, (DWORD)desiredAccess, &hToken) == 0) + throwWindowsException(env, GetLastError()); + return ptr_to_jlong(hToken); +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_OpenThreadToken(JNIEnv* env, + jclass this, jlong thread, jint desiredAccess, jboolean openAsSelf) +{ + HANDLE hThread = (HANDLE)jlong_to_ptr(thread); + HANDLE hToken; + BOOL bOpenAsSelf = (openAsSelf == JNI_TRUE) ? TRUE : FALSE; + + if (OpenThreadToken(hThread, (DWORD)desiredAccess, bOpenAsSelf, &hToken) == 0) { + if (GetLastError() == ERROR_NO_TOKEN) + return (jlong)0; + throwWindowsException(env, GetLastError()); + } + return ptr_to_jlong(hToken); +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_DuplicateTokenEx(JNIEnv* env, + jclass this, jlong token, jint desiredAccess) +{ + HANDLE hToken = (HANDLE)jlong_to_ptr(token); + HANDLE resultToken; + BOOL res; + + res = DuplicateTokenEx(hToken, + (DWORD)desiredAccess, + NULL, + SecurityImpersonation, + TokenImpersonation, + &resultToken); + if (res == 0) + throwWindowsException(env, GetLastError()); + return ptr_to_jlong(resultToken); +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_SetThreadToken(JNIEnv* env, + jclass this, jlong thread, jlong token) +{ + HANDLE hThread = (HANDLE)jlong_to_ptr(thread); + HANDLE hToken = (HANDLE)jlong_to_ptr(token); + + if (SetThreadToken(hThread, hToken) == 0) + throwWindowsException(env, GetLastError()); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetTokenInformation(JNIEnv* env, + jclass this, jlong token, jint tokenInfoClass, jlong tokenInfo, jint tokenInfoLength) +{ + BOOL res; + DWORD lengthNeeded; + HANDLE hToken = (HANDLE)jlong_to_ptr(token); + LPVOID result = (LPVOID)jlong_to_ptr(tokenInfo); + + res = GetTokenInformation(hToken, (TOKEN_INFORMATION_CLASS)tokenInfoClass, (LPVOID)result, + tokenInfoLength, &lengthNeeded); + if (res == 0) { + if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) { + return (jint)lengthNeeded; + } else { + throwWindowsException(env, GetLastError()); + return 0; + } + } else { + return tokenInfoLength; + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_AdjustTokenPrivileges(JNIEnv* env, + jclass this, jlong token, jlong luid, jint attributes) +{ + TOKEN_PRIVILEGES privs[1]; + HANDLE hToken = (HANDLE)jlong_to_ptr(token); + PLUID pLuid = (PLUID)jlong_to_ptr(luid); + + privs[0].PrivilegeCount = 1; + privs[0].Privileges[0].Luid = *pLuid; + privs[0].Privileges[0].Attributes = (DWORD)attributes; + + if (AdjustTokenPrivileges(hToken, FALSE, &privs[0], 1, NULL, NULL) == 0) + throwWindowsException(env, GetLastError()); +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_LookupPrivilegeValue0(JNIEnv* env, + jclass this, jlong name) +{ + LPCWSTR lpName = (LPCWSTR)jlong_to_ptr(name); + PLUID pLuid = LocalAlloc(0, sizeof(LUID)); + + if (pLuid == NULL) { + JNU_ThrowInternalError(env, "Unable to allocate LUID structure"); + } else { + if (LookupPrivilegeValueW(NULL, lpName, pLuid) == 0) + throwWindowsException(env, GetLastError()); + } + return ptr_to_jlong(pLuid); +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_BuildTrusteeWithSid(JNIEnv* env, + jclass this, jlong sid) +{ + PSID pSid = (HANDLE)jlong_to_ptr(sid); + PTRUSTEE_W pTrustee = LocalAlloc(0, sizeof(TRUSTEE_W)); + + if (pTrustee == NULL) { + JNU_ThrowInternalError(env, "Unable to allocate TRUSTEE_W structure"); + } else { + BuildTrusteeWithSidW(pTrustee, pSid); + } + return ptr_to_jlong(pTrustee); +} + +JNIEXPORT jint JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetEffectiveRightsFromAcl(JNIEnv* env, + jclass this, jlong acl, jlong trustee) +{ + ACCESS_MASK access; + PACL pAcl = (PACL)jlong_to_ptr(acl); + PTRUSTEE pTrustee = (PTRUSTEE)jlong_to_ptr(trustee); + + if (GetEffectiveRightsFromAcl(pAcl, pTrustee, &access) != ERROR_SUCCESS) { + throwWindowsException(env, GetLastError()); + } + return (jint)access; +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_CreateSymbolicLink0(JNIEnv* env, + jclass this, jlong linkAddress, jlong targetAddress, jint flags) +{ + LPCWSTR link = jlong_to_ptr(linkAddress); + LPCWSTR target = jlong_to_ptr(targetAddress); + + if (CreateSymbolicLink_func == NULL) { + JNU_ThrowInternalError(env, "Should not get here"); + return; + } + + /* On Windows 64-bit this appears to succeed even when there is insufficient privileges */ + if ((*CreateSymbolicLink_func)(link, target, (DWORD)flags) == 0) + throwWindowsException(env, GetLastError()); +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_CreateHardLink0(JNIEnv* env, + jclass this, jlong newFileAddress, jlong existingFileAddress) +{ + LPCWSTR newFile = jlong_to_ptr(newFileAddress); + LPCWSTR existingFile = jlong_to_ptr(existingFileAddress); + + if (CreateHardLink_func == NULL) { + JNU_ThrowInternalError(env, "Should not get here"); + return; + } + if ((*CreateHardLink_func)(newFile, existingFile, NULL) == 0) + throwWindowsException(env, GetLastError()); +} + +JNIEXPORT jstring JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetFullPathName0(JNIEnv *env, + jclass clz, + jlong pathAddress) +{ + jstring rv = NULL; + WCHAR *lpBuf = NULL; + WCHAR buf[MAX_PATH]; + DWORD len; + LPCWSTR lpFileName = jlong_to_ptr(pathAddress); + + len = GetFullPathNameW(lpFileName, MAX_PATH, buf, NULL); + if (len > 0) { + if (len < MAX_PATH) { + rv = (*env)->NewString(env, buf, len); + } else { + len += 1; /* return length does not include terminator */ + lpBuf = (WCHAR*)malloc(len * sizeof(WCHAR)); + if (lpBuf != NULL) { + len = GetFullPathNameW(lpFileName, len, lpBuf, NULL); + if (len > 0) { + rv = (*env)->NewString(env, lpBuf, len); + } else { + JNU_ThrowInternalError(env, "GetFullPathNameW failed"); + } + free(lpBuf); + } + } + } + if (len == 0) + throwWindowsException(env, GetLastError()); + + return rv; +} + +JNIEXPORT jstring JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetFinalPathNameByHandle(JNIEnv* env, + jclass this, jlong handle) +{ + jstring rv = NULL; + WCHAR *lpBuf = NULL; + WCHAR path[MAX_PATH]; + HANDLE h = (HANDLE)jlong_to_ptr(handle); + DWORD len; + + if (GetFinalPathNameByHandle_func == NULL) { + JNU_ThrowInternalError(env, "Should not get here"); + return NULL; + } + + len = (*GetFinalPathNameByHandle_func)(h, path, MAX_PATH, 0); + if (len > 0) { + if (len < MAX_PATH) { + rv = (*env)->NewString(env, (const jchar *)path, (jsize)len); + } else { + len += 1; /* return length does not include terminator */ + lpBuf = (WCHAR*)malloc(len * sizeof(WCHAR)); + if (lpBuf != NULL) { + len = (*GetFinalPathNameByHandle_func)(h, lpBuf, len, 0); + if (len > 0) { + rv = (*env)->NewString(env, (const jchar *)lpBuf, (jsize)len); + } else { + JNU_ThrowInternalError(env, "GetFinalPathNameByHandleW failed"); + } + free(lpBuf); + } + } + } + + if (len == 0) + throwWindowsException(env, GetLastError()); + + return rv; +} + +JNIEXPORT jlong JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_CreateIoCompletionPort(JNIEnv* env, jclass this, + jlong fileHandle, jlong existingPort, jint completionKey) +{ + HANDLE port = CreateIoCompletionPort((HANDLE)jlong_to_ptr(fileHandle), + (HANDLE)jlong_to_ptr(existingPort), + (DWORD)completionKey, + 0); + if (port == NULL) { + throwWindowsException(env, GetLastError()); + } + return ptr_to_jlong(port); +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_GetQueuedCompletionStatus0(JNIEnv* env, jclass this, + jlong completionPort, jobject obj) +{ + DWORD bytesTransferred; + DWORD completionKey; + OVERLAPPED *lpOverlapped; + BOOL res; + + res = GetQueuedCompletionStatus((HANDLE)jlong_to_ptr(completionPort), + &bytesTransferred, + &completionKey, + &lpOverlapped, + INFINITE); + if (res == 0 && lpOverlapped == NULL) { + throwWindowsException(env, GetLastError()); + } else { + DWORD ioResult = (res == 0) ? GetLastError() : 0; + (*env)->SetIntField(env, obj, completionStatus_error, ioResult); + (*env)->SetIntField(env, obj, completionStatus_bytesTransferred, + (jint)bytesTransferred); + (*env)->SetIntField(env, obj, completionStatus_completionKey, + (jint)completionKey); + + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_PostQueuedCompletionStatus(JNIEnv* env, jclass this, + jlong completionPort, jint completionKey) +{ + BOOL res; + + res = PostQueuedCompletionStatus((HANDLE)jlong_to_ptr(completionPort), + (DWORD)0, /* dwNumberOfBytesTransferred */ + (DWORD)completionKey, + NULL); /* lpOverlapped */ + if (res == 0) { + throwWindowsException(env, GetLastError()); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_ReadDirectoryChangesW(JNIEnv* env, jclass this, + jlong hDirectory, jlong bufferAddress, jint bufferLength, jboolean watchSubTree, jint filter, + jlong bytesReturnedAddress, jlong pOverlapped) +{ + BOOL res; + BOOL subtree = (watchSubTree == JNI_TRUE) ? TRUE : FALSE; + + ((LPOVERLAPPED)jlong_to_ptr(pOverlapped))->hEvent = NULL; + res = ReadDirectoryChangesW((HANDLE)jlong_to_ptr(hDirectory), + (LPVOID)jlong_to_ptr(bufferAddress), + (DWORD)bufferLength, + subtree, + (DWORD)filter, + (LPDWORD)jlong_to_ptr(bytesReturnedAddress), + (LPOVERLAPPED)jlong_to_ptr(pOverlapped), + NULL); + if (res == 0) { + throwWindowsException(env, GetLastError()); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_BackupRead0(JNIEnv* env, jclass this, + jlong hFile, jlong bufferAddress, jint bufferSize, jboolean abort, + jlong context, jobject obj) +{ + BOOL res; + DWORD bytesTransferred; + BOOL a = (abort == JNI_TRUE) ? TRUE : FALSE; + VOID* pContext = (VOID*)jlong_to_ptr(context); + + res = BackupRead((HANDLE)jlong_to_ptr(hFile), + (LPBYTE)jlong_to_ptr(bufferAddress), + (DWORD)bufferSize, + &bytesTransferred, + a, + FALSE, + &pContext); + if (res == 0) { + throwWindowsException(env, GetLastError()); + } else { + (*env)->SetIntField(env, obj, backupResult_bytesTransferred, + bytesTransferred); + (*env)->SetLongField(env, obj, backupResult_context, + ptr_to_jlong(pContext)); + } +} + +JNIEXPORT void JNICALL +Java_sun_nio_fs_WindowsNativeDispatcher_BackupSeek(JNIEnv* env, jclass this, + jlong hFile, jlong bytesToSeek, jlong context) +{ + BOOL res; + jint lowBytesToSeek = (jint)bytesToSeek; + jint highBytesToSeek = (jint)(bytesToSeek >> 32); + DWORD lowBytesSeeked; + DWORD highBytesSeeked; + VOID* pContext = jlong_to_ptr(context); + + res = BackupSeek((HANDLE)jlong_to_ptr(hFile), + (DWORD)lowBytesToSeek, + (DWORD)highBytesToSeek, + &lowBytesSeeked, + &highBytesSeeked, + &pContext); + if (res == 0) { + throwWindowsException(env, GetLastError()); + } +} diff --git a/test/java/nio/channels/AsynchronousChannelGroup/AsExecutor.java b/test/java/nio/channels/AsynchronousChannelGroup/AsExecutor.java new file mode 100644 index 000000000..995c00ffd --- /dev/null +++ b/test/java/nio/channels/AsynchronousChannelGroup/AsExecutor.java @@ -0,0 +1,81 @@ +/* + * 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. + */ + +import java.nio.channels.AsynchronousChannelGroup; +import java.util.concurrent.*; + +/** + * Test that arbitrary tasks can be submitted to a channel group's thread pool. + */ + +public class AsExecutor { + + public static void main(String[] args) throws Exception { + // create channel groups + ThreadFactory factory = new PrivilegedThreadFactory(); + AsynchronousChannelGroup group1 = AsynchronousChannelGroup + .withFixedThreadPool(5, factory); + AsynchronousChannelGroup group2 = AsynchronousChannelGroup + .withCachedThreadPool(Executors.newCachedThreadPool(factory), 0); + + try { + // execute simple tasks + testSimpleTask(group1); + testSimpleTask(group2); + + // install security manager and test again + System.setSecurityManager( new SecurityManager() ); + testSimpleTask(group1); + testSimpleTask(group2); + + // attempt to execute tasks that run with only frames from boot + // class loader on the stack. + testAttackingTask(group1); + testAttackingTask(group2); + } finally { + group1.shutdown(); + group2.shutdown(); + } + } + + static void testSimpleTask(AsynchronousChannelGroup group) throws Exception { + Executor executor = (Executor)group; + final CountDownLatch latch = new CountDownLatch(1); + executor.execute(new Runnable() { + public void run() { + latch.countDown(); + } + }); + latch.await(); + } + + static void testAttackingTask(AsynchronousChannelGroup group) throws Exception { + Executor executor = (Executor)group; + Attack task = new Attack(); + executor.execute(task); + task.waitUntilDone(); + if (!task.failedDueToSecurityException()) + throw new RuntimeException("SecurityException expected"); + } + +} diff --git a/test/java/nio/channels/AsynchronousChannelGroup/Attack.java b/test/java/nio/channels/AsynchronousChannelGroup/Attack.java new file mode 100644 index 000000000..491926c86 --- /dev/null +++ b/test/java/nio/channels/AsynchronousChannelGroup/Attack.java @@ -0,0 +1,63 @@ +/* + * 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. + */ + +import java.net.*; +import java.io.IOException; +import java.util.concurrent.CountDownLatch; + +/** + * A task that attempts to attack the current host. + */ + +public class Attack implements Runnable { + private final CountDownLatch latch = new CountDownLatch(1); + private volatile boolean failedDueToSecurityException; + + public void Attack() { + // check class is on boot class path + if (Attack.class.getClassLoader() != null) + throw new RuntimeException("Attack class not on boot class path"); + } + + @Override + public void run() { + try { + new Socket("127.0.0.1", 9999).close(); + throw new RuntimeException("Connected (not expected)"); + } catch (IOException e) { + throw new RuntimeException("IOException (not expected)"); + } catch (SecurityException e) { + failedDueToSecurityException = true; + } finally { + latch.countDown(); + } + } + + public void waitUntilDone() throws InterruptedException { + latch.await(); + } + + public boolean failedDueToSecurityException() { + return failedDueToSecurityException; + } +} diff --git a/test/java/nio/channels/AsynchronousChannelGroup/BadProperties.java b/test/java/nio/channels/AsynchronousChannelGroup/BadProperties.java new file mode 100644 index 000000000..7c109b1fe --- /dev/null +++ b/test/java/nio/channels/AsynchronousChannelGroup/BadProperties.java @@ -0,0 +1,41 @@ +/* + * 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 4607272 + * @summary Unit test for AsynchronousChannelGroup + * @build BadProperties + * @run main/othervm/fail -Djava.nio.channels.DefaultThreadPool.threadFactory BadProperties + * @run main/othervm/fail -Djava.nio.channels.DefaultThreadPool.threadFactory=Missing BadProperties + * @run main/othervm/fail -Djava.nio.channels.DefaultThreadPool.initialSize BadProperties + * @run main/othervm/fail -Djava.nio.channels.DefaultThreadPool.initialSize=NaN BadProperties + */ + +import java.nio.channels.AsynchronousSocketChannel; +import java.io.IOException; + +public class BadProperties { + public static void main(String[] args) throws IOException { + AsynchronousSocketChannel.open(); + } +} diff --git a/test/java/nio/channels/AsynchronousChannelGroup/Basic.java b/test/java/nio/channels/AsynchronousChannelGroup/Basic.java new file mode 100644 index 000000000..f26ed171d --- /dev/null +++ b/test/java/nio/channels/AsynchronousChannelGroup/Basic.java @@ -0,0 +1,259 @@ +/* + * 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 4607272 + * @summary Unit test for AsynchronousChannelGroup + * @build Basic + * @run main/othervm -XX:-UseVMInterruptibleIO Basic + */ + +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.net.*; +import java.util.*; +import java.util.concurrent.*; +import java.io.IOException; + +public class Basic { + static final Random rand = new Random(); + static final ThreadFactory threadFactory = new ThreadFactory() { + @Override + public Thread newThread(final Runnable r) { + return new Thread(r); + }}; + + + public static void main(String[] args) throws Exception { + shutdownTests(); + shutdownNowTests(); + afterShutdownTests(); + miscTests(); + } + + static void shutdownTests() throws Exception { + System.out.println("-- test shutdown --"); + + // test shutdown with no channels in groups + for (int i=0; i<500; i++) { + ExecutorService pool = null; + AsynchronousChannelGroup group; + if (rand.nextBoolean()) { + pool = Executors.newCachedThreadPool(); + group = AsynchronousChannelGroup.withCachedThreadPool(pool, rand.nextInt(5)); + } else { + int nThreads = 1 + rand.nextInt(8); + group = AsynchronousChannelGroup.withFixedThreadPool(nThreads, threadFactory); + } + group.shutdown(); + if (!group.isShutdown()) + throw new RuntimeException("Group should be shutdown"); + // group should terminate quickly + boolean terminated = group.awaitTermination(3, TimeUnit.SECONDS); + if (!terminated) + throw new RuntimeException("Group should have terminated"); + if (pool != null && !pool.isTerminated()) + throw new RuntimeException("Executor should have terminated"); + } + + // shutdown with channel in group + for (int i=0; i<500; i++) { + ExecutorService pool = null; + AsynchronousChannelGroup group; + if (rand.nextBoolean()) { + pool = Executors.newCachedThreadPool(); + group = AsynchronousChannelGroup.withCachedThreadPool(pool, rand.nextInt(10)); + } else { + int nThreads = 1 + rand.nextInt(8); + group = AsynchronousChannelGroup.withFixedThreadPool(nThreads, threadFactory); + } + // create channel that is bound to group + AsynchronousChannel ch; + switch (rand.nextInt(3)) { + case 0 : ch = AsynchronousSocketChannel.open(group); break; + case 1 : ch = AsynchronousServerSocketChannel.open(group); break; + case 2 : ch = AsynchronousDatagramChannel.open(null, group); break; + default : throw new AssertionError(); + } + group.shutdown(); + if (!group.isShutdown()) + throw new RuntimeException("Group should be shutdown"); + + // last channel so should terminate after this channel is closed + ch.close(); + + // group should terminate quickly + boolean terminated = group.awaitTermination(3, TimeUnit.SECONDS); + if (!terminated) + throw new RuntimeException("Group should have terminated"); + if (pool != null && !pool.isTerminated()) + throw new RuntimeException("Executor should have terminated"); + } + } + + static void shutdownNowTests() throws Exception { + System.out.println("-- test shutdownNow --"); + + for (int i=0; i< 10; i++) { + ExecutorService pool = null; + AsynchronousChannelGroup group; + if (rand.nextBoolean()) { + pool = Executors.newCachedThreadPool(); + group = AsynchronousChannelGroup + .withCachedThreadPool(pool, rand.nextInt(5)); + } else { + int nThreads = 1 + rand.nextInt(8); + group = AsynchronousChannelGroup + .withFixedThreadPool(nThreads, threadFactory); + } + + // I/O in progress + AsynchronousChannel ch; + if (rand.nextBoolean()) { + AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel + .open(group).bind(new InetSocketAddress(0)); + listener.accept(); + ch = listener; + } else { + AsynchronousDatagramChannel adc = + AsynchronousDatagramChannel.open(null, group); + adc.receive(ByteBuffer.allocate(100)); + ch = adc; + } + + // forceful shutdown + group.shutdownNow(); + + // shutdownNow is required to close all channels + if (ch.isOpen()) + throw new RuntimeException("Channel should be closed"); + + boolean terminated = group.awaitTermination(3, TimeUnit.SECONDS); + if (!terminated) + throw new RuntimeException("Group should have terminated"); + if (pool != null && !pool.isTerminated()) + throw new RuntimeException("Executor should have terminated"); + } + } + + // test creating channels in group after group is shutdown + static void afterShutdownTests() throws Exception { + System.out.println("-- test operations after group is shutdown --"); + AsynchronousChannelGroup group = + AsynchronousChannelGroup.withFixedThreadPool(1, threadFactory); + + AsynchronousSocketChannel ch = AsynchronousSocketChannel.open(group); + AsynchronousServerSocketChannel listener = AsynchronousServerSocketChannel.open(group); + + // initiate accept + listener.bind(new InetSocketAddress(0)); + Future result = listener.accept(); + + // shutdown group + group.shutdown(); + if (!group.isShutdown()) + throw new RuntimeException("Group should be shutdown"); + + // attempt to create another channel + try { + AsynchronousSocketChannel.open(group); + throw new RuntimeException("ShutdownChannelGroupException expected"); + } catch (ShutdownChannelGroupException x) { + } + try { + AsynchronousServerSocketChannel.open(group); + throw new RuntimeException("ShutdownChannelGroupException expected"); + } catch (ShutdownChannelGroupException x) { + } + + // attempt to create another channel by connecting. This should cause + // the accept operation to fail. + InetAddress lh = InetAddress.getLocalHost(); + int port = ((InetSocketAddress)listener.getLocalAddress()).getPort(); + InetSocketAddress isa = new InetSocketAddress(lh, port); + ch.connect(isa).get(); + try { + result.get(); + throw new RuntimeException("Connection was accepted"); + } catch (ExecutionException x) { + Throwable cause = x.getCause(); + if (!(cause instanceof IOException)) + throw new RuntimeException("Cause should be IOException"); + cause = cause.getCause(); + if (!(cause instanceof ShutdownChannelGroupException)) + throw new RuntimeException("IOException cause should be ShutdownChannelGroupException"); + } + + // initiate another accept even though channel group is shutdown. + Future res = listener.accept(); + try { + res.get(3, TimeUnit.SECONDS); + throw new RuntimeException("TimeoutException expected"); + } catch (TimeoutException x) { + } + // connect to the listener which should cause the accept to complete + AsynchronousSocketChannel.open().connect(isa); + try { + res.get(); + throw new RuntimeException("Connection was accepted"); + } catch (ExecutionException x) { + Throwable cause = x.getCause(); + if (!(cause instanceof IOException)) + throw new RuntimeException("Cause should be IOException"); + cause = cause.getCause(); + if (!(cause instanceof ShutdownChannelGroupException)) + throw new RuntimeException("IOException cause should be ShutdownChannelGroupException"); + } + + // group should *not* terminate as channels are open + boolean terminated = group.awaitTermination(3, TimeUnit.SECONDS); + if (terminated) + throw new RuntimeException("Group should not have terminated"); + + // close channel; group should terminate quickly + ch.close(); + listener.close(); + terminated = group.awaitTermination(3, TimeUnit.SECONDS); + if (!terminated) + throw new RuntimeException("Group should have terminated"); + } + + static void miscTests() throws Exception { + System.out.println("-- miscellenous tests --"); + try { + AsynchronousChannelGroup.withFixedThreadPool(1, null); + throw new RuntimeException("NPE expected"); + } catch (NullPointerException x) { + } + try { + AsynchronousChannelGroup.withFixedThreadPool(0, threadFactory); + throw new RuntimeException("IAE expected"); + } catch (IllegalArgumentException e) { + } + try { + AsynchronousChannelGroup.withCachedThreadPool(null, 0); + throw new RuntimeException("NPE expected"); + } catch (NullPointerException x) { + } + } +} diff --git a/test/java/nio/channels/AsynchronousChannelGroup/GroupOfOne.java b/test/java/nio/channels/AsynchronousChannelGroup/GroupOfOne.java new file mode 100644 index 000000000..b116245ce --- /dev/null +++ b/test/java/nio/channels/AsynchronousChannelGroup/GroupOfOne.java @@ -0,0 +1,140 @@ +/* + * 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 4607272 + * @summary Unit test for AsynchronousChannelGroup + */ + +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.net.*; +import java.util.concurrent.*; +import java.io.IOException; + +/** + * This test verifies that a channel or channel group can be closed from a + * completion handler when there are no threads available to handle I/O events. + */ + +public class GroupOfOne { + + public static void main(String[] args) throws Exception { + // create listener to accept connections + final AsynchronousServerSocketChannel listener = + AsynchronousServerSocketChannel.open() + .bind(new InetSocketAddress(0)); + listener.accept(null, new CompletionHandler() { + public void completed(AsynchronousSocketChannel ch, Void att) { + listener.accept(null, this); + } + public void failed(Throwable exc, Void att) { + } + public void cancelled(Void att) { + } + }); + + int port = ((InetSocketAddress)(listener.getLocalAddress())).getPort(); + SocketAddress sa = new InetSocketAddress(InetAddress.getLocalHost(), port); + + test(sa, true, false); + test(sa, false, true); + test(sa, true, true); + } + + static void test(SocketAddress sa, + final boolean closeChannel, + final boolean shutdownGroup) + throws Exception + { + // group with 1 thread + final AsynchronousChannelGroup group = AsynchronousChannelGroup + .withFixedThreadPool(1, new ThreadFactory() { + @Override + public Thread newThread(final Runnable r) { + return new Thread(r); + }}); + final AsynchronousSocketChannel ch = AsynchronousSocketChannel.open(group); + + // the latch counts down when: + // 1. The read operation fails (expected) + // 2. the close/shutdown completes + final CountDownLatch latch = new CountDownLatch(2); + + ch.connect(sa, null, new CompletionHandler() { + public void completed(Void result, Void att) { + System.out.println("Connected"); + + // initiate I/O operation that does not complete (successfully) + ByteBuffer buf = ByteBuffer.allocate(100); + ch.read(buf, null, new CompletionHandler() { + public void completed(Integer bytesRead, Void att) { + throw new RuntimeException(); + } + public void failed(Throwable exc, Void att) { + if (!(exc instanceof AsynchronousCloseException)) + throw new RuntimeException(exc); + System.out.println("Read failed (expected)"); + latch.countDown(); + } + public void cancelled(Void att) { + throw new RuntimeException(); + } + }); + + // close channel or shutdown group + try { + if (closeChannel) { + System.out.print("Close channel ..."); + ch.close(); + System.out.println(" done."); + } + if (shutdownGroup) { + System.out.print("Shutdown group ..."); + group.shutdownNow(); + System.out.println(" done."); + } + latch.countDown(); + } catch (IOException e) { + throw new RuntimeException(); + } + } + public void failed(Throwable exc, Void att) { + throw new RuntimeException(exc); + } + public void cancelled(Void att) { + throw new RuntimeException(); + } + }); + + latch.await(); + + // clean-up + group.shutdown(); + boolean terminated = group.awaitTermination(5, TimeUnit.SECONDS); + if (!terminated) + throw new RuntimeException("Group did not terminate"); + + System.out.println("TEST OKAY"); + } +} diff --git a/test/java/nio/channels/AsynchronousChannelGroup/Identity.java b/test/java/nio/channels/AsynchronousChannelGroup/Identity.java new file mode 100644 index 000000000..f41c12c80 --- /dev/null +++ b/test/java/nio/channels/AsynchronousChannelGroup/Identity.java @@ -0,0 +1,166 @@ +/* + * 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 4607272 + * @summary Unit test for AsynchronousChannelGroup + */ + +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.net.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; + +/** + * Tests that the completion handler is invoked by a thread with + * the expected identity. + */ + +public class Identity { + static final Random rand = new Random(); + static final CountDownLatch done = new CountDownLatch(1); + static final AtomicBoolean failed = new AtomicBoolean(false); + + static void fail(String msg) { + failed.set(true); + done.countDown(); + throw new RuntimeException(msg); + } + + // thread-local identifies the thread + private static final ThreadLocal myGroup = + new ThreadLocal() { + @Override protected Integer initialValue() { + return Integer.valueOf(-1); + } + }; + + // creates a ThreadFactory that constructs groups with the given identity + static final ThreadFactory createThreadFactory(final int groupId) { + return new ThreadFactory() { + @Override + public Thread newThread(final Runnable r) { + Thread t = new Thread(new Runnable() { + public void run() { + myGroup.set(groupId); + r.run(); + }}); + t.setDaemon(true); + return t; + } + }; + } + + public static void main(String[] args) throws Exception { + // create listener to accept connections + final AsynchronousServerSocketChannel listener = + AsynchronousServerSocketChannel.open() + .bind(new InetSocketAddress(0)); + listener.accept(null, new CompletionHandler() { + public void completed(final AsynchronousSocketChannel ch, Void att) { + listener.accept(null, this); + + final ByteBuffer buf = ByteBuffer.allocate(100); + ch.read(buf, null, new CompletionHandler() { + public void completed(Integer bytesRead, Void att) { + buf.clear(); + ch.read(buf, null, this); + } + public void failed(Throwable exc, Void att) { + } + public void cancelled(Void att) { + } + }); + } + public void failed(Throwable exc, Void att) { + } + public void cancelled(Void att) { + } + }); + int port = ((InetSocketAddress)(listener.getLocalAddress())).getPort(); + SocketAddress sa = new InetSocketAddress(InetAddress.getLocalHost(), port); + + // create 3-10 channels, each in its own group + final int groupCount = 3 + rand.nextInt(8); + final AsynchronousSocketChannel[] channel = new AsynchronousSocketChannel[groupCount]; + for (int i=0; i() { + public void completed(Integer bytesWritten, Integer groupId) { + if (bytesWritten != 1) + fail("Expected 1 byte to be written"); + if (!myGroup.get().equals(groupId)) + fail("Handler invoked by thread with the wrong identity"); + if (writeCount.decrementAndGet() > 0) { + int id = rand.nextInt(groupCount); + channel[id].write(getBuffer(), id, this); + } else { + done.countDown(); + } + } + public void failed(Throwable exc, Integer groupId) { + fail(exc.getMessage()); + } + public void cancelled(Integer groupId) { + fail("I/O operation was cancelled"); + } + }); + + // wait until + done.await(); + if (failed.get()) + throw new RuntimeException("Test failed - see log for details"); + } + + static ByteBuffer getBuffer() { + ByteBuffer buf; + if (rand.nextBoolean()) { + buf = ByteBuffer.allocateDirect(1); + } else { + buf = ByteBuffer.allocate(1); + } + buf.put((byte)0); + buf.flip(); + return buf; + } +} diff --git a/test/java/nio/channels/AsynchronousChannelGroup/PrivilegedThreadFactory.java b/test/java/nio/channels/AsynchronousChannelGroup/PrivilegedThreadFactory.java new file mode 100644 index 000000000..e2c4575c7 --- /dev/null +++ b/test/java/nio/channels/AsynchronousChannelGroup/PrivilegedThreadFactory.java @@ -0,0 +1,50 @@ +/* + * 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. + */ + +import java.util.concurrent.ThreadFactory; +import java.security.AccessController; +import java.security.PrivilegedAction; + +/** + * The "privileged" ThreadFactory used by the AsExecutor test. + */ + +public class PrivilegedThreadFactory implements ThreadFactory { + public void PrivilegedThreadPoolFactory() { + // check class is on boot class path + if (PrivilegedThreadFactory.class.getClassLoader() != null) + throw new RuntimeException("PrivilegedThreadFactory class not on boot class path"); + } + + @Override + public Thread newThread(final Runnable r) { + return AccessController.doPrivileged(new PrivilegedAction() { + @Override + public Thread run() { + Thread t = new Thread(r); + t.setDaemon(true); + return t; + } + }); + } +} diff --git a/test/java/nio/channels/AsynchronousChannelGroup/Restart.java b/test/java/nio/channels/AsynchronousChannelGroup/Restart.java new file mode 100644 index 000000000..567321e30 --- /dev/null +++ b/test/java/nio/channels/AsynchronousChannelGroup/Restart.java @@ -0,0 +1,133 @@ +/* + * 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 4607272 + * @summary Unit test for AsynchronousChannelGroup + * @build Restart + * @run main/othervm -XX:-UseVMInterruptibleIO Restart + */ + +import java.nio.channels.*; +import java.net.*; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.io.IOException; + +/** + * Exercise replacement of threads in the thread pool when completion handlers + * terminate due to errors or runtime exceptions. + */ + +public class Restart { + static final Random rand = new Random(); + + public static void main(String[] args) throws Exception { + // thread group for thread pools + final ThreadGroup tg = new ThreadGroup("test"); + + // keep track of the number of threads that terminate + final AtomicInteger exceptionCount = new AtomicInteger(0); + final Thread.UncaughtExceptionHandler ueh = + new Thread.UncaughtExceptionHandler() { + public void uncaughtException(Thread t, Throwable e) { + exceptionCount.incrementAndGet(); + } + }; + ThreadFactory factory = new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(tg, r); + t.setUncaughtExceptionHandler(ueh); + return t; + } + }; + + // group with fixed thread pool + int nThreads = 1 + rand.nextInt(4); + AsynchronousChannelGroup group = + AsynchronousChannelGroup.withFixedThreadPool(nThreads, factory); + testRestart(group, 100); + group.shutdown(); + + // group with custom thread pool + ExecutorService pool = Executors.newCachedThreadPool(factory); + group = AsynchronousChannelGroup.withCachedThreadPool(pool, rand.nextInt(5)); + testRestart(group, 100); + group.shutdown(); + + // give time for threads to terminate + Thread.sleep(3000); + int actual = exceptionCount.get(); + if (actual != 200) + throw new RuntimeException(actual + " exceptions, expected: " + 200); + } + + static void testRestart(AsynchronousChannelGroup group, int count) + throws Exception + { + AsynchronousServerSocketChannel listener = + AsynchronousServerSocketChannel.open(group) + .bind(new InetSocketAddress(0)); + + for (int i=0; i() { + public void completed(AsynchronousSocketChannel ch, Void att) { + try { + ch.close(); + } catch (IOException ignore) { } + + latch.countDown(); + + // throw error or runtime exception + if (rand.nextBoolean()) { + throw new Error(); + } else { + throw new RuntimeException(); + } + } + public void failed(Throwable exc, Void att) { + } + public void cancelled(Void att) { + } + }); + + // establish loopback connection which should cause completion + // handler to be invoked. + int port = ((InetSocketAddress)(listener.getLocalAddress())).getPort(); + AsynchronousSocketChannel ch = AsynchronousSocketChannel.open(); + InetAddress lh = InetAddress.getLocalHost(); + ch.connect(new InetSocketAddress(lh, port)).get(); + ch.close(); + + // wait for handler to be invoked + latch.await(); + } + + // clean-up + listener.close(); + } +} diff --git a/test/java/nio/channels/AsynchronousChannelGroup/Unbounded.java b/test/java/nio/channels/AsynchronousChannelGroup/Unbounded.java new file mode 100644 index 000000000..f615c3c50 --- /dev/null +++ b/test/java/nio/channels/AsynchronousChannelGroup/Unbounded.java @@ -0,0 +1,120 @@ +/* + * 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 4607272 + * @summary Unit test for AsynchronousChannelGroup + */ + +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.net.*; +import java.util.concurrent.*; +import java.io.IOException; + +public class Unbounded { + // number of concurrent completion handlers + static final int CONCURRENCY_COUNT = 512; + + public static void main(String[] args) throws Exception { + // all accepted connections are added to a queue + final ArrayBlockingQueue queue = + new ArrayBlockingQueue(CONCURRENCY_COUNT); + + // create listener to accept connections + final AsynchronousServerSocketChannel listener = + AsynchronousServerSocketChannel.open() + .bind(new InetSocketAddress(0)); + listener.accept(null, new CompletionHandler() { + public void completed(AsynchronousSocketChannel ch, Void att) { + queue.add(ch); + listener.accept(null, this); + } + public void failed(Throwable exc, Void att) { + } + public void cancelled(Void att) { + } + }); + System.out.println("Listener created."); + + // establish lots of connections + int port = ((InetSocketAddress)(listener.getLocalAddress())).getPort(); + SocketAddress sa = new InetSocketAddress(InetAddress.getLocalHost(), port); + AsynchronousSocketChannel[] channels = + new AsynchronousSocketChannel[CONCURRENCY_COUNT]; + for (int i=0; i= 3) + throw x; + Thread.sleep(50); + } + } + } + System.out.println("All connection established."); + + // the barrier where all threads (plus the main thread) wait + final CyclicBarrier barrier = new CyclicBarrier(CONCURRENCY_COUNT+1); + + // initiate a read operation on each channel. + for (int i=0; i() { + public void completed(Integer bytesRead, AsynchronousSocketChannel ch) { + try { + ch.close(); + barrier.await(); + } catch (Exception x) { + throw new AssertionError(x); + } + } + public void failed(Throwable exc, AsynchronousSocketChannel ch) { + } + public void cancelled(AsynchronousSocketChannel ch) { + } + }); + } + System.out.println("All read operations outstanding."); + + // write data to each of the accepted connections + int remaining = CONCURRENCY_COUNT; + while (remaining > 0) { + AsynchronousSocketChannel ch = queue.take(); + ch.write(ByteBuffer.wrap("welcome".getBytes())).get(); + ch.close(); + remaining--; + } + + // wait for all threads to reach the barrier + System.out.println("Waiting for all threads to reach barrier"); + barrier.await(); + listener.close(); + } +} diff --git a/test/java/nio/channels/AsynchronousChannelGroup/run_any_task.sh b/test/java/nio/channels/AsynchronousChannelGroup/run_any_task.sh new file mode 100644 index 000000000..97f4f968a --- /dev/null +++ b/test/java/nio/channels/AsynchronousChannelGroup/run_any_task.sh @@ -0,0 +1,52 @@ +# +# 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 4607272 +# @summary Unit test for AsynchronousChannelGrou#execute +# @build AsExecutor PrivilegedThreadFactory Attack +# @run shell run_any_task.sh + +# if TESTJAVA isn't set then we assume an interactive run. + +if [ -z "$TESTJAVA" ]; then + TESTSRC=. + TESTCLASSES=. + JAVA=java + JAR=jar +else + JAVA="${TESTJAVA}/bin/java" + JAR="${TESTJAVA}/bin/jar" +fi + +echo "Creating JAR file ..." +$JAR -cf "${TESTCLASSES}/Privileged.jar" \ + -C "${TESTCLASSES}" PrivilegedThreadFactory.class \ + -C "${TESTCLASSES}" PrivilegedThreadFactory\$1.class \ + -C "${TESTCLASSES}" Attack.class + +echo "Running test ..." +$JAVA -XX:-UseVMInterruptibleIO \ + -Xbootclasspath/a:"${TESTCLASSES}/Privileged.jar" \ + -classpath "${TESTCLASSES}" \ + AsExecutor diff --git a/test/java/nio/channels/AsynchronousDatagramChannel/Basic.java b/test/java/nio/channels/AsynchronousDatagramChannel/Basic.java new file mode 100644 index 000000000..5ed3d83a9 --- /dev/null +++ b/test/java/nio/channels/AsynchronousDatagramChannel/Basic.java @@ -0,0 +1,448 @@ +/* + * 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 4527345 + * @summary Unit test for AsynchronousDatagramChannel + */ + +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.net.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; + +public class Basic { + + public static void main(String[] args) throws Exception { + doReceiveTests(); + doReadTests(); + doSendTests(); + doWriteTests(); + doCancelTests(); + doMulticastTests(); + } + + // basic receive tests + static void doReceiveTests() throws Exception { + final byte[] msg = "hello".getBytes(); + + AsynchronousDatagramChannel ch = AsynchronousDatagramChannel.open() + .bind(new InetSocketAddress(0)); + int port = ((InetSocketAddress)(ch.getLocalAddress())).getPort(); + InetAddress rh = InetAddress.getLocalHost(); + final SocketAddress sa = new InetSocketAddress(rh, port); + + DatagramChannel sender = DatagramChannel.open(); + ByteBuffer dst = ByteBuffer.allocateDirect(100); + + // Test: datagram packet received immediately + sender.send(ByteBuffer.wrap(msg), sa); + dst.clear(); + ch.receive(dst).get(1, TimeUnit.SECONDS); + if (dst.flip().remaining() != msg.length) + throw new RuntimeException("Unexpected number of bytes read"); + + // Test: datagram packet not received immediately + dst.clear(); + final CountDownLatch latch = new CountDownLatch(1); + ch.receive(dst, null, new CompletionHandler() { + public void completed(SocketAddress source, Void att) { + latch.countDown(); + } + public void failed (Throwable exc, Void att) { + } + public void cancelled(Void att) { + } + }); + Thread.sleep(2000); + sender.send(ByteBuffer.wrap(msg), sa); + latch.await(2, TimeUnit.SECONDS); // wait for completion handler + + // Test: timeout + dst.clear(); + final AtomicReference exception = new AtomicReference(); + ch.receive(dst, 2, TimeUnit.SECONDS, null, new CompletionHandler() { + public void completed(SocketAddress source, Void att) { + } + public void failed (Throwable exc, Void att) { + exception.set(exc); + } + public void cancelled(Void att) { + } + }); + Throwable result; + while ((result = exception.get()) == null) { + Thread.sleep(100); + } + if (!(result instanceof InterruptedByTimeoutException)) + throw new RuntimeException("InterruptedByTimeoutException expected"); + + // AsynchronousCloseException + dst = ByteBuffer.allocateDirect(100); + exception.set(null); + ch.receive(dst, null, new CompletionHandler() { + public void completed(SocketAddress source, Void att) { + } + public void failed (Throwable exc, Void att) { + exception.set(exc); + } + public void cancelled(Void att) { + } + }); + ch.close(); + while ((result = exception.get()) == null) { + Thread.sleep(100); + } + if (!(result instanceof AsynchronousCloseException)) + throw new RuntimeException("AsynchronousCloseException expected"); + + // done + sender.close(); + } + + // basic read tests + static void doReadTests() throws Exception { + final byte[] msg = "hello".getBytes(); + + AsynchronousDatagramChannel ch = AsynchronousDatagramChannel.open() + .bind(new InetSocketAddress(0)); + int port = ((InetSocketAddress)(ch.getLocalAddress())).getPort(); + InetAddress lh = InetAddress.getLocalHost(); + final SocketAddress sa = new InetSocketAddress(lh, port); + + DatagramChannel sender = DatagramChannel.open(); + ByteBuffer dst = ByteBuffer.allocateDirect(100); + + // Test: not connected + try { + ch.read(dst); + throw new RuntimeException("NotYetConnectedException expected"); + } catch (NotYetConnectedException e) { + } + + // connect the channel + sender.bind(new InetSocketAddress(0)); + ch.connect(new InetSocketAddress(lh, + ((InetSocketAddress)(sender.getLocalAddress())).getPort())); + + // Test: datagram packet received immediately + sender.send(ByteBuffer.wrap(msg), sa); + dst.clear(); + ch.read(dst).get(1, TimeUnit.SECONDS); + if (dst.flip().remaining() != msg.length) + throw new RuntimeException("Unexpected number of bytes read"); + + // Test: datagram packet not received immediately + dst.clear(); + final CountDownLatch l1 = new CountDownLatch(1); + ch.read(dst, null, new CompletionHandler() { + public void completed(Integer bytesRead, Void att) { + l1.countDown(); + } + public void failed (Throwable exc, Void att) { + } + public void cancelled(Void att) { + } + }); + Thread.sleep(2000); + sender.send(ByteBuffer.wrap(msg), sa); + l1.await(2, TimeUnit.SECONDS); + + // Test: timeout + dst.clear(); + final AtomicReference exception = new AtomicReference(); + ch.read(dst, 2, TimeUnit.SECONDS, null, new CompletionHandler() { + public void completed(Integer bytesRead, Void att) { + } + public void failed (Throwable exc, Void att) { + exception.set(exc); + } + public void cancelled(Void att) { + } + }); + Throwable result; + while ((result = exception.get()) == null) { + Thread.sleep(100); + } + if (!(result instanceof InterruptedByTimeoutException)) + throw new RuntimeException("InterruptedByTimeoutException expected"); + + // AsynchronousCloseException + dst.clear(); + exception.set(null); + ch.read(dst, null, new CompletionHandler() { + public void completed(Integer bytesRead, Void att) { + } + public void failed (Throwable exc, Void att) { + exception.set(exc); + } + public void cancelled(Void att) { + } + }); + ch.close(); + while ((result = exception.get()) == null) { + Thread.sleep(100); + } + if (!(result instanceof AsynchronousCloseException)) + throw new RuntimeException("AsynchronousCloseException expected"); + + // done + sender.close(); + } + + // basic send tests + static void doSendTests() throws Exception { + final byte[] msg = "hello".getBytes(); + + DatagramChannel reader = DatagramChannel.open() + .bind(new InetSocketAddress(0)); + int port = ((InetSocketAddress)(reader.getLocalAddress())).getPort(); + InetAddress rh = InetAddress.getLocalHost(); + SocketAddress sa = new InetSocketAddress(rh, port); + + AsynchronousDatagramChannel ch = AsynchronousDatagramChannel.open(); + + // Test: send datagram packet to reader + int bytesSent = ch.send(ByteBuffer.wrap(msg), sa).get(); + if (bytesSent != msg.length) + throw new RuntimeException("Unexpected number of bytes sent"); + + // check received + ByteBuffer dst = ByteBuffer.allocateDirect(100); + reader.receive(dst); + dst.flip(); + if (dst.remaining() != msg.length) + throw new RuntimeException("Unexpected number of bytes received"); + + // Test: send datagram packet to reader and check completion handler + // is invoked + final CountDownLatch l2 = new CountDownLatch(1); + ch.send(ByteBuffer.wrap(msg), sa, null, new CompletionHandler() { + public void completed(Integer bytesSent, Void att) { + if (bytesSent != msg.length) + throw new RuntimeException("Unexpected number of bytes received"); + l2.countDown(); + } + public void failed (Throwable exc, Void att) { + } + public void cancelled(Void att) { + } + }); + l2.await(5, TimeUnit.SECONDS); + + // check received + dst.clear(); + reader.receive(dst); + dst.flip(); + if (dst.remaining() != msg.length) + throw new RuntimeException("Unexpected number of bytes received"); + + // Test: check that failed method is invoked + ch.close(); + final CountDownLatch l3 = new CountDownLatch(1); + ch.send(ByteBuffer.wrap(msg), sa, null, new CompletionHandler() { + public void completed(Integer bytesSent, Void att) { + throw new RuntimeException("completed method invoked"); + } + public void failed (Throwable exc, Void att) { + if (exc instanceof ClosedChannelException) { + l3.countDown(); + } else { + throw new RuntimeException(exc); + } + } + public void cancelled(Void att) { + } + }); + l3.await(5, TimeUnit.SECONDS); + + // done + reader.close(); + } + + // basic write tests + static void doWriteTests() throws Exception { + final byte[] msg = "hello".getBytes(); + + DatagramChannel reader = DatagramChannel.open() + .bind(new InetSocketAddress(0)); + int port = ((InetSocketAddress)(reader.getLocalAddress())).getPort(); + InetAddress rh = InetAddress.getLocalHost(); + SocketAddress sa = new InetSocketAddress(rh, port); + + AsynchronousDatagramChannel ch = AsynchronousDatagramChannel.open(); + + // Test: unconnected + try { + ch.write(ByteBuffer.wrap(msg)).get(); + throw new RuntimeException("NotYetConnectedException expected"); + } catch (NotYetConnectedException e) { + } + + // Test: connect, and write datagram + ch.connect(sa); + int bytesSent = ch.write(ByteBuffer.wrap(msg)).get(); + if (bytesSent != msg.length) + throw new RuntimeException("Unexpected number of bytes sent"); + + // check received + ByteBuffer dst = ByteBuffer.allocateDirect(100); + reader.receive(dst); + dst.flip(); + if (dst.remaining() != msg.length) + throw new RuntimeException("Unexpected number of bytes received"); + + // Test: write datagram and check completion handler is invoked + final CountDownLatch l2 = new CountDownLatch(1); + ch.write(ByteBuffer.wrap(msg), null, new CompletionHandler() { + public void completed(Integer bytesSent, Void att) { + if (bytesSent != msg.length) + throw new RuntimeException("Unexpected number of bytes received"); + l2.countDown(); + } + public void failed (Throwable exc, Void att) { + } + public void cancelled(Void att) { + } + }); + l2.await(5, TimeUnit.SECONDS); + + // check received + dst.clear(); + reader.receive(dst); + dst.flip(); + if (dst.remaining() != msg.length) + throw new RuntimeException("Unexpected number of bytes received"); + + // done + ch.close(); + reader.close(); + } + + static void cancelAndCheck(Future result, CountDownLatch latch) + throws InterruptedException + { + boolean cancelled = result.cancel(false); + if (!cancelled) + throw new RuntimeException("Not cancelled"); + if (!result.isDone()) + throw new RuntimeException("Should be done"); + try { + result.get(); + throw new RuntimeException("Result not expected"); + } catch (CancellationException e) { + // expected + } catch (ExecutionException e) { + throw new RuntimeException("Should not fail"); + } + + // make sure that completion handler is invoked + latch.await(); + } + + // basic cancel tests + static void doCancelTests() throws Exception { + InetAddress lh = InetAddress.getLocalHost(); + + // timed and non-timed receive + for (int i=0; i<2; i++) { + AsynchronousDatagramChannel ch = + AsynchronousDatagramChannel.open().bind(new InetSocketAddress(0)); + final CountDownLatch latch = new CountDownLatch(1); + long timeout = (i == 0) ? 0L : 60L; + Future remote = ch + .receive(ByteBuffer.allocate(100), timeout, TimeUnit.SECONDS, null, + new CompletionHandler() { + public void completed(SocketAddress source, Void att) { + } + public void failed (Throwable exc, Void att) { + } + public void cancelled(Void att) { + latch.countDown(); + } + }); + cancelAndCheck(remote, latch); + ch.close(); + } + + // timed and non-timed read + for (int i=0; i<2; i++) { + AsynchronousDatagramChannel ch = + AsynchronousDatagramChannel.open().bind(new InetSocketAddress(0)); + ch.connect(new InetSocketAddress(lh, + ((InetSocketAddress)(ch.getLocalAddress())).getPort())); + final CountDownLatch latch = new CountDownLatch(1); + long timeout = (i == 0) ? 0L : 60L; + Future result = ch + .read(ByteBuffer.allocate(100), timeout, TimeUnit.SECONDS, null, + new CompletionHandler() { + public void completed(Integer bytesRead, Void att) { + } + public void failed (Throwable exc, Void att) { + } + public void cancelled(Void att) { + latch.countDown(); + } + }); + cancelAndCheck(result, latch); + ch.close(); + } + } + + // basic multicast test + static void doMulticastTests() throws Exception { + final byte[] msg = "hello".getBytes(); + + AsynchronousDatagramChannel ch = AsynchronousDatagramChannel + .open(StandardProtocolFamily.INET, null) + .setOption(StandardSocketOption.SO_REUSEADDR, true) + .bind(new InetSocketAddress(0)); + + InetAddress lh = InetAddress.getLocalHost(); + int port = ((InetSocketAddress)(ch.getLocalAddress())).getPort(); + + // join group + InetAddress group = InetAddress.getByName("225.4.5.6"); + NetworkInterface interf = NetworkInterface.getByInetAddress(lh); + MembershipKey key = ch.join(group, interf); + + // check key + if (key.channel() != ch) + throw new RuntimeException("Not the expected channel"); + + // send message to group + DatagramChannel sender = DatagramChannel.open(); + sender.send(ByteBuffer.wrap(msg), new InetSocketAddress(group, port)); + sender.close(); + + // check message received + ByteBuffer dst = ByteBuffer.allocate(200); + SocketAddress source = ch.receive(dst).get(2, TimeUnit.SECONDS); + if (!((InetSocketAddress)source).getAddress().equals(lh)) + throw new RuntimeException("Unexpected source"); + + // done + ch.close(); + } +} diff --git a/test/java/nio/channels/AsynchronousFileChannel/Basic.java b/test/java/nio/channels/AsynchronousFileChannel/Basic.java new file mode 100644 index 000000000..5ffb42c0b --- /dev/null +++ b/test/java/nio/channels/AsynchronousFileChannel/Basic.java @@ -0,0 +1,585 @@ +/* + * 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 4607272 + * @summary Unit test for AsynchronousFileChannel + */ + +import java.nio.file.*; +import java.nio.channels.*; +import java.nio.ByteBuffer; +import java.io.File; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicReference; +import static java.nio.file.StandardOpenOption.*; + +public class Basic { + + private static final Random rand = new Random(); + + public static void main(String[] args) throws IOException { + // create temporary file + File blah = File.createTempFile("blah", null); + blah.deleteOnExit(); + + final AsynchronousFileChannel ch = AsynchronousFileChannel + .open(blah.toPath(), READ, WRITE); + + // run tests + testUsingCompletionHandlers(ch); + testUsingWaitOnResult(ch); + testLocking(ch); + testInterruptHandlerThread(ch); + + // close channel and invoke test that expects channel to be closed + ch.close(); + testClosedChannel(ch); + + // these tests open the file themselves + testCustomThreadPool(blah.toPath()); + testAsynchronousClose(blah.toPath()); + testCancel(blah.toPath()); + testTruncate(blah.toPath()); + } + + /* + * Generate buffer with random contents + * Writes buffer to file using a CompletionHandler to consume the result + * of each write operation + * Reads file to EOF to a new buffer using a CompletionHandler to consume + * the result of each read operation + * Compares buffer contents + */ + static void testUsingCompletionHandlers(AsynchronousFileChannel ch) + throws IOException + { + System.out.println("testUsingCompletionHandlers"); + + ch.truncate(0L); + + // generate buffer with random elements and write it to file + ByteBuffer src = genBuffer(); + writeFully(ch, src, 0L); + + // read to EOF or buffer is full + ByteBuffer dst = (rand.nextBoolean()) ? + ByteBuffer.allocateDirect(src.capacity()) : + ByteBuffer.allocate(src.capacity()); + readAll(ch, dst, 0L); + + // check buffers are the same + src.flip(); + dst.flip(); + if (!src.equals(dst)) { + throw new RuntimeException("Contents differ"); + } + } + + /* + * Generate buffer with random contents + * Writes buffer to file, invoking the Future's get method to wait for + * each write operation to complete + * Reads file to EOF to a new buffer, invoking the Future's get method to + * wait for each write operation to complete + * Compares buffer contents + */ + static void testUsingWaitOnResult(AsynchronousFileChannel ch) + throws IOException + { + System.out.println("testUsingWaitOnResult"); + + ch.truncate(0L); + + // generate buffer + ByteBuffer src = genBuffer(); + + // write buffer completely to file + long position = 0L; + while (src.hasRemaining()) { + Future result = ch.write(src, position); + try { + int n = result.get(); + // update position + position += n; + } catch (ExecutionException x) { + throw new RuntimeException(x.getCause()); + } catch (InterruptedException x) { + throw new RuntimeException(x); + } + } + + // read file into new buffer + ByteBuffer dst = (rand.nextBoolean()) ? + ByteBuffer.allocateDirect(src.capacity()) : + ByteBuffer.allocate(src.capacity()); + position = 0L; + int n; + do { + Future result = ch.read(dst, position); + try { + n = result.get(); + + // update position + if (n > 0) position += n; + } catch (ExecutionException x) { + throw new RuntimeException(x.getCause()); + } catch (InterruptedException x) { + throw new RuntimeException(x); + } + } while (n > 0); + + // check buffers are the same + src.flip(); + dst.flip(); + if (!src.equals(dst)) { + throw new RuntimeException("Contents differ"); + } + } + + // exercise lock methods + static void testLocking(AsynchronousFileChannel ch) + throws IOException + { + System.out.println("testLocking"); + + // test 1 - acquire lock and check that tryLock throws + // OverlappingFileLockException + FileLock fl; + try { + fl = ch.lock().get(); + } catch (ExecutionException x) { + throw new RuntimeException(x); + } catch (InterruptedException x) { + throw new RuntimeException("Should not be interrupted"); + } + if (!fl.acquiredBy().equals(ch)) + throw new RuntimeException("FileLock#acquiredBy returned incorrect channel"); + try { + ch.tryLock(); + throw new RuntimeException("OverlappingFileLockException expected"); + } catch (OverlappingFileLockException x) { + } + fl.release(); + + // test 2 - acquire try and check that lock throws OverlappingFileLockException + fl = ch.tryLock(); + if (fl == null) + throw new RuntimeException("Unable to acquire lock"); + try { + ch.lock(null, new CompletionHandler () { + public void completed(FileLock result, Void att) { + } + public void failed(Throwable exc, Void att) { + } + public void cancelled(Void att) { + } + }); + throw new RuntimeException("OverlappingFileLockException expected"); + } catch (OverlappingFileLockException x) { + } + fl.release(); + } + + // interrupt should not close channel + static void testInterruptHandlerThread(final AsynchronousFileChannel ch) { + System.out.println("testInterruptHandlerThread"); + + ByteBuffer buf = ByteBuffer.allocateDirect(100); + final CountDownLatch latch = new CountDownLatch(1); + + ch.read(buf, 0L, null, new CompletionHandler() { + public void completed(Integer result, Void att) { + try { + Thread.currentThread().interrupt(); + long size = ch.size(); + latch.countDown(); + } catch (IOException x) { + x.printStackTrace(); + } + } + public void failed(Throwable exc, Void att) { + } + public void cancelled(Void att) { + } + }); + + // wait for handler to complete + await(latch); + } + + // invoke method on closed channel + static void testClosedChannel(AsynchronousFileChannel ch) { + System.out.println("testClosedChannel"); + + if (ch.isOpen()) + throw new RuntimeException("Channel should be closed"); + + ByteBuffer buf = ByteBuffer.allocateDirect(100); + + // check read fails with ClosedChannelException + try { + ch.read(buf, 0L).get(); + throw new RuntimeException("ExecutionException expected"); + } catch (ExecutionException x) { + if (!(x.getCause() instanceof ClosedChannelException)) + throw new RuntimeException("Cause of ClosedChannelException expected"); + } catch (InterruptedException x) { + } + + // check write fails with ClosedChannelException + try { + ch.write(buf, 0L).get(); + throw new RuntimeException("ExecutionException expected"); + } catch (ExecutionException x) { + if (!(x.getCause() instanceof ClosedChannelException)) + throw new RuntimeException("Cause of ClosedChannelException expected"); + } catch (InterruptedException x) { + } + + // check lock fails with ClosedChannelException + try { + ch.lock().get(); + throw new RuntimeException("ExecutionException expected"); + } catch (ExecutionException x) { + if (!(x.getCause() instanceof ClosedChannelException)) + throw new RuntimeException("Cause of ClosedChannelException expected"); + } catch (InterruptedException x) { + } + } + + + // exercise custom thread pool + static void testCustomThreadPool(Path file) throws IOException { + System.out.println("testCustomThreadPool"); + + // records threads that are created + final List threads = new ArrayList(); + + ThreadFactory threadFactory = new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setDaemon(true); + synchronized (threads) { + threads.add(t); + } + return t; + } + }; + + // exercise tests with varied number of threads + for (int nThreads=1; nThreads<=5; nThreads++) { + synchronized (threads) { + threads.clear(); + } + ExecutorService executor = Executors.newFixedThreadPool(nThreads, threadFactory); + Set opts = EnumSet.of(WRITE); + AsynchronousFileChannel ch = AsynchronousFileChannel.open(file, opts, executor); + try { + for (int i=0; i<10; i++) { + // do I/O operation to see which thread invokes the completion handler + final AtomicReference invoker = new AtomicReference(); + final CountDownLatch latch = new CountDownLatch(1); + + ch.write(genBuffer(), 0L, null, new CompletionHandler() { + public void completed(Integer result, Void att) { + invoker.set(Thread.currentThread()); + latch.countDown(); + } + public void failed(Throwable exc, Void att) { + } + public void cancelled(Void att) { + } + }); + await(latch); + + // check invoker + boolean found = false; + synchronized (threads) { + for (Thread t: threads) { + if (t == invoker.get()) { + found = true; + break; + } + } + } + if (!found) + throw new RuntimeException("Invoker thread not found"); + } + } finally { + ch.close(); + } + } + } + + // exercise asynchronous close + static void testAsynchronousClose(Path file) throws IOException { + System.out.println("testAsynchronousClose"); + + // create file + AsynchronousFileChannel ch = AsynchronousFileChannel + .open(file, WRITE, TRUNCATE_EXISTING); + long size = 0L; + do { + ByteBuffer buf = genBuffer(); + int n = buf.remaining(); + writeFully(ch, buf, size); + size += n; + } while (size < (50L * 1024L * 1024L)); + + ch.close(); + + ch = AsynchronousFileChannel.open(file, WRITE, SYNC); + + // randomize number of writers, buffer size, and positions + + int nwriters = 1 + rand.nextInt(8); + ByteBuffer[] buf = new ByteBuffer[nwriters]; + long[] position = new long[nwriters]; + for (int i=0; i res = ch.write(genBuffer(), 0L, null, + new CompletionHandler() { + public void completed(Integer result, Void att) { + } + public void failed(Throwable exc, Void att) { + } + public void cancelled(Void att) { + latch.countDown(); + } + }); + + // cancel operation + boolean cancelled = res.cancel(mayInterruptIfRunning); + + // check post-conditions + if (!res.isDone()) + throw new RuntimeException("isDone should return true"); + if (res.isCancelled() != cancelled) + throw new RuntimeException("isCancelled not consistent"); + try { + res.get(); + if (!cancelled) + throw new RuntimeException("CancellationException expected"); + } catch (CancellationException x) { + // expected + } catch (ExecutionException x) { + throw new RuntimeException(x); + } catch (InterruptedException x) { + throw new RuntimeException(x); + } + try { + res.get(1, TimeUnit.SECONDS); + throw new RuntimeException("CancellationException expected"); + } catch (CancellationException x) { + // expected + } catch (ExecutionException x) { + throw new RuntimeException(x); + } catch (TimeoutException x) { + throw new RuntimeException(x); + } catch (InterruptedException x) { + throw new RuntimeException(x); + } + + // check that cancelled method is invoked + if (cancelled) + await(latch); + + ch.close(); + } + } + + // exercise truncate method + static void testTruncate(Path file) throws IOException { + System.out.println("testTruncate"); + + // basic tests + AsynchronousFileChannel ch = AsynchronousFileChannel + .open(file, CREATE, WRITE, TRUNCATE_EXISTING); + try { + writeFully(ch, genBuffer(), 0L); + long size = ch.size(); + + // attempt to truncate to a size greater than the current size + if (ch.truncate(size + 1L).size() != size) + throw new RuntimeException("Unexpected size after truncation"); + + // truncate file + if (ch.truncate(size - 1L).size() != (size - 1L)) + throw new RuntimeException("Unexpected size after truncation"); + + // invalid size + try { + ch.truncate(-1L); + throw new RuntimeException("IllegalArgumentException expected"); + } catch (IllegalArgumentException e) { } + + } finally { + ch.close(); + } + + // channel is closed + try { + ch.truncate(0L); + throw new RuntimeException("ClosedChannelException expected"); + } catch (ClosedChannelException e) { } + + // channel is read-only + ch = AsynchronousFileChannel.open(file, READ); + try { + try { + ch.truncate(0L); + throw new RuntimeException("NonWritableChannelException expected"); + } catch (NonWritableChannelException e) { } + } finally { + ch.close(); + } + } + + // returns ByteBuffer with random bytes + static ByteBuffer genBuffer() { + int size = 1024 + rand.nextInt(16000); + byte[] buf = new byte[size]; + boolean useDirect = rand.nextBoolean(); + if (useDirect) { + ByteBuffer bb = ByteBuffer.allocateDirect(buf.length); + bb.put(buf); + bb.flip(); + return bb; + } else { + return ByteBuffer.wrap(buf); + } + } + + // writes all remaining bytes in the buffer to the given channel at the + // given position + static void writeFully(final AsynchronousFileChannel ch, + final ByteBuffer src, + long position) + { + final CountDownLatch latch = new CountDownLatch(1); + + // use position as attachment + ch.write(src, position, position, new CompletionHandler() { + public void completed(Integer result, Long position) { + int n = result; + if (src.hasRemaining()) { + long p = position + n; + ch.write(src, p, p, this); + } else { + latch.countDown(); + } + } + public void failed(Throwable exc, Long position) { + } + public void cancelled(Long position) { + } + }); + + // wait for writes to complete + await(latch); + } + + static void readAll(final AsynchronousFileChannel ch, + final ByteBuffer dst, + long position) + { + final CountDownLatch latch = new CountDownLatch(1); + + // use position as attachment + ch.read(dst, position, position, new CompletionHandler() { + public void completed(Integer result, Long position) { + int n = result; + if (n > 0) { + long p = position + n; + ch.read(dst, p, p, this); + } else { + latch.countDown(); + } + } + public void failed(Throwable exc, Long position) { + } + public void cancelled(Long position) { + } + }); + + // wait for reads to complete + await(latch); + } + + static void await(CountDownLatch latch) { + // wait until done + boolean done = false; + while (!done) { + try { + latch.await(); + done = true; + } catch (InterruptedException x) { } + } + } +} diff --git a/test/java/nio/channels/AsynchronousFileChannel/CustomThreadPool.java b/test/java/nio/channels/AsynchronousFileChannel/CustomThreadPool.java new file mode 100644 index 000000000..9f9025dd2 --- /dev/null +++ b/test/java/nio/channels/AsynchronousFileChannel/CustomThreadPool.java @@ -0,0 +1,67 @@ +/* + * 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 4607272 + * @summary Unit test for java.nio.channels.AsynchronousFileChannel + * @build CustomThreadPool MyThreadFactory + * @run main/othervm -Djava.nio.channels.DefaultThreadPool.threadFactory=MyThreadFactory CustomThreadPool + */ + +import java.io.File; +import static java.nio.file.StandardOpenOption.*; +import java.nio.ByteBuffer; +import java.nio.channels.*; +import java.util.concurrent.atomic.AtomicReference; + +public class CustomThreadPool { + + public static void main(String[] args) throws Exception { + File blah = File.createTempFile("blah", null); + blah.deleteOnExit(); + AsynchronousFileChannel ch = + AsynchronousFileChannel.open(blah.toPath(), READ, WRITE); + ByteBuffer src = ByteBuffer.wrap("Scooby Snacks".getBytes()); + + final AtomicReference invoker = new AtomicReference(); + ch.write(src, 0, invoker, + new CompletionHandler>() { + public void completed(Integer result, AtomicReference invoker) { + invoker.set(Thread.currentThread()); + } + public void failed(Throwable exc, AtomicReference invoker) { + } + public void cancelled(AtomicReference invoker) { + } + }); + Thread t; + while ((t = invoker.get()) == null) { + Thread.sleep(100); + } + ch.close(); + + // check handler was run by known thread + if (!MyThreadFactory.created(t)) + throw new RuntimeException("Handler invoked by unknown thread"); + } +} diff --git a/test/java/nio/channels/AsynchronousFileChannel/Lock.java b/test/java/nio/channels/AsynchronousFileChannel/Lock.java new file mode 100644 index 000000000..38c0f7d0c --- /dev/null +++ b/test/java/nio/channels/AsynchronousFileChannel/Lock.java @@ -0,0 +1,340 @@ +/* + * 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 4607272 + * @summary Unit test for AsynchronousFileChannel#lock method + */ + +import java.net.*; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.file.*; +import static java.nio.file.StandardOpenOption.*; +import java.nio.channels.*; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Random; +import java.util.concurrent.*; + +public class Lock { + + static final Random rand = new Random(); + + public static void main(String[] args) throws Exception { + if (args.length > 0 && args[0].equals("-lockslave")) { + int port = Integer.parseInt(args[1]); + runLockSlave(port); + System.exit(0); + } + + LockSlaveMirror slave = startLockSlave(); + try { + + // create temporary file + File blah = File.createTempFile("blah", null); + blah.deleteOnExit(); + + testLockProtocol(blah, slave); + testAsyncClose(blah, slave); + + } finally { + slave.shutdown(); + } + } + + // test locking protocol + static void testLockProtocol(File file, LockSlaveMirror slave) + throws Exception + { + FileLock fl; + + // slave VM opens file and acquires exclusive lock + slave.open(file.getPath()).lock(); + + AsynchronousFileChannel ch = AsynchronousFileChannel + .open(file.toPath(), READ, WRITE); + + // this VM tries to acquire lock + // (lock should not be acquire until released by slave VM) + Future result = ch.lock(); + try { + result.get(2, TimeUnit.SECONDS); + throw new RuntimeException("Timeout expected"); + } catch (TimeoutException x) { + } + + // slave VM releases lock + slave.unlock(); + + // this VM should now acquire lock + fl = result.get(); + fl.release(); + + // slave VM acquires lock on range + slave.lock(0, 10, false); + + // this VM acquires lock on non-overlapping range + fl = ch.lock(10, 10, false, null, null).get(); + fl.release(); + + // done + ch.close(); + slave.close(); + } + + // test close of channel with outstanding lock operation + static void testAsyncClose(File file, LockSlaveMirror slave) throws Exception { + // slave VM opens file and acquires exclusive lock + slave.open(file.getPath()).lock(); + + for (int i=0; i<100; i++) { + AsynchronousFileChannel ch = AsynchronousFileChannel + .open(file.toPath(), READ, WRITE); + + // try to lock file (should not complete because file is locked by slave) + Future result = ch.lock(); + try { + result.get(rand.nextInt(100), TimeUnit.MILLISECONDS); + throw new RuntimeException("Timeout expected"); + } catch (TimeoutException x) { + } + + // close channel with lock operation outstanding + ch.close(); + + // operation should complete with AsynchronousCloseException + try { + result.get(); + throw new RuntimeException("ExecutionException expected"); + } catch (ExecutionException x) { + if (!(x.getCause() instanceof AsynchronousCloseException)) { + x.getCause().printStackTrace(); + throw new RuntimeException("AsynchronousCloseException expected"); + } + } + } + + slave.close(); + } + + // starts a "lock slave" in another process, returning a mirror object to + // control the slave + static LockSlaveMirror startLockSlave() throws Exception { + ServerSocketChannel ssc = ServerSocketChannel.open() + .bind(new InetSocketAddress(0)); + int port = ((InetSocketAddress)(ssc.getLocalAddress())).getPort(); + + String sep = FileSystems.getDefault().getSeparator(); + + String command = System.getProperty("java.home") + + sep + "bin" + sep + "java Lock -lockslave " + port; + Process p = Runtime.getRuntime().exec(command); + IOHandler.handle(p.getInputStream()); + IOHandler.handle(p.getErrorStream()); + + // wait for slave to connect + SocketChannel sc = ssc.accept(); + return new LockSlaveMirror(sc); + } + + // commands that the slave understands + static final String OPEN_CMD = "open"; + static final String CLOSE_CMD = "close"; + static final String LOCK_CMD = "lock"; + static final String UNLOCK_CMD = "unlock"; + static final char TERMINATOR = ';'; + + // provides a proxy to a "lock slave" + static class LockSlaveMirror { + private final SocketChannel sc; + + LockSlaveMirror(SocketChannel sc) { + this.sc = sc; + } + + private void sendCommand(String cmd, String... params) + throws IOException + { + for (String s: params) { + cmd += " " + s; + } + cmd += TERMINATOR; + + ByteBuffer buf = Charset.defaultCharset().encode(cmd); + while (buf.hasRemaining()) { + sc.write(buf); + } + + // wait for ack + buf = ByteBuffer.allocate(1); + int n = sc.read(buf); + if (n != 1) + throw new RuntimeException("Reply expected"); + if (buf.get(0) != TERMINATOR) + throw new RuntimeException("Terminated expected"); + } + + LockSlaveMirror open(String file) throws IOException { + sendCommand(OPEN_CMD, file); + return this; + } + + void close() throws IOException { + sendCommand(CLOSE_CMD); + } + + LockSlaveMirror lock() throws IOException { + sendCommand(LOCK_CMD); + return this; + } + + + LockSlaveMirror lock(long position, long size, boolean shared) + throws IOException + { + sendCommand(LOCK_CMD, position + "," + size + "," + shared); + return this; + } + + LockSlaveMirror unlock() throws IOException { + sendCommand(UNLOCK_CMD); + return this; + } + + void shutdown() throws IOException { + sc.close(); + } + } + + // Helper class to direct process output to the parent System.out + static class IOHandler implements Runnable { + private final InputStream in; + + IOHandler(InputStream in) { + this.in = in; + } + + static void handle(InputStream in) { + IOHandler handler = new IOHandler(in); + Thread thr = new Thread(handler); + thr.setDaemon(true); + thr.start(); + } + + public void run() { + try { + byte b[] = new byte[100]; + for (;;) { + int n = in.read(b); + if (n < 0) return; + for (int i=0; i threads = new HashSet(); + + static boolean created(Thread t) { + synchronized (threads) { + return threads.contains(t); + } + } + + public MyThreadFactory() { + } + + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setDaemon(true); + synchronized (threads) { + threads.add(t); + } + return t; + } +} diff --git a/test/java/nio/channels/AsynchronousServerSocketChannel/Basic.java b/test/java/nio/channels/AsynchronousServerSocketChannel/Basic.java new file mode 100644 index 000000000..e0965fb6e --- /dev/null +++ b/test/java/nio/channels/AsynchronousServerSocketChannel/Basic.java @@ -0,0 +1,136 @@ +/* + * 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 4607272 + * @summary Unit test for AsynchronousServerSocketChannel + * @run main/timeout=180 Basic + */ + +import java.nio.channels.*; +import java.net.*; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicReference; + +public class Basic { + + public static void main(String[] args) throws Exception { + testBind(); + testAccept(); + } + + static void testBind() throws Exception { + System.out.println("-- bind --"); + + AsynchronousServerSocketChannel ch = AsynchronousServerSocketChannel.open(); + if (ch.getLocalAddress() != null) + throw new RuntimeException("Local address should be 'null'"); + ch.bind(new InetSocketAddress(0), 20); + + // check local address after binding + InetSocketAddress local = (InetSocketAddress)ch.getLocalAddress(); + if (local.getPort() == 0) + throw new RuntimeException("Unexpected port"); + if (!local.getAddress().isAnyLocalAddress()) + throw new RuntimeException("Not bound to a wildcard address"); + + // try to re-bind + try { + ch.bind(new InetSocketAddress(0)); + throw new RuntimeException("AlreadyBoundException expected"); + } catch (AlreadyBoundException x) { + } + ch.close(); + + // check ClosedChannelException + ch = AsynchronousServerSocketChannel.open(); + ch.close(); + try { + ch.bind(new InetSocketAddress(0)); + throw new RuntimeException("ClosedChannelException expected"); + } catch (ClosedChannelException x) { + } + } + + static void testAccept() throws Exception { + System.out.println("-- accept --"); + + final AsynchronousServerSocketChannel listener = + AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(0)); + + InetAddress lh = InetAddress.getLocalHost(); + int port = ((InetSocketAddress)(listener.getLocalAddress())).getPort(); + final InetSocketAddress isa = new InetSocketAddress(lh, port); + + // establish a few loopback connections + for (int i=0; i<100; i++) { + SocketChannel sc = SocketChannel.open(isa); + AsynchronousSocketChannel ch = listener.accept().get(); + sc.close(); + ch.close(); + } + + final AtomicReference exception = new AtomicReference(); + + // start accepting + listener.accept(null, new CompletionHandler() { + public void completed(AsynchronousSocketChannel ch, Void att) { + try { + ch.close(); + } catch (IOException ignore) { } + } + public void failed(Throwable exc, Void att) { + exception.set(exc); + } + public void cancelled(Void att) { + } + }); + + // check AcceptPendingException + try { + listener.accept(); + throw new RuntimeException("AcceptPendingException expected"); + } catch (AcceptPendingException x) { + } + + // asynchronous close + listener.close(); + while (exception.get() == null) + Thread.sleep(100); + if (!(exception.get() instanceof AsynchronousCloseException)) + throw new RuntimeException("AsynchronousCloseException expected"); + + // once closed when a further attemt should throw ClosedChannelException + try { + listener.accept().get(); + throw new RuntimeException("ExecutionException expected"); + } catch (ExecutionException x) { + if (!(x.getCause() instanceof ClosedChannelException)) + throw new RuntimeException("Cause of ClosedChannelException expected"); + } catch (InterruptedException x) { + } + + } +} diff --git a/test/java/nio/channels/AsynchronousServerSocketChannel/WithSecurityManager.java b/test/java/nio/channels/AsynchronousServerSocketChannel/WithSecurityManager.java new file mode 100644 index 000000000..86c76cf1d --- /dev/null +++ b/test/java/nio/channels/AsynchronousServerSocketChannel/WithSecurityManager.java @@ -0,0 +1,76 @@ +/* + * 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 4607272 + * @summary Unit test for AsynchronousServerServerSocketChannel + * @build WithSecurityManager + * @run main/othervm WithSecurityManager allow + * @run main/othervm WithSecurityManager deny + */ + +import java.nio.file.Paths; +import java.nio.channels.*; +import java.net.*; +import java.util.concurrent.*; + +public class WithSecurityManager { + public static void main(String[] args) throws Exception { + boolean allow = false; + String policy = (args[0].equals("allow")) ? "java.policy.allow" : + "java.policy.deny"; + + String testSrc = System.getProperty("test.src"); + if (testSrc == null) + testSrc = "."; + + System.setProperty("java.security.policy", + Paths.get(testSrc).resolve(policy).toString()); + System.setSecurityManager(new SecurityManager()); + + AsynchronousServerSocketChannel listener = + AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(0)); + + InetAddress lh = InetAddress.getLocalHost(); + int port = ((InetSocketAddress)(listener.getLocalAddress())).getPort(); + + // establish and accept connection + SocketChannel sc = SocketChannel.open(new InetSocketAddress(lh, port)); + Future result = listener.accept(); + + if (allow) { + // no security exception + result.get().close(); + } else { + try { + result.get(); + } catch (ExecutionException x) { + if (!(x.getCause() instanceof SecurityException)) + throw new RuntimeException("SecurityException expected"); + } + } + + sc.close(); + listener.close(); + } +} diff --git a/test/java/nio/channels/AsynchronousServerSocketChannel/java.policy.allow b/test/java/nio/channels/AsynchronousServerSocketChannel/java.policy.allow new file mode 100644 index 000000000..73da4b089 --- /dev/null +++ b/test/java/nio/channels/AsynchronousServerSocketChannel/java.policy.allow @@ -0,0 +1,3 @@ +grant { + permission java.net.SocketPermission "*:1024-", "accept,connect,resolve"; +}; diff --git a/test/java/nio/channels/AsynchronousServerSocketChannel/java.policy.deny b/test/java/nio/channels/AsynchronousServerSocketChannel/java.policy.deny new file mode 100644 index 000000000..00b9cb068 --- /dev/null +++ b/test/java/nio/channels/AsynchronousServerSocketChannel/java.policy.deny @@ -0,0 +1,3 @@ +grant { + permission java.net.SocketPermission "*:1024-", "connect,resolve"; +}; diff --git a/test/java/nio/channels/AsynchronousSocketChannel/Basic.java b/test/java/nio/channels/AsynchronousSocketChannel/Basic.java new file mode 100644 index 000000000..8b140f17b --- /dev/null +++ b/test/java/nio/channels/AsynchronousSocketChannel/Basic.java @@ -0,0 +1,805 @@ +/* + * 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 4607272 + * @summary Unit test for AsynchronousSocketChannel + * @run main/timeout=600 Basic + */ + +import java.nio.ByteBuffer; +import java.nio.channels.*; +import static java.net.StandardSocketOption.*; +import java.net.*; +import java.util.Random; +import java.util.concurrent.*; +import java.util.concurrent.atomic.*; +import java.io.IOException; + +public class Basic { + static final Random rand = new Random(); + + public static void main(String[] args) throws Exception { + testBind(); + testSocketOptions(); + testConnect(); + testCloseWhenPending(); + testCancel(); + testRead1(); + testRead2(); + testRead3(); + testWrite1(); + testWrite2(); + testTimeout(); + testShutdown(); + } + + static class Server { + private final ServerSocketChannel ssc; + private final InetSocketAddress address; + + Server() throws IOException { + ssc = ServerSocketChannel.open().bind(new InetSocketAddress(0)); + + InetAddress lh = InetAddress.getLocalHost(); + int port = ((InetSocketAddress)(ssc.getLocalAddress())).getPort(); + address = new InetSocketAddress(lh, port); + } + + InetSocketAddress address() { + return address; + } + + SocketChannel accept() throws IOException { + return ssc.accept(); + } + + void close() { + try { + ssc.close(); + } catch (IOException ignore) { } + } + + } + + static void testBind() throws Exception { + System.out.println("-- bind --"); + + AsynchronousSocketChannel ch = AsynchronousSocketChannel.open(); + if (ch.getLocalAddress() != null) + throw new RuntimeException("Local address should be 'null'"); + ch.bind(new InetSocketAddress(0)); + + // check local address after binding + InetSocketAddress local = (InetSocketAddress)ch.getLocalAddress(); + if (local.getPort() == 0) + throw new RuntimeException("Unexpected port"); + if (!local.getAddress().isAnyLocalAddress()) + throw new RuntimeException("Not bound to a wildcard address"); + + // try to re-bind + try { + ch.bind(new InetSocketAddress(0)); + throw new RuntimeException("AlreadyBoundException expected"); + } catch (AlreadyBoundException x) { + } + ch.close(); + + // check ClosedChannelException + ch = AsynchronousSocketChannel.open(); + ch.close(); + try { + ch.bind(new InetSocketAddress(0)); + throw new RuntimeException("ClosedChannelException expected"); + } catch (ClosedChannelException x) { + } + } + + static void testSocketOptions() throws Exception { + System.out.println("-- socket options --"); + + AsynchronousSocketChannel ch = AsynchronousSocketChannel.open() + .setOption(SO_RCVBUF, 128*1024) + .setOption(SO_SNDBUF, 128*1024) + .setOption(SO_REUSEADDR, true) + .bind(new InetSocketAddress(0)); + + // default values + if ((Boolean)ch.getOption(SO_KEEPALIVE)) + throw new RuntimeException("Default of SO_KEEPALIVE should be 'false'"); + if ((Boolean)ch.getOption(TCP_NODELAY)) + throw new RuntimeException("Default of TCP_NODELAY should be 'false'"); + + // set and check + if (!(Boolean)ch.setOption(SO_KEEPALIVE, true).getOption(SO_KEEPALIVE)) + throw new RuntimeException("SO_KEEPALIVE did not change"); + if (!(Boolean)ch.setOption(TCP_NODELAY, true).getOption(TCP_NODELAY)) + throw new RuntimeException("SO_KEEPALIVE did not change"); + + // read others (can't check as actual value is implementation dependent) + ch.getOption(SO_RCVBUF); + ch.getOption(SO_SNDBUF); + + ch.close(); + } + + static void testConnect() throws Exception { + System.out.println("-- connect --"); + + Server server = new Server(); + AsynchronousSocketChannel ch = AsynchronousSocketChannel.open(); + ch.connect(server.address()).get(); + + // check local address + if (ch.getLocalAddress() == null) + throw new RuntimeException("Not bound to local address"); + + // check remote address + InetSocketAddress remote = (InetSocketAddress)ch.getRemoteAddress(); + if (remote.getPort() != server.address().getPort()) + throw new RuntimeException("Connected to unexpected port"); + if (!remote.getAddress().equals(server.address().getAddress())) + throw new RuntimeException("Connected to unexpected address"); + + // try to connect again + try { + ch.connect(server.address()).get(); + throw new RuntimeException("AlreadyConnectedException expected"); + } catch (AlreadyConnectedException x) { + } + ch.close(); + + // check that connect fails with ClosedChannelException) + ch = AsynchronousSocketChannel.open(); + ch.close(); + try { + ch.connect(server.address()).get(); + throw new RuntimeException("ExecutionException expected"); + } catch (ExecutionException x) { + if (!(x.getCause() instanceof ClosedChannelException)) + throw new RuntimeException("Cause of ClosedChannelException expected"); + } + final AtomicReference connectException = + new AtomicReference(); + ch.connect(server.address(), null, new CompletionHandler() { + public void completed(Void result, Void att) { + } + public void failed(Throwable exc, Void att) { + connectException.set(exc); + } + public void cancelled(Void att) { + } + }); + while (connectException.get() == null) { + Thread.sleep(100); + } + if (!(connectException.get() instanceof ClosedChannelException)) + throw new RuntimeException("ClosedChannelException expected"); + + System.out.println("-- connect to non-existent host --"); + + // test failure + InetAddress badHost = InetAddress.getByName("1.2.3.4"); + if (!badHost.isReachable(10*1000)) { + + ch = AsynchronousSocketChannel.open(); + try { + ch.connect(new InetSocketAddress(badHost, 9876)).get(); + throw new RuntimeException("Connection should not be established"); + } catch (ExecutionException x) { + } + if (ch.isOpen()) + throw new RuntimeException("Channel should be closed"); + } + + server.close(); + } + + static void testCloseWhenPending() throws Exception { + System.out.println("-- asynchronous close when connecting --"); + + AsynchronousSocketChannel ch; + + // asynchronous close while connecting + InetAddress rh = InetAddress.getByName("1.2.3.4"); + if (!rh.isReachable(3000)) { + InetSocketAddress isa = new InetSocketAddress(rh, 1234); + + ch = AsynchronousSocketChannel.open(); + Future result = ch.connect(isa); + + // give time to initiate the connect (SYN) + Thread.sleep(50); + + // close + ch.close(); + + // check that AsynchronousCloseException is thrown + try { + result.get(); + throw new RuntimeException("Should not connect"); + } catch (ExecutionException x) { + if (!(x.getCause() instanceof AsynchronousCloseException)) + throw new RuntimeException(x); + } + } + + System.out.println("-- asynchronous close when reading --"); + + Server server = new Server(); + ch = AsynchronousSocketChannel.open(); + ch.connect(server.address()).get(); + + ByteBuffer dst = ByteBuffer.allocateDirect(100); + Future result = ch.read(dst); + + // attempt a second read - should fail with ReadPendingException + ByteBuffer buf = ByteBuffer.allocateDirect(100); + try { + ch.read(buf); + throw new RuntimeException("ReadPendingException expected"); + } catch (ReadPendingException x) { + } + + // close channel (should cause initial read to complete) + ch.close(); + + // check that AsynchronousCloseException is thrown + try { + result.get(); + throw new RuntimeException("Should not read"); + } catch (ExecutionException x) { + if (!(x.getCause() instanceof AsynchronousCloseException)) + throw new RuntimeException(x); + } + + System.out.println("-- asynchronous close when writing --"); + + ch = AsynchronousSocketChannel.open(); + ch.connect(server.address()).get(); + + final AtomicReference writeException = + new AtomicReference(); + + // write bytes to fill socket buffer + ch.write(genBuffer(), ch, new CompletionHandler() { + public void completed(Integer result, AsynchronousSocketChannel ch) { + ch.write(genBuffer(), ch, this); + } + public void failed(Throwable x, AsynchronousSocketChannel ch) { + writeException.set(x); + } + public void cancelled(AsynchronousSocketChannel ch) { + } + }); + + // give time for socket buffer to fill up. + Thread.sleep(5*1000); + + // attempt a concurrent write - should fail with WritePendingException + try { + ch.write(genBuffer()); + throw new RuntimeException("WritePendingException expected"); + } catch (WritePendingException x) { + } + + // close channel - should cause initial write to complete + ch.close(); + + // wait for exception + while (writeException.get() == null) { + Thread.sleep(100); + } + if (!(writeException.get() instanceof AsynchronousCloseException)) + throw new RuntimeException("AsynchronousCloseException expected"); + + server.close(); + } + + static void testCancel() throws Exception { + System.out.println("-- cancel --"); + + Server server = new Server(); + + for (int i=0; i<2; i++) { + boolean mayInterruptIfRunning = (i == 0) ? false : true; + + // establish loopback connection + AsynchronousSocketChannel ch = AsynchronousSocketChannel.open(); + ch.connect(server.address()).get(); + SocketChannel peer = server.accept(); + + // start read operation + final CountDownLatch latch = new CountDownLatch(1); + ByteBuffer buf = ByteBuffer.allocate(1); + Future res = ch.read(buf, null, + new CompletionHandler() { + public void completed(Integer result, Void att) { + } + public void failed(Throwable exc, Void att) { + } + public void cancelled(Void att) { + latch.countDown(); + } + }); + + // cancel operation + boolean cancelled = res.cancel(mayInterruptIfRunning); + + // check post-conditions + if (!res.isDone()) + throw new RuntimeException("isDone should return true"); + if (res.isCancelled() != cancelled) + throw new RuntimeException("isCancelled not consistent"); + try { + res.get(); + throw new RuntimeException("CancellationException expected"); + } catch (CancellationException x) { + } + try { + res.get(1, TimeUnit.SECONDS); + throw new RuntimeException("CancellationException expected"); + } catch (CancellationException x) { + } + + // check that completion handler executed. + latch.await(); + + ch.close(); + peer.close(); + } + + server.close(); + } + + static void testRead1() throws Exception { + System.out.println("-- read (1) --"); + + Server server = new Server(); + final AsynchronousSocketChannel ch = AsynchronousSocketChannel.open(); + ch.connect(server.address()).get(); + + // read with 0 bytes remaining should complete immediately + ByteBuffer buf = ByteBuffer.allocate(1); + buf.put((byte)0); + int n = ch.read(buf).get(); + if (n != 0) + throw new RuntimeException("0 expected"); + + // write bytes and close connection + SocketChannel sc = server.accept(); + ByteBuffer src = genBuffer(); + sc.setOption(StandardSocketOption.SO_SNDBUF, src.remaining()); + while (src.hasRemaining()) + sc.write(src); + sc.close(); + + // reads should complete immediately + final ByteBuffer dst = ByteBuffer.allocateDirect(src.capacity() + 100); + final CountDownLatch latch = new CountDownLatch(1); + ch.read(dst, null, new CompletionHandler() { + public void completed(Integer result, Void att) { + int n = result; + if (n > 0) { + ch.read(dst, null, this); + } else { + latch.countDown(); + } + } + public void failed(Throwable exc, Void att) { + } + public void cancelled(Void att) { + } + }); + + latch.await(); + + // check buffers + src.flip(); + dst.flip(); + if (!src.equals(dst)) { + throw new RuntimeException("Contents differ"); + } + + // close channel + ch.close(); + + // check read fails with ClosedChannelException + try { + ch.read(dst).get(); + throw new RuntimeException("ExecutionException expected"); + } catch (ExecutionException x) { + if (!(x.getCause() instanceof ClosedChannelException)) + throw new RuntimeException("Cause of ClosedChannelException expected"); + } + + server.close(); + } + + static void testRead2() throws Exception { + System.out.println("-- read (2) --"); + + Server server = new Server(); + + final AsynchronousSocketChannel ch = AsynchronousSocketChannel.open(); + ch.connect(server.address()).get(); + SocketChannel sc = server.accept(); + + ByteBuffer src = genBuffer(); + + // read until the buffer is full + final ByteBuffer dst = ByteBuffer.allocateDirect(src.capacity()); + final CountDownLatch latch = new CountDownLatch(1); + ch.read(dst, null, new CompletionHandler() { + public void completed(Integer result, Void att) { + if (dst.hasRemaining()) { + ch.read(dst, null, this); + } else { + latch.countDown(); + } + } + public void failed(Throwable exc, Void att) { + } + public void cancelled(Void att) { + } + }); + + // trickle the writing + do { + int rem = src.remaining(); + int size = (rem <= 100) ? rem : 50 + rand.nextInt(rem - 100); + ByteBuffer buf = ByteBuffer.allocate(size); + for (int i=0; i() { + public void completed(Long result, Void att) { + long n = result; + if (n <= 0) + throw new RuntimeException("No bytes read"); + latch.countDown(); + } + public void failed(Throwable exc, Void att) { + } + public void cancelled(Void att) { + } + }); + + // write some bytes + sc.write(genBuffer()); + + // read should now complete + latch.await(); + + // write more bytes + sc.write(genBuffer()); + + // read should complete immediately + for (int i=0; i() { + public void completed(Integer result, Void att) { + if (src.hasRemaining()) { + ch.write(src, null, this); + } else { + try { + ch.close(); + } catch (IOException ignore) { } + } + } + public void failed(Throwable exc, Void att) { + } + public void cancelled(Void att) { + } + }); + + // read to EOF or buffer full + ByteBuffer dst = ByteBuffer.allocateDirect(src.capacity() + 100); + do { + n = sc.read(dst); + } while (n > 0); + sc.close(); + + // check buffers + src.flip(); + dst.flip(); + if (!src.equals(dst)) { + throw new RuntimeException("Contents differ"); + } + + // check write fails with ClosedChannelException + try { + ch.read(dst).get(); + throw new RuntimeException("ExecutionException expected"); + } catch (ExecutionException x) { + if (!(x.getCause() instanceof ClosedChannelException)) + throw new RuntimeException("Cause of ClosedChannelException expected"); + } + + server.close(); + } + + // exercise gathering write + static void testWrite2() throws Exception { + System.out.println("-- write (2) --"); + + Server server = new Server(); + final AsynchronousSocketChannel ch = AsynchronousSocketChannel.open(); + ch.connect(server.address()).get(); + SocketChannel sc = server.accept(); + + // write buffers (should complete immediately) + ByteBuffer[] srcs = genBuffers(1); + long n = ch + .write(srcs, 0, srcs.length, 0L, TimeUnit.SECONDS, null, null).get(); + if (n <= 0) + throw new RuntimeException("No bytes written"); + + // set to true to signal that no more buffers should be written + final AtomicBoolean continueWriting = new AtomicBoolean(true); + + // number of bytes written + final AtomicLong bytesWritten = new AtomicLong(n); + + // write until socket buffer is full so as to create the conditions + // for when a write does not complete immediately + srcs = genBuffers(1); + ch.write(srcs, 0, srcs.length, 0L, TimeUnit.SECONDS, null, + new CompletionHandler() { + public void completed(Long result, Void att) { + long n = result; + if (n <= 0) + throw new RuntimeException("No bytes written"); + bytesWritten.addAndGet(n); + if (continueWriting.get()) { + ByteBuffer[] srcs = genBuffers(8); + ch.write(srcs, 0, srcs.length, 0L, TimeUnit.SECONDS, + null, this); + } + } + public void failed(Throwable exc, Void att) { + } + public void cancelled(Void att) { + } + }); + + // give time for socket buffer to fill up. + Thread.sleep(5*1000); + + // signal handler to stop further writing + continueWriting.set(false); + + // read until done + ByteBuffer buf = ByteBuffer.allocateDirect(4096); + long total = 0L; + do { + n = sc.read(buf); + if (n <= 0) + throw new RuntimeException("No bytes read"); + buf.rewind(); + total += n; + } while (total < bytesWritten.get()); + + ch.close(); + sc.close(); + server.close(); + } + + static void testShutdown() throws Exception { + System.out.println("-- shutdown--"); + + Server server = new Server(); + AsynchronousSocketChannel ch = AsynchronousSocketChannel.open(); + ch.connect(server.address()).get(); + SocketChannel sc = server.accept(); + + ByteBuffer buf = ByteBuffer.allocateDirect(1000); + int n; + + // check read + ch.shutdownInput(); + n = ch.read(buf).get(); + if (n != -1) + throw new RuntimeException("-1 expected"); + // check full with full buffer + buf.put(new byte[100]); + n = ch.read(buf).get(); + if (n != -1) + throw new RuntimeException("-1 expected"); + + // check write + ch.shutdownOutput(); + try { + ch.write(buf).get(); + throw new RuntimeException("ClosedChannelException expected"); + } catch (ExecutionException x) { + if (!(x.getCause() instanceof ClosedChannelException)) + throw new RuntimeException("ClosedChannelException expected"); + } + + sc.close(); + ch.close(); + server.close(); + } + + static void testTimeout() throws Exception { + Server server = new Server(); + AsynchronousSocketChannel ch = AsynchronousSocketChannel.open(); + ch.connect(server.address()).get(); + + System.out.println("-- timeout when reading --"); + + // this read should timeout + ByteBuffer dst = ByteBuffer.allocate(512); + try { + ch.read(dst, 3, TimeUnit.SECONDS, null, null).get(); + throw new RuntimeException("Read did not timeout"); + } catch (ExecutionException x) { + if (!(x.getCause() instanceof InterruptedByTimeoutException)) + throw new RuntimeException("InterruptedByTimeoutException expected"); + } + + // after a timeout then further reading should throw unspecified runtime exception + boolean exceptionThrown = false; + try { + ch.read(dst); + } catch (RuntimeException x) { + exceptionThrown = true; + } + if (!exceptionThrown) + throw new RuntimeException("RuntimeException expected after timeout."); + + + System.out.println("-- timeout when writing --"); + + final AtomicReference writeException = new AtomicReference(); + + final long timeout = 5; + final TimeUnit unit = TimeUnit.SECONDS; + + // write bytes to fill socket buffer + ch.write(genBuffer(), timeout, unit, ch, + new CompletionHandler() + { + public void completed(Integer result, AsynchronousSocketChannel ch) { + ch.write(genBuffer(), timeout, unit, ch, this); + } + public void failed(Throwable exc, AsynchronousSocketChannel ch) { + writeException.set(exc); + } + public void cancelled(AsynchronousSocketChannel ch) { + } + }); + + // wait for exception + while (writeException.get() == null) { + Thread.sleep(100); + } + if (!(writeException.get() instanceof InterruptedByTimeoutException)) + throw new RuntimeException("InterruptedByTimeoutException expected"); + + // after a timeout then further writing should throw unspecified runtime exception + exceptionThrown = false; + try { + ch.write(genBuffer()); + } catch (RuntimeException x) { + exceptionThrown = true; + } + if (!exceptionThrown) + throw new RuntimeException("RuntimeException expected after timeout."); + + ch.close(); + } + + // returns ByteBuffer with random bytes + static ByteBuffer genBuffer() { + int size = 1024 + rand.nextInt(16000); + byte[] buf = new byte[size]; + rand.nextBytes(buf); + boolean useDirect = rand.nextBoolean(); + if (useDirect) { + ByteBuffer bb = ByteBuffer.allocateDirect(buf.length); + bb.put(buf); + bb.flip(); + return bb; + } else { + return ByteBuffer.wrap(buf); + } + } + + // return ByteBuffer[] with random bytes + static ByteBuffer[] genBuffers(int max) { + int len = 1; + if (max > 1) + len += rand.nextInt(max); + ByteBuffer[] bufs = new ByteBuffer[len]; + for (int i=0; i readResult; + + Connection() throws Exception { + ServerSocketChannel ssc = + ServerSocketChannel.open().bind(new InetSocketAddress(0)); + InetAddress lh = InetAddress.getLocalHost(); + int port = ((InetSocketAddress)(ssc.getLocalAddress())).getPort(); + SocketAddress remote = new InetSocketAddress(lh, port); + client = AsynchronousSocketChannel.open(); + client.connect(remote).get(); + peer = ssc.accept(); + ssc.close(); + dst = ByteBuffer.allocate(K*K); + } + + void startRead() { + dst.clear(); + readResult = client.read(dst); + } + + void write() throws Exception { + peer.write(ByteBuffer.wrap("X".getBytes())); + } + + void finishRead() throws Exception { + readResult.get(); + } + } + + public static void main(String[] args) throws Exception { + + final int CONNECTION_COUNT = 10; + Connection[] connections = new Connection[CONNECTION_COUNT]; + for (int i=0; i len) + throw new RuntimeException("Too many bytes read"); + if (n > 0) { + total += n; + for (int i=0; i 0); + in.close(); + + } catch (IOException x) { + x.printStackTrace(); + } + } + + int total() { return total; } + int hash() { return hash; } + } + + static class Writer implements Runnable { + private final OutputStream out; + private final int total; + private volatile int hash; + + Writer(OutputStream out) { + this.out = out; + this.total = 50*1000 + rand.nextInt(50*1000); + } + + public void run() { + hash = 0; + int rem = total; + try { + do { + byte[] buf = new byte[1 + rand.nextInt(rem)]; + int off, len; + + // write random bytes + if (rand.nextBoolean()) { + off = 0; + len = buf.length; + } else { + off = rand.nextInt(buf.length); + int r = buf.length - off; + len = (r <= 1) ? 1 : (1 + rand.nextInt(r)); + } + for (int i=0; i 0); + + // close stream when done + out.close(); + + } catch (IOException x) { + x.printStackTrace(); + } + } + + int total() { return total; } + int hash() { return hash; } + } +} diff --git a/test/java/nio/channels/DatagramChannel/BasicMulticastTests.java b/test/java/nio/channels/DatagramChannel/BasicMulticastTests.java index 03b5daa68..6928bcded 100644 --- a/test/java/nio/channels/DatagramChannel/BasicMulticastTests.java +++ b/test/java/nio/channels/DatagramChannel/BasicMulticastTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007-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 @@ -64,11 +64,11 @@ public class BasicMulticastTests { // check key if (!key.isValid()) throw new RuntimeException("key is not valid"); - if (!key.getGroup().equals(group)) + if (!key.group().equals(group)) throw new RuntimeException("group is incorrect"); - if (!key.getNetworkInterface().equals(nif)) + if (!key.networkInterface().equals(nif)) throw new RuntimeException("network interface is incorrect"); - if (key.getSourceAddress() != null) + if (key.sourceAddress() != null) throw new RuntimeException("key is source specific"); // drop membership @@ -86,11 +86,11 @@ public class BasicMulticastTests { } if (!key.isValid()) throw new RuntimeException("key is not valid"); - if (!key.getGroup().equals(group)) + if (!key.group().equals(group)) throw new RuntimeException("group is incorrect"); - if (!key.getNetworkInterface().equals(nif)) + if (!key.networkInterface().equals(nif)) throw new RuntimeException("network interface is incorrect"); - if (!key.getSourceAddress().equals(source)) + if (!key.sourceAddress().equals(source)) throw new RuntimeException("key's source address incorrect"); // drop membership diff --git a/test/java/nio/channels/DatagramChannel/SocketOptionTests.java b/test/java/nio/channels/DatagramChannel/SocketOptionTests.java index e4e85b11f..3df7bbbda 100644 --- a/test/java/nio/channels/DatagramChannel/SocketOptionTests.java +++ b/test/java/nio/channels/DatagramChannel/SocketOptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007-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 @@ -49,7 +49,7 @@ public class SocketOptionTests { DatagramChannel dc = DatagramChannel.open(); // check supported options - Set> options = dc.options(); + Set> options = dc.supportedOptions(); List> expected = Arrays.asList(SO_SNDBUF, SO_RCVBUF, SO_REUSEADDR, SO_BROADCAST, IP_TOS, IP_MULTICAST_IF, IP_MULTICAST_TTL, IP_MULTICAST_LOOP); diff --git a/test/java/nio/channels/ServerSocketChannel/SocketOptionTests.java b/test/java/nio/channels/ServerSocketChannel/SocketOptionTests.java index 6c4a443ed..cba99e0f2 100644 --- a/test/java/nio/channels/ServerSocketChannel/SocketOptionTests.java +++ b/test/java/nio/channels/ServerSocketChannel/SocketOptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007-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 @@ -48,7 +48,7 @@ public class SocketOptionTests { ServerSocketChannel ssc = ServerSocketChannel.open(); // check supported options - Set> options = ssc.options(); + Set> options = ssc.supportedOptions(); if (!options.contains(SO_REUSEADDR)) throw new RuntimeException("SO_REUSEADDR should be supported"); if (!options.contains(SO_RCVBUF)) diff --git a/test/java/nio/channels/SocketChannel/SocketOptionTests.java b/test/java/nio/channels/SocketChannel/SocketOptionTests.java index b6fadced8..abcb97476 100644 --- a/test/java/nio/channels/SocketChannel/SocketOptionTests.java +++ b/test/java/nio/channels/SocketChannel/SocketOptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007-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 @@ -48,7 +48,7 @@ public class SocketOptionTests { SocketChannel sc = SocketChannel.open(); // check supported options - Set> options = sc.options(); + Set> options = sc.supportedOptions(); List expected = Arrays.asList(SO_SNDBUF, SO_RCVBUF, SO_KEEPALIVE, SO_REUSEADDR, SO_LINGER, TCP_NODELAY); for (SocketOption opt: expected) { diff --git a/test/java/nio/channels/etc/NetworkChannelTests.java b/test/java/nio/channels/etc/NetworkChannelTests.java index 5f03453ca..2a29bcebb 100644 --- a/test/java/nio/channels/etc/NetworkChannelTests.java +++ b/test/java/nio/channels/etc/NetworkChannelTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2007-2008 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2007-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 @@ -103,13 +103,14 @@ public class NetworkChannelTests { // closed ch.close(); - if (ch.getLocalAddress() != null) { - throw new RuntimeException("Local address return when closed"); - } + try { + ch.getLocalAddress(); + throw new RuntimeException("ClosedChannelException expected"); + } catch (ClosedChannelException e) { } } /** - * Exercise getConnectedAddress method (SocketChannel only) + * Exercise getRemoteAddress method (SocketChannel only) */ static void connectedAddressTests() throws IOException { ServerSocketChannel ssc = ServerSocketChannel.open() @@ -121,19 +122,21 @@ public class NetworkChannelTests { SocketChannel sc = SocketChannel.open(); // not connected - if (sc.getConnectedAddress() != null) - throw new RuntimeException("getConnectedAddress returned address when not connected"); + if (sc.getRemoteAddress() != null) + throw new RuntimeException("getRemoteAddress returned address when not connected"); // connected sc.connect(server); - SocketAddress remote = sc.getConnectedAddress(); + SocketAddress remote = sc.getRemoteAddress(); if (!remote.equals(server)) - throw new RuntimeException("getConnectedAddress returned incorrect address"); + throw new RuntimeException("getRemoteAddress returned incorrect address"); // closed sc.close(); - if (sc.getConnectedAddress() != null) - throw new RuntimeException("getConnectedAddress returned address when closed"); + try { + sc.getRemoteAddress(); + throw new RuntimeException("ClosedChannelException expected"); + } catch (ClosedChannelException e) { } ssc.close(); } diff --git a/test/java/nio/channels/spi/AsynchronousChannelProvider/CheckProvider.java b/test/java/nio/channels/spi/AsynchronousChannelProvider/CheckProvider.java new file mode 100644 index 000000000..4f6d84838 --- /dev/null +++ b/test/java/nio/channels/spi/AsynchronousChannelProvider/CheckProvider.java @@ -0,0 +1,38 @@ +/* + * 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. + */ + +import java.nio.channels.spi.AsynchronousChannelProvider; + +public class CheckProvider { + public static void main(String[] args) { + Class c = AsynchronousChannelProvider.provider().getClass(); + + String expected = args[0]; + String actual = c.getName(); + + if (!actual.equals(expected)) + throw new RuntimeException("Provider is of type '" + actual + + "', expected '" + expected + "'"); + + } +} diff --git a/test/java/nio/channels/spi/AsynchronousChannelProvider/META-INF/services/java.nio.channels.spi.AsynchronousChannelProvider b/test/java/nio/channels/spi/AsynchronousChannelProvider/META-INF/services/java.nio.channels.spi.AsynchronousChannelProvider new file mode 100644 index 000000000..6b3e87eb4 --- /dev/null +++ b/test/java/nio/channels/spi/AsynchronousChannelProvider/META-INF/services/java.nio.channels.spi.AsynchronousChannelProvider @@ -0,0 +1 @@ +Provider1 diff --git a/test/java/nio/channels/spi/AsynchronousChannelProvider/Provider1.java b/test/java/nio/channels/spi/AsynchronousChannelProvider/Provider1.java new file mode 100644 index 000000000..fc6036a1e --- /dev/null +++ b/test/java/nio/channels/spi/AsynchronousChannelProvider/Provider1.java @@ -0,0 +1,69 @@ +/* + * 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. + */ + +import java.nio.channels.spi.AsynchronousChannelProvider; +import java.nio.channels.*; +import java.net.ProtocolFamily; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; +import java.io.IOException; + +public class Provider1 extends AsynchronousChannelProvider { + public Provider1() { + } + + @Override + public AsynchronousChannelGroup openAsynchronousChannelGroup(int nThreads, ThreadFactory factorry) + throws IOException + { + throw new RuntimeException(); + } + + @Override + public AsynchronousChannelGroup openAsynchronousChannelGroup(ExecutorService executor, int initialSize) + throws IOException + { + throw new RuntimeException(); + } + + @Override + public AsynchronousSocketChannel openAsynchronousSocketChannel + (AsynchronousChannelGroup group) throws IOException + { + throw new RuntimeException(); + } + + @Override + public AsynchronousServerSocketChannel openAsynchronousServerSocketChannel + (AsynchronousChannelGroup group) throws IOException + { + throw new RuntimeException(); + } + + @Override + public AsynchronousDatagramChannel openAsynchronousDatagramChannel + (ProtocolFamily family, AsynchronousChannelGroup group) throws IOException + { + throw new RuntimeException(); + } +} diff --git a/test/java/nio/channels/spi/AsynchronousChannelProvider/Provider2.java b/test/java/nio/channels/spi/AsynchronousChannelProvider/Provider2.java new file mode 100644 index 000000000..a42c12ff9 --- /dev/null +++ b/test/java/nio/channels/spi/AsynchronousChannelProvider/Provider2.java @@ -0,0 +1,69 @@ +/* + * 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. + */ + +import java.nio.channels.spi.AsynchronousChannelProvider; +import java.nio.channels.*; +import java.net.ProtocolFamily; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ThreadFactory; +import java.io.IOException; + +public class Provider2 extends AsynchronousChannelProvider { + public Provider2() { + } + + @Override + public AsynchronousChannelGroup openAsynchronousChannelGroup + (int nThreads, ThreadFactory threadFactory) throws IOException + { + throw new RuntimeException(); + } + + @Override + public AsynchronousChannelGroup openAsynchronousChannelGroup + (ExecutorService executor, int initialSize) throws IOException + { + throw new RuntimeException(); + } + + @Override + public AsynchronousSocketChannel openAsynchronousSocketChannel + (AsynchronousChannelGroup group) throws IOException + { + throw new RuntimeException(); + } + + @Override + public AsynchronousServerSocketChannel openAsynchronousServerSocketChannel + (AsynchronousChannelGroup group) throws IOException + { + throw new RuntimeException(); + } + + @Override + public AsynchronousDatagramChannel openAsynchronousDatagramChannel + (ProtocolFamily family, AsynchronousChannelGroup group) throws IOException + { + throw new RuntimeException(); + } +} diff --git a/test/java/nio/channels/spi/AsynchronousChannelProvider/custom_provider.sh b/test/java/nio/channels/spi/AsynchronousChannelProvider/custom_provider.sh new file mode 100644 index 000000000..13fb2a2fa --- /dev/null +++ b/test/java/nio/channels/spi/AsynchronousChannelProvider/custom_provider.sh @@ -0,0 +1,71 @@ +# +# 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 +# @summary Unit test for java.nio.channels.spi.AsynchronousChannelProvider +# @build Provider1 Provider2 CheckProvider +# @run shell custom_provider.sh + +# if TESTJAVA isn't set then we assume an interactive run. + +if [ -z "$TESTJAVA" ]; then + TESTSRC=. + TESTCLASSES=. + JAVA=java +else + JAVA="${TESTJAVA}/bin/java" +fi + +OS=`uname -s` +case "$OS" in + Windows_* ) + CLASSPATH="${TESTCLASSES};${TESTSRC}" + ;; + * ) + CLASSPATH=${TESTCLASSES}:${TESTSRC} + ;; +esac +export CLASSPATH + +failures=0 + +go() { + echo '' + $JAVA $1 $2 $3 2>&1 + if [ $? != 0 ]; then failures=`expr $failures + 1`; fi +} + +# Run the tests + +go CheckProvider Provider1 +go -Djava.nio.channels.spi.AsynchronousChannelProvider=Provider2 CheckProvider \ + Provider2 + +# +# Results +# +echo '' +if [ $failures -gt 0 ]; + then echo "$failures test(s) failed"; + else echo "All test(s) passed"; fi +exit $failures diff --git a/test/java/nio/file/DirectoryStream/Basic.java b/test/java/nio/file/DirectoryStream/Basic.java new file mode 100644 index 000000000..b92447d70 --- /dev/null +++ b/test/java/nio/file/DirectoryStream/Basic.java @@ -0,0 +1,168 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.DirectoryStream + * @library .. + */ + +import java.nio.file.*; +import java.util.*; +import java.io.IOException; + +public class Basic { + static boolean found; + + static void doTest(final Path dir) throws IOException { + DirectoryStream stream; + + // test that directory is empty + Files.withDirectory(dir, new FileAction() { + public void invoke(FileRef entry) { + throw new RuntimeException("directory not empty"); + } + }); + + // create file in directory + final Path foo = Paths.get("foo"); + dir.resolve(foo).createFile(); + + // iterate over directory and check there is one entry + found = false; + Files.withDirectory(dir, new FileAction() { + public void invoke(Path entry) { + if (entry.getName().equals(foo)) { + if (found) + throw new RuntimeException("entry already found"); + found = true; + } else { + throw new RuntimeException("entry " + entry.getName() + + " not expected"); + } + } + }); + if (!found) + throw new RuntimeException("entry not found"); + + // check filtering: f* should match foo + DirectoryStream.Filter filter = new DirectoryStream.Filter() { + private PathMatcher matcher = + dir.getFileSystem().getPathMatcher("glob:f*"); + public boolean accept(Path file) { + return matcher.matches(file); + } + }; + Files.withDirectory(dir, filter, new FileAction() { + public void invoke(Path entry) { + if (!entry.getName().equals(foo)) + throw new RuntimeException("entry not expected"); + } + }); + + // check filtering: z* should not match any files + filter = new DirectoryStream.Filter() { + private PathMatcher matcher = + dir.getFileSystem().getPathMatcher("glob:z*"); + public boolean accept(Path file) { + return matcher.matches(file); + } + }; + Files.withDirectory(dir, filter, new FileAction() { + public void invoke(FileRef entry) { + throw new RuntimeException("no matching entries expected"); + } + }); + + // check that exception or error thrown by filter is not thrown + // by newDirectoryStream or iterator method. + stream = dir.newDirectoryStream(new DirectoryStream.Filter() { + public boolean accept(Path file) { + throw new RuntimeException("Should not be visible"); + } + }); + try { + stream.iterator(); + } finally { + stream.close(); + } + + // test NotDirectoryException + try { + dir.resolve(foo).newDirectoryStream(); + throw new RuntimeException("NotDirectoryException not thrown"); + } 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 + stream = dir.newDirectoryStream(); + i = stream.iterator(); + try { + stream.iterator(); + throw new RuntimeException("IllegalStateException not thrown as expected"); + } catch (IllegalStateException x) { + } + stream.close(); + try { + 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 IllegalStateException)) + throw new RuntimeException("Cause is not IllegalStateException as expected"); + } + try { + i.next(); + throw new RuntimeException("IllegalStateException not thrown as expected"); + } catch (ConcurrentModificationException x) { + Throwable t = x.getCause(); + if (!(t instanceof IllegalStateException)) + throw new RuntimeException("Cause is not IllegalStateException as expected"); + } + } + + public static void main(String[] args) throws IOException { + Path dir = TestUtil.createTemporaryDirectory(); + try { + doTest(dir); + } finally { + TestUtil.removeAll(dir); + } + } +} diff --git a/test/java/nio/file/DirectoryStream/Filters.java b/test/java/nio/file/DirectoryStream/Filters.java new file mode 100644 index 000000000..ea539c95d --- /dev/null +++ b/test/java/nio/file/DirectoryStream/Filters.java @@ -0,0 +1,241 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.DirectoryStreamFilters + * @library .. + */ + +import java.nio.file.*; +import static java.nio.file.DirectoryStreamFilters.*; +import java.nio.file.attribute.Attributes; +import java.io.*; +import java.util.*; + +public class Filters { + static final Random rand = new Random(); + + // returns a filter that only accepts files that are larger than a given size + static DirectoryStream.Filter newMinimumSizeFilter(final long min) { + return new DirectoryStream.Filter() { + public boolean accept(FileRef file) { + try { + long size = Attributes.readBasicFileAttributes(file).size(); + return size >= min; + } catch (IOException e) { + throw new IOError(e); + } + } + }; + } + + // returns a filter that only accepts files that are matched by a given glob + static DirectoryStream.Filter newGlobFilter(final String glob) { + return new DirectoryStream.Filter() { + PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:"+ glob); + public boolean accept(Path file) { + return matcher.matches(file.getName()); + } + }; + } + + static final int BIG_FILE_THRESHOLD = 8192; + + static int totalCount; + static int htmlCount; + static int bigAndHtmlCount; + static int bigOrHtmlCount; + + // generates random files in the test directory and initializes the counts + static void setup(Path dir) throws IOException { + // create 10-26 files. + totalCount = 10 + rand.nextInt(17); + char firstChar = 'A'; + for (int i=0; i 0) + out.write(new byte[size]); + } finally { + out.close(); + } + System.out.format("Created %s, size %d byte(s)\n", name, size); + } + } + + static boolean isHtml(Path file) { + return file.toString().endsWith(".html"); + } + + static boolean isBig(Path file) throws IOException { + long size = Attributes.readBasicFileAttributes(file).size(); + return size >= BIG_FILE_THRESHOLD; + } + + static void checkCount(int expected, int actual) { + if (actual != expected) + throw new RuntimeException("'" + expected + + "' entries expected, actual: " + actual); + } + + static void doTests(Path dir) throws IOException { + final List> emptyList = Collections.emptyList(); + + // list containing two filters + List> filters = + new ArrayList>(); + filters.add(newMinimumSizeFilter(BIG_FILE_THRESHOLD)); + filters.add(newGlobFilter("*.html")); + + int accepted; + DirectoryStream stream; + + System.out.println("Test: newContentTypeFilter"); + accepted = 0; + stream = dir.newDirectoryStream(newContentTypeFilter("text/html")); + try { + for (Path entry: stream) { + if (!isHtml(entry)) + throw new RuntimeException("html file expected"); + accepted++; + } + } finally { + stream.close(); + } + checkCount(htmlCount, accepted); + + System.out.println("Test: allOf with list of filters"); + accepted = 0; + stream = dir.newDirectoryStream(allOf(filters)); + try { + for (Path entry: stream) { + if (!isHtml(entry)) + throw new RuntimeException("html file expected"); + if (!isBig(entry)) + throw new RuntimeException("big file expected"); + accepted++; + } + } finally { + stream.close(); + } + checkCount(bigAndHtmlCount, accepted); + + System.out.println("Test: allOf with empty list"); + accepted = 0; + stream = dir.newDirectoryStream(allOf(emptyList)); + try { + for (Path entry: stream) { + accepted++; + } + } finally { + stream.close(); + } + checkCount(totalCount, accepted); + + System.out.println("Test: anyOf with list of filters"); + accepted = 0; + stream = dir.newDirectoryStream(anyOf(filters)); + try { + for (Path entry: stream) { + if (!isHtml(entry) && !isBig(entry)) + throw new RuntimeException("html or big file expected"); + accepted++; + } + } finally { + stream.close(); + } + checkCount(bigOrHtmlCount, accepted); + + System.out.println("Test: anyOf with empty list"); + accepted = 0; + stream = dir.newDirectoryStream(anyOf(emptyList)); + try { + for (Path entry: stream) { + accepted++; + } + } finally { + stream.close(); + } + checkCount(0, accepted); + + System.out.println("Test: complementOf"); + accepted = 0; + stream = dir.newDirectoryStream(complementOf(newGlobFilter("*.html"))); + try { + for (Path entry: stream) { + accepted++; + } + } finally { + stream.close(); + } + checkCount(totalCount-htmlCount, accepted); + + System.out.println("Test: nulls"); + try { + newContentTypeFilter(null); + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException npe) { } + try { + allOf(null); + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException npe) { } + try { + anyOf(null); + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException npe) { } + try { + complementOf(null); + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException npe) { } + } + + public static void main(String[] args) throws IOException { + Path dir = TestUtil.createTemporaryDirectory(); + try { + setup(dir); + doTests(dir); + } finally { + TestUtil.removeAll(dir); + } + } +} diff --git a/test/java/nio/file/DirectoryStream/SecureDS.java b/test/java/nio/file/DirectoryStream/SecureDS.java new file mode 100644 index 000000000..98367d895 --- /dev/null +++ b/test/java/nio/file/DirectoryStream/SecureDS.java @@ -0,0 +1,370 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.SecureDirectoryStream + * @library .. + */ + +import java.nio.file.*; +import static java.nio.file.StandardOpenOption.*; +import static java.nio.file.LinkOption.*; +import java.nio.file.attribute.*; +import java.nio.channels.*; +import java.io.IOException; +import java.util.*; + +public class SecureDS { + static boolean supportsLinks; + + public static void main(String[] args) throws IOException { + Path dir = TestUtil.createTemporaryDirectory(); + try { + DirectoryStream stream = dir.newDirectoryStream(); + stream.close(); + if (!(stream instanceof SecureDirectoryStream)) { + System.out.println("SecureDirectoryStream not supported."); + return; + } + + supportsLinks = TestUtil.supportsLinks(dir); + + // run tests + doBasicTests(dir); + doMoveTests(dir); + miscTests(dir); + + } finally { + TestUtil.removeAll(dir); + } + } + + // Exercise each of SecureDirectoryStream's method (except move) + static void doBasicTests(Path dir) throws IOException { + Path dir1 = dir.resolve("dir1").createDirectory(); + Path dir2 = dir.resolve("dir2"); + + // create a file, directory, and two sym links in the directory + Path fileEntry = Paths.get("myfile"); + dir1.resolve(fileEntry).createFile(); + Path dirEntry = Paths.get("mydir"); + dir1.resolve(dirEntry).createDirectory(); + // myfilelink -> myfile + Path link1Entry = Paths.get("myfilelink"); + if (supportsLinks) + dir1.resolve(link1Entry).createSymbolicLink(fileEntry); + // mydirlink -> mydir + Path link2Entry = Paths.get("mydirlink"); + if (supportsLinks) + dir1.resolve(link2Entry).createSymbolicLink(dirEntry); + + // open directory and then move it so that it is no longer accessible + // via its original path. + SecureDirectoryStream stream = + (SecureDirectoryStream)dir1.newDirectoryStream(); + dir1.moveTo(dir2); + + // Test: iterate over all entries + int count = 0; + for (Path entry: stream) { count++; } + assertTrue(count == (supportsLinks ? 4 : 2)); + + // Test: getFileAttributeView to access directory's attributes + assertTrue(stream + .getFileAttributeView(BasicFileAttributeView.class) + .readAttributes() + .isDirectory()); + + // Test: dynamic access to directory's attributes + BasicFileAttributeView view = stream. + getFileAttributeView(BasicFileAttributeView.class); + Map attrs = view.readAttributes("*"); + assertTrue((Boolean)attrs.get("isDirectory")); + attrs = view.readAttributes("isRegularFile", "size"); + assertTrue(!(Boolean)attrs.get("isRegularFile")); + assertTrue((Long)attrs.get("size") >= 0); + int linkCount = (Integer)view.getAttribute("linkCount"); + assertTrue(linkCount > 0); + view.setAttribute("lastModifiedTime", 0L); + + // Test: getFileAttributeView to access attributes of entries + assertTrue(stream + .getFileAttributeView(fileEntry, BasicFileAttributeView.class) + .readAttributes() + .isRegularFile()); + assertTrue(stream + .getFileAttributeView(fileEntry, BasicFileAttributeView.class, NOFOLLOW_LINKS) + .readAttributes() + .isRegularFile()); + assertTrue(stream + .getFileAttributeView(dirEntry, BasicFileAttributeView.class) + .readAttributes() + .isDirectory()); + assertTrue(stream + .getFileAttributeView(dirEntry, BasicFileAttributeView.class, NOFOLLOW_LINKS) + .readAttributes() + .isDirectory()); + if (supportsLinks) { + assertTrue(stream + .getFileAttributeView(link1Entry, BasicFileAttributeView.class) + .readAttributes() + .isRegularFile()); + assertTrue(stream + .getFileAttributeView(link1Entry, BasicFileAttributeView.class, NOFOLLOW_LINKS) + .readAttributes() + .isSymbolicLink()); + assertTrue(stream + .getFileAttributeView(link2Entry, BasicFileAttributeView.class) + .readAttributes() + .isDirectory()); + assertTrue(stream + .getFileAttributeView(link2Entry, BasicFileAttributeView.class, NOFOLLOW_LINKS) + .readAttributes() + .isSymbolicLink()); + } + + // Test: dynamic access to entry attributes + view = stream + .getFileAttributeView(fileEntry, PosixFileAttributeView.class, NOFOLLOW_LINKS); + if (view != null) { + attrs = view.readAttributes("owner", "size"); + UserPrincipal owner = (UserPrincipal)attrs.get("owner"); + assertTrue(owner != null); + assertTrue((Long)attrs.get("size") >= 0L); + view.setAttribute("lastAccessTime", 0L); + } + + // Test: newByteChannel + Set opts = Collections.emptySet(); + stream.newByteChannel(fileEntry, opts).close(); + if (supportsLinks) { + stream.newByteChannel(link1Entry, opts).close(); + try { + Set mixed = new HashSet(); + mixed.add(READ); + mixed.add(NOFOLLOW_LINKS); + stream.newByteChannel(link1Entry, mixed).close(); + shouldNotGetHere(); + } catch (IOException x) { } + } + + // Test: newDirectoryStream + stream.newDirectoryStream(dirEntry, true, null).close(); + stream.newDirectoryStream(dirEntry, false, null).close(); + if (supportsLinks) { + stream.newDirectoryStream(link2Entry, true, null).close(); + try { + stream.newDirectoryStream(link2Entry, false, null).close(); + shouldNotGetHere(); + } catch (IOException x) { } + } + + // Test: delete + if (supportsLinks) { + stream.deleteFile(link1Entry); + stream.deleteFile(link2Entry); + } + 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(); + } + + // Exercise SecureDirectoryStream's move method + static void doMoveTests(Path dir) throws IOException { + Path dir1 = dir.resolve("dir1").createDirectory(); + Path dir2 = dir.resolve("dir2").createDirectory(); + + // create dir1/myfile, dir1/mydir, dir1/mylink + Path fileEntry = Paths.get("myfile"); + dir1.resolve(fileEntry).createFile(); + Path dirEntry = Paths.get("mydir"); + dir1.resolve(dirEntry).createDirectory(); + Path linkEntry = Paths.get("mylink"); + if (supportsLinks) + dir1.resolve(linkEntry).createSymbolicLink(Paths.get("missing")); + + // target name + Path target = Paths.get("newfile"); + + // open stream to both directories + SecureDirectoryStream stream1 = + (SecureDirectoryStream)dir1.newDirectoryStream(); + SecureDirectoryStream stream2 = + (SecureDirectoryStream)dir2.newDirectoryStream(); + + // Test: move dir1/myfile -> dir2/newfile + stream1.move(fileEntry, stream2, target); + assertTrue(dir1.resolve(fileEntry).notExists()); + assertTrue(dir2.resolve(target).exists()); + stream2.deleteFile(target); + + // Test: move dir1/mydir -> dir2/newfile + stream1.move(dirEntry, stream2, target); + assertTrue(dir1.resolve(dirEntry).notExists()); + assertTrue(dir2.resolve(target).exists()); + stream2.deleteDirectory(target); + + // Test: move dir1/mylink -> dir2/newfile + if (supportsLinks) { + stream1.move(linkEntry, stream2, target); + assertTrue(dir2.resolve(target) + .getFileAttributeView(BasicFileAttributeView.class, NOFOLLOW_LINKS) + .readAttributes() + .isSymbolicLink()); + stream2.deleteFile(target); + } + + // Test: move between devices + String testDirAsString = System.getProperty("test.dir"); + if (testDirAsString != null) { + Path testDir = Paths.get(testDirAsString); + if (!dir1.getFileStore().equals(testDir.getFileStore())) { + SecureDirectoryStream ts = + (SecureDirectoryStream)testDir.newDirectoryStream(); + dir1.resolve(fileEntry).createFile(); + try { + stream1.move(fileEntry, ts, target); + shouldNotGetHere(); + } catch (AtomicMoveNotSupportedException x) { } + ts.close(); + stream1.deleteFile(fileEntry); + } + } + + // clean-up + dir1.delete(); + dir2.delete(); + } + + // null and ClosedDirectoryStreamException + static void miscTests(Path dir) throws IOException { + Path file = Paths.get("file"); + dir.resolve(file).createFile(); + + SecureDirectoryStream stream = + (SecureDirectoryStream)dir.newDirectoryStream(); + + // NullPointerException + try { + stream.getFileAttributeView(null); + shouldNotGetHere(); + } catch (NullPointerException x) { } + try { + stream.getFileAttributeView(null, BasicFileAttributeView.class); + shouldNotGetHere(); + } catch (NullPointerException x) { } + try { + stream.getFileAttributeView(file, null); + shouldNotGetHere(); + } catch (NullPointerException x) { } + try { + stream.newByteChannel(null, EnumSet.of(CREATE,WRITE)); + shouldNotGetHere(); + } catch (NullPointerException x) { } + try { + stream.newByteChannel(null, EnumSet.of(CREATE,WRITE,null)); + shouldNotGetHere(); + } catch (NullPointerException x) { } + try { + stream.newByteChannel(file, null); + shouldNotGetHere(); + } catch (NullPointerException x) { } + try { + stream.move(null, stream, file); + shouldNotGetHere(); + } catch (NullPointerException x) { } + try { + stream.move(file, null, file); + shouldNotGetHere(); + } catch (NullPointerException x) { } + try { + stream.move(file, stream, null); + shouldNotGetHere(); + } catch (NullPointerException x) { } + try { + stream.newDirectoryStream(null, true, null); + shouldNotGetHere(); + } catch (NullPointerException x) { } + try { + stream.deleteFile(null); + shouldNotGetHere(); + } catch (NullPointerException x) { } + try { + stream.deleteDirectory(null); + shouldNotGetHere(); + } catch (NullPointerException x) { } + + // close stream + stream.close(); + stream.close(); // should be no-op + + // ClosedDirectoryStreamException + try { + stream.newDirectoryStream(file, true, null); + shouldNotGetHere(); + } catch (ClosedDirectoryStreamException x) { } + try { + stream.newByteChannel(file, EnumSet.of(READ)); + shouldNotGetHere(); + } catch (ClosedDirectoryStreamException x) { } + try { + stream.move(file, stream, file); + shouldNotGetHere(); + } catch (ClosedDirectoryStreamException x) { } + try { + stream.deleteFile(file); + shouldNotGetHere(); + } catch (ClosedDirectoryStreamException x) { } + + // clean-up + dir.resolve(file).delete(); + } + + static void assertTrue(boolean b) { + if (!b) throw new RuntimeException("Assertion failed"); + } + + static void shouldNotGetHere() { + assertTrue(false); + } +} diff --git a/test/java/nio/file/FileStore/Basic.java b/test/java/nio/file/FileStore/Basic.java new file mode 100644 index 000000000..604795db4 --- /dev/null +++ b/test/java/nio/file/FileStore/Basic.java @@ -0,0 +1,87 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.FileStore + * @library .. + */ + +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.io.IOException; +import java.util.*; + +public class Basic { + + public static void main(String[] args) throws IOException { + Path dir = TestUtil.createTemporaryDirectory(); + try { + doTests(dir); + } finally { + TestUtil.removeAll(dir); + } + } + + static void assertTrue(boolean okay) { + if (!okay) + throw new RuntimeException("Assertion failed"); + } + + static void doTests(Path dir) throws IOException { + /** + * Test: Directory should be on FileStore that is writable + */ + assertTrue(!dir.getFileStore().isReadOnly()); + + /** + * Test: Two files should have the same FileStore + */ + FileStore store1 = dir.resolve("foo").createFile().getFileStore(); + FileStore store2 = dir.resolve("bar").createFile().getFileStore(); + assertTrue(store1.equals(store2)); + assertTrue(store2.equals(store1)); + assertTrue(store1.hashCode() == store2.hashCode()); + + /** + * Test: File and FileStore attributes + */ + assertTrue(store1.supportsFileAttributeView("basic")); + + /** + * Test: Enumerate all FileStores + */ + FileStore prev = null; + for (FileStore store: FileSystems.getDefault().getFileStores()) { + System.out.format("%s (name=%s type=%s)\n", store, store.name(), + store.type()); + + // check space attributes + Attributes.readFileStoreSpaceAttributes(store); + + // two distinct FileStores should not be equal + assertTrue(!store.equals(prev)); + prev = store; + } + } +} diff --git a/test/java/nio/file/FileSystem/Basic.java b/test/java/nio/file/FileSystem/Basic.java new file mode 100644 index 000000000..8df7c1e8d --- /dev/null +++ b/test/java/nio/file/FileSystem/Basic.java @@ -0,0 +1,82 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.FileSystem + * @library .. + */ + +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.io.IOException; + +/** + * Simple santity checks for java.nio.file.FileSystem + */ +public class Basic { + + static void check(boolean okay, String msg) { + if (!okay) + throw new RuntimeException(msg); + } + + static void checkSupported(FileSystem fs, String... views) { + for (String view: views) { + check(fs.supportedFileAttributeViews().contains(view), + "support for '" + view + "' expected"); + } + } + + public static void main(String[] args) throws IOException { + FileSystem fs = FileSystems.getDefault(); + + // close should throw UOE + try { + fs.close(); + throw new RuntimeException("UnsupportedOperationException expected"); + } catch (UnsupportedOperationException e) { } + check(fs.isOpen(), "should be open"); + + check(!fs.isReadOnly(), "should provide read-write access"); + + check(fs.provider().getScheme().equals("file"), + "should use 'file' scheme"); + + // santity check method - need to re-visit this in future as I/O errors + // are possible + for (FileStore store: fs.getFileStores()) { + System.out.println(store); + } + + // sanity check supportedFileAttributeViews + checkSupported(fs, "basic"); + String os = System.getProperty("os.name"); + if (os.equals("SunOS")) + checkSupported(fs, "posix", "unix", "owner", "acl", "xattr"); + if (os.equals("Linux")) + checkSupported(fs, "posix", "unix", "owner", "dos", "xattr"); + if (os.equals("Windows")) + checkSupported(fs, "owner", "dos", "acl", "xattr"); + } +} diff --git a/test/java/nio/file/Files/ContentType.java b/test/java/nio/file/Files/ContentType.java new file mode 100644 index 000000000..a0a5afc22 --- /dev/null +++ b/test/java/nio/file/Files/ContentType.java @@ -0,0 +1,91 @@ +/* + * 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. + */ + +import java.nio.file.*; +import java.io.*; + +/** + * Uses Files.probeContentType to probe html file and custom file type. + */ + +public class ContentType { + + static FileRef createHtmlFile() throws IOException { + Path file = File.createTempFile("foo", ".html").toPath(); + OutputStream out = file.newOutputStream(); + try { + out.write("foo".getBytes()); + } finally { + out.close(); + } + + return file; + } + + static FileRef createUnknownFile() throws IOException { + return File.createTempFile("unknown", "unknown-file-type-789").toPath(); + } + + static FileRef createGrapeFile() throws IOException { + return File.createTempFile("red", ".grape").toPath(); + } + + public static void main(String[] args) throws IOException { + + // exercise default file type detector + FileRef file = createHtmlFile(); + try { + String type = Files.probeContentType(file); + if (type == null) { + System.err.println("Content type cannot be determined - test skipped"); + } else { + if (!type.equals("text/html")) + throw new RuntimeException("Unexpected type: " + type); + } + } finally { + TestUtil.deleteUnchecked(file); + } + file = createUnknownFile(); + try { + String type = Files.probeContentType(file); + if (type != null) + throw new RuntimeException(file + " should not be recognized as:" + + type); + } finally { + TestUtil.deleteUnchecked(file); + } + + // exercise custom file type detector + file = createGrapeFile(); + try { + String type = Files.probeContentType(file); + if (type == null) + throw new RuntimeException("Custom file type detector not installed?"); + if (!type.equals("grape/unknown")) + throw new RuntimeException("Unexpected type: " + type); + } finally { + TestUtil.deleteUnchecked(file); + } + + } +} diff --git a/test/java/nio/file/Files/CreateFileTree.java b/test/java/nio/file/Files/CreateFileTree.java new file mode 100644 index 000000000..17549a7e3 --- /dev/null +++ b/test/java/nio/file/Files/CreateFileTree.java @@ -0,0 +1,96 @@ +/* + * 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. + */ + +import java.nio.file.*; +import java.io.IOException; +import java.util.*; + +/** + * Creates a file tree with possible cycles caused by symbolic links + * to ancestor directories. + */ + +public class CreateFileTree { + + static final Random rand = new Random(); + + public static Path createTemporaryDirectory() throws IOException { + Path tmpdir = Paths.get(System.getProperty("java.io.tmpdir")); + Path dir; + do { + dir = tmpdir.resolve("name" + rand.nextInt()); + } while (dir.exists()); + dir.createDirectory(); + return dir; + } + + public static void main(String[] args) throws IOException { + Path top = createTemporaryDirectory(); + if (!top.isAbsolute()) + top = top.toAbsolutePath(); + + List dirs = new ArrayList(); + + // create tree + Queue queue = new ArrayDeque(); + queue.add(top); + int total = 1 + rand.nextInt(20); + int n = 0; + Path dir; + while (((dir = queue.poll()) != null) && (n < total)) { + int r = Math.min((total-n), (1+rand.nextInt(3))); + for (int i=0; i= 2; + + // create a few regular files in the file tree + int files = dirs.size() * 3; + for (int i=0; i() { + public void invoke(Path entry) { + } + }); + npeExpected(); + } catch (NullPointerException e) { + } + + try { + Files.withDirectory(Paths.get("."), (String)null, new FileAction() { + public void invoke(Path entry) { + } + }); + npeExpected(); + } catch (NullPointerException e) { + } + + try { + Files.withDirectory(Paths.get("."), "*", null); + npeExpected(); + } catch (NullPointerException e) { + } + + // test propogation of IOException + Path tmpdir = TestUtil.createTemporaryDirectory(); + try { + tmpdir.resolve("foo").createFile(); + try { + Files.withDirectory(tmpdir, new FileAction() { + public void invoke(Path entry) throws IOException { + throw new IOException(); + } + }); + throw new RuntimeException("IOException expected"); + } catch (IOException e) { + } + } finally { + TestUtil.removeAll(tmpdir); + } + + try { + Files.walkFileTree(null, EnumSet.noneOf(FileVisitOption.class), + Integer.MAX_VALUE, new SimpleFileVisitor(){}); + npeExpected(); + } catch (NullPointerException e) { + } + + try { + Files.walkFileTree(Paths.get("."), null, Integer.MAX_VALUE, + new SimpleFileVisitor(){}); + npeExpected(); + } catch (NullPointerException e) { + } + + try { + Files.walkFileTree(Paths.get("."), EnumSet.noneOf(FileVisitOption.class), + -1, new SimpleFileVisitor(){}); + throw new RuntimeException("IllegalArgumentExpected expected"); + } catch (IllegalArgumentException e) { + } + + try { + Set opts = new HashSet(1); + opts.add(null); + Files.walkFileTree(Paths.get("."), opts, Integer.MAX_VALUE, + new SimpleFileVisitor(){}); + npeExpected(); + } catch (NullPointerException e) { + } + + try { + Files.walkFileTree(Paths.get("."), EnumSet.noneOf(FileVisitOption.class), + Integer.MAX_VALUE, null); + npeExpected(); + } catch (NullPointerException e) { + } + } +} diff --git a/test/java/nio/file/Files/PrintFileTree.java b/test/java/nio/file/Files/PrintFileTree.java new file mode 100644 index 000000000..dc2c49f8d --- /dev/null +++ b/test/java/nio/file/Files/PrintFileTree.java @@ -0,0 +1,78 @@ +/* + * 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. + */ + +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.io.IOException; +import java.util.*; + +/** + * Invokes Files.walkFileTree to traverse a file tree and prints + * each of the directories and files. The -L option causes symbolic + * links to be followed. + */ + +public class PrintFileTree { + + public static void main(String[] args) throws Exception { + boolean followLinks = false; + Path dir; + + if (args[0].equals("-L")) { + followLinks = true; + dir = Paths.get(args[1]); + } else { + dir = Paths.get(args[0]); + } + + Set options = new HashSet(); + if (followLinks) + options.add(FileVisitOption.FOLLOW_LINKS); + + Files.walkFileTree(dir, options, Integer.MAX_VALUE, new FileVisitor() { + public FileVisitResult preVisitDirectory(FileRef dir) { + System.out.println(dir); + return FileVisitResult.CONTINUE; + } + public FileVisitResult preVisitDirectoryFailed(FileRef dir, IOException exc) { + exc.printStackTrace(); + return FileVisitResult.CONTINUE; + } + public FileVisitResult visitFile(FileRef file, BasicFileAttributes attrs) { + System.out.println(file); + return FileVisitResult.CONTINUE; + } + public FileVisitResult postVisitDirectory(FileRef dir, IOException exc) { + if (exc != null) { + exc.printStackTrace(); + return FileVisitResult.TERMINATE; + } + return FileVisitResult.CONTINUE; + } + public FileVisitResult visitFileFailed(FileRef file, IOException exc) { + exc.printStackTrace(); + return FileVisitResult.TERMINATE; + } + }); + } +} diff --git a/test/java/nio/file/Files/SimpleFileTypeDetector.java b/test/java/nio/file/Files/SimpleFileTypeDetector.java new file mode 100644 index 000000000..0b66706d6 --- /dev/null +++ b/test/java/nio/file/Files/SimpleFileTypeDetector.java @@ -0,0 +1,47 @@ +/* + * 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. + */ + +import java.nio.file.*; +import java.nio.file.spi.FileTypeDetector; +import java.io.*; + + +public class SimpleFileTypeDetector extends FileTypeDetector { + public SimpleFileTypeDetector() { + } + + public String probeContentType(FileRef file) throws IOException { + + System.out.println("probe " + file + "..."); + + if (file instanceof Path) { + String name = ((Path)file).toString(); + if (name.endsWith(".grape")) { + return "grape/unknown"; + } + } + + // unknown + return null; + } +} diff --git a/test/java/nio/file/Files/SkipSiblings.java b/test/java/nio/file/Files/SkipSiblings.java new file mode 100644 index 000000000..e85245520 --- /dev/null +++ b/test/java/nio/file/Files/SkipSiblings.java @@ -0,0 +1,85 @@ +/* + * 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. + */ + +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.io.IOException; +import java.util.*; + +/** + * Unit test for Files.walkFileTree to test SKIP_SIBLINGS return value. + */ + +public class SkipSiblings { + + static final Random rand = new Random(); + static final Set skipped = new HashSet(); + + // check if this path's directory has been skipped + static void check(Path path) { + if (skipped.contains(path.getParent())) + throw new RuntimeException(path + " should not have been visited"); + } + + // indicates if the siblings of this path should be skipped + static boolean skip(Path path) { + Path parent = path.getParent(); + if (parent != null && rand.nextBoolean()) { + skipped.add(parent); + return true; + } + return false; + } + + public static void main(String[] args) throws Exception { + Path dir = Paths.get(args[0]); + + Files.walkFileTree(dir, new FileVisitor() { + public FileVisitResult preVisitDirectory(Path dir) { + check(dir); + if (skip(dir)) + return FileVisitResult.SKIP_SIBLINGS; + return FileVisitResult.CONTINUE; + } + public FileVisitResult preVisitDirectoryFailed(Path dir, IOException exc) { + throw new RuntimeException(exc); + } + + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + check(file); + if (skip(file)) + return FileVisitResult.SKIP_SIBLINGS; + return FileVisitResult.CONTINUE; + } + public FileVisitResult postVisitDirectory(Path dir, IOException x) { + if (x != null) + throw new RuntimeException(x); + check(dir); + return FileVisitResult.CONTINUE; + } + public FileVisitResult visitFileFailed(Path file, IOException x) { + throw new RuntimeException(x); + } + }); + } +} diff --git a/test/java/nio/file/Files/TerminateWalk.java b/test/java/nio/file/Files/TerminateWalk.java new file mode 100644 index 000000000..82fc8ed57 --- /dev/null +++ b/test/java/nio/file/Files/TerminateWalk.java @@ -0,0 +1,70 @@ +/* + * 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. + */ + +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.io.IOException; +import java.util.*; + +/** + * Unit test for Files.walkFileTree to test TERMINATE return value + */ + +public class TerminateWalk { + + static final Random rand = new Random(); + static boolean terminated; + + static FileVisitResult maybeTerminate() { + if (terminated) + throw new RuntimeException("FileVisitor invoked after termination"); + if (rand.nextInt(10) == 0) { + terminated = true; + return FileVisitResult.TERMINATE; + } else { + return FileVisitResult.CONTINUE; + } + } + + public static void main(String[] args) throws Exception { + Path dir = Paths.get(args[0]); + + Files.walkFileTree(dir, new FileVisitor() { + public FileVisitResult preVisitDirectory(Path dir) { + return maybeTerminate(); + } + public FileVisitResult preVisitDirectoryFailed(Path dir, IOException exc) { + return maybeTerminate(); + } + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + return maybeTerminate(); + } + public FileVisitResult postVisitDirectory(Path dir, IOException x) { + return maybeTerminate(); + } + public FileVisitResult visitFileFailed(Path file, IOException x) { + return maybeTerminate(); + } + }); + } +} diff --git a/test/java/nio/file/Files/content_type.sh b/test/java/nio/file/Files/content_type.sh new file mode 100644 index 000000000..46f4626c7 --- /dev/null +++ b/test/java/nio/file/Files/content_type.sh @@ -0,0 +1,71 @@ +# +# 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 4313887 +# @summary Unit test for probeContentType method +# @library .. +# @build ContentType SimpleFileTypeDetector +# @run shell content_type.sh + +# if TESTJAVA isn't set then we assume an interactive run. + +if [ -z "$TESTJAVA" ]; then + TESTSRC=. + TESTCLASSES=. + JAVA=java +else + JAVA="${TESTJAVA}/bin/java" +fi + +OS=`uname -s` +case "$OS" in + Windows_* ) + CLASSPATH="${TESTCLASSES};${TESTSRC}" + ;; + * ) + CLASSPATH=${TESTCLASSES}:${TESTSRC} + ;; +esac +export CLASSPATH + +failures=0 + +go() { + echo '' + $JAVA $1 $2 $3 2>&1 + if [ $? != 0 ]; then failures=`expr $failures + 1`; fi +} + +# Run the test + +go ContentType + +# +# Results +# +echo '' +if [ $failures -gt 0 ]; + then echo "$failures test(s) failed"; + else echo "All test(s) passed"; fi +exit $failures diff --git a/test/java/nio/file/Files/walk_file_tree.sh b/test/java/nio/file/Files/walk_file_tree.sh new file mode 100644 index 000000000..73022d31d --- /dev/null +++ b/test/java/nio/file/Files/walk_file_tree.sh @@ -0,0 +1,86 @@ +# +# 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 4313887 +# @summary Unit test for walkFileTree method +# @build CreateFileTree PrintFileTree SkipSiblings TerminateWalk +# @run shell walk_file_tree.sh + +# if TESTJAVA isn't set then we assume an interactive run. + +if [ -z "$TESTJAVA" ]; then + TESTSRC=. + TESTCLASSES=. + JAVA=java +else + JAVA="${TESTJAVA}/bin/java" +fi + +OS=`uname -s` +case "$OS" in + Windows_* ) + echo "This test does not run on Windows" + exit 0 + ;; + * ) + CLASSPATH=${TESTCLASSES}:${TESTSRC} + ;; +esac +export CLASSPATH + +# create the file tree +ROOT=`$JAVA CreateFileTree` +if [ $? != 0 ]; then exit 1; fi + +failures=0 + +# print the file tree and compare output with find(1) +$JAVA PrintFileTree "$ROOT" > out1 +find "$ROOT" > out2 +diff out1 out2 +if [ $? != 0 ]; then failures=`expr $failures + 1`; fi + +# repeat test following links (use -follow instead of -L +# to allow running on older systems) +$JAVA PrintFileTree -L "$ROOT" > out1 +find "$ROOT" -follow > out2 +diff out1 out2 +if [ $? != 0 ]; then failures=`expr $failures + 1`; fi + +# test SKIP_SIBLINGS +$JAVA SkipSiblings "$ROOT" +if [ $? != 0 ]; then failures=`expr $failures + 1`; fi + +# test TERMINATE +$JAVA TerminateWalk "$ROOT" +if [ $? != 0 ]; then failures=`expr $failures + 1`; fi + +# clean-up +rm -r "$ROOT" + +echo '' +if [ $failures -gt 0 ]; + then echo "$failures test(s) failed"; + else echo "Test passed"; fi +exit $failures diff --git a/test/java/nio/file/Path/CopyAndMove.java b/test/java/nio/file/Path/CopyAndMove.java new file mode 100644 index 000000000..4e4c75fd2 --- /dev/null +++ b/test/java/nio/file/Path/CopyAndMove.java @@ -0,0 +1,983 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.Path copyTo/moveTo methods + * @library .. + */ + +import java.nio.ByteBuffer; +import java.nio.file.*; +import static java.nio.file.StandardCopyOption.*; +import static java.nio.file.LinkOption.*; +import java.nio.file.attribute.*; +import java.io.*; +import java.util.*; + +public class CopyAndMove { + static final Random rand = new Random(); + static boolean heads() { return rand.nextBoolean(); } + static boolean supportsLinks; + + public static void main(String[] args) throws Exception { + Path dir1 = TestUtil.createTemporaryDirectory(); + try { + supportsLinks = TestUtil.supportsLinks(dir1); + + // Exercise copyTo + doCopyTests(dir1); + + // Exercise moveTo + // if test.dir differs to temporary file system then can test + // moving between devices + String testDir = System.getProperty("test.dir"); + Path dir2 = (testDir != null) ? Paths.get(testDir) : dir1; + doMoveTests(dir1, dir2); + + } finally { + TestUtil.removeAll(dir1); + } + } + + static void checkBasicAttributes(BasicFileAttributes attrs1, + BasicFileAttributes attrs2) + { + // check file type + assertTrue(attrs1.isRegularFile() == attrs2.isRegularFile()); + assertTrue(attrs1.isDirectory() == attrs2.isDirectory()); + assertTrue(attrs1.isSymbolicLink() == attrs2.isSymbolicLink()); + assertTrue(attrs1.isOther() == attrs2.isOther()); + + // check last modified time (assume millisecond precision) + long time1 = attrs1.resolution().toMillis(attrs1.lastModifiedTime()); + long time2 = attrs1.resolution().toMillis(attrs2.lastModifiedTime()); + assertTrue(time1 == time2); + + // check size + if (attrs1.isRegularFile()) + assertTrue(attrs1.size() == attrs2.size()); + } + + static void checkPosixAttributes(PosixFileAttributes attrs1, + PosixFileAttributes attrs2) + { + assertTrue(attrs1.permissions().equals(attrs2.permissions())); + assertTrue(attrs1.owner().equals(attrs2.owner())); + assertTrue(attrs1.group().equals(attrs2.group())); + } + + static void checkDosAttributes(DosFileAttributes attrs1, + DosFileAttributes attrs2) + { + assertTrue(attrs1.isReadOnly() == attrs2.isReadOnly()); + assertTrue(attrs1.isHidden() == attrs2.isHidden()); + assertTrue(attrs1.isArchive() == attrs2.isArchive()); + assertTrue(attrs1.isSystem() == attrs2.isSystem()); + } + + static void checkUserDefinedFileAttributes(Map attrs1, + Map attrs2) + { + assert attrs1.size() == attrs2.size(); + for (String name: attrs1.keySet()) { + ByteBuffer bb1 = attrs1.get(name); + ByteBuffer bb2 = attrs2.get(name); + assertTrue(bb2 != null); + assertTrue(bb1.equals(bb2)); + } + } + + static Map readUserDefinedFileAttributes(Path file) + throws IOException + { + UserDefinedFileAttributeView view = file + .getFileAttributeView(UserDefinedFileAttributeView.class); + Map result = new HashMap(); + for (String name: view.list()) { + int size = view.size(name); + ByteBuffer bb = ByteBuffer.allocate(size); + int n = view.read(name, bb); + assertTrue(n == size); + bb.flip(); + result.put(name, bb); + } + return result; + } + + // move source to target with verification + static void moveAndVerify(Path source, Path target, CopyOption... options) + throws IOException + { + // read attributes before file is moved + BasicFileAttributes basicAttributes = null; + PosixFileAttributes posixAttributes = null; + DosFileAttributes dosAttributes = null; + Map namedAttributes = null; + + // get file attributes of source file + String os = System.getProperty("os.name"); + if (os.equals("SunOS") || os.equals("Linux")) { + posixAttributes = Attributes.readPosixFileAttributes(source, NOFOLLOW_LINKS); + basicAttributes = posixAttributes; + } + if (os.startsWith("Windows")) { + dosAttributes = Attributes.readDosFileAttributes(source, NOFOLLOW_LINKS); + basicAttributes = dosAttributes; + } + if (basicAttributes == null) + basicAttributes = Attributes.readBasicFileAttributes(source, NOFOLLOW_LINKS); + + // hash file contents if regular file + int hash = (basicAttributes.isRegularFile()) ? computeHash(source) : 0; + + // record link target if symbolic link + Path linkTarget = null; + if (basicAttributes.isSymbolicLink()) + linkTarget = source.readSymbolicLink(); + + // read named attributes if available (and file is not a sym link) + if (!basicAttributes.isSymbolicLink() && + source.getFileStore().supportsFileAttributeView("xattr")) + { + namedAttributes = readUserDefinedFileAttributes(source); + } + + // move file + source.moveTo(target, options); + + // verify source does not exist + assertTrue(source.notExists()); + + // verify file contents + if (basicAttributes.isRegularFile()) { + if (computeHash(target) != hash) + throw new RuntimeException("Failed to verify move of regular file"); + } + + // verify link target + if (basicAttributes.isSymbolicLink()) { + if (!target.readSymbolicLink().equals(linkTarget)) + throw new RuntimeException("Failed to verify move of symbolic link"); + } + + // verify basic attributes + checkBasicAttributes(basicAttributes, + Attributes.readBasicFileAttributes(target, NOFOLLOW_LINKS)); + + // verify POSIX attributes + if (posixAttributes != null && !basicAttributes.isSymbolicLink()) { + checkPosixAttributes(posixAttributes, + Attributes.readPosixFileAttributes(target, NOFOLLOW_LINKS)); + } + + // verify DOS attributes + if (dosAttributes != null && !basicAttributes.isSymbolicLink()) { + checkDosAttributes(dosAttributes, + Attributes.readDosFileAttributes(target, NOFOLLOW_LINKS)); + } + + // verify named attributes + if (namedAttributes != null && + target.getFileStore().supportsFileAttributeView("xattr")) + { + checkUserDefinedFileAttributes(namedAttributes, readUserDefinedFileAttributes(target)); + } + } + + /** + * Tests all possible ways to invoke moveTo + */ + static void doMoveTests(Path dir1, Path dir2) throws IOException { + Path source, target, entry; + + boolean sameDevice = dir1.getFileStore().equals(dir2.getFileStore()); + + // -- regular file -- + + /** + * Test: move regular file, target does not exist + */ + source = createSourceFile(dir1); + target = getTargetFile(dir1); + moveAndVerify(source, target); + target.delete(); + + /** + * Test: move regular file, target exists + */ + source = createSourceFile(dir1); + target = getTargetFile(dir1).createFile(); + try { + moveAndVerify(source, target); + throw new RuntimeException("FileAlreadyExistsException expected"); + } catch (FileAlreadyExistsException x) { + } + target.delete(); + target.createDirectory(); + try { + moveAndVerify(source, target); + throw new RuntimeException("FileAlreadyExistsException expected"); + } catch (FileAlreadyExistsException x) { + } + source.delete(); + target.delete(); + + /** + * Test: move regular file, target does not exist + */ + source = createSourceFile(dir1); + target = getTargetFile(dir1); + moveAndVerify(source, target, REPLACE_EXISTING); + target.delete(); + + /** + * Test: move regular file, target exists + */ + source = createSourceFile(dir1); + target = getTargetFile(dir1).createFile(); + moveAndVerify(source, target, REPLACE_EXISTING); + target.delete(); + + /** + * Test: move regular file, target exists and is empty directory + */ + source = createSourceFile(dir1); + target = getTargetFile(dir1).createDirectory(); + moveAndVerify(source, target, REPLACE_EXISTING); + target.delete(); + + /** + * Test: move regular file, target exists and is non-empty directory + */ + source = createSourceFile(dir1); + target = getTargetFile(dir1).createDirectory(); + entry = target.resolve("foo").createFile(); + try { + moveAndVerify(source, target); + throw new RuntimeException("FileAlreadyExistsException expected"); + } catch (FileAlreadyExistsException x) { + } + entry.delete(); + source.delete(); + target.delete(); + + /** + * Test atomic move of regular file (same file store) + */ + source = createSourceFile(dir1); + target = getTargetFile(dir1); + moveAndVerify(source, target, ATOMIC_MOVE); + target.delete(); + + /** + * Test atomic move of regular file (different file store) + */ + if (!sameDevice) { + source = createSourceFile(dir1); + target = getTargetFile(dir2); + try { + moveAndVerify(source, target, ATOMIC_MOVE); + throw new RuntimeException("AtomicMoveNotSupportedException expected"); + } catch (AtomicMoveNotSupportedException x) { + } + source.delete(); + } + + // -- directories -- + + /* + * Test: move empty directory, target does not exist + */ + source = createSourceDirectory(dir1); + target = getTargetFile(dir1); + moveAndVerify(source, target); + target.delete(); + + /** + * Test: move empty directory, target exists + */ + source = createSourceDirectory(dir1); + target = getTargetFile(dir1).createFile(); + try { + moveAndVerify(source, target); + throw new RuntimeException("FileAlreadyExistsException expected"); + } catch (FileAlreadyExistsException x) { + } + target.delete(); + target.createDirectory(); + try { + moveAndVerify(source, target); + throw new RuntimeException("FileAlreadyExistsException expected"); + } catch (FileAlreadyExistsException x) { + } + source.delete(); + target.delete(); + + /** + * Test: move empty directory, target does not exist + */ + source = createSourceDirectory(dir1); + target = getTargetFile(dir1); + moveAndVerify(source, target, REPLACE_EXISTING); + target.delete(); + + /** + * Test: move empty directory, target exists + */ + source = createSourceDirectory(dir1); + target = getTargetFile(dir1).createFile(); + moveAndVerify(source, target, REPLACE_EXISTING); + target.delete(); + + /** + * Test: move empty, target exists and is empty directory + */ + source = createSourceDirectory(dir1); + target = getTargetFile(dir1).createDirectory(); + moveAndVerify(source, target, REPLACE_EXISTING); + target.delete(); + + /** + * Test: move empty directory, target exists and is non-empty directory + */ + source = createSourceDirectory(dir1); + target = getTargetFile(dir1).createDirectory(); + entry = target.resolve("foo").createFile(); + try { + moveAndVerify(source, target, REPLACE_EXISTING); + throw new RuntimeException("FileAlreadyExistsException expected"); + } catch (FileAlreadyExistsException x) { + } + entry.delete(); + source.delete(); + target.delete(); + + /** + * Test: move non-empty directory (same file system) + */ + source = createSourceDirectory(dir1); + source.resolve("foo").createFile(); + target = getTargetFile(dir1); + moveAndVerify(source, target); + target.resolve("foo").delete(); + target.delete(); + + /** + * Test: move non-empty directory (different file store) + */ + if (!sameDevice) { + source = createSourceDirectory(dir1); + source.resolve("foo").createFile(); + target = getTargetFile(dir2); + try { + moveAndVerify(source, target); + throw new RuntimeException("IOException expected"); + } catch (IOException x) { + } + source.resolve("foo").delete(); + source.delete(); + } + + /** + * Test atomic move of directory (same file store) + */ + source = createSourceDirectory(dir1); + source.resolve("foo").createFile(); + target = getTargetFile(dir1); + moveAndVerify(source, target, ATOMIC_MOVE); + target.resolve("foo").delete(); + target.delete(); + + // -- symbolic links -- + + /** + * Test: Move symbolic link to file, target does not exist + */ + if (supportsLinks) { + Path tmp = createSourceFile(dir1); + source = dir1.resolve("link").createSymbolicLink(tmp); + target = getTargetFile(dir1); + moveAndVerify(source, target); + target.delete(); + tmp.delete(); + } + + /** + * Test: Move symbolic link to directory, target does not exist + */ + if (supportsLinks) { + source = dir1.resolve("link").createSymbolicLink(dir2); + target = getTargetFile(dir1); + moveAndVerify(source, target); + target.delete(); + } + + /** + * Test: Move broken symbolic link, target does not exists + */ + if (supportsLinks) { + Path tmp = Paths.get("doesnotexist"); + source = dir1.resolve("link").createSymbolicLink(tmp); + target = getTargetFile(dir1); + moveAndVerify(source, target); + target.delete(); + } + + /** + * Test: Move symbolic link, target exists + */ + if (supportsLinks) { + source = dir1.resolve("link").createSymbolicLink(dir2); + target = getTargetFile(dir1).createFile(); + try { + moveAndVerify(source, target); + throw new RuntimeException("FileAlreadyExistsException expected"); + } catch (FileAlreadyExistsException x) { + } + source.delete(); + target.delete(); + } + + /** + * Test: Move regular file, target exists + */ + if (supportsLinks) { + source = dir1.resolve("link").createSymbolicLink(dir2); + target = getTargetFile(dir1).createFile(); + moveAndVerify(source, target, REPLACE_EXISTING); + target.delete(); + } + + /** + * Test: move symbolic link, target exists and is empty directory + */ + if (supportsLinks) { + source = dir1.resolve("link").createSymbolicLink(dir2); + target = getTargetFile(dir1).createDirectory(); + moveAndVerify(source, target, REPLACE_EXISTING); + target.delete(); + } + + /** + * Test: symbolic link, target exists and is non-empty directory + */ + if (supportsLinks) { + source = dir1.resolve("link").createSymbolicLink(dir2); + target = getTargetFile(dir1).createDirectory(); + entry = target.resolve("foo").createFile(); + try { + moveAndVerify(source, target); + throw new RuntimeException("FileAlreadyExistsException expected"); + } catch (FileAlreadyExistsException x) { + } + entry.delete(); + source.delete(); + target.delete(); + } + + /** + * Test atomic move of symbolic link (same file store) + */ + if (supportsLinks) { + source = dir1.resolve("link").createSymbolicLink(dir1); + target = getTargetFile(dir1).createFile(); + moveAndVerify(source, target, REPLACE_EXISTING); + target.delete(); + } + + // -- misc. tests -- + + /** + * Test nulls + */ + source = createSourceFile(dir1); + target = getTargetFile(dir1); + try { + source.moveTo(null); + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException x) { } + try { + source.moveTo(target, (CopyOption[])null); + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException x) { } + try { + CopyOption[] opts = { REPLACE_EXISTING, null }; + source.moveTo(target, opts); + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException x) { } + source.delete(); + + /** + * Test UOE + */ + source = createSourceFile(dir1); + target = getTargetFile(dir1); + try { + source.moveTo(target, new CopyOption() { }); + } catch (UnsupportedOperationException x) { } + try { + source.moveTo(target, REPLACE_EXISTING, new CopyOption() { }); + } catch (UnsupportedOperationException x) { } + source.delete(); + } + + // copy source to target with verification + static void copyAndVerify(Path source, Path target, CopyOption... options) + throws IOException + { + source.copyTo(target, options); + + // get attributes of source and target file to verify copy + boolean followLinks = true; + LinkOption[] linkOptions = new LinkOption[0]; + boolean copyAttributes = false; + for (CopyOption opt : options) { + if (opt == NOFOLLOW_LINKS) { + followLinks = false; + linkOptions = new LinkOption[] { NOFOLLOW_LINKS }; + } + if (opt == COPY_ATTRIBUTES) + copyAttributes = true; + } + BasicFileAttributes basicAttributes = Attributes + .readBasicFileAttributes(source, linkOptions); + + // check hash if regular file + if (basicAttributes.isRegularFile()) + assertTrue(computeHash(source) == computeHash(target)); + + // check link target if symbolic link + if (basicAttributes.isSymbolicLink()) + assert( source.readSymbolicLink().equals(target.readSymbolicLink())); + + // check that attributes are copied + if (copyAttributes && followLinks) { + checkBasicAttributes(basicAttributes, + Attributes.readBasicFileAttributes(source, linkOptions)); + + // check POSIX attributes are copied + String os = System.getProperty("os.name"); + if (os.equals("SunOS") || os.equals("Linux")) { + checkPosixAttributes( + Attributes.readPosixFileAttributes(source, linkOptions), + Attributes.readPosixFileAttributes(target, linkOptions)); + } + + // check DOS attributes are copied + if (os.startsWith("Windows")) { + checkDosAttributes( + Attributes.readDosFileAttributes(source, linkOptions), + Attributes.readDosFileAttributes(target, linkOptions)); + } + + // check named attributes are copied + if (followLinks && + source.getFileStore().supportsFileAttributeView("xattr") && + target.getFileStore().supportsFileAttributeView("xattr")) + { + checkUserDefinedFileAttributes(readUserDefinedFileAttributes(source), + readUserDefinedFileAttributes(target)); + } + } + } + + /** + * Tests all possible ways to invoke copyTo + */ + static void doCopyTests(Path dir) throws IOException { + Path source, target, link, entry; + + // -- regular file -- + + /** + * Test: move regular file, target does not exist + */ + source = createSourceFile(dir); + target = getTargetFile(dir); + copyAndVerify(source, target); + source.delete(); + target.delete(); + + /** + * Test: copy regular file, target exists + */ + source = createSourceFile(dir); + target = getTargetFile(dir).createFile(); + try { + copyAndVerify(source, target); + throw new RuntimeException("FileAlreadyExistsException expected"); + } catch (FileAlreadyExistsException x) { + } + target.delete(); + target.createDirectory(); + try { + copyAndVerify(source, target); + throw new RuntimeException("FileAlreadyExistsException expected"); + } catch (FileAlreadyExistsException x) { + } + source.delete(); + target.delete(); + + /** + * Test: copy regular file, target does not exist + */ + source = createSourceFile(dir); + target = getTargetFile(dir); + copyAndVerify(source, target, REPLACE_EXISTING); + source.delete(); + target.delete(); + + /** + * Test: copy regular file, target exists + */ + source = createSourceFile(dir); + target = getTargetFile(dir).createFile(); + copyAndVerify(source, target, REPLACE_EXISTING); + source.delete(); + target.delete(); + + /** + * Test: copy regular file, target exists and is empty directory + */ + source = createSourceFile(dir); + target = getTargetFile(dir).createDirectory(); + copyAndVerify(source, target, REPLACE_EXISTING); + source.delete(); + target.delete(); + + /** + * Test: copy regular file, target exists and is non-empty directory + */ + source = createSourceFile(dir); + target = getTargetFile(dir).createDirectory(); + entry = target.resolve("foo").createFile(); + try { + copyAndVerify(source, target); + throw new RuntimeException("FileAlreadyExistsException expected"); + } catch (FileAlreadyExistsException x) { + } + entry.delete(); + source.delete(); + target.delete(); + + /** + * Test: copy regular file + attributes + */ + source = createSourceFile(dir); + target = getTargetFile(dir); + copyAndVerify(source, target, COPY_ATTRIBUTES); + source.delete(); + target.delete(); + + + // -- directory -- + + /* + * Test: copy directory, target does not exist + */ + source = createSourceDirectory(dir); + target = getTargetFile(dir); + copyAndVerify(source, target); + source.delete(); + target.delete(); + + /** + * Test: copy directory, target exists + */ + source = createSourceDirectory(dir); + target = getTargetFile(dir).createFile(); + try { + copyAndVerify(source, target); + throw new RuntimeException("FileAlreadyExistsException expected"); + } catch (FileAlreadyExistsException x) { + } + target.delete(); + target.createDirectory(); + try { + copyAndVerify(source, target); + throw new RuntimeException("FileAlreadyExistsException expected"); + } catch (FileAlreadyExistsException x) { + } + source.delete(); + target.delete(); + + /** + * Test: copy directory, target does not exist + */ + source = createSourceDirectory(dir); + target = getTargetFile(dir); + copyAndVerify(source, target, REPLACE_EXISTING); + source.delete(); + target.delete(); + + /** + * Test: copy directory, target exists + */ + source = createSourceDirectory(dir); + target = getTargetFile(dir).createFile(); + copyAndVerify(source, target, REPLACE_EXISTING); + source.delete(); + target.delete(); + + /** + * Test: copy directory, target exists and is empty directory + */ + source = createSourceDirectory(dir); + target = getTargetFile(dir).createDirectory(); + copyAndVerify(source, target, REPLACE_EXISTING); + source.delete(); + target.delete(); + + /** + * Test: copy directory, target exists and is non-empty directory + */ + source = createSourceDirectory(dir); + target = getTargetFile(dir).createDirectory(); + entry = target.resolve("foo").createFile(); + try { + copyAndVerify(source, target, REPLACE_EXISTING); + throw new RuntimeException("FileAlreadyExistsException expected"); + } catch (FileAlreadyExistsException x) { + } + entry.delete(); + source.delete(); + target.delete(); + + /* + * Test: copy directory + attributes + */ + source = createSourceDirectory(dir); + target = getTargetFile(dir); + copyAndVerify(source, target, COPY_ATTRIBUTES); + source.delete(); + target.delete(); + + // -- symbolic links -- + + /** + * Test: Follow link + */ + if (supportsLinks) { + source = createSourceFile(dir); + link = dir.resolve("link").createSymbolicLink(source); + target = getTargetFile(dir); + copyAndVerify(link, target); + link.delete(); + source.delete(); + } + + /** + * Test: Copy link (to file) + */ + if (supportsLinks) { + source = createSourceFile(dir); + link = dir.resolve("link").createSymbolicLink(source); + target = getTargetFile(dir); + copyAndVerify(link, target, NOFOLLOW_LINKS); + link.delete(); + source.delete(); + } + + /** + * Test: Copy link (to directory) + */ + if (supportsLinks) { + source = dir.resolve("mydir").createDirectory(); + link = dir.resolve("link").createSymbolicLink(source); + target = getTargetFile(dir); + copyAndVerify(link, target, NOFOLLOW_LINKS); + link.delete(); + source.delete(); + } + + /** + * Test: Copy broken link + */ + if (supportsLinks) { + assertTrue(source.notExists()); + link = dir.resolve("link").createSymbolicLink(source); + target = getTargetFile(dir); + copyAndVerify(link, target, NOFOLLOW_LINKS); + link.delete(); + } + + /** + * Test: Copy link to UNC (Windows only) + */ + if (supportsLinks && + System.getProperty("os.name").startsWith("Windows")) + { + Path unc = Paths.get("\\\\rialto\\share\\file"); + link = dir.resolve("link").createSymbolicLink(unc); + target = getTargetFile(dir); + copyAndVerify(link, target, NOFOLLOW_LINKS); + link.delete(); + } + + // -- misc. tests -- + + /** + * Test nulls + */ + source = createSourceFile(dir); + target = getTargetFile(dir); + try { + source.copyTo(null); + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException x) { } + try { + source.copyTo(target, (CopyOption[])null); + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException x) { } + try { + CopyOption[] opts = { REPLACE_EXISTING, null }; + source.copyTo(target, opts); + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException x) { } + source.delete(); + + /** + * Test UOE + */ + source = createSourceFile(dir); + target = getTargetFile(dir); + try { + source.copyTo(target, new CopyOption() { }); + } catch (UnsupportedOperationException x) { } + try { + source.copyTo(target, REPLACE_EXISTING, new CopyOption() { }); + } catch (UnsupportedOperationException x) { } + source.delete(); + } + + + static void assertTrue(boolean value) { + if (!value) + throw new RuntimeException("Assertion failed"); + } + + // computes simple hash of the given file + static int computeHash(Path file) throws IOException { + int h = 0; + + InputStream in = file.newInputStream(); + try { + byte[] buf = new byte[1024]; + int n; + do { + n = in.read(buf); + for (int i=0; i 0); + } finally { + in.close(); + } + return h; + } + + // create file of random size in given directory + static Path createSourceFile(Path dir) throws IOException { + String name = "source" + Integer.toString(rand.nextInt()); + Path file = dir.resolve(name).createFile(); + byte[] bytes = new byte[rand.nextInt(128*1024)]; + rand.nextBytes(bytes); + OutputStream out = file.newOutputStream(); + try { + out.write(bytes); + } finally { + out.close(); + } + randomizeAttributes(file); + return file; + } + + // create directory in the given directory + static Path createSourceDirectory(Path dir) throws IOException { + String name = "sourcedir" + Integer.toString(rand.nextInt()); + Path subdir = dir.resolve(name).createDirectory(); + randomizeAttributes(subdir); + return subdir; + } + + // "randomize" the file attributes of the given file. + static void randomizeAttributes(Path file) throws IOException { + String os = System.getProperty("os.name"); + boolean isWindows = os.startsWith("Windows"); + boolean isUnix = os.equals("SunOS") || os.equals("Linux"); + boolean isDirectory = Attributes.readBasicFileAttributes(file, NOFOLLOW_LINKS) + .isDirectory(); + + if (isUnix) { + Set perms = Attributes + .readPosixFileAttributes(file, NOFOLLOW_LINKS).permissions(); + PosixFilePermission[] toChange = { + PosixFilePermission.GROUP_READ, + PosixFilePermission.GROUP_WRITE, + PosixFilePermission.GROUP_EXECUTE, + PosixFilePermission.OTHERS_READ, + PosixFilePermission.OTHERS_WRITE, + PosixFilePermission.OTHERS_EXECUTE + }; + for (PosixFilePermission perm: toChange) { + if (heads()) { + perms.add(perm); + } else { + perms.remove(perm); + } + } + Attributes.setPosixFilePermissions(file, perms); + } + + if (isWindows) { + DosFileAttributeView view = file + .getFileAttributeView(DosFileAttributeView.class, NOFOLLOW_LINKS); + // only set or unset the hidden attribute + view.setHidden(heads()); + } + + boolean addUserDefinedFileAttributes = heads() && + file.getFileStore().supportsFileAttributeView("xattr"); + + // remove this when copying a direcory copies its named streams + if (isWindows && isDirectory) addUserDefinedFileAttributes = false; + + if (addUserDefinedFileAttributes) { + UserDefinedFileAttributeView view = file + .getFileAttributeView(UserDefinedFileAttributeView.class); + int n = rand.nextInt(16); + while (n > 0) { + byte[] value = new byte[1 + rand.nextInt(100)]; + view.write("user." + Integer.toString(n), ByteBuffer.wrap(value)); + n--; + } + } + } + + // create name for file in given directory + static Path getTargetFile(Path dir) throws IOException { + String name = "target" + Integer.toString(rand.nextInt()); + return dir.resolve(name); + } + } diff --git a/test/java/nio/file/Path/DeleteOnClose.java b/test/java/nio/file/Path/DeleteOnClose.java new file mode 100644 index 000000000..8bc0e852c --- /dev/null +++ b/test/java/nio/file/Path/DeleteOnClose.java @@ -0,0 +1,77 @@ +/* + * 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. + */ + +import java.nio.file.*; +import static java.nio.file.StandardOpenOption.*; +import java.io.*; +import java.util.*; + +public class DeleteOnClose { + + public static void main(String[] args) throws IOException { + // open file but do not close it. Its existance will be checked by + // the calling script. + Paths.get(args[0]).newByteChannel(READ, WRITE, DELETE_ON_CLOSE); + + // check temporary file has been deleted after closing it + Path file = File.createTempFile("blah", "tmp").toPath(); + file.newByteChannel(READ, WRITE, DELETE_ON_CLOSE).close(); + if (file.exists()) + throw new RuntimeException("Temporary file was not deleted"); + + Path dir = TestUtil.createTemporaryDirectory(); + try { + // check that DELETE_ON_CLOSE fails when file is a sym link + if (TestUtil.supportsLinks(dir)) { + file = dir.resolve("foo").createFile(); + Path link = dir.resolve("link").createSymbolicLink(file); + try { + link.newByteChannel(READ, WRITE, DELETE_ON_CLOSE); + throw new RuntimeException("IOException expected"); + } catch (IOException ignore) { } + } + + // check that DELETE_ON_CLOSE works with files created via open + // directories + DirectoryStream stream = dir.newDirectoryStream(); + try { + if (stream instanceof SecureDirectoryStream) { + SecureDirectoryStream secure = (SecureDirectoryStream)stream; + file = Paths.get("foo"); + + Set opts = new HashSet(); + opts.add(WRITE); + opts.add(DELETE_ON_CLOSE); + secure.newByteChannel(file, opts).close(); + + if (dir.resolve(file).exists()) + throw new RuntimeException("File not deleted"); + } + } finally { + stream.close(); + } + } finally { + TestUtil.removeAll(dir); + } + } +} diff --git a/test/java/nio/file/Path/InterruptCopy.java b/test/java/nio/file/Path/InterruptCopy.java new file mode 100644 index 000000000..d7962224e --- /dev/null +++ b/test/java/nio/file/Path/InterruptCopy.java @@ -0,0 +1,119 @@ +/* + * 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 4313887 + * @summary Unit test for Sun-specific ExtendedCopyOption.INTERRUPTIBLE option + * @library .. + * @run main/othervm -XX:-UseVMInterruptibleIO InterruptCopy + */ + +import java.nio.file.*; +import java.nio.file.attribute.Attributes; +import java.io.*; +import java.util.concurrent.*; +import com.sun.nio.file.ExtendedCopyOption; + +public class InterruptCopy { + + private static final long FILE_SIZE_TO_COPY = 512 * 1024 * 1024; + private static final int DELAY_IN_MS = 500; + + public static void main(String[] args) throws Exception { + Path dir = TestUtil.createTemporaryDirectory(); + try { + FileStore store = dir.getFileStore(); + System.out.format("Checking space (%s)\n", store); + long usableSpace = Attributes + .readFileStoreSpaceAttributes(store).usableSpace(); + if (usableSpace < 2*FILE_SIZE_TO_COPY) { + System.out.println("Insufficient disk space to run test."); + return; + } + doTest(dir); + } finally { + TestUtil.removeAll(dir); + } + } + + static void doTest(Path dir) throws Exception { + final Path source = dir.resolve("foo"); + final Path target = dir.resolve("bar"); + + // create source file (don't create it as sparse file because we + // require the copy to take a long time) + System.out.println("Creating source file..."); + byte[] buf = new byte[32*1024]; + long total = 0; + OutputStream out = source.newOutputStream(); + try { + do { + out.write(buf); + total += buf.length; + } while (total < FILE_SIZE_TO_COPY); + } finally { + out.close(); + } + System.out.println("Source file created."); + + ScheduledExecutorService pool = + Executors.newSingleThreadScheduledExecutor(); + try { + // copy source to target in main thread, interrupting it after a delay + final Thread me = Thread.currentThread(); + pool.schedule(new Runnable() { + public void run() { + me.interrupt(); + }}, DELAY_IN_MS, TimeUnit.MILLISECONDS); + System.out.println("Copying file..."); + try { + source.copyTo(target, ExtendedCopyOption.INTERRUPTIBLE); + throw new RuntimeException("Copy completed (this is not expected)"); + } catch (IOException e) { + boolean interrupted = Thread.interrupted(); + if (!interrupted) + throw new RuntimeException("Interrupt status was not set"); + System.out.println("Copy failed (this is expected)"); + } + + // copy source to target via task in thread pool, interrupting it after + // a delay using cancel(true) + Future result = pool.submit(new Callable() { + public Void call() throws IOException { + System.out.println("Copying file..."); + source.copyTo(target, ExtendedCopyOption.INTERRUPTIBLE, + StandardCopyOption.REPLACE_EXISTING); + return null; + } + }); + Thread.sleep(DELAY_IN_MS); + boolean cancelled = result.cancel(true); + if (!cancelled) + result.get(); + System.out.println("Copy cancelled."); + } finally { + pool.shutdown(); + pool.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + } + } +} diff --git a/test/java/nio/file/Path/Links.java b/test/java/nio/file/Path/Links.java new file mode 100644 index 000000000..3b0d6daea --- /dev/null +++ b/test/java/nio/file/Path/Links.java @@ -0,0 +1,147 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.Path createSymbolicLink, + * readSymbolicLink, and createLink methods + * @library .. + */ + +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.io.*; +import java.util.*; + +public class Links { + + static final boolean isWindows = + System.getProperty("os.name").startsWith("Windows"); + + static void assertTrue(boolean okay) { + if (!okay) + throw new RuntimeException("Assertion failed"); + } + + /** + * Exercise createSymbolicLink and readLink methods + */ + static void testSymLinks(Path dir) throws IOException { + Path link = dir.resolve("link"); + + // Check if sym links are supported + try { + link.createSymbolicLink(Paths.get("foo")); + link.delete(); + } catch (UnsupportedOperationException x) { + // sym links not supported + return; + } catch (IOException x) { + // probably insufficient privileges to create sym links (Windows) + return; + } + + // Test links to various targets + String[] windowsTargets = + { "foo", "C:\\foo", "\\foo", "\\\\server\\share\\foo" }; + String[] otherTargets = { "relative", "/absolute" }; + + String[] targets = (isWindows) ? windowsTargets : otherTargets; + for (String s: targets) { + Path target = Paths.get(s); + link.createSymbolicLink(target); + try { + assertTrue(link.readSymbolicLink().equals(target)); + } finally { + link.delete(); + } + } + } + + /** + * Exercise createLink method + */ + static void testHardLinks(Path dir) throws IOException { + Path foo = dir.resolve("foo").createFile(); + try { + Path bar; + try { + bar = dir.resolve("bar").createLink(foo); + } catch (UnsupportedOperationException x) { + return; + } catch (IOException x) { + // probably insufficient privileges (Windows) + return; + } + try { + Object key1 = Attributes + .readBasicFileAttributes(foo).fileKey(); + Object key2 = Attributes + .readBasicFileAttributes(bar).fileKey(); + assertTrue((key1 == null) || (key1.equals(key2))); + +// Testing of linkCount disabled until linkCount method removed frmo +// BasicFileAttributes +/* + assertTrue(Attributes + .readBasicFileAttributes(foo).linkCount() >= 2); + assertTrue(Attributes + .readBasicFileAttributes(bar).linkCount() >= 2); +*/ + + } finally { + bar.delete(); + } + + + } finally { + foo.delete(); + } + } + + public static void main(String[] args) throws IOException { + Path dir = TestUtil.createTemporaryDirectory(); + try { + testSymLinks(dir); + testHardLinks(dir); + + // repeat tests on Windows with long path + if (isWindows) { + Path dirWithLongPath = null; + try { + dirWithLongPath = TestUtil.createDirectoryWithLongPath(dir); + } catch (IOException x) { + System.out.println("Unable to create long path: " + x); + } + if (dirWithLongPath != null) { + System.out.println(""); + System.out.println("** REPEAT TESTS WITH LONG PATH **"); + testSymLinks(dirWithLongPath); + testHardLinks(dirWithLongPath); + } + } + } finally { + TestUtil.removeAll(dir); + } + } +} diff --git a/test/java/nio/file/Path/Misc.java b/test/java/nio/file/Path/Misc.java new file mode 100644 index 000000000..ba6640f7f --- /dev/null +++ b/test/java/nio/file/Path/Misc.java @@ -0,0 +1,389 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.Path for miscellenous methods not + * covered by other tests + * @library .. + */ + +import java.nio.file.*; +import static java.nio.file.LinkOption.*; +import java.nio.file.attribute.*; +import java.io.*; +import java.util.*; + +public class Misc { + static final boolean isWindows = + System.getProperty("os.name").startsWith("Windows"); + static boolean supportsLinks; + + public static void main(String[] args) throws IOException { + Path dir = TestUtil.createTemporaryDirectory(); + try { + supportsLinks = TestUtil.supportsLinks(dir); + + // equals and hashCode methods + equalsAndHashCode(); + + // checkAccess method + checkAccessTests(dir); + + // getFileAttributeView methods + getFileAttributeViewTests(dir); + + // toRealPath method + toRealPathTests(dir); + + // isSameFile method + isSameFileTests(dir); + + // isHidden method + isHiddenTests(dir); + + } finally { + TestUtil.removeAll(dir); + } + } + + /** + * Exercise equals and hashCode methods + */ + static void equalsAndHashCode() { + + Path thisFile = Paths.get("this"); + Path thatFile = Paths.get("that"); + + assertTrue(thisFile.equals(thisFile)); + assertTrue(!thisFile.equals(thatFile)); + + assertTrue(!thisFile.equals(null)); + assertTrue(!thisFile.equals(new Object())); + + Path likeThis = Paths.get("This"); + if (isWindows) { + // case insensitive + assertTrue(thisFile.equals(likeThis)); + assertTrue(thisFile.hashCode() == likeThis.hashCode()); + } else { + // case senstive + assertTrue(!thisFile.equals(likeThis)); + } + } + + /** + * Exercise checkAccess method + */ + static void checkAccessTests(Path dir) throws IOException { + final Path file = dir.resolve("foo").createFile(); + + /** + * Test: This directory should readable and writable + */ + dir.checkAccess(); + dir.checkAccess(AccessMode.READ); + dir.checkAccess(AccessMode.WRITE); + dir.checkAccess(AccessMode.READ, AccessMode.WRITE); + + /** + * Test: File does not exist + */ + Path doesNotExist = dir.resolve("thisDoesNotExists"); + try { + doesNotExist.checkAccess(); + throw new RuntimeException("NoSuchFileException expected"); + } catch (NoSuchFileException x) { + } + try { + doesNotExist.checkAccess(AccessMode.READ); + throw new RuntimeException("NoSuchFileException expected"); + } catch (NoSuchFileException x) { + } + try { + doesNotExist.checkAccess(AccessMode.WRITE); + throw new RuntimeException("NoSuchFileException expected"); + } catch (NoSuchFileException x) { + } + try { + doesNotExist.checkAccess(AccessMode.EXECUTE); + throw new RuntimeException("NoSuchFileException expected"); + } catch (NoSuchFileException x) { + } + + /** + * Test: Edit ACL to deny WRITE and EXECUTE + */ + AclFileAttributeView view = file + .getFileAttributeView(AclFileAttributeView.class); + if (view != null && + file.getFileStore().supportsFileAttributeView("acl")) + { + UserPrincipal owner = view.getOwner(); + List acl = view.getAcl(); + + // Insert entry to deny WRITE and EXECUTE + AclEntry entry = AclEntry.newBuilder() + .setType(AclEntryType.DENY) + .setPrincipal(owner) + .setPermissions(AclEntryPermission.WRITE_DATA, + AclEntryPermission.EXECUTE) + .build(); + acl.add(0, entry); + view.setAcl(acl); + + try { + file.checkAccess(AccessMode.WRITE); + throw new RuntimeException("AccessDeniedException expected"); + } catch (AccessDeniedException x) { + } + + try { + file.checkAccess(AccessMode.EXECUTE); + throw new RuntimeException("AccessDeniedException expected"); + } catch (AccessDeniedException x) { + } + + + // Restore ACL + acl.remove(0); + view.setAcl(acl); + } + + /** + * Test: Windows DOS read-only attribute + */ + if (isWindows) { + DosFileAttributeView dview = + file.getFileAttributeView(DosFileAttributeView.class); + dview.setReadOnly(true); + try { + file.checkAccess(AccessMode.WRITE); + throw new RuntimeException("AccessDeniedException expected"); + } catch (AccessDeniedException x) { + } + dview.setReadOnly(false); + + // Read-only attribute does not make direcory read-only + dview = dir.getFileAttributeView(DosFileAttributeView.class); + boolean save = dview.readAttributes().isReadOnly(); + dview.setReadOnly(true); + dir.checkAccess(AccessMode.WRITE); + dview.setReadOnly(save); + } + + /** + * Test: null + */ + try { + file.checkAccess((AccessMode)null); + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException ignore) { } + + // clean-up + file.delete(); + } + + /** + * Exercise getFileAttributeFile methods + */ + static void getFileAttributeViewTests(Path dir) { + assertTrue(dir.getFileAttributeView(BasicFileAttributeView.class) + instanceof BasicFileAttributeView); + assertTrue(dir.getFileAttributeView(BasicFileAttributeView.class, NOFOLLOW_LINKS) + instanceof BasicFileAttributeView); + assertTrue(dir.getFileAttributeView("basic") + instanceof BasicFileAttributeView); + assertTrue(dir.getFileAttributeView("basic", NOFOLLOW_LINKS) + instanceof BasicFileAttributeView); + assertTrue(dir.getFileAttributeView(BogusFileAttributeView.class) == null); + assertTrue(dir.getFileAttributeView("bogus") == null); + try { + dir.getFileAttributeView((Class)null); + } catch (NullPointerException ignore) { } + try { + dir.getFileAttributeView(BasicFileAttributeView.class, (LinkOption[])null); + } catch (NullPointerException ignore) { } + try { + dir.getFileAttributeView(BasicFileAttributeView.class, (LinkOption)null); + } catch (NullPointerException ignore) { } + try { + dir.getFileAttributeView((String)null); + } catch (NullPointerException ignore) { } + try { + dir.getFileAttributeView("basic", (LinkOption[])null); + } catch (NullPointerException ignore) { } + try { + dir.getFileAttributeView("basic", (LinkOption)null); + } catch (NullPointerException ignore) { } + + } + interface BogusFileAttributeView extends FileAttributeView { } + + /** + * Exercise toRealPath method + */ + static void toRealPathTests(Path dir) throws IOException { + final Path file = dir.resolve("foo").createFile(); + final Path link = dir.resolve("link"); + + /** + * Test: toRealPath(true) will access same file as toRealPath(false) + */ + assertTrue(file.toRealPath(true).isSameFile(file.toRealPath(false))); + + /** + * Test: toRealPath(true) should resolve links + */ + if (supportsLinks) { + link.createSymbolicLink(file.toAbsolutePath()); + assertTrue(link.toRealPath(true).equals(file.toRealPath(true))); + link.delete(); + } + + + /** + * Test: toRealPath(false) should not resolve links + */ + if (supportsLinks) { + link.createSymbolicLink(file.toAbsolutePath()); + assertTrue(link.toRealPath(false).getName().equals(link.getName())); + link.delete(); + } + + /** + * Test: toRealPath should eliminate "." + */ + assertTrue(dir.resolve(".").toRealPath(true).equals(dir.toRealPath(true))); + assertTrue(dir.resolve(".").toRealPath(false).equals(dir.toRealPath(false))); + + /** + * Test: toRealPath should eliminate ".." when it doesn't follow a + * symbolic link + */ + Path subdir = dir.resolve("subdir").createDirectory(); + assertTrue(subdir.resolve("..").toRealPath(true).equals(dir.toRealPath(true))); + assertTrue(subdir.resolve("..").toRealPath(false).equals(dir.toRealPath(false))); + subdir.delete(); + + // clean-up + file.delete(); + } + + /** + * Exercise isSameFile method + */ + static void isSameFileTests(Path dir) throws IOException { + Path thisFile = dir.resolve("thisFile"); + Path thatFile = dir.resolve("thatFile"); + + /** + * Test: isSameFile for self and null + */ + assertTrue(thisFile.isSameFile(thisFile)); + assertTrue(!thisFile.isSameFile(null)); + + /** + * Test: Neither files exist + */ + try { + thisFile.isSameFile(thatFile); + throw new RuntimeException("IOException not thrown"); + } catch (IOException x) { + } + try { + thatFile.isSameFile(thisFile); + throw new RuntimeException("IOException not thrown"); + } catch (IOException x) { + } + + thisFile.createFile(); + try { + /** + * Test: One file exists + */ + try { + thisFile.isSameFile(thatFile); + throw new RuntimeException("IOException not thrown"); + } catch (IOException x) { + } + try { + thatFile.isSameFile(thisFile); + throw new RuntimeException("IOException not thrown"); + } catch (IOException x) { + } + + thatFile.createFile(); + + /** + * Test: Both file exists + */ + try { + assertTrue(!thisFile.isSameFile(thatFile)); + assertTrue(!thatFile.isSameFile(thisFile)); + } finally { + TestUtil.deleteUnchecked(thatFile); + } + + /** + * Test: Symbolic links + */ + if (supportsLinks) { + thatFile.createSymbolicLink(thisFile); + try { + assertTrue(thisFile.isSameFile(thatFile)); + assertTrue(thatFile.isSameFile(thisFile)); + } finally { + TestUtil.deleteUnchecked(thatFile); + } + } + } finally { + thisFile.delete(false); + } + } + + /** + * Exercise isHidden method + */ + static void isHiddenTests(Path dir) throws IOException { + assertTrue(!dir.isHidden()); + + Path file = dir.resolve(".foo"); + if (isWindows) { + file.createFile(); + try { + Attributes.setAttribute(file, "dos:hidden", true); + assertTrue(file.isHidden()); + } finally { + file.delete(); + } + } else { + assertTrue(file.isHidden()); + } + } + + static void assertTrue(boolean okay) { + if (!okay) + throw new RuntimeException("Assertion Failed"); + } +} diff --git a/test/java/nio/file/Path/PathOps.java b/test/java/nio/file/Path/PathOps.java new file mode 100644 index 000000000..231123c7d --- /dev/null +++ b/test/java/nio/file/Path/PathOps.java @@ -0,0 +1,752 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.Path path operations + */ + +import java.nio.file.*; + +public class PathOps { + + static final java.io.PrintStream out = System.out; + + private String input; + private Path path; + private Exception exc; + + private PathOps(String s) { + out.println(); + input = s; + try { + path = FileSystems.getDefault().getPath(s); + out.format("%s -> %s", s, path); + } catch (Exception x) { + exc = x; + out.format("%s -> %s", s, x); + } + out.println(); + } + + Path path() { + return path; + } + + void fail() { + throw new RuntimeException("PathOps failed"); + } + + void checkPath() { + if (path == null) { + throw new InternalError("path is null"); + } + } + + void check(Object result, String expected) { + out.format("\tExpected: %s\n", expected); + out.format("\tActual: %s\n", result); + if (result == null) { + if (expected == null) return; + } else { + // compare string representations + if (expected != null && result.toString().equals(expected.toString())) + return; + } + fail(); + } + + void check(Object result, boolean expected) { + check(result, Boolean.toString(expected)); + } + + PathOps root(String expected) { + out.println("check root"); + checkPath(); + check(path.getRoot(), expected); + return this; + } + + PathOps parent(String expected) { + out.println("check parent"); + checkPath(); + check(path.getParent(), expected); + return this; + } + + PathOps name(String expected) { + out.println("check name"); + checkPath(); + check(path.getName(), expected); + return this; + } + + PathOps element(int index, String expected) { + out.format("check element %d\n", index); + checkPath(); + check(path.getName(index), expected); + return this; + } + + PathOps subpath(int startIndex, int endIndex, String expected) { + out.format("test subpath(%d,%d)\n", startIndex, endIndex); + checkPath(); + check(path.subpath(startIndex, endIndex), expected); + return this; + } + + PathOps starts(String prefix) { + out.format("test startsWith with %s\n", prefix); + checkPath(); + Path s = FileSystems.getDefault().getPath(prefix); + check(path.startsWith(s), true); + return this; + } + + PathOps notStarts(String prefix) { + out.format("test not startsWith with %s\n", prefix); + checkPath(); + Path s = FileSystems.getDefault().getPath(prefix); + check(path.startsWith(s), false); + return this; + } + + PathOps ends(String suffix) { + out.format("test endsWith %s\n", suffix); + checkPath(); + Path s = FileSystems.getDefault().getPath(suffix); + check(path.endsWith(s), true); + return this; + } + + PathOps notEnds(String suffix) { + out.format("test not endsWith %s\n", suffix); + checkPath(); + Path s = FileSystems.getDefault().getPath(suffix); + check(path.endsWith(s), false); + return this; + } + + PathOps absolute() { + out.println("check path is absolute"); + checkPath(); + check(path.isAbsolute(), true); + return this; + } + + PathOps notAbsolute() { + out.println("check path is not absolute"); + checkPath(); + check(path.isAbsolute(), false); + return this; + } + + PathOps resolve(String other, String expected) { + out.format("test resolve %s\n", other); + checkPath(); + check(path.resolve(other), expected); + return this; + } + + PathOps relativize(String other, String expected) { + out.format("test relativize %s\n", other); + checkPath(); + Path that = FileSystems.getDefault().getPath(other); + check(path.relativize(that), expected); + return this; + } + + PathOps normalize(String expected) { + out.println("check normalized path"); + checkPath(); + check(path.normalize(), expected); + return this; + } + + PathOps string(String expected) { + out.println("check string representation"); + checkPath(); + check(path, expected); + return this; + } + + PathOps invalid() { + if (!(exc instanceof InvalidPathException)) { + out.println("InvalidPathException not thrown as expected"); + fail(); + } + return this; + } + + static PathOps test(String s) { + return new PathOps(s); + } + + // -- PathOpss -- + + static void header(String s) { + out.println(); + out.println(); + out.println("-- " + s + " --"); + } + + static void doWindowsTests() { + header("Windows specific tests"); + + // all components present + test("C:\\a\\b\\c") + .root("C:\\") + .parent("C:\\a\\b") + .name("c"); + test("C:a\\b\\c") + .root("C:") + .parent("C:a\\b") + .name("c"); + test("\\\\server\\share\\a") + .root("\\\\server\\share\\") + .parent("\\\\server\\share\\") + .name("a"); + + // root component only + test("C:\\") + .root("C:\\") + .parent(null) + .name(null); + test("C:") + .root("C:") + .parent(null) + .name(null); + test("\\\\server\\share\\") + .root("\\\\server\\share\\") + .parent(null) + .name(null); + + // no root component + test("a\\b") + .root(null) + .parent("a") + .name("b"); + + // name component only + test("foo") + .root(null) + .parent(null) + .name("foo"); + + // startsWith + test("C:\\") + .starts("C:\\") + .starts("c:\\") + .notStarts("C") + .notStarts("C:"); + test("C:") + .starts("C:") + .starts("c:") + .notStarts("C"); + test("\\") + .starts("\\"); + test("C:\\foo\\bar") + .starts("C:\\") + .starts("C:\\foo") + .starts("C:\\FOO") + .starts("C:\\foo\\bar") + .starts("C:\\Foo\\Bar") + .notStarts("C:") + .notStarts("C") + .notStarts("C:foo"); + test("\\foo\\bar") + .starts("\\") + .starts("\\foo") + .starts("\\foO") + .starts("\\foo\\bar") + .starts("\\fOo\\BaR") + .notStarts("foo") + .notStarts("foo\\bar"); + test("foo\\bar") + .starts("foo") + .starts("foo\\bar") + .notStarts("\\"); + test("\\\\server\\share") + .starts("\\\\server\\share") + .starts("\\\\server\\share\\") + .notStarts("\\"); + + // endsWith + test("C:\\") + .ends("C:\\") + .ends("c:\\") + .notEnds("\\"); + test("C:") + .ends("C:") + .ends("c:"); + test("\\") + .ends("\\"); + test("C:\\foo\\bar") + .ends("bar") + .ends("BAR") + .ends("foo\\bar") + .ends("Foo\\Bar") + .ends("C:\\foo\\bar") + .ends("c:\\foO\\baR") + .notEnds("r") + .notEnds("\\foo\\bar"); + test("\\foo\\bar") + .ends("bar") + .ends("BaR") + .ends("foo\\bar") + .ends("foO\\baR") + .ends("\\foo\\bar") + .ends("\\Foo\\Bar") + .notEnds("oo\\bar"); + test("foo\\bar") + .ends("bar") + .ends("BAR") + .ends("foo\\bar") + .ends("Foo\\Bar") + .notEnds("ar"); + test("\\\\server\\share") + .ends("\\\\server\\share") + .ends("\\\\server\\share\\") + .notEnds("shared") + .notEnds("\\"); + + // elements + test("C:\\a\\b\\c") + .element(0, "a") + .element(1, "b") + .element(2, "c"); + test("foo.bar\\gus.alice") + .element(0, "foo.bar") + .element(1, "gus.alice"); + + // subpath + test("C:\\foo") + .subpath(0, 1, "foo"); + test("C:foo") + .subpath(0, 1, "foo"); + test("foo") + .subpath(0, 1, "foo"); + test("C:\\foo\\bar\\gus") + .subpath(0, 1, "foo") + .subpath(0, 2, "foo\\bar") + .subpath(0, 3, "foo\\bar\\gus") + .subpath(1, 2, "bar") + .subpath(1, 3, "bar\\gus") + .subpath(2, 3, "gus"); + test("\\\\server\\share\\foo") + .subpath(0, 1, "foo"); + + // isAbsolute + test("foo").notAbsolute(); + test("C:").notAbsolute(); + test("C:\\").absolute(); + test("C:\\abc").absolute(); + test("\\\\server\\share\\").absolute(); + + // resolve + test("C:\\") + .resolve("foo", "C:\\foo") + .resolve("D:\\bar", "D:\\bar") + .resolve("\\\\server\\share\\bar", "\\\\server\\share\\bar") + .resolve("C:foo", "C:\\foo") + .resolve("D:foo", "D:foo"); + test("\\") + .resolve("foo", "\\foo") + .resolve("D:bar", "D:bar") + .resolve("C:\\bar", "C:\\bar") + .resolve("\\\\server\\share\\bar", "\\\\server\\share\\bar") + .resolve("\\foo", "\\foo"); + test("\\foo") + .resolve("bar", "\\foo\\bar") + .resolve("D:bar", "D:bar") + .resolve("C:\\bar", "C:\\bar") + .resolve("\\\\server\\share\\bar", "\\\\server\\share\\bar") + .resolve("\\bar", "\\bar"); + test("foo") + .resolve("bar", "foo\\bar") + .resolve("D:\\bar", "D:\\bar") + .resolve("\\\\server\\share\\bar", "\\\\server\\share\\bar") + .resolve("C:bar", "C:bar") + .resolve("D:foo", "D:foo"); + test("C:") + .resolve("foo", "C:foo"); + test("\\\\server\\share\\foo") + .resolve("bar", "\\\\server\\share\\foo\\bar") + .resolve("\\bar", "\\\\server\\share\\bar") + .resolve("D:\\bar", "D:\\bar") + .resolve("\\\\other\\share\\bar", "\\\\other\\share\\bar") + .resolve("D:bar", "D:bar"); + + // relativize + test("foo\\bar") + .relativize("foo\\bar", null) + .relativize("foo", ".."); + test("C:\\a\\b\\c") + .relativize("C:\\a", "..\\.."); + test("\\\\server\\share\\foo") + .relativize("\\\\server\\share\\bar", "..\\bar"); + + // normalize + test("C:\\") + .normalize("C:\\"); + test("C:\\.") + .normalize("C:\\"); + test("C:\\..") + .normalize("C:\\"); + test("\\\\server\\share") + .normalize("\\\\server\\share\\"); + test("\\\\server\\share\\.") + .normalize("\\\\server\\share\\"); + test("\\\\server\\share\\..") + .normalize("\\\\server\\share\\"); + test("C:") + .normalize("C:"); + test("C:.") + .normalize("C:"); + test("C:..") + .normalize("C:.."); + test("\\") + .normalize("\\"); + test("\\.") + .normalize("\\"); + test("\\..") + .normalize("\\"); + test("foo") + .normalize("foo"); + test("foo\\.") + .normalize("foo"); + test("foo\\..") + .normalize(null); + test("C:\\foo") + .normalize("C:\\foo"); + test("C:\\foo\\.") + .normalize("C:\\foo"); + test("C:\\.\\foo") + .normalize("C:\\foo"); + test("C:\\foo\\..") + .normalize("C:\\"); + test("C:\\..\\foo") + .normalize("C:\\foo"); + test("\\\\server\\share\\foo") + .normalize("\\\\server\\share\\foo"); + test("\\\\server\\share\\foo\\.") + .normalize("\\\\server\\share\\foo"); + test("\\\\server\\share\\.\\foo") + .normalize("\\\\server\\share\\foo"); + test("\\\\server\\share\\foo\\..") + .normalize("\\\\server\\share\\"); + test("\\\\server\\share\\..\\foo") + .normalize("\\\\server\\share\\foo"); + test("C:foo") + .normalize("C:foo"); + test("C:foo\\.") + .normalize("C:foo"); + test("C:.\\foo") + .normalize("C:foo"); + test("C:foo\\..") + .normalize("C:"); + test("C:..\\foo") + .normalize("C:..\\foo"); + test("\\foo") + .normalize("\\foo"); + test("\\foo\\.") + .normalize("\\foo"); + test("\\.\\foo") + .normalize("\\foo"); + test("\\foo\\..") + .normalize("\\"); + test("\\..\\foo") + .normalize("\\foo"); + test(".") + .normalize(null); + test("..") + .normalize(".."); + test("\\..\\..") + .normalize("\\"); + test("..\\..\\foo") + .normalize("..\\..\\foo"); + test("foo\\bar\\..") + .normalize("foo"); + test("foo\\bar\\.\\..") + .normalize("foo"); + test("foo\\bar\\gus\\..\\..") + .normalize("foo"); + test(".\\foo\\.\\bar\\.\\gus\\..\\.\\..") + .normalize("foo"); + + // UNC corner cases + test("\\\\server\\share\\") + .root("\\\\server\\share\\") + .parent(null) + .name(null); + test("\\\\server") + .invalid(); + test("\\\\server\\") + .invalid(); + test("\\\\server\\share") + .root("\\\\server\\share\\") + .parent(null) + .name(null); + + // invalid + test(":\\foo") + .invalid(); + test("C::") + .invalid(); + test("C:\\?") // invalid character + .invalid(); + test("C:\\*") // invalid character + .invalid(); + test("C:\\abc\u0001\\foo") + .invalid(); + test("C:\\\u0019\\foo") + .invalid(); + test("\\\\server\u0019\\share") + .invalid(); + test("\\\\server\\share\u0019") + .invalid(); + test("foo\u0000\bar") + .invalid(); + test("C:\\foo ") // trailing space + .invalid(); + test("C:\\foo \\bar") + .invalid(); + //test("C:\\foo.") // trailing dot + //.invalid(); + //test("C:\\foo...\\bar") + //.invalid(); + + // normalization at construction time (remove redundant and replace slashes) + test("C:/a/b/c") + .string("C:\\a\\b\\c") + .root("C:\\") + .parent("C:\\a\\b"); + test("C://a//b//c") + .string("C:\\a\\b\\c") + .root("C:\\") + .parent("C:\\a\\b"); + + // hashCode + header("hashCode"); + int h1 = test("C:\\foo").path().hashCode(); + int h2 = test("c:\\FOO").path().hashCode(); + if (h1 != h2) + throw new RuntimeException("PathOps failed"); + } + + static void doUnixTests() { + header("Unix specific tests"); + + // all components + test("/a/b/c") + .root("/") + .parent("/a/b") + .name("c"); + + // root component only + test("/") + .root("/") + .parent(null) + .name(null); + + // no root component + test("a/b") + .root(null) + .parent("a") + .name("b"); + + // name component only + test("foo") + .root(null) + .parent(null) + .name("foo"); + + // startsWith + test("/") + .starts("/") + .notStarts("/foo"); + test("/foo") + .starts("/") + .starts("/foo") + .notStarts("/f"); + test("/foo/bar") + .starts("/") + .starts("/foo") + .starts("/foo/bar") + .notStarts("/f") + .notStarts("foo") + .notStarts("foo/bar"); + test("foo") + .starts("foo") + .notStarts("f"); + test("foo/bar") + .starts("foo") + .starts("foo/bar") + .notStarts("f") + .notStarts("/foo") + .notStarts("/foo/bar"); + + // endsWith + test("/") + .ends("/") + .notEnds("foo") + .notEnds("/foo"); + test("/foo") + .ends("foo") + .ends("/foo") + .notEnds("/"); + test("/foo/bar") + .ends("bar") + .ends("foo/bar") + .ends("/foo/bar") + .notEnds("/bar"); + test("foo") + .ends("foo"); + test("foo/bar") + .ends("bar") + .ends("foo/bar"); + + // elements + test("a/b/c") + .element(0,"a") + .element(1,"b") + .element(2,"c"); + + // isAbsolute + test("/") + .absolute(); + test("/tmp") + .absolute(); + test("tmp") + .notAbsolute(); + + // resolve + test("/tmp") + .resolve("foo", "/tmp/foo") + .resolve("/foo", "/foo"); + test("tmp") + .resolve("foo", "tmp/foo") + .resolve("/foo", "/foo"); + + // relativize + test("/a/b/c") + .relativize("/a/b/c", null) + .relativize("/a/b/c/d/e", "d/e") + .relativize("/a/x", "../../x"); + + // normalize + test("/") + .normalize("/"); + test("foo") + .normalize("foo"); + test("/foo") + .normalize("/foo"); + test(".") + .normalize(null); + test("..") + .normalize(".."); + test("/..") + .normalize("/"); + test("/../..") + .normalize("/"); + test("foo/.") + .normalize("foo"); + test("./foo") + .normalize("foo"); + test("foo/..") + .normalize(null); + test("../foo") + .normalize("../foo"); + test("../../foo") + .normalize("../../foo"); + test("foo/bar/..") + .normalize("foo"); + test("foo/bar/gus/../..") + .normalize("foo"); + test("/foo/bar/gus/../..") + .normalize("/foo"); + + // invalid + test("foo\u0000\bar") + .invalid(); + + // normalization + test("//foo//bar") + .string("/foo/bar") + .root("/") + .parent("/foo") + .name("bar"); + } + + static void npes() { + header("NullPointerException"); + + Path path = FileSystems.getDefault().getPath("foo"); + + try { + path.resolve((String)null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException npe) { + } + + try { + path.relativize(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException npe) { + } + + try { + path.compareTo(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException npe) { + } + + try { + path.startsWith(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException npe) { + } + + try { + path.endsWith(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException npe) { + } + + } + + public static void main(String[] args) { + // all platforms + npes(); + + // operating system specific + String osname = System.getProperty("os.name"); + if (osname.startsWith("Windows")) { + doWindowsTests(); + } + if (osname.equals("SunOS") || osname.equals("Linux")) { + doUnixTests(); + } + + } +} diff --git a/test/java/nio/file/Path/SBC.java b/test/java/nio/file/Path/SBC.java new file mode 100644 index 000000000..724c8706a --- /dev/null +++ b/test/java/nio/file/Path/SBC.java @@ -0,0 +1,468 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.Path.newByteChannel + * @library .. + */ + +import java.nio.ByteBuffer; +import java.nio.file.*; +import static java.nio.file.StandardOpenOption.*; +import static com.sun.nio.file.ExtendedOpenOption.*; +import java.nio.file.attribute.FileAttribute; +import java.nio.channels.*; +import java.io.IOException; +import java.util.*; + +public class SBC { + + static boolean supportsLinks; + + public static void main(String[] args) throws Exception { + Path dir = TestUtil.createTemporaryDirectory(); + try { + supportsLinks = TestUtil.supportsLinks(dir); + + // open options + createTests(dir); + appendTests(dir); + truncateExistingTests(dir); + noFollowLinksTests(dir); + + // SeekableByteChannel methods + sizeTruncatePositionTests(dir); + + // platform specific + if (System.getProperty("os.name").startsWith("Windows")) + dosSharingOptionTests(dir); + + // misc. tests + badCombinations(dir); + unsupportedOptions(dir); + nullTests(dir); + + } finally { + TestUtil.removeAll(dir); + } + } + + // test CREATE and CREATE_NEW options + static void createTests(Path dir) throws Exception { + Path file = dir.resolve("foo"); + + // CREATE + try { + // create file (no existing file) + file.newByteChannel(CREATE, WRITE).close(); + if (file.notExists()) + throw new RuntimeException("File not created"); + + // create file (existing file) + file.newByteChannel(CREATE, WRITE).close(); + + // create file where existing file is a sym link + if (supportsLinks) { + Path link = dir.resolve("link").createSymbolicLink(file); + try { + // file already exists + link.newByteChannel(CREATE, WRITE).close(); + + // file does not exist + file.delete(); + link.newByteChannel(CREATE, WRITE).close(); + if (file.notExists()) + throw new RuntimeException("File not created"); + + } finally { + TestUtil.deleteUnchecked(link); + } + } + + } finally { + TestUtil.deleteUnchecked(file); + } + + // CREATE_NEW + try { + // create file + file.newByteChannel(CREATE_NEW, WRITE).close(); + if (file.notExists()) + throw new RuntimeException("File not created"); + + // create should fail + try { + SeekableByteChannel sbc = + file.newByteChannel(CREATE_NEW, WRITE); + sbc.close(); + throw new RuntimeException("FileAlreadyExistsException not thrown"); + } catch (FileAlreadyExistsException x) { } + + // create should fail + if (supportsLinks) { + Path link = dir.resolve("link"); + Path target = dir.resolve("thisDoesNotExist"); + link.createSymbolicLink(target); + try { + + try { + SeekableByteChannel sbc = + file.newByteChannel(CREATE_NEW, WRITE); + sbc.close(); + throw new RuntimeException("FileAlreadyExistsException not thrown"); + } catch (FileAlreadyExistsException x) { } + + } finally { + TestUtil.deleteUnchecked(link); + } + } + + + } finally { + TestUtil.deleteUnchecked(file); + } + + // CREATE_NEW + SPARSE + try { + SeekableByteChannel sbc = file + .newByteChannel(CREATE_NEW, WRITE, SPARSE); + try { + final long hole = 2L * 1024L * 1024L * 1024L; + sbc.position(hole); + write(sbc, "hello"); + long size = sbc.size(); + if (size != (hole + 5)) + throw new RuntimeException("Unexpected size"); + } finally { + sbc.close(); + } + } finally { + TestUtil.deleteUnchecked(file); + } + } + + // test APPEND option + static void appendTests(Path dir) throws Exception { + Path file = dir.resolve("foo"); + try { + // "hello there" should be written to file + SeekableByteChannel sbc = file + .newByteChannel(CREATE_NEW, WRITE, APPEND); + try { + write(sbc, "hello "); + sbc.position(0L); + write(sbc, "there"); + } finally { + sbc.close(); + } + + // check file + Scanner s = new Scanner(file); + try { + String line = s.nextLine(); + if (!line.equals("hello there")) + throw new RuntimeException("Unexpected file contents"); + } finally { + s.close(); + } + + // check that read is not allowed + sbc = file.newByteChannel(APPEND); + try { + sbc.read(ByteBuffer.allocate(100)); + } catch (NonReadableChannelException x) { + } finally { + sbc.close(); + } + } finally { + // clean-up + TestUtil.deleteUnchecked(file); + } + } + + // test TRUNCATE_EXISTING option + static void truncateExistingTests(Path dir) throws Exception { + Path file = dir.resolve("foo"); + try { + SeekableByteChannel sbc = + file.newByteChannel(CREATE_NEW, WRITE); + try { + write(sbc, "Have a nice day!"); + } finally { + sbc.close(); + } + + // re-open with truncate option + // write short message and check + sbc = file.newByteChannel(WRITE, TRUNCATE_EXISTING); + try { + write(sbc, "Hello there!"); + } finally { + sbc.close(); + } + Scanner s = new Scanner(file); + try { + String line = s.nextLine(); + if (!line.equals("Hello there!")) + throw new RuntimeException("Unexpected file contents"); + } finally { + s.close(); + } + + // re-open with create + truncate option + // check file is of size 0L + sbc = file.newByteChannel(WRITE, CREATE, TRUNCATE_EXISTING); + try { + long size = ((FileChannel)sbc).size(); + if (size != 0L) + throw new RuntimeException("File not truncated"); + } finally { + sbc.close(); + } + + } finally { + // clean-up + TestUtil.deleteUnchecked(file); + } + + } + + // test NOFOLLOW_LINKS option + static void noFollowLinksTests(Path dir) throws Exception { + if (!supportsLinks) + return; + Path file = dir.resolve("foo").createFile(); + try { + // ln -s foo link + Path link = dir.resolve("link").createSymbolicLink(file); + + // open with NOFOLLOW_LINKS option + try { + link.newByteChannel(READ, LinkOption.NOFOLLOW_LINKS); + throw new RuntimeException(); + } catch (IOException x) { + } finally { + TestUtil.deleteUnchecked(link); + } + + } finally { + // clean-up + TestUtil.deleteUnchecked(file); + } + } + + // test size/truncate/position methods + static void sizeTruncatePositionTests(Path dir) throws Exception { + Path file = dir.resolve("foo"); + try { + SeekableByteChannel sbc = file + .newByteChannel(CREATE_NEW, READ, WRITE); + try { + if (sbc.size() != 0L) + throw new RuntimeException("Unexpected size"); + + // check size + write(sbc, "hello"); + if (sbc.size() != 5L) + throw new RuntimeException("Unexpected size"); + + // truncate (size and position should change) + sbc.truncate(4L); + if (sbc.size() != 4L) + throw new RuntimeException("Unexpected size"); + if (sbc.position() != 4L) + throw new RuntimeException("Unexpected position"); + + // truncate (position should not change) + sbc.position(2L).truncate(3L); + if (sbc.size() != 3L) + throw new RuntimeException("Unexpected size"); + if (sbc.position() != 2L) + throw new RuntimeException("Unexpected position"); + } finally { + sbc.close(); + } + } finally { + TestUtil.deleteUnchecked(file); + } + } + + // Windows specific options for the use by applications that really want + // to use legacy DOS sharing options + static void dosSharingOptionTests(Path dir) throws Exception { + Path file = dir.resolve("foo").createFile(); + try { + SeekableByteChannel ch; + + // no sharing + ch = file.newByteChannel(READ, + NOSHARE_READ, NOSHARE_WRITE, NOSHARE_DELETE); + try { + try { + file.newByteChannel(READ); + throw new RuntimeException("Sharing violation expected"); + } catch (IOException ignore) { } + try { + file.newByteChannel(WRITE); + throw new RuntimeException("Sharing violation expected"); + } catch (IOException ignore) { } + try { + file.delete(); + throw new RuntimeException("Sharing violation expected"); + } catch (IOException ignore) { } + } finally { + ch.close(); + } + + // read allowed + ch = file.newByteChannel(READ, NOSHARE_WRITE, NOSHARE_DELETE); + try { + file.newByteChannel(READ).close(); + try { + file.newByteChannel(WRITE); + throw new RuntimeException("Sharing violation expected"); + } catch (IOException ignore) { } + try { + file.delete(); + throw new RuntimeException("Sharing violation expected"); + } catch (IOException ignore) { } + } finally { + ch.close(); + } + + // write allowed + ch = file.newByteChannel(READ, NOSHARE_READ, NOSHARE_DELETE); + try { + try { + file.newByteChannel(READ); + throw new RuntimeException("Sharing violation expected"); + } catch (IOException ignore) { } + file.newByteChannel(WRITE).close(); + try { + file.delete(); + throw new RuntimeException("Sharing violation expected"); + } catch (IOException ignore) { } + } finally { + ch.close(); + } + + // delete allowed + ch = file.newByteChannel(READ, NOSHARE_READ, NOSHARE_WRITE); + try { + try { + file.newByteChannel(READ); + throw new RuntimeException("Sharing violation expected"); + } catch (IOException ignore) { } + try { + file.newByteChannel(WRITE); + throw new RuntimeException("Sharing violation expected"); + } catch (IOException ignore) { } + file.delete(); + } finally { + ch.close(); + } + + } finally { + TestUtil.deleteUnchecked(file); + } + } + + // invalid combinations of options + static void badCombinations(Path dir) throws Exception { + Path file = dir.resolve("bad"); + + try { + file.newByteChannel(READ, APPEND); + throw new RuntimeException("IllegalArgumentException expected"); + } catch (IllegalArgumentException x) { } + + try { + file.newByteChannel(WRITE, APPEND, TRUNCATE_EXISTING); + throw new RuntimeException("IllegalArgumentException expected"); + } catch (IllegalArgumentException x) { } + } + + // unsupported operations + static void unsupportedOptions(Path dir) throws Exception { + Path file = dir.resolve("bad"); + + OpenOption badOption = new OpenOption() { }; + try { + file.newByteChannel(badOption); + throw new RuntimeException("UnsupportedOperationException expected"); + } catch (UnsupportedOperationException e) { } + try { + file.newByteChannel(READ, WRITE, badOption); + throw new RuntimeException("UnsupportedOperationException expected"); + } catch (UnsupportedOperationException e) { } + } + + // null handling + static void nullTests(Path dir) throws Exception { + Path file = dir.resolve("foo"); + + try { + file.newByteChannel((OpenOption[])null); + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException x) { } + + try { + OpenOption[] opts = { READ, null }; + file.newByteChannel(opts); + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException x) { } + + try { + file.newByteChannel((Set)null); + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException x) { } + + try { + Set opts = new HashSet(); + opts.add(READ); + opts.add(null); + file.newByteChannel(opts); + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException x) { } + + try { + EnumSet opts = EnumSet.of(READ); + file.newByteChannel(opts, (FileAttribute[])null); + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException x) { } + + try { + EnumSet opts = EnumSet.of(READ); + FileAttribute[] attrs = { null }; + file.newByteChannel(opts, attrs); + throw new RuntimeException("NullPointerException expected"); + } catch (NullPointerException x) { } + } + + static void write(WritableByteChannel wbc, String msg) throws IOException { + ByteBuffer buf = ByteBuffer.wrap(msg.getBytes()); + while (buf.hasRemaining()) + wbc.write(buf); + } +} diff --git a/test/java/nio/file/Path/TemporaryFiles.java b/test/java/nio/file/Path/TemporaryFiles.java new file mode 100644 index 000000000..6a9d28d97 --- /dev/null +++ b/test/java/nio/file/Path/TemporaryFiles.java @@ -0,0 +1,76 @@ +/* + * 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. + */ + +import java.nio.file.*; +import static java.nio.file.StandardOpenOption.*; +import java.nio.file.attribute.*; +import java.io.File; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Set; + +public class TemporaryFiles { + + static void checkFile(Path file) throws IOException { + // check file is in temporary directory + Path tmpdir = Paths.get(System.getProperty("java.io.tmpdir")); + if (!file.getParent().equals(tmpdir)) + throw new RuntimeException("Not in temporary directory"); + + // check that file can be opened for reading and writing + file.newByteChannel(READ).close(); + file.newByteChannel(WRITE).close(); + file.newByteChannel(READ,WRITE).close(); + + // check file permissions are 0600 or more secure + if (file.getFileStore().supportsFileAttributeView("posix")) { + Set perms = Attributes + .readPosixFileAttributes(file).permissions(); + perms.remove(PosixFilePermission.OWNER_READ); + perms.remove(PosixFilePermission.OWNER_WRITE); + if (!perms.isEmpty()) + throw new RuntimeException("Temporary file is not secure"); + } + } + + public static void main(String[] args) throws IOException { + Path file = File.createTempFile("blah", null, false).toPath(); + try { + checkFile(file); + } finally { + TestUtil.deleteUnchecked(file); + } + + // temporary file with deleteOnExit + file = File.createTempFile("blah", "tmp", true).toPath(); + checkFile(file); + // write path to temporary file to file so that calling script can + // check that it is deleted + OutputStream out = Paths.get(args[0]).newOutputStream(); + try { + out.write(file.toString().getBytes()); + } finally { + out.close(); + } + } +} diff --git a/test/java/nio/file/Path/UriImportExport.java b/test/java/nio/file/Path/UriImportExport.java new file mode 100644 index 000000000..560423195 --- /dev/null +++ b/test/java/nio/file/Path/UriImportExport.java @@ -0,0 +1,80 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.Path + */ + +import java.nio.file.*; +import java.net.URI; +import java.net.URISyntaxException; +import java.io.PrintStream; + +public class UriImportExport { + + static final PrintStream log = System.out; + static int failures = 0; + + static void test(String fn, String expected) { + log.println(); + Path p = Paths.get(fn); + log.println(p); + URI u = p.toUri(); + log.println(" --> " + u); + if (expected != null && !(u.toString().equals(expected))) { + log.println("FAIL: Expected " + expected); + failures++; + return; + } + Path q = Paths.get(u); + log.println(" --> " + q); + if (!p.toAbsolutePath().equals(q)) { + log.println("FAIL: Expected " + p + ", got " + q); + failures++; + return; + } + } + + static void test(String fn) { + test(fn, null); + } + + public static void main(String[] args) throws Exception { + test("foo"); + test("/foo"); + test("/foo bar"); + + String osname = System.getProperty("os.name"); + if (osname.startsWith("Windows")) { + test("C:\\foo"); + test("C:foo"); + test("\\\\rialto.dublin.com\\share\\"); + test("\\\\fe80--203-baff-fe5a-749ds1.ipv6-literal.net\\share\\missing", + "file://[fe80::203:baff:fe5a:749d%1]/share/missing"); + } + + if (failures > 0) + throw new RuntimeException(failures + " test(s) failed"); + } +} diff --git a/test/java/nio/file/Path/delete_on_close.sh b/test/java/nio/file/Path/delete_on_close.sh new file mode 100644 index 000000000..198e99d72 --- /dev/null +++ b/test/java/nio/file/Path/delete_on_close.sh @@ -0,0 +1,61 @@ +# +# 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 4313887 +# @summary Unit test for DELETE_ON_CLOSE open option +# @library .. +# @build DeleteOnClose +# @run shell delete_on_close.sh + +# if TESTJAVA isn't set then we assume an interactive run. + +if [ -z "$TESTJAVA" ]; then + TESTSRC=. + TESTCLASSES=. + JAVA=java +else + JAVA="${TESTJAVA}/bin/java" +fi + +OS=`uname -s` +case "$OS" in + Windows_* ) + CLASSPATH="${TESTCLASSES};${TESTSRC}" + ;; + * ) + CLASSPATH=${TESTCLASSES}:${TESTSRC} + ;; +esac +export CLASSPATH + +TMPFILE="$$.tmp" +touch $TMPFILE +$JAVA DeleteOnClose $TMPFILE 2>&1 +if [ $? != 0 ]; then exit 1; fi +if [ -f $TMPFILE ]; then + echo "$TMPFILE was not deleted" + exit 1 +fi + +exit 0 diff --git a/test/java/nio/file/Path/temporary_files.sh b/test/java/nio/file/Path/temporary_files.sh new file mode 100644 index 000000000..552dcfdab --- /dev/null +++ b/test/java/nio/file/Path/temporary_files.sh @@ -0,0 +1,65 @@ +# +# 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 4313887 +# @summary Unit test for File.createTempFile (to be be moved to test/java/io/File) +# @library .. +# @build TemporaryFiles +# @run shell temporary_files.sh + +# if TESTJAVA isn't set then we assume an interactive run. + +if [ -z "$TESTJAVA" ]; then + TESTSRC=. + TESTCLASSES=. + JAVA=java +else + JAVA="${TESTJAVA}/bin/java" +fi + +OS=`uname -s` +case "$OS" in + Windows_* ) + CLASSPATH="${TESTCLASSES};${TESTSRC}" + ;; + * ) + CLASSPATH=${TESTCLASSES}:${TESTSRC} + ;; +esac +export CLASSPATH + +TMPFILENAME="$$.tmp" +$JAVA TemporaryFiles $TMPFILENAME 2>&1 +if [ $? != 0 ]; then exit 1; fi +if [ ! -f $TMPFILENAME ]; then + echo "$TMPFILENAME not found" + exit 1 +fi +TMPFILE=`cat $TMPFILENAME` +if [ -f $TMPFILE ]; then + echo "$TMPFILE not deleted" + exit 1 +fi + +exit 0 diff --git a/test/java/nio/file/PathMatcher/Basic.java b/test/java/nio/file/PathMatcher/Basic.java new file mode 100644 index 000000000..16efcf099 --- /dev/null +++ b/test/java/nio/file/PathMatcher/Basic.java @@ -0,0 +1,174 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.PathMatcher + */ + +import java.nio.file.*; +import java.util.regex.PatternSyntaxException; + +public class Basic { + static int failures; + + static void match(String name, String pattern, boolean expectedToMatch) { + System.out.format("%s -> %s", name, pattern); + Path file = Paths.get(name); + boolean matched = file.getFileSystem() + .getPathMatcher("glob:" + pattern).matches(file); + if (matched) + System.out.print(" (matched)"); + else + System.out.print(" (no match)"); + if (matched != expectedToMatch) { + System.out.println(" ==> UNEXPECTED RESULT!"); + failures++; + } else { + System.out.println(" OKAY"); + } + } + + static void assertMatch(String path, String pattern) { + match(path, pattern, true); + } + + static void assertNotMatch(String path, String pattern) { + match(path, pattern, false); + } + + static void assertBadPattern(String path, String pattern) { + System.out.format("Compile bad pattern %s\t", pattern); + try { + FileSystems.getDefault().getPathMatcher("glob:" + pattern); + System.out.println("Compiled ==> UNEXPECTED RESULT!"); + failures++; + } catch (PatternSyntaxException e) { + System.out.println("Failed to compile ==> OKAY"); + } + } + + public static void main(String[] args) { + // basic + assertMatch("foo.html", "foo.html"); + assertNotMatch("foo.html", "foo.htm"); + assertNotMatch("foo.html", "bar.html"); + + // match zero or more characters + assertMatch("foo.html", "f*"); + assertMatch("foo.html", "*.html"); + assertMatch("foo.html", "foo.html*"); + assertMatch("foo.html", "*foo.html"); + assertMatch("foo.html", "*foo.html*"); + assertNotMatch("foo.html", "*.htm"); + assertNotMatch("foo.html", "f.*"); + + // match one character + assertMatch("foo.html", "?oo.html"); + assertMatch("foo.html", "??o.html"); + assertMatch("foo.html", "???.html"); + assertMatch("foo.html", "???.htm?"); + assertNotMatch("foo.html", "foo.???"); + + // group of subpatterns + assertMatch("foo.html", "foo{.html,.class}"); + assertMatch("foo.html", "foo.{class,html}"); + assertNotMatch("foo.html", "foo{.htm,.class}"); + + // bracket expressions + assertMatch("foo.html", "[f]oo.html"); + assertMatch("foo.html", "[e-g]oo.html"); + assertMatch("foo.html", "[abcde-g]oo.html"); + assertMatch("foo.html", "[abcdefx-z]oo.html"); + assertMatch("foo.html", "[!a]oo.html"); + assertMatch("foo.html", "[!a-e]oo.html"); + assertMatch("foo-bar", "foo[-a-z]bar"); // match dash + assertMatch("foo.html", "foo[!-]html"); // match !dash + + // groups of subpattern with bracket expressions + assertMatch("foo.html", "[f]oo.{[h]tml,class}"); + assertMatch("foo.html", "foo.{[a-z]tml,class}"); + assertMatch("foo.html", "foo.{[!a-e]tml,.class}"); + + // assume special characters are allowed in file names + assertMatch("{foo}.html", "\\{foo*"); + assertMatch("{foo}.html", "*\\}.html"); + assertMatch("[foo].html", "\\[foo*"); + assertMatch("[foo].html", "*\\].html"); + + // errors + assertBadPattern("foo.html", "*[a--z]"); // bad range + assertBadPattern("foo.html", "*[a--]"); // bad range + assertBadPattern("foo.html", "*[a-z"); // missing ] + assertBadPattern("foo.html", "*{class,java"); // missing } + assertBadPattern("foo.html", "*.{class,{.java}}"); // nested group + assertBadPattern("foo.html", "*.html\\"); // nothing to escape + + // platform specific + if (System.getProperty("os.name").startsWith("Windows")) { + assertMatch("C:\\foo", "C:\\\\f*"); + assertMatch("C:\\FOO", "c:\\\\f*"); + assertMatch("C:\\foo\\bar\\gus", "C:\\\\**\\\\gus"); + assertMatch("C:\\foo\\bar\\gus", "C:\\\\**"); + } else { + assertMatch("/tmp/foo", "/tmp/*"); + assertMatch("/tmp/foo/bar", "/tmp/**"); + + // some special characters not allowed on Windows + assertMatch("myfile?", "myfile\\?"); + assertMatch("one\\two", "one\\\\two"); + assertMatch("one*two", "one\\*two"); + } + + + + // regex syntax + { + String pattern = ".*\\.html"; + System.out.format("Test regex pattern: %s", pattern); + Path file = Paths.get("foo.html"); + boolean matched = file.getFileSystem() + .getPathMatcher("regex:" + pattern).matches(file); + if (matched) { + System.out.println(" OKAY"); + } else { + System.out.println(" ==> UNEXPECTED RESULT!"); + failures++; + } + } + + // unknown syntax + try { + System.out.format("Test unknown syntax"); + FileSystems.getDefault().getPathMatcher("grep:foo"); + System.out.println(" ==> NOT EXPECTED TO COMPILE"); + failures++; + } catch (UnsupportedOperationException e) { + System.out.println(" OKAY"); + } + + if (failures > 0) + throw new RuntimeException(failures + + " sub-test(s) failed - see log for details"); + } +} diff --git a/test/java/nio/file/TestUtil.java b/test/java/nio/file/TestUtil.java new file mode 100644 index 000000000..c19e28fbd --- /dev/null +++ b/test/java/nio/file/TestUtil.java @@ -0,0 +1,125 @@ +/* + * 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. + */ + +import java.nio.file.*; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.Random; +import java.io.IOException; + +public class TestUtil { + private TestUtil() { + } + + public static Path createTemporaryDirectory() throws IOException { + Path tmpdir = Paths.get(System.getProperty("java.io.tmpdir")); + Random r = new Random(); + + Path dir; + do { + dir = tmpdir.resolve("name" + r.nextInt()); + } while (dir.exists()); + return dir.createDirectory(); + } + + static void removeAll(Path dir) { + Files.walkFileTree(dir, new FileVisitor() { + @Override + public FileVisitResult preVisitDirectory(Path dir) { + return FileVisitResult.CONTINUE; + } + @Override + public FileVisitResult preVisitDirectoryFailed(Path dir, IOException exc) { + System.err.format("Error occured accessing directory %s\n", dir, exc); + return FileVisitResult.CONTINUE; + } + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + try { + file.delete(false); + } catch (IOException x) { + System.err.format("Unable to delete %s: %s\n", file, x); + } + return FileVisitResult.CONTINUE; + } + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) { + try { + dir.delete(false); + } catch (IOException x) { + System.err.format("Unable to delete %s: %s\n", dir, x); + } + return FileVisitResult.CONTINUE; + } + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) { + System.err.format("Unable to visit %s: %s\n", file, exc); + return FileVisitResult.CONTINUE; + } + }); + } + + static void deleteUnchecked(FileRef file) { + try { + file.delete(); + } catch (IOException exc) { + System.err.format("Unable to delete %s: %s\n", file, exc); + } + } + + /** + * Creates a directory tree in the given directory so that the total + * size of the path is more than 2k in size. This is used for long + * path tests on Windows. + */ + static Path createDirectoryWithLongPath(Path dir) + throws IOException + { + StringBuilder sb = new StringBuilder(); + for (int i=0; i<240; i++) { + sb.append('A'); + } + String name = sb.toString(); + do { + dir = dir.resolve(name).resolve("."); + dir.createDirectory(); + } while (dir.toString().length() < 2048); + return dir; + } + + /** + * Returns true if symbolic links are supported + */ + static boolean supportsLinks(Path dir) { + Path link = dir.resolve("testlink"); + Path target = dir.resolve("testtarget"); + try { + link.createSymbolicLink(target); + target.delete(false); + return true; + } catch (UnsupportedOperationException x) { + return false; + } catch (IOException x) { + return false; + } + } +} diff --git a/test/java/nio/file/WatchService/Basic.java b/test/java/nio/file/WatchService/Basic.java new file mode 100644 index 000000000..60c18d74b --- /dev/null +++ b/test/java/nio/file/WatchService/Basic.java @@ -0,0 +1,493 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.WatchService + * @library .. + * @run main/timeout=120 Basic + */ + +import java.nio.file.*; +import static java.nio.file.StandardWatchEventKind.*; +import java.nio.file.attribute.*; +import java.io.*; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * Unit test for WatchService that exercises all methods in various scenarios. + */ + +public class Basic { + + static void createFile(Path file) throws IOException { + file.newOutputStream().close(); + } + + static void takeExpectedKey(WatchService watcher, WatchKey expected) { + System.out.println("take events..."); + WatchKey key; + try { + key = watcher.take(); + } catch (InterruptedException x) { + // not expected + throw new RuntimeException(x); + } + if (key != expected) + throw new RuntimeException("removed unexpected key"); + } + + static void checkExpectedEvent(Iterable> events, + WatchEvent.Kind expectedKind, + Object expectedContext) + { + WatchEvent event = events.iterator().next(); + System.out.format("got event: type=%s, count=%d, context=%s\n", + event.kind(), event.count(), event.context()); + if (event.kind() != expectedKind) + throw new RuntimeException("unexpected event"); + if (!expectedContext.equals(event.context())) + throw new RuntimeException("unexpected context"); + } + + /** + * Simple test of each of the standard events + */ + static void testEvents(Path dir) throws IOException { + System.out.println("-- Standard Events --"); + + FileSystem fs = FileSystems.getDefault(); + Path name = fs.getPath("foo"); + + WatchService watcher = fs.newWatchService(); + try { + // --- ENTRY_CREATE --- + + // register for event + System.out.format("register %s for ENTRY_CREATE\n", dir); + WatchKey myKey = dir.register(watcher, + new WatchEvent.Kind[]{ ENTRY_CREATE }); + + // create file + Path file = dir.resolve("foo"); + System.out.format("create %s\n", file); + createFile(file); + + // remove key and check that we got the ENTRY_CREATE event + takeExpectedKey(watcher, myKey); + checkExpectedEvent(myKey.pollEvents(), + StandardWatchEventKind.ENTRY_CREATE, name); + + System.out.println("reset key"); + if (!myKey.reset()) + throw new RuntimeException("key has been cancalled"); + + System.out.println("OKAY"); + + // --- ENTRY_DELETE --- + + System.out.format("register %s for ENTRY_DELETE\n", dir); + WatchKey deleteKey = dir.register(watcher, + new WatchEvent.Kind[]{ ENTRY_DELETE }); + if (deleteKey != myKey) + throw new RuntimeException("register did not return existing key"); + + System.out.format("delete %s\n", file); + file.delete(false); + takeExpectedKey(watcher, myKey); + checkExpectedEvent(myKey.pollEvents(), + StandardWatchEventKind.ENTRY_DELETE, name); + + System.out.println("reset key"); + if (!myKey.reset()) + throw new RuntimeException("key has been cancalled"); + + System.out.println("OKAY"); + + // create the file for the next test + createFile(file); + + // --- ENTRY_MODIFY --- + + System.out.format("register %s for ENTRY_MODIFY\n", dir); + WatchKey newKey = dir.register(watcher, + new WatchEvent.Kind[]{ ENTRY_MODIFY }); + if (newKey != myKey) + throw new RuntimeException("register did not return existing key"); + + System.out.format("update: %s\n", file); + OutputStream out = file.newOutputStream(EnumSet.of(StandardOpenOption.APPEND)); + try { + out.write("I am a small file".getBytes("UTF-8")); + } finally { + out.close(); + } + + // remove key and check that we got the ENTRY_MODIFY event + takeExpectedKey(watcher, myKey); + checkExpectedEvent(myKey.pollEvents(), + StandardWatchEventKind.ENTRY_MODIFY, name); + System.out.println("OKAY"); + + // done + file.delete(false); + + } finally { + watcher.close(); + } + } + + /** + * Check that a cancelled key will never be queued + */ + static void testCancel(Path dir) throws IOException { + System.out.println("-- Cancel --"); + + WatchService watcher = FileSystems.getDefault().newWatchService(); + try { + + System.out.format("register %s for events\n", dir); + WatchKey myKey = dir.register(watcher, + new WatchEvent.Kind[]{ ENTRY_CREATE }); + + System.out.println("cancel key"); + myKey.cancel(); + + // create a file in the directory + Path file = dir.resolve("mars"); + System.out.format("create: %s\n", file); + createFile(file); + + // poll for keys - there will be none + System.out.println("poll..."); + try { + WatchKey key = watcher.poll(3000, TimeUnit.MILLISECONDS); + if (key != null) + throw new RuntimeException("key should not be queued"); + } catch (InterruptedException x) { + throw new RuntimeException(x); + } + + // done + file.delete(false); + + System.out.println("OKAY"); + + } finally { + watcher.close(); + } + } + + /** + * Check that deleting a registered directory causes the key to be + * cancelled and queued. + */ + static void testAutomaticCancel(Path dir) throws IOException { + System.out.println("-- Automatic Cancel --"); + + Path subdir = dir.resolve("bar").createDirectory(); + + WatchService watcher = FileSystems.getDefault().newWatchService(); + try { + + System.out.format("register %s for events\n", subdir); + WatchKey myKey = subdir.register(watcher, + new WatchEvent.Kind[]{ ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY }); + + System.out.format("delete: %s\n", subdir); + subdir.delete(false); + takeExpectedKey(watcher, myKey); + + System.out.println("reset key"); + if (myKey.reset()) + throw new RuntimeException("Key was not cancelled"); + if (myKey.isValid()) + throw new RuntimeException("Key is still valid"); + + System.out.println("OKAY"); + + } finally { + watcher.close(); + } + } + + /** + * Asynchronous close of watcher causes blocked threads to wakeup + */ + static void testWakeup(Path dir) throws IOException { + System.out.println("-- Wakeup Tests --"); + final WatchService watcher = FileSystems.getDefault().newWatchService(); + Runnable r = new Runnable() { + public void run() { + try { + Thread.sleep(5000); + System.out.println("close WatchService..."); + watcher.close(); + } catch (InterruptedException x) { + x.printStackTrace(); + } catch (IOException x) { + x.printStackTrace(); + } + } + }; + + // start thread to close watch service after delay + new Thread(r).start(); + + try { + System.out.println("take..."); + watcher.take(); + throw new RuntimeException("ClosedWatchServiceException not thrown"); + } catch (InterruptedException x) { + throw new RuntimeException(x); + } catch (ClosedWatchServiceException x) { + System.out.println("ClosedWatchServiceException thrown"); + } + + System.out.println("OKAY"); + } + + /** + * Simple test to check exceptions and other cases + */ + @SuppressWarnings("unchecked") + static void testExceptions(Path dir) throws IOException { + System.out.println("-- Exceptions and other simple tests --"); + + WatchService watcher = FileSystems.getDefault().newWatchService(); + try { + + // Poll tests + + WatchKey key; + System.out.println("poll..."); + key = watcher.poll(); + if (key != null) + throw new RuntimeException("no keys registered"); + + System.out.println("poll with timeout..."); + try { + long start = System.currentTimeMillis(); + key = watcher.poll(3000, TimeUnit.MILLISECONDS); + if (key != null) + throw new RuntimeException("no keys registered"); + long waited = System.currentTimeMillis() - start; + if (waited < 2900) + throw new RuntimeException("poll was too short"); + } catch (InterruptedException x) { + throw new RuntimeException(x); + } + + // IllegalArgumentException + System.out.println("IllegalArgumentException tests..."); + try { + dir.register(watcher, new WatchEvent.Kind[]{ } ); + throw new RuntimeException("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException x) { + } + try { + // OVERFLOW is ignored so this is equivalent to the empty set + dir.register(watcher, new WatchEvent.Kind[]{ OVERFLOW }); + throw new RuntimeException("IllegalArgumentException not thrown"); + } catch (IllegalArgumentException x) { + } + + // UnsupportedOperationException + try { + dir.register(watcher, new WatchEvent.Kind[]{ + new WatchEvent.Kind() { + @Override public String name() { return "custom"; } + @Override public Class type() { return Object.class; } + }}); + } catch (UnsupportedOperationException x) { + } + try { + dir.register(watcher, + new WatchEvent.Kind[]{ ENTRY_CREATE }, + new WatchEvent.Modifier() { + @Override public String name() { return "custom"; } + }); + throw new RuntimeException("UnsupportedOperationException not thrown"); + } catch (UnsupportedOperationException x) { + } + + // NullPointerException + System.out.println("NullPointerException tests..."); + try { + dir.register(null, new WatchEvent.Kind[]{ ENTRY_CREATE }); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + dir.register(watcher, new WatchEvent.Kind[]{ null }); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + dir.register(watcher, new WatchEvent.Kind[]{ ENTRY_CREATE }, + (WatchEvent.Modifier)null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + } finally { + watcher.close(); + } + + // -- ClosedWatchServiceException -- + + System.out.println("ClosedWatchServiceException tests..."); + + try { + watcher.poll(); + throw new RuntimeException("ClosedWatchServiceException not thrown"); + } catch (ClosedWatchServiceException x) { + } + + // assume that poll throws exception immediately + long start = System.currentTimeMillis(); + try { + watcher.poll(10000, TimeUnit.MILLISECONDS); + throw new RuntimeException("ClosedWatchServiceException not thrown"); + } catch (InterruptedException x) { + throw new RuntimeException(x); + } catch (ClosedWatchServiceException x) { + long waited = System.currentTimeMillis() - start; + if (waited > 5000) + throw new RuntimeException("poll was too long"); + } + + try { + watcher.take(); + throw new RuntimeException("ClosedWatchServiceException not thrown"); + } catch (InterruptedException x) { + throw new RuntimeException(x); + } catch (ClosedWatchServiceException x) { + } + + try { + dir.register(watcher, new WatchEvent.Kind[]{ ENTRY_CREATE }); + throw new RuntimeException("ClosedWatchServiceException not thrown"); + } catch (ClosedWatchServiceException x) { + } + + System.out.println("OKAY"); + } + + /** + * Test that directory can be registered with more than one watch service + * and that events don't interfere with each other + */ + static void testTwoWatchers(Path dir) throws IOException { + System.out.println("-- Two watchers test --"); + + FileSystem fs = FileSystems.getDefault(); + WatchService watcher1 = fs.newWatchService(); + WatchService watcher2 = fs.newWatchService(); + try { + Path name1 = fs.getPath("gus1"); + Path name2 = fs.getPath("gus2"); + + // create gus1 + Path file1 = dir.resolve(name1); + System.out.format("create %s\n", file1); + createFile(file1); + + // register with both watch services (different events) + System.out.println("register for different events"); + WatchKey key1 = dir.register(watcher1, + new WatchEvent.Kind[]{ ENTRY_CREATE }); + WatchKey key2 = dir.register(watcher2, + new WatchEvent.Kind[]{ ENTRY_DELETE }); + + if (key1 == key2) + throw new RuntimeException("keys should be different"); + + // create gus2 + Path file2 = dir.resolve(name2); + System.out.format("create %s\n", file2); + createFile(file2); + + // check that key1 got ENTRY_CREATE + takeExpectedKey(watcher1, key1); + checkExpectedEvent(key1.pollEvents(), + StandardWatchEventKind.ENTRY_CREATE, name2); + + // check that key2 got zero events + WatchKey key = watcher2.poll(); + if (key != null) + throw new RuntimeException("key not expected"); + + // delete gus1 + file1.delete(false); + + // check that key2 got ENTRY_DELETE + takeExpectedKey(watcher2, key2); + checkExpectedEvent(key2.pollEvents(), + StandardWatchEventKind.ENTRY_DELETE, name1); + + // check that key1 got zero events + key = watcher1.poll(); + if (key != null) + throw new RuntimeException("key not expected"); + + // reset for next test + key1.reset(); + key2.reset(); + + // change registration with watcher2 so that they are both + // registered for the same event + System.out.println("register for same event"); + key2 = dir.register(watcher2, new WatchEvent.Kind[]{ ENTRY_CREATE }); + + // create file and key2 should be queued + System.out.format("create %s\n", file1); + createFile(file1); + takeExpectedKey(watcher2, key2); + checkExpectedEvent(key2.pollEvents(), + StandardWatchEventKind.ENTRY_CREATE, name1); + + System.out.println("OKAY"); + + } finally { + watcher2.close(); + watcher1.close(); + } + } + + public static void main(String[] args) throws IOException { + Path dir = TestUtil.createTemporaryDirectory(); + try { + + testEvents(dir); + testCancel(dir); + testAutomaticCancel(dir); + testWakeup(dir); + testExceptions(dir); + testTwoWatchers(dir); + + } finally { + TestUtil.removeAll(dir); + } + } +} diff --git a/test/java/nio/file/WatchService/FileTreeModifier.java b/test/java/nio/file/WatchService/FileTreeModifier.java new file mode 100644 index 000000000..741c86df0 --- /dev/null +++ b/test/java/nio/file/WatchService/FileTreeModifier.java @@ -0,0 +1,148 @@ +/* + * 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 4313887 + * @summary Sanity test for Sun-specific FILE_TREE watch event modifier + * @library .. + */ + +import java.nio.file.*; +import static java.nio.file.StandardWatchEventKind.*; +import java.io.IOException; +import java.io.OutputStream; +import java.util.*; +import java.util.concurrent.*; +import static com.sun.nio.file.ExtendedWatchEventModifier.*; + +public class FileTreeModifier { + + static void checkExpectedEvent(WatchService watcher, + WatchEvent.Kind expectedType, + Object expectedContext) + { + WatchKey key; + try { + key = watcher.take(); + } catch (InterruptedException x) { + // should not happen + throw new RuntimeException(x); + } + WatchEvent event = key.pollEvents().iterator().next(); + System.out.format("Event: type=%s, count=%d, context=%s\n", + event.kind(), event.count(), event.context()); + if (event.kind() != expectedType) + throw new RuntimeException("unexpected event"); + if (!expectedContext.equals(event.context())) + throw new RuntimeException("unexpected context"); + } + + static void doTest(Path top) throws IOException { + FileSystem fs = top.getFileSystem(); + WatchService watcher = fs.newWatchService(); + + // create directories + Path subdir = top + .resolve("a").createDirectory() + .resolve("b").createDirectory() + .resolve("c").createDirectory(); + + // Test ENTRY_CREATE with FILE_TREE modifier. + + WatchKey key = top.register(watcher, + new WatchEvent.Kind[]{ ENTRY_CREATE }, FILE_TREE); + + // create file in a/b/c and check we get create event + Path file = subdir.resolve("foo").createFile(); + checkExpectedEvent(watcher, ENTRY_CREATE, top.relativize(file)); + key.reset(); + + // Test ENTRY_DELETE with FILE_TREE modifier. + + WatchKey k = top.register(watcher, + new WatchEvent.Kind[]{ ENTRY_DELETE }, FILE_TREE); + if (k != key) + throw new RuntimeException("Existing key not returned"); + + // delete a/b/c/foo and check we get delete event + file.delete(false); + checkExpectedEvent(watcher, ENTRY_DELETE, top.relativize(file)); + key.reset(); + + // Test changing registration to ENTRY_CREATE without modifier + + k = top.register(watcher, new WatchEvent.Kind[]{ ENTRY_CREATE }); + if (k != key) + throw new RuntimeException("Existing key not returned"); + + // create a/b/c/foo + file.createFile(); + + // check that key is not queued + try { + k = watcher.poll(3, TimeUnit.SECONDS); + } catch (InterruptedException e) { + throw new RuntimeException(); + } + if (k != null) + throw new RuntimeException("WatchKey not expected to be polled"); + + // create bar and check we get create event + file = top.resolve("bar").createFile(); + checkExpectedEvent(watcher, ENTRY_CREATE, top.relativize(file)); + key.reset(); + + // Test changing registration to with FILE_TREE modifier + + k = top.register(watcher, + new WatchEvent.Kind[]{ ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY }, + FILE_TREE); + if (k != key) + throw new RuntimeException("Existing key not returned"); + + // modify bar and check we get modify event + OutputStream out = file.newOutputStream(); + try { + out.write("Double shot expresso please".getBytes("UTF-8")); + } finally { + out.close(); + } + checkExpectedEvent(watcher, ENTRY_MODIFY, top.relativize(file)); + key.reset(); + } + + + public static void main(String[] args) throws IOException { + if (!System.getProperty("os.name").startsWith("Windows")) { + System.out.println("This is Windows-only test at this time!"); + return; + } + + Path dir = TestUtil.createTemporaryDirectory(); + try { + doTest(dir); + } finally { + TestUtil.removeAll(dir); + } + } +} diff --git a/test/java/nio/file/WatchService/SensitivityModifier.java b/test/java/nio/file/WatchService/SensitivityModifier.java new file mode 100644 index 000000000..62aedeeac --- /dev/null +++ b/test/java/nio/file/WatchService/SensitivityModifier.java @@ -0,0 +1,122 @@ +/* + * 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 4313887 + * @summary Sanity test for Sun-specific sensitivyt level watch event modifier + * @library .. + * @run main/timeout=330 Basic + */ + +import java.nio.file.*; +import static java.nio.file.StandardWatchEventKind.*; +import java.io.OutputStream; +import java.io.IOException; +import java.util.Random; +import java.util.concurrent.TimeUnit; +import com.sun.nio.file.SensitivityWatchEventModifier; + +public class SensitivityModifier { + + static final Random rand = new Random(); + + static void register(Path[] dirs, WatchService watcher) throws IOException { + SensitivityWatchEventModifier[] sensitivtives = + SensitivityWatchEventModifier.values(); + for (int i=0; i[]{ ENTRY_MODIFY }, sensivity); + } + } + + static void doTest(Path top) throws Exception { + FileSystem fs = top.getFileSystem(); + WatchService watcher = fs.newWatchService(); + + // create directories and files + int nDirs = 5 + rand.nextInt(20); + int nFiles = 50 + rand.nextInt(50); + Path[] dirs = new Path[nDirs]; + Path[] files = new Path[nFiles]; + for (int i=0; i event = key.pollEvents().iterator().next(); + if (event.kind() != ENTRY_MODIFY) + throw new RuntimeException("Unexpected event: " + event); + Path name = ((WatchEvent)event).context(); + if (!name.equals(file.getName())) + throw new RuntimeException("Unexpected context: " + name); + System.out.println("Event OK"); + + // drain events (to avoid interference) + do { + key.reset(); + key = watcher.poll(1, TimeUnit.SECONDS); + } while (key != null); + + // re-register the directories to force changing their sensitivity + // level + register(dirs, watcher); + } + + // done + watcher.close(); + } + + public static void main(String[] args) throws Exception { + Path dir = TestUtil.createTemporaryDirectory(); + try { + doTest(dir); + } finally { + TestUtil.removeAll(dir); + } + } +} diff --git a/test/java/nio/file/WatchService/WithSecurityManager.java b/test/java/nio/file/WatchService/WithSecurityManager.java new file mode 100644 index 000000000..c227585ee --- /dev/null +++ b/test/java/nio/file/WatchService/WithSecurityManager.java @@ -0,0 +1,83 @@ +/* + * 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 4313887 + * @summary Unit test for Watchable#register's permission checks + * @build WithSecurityManager + * @run main/othervm WithSecurityManager denyAll.policy - fail + * @run main/othervm WithSecurityManager denyAll.policy tree fail + * @run main/othervm WithSecurityManager grantDirOnly.policy - pass + * @run main/othervm WithSecurityManager grantDirOnly.policy tree fail + * @run main/othervm WithSecurityManager grantDirAndOneLevel.policy - pass + * @run main/othervm WithSecurityManager grantDirAndOneLevel.policy tree fail + * @run main/othervm WithSecurityManager grantDirAndTree.policy - pass + * @run main/othervm WithSecurityManager grantDirAndTree.policy tree pass + */ + +import java.nio.file.*; +import java.io.IOException; +import com.sun.nio.file.ExtendedWatchEventModifier; + +public class WithSecurityManager { + + public static void main(String[] args) throws IOException { + String policyFile = args[0]; + boolean recursive = args[1].equals("tree"); + boolean expectedToFail = args[2].equals("fail"); + + // install security manager with the given policy file + String testSrc = System.getProperty("test.src"); + if (testSrc == null) + throw new RuntimeException("This test must be run by jtreg"); + Path dir = Paths.get(testSrc); + System.setProperty("java.security.policy", dir.resolve(policyFile).toString()); + System.setSecurityManager(new SecurityManager()); + + // initialize optional modifier + WatchEvent.Modifier[] modifiers; + if (recursive) { + modifiers = new WatchEvent.Modifier[1]; + modifiers[0] = ExtendedWatchEventModifier.FILE_TREE; + } else { + modifiers = new WatchEvent.Modifier[0]; + } + + // attempt to register directory + try { + dir.register(dir.getFileSystem().newWatchService(), + new WatchEvent.Kind[]{ StandardWatchEventKind.ENTRY_CREATE }, + modifiers); + if (expectedToFail) + throw new RuntimeException("SecurityException not thrown"); + } catch (SecurityException e) { + if (!expectedToFail) + throw e; + } catch (UnsupportedOperationException e) { + // FILE_TREE modifier only supported on some platforms + if (!recursive) + throw new RuntimeException(e); + System.out.println("FILE_TREE option not supported"); + } + } +} diff --git a/test/java/nio/file/WatchService/denyAll.policy b/test/java/nio/file/WatchService/denyAll.policy new file mode 100644 index 000000000..325009477 --- /dev/null +++ b/test/java/nio/file/WatchService/denyAll.policy @@ -0,0 +1,3 @@ +// policy file that does not grant any permissions +grant { +}; diff --git a/test/java/nio/file/WatchService/grantDirAndOneLevel.policy b/test/java/nio/file/WatchService/grantDirAndOneLevel.policy new file mode 100644 index 000000000..1a3464646 --- /dev/null +++ b/test/java/nio/file/WatchService/grantDirAndOneLevel.policy @@ -0,0 +1,5 @@ +// policy file that grants read access to source directory and its entries +grant { + permission java.io.FilePermission "${test.src}", "read"; + permission java.io.FilePermission "${test.src}${file.separator}*", "read"; +}; diff --git a/test/java/nio/file/WatchService/grantDirAndTree.policy b/test/java/nio/file/WatchService/grantDirAndTree.policy new file mode 100644 index 000000000..85bc0d0fb --- /dev/null +++ b/test/java/nio/file/WatchService/grantDirAndTree.policy @@ -0,0 +1,5 @@ +// policy file that grants read access to source directory and all descendants +grant { + permission java.io.FilePermission "${test.src}", "read"; + permission java.io.FilePermission "${test.src}${file.separator}-", "read"; +}; diff --git a/test/java/nio/file/WatchService/grantDirOnly.policy b/test/java/nio/file/WatchService/grantDirOnly.policy new file mode 100644 index 000000000..fca153941 --- /dev/null +++ b/test/java/nio/file/WatchService/grantDirOnly.policy @@ -0,0 +1,4 @@ +// policy file that grants read access to source directory +grant { + permission java.io.FilePermission "${test.src}", "read"; +}; diff --git a/test/java/nio/file/attribute/AclFileAttributeView/Basic.java b/test/java/nio/file/attribute/AclFileAttributeView/Basic.java new file mode 100644 index 000000000..3a9960702 --- /dev/null +++ b/test/java/nio/file/attribute/AclFileAttributeView/Basic.java @@ -0,0 +1,166 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.attribute.AclFileAttribueView + * @library ../.. + */ + +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.io.IOException; +import java.util.*; + +import static java.nio.file.attribute.AclEntryType.*; +import static java.nio.file.attribute.AclEntryPermission.*; +import static java.nio.file.attribute.AclEntryFlag.*; + +public class Basic { + + static void printAcl(List acl) { + for (AclEntry entry: acl) { + System.out.format(" %s%n", entry); + } + } + + // sanity check read and writing ACL + static void testReadWrite(Path dir) throws IOException { + Path file = dir.resolve("foo"); + if (file.notExists()) + file.createFile(); + + AclFileAttributeView view = file + .getFileAttributeView(AclFileAttributeView.class); + + // print existing ACL + List acl = view.getAcl(); + System.out.println(" -- current ACL --"); + printAcl(acl); + + // insert entry to grant owner read access + UserPrincipal owner = view.getOwner(); + AclEntry entry = AclEntry.newBuilder() + .setType(ALLOW) + .setPrincipal(owner) + .setPermissions(READ_DATA, READ_ATTRIBUTES) + .build(); + System.out.println(" -- insert (entry 0) --"); + System.out.format(" %s%n", entry); + acl.add(0, entry); + view.setAcl(acl); + + // re-ACL and check entry + List newacl = view.getAcl(); + System.out.println(" -- current ACL --"); + printAcl(acl); + if (!newacl.get(0).equals(entry)) { + throw new RuntimeException("Entry 0 is not expected"); + } + + // if PosixFileAttributeView then repeat test with OWNER@ + if (file.getFileStore().supportsFileAttributeView("posix")) { + owner = file.getFileSystem().getUserPrincipalLookupService() + .lookupPrincipalByName("OWNER@"); + entry = AclEntry.newBuilder(entry).setPrincipal(owner).build(); + + System.out.println(" -- replace (entry 0) --"); + System.out.format(" %s%n", entry); + + acl.set(0, entry); + view.setAcl(acl); + newacl = view.getAcl(); + System.out.println(" -- current ACL --"); + printAcl(acl); + if (!newacl.get(0).equals(entry)) { + throw new RuntimeException("Entry 0 is not expected"); + } + } + } + + static FileAttribute> asAclAttribute(final List acl) { + return new FileAttribute>() { + public String name() { return "acl:acl"; } + public List value() { return acl; } + }; + } + + static void assertEquals(List actual, List expected) { + if (!actual.equals(expected)) { + System.err.format("Actual: %s\n", actual); + System.err.format("Expected: %s\n", expected); + throw new RuntimeException("ACL not expected"); + } + } + + // sanity check create a file or directory with initial ACL + static void testCreateFile(Path dir) throws IOException { + UserPrincipal user = Attributes.getOwner(dir); + + // create file with initial ACL + System.out.println("-- create file with initial ACL --"); + Path file = dir.resolve("gus"); + List fileAcl = Arrays.asList( + AclEntry.newBuilder() + .setType(AclEntryType.ALLOW) + .setPrincipal(user) + .setPermissions(SYNCHRONIZE, READ_DATA, WRITE_DATA, + READ_ATTRIBUTES, READ_ACL, WRITE_ATTRIBUTES, DELETE) + .build()); + file.createFile(asAclAttribute(fileAcl)); + assertEquals(Attributes.getAcl(file), fileAcl); + + // create directory with initial ACL + System.out.println("-- create directory with initial ACL --"); + Path subdir = dir.resolve("stuff"); + List dirAcl = Arrays.asList( + AclEntry.newBuilder() + .setType(AclEntryType.ALLOW) + .setPrincipal(user) + .setPermissions(SYNCHRONIZE, ADD_FILE, DELETE) + .build(), + AclEntry.newBuilder(fileAcl.get(0)) + .setFlags(FILE_INHERIT) + .build()); + subdir.createDirectory(asAclAttribute(dirAcl)); + assertEquals(Attributes.getAcl(subdir), dirAcl); + } + + public static void main(String[] args) throws IOException { + Path dir = TestUtil.createTemporaryDirectory(); + try { + if (!dir.getFileStore().supportsFileAttributeView("acl")) { + System.out.println("ACLs not supported - test skipped!"); + return; + } + testReadWrite(dir); + + // only currently feasible on Windows + if (System.getProperty("os.name").startsWith("Windows")) + testCreateFile(dir); + + } finally { + TestUtil.removeAll(dir); + } + } +} diff --git a/test/java/nio/file/attribute/Attributes/Basic.java b/test/java/nio/file/attribute/Attributes/Basic.java new file mode 100644 index 000000000..8dfde80d9 --- /dev/null +++ b/test/java/nio/file/attribute/Attributes/Basic.java @@ -0,0 +1,254 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.attribute.Attributes + * @library ../.. + */ + +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.io.IOException; +import java.util.*; +import java.util.concurrent.TimeUnit; + +/** + * Exercises getAttribute/setAttribute/readAttributes methods. + */ + +public class Basic { + + static void assertTrue(boolean okay) { + if (!okay) + throw new RuntimeException("Assertion Failed"); + } + + static void checkEqual(Object o1, Object o2) { + if (o1 == null) { + assertTrue(o2 == null); + } else { + assertTrue (o1.equals(o2)); + } + } + + // Exercise getAttribute/setAttribute/readAttributes on basic attributes + static void checkBasicAttributes(FileRef file, BasicFileAttributes attrs) + throws IOException + { + // getAttribute + checkEqual(attrs.size(), Attributes.getAttribute(file, "size")); + checkEqual(attrs.lastModifiedTime(), + Attributes.getAttribute(file, "basic:lastModifiedTime")); + checkEqual(attrs.lastAccessTime(), + Attributes.getAttribute(file, "lastAccessTime")); + checkEqual(attrs.creationTime(), + Attributes.getAttribute(file, "basic:creationTime")); + assertTrue((Boolean)Attributes.getAttribute(file, "isRegularFile")); + assertTrue(!(Boolean)Attributes.getAttribute(file, "basic:isDirectory")); + assertTrue(!(Boolean)Attributes.getAttribute(file, "isSymbolicLink")); + assertTrue(!(Boolean)Attributes.getAttribute(file, "basic:isOther")); + checkEqual(attrs.linkCount(), + (Integer)Attributes.getAttribute(file, "linkCount")); + checkEqual(attrs.fileKey(), Attributes.getAttribute(file, "basic:fileKey")); + + // setAttribute + if (attrs.resolution() == TimeUnit.MILLISECONDS) { + long modTime = attrs.lastModifiedTime(); + Attributes.setAttribute(file, "basic:lastModifiedTime", 0L); + assertTrue(Attributes.readBasicFileAttributes(file).lastModifiedTime() == 0L); + Attributes.setAttribute(file, "lastModifiedTime", modTime); + assertTrue(Attributes.readBasicFileAttributes(file).lastModifiedTime() == modTime); + } + + // readAttributes + Map map; + map = Attributes.readAttributes(file, "*"); + assertTrue(map.size() >= 11); + checkEqual(attrs.isRegularFile(), map.get("isRegularFile")); // check one + + map = Attributes.readAttributes(file, "basic:*"); + assertTrue(map.size() >= 11); + checkEqual(attrs.lastAccessTime(), map.get("lastAccessTime")); // check one + + map = Attributes.readAttributes(file, "size,lastModifiedTime"); + assertTrue(map.size() == 2); + checkEqual(attrs.size(), map.get("size")); + checkEqual(attrs.lastModifiedTime(), map.get("lastModifiedTime")); + + map = Attributes.readAttributes(file, + "basic:lastModifiedTime,lastAccessTime,linkCount,ShouldNotExist"); + assertTrue(map.size() == 3); + checkEqual(attrs.lastModifiedTime(), map.get("lastModifiedTime")); + checkEqual(attrs.lastAccessTime(), map.get("lastAccessTime")); + checkEqual(attrs.lastAccessTime(), map.get("lastAccessTime")); + } + + // Exercise getAttribute/setAttribute/readAttributes on posix attributes + static void checkPosixAttributes(FileRef file, PosixFileAttributes attrs) + throws IOException + { + checkBasicAttributes(file, attrs); + + // getAttribute + checkEqual(attrs.permissions(), + Attributes.getAttribute(file, "posix:permissions")); + checkEqual(attrs.owner(), + Attributes.getAttribute(file, "posix:owner")); + checkEqual(attrs.group(), + Attributes.getAttribute(file, "posix:group")); + + // setAttribute + Set orig = attrs.permissions(); + Set newPerms = new HashSet(orig); + newPerms.remove(PosixFilePermission.OTHERS_READ); + newPerms.remove(PosixFilePermission.OTHERS_WRITE); + newPerms.remove(PosixFilePermission.OTHERS_EXECUTE); + Attributes.setAttribute(file, "posix:permissions", newPerms); + checkEqual(Attributes.readPosixFileAttributes(file).permissions(), newPerms); + Attributes.setAttribute(file, "posix:permissions", orig); + checkEqual(Attributes.readPosixFileAttributes(file).permissions(), orig); + Attributes.setAttribute(file, "posix:owner", attrs.owner()); + Attributes.setAttribute(file, "posix:group", attrs.group()); + + // readAttributes + Map map; + map = Attributes.readAttributes(file, "posix:*"); + assertTrue(map.size() >= 14); + checkEqual(attrs.permissions(), map.get("permissions")); // check one + + map = Attributes.readAttributes(file, "posix:size,owner,ShouldNotExist"); + assertTrue(map.size() == 2); + checkEqual(attrs.size(), map.get("size")); + checkEqual(attrs.owner(), map.get("owner")); + } + + // Exercise getAttribute/setAttribute/readAttributes on unix attributes + static void checkUnixAttributes(FileRef file) throws IOException { + // getAttribute + int mode = (Integer)Attributes.getAttribute(file, "unix:mode"); + long ino = (Long)Attributes.getAttribute(file, "unix:ino"); + long dev = (Long)Attributes.getAttribute(file, "unix:dev"); + long rdev = (Long)Attributes.getAttribute(file, "unix:rdev"); + int uid = (Integer)Attributes.getAttribute(file, "unix:uid"); + int gid = (Integer)Attributes.getAttribute(file, "unix:gid"); + long ctime = (Long)Attributes.getAttribute(file, "unix:ctime"); + + // readAttributes + Map map; + map = Attributes.readAttributes(file, "unix:*"); + assertTrue(map.size() >= 21); + + map = Attributes.readAttributes(file, "unix:size,uid,gid,ShouldNotExist"); + assertTrue(map.size() == 3); + checkEqual(map.get("size"), + Attributes.readBasicFileAttributes(file).size()); + } + + // Exercise getAttribute/setAttribute/readAttributes on dos attributes + static void checkDosAttributes(FileRef file, DosFileAttributes attrs) + throws IOException + { + checkBasicAttributes(file, attrs); + + // getAttribute + checkEqual(attrs.isReadOnly(), + Attributes.getAttribute(file, "dos:readonly")); + checkEqual(attrs.isHidden(), + Attributes.getAttribute(file, "dos:hidden")); + checkEqual(attrs.isSystem(), + Attributes.getAttribute(file, "dos:system")); + checkEqual(attrs.isArchive(), + Attributes.getAttribute(file, "dos:archive")); + + // setAttribute + boolean value; + + value = attrs.isReadOnly(); + Attributes.setAttribute(file, "dos:readonly", !value); + checkEqual(Attributes.readDosFileAttributes(file).isReadOnly(), !value); + Attributes.setAttribute(file, "dos:readonly", value); + checkEqual(Attributes.readDosFileAttributes(file).isReadOnly(), value); + + value = attrs.isHidden(); + Attributes.setAttribute(file, "dos:hidden", !value); + checkEqual(Attributes.readDosFileAttributes(file).isHidden(), !value); + Attributes.setAttribute(file, "dos:hidden", value); + checkEqual(Attributes.readDosFileAttributes(file).isHidden(), value); + + value = attrs.isSystem(); + Attributes.setAttribute(file, "dos:system", !value); + checkEqual(Attributes.readDosFileAttributes(file).isSystem(), !value); + Attributes.setAttribute(file, "dos:system", value); + checkEqual(Attributes.readDosFileAttributes(file).isSystem(), value); + + value = attrs.isArchive(); + Attributes.setAttribute(file, "dos:archive", !value); + checkEqual(Attributes.readDosFileAttributes(file).isArchive(), !value); + Attributes.setAttribute(file, "dos:archive", value); + checkEqual(Attributes.readDosFileAttributes(file).isArchive(), value); + + // readAttributes + Map map; + map = Attributes.readAttributes(file, "dos:*"); + assertTrue(map.size() >= 15); + checkEqual(attrs.isReadOnly(), map.get("readonly")); // check one + + map = Attributes.readAttributes(file, "dos:size,hidden,ShouldNotExist"); + assertTrue(map.size() == 2); + checkEqual(attrs.size(), map.get("size")); + checkEqual(attrs.isHidden(), map.get("hidden")); + } + + static void doTests(Path dir) throws IOException { + Path file = dir.resolve("foo").createFile(); + FileStore store = file.getFileStore(); + try { + checkBasicAttributes(file, + Attributes.readBasicFileAttributes(file)); + + if (store.supportsFileAttributeView("posix")) + checkPosixAttributes(file, + Attributes.readPosixFileAttributes(file)); + + if (store.supportsFileAttributeView("unix")) + checkUnixAttributes(file); + + if (store.supportsFileAttributeView("dos")) + checkDosAttributes(file, + Attributes.readDosFileAttributes(file)); + } finally { + file.delete(); + } + } + + + public static void main(String[] args) throws IOException { + Path dir = TestUtil.createTemporaryDirectory(); + try { + doTests(dir); + } finally { + TestUtil.removeAll(dir); + } + } +} diff --git a/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java b/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java new file mode 100644 index 000000000..1cc192f2a --- /dev/null +++ b/test/java/nio/file/attribute/BasicFileAttributeView/Basic.java @@ -0,0 +1,150 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.attribute.BasicFileAttributeView + * @library ../.. + */ + +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.io.*; + +public class Basic { + + static void check(boolean okay, String msg) { + if (!okay) + throw new RuntimeException(msg); + } + + static void checkAttributesOfDirectory(Path dir) + throws IOException + { + BasicFileAttributes attrs = Attributes.readBasicFileAttributes(dir); + check(attrs.isDirectory(), "is a directory"); + check(!attrs.isRegularFile(), "is not a regular file"); + check(!attrs.isSymbolicLink(), "is not a link"); + check(!attrs.isOther(), "is not other"); + check(attrs.linkCount() >= 1, "should be at least 1"); + + // last-modified-time should match java.io.File + if (attrs.resolution() == TimeUnit.MILLISECONDS) { + File f = new File(dir.toString()); + check(f.lastModified() == attrs.lastModifiedTime(), + "last-modified time should be the same"); + } + } + + static void checkAttributesOfFile(Path dir, Path file) + throws IOException + { + BasicFileAttributes attrs = Attributes.readBasicFileAttributes(file); + check(attrs.isRegularFile(), "is a regular file"); + check(!attrs.isDirectory(), "is not a directory"); + check(!attrs.isSymbolicLink(), "is not a link"); + check(!attrs.isOther(), "is not other"); + check(attrs.linkCount() >= 1, "should be at least 1"); + + // size and last-modified-time should match java.io.File + File f = new File(file.toString()); + check(f.length() == attrs.size(), "size should be the same"); + if (attrs.resolution() == TimeUnit.MILLISECONDS) { + check(f.lastModified() == attrs.lastModifiedTime(), + "last-modified time should be the same"); + } + + // copy last-modified time and file create time from directory to file, + // re-read attribtues, and check they match + BasicFileAttributeView view = + file.getFileAttributeView(BasicFileAttributeView.class); + BasicFileAttributes dirAttrs = Attributes.readBasicFileAttributes(dir); + view.setTimes(dirAttrs.lastModifiedTime(), null, null, dirAttrs.resolution()); + if (dirAttrs.creationTime() != -1L) { + view.setTimes(null, null, dirAttrs.creationTime(), dirAttrs.resolution()); + } + attrs = view.readAttributes(); + check(attrs.lastModifiedTime() == dirAttrs.lastModifiedTime(), + "last-modified time should be equal"); + if (dirAttrs.creationTime() != -1L) { + check(attrs.creationTime() == dirAttrs.creationTime(), + "create time should be the same"); + } + + // security tests + check (!(attrs instanceof PosixFileAttributes), + "should not be able to cast to PosixFileAttributes"); + } + + static void checkAttributesOfLink(Path link) + throws IOException + { + BasicFileAttributes attrs = Attributes + .readBasicFileAttributes(link, LinkOption.NOFOLLOW_LINKS); + check(attrs.isSymbolicLink(), "is a link"); + check(!attrs.isDirectory(), "is a directory"); + check(!attrs.isRegularFile(), "is not a regular file"); + check(!attrs.isOther(), "is not other"); + check(attrs.linkCount() >= 1, "should be at least 1"); + } + + static void attributeReadWriteTests(Path dir) + throws IOException + { + // create file + Path file = dir.resolve("foo"); + OutputStream out = file.newOutputStream(); + try { + out.write("this is not an empty file".getBytes("UTF-8")); + } finally { + out.close(); + } + + // check attributes of directory and file + checkAttributesOfDirectory(dir); + checkAttributesOfFile(dir, file); + + // symbolic links may be supported + Path link = dir.resolve("link"); + try { + link.createSymbolicLink( file ); + } catch (UnsupportedOperationException x) { + return; + } catch (IOException x) { + return; + } + checkAttributesOfLink(link); + } + + public static void main(String[] args) throws IOException { + // create temporary directory to run tests + Path dir = TestUtil.createTemporaryDirectory(); + try { + attributeReadWriteTests(dir); + } finally { + TestUtil.removeAll(dir); + } + } +} diff --git a/test/java/nio/file/attribute/DosFileAttributeView/Basic.java b/test/java/nio/file/attribute/DosFileAttributeView/Basic.java new file mode 100644 index 000000000..3c8a29618 --- /dev/null +++ b/test/java/nio/file/attribute/DosFileAttributeView/Basic.java @@ -0,0 +1,155 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.attribute.DosFileAttributeView + * @library ../.. + */ + +import java.nio.file.*; +import static java.nio.file.LinkOption.*; +import java.nio.file.attribute.*; +import java.util.*; +import java.io.IOException; + +public class Basic { + + static void check(boolean okay) { + if (!okay) + throw new RuntimeException("Test failed"); + } + + // exercise each setter/getter method, leaving all attributes unset + static void testAttributes(DosFileAttributeView view) throws IOException { + view.setReadOnly(true); + check(view.readAttributes().isReadOnly()); + view.setReadOnly(false); + check(!view.readAttributes().isReadOnly()); + view.setHidden(true); + check(view.readAttributes().isHidden()); + view.setHidden(false); + check(!view.readAttributes().isHidden()); + view.setArchive(true); + check(view.readAttributes().isArchive()); + view.setArchive(false); + check(!view.readAttributes().isArchive()); + view.setSystem(true); + check(view.readAttributes().isSystem()); + view.setSystem(false); + check(!view.readAttributes().isSystem()); + } + + // set the value of all attributes + static void setAll(DosFileAttributeView view, boolean value) + throws IOException + { + view.setReadOnly(value); + view.setHidden(value); + view.setArchive(value); + view.setSystem(value); + } + + // read and write FAT attributes + static void readWriteTests(Path dir) throws IOException { + + // create "foo" and test that we can read/write each FAT attribute + Path file = dir.resolve("foo"); + file.newOutputStream().close(); + try { + testAttributes(file + .getFileAttributeView(DosFileAttributeView.class)); + + // Following tests use a symbolic link so skip if not supported + if (!TestUtil.supportsLinks(dir)) + return; + + Path link = dir.resolve("link").createSymbolicLink(file); + + // test following links + testAttributes(link + .getFileAttributeView(DosFileAttributeView.class)); + + // test not following links + try { + try { + testAttributes(link + .getFileAttributeView(DosFileAttributeView.class, NOFOLLOW_LINKS)); + } catch (IOException x) { + // access to link attributes not supported + return; + } + + // set all attributes on link + // run test on target of link (which leaves them all un-set) + // check that attributes of link remain all set + setAll(link + .getFileAttributeView(DosFileAttributeView.class, NOFOLLOW_LINKS), true); + testAttributes(link + .getFileAttributeView(DosFileAttributeView.class)); + DosFileAttributes attrs = Attributes.readDosFileAttributes(link, NOFOLLOW_LINKS); + check(attrs.isReadOnly()); + check(attrs.isHidden()); + check(attrs.isArchive()); + check(attrs.isSystem()); + setAll(link + .getFileAttributeView(DosFileAttributeView.class, NOFOLLOW_LINKS), false); + + // set all attributes on target + // run test on link (which leaves them all un-set) + // check that attributes of target remain all set + setAll(link + .getFileAttributeView(DosFileAttributeView.class), true); + testAttributes(link + .getFileAttributeView(DosFileAttributeView.class, NOFOLLOW_LINKS)); + attrs = Attributes.readDosFileAttributes(link); + check(attrs.isReadOnly()); + check(attrs.isHidden()); + check(attrs.isArchive()); + check(attrs.isSystem()); + setAll(link + .getFileAttributeView(DosFileAttributeView.class), false); + } finally { + TestUtil.deleteUnchecked(link); + } + } finally { + TestUtil.deleteUnchecked(file); + } + } + + public static void main(String[] args) throws IOException { + // create temporary directory to run tests + Path dir = TestUtil.createTemporaryDirectory(); + + try { + // skip test if DOS file attributes not supported + if (!dir.getFileStore().supportsFileAttributeView("dos")) { + System.out.println("DOS file attribute not supported."); + return; + } + readWriteTests(dir); + } finally { + TestUtil.removeAll(dir); + } + } +} diff --git a/test/java/nio/file/attribute/FileStoreAttributeView/Basic.java b/test/java/nio/file/attribute/FileStoreAttributeView/Basic.java new file mode 100644 index 000000000..993e8c1dd --- /dev/null +++ b/test/java/nio/file/attribute/FileStoreAttributeView/Basic.java @@ -0,0 +1,171 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.attribute.FileStoreAttributeView + * @library ../.. + */ + +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.io.File; +import java.io.IOException; +import java.util.*; + +/** + * Simple unit test for FileStoreAttributeView that checks that the disk space + * attribtues are "close" to the equivalent values reported by java.io.File. + */ + +public class Basic { + + static final long K = 1024L; + static final long G = 1024L * 1024L * 1024L; + + /** + * Print out the disk space information for the given file system + */ + static void printFileStore(FileStore fs) throws IOException { + FileStoreSpaceAttributeView view = + fs.getFileStoreAttributeView(FileStoreSpaceAttributeView.class); + FileStoreSpaceAttributes attrs = view.readAttributes(); + + long total = attrs.totalSpace() / K; + long used = (attrs.totalSpace() - attrs.unallocatedSpace()) / K; + long avail = attrs.usableSpace() / K; + + String s = fs.toString(); + if (s.length() > 20) { + System.out.println(s); + s = ""; + } + System.out.format("%-20s %12d %12d %12d\n", s, total, used, avail); + } + + /** + * Check that two values are within 1GB of each other + */ + static void checkWithin1GB(long value1, long value2) { + long diff = Math.abs(value1 - value2); + if (diff > G) + throw new RuntimeException("values differ by more than 1GB"); + } + + /** + * Check disk space on the file system of the given file + */ + static void checkSpace(Path file) throws IOException { + System.out.println(" -- check space -- "); + System.out.println(file); + + FileStore fs = file.getFileStore(); + System.out.format("Filesystem: %s\n", fs); + + // get values reported by java.io.File + File f = new File(file.toString()); + long total = f.getTotalSpace(); + long free = f.getFreeSpace(); + long usable = f.getUsableSpace(); + System.out.println("java.io.File"); + System.out.format(" Total: %d\n", total); + System.out.format(" Free: %d\n", free); + System.out.format(" Usable: %d\n", usable); + + // get values reported by the FileStoreSpaceAttributeView + FileStoreSpaceAttributes attrs = fs + .getFileStoreAttributeView(FileStoreSpaceAttributeView.class) + .readAttributes(); + System.out.println("java.nio.file.FileStoreSpaceAttributeView:"); + System.out.format(" Total: %d\n", attrs.totalSpace()); + System.out.format(" Free: %d\n", attrs.unallocatedSpace()); + System.out.format(" Usable: %d\n", attrs.usableSpace()); + + // check values are "close" + checkWithin1GB(total, attrs.totalSpace()); + checkWithin1GB(free, attrs.unallocatedSpace()); + checkWithin1GB(usable, attrs.usableSpace()); + + // get values by name (and in bulk) + FileStoreAttributeView view = fs.getFileStoreAttributeView("space"); + checkWithin1GB(total, (Long)view.getAttribute("totalSpace")); + checkWithin1GB(free, (Long)view.getAttribute("unallocatedSpace")); + checkWithin1GB(usable, (Long)view.getAttribute("usableSpace")); + Map map = view.readAttributes("*"); + checkWithin1GB(total, (Long)map.get("totalSpace")); + checkWithin1GB(free, (Long)map.get("unallocatedSpace")); + checkWithin1GB(usable, (Long)map.get("usableSpace")); + map = view.readAttributes("totalSpace", "unallocatedSpace", "usableSpace"); + checkWithin1GB(total, (Long)map.get("totalSpace")); + checkWithin1GB(free, (Long)map.get("unallocatedSpace")); + checkWithin1GB(usable, (Long)map.get("usableSpace")); + } + + /** + * Check (Windows-specific) volume attributes + */ + static void checkVolumeAttributes() throws IOException { + System.out.println(" -- volumes -- "); + for (FileStore store: FileSystems.getDefault().getFileStores()) { + FileStoreAttributeView view = store.getFileStoreAttributeView("volume"); + if (view == null) + continue; + Map attrs = view.readAttributes("*"); + int vsn = (Integer)attrs.get("vsn"); + boolean compressed = (Boolean)attrs.get("compressed"); + boolean removable = (Boolean)attrs.get("removable"); + boolean cdrom = (Boolean)attrs.get("cdrom"); + String type; + if (removable) type = "removable"; + else if (cdrom) type = "cdrom"; + else type = "unknown"; + System.out.format("%s (%s) vsn:%x compressed:%b%n", store.name(), + type, vsn, compressed); + } + + } + + public static void main(String[] args) throws IOException { + // print out the disk space information for all file systems + FileSystem fs = FileSystems.getDefault(); + for (FileStore store: fs.getFileStores()) { + printFileStore(store); + } + + Path dir = TestUtil.createTemporaryDirectory(); + try { + // check space using directory + checkSpace(dir); + + // check space using file + Path file = dir.resolve("foo").createFile(); + checkSpace(file); + + // volume attributes (Windows specific) + checkVolumeAttributes(); + + } finally { + TestUtil.removeAll(dir); + } + } +} diff --git a/test/java/nio/file/attribute/PosixFileAttributeView/Basic.java b/test/java/nio/file/attribute/PosixFileAttributeView/Basic.java new file mode 100644 index 000000000..2ee059bb9 --- /dev/null +++ b/test/java/nio/file/attribute/PosixFileAttributeView/Basic.java @@ -0,0 +1,398 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.attribute.PosixFileAttributeView + * @library ../.. + */ + +import java.nio.file.*; +import static java.nio.file.LinkOption.*; +import java.nio.file.attribute.*; +import java.io.IOException; +import java.util.*; + +/** + * Unit test for PosixFileAttributeView, passing silently if this attribute + * view is not available. + */ + +public class Basic { + + /** + * Use view to update permission to the given mode and check that the + * permissions have been updated. + */ + static void testPermissions(PosixFileAttributeView view, String mode) + throws IOException + { + System.out.format("change mode: %s\n", mode); + Set perms = PosixFilePermissions.fromString(mode); + + // change permissions and re-read them. + view.setPermissions(perms); + Set current = view.readAttributes().permissions(); + if (!current.equals(perms)) { + throw new RuntimeException("Actual permissions: " + + PosixFilePermissions.toString(current) + ", expected: " + + PosixFilePermissions.toString(perms)); + } + + // repeat test using setAttribute/getAttribute + view.setAttribute("permissions", perms); + current = (Set)view.getAttribute("permissions"); + if (!current.equals(perms)) { + throw new RuntimeException("Actual permissions: " + + PosixFilePermissions.toString(current) + ", expected: " + + PosixFilePermissions.toString(perms)); + } + } + + /** + * Check that the actual permissions of a file match or make it more + * secure than requested + */ + static void checkSecure(Set requested, + Set actual) + { + for (PosixFilePermission perm: actual) { + if (!requested.contains(perm)) { + throw new RuntimeException("Actual permissions: " + + PosixFilePermissions.toString(actual) + ", requested: " + + PosixFilePermissions.toString(requested) + + " - file is less secure than requested"); + } + } + } + + /** + * Create file with given mode and check that the file is created with a + * mode that is not less secure + */ + static void createWithPermissions(Path file, + String mode) + throws IOException + { + Set requested = PosixFilePermissions.fromString(mode); + FileAttribute> attr = + PosixFilePermissions.asFileAttribute(requested); + System.out.format("create file with mode: %s\n", mode); + + EnumSet options = EnumSet.of(StandardOpenOption.CREATE_NEW, + StandardOpenOption.WRITE); + file.newOutputStream(options, attr).close(); + try { + checkSecure(requested, file + .getFileAttributeView(PosixFileAttributeView.class) + .readAttributes() + .permissions()); + } finally { + file.delete(false); + } + + System.out.format("create directory with mode: %s\n", mode); + file.createDirectory(attr); + try { + checkSecure(requested, file + .getFileAttributeView(PosixFileAttributeView.class) + .readAttributes() + .permissions()); + } finally { + file.delete(false); + } + } + + /** + * Test the setPermissions/permissions methods. + */ + static void permissionTests(Path dir) + throws IOException + { + System.out.println("-- Permission Tests --"); + + // create file and test updating and reading its permissions + Path file = dir.resolve("foo"); + System.out.format("create %s\n", file); + file.newOutputStream().close(); + try { + // get initial permissions so that we can restore them later + PosixFileAttributeView view = file + .getFileAttributeView(PosixFileAttributeView.class); + Set save = view.readAttributes() + .permissions(); + + // test various modes + try { + testPermissions(view, "---------"); + testPermissions(view, "r--------"); + testPermissions(view, "-w-------"); + testPermissions(view, "--x------"); + testPermissions(view, "rwx------"); + testPermissions(view, "---r-----"); + testPermissions(view, "----w----"); + testPermissions(view, "-----x---"); + testPermissions(view, "---rwx---"); + testPermissions(view, "------r--"); + testPermissions(view, "-------w-"); + testPermissions(view, "--------x"); + testPermissions(view, "------rwx"); + testPermissions(view, "r--r-----"); + testPermissions(view, "r--r--r--"); + testPermissions(view, "rw-rw----"); + testPermissions(view, "rwxrwx---"); + testPermissions(view, "rw-rw-r--"); + testPermissions(view, "r-xr-x---"); + testPermissions(view, "r-xr-xr-x"); + testPermissions(view, "rwxrwxrwx"); + } finally { + view.setPermissions(save); + } + } finally { + file.delete(false); + } + + // create link (to file that doesn't exist) and test reading of + // permissions + if (TestUtil.supportsLinks(dir)) { + Path link = dir.resolve("link"); + System.out.format("create link %s\n", link); + link.createSymbolicLink(file); + try { + PosixFileAttributes attrs = Attributes + .readPosixFileAttributes(link, NOFOLLOW_LINKS); + if (!attrs.isSymbolicLink()) { + throw new RuntimeException("not a link"); + } + } finally { + link.delete(false); + } + } + + System.out.println("OKAY"); + } + + /** + * Test creating a file and directory with initial permissios + */ + static void createTests(Path dir) + throws IOException + { + System.out.println("-- Create Tests --"); + + Path file = dir.resolve("foo"); + + createWithPermissions(file, "---------"); + createWithPermissions(file, "r--------"); + createWithPermissions(file, "-w-------"); + createWithPermissions(file, "--x------"); + createWithPermissions(file, "rwx------"); + createWithPermissions(file, "---r-----"); + createWithPermissions(file, "----w----"); + createWithPermissions(file, "-----x---"); + createWithPermissions(file, "---rwx---"); + createWithPermissions(file, "------r--"); + createWithPermissions(file, "-------w-"); + createWithPermissions(file, "--------x"); + createWithPermissions(file, "------rwx"); + createWithPermissions(file, "r--r-----"); + createWithPermissions(file, "r--r--r--"); + createWithPermissions(file, "rw-rw----"); + createWithPermissions(file, "rwxrwx---"); + createWithPermissions(file, "rw-rw-r--"); + createWithPermissions(file, "r-xr-x---"); + createWithPermissions(file, "r-xr-xr-x"); + createWithPermissions(file, "rwxrwxrwx"); + + System.out.println("OKAY"); + } + + /** + * Test setOwner/setGroup methods - this test simply exercises the + * methods to avoid configuration. + */ + static void ownerTests(Path dir) + throws IOException + { + System.out.println("-- Owner Tests --"); + + Path file = dir.resolve("gus"); + System.out.format("create %s\n", file); + + file.newOutputStream().close(); + try { + + // read attributes of directory to get owner/group + PosixFileAttributeView view = file + .getFileAttributeView(PosixFileAttributeView.class); + PosixFileAttributes attrs = view.readAttributes(); + + // set to existing owner/group + view.setOwner(attrs.owner()); + view.setGroup(attrs.group()); + + // repeat test using setAttribute + Map map = view.readAttributes("owner","group"); + view.setAttribute("owner", map.get("owner")); + view.setAttribute("group", map.get("group")); + + } finally { + file.delete(false); + } + + System.out.println("OKAY"); + } + + /** + * Test the lookupPrincipalByName/lookupPrincipalByGroupName methods + */ + static void lookupPrincipalTests(Path dir) + throws IOException + { + System.out.println("-- Lookup UserPrincipal Tests --"); + + UserPrincipalLookupService lookupService = dir.getFileSystem() + .getUserPrincipalLookupService(); + + // read attributes of directory to get owner/group + PosixFileAttributes attrs = Attributes.readPosixFileAttributes(dir); + + // lookup owner and check it matches file's owner + System.out.format("lookup: %s\n", attrs.owner().getName()); + try { + UserPrincipal owner = lookupService.lookupPrincipalByName(attrs.owner().getName()); + if (owner instanceof GroupPrincipal) + throw new RuntimeException("owner is a group?"); + if (!owner.equals(attrs.owner())) + throw new RuntimeException("owner different from file owner"); + } catch (UserPrincipalNotFoundException x) { + System.out.println("user not found - test skipped"); + } + + // lookup group and check it matches file's group-owner + System.out.format("lookup group: %s\n", attrs.group().getName()); + try { + GroupPrincipal group = lookupService.lookupPrincipalByGroupName(attrs.group().getName()); + if (!group.equals(attrs.group())) + throw new RuntimeException("group different from file group-owner"); + } catch (UserPrincipalNotFoundException x) { + System.out.println("group not found - test skipped"); + } + + // test that UserPrincipalNotFoundException is thrown + String invalidPrincipal = "scumbag99"; + try { + System.out.format("lookup: %s\n", invalidPrincipal); + lookupService.lookupPrincipalByName(invalidPrincipal); + throw new RuntimeException("'" + invalidPrincipal + "' is a valid user?"); + } catch (UserPrincipalNotFoundException x) { + } + try { + System.out.format("lookup group: %s\n", invalidPrincipal); + lookupService.lookupPrincipalByGroupName("idonotexist"); + throw new RuntimeException("'" + invalidPrincipal + "' is a valid group?"); + } catch (UserPrincipalNotFoundException x) { + } + System.out.println("OKAY"); + } + + /** + * Test various exceptions are thrown as expected + */ + @SuppressWarnings("unchecked") + static void exceptionsTests(Path dir) + throws IOException + { + System.out.println("-- Exceptions --"); + + PosixFileAttributeView view = dir + .getFileAttributeView(PosixFileAttributeView.class); + + // NullPointerException + try { + view.setOwner(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + view.setGroup(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + + UserPrincipalLookupService lookupService = dir.getFileSystem() + .getUserPrincipalLookupService(); + try { + lookupService.lookupPrincipalByName(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + lookupService.lookupPrincipalByGroupName(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + view.setPermissions(null); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + try { + Set perms = new HashSet(); + perms.add(null); + view.setPermissions(perms); + throw new RuntimeException("NullPointerException not thrown"); + } catch (NullPointerException x) { + } + + // ClassCastException + try { + Set perms = new HashSet(); // raw type + perms.add(new Object()); + view.setPermissions(perms); + throw new RuntimeException("ClassCastException not thrown"); + } catch (ClassCastException x) { + } + + System.out.println("OKAY"); + } + + public static void main(String[] args) throws IOException { + Path dir = TestUtil.createTemporaryDirectory(); + try { + if (!dir.getFileStore().supportsFileAttributeView("posix")) { + System.out.println("PosixFileAttributeView not supported"); + return; + } + + permissionTests(dir); + createTests(dir); + ownerTests(dir); + lookupPrincipalTests(dir); + exceptionsTests(dir); + + } finally { + TestUtil.removeAll(dir); + } + } +} diff --git a/test/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java b/test/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java new file mode 100644 index 000000000..ffdb5b86e --- /dev/null +++ b/test/java/nio/file/attribute/UserDefinedFileAttributeView/Basic.java @@ -0,0 +1,273 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.attribute.UserDefinedFileAttributeView + * @library ../.. + */ + +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.file.*; +import static java.nio.file.LinkOption.*; +import java.nio.file.attribute.*; +import java.util.Arrays; +import java.util.Map; +import java.util.Random; +import java.io.IOException; + +public class Basic { + + private static Random rand = new Random(); + + private static final String ATTR_NAME = "mime_type"; + private static final String ATTR_VALUE = "text/plain"; + private static final String ATTR_VALUE2 = "text/html"; + + static interface Task { + void run() throws Exception; + } + + static void tryCatch(Class ex, Task task) { + boolean caught = false; + try { + task.run(); + } catch (Throwable x) { + if (ex.isAssignableFrom(x.getClass())) { + caught = true; + } else { + throw new RuntimeException(x); + } + } + if (!caught) + throw new RuntimeException(ex.getName() + " expected"); + } + + static void expectNullPointerException(Task task) { + tryCatch(NullPointerException.class, task); + } + + static boolean hasAttribute(UserDefinedFileAttributeView view, String attr) + throws IOException + { + for (String name: view.list()) { + if (name.equals(ATTR_NAME)) + return true; + } + return false; + } + + static void test(Path file, LinkOption... options) throws IOException { + final UserDefinedFileAttributeView view = file + .getFileAttributeView(UserDefinedFileAttributeView.class, options); + ByteBuffer buf = rand.nextBoolean() ? + ByteBuffer.allocate(100) : ByteBuffer.allocateDirect(100); + + // Test: write + buf.put(ATTR_VALUE.getBytes()).flip(); + int size = buf.remaining(); + int nwrote = view.write(ATTR_NAME, buf); + if (nwrote != size) + throw new RuntimeException("Unexpected number of bytes written"); + + // Test: size + if (view.size(ATTR_NAME) != size) + throw new RuntimeException("Unexpected size"); + + // Test: read + buf.clear(); + int nread = view.read(ATTR_NAME, buf); + if (nread != size) + throw new RuntimeException("Unexpected number of bytes read"); + buf.flip(); + String value = Charset.defaultCharset().decode(buf).toString(); + if (!value.equals(ATTR_VALUE)) + throw new RuntimeException("Unexpected attribute value"); + + // Test: read with insufficient space + tryCatch(IOException.class, new Task() { + public void run() throws IOException { + view.read(ATTR_NAME, ByteBuffer.allocateDirect(1)); + }}); + + // Test: replace value + buf.clear(); + buf.put(ATTR_VALUE2.getBytes()).flip(); + size = buf.remaining(); + view.write(ATTR_NAME, buf); + if (view.size(ATTR_NAME) != size) + throw new RuntimeException("Unexpected size"); + + // Test: list + if (!hasAttribute(view, ATTR_NAME)) + throw new RuntimeException("Attribute name not in list"); + + // Test: delete + view.delete(ATTR_NAME); + if (hasAttribute(view, ATTR_NAME)) + throw new RuntimeException("Attribute name in list"); + + // Test: dynamic access + byte[] valueAsBytes = ATTR_VALUE.getBytes(); + view.setAttribute(ATTR_NAME, valueAsBytes); + byte[] actualAsBytes = (byte[])view.getAttribute(ATTR_NAME); + if (!Arrays.equals(valueAsBytes, actualAsBytes)) + throw new RuntimeException("Unexpected attribute value"); + Map map = view.readAttributes(ATTR_NAME); + if (!Arrays.equals(valueAsBytes, (byte[])map.get(ATTR_NAME))) + throw new RuntimeException("Unexpected attribute value"); + map = view.readAttributes(ATTR_NAME, "*"); + if (!Arrays.equals(valueAsBytes, (byte[])map.get(ATTR_NAME))) + throw new RuntimeException("Unexpected attribute value"); + map = view.readAttributes("DoesNotExist"); + if (!map.isEmpty()) + throw new RuntimeException("Map expected to be empty"); + } + + static void miscTests(Path file) throws IOException { + final UserDefinedFileAttributeView view = file + .getFileAttributeView(UserDefinedFileAttributeView.class); + view.write(ATTR_NAME, ByteBuffer.wrap(ATTR_VALUE.getBytes())); + + // NullPointerException + final ByteBuffer buf = ByteBuffer.allocate(100); + + expectNullPointerException(new Task() { + public void run() throws IOException { + view.read(null, buf); + }}); + expectNullPointerException(new Task() { + public void run() throws IOException { + view.read(ATTR_NAME, null); + }}); + expectNullPointerException(new Task() { + public void run() throws IOException { + view.write(null, buf); + }}); + expectNullPointerException(new Task() { + public void run() throws IOException { + view.write(ATTR_NAME, null); + }}); + expectNullPointerException(new Task() { + public void run() throws IOException { + view.size(null); + }}); + expectNullPointerException(new Task() { + public void run() throws IOException { + view.delete(null); + }}); + expectNullPointerException(new Task() { + public void run() throws IOException { + view.getAttribute(null); + }}); + expectNullPointerException(new Task() { + public void run() throws IOException { + view.setAttribute(ATTR_NAME, null); + }}); + expectNullPointerException(new Task() { + public void run() throws IOException { + view.setAttribute(null, new byte[0]); + }}); + expectNullPointerException(new Task() { + public void run() throws IOException { + view.readAttributes(null); + }}); + expectNullPointerException(new Task() { + public void run() throws IOException { + view.readAttributes("*", (String[])null); + }}); + expectNullPointerException(new Task() { + public void run() throws IOException { + view.readAttributes("*", ATTR_NAME, null); + }}); + + // Read-only buffer + tryCatch(IllegalArgumentException.class, new Task() { + public void run() throws IOException { + ByteBuffer buf = ByteBuffer.wrap(ATTR_VALUE.getBytes()).asReadOnlyBuffer(); + view.write(ATTR_NAME, buf); + buf.flip(); + view.read(ATTR_NAME, buf); + }}); + + // Zero bytes remaining + tryCatch(IOException.class, new Task() { + public void run() throws IOException { + ByteBuffer buf = buf = ByteBuffer.allocateDirect(100); + buf.position(buf.capacity()); + view.read(ATTR_NAME, buf); + }}); + } + + public static void main(String[] args) throws IOException { + // create temporary directory to run tests + Path dir = TestUtil.createTemporaryDirectory(); + try { + if (!dir.getFileStore().supportsFileAttributeView("xattr")) { + System.out.println("UserDefinedFileAttributeView not supported - skip test"); + return; + } + + // test access to user defined attributes of regular file + Path file = dir.resolve("foo.html").createFile(); + try { + test(file); + } finally { + file.delete(); + } + + // test access to user define attributes of directory + file = dir.resolve("foo").createDirectory(); + try { + test(file); + } finally { + file.delete(); + } + + // test access to user defined attributes of sym link + if (TestUtil.supportsLinks(dir)) { + Path target = dir.resolve("doesnotexist"); + Path link = dir.resolve("link").createSymbolicLink(target); + try { + test(link, NOFOLLOW_LINKS); + } catch (IOException x) { + // access to attributes of sym link may not be supported + } finally { + link.delete(); + } + } + + // misc. tests + try { + file = dir.resolve("foo.txt").createFile(); + miscTests(dir); + } finally { + file.delete(); + } + + } finally { + TestUtil.removeAll(dir); + } + } + } diff --git a/test/java/nio/file/spi/SetDefaultProvider.java b/test/java/nio/file/spi/SetDefaultProvider.java new file mode 100644 index 000000000..fa600b05c --- /dev/null +++ b/test/java/nio/file/spi/SetDefaultProvider.java @@ -0,0 +1,44 @@ +/* + * 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 4313887 + * @summary Unit test for java.nio.file.spi.FileSystemProvider + * @build TestProvider SetDefaultProvider + * @run main/othervm -Djava.nio.file.spi.DefaultFileSystemProvider=TestProvider SetDefaultProvider + */ + +import java.nio.file.*; +import java.nio.file.spi.*; + +public class SetDefaultProvider { + public static void main(String[] args) throws Exception { + Class c = FileSystems.getDefault().provider().getClass(); + + Class expected = Class.forName("TestProvider", false, + ClassLoader.getSystemClassLoader()); + + if (c != expected) + throw new RuntimeException(); + } +} diff --git a/test/java/nio/file/spi/TestProvider.java b/test/java/nio/file/spi/TestProvider.java new file mode 100644 index 000000000..a6868acb6 --- /dev/null +++ b/test/java/nio/file/spi/TestProvider.java @@ -0,0 +1,128 @@ +/* + * 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. + */ + +import java.nio.file.spi.FileSystemProvider; +import java.nio.file.*; +import java.nio.file.attribute.*; +import java.net.URI; +import java.util.*; +import java.io.IOException; + +public class TestProvider extends FileSystemProvider { + + private final FileSystem theFileSystem; + + public TestProvider(FileSystemProvider defaultProvider) { + theFileSystem = new TestFileSystem(this); + + } + + @Override + public String getScheme() { + return "file"; + } + + @Override + public FileSystem newFileSystem(URI uri, Map env) { + throw new RuntimeException("not implemented"); + } + + @Override + public FileSystem getFileSystem(URI uri) { + return theFileSystem; + } + + @Override + public Path getPath(URI uri) { + throw new RuntimeException("not implemented"); + } + + static class TestFileSystem extends FileSystem { + private final TestProvider provider; + + TestFileSystem(TestProvider provider) { + this.provider = provider; + } + + @Override + public FileSystemProvider provider() { + return provider; + } + + @Override + public void close() throws IOException { + throw new RuntimeException("not implemented"); + } + + @Override + public boolean isOpen() { + throw new RuntimeException("not implemented"); + } + + @Override + public boolean isReadOnly() { + throw new RuntimeException("not implemented"); + } + + @Override + public String getSeparator() { + throw new RuntimeException("not implemented"); + } + + @Override + public Iterable getRootDirectories() { + throw new RuntimeException("not implemented"); + } + + @Override + public Iterable getFileStores() { + throw new RuntimeException("not implemented"); + } + + @Override + public Set supportedFileAttributeViews() { + throw new RuntimeException("not implemented"); + } + + @Override + public Path getPath(String path) { + throw new RuntimeException("not implemented"); + } + + @Override + public PathMatcher getPathMatcher(String syntaxAndPattern) { + throw new RuntimeException("not implemented"); + } + + @Override + public UserPrincipalLookupService getUserPrincipalLookupService() { + throw new RuntimeException("not implemented"); + } + + @Override + public WatchService newWatchService() throws IOException { + throw new RuntimeException("not implemented"); + } + } + +} -- GitLab