diff --git a/.hgtags b/.hgtags index c06fc47e775fc9808f9883b0664dc777820adeaa..e69a21ab6c01a1186608c4a7b27f043e11aa88dc 100644 --- a/.hgtags +++ b/.hgtags @@ -82,3 +82,7 @@ d58354a69011f3d3354765fa3167567c4c4a9612 jdk7-b101 3b0abcb512807bb6f6d27755bc50103211bde6ee jdk7-b105 b91ef6b60f4e19bf4592c6dd594c9bac62487519 jdk7-b106 882103f334bb23745d3fd70fb7928c347478b0f4 jdk7-b107 +17a5d84b75610255a3527e8ede1da19c91ba7a7e jdk7-b108 +ab0d3f54a63f2aadfcdd2e14b81f79362ce454e2 jdk7-b109 +176586cd040e4dd17a5ff6e91f72df10d7442453 jdk7-b110 +fb63a2688db807a73e2a3de7d9bab298f1bff0e8 jdk7-b111 diff --git a/make/com/Makefile b/make/com/Makefile index baf56b3ddb04c9c68e769ce52a21db1bfaa62aef..37472ea3f3a95f1160d73f0387ef31fad6d4de84 100644 --- a/make/com/Makefile +++ b/make/com/Makefile @@ -31,7 +31,7 @@ BUILDDIR = .. PRODUCT = com include $(BUILDDIR)/common/Defs.gmk -SUBDIRS = sun +SUBDIRS = sun oracle include $(BUILDDIR)/common/Subdirs.gmk all build clean clobber:: diff --git a/make/com/oracle/Makefile b/make/com/oracle/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..7c70dc7827045e0dc5ddf704bbb1c7eed65fbfd4 --- /dev/null +++ b/make/com/oracle/Makefile @@ -0,0 +1,34 @@ +# +# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +BUILDDIR = ../.. +PRODUCT = oracle +include $(BUILDDIR)/common/Defs.gmk + +SUBDIRS = net +include $(BUILDDIR)/common/Subdirs.gmk + +all build clean clobber:: + $(SUBDIRS-loop) diff --git a/make/com/oracle/net/Makefile b/make/com/oracle/net/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..5fd30761699c35994279d76534587ce337d12ba9 --- /dev/null +++ b/make/com/oracle/net/Makefile @@ -0,0 +1,39 @@ +# +# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. +# + +BUILDDIR = ../../.. +PRODUCT = oracle +include $(BUILDDIR)/common/Defs.gmk + +# +# Files to compile +# +AUTO_FILES_JAVA_DIRS = com/oracle/net + +# +# Rules +# +include $(BUILDDIR)/common/Classes.gmk + diff --git a/make/common/Release.gmk b/make/common/Release.gmk index bc40dcc9725ad92c0aee793d6ee76bf4ec940953..b8e95bea2eeb3033d515c7dd01283b0e3cdb2e83 100644 --- a/make/common/Release.gmk +++ b/make/common/Release.gmk @@ -573,13 +573,13 @@ $(NOT_RT_JAR_LIST): FRC $(ECHO) "sun/jvmstat/" >> $@ $(ECHO) "sun/nio/cs/ext/" >> $@ $(ECHO) "sun/awt/HKSCS.class" >> $@ - $(ECHO) "sun/awt/motif/X11GB2312$Decoder.class" >> $@ - $(ECHO) "sun/awt/motif/X11GB2312$Encoder.class" >> $@ + $(ECHO) "sun/awt/motif/X11GB2312\$$Decoder.class" >> $@ + $(ECHO) "sun/awt/motif/X11GB2312\$$Encoder.class" >> $@ $(ECHO) "sun/awt/motif/X11GB2312.class" >> $@ - $(ECHO) "sun/awt/motif/X11GBK$Encoder.class" >> $@ + $(ECHO) "sun/awt/motif/X11GBK\$$Encoder.class" >> $@ $(ECHO) "sun/awt/motif/X11GBK.class" >> $@ - $(ECHO) "sun/awt/motif/X11KSC5601$Decoder.class" >> $@ - $(ECHO) "sun/awt/motif/X11KSC5601$Encoder.class" >> $@ + $(ECHO) "sun/awt/motif/X11KSC5601\$$Decoder.class" >> $@ + $(ECHO) "sun/awt/motif/X11KSC5601\$$Encoder.class" >> $@ $(ECHO) "sun/awt/motif/X11KSC5601.class" >> $@ $(ECHO) "sun/rmi/rmic/" >> $@ $(ECHO) "sun/tools/asm/" >> $@ diff --git a/make/common/shared/Compiler-sun.gmk b/make/common/shared/Compiler-sun.gmk index 712b4ffe52ea0fe1f0bd9116d36c2f1193b6aceb..9de1b49912464a705e3b65c4af639226323c9065 100644 --- a/make/common/shared/Compiler-sun.gmk +++ b/make/common/shared/Compiler-sun.gmk @@ -61,6 +61,7 @@ endif # Get compiler version _CC_VER :=$(shell $(CC) -V 2>&1 | $(HEAD) -n 1) CC_VER :=$(call GetVersion,"$(_CC_VER)") +CC_MINORVER :=$(call MinorVersion,$(CC_VER)) # Name of compilers being used COMPILER_VERSION-5.7 = SS10 @@ -69,8 +70,10 @@ COMPILER_VERSION-5.8 = SS11 COMPILER_NAME-5.8 = Sun Studio 11 COMPILER_VERSION-5.9 = SS12 COMPILER_NAME-5.9 = Sun Studio 12 -COMPILER_VERSION-5.10 = SS13 -COMPILER_NAME-5.10 = Sun Studio 13 +COMPILER_VERSION-5.10 = SS12u1 +COMPILER_NAME-5.10 = Sun Studio 12 Update 1 +COMPILER_VERSION-5.11 = OSS12u2 +COMPILER_NAME-5.11 = Oracle Solaris Studio 12 Update 2 COMPILER_VERSION = $(COMPILER_VERSION-$(CC_VER)) COMPILER_NAME = $(COMPILER_NAME-$(CC_VER)) @@ -112,8 +115,8 @@ ifeq ($(ARCH_FAMILY), i586) XARCH_OPTION_OLD/64 += -xarch=amd64 LINT_XARCH_OPTION_OLD/64 += -Xarch=amd64 endif -# Pick the options we want based on the compiler being used. -ifeq ($(shell expr $(CC_VER) \>= 5.9), 1) +# Pick the options we want based on the compiler being used. (5.9 or newer) +ifeq ($(shell expr $(CC_MINORVER) \>= 9), 1) XARCH_OPTION/32 = $(XARCH_OPTION_NEW/32) XARCH_OPTION/64 = $(XARCH_OPTION_NEW/64) LINT_XARCH_OPTION/32 = $(LINT_XARCH_OPTION_NEW/32) diff --git a/make/common/shared/Defs-versions.gmk b/make/common/shared/Defs-versions.gmk index 870ae229f3bf332631ba410f1d630cf2c4b02644..91fe4be805661f928a7a278da5bded4547672cbc 100644 --- a/make/common/shared/Defs-versions.gmk +++ b/make/common/shared/Defs-versions.gmk @@ -120,10 +120,10 @@ ifeq ($(PLATFORM), solaris) else REQUIRED_FREE_SPACE = 1040000 endif - REQUIRED_COMPILER_NAME = Sun Studio 12 - REQUIRED_COMPILER_VERSION = SS12 + REQUIRED_COMPILER_NAME = Sun Studio 12 Update 1 + REQUIRED_COMPILER_VERSION = SS12u1 ifeq ($(CC_VERSION),sun) - REQUIRED_CC_VER = 5.9 + REQUIRED_CC_VER = 5.10 endif ifeq ($(CC_VERSION),gcc) REQUIRED_CC_VER = 3.4.3 @@ -145,7 +145,7 @@ ifeq ($(PLATFORM), linux) REQUIRED_CC_VER = 4.3.0 endif ifeq ($(CC_VERSION),sun) - REQUIRED_CC_VER = 5.9 + REQUIRED_CC_VER = 5.10 endif endif diff --git a/make/docs/NON_CORE_PKGS.gmk b/make/docs/NON_CORE_PKGS.gmk index 6639eecf893f9f73f99c22b0d73a6d847a2b257c..180241483c1ed05f568906df03a7645165dfb94d 100644 --- a/make/docs/NON_CORE_PKGS.gmk +++ b/make/docs/NON_CORE_PKGS.gmk @@ -91,6 +91,8 @@ SCTPAPI_PKGS = com.sun.nio.sctp TRACING_PKGS = com.sun.tracing \ com.sun.tracing.dtrace +ORACLENET_PKGS = com.oracle.net + # non-core packages in rt.jar NON_CORE_PKGS = $(DOMAPI_PKGS) \ $(MGMT_PKGS) \ @@ -101,5 +103,6 @@ NON_CORE_PKGS = $(DOMAPI_PKGS) \ $(HTTPSERVER_PKGS) \ $(SMARTCARDIO_PKGS) \ $(TRACING_PKGS) \ - $(SCTPAPI_PKGS) + $(SCTPAPI_PKGS) \ + $(ORACLENET_PKGS) diff --git a/make/java/java/FILES_java.gmk b/make/java/java/FILES_java.gmk index 416eeb343d079ef2b93d86f3fd5d3326932e2030..d9af3c10fda1d8c0baba5a0297768a1eb1e07be5 100644 --- a/make/java/java/FILES_java.gmk +++ b/make/java/java/FILES_java.gmk @@ -183,10 +183,22 @@ JAVA_JAVA_java = \ java/util/MissingFormatWidthException.java \ java/util/UnknownFormatConversionException.java \ java/util/UnknownFormatFlagsException.java \ + java/util/IllformedLocaleException.java \ java/util/FormatterClosedException.java \ java/util/ListResourceBundle.java \ sun/util/EmptyListResourceBundle.java \ java/util/Locale.java \ + sun/util/locale/AsciiUtil.java \ + sun/util/locale/BaseLocale.java \ + sun/util/locale/Extension.java \ + sun/util/locale/InternalLocaleBuilder.java \ + sun/util/locale/LanguageTag.java \ + sun/util/locale/LocaleExtensions.java \ + sun/util/locale/LocaleObjectCache.java \ + sun/util/locale/LocaleSyntaxException.java \ + sun/util/locale/ParseStatus.java \ + sun/util/locale/StringTokenIterator.java \ + sun/util/locale/UnicodeLocaleExtension.java \ java/util/LocaleISOData.java \ sun/util/LocaleServiceProviderPool.java \ sun/util/LocaleDataMetaInfo.java \ diff --git a/make/java/net/FILES_c.gmk b/make/java/net/FILES_c.gmk index 642244915af18bd97cf88a35ac516ebb58838912..29ce70a6e68f0eb9b0db682e4ba4a1fadd8978c9 100644 --- a/make/java/net/FILES_c.gmk +++ b/make/java/net/FILES_c.gmk @@ -39,10 +39,6 @@ FILES_c = \ ResolverConfigurationImpl.c \ DefaultProxySelector.c -ifeq ($(PLATFORM), solaris) - FILES_c += SdpProvider.c -endif - ifeq ($(PLATFORM), linux) FILES_c += linux_close.c endif diff --git a/make/java/net/Makefile b/make/java/net/Makefile index 0021087cbcf5cbb71f069a7e7f165d4baaba6a7b..db034f2511e5db84688528f6454ee7c01b0b699a 100644 --- a/make/java/net/Makefile +++ b/make/java/net/Makefile @@ -44,6 +44,8 @@ ifeq ($(PLATFORM), windows) endif FILES_c += NTLMAuthSequence.c FILES_c += NetworkInterface_winXP.c +else + FILES_c += SdpSupport.c endif FILES_export = \ @@ -84,7 +86,8 @@ endif # # Find platform specific native code # -vpath %.c $(PLATFORM_SRC)/native/sun/net/dns $(PLATFORM_SRC)/native/sun/net/www/protocol/http/ntlm $(PLATFORM_SRC)/native/sun/net/spi +vpath %.c $(PLATFORM_SRC)/native/sun/net/dns $(PLATFORM_SRC)/native/sun/net/www/protocol/http/ntlm \ + $(PLATFORM_SRC)/native/sun/net/sdp $(PLATFORM_SRC)/native/sun/net/spi # # Include rules diff --git a/make/java/net/mapfile-vers b/make/java/net/mapfile-vers index fbcf905d173be588a04923e64a5ba92ffa07f45c..0e9a46755d428100de1f170985da85cbbc2fa517 100644 --- a/make/java/net/mapfile-vers +++ b/make/java/net/mapfile-vers @@ -88,9 +88,10 @@ SUNWprivate_1.1 { Java_java_net_PlainDatagramSocketImpl_setTimeToLive; Java_sun_net_dns_ResolverConfigurationImpl_localDomain0; Java_sun_net_dns_ResolverConfigurationImpl_fallbackDomain0; + Java_sun_net_sdp_SdpSupport_convert0; + Java_sun_net_sdp_SdpSupport_create0; Java_sun_net_spi_DefaultProxySelector_init; Java_sun_net_spi_DefaultProxySelector_getSystemProxy; - Java_sun_net_spi_SdpProvider_convert; NET_AllocSockaddr; NET_SockaddrToInetAddress; NET_SockaddrEqualsInetAddress; diff --git a/make/java/nio/FILES_java.gmk b/make/java/nio/FILES_java.gmk index 4ad955d87aab5ce7828827ea7fb8bfb52b388add..e49d5267a4761ffce938d64aaedb157615ce3a4c 100644 --- a/make/java/nio/FILES_java.gmk +++ b/make/java/nio/FILES_java.gmk @@ -83,6 +83,7 @@ FILES_src = \ java/nio/file/ClosedFileSystemException.java \ java/nio/file/ClosedWatchServiceException.java \ java/nio/file/CopyOption.java \ + java/nio/file/DirectoryIteratorException.java \ java/nio/file/DirectoryNotEmptyException.java \ java/nio/file/DirectoryStream.java \ java/nio/file/FileAlreadyExistsException.java \ @@ -199,6 +200,7 @@ FILES_src = \ sun/nio/ch/PipeImpl.java \ sun/nio/ch/PollArrayWrapper.java \ sun/nio/ch/Reflect.java \ + sun/nio/ch/Secrets.java \ sun/nio/ch/SelectionKeyImpl.java \ sun/nio/ch/SelectorImpl.java \ sun/nio/ch/SelectorProviderImpl.java \ diff --git a/make/java/nio/mapfile-linux b/make/java/nio/mapfile-linux index 13cc1cd01ec4e250d8d726b1dd0f0748e9c01c5a..c3645c5ed05dcbb2073242bc1b6f30aa084b9d38 100644 --- a/make/java/nio/mapfile-linux +++ b/make/java/nio/mapfile-linux @@ -89,7 +89,7 @@ SUNWprivate_1.1 { Java_sun_nio_ch_IOUtil_drain; Java_sun_nio_ch_IOUtil_fdVal; Java_sun_nio_ch_IOUtil_initIDs; - Java_sun_nio_ch_IOUtil_initPipe; + Java_sun_nio_ch_IOUtil_makePipe; Java_sun_nio_ch_IOUtil_randomBytes; Java_sun_nio_ch_IOUtil_setfdVal; Java_sun_nio_ch_NativeThread_current; diff --git a/make/java/nio/mapfile-solaris b/make/java/nio/mapfile-solaris index 2b80e4e801ef8acc4c6c0d169ee5383b7b07314f..e0dff0a32f36fcb901c49ce59f8bf08e70d6f84a 100644 --- a/make/java/nio/mapfile-solaris +++ b/make/java/nio/mapfile-solaris @@ -76,7 +76,7 @@ SUNWprivate_1.1 { Java_sun_nio_ch_IOUtil_drain; Java_sun_nio_ch_IOUtil_fdVal; Java_sun_nio_ch_IOUtil_initIDs; - Java_sun_nio_ch_IOUtil_initPipe; + Java_sun_nio_ch_IOUtil_makePipe; Java_sun_nio_ch_IOUtil_randomBytes; Java_sun_nio_ch_IOUtil_setfdVal; Java_sun_nio_ch_NativeThread_current; diff --git a/make/java/redist/Makefile b/make/java/redist/Makefile index 08cee4a33013acfea7af4d995f632e9bef9c6b6b..6f86be97f149a340c2b969bd3fb8fa49656036f7 100644 --- a/make/java/redist/Makefile +++ b/make/java/redist/Makefile @@ -43,6 +43,7 @@ SERVER_LOCATION = server CLIENT_LOCATION = client DB_SUFFIX = _db +DTRACE_SUFFIX = _dtrace ifeq ($(PLATFORM), windows) LIB_LOCATION = $(BINDIR) @@ -56,6 +57,7 @@ JVMMAP_NAME = $(LIB_PREFIX)jvm.map JVMPDB_NAME = $(LIB_PREFIX)jvm.pdb LIBJSIG_NAME = $(LIB_PREFIX)jsig.$(LIBRARY_SUFFIX) JVMDB_NAME = $(LIB_PREFIX)jvm$(DB_SUFFIX).$(LIBRARY_SUFFIX) +JVMDTRACE_NAME = $(LIB_PREFIX)jvm$(DTRACE_SUFFIX).$(LIBRARY_SUFFIX) CLASSSHARINGDATA_DIR = $(BUILDDIR)/tools/sharing @@ -161,6 +163,12 @@ IMPORT_LIST += \ ifeq ($(PLATFORM), solaris) IMPORT_LIST += $(LIB_LOCATION)/$(SERVER_LOCATION)/$(JVMDB_NAME) +# The conditional can be removed when import JDKs contain these files. +ifneq ($(wildcard $(HOTSPOT_SERVER_PATH)/$(JVMDTRACE_NAME)),) + IMPORT_LIST += $(LIB_LOCATION)/$(SERVER_LOCATION)/$(JVMDTRACE_NAME) +else + $(warning WARNING: $(HOTSPOT_SERVER_PATH)/$(JVMDB_NAME) not found!) +endif endif ifneq ($(ZERO_BUILD), true) @@ -171,14 +179,29 @@ IMPORT_LIST += $(LIB_LOCATION)/$(CLIENT_LOCATION)/$(LIBJSIG_NAME) ifeq ($(PLATFORM), solaris) # solaris vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv solaris -ifeq ($(ARCH), sparc) - IMPORT_LIST += $(LIB_LOCATION)/$(SERVER_LOCATION)/64/$(JVMDB_NAME) -endif - IMPORT_LIST += $(LIB_LOCATION)/$(CLIENT_LOCATION)/$(JVMDB_NAME) -ifeq ($(ARCH), sparc) +# The conditional can be removed when import JDKs contain these files. +ifneq ($(wildcard $(HOTSPOT_CLIENT_PATH)/$(JVMDTRACE_NAME)),) + IMPORT_LIST += $(LIB_LOCATION)/$(CLIENT_LOCATION)/$(JVMDTRACE_NAME) IMPORT_LIST += $(LIB_LOCATION)/$(CLIENT_LOCATION)/64/$(JVMDB_NAME) + IMPORT_LIST += $(LIB_LOCATION)/$(CLIENT_LOCATION)/64/$(JVMDTRACE_NAME) +else + $(warning WARNING: $(HOTSPOT_CLIENT_PATH)/$(JVMDTRACE_NAME) not found!) +endif + +# The conditional can be removed when import JDKs contain these files. +ifneq ($(wildcard $(HOTSPOT_SERVER_PATH)/64/$(JVMDB_NAME)),) + IMPORT_LIST += $(LIB_LOCATION)/$(SERVER_LOCATION)/64/$(JVMDB_NAME) +else + $(warning WARNING: $(HOTSPOT_SERVER_PATH)/64/$(JVMDB_NAME) not found!) +endif + +# The conditional can be removed when import JDKs contain these files. +ifneq ($(wildcard $(HOTSPOT_SERVER_PATH)/64/$(JVMDTRACE_NAME)),) + IMPORT_LIST += $(LIB_LOCATION)/$(SERVER_LOCATION)/64/$(JVMDTRACE_NAME) +else + $(warning WARNING: $(HOTSPOT_SERVER_PATH)/64/$(JVMDTRACE_NAME) not found!) endif # For backwards compatability, make a link of the 32-bit client JVM to $(LIBDIR) @@ -224,6 +247,18 @@ $(LIB_LOCATION)/$(SERVER_LOCATION)/$(JVMDB_NAME): $(HOTSPOT_SERVER_PATH)/$(JVMDB $(LIB_LOCATION)/$(SERVER_LOCATION)/64/$(JVMDB_NAME): $(HOTSPOT_SERVER_PATH)/64/$(JVMDB_NAME) $(install-import-file) +$(LIB_LOCATION)/$(CLIENT_LOCATION)/$(JVMDTRACE_NAME): $(HOTSPOT_CLIENT_PATH)/$(JVMDTRACE_NAME) + $(install-import-file) + +$(LIB_LOCATION)/$(CLIENT_LOCATION)/64/$(JVMDTRACE_NAME): $(HOTSPOT_CLIENT_PATH)/64/$(JVMDTRACE_NAME) + $(install-import-file) + +$(LIB_LOCATION)/$(SERVER_LOCATION)/$(JVMDTRACE_NAME): $(HOTSPOT_SERVER_PATH)/$(JVMDTRACE_NAME) + $(install-import-file) + +$(LIB_LOCATION)/$(SERVER_LOCATION)/64/$(JVMDTRACE_NAME): $(HOTSPOT_SERVER_PATH)/64/$(JVMDTRACE_NAME) + $(install-import-file) + $(LIB_LOCATION)/$(SERVER_LOCATION)/$(JVM_NAME): $(HOTSPOT_SERVER_PATH)/$(JVM_NAME) $(install-import-file) diff --git a/make/java/text/base/FILES_java.gmk b/make/java/text/base/FILES_java.gmk index 414390822c3fcd57498827a64b8a54bc07963928..8d721300c27c0d17f1e089697bfbd56f6414af43 100644 --- a/make/java/text/base/FILES_java.gmk +++ b/make/java/text/base/FILES_java.gmk @@ -1,5 +1,5 @@ # -# Copyright (c) 1996, 2007, Oracle and/or its affiliates. All rights reserved. +# Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. # DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. # # This code is free software; you can redistribute it and/or modify it @@ -29,6 +29,7 @@ FILES_java = \ java/text/AttributedString.java \ java/text/BreakDictionary.java \ java/text/BreakIterator.java \ + java/text/CalendarBuilder.java \ java/text/CharacterIterator.java \ java/text/CharacterIteratorFieldDelegate.java \ java/text/ChoiceFormat.java \ diff --git a/make/jprt.properties b/make/jprt.properties index 5a6ca7d4c2bd880b22e8947b109b05b07e464992..4ae844da4a286a38c9fb0fadc63c4b21b06e2a87 100644 --- a/make/jprt.properties +++ b/make/jprt.properties @@ -24,32 +24,12 @@ # # Properties for jprt -jprt.tools.default.release=jdk1.7.0 -# Specific platform list -jprt.build.platforms=\ -solaris_sparc_5.10,\ -solaris_sparcv9_5.10,\ -solaris_i586_5.10,\ -solaris_x64_5.10,\ -linux_i586_2.6,\ -linux_x64_2.6,\ -windows_i586_5.0,\ -windows_x64_5.2 +# Use whatever release that the submitted job requests +jprt.tools.default.release=${jprt.submit.release} -# The different build flavors we want +# The different build flavors we want, we override here so we just get these 2 jprt.build.flavors=product,fastdebug -jprt.run.flavors=c1,c2 -jprt.solaris_sparcv9.run.flavors=c2 -jprt.solaris_x64.run.flavors=c2 -jprt.windows_x64.run.flavors=c2 -jprt.linux_x64.run.flavors=c2 -jprt.run.flavor.c1.option=-client -jprt.run.flavor.c2.option=-server - -# Explicitly designate what the 32bit match is for the 64bit build -jprt.solaris_sparcv9.build.platform.match32=solaris_sparc_5.10 -jprt.solaris_x64.build.platform.match32=solaris_i586_5.10 # Standard test target for everybody jprt.test.targets=*-*-*-jvm98 @@ -83,6 +63,6 @@ jprt2.make.rule.test.targets= \ *-product-*-jdk_rmi, \ *-product-*-jdk_swing, \ -# Directories needed to build -jprt.bundle.exclude.src.dirs=build +# Directories to be excluded from the source bundles +jprt.bundle.exclude.src.dirs=build dist webrev diff --git a/make/sun/cmm/lcms/FILES_c_unix.gmk b/make/sun/cmm/lcms/FILES_c_unix.gmk index 86676ffa8264ef991be5ef5e48c0dec04ee800bf..91787becd5e3a715ef9436f24f2c19787f9176d0 100644 --- a/make/sun/cmm/lcms/FILES_c_unix.gmk +++ b/make/sun/cmm/lcms/FILES_c_unix.gmk @@ -25,7 +25,6 @@ FILES_c = \ cmscam02.c \ - cmscam97.c \ cmscgats.c \ cmscnvrt.c \ cmserr.c \ @@ -35,13 +34,17 @@ FILES_c = \ cmsio0.c \ cmsio1.c \ cmslut.c \ - cmsmatsh.c \ + cmsmd5.c \ cmsmtrx.c \ cmsnamed.c \ + cmsopt.c \ cmspack.c \ cmspcs.c \ + cmsplugin.c \ cmsps2.c \ cmssamp.c \ + cmssm.c \ + cmstypes.c \ cmsvirt.c \ cmswtpnt.c \ cmsxform.c \ diff --git a/make/sun/cmm/lcms/FILES_c_windows.gmk b/make/sun/cmm/lcms/FILES_c_windows.gmk index 86676ffa8264ef991be5ef5e48c0dec04ee800bf..91787becd5e3a715ef9436f24f2c19787f9176d0 100644 --- a/make/sun/cmm/lcms/FILES_c_windows.gmk +++ b/make/sun/cmm/lcms/FILES_c_windows.gmk @@ -25,7 +25,6 @@ FILES_c = \ cmscam02.c \ - cmscam97.c \ cmscgats.c \ cmscnvrt.c \ cmserr.c \ @@ -35,13 +34,17 @@ FILES_c = \ cmsio0.c \ cmsio1.c \ cmslut.c \ - cmsmatsh.c \ + cmsmd5.c \ cmsmtrx.c \ cmsnamed.c \ + cmsopt.c \ cmspack.c \ cmspcs.c \ + cmsplugin.c \ cmsps2.c \ cmssamp.c \ + cmssm.c \ + cmstypes.c \ cmsvirt.c \ cmswtpnt.c \ cmsxform.c \ diff --git a/make/sun/cmm/lcms/Makefile b/make/sun/cmm/lcms/Makefile index 5f7ca42e4e3ed5b69476bb6ba5ecfaa53def9f11..1072c2fac7bf7c1bd74b7cccc10b29f98fa7b139 100644 --- a/make/sun/cmm/lcms/Makefile +++ b/make/sun/cmm/lcms/Makefile @@ -80,8 +80,8 @@ vpath %.c $(SHARE_SRC)/native/$(PKGDIR) vpath %.c $(SHARE_SRC)/native/sun/java2d ifeq ($(PLATFORM), windows) - -OTHER_LDLIBS = user32.lib version.lib $(OBJDIR)/../../../sun.awt/awt/$(OBJDIRNAME)/awt.lib +OTHER_CFLAGS += -DCMS_IS_WINDOWS_ -Dsqrtf=sqrt +OTHER_LDLIBS = $(OBJDIR)/../../../sun.awt/awt/$(OBJDIRNAME)/awt.lib OTHER_INCLUDES += -I$(SHARE_SRC)/native/sun/java2d \ -I$(SHARE_SRC)/native/sun/awt/debug diff --git a/make/sun/net/FILES_java.gmk b/make/sun/net/FILES_java.gmk index 3eba9b01a171db171a57491fc547ec97b8498511..1fcec35f4e49bc03238442660fc047fb07028359 100644 --- a/make/sun/net/FILES_java.gmk +++ b/make/sun/net/FILES_java.gmk @@ -53,6 +53,7 @@ FILES_java = \ sun/net/ftp/FtpProtocolException.java \ sun/net/ftp/impl/FtpClient.java \ sun/net/ftp/impl/DefaultFtpClientProvider.java \ + sun/net/sdp/SdpSupport.java \ sun/net/spi/DefaultProxySelector.java \ sun/net/spi/nameservice/NameServiceDescriptor.java \ sun/net/spi/nameservice/NameService.java \ @@ -136,8 +137,6 @@ FILES_java = \ ifeq ($(PLATFORM), windows) FILES_java += sun/net/www/protocol/http/ntlm/NTLMAuthSequence.java -endif - -ifeq ($(PLATFORM), solaris) - FILES_java += sun/net/spi/SdpProvider.java +else + FILES_java += sun/net/sdp/SdpProvider.java endif diff --git a/make/templates/gpl-cp-header b/make/templates/gpl-cp-header index 59473be222ae78dd11b253a95c59006f6894ff87..ed422f45fdc3fd72d193a4d0c87b517e61464131 100644 --- a/make/templates/gpl-cp-header +++ b/make/templates/gpl-cp-header @@ -17,6 +17,6 @@ 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. +Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +or visit www.oracle.com if you need additional information or have any +questions. diff --git a/make/templates/gpl-header b/make/templates/gpl-header index 07dbc05ecb7e9504a81c778969f5a6f0f600d884..e717b45b516a678eb2dfe85b52730942dba98536 100644 --- a/make/templates/gpl-header +++ b/make/templates/gpl-header @@ -15,6 +15,6 @@ 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. +Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +or visit www.oracle.com if you need additional information or have any +questions. diff --git a/src/share/bin/java.c b/src/share/bin/java.c index 6f24932e550471f657a555e4da7135d796b8fa65..4c61fb7c8534f788cf05d49ada742b9014c69843 100644 --- a/src/share/bin/java.c +++ b/src/share/bin/java.c @@ -712,19 +712,19 @@ SetModulesBootClassPath(const char *jrepath) struct stat statbuf; /* return if jre/lib/rt.jar exists */ - sprintf(pathname, "%s%slib%srt.jar", jrepath, separator, separator); + JLI_Snprintf(pathname, sizeof(pathname), "%s%slib%srt.jar", jrepath, separator, separator); if (stat(pathname, &statbuf) == 0) { return; } /* return if jre/classes exists */ - sprintf(pathname, "%s%sclasses", jrepath, separator); + JLI_Snprintf(pathname, sizeof(pathname), "%s%sclasses", jrepath, separator); if (stat(pathname, &statbuf) == 0) { return; } /* modularized jre */ - sprintf(pathname, "%s%slib%s*", jrepath, separator, separator); + JLI_Snprintf(pathname, sizeof(pathname), "%s%slib%s*", jrepath, separator, separator); s = (char *) JLI_WildcardExpandClasspath(pathname); def = JLI_MemAlloc(sizeof(format) - 2 /* strlen("%s") */ @@ -1624,11 +1624,8 @@ ReadKnownVMs(const char *jrepath, const char * arch, jboolean speculative) if (JLI_IsTraceLauncher()) { start = CounterGet(); } - - JLI_StrCpy(jvmCfgName, jrepath); - JLI_StrCat(jvmCfgName, FILESEP "lib" FILESEP); - JLI_StrCat(jvmCfgName, arch); - JLI_StrCat(jvmCfgName, FILESEP "jvm.cfg"); + JLI_Snprintf(jvmCfgName, sizeof(jvmCfgName), "%s%slib%s%s%sjvm.cfg", + jrepath, FILESEP, FILESEP, arch, FILESEP); jvmCfg = fopen(jvmCfgName, "r"); if (jvmCfg == NULL) { diff --git a/src/share/classes/com/oracle/net/Sdp.java b/src/share/classes/com/oracle/net/Sdp.java new file mode 100644 index 0000000000000000000000000000000000000000..3231607382844ff9bb48cb9aa93fbcff4c62677c --- /dev/null +++ b/src/share/classes/com/oracle/net/Sdp.java @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.oracle.net; + +import java.net.Socket; +import java.net.ServerSocket; +import java.net.SocketImpl; +import java.net.SocketImplFactory; +import java.net.SocketException; +import java.nio.channels.SocketChannel; +import java.nio.channels.ServerSocketChannel; +import java.io.IOException; +import java.io.FileDescriptor; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.lang.reflect.Constructor; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.InvocationTargetException; + +import sun.net.sdp.SdpSupport; + +/** + * This class consists exclusively of static methods that Sockets or Channels to + * sockets that support the InfiniBand Sockets Direct Protocol (SDP). + */ + +public final class Sdp { + private Sdp() { } + + /** + * The package-privage ServerSocket(SocketImpl) constructor + */ + private static final Constructor serverSocketCtor; + static { + try { + serverSocketCtor = (Constructor) + ServerSocket.class.getDeclaredConstructor(SocketImpl.class); + setAccessible(serverSocketCtor); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } + + /** + * The package-private SdpSocketImpl() constructor + */ + private static final Constructor socketImplCtor; + static { + try { + Class cl = Class.forName("java.net.SdpSocketImpl", true, null); + socketImplCtor = (Constructor)cl.getDeclaredConstructor(); + setAccessible(socketImplCtor); + } catch (ClassNotFoundException e) { + throw new AssertionError(e); + } catch (NoSuchMethodException e) { + throw new AssertionError(e); + } + } + + private static void setAccessible(final AccessibleObject o) { + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + o.setAccessible(true); + return null; + } + }); + } + + /** + * SDP enabled Socket. + */ + private static class SdpSocket extends Socket { + SdpSocket(SocketImpl impl) throws SocketException { + super(impl); + } + } + + /** + * Creates a SDP enabled SocketImpl + */ + private static SocketImpl createSocketImpl() { + try { + return socketImplCtor.newInstance(); + } catch (InstantiationException x) { + throw new AssertionError(x); + } catch (IllegalAccessException x) { + throw new AssertionError(x); + } catch (InvocationTargetException x) { + throw new AssertionError(x); + } + } + + /** + * Creates an unconnected and unbound SDP socket. The {@code Socket} is + * associated with a {@link java.net.SocketImpl} of the system-default type. + * + * @return a new Socket + * + * @throws UnsupportedOperationException + * If SDP is not supported + * @throws IOException + * If an I/O error occurs + */ + public static Socket openSocket() throws IOException { + SocketImpl impl = createSocketImpl(); + return new SdpSocket(impl); + } + + /** + * Creates an unbound SDP server socket. The {@code ServerSocket} is + * associated with a {@link java.net.SocketImpl} of the system-default type. + * + * @return a new ServerSocket + * + * @throws UnsupportedOperationException + * If SDP is not supported + * @throws IOException + * If an I/O error occurs + */ + public static ServerSocket openServerSocket() throws IOException { + // create ServerSocket via package-private constructor + SocketImpl impl = createSocketImpl(); + try { + return serverSocketCtor.newInstance(impl); + } catch (IllegalAccessException x) { + throw new AssertionError(x); + } catch (InstantiationException x) { + throw new AssertionError(x); + } catch (InvocationTargetException x) { + Throwable cause = x.getCause(); + if (cause instanceof IOException) + throw (IOException)cause; + if (cause instanceof RuntimeException) + throw (RuntimeException)cause; + throw new RuntimeException(x); + } + } + + /** + * Opens a socket channel to a SDP socket. + * + *

The channel will be associated with the system-wide default + * {@link java.nio.channels.spi.SelectorProvider SelectorProvider}. + * + * @return a new SocketChannel + * + * @throws UnsupportedOperationException + * If SDP is not supported or not supported by the default selector + * provider + * @throws IOException + * If an I/O error occurs. + */ + public static SocketChannel openSocketChannel() throws IOException { + FileDescriptor fd = SdpSupport.createSocket(); + return sun.nio.ch.Secrets.newSocketChannel(fd); + } + + /** + * Opens a socket channel to a SDP socket. + * + *

The channel will be associated with the system-wide default + * {@link java.nio.channels.spi.SelectorProvider SelectorProvider}. + * + * @return a new ServerSocketChannel + * + * @throws UnsupportedOperationException + * If SDP is not supported or not supported by the default selector + * provider + * @throws IOException + * If an I/O error occurs + */ + public static ServerSocketChannel openServerSocketChannel() + throws IOException + { + FileDescriptor fd = SdpSupport.createSocket(); + return sun.nio.ch.Secrets.newServerSocketChannel(fd); + } +} diff --git a/src/share/classes/com/sun/java/swing/SwingUtilities3.java b/src/share/classes/com/sun/java/swing/SwingUtilities3.java index fb2700f70b7b7ff9e91cc93ead0b4ba107b98350..1ca240dd743e0f509e52013bba70a76de13837ac 100644 --- a/src/share/classes/com/sun/java/swing/SwingUtilities3.java +++ b/src/share/classes/com/sun/java/swing/SwingUtilities3.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,13 +31,14 @@ import java.util.Collections; import java.util.Map; import java.util.WeakHashMap; import java.util.concurrent.Callable; +import java.applet.Applet; import java.awt.AWTEvent; import java.awt.EventQueue; import java.awt.Component; import java.awt.Container; +import java.awt.Window; import javax.swing.JComponent; import javax.swing.RepaintManager; -import javax.swing.SwingUtilities; /** * A collection of utility methods for Swing. @@ -91,7 +92,7 @@ public class SwingUtilities3 { */ public static void setVsyncRequested(Container rootContainer, boolean isRequested) { - assert SwingUtilities.getRoot(rootContainer) == rootContainer; + assert (rootContainer instanceof Applet) || (rootContainer instanceof Window); if (isRequested) { vsyncedMap.put(rootContainer, Boolean.TRUE); } else { @@ -106,7 +107,7 @@ public class SwingUtilities3 { * @return {@code true} if vsync painting is requested for {@code rootContainer} */ public static boolean isVsyncRequested(Container rootContainer) { - assert SwingUtilities.getRoot(rootContainer) == rootContainer; + assert (rootContainer instanceof Applet) || (rootContainer instanceof Window); return Boolean.TRUE == vsyncedMap.get(rootContainer); } diff --git a/src/share/classes/com/sun/java/swing/plaf/gtk/GTKPainter.java b/src/share/classes/com/sun/java/swing/plaf/gtk/GTKPainter.java index fa9a5a2fb1547ceafc665b2364101f71bef569d5..57a816932b06a2a35ae1f83f24e100f53e8d6089 100644 --- a/src/share/classes/com/sun/java/swing/plaf/gtk/GTKPainter.java +++ b/src/share/classes/com/sun/java/swing/plaf/gtk/GTKPainter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -399,7 +399,7 @@ class GTKPainter extends SynthPainter { } String detail = "arrow"; - if (name == "ScrollBar.button") { + if ((name == "ScrollBar.button") || (name == "TabbedPane.button")) { if (arrowType == ArrowType.UP || arrowType == ArrowType.DOWN) { detail = "vscrollbar"; } else { @@ -409,7 +409,7 @@ class GTKPainter extends SynthPainter { name == "Spinner.previousButton") { detail = "spinbutton"; } else if (name != "ComboBox.arrowButton") { - assert false; + assert false : "unexpected name: " + name; } int gtkState = GTKLookAndFeel.synthStateToGTKState( @@ -436,7 +436,7 @@ class GTKPainter extends SynthPainter { String name = button.getName(); String detail = "button"; int direction = SwingConstants.CENTER; - if (name == "ScrollBar.button") { + if ((name == "ScrollBar.button") || (name == "TabbedPane.button")) { Integer prop = (Integer) button.getClientProperty("__arrow_direction__"); direction = (prop != null) ? @@ -457,7 +457,7 @@ class GTKPainter extends SynthPainter { } else if (name == "Spinner.nextButton") { detail = "spinbutton_up"; } else if (name != "ComboBox.arrowButton") { - assert false; + assert false : "unexpected name: " + name; } int state = context.getComponentState(); diff --git a/src/share/classes/com/sun/java/swing/plaf/gtk/PangoFonts.java b/src/share/classes/com/sun/java/swing/plaf/gtk/PangoFonts.java index 0c76686ebc98c57d8fc3a7b21d0b44b13931accd..1a60d277757c568bd224a967668200b9f69f9a9e 100644 --- a/src/share/classes/com/sun/java/swing/plaf/gtk/PangoFonts.java +++ b/src/share/classes/com/sun/java/swing/plaf/gtk/PangoFonts.java @@ -150,11 +150,6 @@ class PangoFonts { * case for it to be a problem the values would have to be different. * It also seems unlikely to arise except when a user explicitly * deletes the X resource database entry. - * 3) Because of rounding errors sizes may differ very slightly - * between JDK and GTK. To fix that would at the very least require - * Swing to specify floating pt font sizes. - * Eg "10 pts" for GTK at 96 dpi to get the same size at Java 2D's - * 72 dpi you'd need to specify exactly 13.33. * There also some other issues to be aware of for the future: * GTK specifies the Xft.dpi value as server-wide which when used * on systems with 2 distinct X screens with different physical DPI @@ -197,11 +192,16 @@ class PangoFonts { String fcFamilyLC = family.toLowerCase(); if (FontUtilities.mapFcName(fcFamilyLC) != null) { /* family is a Fc/Pango logical font which we need to expand. */ - return FontUtilities.getFontConfigFUIR(fcFamilyLC, style, size); + Font font = FontUtilities.getFontConfigFUIR(fcFamilyLC, style, size); + font = font.deriveFont(style, (float)dsize); + return new FontUIResource(font); } else { /* It's a physical font which we will create with a fallback */ - Font font = new FontUIResource(family, style, size); - return FontUtilities.getCompositeFontUIResource(font); + Font font = new Font(family, style, size); + /* a roundabout way to set the font size in floating points */ + font = font.deriveFont(style, (float)dsize); + FontUIResource fuir = new FontUIResource(font); + return FontUtilities.getCompositeFontUIResource(fuir); } } diff --git a/src/share/classes/com/sun/java/swing/plaf/motif/MotifBorders.java b/src/share/classes/com/sun/java/swing/plaf/motif/MotifBorders.java index cd68cca1bdf3fb45ccd37ac7b0c568a9b79b0c33..4b140f17f89c4786b1f36d6cbbf8db6bb4087537 100644 --- a/src/share/classes/com/sun/java/swing/plaf/motif/MotifBorders.java +++ b/src/share/classes/com/sun/java/swing/plaf/motif/MotifBorders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,8 +40,6 @@ import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; -import java.io.Serializable; - /** * Factory object that can vend Icons appropriate for the basic L & F. *

@@ -99,7 +97,7 @@ public class MotifBorders { } public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) { - if (((JComponent)c).hasFocus()) { + if (c.hasFocus()) { g.setColor(focus); g.drawRect(x, y, w-1, h-1); } else { @@ -233,6 +231,9 @@ public class MotifBorders { } public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + if (!(c instanceof JMenuBar)) { + return; + } JMenuBar menuBar = (JMenuBar)c; if (menuBar.isBorderPainted() == true) { // this draws the MenuBar border @@ -658,6 +659,9 @@ public class MotifBorders { * @param height the height of the painted border */ public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + if (!(c instanceof JPopupMenu)) { + return; + } Font origFont = g.getFont(); Color origColor = g.getColor(); @@ -701,6 +705,9 @@ public class MotifBorders { * @param insets the object to be reinitialized */ public Insets getBorderInsets(Component c, Insets insets) { + if (!(c instanceof JPopupMenu)) { + return insets; + } FontMetrics fm; int descent = 0; int ascent = 16; diff --git a/src/share/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java b/src/share/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java index 537618c6b01047371bd26d441cfd14b825edac59..286c3a516bddf502b10b8ff283c52a91a98985ca 100644 --- a/src/share/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java +++ b/src/share/classes/com/sun/java/swing/plaf/windows/WindowsBorders.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -32,12 +32,8 @@ import javax.swing.plaf.basic.*; import java.awt.Component; import java.awt.Insets; -import java.awt.Dimension; -import java.awt.Image; -import java.awt.Rectangle; import java.awt.Color; import java.awt.Graphics; -import java.io.Serializable; import static com.sun.java.swing.plaf.windows.TMSchema.*; import static com.sun.java.swing.plaf.windows.XPStyle.Skin; @@ -159,6 +155,9 @@ public class WindowsBorders { public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { + if (!(c instanceof JToolBar)) { + return; + } g.translate(x, y); XPStyle xp = XPStyle.getXP(); @@ -190,33 +189,33 @@ public class WindowsBorders { } else { - if (!vertical) { - if (c.getComponentOrientation().isLeftToRight()) { + if (!vertical) { + if (c.getComponentOrientation().isLeftToRight()) { + g.setColor(shadow); + g.drawLine(4, 3, 4, height - 4); + g.drawLine(4, height - 4, 2, height - 4); + + g.setColor(highlight); + g.drawLine(2, 3, 3, 3); + g.drawLine(2, 3, 2, height - 5); + } else { + g.setColor(shadow); + g.drawLine(width - 3, 3, width - 3, height - 4); + g.drawLine(width - 4, height - 4, width - 4, height - 4); + + g.setColor(highlight); + g.drawLine(width - 5, 3, width - 4, 3); + g.drawLine(width - 5, 3, width - 5, height - 5); + } + } else { // Vertical g.setColor(shadow); - g.drawLine(4, 3, 4, height - 4); - g.drawLine(4, height - 4, 2, height - 4); + g.drawLine(3, 4, width - 4, 4); + g.drawLine(width - 4, 2, width - 4, 4); g.setColor(highlight); - g.drawLine(2, 3, 3, 3); - g.drawLine(2, 3, 2, height - 5); - } else { - g.setColor(shadow); - g.drawLine(width - 3, 3, width - 3, height - 4); - g.drawLine(width - 4, height - 4, width - 4, height - 4); - - g.setColor(highlight); - g.drawLine(width - 5, 3, width - 4, 3); - g.drawLine(width - 5, 3, width - 5, height - 5); + g.drawLine(3, 2, width - 4, 2); + g.drawLine(3, 2, 3, 3); } - } else { // Vertical - g.setColor(shadow); - g.drawLine(3, 4, width - 4, 4); - g.drawLine(width - 4, 2, width - 4, 4); - - g.setColor(highlight); - g.drawLine(3, 2, width - 4, 2); - g.drawLine(3, 2, 3, 3); - } } } @@ -225,6 +224,9 @@ public class WindowsBorders { public Insets getBorderInsets(Component c, Insets insets) { insets.set(1,1,1,1); + if (!(c instanceof JToolBar)) { + return insets; + } if (((JToolBar)c).isFloatable()) { int gripInset = (XPStyle.getXP() != null) ? 12 : 9; if (((JToolBar)c).getOrientation() == HORIZONTAL) { diff --git a/src/share/classes/com/sun/media/sound/AbstractMidiDevice.java b/src/share/classes/com/sun/media/sound/AbstractMidiDevice.java index 4a4734d241c16ed7617e14025df9b2e931fa7085..97bafe3f9a6ac5178010fdfb3e16dc3f3e95056a 100644 --- a/src/share/classes/com/sun/media/sound/AbstractMidiDevice.java +++ b/src/share/classes/com/sun/media/sound/AbstractMidiDevice.java @@ -474,7 +474,7 @@ abstract class AbstractMidiDevice implements MidiDevice, ReferenceCountingDevice This is necessary for Receivers retrieved via MidiSystem.getReceiver() (which opens the device implicitely). */ - protected abstract class AbstractReceiver implements Receiver { + protected abstract class AbstractReceiver implements MidiDeviceReceiver { private boolean open = true; @@ -508,6 +508,10 @@ abstract class AbstractMidiDevice implements MidiDevice, ReferenceCountingDevice AbstractMidiDevice.this.closeInternal(this); } + public MidiDevice getMidiDevice() { + return AbstractMidiDevice.this; + } + protected boolean isOpen() { return open; } @@ -529,7 +533,7 @@ abstract class AbstractMidiDevice implements MidiDevice, ReferenceCountingDevice * Also, it has some optimizations regarding sending to the Receivers, * for known Receivers, and managing itself in the TransmitterList. */ - protected class BasicTransmitter implements Transmitter { + protected class BasicTransmitter implements MidiDeviceTransmitter { private Receiver receiver = null; TransmitterList tlist = null; @@ -568,6 +572,9 @@ abstract class AbstractMidiDevice implements MidiDevice, ReferenceCountingDevice } } + public MidiDevice getMidiDevice() { + return AbstractMidiDevice.this; + } } // class BasicTransmitter diff --git a/src/share/classes/com/sun/media/sound/AudioFloatConverter.java b/src/share/classes/com/sun/media/sound/AudioFloatConverter.java index 45bd2a52c973c1ba7b4ec9e9739d10e20ea4fe60..95068ed6126b3b500e737a02a06acbdfb513b29c 100644 --- a/src/share/classes/com/sun/media/sound/AudioFloatConverter.java +++ b/src/share/classes/com/sun/media/sound/AudioFloatConverter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,8 +40,6 @@ import javax.sound.sampled.AudioFormat.Encoding; */ public abstract class AudioFloatConverter { - public static final Encoding PCM_FLOAT = new Encoding("PCM_FLOAT"); - /*************************************************************************** * * LSB Filter, used filter least significant byte in samples arrays. @@ -982,7 +980,7 @@ public abstract class AudioFloatConverter { format.getSampleSizeInBits() + 7) / 8) - 4); } } - } else if (format.getEncoding().equals(PCM_FLOAT)) { + } else if (format.getEncoding().equals(Encoding.PCM_FLOAT)) { if (format.getSampleSizeInBits() == 32) { if (format.isBigEndian()) conv = new AudioFloatConversion32B(); diff --git a/src/share/classes/com/sun/media/sound/AudioFloatFormatConverter.java b/src/share/classes/com/sun/media/sound/AudioFloatFormatConverter.java index 506eab542ac2e059391ef57dced8a2089a0bc362..0b73dca066633c31b222f0eccf0a5ce4271d4277 100644 --- a/src/share/classes/com/sun/media/sound/AudioFloatFormatConverter.java +++ b/src/share/classes/com/sun/media/sound/AudioFloatFormatConverter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -175,7 +175,6 @@ public class AudioFloatFormatConverter extends FormatConversionProvider { for (int c = 0; c < targetChannels; c++) { for (int i = 0, ix = off + c; i < len2; i++, ix += cs) { b[ix] = conversion_buffer[i]; - ; } } } else if (targetChannels == 1) { @@ -186,7 +185,6 @@ public class AudioFloatFormatConverter extends FormatConversionProvider { for (int c = 1; c < sourceChannels; c++) { for (int i = c, ix = off; i < len2; i += cs, ix++) { b[ix] += conversion_buffer[i]; - ; } } float vol = 1f / ((float) sourceChannels); @@ -390,6 +388,7 @@ public class AudioFloatFormatConverter extends FormatConversionProvider { return -1; if (len < 0) return 0; + int offlen = off + len; int remain = len / nrofchannels; int destPos = 0; int in_end = ibuffer_len; @@ -423,7 +422,7 @@ public class AudioFloatFormatConverter extends FormatConversionProvider { for (int c = 0; c < nrofchannels; c++) { int ix = 0; float[] buff = cbuffer[c]; - for (int i = c; i < b.length; i += nrofchannels) { + for (int i = c + off; i < offlen; i += nrofchannels) { b[i] = buff[ix++]; } } @@ -447,7 +446,7 @@ public class AudioFloatFormatConverter extends FormatConversionProvider { } public long skip(long len) throws IOException { - if (len > 0) + if (len < 0) return 0; if (skipbuffer == null) skipbuffer = new float[1024 * targetFormat.getFrameSize()]; @@ -470,7 +469,7 @@ public class AudioFloatFormatConverter extends FormatConversionProvider { } private Encoding[] formats = { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED, - AudioFloatConverter.PCM_FLOAT }; + Encoding.PCM_FLOAT }; public AudioInputStream getAudioInputStream(Encoding targetEncoding, AudioInputStream sourceStream) { @@ -482,7 +481,7 @@ public class AudioFloatFormatConverter extends FormatConversionProvider { float samplerate = format.getSampleRate(); int bits = format.getSampleSizeInBits(); boolean bigendian = format.isBigEndian(); - if (targetEncoding.equals(AudioFloatConverter.PCM_FLOAT)) + if (targetEncoding.equals(Encoding.PCM_FLOAT)) bits = 32; AudioFormat targetFormat = new AudioFormat(encoding, samplerate, bits, channels, channels * bits / 8, samplerate, bigendian); @@ -521,19 +520,19 @@ public class AudioFloatFormatConverter extends FormatConversionProvider { public Encoding[] getSourceEncodings() { return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED, - AudioFloatConverter.PCM_FLOAT }; + Encoding.PCM_FLOAT }; } public Encoding[] getTargetEncodings() { return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED, - AudioFloatConverter.PCM_FLOAT }; + Encoding.PCM_FLOAT }; } public Encoding[] getTargetEncodings(AudioFormat sourceFormat) { if (AudioFloatConverter.getConverter(sourceFormat) == null) return new Encoding[0]; return new Encoding[] { Encoding.PCM_SIGNED, Encoding.PCM_UNSIGNED, - AudioFloatConverter.PCM_FLOAT }; + Encoding.PCM_FLOAT }; } public AudioFormat[] getTargetFormats(Encoding targetEncoding, @@ -572,17 +571,17 @@ public class AudioFloatFormatConverter extends FormatConversionProvider { } } - if (targetEncoding.equals(AudioFloatConverter.PCM_FLOAT)) { - formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT, + if (targetEncoding.equals(Encoding.PCM_FLOAT)) { + formats.add(new AudioFormat(Encoding.PCM_FLOAT, AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4, AudioSystem.NOT_SPECIFIED, false)); - formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT, + formats.add(new AudioFormat(Encoding.PCM_FLOAT, AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4, AudioSystem.NOT_SPECIFIED, true)); - formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT, + formats.add(new AudioFormat(Encoding.PCM_FLOAT, AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8, AudioSystem.NOT_SPECIFIED, false)); - formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT, + formats.add(new AudioFormat(Encoding.PCM_FLOAT, AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8, AudioSystem.NOT_SPECIFIED, true)); } diff --git a/src/share/classes/com/sun/media/sound/AudioSynthesizerPropertyInfo.java b/src/share/classes/com/sun/media/sound/AudioSynthesizerPropertyInfo.java index c37f48ac37c24e6854f449b5233afa81c82b9271..2c5e160a4bb595e7ff275ed6a3a0534be317c23b 100644 --- a/src/share/classes/com/sun/media/sound/AudioSynthesizerPropertyInfo.java +++ b/src/share/classes/com/sun/media/sound/AudioSynthesizerPropertyInfo.java @@ -42,11 +42,14 @@ public class AudioSynthesizerPropertyInfo { */ public AudioSynthesizerPropertyInfo(String name, Object value) { this.name = name; - this.value = value; if (value instanceof Class) valueClass = (Class)value; - else if (value != null) - valueClass = value.getClass(); + else + { + this.value = value; + if (value != null) + valueClass = value.getClass(); + } } /** * The name of the property. diff --git a/src/share/classes/com/sun/media/sound/DLSSoundbank.java b/src/share/classes/com/sun/media/sound/DLSSoundbank.java index 6fdae7c3988cd21326f8637db5d36fd9a0317495..2b490dd6061f2036ce794684ddbdc0e756ab5b1e 100644 --- a/src/share/classes/com/sun/media/sound/DLSSoundbank.java +++ b/src/share/classes/com/sun/media/sound/DLSSoundbank.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -781,7 +781,7 @@ public class DLSSoundbank implements Soundbank { } if (sampleformat == 3) { audioformat = new AudioFormat( - AudioFloatConverter.PCM_FLOAT, samplerate, bits, + Encoding.PCM_FLOAT, samplerate, bits, channels, framesize, samplerate, false); } @@ -965,7 +965,7 @@ public class DLSSoundbank implements Soundbank { sampleformat = 1; else if (audioformat.getEncoding().equals(Encoding.PCM_SIGNED)) sampleformat = 1; - else if (audioformat.getEncoding().equals(AudioFloatConverter.PCM_FLOAT)) + else if (audioformat.getEncoding().equals(Encoding.PCM_FLOAT)) sampleformat = 3; fmt_chunk.writeUnsignedShort(sampleformat); diff --git a/src/share/classes/com/sun/media/sound/MidiDeviceReceiverEnvelope.java b/src/share/classes/com/sun/media/sound/MidiDeviceReceiverEnvelope.java new file mode 100644 index 0000000000000000000000000000000000000000..9a962bbd4127aeec18103aaa438a95315da6b331 --- /dev/null +++ b/src/share/classes/com/sun/media/sound/MidiDeviceReceiverEnvelope.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.media.sound; + +import javax.sound.midi.*; + + +/** + * Helper class which allows to convert {@code Receiver} + * to {@code MidiDeviceReceiver}. + * + * @author Alex Menkov + */ +public class MidiDeviceReceiverEnvelope implements MidiDeviceReceiver { + + private final MidiDevice device; + private final Receiver receiver; + + /** + * Creates a new {@code MidiDeviceReceiverEnvelope} object which + * envelops the specified {@code Receiver} + * and is owned by the specified {@code MidiDevice}. + * + * @param device the owner {@code MidiDevice} + * @param receiver the {@code Receiver} to be enveloped + */ + public MidiDeviceReceiverEnvelope(MidiDevice device, Receiver receiver) { + if (device == null || receiver == null) { + throw new NullPointerException(); + } + this.device = device; + this.receiver = receiver; + } + + // Receiver implementation + public void close() { + receiver.close(); + } + + public void send(MidiMessage message, long timeStamp) { + receiver.send(message, timeStamp); + } + + // MidiDeviceReceiver implementation + public MidiDevice getMidiDevice() { + return device; + } + + /** + * Obtains the receiver enveloped + * by this {@code MidiDeviceReceiverEnvelope} object. + * + * @return the enveloped receiver + */ + public Receiver getReceiver() { + return receiver; + } +} diff --git a/src/share/classes/com/sun/media/sound/MidiDeviceTransmitterEnvelope.java b/src/share/classes/com/sun/media/sound/MidiDeviceTransmitterEnvelope.java new file mode 100644 index 0000000000000000000000000000000000000000..e20d430a042cd7ae7ec3b970ab36ed68eb1d42e8 --- /dev/null +++ b/src/share/classes/com/sun/media/sound/MidiDeviceTransmitterEnvelope.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.media.sound; + +import javax.sound.midi.*; + + +/** + * Helper class which allows to convert {@code Transmitter} + * to {@code MidiDeviceTransmitter}. + * + * @author Alex Menkov + */ +public class MidiDeviceTransmitterEnvelope implements MidiDeviceTransmitter { + + private final MidiDevice device; + private final Transmitter transmitter; + + /** + * Creates a new {@code MidiDeviceTransmitterEnvelope} object which + * envelops the specified {@code Transmitter} + * and is owned by the specified {@code MidiDevice}. + * + * @param device the owner {@code MidiDevice} + * @param transmitter the {@code Transmitter} to be enveloped + */ + public MidiDeviceTransmitterEnvelope(MidiDevice device, Transmitter transmitter) { + if (device == null || transmitter == null) { + throw new NullPointerException(); + } + this.device = device; + this.transmitter = transmitter; + } + + // Transmitter implementation + public void setReceiver(Receiver receiver) { + transmitter.setReceiver(receiver); + } + + public Receiver getReceiver() { + return transmitter.getReceiver(); + } + + public void close() { + transmitter.close(); + } + + + // MidiDeviceReceiver implementation + public MidiDevice getMidiDevice() { + return device; + } + + /** + * Obtains the transmitter enveloped + * by this {@code MidiDeviceTransmitterEnvelope} object. + * + * @return the enveloped transmitter + */ + public Transmitter getTransmitter() { + return transmitter; + } +} diff --git a/src/share/classes/com/sun/media/sound/ModelByteBufferWavetable.java b/src/share/classes/com/sun/media/sound/ModelByteBufferWavetable.java index 9ad439a6f696f7ff26d827002a474633dd587256..bc3f63ac1a17d3e411b50730b86f661cc8800d83 100644 --- a/src/share/classes/com/sun/media/sound/ModelByteBufferWavetable.java +++ b/src/share/classes/com/sun/media/sound/ModelByteBufferWavetable.java @@ -205,7 +205,8 @@ public class ModelByteBufferWavetable implements ModelWavetable { } if (buffer.array() == null) { return AudioFloatInputStream.getInputStream(new AudioInputStream( - buffer.getInputStream(), format, buffer.capacity())); + buffer.getInputStream(), format, + buffer.capacity() / format.getFrameSize())); } if (buffer8 != null) { if (format.getEncoding().equals(Encoding.PCM_SIGNED) diff --git a/src/share/classes/com/sun/media/sound/ModelInstrument.java b/src/share/classes/com/sun/media/sound/ModelInstrument.java index 5ecb84c0d178ef4d249b87a7c701ff4e77effbc3..89969209f71f056995e76e28f3b46c175f4a71ca 100644 --- a/src/share/classes/com/sun/media/sound/ModelInstrument.java +++ b/src/share/classes/com/sun/media/sound/ModelInstrument.java @@ -56,7 +56,7 @@ public abstract class ModelInstrument extends Instrument { public ModelDirector getDirector(ModelPerformer[] performers, MidiChannel channel, ModelDirectedPlayer player) { - return new ModelStandardDirector(performers, player); + return new ModelStandardIndexedDirector(performers, player); } public ModelPerformer[] getPerformers() { diff --git a/src/share/classes/com/sun/media/sound/ModelStandardIndexedDirector.java b/src/share/classes/com/sun/media/sound/ModelStandardIndexedDirector.java new file mode 100644 index 0000000000000000000000000000000000000000..d5181e39cee56bc9b8bffc7721f9c9d2fa09569b --- /dev/null +++ b/src/share/classes/com/sun/media/sound/ModelStandardIndexedDirector.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.sun.media.sound; + +/** + * A standard indexed director who chooses performers + * by there keyfrom,keyto,velfrom,velto properties. + * + * @author Karl Helgason + */ +public class ModelStandardIndexedDirector implements ModelDirector { + + ModelPerformer[] performers; + ModelDirectedPlayer player; + boolean noteOnUsed = false; + boolean noteOffUsed = false; + + // Variables needed for index + byte[][] trantables; + int[] counters; + int[][] mat; + + public ModelStandardIndexedDirector(ModelPerformer[] performers, + ModelDirectedPlayer player) { + this.performers = performers; + this.player = player; + for (int i = 0; i < performers.length; i++) { + ModelPerformer p = performers[i]; + if (p.isReleaseTriggered()) { + noteOffUsed = true; + } else { + noteOnUsed = true; + } + } + buildindex(); + } + + private int[] lookupIndex(int x, int y) { + if ((x >= 0) && (x < 128) && (y >= 0) && (y < 128)) { + int xt = trantables[0][x]; + int yt = trantables[1][y]; + if (xt != -1 && yt != -1) { + return mat[xt + yt * counters[0]]; + } + } + return null; + } + + private int restrict(int value) { + if(value < 0) return 0; + if(value > 127) return 127; + return value; + } + + private void buildindex() { + trantables = new byte[2][129]; + counters = new int[trantables.length]; + for (ModelPerformer performer : performers) { + int keyFrom = performer.getKeyFrom(); + int keyTo = performer.getKeyTo(); + int velFrom = performer.getVelFrom(); + int velTo = performer.getVelTo(); + if (keyFrom > keyTo) continue; + if (velFrom > velTo) continue; + keyFrom = restrict(keyFrom); + keyTo = restrict(keyTo); + velFrom = restrict(velFrom); + velTo = restrict(velTo); + trantables[0][keyFrom] = 1; + trantables[0][keyTo + 1] = 1; + trantables[1][velFrom] = 1; + trantables[1][velTo + 1] = 1; + } + for (int d = 0; d < trantables.length; d++) { + byte[] trantable = trantables[d]; + int transize = trantable.length; + for (int i = transize - 1; i >= 0; i--) { + if (trantable[i] == 1) { + trantable[i] = -1; + break; + } + trantable[i] = -1; + } + int counter = -1; + for (int i = 0; i < transize; i++) { + if (trantable[i] != 0) { + counter++; + if (trantable[i] == -1) + break; + } + trantable[i] = (byte) counter; + } + counters[d] = counter; + } + mat = new int[counters[0] * counters[1]][]; + int ix = 0; + for (ModelPerformer performer : performers) { + int keyFrom = performer.getKeyFrom(); + int keyTo = performer.getKeyTo(); + int velFrom = performer.getVelFrom(); + int velTo = performer.getVelTo(); + if (keyFrom > keyTo) continue; + if (velFrom > velTo) continue; + keyFrom = restrict(keyFrom); + keyTo = restrict(keyTo); + velFrom = restrict(velFrom); + velTo = restrict(velTo); + int x_from = trantables[0][keyFrom]; + int x_to = trantables[0][keyTo + 1]; + int y_from = trantables[1][velFrom]; + int y_to = trantables[1][velTo + 1]; + if (x_to == -1) + x_to = counters[0]; + if (y_to == -1) + y_to = counters[1]; + for (int y = y_from; y < y_to; y++) { + int i = x_from + y * counters[0]; + for (int x = x_from; x < x_to; x++) { + int[] mprev = mat[i]; + if (mprev == null) { + mat[i] = new int[] { ix }; + } else { + int[] mnew = new int[mprev.length + 1]; + mnew[mnew.length - 1] = ix; + for (int k = 0; k < mprev.length; k++) + mnew[k] = mprev[k]; + mat[i] = mnew; + } + i++; + } + } + ix++; + } + } + + public void close() { + } + + public void noteOff(int noteNumber, int velocity) { + if (!noteOffUsed) + return; + int[] plist = lookupIndex(noteNumber, velocity); + if(plist == null) return; + for (int i : plist) { + ModelPerformer p = performers[i]; + if (p.isReleaseTriggered()) { + player.play(i, null); + } + } + } + + public void noteOn(int noteNumber, int velocity) { + if (!noteOnUsed) + return; + int[] plist = lookupIndex(noteNumber, velocity); + if(plist == null) return; + for (int i : plist) { + ModelPerformer p = performers[i]; + if (!p.isReleaseTriggered()) { + player.play(i, null); + } + } + } +} diff --git a/src/share/classes/com/sun/media/sound/SoftChannel.java b/src/share/classes/com/sun/media/sound/SoftChannel.java index 1526cef99c1c4984702a4e455d20c1e9030ea573..1fc20b75b83897342a70c2b607862b8edc02dbeb 100644 --- a/src/share/classes/com/sun/media/sound/SoftChannel.java +++ b/src/share/classes/com/sun/media/sound/SoftChannel.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -116,7 +116,7 @@ public class SoftChannel implements MidiChannel, ModelDirectedPlayer { protected int tuning_program = 0; protected SoftInstrument current_instrument = null; protected ModelChannelMixer current_mixer = null; - private ModelDirector current_director = null; + protected ModelDirector current_director = null; // Controller Destination Settings protected int cds_control_number = -1; @@ -1264,13 +1264,16 @@ public class SoftChannel implements MidiChannel, ModelDirectedPlayer { } public void programChange(int bank, int program) { - bank = restrict7Bit(bank); + bank = restrict14Bit(bank); program = restrict7Bit(program); synchronized (control_mutex) { mainmixer.activity(); - this.bank = bank; - this.program = program; - current_instrument = null; + if(this.bank != bank || this.program != program) + { + this.bank = bank; + this.program = program; + current_instrument = null; + } } } diff --git a/src/share/classes/com/sun/media/sound/SoftMixingMixer.java b/src/share/classes/com/sun/media/sound/SoftMixingMixer.java index e6b9ee4ec7aa289f9169d7daeceb059374ff08bd..abc31cb8fa1e58adc7c441bec63755ad6959d612 100644 --- a/src/share/classes/com/sun/media/sound/SoftMixingMixer.java +++ b/src/share/classes/com/sun/media/sound/SoftMixingMixer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -118,16 +118,16 @@ public class SoftMixingMixer implements Mixer { AudioSystem.NOT_SPECIFIED, bits, channels, channels * bits / 8, AudioSystem.NOT_SPECIFIED, true)); } - formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT, + formats.add(new AudioFormat(Encoding.PCM_FLOAT, AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4, AudioSystem.NOT_SPECIFIED, false)); - formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT, + formats.add(new AudioFormat(Encoding.PCM_FLOAT, AudioSystem.NOT_SPECIFIED, 32, channels, channels * 4, AudioSystem.NOT_SPECIFIED, true)); - formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT, + formats.add(new AudioFormat(Encoding.PCM_FLOAT, AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8, AudioSystem.NOT_SPECIFIED, false)); - formats.add(new AudioFormat(AudioFloatConverter.PCM_FLOAT, + formats.add(new AudioFormat(Encoding.PCM_FLOAT, AudioSystem.NOT_SPECIFIED, 64, channels, channels * 8, AudioSystem.NOT_SPECIFIED, true)); } diff --git a/src/share/classes/com/sun/media/sound/SoftReceiver.java b/src/share/classes/com/sun/media/sound/SoftReceiver.java index e7c230e5122315cb097a8f56df4a11e6023c3eb6..03a0067f4adb18e6b5872b9e360645812ac5c8b1 100644 --- a/src/share/classes/com/sun/media/sound/SoftReceiver.java +++ b/src/share/classes/com/sun/media/sound/SoftReceiver.java @@ -27,6 +27,7 @@ package com.sun.media.sound; import java.util.TreeMap; import javax.sound.midi.MidiDevice; +import javax.sound.midi.MidiDeviceReceiver; import javax.sound.midi.MidiMessage; import javax.sound.midi.ShortMessage; diff --git a/src/share/classes/com/sun/media/sound/SoftSynthesizer.java b/src/share/classes/com/sun/media/sound/SoftSynthesizer.java index 1c33dfc48a3aba432c98f1a37ddc923b070aa5ff..a868bd441a2892cf6cedac903ef5621b353fc9bd 100644 --- a/src/share/classes/com/sun/media/sound/SoftSynthesizer.java +++ b/src/share/classes/com/sun/media/sound/SoftSynthesizer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,16 +25,25 @@ package com.sun.media.sound; +import java.io.BufferedInputStream; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import java.lang.ref.WeakReference; -import java.security.AccessControlException; +import java.security.AccessController; +import java.security.PrivilegedAction; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.prefs.BackingStoreException; +import java.util.prefs.Preferences; import javax.sound.midi.Instrument; import javax.sound.midi.MidiChannel; @@ -182,6 +191,7 @@ public class SoftSynthesizer implements AudioSynthesizer, // 1: DLS Voice Allocation protected int voice_allocation_mode = 0; + protected boolean load_default_soundbank = false; protected boolean reverb_light = true; protected boolean reverb_on = true; protected boolean chorus_on = true; @@ -226,8 +236,6 @@ public class SoftSynthesizer implements AudioSynthesizer, = new HashMap(); private Map inslist = new HashMap(); - private Map availlist - = new HashMap(); private Map loadedlist = new HashMap(); @@ -275,10 +283,12 @@ public class SoftSynthesizer implements AudioSynthesizer, synchronized (control_mutex) { if (channels != null) for (SoftChannel c : channels) + { c.current_instrument = null; + c.current_director = null; + } for (Instrument instrument : instruments) { String pat = patchToString(instrument.getPatch()); - availlist.remove(pat); SoftInstrument softins = new SoftInstrument((ModelInstrument) instrument); inslist.put(pat, softins); @@ -341,6 +351,7 @@ public class SoftSynthesizer implements AudioSynthesizer, number_of_midi_channels = (Integer)items[10].value; jitter_correction = (Boolean)items[11].value; reverb_light = (Boolean)items[12].value; + load_default_soundbank = (Boolean)items[13].value; } private String patchToString(Patch patch) { @@ -578,7 +589,9 @@ public class SoftSynthesizer implements AudioSynthesizer, c.current_instrument = null; inslist.remove(pat); loadedlist.remove(pat); - availlist.remove(pat); + for (int i = 0; i < channels.length; i++) { + channels[i].allSoundOff(); + } } } @@ -600,7 +613,7 @@ public class SoftSynthesizer implements AudioSynthesizer, return false; synchronized (control_mutex) { - if (!loadedlist.containsValue(to) && !availlist.containsValue(to)) + if (!loadedlist.containsValue(to)) throw new IllegalArgumentException("Instrument to is not loaded."); unloadInstrument(from); ModelMappedInstrument mfrom = new ModelMappedInstrument( @@ -609,118 +622,155 @@ public class SoftSynthesizer implements AudioSynthesizer, } } - public synchronized Soundbank getDefaultSoundbank() { - if (defaultSoundBank == null) { - try { - File javahome = new File(System.getProperties().getProperty( - "java.home")); - File libaudio = new File(new File(javahome, "lib"), "audio"); - - if (libaudio.exists()) { - File foundfile = null; - File[] files = libaudio.listFiles(); - if (files != null) { - for (int i = 0; i < files.length; i++) { - File file = files[i]; - if (file.isFile()) { - String lname = file.getName().toLowerCase(); - if (lname.endsWith(".sf2") || - lname.endsWith(".dls")) { - if (foundfile == null || (file.length() > - foundfile.length())) { - foundfile = file; + public Soundbank getDefaultSoundbank() { + synchronized (SoftSynthesizer.class) { + if (defaultSoundBank != null) + return defaultSoundBank; + + List> actions = + new ArrayList>(); + + actions.add(new PrivilegedAction() { + public InputStream run() { + File javahome = new File(System.getProperties() + .getProperty("java.home")); + File libaudio = new File(new File(javahome, "lib"), "audio"); + if (libaudio.exists()) { + File foundfile = null; + File[] files = libaudio.listFiles(); + if (files != null) { + for (int i = 0; i < files.length; i++) { + File file = files[i]; + if (file.isFile()) { + String lname = file.getName().toLowerCase(); + if (lname.endsWith(".sf2") + || lname.endsWith(".dls")) { + if (foundfile == null + || (file.length() > foundfile + .length())) { + foundfile = file; + } } } } } + if (foundfile != null) { + try { + return new FileInputStream(foundfile); + } catch (IOException e) { + } + } } - if (foundfile != null) { - try { - Soundbank sbk = MidiSystem.getSoundbank(foundfile); - defaultSoundBank = sbk; - return defaultSoundBank; - } catch (Exception e) { - //e.printStackTrace(); + return null; + } + }); + + actions.add(new PrivilegedAction() { + public InputStream run() { + if (System.getProperties().getProperty("os.name") + .startsWith("Windows")) { + File gm_dls = new File(System.getenv("SystemRoot") + + "\\system32\\drivers\\gm.dls"); + if (gm_dls.exists()) { + try { + return new FileInputStream(gm_dls); + } catch (IOException e) { + } } } + return null; } - - if (System.getProperties().getProperty("os.name") - .startsWith("Windows")) { - File gm_dls = new File(System.getenv("SystemRoot") - + "\\system32\\drivers\\gm.dls"); - if (gm_dls.exists()) { + }); + + actions.add(new PrivilegedAction() { + public InputStream run() { + /* + * Try to load saved generated soundbank + */ + File userhome = new File(System.getProperty("user.home"), + ".gervill"); + File emg_soundbank_file = new File(userhome, + "soundbank-emg.sf2"); + if (emg_soundbank_file.exists()) { try { - Soundbank sbk = MidiSystem.getSoundbank(gm_dls); - defaultSoundBank = sbk; - return defaultSoundBank; - } catch (Exception e) { - //e.printStackTrace(); + return new FileInputStream(emg_soundbank_file); + } catch (IOException e) { } } + return null; } - } catch (AccessControlException e) { - } catch (Exception e) { - //e.printStackTrace(); - } - - File userhome = null; - File emg_soundbank_file = null; + }); - /* - * Try to load saved generated soundbank - */ - try { - userhome = new File(System.getProperty("user.home"), - ".gervill"); - emg_soundbank_file = new File(userhome, "soundbank-emg.sf2"); - Soundbank sbk = MidiSystem.getSoundbank(emg_soundbank_file); - defaultSoundBank = sbk; - return defaultSoundBank; - } catch (AccessControlException e) { - } catch (Exception e) { - //e.printStackTrace(); + for (PrivilegedAction action : actions) { + try { + InputStream is = AccessController.doPrivileged(action); + if(is == null) continue; + Soundbank sbk; + try { + sbk = MidiSystem.getSoundbank(new BufferedInputStream(is)); + } finally { + is.close(); + } + if (sbk != null) { + defaultSoundBank = sbk; + return defaultSoundBank; + } + } catch (Exception e) { + } } try { - /* - * Generate emergency soundbank + * Generate emergency soundbank */ defaultSoundBank = EmergencySoundbank.createSoundbank(); + } catch (Exception e) { + } + if (defaultSoundBank != null) { /* - * Save generated soundbank to disk for faster future use. + * Save generated soundbank to disk for faster future use. */ - if(defaultSoundBank != null) - { - if(!userhome.exists()) userhome.mkdirs(); - if(!emg_soundbank_file.exists()) - ((SF2Soundbank)defaultSoundBank).save(emg_soundbank_file); + OutputStream out = AccessController + .doPrivileged(new PrivilegedAction() { + public OutputStream run() { + try { + File userhome = new File(System + .getProperty("user.home"), + ".gervill"); + if (!userhome.exists()) + userhome.mkdirs(); + File emg_soundbank_file = new File( + userhome, "soundbank-emg.sf2"); + if (emg_soundbank_file.exists()) + return null; + return new FileOutputStream( + emg_soundbank_file); + } catch (IOException e) { + } catch (SecurityException e) { + } + return null; + } + }); + if (out != null) { + try { + ((SF2Soundbank) defaultSoundBank).save(out); + out.close(); + } catch (IOException e) { + } } - } catch (Exception e) { - //e.printStackTrace(); } - } return defaultSoundBank; } public Instrument[] getAvailableInstruments() { - if (!isOpen()) { - Soundbank defsbk = getDefaultSoundbank(); - if (defsbk == null) - return new Instrument[0]; - return defsbk.getInstruments(); - } - - synchronized (control_mutex) { - ModelInstrument[] inslist_array = - new ModelInstrument[availlist.values().size()]; - availlist.values().toArray(inslist_array); - Arrays.sort(inslist_array, new ModelInstrumentComparator()); - return inslist_array; - } + Soundbank defsbk = getDefaultSoundbank(); + if (defsbk == null) + return new Instrument[0]; + Instrument[] inslist_array = defsbk.getInstruments(); + Arrays.sort(inslist_array, new ModelInstrumentComparator()); + return inslist_array; } public Instrument[] getLoadedInstruments() { @@ -794,6 +844,31 @@ public class SoftSynthesizer implements AudioSynthesizer, return info; } + private Properties getStoredProperties() { + return AccessController + .doPrivileged(new PrivilegedAction() { + public Properties run() { + Properties p = new Properties(); + String notePath = "/com/sun/media/sound/softsynthesizer"; + try { + Preferences prefroot = Preferences.userRoot(); + if (prefroot.nodeExists(notePath)) { + Preferences prefs = prefroot.node(notePath); + String[] prefs_keys = prefs.keys(); + for (String prefs_key : prefs_keys) { + String val = prefs.get(prefs_key, null); + if (val != null) + p.setProperty(prefs_key, val); + } + } + } catch (BackingStoreException e) { + } catch (SecurityException e) { + } + return p; + } + }); + } + public AudioSynthesizerPropertyInfo[] getPropertyInfo(Map info) { List list = new ArrayList(); @@ -861,17 +936,92 @@ public class SoftSynthesizer implements AudioSynthesizer, item.description = "Turn light reverb mode on or off"; list.add(item); + item = new AudioSynthesizerPropertyInfo("load default soundbank", o?load_default_soundbank:true); + item.description = "Enabled/disable loading default soundbank"; + list.add(item); + AudioSynthesizerPropertyInfo[] items; items = list.toArray(new AudioSynthesizerPropertyInfo[list.size()]); - if (info != null) - for (AudioSynthesizerPropertyInfo item2: items) { - Object v = info.get(item2.name); + Properties storedProperties = getStoredProperties(); + + for (AudioSynthesizerPropertyInfo item2 : items) { + Object v = (info == null) ? null : info.get(item2.name); + v = (v != null) ? v : storedProperties.getProperty(item2.name); + if (v != null) { Class c = (item2.valueClass); - if (v != null) - if (c.isInstance(v)) - item2.value = v; + if (c.isInstance(v)) + item2.value = v; + else if (v instanceof String) { + String s = (String) v; + if (c == Boolean.class) { + if (s.equalsIgnoreCase("true")) + item2.value = Boolean.TRUE; + if (s.equalsIgnoreCase("false")) + item2.value = Boolean.FALSE; + } else if (c == AudioFormat.class) { + int channels = 2; + boolean signed = true; + boolean bigendian = false; + int bits = 16; + float sampleRate = 44100f; + try { + StringTokenizer st = new StringTokenizer(s, ", "); + String prevToken = ""; + while (st.hasMoreTokens()) { + String token = st.nextToken().toLowerCase(); + if (token.equals("mono")) + channels = 1; + if (token.startsWith("channel")) + channels = Integer.parseInt(prevToken); + if (token.contains("unsigned")) + signed = false; + if (token.equals("big-endian")) + bigendian = true; + if (token.equals("bit")) + bits = Integer.parseInt(prevToken); + if (token.equals("hz")) + sampleRate = Float.parseFloat(prevToken); + prevToken = token; + } + item2.value = new AudioFormat(sampleRate, bits, + channels, signed, bigendian); + } catch (NumberFormatException e) { + } + + } else + try { + if (c == Byte.class) + item2.value = Byte.valueOf(s); + else if (c == Short.class) + item2.value = Short.valueOf(s); + else if (c == Integer.class) + item2.value = Integer.valueOf(s); + else if (c == Long.class) + item2.value = Long.valueOf(s); + else if (c == Float.class) + item2.value = Float.valueOf(s); + else if (c == Double.class) + item2.value = Double.valueOf(s); + } catch (NumberFormatException e) { + } + } else if (v instanceof Number) { + Number n = (Number) v; + if (c == Byte.class) + item2.value = Byte.valueOf(n.byteValue()); + if (c == Short.class) + item2.value = Short.valueOf(n.shortValue()); + if (c == Integer.class) + item2.value = Integer.valueOf(n.intValue()); + if (c == Long.class) + item2.value = Long.valueOf(n.longValue()); + if (c == Float.class) + item2.value = Float.valueOf(n.floatValue()); + if (c == Double.class) + item2.value = Double.valueOf(n.doubleValue()); + } } + } return items; } @@ -1007,11 +1157,12 @@ public class SoftSynthesizer implements AudioSynthesizer, if (targetFormat != null) setFormat(targetFormat); - Soundbank defbank = getDefaultSoundbank(); - if (defbank != null) { - loadAllInstruments(defbank); - availlist.putAll(loadedlist); - loadedlist.clear(); + if (load_default_soundbank) + { + Soundbank defbank = getDefaultSoundbank(); + if (defbank != null) { + loadAllInstruments(defbank); + } } voices = new SoftVoice[maxpoly]; @@ -1117,7 +1268,6 @@ public class SoftSynthesizer implements AudioSynthesizer, } inslist.clear(); - availlist.clear(); loadedlist.clear(); tunings.clear(); diff --git a/src/share/classes/com/sun/media/sound/SoftVoice.java b/src/share/classes/com/sun/media/sound/SoftVoice.java index 6f891959dcae6b07e007d82f3152b905a4f024e0..8e8fd5ecee05dbd5d60add208b8258c58796ab48 100644 --- a/src/share/classes/com/sun/media/sound/SoftVoice.java +++ b/src/share/classes/com/sun/media/sound/SoftVoice.java @@ -279,9 +279,12 @@ public class SoftVoice extends VoiceStatus { } protected void updateTuning(SoftTuning newtuning) { + tuning = newtuning; tunedKey = tuning.getTuning(note) / 100.0; if (!portamento) { co_noteon_keynumber[0] = tunedKey * (1.0 / 128.0); + if(performer == null) + return; int[] c = performer.midi_connections[4]; if (c == null) return; @@ -433,6 +436,8 @@ public class SoftVoice extends VoiceStatus { } protected void setPolyPressure(int pressure) { + if(performer == null) + return; int[] c = performer.midi_connections[2]; if (c == null) return; @@ -441,6 +446,8 @@ public class SoftVoice extends VoiceStatus { } protected void setChannelPressure(int pressure) { + if(performer == null) + return; int[] c = performer.midi_connections[1]; if (c == null) return; @@ -449,6 +456,8 @@ public class SoftVoice extends VoiceStatus { } protected void controlChange(int controller, int value) { + if(performer == null) + return; int[] c = performer.midi_ctrl_connections[controller]; if (c == null) return; @@ -457,6 +466,8 @@ public class SoftVoice extends VoiceStatus { } protected void nrpnChange(int controller, int value) { + if(performer == null) + return; int[] c = performer.midi_nrpn_connections.get(controller); if (c == null) return; @@ -465,6 +476,8 @@ public class SoftVoice extends VoiceStatus { } protected void rpnChange(int controller, int value) { + if(performer == null) + return; int[] c = performer.midi_rpn_connections.get(controller); if (c == null) return; @@ -473,6 +486,8 @@ public class SoftVoice extends VoiceStatus { } protected void setPitchBend(int bend) { + if(performer == null) + return; int[] c = performer.midi_connections[0]; if (c == null) return; @@ -499,6 +514,8 @@ public class SoftVoice extends VoiceStatus { co_noteon_on[0] = -1; + if(performer == null) + return; int[] c = performer.midi_connections[3]; if (c == null) return; @@ -527,6 +544,8 @@ public class SoftVoice extends VoiceStatus { co_noteon_on[0] = 0; + if(performer == null) + return; int[] c = performer.midi_connections[3]; if (c == null) return; @@ -543,6 +562,8 @@ public class SoftVoice extends VoiceStatus { sustain = true; co_noteon_on[0] = 1; + if(performer == null) + return; int[] c = performer.midi_connections[3]; if (c == null) return; @@ -555,6 +576,11 @@ public class SoftVoice extends VoiceStatus { active = false; stopping = false; audiostarted = false; + instrument = null; + performer = null; + connections = null; + extendedConnectionBlocks = null; + channelmixer = null; if (osc_stream != null) try { osc_stream.close(); diff --git a/src/share/classes/com/sun/media/sound/WaveExtensibleFileReader.java b/src/share/classes/com/sun/media/sound/WaveExtensibleFileReader.java index 69304dc2a64f07f82bcd688141826a42ca2e5f40..14a1848baaf53de9e19c4edc1a95131c752a9f65 100644 --- a/src/share/classes/com/sun/media/sound/WaveExtensibleFileReader.java +++ b/src/share/classes/com/sun/media/sound/WaveExtensibleFileReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -271,7 +271,7 @@ public class WaveExtensibleFileReader extends AudioFileReader { bits, channels, framesize, samplerate, false, p); } } else if (subFormat.equals(SUBTYPE_IEEE_FLOAT)) { - audioformat = new AudioFormat(AudioFloatConverter.PCM_FLOAT, + audioformat = new AudioFormat(Encoding.PCM_FLOAT, samplerate, bits, channels, framesize, samplerate, false, p); } else throw new UnsupportedAudioFileException(); diff --git a/src/share/classes/com/sun/media/sound/WaveFloatFileReader.java b/src/share/classes/com/sun/media/sound/WaveFloatFileReader.java index bbb8095c959f007e2ad43a7967f82a546e0274ce..8db4113b702848a6f91c49eaeaa9b1293e25964d 100644 --- a/src/share/classes/com/sun/media/sound/WaveFloatFileReader.java +++ b/src/share/classes/com/sun/media/sound/WaveFloatFileReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -33,6 +33,7 @@ import java.net.URL; import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioFormat.Encoding; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.UnsupportedAudioFileException; @@ -102,7 +103,7 @@ public class WaveFloatFileReader extends AudioFileReader { throw new UnsupportedAudioFileException(); AudioFormat audioformat = new AudioFormat( - AudioFloatConverter.PCM_FLOAT, samplerate, bits, channels, + Encoding.PCM_FLOAT, samplerate, bits, channels, framesize, samplerate, false); AudioFileFormat fileformat = new AudioFileFormat( AudioFileFormat.Type.WAVE, audioformat, diff --git a/src/share/classes/com/sun/media/sound/WaveFloatFileWriter.java b/src/share/classes/com/sun/media/sound/WaveFloatFileWriter.java index 324e868211cd6dfdc31205f0b114def76e8e4cfc..a61792bacc463c813b3da7f4102e47b33af10947 100644 --- a/src/share/classes/com/sun/media/sound/WaveFloatFileWriter.java +++ b/src/share/classes/com/sun/media/sound/WaveFloatFileWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,6 +30,7 @@ import java.io.OutputStream; import javax.sound.sampled.AudioFileFormat; import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioFormat.Encoding; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.AudioFileFormat.Type; @@ -48,8 +49,7 @@ public class WaveFloatFileWriter extends AudioFileWriter { public Type[] getAudioFileTypes(AudioInputStream stream) { - if (!stream.getFormat().getEncoding().equals( - AudioFloatConverter.PCM_FLOAT)) + if (!stream.getFormat().getEncoding().equals(Encoding.PCM_FLOAT)) return new Type[0]; return new Type[] { Type.WAVE }; } @@ -58,8 +58,7 @@ public class WaveFloatFileWriter extends AudioFileWriter { if (!Type.WAVE.equals(type)) throw new IllegalArgumentException("File type " + type + " not supported."); - if (!stream.getFormat().getEncoding().equals( - AudioFloatConverter.PCM_FLOAT)) + if (!stream.getFormat().getEncoding().equals(Encoding.PCM_FLOAT)) throw new IllegalArgumentException("File format " + stream.getFormat() + " not supported."); } diff --git a/src/share/classes/com/sun/rowset/CachedRowSetImpl.java b/src/share/classes/com/sun/rowset/CachedRowSetImpl.java index 2f7478b4e268d4b974ab25f773d3b7a3e16e70e9..cc3ebbfc21b4d5b104a21d986ec84bad1df5c01f 100644 --- a/src/share/classes/com/sun/rowset/CachedRowSetImpl.java +++ b/src/share/classes/com/sun/rowset/CachedRowSetImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -518,7 +518,7 @@ public class CachedRowSetImpl extends BaseRowSet implements RowSet, RowSetIntern setReadOnly(true); setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); setEscapeProcessing(true); - setTypeMap(null); + //setTypeMap(null); checkTransactionalWriter(); //Instantiating the vector for MatchColumns @@ -679,7 +679,10 @@ public class CachedRowSetImpl extends BaseRowSet implements RowSet, RowSetIntern } else if (obj instanceof Clob) { obj = new SerialClob((Clob)obj); } else if (obj instanceof java.sql.Array) { - obj = new SerialArray((java.sql.Array)obj, map); + if(map != null) + obj = new SerialArray((java.sql.Array)obj, map); + else + obj = new SerialArray((java.sql.Array)obj); } ((Row)currentRow).initColumnObject(i, obj); @@ -762,7 +765,7 @@ public class CachedRowSetImpl extends BaseRowSet implements RowSet, RowSetIntern if( conn != null){ // JDBC 4.0 mandates as does the Java EE spec that all DataBaseMetaData methods // must be implemented, therefore, the previous fix for 5055528 is being backed out - dbmslocatorsUpdateCopy = conn.getMetaData().locatorsUpdateCopy(); + dbmslocatorsUpdateCopy = conn.getMetaData().locatorsUpdateCopy(); } } @@ -6322,6 +6325,7 @@ public class CachedRowSetImpl extends BaseRowSet implements RowSet, RowSetIntern crs.RowSetMD = RowSetMD; crs.numRows = 1; crs.cursorPos = 0; + crs.setTypeMap(this.getTypeMap()); // make sure we don't get someone playing with these // %%% is this now necessary ??? @@ -10114,7 +10118,7 @@ a * during the deserialization process * */ - protected void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { // Default state initialization happens here ois.defaultReadObject(); // Initialization of transient Res Bundle happens here . @@ -10125,5 +10129,15 @@ a } } - static final long serialVersionUID =1884577171200622428L; + + //------------------------- JDBC 4.1 ----------------------------------- + public T getObject(int columnIndex, Class type) throws SQLException { + throw new SQLFeatureNotSupportedException("Not supported yet."); + } + + public T getObject(String columnLabel, Class type) throws SQLException { + throw new SQLFeatureNotSupportedException("Not supported yet."); + } + + static final long serialVersionUID =1884577171200622428L; } diff --git a/src/share/classes/com/sun/rowset/FilteredRowSetImpl.java b/src/share/classes/com/sun/rowset/FilteredRowSetImpl.java index dd765d84c9344098d5cd4a8c5c7b736d544e55f1..a9becf17d370649eb45617381605f8504a3b3260 100644 --- a/src/share/classes/com/sun/rowset/FilteredRowSetImpl.java +++ b/src/share/classes/com/sun/rowset/FilteredRowSetImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1746,5 +1746,23 @@ public class FilteredRowSetImpl extends WebRowSetImpl implements Serializable, C onInsertRow = false; super.insertRow(); } - static final long serialVersionUID = 6178454588413509360L; + + /** + * This method re populates the resBundle + * during the deserialization process + * + */ + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + // Default state initialization happens here + ois.defaultReadObject(); + // Initialization of transient Res Bundle happens here . + try { + resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); + } catch(IOException ioe) { + throw new RuntimeException(ioe); + } + + } + + static final long serialVersionUID = 6178454588413509360L; } // end FilteredRowSetImpl class diff --git a/src/share/classes/com/sun/rowset/JdbcRowSetImpl.java b/src/share/classes/com/sun/rowset/JdbcRowSetImpl.java index 3c34ccf61e9bea1c216c50fa8287b68f6d9be0c4..34178d4984d244621b38c6263c9198a42a5d7949 100644 --- a/src/share/classes/com/sun/rowset/JdbcRowSetImpl.java +++ b/src/share/classes/com/sun/rowset/JdbcRowSetImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -101,7 +101,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { private Vector strMatchColumns; - protected transient JdbcRowSetResourceBundle jdbcResBundle; + protected transient JdbcRowSetResourceBundle resBundle; /** * Constructs a default JdbcRowSet object. @@ -140,7 +140,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { rs = null; try { - jdbcResBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); + resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); } catch(IOException ioe) { throw new RuntimeException(ioe); } @@ -154,42 +154,42 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { try { setShowDeleted(false); } catch(SQLException sqle) { - System.err.println(jdbcResBundle.handleGetObject("jdbcrowsetimpl.setshowdeleted").toString() + + System.err.println(resBundle.handleGetObject("jdbcrowsetimpl.setshowdeleted").toString() + sqle.getLocalizedMessage()); } try { setQueryTimeout(0); } catch(SQLException sqle) { - System.err.println(jdbcResBundle.handleGetObject("jdbcrowsetimpl.setquerytimeout").toString() + + System.err.println(resBundle.handleGetObject("jdbcrowsetimpl.setquerytimeout").toString() + sqle.getLocalizedMessage()); } try { setMaxRows(0); } catch(SQLException sqle) { - System.err.println(jdbcResBundle.handleGetObject("jdbcrowsetimpl.setmaxrows").toString() + + System.err.println(resBundle.handleGetObject("jdbcrowsetimpl.setmaxrows").toString() + sqle.getLocalizedMessage()); } try { setMaxFieldSize(0); } catch(SQLException sqle) { - System.err.println(jdbcResBundle.handleGetObject("jdbcrowsetimpl.setmaxfieldsize").toString() + + System.err.println(resBundle.handleGetObject("jdbcrowsetimpl.setmaxfieldsize").toString() + sqle.getLocalizedMessage()); } try { setEscapeProcessing(true); } catch(SQLException sqle) { - System.err.println(jdbcResBundle.handleGetObject("jdbcrowsetimpl.setescapeprocessing").toString() + + System.err.println(resBundle.handleGetObject("jdbcrowsetimpl.setescapeprocessing").toString() + sqle.getLocalizedMessage()); } try { setConcurrency(ResultSet.CONCUR_UPDATABLE); } catch (SQLException sqle) { - System.err.println(jdbcResBundle.handleGetObject("jdbcrowsetimpl.setconcurrency").toString() + + System.err.println(resBundle.handleGetObject("jdbcrowsetimpl.setconcurrency").toString() + sqle.getLocalizedMessage()); } @@ -198,7 +198,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { try { setType(ResultSet.TYPE_SCROLL_INSENSITIVE); } catch(SQLException sqle){ - System.err.println(jdbcResBundle.handleGetObject("jdbcrowsetimpl.settype").toString() + + System.err.println(resBundle.handleGetObject("jdbcrowsetimpl.settype").toString() + sqle.getLocalizedMessage()); } @@ -207,7 +207,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { try { setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED); } catch(SQLException sqle){ - System.err.println(jdbcResBundle.handleGetObject("jdbcrowsetimpl.settransactionisolation").toString() + + System.err.println(resBundle.handleGetObject("jdbcrowsetimpl.settransactionisolation").toString() + sqle.getLocalizedMessage()); } @@ -263,7 +263,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { rs = null; try { - jdbcResBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); + resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); } catch(IOException ioe) { throw new RuntimeException(ioe); } @@ -338,7 +338,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { rs = null; try { - jdbcResBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); + resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); } catch(IOException ioe) { throw new RuntimeException(ioe); } @@ -430,7 +430,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { rs = res; try { - jdbcResBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); + resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); } catch(IOException ioe) { throw new RuntimeException(ioe); } @@ -517,7 +517,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { // to the db, implies undesirable state so throw exception if (conn == null && ps == null && rs == null ) { - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.invalstate").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.invalstate").toString()); } } @@ -593,28 +593,28 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { try { ps.setEscapeProcessing(getEscapeProcessing()); } catch (SQLException ex) { - System.err.println(jdbcResBundle.handleGetObject("jdbcrowsetimpl.setescapeprocessing").toString() + + System.err.println(resBundle.handleGetObject("jdbcrowsetimpl.setescapeprocessing").toString() + ex.getLocalizedMessage()); } try { ps.setMaxFieldSize(getMaxFieldSize()); } catch (SQLException ex) { - System.err.println(jdbcResBundle.handleGetObject("jdbcrowsetimpl.setmaxfieldsize").toString() + + System.err.println(resBundle.handleGetObject("jdbcrowsetimpl.setmaxfieldsize").toString() + ex.getLocalizedMessage()); } try { ps.setMaxRows(getMaxRows()); } catch (SQLException ex) { - System.err.println(jdbcResBundle.handleGetObject("jdbcrowsetimpl.setmaxrows").toString() + + System.err.println(resBundle.handleGetObject("jdbcrowsetimpl.setmaxrows").toString() + ex.getLocalizedMessage()); } try { ps.setQueryTimeout(getQueryTimeout()); } catch (SQLException ex) { - System.err.println(jdbcResBundle.handleGetObject("jdbcrowsetimpl.setquerytimeout").toString() + + System.err.println(resBundle.handleGetObject("jdbcrowsetimpl.setquerytimeout").toString() + ex.getLocalizedMessage()); } @@ -651,7 +651,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { } } catch (javax.naming.NamingException ex) { - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.connect").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.connect").toString()); } } else if (getUrl() != null) { @@ -681,7 +681,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { } ps = conn.prepareStatement(getCommand(),ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE); } catch (SQLException ex) { - System.err.println(jdbcResBundle.handleGetObject("jdbcrowsetimpl.prepare").toString() + + System.err.println(resBundle.handleGetObject("jdbcrowsetimpl.prepare").toString() + ex.getLocalizedMessage()); if (ps != null) @@ -721,15 +721,15 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { if (param[0] instanceof java.sql.Date || param[0] instanceof java.sql.Time || param[0] instanceof java.sql.Timestamp) { - System.err.println(jdbcResBundle.handleGetObject("jdbcrowsetimpl.detecteddate")); + System.err.println(resBundle.handleGetObject("jdbcrowsetimpl.detecteddate")); if (param[1] instanceof java.util.Calendar) { - System.err.println(jdbcResBundle.handleGetObject("jdbcrowsetimpl.detectedcalendar")); + System.err.println(resBundle.handleGetObject("jdbcrowsetimpl.detectedcalendar")); ps.setDate(i + 1, (java.sql.Date)param[0], (java.util.Calendar)param[1]); continue; } else { - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.paramtype").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.paramtype").toString()); } } @@ -770,7 +770,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { (java.io.InputStream)param[0], ((Integer)param[1]).intValue()); default: - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.paramtype").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.paramtype").toString()); } } @@ -784,7 +784,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { continue; } - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.paramtype").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.paramtype").toString()); } else { // common case - this catches all SQL92 types @@ -3749,7 +3749,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { for( int j= 0 ;j < columnIdxes.length; j++) { i_val = (Integer.parseInt(iMatchColumns.get(j).toString())); if(columnIdxes[j] != i_val) { - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.matchcols").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.matchcols").toString()); } } @@ -3776,7 +3776,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { for(int j = 0 ;j < columnIdxes.length; j++) { if( !columnIdxes[j].equals(strMatchColumns.get(j)) ){ - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.matchcols").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.matchcols").toString()); } } @@ -3800,7 +3800,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { String []str_temp = new String[strMatchColumns.size()]; if( strMatchColumns.get(0) == null) { - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.setmatchcols").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.setmatchcols").toString()); } strMatchColumns.copyInto(str_temp); @@ -3825,7 +3825,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { i_val = ((Integer)iMatchColumns.get(0)).intValue(); if( i_val == -1 ) { - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.setmatchcols").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.setmatchcols").toString()); } @@ -3859,7 +3859,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { for(int j = 0 ; j < columnIdxes.length; j++) { if( columnIdxes[j] < 0 ) { - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.matchcols1").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.matchcols1").toString()); } } for(int i = 0 ;i < columnIdxes.length; i++) { @@ -3886,7 +3886,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { for(int j = 0; j < columnNames.length; j++) { if( columnNames[j] == null || columnNames[j].equals("")) { - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.matchcols2").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.matchcols2").toString()); } } for( int i = 0; i < columnNames.length; i++) { @@ -3915,7 +3915,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { public void setMatchColumn(int columnIdx) throws SQLException { // validate, if col is ok to be set if(columnIdx < 0) { - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.matchcols1").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.matchcols1").toString()); } else { // set iMatchColumn iMatchColumns.set(0, new Integer(columnIdx)); @@ -3941,7 +3941,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { public void setMatchColumn(String columnName) throws SQLException { // validate, if col is ok to be set if(columnName.equals(null) || ((columnName = columnName.trim()) == "" )) { - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.matchcols2").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.matchcols2").toString()); } else { // set strMatchColumn strMatchColumns.set(0, columnName); @@ -3966,9 +3966,9 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { public void unsetMatchColumn(int columnIdx) throws SQLException { // check if we are unsetting the SAME column if(! iMatchColumns.get(0).equals(new Integer(columnIdx) ) ) { - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.unsetmatch").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.unsetmatch").toString()); } else if(strMatchColumns.get(0) != null) { - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.usecolname").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.usecolname").toString()); } else { // that is, we are unsetting it. iMatchColumns.set(0, new Integer(-1)); @@ -3995,9 +3995,9 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { columnName = columnName.trim(); if(!((strMatchColumns.get(0)).equals(columnName))) { - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.unsetmatch").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.unsetmatch").toString()); } else if( ((Integer)(iMatchColumns.get(0))).intValue() > 0) { - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.usecolid").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.usecolid").toString()); } else { strMatchColumns.set(0, null); // that is, we are unsetting it. } @@ -4152,7 +4152,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { private void checkTypeConcurrency() throws SQLException { if(rs.getType() == TYPE_FORWARD_ONLY || rs.getConcurrency() == CONCUR_READ_ONLY) { - throw new SQLException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.resnotupd").toString()); + throw new SQLException(resBundle.handleGetObject("jdbcrowsetimpl.resnotupd").toString()); } } @@ -4642,7 +4642,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 6.0 */ public SQLXML getSQLXML(int columnIndex) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -4653,7 +4653,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @throws SQLException if a database access error occurs */ public SQLXML getSQLXML(String colName) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -4668,7 +4668,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 6.0 */ public RowId getRowId(int columnIndex) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -4683,7 +4683,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 6.0 */ public RowId getRowId(String columnName) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -4699,7 +4699,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 6.0 */ public void updateRowId(int columnIndex, RowId x) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -4715,7 +4715,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 6.0 */ public void updateRowId(String columnName, RowId x) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -4725,7 +4725,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 6.0 */ public int getHoldability() throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -4736,7 +4736,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 6.0 */ public boolean isClosed() throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -4748,7 +4748,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 6.0 */ public void updateNString(int columnIndex, String nString) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -4760,7 +4760,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 6.0 */ public void updateNString(String columnName, String nString) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -4773,7 +4773,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 6.0 */ public void updateNClob(int columnIndex, NClob nClob) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -4785,7 +4785,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 6.0 */ public void updateNClob(String columnName, NClob nClob) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -4800,7 +4800,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 6.0 */ public NClob getNClob(int i) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -4816,7 +4816,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 6.0 */ public NClob getNClob(String colName) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } public T unwrap(java.lang.Class iface) throws java.sql.SQLException{ @@ -4836,7 +4836,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 1.6 */ public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -4848,7 +4848,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 1.6 */ public void setSQLXML(String parameterName, SQLXML xmlObject) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -4863,7 +4863,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 1.6 */ public void setRowId(int parameterIndex, RowId x) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -4877,7 +4877,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 1.6 */ public void setRowId(String parameterName, RowId x) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -4897,7 +4897,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 1.6 */ public void setNString(int parameterIndex, String value) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -4925,7 +4925,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 1.6 */ public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -4940,7 +4940,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 1.6 */ public void setNClob(String parameterName, NClob value) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -4960,7 +4960,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 1.6 */ public java.io.Reader getNCharacterStream(int columnIndex) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -4980,7 +4980,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 1.6 */ public java.io.Reader getNCharacterStream(String columnName) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -4996,7 +4996,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 1.6 */ public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5013,7 +5013,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 1.6 */ public void updateSQLXML(String columnName, SQLXML xmlObject) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5031,7 +5031,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 1.6 */ public String getNString(int columnIndex) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5049,7 +5049,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { * @since 1.6 */ public String getNString(String columnName) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5071,7 +5071,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { java.io.Reader x, long length) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5093,7 +5093,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { java.io.Reader x, long length) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5123,7 +5123,7 @@ public class JdbcRowSetImpl extends BaseRowSet implements JdbcRowSet, Joinable { */ public void updateNCharacterStream(int columnIndex, java.io.Reader x) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5155,7 +5155,7 @@ bel is the name of the column */ public void updateNCharacterStream(String columnLabel, java.io.Reader reader) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5188,7 +5188,7 @@ bel is the name of the column * @since 1.6 */ public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5221,7 +5221,7 @@ bel is the name of the column * @since 1.6 */ public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5256,7 +5256,7 @@ bel is the name of the column * @since 1.6 */ public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5291,7 +5291,7 @@ bel is the name of the column * @since 1.6 */ public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5323,7 +5323,7 @@ bel is the name of the column * @since 1.6 */ public void updateClob(int columnIndex, Reader reader, long length) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5355,7 +5355,7 @@ bel is the name of the column * @since 1.6 */ public void updateClob(String columnLabel, Reader reader, long length) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5389,7 +5389,7 @@ bel is the name of the column * @since 1.6 */ public void updateClob(int columnIndex, Reader reader) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5424,7 +5424,7 @@ bel is the name of the column * @since 1.6 */ public void updateClob(String columnLabel, Reader reader) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5458,7 +5458,7 @@ bel is the name of the column * @since 1.6 */ public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5492,7 +5492,7 @@ bel is the name of the column * @since 1.6 */ public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5528,7 +5528,7 @@ bel is the name of the column * @since 1.6 */ public void updateNClob(int columnIndex, Reader reader) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5565,7 +5565,7 @@ bel is the name of the column * @since 1.6 */ public void updateNClob(String columnLabel, Reader reader) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -5590,7 +5590,7 @@ bel is the name of the column public void updateAsciiStream(int columnIndex, java.io.InputStream x, long length) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5614,7 +5614,7 @@ bel is the name of the column public void updateBinaryStream(int columnIndex, java.io.InputStream x, long length) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5638,7 +5638,7 @@ bel is the name of the column public void updateCharacterStream(int columnIndex, java.io.Reader x, long length) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5662,7 +5662,7 @@ bel is the name of the column public void updateAsciiStream(String columnLabel, java.io.InputStream x, long length) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5687,7 +5687,7 @@ bel is the name of the column */ public void updateAsciiStream(int columnIndex, java.io.InputStream x) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5713,7 +5713,7 @@ bel is the name of the column */ public void updateAsciiStream(String columnLabel, java.io.InputStream x) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -5738,7 +5738,7 @@ bel is the name of the column public void updateBinaryStream(String columnLabel, java.io.InputStream x, long length) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5763,7 +5763,7 @@ bel is the name of the column */ public void updateBinaryStream(int columnIndex, java.io.InputStream x) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -5790,7 +5790,7 @@ bel is the name of the column */ public void updateBinaryStream(String columnLabel, java.io.InputStream x) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -5816,7 +5816,7 @@ bel is the name of the column public void updateCharacterStream(String columnLabel, java.io.Reader reader, long length) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5841,7 +5841,7 @@ bel is the name of the column */ public void updateCharacterStream(int columnIndex, java.io.Reader x) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5868,7 +5868,7 @@ bel is the name of the column */ public void updateCharacterStream(String columnLabel, java.io.Reader reader) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -5885,7 +5885,7 @@ bel is the name of the column * @since 1.4 */ public void setURL(int parameterIndex, java.net.URL x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -5914,7 +5914,7 @@ bel is the name of the column */ public void setNClob(int parameterIndex, Reader reader) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -5942,7 +5942,7 @@ bel is the name of the column */ public void setNClob(String parameterName, Reader reader, long length) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -5969,7 +5969,7 @@ bel is the name of the column */ public void setNClob(String parameterName, Reader reader) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -5996,7 +5996,7 @@ bel is the name of the column */ public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -6012,7 +6012,7 @@ a * @since 1.6 */ public void setNClob(int parameterIndex, NClob value) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -6029,7 +6029,7 @@ a */ public void setNString(String parameterName, String value) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6046,7 +6046,7 @@ a * @since 1.6 */ public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -6066,7 +6066,7 @@ a */ public void setNCharacterStream(String parameterName, Reader value, long length) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6092,7 +6092,7 @@ a * @since 1.6 */ public void setNCharacterStream(String parameterName, Reader value) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6118,7 +6118,7 @@ a */ public void setTimestamp(String parameterName, java.sql.Timestamp x, Calendar cal) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6144,7 +6144,7 @@ a */ public void setClob(String parameterName, Reader reader, long length) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -6163,7 +6163,7 @@ a * @since 1.6 */ public void setClob (String parameterName, Clob x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6188,7 +6188,7 @@ a */ public void setClob(String parameterName, Reader reader) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -6210,7 +6210,7 @@ a */ public void setDate(String parameterName, java.sql.Date x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6236,7 +6236,7 @@ a */ public void setDate(String parameterName, java.sql.Date x, Calendar cal) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -6256,7 +6256,7 @@ a */ public void setTime(String parameterName, java.sql.Time x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6282,7 +6282,7 @@ a */ public void setTime(String parameterName, java.sql.Time x, Calendar cal) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6308,7 +6308,7 @@ a */ public void setClob(int parameterIndex, Reader reader) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -6333,7 +6333,7 @@ a */ public void setClob(int parameterIndex, Reader reader, long length) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -6363,7 +6363,7 @@ a */ public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6395,7 +6395,7 @@ a */ public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6426,7 +6426,7 @@ a */ public void setBlob(String parameterName, InputStream inputStream, long length) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -6444,7 +6444,7 @@ a * @since 1.6 */ public void setBlob (String parameterName, Blob x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6470,7 +6470,7 @@ a */ public void setBlob(String parameterName, InputStream inputStream) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6516,7 +6516,7 @@ a */ public void setObject(String parameterName, Object x, int targetSqlType, int scale) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6542,7 +6542,7 @@ a */ public void setObject(String parameterName, Object x, int targetSqlType) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6582,7 +6582,7 @@ a * @since 1.4 */ public void setObject(String parameterName, Object x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6609,7 +6609,7 @@ a */ public void setAsciiStream(String parameterName, java.io.InputStream x, int length) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -6636,7 +6636,7 @@ a */ public void setBinaryStream(String parameterName, java.io.InputStream x, int length) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6665,7 +6665,7 @@ a public void setCharacterStream(String parameterName, java.io.Reader reader, int length) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6692,7 +6692,7 @@ a */ public void setAsciiStream(String parameterName, java.io.InputStream x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -6719,7 +6719,7 @@ a */ public void setBinaryStream(String parameterName, java.io.InputStream x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6748,7 +6748,7 @@ a */ public void setCharacterStream(String parameterName, java.io.Reader reader) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6767,7 +6767,7 @@ a * @since 1.4 */ public void setBigDecimal(String parameterName, BigDecimal x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6788,7 +6788,7 @@ a * @since 1.4 */ public void setString(String parameterName, String x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -6810,7 +6810,7 @@ a * @since 1.4 */ public void setBytes(String parameterName, byte x[]) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6830,7 +6830,7 @@ a */ public void setTimestamp(String parameterName, java.sql.Timestamp x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6847,7 +6847,7 @@ a * @since 1.4 */ public void setNull(String parameterName, int sqlType) throws SQLException { - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6884,7 +6884,7 @@ a */ public void setNull (String parameterName, int sqlType, String typeName) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6902,7 +6902,7 @@ a * @since 1.4 */ public void setBoolean(String parameterName, boolean x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -6922,7 +6922,7 @@ a * @since 1.4 */ public void setByte(String parameterName, byte x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -6941,7 +6941,7 @@ a * @since 1.4 */ public void setShort(String parameterName, short x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -6960,7 +6960,7 @@ a * @since 1.4 */ public void setInt(String parameterName, int x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -6978,7 +6978,7 @@ a * @since 1.4 */ public void setLong(String parameterName, long x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } @@ -6997,7 +6997,7 @@ a * @since 1.4 */ public void setFloat(String parameterName, float x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -7015,7 +7015,7 @@ a * @since 1.4 */ public void setDouble(String parameterName, double x) throws SQLException{ - throw new SQLFeatureNotSupportedException(jdbcResBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); + throw new SQLFeatureNotSupportedException(resBundle.handleGetObject("jdbcrowsetimpl.featnotsupp").toString()); } /** @@ -7023,15 +7023,25 @@ a * during the deserialization process * */ - protected void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { // Default state initialization happens here ois.defaultReadObject(); // Initialization of transient Res Bundle happens here . try { - jdbcResBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); + resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); } catch(IOException ioe) {} } static final long serialVersionUID = -3591946023893483003L; + + //------------------------- JDBC 4.1 ----------------------------------- + + public T getObject(int columnIndex, Class type) throws SQLException { + throw new SQLFeatureNotSupportedException("Not supported yet."); + } + + public T getObject(String columnLabel, Class type) throws SQLException { + throw new SQLFeatureNotSupportedException("Not supported yet."); + } } diff --git a/src/share/classes/com/sun/rowset/JdbcRowSetResourceBundle.java b/src/share/classes/com/sun/rowset/JdbcRowSetResourceBundle.java index a508904eb0d27559a19238b5ee4c288bf88b7613..265b44a62766efdaa99f5dee4e7732a6bba0f1c1 100644 --- a/src/share/classes/com/sun/rowset/JdbcRowSetResourceBundle.java +++ b/src/share/classes/com/sun/rowset/JdbcRowSetResourceBundle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -153,4 +153,5 @@ public class JdbcRowSetResourceBundle implements Serializable { return propResBundle.handleGetObject(key); } + static final long serialVersionUID = 436199386225359954L; } diff --git a/src/share/classes/com/sun/rowset/JoinRowSetImpl.java b/src/share/classes/com/sun/rowset/JoinRowSetImpl.java index 00f427a5eeaa2bf31f0c18b22217e2770c0b1ece..6963d5748f87004da6f611702c8339b81f803203 100644 --- a/src/share/classes/com/sun/rowset/JoinRowSetImpl.java +++ b/src/share/classes/com/sun/rowset/JoinRowSetImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -127,6 +127,11 @@ public class JoinRowSetImpl extends WebRowSetImpl implements JoinRowSet { strMatchKey = null; supportedJOINs = new boolean[] {false, true, false, false, false}; + try { + resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); + } catch(IOException ioe) { + throw new RuntimeException(ioe); + } } @@ -4306,5 +4311,22 @@ public class JoinRowSetImpl extends WebRowSetImpl implements JoinRowSet { return crsInternal.createCopySchema(); } - static final long serialVersionUID = -5590501621560008453L; + /** + * This method re populates the resBundle + * during the deserialization process + * + */ + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + // Default state initialization happens here + ois.defaultReadObject(); + // Initialization of transient Res Bundle happens here . + try { + resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); + } catch(IOException ioe) { + throw new RuntimeException(ioe); + } + + } + + static final long serialVersionUID = -5590501621560008453L; } diff --git a/src/share/classes/com/sun/rowset/RowSetFactoryImpl.java b/src/share/classes/com/sun/rowset/RowSetFactoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..6f1c0fa385efa1aac2384bc563bc082bd5a06498 --- /dev/null +++ b/src/share/classes/com/sun/rowset/RowSetFactoryImpl.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.rowset; + +import java.sql.SQLException; +import javax.sql.rowset.CachedRowSet; +import javax.sql.rowset.FilteredRowSet; +import javax.sql.rowset.JdbcRowSet; +import javax.sql.rowset.JoinRowSet; +import javax.sql.rowset.WebRowSet; +import javax.sql.rowset.RowSetFactory; + +/** + * This is the implementation specific class for the + * javax.sql.rowset.spi.RowSetFactory. This is the platform + * default implementation for the Java SE platform. + * + * @author Lance Andersen + * + * + * @version 1.7 + */ +public final class RowSetFactoryImpl implements RowSetFactory { + + public CachedRowSet createCachedRowSet() throws SQLException { + return new com.sun.rowset.CachedRowSetImpl(); + } + + public FilteredRowSet createFilteredRowSet() throws SQLException { + return new com.sun.rowset.FilteredRowSetImpl(); + } + + + public JdbcRowSet createJdbcRowSet() throws SQLException { + return new com.sun.rowset.JdbcRowSetImpl(); + } + + public JoinRowSet createJoinRowSet() throws SQLException { + return new com.sun.rowset.JoinRowSetImpl(); + } + + public WebRowSet createWebRowSet() throws SQLException { + return new com.sun.rowset.WebRowSetImpl(); + } + +} diff --git a/src/share/classes/com/sun/rowset/WebRowSetImpl.java b/src/share/classes/com/sun/rowset/WebRowSetImpl.java index 2b9e975129c96cb892c8c3c197c44075c4bc7cff..5a62c78b231290736cd2700c4dba6218970a5070 100644 --- a/src/share/classes/com/sun/rowset/WebRowSetImpl.java +++ b/src/share/classes/com/sun/rowset/WebRowSetImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -103,6 +103,12 @@ public class WebRowSetImpl extends CachedRowSetImpl implements WebRowSet { */ public WebRowSetImpl(Hashtable env) throws SQLException { + try { + resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); + } catch(IOException ioe) { + throw new RuntimeException(ioe); + } + if ( env == null) { throw new SQLException(resBundle.handleGetObject("webrowsetimpl.nullhash").toString()); } @@ -263,5 +269,23 @@ public class WebRowSetImpl extends CachedRowSetImpl implements WebRowSet { this.writeXml(oStream); } -static final long serialVersionUID = -8771775154092422943L; + + /** + * This method re populates the resBundle + * during the deserialization process + * + */ + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + // Default state initialization happens here + ois.defaultReadObject(); + // Initialization of transient Res Bundle happens here . + try { + resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); + } catch(IOException ioe) { + throw new RuntimeException(ioe); + } + + } + + static final long serialVersionUID = -8771775154092422943L; } diff --git a/src/share/classes/com/sun/rowset/internal/CachedRowSetReader.java b/src/share/classes/com/sun/rowset/internal/CachedRowSetReader.java index d5b4055cc3c84c5879833baec44216fe5a20fe45..31510026c476749de4c92fae128025164371e85d 100644 --- a/src/share/classes/com/sun/rowset/internal/CachedRowSetReader.java +++ b/src/share/classes/com/sun/rowset/internal/CachedRowSetReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -490,4 +490,17 @@ public class CachedRowSetReader implements RowSetReader, Serializable { startPosition = pos; } + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + // Default state initialization happens here + ois.defaultReadObject(); + // Initialization of Res Bundle happens here . + try { + resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); + } catch(IOException ioe) { + throw new RuntimeException(ioe); + } + + } + + static final long serialVersionUID =5049738185801363801L; } diff --git a/src/share/classes/com/sun/rowset/internal/CachedRowSetWriter.java b/src/share/classes/com/sun/rowset/internal/CachedRowSetWriter.java index 08ec71c0eb794098267fc8ea8a52819024a2c2d4..d679dad580b0ae4b85aa0cb08a8960d91aacad39 100644 --- a/src/share/classes/com/sun/rowset/internal/CachedRowSetWriter.java +++ b/src/share/classes/com/sun/rowset/internal/CachedRowSetWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -31,7 +31,13 @@ import java.util.*; import java.io.*; import com.sun.rowset.*; +import java.text.MessageFormat; import javax.sql.rowset.*; +import javax.sql.rowset.serial.SQLInputImpl; +import javax.sql.rowset.serial.SerialArray; +import javax.sql.rowset.serial.SerialBlob; +import javax.sql.rowset.serial.SerialClob; +import javax.sql.rowset.serial.SerialStruct; import javax.sql.rowset.spi.*; @@ -53,6 +59,7 @@ import javax.sql.rowset.spi.*; * Standard JDBC RowSet implementations provide an object instance of this * writer by invoking the SyncProvider.getRowSetWriter() method. * + * @version 0.2 * @author Jonathan Bruce * @see javax.sql.rowset.spi.SyncProvider * @see javax.sql.rowset.spi.SyncFactory @@ -508,10 +515,11 @@ public class CachedRowSetWriter implements TransactionalWriter, Serializable { ResultSet rs = null; rs = pstmt.executeQuery(); - if (rs.next() == true) { + ResultSetMetaData rsmd = rs.getMetaData(); + if (rs.next()) { if (rs.next()) { - /** More than one row conflict. + /** More than one row conflict. * If rs has only one row we are able to * uniquely identify the row where update * have to happen else if more than one @@ -528,7 +536,7 @@ public class CachedRowSetWriter implements TransactionalWriter, Serializable { // we require the record in rs to be used. // rs.close(); // pstmt.close(); - rs.first(); + rs.first(); // how many fields need to be updated int colsNotChanged = 0; @@ -552,6 +560,49 @@ public class CachedRowSetWriter implements TransactionalWriter, Serializable { orig = origVals.getObject(i); curr = crs.getObject(i); rsval = rs.getObject(i); + /* + * the following block creates equivalent objects + * that would have been created if this rs is populated + * into a CachedRowSet so that comparison of the column values + * from the ResultSet and CachedRowSet are possible + */ + Map map = (crs.getTypeMap() == null)?con.getTypeMap():crs.getTypeMap(); + if (rsval instanceof Struct) { + + Struct s = (Struct)rsval; + + // look up the class in the map + Class c = null; + c = (Class)map.get(s.getSQLTypeName()); + if (c != null) { + // create new instance of the class + SQLData obj = null; + try { + obj = (SQLData)c.newInstance(); + } catch (java.lang.InstantiationException ex) { + throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.unableins").toString(), + ex.getMessage())); + } catch (java.lang.IllegalAccessException ex) { + throw new SQLException(MessageFormat.format(resBundle.handleGetObject("cachedrowsetimpl.unableins").toString(), + ex.getMessage())); + } + // get the attributes from the struct + Object attribs[] = s.getAttributes(map); + // create the SQLInput "stream" + SQLInputImpl sqlInput = new SQLInputImpl(attribs, map); + // read the values... + obj.readSQL(sqlInput, s.getSQLTypeName()); + rsval = obj; + } + } else if (rsval instanceof SQLData) { + rsval = new SerialStruct((SQLData)rsval, map); + } else if (rsval instanceof Blob) { + rsval = new SerialBlob((Blob)rsval); + } else if (rsval instanceof Clob) { + rsval = new SerialClob((Clob)rsval); + } else if (rsval instanceof java.sql.Array) { + rsval = new SerialArray((java.sql.Array)rsval, map); + } // reset boolNull if it had been set boolNull = true; @@ -669,6 +720,9 @@ public class CachedRowSetWriter implements TransactionalWriter, Serializable { } } //end for + rs.close(); + pstmt.close(); + this.crsResolve.insertRow(); this.crsResolve.moveToCurrentRow(); @@ -1179,11 +1233,22 @@ public class CachedRowSetWriter implements TransactionalWriter, Serializable { private void buildKeyDesc(CachedRowSet crs) throws SQLException { keyCols = crs.getKeyColumns(); + ResultSetMetaData resultsetmd = crs.getMetaData(); if (keyCols == null || keyCols.length == 0) { - keyCols = new int[callerColumnCount]; - for (int i = 0; i < keyCols.length; ) { - keyCols[i] = ++i; + ArrayList listKeys = new ArrayList(); + + for (int i = 0; i < callerColumnCount; i++ ) { + if(resultsetmd.getColumnType(i+1) != java.sql.Types.CLOB && + resultsetmd.getColumnType(i+1) != java.sql.Types.STRUCT && + resultsetmd.getColumnType(i+1) != java.sql.Types.SQLXML && + resultsetmd.getColumnType(i+1) != java.sql.Types.BLOB && + resultsetmd.getColumnType(i+1) != java.sql.Types.ARRAY && + resultsetmd.getColumnType(i+1) != java.sql.Types.OTHER ) + listKeys.add(i+1); } + keyCols = new int[listKeys.size()]; + for (int i = 0; i < listKeys.size(); i++ ) + keyCols[i] = listKeys.get(i); } params = new Object[keyCols.length]; } @@ -1359,4 +1424,17 @@ public class CachedRowSetWriter implements TransactionalWriter, Serializable { } } + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + // Default state initialization happens here + ois.defaultReadObject(); + // Initialization of Res Bundle happens here . + try { + resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); + } catch(IOException ioe) { + throw new RuntimeException(ioe); + } + + } + + static final long serialVersionUID =-8506030970299413976L; } diff --git a/src/share/classes/com/sun/rowset/internal/InsertRow.java b/src/share/classes/com/sun/rowset/internal/InsertRow.java index f8474dc6f24ba45b496a89fed05f51a54f340169..914d6fb3a513d494f709efbabf505a157e4e3b6c 100644 --- a/src/share/classes/com/sun/rowset/internal/InsertRow.java +++ b/src/share/classes/com/sun/rowset/internal/InsertRow.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -157,4 +157,23 @@ public class InsertRow extends BaseRow implements Serializable, Cloneable { origVals[idx - 1] = val; markColInserted(idx - 1); } + + /** + * This method re populates the resBundle + * during the deserialization process + * + */ + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + // Default state initialization happens here + ois.defaultReadObject(); + // Initialization of transient Res Bundle happens here . + try { + resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); + } catch(IOException ioe) { + throw new RuntimeException(ioe); + } + + } + + static final long serialVersionUID = 1066099658102869344L; } diff --git a/src/share/classes/com/sun/rowset/internal/SyncResolverImpl.java b/src/share/classes/com/sun/rowset/internal/SyncResolverImpl.java index 9f23cfb27a87ab628bd76cf14a8252886b3694fb..10573ae4d4de47e4912328918edb2753430f8f07 100644 --- a/src/share/classes/com/sun/rowset/internal/SyncResolverImpl.java +++ b/src/share/classes/com/sun/rowset/internal/SyncResolverImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2004, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,7 @@ import javax.sql.rowset.spi.*; import com.sun.rowset.*; import java.io.IOException; +import java.io.ObjectInputStream; /** * There will be two sets of data which will be maintained by the rowset at the @@ -4837,4 +4838,23 @@ public class SyncResolverImpl extends CachedRowSetImpl implements SyncResolver { throws SQLException { throw new UnsupportedOperationException("Operation not yet supported"); } + + /** + * This method re populates the resBundle + * during the deserialization process + * + */ + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + // Default state initialization happens here + ois.defaultReadObject(); + // Initialization of transient Res Bundle happens here . + try { + resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); + } catch(IOException ioe) { + throw new RuntimeException(ioe); + } + + } + + static final long serialVersionUID = -3345004441725080251L; } //end class diff --git a/src/share/classes/com/sun/rowset/internal/WebRowSetXmlReader.java b/src/share/classes/com/sun/rowset/internal/WebRowSetXmlReader.java index 12a85ea77c9536a7dee0bc18e5eeaa2fa39a75d1..a0d99e4fbcf478123a78fbaa284d6d33f118c6aa 100644 --- a/src/share/classes/com/sun/rowset/internal/WebRowSetXmlReader.java +++ b/src/share/classes/com/sun/rowset/internal/WebRowSetXmlReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -216,4 +216,22 @@ public class WebRowSetXmlReader implements XmlReader, Serializable { public void readData(RowSetInternal caller) { } + /** + * This method re populates the resBundle + * during the deserialization process + * + */ + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + // Default state initialization happens here + ois.defaultReadObject(); + // Initialization of transient Res Bundle happens here . + try { + resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); + } catch(IOException ioe) { + throw new RuntimeException(ioe); + } + + } + + static final long serialVersionUID = -9127058392819008014L; } diff --git a/src/share/classes/com/sun/rowset/internal/WebRowSetXmlWriter.java b/src/share/classes/com/sun/rowset/internal/WebRowSetXmlWriter.java index 6cc6788628acabf946b26f5e75894d09a093d30a..f0d59647a7f5fe2e8740828e712daf462c86a98b 100644 --- a/src/share/classes/com/sun/rowset/internal/WebRowSetXmlWriter.java +++ b/src/share/classes/com/sun/rowset/internal/WebRowSetXmlWriter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -663,4 +663,23 @@ public class WebRowSetXmlWriter implements XmlWriter, Serializable { return s; } + + /** + * This method re populates the resBundle + * during the deserialization process + * + */ + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + // Default state initialization happens here + ois.defaultReadObject(); + // Initialization of transient Res Bundle happens here . + try { + resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); + } catch(IOException ioe) { + throw new RuntimeException(ioe); + } + + } + + static final long serialVersionUID = 7163134986189677641L; } diff --git a/src/share/classes/com/sun/rowset/providers/RIOptimisticProvider.java b/src/share/classes/com/sun/rowset/providers/RIOptimisticProvider.java index 545f8938f6c8e942c64597986f6a69d513d36c14..5fb0416736695add77469747c0dd815b6f5da5df 100644 --- a/src/share/classes/com/sun/rowset/providers/RIOptimisticProvider.java +++ b/src/share/classes/com/sun/rowset/providers/RIOptimisticProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -245,4 +245,18 @@ public final class RIOptimisticProvider extends SyncProvider implements Serializ public String getVendor() { return this.vendorName; } + + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + // Default state initialization happens here + ois.defaultReadObject(); + // Initialization of transient Res Bundle happens here . + try { + resBundle = JdbcRowSetResourceBundle.getJdbcRowSetResourceBundle(); + } catch(IOException ioe) { + throw new RuntimeException(ioe); + } + + } + static final long serialVersionUID =-3143367176751761936L; + } diff --git a/src/share/classes/com/sun/security/ntlm/Client.java b/src/share/classes/com/sun/security/ntlm/Client.java new file mode 100644 index 0000000000000000000000000000000000000000..aed8f37084dd795dab914844246fed0dc846d1dc --- /dev/null +++ b/src/share/classes/com/sun/security/ntlm/Client.java @@ -0,0 +1,212 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.security.ntlm; + +import java.math.BigInteger; +import java.util.Arrays; +import java.util.Date; +import java.util.Locale; + +/** + * The NTLM client. Not multi-thread enabled.

+ * Example: + *

+ * Client client = new Client(null, "host", "dummy",
+ *       "REALM", "t0pSeCr3t".toCharArray());
+ * byte[] type1 = client.type1();
+ * // Send type1 to server and receive response as type2
+ * byte[] type3 = client.type3(type2, nonce);
+ * // Send type3 to server
+ * 
+ */ +public final class Client extends NTLM { + final private String hostname; + final private String username; + + private String domain; // might be updated by Type 2 msg + private byte[] pw1, pw2; + + /** + * Creates an NTLM Client instance. + * @param version the NTLM version to use, which can be: + *
    + *
  • LM/NTLM: Original NTLM v1 + *
  • LM: Original NTLM v1, LM only + *
  • NTLM: Original NTLM v1, NTLM only + *
  • NTLM2: NTLM v1 with Client Challenge + *
  • LMv2/NTLMv2: NTLM v2 + *
  • LMv2: NTLM v2, LM only + *
  • NTLMv2: NTLM v2, NTLM only + *
+ * If null, "LMv2/NTLMv2" will be used. + * @param hostname hostname of the client, can be null + * @param username username to be authenticated, must not be null + * @param domain domain of {@code username}, can be null + * @param password password for {@code username}, must not be not null. + * This method does not make any modification to this parameter, it neither + * needs to access the content of this parameter after this method call, + * so you are free to modify or nullify this parameter after this call. + * @throws NullPointerException if {@code username} or {@code password} is null. + * @throws NTLMException if {@code version} is illegal + */ + public Client(String version, String hostname, String username, + String domain, char[] password) throws NTLMException { + super(version); + if ((username == null || password == null)) { + throw new NullPointerException("username/password cannot be null"); + } + this.hostname = hostname; + this.username = username; + this.domain = domain; + this.pw1 = getP1(password); + this.pw2 = getP2(password); + debug("NTLM Client: (h,u,t,version(v)) = (%s,%s,%s,%s(%s))\n", + hostname, username, domain, version, v.toString()); + } + + /** + * Generates the Type 1 message + * @return the message generated + */ + public byte[] type1() { + Writer p = new Writer(1, 32); + int flags = 0x8203; + if (hostname != null) { + flags |= 0x2000; + } + if (domain != null) { + flags |= 0x1000; + } + if (v != Version.NTLM) { + flags |= 0x80000; + } + p.writeInt(12, flags); + p.writeSecurityBuffer(24, hostname, false); + p.writeSecurityBuffer(16, domain, false); + debug("NTLM Client: Type 1 created\n"); + debug(p.getBytes()); + return p.getBytes(); + } + + /** + * Generates the Type 3 message + * @param type2 the responding Type 2 message from server, must not be null + * @param nonce random 8-byte array to be used in message generation, + * must not be null except for original NTLM v1 + * @return the message generated + * @throws NullPointerException if {@code type2} or {@code nonce} is null + * for NTLM v1. + * @throws NTLMException if the incoming message is invalid + */ + public byte[] type3(byte[] type2, byte[] nonce) throws NTLMException { + if (type2 == null || (v != Version.NTLM && nonce == null)) { + throw new NullPointerException("type2 and nonce cannot be null"); + } + debug("NTLM Client: Type 2 received\n"); + debug(type2); + Reader r = new Reader(type2); + byte[] challenge = r.readBytes(24, 8); + int inputFlags = r.readInt(20); + boolean unicode = (inputFlags & 1) == 1; + String domainFromServer = r.readSecurityBuffer(12, unicode); + if (domainFromServer != null) { + domain = domainFromServer; + } + if (domain == null) { + throw new NTLMException(NTLMException.NO_DOMAIN_INFO, + "No domain info"); + } + + int flags = 0x88200 | (inputFlags & 3); + Writer p = new Writer(3, 64); + byte[] lm = null, ntlm = null; + + p.writeSecurityBuffer(28, domain, unicode); + p.writeSecurityBuffer(36, username, unicode); + p.writeSecurityBuffer(44, hostname, unicode); + + if (v == Version.NTLM) { + byte[] lmhash = calcLMHash(pw1); + byte[] nthash = calcNTHash(pw2); + if (writeLM) lm = calcResponse (lmhash, challenge); + if (writeNTLM) ntlm = calcResponse (nthash, challenge); + } else if (v == Version.NTLM2) { + byte[] nthash = calcNTHash(pw2); + lm = ntlm2LM(nonce); + ntlm = ntlm2NTLM(nthash, nonce, challenge); + } else { + byte[] nthash = calcNTHash(pw2); + if (writeLM) lm = calcV2(nthash, + username.toUpperCase(Locale.US)+domain, nonce, challenge); + if (writeNTLM) { + byte[] alist = type2.length > 48 ? + r.readSecurityBuffer(40) : new byte[0]; + byte[] blob = new byte[32+alist.length]; + System.arraycopy(new byte[]{1,1,0,0,0,0,0,0}, 0, blob, 0, 8); + // TS + byte[] time = BigInteger.valueOf(new Date().getTime()) + .add(new BigInteger("11644473600000")) + .multiply(BigInteger.valueOf(10000)) + .toByteArray(); + for (int i=0; iSystem.out.printf(format, args) is + * called. This method is designed to be overridden by child classes to + * match their own debugging/logging mechanisms. + * @param format a format string + * @param args the arguments referenced by format + * @see java.io.PrintStream#printf(java.lang.String, java.lang.Object[]) + */ + public void debug(String format, Object... args) { + if (DEBUG) { + System.out.printf(format, args); + } + } + + /** + * Prints out the content of a byte array, called in various places inside + * the NTLM implementation for debugging/logging purposes. When the system + * property "ntlm.debug" is set, the hexdump of the array is printed into + * System.out. This method is designed to be overridden by child classes to + * match their own debugging/logging mechanisms. + * @param bytes the byte array to print out + */ + public void debug(byte[] bytes) { + if (DEBUG) { + try { + new sun.misc.HexDumpEncoder().encodeBuffer(bytes, System.out); + } catch (IOException ioe) { + // Impossible + } + } + } + + /** + * Reading an NTLM packet + */ + static class Reader { + + private final byte[] internal; + + Reader(byte[] data) { + internal = data; + } + + int readInt(int offset) throws NTLMException { + try { + return internal[offset] & 0xff + + (internal[offset+1] & 0xff << 8) + + (internal[offset+2] & 0xff << 16) + + (internal[offset+3] & 0xff << 24); + } catch (ArrayIndexOutOfBoundsException ex) { + throw new NTLMException(NTLMException.PACKET_READ_ERROR, + "Input message incorrect size"); + } + } + + int readShort(int offset) throws NTLMException { + try { + return internal[offset] & 0xff + + (internal[offset+1] & 0xff << 8); + } catch (ArrayIndexOutOfBoundsException ex) { + throw new NTLMException(NTLMException.PACKET_READ_ERROR, + "Input message incorrect size"); + } + } + + byte[] readBytes(int offset, int len) throws NTLMException { + try { + return Arrays.copyOfRange(internal, offset, offset + len); + } catch (ArrayIndexOutOfBoundsException ex) { + throw new NTLMException(NTLMException.PACKET_READ_ERROR, + "Input message incorrect size"); + } + } + + byte[] readSecurityBuffer(int offset) throws NTLMException { + int pos = readInt(offset+4); + if (pos == 0) return null; + try { + return Arrays.copyOfRange( + internal, pos, pos + readShort(offset)); + } catch (ArrayIndexOutOfBoundsException ex) { + throw new NTLMException(NTLMException.PACKET_READ_ERROR, + "Input message incorrect size"); + } + } + + String readSecurityBuffer(int offset, boolean unicode) + throws NTLMException { + byte[] raw = readSecurityBuffer(offset); + try { + return raw == null ? null : new String( + raw, unicode ? "UnicodeLittleUnmarked" : "ISO8859_1"); + } catch (UnsupportedEncodingException ex) { + throw new NTLMException(NTLMException.PACKET_READ_ERROR, + "Invalid input encoding"); + } + } + } + + /** + * Writing an NTLM packet + */ + static class Writer { + + private byte[] internal; // buffer + private int current; // current written content interface buffer + + /** + * Starts writing a NTLM packet + * @param type NEGOTIATE || CHALLENGE || AUTHENTICATE + * @param len the base length, without security buffers + */ + Writer(int type, int len) { + assert len < 256; + internal = new byte[256]; + current = len; + System.arraycopy ( + new byte[] {'N','T','L','M','S','S','P',0,(byte)type}, + 0, internal, 0, 9); + } + + void writeShort(int offset, int number) { + internal[offset] = (byte)(number); + internal[offset+1] = (byte)(number >> 8); + } + + void writeInt(int offset, int number) { + internal[offset] = (byte)(number); + internal[offset+1] = (byte)(number >> 8); + internal[offset+2] = (byte)(number >> 16); + internal[offset+3] = (byte)(number >> 24); + } + + void writeBytes(int offset, byte[] data) { + System.arraycopy(data, 0, internal, offset, data.length); + } + + void writeSecurityBuffer(int offset, byte[] data) { + if (data == null) { + writeShort(offset+4, current); + } else { + int len = data.length; + if (current + len > internal.length) { + internal = Arrays.copyOf(internal, current + len + 256); + } + writeShort(offset, len); + writeShort(offset+2, len); + writeShort(offset+4, current); + System.arraycopy(data, 0, internal, current, len); + current += len; + } + } + + void writeSecurityBuffer(int offset, String str, boolean unicode) { + try { + writeSecurityBuffer(offset, str == null ? null : str.getBytes( + unicode ? "UnicodeLittleUnmarked" : "ISO8859_1")); + } catch (UnsupportedEncodingException ex) { + assert false; + } + } + + byte[] getBytes() { + return Arrays.copyOf(internal, current); + } + } + + // LM/NTLM + + /* Convert a 7 byte array to an 8 byte array (for a des key with parity) + * input starts at offset off + */ + byte[] makeDesKey (byte[] input, int off) { + int[] in = new int [input.length]; + for (int i=0; i> 1)); + out[2] = (byte)(((in[off+1] << 6) & 0xFF) | (in[off+2] >> 2)); + out[3] = (byte)(((in[off+2] << 5) & 0xFF) | (in[off+3] >> 3)); + out[4] = (byte)(((in[off+3] << 4) & 0xFF) | (in[off+4] >> 4)); + out[5] = (byte)(((in[off+4] << 3) & 0xFF) | (in[off+5] >> 5)); + out[6] = (byte)(((in[off+5] << 2) & 0xFF) | (in[off+6] >> 6)); + out[7] = (byte)((in[off+6] << 1) & 0xFF); + return out; + } + + byte[] calcLMHash (byte[] pwb) { + byte[] magic = {0x4b, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25}; + byte[] pwb1 = new byte [14]; + int len = pwb.length; + if (len > 14) + len = 14; + System.arraycopy (pwb, 0, pwb1, 0, len); /* Zero padded */ + + try { + DESKeySpec dks1 = new DESKeySpec (makeDesKey (pwb1, 0)); + DESKeySpec dks2 = new DESKeySpec (makeDesKey (pwb1, 7)); + + SecretKey key1 = fac.generateSecret (dks1); + SecretKey key2 = fac.generateSecret (dks2); + cipher.init (Cipher.ENCRYPT_MODE, key1); + byte[] out1 = cipher.doFinal (magic, 0, 8); + cipher.init (Cipher.ENCRYPT_MODE, key2); + byte[] out2 = cipher.doFinal (magic, 0, 8); + byte[] result = new byte [21]; + System.arraycopy (out1, 0, result, 0, 8); + System.arraycopy (out2, 0, result, 8, 8); + return result; + } catch (InvalidKeyException ive) { + // Will not happen, all key material are 8 bytes + assert false; + } catch (InvalidKeySpecException ikse) { + // Will not happen, we only feed DESKeySpec to DES factory + assert false; + } catch (IllegalBlockSizeException ibse) { + // Will not happen, we encrypt 8 bytes + assert false; + } catch (BadPaddingException bpe) { + // Will not happen, this is encryption + assert false; + } + return null; // will not happen, we returned already + } + + byte[] calcNTHash (byte[] pw) { + byte[] out = md4.digest (pw); + byte[] result = new byte [21]; + System.arraycopy (out, 0, result, 0, 16); + return result; + } + + /* key is a 21 byte array. Split it into 3 7 byte chunks, + * Convert each to 8 byte DES keys, encrypt the text arg with + * each key and return the three results in a sequential [] + */ + byte[] calcResponse (byte[] key, byte[] text) { + try { + assert key.length == 21; + DESKeySpec dks1 = new DESKeySpec(makeDesKey(key, 0)); + DESKeySpec dks2 = new DESKeySpec(makeDesKey(key, 7)); + DESKeySpec dks3 = new DESKeySpec(makeDesKey(key, 14)); + SecretKey key1 = fac.generateSecret(dks1); + SecretKey key2 = fac.generateSecret(dks2); + SecretKey key3 = fac.generateSecret(dks3); + cipher.init(Cipher.ENCRYPT_MODE, key1); + byte[] out1 = cipher.doFinal(text, 0, 8); + cipher.init(Cipher.ENCRYPT_MODE, key2); + byte[] out2 = cipher.doFinal(text, 0, 8); + cipher.init(Cipher.ENCRYPT_MODE, key3); + byte[] out3 = cipher.doFinal(text, 0, 8); + byte[] result = new byte[24]; + System.arraycopy(out1, 0, result, 0, 8); + System.arraycopy(out2, 0, result, 8, 8); + System.arraycopy(out3, 0, result, 16, 8); + return result; + } catch (IllegalBlockSizeException ex) { // None will happen + assert false; + } catch (BadPaddingException ex) { + assert false; + } catch (InvalidKeySpecException ex) { + assert false; + } catch (InvalidKeyException ex) { + assert false; + } + return null; + } + + // LMv2/NTLMv2 + + byte[] hmacMD5(byte[] key, byte[] text) { + try { + SecretKeySpec skey = + new SecretKeySpec(Arrays.copyOf(key, 16), "HmacMD5"); + hmac.init(skey); + return hmac.doFinal(text); + } catch (InvalidKeyException ex) { + assert false; + } catch (RuntimeException e) { + assert false; + } + return null; + } + + byte[] calcV2(byte[] nthash, String text, byte[] blob, byte[] challenge) { + try { + byte[] ntlmv2hash = hmacMD5(nthash, + text.getBytes("UnicodeLittleUnmarked")); + byte[] cn = new byte[blob.length+8]; + System.arraycopy(challenge, 0, cn, 0, 8); + System.arraycopy(blob, 0, cn, 8, blob.length); + byte[] result = new byte[16+blob.length]; + System.arraycopy(hmacMD5(ntlmv2hash, cn), 0, result, 0, 16); + System.arraycopy(blob, 0, result, 16, blob.length); + return result; + } catch (UnsupportedEncodingException ex) { + assert false; + } + return null; + } + + // NTLM2 LM/NTLM + + static byte[] ntlm2LM(byte[] nonce) { + return Arrays.copyOf(nonce, 24); + } + + byte[] ntlm2NTLM(byte[] ntlmHash, byte[] nonce, byte[] challenge) { + byte[] b = Arrays.copyOf(challenge, 16); + System.arraycopy(nonce, 0, b, 8, 8); + byte[] sesshash = Arrays.copyOf(md5.digest(b), 8); + return calcResponse(ntlmHash, sesshash); + } + + // Password in ASCII and UNICODE + + static byte[] getP1(char[] password) { + try { + return new String(password).toUpperCase().getBytes("ISO8859_1"); + } catch (UnsupportedEncodingException ex) { + return null; + } + } + + static byte[] getP2(char[] password) { + try { + return new String(password).getBytes("UnicodeLittleUnmarked"); + } catch (UnsupportedEncodingException ex) { + return null; + } + } +} diff --git a/src/share/classes/com/sun/security/ntlm/NTLMException.java b/src/share/classes/com/sun/security/ntlm/NTLMException.java new file mode 100644 index 0000000000000000000000000000000000000000..273825de8b7b4cc5a9dd38cee0f5cf1296b2e238 --- /dev/null +++ b/src/share/classes/com/sun/security/ntlm/NTLMException.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.security.ntlm; + +import java.security.GeneralSecurityException; + +/** + * An NTLM-related Exception + */ +public final class NTLMException extends GeneralSecurityException { + + /** + * If the incoming packet is invalid. + */ + public final static int PACKET_READ_ERROR = 1; + + /** + * If the client cannot get a domain value from the server and the + * caller has not provided one. + */ + public final static int NO_DOMAIN_INFO = 2; + + /** + * If the domain provided by the client does not match the one received + * from server. + */ + //public final static int DOMAIN_UNMATCH = 3; + + /** + * If the client name is not found on server's user database. + */ + public final static int USER_UNKNOWN = 3; + + /** + * If authentication fails. + */ + public final static int AUTH_FAILED = 4; + + /** + * If an illegal version string is provided. + */ + public final static int BAD_VERSION = 5; + + private int errorCode; + + /** + * Constructs an NTLMException object. + * @param errorCode the error code, which can be retrieved by + * the {@link #errorCode() } method. + * @param msg the string message, which can be retrived by + * the {@link Exception#getMessage() } method. + */ + public NTLMException(int errorCode, String msg) { + super(msg); + this.errorCode = errorCode; + } + + /** + * Returns the error code associated with this NTLMException. + * @return the error code + */ + public int errorCode() { + return errorCode; + } +} diff --git a/src/share/classes/com/sun/security/ntlm/Server.java b/src/share/classes/com/sun/security/ntlm/Server.java new file mode 100644 index 0000000000000000000000000000000000000000..1871375a4b7f50394b5847115e25eabc2f9a54e0 --- /dev/null +++ b/src/share/classes/com/sun/security/ntlm/Server.java @@ -0,0 +1,205 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.security.ntlm; + +import java.util.Arrays; +import java.util.Locale; + +/** + * The NTLM server, not multi-thread enabled.

+ * Example: + *

+ * Server server = new Server(null, "REALM") {
+ *     public char[] getPassword(String ntdomain, String username) {
+ *         switch (username) {
+ *             case "dummy": return "t0pSeCr3t".toCharArray();
+ *             case "guest": return "".toCharArray();
+ *             default: return null;
+ *         }
+ *     }
+ * };
+ * // Receive client request as type1
+ * byte[] type2 = server.type2(type1, nonce);
+ * // Send type2 to client and receive type3
+ * verify(type3, nonce);
+ * 
+ */ +public abstract class Server extends NTLM { + final private String domain; + final private boolean allVersion; + /** + * Creates a Server instance. + * @param version the NTLM version to use, which can be: + *
    + *
  • NTLM: Original NTLM v1 + *
  • NTLM2: NTLM v1 with Client Challenge + *
  • NTLMv2: NTLM v2 + *
+ * If null, all versions will be supported. Please note that unless NTLM2 + * is selected, authentication succeeds if one of LM (or LMv2) or + * NTLM (or NTLMv2) is verified. + * @param domain the domain, must not be null + * @throws NullPointerException if {@code domain} is null. + */ + public Server(String version, String domain) throws NTLMException { + super(version); + if (domain == null) { + throw new NullPointerException("domain cannot be null"); + } + this.allVersion = (version == null); + this.domain = domain; + debug("NTLM Server: (t,version) = (%s,%s)\n", domain, version); + } + + /** + * Generates the Type 2 message + * @param type1 the Type1 message received, must not be null + * @param nonce the random 8-byte array to be used in message generation, + * must not be null + * @return the message generated + * @throws NullPointerException if type1 or nonce is null + * @throws NTLMException if the incoming message is invalid + */ + public byte[] type2(byte[] type1, byte[] nonce) { + if (nonce == null) { + throw new NullPointerException("nonce cannot be null"); + } + debug("NTLM Server: Type 1 received\n"); + if (type1 != null) debug(type1); + Writer p = new Writer(2, 32); + int flags = 0x80205; + p.writeSecurityBuffer(12, domain, true); + p.writeInt(20, flags); + p.writeBytes(24, nonce); + debug("NTLM Server: Type 2 created\n"); + debug(p.getBytes()); + return p.getBytes(); + } + + /** + * Verifies the Type3 message received from client and returns + * various negotiated information. + * @param type3 the incoming Type3 message from client, must not be null + * @param nonce the same nonce provided in {@link #type2}, must not be null + * @return username and hostname of the client in a byte array + * @throws NullPointerException if {@code type3} or {@code nonce} is null + * @throws NTLMException if the incoming message is invalid + */ + public String[] verify(byte[] type3, byte[] nonce) + throws NTLMException { + if (type3 == null || nonce == null) { + throw new NullPointerException("type1 or nonce cannot be null"); + } + debug("NTLM Server: Type 3 received\n"); + if (type3 != null) debug(type3); + Reader r = new Reader(type3); + String username = r.readSecurityBuffer(36, true); + String hostname = r.readSecurityBuffer(44, true); + String incomingDomain = r.readSecurityBuffer(28, true); + /*if (incomingDomain != null && !incomingDomain.equals(domain)) { + throw new NTLMException(NTLMException.DOMAIN_UNMATCH, + "Wrong domain: " + incomingDomain + + " vs " + domain); // Needed? + }*/ + boolean verified = false; + char[] password = getPassword(domain, username); + if (password == null) { + throw new NTLMException(NTLMException.USER_UNKNOWN, + "Unknown user"); + } + byte[] incomingLM = r.readSecurityBuffer(12); + byte[] incomingNTLM = r.readSecurityBuffer(20); + + if (!verified && (allVersion || v == Version.NTLM)) { + if (incomingLM.length > 0) { + byte[] pw1 = getP1(password); + byte[] lmhash = calcLMHash(pw1); + byte[] lmresponse = calcResponse (lmhash, nonce); + if (Arrays.equals(lmresponse, incomingLM)) { + verified = true; + } + } + if (incomingNTLM.length > 0) { + byte[] pw2 = getP2(password); + byte[] nthash = calcNTHash(pw2); + byte[] ntresponse = calcResponse (nthash, nonce); + if (Arrays.equals(ntresponse, incomingNTLM)) { + verified = true; + } + } + debug("NTLM Server: verify using NTLM: " + verified + "\n"); + } + if (!verified && (allVersion || v == Version.NTLM2)) { + byte[] pw2 = getP2(password); + byte[] nthash = calcNTHash(pw2); + byte[] clientNonce = Arrays.copyOf(incomingLM, 8); + byte[] ntlmresponse = ntlm2NTLM(nthash, clientNonce, nonce); + if (Arrays.equals(incomingNTLM, ntlmresponse)) { + verified = true; + } + debug("NTLM Server: verify using NTLM2: " + verified + "\n"); + } + if (!verified && (allVersion || v == Version.NTLMv2)) { + byte[] pw2 = getP2(password); + byte[] nthash = calcNTHash(pw2); + if (incomingLM.length > 0) { + byte[] clientNonce = Arrays.copyOfRange( + incomingLM, 16, incomingLM.length); + byte[] lmresponse = calcV2(nthash, + username.toUpperCase(Locale.US)+incomingDomain, + clientNonce, nonce); + if (Arrays.equals(lmresponse, incomingLM)) { + verified = true; + } + } + if (incomingNTLM.length > 0) { + byte[] clientBlob = Arrays.copyOfRange( + incomingNTLM, 16, incomingNTLM.length); + byte[] ntlmresponse = calcV2(nthash, + username.toUpperCase(Locale.US)+incomingDomain, + clientBlob, nonce); + if (Arrays.equals(ntlmresponse, incomingNTLM)) { + verified = true; + } + } + debug("NTLM Server: verify using NTLMv2: " + verified + "\n"); + } + if (!verified) { + throw new NTLMException(NTLMException.AUTH_FAILED, + "None of LM and NTLM verified"); + } + return new String[] {username, hostname}; + } + + /** + * Retrieves the password for a given user. This method should be + * overridden in a concrete class. + * @param domain can be null + * @param username must not be null + * @return the password for the user, or null if unknown + */ + public abstract char[] getPassword(String domain, String username); +} diff --git a/src/share/classes/com/sun/security/ntlm/Version.java b/src/share/classes/com/sun/security/ntlm/Version.java new file mode 100644 index 0000000000000000000000000000000000000000..bd4d0c4a07d85214b67c78e1f36079d33fff8c25 --- /dev/null +++ b/src/share/classes/com/sun/security/ntlm/Version.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.security.ntlm; + +enum Version { + NTLM, NTLM2, NTLMv2 +} diff --git a/src/share/classes/com/sun/security/sasl/Provider.java b/src/share/classes/com/sun/security/sasl/Provider.java index 8e43d59f357bb8b15cf20db5902b0adb55e3e359..8b9c00c8800887d6be449409d6c5070d62fc8100 100644 --- a/src/share/classes/com/sun/security/sasl/Provider.java +++ b/src/share/classes/com/sun/security/sasl/Provider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,10 +35,12 @@ import java.security.PrivilegedAction; * - CRAM-MD5 * - DIGEST-MD5 * - GSSAPI/Kerberos v5 + * - NTLM * And server support for * - CRAM-MD5 * - DIGEST-MD5 * - GSSAPI/Kerberos v5 + * - NTLM */ public final class Provider extends java.security.Provider { @@ -47,8 +49,8 @@ public final class Provider extends java.security.Provider { private static final String info = "Sun SASL provider" + "(implements client mechanisms for: " + - "DIGEST-MD5, GSSAPI, EXTERNAL, PLAIN, CRAM-MD5;" + - " server mechanisms for: DIGEST-MD5, GSSAPI, CRAM-MD5)"; + "DIGEST-MD5, GSSAPI, EXTERNAL, PLAIN, CRAM-MD5, NTLM;" + + " server mechanisms for: DIGEST-MD5, GSSAPI, CRAM-MD5, NTLM)"; public Provider() { super("SunSASL", 1.7d, info); @@ -58,6 +60,8 @@ public final class Provider extends java.security.Provider { // Client mechanisms put("SaslClientFactory.DIGEST-MD5", "com.sun.security.sasl.digest.FactoryImpl"); + put("SaslClientFactory.NTLM", + "com.sun.security.sasl.ntlm.FactoryImpl"); put("SaslClientFactory.GSSAPI", "com.sun.security.sasl.gsskerb.FactoryImpl"); @@ -75,6 +79,8 @@ public final class Provider extends java.security.Provider { "com.sun.security.sasl.gsskerb.FactoryImpl"); put("SaslServerFactory.DIGEST-MD5", "com.sun.security.sasl.digest.FactoryImpl"); + put("SaslServerFactory.NTLM", + "com.sun.security.sasl.ntlm.FactoryImpl"); return null; } }); diff --git a/src/share/classes/com/sun/security/sasl/ntlm/FactoryImpl.java b/src/share/classes/com/sun/security/sasl/ntlm/FactoryImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..6ee1b66f968e1c4bc78a8f034a3652fd2452b96a --- /dev/null +++ b/src/share/classes/com/sun/security/sasl/ntlm/FactoryImpl.java @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.security.sasl.ntlm; + +import java.util.Map; + +import javax.security.sasl.*; +import javax.security.auth.callback.CallbackHandler; + +import com.sun.security.sasl.util.PolicyUtils; + + +/** + * Client and server factory for NTLM SASL client/server mechanisms. + * See NTLMClient and NTLMServer for input requirements. + * + * @since 1.7 + */ + +public final class FactoryImpl implements SaslClientFactory, +SaslServerFactory{ + + private static final String myMechs[] = { "NTLM" }; + private static final int mechPolicies[] = { + PolicyUtils.NOPLAINTEXT|PolicyUtils.NOANONYMOUS + }; + + /** + * Empty constructor. + */ + public FactoryImpl() { + } + + /** + * Returns a new instance of the NTLM SASL client mechanism. + * Argument checks are performed in SaslClient's constructor. + * @returns a new SaslClient ; otherwise null if unsuccessful. + * @throws SaslException If there is an error creating the NTLM + * SASL client. + */ + public SaslClient createSaslClient(String[] mechs, + String authorizationId, String protocol, String serverName, + Map props, CallbackHandler cbh) + throws SaslException { + + for (int i=0; i props, CallbackHandler cbh) + throws SaslException { + + if (mech.equals("NTLM") && + PolicyUtils.checkPolicy(mechPolicies[0], props)) { + if (props != null) { + String qop = (String)props.get(Sasl.QOP); + if (qop != null && !qop.equals("auth")) { + throw new SaslException("NTLM only support auth"); + } + } + if (cbh == null) { + throw new SaslException( + "Callback handler with support for AuthorizeCallback, "+ + "RealmCallback, NameCallback, and PasswordCallback " + + "required"); + } + return new NTLMServer(mech, protocol, serverName, props, cbh); + } + return null; + } + + /** + * Returns the authentication mechanisms that this factory can produce. + * + * @returns String[] {"NTLM"} if policies in env match those of this + * factory. + */ + public String[] getMechanismNames(Map env) { + return PolicyUtils.filterMechs(myMechs, mechPolicies, env); + } +} diff --git a/src/share/classes/com/sun/security/sasl/ntlm/NTLMClient.java b/src/share/classes/com/sun/security/sasl/ntlm/NTLMClient.java new file mode 100644 index 0000000000000000000000000000000000000000..e57466758743040ba26093a149253710b073cbad --- /dev/null +++ b/src/share/classes/com/sun/security/sasl/ntlm/NTLMClient.java @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.security.sasl.ntlm; + +import com.sun.security.ntlm.Client; +import com.sun.security.ntlm.NTLMException; +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.Map; +import java.util.Random; +import javax.security.auth.callback.Callback; + + +import javax.security.sasl.*; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * Required callbacks: + * - RealmCallback + * handle can provide domain info for authentication, optional + * - NameCallback + * handler must enter username to use for authentication + * - PasswordCallback + * handler must enter password for username to use for authentication + * + * Environment properties that affect behavior of implementation: + * + * javax.security.sasl.qop + * String, quality of protection; only "auth" is accepted, default "auth" + * + * com.sun.security.sasl.ntlm.version + * String, name a specific version to use; can be: + * LM/NTLM: Original NTLM v1 + * LM: Original NTLM v1, LM only + * NTLM: Original NTLM v1, NTLM only + * NTLM2: NTLM v1 with Client Challenge + * LMv2/NTLMv2: NTLM v2 + * LMv2: NTLM v2, LM only + * NTLMv2: NTLM v2, NTLM only + * If not specified, use system property "ntlm.version". If + * still not specified, use default value "LMv2/NTLMv2". + * + * com.sun.security.sasl.ntlm.random + * java.util.Random, the nonce source to be used in NTLM v2 or NTLM v1 with + * Client Challenge. Default null, an internal java.util.Random object + * will be used + * + * Negotiated Properties: + * + * javax.security.sasl.qop + * Always "auth" + * + * com.sun.security.sasl.html.domain + * The domain for the user, provided by the server + * + * @see RFC 2222 + * - Simple Authentication and Security Layer (SASL) + * + */ +final class NTLMClient implements SaslClient { + + private static final String NTLM_VERSION = + "com.sun.security.sasl.ntlm.version"; + private static final String NTLM_RANDOM = + "com.sun.security.sasl.ntlm.random"; + private final static String NTLM_DOMAIN = + "com.sun.security.sasl.ntlm.domain"; + private final static String NTLM_HOSTNAME = + "com.sun.security.sasl.ntlm.hostname"; + + private final Client client; + private final String mech; + private final Random random; + + private int step = 0; // 0-start,1-nego,2-auth,3-done + + /** + * @param mech non-null + * @param authorizationId can be null or empty and ignored + * @param protocol non-null for Sasl, useless for NTLM + * @param serverName non-null for Sasl, but can be null for NTLM + * @param props can be null + * @param cbh can be null for Sasl, but will throw NPE for NTLM + * @throws SaslException + */ + NTLMClient(String mech, String authzid, String protocol, String serverName, + Map props, CallbackHandler cbh) throws SaslException { + + this.mech = mech; + String version = null; + Random rtmp = null; + String hostname = null; + + if (props != null) { + String qop = (String)props.get(Sasl.QOP); + if (qop != null && !qop.equals("auth")) { + throw new SaslException("NTLM only support auth"); + } + version = (String)props.get(NTLM_VERSION); + rtmp = (Random)props.get(NTLM_RANDOM); + hostname = (String)props.get(NTLM_HOSTNAME); + } + this.random = rtmp != null ? rtmp : new Random(); + + if (version == null) { + version = System.getProperty("ntlm.version"); + } + + RealmCallback dcb = (serverName != null && !serverName.isEmpty())? + new RealmCallback("Realm: ", serverName) : + new RealmCallback("Realm: "); + NameCallback ncb = (authzid != null && !authzid.isEmpty()) ? + new NameCallback("User name: ", authzid) : + new NameCallback("User name: "); + PasswordCallback pcb = + new PasswordCallback("Password: ", false); + + try { + cbh.handle(new Callback[] {dcb, ncb, pcb}); + } catch (UnsupportedCallbackException e) { + throw new SaslException("NTLM: Cannot perform callback to " + + "acquire realm, username or password", e); + } catch (IOException e) { + throw new SaslException( + "NTLM: Error acquiring realm, username or password", e); + } + + if (hostname == null) { + try { + hostname = InetAddress.getLocalHost().getCanonicalHostName(); + } catch (UnknownHostException e) { + hostname = "localhost"; + } + } + try { + client = new Client(version, hostname, + ncb.getName(), + dcb.getText(), + pcb.getPassword()); + } catch (NTLMException ne) { + throw new SaslException( + "NTLM: Invalid version string: " + version, ne); + } + } + + @Override + public String getMechanismName() { + return mech; + } + + @Override + public boolean isComplete() { + return step >= 2; + } + + @Override + public byte[] unwrap(byte[] incoming, int offset, int len) + throws SaslException { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public byte[] wrap(byte[] outgoing, int offset, int len) + throws SaslException { + throw new UnsupportedOperationException("Not supported."); + } + + @Override + public Object getNegotiatedProperty(String propName) { + if (propName.equals(Sasl.QOP)) { + return "auth"; + } else if (propName.equals(NTLM_DOMAIN)) { + return client.getDomain(); + } else { + return null; + } + } + + @Override + public void dispose() throws SaslException { + client.dispose(); + } + + @Override + public boolean hasInitialResponse() { + return true; + } + + @Override + public byte[] evaluateChallenge(byte[] challenge) throws SaslException { + step++; + if (step == 1) { + return client.type1(); + } else { + try { + byte[] nonce = new byte[8]; + random.nextBytes(nonce); + return client.type3(challenge, nonce); + } catch (NTLMException ex) { + throw new SaslException("Type3 creation failed", ex); + } + } + } +} diff --git a/src/share/classes/com/sun/security/sasl/ntlm/NTLMServer.java b/src/share/classes/com/sun/security/sasl/ntlm/NTLMServer.java new file mode 100644 index 0000000000000000000000000000000000000000..7adbeb7d37e2e9ea9b84b8d3508f78479ac16f8f --- /dev/null +++ b/src/share/classes/com/sun/security/sasl/ntlm/NTLMServer.java @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package com.sun.security.sasl.ntlm; + +import com.sun.security.ntlm.NTLMException; +import com.sun.security.ntlm.Server; +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.util.Map; +import java.util.Random; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.sasl.*; + +/** + * Required callbacks: + * - RealmCallback + * used as key by handler to fetch password, optional + * - NameCallback + * used as key by handler to fetch password + * - PasswordCallback + * handler must enter password for username/realm supplied + * + * Environment properties that affect the implementation: + * + * javax.security.sasl.qop + * String, quality of protection; only "auth" is accepted, default "auth" + * + * com.sun.security.sasl.ntlm.version + * String, name a specific version to accept: + * LM/NTLM: Original NTLM v1 + * LM: Original NTLM v1, LM only + * NTLM: Original NTLM v1, NTLM only + * NTLM2: NTLM v1 with Client Challenge + * LMv2/NTLMv2: NTLM v2 + * LMv2: NTLM v2, LM only + * NTLMv2: NTLM v2, NTLM only + * If not specified, use system property "ntlm.version". If also + * not specfied, all versions are accepted. + * + * com.sun.security.sasl.ntlm.domain + * String, the domain of the server, default is server name (fqdn parameter) + * + * com.sun.security.sasl.ntlm.random + * java.util.Random, the nonce source. Default null, an internal + * java.util.Random object will be used + * + * Negotiated Properties: + * + * javax.security.sasl.qop + * Always "auth" + * + * com.sun.security.sasl.ntlm.hostname + * The hostname for the user, provided by the client + * + */ + +final class NTLMServer implements SaslServer { + + private final static String NTLM_VERSION = + "com.sun.security.sasl.ntlm.version"; + private final static String NTLM_DOMAIN = + "com.sun.security.sasl.ntlm.domain"; + private final static String NTLM_HOSTNAME = + "com.sun.security.sasl.ntlm.hostname"; + private static final String NTLM_RANDOM = + "com.sun.security.sasl.ntlm.random"; + + private final Random random; + private final Server server; + private byte[] nonce; + private int step = 0; + private String authzId; + private final String mech; + private String hostname; + + /** + * @param mech not null + * @param protocol not null for Sasl, ignored in NTLM + * @param serverName not null for Sasl, can be null in NTLM. If non-null, + * might be used as domain if not provided in props + * @param props can be null + * @param cbh can be null for Sasl, but will throw NPE in auth for NTLM + * @throws SaslException + */ + NTLMServer(String mech, String protocol, String serverName, + Map props, final CallbackHandler cbh) throws SaslException { + + this.mech = mech; + String version = null; + String domain = null; + Random rtmp = null; + + if (props != null) { + domain = (String) props.get(NTLM_DOMAIN); + version = (String)props.get(NTLM_VERSION); + rtmp = (Random)props.get(NTLM_RANDOM); + } + random = rtmp != null ? rtmp : new Random(); + + if (version == null) { + version = System.getProperty("ntlm.version"); + } + if (domain == null) { + domain = serverName; + } + if (domain == null) { + throw new NullPointerException("Domain must be provided as" + + " the serverName argument or in props"); + } + + try { + server = new Server(version, domain) { + public char[] getPassword(String ntdomain, String username) { + try { + RealmCallback rcb = new RealmCallback( + "Domain: ", ntdomain); + NameCallback ncb = new NameCallback( + "Name: ", username); + PasswordCallback pcb = new PasswordCallback( + "Password: ", false); + cbh.handle(new Callback[] { rcb, ncb, pcb }); + char[] passwd = pcb.getPassword(); + pcb.clearPassword(); + return passwd; + } catch (IOException ioe) { + return null; + } catch (UnsupportedCallbackException uce) { + return null; + } + } + }; + } catch (NTLMException ne) { + throw new SaslException( + "NTLM: Invalid version string: " + version, ne); + } + nonce = new byte[8]; + } + + @Override + public String getMechanismName() { + return mech; + } + + @Override + public byte[] evaluateResponse(byte[] response) throws SaslException { + try { + step++; + if (step == 1) { + random.nextBytes(nonce); + return server.type2(response, nonce); + } else { + String[] out = server.verify(response, nonce); + authzId = out[0]; + hostname = out[1]; + return null; + } + } catch (GeneralSecurityException ex) { + throw new SaslException("", ex); + } + } + + @Override + public boolean isComplete() { + return step >= 2; + } + + @Override + public String getAuthorizationID() { + return authzId; + } + + @Override + public byte[] unwrap(byte[] incoming, int offset, int len) + throws SaslException { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public byte[] wrap(byte[] outgoing, int offset, int len) + throws SaslException { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Object getNegotiatedProperty(String propName) { + if (propName.equals(Sasl.QOP)) { + return "auth"; + } else if (propName.equals(NTLM_HOSTNAME)) { + return hostname; + } else { + return null; + } + } + + @Override + public void dispose() throws SaslException { + return; + } +} diff --git a/src/share/classes/java/lang/AutoCloseable.java b/src/share/classes/java/lang/AutoCloseable.java index 18c28fe887bd099b229cb3b1eb736b356be64ef8..a44f5840cd723e055488d757a55849c59980fbc9 100644 --- a/src/share/classes/java/lang/AutoCloseable.java +++ b/src/share/classes/java/lang/AutoCloseable.java @@ -34,8 +34,8 @@ package java.lang; public interface AutoCloseable { /** * Close this resource, relinquishing any underlying resources. - * This method is invoked automatically by the automatic resource - * management block construct. + * This method is invoked automatically by the {@code + * try}-with-resources statement. * *

Classes implementing this method are strongly encouraged to * be declared to throw more specific exceptions (or no exception diff --git a/src/share/classes/java/lang/Character.java b/src/share/classes/java/lang/Character.java index 69134bda77f1235c264b16bc8be0228558a919ae..0a64916d3bfe9fd79db31b305c946f38161b2b01 100644 --- a/src/share/classes/java/lang/Character.java +++ b/src/share/classes/java/lang/Character.java @@ -595,7 +595,6 @@ class Character implements java.io.Serializable, Comparable { /** * Constructs a new Subset instance. * - * @exception NullPointerException if name is null * @param name The name of this subset * @exception NullPointerException if name is null */ diff --git a/src/share/classes/java/lang/CharacterName.java b/src/share/classes/java/lang/CharacterName.java index d097340871d93619953e7012add5566c676fe163..cac053b10e8fa37654746e9a0930ea4fbd19c19a 100644 --- a/src/share/classes/java/lang/CharacterName.java +++ b/src/share/classes/java/lang/CharacterName.java @@ -1,12 +1,12 @@ /* - * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved. + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Sun designates this + * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided - * by Sun in the LICENSE file that accompanied this code. + * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or @@ -18,9 +18,9 @@ * 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. + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. */ package java.lang; diff --git a/src/share/classes/java/lang/ClassLoader.java b/src/share/classes/java/lang/ClassLoader.java index d3346417c0fe2db6087d4b5dfa86c916ba1b8ce2..fd9ba91d720dee31da3e731cb6aa273eb568e313 100644 --- a/src/share/classes/java/lang/ClassLoader.java +++ b/src/share/classes/java/lang/ClassLoader.java @@ -823,7 +823,7 @@ public abstract class ClassLoader { * * * @param name - * The expected binary namebinary name. of the class, or * null if not known * * @param b diff --git a/src/share/classes/java/lang/Object.java b/src/share/classes/java/lang/Object.java index 2e1011a60dad90c32b483f3acd4dd7db98cdfc30..c730db44e23cc6318b95787aa1218b36ef81a715 100644 --- a/src/share/classes/java/lang/Object.java +++ b/src/share/classes/java/lang/Object.java @@ -189,7 +189,9 @@ public class Object { * specific cloning operation. First, if the class of this object does * not implement the interface {@code Cloneable}, then a * {@code CloneNotSupportedException} is thrown. Note that all arrays - * are considered to implement the interface {@code Cloneable}. + * are considered to implement the interface {@code Cloneable} and that + * the return type of the {@code clone} method of an array type {@code T[]} + * is {@code T[]} where T is any reference or primitive type. * Otherwise, this method creates a new instance of the class of this * object and initializes all its fields with exactly the contents of * the corresponding fields of this object, as if by assignment; the diff --git a/src/share/classes/java/lang/Throwable.java b/src/share/classes/java/lang/Throwable.java index 4d4169139e8078558c790702615892e97b0167d7..0073d3f3c3d5a7b190dc76d50e9bdcf1574a9c27 100644 --- a/src/share/classes/java/lang/Throwable.java +++ b/src/share/classes/java/lang/Throwable.java @@ -498,8 +498,8 @@ public class Throwable implements Serializable { * } * * As of release 7, the platform supports the notion of - * suppressed exceptions (in conjunction with automatic - * resource management blocks). Any exceptions that were + * suppressed exceptions (in conjunction with the {@code + * try}-with-resources statement). Any exceptions that were * suppressed in order to deliver an exception are printed out * beneath the stack trace. The format of this information * depends on the implementation, but the following example may be @@ -805,7 +805,7 @@ public class Throwable implements Serializable { /** * Adds the specified exception to the list of exceptions that - * were suppressed, typically by the automatic resource management + * were suppressed, typically by the {@code try}-with-resources * statement, in order to deliver this exception. * *

Note that when one exception {@linkplain @@ -839,7 +839,7 @@ public class Throwable implements Serializable { /** * Returns an array containing all of the exceptions that were - * suppressed, typically by the automatic resource management + * suppressed, typically by the {@code try}-with-resources * statement, in order to deliver this exception. * * @return an array containing all of the exceptions that were diff --git a/src/share/classes/java/lang/reflect/Constructor.java b/src/share/classes/java/lang/reflect/Constructor.java index 6515eb282d9e72022cd1ed18beb343926977b738..e4b808e41bf290be3c05fd08f1da9cdba4572cfe 100644 --- a/src/share/classes/java/lang/reflect/Constructor.java +++ b/src/share/classes/java/lang/reflect/Constructor.java @@ -166,8 +166,7 @@ public final /** * Returns the name of this constructor, as a string. This is - * always the same as the simple name of the constructor's declaring - * class. + * the binary name of the constructor's declaring class. */ public String getName() { return getDeclaringClass().getName(); diff --git a/src/share/classes/java/net/SdpSocketImpl.java b/src/share/classes/java/net/SdpSocketImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..b5b023e2433a6bb34be01727271e2350835b9624 --- /dev/null +++ b/src/share/classes/java/net/SdpSocketImpl.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.IOException; +import java.io.FileDescriptor; + +import sun.net.sdp.SdpSupport; + +/** + * SocketImpl that supports the SDP protocol + */ +class SdpSocketImpl extends PlainSocketImpl { + SdpSocketImpl() { } + + @Override + protected void create(boolean stream) throws IOException { + if (!stream) + throw new UnsupportedOperationException("Must be a stream socket"); + fd = SdpSupport.createSocket(); + if (socket != null) + socket.setCreated(); + if (serverSocket != null) + serverSocket.setCreated(); + } +} diff --git a/src/share/classes/java/net/ServerSocket.java b/src/share/classes/java/net/ServerSocket.java index 8189d8aa4f0fd8865fe83f8df62e44e8c9350657..d4b44e4ccb29324a725abd1357dfeaeaae4d1e53 100644 --- a/src/share/classes/java/net/ServerSocket.java +++ b/src/share/classes/java/net/ServerSocket.java @@ -68,6 +68,15 @@ class ServerSocket implements java.io.Closeable { */ private boolean oldImpl = false; + /** + * Package-private constructor to create a ServerSocket associated with + * the given SocketImpl. + */ + ServerSocket(SocketImpl impl) { + this.impl = impl; + impl.setServerSocket(this); + } + /** * Creates an unbound server socket. * diff --git a/src/share/classes/java/nio/file/DirectoryIteratorException.java b/src/share/classes/java/nio/file/DirectoryIteratorException.java new file mode 100644 index 0000000000000000000000000000000000000000..729c84a77cda1ad973c34bdff4495135055a66b6 --- /dev/null +++ b/src/share/classes/java/nio/file/DirectoryIteratorException.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.nio.file; + +import java.util.ConcurrentModificationException; +import java.util.Objects; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.InvalidObjectException; + +/** + * Runtime exception thrown if an I/O error is encountered when iterating over + * the entries in a directory. The I/O error is retrieved as an {@link + * IOException} using the {@link #getCause() getCause()} method. + * + * @since 1.7 + * @see DirectoryStream + */ + +public final class DirectoryIteratorException + extends ConcurrentModificationException +{ + private static final long serialVersionUID = -6012699886086212874L; + + /** + * Constructs an instance of this class. + * + * @param cause + * the {@code IOException} that caused the directory iteration + * to fail + * + * @throws NullPointerException + * if the cause is {@code null} + */ + public DirectoryIteratorException(IOException cause) { + super(Objects.nonNull(cause)); + } + + /** + * Returns the cause of this exception. + * + * @return the cause + */ + @Override + public IOException getCause() { + return (IOException)super.getCause(); + } + + /** + * Called to read the object from a stream. + * + * @throws InvalidObjectException + * if the object is invalid or has a cause that is not + * an {@code IOException} + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException + { + s.defaultReadObject(); + Throwable cause = super.getCause(); + if (!(cause instanceof IOException)) + throw new InvalidObjectException("Cause must be an IOException"); + } +} diff --git a/src/share/classes/java/nio/file/DirectoryStream.java b/src/share/classes/java/nio/file/DirectoryStream.java index 61fa52cec4d7ffe54ff60e95aa39591985155501..fd7bf85c9398c4874a690c9368fe227cfbf9444e 100644 --- a/src/share/classes/java/nio/file/DirectoryStream.java +++ b/src/share/classes/java/nio/file/DirectoryStream.java @@ -31,60 +31,84 @@ import java.io.IOException; /** * An object to iterate over the entries in a directory. A directory stream - * allows for convenient use of the for-each construct: + * allows for the convenient use of the for-each construct to iterate over a + * directory. + * + *

While {@code DirectoryStream} extends {@code Iterable}, it is not a + * general-purpose {@code Iterable} as it supports only a single {@code + * Iterator}; invoking the {@link #iterator iterator} method to obtain a second + * or subsequent iterator throws {@code IllegalStateException}. + * + *

An important property of the directory stream's {@code Iterator} is that + * its {@link Iterator#hasNext() hasNext} method is guaranteed to read-ahead by + * at least one element. If {@code hasNext} method returns {@code true}, and is + * followed by a call to the {@code next} method, it is guaranteed that the + * {@code next} method will not throw an exception due to an I/O error, or + * because the stream has been {@link #close closed}. The {@code Iterator} does + * not support the {@link Iterator#remove remove} operation. + * + *

A {@code DirectoryStream} is opened upon creation and is closed by + * invoking the {@code close} method. Closing a directory stream releases any + * resources associated with the stream. Failure to close the stream may result + * in a resource leak. The try-with-resources statement provides a useful + * construct to ensure that the stream is closed: *

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

A {@code DirectoryStream} is not a general-purpose {@code Iterable}. - * While this interface extends {@code Iterable}, the {@code iterator} method - * may only be invoked once to obtain the iterator; a second, or subsequent, - * call to the {@code iterator} method throws {@code IllegalStateException}. - * - *

A {@code DirectoryStream} is opened upon creation and is closed by - * invoking the {@link #close close} method. Closing the directory stream - * releases any resources associated with the stream. Once a directory stream - * is closed, all further method invocations on the iterator throw {@link - * java.util.ConcurrentModificationException} with cause {@link - * ClosedDirectoryStreamException}. + *

Once a directory stream is closed, then further access to the directory, + * using the {@code Iterator}, behaves as if the end of stream has been reached. + * Due to read-ahead, the {@code Iterator} may return one or more elements + * after the directory stream has been closed. Once these buffered elements + * have been read, then subsequent calls to the {@code hasNext} method returns + * {@code false}, and subsequent calls to the {@code next} method will throw + * {@code NoSuchElementException}. * *

A directory stream is not required to be asynchronously closeable. * If a thread is blocked on the directory stream's iterator reading from the * directory, and another thread invokes the {@code close} method, then the * second thread may block until the read operation is complete. * - *

The {@link Iterator#hasNext() hasNext} and {@link Iterator#next() next} - * methods can encounter an I/O error when iterating over the directory in which - * case {@code ConcurrentModificationException} is thrown with cause - * {@link java.io.IOException}. The {@code hasNext} method is guaranteed to - * read-ahead by at least one element. This means that if the {@code hasNext} - * method returns {@code true} and is followed by a call to the {@code next} - * method then it is guaranteed not to fail with a {@code - * ConcurrentModificationException}. + *

If an I/O error is encountered when accessing the directory then it + * causes the {@code Iterator}'s {@code hasNext} or {@code next} methods to + * throw {@link DirectoryIteratorException} with the {@link IOException} as the + * cause. As stated above, the {@code hasNext} method is guaranteed to + * read-ahead by at least one element. This means that if {@code hasNext} method + * returns {@code true}, and is followed by a call to the {@code next} method, + * then it is guaranteed that the {@code next} method will not fail with a + * {@code DirectoryIteratorException}. * *

The elements returned by the iterator are in no specific order. Some file * systems maintain special links to the directory itself and the directory's * parent directory. Entries representing these links are not returned by the * iterator. * - *

The iterator's {@link Iterator#remove() remove} method removes the - * directory entry for the last element returned by the iterator, as if by - * invoking the {@link Path#delete delete} method. If an I/O error or - * security exception occurs then {@code ConcurrentModificationException} is - * thrown with the cause. - * *

The iterator is weakly consistent. It is thread safe but does not * freeze the directory while iterating, so it may (or may not) reflect updates * to the directory that occur after the {@code DirectoryStream} is created. * + *

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

+ *   List<Path> listSourceFiles(Path dir) throws IOException {
+ *       List<Path> result = new ArrayList<Path>();
+ *       try (DirectoryStream<Path> stream = dir.newDirectoryStream("*.{c,h,cpp,hpp,java}")) {
+ *           for (Path entry: stream) {
+ *               result.add(entry);
+ *           }
+ *       } catch (DirectoryIteratorException ex) {
+ *           // I/O error encounted during the iteration, the cause is an IOException
+ *           throw ex.getCause();
+ *       }
+ *       return result;
+ *   }
+ * 
* @param The type of element returned by the iterator * * @since 1.7 diff --git a/src/share/classes/java/nio/file/Path.java b/src/share/classes/java/nio/file/Path.java index 029feee58a0a4fd6abbf38359a115346b436d273..abdc77750cc83e2ccc0bff8fc4311cc4db009cd2 100644 --- a/src/share/classes/java/nio/file/Path.java +++ b/src/share/classes/java/nio/file/Path.java @@ -984,11 +984,11 @@ public abstract class Path * directory. * *

Where the filter terminates due to an uncaught error or runtime - * exception then it is propogated to the iterator's {@link Iterator#hasNext() + * exception then it is propagated to the {@link Iterator#hasNext() * hasNext} or {@link Iterator#next() next} method. Where an {@code - * IOException} is thrown, it is propogated as a {@link - * java.util.ConcurrentModificationException} with the {@code - * IOException} as the cause. + * IOException} is thrown, it results in the {@code hasNext} or {@code + * next} method throwing a {@link DirectoryIteratorException} with the + * {@code IOException} as the cause. * *

When an implementation supports operations on entries in the * directory that execute in a race-free manner then the returned directory diff --git a/src/share/classes/java/nio/file/SecureDirectoryStream.java b/src/share/classes/java/nio/file/SecureDirectoryStream.java index 362d1f547ea0dc3540e5f5018155464d4ee9843b..d4b00e1158c70c04fa7f4d20aa112136d48ff5af 100644 --- a/src/share/classes/java/nio/file/SecureDirectoryStream.java +++ b/src/share/classes/java/nio/file/SecureDirectoryStream.java @@ -47,15 +47,6 @@ import java.io.IOException; * newDirectoryStream} method will be a {@code SecureDirectoryStream} and must * be cast to that type in order to invoke the methods defined by this interface. * - *

As specified by {@code DirectoryStream}, the iterator's {@link - * java.util.Iterator#remove() remove} method removes the directory entry for - * the last element returned by the iterator. In the case of a {@code - * SecureDirectoryStream} the {@code remove} method behaves as if by invoking - * the {@link #deleteFile deleteFile} or {@link #deleteDirectory deleteDirectory} - * methods defined by this interface. The {@code remove} may require to examine - * the file to determine if the file is a directory, and consequently, it may - * not be atomic with respect to other file system operations. - * *

In the case of the default {@link java.nio.file.spi.FileSystemProvider * provider}, and a security manager is set, then the permission checks are * performed using the path obtained by resolving the given relative path diff --git a/src/share/classes/java/sql/CallableStatement.java b/src/share/classes/java/sql/CallableStatement.java index c328858de3694b96d350bdcf90d9762b48365560..2f2a77594e113a74e0e54cd06c947fe19cffbaa1 100644 --- a/src/share/classes/java/sql/CallableStatement.java +++ b/src/share/classes/java/sql/CallableStatement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -2436,4 +2436,64 @@ public interface CallableStatement extends PreparedStatement { */ void setNClob(String parameterName, Reader reader) throws SQLException; + + //------------------------- JDBC 4.1 ----------------------------------- + + + /** + *

Returns an object representing the value of OUT parameter + * {@code parameterIndex} and will convert from the + * SQL type of the parameter to the requested Java data type, if the + * conversion is supported. If the conversion is not + * supported or null is specified for the type, a + * SQLException is thrown. + *

+ * At a minimum, an implementation must support the conversions defined in + * Appendix B, Table B-3 and conversion of appropriate user defined SQL + * types to a Java type which implements {@code SQLData}, or {@code Struct}. + * Additional conversions may be supported and are vendor defined. + * + * @param parameterIndex the first parameter is 1, the second is 2, and so on + * @param type Class representing the Java data type to convert the + * designated parameter to. + * @return an instance of {@code type} holding the OUT parameter value + * @throws SQLException if conversion is not supported, type is null or + * another error occurs. The getCause() method of the + * exception may provide a more detailed exception, for example, if + * a conversion error occurs + * @throws SQLFeatureNotSupportedException if the JDBC driver does not support + * this method + * @since 1.7 + */ + public T getObject(int parameterIndex, Class type) throws SQLException; + + + /** + *

Returns an object representing the value of OUT parameter + * {@code parameterName} and will convert from the + * SQL type of the parameter to the requested Java data type, if the + * conversion is supported. If the conversion is not + * supported or null is specified for the type, a + * SQLException is thrown. + *

+ * At a minimum, an implementation must support the conversions defined in + * Appendix B, Table B-3 and conversion of appropriate user defined SQL + * types to a Java type which implements {@code SQLData}, or {@code Struct}. + * Additional conversions may be supported and are vendor defined. + * + * @param parameterName the name of the parameter + * @param type Class representing the Java data type to convert + * the designated parameter to. + * @return an instance of {@code type} holding the OUT parameter + * value + * @throws SQLException if conversion is not supported, type is null or + * another error occurs. The getCause() method of the + * exception may provide a more detailed exception, for example, if + * a conversion error occurs + * @throws SQLFeatureNotSupportedException if the JDBC driver does not support + * this method + * @since 1.7 + */ + public T getObject(String parameterName, Class type) throws SQLException; + } diff --git a/src/share/classes/java/sql/Connection.java b/src/share/classes/java/sql/Connection.java index b850e25a38da9561a9ca724e33570a978d4fa190..9b6706562ba7e2b2a81974370f87c5702baded95 100644 --- a/src/share/classes/java/sql/Connection.java +++ b/src/share/classes/java/sql/Connection.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,6 +26,7 @@ package java.sql; import java.util.Properties; +import java.util.concurrent.Executor; /** *

A connection (session) with a specific @@ -38,7 +39,7 @@ import java.util.Properties; * information is obtained with the getMetaData method. * *

Note: When configuring a Connection, JDBC applications - * should use the appropritate Connection method such as + * should use the appropriate Connection method such as * setAutoCommit or setTransactionIsolation. * Applications should not invoke SQL commands directly to change the connection's * configuration when there is a JDBC method available. By default a Connection object is in @@ -80,7 +81,7 @@ import java.util.Properties; * @see ResultSet * @see DatabaseMetaData */ -public interface Connection extends Wrapper { +public interface Connection extends Wrapper, AutoCloseable { /** * Creates a Statement object for sending @@ -347,6 +348,13 @@ public interface Connection extends Wrapper { *

* If the driver does not support catalogs, it will * silently ignore this request. + *

+ * Calling {@code setCatalog} has no effect on previously created or prepared + * {@code Statement} objects. It is implementation defined whether a DBMS + * prepare operation takes place immediately when the {@code Connection} + * method {@code prepareStatement} or {@code prepareCall} is invoked. + * For maximum portability, {@code setCatalog} should be called before a + * {@code Statement} is created or prepared. * * @param catalog the name of a catalog (subspace in this * Connection object's database) in which to work @@ -598,7 +606,17 @@ public interface Connection extends Wrapper { * Connection object. * Unless the application has added an entry, the type map returned * will be empty. - * + *

+ * You must invoke setTypeMap after making changes to the + * Map object returned from + * getTypeMap as a JDBC driver may create an internal + * copy of the Map object passed to setTypeMap: + *

+ *

+     *      Map<String,Class<?>> myMap = con.getTypeMap();
+     *      myMap.put("mySchemaName.ATHLETES", Athletes.class);
+     *      con.setTypeMap(myMap);
+     * 
* @return the java.util.Map object associated * with this Connection object * @exception SQLException if a database access error occurs @@ -614,7 +632,16 @@ public interface Connection extends Wrapper { * Installs the given TypeMap object as the type map for * this Connection object. The type map will be used for the * custom mapping of SQL structured types and distinct types. - * + *

+ * You must set the the values for the TypeMap prior to + * callng setMap as a JDBC driver may create an internal copy + * of the TypeMap: + *

+ *

+     *      Map myMap<String,Class<?>> = new HashMap<String,Class<?>>();
+     *      myMap.put("mySchemaName.ATHLETES", Athletes.class);
+     *      con.setTypeMap(myMap);
+     * 
* @param map the java.util.Map object to install * as the replacement for this Connection * object's default type map @@ -1274,4 +1301,186 @@ SQLException; */ Struct createStruct(String typeName, Object[] attributes) throws SQLException; + + //--------------------------JDBC 4.1 ----------------------------- + + /** + * Sets the given schema name to access. + *

+ * If the driver does not support schemas, it will + * silently ignore this request. + *

+ * Calling {@code setSchema} has no effect on previously created or prepared + * {@code Statement} objects. It is implementation defined whether a DBMS + * prepare operation takes place immediately when the {@code Connection} + * method {@code prepareStatement} or {@code prepareCall} is invoked. + * For maximum portability, {@code setSchema} should be called before a + * {@code Statement} is created or prepared. + * + * @param schema the name of a schema in which to work + * @exception SQLException if a database access error occurs + * or this method is called on a closed connection + * @see #getSchema + * @since 1.7 + */ + void setSchema(String schema) throws SQLException; + + /** + * Retrieves this Connection object's current schema name. + * + * @return the current schema name or null if there is none + * @exception SQLException if a database access error occurs + * or this method is called on a closed connection + * @see #setSchema + * @since 1.7 + */ + String getSchema() throws SQLException; + + /** + * Terminates an open connection. Calling abort results in: + *

    + *
  • The connection marked as closed + *
  • Closes any physical connection to the database + *
  • Releases resources used by the connection + *
  • Insures that any thread that is currently accessing the connection + * will either progress to completion or throw an SQLException. + *
+ *

+ * Calling abort marks the connection closed and releases any + * resources. Calling abort on a closed connection is a + * no-op. + *

+ * It is possible that the aborting and releasing of the resources that are + * held by the connection can take an extended period of time. When the + * abort method returns, the connection will have been marked as + * closed and the Executor that was passed as a parameter to abort + * may still be executing tasks to release resources. + *

+ * This method checks to see that there is an SQLPermission + * object before allowing the method to proceed. If a + * SecurityManager exists and its + * checkPermission method denies calling abort, + * this method throws a + * java.lang.SecurityException. + * @param executor The Executor implementation which will + * be used by abort. + * @throws java.sql.SQLException if a database access error occurs or + * the {@code executor} is {@code null}, + * @throws java.lang.SecurityException if a security manager exists and its + * checkPermission method denies calling abort + * @see SecurityManager#checkPermission + * @see Executor + * @since 1.7 + */ + void abort(Executor executor) throws SQLException; + + /** + * + * Sets the maximum period a Connection or + * objects created from the Connection + * will wait for the database to reply to any one request. If any + * request remains unanswered, the waiting method will + * return with a SQLException, and the Connection + * or objects created from the Connection will be marked as + * closed. Any subsequent use of + * the objects, with the exception of the close, + * isClosed or Connection.isValid + * methods, will result in a SQLException. + *

+ * Note: This method is intended to address a rare but serious + * condition where network partitions can cause threads issuing JDBC calls + * to hang uninterruptedly in socket reads, until the OS TCP-TIMEOUT + * (typically 10 minutes). This method is related to the + * {@link #abort abort() } method which provides an administrator + * thread a means to free any such threads in cases where the + * JDBC connection is accessible to the administrator thread. + * The setNetworkTimeout method will cover cases where + * there is no administrator thread, or it has no access to the + * connection. This method is severe in it's effects, and should be + * given a high enough value so it is never triggered before any more + * normal timeouts, such as transaction timeouts. + *

+ * JDBC driver implementations may also choose to support the + * {@code setNetworkTimeout} method to impose a limit on database + * response time, in environments where no network is present. + *

+ * Drivers may internally implement some or all of their API calls with + * multiple internal driver-database transmissions, and it is left to the + * driver implementation to determine whether the limit will be + * applied always to the response to the API call, or to any + * single request made during the API call. + *

+ * + * This method can be invoked more than once, such as to set a limit for an + * area of JDBC code, and to reset to the default on exit from this area. + * Invocation of this method has no impact on already outstanding + * requests. + *

+ * The {@code Statement.setQueryTimeout()} timeout value is independent of the + * timeout value specified in {@code setNetworkTimeout}. If the query timeout + * expires before the network timeout then the + * statement execution will be canceled. If the network is still + * active the result will be that both the statement and connection + * are still usable. However if the network timeout expires before + * the query timeout or if the statement timeout fails due to network + * problems, the connection will be marked as closed, any resources held by + * the connection will be released and both the connection and + * statement will be unusable. + *

+ * When the driver determines that the {@code setNetworkTimeout} timeout + * value has expired, the JDBC driver marks the connection + * closed and releases any resources held by the connection. + *

+ * + * This method checks to see that there is an SQLPermission + * object before allowing the method to proceed. If a + * SecurityManager exists and its + * checkPermission method denies calling + * setNetworkTimeout, this method throws a + * java.lang.SecurityException. + * + * @param executor The Executor implementation which will + * be used by setNetworkTimeout. + * @param milliseconds The time in milliseconds to wait for the database + * operation + * to complete. If the JDBC driver does not support milliseconds, the + * JDBC driver will round the value up to the nearest second. If the + * timeout period expires before the operation + * completes, a SQLException will be thrown. + * A value of 0 indicates that there is not timeout for database operations. + * @throws java.sql.SQLException if a database access error occurs, this + * method is called on a closed connection, + * the {@code executor} is {@code null}, + * or the value specified for seconds is less than 0. + * @throws java.lang.SecurityException if a security manager exists and its + * checkPermission method denies calling + * setNetworkTimeout. + * @exception SQLFeatureNotSupportedException if the JDBC driver does not support + * this method + * @see SecurityManager#checkPermission + * @see Statement#setQueryTimeout + * @see #getNetworkTimeout + * @see #abort + * @see Executor + * @since 1.7 + */ + void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException; + + + /** + * Retrieves the number of milliseconds the driver will + * wait for a database request to complete. + * If the limit is exceeded, a + * SQLException is thrown. + * + * @return the current timeout limit in milliseconds; zero means there is + * no limit + * @throws SQLException if a database access error occurs or + * this method is called on a closed Connection + * @exception SQLFeatureNotSupportedException if the JDBC driver does not support + * this method + * @see #setNetworkTimeout + * @since 1.7 + */ + int getNetworkTimeout() throws SQLException; } diff --git a/src/share/classes/java/sql/DatabaseMetaData.java b/src/share/classes/java/sql/DatabaseMetaData.java index 0792302620331bbc6c8f036bf496c500d60a6cfe..d867986c6bf0ac72bf587ee25c75b6f908551df4 100644 --- a/src/share/classes/java/sql/DatabaseMetaData.java +++ b/src/share/classes/java/sql/DatabaseMetaData.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -1342,10 +1342,10 @@ public interface DatabaseMetaData extends Wrapper { * defined. *

  • IS_NULLABLE String => ISO rules are used to determine the nullability for a column. *
      - *
    • YES --- if the parameter can include NULLs - *
    • NO --- if the parameter cannot include NULLs + *
    • YES --- if the column can include NULLs + *
    • NO --- if the column cannot include NULLs *
    • empty string --- if the nullability for the - * parameter is unknown + * column is unknown *
    *
  • SPECIFIC_NAME String => the name which uniquely identifies this procedure within its schema. * @@ -1610,17 +1610,17 @@ public interface DatabaseMetaData extends Wrapper { * (starting at 1) *
  • IS_NULLABLE String => ISO rules are used to determine the nullability for a column. *
      - *
    • YES --- if the parameter can include NULLs - *
    • NO --- if the parameter cannot include NULLs + *
    • YES --- if the column can include NULLs + *
    • NO --- if the column cannot include NULLs *
    • empty string --- if the nullability for the - * parameter is unknown + * column is unknown *
    - *
  • SCOPE_CATLOG String => catalog of table that is the scope + *
  • SCOPE_CATALOG String => catalog of table that is the scope * of a reference attribute (null if DATA_TYPE isn't REF) *
  • SCOPE_SCHEMA String => schema of table that is the scope * of a reference attribute (null if the DATA_TYPE isn't REF) *
  • SCOPE_TABLE String => table name that this the scope - * of a reference attribure (null if the DATA_TYPE isn't REF) + * of a reference attribute (null if the DATA_TYPE isn't REF) *
  • SOURCE_DATA_TYPE short => source type of a distinct type or user-generated * Ref type, SQL type from java.sql.Types (null if DATA_TYPE * isn't DISTINCT or user-generated REF) @@ -1629,11 +1629,16 @@ public interface DatabaseMetaData extends Wrapper { *
  • YES --- if the column is auto incremented *
  • NO --- if the column is not auto incremented *
  • empty string --- if it cannot be determined whether the column is auto incremented - * parameter is unknown + * + *
  • IS_GENERATEDCOLUMN String => Indicates whether this is a generated column + *
      + *
    • YES --- if this a generated column + *
    • NO --- if this not a generated column + *
    • empty string --- if it cannot be determined whether this is a generated column *
    * * - *

    The COLUMN_SIZE column the specified column size for the given column. + *

    The COLUMN_SIZE column specifies the column size for the given column. * For numeric data, this is the maximum precision. For character data, this is the length in characters. * For datetime datatypes, this is the length in characters of the String representation (assuming the * maximum allowed precision of the fractional seconds component). For binary data, this is the length in bytes. For the ROWID datatype, @@ -3186,7 +3191,7 @@ public interface DatabaseMetaData extends Wrapper { * Retrieves whether this database supports statement pooling. * * @return true if so; false otherwise - * @throws SQLExcpetion if a database access error occurs + * @throws SQLException if a database access error occurs * @since 1.4 */ boolean supportsStatementPooling() throws SQLException; @@ -3568,4 +3573,83 @@ public interface DatabaseMetaData extends Wrapper { */ int functionReturnsTable = 2; + //--------------------------JDBC 4.1 ----------------------------- + + /** + * Retrieves a description of the pseudo or hidden columns available + * in a given table within the specified catalog and schema. + * Pseudo or hidden columns may not always be stored within + * a table and are not visible in a ResultSet unless they are + * specified in the query's outermost SELECT list. Pseudo or hidden + * columns may not necessarily be able to be modified. If there are + * no pseudo or hidden columns, an empty ResultSet is returned. + * + *

    Only column descriptions matching the catalog, schema, table + * and column name criteria are returned. They are ordered by + * TABLE_CAT,TABLE_SCHEM, TABLE_NAME + * and COLUMN_NAME. + * + *

    Each column description has the following columns: + *

      + *
    1. TABLE_CAT String => table catalog (may be null) + *
    2. TABLE_SCHEM String => table schema (may be null) + *
    3. TABLE_NAME String => table name + *
    4. COLUMN_NAME String => column name + *
    5. DATA_TYPE int => SQL type from java.sql.Types + *
    6. COLUMN_SIZE int => column size. + *
    7. DECIMAL_DIGITS int => the number of fractional digits. Null is returned for data types where + * DECIMAL_DIGITS is not applicable. + *
    8. NUM_PREC_RADIX int => Radix (typically either 10 or 2) + *
    9. COLUMN_USAGE String => The allowed usage for the column. The + * value returned will correspond to the enum name returned by {@link PseudoColumnUsage#name PseudoColumnUsage.name()} + *
    10. REMARKS String => comment describing column (may be null) + *
    11. CHAR_OCTET_LENGTH int => for char types the + * maximum number of bytes in the column + *
    12. IS_NULLABLE String => ISO rules are used to determine the nullability for a column. + *
        + *
      • YES --- if the column can include NULLs + *
      • NO --- if the column cannot include NULLs + *
      • empty string --- if the nullability for the column is unknown + *
      + *
    + * + *

    The COLUMN_SIZE column specifies the column size for the given column. + * For numeric data, this is the maximum precision. For character data, this is the length in characters. + * For datetime datatypes, this is the length in characters of the String representation (assuming the + * maximum allowed precision of the fractional seconds component). For binary data, this is the length in bytes. For the ROWID datatype, + * this is the length in bytes. Null is returned for data types where the + * column size is not applicable. + * + * @param catalog a catalog name; must match the catalog name as it + * is stored in the database; "" retrieves those without a catalog; + * null means that the catalog name should not be used to narrow + * the search + * @param schemaPattern a schema name pattern; must match the schema name + * as it is stored in the database; "" retrieves those without a schema; + * null means that the schema name should not be used to narrow + * the search + * @param tableNamePattern a table name pattern; must match the + * table name as it is stored in the database + * @param columnNamePattern a column name pattern; must match the column + * name as it is stored in the database + * @return ResultSet - each row is a column description + * @exception SQLException if a database access error occurs + * @see PseudoColumnUsage + * @since 1.7 + */ + ResultSet getPseudoColumns(String catalog, String schemaPattern, + String tableNamePattern, String columnNamePattern) + throws SQLException; + + /** + * Retrieves whether a generated key will always be returned if the column + * name(s) or indexe(s) specified for the auto generated key column(s) + * are valid and the statement succeeds. The key that is returned may or + * may not be based on the column(s) for the auto generated key. + * Consult your JDBC driver documentation for additional details. + * @return true if so; false otherwise + * @exception SQLException if a database access error occurs + * @since 1.7 + */ + boolean generatedKeyAlwaysReturned() throws SQLException; } diff --git a/src/share/classes/java/sql/Date.java b/src/share/classes/java/sql/Date.java index 7c2356e75c2bd61dfd87dbbf8da0619b53f5f266..1d82ab36a1b2eaa43796401eec71f78779190998 100644 --- a/src/share/classes/java/sql/Date.java +++ b/src/share/classes/java/sql/Date.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -96,11 +96,12 @@ public class Date extends java.util.Date { * a Date value. * * @param s a String object representing a date in - * in the format "yyyy-mm-dd" + * in the format "yyyy-[m]m-[d]d". The leading zero for mm + * and dd may also be omitted. * @return a java.sql.Date object representing the * given date * @throws IllegalArgumentException if the date given is not in the - * JDBC date escape format (yyyy-mm-dd) + * JDBC date escape format (yyyy-[m]m-[d]d) */ public static Date valueOf(String s) { final int YEAR_LENGTH = 4; @@ -123,8 +124,9 @@ public class Date extends java.util.Date { String yyyy = s.substring(0, firstDash); String mm = s.substring(firstDash + 1, secondDash); String dd = s.substring(secondDash + 1); - if (yyyy.length() == YEAR_LENGTH && mm.length() == MONTH_LENGTH && - dd.length() == DAY_LENGTH) { + if (yyyy.length() == YEAR_LENGTH && + (mm.length() >= 1 && mm.length() <= MONTH_LENGTH) && + (dd.length() >= 1 && dd.length() <= DAY_LENGTH)) { int year = Integer.parseInt(yyyy); int month = Integer.parseInt(mm); int day = Integer.parseInt(dd); diff --git a/src/share/classes/java/sql/Driver.java b/src/share/classes/java/sql/Driver.java index 8325452534cb9f14c5114036a1d3206b4e81ace6..4abc6b3c81f213718408b9a81c9fb1bd13963491 100644 --- a/src/share/classes/java/sql/Driver.java +++ b/src/share/classes/java/sql/Driver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,8 @@ package java.sql; +import java.util.logging.Logger; + /** * The interface that every driver class must implement. *

    The Java SQL framework allows for multiple database drivers. @@ -150,4 +152,19 @@ public interface Driver { * otherwise */ boolean jdbcCompliant(); + + //------------------------- JDBC 4.1 ----------------------------------- + + /** + * Return the parent Logger of all the Loggers used by this driver. This + * should be the Logger farthest from the root Logger that is + * still an ancestor of all of the Loggers used by this driver. Configuring + * this Logger will affect all of the log messages generated by the driver. + * In the worst case, this may be the root Logger. + * + * @return the parent Logger for this driver + * @throws SQLFeatureNotSupportedException if the driver does not use java.util.logging. + * @since 1.7 + */ + public Logger getParentLogger() throws SQLFeatureNotSupportedException; } diff --git a/src/share/classes/java/sql/PreparedStatement.java b/src/share/classes/java/sql/PreparedStatement.java index eb55d199fcc6aa8377340b9307a97b2ae44e240e..1d32c3dd0b9a84d5b90464763055f5d7b95c1741 100644 --- a/src/share/classes/java/sql/PreparedStatement.java +++ b/src/share/classes/java/sql/PreparedStatement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -69,6 +69,10 @@ public interface PreparedStatement extends Statement { * @exception SQLException if a database access error occurs; * this method is called on a closed PreparedStatement or the SQL * statement does not return a ResultSet object + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} */ ResultSet executeQuery() throws SQLException; @@ -82,8 +86,11 @@ public interface PreparedStatement extends Statement { * or (2) 0 for SQL statements that return nothing * @exception SQLException if a database access error occurs; * this method is called on a closed PreparedStatement - * or the SQL - * statement returns a ResultSet object + * or the SQL statement returns a ResultSet object + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} */ int executeUpdate() throws SQLException; @@ -463,6 +470,10 @@ public interface PreparedStatement extends Statement { * @exception SQLException if a database access error occurs; * this method is called on a closed PreparedStatement * or an argument is supplied to this method + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} * @see Statement#execute * @see Statement#getResultSet * @see Statement#getUpdateCount @@ -1208,4 +1219,5 @@ public interface PreparedStatement extends Statement { void setNClob(int parameterIndex, Reader reader) throws SQLException; + } diff --git a/src/share/classes/java/sql/PseudoColumnUsage.java b/src/share/classes/java/sql/PseudoColumnUsage.java new file mode 100644 index 0000000000000000000000000000000000000000..bc9c1add47c436744289843bdeb508e5e2a3ff29 --- /dev/null +++ b/src/share/classes/java/sql/PseudoColumnUsage.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.sql; + + +/** + * Enumeration for pseudo/hidden column usage. + * + * @since 1.7 + * @see DatabaseMetaData#getPseudoColumns + */ +public enum PseudoColumnUsage { + + /** + * The pseudo/hidden column may only be used in a SELECT list. + */ + SELECT_LIST_ONLY, + + /** + * The pseudo/hidden column may only be used in a WHERE clause. + */ + WHERE_CLAUSE_ONLY, + + /** + * There are no restrictions on the usage of the pseudo/hidden columns. + */ + NO_USAGE_RESTRICTIONS, + + /** + * The usage of the pseudo/hidden column cannot be determined. + */ + USAGE_UNKNOWN + +} diff --git a/src/share/classes/java/sql/ResultSet.java b/src/share/classes/java/sql/ResultSet.java index 579270c783faa940b519f856820f910caf402795..a8aac4ffeea626dde677dd31682cec609210aa96 100644 --- a/src/share/classes/java/sql/ResultSet.java +++ b/src/share/classes/java/sql/ResultSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -145,7 +145,7 @@ import java.io.InputStream; * @see ResultSetMetaData */ -public interface ResultSet extends Wrapper { +public interface ResultSet extends Wrapper, AutoCloseable { /** * Moves the cursor froward one row from its current position. @@ -1187,6 +1187,9 @@ public interface ResultSet extends Wrapper { * cursor on the last row; calling the method absolute(-2) * moves the cursor to the next-to-last row, and so on. * + *

    If the row number specified is zero, the cursor is moved to + * before the first row. + * *

    An attempt to position the cursor beyond the first/last row in * the result set leaves the cursor before the first row or after * the last row. @@ -1196,9 +1199,10 @@ public interface ResultSet extends Wrapper { * is the same as calling last(). * * @param row the number of the row to which the cursor should move. - * A positive number indicates the row number counting from the - * beginning of the result set; a negative number indicates the - * row number counting from the end of the result set + * A value of zero indicates that the cursor will be positioned + * before the first row; a positive number indicates the row number + * counting from the beginning of the result set; a negative number + * indicates the row number counting from the end of the result set * @return true if the cursor is moved to a position in this * ResultSet object; * false if the cursor is before the first row or after the @@ -2529,7 +2533,7 @@ public interface ResultSet extends Wrapper { * @exception SQLException if the columnLabel is not valid; * if a database access error occurs * or this method is called on a closed result set - * @exception SQLFeatureNotSupportedException if the JDBC driver does not support + * @exception SQLFeatureNotSupportedException if the JDBC driver does not support * this method * @since 1.2 */ @@ -4072,4 +4076,64 @@ public interface ResultSet extends Wrapper { */ void updateNClob(String columnLabel, Reader reader) throws SQLException; + //------------------------- JDBC 4.1 ----------------------------------- + + + /** + *

    Retrieves the value of the designated column in the current row + * of this ResultSet object and will convert from the + * SQL type of the column to the requested Java data type, if the + * conversion is supported. If the conversion is not + * supported or null is specified for the type, a + * SQLException is thrown. + *

    + * At a minimum, an implementation must support the conversions defined in + * Appendix B, Table B-3 and conversion of appropriate user defined SQL + * types to a Java type which implements {@code SQLData}, or {@code Struct}. + * Additional conversions may be supported and are vendor defined. + * + * @param columnIndex the first column is 1, the second is 2, ... + * @param type Class representing the Java data type to convert the designated + * column to. + * @return an instance of {@code type} holding the column value + * @throws SQLException if conversion is not supported, type is null or + * another error occurs. The getCause() method of the + * exception may provide a more detailed exception, for example, if + * a conversion error occurs + * @throws SQLFeatureNotSupportedException if the JDBC driver does not support + * this method + * @since 1.7 + */ + public T getObject(int columnIndex, Class type) throws SQLException; + + + /** + *

    Retrieves the value of the designated column in the current row + * of this ResultSet object and will convert from the + * SQL type of the column to the requested Java data type, if the + * conversion is supported. If the conversion is not + * supported or null is specified for the type, a + * SQLException is thrown. + *

    + * At a minimum, an implementation must support the conversions defined in + * Appendix B, Table B-3 and conversion of appropriate user defined SQL + * types to a Java type which implements {@code SQLData}, or {@code Struct}. + * Additional conversions may be supported and are vendor defined. + * + * @param columnLabel the label for the column specified with the SQL AS clause. + * If the SQL AS clause was not specified, then the label is the name + * of the column + * @param type Class representing the Java data type to convert the designated + * column to. + * @return an instance of {@code type} holding the column value + * @throws SQLException if conversion is not supported, type is null or + * another error occurs. The getCause() method of the + * exception may provide a more detailed exception, for example, if + * a conversion error occurs + * @throws SQLFeatureNotSupportedException if the JDBC driver does not support + * this method + * @since 1.7 + */ + public T getObject(String columnLabel, Class type) throws SQLException; + } diff --git a/src/share/classes/java/sql/SQLDataException.java b/src/share/classes/java/sql/SQLDataException.java index 813b994978287ed3495d79cbdf0d612e903433d1..8dc1753a6b7f00ac8573b17d3452bd907cefcf96 100644 --- a/src/share/classes/java/sql/SQLDataException.java +++ b/src/share/classes/java/sql/SQLDataException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,10 +26,13 @@ package java.sql; /** - * The subclass of {@link SQLException} thrown when the SQLState class value is '22'. This indicates - * various data errors, including but not limited to not-allowed conversion, division by 0 - * and invalid arguments to functions. - * + * The subclass of {@link SQLException} thrown when the SQLState class value + * is '22', or under vendor-specified conditions. This indicates + * various data errors, including but not limited to data conversion errors, + * division by 0, and invalid arguments to functions. + *

    + * Please consult your driver vendor documentation for the vendor-specified + * conditions for which this Exception may be thrown. * @since 1.6 */ public class SQLDataException extends SQLNonTransientException { diff --git a/src/share/classes/java/sql/SQLIntegrityConstraintViolationException.java b/src/share/classes/java/sql/SQLIntegrityConstraintViolationException.java index 0c7160d2675e07d3bd91bca4d7fd7a157ae14c51..0f92ac50dfa9ebe94ec6c21861d6cc377ae7f14b 100644 --- a/src/share/classes/java/sql/SQLIntegrityConstraintViolationException.java +++ b/src/share/classes/java/sql/SQLIntegrityConstraintViolationException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,9 +26,13 @@ package java.sql; /** - * The subclass of {@link SQLException} thrown when the SQLState class value is '23'. This indicates that an integrity + * The subclass of {@link SQLException} thrown when the SQLState class value + * is '23', or under vendor-specified conditions. + * This indicates that an integrity * constraint (foreign key, primary key or unique key) has been violated. - * + *

    + * Please consult your driver vendor documentation for the vendor-specified + * conditions for which this Exception may be thrown. * @since 1.6 */ public class SQLIntegrityConstraintViolationException extends SQLNonTransientException { diff --git a/src/share/classes/java/sql/SQLInvalidAuthorizationSpecException.java b/src/share/classes/java/sql/SQLInvalidAuthorizationSpecException.java index 34ccfe13301dd475717635578bc405f5c248fcf2..0b4d206df963386e5d31868ebcf755984d60b6ed 100644 --- a/src/share/classes/java/sql/SQLInvalidAuthorizationSpecException.java +++ b/src/share/classes/java/sql/SQLInvalidAuthorizationSpecException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,9 +26,13 @@ package java.sql; /** - * The subclass of {@link SQLException} thrown when the SQLState class value is '28'. This indicated that the - * authorization credentials presented during connection establishment are not valid. - * + * The subclass of {@link SQLException} thrown when the SQLState class value + * is '28', or under vendor-specified conditions. This indicates that + * the authorization credentials presented during connection establishment + * are not valid. + *

    + * Please consult your driver vendor documentation for the vendor-specified + * conditions for which this Exception may be thrown. * @since 1.6 */ public class SQLInvalidAuthorizationSpecException extends SQLNonTransientException { diff --git a/src/share/classes/java/sql/SQLNonTransientConnectionException.java b/src/share/classes/java/sql/SQLNonTransientConnectionException.java index 089f7f439ed3d66a6900b18d6cc416425b729143..036c881f2051d3b802eddb30dc75632edb986077 100644 --- a/src/share/classes/java/sql/SQLNonTransientConnectionException.java +++ b/src/share/classes/java/sql/SQLNonTransientConnectionException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,11 +26,13 @@ package java.sql; /** - *

    The subclass of {@link SQLException} thrown for the SQLState - * class value '08', representing - * that the connection operation that failed will not succeed when + * The subclass of {@link SQLException} thrown for the SQLState + * class value '08', or under vendor-specified conditions. This + * indicates that the connection operation that failed will not succeed if * the operation is retried without the cause of the failure being corrected. *

    + * Please consult your driver vendor documentation for the vendor-specified + * conditions for which this Exception may be thrown. * @since 1.6 */ public class SQLNonTransientConnectionException extends java.sql.SQLNonTransientException { diff --git a/src/share/classes/java/sql/SQLPermission.java b/src/share/classes/java/sql/SQLPermission.java index e5b3ff69e004ef0e0ad71d3141c5dcfb0a460221..e7e799c1caca038fb4defc9d04046693f00f705d 100644 --- a/src/share/classes/java/sql/SQLPermission.java +++ b/src/share/classes/java/sql/SQLPermission.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -30,9 +30,14 @@ import java.security.*; /** * The permission for which the SecurityManager will check - * when code that is running in an applet calls the - * DriverManager.setLogWriter method or the - * DriverManager.setLogStream (deprecated) method. + * when code that is running in an applet, or an application with a + * SecurityManager enabled, calls the + * DriverManager.setLogWriter method, + * DriverManager.setLogStream (deprecated) method, + * {@code SyncFactory.setJNDIContext} method, + * {@code SyncFactory.setLogger} method, + * {@code Connection.setNetworktimeout} method, + * or the Connection.abort method. * If there is no SQLPermission object, these methods * throw a java.lang.SecurityException as a runtime exception. *

    @@ -48,7 +53,6 @@ import java.security.*; * but *loadLibrary or a*b is not valid. *

    * The following table lists all the possible SQLPermission target names. - * Currently, the only name allowed is setLog. * The table gives a description of what the permission allows * and a discussion of the risks of granting code the permission. *

    @@ -67,9 +71,33 @@ import java.security.*; * The contents of the log may contain usernames and passwords, * SQL statements, and SQL data. * + * + * callAbort + * Allows the invocation of the {@code Connection} method + * {@code abort} + * Permits an application to terminate a physical connection to a + * database. + * + * + * setSyncFactory + * Allows the invocation of the {@code SyncFactory} methods + * {@code setJNDIContext} and {@code setLogger} + * Permits an application to specify the JNDI context from which the + * {@code SyncProvider} implementations can be retrieved from and the logging + * object to be used by the{@codeSyncProvider} implementation. + * * + * + * setNetworkTimeout + * Allows the invocation of the {@code Connection} method + * {@code setNetworkTimeout} + * Permits an application to specify the maximum period a + * Connection or + * objects created from the Connection + * will wait for the database to reply to any one request. + * * - * + *

    * The person running an applet decides what permissions to allow * and will run the Policy Tool to create an * SQLPermission in a policy file. A programmer does diff --git a/src/share/classes/java/sql/SQLSyntaxErrorException.java b/src/share/classes/java/sql/SQLSyntaxErrorException.java index a1865d93bbb506cfd0b83b35251d2c54c470d40d..b65dec722c75aaf8e8ca3374da0450e423d9c1a8 100644 --- a/src/share/classes/java/sql/SQLSyntaxErrorException.java +++ b/src/share/classes/java/sql/SQLSyntaxErrorException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,9 +26,12 @@ package java.sql; /** - * The subclass of {@link SQLException} thrown when the SQLState class value is '42'. This indicates that the + * The subclass of {@link SQLException} thrown when the SQLState class value + * is '42', or under vendor-specified conditions. This indicates that the * in-progress query has violated SQL syntax rules. - * + *

    + * Please consult your driver vendor documentation for the vendor-specified + * conditions for which this Exception may be thrown. * @since 1.6 */ public class SQLSyntaxErrorException extends SQLNonTransientException { diff --git a/src/share/classes/java/sql/SQLTransactionRollbackException.java b/src/share/classes/java/sql/SQLTransactionRollbackException.java index 9e05add35add721771f15c284bbf5afc417f0fd5..90eb702994b4a30dba31ea7df21414b9038ce2fa 100644 --- a/src/share/classes/java/sql/SQLTransactionRollbackException.java +++ b/src/share/classes/java/sql/SQLTransactionRollbackException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -26,10 +26,13 @@ package java.sql; /** - * The subclass of {@link SQLException} thrown when the SQLState class value is '40'. This indicates that the - * current statement was automatically rolled back by the database becuase of deadlock or other - * transaction serialization failures. - * + * The subclass of {@link SQLException} thrown when the SQLState class value + * is '40', or under vendor-specified conditions. This indicates that the + * current statement was automatically rolled back by the database because + * of deadlock or other transaction serialization failures. + *

    + * Please consult your driver vendor documentation for the vendor-specified + * conditions for which this Exception may be thrown. * @since 1.6 */ public class SQLTransactionRollbackException extends SQLTransientException { diff --git a/src/share/classes/java/sql/SQLTransientConnectionException.java b/src/share/classes/java/sql/SQLTransientConnectionException.java index 865793c1559756715f3df7a60e1477745acdcd0e..cebc67d67d4536e5c0a58d1de9cbd3f3095dd7ce 100644 --- a/src/share/classes/java/sql/SQLTransientConnectionException.java +++ b/src/share/classes/java/sql/SQLTransientConnectionException.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,11 +27,12 @@ package java.sql; /** * The subclass of {@link SQLException} for the SQLState class - * value '08', representing - * that the connection operation that failed might be able to succeed when + * value '08', or under vendor-specified conditions. This indicates + * that the connection operation that failed might be able to succeed if * the operation is retried without any application-level changes. - *

    - * + *

    + * Please consult your driver vendor documentation for the vendor-specified + * conditions for which this Exception may be thrown. * @since 1.6 */ public class SQLTransientConnectionException extends java.sql.SQLTransientException { diff --git a/src/share/classes/java/sql/Statement.java b/src/share/classes/java/sql/Statement.java index a99f2d348bea92acb79d9adb98146da07828608e..88684a79a57ca385a80ac23081df02ce2bed8695 100644 --- a/src/share/classes/java/sql/Statement.java +++ b/src/share/classes/java/sql/Statement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,20 +40,27 @@ package java.sql; * @see Connection#createStatement * @see ResultSet */ -public interface Statement extends Wrapper { +public interface Statement extends Wrapper, AutoCloseable { /** * Executes the given SQL statement, which returns a single * ResultSet object. - * + *

    + * Note:This method cannot be called on a + * PreparedStatement or CallableStatement. * @param sql an SQL statement to be sent to the database, typically a * static SQL SELECT statement * @return a ResultSet object that contains the data produced * by the given query; never null * @exception SQLException if a database access error occurs, - * this method is called on a closed Statement or the given + * this method is called on a closed Statement, the given * SQL statement produces anything other than a single - * ResultSet object + * ResultSet object, the method is called on a + * PreparedStatement or CallableStatement + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} */ ResultSet executeQuery(String sql) throws SQLException; @@ -61,7 +68,9 @@ public interface Statement extends Wrapper { * Executes the given SQL statement, which may be an INSERT, * UPDATE, or DELETE statement or an * SQL statement that returns nothing, such as an SQL DDL statement. - * + *

    + * Note:This method cannot be called on a + * PreparedStatement or CallableStatement. * @param sql an SQL Data Manipulation Language (DML) statement, such as INSERT, UPDATE or * DELETE; or an SQL statement that returns nothing, * such as a DDL statement. @@ -70,8 +79,13 @@ public interface Statement extends Wrapper { * or (2) 0 for SQL statements that return nothing * * @exception SQLException if a database access error occurs, - * this method is called on a closed Statement or the given - * SQL statement produces a ResultSet object + * this method is called on a closed Statement, the given + * SQL statement produces a ResultSet object, the method is called on a + * PreparedStatement or CallableStatement + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} */ int executeUpdate(String sql) throws SQLException; @@ -198,11 +212,21 @@ public interface Statement extends Wrapper { /** * Sets the number of seconds the driver will wait for a * Statement object to execute to the given number of seconds. - * If the limit is exceeded, an SQLException is thrown. A JDBC - * driver must apply this limit to the execute, - * executeQuery and executeUpdate methods. JDBC driver - * implementations may also apply this limit to ResultSet methods + *By default there is no limit on the amount of time allowed for a running + * statement to complete. If the limit is exceeded, an + * SQLTimeoutException is thrown. + * A JDBC driver must apply this limit to the execute, + * executeQuery and executeUpdate methods. + *

    + * Note: JDBC driver implementations may also apply this + * limit to {@code ResultSet} methods * (consult your driver vendor documentation for details). + *

    + * Note: In the case of {@code Statement} batching, it is + * implementation defined as to whether the time-out is applied to + * individual SQL commands added via the {@code addBatch} method or to + * the entire batch of SQL commands invoked by the {@code executeBatch} + * method (consult your driver vendor documentation for details). * * @param seconds the new query timeout limit in seconds; zero means * there is no limit @@ -300,13 +324,21 @@ public interface Statement extends Wrapper { * getResultSet or getUpdateCount * to retrieve the result, and getMoreResults to * move to any subsequent result(s). - * + *

    + *Note:This method cannot be called on a + * PreparedStatement or CallableStatement. * @param sql any SQL statement * @return true if the first result is a ResultSet * object; false if it is an update count or there are * no results - * @exception SQLException if a database access error occurs or - * this method is called on a closed Statement + * @exception SQLException if a database access error occurs, + * this method is called on a closed Statement, + * the method is called on a + * PreparedStatement or CallableStatement + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} * @see #getResultSet * @see #getUpdateCount * @see #getMoreResults @@ -465,12 +497,14 @@ public interface Statement extends Wrapper { * Statement object. The commands in this list can be * executed as a batch by calling the method executeBatch. *

    - * + *Note:This method cannot be called on a + * PreparedStatement or CallableStatement. * @param sql typically this is a SQL INSERT or * UPDATE statement * @exception SQLException if a database access error occurs, - * this method is called on a closed Statement or the - * driver does not support batch updates + * this method is called on a closed Statement, the + * driver does not support batch updates, the method is called on a + * PreparedStatement or CallableStatement * @see #executeBatch * @see DatabaseMetaData#supportsBatchUpdates * @since 1.2 @@ -536,7 +570,10 @@ public interface Statement extends Wrapper { * driver does not support batch statements. Throws {@link BatchUpdateException} * (a subclass of SQLException) if one of the commands sent to the * database fails to execute properly or attempts to return a result set. - * + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} * * @see #addBatch * @see DatabaseMetaData#supportsBatchUpdates @@ -678,7 +715,9 @@ public interface Statement extends Wrapper { * flag if the SQL statement * is not an INSERT statement, or an SQL statement able to return * auto-generated keys (the list of such statements is vendor-specific). - * + *

    + * Note:This method cannot be called on a + * PreparedStatement or CallableStatement. * @param sql an SQL Data Manipulation Language (DML) statement, such as INSERT, UPDATE or * DELETE; or an SQL statement that returns nothing, * such as a DDL statement. @@ -693,10 +732,15 @@ public interface Statement extends Wrapper { * * @exception SQLException if a database access error occurs, * this method is called on a closed Statement, the given - * SQL statement returns a ResultSet object, or - * the given constant is not one of those allowed + * SQL statement returns a ResultSet object, + * the given constant is not one of those allowed, the method is called on a + * PreparedStatement or CallableStatement * @exception SQLFeatureNotSupportedException if the JDBC driver does not support * this method with a constant of Statement.RETURN_GENERATED_KEYS + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} * @since 1.4 */ int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException; @@ -709,7 +753,9 @@ public interface Statement extends Wrapper { * available. The driver will ignore the array if the SQL statement * is not an INSERT statement, or an SQL statement able to return * auto-generated keys (the list of such statements is vendor-specific). - * + *

    + * Note:This method cannot be called on a + * PreparedStatement or CallableStatement. * @param sql an SQL Data Manipulation Language (DML) statement, such as INSERT, UPDATE or * DELETE; or an SQL statement that returns nothing, * such as a DDL statement. @@ -721,10 +767,15 @@ public interface Statement extends Wrapper { * * @exception SQLException if a database access error occurs, * this method is called on a closed Statement, the SQL - * statement returns a ResultSet object, or the - * second argument supplied to this method is not an int array - * whose elements are valid column indexes + * statement returns a ResultSet object,the second argument + * supplied to this method is not an + * int array whose elements are valid column indexes, the method is called on a + * PreparedStatement or CallableStatement * @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} * @since 1.4 */ int executeUpdate(String sql, int columnIndexes[]) throws SQLException; @@ -737,7 +788,9 @@ public interface Statement extends Wrapper { * available. The driver will ignore the array if the SQL statement * is not an INSERT statement, or an SQL statement able to return * auto-generated keys (the list of such statements is vendor-specific). - * + *

    + * Note:This method cannot be called on a + * PreparedStatement or CallableStatement. * @param sql an SQL Data Manipulation Language (DML) statement, such as INSERT, UPDATE or * DELETE; or an SQL statement that returns nothing, * such as a DDL statement. @@ -748,11 +801,15 @@ public interface Statement extends Wrapper { * that return nothing * @exception SQLException if a database access error occurs, * this method is called on a closed Statement, the SQL - * statement returns a ResultSet object, or the + * statement returns a ResultSet object, the * second argument supplied to this method is not a String array - * whose elements are valid column names - * + * whose elements are valid column names, the method is called on a + * PreparedStatement or CallableStatement * @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} * @since 1.4 */ int executeUpdate(String sql, String columnNames[]) throws SQLException; @@ -776,7 +833,9 @@ public interface Statement extends Wrapper { * getResultSet or getUpdateCount * to retrieve the result, and getMoreResults to * move to any subsequent result(s). - * + *

    + *Note:This method cannot be called on a + * PreparedStatement or CallableStatement. * @param sql any SQL statement * @param autoGeneratedKeys a constant indicating whether auto-generated * keys should be made available for retrieval using the method @@ -787,12 +846,18 @@ public interface Statement extends Wrapper { * object; false if it is an update count or there are * no results * @exception SQLException if a database access error occurs, - * this method is called on a closed Statement or the second + * this method is called on a closed Statement, the second * parameter supplied to this method is not * Statement.RETURN_GENERATED_KEYS or - * Statement.NO_GENERATED_KEYS. + * Statement.NO_GENERATED_KEYS, + * the method is called on a + * PreparedStatement or CallableStatement * @exception SQLFeatureNotSupportedException if the JDBC driver does not support * this method with a constant of Statement.RETURN_GENERATED_KEYS + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} * @see #getResultSet * @see #getUpdateCount * @see #getMoreResults @@ -823,7 +888,9 @@ public interface Statement extends Wrapper { * getResultSet or getUpdateCount * to retrieve the result, and getMoreResults to * move to any subsequent result(s). - * + *

    + * Note:This method cannot be called on a + * PreparedStatement or CallableStatement. * @param sql any SQL statement * @param columnIndexes an array of the indexes of the columns in the * inserted row that should be made available for retrieval by a @@ -832,10 +899,15 @@ public interface Statement extends Wrapper { * object; false if it is an update count or there * are no results * @exception SQLException if a database access error occurs, - * this method is called on a closed Statement or the + * this method is called on a closed Statement, the * elements in the int array passed to this method - * are not valid column indexes + * are not valid column indexes, the method is called on a + * PreparedStatement or CallableStatement * @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} * @see #getResultSet * @see #getUpdateCount * @see #getMoreResults @@ -865,7 +937,9 @@ public interface Statement extends Wrapper { * getResultSet or getUpdateCount * to retrieve the result, and getMoreResults to * move to any subsequent result(s). - * + *

    + * Note:This method cannot be called on a + * PreparedStatement or CallableStatement. * @param sql any SQL statement * @param columnNames an array of the names of the columns in the inserted * row that should be made available for retrieval by a call to the @@ -874,10 +948,15 @@ public interface Statement extends Wrapper { * object; false if it is an update count or there * are no more results * @exception SQLException if a database access error occurs, - * this method is called on a closed Statement or the + * this method is called on a closed Statement,the * elements of the String array passed to this - * method are not valid column names + * method are not valid column names, the method is called on a + * PreparedStatement or CallableStatement * @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method + * @throws SQLTimeoutException when the driver has determined that the + * timeout value that was specified by the {@code setQueryTimeout} + * method has been exceeded and has at least attempted to cancel + * the currently running {@code Statement} * @see #getResultSet * @see #getUpdateCount * @see #getMoreResults @@ -951,4 +1030,34 @@ public interface Statement extends Wrapper { boolean isPoolable() throws SQLException; + //--------------------------JDBC 4.1 ----------------------------- + + /** + * Specifies that this {@code Statement} will be closed when all its + * dependent result sets are closed. If execution of the {@code Statement} + * does not produce any result sets, this method has no effect. + *

    + * Note: Multiple calls to {@code closeOnCompletion} do + * not toggle the effect on this {@code Statement}. However, a call to + * {@code closeOnCompletion} does effect both the subsequent execution of + * statements, and statements that currently have open, dependent, + * result sets. + * + * @throws SQLException if this method is called on a closed + * {@code Statement} + * @since 1.7 + */ + public void closeOnCompletion() throws SQLException; + + /** + * Returns a value indicating whether this {@code Statement} will be + * closed when all dependent objects such as resultsets are closed. + * @return {@code true} if the {@code Statement} will be closed when all + * of its dependent objects are closed; {@code false} otherwise + * @throws SQLException if this method is called on a closed + * {@code Statement} + * @since 1.7 + */ + public boolean isCloseOnCompletion() throws SQLException; + } diff --git a/src/share/classes/java/sql/Timestamp.java b/src/share/classes/java/sql/Timestamp.java index 4852d8d33afc9154dcd499259515156036ab4cc8..411c04eb19e2749136c937323644fddcffc092af 100644 --- a/src/share/classes/java/sql/Timestamp.java +++ b/src/share/classes/java/sql/Timestamp.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -155,19 +155,26 @@ public class Timestamp extends java.util.Date { * Converts a String object in JDBC timestamp escape format to a * Timestamp value. * - * @param s timestamp in format yyyy-mm-dd hh:mm:ss[.f...]. The - * fractional seconds may be omitted. + * @param s timestamp in format yyyy-[m]m-[d]d hh:mm:ss[.f...]. The + * fractional seconds may be omitted. The leading zero for mm + * and dd may also be omitted. + * * @return corresponding Timestamp value * @exception java.lang.IllegalArgumentException if the given argument - * does not have the format yyyy-mm-dd hh:mm:ss[.f...] + * does not have the format yyyy-[m]m-[d]d hh:mm:ss[.f...] */ public static Timestamp valueOf(String s) { + final int YEAR_LENGTH = 4; + final int MONTH_LENGTH = 2; + final int DAY_LENGTH = 2; + final int MAX_MONTH = 12; + final int MAX_DAY = 31; String date_s; String time_s; String nanos_s; - int year; - int month; - int day; + int year = 0; + int month = 0; + int day = 0; int hour; int minute; int second; @@ -182,17 +189,9 @@ public class Timestamp extends java.util.Date { String zeros = "000000000"; String delimiterDate = "-"; String delimiterTime = ":"; - StringTokenizer stringTokeninzerDate; - StringTokenizer stringTokeninzerTime; if (s == null) throw new java.lang.IllegalArgumentException("null string"); - int counterD = 0; - int intDate[] = {4,2,2}; - - int counterT = 0; - int intTime[] = {2,2,12}; - // Split the string into date and time components s = s.trim(); dividingSpace = s.indexOf(' '); @@ -203,30 +202,6 @@ public class Timestamp extends java.util.Date { throw new java.lang.IllegalArgumentException(formatError); } - stringTokeninzerTime = new StringTokenizer(time_s, delimiterTime); - stringTokeninzerDate = new StringTokenizer(date_s, delimiterDate); - - while(stringTokeninzerDate.hasMoreTokens()) { - String tokenDate = stringTokeninzerDate.nextToken(); - if(tokenDate.length() != intDate[counterD] ) { - throw new java.lang.IllegalArgumentException(formatError); - } - counterD++; - } - - /* - //Commenting this portion out for checking of time - - while(stringTokeninzerTime.hasMoreTokens()) { - String tokenTime = stringTokeninzerTime.nextToken(); - - if (counterT < 2 && tokenTime.length() != intTime[counterT] ) { - throw new java.lang.IllegalArgumentException(formatError); - } - counterT++; - } - */ - // Parse the date firstDash = date_s.indexOf('-'); secondDash = date_s.indexOf('-', firstDash+1); @@ -239,14 +214,24 @@ public class Timestamp extends java.util.Date { period = time_s.indexOf('.', secondColon+1); // Convert the date - if ((firstDash > 0) && (secondDash > 0) && - (secondDash < date_s.length()-1)) { - year = Integer.parseInt(date_s.substring(0, firstDash)) - 1900; - month = - Integer.parseInt(date_s.substring - (firstDash+1, secondDash)) - 1; - day = Integer.parseInt(date_s.substring(secondDash+1)); - } else { + boolean parsedDate = false; + if ((firstDash > 0) && (secondDash > 0) && (secondDash < date_s.length() - 1)) { + String yyyy = date_s.substring(0, firstDash); + String mm = date_s.substring(firstDash + 1, secondDash); + String dd = date_s.substring(secondDash + 1); + if (yyyy.length() == YEAR_LENGTH && + (mm.length() >= 1 && mm.length() <= MONTH_LENGTH) && + (dd.length() >= 1 && dd.length() <= DAY_LENGTH)) { + year = Integer.parseInt(yyyy); + month = Integer.parseInt(mm); + day = Integer.parseInt(dd); + + if ((month >= 1 && month <= MAX_MONTH) && (day >= 1 && day <= MAX_DAY)) { + parsedDate = true; + } + } + } + if (! parsedDate) { throw new java.lang.IllegalArgumentException(formatError); } @@ -272,10 +257,10 @@ public class Timestamp extends java.util.Date { second = Integer.parseInt(time_s.substring(secondColon+1)); } } else { - throw new java.lang.IllegalArgumentException(); + throw new java.lang.IllegalArgumentException(formatError); } - return new Timestamp(year, month, day, hour, minute, second, a_nanos); + return new Timestamp(year - 1900, month - 1, day, hour, minute, second, a_nanos); } /** @@ -502,14 +487,10 @@ public class Timestamp extends java.util.Date { /** * Compares this Timestamp object to the given - * Date, which must be a Timestamp - * object. If the argument is not a Timestamp object, - * this method throws a ClassCastException object. - * (Timestamp objects are - * comparable only to other Timestamp objects.) + * Date object. * - * @param o the Date to be compared, which must be a - * Timestamp object + * @param o the Date to be compared to + * this Timestamp object * @return the value 0 if this Timestamp object * and the given object are equal; a value less than 0 * if this Timestamp object is before the given argument; diff --git a/src/share/classes/java/text/CalendarBuilder.java b/src/share/classes/java/text/CalendarBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..0b73cd2ba90b3f948e38cd48bb5dabe4fc6c20ca --- /dev/null +++ b/src/share/classes/java/text/CalendarBuilder.java @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.text; + +import java.util.Calendar; +import static java.util.GregorianCalendar.*; + +/** + * {@code CalendarBuilder} keeps field-value pairs for setting + * the calendar fields of the given {@code Calendar}. It has the + * {@link Calendar#FIELD_COUNT FIELD_COUNT}-th field for the week year + * support. Also {@code ISO_DAY_OF_WEEK} is used to specify + * {@code DAY_OF_WEEK} in the ISO day of week numbering. + * + *

    {@code CalendarBuilder} retains the semantic of the pseudo + * timestamp for fields. {@code CalendarBuilder} uses a single + * int array combining fields[] and stamp[] of {@code Calendar}. + * + * @author Masayoshi Okutsu + */ +class CalendarBuilder { + /* + * Pseudo time stamp constants used in java.util.Calendar + */ + private static final int UNSET = 0; + private static final int COMPUTED = 1; + private static final int MINIMUM_USER_STAMP = 2; + + private static final int MAX_FIELD = FIELD_COUNT + 1; + + public static final int WEEK_YEAR = FIELD_COUNT; + public static final int ISO_DAY_OF_WEEK = 1000; // pseudo field index + + // stamp[] (lower half) and field[] (upper half) combined + private final int[] field; + private int nextStamp; + private int maxFieldIndex; + + CalendarBuilder() { + field = new int[MAX_FIELD * 2]; + nextStamp = MINIMUM_USER_STAMP; + maxFieldIndex = -1; + } + + CalendarBuilder set(int index, int value) { + if (index == ISO_DAY_OF_WEEK) { + index = DAY_OF_WEEK; + value = toCalendarDayOfWeek(value); + } + field[index] = nextStamp++; + field[MAX_FIELD + index] = value; + if (index > maxFieldIndex && index < FIELD_COUNT) { + maxFieldIndex = index; + } + return this; + } + + CalendarBuilder addYear(int value) { + field[MAX_FIELD + YEAR] += value; + field[MAX_FIELD + WEEK_YEAR] += value; + return this; + } + + boolean isSet(int index) { + if (index == ISO_DAY_OF_WEEK) { + index = DAY_OF_WEEK; + } + return field[index] > UNSET; + } + + Calendar establish(Calendar cal) { + boolean weekDate = isSet(WEEK_YEAR) + && field[WEEK_YEAR] > field[YEAR]; + if (weekDate && !cal.isWeekDateSupported()) { + // Use YEAR instead + if (!isSet(YEAR)) { + set(YEAR, field[MAX_FIELD + WEEK_YEAR]); + } + weekDate = false; + } + + cal.clear(); + // Set the fields from the min stamp to the max stamp so that + // the field resolution works in the Calendar. + for (int stamp = MINIMUM_USER_STAMP; stamp < nextStamp; stamp++) { + for (int index = 0; index <= maxFieldIndex; index++) { + if (field[index] == stamp) { + cal.set(index, field[MAX_FIELD + index]); + break; + } + } + } + + if (weekDate) { + int weekOfYear = isSet(WEEK_OF_YEAR) ? field[MAX_FIELD + WEEK_OF_YEAR] : 1; + int dayOfWeek = isSet(DAY_OF_WEEK) ? + field[MAX_FIELD + DAY_OF_WEEK] : cal.getFirstDayOfWeek(); + if (!isValidDayOfWeek(dayOfWeek) && cal.isLenient()) { + if (dayOfWeek >= 8) { + dayOfWeek--; + weekOfYear += dayOfWeek / 7; + dayOfWeek = (dayOfWeek % 7) + 1; + } else { + while (dayOfWeek <= 0) { + dayOfWeek += 7; + weekOfYear--; + } + } + dayOfWeek = toCalendarDayOfWeek(dayOfWeek); + } + cal.setWeekDate(field[MAX_FIELD + WEEK_YEAR], weekOfYear, dayOfWeek); + } + return cal; + } + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("CalendarBuilder:["); + for (int i = 0; i < field.length; i++) { + if (isSet(i)) { + sb.append(i).append('=').append(field[MAX_FIELD + i]).append(','); + } + } + int lastIndex = sb.length() - 1; + if (sb.charAt(lastIndex) == ',') { + sb.setLength(lastIndex); + } + sb.append(']'); + return sb.toString(); + } + + static int toISODayOfWeek(int calendarDayOfWeek) { + return calendarDayOfWeek == SUNDAY ? 7 : calendarDayOfWeek - 1; + } + + static int toCalendarDayOfWeek(int isoDayOfWeek) { + if (!isValidDayOfWeek(isoDayOfWeek)) { + // adjust later for lenient mode + return isoDayOfWeek; + } + return isoDayOfWeek == 7 ? SUNDAY : isoDayOfWeek + 1; + } + + static boolean isValidDayOfWeek(int dayOfWeek) { + return dayOfWeek > 0 && dayOfWeek <= 7; + } +} diff --git a/src/share/classes/java/text/DateFormat.java b/src/share/classes/java/text/DateFormat.java index 8bc6d017dd89cc3fc60a37df08e5740daf0c7f26..fdd8eebea07129d938288f7e7e23ac17146ea62e 100644 --- a/src/share/classes/java/text/DateFormat.java +++ b/src/share/classes/java/text/DateFormat.java @@ -443,7 +443,7 @@ public abstract class DateFormat extends Format { */ public final static DateFormat getTimeInstance() { - return get(DEFAULT, 0, 1, Locale.getDefault()); + return get(DEFAULT, 0, 1, Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -455,7 +455,7 @@ public abstract class DateFormat extends Format { */ public final static DateFormat getTimeInstance(int style) { - return get(style, 0, 1, Locale.getDefault()); + return get(style, 0, 1, Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -479,7 +479,7 @@ public abstract class DateFormat extends Format { */ public final static DateFormat getDateInstance() { - return get(0, DEFAULT, 2, Locale.getDefault()); + return get(0, DEFAULT, 2, Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -491,7 +491,7 @@ public abstract class DateFormat extends Format { */ public final static DateFormat getDateInstance(int style) { - return get(0, style, 2, Locale.getDefault()); + return get(0, style, 2, Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -515,7 +515,7 @@ public abstract class DateFormat extends Format { */ public final static DateFormat getDateTimeInstance() { - return get(DEFAULT, DEFAULT, 3, Locale.getDefault()); + return get(DEFAULT, DEFAULT, 3, Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -530,7 +530,7 @@ public abstract class DateFormat extends Format { public final static DateFormat getDateTimeInstance(int dateStyle, int timeStyle) { - return get(timeStyle, dateStyle, 3, Locale.getDefault()); + return get(timeStyle, dateStyle, 3, Locale.getDefault(Locale.Category.FORMAT)); } /** diff --git a/src/share/classes/java/text/DateFormatSymbols.java b/src/share/classes/java/text/DateFormatSymbols.java index 70b4955114ad3bd78cfb287a49df7eb09f0a5a11..4d6f08f4cacbf730d01802c98c7fd8c86cbdfed9 100644 --- a/src/share/classes/java/text/DateFormatSymbols.java +++ b/src/share/classes/java/text/DateFormatSymbols.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -118,7 +118,7 @@ public class DateFormatSymbols implements Serializable, Cloneable { */ public DateFormatSymbols() { - initializeData(Locale.getDefault()); + initializeData(Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -226,7 +226,30 @@ public class DateFormatSymbols implements Serializable, Cloneable { * Unlocalized date-time pattern characters. For example: 'y', 'd', etc. * All locales use the same these unlocalized pattern characters. */ - static final String patternChars = "GyMdkHmsSEDFwWahKzZ"; + static final String patternChars = "GyMdkHmsSEDFwWahKzZYuX"; + + static final int PATTERN_ERA = 0; // G + static final int PATTERN_YEAR = 1; // y + static final int PATTERN_MONTH = 2; // M + static final int PATTERN_DAY_OF_MONTH = 3; // d + static final int PATTERN_HOUR_OF_DAY1 = 4; // k + static final int PATTERN_HOUR_OF_DAY0 = 5; // H + static final int PATTERN_MINUTE = 6; // m + static final int PATTERN_SECOND = 7; // s + static final int PATTERN_MILLISECOND = 8; // S + static final int PATTERN_DAY_OF_WEEK = 9; // E + static final int PATTERN_DAY_OF_YEAR = 10; // D + static final int PATTERN_DAY_OF_WEEK_IN_MONTH = 11; // F + static final int PATTERN_WEEK_OF_YEAR = 12; // w + static final int PATTERN_WEEK_OF_MONTH = 13; // W + static final int PATTERN_AM_PM = 14; // a + static final int PATTERN_HOUR1 = 15; // h + static final int PATTERN_HOUR0 = 16; // K + static final int PATTERN_ZONE_NAME = 17; // z + static final int PATTERN_ZONE_VALUE = 18; // Z + static final int PATTERN_WEEK_YEAR = 19; // Y + static final int PATTERN_ISO_DAY_OF_WEEK = 20; // u + static final int PATTERN_ISO_ZONE = 21; // X /** * Localized date-time pattern characters. For example, a locale may @@ -282,7 +305,7 @@ public class DateFormatSymbols implements Serializable, Cloneable { * @since 1.6 */ public static final DateFormatSymbols getInstance() { - return getInstance(Locale.getDefault()); + return getInstance(Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -505,7 +528,7 @@ public class DateFormatSymbols implements Serializable, Cloneable { * @return the localized date-time pattern characters. */ public String getLocalPatternChars() { - return new String(localPatternChars); + return localPatternChars; } /** @@ -514,7 +537,8 @@ public class DateFormatSymbols implements Serializable, Cloneable { * pattern characters. */ public void setLocalPatternChars(String newLocalPatternChars) { - localPatternChars = new String(newLocalPatternChars); + // Call toString() to throw an NPE in case the argument is null + localPatternChars = newLocalPatternChars.toString(); } /** @@ -699,7 +723,7 @@ public class DateFormatSymbols implements Serializable, Cloneable { } else { dst.zoneStrings = null; } - dst.localPatternChars = new String (src.localPatternChars); + dst.localPatternChars = src.localPatternChars; } /** diff --git a/src/share/classes/java/text/DecimalFormat.java b/src/share/classes/java/text/DecimalFormat.java index 01bc8c5d02d056cef80514fd89b984c929f2bf8f..3a0d9b475bfd1de36fe806a6dd0d1c7b38ca970b 100644 --- a/src/share/classes/java/text/DecimalFormat.java +++ b/src/share/classes/java/text/DecimalFormat.java @@ -392,7 +392,7 @@ public class DecimalFormat extends NumberFormat { * @see java.text.NumberFormat#getPercentInstance */ public DecimalFormat() { - Locale def = Locale.getDefault(); + Locale def = Locale.getDefault(Locale.Category.FORMAT); // try to get the pattern from the cache String pattern = (String) cachedLocaleData.get(def); if (pattern == null) { /* cache miss */ @@ -430,7 +430,7 @@ public class DecimalFormat extends NumberFormat { */ public DecimalFormat(String pattern) { // Always applyPattern after the symbols are set - this.symbols = new DecimalFormatSymbols(Locale.getDefault()); + this.symbols = new DecimalFormatSymbols(Locale.getDefault(Locale.Category.FORMAT)); applyPattern(pattern, false); } diff --git a/src/share/classes/java/text/DecimalFormatSymbols.java b/src/share/classes/java/text/DecimalFormatSymbols.java index d13d61320afaf81033f278ae1d7ca4dcf63587f1..b3c55b82c20378094195d0fe0d0047bc5c36c437 100644 --- a/src/share/classes/java/text/DecimalFormatSymbols.java +++ b/src/share/classes/java/text/DecimalFormatSymbols.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -43,10 +43,10 @@ import java.io.ObjectInputStream; import java.io.Serializable; import java.text.spi.DecimalFormatSymbolsProvider; import java.util.Currency; -import java.util.Hashtable; import java.util.Locale; import java.util.ResourceBundle; -import java.util.spi.LocaleServiceProvider; +import java.util.concurrent.ConcurrentHashMap; + import sun.util.LocaleServiceProviderPool; import sun.util.resources.LocaleData; @@ -76,7 +76,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { * {@link #getInstance(Locale) getInstance} method. */ public DecimalFormatSymbols() { - initialize( Locale.getDefault() ); + initialize( Locale.getDefault(Locale.Category.FORMAT) ); } /** @@ -125,7 +125,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { * @since 1.6 */ public static final DecimalFormatSymbols getInstance() { - return getInstance(Locale.getDefault()); + return getInstance(Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -527,10 +527,17 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { // get resource bundle data - try the cache first boolean needCacheUpdate = false; - Object[] data = (Object[]) cachedLocaleData.get(locale); + Object[] data = cachedLocaleData.get(locale); if (data == null) { /* cache miss */ + // When numbering system is thai (Locale's extension contains u-nu-thai), + // we read the data from th_TH_TH. + Locale lookupLocale = locale; + String numberType = locale.getUnicodeLocaleType("nu"); + if (numberType != null && numberType.equals("thai")) { + lookupLocale = new Locale("th", "TH", "TH"); + } data = new Object[3]; - ResourceBundle rb = LocaleData.getNumberFormatData(locale); + ResourceBundle rb = LocaleData.getNumberFormatData(lookupLocale); data[0] = rb.getStringArray("NumberElements"); needCacheUpdate = true; } @@ -586,7 +593,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { monetarySeparator = decimalSeparator; if (needCacheUpdate) { - cachedLocaleData.put(locale, data); + cachedLocaleData.putIfAbsent(locale, data); } } @@ -806,7 +813,7 @@ public class DecimalFormatSymbols implements Cloneable, Serializable { * cache to hold the NumberElements and the Currency * of a Locale. */ - private static final Hashtable cachedLocaleData = new Hashtable(3); + private static final ConcurrentHashMap cachedLocaleData = new ConcurrentHashMap(3); /** * Obtains a DecimalFormatSymbols instance from a DecimalFormatSymbolsProvider diff --git a/src/share/classes/java/text/MessageFormat.java b/src/share/classes/java/text/MessageFormat.java index a0025985baa12eab315f15101e7c2e8de0e99c65..ec5c7ff0d65498a4a94b21a9f9195eca47de88bb 100644 --- a/src/share/classes/java/text/MessageFormat.java +++ b/src/share/classes/java/text/MessageFormat.java @@ -363,7 +363,7 @@ public class MessageFormat extends Format { * @exception IllegalArgumentException if the pattern is invalid */ public MessageFormat(String pattern) { - this.locale = Locale.getDefault(); + this.locale = Locale.getDefault(Locale.Category.FORMAT); applyPattern(pattern); } diff --git a/src/share/classes/java/text/NumberFormat.java b/src/share/classes/java/text/NumberFormat.java index fd4e92884faf5bb309c1c552768f676702dfb4f3..d80e4bcbd53bc1f4151d30939dc2e528965d1f95 100644 --- a/src/share/classes/java/text/NumberFormat.java +++ b/src/share/classes/java/text/NumberFormat.java @@ -381,7 +381,7 @@ public abstract class NumberFormat extends Format { * {@link #getNumberInstance() getNumberInstance()}. */ public final static NumberFormat getInstance() { - return getInstance(Locale.getDefault(), NUMBERSTYLE); + return getInstance(Locale.getDefault(Locale.Category.FORMAT), NUMBERSTYLE); } /** @@ -397,7 +397,7 @@ public abstract class NumberFormat extends Format { * Returns a general-purpose number format for the current default locale. */ public final static NumberFormat getNumberInstance() { - return getInstance(Locale.getDefault(), NUMBERSTYLE); + return getInstance(Locale.getDefault(Locale.Category.FORMAT), NUMBERSTYLE); } /** @@ -420,7 +420,7 @@ public abstract class NumberFormat extends Format { * @since 1.4 */ public final static NumberFormat getIntegerInstance() { - return getInstance(Locale.getDefault(), INTEGERSTYLE); + return getInstance(Locale.getDefault(Locale.Category.FORMAT), INTEGERSTYLE); } /** @@ -443,7 +443,7 @@ public abstract class NumberFormat extends Format { * Returns a currency format for the current default locale. */ public final static NumberFormat getCurrencyInstance() { - return getInstance(Locale.getDefault(), CURRENCYSTYLE); + return getInstance(Locale.getDefault(Locale.Category.FORMAT), CURRENCYSTYLE); } /** @@ -457,7 +457,7 @@ public abstract class NumberFormat extends Format { * Returns a percentage format for the current default locale. */ public final static NumberFormat getPercentInstance() { - return getInstance(Locale.getDefault(), PERCENTSTYLE); + return getInstance(Locale.getDefault(Locale.Category.FORMAT), PERCENTSTYLE); } /** @@ -471,7 +471,7 @@ public abstract class NumberFormat extends Format { * Returns a scientific format for the current default locale. */ /*public*/ final static NumberFormat getScientificInstance() { - return getInstance(Locale.getDefault(), SCIENTIFICSTYLE); + return getInstance(Locale.getDefault(Locale.Category.FORMAT), SCIENTIFICSTYLE); } /** diff --git a/src/share/classes/java/text/SimpleDateFormat.java b/src/share/classes/java/text/SimpleDateFormat.java index 060e7bf642e65b307dc654e7c3a45a87e9b4b759..defd076ed216ba9ccc9320a5274317288ead4815 100644 --- a/src/share/classes/java/text/SimpleDateFormat.java +++ b/src/share/classes/java/text/SimpleDateFormat.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2008, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -55,6 +55,8 @@ import sun.util.calendar.CalendarUtils; import sun.util.calendar.ZoneInfoFile; import sun.util.resources.LocaleData; +import static java.text.DateFormatSymbols.*; + /** * SimpleDateFormat is a concrete class for formatting and * parsing dates in a locale-sensitive manner. It allows for formatting @@ -108,40 +110,50 @@ import sun.util.resources.LocaleData; * Year * 1996; 96 * + * Y + * Week year + * Year + * 2009; 09 + * * M * Month in year * Month * July; Jul; 07 - * + * * w * Week in year * Number * 27 - * + * * W * Week in month * Number * 2 - * + * * D * Day in year * Number * 189 - * + * * d * Day in month * Number * 10 - * + * * F * Day of week in month * Number * 2 - * + * * E - * Day in week + * Day name in week * Text * Tuesday; Tue + * + * u + * Day number of week (1 = Monday, ..., 7 = Sunday) + * Number + * 1 * * a * Am/pm marker @@ -192,6 +204,11 @@ import sun.util.resources.LocaleData; * Time zone * RFC 822 time zone * -0800 + * + * X + * Time zone + * ISO 8601 time zone + * -08; -0800; -08:00 * * * Pattern letters are usually repeated, as their number determines the @@ -202,12 +219,12 @@ import sun.util.resources.LocaleData; * the full form is used; otherwise a short or abbreviated form * is used if available. * For parsing, both forms are accepted, independent of the number - * of pattern letters. + * of pattern letters.

  • *
  • Number: * For formatting, the number of pattern letters is the minimum * number of digits, and shorter numbers are zero-padded to this amount. * For parsing, the number of pattern letters is ignored unless - * it's needed to separate two adjacent fields. + * it's needed to separate two adjacent fields.

  • *
  • Year: * If the formatter's {@link #getCalendar() Calendar} is the Gregorian * calendar, the following rules are applied.
    @@ -239,11 +256,20 @@ import sun.util.resources.LocaleData; * letters is 4 or more, a calendar specific {@linkplain * Calendar#LONG long form} is used. Otherwise, a calendar * specific {@linkplain Calendar#SHORT short or abbreviated form} - * is used. + * is used.
    + *
    + * If week year {@code 'Y'} is specified and the {@linkplain + * #getCalendar() calendar} doesn't support any week + * years, the calendar year ({@code 'y'}) is used instead. The + * support of week years can be tested with a call to {@link + * DateFormat#getCalendar() getCalendar()}.{@link + * java.util.Calendar#isWeekDateSupported() + * isWeekDateSupported()}.

  • *
  • Month: * If the number of pattern letters is 3 or more, the month is * interpreted as text; otherwise, - * it is interpreted as a number. + * it is interpreted as a number.

  • *
  • General time zone: * Time zones are interpreted as text if they have * names. For time zones representing a GMT offset value, the @@ -264,9 +290,10 @@ import sun.util.resources.LocaleData; * 00 and 59. The format is locale independent and digits must be taken * from the Basic Latin block of the Unicode standard. *

    For parsing, RFC 822 time zones are also - * accepted. + * accepted.

  • *
  • RFC 822 time zone: * For formatting, the RFC 822 4-digit time zone format is used: + * *
      *     RFC822TimeZone:
      *             Sign TwoDigitHours Minutes
    @@ -274,8 +301,41 @@ import sun.util.resources.LocaleData;
      *             Digit Digit
    * TwoDigitHours must be between 00 and 23. Other definitions * are as for general time zones. + * *

    For parsing, general time zones are also * accepted. + *

  • ISO 8601 Time zone: + * The number of pattern letters designates the format for both formatting + * and parsing as follows: + *
    + *     ISO8601TimeZone:
    + *             OneLetterISO8601TimeZone
    + *             TwoLetterISO8601TimeZone
    + *             ThreeLetterISO8601TimeZone
    + *     OneLetterISO8601TimeZone:
    + *             Sign TwoDigitHours
    + *             {@code Z}
    + *     TwoLetterISO8601TimeZone:
    + *             Sign TwoDigitHours Minutes
    + *             {@code Z}
    + *     ThreeLetterISO8601TimeZone:
    + *             Sign TwoDigitHours {@code :} Minutes
    + *             {@code Z}
    + * Other definitions are as for general time zones or + * RFC 822 time zones. + * + *

    For formatting, if the offset value from GMT is 0, {@code "Z"} is + * produced. If the number of pattern letters is 1, any fraction of an hour + * is ignored. For example, if the pattern is {@code "X"} and the time zone is + * {@code "GMT+05:30"}, {@code "+05"} is produced. + * + *

    For parsing, {@code "Z"} is parsed as the UTC time zone designator. + * General time zones are not accepted. + * + *

    If the number of pattern letters is 4 or more, {@link + * IllegalArgumentException} is thrown when constructing a {@code + * SimpleDateFormat} or {@linkplain #applyPattern(String) applying a + * pattern}. * * SimpleDateFormat also supports localized date and time * pattern strings. In these strings, the pattern letters described above @@ -321,6 +381,12 @@ import sun.util.resources.LocaleData; * * "yyyy-MM-dd'T'HH:mm:ss.SSSZ" * 2001-07-04T12:08:56.235-0700 + * + * "yyyy-MM-dd'T'HH:mm:ss.SSSXXX" + * 2001-07-04T12:08:56.235-07:00 + * + * "YYYY-'W'ww-u" + * 2001-W27-3 * * * @@ -474,7 +540,7 @@ public class SimpleDateFormat extends DateFormat { * class. */ public SimpleDateFormat() { - this(SHORT, SHORT, Locale.getDefault()); + this(SHORT, SHORT, Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -490,7 +556,7 @@ public class SimpleDateFormat extends DateFormat { */ public SimpleDateFormat(String pattern) { - this(pattern, Locale.getDefault()); + this(pattern, Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -535,7 +601,7 @@ public class SimpleDateFormat extends DateFormat { this.pattern = pattern; this.formatData = (DateFormatSymbols) formatSymbols.clone(); - this.locale = Locale.getDefault(); + this.locale = Locale.getDefault(Locale.Category.FORMAT); initializeCalendar(this.locale); initialize(this.locale); useDateFormatSymbols = true; @@ -815,6 +881,9 @@ public class SimpleDateFormat extends DateFormat { * Encodes the given tag and length and puts encoded char(s) into buffer. */ private static final void encode(int tag, int length, StringBuilder buffer) { + if (tag == PATTERN_ISO_ZONE && length >= 4) { + throw new IllegalArgumentException("invalid ISO 8601 format: length=" + length); + } if (length < 255) { buffer.append((char)(tag << 8 | length)); } else { @@ -877,7 +946,7 @@ public class SimpleDateFormat extends DateFormat { * @param pos the formatting position. On input: an alignment field, * if desired. On output: the offsets of the alignment field. * @return the formatted date-time string. - * @exception NullPointerException if the given date is null + * @exception NullPointerException if the given {@code date} is {@code null}. */ public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition pos) @@ -968,6 +1037,10 @@ public class SimpleDateFormat extends DateFormat { Calendar.DAY_OF_YEAR, Calendar.DAY_OF_WEEK_IN_MONTH, Calendar.WEEK_OF_YEAR, Calendar.WEEK_OF_MONTH, Calendar.AM_PM, Calendar.HOUR, Calendar.HOUR, Calendar.ZONE_OFFSET, + Calendar.ZONE_OFFSET, + // Pseudo Calendar fields + CalendarBuilder.WEEK_YEAR, + CalendarBuilder.ISO_DAY_OF_WEEK, Calendar.ZONE_OFFSET }; @@ -982,6 +1055,8 @@ public class SimpleDateFormat extends DateFormat { DateFormat.WEEK_OF_MONTH_FIELD, DateFormat.AM_PM_FIELD, DateFormat.HOUR1_FIELD, DateFormat.HOUR0_FIELD, DateFormat.TIMEZONE_FIELD, DateFormat.TIMEZONE_FIELD, + DateFormat.YEAR_FIELD, DateFormat.DAY_OF_WEEK_FIELD, + DateFormat.TIMEZONE_FIELD }; // Maps from DecimalFormatSymbols index to Field constant @@ -993,6 +1068,8 @@ public class SimpleDateFormat extends DateFormat { Field.WEEK_OF_YEAR, Field.WEEK_OF_MONTH, Field.AM_PM, Field.HOUR1, Field.HOUR0, Field.TIME_ZONE, Field.TIME_ZONE, + Field.YEAR, Field.DAY_OF_WEEK, + Field.TIME_ZONE }; /** @@ -1007,9 +1084,24 @@ public class SimpleDateFormat extends DateFormat { int beginOffset = buffer.length(); int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex]; - int value = calendar.get(field); + int value; + if (field == CalendarBuilder.WEEK_YEAR) { + if (calendar.isWeekDateSupported()) { + value = calendar.getWeekYear(); + } else { + // use calendar year 'y' instead + patternCharIndex = PATTERN_YEAR; + field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex]; + value = calendar.get(field); + } + } else if (field == CalendarBuilder.ISO_DAY_OF_WEEK) { + value = CalendarBuilder.toISODayOfWeek(calendar.get(Calendar.DAY_OF_WEEK)); + } else { + value = calendar.get(field); + } + int style = (count >= 4) ? Calendar.LONG : Calendar.SHORT; - if (!useDateFormatSymbols) { + if (!useDateFormatSymbols && field != CalendarBuilder.ISO_DAY_OF_WEEK) { current = calendar.getDisplayName(field, style, locale); } @@ -1018,7 +1110,7 @@ public class SimpleDateFormat extends DateFormat { // zeroPaddingNumber() must be fixed. switch (patternCharIndex) { - case 0: // 'G' - ERA + case PATTERN_ERA: // 'G' if (useDateFormatSymbols) { String[] eras = formatData.getEras(); if (value < eras.length) @@ -1028,7 +1120,8 @@ public class SimpleDateFormat extends DateFormat { current = ""; break; - case 1: // 'y' - YEAR + case PATTERN_WEEK_YEAR: // 'Y' + case PATTERN_YEAR: // 'y' if (calendar instanceof GregorianCalendar) { if (count != 2) zeroPaddingNumber(value, count, maxIntCount, buffer); @@ -1042,7 +1135,7 @@ public class SimpleDateFormat extends DateFormat { } break; - case 2: // 'M' - MONTH + case PATTERN_MONTH: // 'M' if (useDateFormatSymbols) { String[] months; if (count >= 4) { @@ -1062,7 +1155,7 @@ public class SimpleDateFormat extends DateFormat { } break; - case 4: // 'k' - HOUR_OF_DAY: 1-based. eg, 23:59 + 1 hour =>> 24:59 + case PATTERN_HOUR_OF_DAY1: // 'k' 1-based. eg, 23:59 + 1 hour =>> 24:59 if (current == null) { if (value == 0) zeroPaddingNumber(calendar.getMaximum(Calendar.HOUR_OF_DAY)+1, @@ -1072,7 +1165,7 @@ public class SimpleDateFormat extends DateFormat { } break; - case 9: // 'E' - DAY_OF_WEEK + case PATTERN_DAY_OF_WEEK: // 'E' if (useDateFormatSymbols) { String[] weekdays; if (count >= 4) { @@ -1085,14 +1178,14 @@ public class SimpleDateFormat extends DateFormat { } break; - case 14: // 'a' - AM_PM + case PATTERN_AM_PM: // 'a' if (useDateFormatSymbols) { String[] ampm = formatData.getAmPmStrings(); current = ampm[value]; } break; - case 15: // 'h' - HOUR:1-based. eg, 11PM + 1 hour =>> 12 AM + case PATTERN_HOUR1: // 'h' 1-based. eg, 11PM + 1 hour =>> 12 AM if (current == null) { if (value == 0) zeroPaddingNumber(calendar.getLeastMaximum(Calendar.HOUR)+1, @@ -1102,7 +1195,7 @@ public class SimpleDateFormat extends DateFormat { } break; - case 17: // 'z' - ZONE_OFFSET + case PATTERN_ZONE_NAME: // 'z' if (current == null) { if (formatData.locale == null || formatData.isZoneStringsSet) { int zoneIndex = @@ -1129,7 +1222,7 @@ public class SimpleDateFormat extends DateFormat { } break; - case 18: // 'Z' - ZONE_OFFSET ("-/+hhmm" form) + case PATTERN_ZONE_VALUE: // 'Z' ("-/+hhmm" form) value = (calendar.get(Calendar.ZONE_OFFSET) + calendar.get(Calendar.DST_OFFSET)) / 60000; @@ -1144,17 +1237,46 @@ public class SimpleDateFormat extends DateFormat { CalendarUtils.sprintf0d(buffer, num, width); break; + case PATTERN_ISO_ZONE: // 'X' + value = calendar.get(Calendar.ZONE_OFFSET) + + calendar.get(Calendar.DST_OFFSET); + + if (value == 0) { + buffer.append('Z'); + break; + } + + value /= 60000; + if (value >= 0) { + buffer.append('+'); + } else { + buffer.append('-'); + value = -value; + } + + CalendarUtils.sprintf0d(buffer, value / 60, 2); + if (count == 1) { + break; + } + + if (count == 3) { + buffer.append(':'); + } + CalendarUtils.sprintf0d(buffer, value % 60, 2); + break; + default: - // case 3: // 'd' - DATE - // case 5: // 'H' - HOUR_OF_DAY:0-based. eg, 23:59 + 1 hour =>> 00:59 - // case 6: // 'm' - MINUTE - // case 7: // 's' - SECOND - // case 8: // 'S' - MILLISECOND - // case 10: // 'D' - DAY_OF_YEAR - // case 11: // 'F' - DAY_OF_WEEK_IN_MONTH - // case 12: // 'w' - WEEK_OF_YEAR - // case 13: // 'W' - WEEK_OF_MONTH - // case 16: // 'K' - HOUR: 0-based. eg, 11PM + 1 hour =>> 0 AM + // case PATTERN_DAY_OF_MONTH: // 'd' + // case PATTERN_HOUR_OF_DAY0: // 'H' 0-based. eg, 23:59 + 1 hour =>> 00:59 + // case PATTERN_MINUTE: // 'm' + // case PATTERN_SECOND: // 's' + // case PATTERN_MILLISECOND: // 'S' + // case PATTERN_DAY_OF_YEAR: // 'D' + // case PATTERN_DAY_OF_WEEK_IN_MONTH: // 'F' + // case PATTERN_WEEK_OF_YEAR: // 'w' + // case PATTERN_WEEK_OF_MONTH: // 'W' + // case PATTERN_HOUR0: // 'K' eg, 11PM + 1 hour =>> 0 AM + // case PATTERN_ISO_DAY_OF_WEEK: // 'u' pseudo field, Monday = 1, ..., Sunday = 7 if (current == null) { zeroPaddingNumber(value, count, maxIntCount, buffer); } @@ -1264,10 +1386,9 @@ public class SimpleDateFormat extends DateFormat { int oldStart = start; int textLength = text.length(); - calendar.clear(); // Clears all the time fields - boolean[] ambiguousYear = {false}; + CalendarBuilder calb = new CalendarBuilder(); for (int i = 0; i < compiledPattern.length; ) { int tag = compiledPattern[i] >>> 8; @@ -1340,7 +1461,7 @@ public class SimpleDateFormat extends DateFormat { } start = subParse(text, start, tag, count, obeyCount, ambiguousYear, pos, - useFollowingMinusSignAsDelimiter); + useFollowingMinusSignAsDelimiter, calb); if (start < 0) { pos.index = oldStart; return null; @@ -1354,46 +1475,16 @@ public class SimpleDateFormat extends DateFormat { pos.index = start; - // This part is a problem: When we call parsedDate.after, we compute the time. - // Take the date April 3 2004 at 2:30 am. When this is first set up, the year - // will be wrong if we're parsing a 2-digit year pattern. It will be 1904. - // April 3 1904 is a Sunday (unlike 2004) so it is the DST onset day. 2:30 am - // is therefore an "impossible" time, since the time goes from 1:59 to 3:00 am - // on that day. It is therefore parsed out to fields as 3:30 am. Then we - // add 100 years, and get April 3 2004 at 3:30 am. Note that April 3 2004 is - // a Saturday, so it can have a 2:30 am -- and it should. [LIU] - /* - Date parsedDate = calendar.getTime(); - if( ambiguousYear[0] && !parsedDate.after(defaultCenturyStart) ) { - calendar.add(Calendar.YEAR, 100); - parsedDate = calendar.getTime(); - } - */ - // Because of the above condition, save off the fields in case we need to readjust. - // The procedure we use here is not particularly efficient, but there is no other - // way to do this given the API restrictions present in Calendar. We minimize - // inefficiency by only performing this computation when it might apply, that is, - // when the two-digit year is equal to the start year, and thus might fall at the - // front or the back of the default century. This only works because we adjust - // the year correctly to start with in other cases -- see subParse(). Date parsedDate; try { - if (ambiguousYear[0]) // If this is true then the two-digit year == the default start year - { - // We need a copy of the fields, and we need to avoid triggering a call to - // complete(), which will recalculate the fields. Since we can't access - // the fields[] array in Calendar, we clone the entire object. This will - // stop working if Calendar.clone() is ever rewritten to call complete(). - Calendar savedCalendar = (Calendar)calendar.clone(); - parsedDate = calendar.getTime(); - if (parsedDate.before(defaultCenturyStart)) - { - // We can't use add here because that does a complete() first. - savedCalendar.set(Calendar.YEAR, defaultCenturyStartYear + 100); - parsedDate = savedCalendar.getTime(); + parsedDate = calb.establish(calendar).getTime(); + // If the year value is ambiguous, + // then the two-digit year == the default start year + if (ambiguousYear[0]) { + if (parsedDate.before(defaultCenturyStart)) { + parsedDate = calb.addYear(100).establish(calendar).getTime(); } } - else parsedDate = calendar.getTime(); } // An IllegalArgumentException will be thrown by Calendar.getTime() // if any fields are out of range, e.g., MONTH == 17. @@ -1415,7 +1506,7 @@ public class SimpleDateFormat extends DateFormat { * @return the new start position if matching succeeded; a negative number * indicating matching failure, otherwise. */ - private int matchString(String text, int start, int field, String[] data) + private int matchString(String text, int start, int field, String[] data, CalendarBuilder calb) { int i = 0; int count = data.length; @@ -1441,7 +1532,7 @@ public class SimpleDateFormat extends DateFormat { } if (bestMatch >= 0) { - calendar.set(field, bestMatch); + calb.set(field, bestMatch); return start + bestMatchLength; } return -start; @@ -1452,7 +1543,8 @@ public class SimpleDateFormat extends DateFormat { * String[]). This method takes a Map instead of * String[]. */ - private int matchString(String text, int start, int field, Map data) { + private int matchString(String text, int start, int field, + Map data, CalendarBuilder calb) { if (data != null) { String bestMatch = null; @@ -1466,7 +1558,7 @@ public class SimpleDateFormat extends DateFormat { } if (bestMatch != null) { - calendar.set(field, data.get(bestMatch)); + calb.set(field, data.get(bestMatch)); return start + bestMatch.length(); } } @@ -1486,11 +1578,22 @@ public class SimpleDateFormat extends DateFormat { return -1; } + private boolean matchDSTString(String text, int start, int zoneIndex, int standardIndex, + String[][] zoneStrings) { + int index = standardIndex + 2; + String zoneName = zoneStrings[zoneIndex][index]; + if (text.regionMatches(true, start, + zoneName, 0, zoneName.length())) { + return true; + } + return false; + } + /** * find time zone 'text' matched zoneStrings and set to internal * calendar. */ - private int subParseZoneString(String text, int start) { + private int subParseZoneString(String text, int start, CalendarBuilder calb) { boolean useSameName = false; // true if standard and daylight time use the same abbreviation. TimeZone currentTimeZone = getTimeZone(); @@ -1524,6 +1627,7 @@ public class SimpleDateFormat extends DateFormat { } } } + if (tz == null) { int len = zoneStrings.length; for (int i = 0; i < len; i++) { @@ -1549,8 +1653,8 @@ public class SimpleDateFormat extends DateFormat { // determine the local time. (6645292) int dstAmount = (nameIndex >= 3) ? tz.getDSTSavings() : 0; if (!(useSameName || (nameIndex >= 3 && dstAmount == 0))) { - calendar.set(Calendar.ZONE_OFFSET, tz.getRawOffset()); - calendar.set(Calendar.DST_OFFSET, dstAmount); + calb.set(Calendar.ZONE_OFFSET, tz.getRawOffset()) + .set(Calendar.DST_OFFSET, dstAmount); } return (start + zoneNames[nameIndex].length()); } @@ -1577,11 +1681,15 @@ public class SimpleDateFormat extends DateFormat { private int subParse(String text, int start, int patternCharIndex, int count, boolean obeyCount, boolean[] ambiguousYear, ParsePosition origPos, - boolean useFollowingMinusSignAsDelimiter) { + boolean useFollowingMinusSignAsDelimiter, CalendarBuilder calb) { Number number = null; int value = 0; ParsePosition pos = new ParsePosition(0); pos.index = start; + if (patternCharIndex == PATTERN_WEEK_YEAR && !calendar.isWeekDateSupported()) { + // use calendar year 'y' instead + patternCharIndex = PATTERN_YEAR; + } int field = PATTERN_INDEX_TO_CALENDAR_FIELD[patternCharIndex]; // If there are any spaces here, skip over them. If we hit the end @@ -1602,10 +1710,11 @@ public class SimpleDateFormat extends DateFormat { // a number value. We handle further, more generic cases below. We need // to handle some of them here because some fields require extra processing on // the parsed value. - if (patternCharIndex == 4 /* HOUR_OF_DAY1_FIELD */ || - patternCharIndex == 15 /* HOUR1_FIELD */ || - (patternCharIndex == 2 /* MONTH_FIELD */ && count <= 2) || - patternCharIndex == 1 /* YEAR_FIELD */) { + if (patternCharIndex == PATTERN_HOUR_OF_DAY1 || + patternCharIndex == PATTERN_HOUR1 || + (patternCharIndex == PATTERN_MONTH && count <= 2) || + patternCharIndex == PATTERN_YEAR || + patternCharIndex == PATTERN_WEEK_YEAR) { // It would be good to unify this with the obeyCount logic below, // but that's going to be difficult. if (obeyCount) { @@ -1617,7 +1726,7 @@ public class SimpleDateFormat extends DateFormat { number = numberFormat.parse(text, pos); } if (number == null) { - if (patternCharIndex != 1 || calendar instanceof GregorianCalendar) { + if (patternCharIndex != PATTERN_YEAR || calendar instanceof GregorianCalendar) { break parsing; } } else { @@ -1638,33 +1747,34 @@ public class SimpleDateFormat extends DateFormat { int index; switch (patternCharIndex) { - case 0: // 'G' - ERA + case PATTERN_ERA: // 'G' if (useDateFormatSymbols) { - if ((index = matchString(text, start, Calendar.ERA, formatData.getEras())) > 0) { + if ((index = matchString(text, start, Calendar.ERA, formatData.getEras(), calb)) > 0) { return index; } } else { Map map = calendar.getDisplayNames(field, Calendar.ALL_STYLES, locale); - if ((index = matchString(text, start, field, map)) > 0) { + if ((index = matchString(text, start, field, map, calb)) > 0) { return index; } } break parsing; - case 1: // 'y' - YEAR + case PATTERN_WEEK_YEAR: // 'Y' + case PATTERN_YEAR: // 'y' if (!(calendar instanceof GregorianCalendar)) { // calendar might have text representations for year values, // such as "\u5143" in JapaneseImperialCalendar. int style = (count >= 4) ? Calendar.LONG : Calendar.SHORT; Map map = calendar.getDisplayNames(field, style, locale); if (map != null) { - if ((index = matchString(text, start, field, map)) > 0) { + if ((index = matchString(text, start, field, map, calb)) > 0) { return index; } } - calendar.set(field, value); + calb.set(field, value); return pos.index; } @@ -1676,8 +1786,7 @@ public class SimpleDateFormat extends DateFormat { // is treated literally: "2250", "-1", "1", "002". if (count <= 2 && (pos.index - start) == 2 && Character.isDigit(text.charAt(start)) - && Character.isDigit(text.charAt(start+1))) - { + && Character.isDigit(text.charAt(start+1))) { // Assume for example that the defaultCenturyStart is 6/18/1903. // This means that two-digit years will be forced into the range // 6/18/1903 to 6/17/2003. As a result, years 00, 01, and 02 @@ -1691,16 +1800,16 @@ public class SimpleDateFormat extends DateFormat { value += (defaultCenturyStartYear/100)*100 + (value < ambiguousTwoDigitYear ? 100 : 0); } - calendar.set(Calendar.YEAR, value); + calb.set(field, value); return pos.index; - case 2: // 'M' - MONTH + case PATTERN_MONTH: // 'M' if (count <= 2) // i.e., M or MM. { // Don't want to parse the month if it is a string // while pattern uses numeric style: M or MM. // [We computed 'value' above.] - calendar.set(Calendar.MONTH, value - 1); + calb.set(Calendar.MONTH, value - 1); return pos.index; } @@ -1710,50 +1819,50 @@ public class SimpleDateFormat extends DateFormat { // Try count == 4 first: int newStart = 0; if ((newStart = matchString(text, start, Calendar.MONTH, - formatData.getMonths())) > 0) { + formatData.getMonths(), calb)) > 0) { return newStart; } // count == 4 failed, now try count == 3 if ((index = matchString(text, start, Calendar.MONTH, - formatData.getShortMonths())) > 0) { + formatData.getShortMonths(), calb)) > 0) { return index; } } else { Map map = calendar.getDisplayNames(field, Calendar.ALL_STYLES, locale); - if ((index = matchString(text, start, field, map)) > 0) { + if ((index = matchString(text, start, field, map, calb)) > 0) { return index; } } break parsing; - case 4: // 'k' - HOUR_OF_DAY: 1-based. eg, 23:59 + 1 hour =>> 24:59 + case PATTERN_HOUR_OF_DAY1: // 'k' 1-based. eg, 23:59 + 1 hour =>> 24:59 // [We computed 'value' above.] if (value == calendar.getMaximum(Calendar.HOUR_OF_DAY)+1) value = 0; - calendar.set(Calendar.HOUR_OF_DAY, value); + calb.set(Calendar.HOUR_OF_DAY, value); return pos.index; - case 9: - { // 'E' - DAY_OF_WEEK + case PATTERN_DAY_OF_WEEK: // 'E' + { if (useDateFormatSymbols) { // Want to be able to parse both short and long forms. // Try count == 4 (DDDD) first: int newStart = 0; if ((newStart=matchString(text, start, Calendar.DAY_OF_WEEK, - formatData.getWeekdays())) > 0) { + formatData.getWeekdays(), calb)) > 0) { return newStart; } // DDDD failed, now try DDD if ((index = matchString(text, start, Calendar.DAY_OF_WEEK, - formatData.getShortWeekdays())) > 0) { + formatData.getShortWeekdays(), calb)) > 0) { return index; } } else { int[] styles = { Calendar.LONG, Calendar.SHORT }; for (int style : styles) { Map map = calendar.getDisplayNames(field, style, locale); - if ((index = matchString(text, start, field, map)) > 0) { + if ((index = matchString(text, start, field, map, calb)) > 0) { return index; } } @@ -1761,27 +1870,28 @@ public class SimpleDateFormat extends DateFormat { } break parsing; - case 14: // 'a' - AM_PM + case PATTERN_AM_PM: // 'a' if (useDateFormatSymbols) { - if ((index = matchString(text, start, Calendar.AM_PM, formatData.getAmPmStrings())) > 0) { + if ((index = matchString(text, start, Calendar.AM_PM, + formatData.getAmPmStrings(), calb)) > 0) { return index; } } else { Map map = calendar.getDisplayNames(field, Calendar.ALL_STYLES, locale); - if ((index = matchString(text, start, field, map)) > 0) { + if ((index = matchString(text, start, field, map, calb)) > 0) { return index; } } break parsing; - case 15: // 'h' - HOUR:1-based. eg, 11PM + 1 hour =>> 12 AM + case PATTERN_HOUR1: // 'h' 1-based. eg, 11PM + 1 hour =>> 12 AM // [We computed 'value' above.] if (value == calendar.getLeastMaximum(Calendar.HOUR)+1) value = 0; - calendar.set(Calendar.HOUR, value); + calb.set(Calendar.HOUR, value); return pos.index; - case 17: // 'z' - ZONE_OFFSET - case 18: // 'Z' - ZONE_OFFSET + case PATTERN_ZONE_NAME: // 'z' + case PATTERN_ZONE_VALUE: // 'Z' // First try to parse generic forms such as GMT-07:00. Do this first // in case localized TimeZoneNames contains the string "GMT" // for a zone; in that case, we don't want to match the first three @@ -1797,7 +1907,7 @@ public class SimpleDateFormat extends DateFormat { if ((text.length() - start) >= GMT.length() && text.regionMatches(true, start, GMT, 0, GMT.length())) { int num; - calendar.set(Calendar.DST_OFFSET, 0); + calb.set(Calendar.DST_OFFSET, 0); pos.index = start + GMT.length(); try { // try-catch for "GMT" only time zone string @@ -1810,8 +1920,8 @@ public class SimpleDateFormat extends DateFormat { } catch(StringIndexOutOfBoundsException e) {} - if (sign == 0) { /* "GMT" without offset */ - calendar.set(Calendar.ZONE_OFFSET, 0); + if (sign == 0) { /* "GMT" without offset */ + calb.set(Calendar.ZONE_OFFSET, 0); return pos.index; } @@ -1875,7 +1985,7 @@ public class SimpleDateFormat extends DateFormat { sign = -1; } else { // Try parsing the text as a time zone name (abbr). - int i = subParseZoneString(text, pos.index); + int i = subParseZoneString(text, pos.index, calb); if (i != 0) { return i; } @@ -1933,24 +2043,112 @@ public class SimpleDateFormat extends DateFormat { // arrive here if the form GMT+/-... or an RFC 822 form was seen. if (sign != 0) { offset *= MILLIS_PER_MINUTE * sign; - calendar.set(Calendar.ZONE_OFFSET, offset); - calendar.set(Calendar.DST_OFFSET, 0); + calb.set(Calendar.ZONE_OFFSET, offset).set(Calendar.DST_OFFSET, 0); + return ++pos.index; + } + } + break parsing; + + case PATTERN_ISO_ZONE: // 'X' + { + int sign = 0; + int offset = 0; + + iso8601: { + try { + char c = text.charAt(pos.index); + if (c == 'Z') { + calb.set(Calendar.ZONE_OFFSET, 0).set(Calendar.DST_OFFSET, 0); + return ++pos.index; + } + + // parse text as "+/-hh[[:]mm]" based on count + if (c == '+') { + sign = 1; + } else if (c == '-') { + sign = -1; + } + // Look for hh. + int hours = 0; + c = text.charAt(++pos.index); + if (c < '0' || c > '9') { /* must be from '0' to '9'. */ + break parsing; + } + hours = c - '0'; + c = text.charAt(++pos.index); + if (c < '0' || c > '9') { /* must be from '0' to '9'. */ + break parsing; + } + hours *= 10; + hours += c - '0'; + if (hours > 23) { + break parsing; + } + + if (count == 1) { // "X" + offset = hours * 60; + break iso8601; + } + + c = text.charAt(++pos.index); + // Skip ':' if "XXX" + if (c == ':') { + if (count == 2) { + break parsing; + } + c = text.charAt(++pos.index); + } else { + if (count == 3) { + // missing ':' + break parsing; + } + } + + // Look for mm. + int minutes = 0; + if (c < '0' || c > '9') { /* must be from '0' to '9'. */ + break parsing; + } + minutes = c - '0'; + c = text.charAt(++pos.index); + if (c < '0' || c > '9') { /* must be from '0' to '9'. */ + break parsing; + } + minutes *= 10; + minutes += c - '0'; + + if (minutes > 59) { + break parsing; + } + + offset = hours * 60 + minutes; + } catch (StringIndexOutOfBoundsException e) { + break parsing; + } + } + + // Do the final processing for both of the above cases. We only + // arrive here if the form GMT+/-... or an RFC 822 form was seen. + if (sign != 0) { + offset *= MILLIS_PER_MINUTE * sign; + calb.set(Calendar.ZONE_OFFSET, offset).set(Calendar.DST_OFFSET, 0); return ++pos.index; } } break parsing; default: - // case 3: // 'd' - DATE - // case 5: // 'H' - HOUR_OF_DAY:0-based. eg, 23:59 + 1 hour =>> 00:59 - // case 6: // 'm' - MINUTE - // case 7: // 's' - SECOND - // case 8: // 'S' - MILLISECOND - // case 10: // 'D' - DAY_OF_YEAR - // case 11: // 'F' - DAY_OF_WEEK_IN_MONTH - // case 12: // 'w' - WEEK_OF_YEAR - // case 13: // 'W' - WEEK_OF_MONTH - // case 16: // 'K' - HOUR: 0-based. eg, 11PM + 1 hour =>> 0 AM + // case PATTERN_DAY_OF_MONTH: // 'd' + // case PATTERN_HOUR_OF_DAY0: // 'H' 0-based. eg, 23:59 + 1 hour =>> 00:59 + // case PATTERN_MINUTE: // 'm' + // case PATTERN_SECOND: // 's' + // case PATTERN_MILLISECOND: // 'S' + // case PATTERN_DAY_OF_YEAR: // 'D' + // case PATTERN_DAY_OF_WEEK_IN_MONTH: // 'F' + // case PATTERN_WEEK_OF_YEAR: // 'w' + // case PATTERN_WEEK_OF_MONTH: // 'W' + // case PATTERN_HOUR0: // 'K' 0-based. eg, 11PM + 1 hour =>> 0 AM + // case PATTERN_ISO_DAY_OF_WEEK: // 'u' (pseudo field); // Handle "generic" fields if (obeyCount) { @@ -1973,7 +2171,7 @@ public class SimpleDateFormat extends DateFormat { pos.index--; } - calendar.set(field, value); + calb.set(field, value); return pos.index; } break parsing; @@ -2020,11 +2218,18 @@ public class SimpleDateFormat extends DateFormat { inQuote = true; else if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) { int ci = from.indexOf(c); - if (ci == -1) + if (ci >= 0) { + // patternChars is longer than localPatternChars due + // to serialization compatibility. The pattern letters + // unsupported by localPatternChars pass through. + if (ci < to.length()) { + c = to.charAt(ci); + } + } else { throw new IllegalArgumentException("Illegal pattern " + " character '" + c + "'"); - c = to.charAt(ci); + } } } result.append(c); @@ -2061,7 +2266,7 @@ public class SimpleDateFormat extends DateFormat { * @exception NullPointerException if the given pattern is null * @exception IllegalArgumentException if the given pattern is invalid */ - public void applyPattern (String pattern) + public void applyPattern(String pattern) { compiledPattern = compile(pattern); this.pattern = pattern; diff --git a/src/share/classes/java/util/Calendar.java b/src/share/classes/java/util/Calendar.java index 80a193687d08187b7de290ca8bc932830f083301..7ec5e87ec39c791b5a5ee60e891dc5d505280158 100644 --- a/src/share/classes/java/util/Calendar.java +++ b/src/share/classes/java/util/Calendar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -119,7 +119,7 @@ import sun.util.resources.LocaleData; * calculating its time or calendar field values if any out-of-range field * value has been set. * - *

    First Week

    + *

    First Week

    * * Calendar defines a locale-specific seven day week using two * parameters: the first day of the week and the minimal days in first week @@ -933,7 +933,7 @@ public abstract class Calendar implements Serializable, Cloneable, ComparableThe default implementation of this method returns {@code false}. + * + * @return {@code true} if this {@code Calendar} supports week dates; + * {@code false} otherwise. + * @see #getWeekYear() + * @see #setWeekDate(int,int,int) + * @see #getWeeksInWeekYear() + * @since 1.7 + */ + public boolean isWeekDateSupported() { + return false; + } + + /** + * Returns the week year represented by this {@code Calendar}. The + * week year is in sync with the week cycle. The {@linkplain + * #getFirstDayOfWeek() first day of the first week} is the first + * day of the week year. + * + *

    The default implementation of this method throws an + * {@link UnsupportedOperationException}. + * + * @return the week year of this {@code Calendar} + * @exception UnsupportedOperationException + * if any week year numbering isn't supported + * in this {@code Calendar}. + * @see #isWeekDateSupported() + * @see #getFirstDayOfWeek() + * @see #getMinimalDaysInFirstWeek() + * @since 1.7 + */ + public int getWeekYear() { + throw new UnsupportedOperationException(); + } + + /** + * Sets the date of this {@code Calendar} with the the given date + * specifiers - week year, week of year, and day of week. + * + *

    Unlike the {@code set} method, all of the calendar fields + * and {@code time} values are calculated upon return. + * + *

    If {@code weekOfYear} is out of the valid week-of-year range + * in {@code weekYear}, the {@code weekYear} and {@code + * weekOfYear} values are adjusted in lenient mode, or an {@code + * IllegalArgumentException} is thrown in non-lenient mode. + * + *

    The default implementation of this method throws an + * {@code UnsupportedOperationException}. + * + * @param weekYear the week year + * @param weekOfYear the week number based on {@code weekYear} + * @param dayOfWeek the day of week value: one of the constants + * for the {@link #DAY_OF_WEEK} field: {@link + * #SUNDAY}, ..., {@link #SATURDAY}. + * @exception IllegalArgumentException + * if any of the given date specifiers is invalid + * or any of the calendar fields are inconsistent + * with the given date specifiers in non-lenient mode + * @exception UnsupportedOperationException + * if any week year numbering isn't supported in this + * {@code Calendar}. + * @see #isWeekDateSupported() + * @see #getFirstDayOfWeek() + * @see #getMinimalDaysInFirstWeek() + * @since 1.7 + */ + public void setWeekDate(int weekYear, int weekOfYear, int dayOfWeek) { + throw new UnsupportedOperationException(); + } + + /** + * Returns the number of weeks in the week year represented by this + * {@code Calendar}. + * + *

    The default implementation of this method throws an + * {@code UnsupportedOperationException}. + * + * @return the number of weeks in the week year. + * @exception UnsupportedOperationException + * if any week year numbering isn't supported in this + * {@code Calendar}. + * @see #WEEK_OF_YEAR + * @see #isWeekDateSupported() + * @see #getWeekYear() + * @see #getActualMaximum(int) + * @since 1.7 + */ + public int getWeeksInWeekYear() { + throw new UnsupportedOperationException(); + } + /** * Returns the minimum value for the given calendar field of this * Calendar instance. The minimum value is defined as diff --git a/src/share/classes/java/util/ConcurrentModificationException.java b/src/share/classes/java/util/ConcurrentModificationException.java index b96b451aab9e779810595a276e81ee3098d419cb..956fbdfaf6293e8a0d12a4d283a517159ac9303e 100644 --- a/src/share/classes/java/util/ConcurrentModificationException.java +++ b/src/share/classes/java/util/ConcurrentModificationException.java @@ -49,9 +49,9 @@ package java.util; *

    Note that fail-fast behavior cannot be guaranteed as it is, generally * speaking, impossible to make any hard guarantees in the presence of * unsynchronized concurrent modification. Fail-fast operations - * throw ConcurrentModificationException on a best-effort basis. + * throw {@code ConcurrentModificationException} on a best-effort basis. * Therefore, it would be wrong to write a program that depended on this - * exception for its correctness: ConcurrentModificationException + * exception for its correctness: {@code ConcurrentModificationException} * should be used only to detect bugs. * * @author Josh Bloch @@ -77,7 +77,7 @@ public class ConcurrentModificationException extends RuntimeException { } /** - * Constructs a ConcurrentModificationException with the + * Constructs a {@code ConcurrentModificationException} with the * specified detail message. * * @param message the detail message pertaining to this exception. @@ -85,4 +85,39 @@ public class ConcurrentModificationException extends RuntimeException { public ConcurrentModificationException(String message) { super(message); } + + /** + * Constructs a new exception with the specified cause and a detail + * message of {@code (cause==null ? null : cause.toString())} (which + * typically contains the class and detail message of {@code cause}. + * + * @param cause the cause (which is saved for later retrieval by the + * {@link Throwable#getCause()} method). (A {@code null} value is + * permitted, and indicates that the cause is nonexistent or + * unknown.) + * @since 1.7 + */ + public ConcurrentModificationException(Throwable cause) { + super(cause); + } + + /** + * Constructs a new exception with the specified detail message and + * cause. + * + *

    Note that the detail message associated with cause is + * not automatically incorporated in this exception's detail + * message. + * + * @param message the detail message (which is saved for later retrieval + * by the {@link Throwable#getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the + * {@link Throwable#getCause()} method). (A {@code null} value + * is permitted, and indicates that the cause is nonexistent or + * unknown.) + * @since 1.7 + */ + public ConcurrentModificationException(String message, Throwable cause) { + super(message, cause); + } } diff --git a/src/share/classes/java/util/Currency.java b/src/share/classes/java/util/Currency.java index f7cedbe56cda419cc0b23e91ab0e0d31ad1646ba..daae561219f59a6742db34cb429970b8b264549b 100644 --- a/src/share/classes/java/util/Currency.java +++ b/src/share/classes/java/util/Currency.java @@ -452,7 +452,7 @@ public final class Currency implements Serializable { * @return the symbol of this currency for the default locale */ public String getSymbol() { - return getSymbol(Locale.getDefault()); + return getSymbol(Locale.getDefault(Locale.Category.DISPLAY)); } /** @@ -528,7 +528,7 @@ public final class Currency implements Serializable { * @since 1.7 */ public String getDisplayName() { - return getDisplayName(Locale.getDefault()); + return getDisplayName(Locale.getDefault(Locale.Category.DISPLAY)); } /** diff --git a/src/share/classes/java/util/Formatter.java b/src/share/classes/java/util/Formatter.java index 060ffd2768b1a5625051ba097795c8c09f3d4a63..55fd610d39d53244b9d9c389708a00aacf9f1bd3 100644 --- a/src/share/classes/java/util/Formatter.java +++ b/src/share/classes/java/util/Formatter.java @@ -1866,7 +1866,7 @@ public final class Formatter implements Closeable, Flushable { * virtual machine. */ public Formatter() { - init(new StringBuilder(), Locale.getDefault()); + init(new StringBuilder(), Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -1882,7 +1882,7 @@ public final class Formatter implements Closeable, Flushable { public Formatter(Appendable a) { if (a == null) a = new StringBuilder(); - init(a, Locale.getDefault()); + init(a, Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -1949,7 +1949,7 @@ public final class Formatter implements Closeable, Flushable { */ public Formatter(String fileName) throws FileNotFoundException { init(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))), - Locale.getDefault()); + Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -1985,7 +1985,7 @@ public final class Formatter implements Closeable, Flushable { public Formatter(String fileName, String csn) throws FileNotFoundException, UnsupportedEncodingException { - this(fileName, csn, Locale.getDefault()); + this(fileName, csn, Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -2057,7 +2057,7 @@ public final class Formatter implements Closeable, Flushable { */ public Formatter(File file) throws FileNotFoundException { init(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))), - Locale.getDefault()); + Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -2093,7 +2093,7 @@ public final class Formatter implements Closeable, Flushable { public Formatter(File file, String csn) throws FileNotFoundException, UnsupportedEncodingException { - this(file, csn, Locale.getDefault()); + this(file, csn, Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -2152,7 +2152,7 @@ public final class Formatter implements Closeable, Flushable { public Formatter(PrintStream ps) { if (ps == null) throw new NullPointerException(); - init((Appendable)ps, Locale.getDefault()); + init((Appendable)ps, Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -2171,7 +2171,7 @@ public final class Formatter implements Closeable, Flushable { */ public Formatter(OutputStream os) { init(new BufferedWriter(new OutputStreamWriter(os)), - Locale.getDefault()); + Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -2195,7 +2195,7 @@ public final class Formatter implements Closeable, Flushable { public Formatter(OutputStream os, String csn) throws UnsupportedEncodingException { - this(os, csn, Locale.getDefault()); + this(os, csn, Locale.getDefault(Locale.Category.FORMAT)); } /** diff --git a/src/share/classes/java/util/GregorianCalendar.java b/src/share/classes/java/util/GregorianCalendar.java index 9c0ff0d5cd4f0230dcb3747c369d59cd3b7dcfab..75402f0ff65f5b4f4129fcdb2ac14ed381ecc136 100644 --- a/src/share/classes/java/util/GregorianCalendar.java +++ b/src/share/classes/java/util/GregorianCalendar.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -88,23 +88,49 @@ import sun.util.calendar.ZoneInfo; * adjustment may be made if desired for dates that are prior to the Gregorian * changeover and which fall between January 1 and March 24. * - *

    Values calculated for the WEEK_OF_YEAR field range from 1 to - * 53. Week 1 for a year is the earliest seven day period starting on - * getFirstDayOfWeek() that contains at least - * getMinimalDaysInFirstWeek() days from that year. It thus - * depends on the values of getMinimalDaysInFirstWeek(), - * getFirstDayOfWeek(), and the day of the week of January 1. - * Weeks between week 1 of one year and week 1 of the following year are - * numbered sequentially from 2 to 52 or 53 (as needed). - - *

    For example, January 1, 1998 was a Thursday. If - * getFirstDayOfWeek() is MONDAY and - * getMinimalDaysInFirstWeek() is 4 (these are the values - * reflecting ISO 8601 and many national standards), then week 1 of 1998 starts - * on December 29, 1997, and ends on January 4, 1998. If, however, - * getFirstDayOfWeek() is SUNDAY, then week 1 of 1998 - * starts on January 4, 1998, and ends on January 10, 1998; the first three days - * of 1998 then are part of week 53 of 1997. + *

    Week Of Year and Week Year

    + * + *

    Values calculated for the {@link Calendar#WEEK_OF_YEAR + * WEEK_OF_YEAR} field range from 1 to 53. The first week of a + * calendar year is the earliest seven day period starting on {@link + * Calendar#getFirstDayOfWeek() getFirstDayOfWeek()} that contains at + * least {@link Calendar#getMinimalDaysInFirstWeek() + * getMinimalDaysInFirstWeek()} days from that year. It thus depends + * on the values of {@code getMinimalDaysInFirstWeek()}, {@code + * getFirstDayOfWeek()}, and the day of the week of January 1. Weeks + * between week 1 of one year and week 1 of the following year + * (exclusive) are numbered sequentially from 2 to 52 or 53 (except + * for year(s) involved in the Julian-Gregorian transition). + * + *

    The {@code getFirstDayOfWeek()} and {@code + * getMinimalDaysInFirstWeek()} values are initialized using + * locale-dependent resources when constructing a {@code + * GregorianCalendar}. The week + * determination is compatible with the ISO 8601 standard when {@code + * getFirstDayOfWeek()} is {@code MONDAY} and {@code + * getMinimalDaysInFirstWeek()} is 4, which values are used in locales + * where the standard is preferred. These values can explicitly be set by + * calling {@link Calendar#setFirstDayOfWeek(int) setFirstDayOfWeek()} and + * {@link Calendar#setMinimalDaysInFirstWeek(int) + * setMinimalDaysInFirstWeek()}. + * + *

    A week year is in sync with a + * {@code WEEK_OF_YEAR} cycle. All weeks between the first and last + * weeks (inclusive) have the same week year value. + * Therefore, the first and last days of a week year may have + * different calendar year values. + * + *

    For example, January 1, 1998 is a Thursday. If {@code + * getFirstDayOfWeek()} is {@code MONDAY} and {@code + * getMinimalDaysInFirstWeek()} is 4 (ISO 8601 standard compatible + * setting), then week 1 of 1998 starts on December 29, 1997, and ends + * on January 4, 1998. The week year is 1998 for the last three days + * of calendar year 1997. If, however, {@code getFirstDayOfWeek()} is + * {@code SUNDAY}, then week 1 of 1998 starts on January 4, 1998, and + * ends on January 10, 1998; the first three days of 1998 then are + * part of week 53 of 1997 and their week year is 1997. + * + *

    Week Of Month

    * *

    Values calculated for the WEEK_OF_MONTH field range from 0 * to 6. Week 1 of a month (the days with WEEK_OF_MONTH = @@ -124,7 +150,9 @@ import sun.util.calendar.ZoneInfo; * getMinimalDaysInFirstWeek() is changed to 3, then January 1 * through January 3 have a WEEK_OF_MONTH of 1. * - *

    The clear methods set calendar field(s) + *

    Default Fields Values

    + * + *

    The clear method sets calendar field(s) * undefined. GregorianCalendar uses the following * default value for each calendar field if its value is undefined. * @@ -555,7 +583,7 @@ public class GregorianCalendar extends Calendar { * in the default time zone with the default locale. */ public GregorianCalendar() { - this(TimeZone.getDefaultRef(), Locale.getDefault()); + this(TimeZone.getDefaultRef(), Locale.getDefault(Locale.Category.FORMAT)); setZoneShared(true); } @@ -566,7 +594,7 @@ public class GregorianCalendar extends Calendar { * @param zone the given time zone. */ public GregorianCalendar(TimeZone zone) { - this(zone, Locale.getDefault()); + this(zone, Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -1625,6 +1653,13 @@ public class GregorianCalendar extends Calendar { * is 29 because 2004 is a leap year, and if the date of this * instance is February 1, 2005, it's 28. * + *

    This method calculates the maximum value of {@link + * Calendar#WEEK_OF_YEAR WEEK_OF_YEAR} based on the {@link + * Calendar#YEAR YEAR} (calendar year) value, not the week year. Call {@link + * #getWeeksInWeekYear()} to get the maximum value of {@code + * WEEK_OF_YEAR} in the week year of this {@code GregorianCalendar}. + * * @param field the calendar field * @return the maximum of the given field for the time value of * this GregorianCalendar @@ -1742,8 +1777,13 @@ public class GregorianCalendar extends Calendar { if (gc == this) { gc = (GregorianCalendar) gc.clone(); } - gc.set(DAY_OF_YEAR, getActualMaximum(DAY_OF_YEAR)); + int maxDayOfYear = getActualMaximum(DAY_OF_YEAR); + gc.set(DAY_OF_YEAR, maxDayOfYear); value = gc.get(WEEK_OF_YEAR); + if (internalGet(YEAR) != gc.getWeekYear()) { + gc.set(DAY_OF_YEAR, maxDayOfYear - 7); + value = gc.get(WEEK_OF_YEAR); + } } break; @@ -1934,46 +1974,241 @@ public class GregorianCalendar extends Calendar { } } -////////////////////// -// Proposed public API -////////////////////// + /** + * Returns {@code true} indicating this {@code GregorianCalendar} + * supports week dates. + * + * @return {@code true} (always) + * @see #getWeekYear() + * @see #setWeekDate(int,int,int) + * @see #getWeeksInWeekYear() + * @since 1.7 + */ + @Override + public final boolean isWeekDateSupported() { + return true; + } /** - * Returns the year that corresponds to the WEEK_OF_YEAR field. - * This may be one year before or after the Gregorian or Julian year stored - * in the YEAR field. For example, January 1, 1999 is considered - * Friday of week 53 of 1998 (if minimal days in first week is - * 2 or less, and the first day of the week is Sunday). Given - * these same settings, the ISO year of January 1, 1999 is - * 1998. + * Returns the week year represented by this + * {@code GregorianCalendar}. The dates in the weeks between 1 and the + * maximum week number of the week year have the same week year value + * that may be one year before or after the {@link Calendar#YEAR YEAR} + * (calendar year) value. * - *

    This method calls {@link Calendar#complete} before - * calculating the week-based year. + *

    This method calls {@link Calendar#complete()} before + * calculating the week year. * - * @return the year corresponding to the WEEK_OF_YEAR field, which - * may be one year before or after the YEAR field. - * @see #YEAR - * @see #WEEK_OF_YEAR + * @return the week year represented by this {@code GregorianCalendar}. + * If the {@link Calendar#ERA ERA} value is {@link #BC}, the year is + * represented by 0 or a negative number: BC 1 is 0, BC 2 + * is -1, BC 3 is -2, and so on. + * @throws IllegalArgumentException + * if any of the calendar fields is invalid in non-lenient mode. + * @see #isWeekDateSupported() + * @see #getWeeksInWeekYear() + * @see Calendar#getFirstDayOfWeek() + * @see Calendar#getMinimalDaysInFirstWeek() + * @since 1.7 */ - /* - public int getWeekBasedYear() { - complete(); - // TODO: Below doesn't work for gregorian cutover... - int weekOfYear = internalGet(WEEK_OF_YEAR); - int year = internalGet(YEAR); - if (internalGet(MONTH) == Calendar.JANUARY) { - if (weekOfYear >= 52) { + @Override + public int getWeekYear() { + int year = get(YEAR); // implicitly calls complete() + if (internalGetEra() == BCE) { + year = 1 - year; + } + + // Fast path for the Gregorian calendar years that are never + // affected by the Julian-Gregorian transition + if (year > gregorianCutoverYear + 1) { + int weekOfYear = internalGet(WEEK_OF_YEAR); + if (internalGet(MONTH) == JANUARY) { + if (weekOfYear >= 52) { + --year; + } + } else { + if (weekOfYear == 1) { + ++year; + } + } + return year; + } + + // General (slow) path + int dayOfYear = internalGet(DAY_OF_YEAR); + int maxDayOfYear = getActualMaximum(DAY_OF_YEAR); + int minimalDays = getMinimalDaysInFirstWeek(); + + // Quickly check the possibility of year adjustments before + // cloning this GregorianCalendar. + if (dayOfYear > minimalDays && dayOfYear < (maxDayOfYear - 6)) { + return year; + } + + // Create a clone to work on the calculation + GregorianCalendar cal = (GregorianCalendar) clone(); + cal.setLenient(true); + // Use GMT so that intermediate date calculations won't + // affect the time of day fields. + cal.setTimeZone(TimeZone.getTimeZone("GMT")); + // Go to the first day of the year, which is usually January 1. + cal.set(DAY_OF_YEAR, 1); + cal.complete(); + + // Get the first day of the first day-of-week in the year. + int delta = getFirstDayOfWeek() - cal.get(DAY_OF_WEEK); + if (delta != 0) { + if (delta < 0) { + delta += 7; + } + cal.add(DAY_OF_YEAR, delta); + } + int minDayOfYear = cal.get(DAY_OF_YEAR); + if (dayOfYear < minDayOfYear) { + if (minDayOfYear <= minimalDays) { --year; } } else { - if (weekOfYear == 1) { - ++year; + cal.set(YEAR, year + 1); + cal.set(DAY_OF_YEAR, 1); + cal.complete(); + int del = getFirstDayOfWeek() - cal.get(DAY_OF_WEEK); + if (del != 0) { + if (del < 0) { + del += 7; + } + cal.add(DAY_OF_YEAR, del); + } + minDayOfYear = cal.get(DAY_OF_YEAR) - 1; + if (minDayOfYear == 0) { + minDayOfYear = 7; + } + if (minDayOfYear >= minimalDays) { + int days = maxDayOfYear - dayOfYear + 1; + if (days <= (7 - minDayOfYear)) { + ++year; + } } } return year; } - */ + /** + * Sets this {@code GregorianCalendar} to the date given by the + * date specifiers - {@code weekYear}, + * {@code weekOfYear}, and {@code dayOfWeek}. {@code weekOfYear} + * follows the {@code WEEK_OF_YEAR} + * numbering. The {@code dayOfWeek} value must be one of the + * {@link Calendar#DAY_OF_WEEK DAY_OF_WEEK} values: {@link + * Calendar#SUNDAY SUNDAY} to {@link Calendar#SATURDAY SATURDAY}. + * + *

    Note that the numeric day-of-week representation differs from + * the ISO 8601 standard, and that the {@code weekOfYear} + * numbering is compatible with the standard when {@code + * getFirstDayOfWeek()} is {@code MONDAY} and {@code + * getMinimalDaysInFirstWeek()} is 4. + * + *

    Unlike the {@code set} method, all of the calendar fields + * and the instant of time value are calculated upon return. + * + *

    If {@code weekOfYear} is out of the valid week-of-year + * range in {@code weekYear}, the {@code weekYear} + * and {@code weekOfYear} values are adjusted in lenient + * mode, or an {@code IllegalArgumentException} is thrown in + * non-lenient mode. + * + * @param weekYear the week year + * @param weekOfYear the week number based on {@code weekYear} + * @param dayOfWeek the day of week value: one of the constants + * for the {@link #DAY_OF_WEEK DAY_OF_WEEK} field: + * {@link Calendar#SUNDAY SUNDAY}, ..., + * {@link Calendar#SATURDAY SATURDAY}. + * @exception IllegalArgumentException + * if any of the given date specifiers is invalid, + * or if any of the calendar fields are inconsistent + * with the given date specifiers in non-lenient mode + * @see GregorianCalendar#isWeekDateSupported() + * @see Calendar#getFirstDayOfWeek() + * @see Calendar#getMinimalDaysInFirstWeek() + * @since 1.7 + */ + @Override + public void setWeekDate(int weekYear, int weekOfYear, int dayOfWeek) { + if (dayOfWeek < SUNDAY || dayOfWeek > SATURDAY) { + throw new IllegalArgumentException("invalid dayOfWeek: " + dayOfWeek); + } + + // To avoid changing the time of day fields by date + // calculations, use a clone with the GMT time zone. + GregorianCalendar gc = (GregorianCalendar) clone(); + gc.setLenient(true); + int era = gc.get(ERA); + gc.clear(); + gc.setTimeZone(TimeZone.getTimeZone("GMT")); + gc.set(ERA, era); + gc.set(YEAR, weekYear); + gc.set(WEEK_OF_YEAR, 1); + gc.set(DAY_OF_WEEK, getFirstDayOfWeek()); + int days = dayOfWeek - getFirstDayOfWeek(); + if (days < 0) { + days += 7; + } + days += 7 * (weekOfYear - 1); + if (days != 0) { + gc.add(DAY_OF_YEAR, days); + } else { + gc.complete(); + } + + if (!isLenient() && + (gc.getWeekYear() != weekYear + || gc.internalGet(WEEK_OF_YEAR) != weekOfYear + || gc.internalGet(DAY_OF_WEEK) != dayOfWeek)) { + throw new IllegalArgumentException(); + } + + set(ERA, gc.internalGet(ERA)); + set(YEAR, gc.internalGet(YEAR)); + set(MONTH, gc.internalGet(MONTH)); + set(DAY_OF_MONTH, gc.internalGet(DAY_OF_MONTH)); + + // to avoid throwing an IllegalArgumentException in + // non-lenient, set WEEK_OF_YEAR internally + internalSet(WEEK_OF_YEAR, weekOfYear); + complete(); + } + + /** + * Returns the number of weeks in the week year + * represented by this {@code GregorianCalendar}. + * + *

    For example, if this {@code GregorianCalendar}'s date is + * December 31, 2008 with the ISO + * 8601 compatible setting, this method will return 53 for the + * period: December 29, 2008 to January 3, 2010 while {@link + * #getActualMaximum(int) getActualMaximum(WEEK_OF_YEAR)} will return + * 52 for the period: December 31, 2007 to December 28, 2008. + * + * @return the number of weeks in the week year. + * @see Calendar#WEEK_OF_YEAR + * @see #getWeekYear() + * @see #getActualMaximum(int) + * @since 1.7 + */ + public int getWeeksInWeekYear() { + GregorianCalendar gc = getNormalizedCalendar(); + int weekYear = gc.getWeekYear(); + if (weekYear == gc.internalGet(YEAR)) { + return gc.getActualMaximum(WEEK_OF_YEAR); + } + + // Use the 2nd week for calculating the max of WEEK_OF_YEAR + if (gc == this) { + gc = (GregorianCalendar) gc.clone(); + } + gc.setWeekDate(weekYear, 2, internalGet(DAY_OF_WEEK)); + return gc.getActualMaximum(WEEK_OF_YEAR); + } ///////////////////////////// // Time => Fields computation @@ -2178,7 +2413,7 @@ public class GregorianCalendar extends Calendar { // If we are in the cutover year, we need some special handling. if (normalizedYear == cutoverYear) { // Need to take care of the "missing" days. - if (getCutoverCalendarSystem() == jcal) { + if (gregorianCutoverYearJulian <= gregorianCutoverYear) { // We need to find out where we are. The cutover // gap could even be more than one year. (One // year difference in ~48667 years.) @@ -2208,27 +2443,36 @@ public class GregorianCalendar extends Calendar { // December 31, which is not always true in // GregorianCalendar. long fixedDec31 = fixedDateJan1 - 1; - long prevJan1; + long prevJan1 = fixedDateJan1 - 365; if (normalizedYear > (cutoverYear + 1)) { - prevJan1 = fixedDateJan1 - 365; if (CalendarUtils.isGregorianLeapYear(normalizedYear - 1)) { --prevJan1; } + } else if (normalizedYear <= gregorianCutoverYearJulian) { + if (CalendarUtils.isJulianLeapYear(normalizedYear - 1)) { + --prevJan1; + } } else { BaseCalendar calForJan1 = calsys; - int prevYear = normalizedYear - 1; - if (prevYear == cutoverYear) { + //int prevYear = normalizedYear - 1; + int prevYear = getCalendarDate(fixedDec31).getNormalizedYear(); + if (prevYear == gregorianCutoverYear) { calForJan1 = getCutoverCalendarSystem(); - } - prevJan1 = calForJan1.getFixedDate(prevYear, - BaseCalendar.JANUARY, - 1, - null); - while (prevJan1 > fixedDec31) { - prevJan1 = getJulianCalendarSystem().getFixedDate(--prevYear, - BaseCalendar.JANUARY, - 1, - null); + if (calForJan1 == jcal) { + prevJan1 = calForJan1.getFixedDate(prevYear, + BaseCalendar.JANUARY, + 1, + null); + } else { + prevJan1 = gregorianCutoverDate; + calForJan1 = gcal; + } + } else if (prevYear <= gregorianCutoverYearJulian) { + calForJan1 = getJulianCalendarSystem(); + prevJan1 = calForJan1.getFixedDate(prevYear, + BaseCalendar.JANUARY, + 1, + null); } } weekOfYear = getWeekNumber(prevJan1, fixedDec31); @@ -2260,14 +2504,20 @@ public class GregorianCalendar extends Calendar { if (nextYear == gregorianCutoverYear) { calForJan1 = getCutoverCalendarSystem(); } - long nextJan1 = calForJan1.getFixedDate(nextYear, - BaseCalendar.JANUARY, - 1, - null); - if (nextJan1 < fixedDate) { + + long nextJan1; + if (nextYear > gregorianCutoverYear + || gregorianCutoverYearJulian == gregorianCutoverYear + || nextYear == gregorianCutoverYearJulian) { + nextJan1 = calForJan1.getFixedDate(nextYear, + BaseCalendar.JANUARY, + 1, + null); + } else { nextJan1 = gregorianCutoverDate; calForJan1 = gcal; } + long nextJan1st = calForJan1.getDayOfWeekDateOnOrBefore(nextJan1 + 6, getFirstDayOfWeek()); int ndays = (int)(nextJan1st - nextJan1); @@ -2409,10 +2659,24 @@ public class GregorianCalendar extends Calendar { } gfd = jfd; } else { - gfd = fixedDate + getFixedDate(gcal, year, fieldMask); jfd = fixedDate + getFixedDate(getJulianCalendarSystem(), year, fieldMask); + gfd = fixedDate + getFixedDate(gcal, year, fieldMask); } + // Now we have to determine which calendar date it is. + + // If the date is relative from the beginning of the year + // in the Julian calendar, then use jfd; + if (isFieldSet(fieldMask, DAY_OF_YEAR) || isFieldSet(fieldMask, WEEK_OF_YEAR)) { + if (gregorianCutoverYear == gregorianCutoverYearJulian) { + fixedDate = jfd; + break calculateFixedDate; + } else if (year == gregorianCutoverYear) { + fixedDate = gfd; + break calculateFixedDate; + } + } + if (gfd >= gregorianCutoverDate) { if (jfd >= gregorianCutoverDate) { fixedDate = gfd; @@ -2494,9 +2758,10 @@ public class GregorianCalendar extends Calendar { continue; } if (originalFields[field] != internalGet(field)) { + String s = originalFields[field] + " -> " + internalGet(field); // Restore the original field values System.arraycopy(originalFields, 0, fields, 0, fields.length); - throw new IllegalArgumentException(getFieldName(field)); + throw new IllegalArgumentException(getFieldName(field) + ": " + s); } } } @@ -2669,9 +2934,7 @@ public class GregorianCalendar extends Calendar { * method returns Gregorian. Otherwise, Julian. */ private BaseCalendar getCutoverCalendarSystem() { - CalendarDate date = getGregorianCutoverDate(); - if (date.getMonth() == BaseCalendar.JANUARY - && date.getDayOfMonth() == 1) { + if (gregorianCutoverYearJulian < gregorianCutoverYear) { return gcal; } return getJulianCalendarSystem(); diff --git a/src/share/classes/java/util/IllformedLocaleException.java b/src/share/classes/java/util/IllformedLocaleException.java new file mode 100644 index 0000000000000000000000000000000000000000..5c0c4da1314efb218737039fa72d765376e2f05f --- /dev/null +++ b/src/share/classes/java/util/IllformedLocaleException.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + ******************************************************************************* + * Copyright (C) 2009-2010, International Business Machines Corporation and * + * others. All Rights Reserved. * + ******************************************************************************* + */ + +package java.util; + +/** + * Thrown by methods in {@link Locale} and {@link Locale.Builder} to + * indicate that an argument is not a well-formed BCP 47 tag. + * + * @see Locale + * @since 1.7 + */ +public class IllformedLocaleException extends RuntimeException { + + private static final long serialVersionUID = -5245986824925681401L; + + private int _errIdx = -1; + + /** + * Constructs a new IllformedLocaleException with no + * detail message and -1 as the error index. + */ + public IllformedLocaleException() { + super(); + } + + /** + * Constructs a new IllformedLocaleException with the + * given message and -1 as the error index. + * + * @param message the message + */ + public IllformedLocaleException(String message) { + super(message); + } + + /** + * Constructs a new IllformedLocaleException with the + * given message and the error index. The error index is the approximate + * offset from the start of the ill-formed value to the point where the + * parse first detected an error. A negative error index value indicates + * either the error index is not applicable or unknown. + * + * @param message the message + * @param errorIndex the index + */ + public IllformedLocaleException(String message, int errorIndex) { + super(message + ((errorIndex < 0) ? "" : " [at index " + errorIndex + "]")); + _errIdx = errorIndex; + } + + /** + * Returns the index where the error was found. A negative value indicates + * either the error index is not applicable or unknown. + * + * @return the error index + */ + public int getErrorIndex() { + return _errIdx; + } +} diff --git a/src/share/classes/java/util/Locale.java b/src/share/classes/java/util/Locale.java index c3137f6dd1666871050e8698a8c48db4c64ac543..49b85866ed32dedec295cf541c5b467d24998ef8 100644 --- a/src/share/classes/java/util/Locale.java +++ b/src/share/classes/java/util/Locale.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -40,86 +40,240 @@ package java.util; -import java.io.*; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamField; +import java.io.Serializable; import java.security.AccessController; import java.text.MessageFormat; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; import java.util.spi.LocaleNameProvider; -import java.util.spi.LocaleServiceProvider; + import sun.security.action.GetPropertyAction; import sun.util.LocaleServiceProviderPool; +import sun.util.locale.AsciiUtil; +import sun.util.locale.BaseLocale; +import sun.util.locale.InternalLocaleBuilder; +import sun.util.locale.LanguageTag; +import sun.util.locale.LocaleExtensions; +import sun.util.locale.LocaleObjectCache; +import sun.util.locale.LocaleSyntaxException; +import sun.util.locale.ParseStatus; +import sun.util.locale.UnicodeLocaleExtension; import sun.util.resources.LocaleData; import sun.util.resources.OpenListResourceBundle; /** - * * A Locale object represents a specific geographical, political, * or cultural region. An operation that requires a Locale to perform * its task is called locale-sensitive and uses the Locale * to tailor information for the user. For example, displaying a number - * is a locale-sensitive operation--the number should be formatted - * according to the customs/conventions of the user's native country, + * is a locale-sensitive operation— the number should be formatted + * according to the customs and conventions of the user's native country, * region, or culture. * - *

    - * Create a Locale object using the constructors in this class: + *

    The Locale class implements identifiers + * interchangeable with BCP 47 (IETF BCP 47, "Tags for Identifying + * Languages"), with support for the LDML (UTS#35, "Unicode Locale + * Data Markup Language") BCP 47-compatible extensions for locale data + * exchange. + * + *

    A Locale object logically consists of the fields + * described below. + * + *

    + *
    language
    + * + *
    ISO 639 alpha-2 or alpha-3 language code, or registered + * language subtags up to 8 alpha letters (for future enhancements). + * When a language has both an alpha-2 code and an alpha-3 code, the + * alpha-2 code must be used. You can find a full list of valid + * language codes in the IANA Language Subtag Registry (search for + * "Type: language"). The language field is case insensitive, but + * Locale always canonicalizes to lower case.

    + * + *
    Well-formed language values have the form + * [a-zA-Z]{2,8}. Note that this is not the the full + * BCP47 language production, since it excludes extlang. They are + * not needed since modern three-letter language codes replace + * them.

    + * + *
    Example: "en" (English), "ja" (Japanese), "kok" (Konkani)

    + * + *
    script
    + * + *
    ISO 15924 alpha-4 script code. You can find a full list of + * valid script codes in the IANA Language Subtag Registry (search + * for "Type: script"). The script field is case insensitive, but + * Locale always canonicalizes to title case (the first + * letter is upper case and the rest of the letters are lower + * case).

    + * + *
    Well-formed script values have the form + * [a-zA-Z]{4}

    + * + *
    Example: "Latn" (Latin), "Cyrl" (Cyrillic)

    + * + *
    country (region)
    + * + *
    ISO 3166 alpha-2 country code or UN M.49 numeric-3 area code. + * You can find a full list of valid country and region codes in the + * IANA Language Subtag Registry (search for "Type: region"). The + * country (region) field is case insensitive, but + * Locale always canonicalizes to upper case.

    + * + *
    Well-formed country/region values have + * the form [a-zA-Z]{2} | [0-9]{3}

    + * + *
    Example: "US" (United States), "FR" (France), "029" + * (Caribbean)

    + * + *
    variant
    + * + *
    Any arbitrary value used to indicate a variation of a + * Locale. Where there are two or more variant values + * each indicating its own semantics, these values should be ordered + * by importance, with most important first, separated by + * underscore('_'). The variant field is case sensitive.

    + * + *
    Note: IETF BCP 47 places syntactic restrictions on variant + * subtags. Also BCP 47 subtags are strictly used to indicate + * additional variations that define a language or its dialects that + * are not covered by any combinations of language, script and + * region subtags. You can find a full list of valid variant codes + * in the IANA Language Subtag Registry (search for "Type: variant"). + * + *

    However, the variant field in Locale has + * historically been used for any kind of variation, not just + * language variations. For example, some supported variants + * available in Java SE Runtime Environments indicate alternative + * cultural behaviors such as calendar type or number script. In + * BCP 47 this kind of information, which does not identify the + * language, is supported by extension subtags or private use + * subtags.


    + * + *
    Well-formed variant values have the form SUBTAG + * (('_'|'-') SUBTAG)* where SUBTAG = + * [0-9][0-9a-zA-Z]{3} | [0-9a-zA-Z]{5,8}. (Note: BCP 47 only + * uses hyphen ('-') as a delimiter, this is more lenient).

    + * + *
    Example: "polyton" (Polytonic Greek), "POSIX"

    + * + *
    extensions
    + * + *
    A map from single character keys to string values, indicating + * extensions apart from language identification. The extensions in + * Locale implement the semantics and syntax of BCP 47 + * extension subtags and private use subtags. The extensions are + * case insensitive, but Locale canonicalizes all + * extension keys and values to lower case. Note that extensions + * cannot have empty values.

    + * + *
    Well-formed keys are single characters from the set + * [0-9a-zA-Z]. Well-formed values have the form + * SUBTAG ('-' SUBTAG)* where for the key 'x' + * SUBTAG = [0-9a-zA-Z]{1,8} and for other keys + * SUBTAG = [0-9a-zA-Z]{2,8} (that is, 'x' allows + * single-character subtags).

    + * + *
    Example: key="u"/value="ca-japanese" (Japanese Calendar), + * key="x"/value="java-1-7"
    + *
    + * + * Note: Although BCP 47 requires field values to be registered + * in the IANA Language Subtag Registry, the Locale class + * does not provide any validation features. The Builder + * only checks if an individual field satisfies the syntactic + * requirement (is well-formed), but does not validate the value + * itself. See {@link Builder} for details. + * + *

    Unicode locale/language extension

    + * + *

    UTS#35, "Unicode Locale Data Markup Language" defines optional + * attributes and keywords to override or refine the default behavior + * associated with a locale. A keyword is represented by a pair of + * key and type. For example, "nu-thai" indicates that Thai local + * digits (value:"thai") should be used for formatting numbers + * (key:"nu"). + * + *

    The keywords are mapped to a BCP 47 extension value using the + * extension key 'u' ({@link #UNICODE_LOCALE_EXTENSION}). The above + * example, "nu-thai", becomes the extension "u-nu-thai".code + * + *

    Thus, when a Locale object contains Unicode locale + * attributes and keywords, + * getExtension(UNICODE_LOCALE_EXTENSION) will return a + * String representing this information, for example, "nu-thai". The + * Locale class also provides {@link + * #getUnicodeLocaleAttributes}, {@link #getUnicodeLocaleKeys}, and + * {@link #getUnicodeLocaleType} which allow you to access Unicode + * locale attributes and key/type pairs directly. When represented as + * a string, the Unicode Locale Extension lists attributes + * alphabetically, followed by key/type sequences with keys listed + * alphabetically (the order of subtags comprising a key's type is + * fixed when the type is defined) + * + *

    A well-formed locale key has the form + * [0-9a-zA-Z]{2}. A well-formed locale type has the + * form "" | [0-9a-zA-Z]{3,8} ('-' [0-9a-zA-Z]{3,8})* (it + * can be empty, or a series of subtags 3-8 alphanums in length). A + * well-formed locale attribute has the form + * [0-9a-zA-Z]{3,8} (it is a single subtag with the same + * form as a locale type subtag). + * + *

    The Unicode locale extension specifies optional behavior in + * locale-sensitive services. Although the LDML specification defines + * various keys and values, actual locale-sensitive service + * implementations in a Java Runtime Environment might not support any + * particular Unicode locale attributes or key/type pairs. + * + *

    Creating a Locale

    + * + *

    There are several different ways to create a Locale + * object. + * + *

    Builder
    + * + *

    Using {@link Builder} you can construct a Locale object + * that conforms to BCP 47 syntax. + * + *

    Constructors
    + * + *

    The Locale class provides three constructors: *

    *
    - * Locale(String language)
    - * Locale(String language, String country)
    - * Locale(String language, String country, String variant)
    + *     {@link #Locale(String language)}
    + *     {@link #Locale(String language, String country)}
    + *     {@link #Locale(String language, String country, String variant)}
      * 
    *
    - * The language argument is a valid ISO Language Code. - * These codes are the lower-case, two-letter codes as defined by ISO-639. - * You can find a full list of these codes at a number of sites, such as: - *
    - * http://www.loc.gov/standards/iso639-2/php/English_list.php - * - *

    - * The country argument is a valid ISO Country Code. These - * codes are the upper-case, two-letter codes as defined by ISO-3166. - * You can find a full list of these codes at a number of sites, such as: - *
    - * http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html - * - *

    - * The variant argument is a vendor or browser-specific code. - * For example, use WIN for Windows, MAC for Macintosh, and POSIX for POSIX. - * Where there are two variants, separate them with an underscore, and - * put the most important one first. For example, a Traditional Spanish collation - * might construct a locale with parameters for language, country and variant as: - * "es", "ES", "Traditional_WIN". - * - *

    - * Because a Locale object is just an identifier for a region, - * no validity check is performed when you construct a Locale. - * If you want to see whether particular resources are available for the - * Locale you construct, you must query those resources. For - * example, ask the NumberFormat for the locales it supports - * using its getAvailableLocales method. - *
    Note: When you ask for a resource for a particular - * locale, you get back the best available match, not necessarily - * precisely what you asked for. For more information, look at - * {@link ResourceBundle}. - * - *

    - * The Locale class provides a number of convenient constants + * These constructors allow you to create a Locale object + * with language, country and variant, but you cannot specify + * script or extensions. + * + *

    Factory Methods
    + * + *

    The method {@link #forLanguageTag} creates a Locale + * object for a well-formed BCP 47 language tag. + * + *

    Locale Constants
    + * + *

    The Locale class provides a number of convenient constants * that you can use to create Locale objects for commonly used * locales. For example, the following creates a Locale object * for the United States: *

    *
    - * Locale.US
    + *     Locale.US
      * 
    *
    * - *

    - * Once you've created a Locale you can query it for information about - * itself. Use getCountry to get the ISO Country Code and - * getLanguage to get the ISO Language Code. You can - * use getDisplayCountry to get the + *

    Use of Locale

    + * + *

    Once you've created a Locale you can query it for information + * about itself. Use getCountry to get the country (or region) + * code and getLanguage to get the language code. + * You can use getDisplayCountry to get the * name of the country suitable for displaying to the user. Similarly, * you can use getDisplayLanguage to get the name of * the language suitable for displaying to the user. Interestingly, @@ -127,28 +281,27 @@ import sun.util.resources.OpenListResourceBundle; * and have two versions: one that uses the default locale and one * that uses the locale specified as an argument. * - *

    - * The Java Platform provides a number of classes that perform locale-sensitive + *

    The Java Platform provides a number of classes that perform locale-sensitive * operations. For example, the NumberFormat class formats - * numbers, currency, or percentages in a locale-sensitive manner. Classes - * such as NumberFormat have a number of convenience methods + * numbers, currency, and percentages in a locale-sensitive manner. Classes + * such as NumberFormat have several convenience methods * for creating a default object of that type. For example, the * NumberFormat class provides these three convenience methods * for creating a default NumberFormat object: *

    *
    - * NumberFormat.getInstance()
    - * NumberFormat.getCurrencyInstance()
    - * NumberFormat.getPercentInstance()
    + *     NumberFormat.getInstance()
    + *     NumberFormat.getCurrencyInstance()
    + *     NumberFormat.getPercentInstance()
      * 
    *
    - * These methods have two variants; one with an explicit locale - * and one without; the latter using the default locale. + * Each of these methods has two variants; one with an explicit locale + * and one without; the latter uses the default locale: *
    *
    - * NumberFormat.getInstance(myLocale)
    - * NumberFormat.getCurrencyInstance(myLocale)
    - * NumberFormat.getPercentInstance(myLocale)
    + *     NumberFormat.getInstance(myLocale)
    + *     NumberFormat.getCurrencyInstance(myLocale)
    + *     NumberFormat.getPercentInstance(myLocale)
      * 
    *
    * A Locale is the mechanism for identifying the kind of object @@ -156,75 +309,162 @@ import sun.util.resources.OpenListResourceBundle; * just a mechanism for identifying objects, * not a container for the objects themselves. * - * @see ResourceBundle - * @see java.text.Format - * @see java.text.NumberFormat - * @see java.text.Collator - * @author Mark Davis - * @since 1.1 + *

    Compatibility

    + * + *

    In order to maintain compatibility with existing usage, Locale's + * constructors retain their behavior prior to the Java Runtime + * Environment version 1.7. The same is largely true for the + * toString method. Thus Locale objects can continue to + * be used as they were. In particular, clients who parse the output + * of toString into language, country, and variant fields can continue + * to do so (although this is strongly discouraged), although the + * variant field will have additional information in it if script or + * extensions are present. + * + *

    In addition, BCP 47 imposes syntax restrictions that are not + * imposed by Locale's constructors. This means that conversions + * between some Locales and BCP 47 language tags cannot be made without + * losing information. Thus toLanguageTag cannot + * represent the state of locales whose language, country, or variant + * do not conform to BCP 47. + * + *

    Because of these issues, it is recommended that clients migrate + * away from constructing non-conforming locales and use the + * forLanguageTag and Locale.Builder APIs instead. + * Clients desiring a string representation of the complete locale can + * then always rely on toLanguageTag for this purpose. + * + *

    Special cases
    + * + *

    For compatibility reasons, two + * non-conforming locales are treated as special cases. These are + * ja_JP_JP and th_TH_TH. These are ill-formed + * in BCP 47 since the variants are too short. To ease migration to BCP 47, + * these are treated specially during construction. These two cases (and only + * these) cause a constructor to generate an extension, all other values behave + * exactly as they did prior to Java 7. + * + *

    Java has used ja_JP_JP to represent Japanese as used in + * Japan together with the Japanese Imperial calendar. This is now + * representable using a Unicode locale extension, by specifying the + * Unicode locale key ca (for "calendar") and type + * japanese. When the Locale constructor is called with the + * arguments "ja", "JP", "JP", the extension "u-ca-japanese" is + * automatically added. + * + *

    Java has used th_TH_TH to represent Thai as used in + * Thailand together with Thai digits. This is also now representable using + * a Unicode locale extension, by specifying the Unicode locale key + * nu (for "number") and value thai. When the Locale + * constructor is called with the arguments "th", "TH", "TH", the + * extension "u-nu-thai" is automatically added. + * + *

    Serialization
    + * + *

    During serialization, writeObject writes all fields to the output + * stream, including extensions. + * + *

    During deserialization, readResolve adds extensions as described + * in Special Cases, only + * for the two cases th_TH_TH and ja_JP_JP. + * + *

    Legacy language codes
    + * + *

    Locale's constructor has always converted three language codes to + * their earlier, obsoleted forms: he maps to iw, + * yi maps to ji, and id maps to + * in. This continues to be the case, in order to not break + * backwards compatibility. + * + *

    The APIs added in 1.7 map between the old and new language codes, + * maintaining the old codes internal to Locale (so that + * getLanguage and toString reflect the old + * code), but using the new codes in the BCP 47 language tag APIs (so + * that toLanguageTag reflects the new one). This + * preserves the equivalence between Locales no matter which code or + * API is used to construct them. Java's default resource bundle + * lookup mechanism also implements this mapping, so that resources + * can be named using either convention, see {@link ResourceBundle.Control}. + * + *

    Three-letter language/country(region) codes
    + * + *

    The Locale constructors have always specified that the language + * and the country param be two characters in length, although in + * practice they have accepted any length. The specification has now + * been relaxed to allow language codes of two to eight characters and + * country (region) codes of two to three characters, and in + * particular, three-letter language codes and three-digit region + * codes as specified in the IANA Language Subtag Registry. For + * compatibility, the implementation still does not impose a length + * constraint. + * + * @see Builder + * @see ResourceBundle + * @see java.text.Format + * @see java.text.NumberFormat + * @see java.text.Collator + * @author Mark Davis + * @since 1.1 */ - public final class Locale implements Cloneable, Serializable { - // cache to store singleton Locales - private final static ConcurrentHashMap cache = - new ConcurrentHashMap(32); + static private final Cache LOCALECACHE = new Cache(); /** Useful constant for language. */ - static public final Locale ENGLISH = createSingleton("en__", "en", ""); + static public final Locale ENGLISH = getInstance("en", "", ""); /** Useful constant for language. */ - static public final Locale FRENCH = createSingleton("fr__", "fr", ""); + static public final Locale FRENCH = getInstance("fr", "", ""); /** Useful constant for language. */ - static public final Locale GERMAN = createSingleton("de__", "de", ""); + static public final Locale GERMAN = getInstance("de", "", ""); /** Useful constant for language. */ - static public final Locale ITALIAN = createSingleton("it__", "it", ""); + static public final Locale ITALIAN = getInstance("it", "", ""); /** Useful constant for language. */ - static public final Locale JAPANESE = createSingleton("ja__", "ja", ""); + static public final Locale JAPANESE = getInstance("ja", "", ""); /** Useful constant for language. */ - static public final Locale KOREAN = createSingleton("ko__", "ko", ""); + static public final Locale KOREAN = getInstance("ko", "", ""); /** Useful constant for language. */ - static public final Locale CHINESE = createSingleton("zh__", "zh", ""); + static public final Locale CHINESE = getInstance("zh", "", ""); /** Useful constant for language. */ - static public final Locale SIMPLIFIED_CHINESE = createSingleton("zh_CN_", "zh", "CN"); + static public final Locale SIMPLIFIED_CHINESE = getInstance("zh", "CN", ""); /** Useful constant for language. */ - static public final Locale TRADITIONAL_CHINESE = createSingleton("zh_TW_", "zh", "TW"); + static public final Locale TRADITIONAL_CHINESE = getInstance("zh", "TW", ""); /** Useful constant for country. */ - static public final Locale FRANCE = createSingleton("fr_FR_", "fr", "FR"); + static public final Locale FRANCE = getInstance("fr", "FR", ""); /** Useful constant for country. */ - static public final Locale GERMANY = createSingleton("de_DE_", "de", "DE"); + static public final Locale GERMANY = getInstance("de", "DE", ""); /** Useful constant for country. */ - static public final Locale ITALY = createSingleton("it_IT_", "it", "IT"); + static public final Locale ITALY = getInstance("it", "IT", ""); /** Useful constant for country. */ - static public final Locale JAPAN = createSingleton("ja_JP_", "ja", "JP"); + static public final Locale JAPAN = getInstance("ja", "JP", ""); /** Useful constant for country. */ - static public final Locale KOREA = createSingleton("ko_KR_", "ko", "KR"); + static public final Locale KOREA = getInstance("ko", "KR", ""); /** Useful constant for country. */ @@ -240,19 +480,19 @@ public final class Locale implements Cloneable, Serializable { /** Useful constant for country. */ - static public final Locale UK = createSingleton("en_GB_", "en", "GB"); + static public final Locale UK = getInstance("en", "GB", ""); /** Useful constant for country. */ - static public final Locale US = createSingleton("en_US_", "en", "US"); + static public final Locale US = getInstance("en", "US", ""); /** Useful constant for country. */ - static public final Locale CANADA = createSingleton("en_CA_", "en", "CA"); + static public final Locale CANADA = getInstance("en", "CA", ""); /** Useful constant for country. */ - static public final Locale CANADA_FRENCH = createSingleton("fr_CA_", "fr", "CA"); + static public final Locale CANADA_FRENCH = getInstance("fr", "CA", ""); /** * Useful constant for the root locale. The root locale is the locale whose @@ -262,7 +502,25 @@ public final class Locale implements Cloneable, Serializable { * * @since 1.6 */ - static public final Locale ROOT = createSingleton("__", "", ""); + static public final Locale ROOT = getInstance("", "", ""); + + /** + * The key for the private use extension ('x'). + * + * @see #getExtension(char) + * @see Builder#setExtension(char, String) + * @since 1.7 + */ + static public final char PRIVATE_USE_EXTENSION = 'x'; + + /** + * The key for Unicode locale extension ('u'). + * + * @see #getExtension(char) + * @see Builder#setExtension(char, String) + * @since 1.7 + */ + static public final char UNICODE_LOCALE_EXTENSION = 'u'; /** serialization ID */ @@ -274,32 +532,67 @@ public final class Locale implements Cloneable, Serializable { private static final int DISPLAY_LANGUAGE = 0; private static final int DISPLAY_COUNTRY = 1; private static final int DISPLAY_VARIANT = 2; + private static final int DISPLAY_SCRIPT = 3; + + /** + * Private constructor used by getInstance method + */ + private Locale(BaseLocale baseLocale, LocaleExtensions extensions) { + _baseLocale = baseLocale; + _extensions = extensions; + } /** - * Construct a locale from language, country, variant. - * NOTE: ISO 639 is not a stable standard; some of the language codes it defines - * (specifically iw, ji, and in) have changed. This constructor accepts both the - * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other + * Construct a locale from language, country and variant. + * This constructor normalizes the language value to lowercase and + * the country value to uppercase. + *

    + * Note: + *

      + *
    • ISO 639 is not a stable standard; some of the language codes it defines + * (specifically "iw", "ji", and "in") have changed. This constructor accepts both the + * old codes ("iw", "ji", and "in") and the new codes ("he", "yi", and "id"), but all other * API on Locale will return only the OLD codes. - * @param language lowercase two-letter ISO-639 code. - * @param country uppercase two-letter ISO-3166 code. - * @param variant vendor and browser specific code. See class description. + *
    • For backward compatibility reasons, this constructor does not make + * any syntactic checks on the input. + *
    • The two cases ("ja", "JP", "JP") and ("th", "TH", "TH") are handled specially, + * see Special Cases for more information. + *
    + * + * @param language An ISO 639 alpha-2 or alpha-3 language code, or a language subtag + * up to 8 characters in length. See the Locale class description about + * valid language values. + * @param country An ISO 3166 alpha-2 country code or a UN M.49 numeric-3 area code. + * See the Locale class description about valid country values. + * @param variant Any arbitrary value used to indicate a variation of a Locale. + * See the Locale class description for the details. * @exception NullPointerException thrown if any argument is null. */ public Locale(String language, String country, String variant) { - this.language = convertOldISOCodes(language); - this.country = toUpperCase(country).intern(); - this.variant = variant.intern(); + _baseLocale = BaseLocale.getInstance(convertOldISOCodes(language), "", country, variant); + _extensions = getCompatibilityExtensions(language, "", country, variant); } /** - * Construct a locale from language, country. - * NOTE: ISO 639 is not a stable standard; some of the language codes it defines - * (specifically iw, ji, and in) have changed. This constructor accepts both the - * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other + * Construct a locale from language and country. + * This constructor normalizes the language value to lowercase and + * the country value to uppercase. + *

    + * Note: + *

      + *
    • ISO 639 is not a stable standard; some of the language codes it defines + * (specifically "iw", "ji", and "in") have changed. This constructor accepts both the + * old codes ("iw", "ji", and "in") and the new codes ("he", "yi", and "id"), but all other * API on Locale will return only the OLD codes. - * @param language lowercase two-letter ISO-639 code. - * @param country uppercase two-letter ISO-3166 code. + *
    • For backward compatibility reasons, this constructor does not make + * any syntactic checks on the input. + *
    + * + * @param language An ISO 639 alpha-2 or alpha-3 language code, or a language subtag + * up to 8 characters in length. See the Locale class description about + * valid language values. + * @param country An ISO 3166 alpha-2 country code or a UN M.49 numeric-3 area code. + * See the Locale class description about valid country values. * @exception NullPointerException thrown if either argument is null. */ public Locale(String language, String country) { @@ -308,11 +601,21 @@ public final class Locale implements Cloneable, Serializable { /** * Construct a locale from a language code. - * NOTE: ISO 639 is not a stable standard; some of the language codes it defines - * (specifically iw, ji, and in) have changed. This constructor accepts both the - * old codes (iw, ji, and in) and the new codes (he, yi, and id), but all other + * This constructor normalizes the language value to lowercase. + *

    + * Note: + *

      + *
    • ISO 639 is not a stable standard; some of the language codes it defines + * (specifically "iw", "ji", and "in") have changed. This constructor accepts both the + * old codes ("iw", "ji", and "in") and the new codes ("he", "yi", and "id"), but all other * API on Locale will return only the OLD codes. - * @param language lowercase two-letter ISO-639 code. + *
    • For backward compatibility reasons, this constructor does not make + * any syntactic checks on the input. + *
    + * + * @param language An ISO 639 alpha-2 or alpha-3 language code, or a language subtag + * up to 8 characters in length. See the Locale class description about + * valid language values. * @exception NullPointerException thrown if argument is null. * @since 1.4 */ @@ -320,32 +623,6 @@ public final class Locale implements Cloneable, Serializable { this(language, "", ""); } - /** - * Constructs a Locale using language - * and country. This constructor assumes that - * language and contry are interned and - * it is invoked by createSingleton only. (flag is just for - * avoiding the conflict with the public constructors. - */ - private Locale(String language, String country, boolean flag) { - this.language = language; - this.country = country; - this.variant = ""; - } - - /** - * Creates a Locale instance with the given - * language and counry and puts the - * instance under the given key in the cache. This - * method must be called only when initializing the Locale - * constants. - */ - private static Locale createSingleton(String key, String language, String country) { - Locale locale = new Locale(language, country, false); - cache.put(key, locale); - return locale; - } - /** * Returns a Locale constructed from the given * language, country and @@ -354,29 +631,70 @@ public final class Locale implements Cloneable, Serializable { * returned. Otherwise, a new Locale instance is * created and cached. * - * @param language lowercase two-letter ISO-639 code. - * @param country uppercase two-letter ISO-3166 code. + * @param language lowercase 2 to 8 language code. + * @param country uppercase two-letter ISO-3166 code and numric-3 UN M.49 area code. * @param variant vendor and browser specific code. See class description. * @return the Locale instance requested * @exception NullPointerException if any argument is null. */ static Locale getInstance(String language, String country, String variant) { - if (language== null || country == null || variant == null) { + return getInstance(language, "", country, variant, LocaleExtensions.EMPTY_EXTENSIONS); + } + + static Locale getInstance(String language, String script, String country, + String variant, LocaleExtensions extensions) { + if (language== null || script == null || country == null || variant == null) { throw new NullPointerException(); } - StringBuilder sb = new StringBuilder(); - sb.append(language).append('_').append(country).append('_').append(variant); - String key = sb.toString(); - Locale locale = cache.get(key); - if (locale == null) { - locale = new Locale(language, country, variant); - Locale l = cache.putIfAbsent(key, locale); - if (l != null) { - locale = l; + if (extensions == null) { + extensions = LocaleExtensions.EMPTY_EXTENSIONS; + } + + if (extensions.equals(LocaleExtensions.EMPTY_EXTENSIONS)) { + extensions = getCompatibilityExtensions(language, script, country, variant); + } + + BaseLocale baseloc = BaseLocale.getInstance(language, script, country, variant); + return getInstance(baseloc, extensions); + } + + static Locale getInstance(BaseLocale baseloc, LocaleExtensions extensions) { + LocaleKey key = new LocaleKey(baseloc, extensions); + return LOCALECACHE.get(key); + } + + private static class Cache extends LocaleObjectCache { + public Cache() { + } + protected Locale createObject(LocaleKey key) { + return new Locale(key._base, key._exts); + } + } + + private static class LocaleKey { + private BaseLocale _base; + private LocaleExtensions _exts; + + private LocaleKey(BaseLocale baseLocale, LocaleExtensions extensions) { + _base = baseLocale; + _exts = extensions; + } + + public boolean equals(Object obj) { + if (this == obj) { + return true; } + if (!(obj instanceof LocaleKey)) { + return false; + } + LocaleKey other = (LocaleKey)obj; + return _base.equals(other._base) && _exts.equals(other._exts); + } + + public int hashCode() { + return _base.hashCode() ^ _exts.hashCode(); } - return locale; } /** @@ -395,31 +713,103 @@ public final class Locale implements Cloneable, Serializable { // do not synchronize this method - see 4071298 // it's OK if more than one default locale happens to be created if (defaultLocale == null) { - String language, region, country, variant; + initDefault(); + } + return defaultLocale; + } + + /** + * Gets the current value of the default locale for the specified Category + * for this instance of the Java Virtual Machine. + *

    + * The Java Virtual Machine sets the default locale during startup based + * on the host environment. It is used by many locale-sensitive methods + * if no locale is explicitly specified. It can be changed using the + * setDefault(Locale.Category, Locale) method. + * + * @param category - the specified category to get the default locale + * @throws NullPointerException - if category is null + * @return the default locale for the specified Category for this instance + * of the Java Virtual Machine + * @see #setDefault(Locale.Category, Locale) + * @since 1.7 + */ + public static Locale getDefault(Locale.Category category) { + // do not synchronize this method - see 4071298 + // it's OK if more than one default locale happens to be created + switch (category) { + case DISPLAY: + if (defaultDisplayLocale == null) { + initDefault(category); + } + return defaultDisplayLocale; + case FORMAT: + if (defaultFormatLocale == null) { + initDefault(category); + } + return defaultFormatLocale; + default: + assert false: "Unknown Category"; + } + return getDefault(); + } + + private static void initDefault() { + String language, region, country, variant; + language = AccessController.doPrivileged( + new GetPropertyAction("user.language", "en")); + // for compatibility, check for old user.region property + region = AccessController.doPrivileged( + new GetPropertyAction("user.region")); + if (region != null) { + // region can be of form country, country_variant, or _variant + int i = region.indexOf('_'); + if (i >= 0) { + country = region.substring(0, i); + variant = region.substring(i + 1); + } else { + country = region; + variant = ""; + } + } else { + country = AccessController.doPrivileged( + new GetPropertyAction("user.country", "")); + variant = AccessController.doPrivileged( + new GetPropertyAction("user.variant", "")); + } + defaultLocale = getInstance(language, country, variant); + } + + private static void initDefault(Locale.Category category) { + String language, region, country, variant; + switch (category) { + case DISPLAY: language = AccessController.doPrivileged( - new GetPropertyAction("user.language", "en")); - // for compatibility, check for old user.region property - region = AccessController.doPrivileged( - new GetPropertyAction("user.region")); - if (region != null) { - // region can be of form country, country_variant, or _variant - int i = region.indexOf('_'); - if (i >= 0) { - country = region.substring(0, i); - variant = region.substring(i + 1); - } else { - country = region; - variant = ""; - } + new GetPropertyAction("user.language.display", "")); + if ("".equals(language)) { + defaultDisplayLocale = getDefault(); } else { country = AccessController.doPrivileged( - new GetPropertyAction("user.country", "")); + new GetPropertyAction("user.country.display", "")); variant = AccessController.doPrivileged( - new GetPropertyAction("user.variant", "")); + new GetPropertyAction("user.variant.display", "")); + defaultDisplayLocale = getInstance(language, country, variant); } - defaultLocale = getInstance(language, country, variant); + break; + case FORMAT: + language = AccessController.doPrivileged( + new GetPropertyAction("user.language.format", "")); + if ("".equals(language)) { + defaultFormatLocale = getDefault(); + } else { + country = AccessController.doPrivileged( + new GetPropertyAction("user.country.format", "")); + variant = AccessController.doPrivileged( + new GetPropertyAction("user.variant.format", "")); + defaultFormatLocale = getInstance(language, country, variant); + } + break; } - return defaultLocale; } /** @@ -438,6 +828,9 @@ public final class Locale implements Cloneable, Serializable { * of functionality, this method should only be used if the caller * is prepared to reinitialize locale-sensitive code running * within the same Java Virtual Machine. + *

    + * By setting the default locale with this method, all of the default + * locales for each Category are also set to the specified default locale. * * @throws SecurityException * if a security manager exists and its @@ -448,13 +841,59 @@ public final class Locale implements Cloneable, Serializable { * @see java.util.PropertyPermission */ public static synchronized void setDefault(Locale newLocale) { + setDefault(Category.DISPLAY, newLocale); + setDefault(Category.FORMAT, newLocale); + defaultLocale = newLocale; + } + + /** + * Sets the default locale for the specified Category for this instance + * of the Java Virtual Machine. This does not affect the host locale. + *

    + * If there is a security manager, its checkPermission method is called + * with a PropertyPermission("user.language", "write") permission before + * the default locale is changed. + *

    + * The Java Virtual Machine sets the default locale during startup based + * on the host environment. It is used by many locale-sensitive methods + * if no locale is explicitly specified. + *

    + * Since changing the default locale may affect many different areas of + * functionality, this method should only be used if the caller is + * prepared to reinitialize locale-sensitive code running within the + * same Java Virtual Machine. + *

    + * + * @param category - the specified category to set the default locale + * @param newLocale - the new default locale + * @throws SecurityException - if a security manager exists and its + * checkPermission method doesn't allow the operation. + * @throws NullPointerException - if category and/or newLocale is null + * @see SecurityManager.checkPermission(java.security.Permission) + * @see PropertyPermission + * @see #getDefault(Locale.Category) + * @since 1.7 + */ + public static synchronized void setDefault(Locale.Category category, + Locale newLocale) { + if (category == null) + throw new NullPointerException("Category cannot be NULL"); if (newLocale == null) throw new NullPointerException("Can't set default locale to NULL"); SecurityManager sm = System.getSecurityManager(); if (sm != null) sm.checkPermission(new PropertyPermission ("user.language", "write")); - defaultLocale = newLocale; + switch (category) { + case DISPLAY: + defaultDisplayLocale = newLocale; + break; + case FORMAT: + defaultFormatLocale = newLocale; + break; + default: + assert false: "Unknown Category"; + } } /** @@ -474,6 +913,11 @@ public final class Locale implements Cloneable, Serializable { /** * Returns a list of all 2-letter country codes defined in ISO 3166. * Can be used to create Locales. + *

    + * Note: The Locale class also supports other codes for + * country (region), such as 3-letter numeric UN M.49 area codes. + * Therefore, the list returned by this method does not contain ALL valid + * codes that can be used to create Locales. */ public static String[] getISOCountries() { if (isoCountries == null) { @@ -487,9 +931,16 @@ public final class Locale implements Cloneable, Serializable { /** * Returns a list of all 2-letter language codes defined in ISO 639. * Can be used to create Locales. - * [NOTE: ISO 639 is not a stable standard-- some languages' codes have changed. + *

    + * Note: + *

      + *
    • ISO 639 is not a stable standard— some languages' codes have changed. * The list this function returns includes both the new and the old codes for the - * languages whose codes have changed.] + * languages whose codes have changed. + *
    • The Locale class also supports language codes up to + * 8 characters in length. Therefore, the list returned by this method does + * not contain ALL valid codes that can be used to create Locales. + *
    */ public static String[] getISOLanguages() { if (isoLanguages == null) { @@ -510,100 +961,516 @@ public final class Locale implements Cloneable, Serializable { } /** - * Returns the language code for this locale, which will either be the empty string - * or a lowercase ISO 639 code. - *

    NOTE: ISO 639 is not a stable standard-- some languages' codes have changed. + * Returns the language code of this Locale. + * + *

    Note: ISO 639 is not a stable standard— some languages' codes have changed. * Locale's constructor recognizes both the new and the old codes for the languages * whose codes have changed, but this function always returns the old code. If you - * want to check for a specific language whose code has changed, don't do

    -     * if (locale.getLanguage().equals("he"))
    +     * want to check for a specific language whose code has changed, don't do
    +     * 
    +     * if (locale.getLanguage().equals("he")) // BAD!
    +     *    ...
    +     * 
    + * Instead, do + *
    +     * if (locale.getLanguage().equals(new Locale("he").getLanguage()))
          *    ...
    -     * 
    Instead, do
    -     * if (locale.getLanguage().equals(new Locale("he", "", "").getLanguage()))
    -     *    ...
    + *
    + * @return The language code, or the empty string if none is defined. * @see #getDisplayLanguage */ public String getLanguage() { - return language; + return _baseLocale.getLanguage(); } /** - * Returns the country/region code for this locale, which will - * either be the empty string or an uppercase ISO 3166 2-letter code. + * Returns the script for this locale, which should + * either be the empty string or an ISO 15924 4-letter script + * code. The first letter is uppercase and the rest are + * lowercase, for example, 'Latn', 'Cyrl'. + * + * @return The script code, or the empty string if none is defined. + * @see #getDisplayScript + * @since 1.7 + */ + public String getScript() { + return _baseLocale.getScript(); + } + + /** + * Returns the country/region code for this locale, which should + * either be the empty string, an uppercase ISO 3166 2-letter code, + * or a UN M.49 3-digit code. + * + * @return The country/region code, or the empty string if none is defined. * @see #getDisplayCountry */ public String getCountry() { - return country; + return _baseLocale.getRegion(); } /** * Returns the variant code for this locale. + * + * @return The variant code, or the empty string if none is defined. * @see #getDisplayVariant */ public String getVariant() { - return variant; + return _baseLocale.getVariant(); + } + + /** + * Returns the extension (or private use) value associated with + * the specified key, or null if there is no extension + * associated with the key. To be well-formed, the key must be one + * of [0-9A-Za-z]. Keys are case-insensitive, so + * for example 'z' and 'Z' represent the same extension. + * + * @param key the extension key + * @return The extension, or null if this locale defines no + * extension for the specified key. + * @throws IllegalArgumentException if key is not well-formed + * @see #PRIVATE_USE_EXTENSION + * @see #UNICODE_LOCALE_EXTENSION + * @since 1.7 + */ + public String getExtension(char key) { + if (!LocaleExtensions.isValidKey(key)) { + throw new IllegalArgumentException("Ill-formed extension key: " + key); + } + return _extensions.getExtensionValue(key); + } + + /** + * Returns the set of extension keys associated with this locale, or the + * empty set if it has no extensions. The returned set is unmodifiable. + * The keys will all be lower-case. + * + * @return The set of extension keys, or the empty set if this locale has + * no extensions. + * @since 1.7 + */ + public Set getExtensionKeys() { + return _extensions.getKeys(); + } + + /** + * Returns the set of unicode locale attributes associated with + * this locale, or the empty set if it has no attributes. The + * returned set is unmodifiable. + * + * @return The set of attributes. + * @since 1.7 + */ + public Set getUnicodeLocaleAttributes() { + return _extensions.getUnicodeLocaleAttributes(); + } + + /** + * Returns the Unicode locale type associated with the specified Unicode locale key + * for this locale. Returns the empty string for keys that are defined with no type. + * Returns null if the key is not defined. Keys are case-insensitive. The key must + * be two alphanumeric characters ([0-9a-zA-Z]), or an IllegalArgumentException is + * thrown. + * + * @param key the Unicode locale key + * @return The Unicode locale type associated with the key, or null if the + * locale does not define the key. + * @throws IllegalArgumentException if the key is not well-formed + * @throws NullPointerException if key is null + * @since 1.7 + */ + public String getUnicodeLocaleType(String key) { + if (!UnicodeLocaleExtension.isKey(key)) { + throw new IllegalArgumentException("Ill-formed Unicode locale key: " + key); + } + return _extensions.getUnicodeLocaleType(key); + } + + /** + * Returns the set of Unicode locale keys defined by this locale, or the empty set if + * this locale has none. The returned set is immutable. Keys are all lower case. + * + * @return The set of Unicode locale keys, or the empty set if this locale has + * no Unicode locale keywords. + * @since 1.7 + */ + public Set getUnicodeLocaleKeys() { + return _extensions.getUnicodeLocaleKeys(); } /** - * Getter for the programmatic name of the entire locale, - * with the language, country and variant separated by underbars. - * Language is always lower case, and country is always upper case. - * If the language is missing, the string will begin with an underbar. - * If both the language and country fields are missing, this function - * will return the empty string, even if the variant field is filled in - * (you can't have a locale with just a variant-- the variant must accompany - * a valid language or country code). - * Examples: "en", "de_DE", "_GB", "en_US_WIN", "de__POSIX", "fr__MAC" + * Package locale method returning the Locale's BaseLocale, + * used by ResourceBundle + * @return base locale of this Locale + */ + BaseLocale getBaseLocale() { + return _baseLocale; + } + + /** + * Package local method returning the Locale's LocaleExtensions, + * used by ResourceBundle + * @return locale exnteions of this Locale + */ + LocaleExtensions getLocaleExtensions() { + return _extensions; + } + + /** + * Returns a string representation of this Locale + * object, consisting of language, country, variant, script, + * and extensions as below: + *

    + * language + "_" + country + "_" + (variant + "_#" | "#") + script + "-" + extensions + *
    + * + * Language is always lower case, country is always upper case, script is always title + * case, and extensions are always lower case. Extensions and private use subtags + * will be in canonical order as explained in {@link #toLanguageTag}. + * + *

    When the locale has neither script nor extensions, the result is the same as in + * Java 6 and prior. + * + *

    If both the language and country fields are missing, this function will return + * the empty string, even if the variant, script, or extensions field is present (you + * can't have a locale with just a variant, the variant must accompany a well-formed + * language or country code). + * + *

    If script or extensions are present and variant is missing, no underscore is + * added before the "#". + * + *

    This behavior is designed to support debugging and to be compatible with + * previous uses of toString that expected language, country, and variant + * fields only. To represent a Locale as a String for interchange purposes, use + * {@link #toLanguageTag}. + * + *

    Examples:

      + *
    • en + *
    • de_DE + *
    • _GB + *
    • en_US_WIN + *
    • de__POSIX + *
    • zh_CN_#Hans + *
    • zh_TW_#Hant-x-java + *
    • th_TH_TH_#u-nu-thai
    + * + * @return A string representation of the Locale, for debugging. * @see #getDisplayName + * @see #toLanguageTag */ public final String toString() { - boolean l = language.length() != 0; - boolean c = country.length() != 0; - boolean v = variant.length() != 0; - StringBuilder result = new StringBuilder(language); - if (c||(l&&v)) { - result.append('_').append(country); // This may just append '_' + boolean l = (_baseLocale.getLanguage().length() != 0); + boolean s = (_baseLocale.getScript().length() != 0); + boolean r = (_baseLocale.getRegion().length() != 0); + boolean v = (_baseLocale.getVariant().length() != 0); + boolean e = (_extensions.getID().length() != 0); + + StringBuilder result = new StringBuilder(_baseLocale.getLanguage()); + if (r || (l && v)) { + result.append('_') + .append(_baseLocale.getRegion()); // This may just append '_' } - if (v&&(l||c)) { - result.append('_').append(variant); + if (v && (l || r)) { + result.append('_') + .append(_baseLocale.getVariant()); + } + + if (s && (l || r)) { + result.append("_#") + .append(_baseLocale.getScript()); + } + + if (e && (l || r)) { + result.append('_'); + if (!s) { + result.append('#'); + } + result.append(_extensions.getID()); } + return result.toString(); } /** - * Returns a three-letter abbreviation for this locale's language. If the locale - * doesn't specify a language, this will be the empty string. Otherwise, this will - * be a lowercase ISO 639-2/T language code. - * The ISO 639-2 language codes can be found on-line at - * - * http://www.loc.gov/standards/iso639-2/englangn.html. - * @exception MissingResourceException Throws MissingResourceException if the + * Returns a well-formed IETF BCP 47 language tag representing + * this locale. + * + *

    If this Locale has a language, country, or + * variant that does not satisfy the IETF BCP 47 language tag + * syntax requirements, this method handles these fields as + * described below: + * + *

    Language: If language is empty, or not well-formed (for example "a" or + * "e2"), it will be emitted as "und" (Undetermined). + * + *

    Country: If country is not well-formed (for example "12" or "USA"), + * it will be omitted. + * + *

    Variant: If variant is well-formed, each sub-segment + * (delimited by '-' or '_') is emitted as a subtag. Otherwise: + *

      + * + *
    • if all sub-segments match [0-9a-zA-Z]{1,8} + * (for example "WIN" or "Oracle_JDK_Standard_Edition"), the first + * ill-formed sub-segment and all following will be appended to + * the private use subtag. The first appended subtag will be + * "lvariant", followed by the sub-segments in order, separated by + * hyphen. For example, "x-lvariant-WIN", + * "Oracle-x-lvariant-JDK-Standard-Edition". + * + *
    • if any sub-segment does not match + * [0-9a-zA-Z]{1,8}, the variant will be truncated + * and the problematic sub-segment and all following sub-segments + * will be omitted. If the remainder is non-empty, it will be + * emitted as a private use subtag as above (even if the remainder + * turns out to be well-formed). For example, + * "Solaris_isjustthecoolestthing" is emitted as + * "x-lvariant-Solaris", not as "solaris".
    + * + *

    Compatibility special cases:

      + * + *
    • The language codes "iw", "ji", and "in" are handled + * specially. Java uses these deprecated codes for compatibility + * reasons. The toLanguageTag method converts these + * three codes (and only these three) to "he", "yi", and "id" + * respectively. + * + *
    • A locale with language "no", country "NO", and variant + * "NY", representing Norwegian Nynorsk, will be represented as + * having language "nn", country "NO", and empty variant. This is + * because some JVMs used the deprecated form to represent the + * user's default locale, and for compatibility reasons that Take a has + * not been changed.
    + * + *

    Note: Although the language tag created by this + * method is well-formed (satisfies the syntax requirements + * defined by the IETF BCP 47 specification), it is not + * necessarily a valid BCP 47 language tag. For example, + *

    +     *   new Locale("xx", "YY").toLanguageTag();
    + * + * will return "xx-YY", but the language subtag "xx" and the + * region subtag "YY" are invalid because they are not registered + * in the IANA Language Subtag Registry. + * + * @return a BCP47 language tag representing the locale + * @see #forLanguageTag(String) + * @since 1.7 + */ + public String toLanguageTag() { + LanguageTag tag = LanguageTag.parseLocale(_baseLocale, _extensions); + StringBuilder buf = new StringBuilder(); + + String subtag = tag.getLanguage(); + buf.append(LanguageTag.canonicalizeLanguage(subtag)); + + subtag = tag.getScript(); + if (subtag.length() > 0) { + buf.append(LanguageTag.SEP); + buf.append(LanguageTag.canonicalizeScript(subtag)); + } + + subtag = tag.getRegion(); + if (subtag.length() > 0) { + buf.append(LanguageTag.SEP); + buf.append(LanguageTag.canonicalizeRegion(subtag)); + } + + Listsubtags = tag.getVariants(); + for (String s : subtags) { + buf.append(LanguageTag.SEP); + // preserve casing + buf.append(s); + } + + subtags = tag.getExtensions(); + for (String s : subtags) { + buf.append(LanguageTag.SEP); + buf.append(LanguageTag.canonicalizeExtension(s)); + } + + subtag = tag.getPrivateuse(); + if (subtag.length() > 0) { + buf.append(LanguageTag.SEP).append(LanguageTag.PRIVATEUSE).append(LanguageTag.SEP); + // preserve casing + buf.append(subtag); + } + + return buf.toString(); + } + + /** + * Returns a locale for the specified IETF BCP 47 language tag string. + * + *

    If the specified language tag contains any ill-formed subtags, + * the first such subtag and all following subtags are ignored. Compare + * to {@link Locale.Builder#setLanguageTag} which throws an exception + * in this case. + * + *

    The following conversions are performed:

      + * + *
    • The language code "und" is mapped to language "". + * + *
    • The language codes "he", "yi", and "id" are mapped to "iw", + * "ji", and "in" respectively. (This is the same canonicalization + * that's done in Locale's constructors.) + * + *
    • The portion of a private use subtag prefixed by "lvariant", + * if any, is removed and appended to the variant field in the + * result locale (without case normalization). If it is then + * empty, the private use subtag is discarded: + * + *
      +     *     Locale loc;
      +     *     loc = Locale.forLanguageTag("en-US-x-lvariant-POSIX);
      +     *     loc.getVariant(); // returns "POSIX"
      +     *     loc.getExtension('x'); // returns null
      +     *
      +     *     loc = Locale.forLanguageTag("de-POSIX-x-URP-lvariant-Abc-Def");
      +     *     loc.getVariant(); // returns "POSIX_Abc_Def"
      +     *     loc.getExtension('x'); // returns "urp"
      +     * 
      + * + *
    • When the languageTag argument contains an extlang subtag, + * the first such subtag is used as the language, and the primary + * language subtag and other extlang subtags are ignored: + * + *
      +     *     Locale.forLanguageTag("ar-aao").getLanguage(); // returns "aao"
      +     *     Locale.forLanguageTag("en-abc-def-us").toString(); // returns "abc_US"
      +     * 
      + * + *
    • Case is normalized except for variant tags, which are left + * unchanged. Language is normalized to lower case, script to + * title case, country to upper case, and extensions to lower + * case. + * + *
    • If, after processing, the locale would exactly match either + * ja_JP_JP or th_TH_TH with no extensions, the appropriate + * extensions are added as though the constructor had been called: + * + *
      +     *    Locale.forLanguageTag("ja-JP-x-lvariant-JP).toLanguageTag();
      +     *    // returns ja-JP-u-ca-japanese-x-lvariant-JP
      +     *    Locale.forLanguageTag("th-TH-x-lvariant-TH).toLanguageTag();
      +     *    // returns th-TH-u-nu-thai-x-lvariant-TH
      +     * 
    + * + *

    This implements the 'Language-Tag' production of BCP47, and + * so supports grandfathered (regular and irregular) as well as + * private use language tags. Stand alone private use tags are + * represented as empty language and extension 'x-whatever', + * and grandfathered tags are converted to their canonical replacements + * where they exist. + * + *

    Grandfathered tags with canonical replacements are as follows: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    grandfathered tag modern replacement
    art-lojban jbo
    i-ami ami
    i-bnn bnn
    i-hak hak
    i-klingon tlh
    i-lux lb
    i-navajo nv
    i-pwn pwn
    i-tao tao
    i-tay tay
    i-tsu tsu
    no-bok nb
    no-nyn nn
    sgn-BE-FR sfb
    sgn-BE-NL vgt
    sgn-CH-DE sgg
    zh-guoyu cmn
    zh-hakka hak
    zh-min-nan nan
    zh-xiang hsn
    + * + *

    Grandfathered tags with no modern replacement will be + * converted as follows: + * + * + * + * + * + * + * + * + * + * + * + *
    grandfathered tag converts to
    cel-gaulish xtg-x-cel-gaulish
    en-GB-oed en-GB-x-oed
    i-default en-x-i-default
    i-enochian und-x-i-enochian
    i-mingo see-x-i-mingo
    zh-min nan-x-zh-min
    + * + *

    For a list of all grandfathered tags, see the + * IANA Language Subtag Registry (search for "Type: grandfathered"). + * + *

    Note: there is no guarantee that toLanguageTag + * and forLanguageTag will round-trip. + * + * @param languageTag the language tag + * @return The locale that best represents the language tag. + * @throws NullPointerException if languageTag is null + * @see #toLanguageTag() + * @see java.util.Locale.Builder#setLanguageTag(String) + * @since 1.7 + */ + public static Locale forLanguageTag(String languageTag) { + LanguageTag tag = LanguageTag.parse(languageTag, null); + InternalLocaleBuilder bldr = new InternalLocaleBuilder(); + bldr.setLanguageTag(tag); + return getInstance(bldr.getBaseLocale(), bldr.getLocaleExtensions()); + } + + /** + * Returns a three-letter abbreviation of this locale's language. + * If the language matches an ISO 639-1 two-letter code, the + * corresponding ISO 639-2/T three-letter lowercase code is + * returned. The ISO 639-2 language codes can be found on-line, + * see "Codes for the Representation of Names of Languages Part 2: + * Alpha-3 Code". If the locale specifies a three-letter + * language, the language is returned as is. If the locale does + * not specify a language the empty string is returned. + * + * @return A three-letter abbreviation of this locale's language. + * @exception MissingResourceException Throws MissingResourceException if * three-letter language abbreviation is not available for this locale. */ public String getISO3Language() throws MissingResourceException { - String language3 = getISO3Code(language, LocaleISOData.isoLanguageTable); + String language3 = getISO3Code(_baseLocale.getLanguage(), LocaleISOData.isoLanguageTable); if (language3 == null) { throw new MissingResourceException("Couldn't find 3-letter language code for " - + language, "FormatData_" + toString(), "ShortLanguage"); + + _baseLocale.getLanguage(), "FormatData_" + toString(), "ShortLanguage"); } return language3; } /** - * Returns a three-letter abbreviation for this locale's country. If the locale - * doesn't specify a country, this will be the empty string. Otherwise, this will - * be an uppercase ISO 3166 3-letter country code. - * The ISO 3166-2 country codes can be found on-line at - * - * http://www.davros.org/misc/iso3166.txt. + * Returns a three-letter abbreviation for this locale's country. + * If the country matches an ISO 3166-1 alpha-2 code, the + * corresponding ISO 3166-1 alpha-3 uppercase code is returned. + * If the locale doesn't specify a country, this will be the empty + * string. + * + *

    The ISO 3166-1 codes can be found on-line. + * + * @return A three-letter abbreviation of this locale's country. * @exception MissingResourceException Throws MissingResourceException if the * three-letter country abbreviation is not available for this locale. */ public String getISO3Country() throws MissingResourceException { - String country3 = getISO3Code(country, LocaleISOData.isoCountryTable); + String country3 = getISO3Code(_baseLocale.getRegion(), LocaleISOData.isoCountryTable); if (country3 == null) { throw new MissingResourceException("Couldn't find 3-letter country code for " - + country, "FormatData_" + toString(), "ShortCountry"); + + _baseLocale.getRegion(), "FormatData_" + toString(), "ShortCountry"); } return country3; } @@ -642,7 +1509,7 @@ public final class Locale implements Cloneable, Serializable { * value. If the locale doesn't specify a language, this function returns the empty string. */ public final String getDisplayLanguage() { - return getDisplayLanguage(getDefault()); + return getDisplayLanguage(getDefault(Category.DISPLAY)); } /** @@ -661,7 +1528,33 @@ public final class Locale implements Cloneable, Serializable { * @exception NullPointerException if inLocale is null */ public String getDisplayLanguage(Locale inLocale) { - return getDisplayString(language, inLocale, DISPLAY_LANGUAGE); + return getDisplayString(_baseLocale.getLanguage(), inLocale, DISPLAY_LANGUAGE); + } + + /** + * Returns a name for the the locale's script that is appropriate for display to + * the user. If possible, the name will be localized for the default locale. Returns + * the empty string if this locale doesn't specify a script code. + * + * @return the display name of the script code for the current default locale + * @since 1.7 + */ + public String getDisplayScript() { + return getDisplayScript(getDefault()); + } + + /** + * Returns a name for the locale's script that is appropriate + * for display to the user. If possible, the name will be + * localized for the given locale. Returns the empty string if + * this locale doesn't specify a script code. + * + * @return the display name of the script code for the current default locale + * @throws NullPointerException if inLocale is null + * @since 1.7 + */ + public String getDisplayScript(Locale inLocale) { + return getDisplayString(_baseLocale.getScript(), inLocale, DISPLAY_SCRIPT); } /** @@ -677,7 +1570,7 @@ public final class Locale implements Cloneable, Serializable { * value. If the locale doesn't specify a country, this function returns the empty string. */ public final String getDisplayCountry() { - return getDisplayCountry(getDefault()); + return getDisplayCountry(getDefault(Category.DISPLAY)); } /** @@ -696,7 +1589,7 @@ public final class Locale implements Cloneable, Serializable { * @exception NullPointerException if inLocale is null */ public String getDisplayCountry(Locale inLocale) { - return getDisplayString(country, inLocale, DISPLAY_COUNTRY); + return getDisplayString(_baseLocale.getRegion(), inLocale, DISPLAY_COUNTRY); } private String getDisplayString(String code, Locale inLocale, int type) { @@ -744,7 +1637,7 @@ public final class Locale implements Cloneable, Serializable { * doesn't specify a variant code, this function returns the empty string. */ public final String getDisplayVariant() { - return getDisplayVariant(getDefault()); + return getDisplayVariant(getDefault(Category.DISPLAY)); } /** @@ -755,7 +1648,7 @@ public final class Locale implements Cloneable, Serializable { * @exception NullPointerException if inLocale is null */ public String getDisplayVariant(Locale inLocale) { - if (variant.length() == 0) + if (_baseLocale.getVariant().length() == 0) return ""; OpenListResourceBundle bundle = LocaleData.getLocaleNames(inLocale); @@ -776,39 +1669,44 @@ public final class Locale implements Cloneable, Serializable { /** * Returns a name for the locale that is appropriate for display to the - * user. This will be the values returned by getDisplayLanguage(), getDisplayCountry(), - * and getDisplayVariant() assembled into a single string. The display name will have - * one of the following forms:

    - * language (country, variant)

    - * language (country)

    - * language (variant)

    - * country (variant)

    - * language

    - * country

    - * variant

    - * depending on which fields are specified in the locale. If the language, country, - * and variant fields are all empty, this function returns the empty string. + * user. This will be the values returned by getDisplayLanguage(), + * getDisplayScript(), getDisplayCountry(), and getDisplayVariant() assembled + * into a single string. The the non-empty values are used in order, + * with the second and subsequent names in parentheses. For example: + *
    + * language (script, country, variant)
    + * language (country)
    + * language (variant)
    + * script (country)
    + * country
    + *
    + * depending on which fields are specified in the locale. If the + * language, sacript, country, and variant fields are all empty, + * this function returns the empty string. */ public final String getDisplayName() { - return getDisplayName(getDefault()); + return getDisplayName(getDefault(Category.DISPLAY)); } /** - * Returns a name for the locale that is appropriate for display to the - * user. This will be the values returned by getDisplayLanguage(), getDisplayCountry(), - * and getDisplayVariant() assembled into a single string. The display name will have - * one of the following forms:

    - * language (country, variant)

    - * language (country)

    - * language (variant)

    - * country (variant)

    - * language

    - * country

    - * variant

    - * depending on which fields are specified in the locale. If the language, country, - * and variant fields are all empty, this function returns the empty string. + * Returns a name for the locale that is appropriate for display + * to the user. This will be the values returned by + * getDisplayLanguage(), getDisplayScript(),getDisplayCountry(), + * and getDisplayVariant() assembled into a single string. + * The non-empty values are used in order, + * with the second and subsequent names in parentheses. For example: + *
    + * language (script, country, variant)
    + * language (country)
    + * language (variant)
    + * script (country)
    + * country
    + *
    + * depending on which fields are specified in the locale. If the + * language, script, country, and variant fields are all empty, + * this function returns the empty string. * - * @exception NullPointerException if inLocale is null + * @throws NullPointerException if inLocale is null */ public String getDisplayName(Locale inLocale) { OpenListResourceBundle bundle = LocaleData.getLocaleNames(inLocale); @@ -888,7 +1786,7 @@ public final class Locale implements Cloneable, Serializable { } /** - * Overrides Cloneable + * Overrides Cloneable. */ public Object clone() { @@ -908,7 +1806,7 @@ public final class Locale implements Cloneable, Serializable { public int hashCode() { int hc = hashCodeValue; if (hc == 0) { - hc = (language.hashCode() << 8) ^ country.hashCode() ^ (variant.hashCode() << 4); + hc = _baseLocale.hashCode() ^ _extensions.hashCode(); hashCodeValue = hc; } return hc; @@ -918,8 +1816,8 @@ public final class Locale implements Cloneable, Serializable { /** * Returns true if this Locale is equal to another object. A Locale is - * deemed equal to another Locale with identical language, country, - * and variant, and unequal to all other objects. + * deemed equal to another Locale with identical language, script, country, + * variant and extensions, and unequal to all other objects. * * @return true if this Locale is equal to the specified object. */ @@ -929,47 +1827,24 @@ public final class Locale implements Cloneable, Serializable { return true; if (!(obj instanceof Locale)) return false; - Locale other = (Locale) obj; - return language == other.language - && country == other.country - && variant == other.variant; + BaseLocale otherBase = ((Locale)obj)._baseLocale; + LocaleExtensions otherExt = ((Locale)obj)._extensions; + return _baseLocale.equals(otherBase) && _extensions.equals(otherExt); } // ================= privates ===================================== - // XXX instance and class variables. For now keep these separate, since it is - // faster to match. Later, make into single string. - - /** - * @serial - * @see #getLanguage - */ - private final String language; - - /** - * @serial - * @see #getCountry - */ - private final String country; - - /** - * @serial - * @see #getVariant - */ - private final String variant; - - /** - * Placeholder for the object's hash code. Always -1. - * @serial - */ - private volatile int hashcode = -1; // lazy evaluate + private transient BaseLocale _baseLocale; + private transient LocaleExtensions _extensions; /** - * Calculated hashcode to fix 4518797. + * Calculated hashcode */ private transient volatile int hashCodeValue = 0; private static Locale defaultLocale = null; + private static Locale defaultDisplayLocale = null; + private static Locale defaultFormatLocale = null; /** * Return an array of the display names of the variant. @@ -978,7 +1853,7 @@ public final class Locale implements Cloneable, Serializable { */ private String[] getDisplayVariantArray(OpenListResourceBundle bundle, Locale inLocale) { // Split the variant name into tokens separated by '_'. - StringTokenizer tokenizer = new StringTokenizer(variant, "_"); + StringTokenizer tokenizer = new StringTokenizer(_baseLocale.getVariant(), "_"); String[] names = new String[tokenizer.countTokens()]; // For each variant token, lookup the display name. If @@ -1056,49 +1931,102 @@ public final class Locale implements Cloneable, Serializable { } /** - * Replace the deserialized Locale object with a newly - * created object. Newer language codes are replaced with older ISO - * codes. The country and variant codes are replaced with internalized - * String copies. - */ - private Object readResolve() throws java.io.ObjectStreamException { - return getInstance(language, country, variant); - } + * @serialField language String + * language subtag in lower case. (See getLanguage()) + * @serialField country String + * country subtag in upper case. (See getCountry()) + * @serialField variant String + * variant subtags separated by LOWLINE characters. (See getVariant()) + * @serialField hashcode int + * deprectated, for forward compatibility only + * @serialField script String + * script subtag in title case (See getScript()) + * @serialField extensions String + * canonical representation of extensions, that is, + * BCP47 extensions in alphabetical order followed by + * BCP47 private use subtags, all in lower case letters + * separated by HYPHEN-MINUS characters. + * (See getExtensionKeys(), + * getExtension(char)) + */ + private static final ObjectStreamField[] serialPersistentFields = { + new ObjectStreamField("language", String.class), + new ObjectStreamField("country", String.class), + new ObjectStreamField("variant", String.class), + new ObjectStreamField("hashcode", int.class), + new ObjectStreamField("script", String.class), + new ObjectStreamField("extensions", String.class), + }; - private static volatile String[] isoLanguages = null; - - private static volatile String[] isoCountries = null; + /** + * Serializes this Locale to the specified ObjectOutputStream. + * @param out the ObjectOutputStream to write + * @throws IOException + * @since 1.7 + */ + private void writeObject(ObjectOutputStream out) throws IOException { + ObjectOutputStream.PutField fields = out.putFields(); + fields.put("language", _baseLocale.getLanguage()); + fields.put("script", _baseLocale.getScript()); + fields.put("country", _baseLocale.getRegion()); + fields.put("variant", _baseLocale.getVariant()); + fields.put("extensions", _extensions.getID()); + fields.put("hashcode", -1); // place holder just for backward support + out.writeFields(); + } - /* - * Locale needs its own, locale insensitive version of toLowerCase to - * avoid circularity problems between Locale and String. - * The most straightforward algorithm is used. Look at optimizations later. - */ - private String toLowerCase(String str) { - char[] buf = new char[str.length()]; - for (int i = 0; i < buf.length; i++) { - buf[i] = Character.toLowerCase(str.charAt(i)); + /** + * Deserialize this Locale. + * @param in the ObjectInputStream to read + * @throws IOException + * @throws ClassNotFoundException + * @throws IllformdLocaleException + * @since 1.7 + */ + private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { + ObjectInputStream.GetField fields = in.readFields(); + String language = (String)fields.get("language", ""); + String script = (String)fields.get("script", ""); + String country = (String)fields.get("country", ""); + String variant = (String)fields.get("variant", ""); + String extStr = (String)fields.get("extensions", ""); + _baseLocale = BaseLocale.getInstance(convertOldISOCodes(language), script, country, variant); + try { + InternalLocaleBuilder bldr = new InternalLocaleBuilder(); + bldr.setExtensions(extStr); + _extensions = bldr.getLocaleExtensions(); + } catch (LocaleSyntaxException e) { + throw new IllformedLocaleException(e.getMessage()); } - return new String( buf ); } - /* - * Locale needs its own, locale insensitive version of toUpperCase to - * avoid circularity problems between Locale and String. - * The most straightforward algorithm is used. Look at optimizations later. + /** + * Returns a cached Locale instance equivalent to + * the deserialized Locale. When serialized + * language, country and variant fields read from the object data stream + * are exactly "ja", "JP", "JP" or "th", "TH", "TH" and script/extensions + * fields are empty, this method supplies UNICODE_LOCALE_EXTENSION + * "ca"/"japanese" (calendar type is "japanese") or "nu"/"thai" (number script + * type is "thai"). See Special Cases + * for more information. + * + * @return an instance of Locale equivalent to + * the deserialized Locale. + * @throws java.io.ObjectStreamException */ - private String toUpperCase(String str) { - char[] buf = new char[str.length()]; - for (int i = 0; i < buf.length; i++) { - buf[i] = Character.toUpperCase(str.charAt(i)); - } - return new String( buf ); + private Object readResolve() throws java.io.ObjectStreamException { + return getInstance(_baseLocale.getLanguage(), _baseLocale.getScript(), + _baseLocale.getRegion(), _baseLocale.getVariant(), _extensions); } - private String convertOldISOCodes(String language) { + private static volatile String[] isoLanguages = null; + + private static volatile String[] isoCountries = null; + + private static String convertOldISOCodes(String language) { // we accept both the old and the new ISO codes for the languages whose ISO // codes have changed, but we always store the OLD code, for backward compatibility - language = toLowerCase(language).intern(); + language = AsciiUtil.toLowerString(language).intern(); if (language == "he") { return "iw"; } else if (language == "yi") { @@ -1110,6 +2038,25 @@ public final class Locale implements Cloneable, Serializable { } } + private static LocaleExtensions getCompatibilityExtensions(String language, String script, String country, String variant) { + LocaleExtensions extensions = LocaleExtensions.EMPTY_EXTENSIONS; + // Special cases for backward compatibility support + if (AsciiUtil.caseIgnoreMatch(language, "ja") + && script.length() == 0 + && AsciiUtil.caseIgnoreMatch(country, "JP") + && AsciiUtil.caseIgnoreMatch(variant, "JP")) { + // ja_JP_JP -> u-ca-japanese (calendar = japanese) + extensions = LocaleExtensions.CALENDAR_JAPANESE; + } else if (AsciiUtil.caseIgnoreMatch(language, "th") + && script.length() == 0 + && AsciiUtil.caseIgnoreMatch(country, "TH") + && AsciiUtil.caseIgnoreMatch(variant, "TH")) { + // th_TH_TH -> u-nu-thai (numbersystem = thai) + extensions = LocaleExtensions.NUMBER_THAI; + } + return extensions; + } + /** * Obtains a localized locale names from a LocaleNameProvider * implementation. @@ -1133,6 +2080,8 @@ public final class Locale implements Cloneable, Serializable { return localeNameProvider.getDisplayCountry(code, locale); case DISPLAY_VARIANT: return localeNameProvider.getDisplayVariant(code, locale); + case DISPLAY_SCRIPT: + return localeNameProvider.getDisplayScript(code, locale); default: assert false; // shouldn't happen } @@ -1140,4 +2089,370 @@ public final class Locale implements Cloneable, Serializable { return null; } } + + /** + * Enum for locale categories. These locale categories are used to get/set + * the default locale for the specific functionality represented by the + * category. + * + * @see #getDefault(Locale.Category) + * @see #setDefault(Locale.Category, Locale) + * @since 1.7 + */ + public enum Category { + + /** + * Category used to represent the default locale for + * displaying user interfaces. + */ + DISPLAY, + + /** + * Category used to represent the default locale for + * formatting dates, numbers, and/or currencies. + */ + FORMAT, + } + + /** + * Builder is used to build instances of Locale + * from values configured by the setters. Unlike the Locale + * constructors, the Builder checks if a value configured by a + * setter satisfies the syntax requirements defined by the Locale + * class. A Locale object created by a Builder is + * well-formed and can be transformed to a well-formed IETF BCP 47 language tag + * without losing information. + * + *

    Note: The Locale class does not provide any + * syntactic restrictions on variant, while BCP 47 requires each variant + * subtag to be 5 to 8 alphanumerics or a single numeric followed by 3 + * alphanumerics. The method setVariant throws + * IllformedLocaleException for a variant that does not satisfy + * this restriction. If it is necessary to support such a variant, use a + * Locale constructor. However, keep in mind that a Locale + * object created this way might lose the variant information when + * transformed to a BCP 47 language tag. + * + *

    The following example shows how to create a Locale object + * with the Builder. + *

    + *
    +     *     Locale aLocale = new Builder().setLanguage("sr").setScript("Latn").setRegion("RS").build();
    +     * 
    + *
    + * + *

    Builders can be reused; clear() resets all + * fields to their default values. + * + * @see Locale#forLanguageTag + * @since 1.7 + */ + public static final class Builder { + private InternalLocaleBuilder _locbld; + + /** + * Constructs an empty Builder. The default value of all + * fields, extensions, and private use information is the + * empty string. + */ + public Builder() { + _locbld = new InternalLocaleBuilder(); + } + + /** + * Resets the Builder to match the provided + * locale. Existing state is discarded. + * + *

    All fields of the locale must be well-formed, see {@link Locale}. + * + *

    Locales with any ill-formed fields cause + * IllformedLocaleException to be thrown, except for the + * following three cases which are accepted for compatibility + * reasons:

      + *
    • Locale("ja", "JP", "JP") is treated as "ja-JP-u-ca-japanese" + *
    • Locale("th", "TH", "TH") is treated as "th-TH-u-nu-thai" + *
    • Locale("no", "NO", "NY") is treated as "nn-NO"
    + * + * @param locale the locale + * @return This builder. + * @throws IllformedLocaleException if locale has + * any ill-formed fields. + * @throws NullPointerException if locale is null. + */ + public Builder setLocale(Locale locale) { + try { + _locbld.setLocale(locale._baseLocale, locale._extensions); + } catch (LocaleSyntaxException e) { + throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); + } + return this; + } + + /** + * Resets the Builder to match the provided IETF BCP 47 + * language tag. Discards the existing state. Null and the + * empty string cause the builder to be reset, like {@link + * #clear}. Grandfathered tags (see {@link + * Locale#forLanguageTag}) are converted to their canonical + * form before being processed. Otherwise, the language tag + * must be well-formed (see {@link Locale}) or an exception is + * thrown (unlike Locale.forLanguageTag, which + * just discards ill-formed and following portions of the + * tag). + * + * @param languageTag the language tag + * @return This builder. + * @throws IllformedLocaleException if languageTag is ill-formed + * @see Locale#forLanguageTag(String) + */ + public Builder setLanguageTag(String languageTag) { + ParseStatus sts = new ParseStatus(); + LanguageTag tag = LanguageTag.parse(languageTag, sts); + if (sts.isError()) { + throw new IllformedLocaleException(sts.getErrorMessage(), sts.getErrorIndex()); + } + _locbld.setLanguageTag(tag); + + return this; + } + + /** + * Sets the language. If language is the empty string or + * null, the language in this Builder is removed. Otherwise, + * the language must be well-formed + * or an exception is thrown. + * + *

    The typical language value is a two or three-letter language + * code as defined in ISO639. + * + * @param language the language + * @return This builder. + * @throws IllformedLocaleException if language is ill-formed + */ + public Builder setLanguage(String language) { + try { + _locbld.setLanguage(language); + } catch (LocaleSyntaxException e) { + throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); + } + return this; + } + + /** + * Sets the script. If script is null or the empty string, + * the script in this Builder is removed. + * Otherwise, the script must be well-formed or an + * exception is thrown. + * + *

    The typical script value is a four-letter script code as defined by ISO 15924. + * + * @param script the script + * @return This builder. + * @throws IllformedLocaleException if script is ill-formed + */ + public Builder setScript(String script) { + try { + _locbld.setScript(script); + } catch (LocaleSyntaxException e) { + throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); + } + return this; + } + + /** + * Sets the region. If region is null or the empty string, the region + * in this Builder is removed. Otherwise, + * the region must be well-formed or an + * exception is thrown. + * + *

    The typical region value is a two-letter ISO 3166 code or a + * three-digit UN M.49 area code. + * + *

    The country value in the Locale created by the + * Builder is always normalized to upper case. + * + * @param region the region + * @return This builder. + * @throws IllformedLocaleException if region is ill-formed + */ + public Builder setRegion(String region) { + try { + _locbld.setRegion(region); + } catch (LocaleSyntaxException e) { + throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); + } + return this; + } + + /** + * Sets the variant. If variant is null or the empty string, the + * variant in this Builder is removed. Otherwise, it + * must consist of one or more well-formed + * subtags, or an exception is thrown. + * + *

    Note: This method checks if variant + * satisfies the IETF BCP 47 variant subtag's syntax requirements, + * and normalizes the value to lowercase letters. However, + * the Locale class does not impose any syntactic + * restriction on variant, and the variant value in + * Locale is case sensitive. To set such a variant, + * use a Locale constructor. + * + * @param variant the variant + * @return This builder. + * @throws IllformedLocaleException if variant is ill-formed + */ + public Builder setVariant(String variant) { + try { + _locbld.setVariant(variant); + } catch (LocaleSyntaxException e) { + throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); + } + return this; + } + + /** + * Sets the extension for the given key. If the value is null or the + * empty string, the extension is removed. Otherwise, the extension + * must be well-formed or an exception + * is thrown. + * + *

    Note: The key {@link Locale#UNICODE_LOCALE_EXTENSION + * UNICODE_LOCALE_EXTENSION} ('u') is used for the Unicode locale extension. + * Setting a value for this key replaces any existing Unicode locale key/type + * pairs with those defined in the extension. + * + *

    Note: The key {@link Locale#PRIVATE_USE_EXTENSION + * PRIVATE_USE_EXTENSION} ('x') is used for the private use code. To be + * well-formed, the value for this key needs only to have subtags of one to + * eight alphanumeric characters, not two to eight as in the general case. + * + * @param key the extension key + * @param value the extension value + * @return This builder. + * @throws IllformedLocaleException if key is illegal + * or value is ill-formed + * @see #setUnicodeLocaleKeyword(String, String) + */ + public Builder setExtension(char key, String value) { + try { + _locbld.setExtension(key, value); + } catch (LocaleSyntaxException e) { + throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); + } + return this; + } + + /** + * Sets the Unicode locale keyword type for the given key. If the type + * is null, the Unicode keyword is removed. Otherwise, the key must be + * non-null and both key and type must be well-formed or an exception + * is thrown. + * + *

    Keys and types are converted to lower case. + * + *

    Note:Setting the 'u' extension via {@link #setExtension} + * replaces all Unicode locale keywords with those defined in the + * extension. + * + * @param key the Unicode locale key + * @param type the Unicode locale type + * @return This builder. + * @throws IllformedLocaleException if key or type + * is ill-formed + * @throws NullPointerException if key is null + * @see #setExtension(char, String) + */ + public Builder setUnicodeLocaleKeyword(String key, String type) { + try { + _locbld.setUnicodeLocaleKeyword(key, type); + } catch (LocaleSyntaxException e) { + throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); + } + return this; + } + + /** + * Adds a unicode locale attribute, if not already present, otherwise + * has no effect. The attribute must not be null and must be well-formed or an exception + * is thrown. + * + * @param attribute the attribute + * @return This builder. + * @throws NullPointerException if attribute is null + * @throws IllformedLocaleException if attribute is ill-formed + * @see #setExtension(char, String) + */ + public Builder addUnicodeLocaleAttribute(String attribute) { + try { + _locbld.addUnicodeLocaleAttribute(attribute); + } catch (LocaleSyntaxException e) { + throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); + } + return this; + } + + /** + * Removes a unicode locale attribute, if present, otherwise has no + * effect. The attribute must not be null and must be well-formed or an exception + * is thrown. + * + *

    Attribute comparision for removal is case-insensitive. + * + * @param attribute the attribute + * @return This builder. + * @throws NullPointerException if attribute is null + * @throws IllformedLocaleException if attribute is ill-formed + * @see #setExtension(char, String) + */ + public Builder removeUnicodeLocaleAttribute(String attribute) { + try { + _locbld.removeUnicodeLocaleAttribute(attribute); + } catch (LocaleSyntaxException e) { + throw new IllformedLocaleException(e.getMessage(), e.getErrorIndex()); + } + return this; + } + + /** + * Resets the builder to its initial, empty state. + * + * @return This builder. + */ + public Builder clear() { + _locbld.clear(); + return this; + } + + /** + * Resets the extensions to their initial, empty state. + * Language, script, region and variant are unchanged. + * + * @return This builder. + * @see #setExtension(char, String) + */ + public Builder clearExtensions() { + _locbld.clearExtensions(); + return this; + } + + /** + * Returns an instance of Locale created from the fields set + * on this builder. + * + *

    This applies the conversions listed in {@link Locale#forLanguageTag} + * when constructing a Locale. (Grandfathered tags are handled in + * {@link #setLanguageTag}.) + * + * @return A Locale. + */ + public Locale build() { + BaseLocale baseloc = _locbld.getBaseLocale(); + LocaleExtensions extensions = _locbld.getLocaleExtensions(); + return Locale.getInstance(baseloc, extensions); + } + } } diff --git a/src/share/classes/java/util/Properties.java b/src/share/classes/java/util/Properties.java index 4012f2f5e6cd8fd95732bfbf7ef50c94ccd35edf..6f9c562e4f8b7729f69946b2a0bccb369475a3e1 100644 --- a/src/share/classes/java/util/Properties.java +++ b/src/share/classes/java/util/Properties.java @@ -912,9 +912,13 @@ class Properties extends Hashtable { * *

    The specified stream remains open after this method returns. * - * @param os the output stream on which to emit the XML document. - * @param comment a description of the property list, or null - * if no comment is desired. + * @param os the output stream on which to emit the XML document. + * @param comment a description of the property list, or null + * if no comment is desired. + * @param encoding the name of a supported + * + * character encoding + * * @throws IOException if writing to the specified output stream * results in an IOException. * @throws NullPointerException if os is null, diff --git a/src/share/classes/java/util/ResourceBundle.java b/src/share/classes/java/util/ResourceBundle.java index 56888362bbd88f9195b54dfd2b802b93ae9e1b61..e645fe91439aefd4bcdeb38afc59488e2d5e9b22 100644 --- a/src/share/classes/java/util/ResourceBundle.java +++ b/src/share/classes/java/util/ResourceBundle.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,16 +56,18 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.jar.JarEntry; +import sun.util.locale.BaseLocale; +import sun.util.locale.LocaleExtensions; +import sun.util.locale.LocaleObjectCache; + /** * - * Resource bundles contain locale-specific objects. - * When your program needs a locale-specific resource, - * a String for example, your program can load it - * from the resource bundle that is appropriate for the - * current user's locale. In this way, you can write - * program code that is largely independent of the user's - * locale isolating most, if not all, of the locale-specific + * Resource bundles contain locale-specific objects. When your program needs a + * locale-specific resource, a String for example, your program can + * load it from the resource bundle that is appropriate for the current user's + * locale. In this way, you can write program code that is largely independent + * of the user's locale isolating most, if not all, of the locale-specific * information in resource bundles. * *

    @@ -854,87 +856,140 @@ public abstract class ResourceBundle { } /** - * Gets a resource bundle using the specified base name, locale, and class loader. + * Gets a resource bundle using the specified base name, locale, and class + * loader. + * + *

    This method behaves the same as calling + * {@link #getBundle(String, Locale, ClassLoader, Control)} passing a + * default instance of {@link Control}. The following describes this behavior. + * + *

    getBundle uses the base name, the specified locale, and + * the default locale (obtained from {@link java.util.Locale#getDefault() + * Locale.getDefault}) to generate a sequence of candidate bundle names. If the specified + * locale's language, script, country, and variant are all empty strings, + * then the base name is the only candidate bundle name. Otherwise, a list + * of candidate locales is generated from the attribute values of the + * specified locale (language, script, country and variant) and appended to + * the base name. Typically, this will look like the following: * - *

    - * Conceptually, getBundle uses the following strategy for locating and instantiating - * resource bundles: - *

    - * getBundle uses the base name, the specified locale, and the default - * locale (obtained from {@link java.util.Locale#getDefault() Locale.getDefault}) - * to generate a sequence of candidate bundle names. - * If the specified locale's language, country, and variant are all empty - * strings, then the base name is the only candidate bundle name. - * Otherwise, the following sequence is generated from the attribute - * values of the specified locale (language1, country1, and variant1) - * and of the default locale (language2, country2, and variant2): - *

      - *
    • baseName + "_" + language1 + "_" + country1 + "_" + variant1 - *
    • baseName + "_" + language1 + "_" + country1 - *
    • baseName + "_" + language1 - *
    • baseName + "_" + language2 + "_" + country2 + "_" + variant2 - *
    • baseName + "_" + language2 + "_" + country2 - *
    • baseName + "_" + language2 - *
    • baseName - *
    - *

    - * Candidate bundle names where the final component is an empty string are omitted. - * For example, if country1 is an empty string, the second candidate bundle name is omitted. + *

    +     *     baseName + "_" + language + "_" + script + "_" + country + "_" + variant
    +     *     baseName + "_" + language + "_" + script + "_" + country
    +     *     baseName + "_" + language + "_" + script
    +     *     baseName + "_" + language + "_" + country + "_" + variant
    +     *     baseName + "_" + language + "_" + country
    +     *     baseName + "_" + language
    +     * 
    * - *

    - * getBundle then iterates over the candidate bundle names to find the first - * one for which it can instantiate an actual resource bundle. For each candidate - * bundle name, it attempts to create a resource bundle: - *

      - *
    • - * First, it attempts to load a class using the candidate bundle name. - * If such a class can be found and loaded using the specified class loader, is assignment - * compatible with ResourceBundle, is accessible from ResourceBundle, and can be instantiated, - * getBundle creates a new instance of this class and uses it as the result - * resource bundle. - *
    • - * Otherwise, getBundle attempts to locate a property resource file. - * It generates a path name from the candidate bundle name by replacing all "." characters - * with "/" and appending the string ".properties". - * It attempts to find a "resource" with this name using - * {@link java.lang.ClassLoader#getResource(java.lang.String) ClassLoader.getResource}. - * (Note that a "resource" in the sense of getResource has nothing to do with - * the contents of a resource bundle, it is just a container of data, such as a file.) - * If it finds a "resource", it attempts to create a new - * {@link PropertyResourceBundle} instance from its contents. - * If successful, this instance becomes the result resource bundle. - *
    + *

    Candidate bundle names where the final component is an empty string + * are omitted, along with the underscore. For example, if country is an + * empty string, the second and the fifth candidate bundle names above + * would be omitted. Also, if script is an empty string, the candidate names + * including script are omitted. For example, a locale with language "de" + * and variant "JAVA" will produce candidate names with base name + * "MyResource" below. * - *

    - * If no result resource bundle has been found, a MissingResourceException - * is thrown. - * - *

    - * Once a result resource bundle has been found, its parent chain is instantiated. - * getBundle iterates over the candidate bundle names that can be - * obtained by successively removing variant, country, and language - * (each time with the preceding "_") from the bundle name of the result resource bundle. - * As above, candidate bundle names where the final component is an empty string are omitted. - * With each of the candidate bundle names it attempts to instantiate a resource bundle, as - * described above. - * Whenever it succeeds, it calls the previously instantiated resource + *

    +     *     MyResource_de__JAVA
    +     *     MyResource_de
    +     * 
    + * + * In the case that the variant contains one or more underscores ('_'), a + * sequence of bundle names generated by truncating the last underscore and + * the part following it is inserted after a candidate bundle name with the + * original variant. For example, for a locale with language "en", script + * "Latn, country "US" and variant "WINDOWS_VISTA", and bundle base name + * "MyResource", the list of candidate bundle names below is generated: + * + *
    +     * MyResource_en_Latn_US_WINDOWS_VISTA
    +     * MyResource_en_Latn_US_WINDOWS
    +     * MyResource_en_Latn_US
    +     * MyResource_en_Latn
    +     * MyResource_en_US_WINDOWS_VISTA
    +     * MyResource_en_US_WINDOWS
    +     * MyResource_en_US
    +     * MyResource_en
    +     * 
    + * + *
    Note: For some Locales, the list of + * candidate bundle names contains extra names, or the order of bundle names + * is slightly modified. See the description of the default implementation + * of {@link Control#getCandidateLocales(String, Locale) + * getCandidateLocales} for details.
    + * + *

    getBundle then iterates over the candidate bundle names + * to find the first one for which it can instantiate an actual + * resource bundle. It uses the default controls' {@link Control#getFormats + * getFormats} method, which generates two bundle names for each generated + * name, the first a class name and the second a properties file name. For + * each candidate bundle name, it attempts to create a resource bundle: + * + *

    • First, it attempts to load a class using the generated class name. + * If such a class can be found and loaded using the specified class + * loader, is assignment compatible with ResourceBundle, is accessible from + * ResourceBundle, and can be instantiated, getBundle creates a + * new instance of this class and uses it as the result resource + * bundle. + * + *
    • Otherwise, getBundle attempts to locate a property + * resource file using the generated properties file name. It generates a + * path name from the candidate bundle name by replacing all "." characters + * with "/" and appending the string ".properties". It attempts to find a + * "resource" with this name using {@link + * java.lang.ClassLoader#getResource(java.lang.String) + * ClassLoader.getResource}. (Note that a "resource" in the sense of + * getResource has nothing to do with the contents of a + * resource bundle, it is just a container of data, such as a file.) If it + * finds a "resource", it attempts to create a new {@link + * PropertyResourceBundle} instance from its contents. If successful, this + * instance becomes the result resource bundle.
    + * + *

    This continues until a result resource bundle is instantiated or the + * list of candidate bundle names is exhausted. If no matching resource + * bundle is found, the default control's {@link Control#getFallbackLocale + * getFallbackLocale} method is called, which returns the current default + * locale. A new sequence of candidate locale names is generated using this + * locale and and searched again, as above. + * + *

    If still no result bundle is found, the base name alone is looked up. If + * this still fails, a MissingResourceException is thrown. + * + *

    Once a result resource bundle has been found, + * its parent chain is instantiated. If the result bundle already + * has a parent (perhaps because it was returned from a cache) the chain is + * complete. + * + *

    Otherwise, getBundle examines the remainder of the + * candidate locale list that was used during the pass that generated the + * result resource bundle. (As before, candidate bundle names where the + * final component is an empty string are omitted.) When it comes to the + * end of the candidate list, it tries the plain bundle name. With each of the + * candidate bundle names it attempts to instantiate a resource bundle (first + * looking for a class and then a properties file, as described above). + * + *

    Whenever it succeeds, it calls the previously instantiated resource * bundle's {@link #setParent(java.util.ResourceBundle) setParent} method - * with the new resource bundle, unless the previously instantiated resource - * bundle already has a non-null parent. + * with the new resource bundle. This continues until the list of names + * is exhausted or the current bundle already has a non-null parent. * - *

    - * getBundle caches instantiated resource bundles and - * may return the same resource bundle instance multiple - * times. + *

    Once the parent chain is complete, the bundle is returned. * - *

    - * The baseName argument should be a fully qualified class name. However, for - * compatibility with earlier versions, Sun's Java SE Runtime Environments do not verify this, - * and so it is possible to access PropertyResourceBundles by specifying a - * path name (using "/") instead of a fully qualified class name (using "."). + *

    Note: getBundle caches instantiated resource + * bundles and might return the same resource bundle instance multiple times. + * + *

    Note:The baseName argument should be a fully + * qualified class name. However, for compatibility with earlier versions, + * Sun's Java SE Runtime Environments do not verify this, and so it is + * possible to access PropertyResourceBundles by specifying a + * path name (using "/") instead of a fully qualified class name (using + * "."). * *

    - * Example:
    The following class and property files are provided: + * Example: + *

    + * The following class and property files are provided: *

          *     MyResources.class
          *     MyResources.properties
    @@ -944,22 +999,26 @@ public abstract class ResourceBundle {
          *     MyResources_en.properties
          *     MyResources_es_ES.class
          * 
    - * The contents of all files are valid (that is, public non-abstract subclasses of ResourceBundle for - * the ".class" files, syntactically correct ".properties" files). - * The default locale is Locale("en", "GB"). - *

    - * Calling getBundle with the shown locale argument values instantiates - * resource bundles from the following sources: - *

      - *
    • Locale("fr", "CH"): result MyResources_fr_CH.class, parent MyResources_fr.properties, parent MyResources.class - *
    • Locale("fr", "FR"): result MyResources_fr.properties, parent MyResources.class - *
    • Locale("de", "DE"): result MyResources_en.properties, parent MyResources.class - *
    • Locale("en", "US"): result MyResources_en.properties, parent MyResources.class - *
    • Locale("es", "ES"): result MyResources_es_ES.class, parent MyResources.class - *
    - *

    The file MyResources_fr_CH.properties is never used because it is hidden by - * MyResources_fr_CH.class. Likewise, MyResources.properties is also hidden by - * MyResources.class. + * + * The contents of all files are valid (that is, public non-abstract + * subclasses of ResourceBundle for the ".class" files, + * syntactically correct ".properties" files). The default locale is + * Locale("en", "GB"). + * + *

    Calling getBundle with the locale arguments below will + * instantiate resource bundles as follows: + * + * + * + * + * + * + * + *
    Locale("fr", "CH")MyResources_fr_CH.class, parent MyResources_fr.properties, parent MyResources.class
    Locale("fr", "FR")MyResources_fr.properties, parent MyResources.class
    Locale("de", "DE")MyResources_en.properties, parent MyResources.class
    Locale("en", "US")MyResources_en.properties, parent MyResources.class
    Locale("es", "ES")MyResources_es_ES.class, parent MyResources.class
    + * + *

    The file MyResources_fr_CH.properties is never used because it is + * hidden by the MyResources_fr_CH.class. Likewise, MyResources.properties + * is also hidden by MyResources.class. * * @param baseName the base name of the resource bundle, a fully qualified class name * @param locale the locale for which a resource bundle is desired @@ -1095,8 +1154,6 @@ public abstract class ResourceBundle { * href="./ResourceBundle.html#parent_chain">parent chain is * instantiated based on the list of candidate locales from which it was * found. Finally, the bundle is returned to the caller.

  • - * - * * * *

    During the resource bundle loading process above, this factory @@ -1119,7 +1176,6 @@ public abstract class ResourceBundle { * {@link Control#getTimeToLive(String,Locale) * control.getTimeToLive} for details. * - * *

    The following is an example of the bundle loading process with the * default ResourceBundle.Control implementation. * @@ -1131,7 +1187,6 @@ public abstract class ResourceBundle { *

  • Available resource bundles: * foo/bar/Messages_fr.properties and * foo/bar/Messages.properties
  • - * * * *

    First, getBundle tries loading a resource bundle in @@ -1811,8 +1866,8 @@ public abstract class ResourceBundle { * handleGetObject} method returns null. Once the * Set has been created, the value is kept in this * ResourceBundle in order to avoid producing the - * same Set in the next calls. Override this method - * in subclass implementations for faster handling. + * same Set in subsequent calls. Subclasses can + * override this method for faster handling. * * @return a Set of the keys contained only in this * ResourceBundle @@ -2177,24 +2232,133 @@ public abstract class ResourceBundle { * ResourceBundle.getBundle factory method loads only * the base bundle as the resulting resource bundle. * - *

    It is not a requirement to return an immutable - * (unmodifiable) List. However, the returned - * List must not be mutated after it has been - * returned by getCandidateLocales. + *

    It is not a requirement to return an immutable (unmodifiable) + * List. However, the returned List must not + * be mutated after it has been returned by + * getCandidateLocales. * *

    The default implementation returns a List containing - * Locales in the following sequence: - *

    -         *     Locale(language, country, variant)
    -         *     Locale(language, country)
    -         *     Locale(language)
    -         *     Locale.ROOT
    -         * 
    - * where language, country and - * variant are the language, country and variant values - * of the given locale, respectively. Locales where the + * Locales using the rules described below. In the + * description below, L, S, C and V + * respectively represent non-empty language, script, country, and + * variant. For example, [L, C] represents a + * Locale that has non-empty values only for language and + * country. The form L("xx") represents the (non-empty) + * language value is "xx". For all cases, Locales whose * final component values are empty strings are omitted. * + *
    1. For an input Locale with an empty script value, + * append candidate Locales by omitting the final component + * one by one as below: + * + *
        + *
      • [L, C, V] + *
      • [L, C] + *
      • [L] + *
      • Locale.ROOT + *
      + * + *
    2. For an input Locale with a non-empty script value, + * append candidate Locales by omitting the final component + * up to language, then append candidates generated from the + * Locale with country and variant restored: + * + *
        + *
      • [L, S, C, V] + *
      • [L, S, C] + *
      • [L, S] + *
      • [L, C, V] + *
      • [L, C] + *
      • [L] + *
      • Locale.ROOT + *
      + * + *
    3. For an input Locale with a variant value consisting + * of multiple subtags separated by underscore, generate candidate + * Locales by omitting the variant subtags one by one, then + * insert them after every occurence of Locales with the + * full variant value in the original list. For example, if the + * the variant consists of two subtags V1 and V2: + * + *
        + *
      • [L, S, C, V1, V2] + *
      • [L, S, C, V1] + *
      • [L, S, C] + *
      • [L, S] + *
      • [L, C, V1, V2] + *
      • [L, C, V1] + *
      • [L, C] + *
      • [L] + *
      • Locale.ROOT + *
      + * + *
    4. Special cases for Chinese. When an input Locale has the + * language "zh" (Chinese) and an empty script value, either "Hans" (Simplified) or + * "Hant" (Traditional) might be supplied, depending on the country. + * When the country is "CN" (China) or "SG" (Singapore), "Hans" is supplied. + * When the country is "HK" (Hong Kong SAR China), "MO" (Macau SAR China), + * or "TW" (Taiwan), "Hant" is supplied. For all other countries or when the country + * is empty, no script is supplied. For example, for Locale("zh", "CN") + * , the candidate list will be: + *
        + *
      • [L("zh"), S("Hans"), C("CN")] + *
      • [L("zh"), S("Hans")] + *
      • [L("zh"), C("CN")] + *
      • [L("zh")] + *
      • Locale.ROOT + *
      + * + * For Locale("zh", "TW"), the candidate list will be: + *
        + *
      • [L("zh"), S("Hant"), C("TW")] + *
      • [L("zh"), S("Hant")] + *
      • [L("zh"), C("TW")] + *
      • [L("zh")] + *
      • Locale.ROOT + *
      + * + *
    5. Special cases for Norwegian. Both Locale("no", "NO", + * "NY") and Locale("nn", "NO") represent Norwegian + * Nynorsk. When a locale's language is "nn", the standard candidate + * list is generated up to [L("nn")], and then the following + * candidates are added: + * + *
      • [L("no"), C("NO"), V("NY")] + *
      • [L("no"), C("NO")] + *
      • [L("no")] + *
      • Locale.ROOT + *
      + * + * If the locale is exactly Locale("no", "NO", "NY"), it is first + * converted to Locale("nn", "NO") and then the above procedure is + * followed. + * + *

      Also, Java treats the language "no" as a synonym of Norwegian + * Bokmål "nb". Except for the single case Locale("no", + * "NO", "NY") (handled above), when an input Locale + * has language "no" or "nb", candidate Locales with + * language code "no" and "nb" are interleaved, first using the + * requested language, then using its synonym. For example, + * Locale("nb", "NO", "POSIX") generates the following + * candidate list: + * + *

        + *
      • [L("nb"), C("NO"), V("POSIX")] + *
      • [L("no"), C("NO"), V("POSIX")] + *
      • [L("nb"), C("NO")] + *
      • [L("no"), C("NO")] + *
      • [L("nb")] + *
      • [L("no")] + *
      • Locale.ROOT + *
      + * + * Locale("no", "NO", "POSIX") would generate the same list + * except that locales with "no" would appear before the corresponding + * locales with "nb".
    6. + * + * + *
    + * *

    The default implementation uses an {@link ArrayList} that * overriding implementations may modify before returning it to the * caller. However, a subclass must not modify it after it has @@ -2231,24 +2395,119 @@ public abstract class ResourceBundle { if (baseName == null) { throw new NullPointerException(); } - String language = locale.getLanguage(); - String country = locale.getCountry(); - String variant = locale.getVariant(); + return new ArrayList(CANDIDATES_CACHE.get(locale.getBaseLocale())); + } - List locales = new ArrayList(4); - if (variant.length() > 0) { - locales.add(locale); - } - if (country.length() > 0) { - locales.add((locales.size() == 0) ? - locale : Locale.getInstance(language, country, "")); + private static final CandidateListCache CANDIDATES_CACHE = new CandidateListCache(); + + private static class CandidateListCache extends LocaleObjectCache> { + protected List createObject(BaseLocale base) { + String language = base.getLanguage(); + String script = base.getScript(); + String region = base.getRegion(); + String variant = base.getVariant(); + + // Special handling for Norwegian + boolean isNorwegianBokmal = false; + boolean isNorwegianNynorsk = false; + if (language.equals("no")) { + if (region.equals("NO") && variant.equals("NY")) { + variant = ""; + isNorwegianNynorsk = true; + } else { + isNorwegianBokmal = true; + } + } + if (language.equals("nb") || isNorwegianBokmal) { + List tmpList = getDefaultList("nb", script, region, variant); + // Insert a locale replacing "nb" with "no" for every list entry + List bokmalList = new LinkedList(); + for (Locale l : tmpList) { + bokmalList.add(l); + if (l.getLanguage().length() == 0) { + break; + } + bokmalList.add(Locale.getInstance("no", l.getScript(), l.getCountry(), + l.getVariant(), LocaleExtensions.EMPTY_EXTENSIONS)); + } + return bokmalList; + } else if (language.equals("nn") || isNorwegianNynorsk) { + // Insert no_NO_NY, no_NO, no after nn + List nynorskList = getDefaultList("nn", script, region, variant); + int idx = nynorskList.size() - 1; + nynorskList.add(idx++, Locale.getInstance("no", "NO", "NY")); + nynorskList.add(idx++, Locale.getInstance("no", "NO", "")); + nynorskList.add(idx++, Locale.getInstance("no", "", "")); + return nynorskList; + } + // Special handling for Chinese + else if (language.equals("zh")) { + if (script.length() == 0 && region.length() > 0) { + // Supply script for users who want to use zh_Hans/zh_Hant + // as bundle names (recommended for Java7+) + if (region.equals("TW") || region.equals("HK") || region.equals("MO")) { + script = "Hant"; + } else if (region.equals("CN") || region.equals("SG")) { + script = "Hans"; + } + } else if (script.length() > 0 && region.length() == 0) { + // Supply region(country) for users who still package Chinese + // bundles using old convension. + if (script.equals("Hans")) { + region = "CN"; + } else if (script.equals("Hant")) { + region = "TW"; + } + } + } + + return getDefaultList(language, script, region, variant); } - if (language.length() > 0) { - locales.add((locales.size() == 0) ? - locale : Locale.getInstance(language, "", "")); + + private static List getDefaultList(String language, String script, String region, String variant) { + List variants = null; + + if (variant.length() > 0) { + variants = new LinkedList(); + int idx = variant.length(); + while (idx != -1) { + variants.add(variant.substring(0, idx)); + idx = variant.lastIndexOf('_', --idx); + } + } + + LinkedList list = new LinkedList(); + + if (variants != null) { + for (String v : variants) { + list.add(Locale.getInstance(language, script, region, v, LocaleExtensions.EMPTY_EXTENSIONS)); + } + } + if (region.length() > 0) { + list.add(Locale.getInstance(language, script, region, "", LocaleExtensions.EMPTY_EXTENSIONS)); + } + if (script.length() > 0) { + list.add(Locale.getInstance(language, script, "", "", LocaleExtensions.EMPTY_EXTENSIONS)); + + // With script, after truncating variant, region and script, + // start over without script. + if (variants != null) { + for (String v : variants) { + list.add(Locale.getInstance(language, "", region, v, LocaleExtensions.EMPTY_EXTENSIONS)); + } + } + if (region.length() > 0) { + list.add(Locale.getInstance(language, "", region, "", LocaleExtensions.EMPTY_EXTENSIONS)); + } + } + if (language.length() > 0) { + list.add(Locale.getInstance(language, "", "", "", LocaleExtensions.EMPTY_EXTENSIONS)); + } + // Add root locale at the end + list.add(Locale.ROOT); + + return list; } - locales.add(Locale.ROOT); - return locales; } /** @@ -2606,13 +2865,14 @@ public abstract class ResourceBundle { * *

    This implementation returns the following value: *

    -         *     baseName + "_" + language + "_" + country + "_" + variant
    +         *     baseName + "_" + language + "_" + script + "_" + country + "_" + variant
              * 
    - * where language, country and - * variant are the language, country and variant values - * of locale, respectively. Final component values that - * are empty Strings are omitted along with the preceding '_'. If - * all of the values are empty strings, then baseName + * where language, script, country, + * and variant are the language, script, country, and variant + * values of locale, respectively. Final component values that + * are empty Strings are omitted along with the preceding '_'. When the + * script is empty, the script value is ommitted along with the preceding '_'. + * If all of the values are empty strings, then baseName * is returned. * *

    For example, if baseName is @@ -2643,6 +2903,7 @@ public abstract class ResourceBundle { } String language = locale.getLanguage(); + String script = locale.getScript(); String country = locale.getCountry(); String variant = locale.getVariant(); @@ -2652,12 +2913,22 @@ public abstract class ResourceBundle { StringBuilder sb = new StringBuilder(baseName); sb.append('_'); - if (variant != "") { - sb.append(language).append('_').append(country).append('_').append(variant); - } else if (country != "") { - sb.append(language).append('_').append(country); + if (script != "") { + if (variant != "") { + sb.append(language).append('_').append(script).append('_').append(country).append('_').append(variant); + } else if (country != "") { + sb.append(language).append('_').append(script).append('_').append(country); + } else { + sb.append(language).append('_').append(script); + } } else { - sb.append(language); + if (variant != "") { + sb.append(language).append('_').append(country).append('_').append(variant); + } else if (country != "") { + sb.append(language).append('_').append(country); + } else { + sb.append(language); + } } return sb.toString(); diff --git a/src/share/classes/java/util/Scanner.java b/src/share/classes/java/util/Scanner.java index 615250ccc3ccea031551fa6362b4ebfbc1dcb51d..139e5fa38d213c4960913ea9343f07a20cfab21a 100644 --- a/src/share/classes/java/util/Scanner.java +++ b/src/share/classes/java/util/Scanner.java @@ -582,7 +582,7 @@ public final class Scanner implements Iterator, Closeable { matcher = delimPattern.matcher(buf); matcher.useTransparentBounds(true); matcher.useAnchoringBounds(false); - useLocale(Locale.getDefault()); + useLocale(Locale.getDefault(Locale.Category.FORMAT)); } /** @@ -2642,7 +2642,7 @@ public final class Scanner implements Iterator, Closeable { */ public Scanner reset() { delimPattern = WHITESPACE_PATTERN; - useLocale(Locale.getDefault()); + useLocale(Locale.getDefault(Locale.Category.FORMAT)); useRadix(10); clearCaches(); return this; diff --git a/src/share/classes/java/util/TimeZone.java b/src/share/classes/java/util/TimeZone.java index 1a68ca3e887a226ffc9e92ef48bfc13a0741185f..b759f9126d5c40ca21b632ae93a76429e7efb194 100644 --- a/src/share/classes/java/util/TimeZone.java +++ b/src/share/classes/java/util/TimeZone.java @@ -312,7 +312,7 @@ abstract public class TimeZone implements Serializable, Cloneable { * @since 1.2 */ public final String getDisplayName() { - return getDisplayName(false, LONG, Locale.getDefault()); + return getDisplayName(false, LONG, Locale.getDefault(Locale.Category.DISPLAY)); } /** @@ -342,7 +342,7 @@ abstract public class TimeZone implements Serializable, Cloneable { * @since 1.2 */ public final String getDisplayName(boolean daylight, int style) { - return getDisplayName(daylight, style, Locale.getDefault()); + return getDisplayName(daylight, style, Locale.getDefault(Locale.Category.DISPLAY)); } /** diff --git a/src/share/classes/java/util/concurrent/ForkJoinPool.java b/src/share/classes/java/util/concurrent/ForkJoinPool.java index f250a5e9835e237b001b2532de39a81b312c7420..122ce86737e557d4ae81c80af47c73ef83059301 100644 --- a/src/share/classes/java/util/concurrent/ForkJoinPool.java +++ b/src/share/classes/java/util/concurrent/ForkJoinPool.java @@ -40,16 +40,23 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; -import java.util.concurrent.locks.Condition; +import java.util.concurrent.AbstractExecutorService; +import java.util.concurrent.Callable; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.RunnableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.LockSupport; import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicLong; /** * An {@link ExecutorService} for running {@link ForkJoinTask}s. * A {@code ForkJoinPool} provides the entry point for submissions - * from non-{@code ForkJoinTask}s, as well as management and + * from non-{@code ForkJoinTask} clients, as well as management and * monitoring operations. * *

    A {@code ForkJoinPool} differs from other kinds of {@link @@ -58,29 +65,19 @@ import java.util.concurrent.atomic.AtomicLong; * execute subtasks created by other active tasks (eventually blocking * waiting for work if none exist). This enables efficient processing * when most tasks spawn other subtasks (as do most {@code - * ForkJoinTask}s). A {@code ForkJoinPool} may also be used for mixed - * execution of some plain {@code Runnable}- or {@code Callable}- - * based activities along with {@code ForkJoinTask}s. When setting - * {@linkplain #setAsyncMode async mode}, a {@code ForkJoinPool} may - * also be appropriate for use with fine-grained tasks of any form - * that are never joined. Otherwise, other {@code ExecutorService} - * implementations are typically more appropriate choices. + * ForkJoinTask}s). When setting asyncMode to true in + * constructors, {@code ForkJoinPool}s may also be appropriate for use + * with event-style tasks that are never joined. * *

    A {@code ForkJoinPool} is constructed with a given target * parallelism level; by default, equal to the number of available - * processors. Unless configured otherwise via {@link - * #setMaintainsParallelism}, the pool attempts to maintain this - * number of active (or available) threads by dynamically adding, - * suspending, or resuming internal worker threads, even if some tasks - * are stalled waiting to join others. However, no such adjustments - * are performed in the face of blocked IO or other unmanaged - * synchronization. The nested {@link ManagedBlocker} interface - * enables extension of the kinds of synchronization accommodated. - * The target parallelism level may also be changed dynamically - * ({@link #setParallelism}). The total number of threads may be - * limited using method {@link #setMaximumPoolSize}, in which case it - * may become possible for the activities of a pool to stall due to - * the lack of available threads to process new tasks. + * processors. The pool attempts to maintain enough active (or + * available) threads by dynamically adding, suspending, or resuming + * internal worker threads, even if some tasks are stalled waiting to + * join others. However, no such adjustments are guaranteed in the + * face of blocked IO or other unmanaged synchronization. The nested + * {@link ManagedBlocker} interface enables extension of the kinds of + * synchronization accommodated. * *

    In addition to execution and lifecycle control methods, this * class provides status check methods (for example @@ -89,6 +86,40 @@ import java.util.concurrent.atomic.AtomicLong; * {@link #toString} returns indications of pool state in a * convenient form for informal monitoring. * + *

    As is the case with other ExecutorServices, there are three + * main task execution methods summarized in the following + * table. These are designed to be used by clients not already engaged + * in fork/join computations in the current pool. The main forms of + * these methods accept instances of {@code ForkJoinTask}, but + * overloaded forms also allow mixed execution of plain {@code + * Runnable}- or {@code Callable}- based activities as well. However, + * tasks that are already executing in a pool should normally + * NOT use these pool execution methods, but instead use the + * within-computation forms listed in the table. + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    Call from non-fork/join clients Call from within fork/join computations
    Arrange async execution {@link #execute(ForkJoinTask)} {@link ForkJoinTask#fork}
    Await and obtain result {@link #invoke(ForkJoinTask)} {@link ForkJoinTask#invoke}
    Arrange exec and obtain Future {@link #submit(ForkJoinTask)} {@link ForkJoinTask#fork} (ForkJoinTasks are Futures)
    + * *

    Sample Usage. Normally a single {@code ForkJoinPool} is * used for all parallel task execution in a program or subsystem. * Otherwise, use would not usually outweigh the construction and @@ -113,7 +144,8 @@ import java.util.concurrent.atomic.AtomicLong; * {@code IllegalArgumentException}. * *

    This implementation rejects submitted tasks (that is, by throwing - * {@link RejectedExecutionException}) only when the pool is shut down. + * {@link RejectedExecutionException}) only when the pool is shut down + * or internal resources have been exhausted. * * @since 1.7 * @author Doug Lea @@ -121,16 +153,247 @@ import java.util.concurrent.atomic.AtomicLong; public class ForkJoinPool extends AbstractExecutorService { /* - * See the extended comments interspersed below for design, - * rationale, and walkthroughs. + * Implementation Overview + * + * This class provides the central bookkeeping and control for a + * set of worker threads: Submissions from non-FJ threads enter + * into a submission queue. Workers take these tasks and typically + * split them into subtasks that may be stolen by other workers. + * The main work-stealing mechanics implemented in class + * ForkJoinWorkerThread give first priority to processing tasks + * from their own queues (LIFO or FIFO, depending on mode), then + * to randomized FIFO steals of tasks in other worker queues, and + * lastly to new submissions. These mechanics do not consider + * affinities, loads, cache localities, etc, so rarely provide the + * best possible performance on a given machine, but portably + * provide good throughput by averaging over these factors. + * (Further, even if we did try to use such information, we do not + * usually have a basis for exploiting it. For example, some sets + * of tasks profit from cache affinities, but others are harmed by + * cache pollution effects.) + * + * Beyond work-stealing support and essential bookkeeping, the + * main responsibility of this framework is to take actions when + * one worker is waiting to join a task stolen (or always held by) + * another. Because we are multiplexing many tasks on to a pool + * of workers, we can't just let them block (as in Thread.join). + * We also cannot just reassign the joiner's run-time stack with + * another and replace it later, which would be a form of + * "continuation", that even if possible is not necessarily a good + * idea. Given that the creation costs of most threads on most + * systems mainly surrounds setting up runtime stacks, thread + * creation and switching is usually not much more expensive than + * stack creation and switching, and is more flexible). Instead we + * combine two tactics: + * + * Helping: Arranging for the joiner to execute some task that it + * would be running if the steal had not occurred. Method + * ForkJoinWorkerThread.helpJoinTask tracks joining->stealing + * links to try to find such a task. + * + * Compensating: Unless there are already enough live threads, + * method helpMaintainParallelism() may create or + * re-activate a spare thread to compensate for blocked + * joiners until they unblock. + * + * It is impossible to keep exactly the target (parallelism) + * number of threads running at any given time. Determining + * existence of conservatively safe helping targets, the + * availability of already-created spares, and the apparent need + * to create new spares are all racy and require heuristic + * guidance, so we rely on multiple retries of each. Compensation + * occurs in slow-motion. It is triggered only upon timeouts of + * Object.wait used for joins. This reduces poor decisions that + * would otherwise be made when threads are waiting for others + * that are stalled because of unrelated activities such as + * garbage collection. + * + * The ManagedBlocker extension API can't use helping so relies + * only on compensation in method awaitBlocker. + * + * The main throughput advantages of work-stealing stem from + * decentralized control -- workers mostly steal tasks from each + * other. We do not want to negate this by creating bottlenecks + * implementing other management responsibilities. So we use a + * collection of techniques that avoid, reduce, or cope well with + * contention. These entail several instances of bit-packing into + * CASable fields to maintain only the minimally required + * atomicity. To enable such packing, we restrict maximum + * parallelism to (1<<15)-1 (enabling twice this (to accommodate + * unbalanced increments and decrements) to fit into a 16 bit + * field, which is far in excess of normal operating range. Even + * though updates to some of these bookkeeping fields do sometimes + * contend with each other, they don't normally cache-contend with + * updates to others enough to warrant memory padding or + * isolation. So they are all held as fields of ForkJoinPool + * objects. The main capabilities are as follows: + * + * 1. Creating and removing workers. Workers are recorded in the + * "workers" array. This is an array as opposed to some other data + * structure to support index-based random steals by workers. + * Updates to the array recording new workers and unrecording + * terminated ones are protected from each other by a lock + * (workerLock) but the array is otherwise concurrently readable, + * and accessed directly by workers. To simplify index-based + * operations, the array size is always a power of two, and all + * readers must tolerate null slots. Currently, all worker thread + * creation is on-demand, triggered by task submissions, + * replacement of terminated workers, and/or compensation for + * blocked workers. However, all other support code is set up to + * work with other policies. + * + * To ensure that we do not hold on to worker references that + * would prevent GC, ALL accesses to workers are via indices into + * the workers array (which is one source of some of the unusual + * code constructions here). In essence, the workers array serves + * as a WeakReference mechanism. Thus for example the event queue + * stores worker indices, not worker references. Access to the + * workers in associated methods (for example releaseEventWaiters) + * must both index-check and null-check the IDs. All such accesses + * ignore bad IDs by returning out early from what they are doing, + * since this can only be associated with shutdown, in which case + * it is OK to give up. On termination, we just clobber these + * data structures without trying to use them. + * + * 2. Bookkeeping for dynamically adding and removing workers. We + * aim to approximately maintain the given level of parallelism. + * When some workers are known to be blocked (on joins or via + * ManagedBlocker), we may create or resume others to take their + * place until they unblock (see below). Implementing this + * requires counts of the number of "running" threads (i.e., those + * that are neither blocked nor artificially suspended) as well as + * the total number. These two values are packed into one field, + * "workerCounts" because we need accurate snapshots when deciding + * to create, resume or suspend. Note however that the + * correspondence of these counts to reality is not guaranteed. In + * particular updates for unblocked threads may lag until they + * actually wake up. + * + * 3. Maintaining global run state. The run state of the pool + * consists of a runLevel (SHUTDOWN, TERMINATING, etc) similar to + * those in other Executor implementations, as well as a count of + * "active" workers -- those that are, or soon will be, or + * recently were executing tasks. The runLevel and active count + * are packed together in order to correctly trigger shutdown and + * termination. Without care, active counts can be subject to very + * high contention. We substantially reduce this contention by + * relaxing update rules. A worker must claim active status + * prospectively, by activating if it sees that a submitted or + * stealable task exists (it may find after activating that the + * task no longer exists). It stays active while processing this + * task (if it exists) and any other local subtasks it produces, + * until it cannot find any other tasks. It then tries + * inactivating (see method preStep), but upon update contention + * instead scans for more tasks, later retrying inactivation if it + * doesn't find any. + * + * 4. Managing idle workers waiting for tasks. We cannot let + * workers spin indefinitely scanning for tasks when none are + * available. On the other hand, we must quickly prod them into + * action when new tasks are submitted or generated. We + * park/unpark these idle workers using an event-count scheme. + * Field eventCount is incremented upon events that may enable + * workers that previously could not find a task to now find one: + * Submission of a new task to the pool, or another worker pushing + * a task onto a previously empty queue. (We also use this + * mechanism for configuration and termination actions that + * require wakeups of idle workers). Each worker maintains its + * last known event count, and blocks when a scan for work did not + * find a task AND its lastEventCount matches the current + * eventCount. Waiting idle workers are recorded in a variant of + * Treiber stack headed by field eventWaiters which, when nonzero, + * encodes the thread index and count awaited for by the worker + * thread most recently calling eventSync. This thread in turn has + * a record (field nextEventWaiter) for the next waiting worker. + * In addition to allowing simpler decisions about need for + * wakeup, the event count bits in eventWaiters serve the role of + * tags to avoid ABA errors in Treiber stacks. Upon any wakeup, + * released threads also try to release at most two others. The + * net effect is a tree-like diffusion of signals, where released + * threads (and possibly others) help with unparks. To further + * reduce contention effects a bit, failed CASes to increment + * field eventCount are tolerated without retries in signalWork. + * Conceptually they are merged into the same event, which is OK + * when their only purpose is to enable workers to scan for work. + * + * 5. Managing suspension of extra workers. When a worker notices + * (usually upon timeout of a wait()) that there are too few + * running threads, we may create a new thread to maintain + * parallelism level, or at least avoid starvation. Usually, extra + * threads are needed for only very short periods, yet join + * dependencies are such that we sometimes need them in + * bursts. Rather than create new threads each time this happens, + * we suspend no-longer-needed extra ones as "spares". For most + * purposes, we don't distinguish "extra" spare threads from + * normal "core" threads: On each call to preStep (the only point + * at which we can do this) a worker checks to see if there are + * now too many running workers, and if so, suspends itself. + * Method helpMaintainParallelism looks for suspended threads to + * resume before considering creating a new replacement. The + * spares themselves are encoded on another variant of a Treiber + * Stack, headed at field "spareWaiters". Note that the use of + * spares is intrinsically racy. One thread may become a spare at + * about the same time as another is needlessly being created. We + * counteract this and related slop in part by requiring resumed + * spares to immediately recheck (in preStep) to see whether they + * should re-suspend. + * + * 6. Killing off unneeded workers. A timeout mechanism is used to + * shed unused workers: The oldest (first) event queue waiter uses + * a timed rather than hard wait. When this wait times out without + * a normal wakeup, it tries to shutdown any one (for convenience + * the newest) other spare or event waiter via + * tryShutdownUnusedWorker. This eventually reduces the number of + * worker threads to a minimum of one after a long enough period + * without use. + * + * 7. Deciding when to create new workers. The main dynamic + * control in this class is deciding when to create extra threads + * in method helpMaintainParallelism. We would like to keep + * exactly #parallelism threads running, which is an impossible + * task. We always need to create one when the number of running + * threads would become zero and all workers are busy. Beyond + * this, we must rely on heuristics that work well in the + * presence of transient phenomena such as GC stalls, dynamic + * compilation, and wake-up lags. These transients are extremely + * common -- we are normally trying to fully saturate the CPUs on + * a machine, so almost any activity other than running tasks + * impedes accuracy. Our main defense is to allow parallelism to + * lapse for a while during joins, and use a timeout to see if, + * after the resulting settling, there is still a need for + * additional workers. This also better copes with the fact that + * some of the methods in this class tend to never become compiled + * (but are interpreted), so some components of the entire set of + * controls might execute 100 times faster than others. And + * similarly for cases where the apparent lack of work is just due + * to GC stalls and other transient system activity. + * + * Beware that there is a lot of representation-level coupling + * among classes ForkJoinPool, ForkJoinWorkerThread, and + * ForkJoinTask. For example, direct access to "workers" array by + * workers, and direct access to ForkJoinTask.status by both + * ForkJoinPool and ForkJoinWorkerThread. There is little point + * trying to reduce this, since any associated future changes in + * representations will need to be accompanied by algorithmic + * changes anyway. + * + * Style notes: There are lots of inline assignments (of form + * "while ((local = field) != 0)") which are usually the simplest + * way to ensure the required read orderings (which are sometimes + * critical). Also several occurrences of the unusual "do {} + * while (!cas...)" which is the simplest way to force an update of + * a CAS'ed variable. There are also other coding oddities that + * help some methods perform reasonably even when interpreted (not + * compiled), at the expense of some messy constructions that + * reduce byte code counts. + * + * The order of declarations in this file is: (1) statics (2) + * fields (along with constants used when unpacking some of them) + * (3) internal control methods (4) callbacks and other support + * for ForkJoinTask and ForkJoinWorkerThread classes, (5) exported + * methods (plus a few little helpers). */ - /** Mask for packing and unpacking shorts */ - private static final int shortMask = 0xffff; - - /** Max pool size -- must be a power of two minus 1 */ - private static final int MAX_THREADS = 0x7FFF; - /** * Factory for creating new {@link ForkJoinWorkerThread}s. * A {@code ForkJoinWorkerThreadFactory} must be defined and used @@ -151,14 +414,10 @@ public class ForkJoinPool extends AbstractExecutorService { * Default ForkJoinWorkerThreadFactory implementation; creates a * new ForkJoinWorkerThread. */ - static class DefaultForkJoinWorkerThreadFactory + static class DefaultForkJoinWorkerThreadFactory implements ForkJoinWorkerThreadFactory { public ForkJoinWorkerThread newThread(ForkJoinPool pool) { - try { - return new ForkJoinWorkerThread(pool); - } catch (OutOfMemoryError oom) { - return null; - } + return new ForkJoinWorkerThread(pool); } } @@ -194,508 +453,922 @@ public class ForkJoinPool extends AbstractExecutorService { new AtomicInteger(); /** - * Array holding all worker threads in the pool. Initialized upon - * first use. Array size must be a power of two. Updates and - * replacements are protected by workerLock, but it is always kept - * in a consistent enough state to be randomly accessed without - * locking by workers performing work-stealing. + * The time to block in a join (see awaitJoin) before checking if + * a new worker should be (re)started to maintain parallelism + * level. The value should be short enough to maintain global + * responsiveness and progress but long enough to avoid + * counterproductive firings during GC stalls or unrelated system + * activity, and to not bog down systems with continual re-firings + * on GCs or legitimately long waits. */ - volatile ForkJoinWorkerThread[] workers; + private static final long JOIN_TIMEOUT_MILLIS = 250L; // 4 per second /** - * Lock protecting access to workers. + * The wakeup interval (in nanoseconds) for the oldest worker + * waiting for an event to invoke tryShutdownUnusedWorker to + * shrink the number of workers. The exact value does not matter + * too much. It must be short enough to release resources during + * sustained periods of idleness, but not so short that threads + * are continually re-created. */ - private final ReentrantLock workerLock; + private static final long SHRINK_RATE_NANOS = + 30L * 1000L * 1000L * 1000L; // 2 per minute /** - * Condition for awaitTermination. + * Absolute bound for parallelism level. Twice this number plus + * one (i.e., 0xfff) must fit into a 16bit field to enable + * word-packing for some counts and indices. */ - private final Condition termination; + private static final int MAX_WORKERS = 0x7fff; /** - * The uncaught exception handler used when any worker - * abruptly terminates + * Array holding all worker threads in the pool. Array size must + * be a power of two. Updates and replacements are protected by + * workerLock, but the array is always kept in a consistent enough + * state to be randomly accessed without locking by workers + * performing work-stealing, as well as other traversal-based + * methods in this class. All readers must tolerate that some + * array slots may be null. */ - private Thread.UncaughtExceptionHandler ueh; + volatile ForkJoinWorkerThread[] workers; /** - * Creation factory for worker threads. + * Queue for external submissions. */ - private final ForkJoinWorkerThreadFactory factory; + private final LinkedTransferQueue> submissionQueue; /** - * Head of stack of threads that were created to maintain - * parallelism when other threads blocked, but have since - * suspended when the parallelism level rose. + * Lock protecting updates to workers array. */ - private volatile WaitQueueNode spareStack; + private final ReentrantLock workerLock; /** - * Sum of per-thread steal counts, updated only when threads are - * idle or terminating. + * Latch released upon termination. */ - private final AtomicLong stealCount; + private final Phaser termination; /** - * Queue for external submissions. + * Creation factory for worker threads. */ - private final LinkedTransferQueue> submissionQueue; + private final ForkJoinWorkerThreadFactory factory; /** - * Head of Treiber stack for barrier sync. See below for explanation. + * Sum of per-thread steal counts, updated only when threads are + * idle or terminating. */ - private volatile WaitQueueNode syncStack; + private volatile long stealCount; /** - * The count for event barrier + * Encoded record of top of Treiber stack of threads waiting for + * events. The top 32 bits contain the count being waited for. The + * bottom 16 bits contains one plus the pool index of waiting + * worker thread. (Bits 16-31 are unused.) */ - private volatile long eventCount; + private volatile long eventWaiters; - /** - * Pool number, just for assigning useful names to worker threads - */ - private final int poolNumber; + private static final int EVENT_COUNT_SHIFT = 32; + private static final long WAITER_ID_MASK = (1L << 16) - 1L; /** - * The maximum allowed pool size + * A counter for events that may wake up worker threads: + * - Submission of a new task to the pool + * - A worker pushing a task on an empty queue + * - termination */ - private volatile int maxPoolSize; + private volatile int eventCount; /** - * The desired parallelism level, updated only under workerLock. + * Encoded record of top of Treiber stack of spare threads waiting + * for resumption. The top 16 bits contain an arbitrary count to + * avoid ABA effects. The bottom 16bits contains one plus the pool + * index of waiting worker thread. */ - private volatile int parallelism; + private volatile int spareWaiters; + + private static final int SPARE_COUNT_SHIFT = 16; + private static final int SPARE_ID_MASK = (1 << 16) - 1; /** - * True if use local fifo, not default lifo, for local polling + * Lifecycle control. The low word contains the number of workers + * that are (probably) executing tasks. This value is atomically + * incremented before a worker gets a task to run, and decremented + * when a worker has no tasks and cannot find any. Bits 16-18 + * contain runLevel value. When all are zero, the pool is + * running. Level transitions are monotonic (running -> shutdown + * -> terminating -> terminated) so each transition adds a bit. + * These are bundled together to ensure consistent read for + * termination checks (i.e., that runLevel is at least SHUTDOWN + * and active threads is zero). + * + * Notes: Most direct CASes are dependent on these bitfield + * positions. Also, this field is non-private to enable direct + * performance-sensitive CASes in ForkJoinWorkerThread. */ - private volatile boolean locallyFifo; + volatile int runState; + + // Note: The order among run level values matters. + private static final int RUNLEVEL_SHIFT = 16; + private static final int SHUTDOWN = 1 << RUNLEVEL_SHIFT; + private static final int TERMINATING = 1 << (RUNLEVEL_SHIFT + 1); + private static final int TERMINATED = 1 << (RUNLEVEL_SHIFT + 2); + private static final int ACTIVE_COUNT_MASK = (1 << RUNLEVEL_SHIFT) - 1; /** * Holds number of total (i.e., created and not yet terminated) * and running (i.e., not blocked on joins or other managed sync) - * threads, packed into one int to ensure consistent snapshot when + * threads, packed together to ensure consistent snapshot when * making decisions about creating and suspending spare - * threads. Updated only by CAS. Note: CASes in - * updateRunningCount and preJoin assume that running active count - * is in low word, so need to be modified if this changes. + * threads. Updated only by CAS. Note that adding a new worker + * requires incrementing both counts, since workers start off in + * running state. */ private volatile int workerCounts; - private static int totalCountOf(int s) { return s >>> 16; } - private static int runningCountOf(int s) { return s & shortMask; } - private static int workerCountsFor(int t, int r) { return (t << 16) + r; } + private static final int TOTAL_COUNT_SHIFT = 16; + private static final int RUNNING_COUNT_MASK = (1 << TOTAL_COUNT_SHIFT) - 1; + private static final int ONE_RUNNING = 1; + private static final int ONE_TOTAL = 1 << TOTAL_COUNT_SHIFT; /** - * Adds delta (which may be negative) to running count. This must - * be called before (with negative arg) and after (with positive) - * any managed synchronization (i.e., mainly, joins). - * - * @param delta the number to add + * The target parallelism level. + * Accessed directly by ForkJoinWorkerThreads. */ - final void updateRunningCount(int delta) { - int s; - do {} while (!casWorkerCounts(s = workerCounts, s + delta)); - } + final int parallelism; /** - * Adds delta (which may be negative) to both total and running - * count. This must be called upon creation and termination of - * worker threads. - * - * @param delta the number to add + * True if use local fifo, not default lifo, for local polling + * Read by, and replicated by ForkJoinWorkerThreads */ - private void updateWorkerCount(int delta) { - int d = delta + (delta << 16); // add to both lo and hi parts - int s; - do {} while (!casWorkerCounts(s = workerCounts, s + d)); - } + final boolean locallyFifo; /** - * Lifecycle control. High word contains runState, low word - * contains the number of workers that are (probably) executing - * tasks. This value is atomically incremented before a worker - * gets a task to run, and decremented when worker has no tasks - * and cannot find any. These two fields are bundled together to - * support correct termination triggering. Note: activeCount - * CAS'es cheat by assuming active count is in low word, so need - * to be modified if this changes + * The uncaught exception handler used when any worker abruptly + * terminates. */ - private volatile int runControl; + private final Thread.UncaughtExceptionHandler ueh; - // RunState values. Order among values matters - private static final int RUNNING = 0; - private static final int SHUTDOWN = 1; - private static final int TERMINATING = 2; - private static final int TERMINATED = 3; + /** + * Pool number, just for assigning useful names to worker threads + */ + private final int poolNumber; - private static int runStateOf(int c) { return c >>> 16; } - private static int activeCountOf(int c) { return c & shortMask; } - private static int runControlFor(int r, int a) { return (r << 16) + a; } + // Utilities for CASing fields. Note that most of these + // are usually manually inlined by callers /** - * Tries incrementing active count; fails on contention. - * Called by workers before/during executing tasks. - * - * @return true on success + * Increments running count part of workerCounts */ - final boolean tryIncrementActiveCount() { - int c = runControl; - return casRunControl(c, c+1); + final void incrementRunningCount() { + int c; + do {} while (!UNSAFE.compareAndSwapInt(this, workerCountsOffset, + c = workerCounts, + c + ONE_RUNNING)); } /** - * Tries decrementing active count; fails on contention. - * Possibly triggers termination on success. - * Called by workers when they can't find tasks. - * - * @return true on success + * Tries to decrement running count unless already zero */ - final boolean tryDecrementActiveCount() { - int c = runControl; - int nextc = c - 1; - if (!casRunControl(c, nextc)) + final boolean tryDecrementRunningCount() { + int wc = workerCounts; + if ((wc & RUNNING_COUNT_MASK) == 0) return false; - if (canTerminateOnShutdown(nextc)) - terminateOnShutdown(); - return true; + return UNSAFE.compareAndSwapInt(this, workerCountsOffset, + wc, wc - ONE_RUNNING); + } + + /** + * Forces decrement of encoded workerCounts, awaiting nonzero if + * (rarely) necessary when other count updates lag. + * + * @param dr -- either zero or ONE_RUNNING + * @param dt -- either zero or ONE_TOTAL + */ + private void decrementWorkerCounts(int dr, int dt) { + for (;;) { + int wc = workerCounts; + if ((wc & RUNNING_COUNT_MASK) - dr < 0 || + (wc >>> TOTAL_COUNT_SHIFT) - dt < 0) { + if ((runState & TERMINATED) != 0) + return; // lagging termination on a backout + Thread.yield(); + } + if (UNSAFE.compareAndSwapInt(this, workerCountsOffset, + wc, wc - (dr + dt))) + return; + } } /** - * Returns {@code true} if argument represents zero active count - * and nonzero runstate, which is the triggering condition for - * terminating on shutdown. + * Tries decrementing active count; fails on contention. + * Called when workers cannot find tasks to run. */ - private static boolean canTerminateOnShutdown(int c) { - // i.e. least bit is nonzero runState bit - return ((c & -c) >>> 16) != 0; + final boolean tryDecrementActiveCount() { + int c; + return UNSAFE.compareAndSwapInt(this, runStateOffset, + c = runState, c - 1); } /** - * Transition run state to at least the given state. Return true - * if not already at least given state. + * Advances to at least the given level. Returns true if not + * already in at least the given level. */ - private boolean transitionRunStateTo(int state) { + private boolean advanceRunLevel(int level) { for (;;) { - int c = runControl; - if (runStateOf(c) >= state) + int s = runState; + if ((s & level) != 0) return false; - if (casRunControl(c, runControlFor(state, activeCountOf(c)))) + if (UNSAFE.compareAndSwapInt(this, runStateOffset, s, s | level)) return true; } } + // workers array maintenance + /** - * Controls whether to add spares to maintain parallelism + * Records and returns a workers array index for new worker. */ - private volatile boolean maintainsParallelism; - - // Constructors + private int recordWorker(ForkJoinWorkerThread w) { + // Try using slot totalCount-1. If not available, scan and/or resize + int k = (workerCounts >>> TOTAL_COUNT_SHIFT) - 1; + final ReentrantLock lock = this.workerLock; + lock.lock(); + try { + ForkJoinWorkerThread[] ws = workers; + int n = ws.length; + if (k < 0 || k >= n || ws[k] != null) { + for (k = 0; k < n && ws[k] != null; ++k) + ; + if (k == n) + ws = Arrays.copyOf(ws, n << 1); + } + ws[k] = w; + workers = ws; // volatile array write ensures slot visibility + } finally { + lock.unlock(); + } + return k; + } /** - * Creates a {@code ForkJoinPool} with parallelism equal to {@link - * java.lang.Runtime#availableProcessors}, and using the {@linkplain - * #defaultForkJoinWorkerThreadFactory default thread factory}. - * - * @throws SecurityException if a security manager exists and - * the caller is not permitted to modify threads - * because it does not hold {@link - * java.lang.RuntimePermission}{@code ("modifyThread")} + * Nulls out record of worker in workers array. */ - public ForkJoinPool() { - this(Runtime.getRuntime().availableProcessors(), - defaultForkJoinWorkerThreadFactory); + private void forgetWorker(ForkJoinWorkerThread w) { + int idx = w.poolIndex; + // Locking helps method recordWorker avoid unnecessary expansion + final ReentrantLock lock = this.workerLock; + lock.lock(); + try { + ForkJoinWorkerThread[] ws = workers; + if (idx >= 0 && idx < ws.length && ws[idx] == w) // verify + ws[idx] = null; + } finally { + lock.unlock(); + } } /** - * Creates a {@code ForkJoinPool} with the indicated parallelism - * level and using the {@linkplain - * #defaultForkJoinWorkerThreadFactory default thread factory}. + * Final callback from terminating worker. Removes record of + * worker from array, and adjusts counts. If pool is shutting + * down, tries to complete termination. * - * @param parallelism the parallelism level - * @throws IllegalArgumentException if parallelism less than or - * equal to zero, or greater than implementation limit - * @throws SecurityException if a security manager exists and - * the caller is not permitted to modify threads - * because it does not hold {@link - * java.lang.RuntimePermission}{@code ("modifyThread")} + * @param w the worker */ - public ForkJoinPool(int parallelism) { - this(parallelism, defaultForkJoinWorkerThreadFactory); + final void workerTerminated(ForkJoinWorkerThread w) { + forgetWorker(w); + decrementWorkerCounts(w.isTrimmed()? 0 : ONE_RUNNING, ONE_TOTAL); + while (w.stealCount != 0) // collect final count + tryAccumulateStealCount(w); + tryTerminate(false); } + // Waiting for and signalling events + /** - * Creates a {@code ForkJoinPool} with parallelism equal to {@link - * java.lang.Runtime#availableProcessors}, and using the given - * thread factory. - * - * @param factory the factory for creating new threads - * @throws NullPointerException if the factory is null - * @throws SecurityException if a security manager exists and - * the caller is not permitted to modify threads - * because it does not hold {@link - * java.lang.RuntimePermission}{@code ("modifyThread")} + * Releases workers blocked on a count not equal to current count. + * Normally called after precheck that eventWaiters isn't zero to + * avoid wasted array checks. Gives up upon a change in count or + * upon releasing two workers, letting others take over. */ - public ForkJoinPool(ForkJoinWorkerThreadFactory factory) { - this(Runtime.getRuntime().availableProcessors(), factory); + private void releaseEventWaiters() { + ForkJoinWorkerThread[] ws = workers; + int n = ws.length; + long h = eventWaiters; + int ec = eventCount; + boolean releasedOne = false; + ForkJoinWorkerThread w; int id; + while ((id = ((int)(h & WAITER_ID_MASK)) - 1) >= 0 && + (int)(h >>> EVENT_COUNT_SHIFT) != ec && + id < n && (w = ws[id]) != null) { + if (UNSAFE.compareAndSwapLong(this, eventWaitersOffset, + h, w.nextWaiter)) { + LockSupport.unpark(w); + if (releasedOne) // exit on second release + break; + releasedOne = true; + } + if (eventCount != ec) + break; + h = eventWaiters; + } } /** - * Creates a {@code ForkJoinPool} with the given parallelism and - * thread factory. - * - * @param parallelism the parallelism level - * @param factory the factory for creating new threads - * @throws IllegalArgumentException if parallelism less than or - * equal to zero, or greater than implementation limit - * @throws NullPointerException if the factory is null - * @throws SecurityException if a security manager exists and - * the caller is not permitted to modify threads - * because it does not hold {@link - * java.lang.RuntimePermission}{@code ("modifyThread")} + * Tries to advance eventCount and releases waiters. Called only + * from workers. */ - public ForkJoinPool(int parallelism, ForkJoinWorkerThreadFactory factory) { - if (parallelism <= 0 || parallelism > MAX_THREADS) - throw new IllegalArgumentException(); - if (factory == null) - throw new NullPointerException(); - checkPermission(); - this.factory = factory; - this.parallelism = parallelism; - this.maxPoolSize = MAX_THREADS; - this.maintainsParallelism = true; - this.poolNumber = poolNumberGenerator.incrementAndGet(); - this.workerLock = new ReentrantLock(); - this.termination = workerLock.newCondition(); - this.stealCount = new AtomicLong(); - this.submissionQueue = new LinkedTransferQueue>(); - // worker array and workers are lazily constructed + final void signalWork() { + int c; // try to increment event count -- CAS failure OK + UNSAFE.compareAndSwapInt(this, eventCountOffset, c = eventCount, c+1); + if (eventWaiters != 0L) + releaseEventWaiters(); } /** - * Creates a new worker thread using factory. + * Adds the given worker to event queue and blocks until + * terminating or event count advances from the given value * - * @param index the index to assign worker - * @return new worker, or null if factory failed - */ - private ForkJoinWorkerThread createWorker(int index) { - Thread.UncaughtExceptionHandler h = ueh; - ForkJoinWorkerThread w = factory.newThread(this); - if (w != null) { - w.poolIndex = index; - w.setDaemon(true); - w.setAsyncMode(locallyFifo); - w.setName("ForkJoinPool-" + poolNumber + "-worker-" + index); - if (h != null) - w.setUncaughtExceptionHandler(h); + * @param w the calling worker thread + * @param ec the count + */ + private void eventSync(ForkJoinWorkerThread w, int ec) { + long nh = (((long)ec) << EVENT_COUNT_SHIFT) | ((long)(w.poolIndex+1)); + long h; + while ((runState < SHUTDOWN || !tryTerminate(false)) && + (((int)((h = eventWaiters) & WAITER_ID_MASK)) == 0 || + (int)(h >>> EVENT_COUNT_SHIFT) == ec) && + eventCount == ec) { + if (UNSAFE.compareAndSwapLong(this, eventWaitersOffset, + w.nextWaiter = h, nh)) { + awaitEvent(w, ec); + break; + } } - return w; } /** - * Returns a good size for worker array given pool size. - * Currently requires size to be a power of two. - */ - private static int arraySizeFor(int poolSize) { - if (poolSize <= 1) - return 1; - // See Hackers Delight, sec 3.2 - int c = poolSize >= MAX_THREADS ? MAX_THREADS : (poolSize - 1); - c |= c >>> 1; - c |= c >>> 2; - c |= c >>> 4; - c |= c >>> 8; - c |= c >>> 16; - return c + 1; + * Blocks the given worker (that has already been entered as an + * event waiter) until terminating or event count advances from + * the given value. The oldest (first) waiter uses a timed wait to + * occasionally one-by-one shrink the number of workers (to a + * minimum of one) if the pool has not been used for extended + * periods. + * + * @param w the calling worker thread + * @param ec the count + */ + private void awaitEvent(ForkJoinWorkerThread w, int ec) { + while (eventCount == ec) { + if (tryAccumulateStealCount(w)) { // transfer while idle + boolean untimed = (w.nextWaiter != 0L || + (workerCounts & RUNNING_COUNT_MASK) <= 1); + long startTime = untimed? 0 : System.nanoTime(); + Thread.interrupted(); // clear/ignore interrupt + if (eventCount != ec || w.runState != 0 || + runState >= TERMINATING) // recheck after clear + break; + if (untimed) + LockSupport.park(w); + else { + LockSupport.parkNanos(w, SHRINK_RATE_NANOS); + if (eventCount != ec || w.runState != 0 || + runState >= TERMINATING) + break; + if (System.nanoTime() - startTime >= SHRINK_RATE_NANOS) + tryShutdownUnusedWorker(ec); + } + } + } } + // Maintaining parallelism + /** - * Creates or resizes array if necessary to hold newLength. - * Call only under exclusion. - * - * @return the array + * Pushes worker onto the spare stack. */ - private ForkJoinWorkerThread[] ensureWorkerArrayCapacity(int newLength) { - ForkJoinWorkerThread[] ws = workers; - if (ws == null) - return workers = new ForkJoinWorkerThread[arraySizeFor(newLength)]; - else if (newLength > ws.length) - return workers = Arrays.copyOf(ws, arraySizeFor(newLength)); - else - return ws; + final void pushSpare(ForkJoinWorkerThread w) { + int ns = (++w.spareCount << SPARE_COUNT_SHIFT) | (w.poolIndex + 1); + do {} while (!UNSAFE.compareAndSwapInt(this, spareWaitersOffset, + w.nextSpare = spareWaiters,ns)); } /** - * Tries to shrink workers into smaller array after one or more terminate. + * Tries (once) to resume a spare if the number of running + * threads is less than target. */ - private void tryShrinkWorkerArray() { + private void tryResumeSpare() { + int sw, id; ForkJoinWorkerThread[] ws = workers; - if (ws != null) { - int len = ws.length; - int last = len - 1; - while (last >= 0 && ws[last] == null) - --last; - int newLength = arraySizeFor(last+1); - if (newLength < len) - workers = Arrays.copyOf(ws, newLength); + int n = ws.length; + ForkJoinWorkerThread w; + if ((sw = spareWaiters) != 0 && + (id = (sw & SPARE_ID_MASK) - 1) >= 0 && + id < n && (w = ws[id]) != null && + (workerCounts & RUNNING_COUNT_MASK) < parallelism && + spareWaiters == sw && + UNSAFE.compareAndSwapInt(this, spareWaitersOffset, + sw, w.nextSpare)) { + int c; // increment running count before resume + do {} while (!UNSAFE.compareAndSwapInt + (this, workerCountsOffset, + c = workerCounts, c + ONE_RUNNING)); + if (w.tryUnsuspend()) + LockSupport.unpark(w); + else // back out if w was shutdown + decrementWorkerCounts(ONE_RUNNING, 0); } } /** - * Initializes workers if necessary. - */ - final void ensureWorkerInitialization() { - ForkJoinWorkerThread[] ws = workers; - if (ws == null) { - final ReentrantLock lock = this.workerLock; - lock.lock(); - try { - ws = workers; - if (ws == null) { - int ps = parallelism; - ws = ensureWorkerArrayCapacity(ps); - for (int i = 0; i < ps; ++i) { - ForkJoinWorkerThread w = createWorker(i); - if (w != null) { - ws[i] = w; - w.start(); - updateWorkerCount(1); - } + * Tries to increase the number of running workers if below target + * parallelism: If a spare exists tries to resume it via + * tryResumeSpare. Otherwise, if not enough total workers or all + * existing workers are busy, adds a new worker. In all cases also + * helps wake up releasable workers waiting for work. + */ + private void helpMaintainParallelism() { + int pc = parallelism; + int wc, rs, tc; + while (((wc = workerCounts) & RUNNING_COUNT_MASK) < pc && + (rs = runState) < TERMINATING) { + if (spareWaiters != 0) + tryResumeSpare(); + else if ((tc = wc >>> TOTAL_COUNT_SHIFT) >= MAX_WORKERS || + (tc >= pc && (rs & ACTIVE_COUNT_MASK) != tc)) + break; // enough total + else if (runState == rs && workerCounts == wc && + UNSAFE.compareAndSwapInt(this, workerCountsOffset, wc, + wc + (ONE_RUNNING|ONE_TOTAL))) { + ForkJoinWorkerThread w = null; + try { + w = factory.newThread(this); + } finally { // adjust on null or exceptional factory return + if (w == null) { + decrementWorkerCounts(ONE_RUNNING, ONE_TOTAL); + tryTerminate(false); // handle failure during shutdown } } - } finally { - lock.unlock(); + if (w == null) + break; + w.start(recordWorker(w), ueh); + if ((workerCounts >>> TOTAL_COUNT_SHIFT) >= pc) { + int c; // advance event count + UNSAFE.compareAndSwapInt(this, eventCountOffset, + c = eventCount, c+1); + break; // add at most one unless total below target + } } } + if (eventWaiters != 0L) + releaseEventWaiters(); } /** - * Worker creation and startup for threads added via setParallelism. + * Callback from the oldest waiter in awaitEvent waking up after a + * period of non-use. If all workers are idle, tries (once) to + * shutdown an event waiter or a spare, if one exists. Note that + * we don't need CAS or locks here because the method is called + * only from one thread occasionally waking (and even misfires are + * OK). Note that until the shutdown worker fully terminates, + * workerCounts will overestimate total count, which is tolerable. + * + * @param ec the event count waited on by caller (to abort + * attempt if count has since changed). */ - private void createAndStartAddedWorkers() { - resumeAllSpares(); // Allow spares to convert to nonspare - int ps = parallelism; - ForkJoinWorkerThread[] ws = ensureWorkerArrayCapacity(ps); - int len = ws.length; - // Sweep through slots, to keep lowest indices most populated - int k = 0; - while (k < len) { - if (ws[k] != null) { - ++k; - continue; + private void tryShutdownUnusedWorker(int ec) { + if (runState == 0 && eventCount == ec) { // only trigger if all idle + ForkJoinWorkerThread[] ws = workers; + int n = ws.length; + ForkJoinWorkerThread w = null; + boolean shutdown = false; + int sw; + long h; + if ((sw = spareWaiters) != 0) { // prefer killing spares + int id = (sw & SPARE_ID_MASK) - 1; + if (id >= 0 && id < n && (w = ws[id]) != null && + UNSAFE.compareAndSwapInt(this, spareWaitersOffset, + sw, w.nextSpare)) + shutdown = true; } - int s = workerCounts; - int tc = totalCountOf(s); - int rc = runningCountOf(s); - if (rc >= ps || tc >= ps) - break; - if (casWorkerCounts (s, workerCountsFor(tc+1, rc+1))) { - ForkJoinWorkerThread w = createWorker(k); - if (w != null) { - ws[k++] = w; - w.start(); - } - else { - updateWorkerCount(-1); // back out on failed creation + else if ((h = eventWaiters) != 0L) { + long nh; + int id = ((int)(h & WAITER_ID_MASK)) - 1; + if (id >= 0 && id < n && (w = ws[id]) != null && + (nh = w.nextWaiter) != 0L && // keep at least one worker + UNSAFE.compareAndSwapLong(this, eventWaitersOffset, h, nh)) + shutdown = true; + } + if (w != null && shutdown) { + w.shutdown(); + LockSupport.unpark(w); + } + } + releaseEventWaiters(); // in case of interference + } + + /** + * Callback from workers invoked upon each top-level action (i.e., + * stealing a task or taking a submission and running it). + * Performs one or more of the following: + * + * 1. If the worker is active and either did not run a task + * or there are too many workers, try to set its active status + * to inactive and update activeCount. On contention, we may + * try again in this or a subsequent call. + * + * 2. If not enough total workers, help create some. + * + * 3. If there are too many running workers, suspend this worker + * (first forcing inactive if necessary). If it is not needed, + * it may be shutdown while suspended (via + * tryShutdownUnusedWorker). Otherwise, upon resume it + * rechecks running thread count and need for event sync. + * + * 4. If worker did not run a task, await the next task event via + * eventSync if necessary (first forcing inactivation), upon + * which the worker may be shutdown via + * tryShutdownUnusedWorker. Otherwise, help release any + * existing event waiters that are now releasable, + * + * @param w the worker + * @param ran true if worker ran a task since last call to this method + */ + final void preStep(ForkJoinWorkerThread w, boolean ran) { + int wec = w.lastEventCount; + boolean active = w.active; + boolean inactivate = false; + int pc = parallelism; + int rs; + while (w.runState == 0 && (rs = runState) < TERMINATING) { + if ((inactivate || (active && (rs & ACTIVE_COUNT_MASK) >= pc)) && + UNSAFE.compareAndSwapInt(this, runStateOffset, rs, rs - 1)) + inactivate = active = w.active = false; + int wc = workerCounts; + if ((wc & RUNNING_COUNT_MASK) > pc) { + if (!(inactivate |= active) && // must inactivate to suspend + workerCounts == wc && // try to suspend as spare + UNSAFE.compareAndSwapInt(this, workerCountsOffset, + wc, wc - ONE_RUNNING)) + w.suspendAsSpare(); + } + else if ((wc >>> TOTAL_COUNT_SHIFT) < pc) + helpMaintainParallelism(); // not enough workers + else if (!ran) { + long h = eventWaiters; + int ec = eventCount; + if (h != 0L && (int)(h >>> EVENT_COUNT_SHIFT) != ec) + releaseEventWaiters(); // release others before waiting + else if (ec != wec) { + w.lastEventCount = ec; // no need to wait break; } + else if (!(inactivate |= active)) + eventSync(w, wec); // must inactivate before sync } + else + break; } } - // Execution methods + /** + * Helps and/or blocks awaiting join of the given task. + * See above for explanation. + * + * @param joinMe the task to join + * @param worker the current worker thread + */ + final void awaitJoin(ForkJoinTask joinMe, ForkJoinWorkerThread worker) { + int retries = 2 + (parallelism >> 2); // #helpJoins before blocking + while (joinMe.status >= 0) { + int wc; + worker.helpJoinTask(joinMe); + if (joinMe.status < 0) + break; + else if (retries > 0) + --retries; + else if (((wc = workerCounts) & RUNNING_COUNT_MASK) != 0 && + UNSAFE.compareAndSwapInt(this, workerCountsOffset, + wc, wc - ONE_RUNNING)) { + int stat, c; long h; + while ((stat = joinMe.status) >= 0 && + (h = eventWaiters) != 0L && // help release others + (int)(h >>> EVENT_COUNT_SHIFT) != eventCount) + releaseEventWaiters(); + if (stat >= 0 && + ((workerCounts & RUNNING_COUNT_MASK) == 0 || + (stat = + joinMe.internalAwaitDone(JOIN_TIMEOUT_MILLIS)) >= 0)) + helpMaintainParallelism(); // timeout or no running workers + do {} while (!UNSAFE.compareAndSwapInt + (this, workerCountsOffset, + c = workerCounts, c + ONE_RUNNING)); + if (stat < 0) + break; // else restart + } + } + } /** - * Common code for execute, invoke and submit + * Same idea as awaitJoin, but no helping, retries, or timeouts. */ - private void doSubmit(ForkJoinTask task) { - if (task == null) - throw new NullPointerException(); - if (isShutdown()) - throw new RejectedExecutionException(); - if (workers == null) - ensureWorkerInitialization(); - submissionQueue.offer(task); - signalIdleWorkers(); + final void awaitBlocker(ManagedBlocker blocker) + throws InterruptedException { + while (!blocker.isReleasable()) { + int wc = workerCounts; + if ((wc & RUNNING_COUNT_MASK) != 0 && + UNSAFE.compareAndSwapInt(this, workerCountsOffset, + wc, wc - ONE_RUNNING)) { + try { + while (!blocker.isReleasable()) { + long h = eventWaiters; + if (h != 0L && + (int)(h >>> EVENT_COUNT_SHIFT) != eventCount) + releaseEventWaiters(); + else if ((workerCounts & RUNNING_COUNT_MASK) == 0 && + runState < TERMINATING) + helpMaintainParallelism(); + else if (blocker.block()) + break; + } + } finally { + int c; + do {} while (!UNSAFE.compareAndSwapInt + (this, workerCountsOffset, + c = workerCounts, c + ONE_RUNNING)); + } + break; + } + } } /** - * Performs the given task, returning its result upon completion. + * Possibly initiates and/or completes termination. * - * @param task the task - * @return the task's result - * @throws NullPointerException if the task is null - * @throws RejectedExecutionException if the task cannot be - * scheduled for execution - */ - public T invoke(ForkJoinTask task) { - doSubmit(task); - return task.join(); + * @param now if true, unconditionally terminate, else only + * if shutdown and empty queue and no active workers + * @return true if now terminating or terminated + */ + private boolean tryTerminate(boolean now) { + if (now) + advanceRunLevel(SHUTDOWN); // ensure at least SHUTDOWN + else if (runState < SHUTDOWN || + !submissionQueue.isEmpty() || + (runState & ACTIVE_COUNT_MASK) != 0) + return false; + + if (advanceRunLevel(TERMINATING)) + startTerminating(); + + // Finish now if all threads terminated; else in some subsequent call + if ((workerCounts >>> TOTAL_COUNT_SHIFT) == 0) { + advanceRunLevel(TERMINATED); + termination.arrive(); + } + return true; } /** - * Arranges for (asynchronous) execution of the given task. + * Actions on transition to TERMINATING * - * @param task the task - * @throws NullPointerException if the task is null - * @throws RejectedExecutionException if the task cannot be - * scheduled for execution + * Runs up to four passes through workers: (0) shutting down each + * (without waking up if parked) to quickly spread notifications + * without unnecessary bouncing around event queues etc (1) wake + * up and help cancel tasks (2) interrupt (3) mop up races with + * interrupted workers + */ + private void startTerminating() { + cancelSubmissions(); + for (int passes = 0; passes < 4 && workerCounts != 0; ++passes) { + int c; // advance event count + UNSAFE.compareAndSwapInt(this, eventCountOffset, + c = eventCount, c+1); + eventWaiters = 0L; // clobber lists + spareWaiters = 0; + for (ForkJoinWorkerThread w : workers) { + if (w != null) { + w.shutdown(); + if (passes > 0 && !w.isTerminated()) { + w.cancelTasks(); + LockSupport.unpark(w); + if (passes > 1) { + try { + w.interrupt(); + } catch (SecurityException ignore) { + } + } + } + } + } + } + } + + /** + * Clears out and cancels submissions, ignoring exceptions. */ - public void execute(ForkJoinTask task) { - doSubmit(task); + private void cancelSubmissions() { + ForkJoinTask task; + while ((task = submissionQueue.poll()) != null) { + try { + task.cancel(false); + } catch (Throwable ignore) { + } + } } - // AbstractExecutorService methods + // misc support for ForkJoinWorkerThread /** - * @throws NullPointerException if the task is null - * @throws RejectedExecutionException if the task cannot be - * scheduled for execution + * Returns pool number. */ - public void execute(Runnable task) { - ForkJoinTask job; - if (task instanceof ForkJoinTask) // avoid re-wrap - job = (ForkJoinTask) task; - else - job = ForkJoinTask.adapt(task, null); - doSubmit(job); + final int getPoolNumber() { + return poolNumber; + } + + /** + * Tries to accumulate steal count from a worker, clearing + * the worker's value if successful. + * + * @return true if worker steal count now zero + */ + final boolean tryAccumulateStealCount(ForkJoinWorkerThread w) { + int sc = w.stealCount; + long c = stealCount; + // CAS even if zero, for fence effects + if (UNSAFE.compareAndSwapLong(this, stealCountOffset, c, c + sc)) { + if (sc != 0) + w.stealCount = 0; + return true; + } + return sc == 0; } /** + * Returns the approximate (non-atomic) number of idle threads per + * active thread. + */ + final int idlePerActive() { + int pc = parallelism; // use parallelism, not rc + int ac = runState; // no mask -- artificially boosts during shutdown + // Use exact results for small values, saturate past 4 + return ((pc <= ac) ? 0 : + (pc >>> 1 <= ac) ? 1 : + (pc >>> 2 <= ac) ? 3 : + pc >>> 3); + } + + // Public and protected methods + + // Constructors + + /** + * Creates a {@code ForkJoinPool} with parallelism equal to {@link + * java.lang.Runtime#availableProcessors}, using the {@linkplain + * #defaultForkJoinWorkerThreadFactory default thread factory}, + * no UncaughtExceptionHandler, and non-async LIFO processing mode. + * + * @throws SecurityException if a security manager exists and + * the caller is not permitted to modify threads + * because it does not hold {@link + * java.lang.RuntimePermission}{@code ("modifyThread")} + */ + public ForkJoinPool() { + this(Runtime.getRuntime().availableProcessors(), + defaultForkJoinWorkerThreadFactory, null, false); + } + + /** + * Creates a {@code ForkJoinPool} with the indicated parallelism + * level, the {@linkplain + * #defaultForkJoinWorkerThreadFactory default thread factory}, + * no UncaughtExceptionHandler, and non-async LIFO processing mode. + * + * @param parallelism the parallelism level + * @throws IllegalArgumentException if parallelism less than or + * equal to zero, or greater than implementation limit + * @throws SecurityException if a security manager exists and + * the caller is not permitted to modify threads + * because it does not hold {@link + * java.lang.RuntimePermission}{@code ("modifyThread")} + */ + public ForkJoinPool(int parallelism) { + this(parallelism, defaultForkJoinWorkerThreadFactory, null, false); + } + + /** + * Creates a {@code ForkJoinPool} with the given parameters. + * + * @param parallelism the parallelism level. For default value, + * use {@link java.lang.Runtime#availableProcessors}. + * @param factory the factory for creating new threads. For default value, + * use {@link #defaultForkJoinWorkerThreadFactory}. + * @param handler the handler for internal worker threads that + * terminate due to unrecoverable errors encountered while executing + * tasks. For default value, use {@code null}. + * @param asyncMode if true, + * establishes local first-in-first-out scheduling mode for forked + * tasks that are never joined. This mode may be more appropriate + * than default locally stack-based mode in applications in which + * worker threads only process event-style asynchronous tasks. + * For default value, use {@code false}. + * @throws IllegalArgumentException if parallelism less than or + * equal to zero, or greater than implementation limit + * @throws NullPointerException if the factory is null + * @throws SecurityException if a security manager exists and + * the caller is not permitted to modify threads + * because it does not hold {@link + * java.lang.RuntimePermission}{@code ("modifyThread")} + */ + public ForkJoinPool(int parallelism, + ForkJoinWorkerThreadFactory factory, + Thread.UncaughtExceptionHandler handler, + boolean asyncMode) { + checkPermission(); + if (factory == null) + throw new NullPointerException(); + if (parallelism <= 0 || parallelism > MAX_WORKERS) + throw new IllegalArgumentException(); + this.parallelism = parallelism; + this.factory = factory; + this.ueh = handler; + this.locallyFifo = asyncMode; + int arraySize = initialArraySizeFor(parallelism); + this.workers = new ForkJoinWorkerThread[arraySize]; + this.submissionQueue = new LinkedTransferQueue>(); + this.workerLock = new ReentrantLock(); + this.termination = new Phaser(1); + this.poolNumber = poolNumberGenerator.incrementAndGet(); + } + + /** + * Returns initial power of two size for workers array. + * @param pc the initial parallelism level + */ + private static int initialArraySizeFor(int pc) { + // If possible, initially allocate enough space for one spare + int size = pc < MAX_WORKERS ? pc + 1 : MAX_WORKERS; + // See Hackers Delight, sec 3.2. We know MAX_WORKERS < (1 >>> 16) + size |= size >>> 1; + size |= size >>> 2; + size |= size >>> 4; + size |= size >>> 8; + return size + 1; + } + + // Execution methods + + /** + * Common code for execute, invoke and submit + */ + private void doSubmit(ForkJoinTask task) { + if (task == null) + throw new NullPointerException(); + if (runState >= SHUTDOWN) + throw new RejectedExecutionException(); + submissionQueue.offer(task); + int c; // try to increment event count -- CAS failure OK + UNSAFE.compareAndSwapInt(this, eventCountOffset, c = eventCount, c+1); + helpMaintainParallelism(); // create, start, or resume some workers + } + + /** + * Performs the given task, returning its result upon completion. + * + * @param task the task + * @return the task's result * @throws NullPointerException if the task is null * @throws RejectedExecutionException if the task cannot be * scheduled for execution */ - public ForkJoinTask submit(Callable task) { - ForkJoinTask job = ForkJoinTask.adapt(task); - doSubmit(job); - return job; + public T invoke(ForkJoinTask task) { + doSubmit(task); + return task.join(); } /** + * Arranges for (asynchronous) execution of the given task. + * + * @param task the task * @throws NullPointerException if the task is null * @throws RejectedExecutionException if the task cannot be * scheduled for execution */ - public ForkJoinTask submit(Runnable task, T result) { - ForkJoinTask job = ForkJoinTask.adapt(task, result); - doSubmit(job); - return job; + public void execute(ForkJoinTask task) { + doSubmit(task); } + // AbstractExecutorService methods + /** * @throws NullPointerException if the task is null * @throws RejectedExecutionException if the task cannot be * scheduled for execution */ - public ForkJoinTask submit(Runnable task) { + public void execute(Runnable task) { ForkJoinTask job; if (task instanceof ForkJoinTask) // avoid re-wrap job = (ForkJoinTask) task; else job = ForkJoinTask.adapt(task, null); doSubmit(job); - return job; } /** @@ -712,6 +1385,42 @@ public class ForkJoinPool extends AbstractExecutorService { return task; } + /** + * @throws NullPointerException if the task is null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + */ + public ForkJoinTask submit(Callable task) { + ForkJoinTask job = ForkJoinTask.adapt(task); + doSubmit(job); + return job; + } + + /** + * @throws NullPointerException if the task is null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + */ + public ForkJoinTask submit(Runnable task, T result) { + ForkJoinTask job = ForkJoinTask.adapt(task, result); + doSubmit(job); + return job; + } + + /** + * @throws NullPointerException if the task is null + * @throws RejectedExecutionException if the task cannot be + * scheduled for execution + */ + public ForkJoinTask submit(Runnable task) { + ForkJoinTask job; + if (task instanceof ForkJoinTask) // avoid re-wrap + job = (ForkJoinTask) task; + else + job = ForkJoinTask.adapt(task, null); + doSubmit(job); + return job; + } /** * @throws NullPointerException {@inheritDoc} @@ -725,7 +1434,7 @@ public class ForkJoinPool extends AbstractExecutorService { invoke(new InvokeAll(forkJoinTasks)); @SuppressWarnings({"unchecked", "rawtypes"}) - List> futures = (List>) (List) forkJoinTasks; + List> futures = (List>) (List) forkJoinTasks; return futures; } @@ -739,8 +1448,6 @@ public class ForkJoinPool extends AbstractExecutorService { private static final long serialVersionUID = -7914297376763021607L; } - // Configuration and status settings and queries - /** * Returns the factory used for constructing new workers. * @@ -757,84 +1464,7 @@ public class ForkJoinPool extends AbstractExecutorService { * @return the handler, or {@code null} if none */ public Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() { - Thread.UncaughtExceptionHandler h; - final ReentrantLock lock = this.workerLock; - lock.lock(); - try { - h = ueh; - } finally { - lock.unlock(); - } - return h; - } - - /** - * Sets the handler for internal worker threads that terminate due - * to unrecoverable errors encountered while executing tasks. - * Unless set, the current default or ThreadGroup handler is used - * as handler. - * - * @param h the new handler - * @return the old handler, or {@code null} if none - * @throws SecurityException if a security manager exists and - * the caller is not permitted to modify threads - * because it does not hold {@link - * java.lang.RuntimePermission}{@code ("modifyThread")} - */ - public Thread.UncaughtExceptionHandler - setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler h) { - checkPermission(); - Thread.UncaughtExceptionHandler old = null; - final ReentrantLock lock = this.workerLock; - lock.lock(); - try { - old = ueh; - ueh = h; - ForkJoinWorkerThread[] ws = workers; - if (ws != null) { - for (int i = 0; i < ws.length; ++i) { - ForkJoinWorkerThread w = ws[i]; - if (w != null) - w.setUncaughtExceptionHandler(h); - } - } - } finally { - lock.unlock(); - } - return old; - } - - - /** - * Sets the target parallelism level of this pool. - * - * @param parallelism the target parallelism - * @throws IllegalArgumentException if parallelism less than or - * equal to zero or greater than maximum size bounds - * @throws SecurityException if a security manager exists and - * the caller is not permitted to modify threads - * because it does not hold {@link - * java.lang.RuntimePermission}{@code ("modifyThread")} - */ - public void setParallelism(int parallelism) { - checkPermission(); - if (parallelism <= 0 || parallelism > maxPoolSize) - throw new IllegalArgumentException(); - final ReentrantLock lock = this.workerLock; - lock.lock(); - try { - if (isProcessingTasks()) { - int p = this.parallelism; - this.parallelism = parallelism; - if (parallelism > p) - createAndStartAddedWorkers(); - else - trimSpares(); - } - } finally { - lock.unlock(); - } - signalIdleWorkers(); + return ueh; } /** @@ -848,92 +1478,14 @@ public class ForkJoinPool extends AbstractExecutorService { /** * Returns the number of worker threads that have started but not - * yet terminated. This result returned by this method may differ + * yet terminated. The result returned by this method may differ * from {@link #getParallelism} when threads are created to * maintain parallelism when others are cooperatively blocked. * * @return the number of worker threads */ public int getPoolSize() { - return totalCountOf(workerCounts); - } - - /** - * Returns the maximum number of threads allowed to exist in the - * pool. Unless set using {@link #setMaximumPoolSize}, the - * maximum is an implementation-defined value designed only to - * prevent runaway growth. - * - * @return the maximum - */ - public int getMaximumPoolSize() { - return maxPoolSize; - } - - /** - * Sets the maximum number of threads allowed to exist in the - * pool. The given value should normally be greater than or equal - * to the {@link #getParallelism parallelism} level. Setting this - * value has no effect on current pool size. It controls - * construction of new threads. - * - * @throws IllegalArgumentException if negative or greater than - * internal implementation limit - */ - public void setMaximumPoolSize(int newMax) { - if (newMax < 0 || newMax > MAX_THREADS) - throw new IllegalArgumentException(); - maxPoolSize = newMax; - } - - - /** - * Returns {@code true} if this pool dynamically maintains its - * target parallelism level. If false, new threads are added only - * to avoid possible starvation. This setting is by default true. - * - * @return {@code true} if maintains parallelism - */ - public boolean getMaintainsParallelism() { - return maintainsParallelism; - } - - /** - * Sets whether this pool dynamically maintains its target - * parallelism level. If false, new threads are added only to - * avoid possible starvation. - * - * @param enable {@code true} to maintain parallelism - */ - public void setMaintainsParallelism(boolean enable) { - maintainsParallelism = enable; - } - - /** - * Establishes local first-in-first-out scheduling mode for forked - * tasks that are never joined. This mode may be more appropriate - * than default locally stack-based mode in applications in which - * worker threads only process asynchronous tasks. This method is - * designed to be invoked only when the pool is quiescent, and - * typically only before any tasks are submitted. The effects of - * invocations at other times may be unpredictable. - * - * @param async if {@code true}, use locally FIFO scheduling - * @return the previous mode - * @see #getAsyncMode - */ - public boolean setAsyncMode(boolean async) { - boolean oldMode = locallyFifo; - locallyFifo = async; - ForkJoinWorkerThread[] ws = workers; - if (ws != null) { - for (int i = 0; i < ws.length; ++i) { - ForkJoinWorkerThread t = ws[i]; - if (t != null) - t.setAsyncMode(async); - } - } - return oldMode; + return workerCounts >>> TOTAL_COUNT_SHIFT; } /** @@ -941,7 +1493,6 @@ public class ForkJoinPool extends AbstractExecutorService { * scheduling mode for forked tasks that are never joined. * * @return {@code true} if this pool uses async mode - * @see #setAsyncMode */ public boolean getAsyncMode() { return locallyFifo; @@ -950,12 +1501,13 @@ public class ForkJoinPool extends AbstractExecutorService { /** * Returns an estimate of the number of worker threads that are * not blocked waiting to join tasks or for other managed - * synchronization. + * synchronization. This method may overestimate the + * number of running threads. * * @return the number of worker threads */ public int getRunningThreadCount() { - return runningCountOf(workerCounts); + return workerCounts & RUNNING_COUNT_MASK; } /** @@ -966,19 +1518,7 @@ public class ForkJoinPool extends AbstractExecutorService { * @return the number of active threads */ public int getActiveThreadCount() { - return activeCountOf(runControl); - } - - /** - * Returns an estimate of the number of threads that are currently - * idle waiting for tasks. This method may underestimate the - * number of idle threads. - * - * @return the number of idle threads - */ - final int getIdleThreadCount() { - int c = runningCountOf(workerCounts) - activeCountOf(runControl); - return (c <= 0) ? 0 : c; + return runState & ACTIVE_COUNT_MASK; } /** @@ -993,7 +1533,7 @@ public class ForkJoinPool extends AbstractExecutorService { * @return {@code true} if all threads are currently idle */ public boolean isQuiescent() { - return activeCountOf(runControl) == 0; + return (runState & ACTIVE_COUNT_MASK) == 0; } /** @@ -1008,17 +1548,7 @@ public class ForkJoinPool extends AbstractExecutorService { * @return the number of steals */ public long getStealCount() { - return stealCount.get(); - } - - /** - * Accumulates steal count from a worker. - * Call only when worker known to be idle. - */ - private void updateStealCount(ForkJoinWorkerThread w) { - int sc = w.getAndClearStealCount(); - if (sc != 0) - stealCount.addAndGet(sc); + return stealCount; } /** @@ -1033,14 +1563,9 @@ public class ForkJoinPool extends AbstractExecutorService { */ public long getQueuedTaskCount() { long count = 0; - ForkJoinWorkerThread[] ws = workers; - if (ws != null) { - for (int i = 0; i < ws.length; ++i) { - ForkJoinWorkerThread t = ws[i]; - if (t != null) - count += t.getQueueSize(); - } - } + for (ForkJoinWorkerThread w : workers) + if (w != null) + count += w.getQueueSize(); return count; } @@ -1094,16 +1619,11 @@ public class ForkJoinPool extends AbstractExecutorService { * @return the number of elements transferred */ protected int drainTasksTo(Collection> c) { - int n = submissionQueue.drainTo(c); - ForkJoinWorkerThread[] ws = workers; - if (ws != null) { - for (int i = 0; i < ws.length; ++i) { - ForkJoinWorkerThread w = ws[i]; - if (w != null) - n += w.drainTasksTo(c); - } - } - return n; + int count = submissionQueue.drainTo(c); + for (ForkJoinWorkerThread w : workers) + if (w != null) + count += w.drainTasksTo(c); + return count; } /** @@ -1114,36 +1634,34 @@ public class ForkJoinPool extends AbstractExecutorService { * @return a string identifying this pool, as well as its state */ public String toString() { - int ps = parallelism; - int wc = workerCounts; - int rc = runControl; long st = getStealCount(); long qt = getQueuedTaskCount(); long qs = getQueuedSubmissionCount(); + int wc = workerCounts; + int tc = wc >>> TOTAL_COUNT_SHIFT; + int rc = wc & RUNNING_COUNT_MASK; + int pc = parallelism; + int rs = runState; + int ac = rs & ACTIVE_COUNT_MASK; return super.toString() + - "[" + runStateToString(runStateOf(rc)) + - ", parallelism = " + ps + - ", size = " + totalCountOf(wc) + - ", active = " + activeCountOf(rc) + - ", running = " + runningCountOf(wc) + + "[" + runLevelToString(rs) + + ", parallelism = " + pc + + ", size = " + tc + + ", active = " + ac + + ", running = " + rc + ", steals = " + st + ", tasks = " + qt + ", submissions = " + qs + "]"; } - private static String runStateToString(int rs) { - switch(rs) { - case RUNNING: return "Running"; - case SHUTDOWN: return "Shutting down"; - case TERMINATING: return "Terminating"; - case TERMINATED: return "Terminated"; - default: throw new Error("Unknown run state"); - } + private static String runLevelToString(int s) { + return ((s & TERMINATED) != 0 ? "Terminated" : + ((s & TERMINATING) != 0 ? "Terminating" : + ((s & SHUTDOWN) != 0 ? "Shutting down" : + "Running"))); } - // lifecycle control - /** * Initiates an orderly shutdown in which previously submitted * tasks are executed, but no new tasks will be accepted. @@ -1158,23 +1676,8 @@ public class ForkJoinPool extends AbstractExecutorService { */ public void shutdown() { checkPermission(); - transitionRunStateTo(SHUTDOWN); - if (canTerminateOnShutdown(runControl)) { - if (workers == null) { // shutting down before workers created - final ReentrantLock lock = this.workerLock; - lock.lock(); - try { - if (workers == null) { - terminate(); - transitionRunStateTo(TERMINATED); - termination.signalAll(); - } - } finally { - lock.unlock(); - } - } - terminateOnShutdown(); - } + advanceRunLevel(SHUTDOWN); + tryTerminate(false); } /** @@ -1195,7 +1698,7 @@ public class ForkJoinPool extends AbstractExecutorService { */ public List shutdownNow() { checkPermission(); - terminate(); + tryTerminate(true); return Collections.emptyList(); } @@ -1205,7 +1708,7 @@ public class ForkJoinPool extends AbstractExecutorService { * @return {@code true} if all tasks have completed following shut down */ public boolean isTerminated() { - return runStateOf(runControl) == TERMINATED; + return runState >= TERMINATED; } /** @@ -1219,7 +1722,7 @@ public class ForkJoinPool extends AbstractExecutorService { * @return {@code true} if terminating but not yet terminated */ public boolean isTerminating() { - return runStateOf(runControl) == TERMINATING; + return (runState & (TERMINATING|TERMINATED)) == TERMINATING; } /** @@ -1228,15 +1731,7 @@ public class ForkJoinPool extends AbstractExecutorService { * @return {@code true} if this pool has been shut down */ public boolean isShutdown() { - return runStateOf(runControl) >= SHUTDOWN; - } - - /** - * Returns true if pool is not terminating or terminated. - * Used internally to suppress execution when terminating. - */ - final boolean isProcessingTasks() { - return runStateOf(runControl) < TERMINATING; + return runState >= SHUTDOWN; } /** @@ -1252,585 +1747,10 @@ public class ForkJoinPool extends AbstractExecutorService { */ public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { - long nanos = unit.toNanos(timeout); - final ReentrantLock lock = this.workerLock; - lock.lock(); - try { - for (;;) { - if (isTerminated()) - return true; - if (nanos <= 0) - return false; - nanos = termination.awaitNanos(nanos); - } - } finally { - lock.unlock(); - } - } - - // Shutdown and termination support - - /** - * Callback from terminating worker. Nulls out the corresponding - * workers slot, and if terminating, tries to terminate; else - * tries to shrink workers array. - * - * @param w the worker - */ - final void workerTerminated(ForkJoinWorkerThread w) { - updateStealCount(w); - updateWorkerCount(-1); - final ReentrantLock lock = this.workerLock; - lock.lock(); - try { - ForkJoinWorkerThread[] ws = workers; - if (ws != null) { - int idx = w.poolIndex; - if (idx >= 0 && idx < ws.length && ws[idx] == w) - ws[idx] = null; - if (totalCountOf(workerCounts) == 0) { - terminate(); // no-op if already terminating - transitionRunStateTo(TERMINATED); - termination.signalAll(); - } - else if (isProcessingTasks()) { - tryShrinkWorkerArray(); - tryResumeSpare(true); // allow replacement - } - } - } finally { - lock.unlock(); - } - signalIdleWorkers(); - } - - /** - * Initiates termination. - */ - private void terminate() { - if (transitionRunStateTo(TERMINATING)) { - stopAllWorkers(); - resumeAllSpares(); - signalIdleWorkers(); - cancelQueuedSubmissions(); - cancelQueuedWorkerTasks(); - interruptUnterminatedWorkers(); - signalIdleWorkers(); // resignal after interrupt - } - } - - /** - * Possibly terminates when on shutdown state. - */ - private void terminateOnShutdown() { - if (!hasQueuedSubmissions() && canTerminateOnShutdown(runControl)) - terminate(); - } - - /** - * Clears out and cancels submissions. - */ - private void cancelQueuedSubmissions() { - ForkJoinTask task; - while ((task = pollSubmission()) != null) - task.cancel(false); - } - - /** - * Cleans out worker queues. - */ - private void cancelQueuedWorkerTasks() { - final ReentrantLock lock = this.workerLock; - lock.lock(); - try { - ForkJoinWorkerThread[] ws = workers; - if (ws != null) { - for (int i = 0; i < ws.length; ++i) { - ForkJoinWorkerThread t = ws[i]; - if (t != null) - t.cancelTasks(); - } - } - } finally { - lock.unlock(); - } - } - - /** - * Sets each worker's status to terminating. Requires lock to avoid - * conflicts with add/remove. - */ - private void stopAllWorkers() { - final ReentrantLock lock = this.workerLock; - lock.lock(); - try { - ForkJoinWorkerThread[] ws = workers; - if (ws != null) { - for (int i = 0; i < ws.length; ++i) { - ForkJoinWorkerThread t = ws[i]; - if (t != null) - t.shutdownNow(); - } - } - } finally { - lock.unlock(); - } - } - - /** - * Interrupts all unterminated workers. This is not required for - * sake of internal control, but may help unstick user code during - * shutdown. - */ - private void interruptUnterminatedWorkers() { - final ReentrantLock lock = this.workerLock; - lock.lock(); try { - ForkJoinWorkerThread[] ws = workers; - if (ws != null) { - for (int i = 0; i < ws.length; ++i) { - ForkJoinWorkerThread t = ws[i]; - if (t != null && !t.isTerminated()) { - try { - t.interrupt(); - } catch (SecurityException ignore) { - } - } - } - } - } finally { - lock.unlock(); - } - } - - - /* - * Nodes for event barrier to manage idle threads. Queue nodes - * are basic Treiber stack nodes, also used for spare stack. - * - * The event barrier has an event count and a wait queue (actually - * a Treiber stack). Workers are enabled to look for work when - * the eventCount is incremented. If they fail to find work, they - * may wait for next count. Upon release, threads help others wake - * up. - * - * Synchronization events occur only in enough contexts to - * maintain overall liveness: - * - * - Submission of a new task to the pool - * - Resizes or other changes to the workers array - * - pool termination - * - A worker pushing a task on an empty queue - * - * The case of pushing a task occurs often enough, and is heavy - * enough compared to simple stack pushes, to require special - * handling: Method signalWork returns without advancing count if - * the queue appears to be empty. This would ordinarily result in - * races causing some queued waiters not to be woken up. To avoid - * this, the first worker enqueued in method sync (see - * syncIsReleasable) rescans for tasks after being enqueued, and - * helps signal if any are found. This works well because the - * worker has nothing better to do, and so might as well help - * alleviate the overhead and contention on the threads actually - * doing work. Also, since event counts increments on task - * availability exist to maintain liveness (rather than to force - * refreshes etc), it is OK for callers to exit early if - * contending with another signaller. - */ - static final class WaitQueueNode { - WaitQueueNode next; // only written before enqueued - volatile ForkJoinWorkerThread thread; // nulled to cancel wait - final long count; // unused for spare stack - - WaitQueueNode(long c, ForkJoinWorkerThread w) { - count = c; - thread = w; - } - - /** - * Wakes up waiter, returning false if known to already - */ - boolean signal() { - ForkJoinWorkerThread t = thread; - if (t == null) - return false; - thread = null; - LockSupport.unpark(t); - return true; - } - - /** - * Awaits release on sync. - */ - void awaitSyncRelease(ForkJoinPool p) { - while (thread != null && !p.syncIsReleasable(this)) - LockSupport.park(this); - } - - /** - * Awaits resumption as spare. - */ - void awaitSpareRelease() { - while (thread != null) { - if (!Thread.interrupted()) - LockSupport.park(this); - } - } - } - - /** - * Ensures that no thread is waiting for count to advance from the - * current value of eventCount read on entry to this method, by - * releasing waiting threads if necessary. - * - * @return the count - */ - final long ensureSync() { - long c = eventCount; - WaitQueueNode q; - while ((q = syncStack) != null && q.count < c) { - if (casBarrierStack(q, null)) { - do { - q.signal(); - } while ((q = q.next) != null); - break; - } - } - return c; - } - - /** - * Increments event count and releases waiting threads. - */ - private void signalIdleWorkers() { - long c; - do {} while (!casEventCount(c = eventCount, c+1)); - ensureSync(); - } - - /** - * Signals threads waiting to poll a task. Because method sync - * rechecks availability, it is OK to only proceed if queue - * appears to be non-empty, and OK to skip under contention to - * increment count (since some other thread succeeded). - */ - final void signalWork() { - long c; - WaitQueueNode q; - if (syncStack != null && - casEventCount(c = eventCount, c+1) && - (((q = syncStack) != null && q.count <= c) && - (!casBarrierStack(q, q.next) || !q.signal()))) - ensureSync(); - } - - /** - * Waits until event count advances from last value held by - * caller, or if excess threads, caller is resumed as spare, or - * caller or pool is terminating. Updates caller's event on exit. - * - * @param w the calling worker thread - */ - final void sync(ForkJoinWorkerThread w) { - updateStealCount(w); // Transfer w's count while it is idle - - while (!w.isShutdown() && isProcessingTasks() && !suspendIfSpare(w)) { - long prev = w.lastEventCount; - WaitQueueNode node = null; - WaitQueueNode h; - while (eventCount == prev && - ((h = syncStack) == null || h.count == prev)) { - if (node == null) - node = new WaitQueueNode(prev, w); - if (casBarrierStack(node.next = h, node)) { - node.awaitSyncRelease(this); - break; - } - } - long ec = ensureSync(); - if (ec != prev) { - w.lastEventCount = ec; - break; - } - } - } - - /** - * Returns {@code true} if worker waiting on sync can proceed: - * - on signal (thread == null) - * - on event count advance (winning race to notify vs signaller) - * - on interrupt - * - if the first queued node, we find work available - * If node was not signalled and event count not advanced on exit, - * then we also help advance event count. - * - * @return {@code true} if node can be released - */ - final boolean syncIsReleasable(WaitQueueNode node) { - long prev = node.count; - if (!Thread.interrupted() && node.thread != null && - (node.next != null || - !ForkJoinWorkerThread.hasQueuedTasks(workers)) && - eventCount == prev) - return false; - if (node.thread != null) { - node.thread = null; - long ec = eventCount; - if (prev <= ec) // help signal - casEventCount(ec, ec+1); - } - return true; - } - - /** - * Returns {@code true} if a new sync event occurred since last - * call to sync or this method, if so, updating caller's count. - */ - final boolean hasNewSyncEvent(ForkJoinWorkerThread w) { - long lc = w.lastEventCount; - long ec = ensureSync(); - if (ec == lc) + return termination.awaitAdvanceInterruptibly(0, timeout, unit) > 0; + } catch (TimeoutException ex) { return false; - w.lastEventCount = ec; - return true; - } - - // Parallelism maintenance - - /** - * Decrements running count; if too low, adds spare. - * - * Conceptually, all we need to do here is add or resume a - * spare thread when one is about to block (and remove or - * suspend it later when unblocked -- see suspendIfSpare). - * However, implementing this idea requires coping with - * several problems: we have imperfect information about the - * states of threads. Some count updates can and usually do - * lag run state changes, despite arrangements to keep them - * accurate (for example, when possible, updating counts - * before signalling or resuming), especially when running on - * dynamic JVMs that don't optimize the infrequent paths that - * update counts. Generating too many threads can make these - * problems become worse, because excess threads are more - * likely to be context-switched with others, slowing them all - * down, especially if there is no work available, so all are - * busy scanning or idling. Also, excess spare threads can - * only be suspended or removed when they are idle, not - * immediately when they aren't needed. So adding threads will - * raise parallelism level for longer than necessary. Also, - * FJ applications often encounter highly transient peaks when - * many threads are blocked joining, but for less time than it - * takes to create or resume spares. - * - * @param joinMe if non-null, return early if done - * @param maintainParallelism if true, try to stay within - * target counts, else create only to avoid starvation - * @return true if joinMe known to be done - */ - final boolean preJoin(ForkJoinTask joinMe, - boolean maintainParallelism) { - maintainParallelism &= maintainsParallelism; // overrride - boolean dec = false; // true when running count decremented - while (spareStack == null || !tryResumeSpare(dec)) { - int counts = workerCounts; - if (dec || (dec = casWorkerCounts(counts, --counts))) { - if (!needSpare(counts, maintainParallelism)) - break; - if (joinMe.status < 0) - return true; - if (tryAddSpare(counts)) - break; - } - } - return false; - } - - /** - * Same idea as preJoin - */ - final boolean preBlock(ManagedBlocker blocker, - boolean maintainParallelism) { - maintainParallelism &= maintainsParallelism; - boolean dec = false; - while (spareStack == null || !tryResumeSpare(dec)) { - int counts = workerCounts; - if (dec || (dec = casWorkerCounts(counts, --counts))) { - if (!needSpare(counts, maintainParallelism)) - break; - if (blocker.isReleasable()) - return true; - if (tryAddSpare(counts)) - break; - } - } - return false; - } - - /** - * Returns {@code true} if a spare thread appears to be needed. - * If maintaining parallelism, returns true when the deficit in - * running threads is more than the surplus of total threads, and - * there is apparently some work to do. This self-limiting rule - * means that the more threads that have already been added, the - * less parallelism we will tolerate before adding another. - * - * @param counts current worker counts - * @param maintainParallelism try to maintain parallelism - */ - private boolean needSpare(int counts, boolean maintainParallelism) { - int ps = parallelism; - int rc = runningCountOf(counts); - int tc = totalCountOf(counts); - int runningDeficit = ps - rc; - int totalSurplus = tc - ps; - return (tc < maxPoolSize && - (rc == 0 || totalSurplus < 0 || - (maintainParallelism && - runningDeficit > totalSurplus && - ForkJoinWorkerThread.hasQueuedTasks(workers)))); - } - - /** - * Adds a spare worker if lock available and no more than the - * expected numbers of threads exist. - * - * @return true if successful - */ - private boolean tryAddSpare(int expectedCounts) { - final ReentrantLock lock = this.workerLock; - int expectedRunning = runningCountOf(expectedCounts); - int expectedTotal = totalCountOf(expectedCounts); - boolean success = false; - boolean locked = false; - // confirm counts while locking; CAS after obtaining lock - try { - for (;;) { - int s = workerCounts; - int tc = totalCountOf(s); - int rc = runningCountOf(s); - if (rc > expectedRunning || tc > expectedTotal) - break; - if (!locked && !(locked = lock.tryLock())) - break; - if (casWorkerCounts(s, workerCountsFor(tc+1, rc+1))) { - createAndStartSpare(tc); - success = true; - break; - } - } - } finally { - if (locked) - lock.unlock(); - } - return success; - } - - /** - * Adds the kth spare worker. On entry, pool counts are already - * adjusted to reflect addition. - */ - private void createAndStartSpare(int k) { - ForkJoinWorkerThread w = null; - ForkJoinWorkerThread[] ws = ensureWorkerArrayCapacity(k + 1); - int len = ws.length; - // Probably, we can place at slot k. If not, find empty slot - if (k < len && ws[k] != null) { - for (k = 0; k < len && ws[k] != null; ++k) - ; - } - if (k < len && isProcessingTasks() && (w = createWorker(k)) != null) { - ws[k] = w; - w.start(); - } - else - updateWorkerCount(-1); // adjust on failure - signalIdleWorkers(); - } - - /** - * Suspends calling thread w if there are excess threads. Called - * only from sync. Spares are enqueued in a Treiber stack using - * the same WaitQueueNodes as barriers. They are resumed mainly - * in preJoin, but are also woken on pool events that require all - * threads to check run state. - * - * @param w the caller - */ - private boolean suspendIfSpare(ForkJoinWorkerThread w) { - WaitQueueNode node = null; - int s; - while (parallelism < runningCountOf(s = workerCounts)) { - if (node == null) - node = new WaitQueueNode(0, w); - if (casWorkerCounts(s, s-1)) { // representation-dependent - // push onto stack - do {} while (!casSpareStack(node.next = spareStack, node)); - // block until released by resumeSpare - node.awaitSpareRelease(); - return true; - } - } - return false; - } - - /** - * Tries to pop and resume a spare thread. - * - * @param updateCount if true, increment running count on success - * @return true if successful - */ - private boolean tryResumeSpare(boolean updateCount) { - WaitQueueNode q; - while ((q = spareStack) != null) { - if (casSpareStack(q, q.next)) { - if (updateCount) - updateRunningCount(1); - q.signal(); - return true; - } - } - return false; - } - - /** - * Pops and resumes all spare threads. Same idea as ensureSync. - * - * @return true if any spares released - */ - private boolean resumeAllSpares() { - WaitQueueNode q; - while ( (q = spareStack) != null) { - if (casSpareStack(q, null)) { - do { - updateRunningCount(1); - q.signal(); - } while ((q = q.next) != null); - return true; - } - } - return false; - } - - /** - * Pops and shuts down excessive spare threads. Call only while - * holding lock. This is not guaranteed to eliminate all excess - * threads, only those suspended as spares, which are the ones - * unlikely to be needed in the future. - */ - private void trimSpares() { - int surplus = totalCountOf(workerCounts) - parallelism; - WaitQueueNode q; - while (surplus > 0 && (q = spareStack) != null) { - if (casSpareStack(q, null)) { - do { - updateRunningCount(1); - ForkJoinWorkerThread w = q.thread; - if (w != null && surplus > 0 && - runningCountOf(workerCounts) > 0 && w.shutdown()) - --surplus; - q.signal(); - } while ((q = q.next) != null); - } } } @@ -1838,11 +1758,17 @@ public class ForkJoinPool extends AbstractExecutorService { * Interface for extending managed parallelism for tasks running * in {@link ForkJoinPool}s. * - *

    A {@code ManagedBlocker} provides two methods. - * Method {@code isReleasable} must return {@code true} if - * blocking is not necessary. Method {@code block} blocks the - * current thread if necessary (perhaps internally invoking - * {@code isReleasable} before actually blocking). + *

    A {@code ManagedBlocker} provides two methods. Method + * {@code isReleasable} must return {@code true} if blocking is + * not necessary. Method {@code block} blocks the current thread + * if necessary (perhaps internally invoking {@code isReleasable} + * before actually blocking). The unusual methods in this API + * accommodate synchronizers that may, but don't usually, block + * for long periods. Similarly, they allow more efficient internal + * handling of cases in which additional workers may be, but + * usually are not, needed to ensure sufficient parallelism. + * Toward this end, implementations of method {@code isReleasable} + * must be amenable to repeated invocation. * *

    For example, here is a ManagedBlocker based on a * ReentrantLock: @@ -1860,6 +1786,26 @@ public class ForkJoinPool extends AbstractExecutorService { * return hasLock || (hasLock = lock.tryLock()); * } * }} + * + *

    Here is a class that possibly blocks waiting for an + * item on a given queue: + *

     {@code
    +     * class QueueTaker implements ManagedBlocker {
    +     *   final BlockingQueue queue;
    +     *   volatile E item = null;
    +     *   QueueTaker(BlockingQueue q) { this.queue = q; }
    +     *   public boolean block() throws InterruptedException {
    +     *     if (item == null)
    +     *       item = queue.take();
    +     *     return true;
    +     *   }
    +     *   public boolean isReleasable() {
    +     *     return item != null || (item = queue.poll()) != null;
    +     *   }
    +     *   public E getItem() { // call after pool.managedBlock completes
    +     *     return item;
    +     *   }
    +     * }}
    */ public static interface ManagedBlocker { /** @@ -1883,14 +1829,7 @@ public class ForkJoinPool extends AbstractExecutorService { * Blocks in accord with the given blocker. If the current thread * is a {@link ForkJoinWorkerThread}, this method possibly * arranges for a spare thread to be activated if necessary to - * ensure parallelism while the current thread is blocked. - * - *

    If {@code maintainParallelism} is {@code true} and the pool - * supports it ({@link #getMaintainsParallelism}), this method - * attempts to maintain the pool's nominal parallelism. Otherwise - * it activates a thread only if necessary to avoid complete - * starvation. This option may be preferable when blockages use - * timeouts, or are almost always brief. + * ensure sufficient parallelism while the current thread is blocked. * *

    If the caller is not a {@link ForkJoinTask}, this method is * behaviorally equivalent to @@ -1904,33 +1843,18 @@ public class ForkJoinPool extends AbstractExecutorService { * first be expanded to ensure parallelism, and later adjusted. * * @param blocker the blocker - * @param maintainParallelism if {@code true} and supported by - * this pool, attempt to maintain the pool's nominal parallelism; - * otherwise activate a thread only if necessary to avoid - * complete starvation. * @throws InterruptedException if blocker.block did so */ - public static void managedBlock(ManagedBlocker blocker, - boolean maintainParallelism) + public static void managedBlock(ManagedBlocker blocker) throws InterruptedException { Thread t = Thread.currentThread(); - ForkJoinPool pool = ((t instanceof ForkJoinWorkerThread) ? - ((ForkJoinWorkerThread) t).pool : null); - if (!blocker.isReleasable()) { - try { - if (pool == null || - !pool.preBlock(blocker, maintainParallelism)) - awaitBlocker(blocker); - } finally { - if (pool != null) - pool.updateRunningCount(1); - } + if (t instanceof ForkJoinWorkerThread) { + ForkJoinWorkerThread w = (ForkJoinWorkerThread) t; + w.pool.awaitBlocker(blocker); + } + else { + do {} while (!blocker.isReleasable() && !blocker.block()); } - } - - private static void awaitBlocker(ManagedBlocker blocker) - throws InterruptedException { - do {} while (!blocker.isReleasable() && !blocker.block()); } // AbstractExecutorService overrides. These rely on undocumented @@ -1948,32 +1872,18 @@ public class ForkJoinPool extends AbstractExecutorService { // Unsafe mechanics private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); - private static final long eventCountOffset = - objectFieldOffset("eventCount", ForkJoinPool.class); private static final long workerCountsOffset = objectFieldOffset("workerCounts", ForkJoinPool.class); - private static final long runControlOffset = - objectFieldOffset("runControl", ForkJoinPool.class); - private static final long syncStackOffset = - objectFieldOffset("syncStack",ForkJoinPool.class); - private static final long spareStackOffset = - objectFieldOffset("spareStack", ForkJoinPool.class); - - private boolean casEventCount(long cmp, long val) { - return UNSAFE.compareAndSwapLong(this, eventCountOffset, cmp, val); - } - private boolean casWorkerCounts(int cmp, int val) { - return UNSAFE.compareAndSwapInt(this, workerCountsOffset, cmp, val); - } - private boolean casRunControl(int cmp, int val) { - return UNSAFE.compareAndSwapInt(this, runControlOffset, cmp, val); - } - private boolean casSpareStack(WaitQueueNode cmp, WaitQueueNode val) { - return UNSAFE.compareAndSwapObject(this, spareStackOffset, cmp, val); - } - private boolean casBarrierStack(WaitQueueNode cmp, WaitQueueNode val) { - return UNSAFE.compareAndSwapObject(this, syncStackOffset, cmp, val); - } + private static final long runStateOffset = + objectFieldOffset("runState", ForkJoinPool.class); + private static final long eventCountOffset = + objectFieldOffset("eventCount", ForkJoinPool.class); + private static final long eventWaitersOffset = + objectFieldOffset("eventWaiters", ForkJoinPool.class); + private static final long stealCountOffset = + objectFieldOffset("stealCount", ForkJoinPool.class); + private static final long spareWaitersOffset = + objectFieldOffset("spareWaiters", ForkJoinPool.class); private static long objectFieldOffset(String field, Class klazz) { try { diff --git a/src/share/classes/java/util/concurrent/ForkJoinTask.java b/src/share/classes/java/util/concurrent/ForkJoinTask.java index 58f6e01b2af7c3ddfd390b4eef44aad163e9498f..598578f0cf1e7189584e6a8fe85d81f445c1cd38 100644 --- a/src/share/classes/java/util/concurrent/ForkJoinTask.java +++ b/src/share/classes/java/util/concurrent/ForkJoinTask.java @@ -91,10 +91,7 @@ import java.util.WeakHashMap; * results of a task is {@link #join}, but there are several variants: * The {@link Future#get} methods support interruptible and/or timed * waits for completion and report results using {@code Future} - * conventions. Method {@link #helpJoin} enables callers to actively - * execute other tasks while awaiting joins, which is sometimes more - * efficient but only applies when all subtasks are known to be - * strictly tree-structured. Method {@link #invoke} is semantically + * conventions. Method {@link #invoke} is semantically * equivalent to {@code fork(); join()} but always attempts to begin * execution in the current thread. The "quiet" forms of * these methods do not extract results or report exceptions. These @@ -130,7 +127,7 @@ import java.util.WeakHashMap; * ForkJoinTasks (as may be determined using method {@link * #inForkJoinPool}). Attempts to invoke them in other contexts * result in exceptions or errors, possibly including - * ClassCastException. + * {@code ClassCastException}. * *

    Most base support methods are {@code final}, to prevent * overriding of implementations that are intrinsically tied to the @@ -152,9 +149,8 @@ import java.util.WeakHashMap; * *

    This class provides {@code adapt} methods for {@link Runnable} * and {@link Callable}, that may be of use when mixing execution of - * {@code ForkJoinTasks} with other kinds of tasks. When all tasks - * are of this form, consider using a pool in - * {@linkplain ForkJoinPool#setAsyncMode async mode}. + * {@code ForkJoinTasks} with other kinds of tasks. When all tasks are + * of this form, consider using a pool constructed in asyncMode. * *

    ForkJoinTasks are {@code Serializable}, which enables them to be * used in extensions such as remote execution frameworks. It is @@ -166,33 +162,43 @@ import java.util.WeakHashMap; */ public abstract class ForkJoinTask implements Future, Serializable { - /** - * Run control status bits packed into a single int to minimize - * footprint and to ensure atomicity (via CAS). Status is - * initially zero, and takes on nonnegative values until - * completed, upon which status holds COMPLETED. CANCELLED, or - * EXCEPTIONAL, which use the top 3 bits. Tasks undergoing - * blocking waits by other threads have SIGNAL_MASK bits set -- - * bit 15 for external (nonFJ) waits, and the rest a count of - * waiting FJ threads. (This representation relies on - * ForkJoinPool max thread limits). Completion of a stolen task - * with SIGNAL_MASK bits set awakens waiter via notifyAll. Even - * though suboptimal for some purposes, we use basic builtin - * wait/notify to take advantage of "monitor inflation" in JVMs - * that we would otherwise need to emulate to avoid adding further - * per-task bookkeeping overhead. Note that bits 16-28 are - * currently unused. Also value 0x80000000 is available as spare - * completion value. + /* + * See the internal documentation of class ForkJoinPool for a + * general implementation overview. ForkJoinTasks are mainly + * responsible for maintaining their "status" field amidst relays + * to methods in ForkJoinWorkerThread and ForkJoinPool. The + * methods of this class are more-or-less layered into (1) basic + * status maintenance (2) execution and awaiting completion (3) + * user-level methods that additionally report results. This is + * sometimes hard to see because this file orders exported methods + * in a way that flows well in javadocs. In particular, most + * join mechanics are in method quietlyJoin, below. + */ + + /* + * The status field holds run control status bits packed into a + * single int to minimize footprint and to ensure atomicity (via + * CAS). Status is initially zero, and takes on nonnegative + * values until completed, upon which status holds value + * NORMAL, CANCELLED, or EXCEPTIONAL. Tasks undergoing blocking + * waits by other threads have the SIGNAL bit set. Completion of + * a stolen task with SIGNAL set awakens any waiters via + * notifyAll. Even though suboptimal for some purposes, we use + * basic builtin wait/notify to take advantage of "monitor + * inflation" in JVMs that we would otherwise need to emulate to + * avoid adding further per-task bookkeeping overhead. We want + * these monitors to be "fat", i.e., not use biasing or thin-lock + * techniques, so use some odd coding idioms that tend to avoid + * them. */ + + /** The run status of this task */ volatile int status; // accessed directly by pool and workers - static final int COMPLETION_MASK = 0xe0000000; - static final int NORMAL = 0xe0000000; // == mask - static final int CANCELLED = 0xc0000000; - static final int EXCEPTIONAL = 0xa0000000; - static final int SIGNAL_MASK = 0x0000ffff; - static final int INTERNAL_SIGNAL_MASK = 0x00007fff; - static final int EXTERNAL_SIGNAL = 0x00008000; // top bit of low word + private static final int NORMAL = -1; + private static final int CANCELLED = -2; + private static final int EXCEPTIONAL = -3; + private static final int SIGNAL = 1; /** * Table of exceptions thrown by tasks, to enable reporting by @@ -206,176 +212,94 @@ public abstract class ForkJoinTask implements Future, Serializable { Collections.synchronizedMap (new WeakHashMap, Throwable>()); - // within-package utilities + // Maintaining completion status /** - * Gets current worker thread, or null if not a worker thread. - */ - static ForkJoinWorkerThread getWorker() { - Thread t = Thread.currentThread(); - return ((t instanceof ForkJoinWorkerThread) ? - (ForkJoinWorkerThread) t : null); - } - - final boolean casStatus(int cmp, int val) { - return UNSAFE.compareAndSwapInt(this, statusOffset, cmp, val); - } - - /** - * Workaround for not being able to rethrow unchecked exceptions. - */ - static void rethrowException(Throwable ex) { - if (ex != null) - UNSAFE.throwException(ex); - } - - // Setting completion status - - /** - * Marks completion and wakes up threads waiting to join this task. + * Marks completion and wakes up threads waiting to join this task, + * also clearing signal request bits. * * @param completion one of NORMAL, CANCELLED, EXCEPTIONAL */ - final void setCompletion(int completion) { - ForkJoinPool pool = getPool(); - if (pool != null) { - int s; // Clear signal bits while setting completion status - do {} while ((s = status) >= 0 && !casStatus(s, completion)); - - if ((s & SIGNAL_MASK) != 0) { - if ((s &= INTERNAL_SIGNAL_MASK) != 0) - pool.updateRunningCount(s); - synchronized (this) { notifyAll(); } + private void setCompletion(int completion) { + int s; + while ((s = status) >= 0) { + if (UNSAFE.compareAndSwapInt(this, statusOffset, s, completion)) { + if (s != 0) + synchronized (this) { notifyAll(); } + break; } } - else - externallySetCompletion(completion); - } - - /** - * Version of setCompletion for non-FJ threads. Leaves signal - * bits for unblocked threads to adjust, and always notifies. - */ - private void externallySetCompletion(int completion) { - int s; - do {} while ((s = status) >= 0 && - !casStatus(s, (s & SIGNAL_MASK) | completion)); - synchronized (this) { notifyAll(); } - } - - /** - * Sets status to indicate normal completion. - */ - final void setNormalCompletion() { - // Try typical fast case -- single CAS, no signal, not already done. - // Manually expand casStatus to improve chances of inlining it - if (!UNSAFE.compareAndSwapInt(this, statusOffset, 0, NORMAL)) - setCompletion(NORMAL); } - // internal waiting and notification - /** - * Performs the actual monitor wait for awaitDone. + * Records exception and sets exceptional completion. + * + * @return status on exit */ - private void doAwaitDone() { - // Minimize lock bias and in/de-flation effects by maximizing - // chances of waiting inside sync - try { - while (status >= 0) - synchronized (this) { if (status >= 0) wait(); } - } catch (InterruptedException ie) { - onInterruptedWait(); - } + private void setExceptionalCompletion(Throwable rex) { + exceptionMap.put(this, rex); + setCompletion(EXCEPTIONAL); } /** - * Performs the actual timed monitor wait for awaitDone. + * Blocks a worker thread until completion. Called only by + * pool. Currently unused -- pool-based waits use timeout + * version below. */ - private void doAwaitDone(long startTime, long nanos) { - synchronized (this) { + final void internalAwaitDone() { + int s; // the odd construction reduces lock bias effects + while ((s = status) >= 0) { try { - while (status >= 0) { - long nt = nanos - (System.nanoTime() - startTime); - if (nt <= 0) - break; - wait(nt / 1000000, (int) (nt % 1000000)); + synchronized(this) { + if (UNSAFE.compareAndSwapInt(this, statusOffset, s,SIGNAL)) + wait(); } } catch (InterruptedException ie) { - onInterruptedWait(); + cancelIfTerminating(); } } } - // Awaiting completion - /** - * Sets status to indicate there is joiner, then waits for join, - * surrounded with pool notifications. + * Blocks a worker thread until completed or timed out. Called + * only by pool. * - * @return status upon exit + * @return status on exit */ - private int awaitDone(ForkJoinWorkerThread w, - boolean maintainParallelism) { - ForkJoinPool pool = (w == null) ? null : w.pool; + final int internalAwaitDone(long millis) { int s; - while ((s = status) >= 0) { - if (casStatus(s, (pool == null) ? s|EXTERNAL_SIGNAL : s+1)) { - if (pool == null || !pool.preJoin(this, maintainParallelism)) - doAwaitDone(); - if (((s = status) & INTERNAL_SIGNAL_MASK) != 0) - adjustPoolCountsOnUnblock(pool); - break; - } - } - return s; - } - - /** - * Timed version of awaitDone - * - * @return status upon exit - */ - private int awaitDone(ForkJoinWorkerThread w, long nanos) { - ForkJoinPool pool = (w == null) ? null : w.pool; - int s; - while ((s = status) >= 0) { - if (casStatus(s, (pool == null) ? s|EXTERNAL_SIGNAL : s+1)) { - long startTime = System.nanoTime(); - if (pool == null || !pool.preJoin(this, false)) - doAwaitDone(startTime, nanos); - if ((s = status) >= 0) { - adjustPoolCountsOnCancelledWait(pool); - s = status; + if ((s = status) >= 0) { + try { + synchronized(this) { + if (UNSAFE.compareAndSwapInt(this, statusOffset, s,SIGNAL)) + wait(millis, 0); } - if (s < 0 && (s & INTERNAL_SIGNAL_MASK) != 0) - adjustPoolCountsOnUnblock(pool); - break; + } catch (InterruptedException ie) { + cancelIfTerminating(); } + s = status; } return s; } /** - * Notifies pool that thread is unblocked. Called by signalled - * threads when woken by non-FJ threads (which is atypical). + * Blocks a non-worker-thread until completion. */ - private void adjustPoolCountsOnUnblock(ForkJoinPool pool) { + private void externalAwaitDone() { int s; - do {} while ((s = status) < 0 && !casStatus(s, s & COMPLETION_MASK)); - if (pool != null && (s &= INTERNAL_SIGNAL_MASK) != 0) - pool.updateRunningCount(s); - } - - /** - * Notifies pool to adjust counts on cancelled or timed out wait. - */ - private void adjustPoolCountsOnCancelledWait(ForkJoinPool pool) { - if (pool != null) { - int s; - while ((s = status) >= 0 && (s & INTERNAL_SIGNAL_MASK) != 0) { - if (casStatus(s, s - 1)) { - pool.updateRunningCount(1); + while ((s = status) >= 0) { + synchronized(this) { + if (UNSAFE.compareAndSwapInt(this, statusOffset, s, SIGNAL)){ + boolean interrupted = false; + while (status >= 0) { + try { + wait(); + } catch (InterruptedException ie) { + interrupted = true; + } + } + if (interrupted) + Thread.currentThread().interrupt(); break; } } @@ -383,153 +307,19 @@ public abstract class ForkJoinTask implements Future, Serializable { } /** - * Handles interruptions during waits. - */ - private void onInterruptedWait() { - ForkJoinWorkerThread w = getWorker(); - if (w == null) - Thread.currentThread().interrupt(); // re-interrupt - else if (w.isTerminating()) - cancelIgnoringExceptions(); - // else if FJworker, ignore interrupt - } - - // Recording and reporting exceptions - - private void setDoneExceptionally(Throwable rex) { - exceptionMap.put(this, rex); - setCompletion(EXCEPTIONAL); - } - - /** - * Throws the exception associated with status s. - * - * @throws the exception - */ - private void reportException(int s) { - if ((s &= COMPLETION_MASK) < NORMAL) { - if (s == CANCELLED) - throw new CancellationException(); - else - rethrowException(exceptionMap.get(this)); - } - } - - /** - * Returns result or throws exception using j.u.c.Future conventions. - * Only call when {@code isDone} known to be true or thread known - * to be interrupted. - */ - private V reportFutureResult() - throws InterruptedException, ExecutionException { - if (Thread.interrupted()) - throw new InterruptedException(); - int s = status & COMPLETION_MASK; - if (s < NORMAL) { - Throwable ex; - if (s == CANCELLED) - throw new CancellationException(); - if (s == EXCEPTIONAL && (ex = exceptionMap.get(this)) != null) - throw new ExecutionException(ex); - } - return getRawResult(); - } - - /** - * Returns result or throws exception using j.u.c.Future conventions - * with timeouts. - */ - private V reportTimedFutureResult() - throws InterruptedException, ExecutionException, TimeoutException { - if (Thread.interrupted()) - throw new InterruptedException(); - Throwable ex; - int s = status & COMPLETION_MASK; - if (s == NORMAL) - return getRawResult(); - else if (s == CANCELLED) - throw new CancellationException(); - else if (s == EXCEPTIONAL && (ex = exceptionMap.get(this)) != null) - throw new ExecutionException(ex); - else - throw new TimeoutException(); - } - - // internal execution methods - - /** - * Calls exec, recording completion, and rethrowing exception if - * encountered. Caller should normally check status before calling. - * - * @return true if completed normally - */ - private boolean tryExec() { - try { // try block must contain only call to exec - if (!exec()) - return false; - } catch (Throwable rex) { - setDoneExceptionally(rex); - rethrowException(rex); - return false; // not reached - } - setNormalCompletion(); - return true; - } - - /** - * Main execution method used by worker threads. Invokes - * base computation unless already complete. + * Unless done, calls exec and records status if completed, but + * doesn't wait for completion otherwise. Primary execution method + * for ForkJoinWorkerThread. */ final void quietlyExec() { - if (status >= 0) { - try { - if (!exec()) - return; - } catch (Throwable rex) { - setDoneExceptionally(rex); - return; - } - setNormalCompletion(); - } - } - - /** - * Calls exec(), recording but not rethrowing exception. - * Caller should normally check status before calling. - * - * @return true if completed normally - */ - private boolean tryQuietlyInvoke() { try { - if (!exec()) - return false; + if (status < 0 || !exec()) + return; } catch (Throwable rex) { - setDoneExceptionally(rex); - return false; - } - setNormalCompletion(); - return true; - } - - /** - * Cancels, ignoring any exceptions it throws. - */ - final void cancelIgnoringExceptions() { - try { - cancel(false); - } catch (Throwable ignore) { + setExceptionalCompletion(rex); + return; } - } - - /** - * Main implementation of helpJoin - */ - private int busyJoin(ForkJoinWorkerThread w) { - int s; - ForkJoinTask t; - while ((s = status) >= 0 && (t = w.scanWhileJoining(this)) != null) - t.quietlyExec(); - return (s >= 0) ? awaitDone(w, false) : s; // block if no work + setCompletion(NORMAL); // must be outside try block } // public methods @@ -567,34 +357,41 @@ public abstract class ForkJoinTask implements Future, Serializable { * @return the computed result */ public final V join() { - ForkJoinWorkerThread w = getWorker(); - if (w == null || status < 0 || !w.unpushTask(this) || !tryExec()) - reportException(awaitDone(w, true)); + quietlyJoin(); + Throwable ex; + if (status < NORMAL && (ex = getException()) != null) + UNSAFE.throwException(ex); return getRawResult(); } /** * Commences performing this task, awaits its completion if - * necessary, and return its result, or throws an (unchecked) - * exception if the underlying computation did so. + * necessary, and returns its result, or throws an (unchecked) + * {@code RuntimeException} or {@code Error} if the underlying + * computation did so. * * @return the computed result */ public final V invoke() { - if (status >= 0 && tryExec()) - return getRawResult(); - else - return join(); + quietlyInvoke(); + Throwable ex; + if (status < NORMAL && (ex = getException()) != null) + UNSAFE.throwException(ex); + return getRawResult(); } /** * Forks the given tasks, returning when {@code isDone} holds for * each task or an (unchecked) exception is encountered, in which - * case the exception is rethrown. If either task encounters an - * exception, the other one may be, but is not guaranteed to be, - * cancelled. If both tasks throw an exception, then this method - * throws one of them. The individual status of each task may be - * checked using {@link #getException()} and related methods. + * case the exception is rethrown. If more than one task + * encounters an exception, then this method throws any one of + * these exceptions. If any task encounters an exception, the + * other may be cancelled. However, the execution status of + * individual tasks is not guaranteed upon exceptional return. The + * status of each task may be obtained using {@link + * #getException()} and related methods to check if they have been + * cancelled, completed normally or exceptionally, or left + * unprocessed. * *

    This method may be invoked only from within {@code * ForkJoinTask} computations (as may be determined using method @@ -615,12 +412,14 @@ public abstract class ForkJoinTask implements Future, Serializable { /** * Forks the given tasks, returning when {@code isDone} holds for * each task or an (unchecked) exception is encountered, in which - * case the exception is rethrown. If any task encounters an - * exception, others may be, but are not guaranteed to be, - * cancelled. If more than one task encounters an exception, then - * this method throws any one of these exceptions. The individual - * status of each task may be checked using {@link #getException()} - * and related methods. + * case the exception is rethrown. If more than one task + * encounters an exception, then this method throws any one of + * these exceptions. If any task encounters an exception, others + * may be cancelled. However, the execution status of individual + * tasks is not guaranteed upon exceptional return. The status of + * each task may be obtained using {@link #getException()} and + * related methods to check if they have been cancelled, completed + * normally or exceptionally, or left unprocessed. * *

    This method may be invoked only from within {@code * ForkJoinTask} computations (as may be determined using method @@ -644,7 +443,7 @@ public abstract class ForkJoinTask implements Future, Serializable { t.fork(); else { t.quietlyInvoke(); - if (ex == null) + if (ex == null && t.status < NORMAL) ex = t.getException(); } } @@ -655,26 +454,27 @@ public abstract class ForkJoinTask implements Future, Serializable { t.cancel(false); else { t.quietlyJoin(); - if (ex == null) + if (ex == null && t.status < NORMAL) ex = t.getException(); } } } if (ex != null) - rethrowException(ex); + UNSAFE.throwException(ex); } /** * Forks all tasks in the specified collection, returning when * {@code isDone} holds for each task or an (unchecked) exception - * is encountered. If any task encounters an exception, others - * may be, but are not guaranteed to be, cancelled. If more than - * one task encounters an exception, then this method throws any - * one of these exceptions. The individual status of each task - * may be checked using {@link #getException()} and related - * methods. The behavior of this operation is undefined if the - * specified collection is modified while the operation is in - * progress. + * is encountered, in which case the exception is rethrown. If + * more than one task encounters an exception, then this method + * throws any one of these exceptions. If any task encounters an + * exception, others may be cancelled. However, the execution + * status of individual tasks is not guaranteed upon exceptional + * return. The status of each task may be obtained using {@link + * #getException()} and related methods to check if they have been + * cancelled, completed normally or exceptionally, or left + * unprocessed. * *

    This method may be invoked only from within {@code * ForkJoinTask} computations (as may be determined using method @@ -706,7 +506,7 @@ public abstract class ForkJoinTask implements Future, Serializable { t.fork(); else { t.quietlyInvoke(); - if (ex == null) + if (ex == null && t.status < NORMAL) ex = t.getException(); } } @@ -717,13 +517,13 @@ public abstract class ForkJoinTask implements Future, Serializable { t.cancel(false); else { t.quietlyJoin(); - if (ex == null) + if (ex == null && t.status < NORMAL) ex = t.getException(); } } } if (ex != null) - rethrowException(ex); + UNSAFE.throwException(ex); return tasks; } @@ -753,7 +553,35 @@ public abstract class ForkJoinTask implements Future, Serializable { */ public boolean cancel(boolean mayInterruptIfRunning) { setCompletion(CANCELLED); - return (status & COMPLETION_MASK) == CANCELLED; + return status == CANCELLED; + } + + /** + * Cancels, ignoring any exceptions thrown by cancel. Used during + * worker and pool shutdown. Cancel is spec'ed not to throw any + * exceptions, but if it does anyway, we have no recourse during + * shutdown, so guard against this case. + */ + final void cancelIgnoringExceptions() { + try { + cancel(false); + } catch (Throwable ignore) { + } + } + + /** + * Cancels if current thread is a terminating worker thread, + * ignoring any exceptions thrown by cancel. + */ + final void cancelIfTerminating() { + Thread t = Thread.currentThread(); + if ((t instanceof ForkJoinWorkerThread) && + ((ForkJoinWorkerThread) t).isTerminating()) { + try { + cancel(false); + } catch (Throwable ignore) { + } + } } public final boolean isDone() { @@ -761,7 +589,7 @@ public abstract class ForkJoinTask implements Future, Serializable { } public final boolean isCancelled() { - return (status & COMPLETION_MASK) == CANCELLED; + return status == CANCELLED; } /** @@ -770,7 +598,7 @@ public abstract class ForkJoinTask implements Future, Serializable { * @return {@code true} if this task threw an exception or was cancelled */ public final boolean isCompletedAbnormally() { - return (status & COMPLETION_MASK) < NORMAL; + return status < NORMAL; } /** @@ -781,7 +609,7 @@ public abstract class ForkJoinTask implements Future, Serializable { * exception and was not cancelled */ public final boolean isCompletedNormally() { - return (status & COMPLETION_MASK) == NORMAL; + return status == NORMAL; } /** @@ -792,7 +620,7 @@ public abstract class ForkJoinTask implements Future, Serializable { * @return the exception, or {@code null} if none */ public final Throwable getException() { - int s = status & COMPLETION_MASK; + int s = status; return ((s >= NORMAL) ? null : (s == CANCELLED) ? new CancellationException() : exceptionMap.get(this)); @@ -813,20 +641,21 @@ public abstract class ForkJoinTask implements Future, Serializable { * thrown will be a {@code RuntimeException} with cause {@code ex}. */ public void completeExceptionally(Throwable ex) { - setDoneExceptionally((ex instanceof RuntimeException) || - (ex instanceof Error) ? ex : - new RuntimeException(ex)); + setExceptionalCompletion((ex instanceof RuntimeException) || + (ex instanceof Error) ? ex : + new RuntimeException(ex)); } /** * Completes this task, and if not already aborted or cancelled, - * returning a {@code null} result upon {@code join} and related - * operations. This method may be used to provide results for - * asynchronous tasks, or to provide alternative handling for - * tasks that would not otherwise complete normally. Its use in - * other situations is discouraged. This method is - * overridable, but overridden versions must invoke {@code super} - * implementation to maintain guarantees. + * returning the given value as the result of subsequent + * invocations of {@code join} and related operations. This method + * may be used to provide results for asynchronous tasks, or to + * provide alternative handling for tasks that would not otherwise + * complete normally. Its use in other situations is + * discouraged. This method is overridable, but overridden + * versions must invoke {@code super} implementation to maintain + * guarantees. * * @param value the result value for this task */ @@ -834,97 +663,151 @@ public abstract class ForkJoinTask implements Future, Serializable { try { setRawResult(value); } catch (Throwable rex) { - setDoneExceptionally(rex); + setExceptionalCompletion(rex); return; } - setNormalCompletion(); + setCompletion(NORMAL); } public final V get() throws InterruptedException, ExecutionException { - ForkJoinWorkerThread w = getWorker(); - if (w == null || status < 0 || !w.unpushTask(this) || !tryQuietlyInvoke()) - awaitDone(w, true); - return reportFutureResult(); + quietlyJoin(); + if (Thread.interrupted()) + throw new InterruptedException(); + int s = status; + if (s < NORMAL) { + Throwable ex; + if (s == CANCELLED) + throw new CancellationException(); + if (s == EXCEPTIONAL && (ex = exceptionMap.get(this)) != null) + throw new ExecutionException(ex); + } + return getRawResult(); } public final V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + Thread t = Thread.currentThread(); + ForkJoinPool pool; + if (t instanceof ForkJoinWorkerThread) { + ForkJoinWorkerThread w = (ForkJoinWorkerThread) t; + if (status >= 0 && w.unpushTask(this)) + quietlyExec(); + pool = w.pool; + } + else + pool = null; + /* + * Timed wait loop intermixes cases for FJ (pool != null) and + * non FJ threads. For FJ, decrement pool count but don't try + * for replacement; increment count on completion. For non-FJ, + * deal with interrupts. This is messy, but a little less so + * than is splitting the FJ and nonFJ cases. + */ + boolean interrupted = false; + boolean dec = false; // true if pool count decremented long nanos = unit.toNanos(timeout); - ForkJoinWorkerThread w = getWorker(); - if (w == null || status < 0 || !w.unpushTask(this) || !tryQuietlyInvoke()) - awaitDone(w, nanos); - return reportTimedFutureResult(); - } - - /** - * Possibly executes other tasks until this task {@link #isDone is - * done}, then returns the result of the computation. This method - * may be more efficient than {@code join}, but is only applicable - * when there are no potential dependencies between continuation - * of the current task and that of any other task that might be - * executed while helping. (This usually holds for pure - * divide-and-conquer tasks). - * - *

    This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method - * {@link #inForkJoinPool}). Attempts to invoke in other contexts - * result in exceptions or errors, possibly including {@code - * ClassCastException}. - * - * @return the computed result - */ - public final V helpJoin() { - ForkJoinWorkerThread w = (ForkJoinWorkerThread) Thread.currentThread(); - if (status < 0 || !w.unpushTask(this) || !tryExec()) - reportException(busyJoin(w)); - return getRawResult(); - } - - /** - * Possibly executes other tasks until this task {@link #isDone is - * done}. This method may be useful when processing collections - * of tasks when some have been cancelled or otherwise known to - * have aborted. - * - *

    This method may be invoked only from within {@code - * ForkJoinTask} computations (as may be determined using method - * {@link #inForkJoinPool}). Attempts to invoke in other contexts - * result in exceptions or errors, possibly including {@code - * ClassCastException}. - */ - public final void quietlyHelpJoin() { - if (status >= 0) { - ForkJoinWorkerThread w = - (ForkJoinWorkerThread) Thread.currentThread(); - if (!w.unpushTask(this) || !tryQuietlyInvoke()) - busyJoin(w); + for (;;) { + if (pool == null && Thread.interrupted()) { + interrupted = true; + break; + } + int s = status; + if (s < 0) + break; + if (UNSAFE.compareAndSwapInt(this, statusOffset, s, SIGNAL)) { + long startTime = System.nanoTime(); + long nt; // wait time + while (status >= 0 && + (nt = nanos - (System.nanoTime() - startTime)) > 0) { + if (pool != null && !dec) + dec = pool.tryDecrementRunningCount(); + else { + long ms = nt / 1000000; + int ns = (int) (nt % 1000000); + try { + synchronized(this) { + if (status >= 0) + wait(ms, ns); + } + } catch (InterruptedException ie) { + if (pool != null) + cancelIfTerminating(); + else { + interrupted = true; + break; + } + } + } + } + break; + } } + if (pool != null && dec) + pool.incrementRunningCount(); + if (interrupted) + throw new InterruptedException(); + int es = status; + if (es != NORMAL) { + Throwable ex; + if (es == CANCELLED) + throw new CancellationException(); + if (es == EXCEPTIONAL && (ex = exceptionMap.get(this)) != null) + throw new ExecutionException(ex); + throw new TimeoutException(); + } + return getRawResult(); } /** - * Joins this task, without returning its result or throwing an + * Joins this task, without returning its result or throwing its * exception. This method may be useful when processing * collections of tasks when some have been cancelled or otherwise * known to have aborted. */ public final void quietlyJoin() { - if (status >= 0) { - ForkJoinWorkerThread w = getWorker(); - if (w == null || !w.unpushTask(this) || !tryQuietlyInvoke()) - awaitDone(w, true); + Thread t; + if ((t = Thread.currentThread()) instanceof ForkJoinWorkerThread) { + ForkJoinWorkerThread w = (ForkJoinWorkerThread) t; + if (status >= 0) { + if (w.unpushTask(this)) { + boolean completed; + try { + completed = exec(); + } catch (Throwable rex) { + setExceptionalCompletion(rex); + return; + } + if (completed) { + setCompletion(NORMAL); + return; + } + } + w.joinTask(this); + } } + else + externalAwaitDone(); } /** * Commences performing this task and awaits its completion if - * necessary, without returning its result or throwing an - * exception. This method may be useful when processing - * collections of tasks when some have been cancelled or otherwise - * known to have aborted. + * necessary, without returning its result or throwing its + * exception. */ public final void quietlyInvoke() { - if (status >= 0 && !tryQuietlyInvoke()) - quietlyJoin(); + if (status >= 0) { + boolean completed; + try { + completed = exec(); + } catch (Throwable rex) { + setExceptionalCompletion(rex); + return; + } + if (completed) + setCompletion(NORMAL); + else + quietlyJoin(); + } } /** @@ -956,7 +839,7 @@ public abstract class ForkJoinTask implements Future, Serializable { * pre-constructed trees of subtasks in loops. */ public void reinitialize() { - if ((status & COMPLETION_MASK) == EXCEPTIONAL) + if (status == EXCEPTIONAL) exceptionMap.remove(this); status = 0; } @@ -1246,7 +1129,7 @@ public abstract class ForkJoinTask implements Future, Serializable { private static final long serialVersionUID = -7721805057305804111L; /** - * Saves the state to a stream. + * Saves the state to a stream (that is, serializes it). * * @serialData the current run status and the exception thrown * during execution, or {@code null} if none @@ -1259,18 +1142,16 @@ public abstract class ForkJoinTask implements Future, Serializable { } /** - * Reconstitutes the instance from a stream. + * Reconstitutes the instance from a stream (that is, deserializes it). * * @param s the stream */ private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); - status &= ~INTERNAL_SIGNAL_MASK; // clear internal signal counts - status |= EXTERNAL_SIGNAL; // conservatively set external signal Object ex = s.readObject(); if (ex != null) - setDoneExceptionally((Throwable) ex); + setExceptionalCompletion((Throwable) ex); } // Unsafe mechanics diff --git a/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java b/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java index 0dc14c71a2dc86fbeb4f864516d5bb3f4c2c55c9..784dd8056bca302cd9724cea05b8e78c92464d29 100644 --- a/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java +++ b/src/share/classes/java/util/concurrent/ForkJoinWorkerThread.java @@ -35,7 +35,9 @@ package java.util.concurrent; +import java.util.Random; import java.util.Collection; +import java.util.concurrent.locks.LockSupport; /** * A thread managed by a {@link ForkJoinPool}. This class is @@ -52,46 +54,55 @@ import java.util.Collection; */ public class ForkJoinWorkerThread extends Thread { /* - * Algorithm overview: - * - * 1. Work-Stealing: Work-stealing queues are special forms of - * Deques that support only three of the four possible - * end-operations -- push, pop, and deq (aka steal), and only do - * so under the constraints that push and pop are called only from - * the owning thread, while deq may be called from other threads. - * (If you are unfamiliar with them, you probably want to read - * Herlihy and Shavit's book "The Art of Multiprocessor - * programming", chapter 16 describing these in more detail before - * proceeding.) The main work-stealing queue design is roughly - * similar to "Dynamic Circular Work-Stealing Deque" by David - * Chase and Yossi Lev, SPAA 2005 - * (http://research.sun.com/scalable/pubs/index.html). The main - * difference ultimately stems from gc requirements that we null - * out taken slots as soon as we can, to maintain as small a - * footprint as possible even in programs generating huge numbers - * of tasks. To accomplish this, we shift the CAS arbitrating pop - * vs deq (steal) from being on the indices ("base" and "sp") to - * the slots themselves (mainly via method "casSlotNull()"). So, - * both a successful pop and deq mainly entail CAS'ing a non-null - * slot to null. Because we rely on CASes of references, we do - * not need tag bits on base or sp. They are simple ints as used - * in any circular array-based queue (see for example ArrayDeque). - * Updates to the indices must still be ordered in a way that - * guarantees that (sp - base) > 0 means the queue is empty, but - * otherwise may err on the side of possibly making the queue - * appear nonempty when a push, pop, or deq have not fully - * committed. Note that this means that the deq operation, - * considered individually, is not wait-free. One thief cannot - * successfully continue until another in-progress one (or, if - * previously empty, a push) completes. However, in the - * aggregate, we ensure at least probabilistic - * non-blockingness. If an attempted steal fails, a thief always - * chooses a different random victim target to try next. So, in - * order for one thief to progress, it suffices for any - * in-progress deq or new push on any empty queue to complete. One - * reason this works well here is that apparently-nonempty often - * means soon-to-be-stealable, which gives threads a chance to - * activate if necessary before stealing (see below). + * Overview: + * + * ForkJoinWorkerThreads are managed by ForkJoinPools and perform + * ForkJoinTasks. This class includes bookkeeping in support of + * worker activation, suspension, and lifecycle control described + * in more detail in the internal documentation of class + * ForkJoinPool. And as described further below, this class also + * includes special-cased support for some ForkJoinTask + * methods. But the main mechanics involve work-stealing: + * + * Work-stealing queues are special forms of Deques that support + * only three of the four possible end-operations -- push, pop, + * and deq (aka steal), under the further constraints that push + * and pop are called only from the owning thread, while deq may + * be called from other threads. (If you are unfamiliar with + * them, you probably want to read Herlihy and Shavit's book "The + * Art of Multiprocessor programming", chapter 16 describing these + * in more detail before proceeding.) The main work-stealing + * queue design is roughly similar to those in the papers "Dynamic + * Circular Work-Stealing Deque" by Chase and Lev, SPAA 2005 + * (http://research.sun.com/scalable/pubs/index.html) and + * "Idempotent work stealing" by Michael, Saraswat, and Vechev, + * PPoPP 2009 (http://portal.acm.org/citation.cfm?id=1504186). + * The main differences ultimately stem from gc requirements that + * we null out taken slots as soon as we can, to maintain as small + * a footprint as possible even in programs generating huge + * numbers of tasks. To accomplish this, we shift the CAS + * arbitrating pop vs deq (steal) from being on the indices + * ("base" and "sp") to the slots themselves (mainly via method + * "casSlotNull()"). So, both a successful pop and deq mainly + * entail a CAS of a slot from non-null to null. Because we rely + * on CASes of references, we do not need tag bits on base or sp. + * They are simple ints as used in any circular array-based queue + * (see for example ArrayDeque). Updates to the indices must + * still be ordered in a way that guarantees that sp == base means + * the queue is empty, but otherwise may err on the side of + * possibly making the queue appear nonempty when a push, pop, or + * deq have not fully committed. Note that this means that the deq + * operation, considered individually, is not wait-free. One thief + * cannot successfully continue until another in-progress one (or, + * if previously empty, a push) completes. However, in the + * aggregate, we ensure at least probabilistic non-blockingness. + * If an attempted steal fails, a thief always chooses a different + * random victim target to try next. So, in order for one thief to + * progress, it suffices for any in-progress deq or new push on + * any empty queue to complete. One reason this works well here is + * that apparently-nonempty often means soon-to-be-stealable, + * which gives threads a chance to set activation status if + * necessary before stealing. * * This approach also enables support for "async mode" where local * task processing is in FIFO, not LIFO order; simply by using a @@ -99,24 +110,54 @@ public class ForkJoinWorkerThread extends Thread { * by the ForkJoinPool). This allows use in message-passing * frameworks in which tasks are never joined. * - * Efficient implementation of this approach currently relies on - * an uncomfortable amount of "Unsafe" mechanics. To maintain + * When a worker would otherwise be blocked waiting to join a + * task, it first tries a form of linear helping: Each worker + * records (in field currentSteal) the most recent task it stole + * from some other worker. Plus, it records (in field currentJoin) + * the task it is currently actively joining. Method joinTask uses + * these markers to try to find a worker to help (i.e., steal back + * a task from and execute it) that could hasten completion of the + * actively joined task. In essence, the joiner executes a task + * that would be on its own local deque had the to-be-joined task + * not been stolen. This may be seen as a conservative variant of + * the approach in Wagner & Calder "Leapfrogging: a portable + * technique for implementing efficient futures" SIGPLAN Notices, + * 1993 (http://portal.acm.org/citation.cfm?id=155354). It differs + * in that: (1) We only maintain dependency links across workers + * upon steals, rather than use per-task bookkeeping. This may + * require a linear scan of workers array to locate stealers, but + * usually doesn't because stealers leave hints (that may become + * stale/wrong) of where to locate them. This isolates cost to + * when it is needed, rather than adding to per-task overhead. + * (2) It is "shallow", ignoring nesting and potentially cyclic + * mutual steals. (3) It is intentionally racy: field currentJoin + * is updated only while actively joining, which means that we + * miss links in the chain during long-lived tasks, GC stalls etc + * (which is OK since blocking in such cases is usually a good + * idea). (4) We bound the number of attempts to find work (see + * MAX_HELP_DEPTH) and fall back to suspending the worker and if + * necessary replacing it with a spare (see + * ForkJoinPool.awaitJoin). + * + * Efficient implementation of these algorithms currently relies + * on an uncomfortable amount of "Unsafe" mechanics. To maintain * correct orderings, reads and writes of variable base require - * volatile ordering. Variable sp does not require volatile write - * but needs cheaper store-ordering on writes. Because they are - * protected by volatile base reads, reads of the queue array and - * its slots do not need volatile load semantics, but writes (in - * push) require store order and CASes (in pop and deq) require - * (volatile) CAS semantics. (See "Idempotent work stealing" by - * Michael, Saraswat, and Vechev, PPoPP 2009 - * http://portal.acm.org/citation.cfm?id=1504186 for an algorithm - * with similar properties, but without support for nulling - * slots.) Since these combinations aren't supported using - * ordinary volatiles, the only way to accomplish these - * efficiently is to use direct Unsafe calls. (Using external - * AtomicIntegers and AtomicReferenceArrays for the indices and - * array is significantly slower because of memory locality and - * indirection effects.) + * volatile ordering. Variable sp does not require volatile + * writes but still needs store-ordering, which we accomplish by + * pre-incrementing sp before filling the slot with an ordered + * store. (Pre-incrementing also enables backouts used in + * joinTask.) Because they are protected by volatile base reads, + * reads of the queue array and its slots by other threads do not + * need volatile load semantics, but writes (in push) require + * store order and CASes (in pop and deq) require (volatile) CAS + * semantics. (Michael, Saraswat, and Vechev's algorithm has + * similar properties, but without support for nulling slots.) + * Since these combinations aren't supported using ordinary + * volatiles, the only way to accomplish these efficiently is to + * use direct Unsafe calls. (Using external AtomicIntegers and + * AtomicReferenceArrays for the indices and array is + * significantly slower because of memory locality and indirection + * effects.) * * Further, performance on most platforms is very sensitive to * placement and sizing of the (resizable) queue array. Even @@ -124,56 +165,45 @@ public class ForkJoinWorkerThread extends Thread { * initial size must be large enough to counteract cache * contention effects across multiple queues (especially in the * presence of GC cardmarking). Also, to improve thread-locality, - * queues are currently initialized immediately after the thread - * gets the initial signal to start processing tasks. However, - * all queue-related methods except pushTask are written in a way - * that allows them to instead be lazily allocated and/or disposed - * of when empty. All together, these low-level implementation - * choices produce as much as a factor of 4 performance - * improvement compared to naive implementations, and enable the - * processing of billions of tasks per second, sometimes at the - * expense of ugliness. - * - * 2. Run control: The primary run control is based on a global - * counter (activeCount) held by the pool. It uses an algorithm - * similar to that in Herlihy and Shavit section 17.6 to cause - * threads to eventually block when all threads declare they are - * inactive. For this to work, threads must be declared active - * when executing tasks, and before stealing a task. They must be - * inactive before blocking on the Pool Barrier (awaiting a new - * submission or other Pool event). In between, there is some free - * play which we take advantage of to avoid contention and rapid - * flickering of the global activeCount: If inactive, we activate - * only if a victim queue appears to be nonempty (see above). - * Similarly, a thread tries to inactivate only after a full scan - * of other threads. The net effect is that contention on - * activeCount is rarely a measurable performance issue. (There - * are also a few other cases where we scan for work rather than - * retry/block upon contention.) - * - * 3. Selection control. We maintain policy of always choosing to - * run local tasks rather than stealing, and always trying to - * steal tasks before trying to run a new submission. All steals - * are currently performed in randomly-chosen deq-order. It may be - * worthwhile to bias these with locality / anti-locality - * information, but doing this well probably requires more - * lower-level information from JVMs than currently provided. + * queues are initialized after starting. All together, these + * low-level implementation choices produce as much as a factor of + * 4 performance improvement compared to naive implementations, + * and enable the processing of billions of tasks per second, + * sometimes at the expense of ugliness. + */ + + /** + * Generator for initial random seeds for random victim + * selection. This is used only to create initial seeds. Random + * steals use a cheaper xorshift generator per steal attempt. We + * expect only rare contention on seedGenerator, so just use a + * plain Random. + */ + private static final Random seedGenerator = new Random(); + + /** + * The maximum stolen->joining link depth allowed in helpJoinTask. + * Depths for legitimate chains are unbounded, but we use a fixed + * constant to avoid (otherwise unchecked) cycles and bound + * staleness of traversal parameters at the expense of sometimes + * blocking when we could be helping. */ + private static final int MAX_HELP_DEPTH = 8; /** * Capacity of work-stealing queue array upon initialization. - * Must be a power of two. Initial size must be at least 2, but is + * Must be a power of two. Initial size must be at least 4, but is * padded to minimize cache effects. */ private static final int INITIAL_QUEUE_CAPACITY = 1 << 13; /** * Maximum work-stealing queue array size. Must be less than or - * equal to 1 << 28 to ensure lack of index wraparound. (This - * is less than usual bounds, because we need leftshift by 3 - * to be in int range). + * equal to 1 << (31 - width of array entry) to ensure lack of + * index wraparound. The value is set in the static block + * at the end of this file after obtaining width. */ - private static final int MAXIMUM_QUEUE_CAPACITY = 1 << 28; + private static final int MAXIMUM_QUEUE_CAPACITY; /** * The pool this thread works in. Accessed directly by ForkJoinTask. @@ -182,65 +212,118 @@ public class ForkJoinWorkerThread extends Thread { /** * The work-stealing queue array. Size must be a power of two. - * Initialized when thread starts, to improve memory locality. + * Initialized in onStart, to improve memory locality. */ private ForkJoinTask[] queue; + /** + * Index (mod queue.length) of least valid queue slot, which is + * always the next position to steal from if nonempty. + */ + private volatile int base; + /** * Index (mod queue.length) of next queue slot to push to or pop - * from. It is written only by owner thread, via ordered store. - * Both sp and base are allowed to wrap around on overflow, but - * (sp - base) still estimates size. + * from. It is written only by owner thread, and accessed by other + * threads only after reading (volatile) base. Both sp and base + * are allowed to wrap around on overflow, but (sp - base) still + * estimates size. */ - private volatile int sp; + private int sp; /** - * Index (mod queue.length) of least valid queue slot, which is - * always the next position to steal from if nonempty. + * The index of most recent stealer, used as a hint to avoid + * traversal in method helpJoinTask. This is only a hint because a + * worker might have had multiple steals and this only holds one + * of them (usually the most current). Declared non-volatile, + * relying on other prevailing sync to keep reasonably current. */ - private volatile int base; + private int stealHint; /** - * Activity status. When true, this worker is considered active. - * Must be false upon construction. It must be true when executing - * tasks, and BEFORE stealing a task. It must be false before - * calling pool.sync. + * Run state of this worker. In addition to the usual run levels, + * tracks if this worker is suspended as a spare, and if it was + * killed (trimmed) while suspended. However, "active" status is + * maintained separately and modified only in conjunction with + * CASes of the pool's runState (which are currently sadly + * manually inlined for performance.) Accessed directly by pool + * to simplify checks for normal (zero) status. */ - private boolean active; + volatile int runState; + + private static final int TERMINATING = 0x01; + private static final int TERMINATED = 0x02; + private static final int SUSPENDED = 0x04; // inactive spare + private static final int TRIMMED = 0x08; // killed while suspended /** - * Run state of this worker. Supports simple versions of the usual - * shutdown/shutdownNow control. + * Number of steals. Directly accessed (and reset) by + * pool.tryAccumulateStealCount when idle. */ - private volatile int runState; + int stealCount; /** * Seed for random number generator for choosing steal victims. - * Uses Marsaglia xorshift. Must be nonzero upon initialization. + * Uses Marsaglia xorshift. Must be initialized as nonzero. */ private int seed; /** - * Number of steals, transferred to pool when idle + * Activity status. When true, this worker is considered active. + * Accessed directly by pool. Must be false upon construction. + */ + boolean active; + + /** + * True if use local fifo, not default lifo, for local polling. + * Shadows value from ForkJoinPool. */ - private int stealCount; + private final boolean locallyFifo; /** * Index of this worker in pool array. Set once by pool before - * running, and accessed directly by pool during cleanup etc. + * running, and accessed directly by pool to locate this worker in + * its workers array. */ int poolIndex; /** - * The last barrier event waited for. Accessed in pool callback - * methods, but only by current thread. + * The last pool event waited for. Accessed only by pool in + * callback methods invoked within this thread. + */ + int lastEventCount; + + /** + * Encoded index and event count of next event waiter. Accessed + * only by ForkJoinPool for managing event waiters. + */ + volatile long nextWaiter; + + /** + * Number of times this thread suspended as spare. Accessed only + * by pool. + */ + int spareCount; + + /** + * Encoded index and count of next spare waiter. Accessed only + * by ForkJoinPool for managing spares. */ - long lastEventCount; + volatile int nextSpare; /** - * True if use local fifo, not default lifo, for local polling + * The task currently being joined, set only when actively trying + * to help other stealers in helpJoinTask. Written only by this + * thread, but read by others. */ - private boolean locallyFifo; + private volatile ForkJoinTask currentJoin; + + /** + * The task most recently stolen from another worker (or + * submission queue). Written only by this thread, but read by + * others. + */ + private volatile ForkJoinTask currentSteal; /** * Creates a ForkJoinWorkerThread operating in the given pool. @@ -249,13 +332,24 @@ public class ForkJoinWorkerThread extends Thread { * @throws NullPointerException if pool is null */ protected ForkJoinWorkerThread(ForkJoinPool pool) { - if (pool == null) throw new NullPointerException(); this.pool = pool; - // Note: poolIndex is set by pool during construction - // Remaining initialization is deferred to onStart + this.locallyFifo = pool.locallyFifo; + setDaemon(true); + // To avoid exposing construction details to subclasses, + // remaining initialization is in start() and onStart() } - // Public access methods + /** + * Performs additional initialization and starts this thread. + */ + final void start(int poolIndex, UncaughtExceptionHandler ueh) { + this.poolIndex = poolIndex; + if (ueh != null) + setUncaughtExceptionHandler(ueh); + start(); + } + + // Public/protected methods /** * Returns the pool hosting this thread. @@ -280,81 +374,55 @@ public class ForkJoinWorkerThread extends Thread { } /** - * Establishes local first-in-first-out scheduling mode for forked - * tasks that are never joined. - * - * @param async if true, use locally FIFO scheduling + * Initializes internal state after construction but before + * processing any tasks. If you override this method, you must + * invoke @code{super.onStart()} at the beginning of the method. + * Initialization requires care: Most fields must have legal + * default values, to ensure that attempted accesses from other + * threads work correctly even before this thread starts + * processing tasks. */ - void setAsyncMode(boolean async) { - locallyFifo = async; - } - - // Runstate management - - // Runstate values. Order matters - private static final int RUNNING = 0; - private static final int SHUTDOWN = 1; - private static final int TERMINATING = 2; - private static final int TERMINATED = 3; - - final boolean isShutdown() { return runState >= SHUTDOWN; } - final boolean isTerminating() { return runState >= TERMINATING; } - final boolean isTerminated() { return runState == TERMINATED; } - final boolean shutdown() { return transitionRunStateTo(SHUTDOWN); } - final boolean shutdownNow() { return transitionRunStateTo(TERMINATING); } + protected void onStart() { + int rs = seedGenerator.nextInt(); + seed = rs == 0? 1 : rs; // seed must be nonzero - /** - * Transitions to at least the given state. - * - * @return {@code true} if not already at least at given state - */ - private boolean transitionRunStateTo(int state) { - for (;;) { - int s = runState; - if (s >= state) - return false; - if (UNSAFE.compareAndSwapInt(this, runStateOffset, s, state)) - return true; - } - } + // Allocate name string and arrays in this thread + String pid = Integer.toString(pool.getPoolNumber()); + String wid = Integer.toString(poolIndex); + setName("ForkJoinPool-" + pid + "-worker-" + wid); - /** - * Tries to set status to active; fails on contention. - */ - private boolean tryActivate() { - if (!active) { - if (!pool.tryIncrementActiveCount()) - return false; - active = true; - } - return true; + queue = new ForkJoinTask[INITIAL_QUEUE_CAPACITY]; } /** - * Tries to set status to inactive; fails on contention. + * Performs cleanup associated with termination of this worker + * thread. If you override this method, you must invoke + * {@code super.onTermination} at the end of the overridden method. + * + * @param exception the exception causing this thread to abort due + * to an unrecoverable error, or {@code null} if completed normally */ - private boolean tryInactivate() { - if (active) { - if (!pool.tryDecrementActiveCount()) - return false; - active = false; + protected void onTermination(Throwable exception) { + try { + ForkJoinPool p = pool; + if (active) { + int a; // inline p.tryDecrementActiveCount + active = false; + do {} while (!UNSAFE.compareAndSwapInt + (p, poolRunStateOffset, a = p.runState, a - 1)); + } + cancelTasks(); + setTerminated(); + p.workerTerminated(this); + } catch (Throwable ex) { // Shouldn't ever happen + if (exception == null) // but if so, at least rethrown + exception = ex; + } finally { + if (exception != null) + UNSAFE.throwException(exception); } - return true; } - /** - * Computes next value for random victim probe. Scans don't - * require a very high quality generator, but also not a crummy - * one. Marsaglia xor-shift is cheap and works well. - */ - private static int xorShift(int r) { - r ^= (r << 13); - r ^= (r >>> 17); - return r ^ (r << 5); - } - - // Lifecycle methods - /** * This method is required to be public, but should never be * called explicitly. It performs the main run loop to execute @@ -364,7 +432,6 @@ public class ForkJoinWorkerThread extends Thread { Throwable exception = null; try { onStart(); - pool.sync(this); // await first pool event mainLoop(); } catch (Throwable ex) { exception = ex; @@ -373,138 +440,150 @@ public class ForkJoinWorkerThread extends Thread { } } + // helpers for run() + /** - * Executes tasks until shut down. + * Finds and executes tasks, and checks status while running. */ private void mainLoop() { - while (!isShutdown()) { - ForkJoinTask t = pollTask(); - if (t != null || (t = pollSubmission()) != null) - t.quietlyExec(); - else if (tryInactivate()) - pool.sync(this); + boolean ran = false; // true if ran a task on last step + ForkJoinPool p = pool; + for (;;) { + p.preStep(this, ran); + if (runState != 0) + break; + ran = tryExecSteal() || tryExecSubmission(); } } /** - * Initializes internal state after construction but before - * processing any tasks. If you override this method, you must - * invoke super.onStart() at the beginning of the method. - * Initialization requires care: Most fields must have legal - * default values, to ensure that attempted accesses from other - * threads work correctly even before this thread starts - * processing tasks. + * Tries to steal a task and execute it. + * + * @return true if ran a task */ - protected void onStart() { - // Allocate while starting to improve chances of thread-local - // isolation - queue = new ForkJoinTask[INITIAL_QUEUE_CAPACITY]; - // Initial value of seed need not be especially random but - // should differ across workers and must be nonzero - int p = poolIndex + 1; - seed = p + (p << 8) + (p << 16) + (p << 24); // spread bits + private boolean tryExecSteal() { + ForkJoinTask t; + if ((t = scan()) != null) { + t.quietlyExec(); + UNSAFE.putOrderedObject(this, currentStealOffset, null); + if (sp != base) + execLocalTasks(); + return true; + } + return false; } /** - * Performs cleanup associated with termination of this worker - * thread. If you override this method, you must invoke - * {@code super.onTermination} at the end of the overridden method. + * If a submission exists, try to activate and run it. * - * @param exception the exception causing this thread to abort due - * to an unrecoverable error, or {@code null} if completed normally + * @return true if ran a task */ - protected void onTermination(Throwable exception) { - // Execute remaining local tasks unless aborting or terminating - while (exception == null && pool.isProcessingTasks() && base != sp) { - try { - ForkJoinTask t = popTask(); - if (t != null) + private boolean tryExecSubmission() { + ForkJoinPool p = pool; + // This loop is needed in case attempt to activate fails, in + // which case we only retry if there still appears to be a + // submission. + while (p.hasQueuedSubmissions()) { + ForkJoinTask t; int a; + if (active || // inline p.tryIncrementActiveCount + (active = UNSAFE.compareAndSwapInt(p, poolRunStateOffset, + a = p.runState, a + 1))) { + if ((t = p.pollSubmission()) != null) { + UNSAFE.putOrderedObject(this, currentStealOffset, t); t.quietlyExec(); - } catch (Throwable ex) { - exception = ex; + UNSAFE.putOrderedObject(this, currentStealOffset, null); + if (sp != base) + execLocalTasks(); + return true; + } } } - // Cancel other tasks, transition status, notify pool, and - // propagate exception to uncaught exception handler - try { - do {} while (!tryInactivate()); // ensure inactive - cancelTasks(); - runState = TERMINATED; - pool.workerTerminated(this); - } catch (Throwable ex) { // Shouldn't ever happen - if (exception == null) // but if so, at least rethrown - exception = ex; - } finally { - if (exception != null) - ForkJoinTask.rethrowException(exception); - } - } - - // Intrinsics-based support for queue operations. - - private static long slotOffset(int i) { - return ((long) i << qShift) + qBase; + return false; } /** - * Adds in store-order the given task at given slot of q to null. - * Caller must ensure q is non-null and index is in range. + * Runs local tasks until queue is empty or shut down. Call only + * while active. */ - private static void setSlot(ForkJoinTask[] q, int i, - ForkJoinTask t) { - UNSAFE.putOrderedObject(q, slotOffset(i), t); + private void execLocalTasks() { + while (runState == 0) { + ForkJoinTask t = locallyFifo ? locallyDeqTask() : popTask(); + if (t != null) + t.quietlyExec(); + else if (sp == base) + break; + } } + /* + * Intrinsics-based atomic writes for queue slots. These are + * basically the same as methods in AtomicReferenceArray, but + * specialized for (1) ForkJoinTask elements (2) requirement that + * nullness and bounds checks have already been performed by + * callers and (3) effective offsets are known not to overflow + * from int to long (because of MAXIMUM_QUEUE_CAPACITY). We don't + * need corresponding version for reads: plain array reads are OK + * because they are protected by other volatile reads and are + * confirmed by CASes. + * + * Most uses don't actually call these methods, but instead contain + * inlined forms that enable more predictable optimization. We + * don't define the version of write used in pushTask at all, but + * instead inline there a store-fenced array slot write. + */ + /** - * CAS given slot of q to null. Caller must ensure q is non-null - * and index is in range. + * CASes slot i of array q from t to null. Caller must ensure q is + * non-null and index is in range. */ - private static boolean casSlotNull(ForkJoinTask[] q, int i, - ForkJoinTask t) { - return UNSAFE.compareAndSwapObject(q, slotOffset(i), t, null); + private static final boolean casSlotNull(ForkJoinTask[] q, int i, + ForkJoinTask t) { + return UNSAFE.compareAndSwapObject(q, (i << qShift) + qBase, t, null); } /** - * Sets sp in store-order. + * Performs a volatile write of the given task at given slot of + * array q. Caller must ensure q is non-null and index is in + * range. This method is used only during resets and backouts. */ - private void storeSp(int s) { - UNSAFE.putOrderedInt(this, spOffset, s); + private static final void writeSlot(ForkJoinTask[] q, int i, + ForkJoinTask t) { + UNSAFE.putObjectVolatile(q, (i << qShift) + qBase, t); } - // Main queue methods + // queue methods /** - * Pushes a task. Called only by current thread. + * Pushes a task. Call only from this thread. * * @param t the task. Caller must ensure non-null. */ final void pushTask(ForkJoinTask t) { ForkJoinTask[] q = queue; - int mask = q.length - 1; - int s = sp; - setSlot(q, s & mask, t); - storeSp(++s); - if ((s -= base) == 1) - pool.signalWork(); - else if (s >= mask) - growQueue(); + int mask = q.length - 1; // implicit assert q != null + int s = sp++; // ok to increment sp before slot write + UNSAFE.putOrderedObject(q, ((s & mask) << qShift) + qBase, t); + if ((s -= base) == 0) + pool.signalWork(); // was empty + else if (s == mask) + growQueue(); // is full } /** * Tries to take a task from the base of the queue, failing if - * either empty or contended. + * empty or contended. Note: Specializations of this code appear + * in locallyDeqTask and elsewhere. * * @return a task, or null if none or contended */ final ForkJoinTask deqTask() { ForkJoinTask t; ForkJoinTask[] q; - int i; - int b; + int b, i; if (sp != (b = base) && (q = queue) != null && // must read q after b - (t = q[i = (q.length - 1) & b]) != null && - casSlotNull(q, i, t)) { + (t = q[i = (q.length - 1) & b]) != null && base == b && + UNSAFE.compareAndSwapObject(q, (i << qShift) + qBase, t, null)) { base = b + 1; return t; } @@ -512,19 +591,20 @@ public class ForkJoinWorkerThread extends Thread { } /** - * Tries to take a task from the base of own queue, activating if - * necessary, failing only if empty. Called only by current thread. + * Tries to take a task from the base of own queue. Assumes active + * status. Called only by this thread. * * @return a task, or null if none */ final ForkJoinTask locallyDeqTask() { - int b; - while (sp != (b = base)) { - if (tryActivate()) { - ForkJoinTask[] q = queue; - int i = (q.length - 1) & b; - ForkJoinTask t = q[i]; - if (t != null && casSlotNull(q, i, t)) { + ForkJoinTask[] q = queue; + if (q != null) { + ForkJoinTask t; + int b, i; + while (sp != (b = base)) { + if ((t = q[i = (q.length - 1) & b]) != null && base == b && + UNSAFE.compareAndSwapObject(q, (i << qShift) + qBase, + t, null)) { base = b + 1; return t; } @@ -534,46 +614,50 @@ public class ForkJoinWorkerThread extends Thread { } /** - * Returns a popped task, or null if empty. Ensures active status - * if non-null. Called only by current thread. + * Returns a popped task, or null if empty. Assumes active status. + * Called only by this thread. */ - final ForkJoinTask popTask() { - int s = sp; - while (s != base) { - if (tryActivate()) { - ForkJoinTask[] q = queue; - int mask = q.length - 1; - int i = (s - 1) & mask; + private ForkJoinTask popTask() { + ForkJoinTask[] q = queue; + if (q != null) { + int s; + while ((s = sp) != base) { + int i = (q.length - 1) & --s; + long u = (i << qShift) + qBase; // raw offset ForkJoinTask t = q[i]; - if (t == null || !casSlotNull(q, i, t)) + if (t == null) // lost to stealer break; - storeSp(s - 1); - return t; + if (UNSAFE.compareAndSwapObject(q, u, t, null)) { + sp = s; // putOrderedInt may encourage more timely write + // UNSAFE.putOrderedInt(this, spOffset, s); + return t; + } } } return null; } /** - * Specialized version of popTask to pop only if - * topmost element is the given task. Called only - * by current thread while active. + * Specialized version of popTask to pop only if topmost element + * is the given task. Called only by this thread while active. * * @param t the task. Caller must ensure non-null. */ final boolean unpushTask(ForkJoinTask t) { + int s; ForkJoinTask[] q = queue; - int mask = q.length - 1; - int s = sp - 1; - if (casSlotNull(q, s & mask, t)) { - storeSp(s); + if ((s = sp) != base && q != null && + UNSAFE.compareAndSwapObject + (q, (((q.length - 1) & --s) << qShift) + qBase, t, null)) { + sp = s; // putOrderedInt may encourage more timely write + // UNSAFE.putOrderedInt(this, spOffset, s); return true; } return false; } /** - * Returns next task or null if empty or contended + * Returns next task, or null if empty or contended. */ final ForkJoinTask peekTask() { ForkJoinTask[] q = queue; @@ -606,104 +690,209 @@ public class ForkJoinWorkerThread extends Thread { ForkJoinTask t = oldQ[oldIndex]; if (t != null && !casSlotNull(oldQ, oldIndex, t)) t = null; - setSlot(newQ, b & newMask, t); + writeSlot(newQ, b & newMask, t); } while (++b != bf); pool.signalWork(); } + /** + * Computes next value for random victim probe in scan(). Scans + * don't require a very high quality generator, but also not a + * crummy one. Marsaglia xor-shift is cheap and works well enough. + * Note: This is manually inlined in scan(). + */ + private static final int xorShift(int r) { + r ^= r << 13; + r ^= r >>> 17; + return r ^ (r << 5); + } + /** * Tries to steal a task from another worker. Starts at a random * index of workers array, and probes workers until finding one * with non-empty queue or finding that all are empty. It * randomly selects the first n probes. If these are empty, it - * resorts to a full circular traversal, which is necessary to - * accurately set active status by caller. Also restarts if pool - * events occurred since last scan, which forces refresh of - * workers array, in case barrier was associated with resize. + * resorts to a circular sweep, which is necessary to accurately + * set active status. (The circular sweep uses steps of + * approximately half the array size plus 1, to avoid bias + * stemming from leftmost packing of the array in ForkJoinPool.) * * This method must be both fast and quiet -- usually avoiding * memory accesses that could disrupt cache sharing etc other than - * those needed to check for and take tasks. This accounts for, - * among other things, updating random seed in place without - * storing it until exit. + * those needed to check for and take tasks (or to activate if not + * already active). This accounts for, among other things, + * updating random seed in place without storing it until exit. * * @return a task, or null if none found */ private ForkJoinTask scan() { - ForkJoinTask t = null; - int r = seed; // extract once to keep scan quiet - ForkJoinWorkerThread[] ws; // refreshed on outer loop - int mask; // must be power 2 minus 1 and > 0 - outer:do { - if ((ws = pool.workers) != null && (mask = ws.length - 1) > 0) { - int idx = r; - int probes = ~mask; // use random index while negative - for (;;) { - r = xorShift(r); // update random seed - ForkJoinWorkerThread v = ws[mask & idx]; - if (v == null || v.sp == v.base) { - if (probes <= mask) - idx = (probes++ < 0) ? r : (idx + 1); - else - break; + ForkJoinPool p = pool; + ForkJoinWorkerThread[] ws; // worker array + int n; // upper bound of #workers + if ((ws = p.workers) != null && (n = ws.length) > 1) { + boolean canSteal = active; // shadow active status + int r = seed; // extract seed once + int mask = n - 1; + int j = -n; // loop counter + int k = r; // worker index, random if j < 0 + for (;;) { + ForkJoinWorkerThread v = ws[k & mask]; + r ^= r << 13; r ^= r >>> 17; r ^= r << 5; // inline xorshift + ForkJoinTask[] q; ForkJoinTask t; int b, a; + if (v != null && (b = v.base) != v.sp && + (q = v.queue) != null) { + int i = (q.length - 1) & b; + long u = (i << qShift) + qBase; // raw offset + int pid = poolIndex; + if ((t = q[i]) != null) { + if (!canSteal && // inline p.tryIncrementActiveCount + UNSAFE.compareAndSwapInt(p, poolRunStateOffset, + a = p.runState, a + 1)) + canSteal = active = true; + if (canSteal && v.base == b++ && + UNSAFE.compareAndSwapObject(q, u, t, null)) { + v.base = b; + v.stealHint = pid; + UNSAFE.putOrderedObject(this, + currentStealOffset, t); + seed = r; + ++stealCount; + return t; + } } - else if (!tryActivate() || (t = v.deqTask()) == null) - continue outer; // restart on contention - else - break outer; + j = -n; + k = r; // restart on contention } + else if (++j <= 0) + k = r; + else if (j <= n) + k += (n >>> 1) | 1; + else + break; } - } while (pool.hasNewSyncEvent(this)); // retry on pool events - seed = r; - return t; + } + return null; } + // Run State management + + // status check methods used mainly by ForkJoinPool + final boolean isRunning() { return runState == 0; } + final boolean isTerminating() { return (runState & TERMINATING) != 0; } + final boolean isTerminated() { return (runState & TERMINATED) != 0; } + final boolean isSuspended() { return (runState & SUSPENDED) != 0; } + final boolean isTrimmed() { return (runState & TRIMMED) != 0; } + /** - * Gets and removes a local or stolen task. - * - * @return a task, if available + * Sets state to TERMINATING. Does NOT unpark or interrupt + * to wake up if currently blocked. Callers must do so if desired. */ - final ForkJoinTask pollTask() { - ForkJoinTask t = locallyFifo ? locallyDeqTask() : popTask(); - if (t == null && (t = scan()) != null) - ++stealCount; - return t; + final void shutdown() { + for (;;) { + int s = runState; + if ((s & (TERMINATING|TERMINATED)) != 0) + break; + if ((s & SUSPENDED) != 0) { // kill and wakeup if suspended + if (UNSAFE.compareAndSwapInt(this, runStateOffset, s, + (s & ~SUSPENDED) | + (TRIMMED|TERMINATING))) + break; + } + else if (UNSAFE.compareAndSwapInt(this, runStateOffset, s, + s | TERMINATING)) + break; + } } /** - * Gets a local task. - * - * @return a task, if available + * Sets state to TERMINATED. Called only by onTermination(). */ - final ForkJoinTask pollLocalTask() { - return locallyFifo ? locallyDeqTask() : popTask(); + private void setTerminated() { + int s; + do {} while (!UNSAFE.compareAndSwapInt(this, runStateOffset, + s = runState, + s | (TERMINATING|TERMINATED))); } /** - * Returns a pool submission, if one exists, activating first. + * If suspended, tries to set status to unsuspended. + * Does NOT wake up if blocked. * - * @return a submission, if available + * @return true if successful */ - private ForkJoinTask pollSubmission() { + final boolean tryUnsuspend() { + int s; + while (((s = runState) & SUSPENDED) != 0) { + if (UNSAFE.compareAndSwapInt(this, runStateOffset, s, + s & ~SUSPENDED)) + return true; + } + return false; + } + + /** + * Sets suspended status and blocks as spare until resumed + * or shutdown. + */ + final void suspendAsSpare() { + for (;;) { // set suspended unless terminating + int s = runState; + if ((s & TERMINATING) != 0) { // must kill + if (UNSAFE.compareAndSwapInt(this, runStateOffset, s, + s | (TRIMMED | TERMINATING))) + return; + } + else if (UNSAFE.compareAndSwapInt(this, runStateOffset, s, + s | SUSPENDED)) + break; + } ForkJoinPool p = pool; - while (p.hasQueuedSubmissions()) { - ForkJoinTask t; - if (tryActivate() && (t = p.pollSubmission()) != null) - return t; + p.pushSpare(this); + while ((runState & SUSPENDED) != 0) { + if (p.tryAccumulateStealCount(this)) { + interrupted(); // clear/ignore interrupts + if ((runState & SUSPENDED) == 0) + break; + LockSupport.park(this); + } } - return null; } - // Methods accessed only by Pool + // Misc support methods for ForkJoinPool + + /** + * Returns an estimate of the number of tasks in the queue. Also + * used by ForkJoinTask. + */ + final int getQueueSize() { + int n; // external calls must read base first + return (n = -base + sp) <= 0 ? 0 : n; + } /** * Removes and cancels all tasks in queue. Can be called from any * thread. */ final void cancelTasks() { - ForkJoinTask t; - while (base != sp && (t = deqTask()) != null) - t.cancelIgnoringExceptions(); + ForkJoinTask cj = currentJoin; // try to cancel ongoing tasks + if (cj != null) { + currentJoin = null; + cj.cancelIgnoringExceptions(); + try { + this.interrupt(); // awaken wait + } catch (SecurityException ignore) { + } + } + ForkJoinTask cs = currentSteal; + if (cs != null) { + currentSteal = null; + cs.cancelIgnoringExceptions(); + } + while (base != sp) { + ForkJoinTask t = deqTask(); + if (t != null) + t.cancelIgnoringExceptions(); + } } /** @@ -713,87 +902,266 @@ public class ForkJoinWorkerThread extends Thread { */ final int drainTasksTo(Collection> c) { int n = 0; - ForkJoinTask t; - while (base != sp && (t = deqTask()) != null) { - c.add(t); - ++n; + while (base != sp) { + ForkJoinTask t = deqTask(); + if (t != null) { + c.add(t); + ++n; + } } return n; } + // Support methods for ForkJoinTask + /** - * Gets and clears steal count for accumulation by pool. Called - * only when known to be idle (in pool.sync and termination). + * Gets and removes a local task. + * + * @return a task, if available */ - final int getAndClearStealCount() { - int sc = stealCount; - stealCount = 0; - return sc; + final ForkJoinTask pollLocalTask() { + ForkJoinPool p = pool; + while (sp != base) { + int a; // inline p.tryIncrementActiveCount + if (active || + (active = UNSAFE.compareAndSwapInt(p, poolRunStateOffset, + a = p.runState, a + 1))) + return locallyFifo ? locallyDeqTask() : popTask(); + } + return null; } /** - * Returns {@code true} if at least one worker in the given array - * appears to have at least one queued task. + * Gets and removes a local or stolen task. * - * @param ws array of workers + * @return a task, if available */ - static boolean hasQueuedTasks(ForkJoinWorkerThread[] ws) { - if (ws != null) { - int len = ws.length; - for (int j = 0; j < 2; ++j) { // need two passes for clean sweep - for (int i = 0; i < len; ++i) { - ForkJoinWorkerThread w = ws[i]; - if (w != null && w.sp != w.base) - return true; - } - } + final ForkJoinTask pollTask() { + ForkJoinTask t = pollLocalTask(); + if (t == null) { + t = scan(); + // cannot retain/track/help steal + UNSAFE.putOrderedObject(this, currentStealOffset, null); } - return false; + return t; } - // Support methods for ForkJoinTask - /** - * Returns an estimate of the number of tasks in the queue. + * Possibly runs some tasks and/or blocks, until task is done. + * + * @param joinMe the task to join */ - final int getQueueSize() { - // suppress momentarily negative values - return Math.max(0, sp - base); + final void joinTask(ForkJoinTask joinMe) { + // currentJoin only written by this thread; only need ordered store + ForkJoinTask prevJoin = currentJoin; + UNSAFE.putOrderedObject(this, currentJoinOffset, joinMe); + if (sp != base) + localHelpJoinTask(joinMe); + if (joinMe.status >= 0) + pool.awaitJoin(joinMe, this); + UNSAFE.putOrderedObject(this, currentJoinOffset, prevJoin); } /** - * Returns an estimate of the number of tasks, offset by a - * function of number of idle workers. + * Run tasks in local queue until given task is done. + * + * @param joinMe the task to join */ - final int getEstimatedSurplusTaskCount() { - // The halving approximates weighting idle vs non-idle workers - return (sp - base) - (pool.getIdleThreadCount() >>> 1); + private void localHelpJoinTask(ForkJoinTask joinMe) { + int s; + ForkJoinTask[] q; + while (joinMe.status >= 0 && (s = sp) != base && (q = queue) != null) { + int i = (q.length - 1) & --s; + long u = (i << qShift) + qBase; // raw offset + ForkJoinTask t = q[i]; + if (t == null) // lost to a stealer + break; + if (UNSAFE.compareAndSwapObject(q, u, t, null)) { + /* + * This recheck (and similarly in helpJoinTask) + * handles cases where joinMe is independently + * cancelled or forced even though there is other work + * available. Back out of the pop by putting t back + * into slot before we commit by writing sp. + */ + if (joinMe.status < 0) { + UNSAFE.putObjectVolatile(q, u, t); + break; + } + sp = s; + // UNSAFE.putOrderedInt(this, spOffset, s); + t.quietlyExec(); + } + } } /** - * Scans, returning early if joinMe done. + * Unless terminating, tries to locate and help perform tasks for + * a stealer of the given task, or in turn one of its stealers. + * Traces currentSteal->currentJoin links looking for a thread + * working on a descendant of the given task and with a non-empty + * queue to steal back and execute tasks from. + * + * The implementation is very branchy to cope with potential + * inconsistencies or loops encountering chains that are stale, + * unknown, or of length greater than MAX_HELP_DEPTH links. All + * of these cases are dealt with by just returning back to the + * caller, who is expected to retry if other join mechanisms also + * don't work out. + * + * @param joinMe the task to join */ - final ForkJoinTask scanWhileJoining(ForkJoinTask joinMe) { - ForkJoinTask t = pollTask(); - if (t != null && joinMe.status < 0 && sp == base) { - pushTask(t); // unsteal if done and this task would be stealable - t = null; + final void helpJoinTask(ForkJoinTask joinMe) { + ForkJoinWorkerThread[] ws; + int n; + if (joinMe.status < 0) // already done + return; + if ((runState & TERMINATING) != 0) { // cancel if shutting down + joinMe.cancelIgnoringExceptions(); + return; + } + if ((ws = pool.workers) == null || (n = ws.length) <= 1) + return; // need at least 2 workers + + ForkJoinTask task = joinMe; // base of chain + ForkJoinWorkerThread thread = this; // thread with stolen task + for (int d = 0; d < MAX_HELP_DEPTH; ++d) { // chain length + // Try to find v, the stealer of task, by first using hint + ForkJoinWorkerThread v = ws[thread.stealHint & (n - 1)]; + if (v == null || v.currentSteal != task) { + for (int j = 0; ; ++j) { // search array + if (j < n) { + ForkJoinTask vs; + if ((v = ws[j]) != null && + (vs = v.currentSteal) != null) { + if (joinMe.status < 0 || task.status < 0) + return; // stale or done + if (vs == task) { + thread.stealHint = j; + break; // save hint for next time + } + } + } + else + return; // no stealer + } + } + for (;;) { // Try to help v, using specialized form of deqTask + if (joinMe.status < 0) + return; + int b = v.base; + ForkJoinTask[] q = v.queue; + if (b == v.sp || q == null) + break; + int i = (q.length - 1) & b; + long u = (i << qShift) + qBase; + ForkJoinTask t = q[i]; + int pid = poolIndex; + ForkJoinTask ps = currentSteal; + if (task.status < 0) + return; // stale or done + if (t != null && v.base == b++ && + UNSAFE.compareAndSwapObject(q, u, t, null)) { + if (joinMe.status < 0) { + UNSAFE.putObjectVolatile(q, u, t); + return; // back out on cancel + } + v.base = b; + v.stealHint = pid; + UNSAFE.putOrderedObject(this, currentStealOffset, t); + t.quietlyExec(); + UNSAFE.putOrderedObject(this, currentStealOffset, ps); + } + } + // Try to descend to find v's stealer + ForkJoinTask next = v.currentJoin; + if (task.status < 0 || next == null || next == task || + joinMe.status < 0) + return; + task = next; + thread = v; } - return t; + } + + /** + * Implements ForkJoinTask.getSurplusQueuedTaskCount(). + * Returns an estimate of the number of tasks, offset by a + * function of number of idle workers. + * + * This method provides a cheap heuristic guide for task + * partitioning when programmers, frameworks, tools, or languages + * have little or no idea about task granularity. In essence by + * offering this method, we ask users only about tradeoffs in + * overhead vs expected throughput and its variance, rather than + * how finely to partition tasks. + * + * In a steady state strict (tree-structured) computation, each + * thread makes available for stealing enough tasks for other + * threads to remain active. Inductively, if all threads play by + * the same rules, each thread should make available only a + * constant number of tasks. + * + * The minimum useful constant is just 1. But using a value of 1 + * would require immediate replenishment upon each steal to + * maintain enough tasks, which is infeasible. Further, + * partitionings/granularities of offered tasks should minimize + * steal rates, which in general means that threads nearer the top + * of computation tree should generate more than those nearer the + * bottom. In perfect steady state, each thread is at + * approximately the same level of computation tree. However, + * producing extra tasks amortizes the uncertainty of progress and + * diffusion assumptions. + * + * So, users will want to use values larger, but not much larger + * than 1 to both smooth over transient shortages and hedge + * against uneven progress; as traded off against the cost of + * extra task overhead. We leave the user to pick a threshold + * value to compare with the results of this call to guide + * decisions, but recommend values such as 3. + * + * When all threads are active, it is on average OK to estimate + * surplus strictly locally. In steady-state, if one thread is + * maintaining say 2 surplus tasks, then so are others. So we can + * just use estimated queue length (although note that (sp - base) + * can be an overestimate because of stealers lagging increments + * of base). However, this strategy alone leads to serious + * mis-estimates in some non-steady-state conditions (ramp-up, + * ramp-down, other stalls). We can detect many of these by + * further considering the number of "idle" threads, that are + * known to have zero queued tasks, so compensate by a factor of + * (#idle/#active) threads. + */ + final int getEstimatedSurplusTaskCount() { + return sp - base - pool.idlePerActive(); } /** * Runs tasks until {@code pool.isQuiescent()}. */ final void helpQuiescePool() { + ForkJoinTask ps = currentSteal; // to restore below for (;;) { - ForkJoinTask t = pollTask(); - if (t != null) + ForkJoinTask t = pollLocalTask(); + if (t != null || (t = scan()) != null) t.quietlyExec(); - else if (tryInactivate() && pool.isQuiescent()) - break; + else { + ForkJoinPool p = pool; + int a; // to inline CASes + if (active) { + if (!UNSAFE.compareAndSwapInt + (p, poolRunStateOffset, a = p.runState, a - 1)) + continue; // retry later + active = false; // inactivate + UNSAFE.putOrderedObject(this, currentStealOffset, ps); + } + if (p.isQuiescent()) { + active = true; // re-activate + do {} while (!UNSAFE.compareAndSwapInt + (p, poolRunStateOffset, a = p.runState, a+1)); + return; + } + } } - do {} while (!tryActivate()); // re-activate on exit } // Unsafe mechanics @@ -803,15 +1171,23 @@ public class ForkJoinWorkerThread extends Thread { objectFieldOffset("sp", ForkJoinWorkerThread.class); private static final long runStateOffset = objectFieldOffset("runState", ForkJoinWorkerThread.class); - private static final long qBase; + private static final long currentJoinOffset = + objectFieldOffset("currentJoin", ForkJoinWorkerThread.class); + private static final long currentStealOffset = + objectFieldOffset("currentSteal", ForkJoinWorkerThread.class); + private static final long qBase = + UNSAFE.arrayBaseOffset(ForkJoinTask[].class); + private static final long poolRunStateOffset = // to inline CAS + objectFieldOffset("runState", ForkJoinPool.class); + private static final int qShift; static { - qBase = UNSAFE.arrayBaseOffset(ForkJoinTask[].class); int s = UNSAFE.arrayIndexScale(ForkJoinTask[].class); if ((s & (s-1)) != 0) throw new Error("data type scale not a power of two"); qShift = 31 - Integer.numberOfLeadingZeros(s); + MAXIMUM_QUEUE_CAPACITY = 1 << (31 - qShift); } private static long objectFieldOffset(String field, Class klazz) { diff --git a/src/share/classes/java/util/concurrent/LinkedTransferQueue.java b/src/share/classes/java/util/concurrent/LinkedTransferQueue.java index ae327cabf36363a00c8b1d7d15f71193c2ba2f31..82bed9290729a17e563ebbf7236e4099516be29d 100644 --- a/src/share/classes/java/util/concurrent/LinkedTransferQueue.java +++ b/src/share/classes/java/util/concurrent/LinkedTransferQueue.java @@ -42,6 +42,7 @@ import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Queue; import java.util.concurrent.locks.LockSupport; + /** * An unbounded {@link TransferQueue} based on linked nodes. * This queue orders elements FIFO (first-in-first-out) with respect @@ -233,24 +234,6 @@ public class LinkedTransferQueue extends AbstractQueue * additional GC bookkeeping ("write barriers") that are sometimes * more costly than the writes themselves because of contention). * - * Removal of interior nodes (due to timed out or interrupted - * waits, or calls to remove(x) or Iterator.remove) can use a - * scheme roughly similar to that described in Scherer, Lea, and - * Scott's SynchronousQueue. Given a predecessor, we can unsplice - * any node except the (actual) tail of the queue. To avoid - * build-up of cancelled trailing nodes, upon a request to remove - * a trailing node, it is placed in field "cleanMe" to be - * unspliced upon the next call to unsplice any other node. - * Situations needing such mechanics are not common but do occur - * in practice; for example when an unbounded series of short - * timed calls to poll repeatedly time out but never otherwise - * fall off the list because of an untimed call to take at the - * front of the queue. Note that maintaining field cleanMe does - * not otherwise much impact garbage retention even if never - * cleared by some other call because the held node will - * eventually either directly or indirectly lead to a self-link - * once off the list. - * * *** Overview of implementation *** * * We use a threshold-based approach to updates, with a slack @@ -266,15 +249,10 @@ public class LinkedTransferQueue extends AbstractQueue * per-thread one available, but even ThreadLocalRandom is too * heavy for these purposes. * - * With such a small slack threshold value, it is rarely - * worthwhile to augment this with path short-circuiting; i.e., - * unsplicing nodes between head and the first unmatched node, or - * similarly for tail, rather than advancing head or tail - * proper. However, it is used (in awaitMatch) immediately before - * a waiting thread starts to block, as a final bit of helping at - * a point when contention with others is extremely unlikely - * (since if other threads that could release it are operating, - * then the current thread wouldn't be blocking). + * With such a small slack threshold value, it is not worthwhile + * to augment this with path short-circuiting (i.e., unsplicing + * interior nodes) except in the case of cancellation/removal (see + * below). * * We allow both the head and tail fields to be null before any * nodes are enqueued; initializing upon first append. This @@ -356,6 +334,70 @@ public class LinkedTransferQueue extends AbstractQueue * versa) compared to their predecessors receive additional * chained spins, reflecting longer paths typically required to * unblock threads during phase changes. + * + * + * ** Unlinking removed interior nodes ** + * + * In addition to minimizing garbage retention via self-linking + * described above, we also unlink removed interior nodes. These + * may arise due to timed out or interrupted waits, or calls to + * remove(x) or Iterator.remove. Normally, given a node that was + * at one time known to be the predecessor of some node s that is + * to be removed, we can unsplice s by CASing the next field of + * its predecessor if it still points to s (otherwise s must + * already have been removed or is now offlist). But there are two + * situations in which we cannot guarantee to make node s + * unreachable in this way: (1) If s is the trailing node of list + * (i.e., with null next), then it is pinned as the target node + * for appends, so can only be removed later after other nodes are + * appended. (2) We cannot necessarily unlink s given a + * predecessor node that is matched (including the case of being + * cancelled): the predecessor may already be unspliced, in which + * case some previous reachable node may still point to s. + * (For further explanation see Herlihy & Shavit "The Art of + * Multiprocessor Programming" chapter 9). Although, in both + * cases, we can rule out the need for further action if either s + * or its predecessor are (or can be made to be) at, or fall off + * from, the head of list. + * + * Without taking these into account, it would be possible for an + * unbounded number of supposedly removed nodes to remain + * reachable. Situations leading to such buildup are uncommon but + * can occur in practice; for example when a series of short timed + * calls to poll repeatedly time out but never otherwise fall off + * the list because of an untimed call to take at the front of the + * queue. + * + * When these cases arise, rather than always retraversing the + * entire list to find an actual predecessor to unlink (which + * won't help for case (1) anyway), we record a conservative + * estimate of possible unsplice failures (in "sweepVotes"). + * We trigger a full sweep when the estimate exceeds a threshold + * ("SWEEP_THRESHOLD") indicating the maximum number of estimated + * removal failures to tolerate before sweeping through, unlinking + * cancelled nodes that were not unlinked upon initial removal. + * We perform sweeps by the thread hitting threshold (rather than + * background threads or by spreading work to other threads) + * because in the main contexts in which removal occurs, the + * caller is already timed-out, cancelled, or performing a + * potentially O(n) operation (e.g. remove(x)), none of which are + * time-critical enough to warrant the overhead that alternatives + * would impose on other threads. + * + * Because the sweepVotes estimate is conservative, and because + * nodes become unlinked "naturally" as they fall off the head of + * the queue, and because we allow votes to accumulate even while + * sweeps are in progress, there are typically significantly fewer + * such nodes than estimated. Choice of a threshold value + * balances the likelihood of wasted effort and contention, versus + * providing a worst-case bound on retention of interior nodes in + * quiescent queues. The value defined below was chosen + * empirically to balance these under various timeout scenarios. + * + * Note that we cannot self-link unlinked interior nodes during + * sweeps. However, the associated garbage chains terminate when + * some successor ultimately falls off the head of the list and is + * self-linked. */ /** True if on multiprocessor */ @@ -381,12 +423,20 @@ public class LinkedTransferQueue extends AbstractQueue */ private static final int CHAINED_SPINS = FRONT_SPINS >>> 1; + /** + * The maximum number of estimated removal failures (sweepVotes) + * to tolerate before sweeping through the queue unlinking + * cancelled nodes that were not unlinked upon initial + * removal. See above for explanation. The value must be at least + * two to avoid useless sweeps when removing trailing nodes. + */ + static final int SWEEP_THRESHOLD = 32; + /** * Queue nodes. Uses Object, not E, for items to allow forgetting * them after use. Relies heavily on Unsafe mechanics to minimize - * unnecessary ordering constraints: Writes that intrinsically - * precede or follow CASes use simple relaxed forms. Other - * cleanups use releasing/lazy writes. + * unnecessary ordering constraints: Writes that are intrinsically + * ordered wrt other accesses or CASes use simple relaxed forms. */ static final class Node { final boolean isData; // false if this is a request node @@ -400,13 +450,13 @@ public class LinkedTransferQueue extends AbstractQueue } final boolean casItem(Object cmp, Object val) { - // assert cmp == null || cmp.getClass() != Node.class; + // assert cmp == null || cmp.getClass() != Node.class; return UNSAFE.compareAndSwapObject(this, itemOffset, cmp, val); } /** - * Creates a new node. Uses relaxed write because item can only - * be seen if followed by CAS. + * Constructs a new node. Uses relaxed write because item can + * only be seen after publication via casNext. */ Node(Object item, boolean isData) { UNSAFE.putObject(this, itemOffset, item); // relaxed write @@ -422,13 +472,17 @@ public class LinkedTransferQueue extends AbstractQueue } /** - * Sets item to self (using a releasing/lazy write) and waiter - * to null, to avoid garbage retention after extracting or - * cancelling. + * Sets item to self and waiter to null, to avoid garbage + * retention after matching or cancelling. Uses relaxed writes + * because order is already constrained in the only calling + * contexts: item is forgotten only after volatile/atomic + * mechanics that extract items. Similarly, clearing waiter + * follows either CAS or return from park (if ever parked; + * else we don't care). */ final void forgetContents() { - UNSAFE.putOrderedObject(this, itemOffset, this); - UNSAFE.putOrderedObject(this, waiterOffset, null); + UNSAFE.putObject(this, itemOffset, this); + UNSAFE.putObject(this, waiterOffset, null); } /** @@ -462,7 +516,7 @@ public class LinkedTransferQueue extends AbstractQueue * Tries to artificially match a data node -- used by remove. */ final boolean tryMatchData() { - // assert isData; + // assert isData; Object x = item; if (x != null && x != this && casItem(x, null)) { LockSupport.unpark(waiter); @@ -486,12 +540,12 @@ public class LinkedTransferQueue extends AbstractQueue /** head of the queue; null until first enqueue */ transient volatile Node head; - /** predecessor of dangling unspliceable node */ - private transient volatile Node cleanMe; // decl here reduces contention - /** tail of the queue; null until first append */ private transient volatile Node tail; + /** The number of apparent failures to unsplice removed nodes */ + private transient volatile int sweepVotes; + // CAS methods for fields private boolean casTail(Node cmp, Node val) { return UNSAFE.compareAndSwapObject(this, tailOffset, cmp, val); @@ -501,8 +555,8 @@ public class LinkedTransferQueue extends AbstractQueue return UNSAFE.compareAndSwapObject(this, headOffset, cmp, val); } - private boolean casCleanMe(Node cmp, Node val) { - return UNSAFE.compareAndSwapObject(this, cleanMeOffset, cmp, val); + private boolean casSweepVotes(int cmp, int val) { + return UNSAFE.compareAndSwapInt(this, sweepVotesOffset, cmp, val); } /* @@ -515,7 +569,7 @@ public class LinkedTransferQueue extends AbstractQueue @SuppressWarnings("unchecked") static E cast(Object item) { - // assert item == null || item.getClass() != Node.class; + // assert item == null || item.getClass() != Node.class; return (E) item; } @@ -544,10 +598,8 @@ public class LinkedTransferQueue extends AbstractQueue break; if (p.casItem(item, e)) { // match for (Node q = p; q != h;) { - Node n = q.next; // update head by 2 - if (n != null) // unless singleton - q = n; - if (head == h && casHead(h, q)) { + Node n = q.next; // update by 2 unless singleton + if (head == h && casHead(h, n == null? q : n)) { h.forgetNext(); break; } // advance and retry @@ -632,12 +684,12 @@ public class LinkedTransferQueue extends AbstractQueue for (;;) { Object item = s.item; if (item != e) { // matched - // assert item != s; + // assert item != s; s.forgetContents(); // avoid garbage return this.cast(item); } if ((w.isInterrupted() || (timed && nanos <= 0)) && - s.casItem(e, s)) { // cancel + s.casItem(e, s)) { // cancel unsplice(pred, s); return e; } @@ -647,9 +699,8 @@ public class LinkedTransferQueue extends AbstractQueue randomYields = ThreadLocalRandom.current(); } else if (spins > 0) { // spin - if (--spins == 0) - shortenHeadPath(); // reduce slack before blocking - else if (randomYields.nextInt(CHAINED_SPINS) == 0) + --spins; + if (randomYields.nextInt(CHAINED_SPINS) == 0) Thread.yield(); // occasionally yield } else if (s.waiter == null) { @@ -663,8 +714,6 @@ public class LinkedTransferQueue extends AbstractQueue } else { LockSupport.park(this); - s.waiter = null; - spins = -1; // spin if front upon wakeup } } } @@ -685,27 +734,6 @@ public class LinkedTransferQueue extends AbstractQueue return 0; } - /** - * Tries (once) to unsplice nodes between head and first unmatched - * or trailing node; failing on contention. - */ - private void shortenHeadPath() { - Node h, hn, p, q; - if ((p = h = head) != null && h.isMatched() && - (q = hn = h.next) != null) { - Node n; - while ((n = q.next) != q) { - if (n == null || !q.isMatched()) { - if (hn != q && h.next == hn) - h.casNext(hn, q); - break; - } - p = q; - q = n; - } - } - } - /* -------------- Traversal methods -------------- */ /** @@ -818,7 +846,8 @@ public class LinkedTransferQueue extends AbstractQueue public final void remove() { Node p = lastRet; if (p == null) throw new IllegalStateException(); - findAndRemoveDataNode(lastPred, p); + if (p.tryMatchData()) + unsplice(lastPred, p); } } @@ -828,99 +857,68 @@ public class LinkedTransferQueue extends AbstractQueue * Unsplices (now or later) the given deleted/cancelled node with * the given predecessor. * - * @param pred predecessor of node to be unspliced + * @param pred a node that was at one time known to be the + * predecessor of s, or null or s itself if s is/was at head * @param s the node to be unspliced */ - private void unsplice(Node pred, Node s) { - s.forgetContents(); // clear unneeded fields + final void unsplice(Node pred, Node s) { + s.forgetContents(); // forget unneeded fields /* - * At any given time, exactly one node on list cannot be - * unlinked -- the last inserted node. To accommodate this, if - * we cannot unlink s, we save its predecessor as "cleanMe", - * processing the previously saved version first. Because only - * one node in the list can have a null next, at least one of - * node s or the node previously saved can always be - * processed, so this always terminates. + * See above for rationale. Briefly: if pred still points to + * s, try to unlink s. If s cannot be unlinked, because it is + * trailing node or pred might be unlinked, and neither pred + * nor s are head or offlist, add to sweepVotes, and if enough + * votes have accumulated, sweep. */ - if (pred != null && pred != s) { - while (pred.next == s) { - Node oldpred = (cleanMe == null) ? null : reclean(); - Node n = s.next; - if (n != null) { - if (n != s) - pred.casNext(s, n); - break; + if (pred != null && pred != s && pred.next == s) { + Node n = s.next; + if (n == null || + (n != s && pred.casNext(s, n) && pred.isMatched())) { + for (;;) { // check if at, or could be, head + Node h = head; + if (h == pred || h == s || h == null) + return; // at head or list empty + if (!h.isMatched()) + break; + Node hn = h.next; + if (hn == null) + return; // now empty + if (hn != h && casHead(h, hn)) + h.forgetNext(); // advance head } - if (oldpred == pred || // Already saved - ((oldpred == null || oldpred.next == s) && - casCleanMe(oldpred, pred))) { - break; + if (pred.next != pred && s.next != s) { // recheck if offlist + for (;;) { // sweep now if enough votes + int v = sweepVotes; + if (v < SWEEP_THRESHOLD) { + if (casSweepVotes(v, v + 1)) + break; + } + else if (casSweepVotes(v, 0)) { + sweep(); + break; + } + } } } } } /** - * Tries to unsplice the deleted/cancelled node held in cleanMe - * that was previously uncleanable because it was at tail. - * - * @return current cleanMe node (or null) + * Unlinks matched (typically cancelled) nodes encountered in a + * traversal from head. */ - private Node reclean() { - /* - * cleanMe is, or at one time was, predecessor of a cancelled - * node s that was the tail so could not be unspliced. If it - * is no longer the tail, try to unsplice if necessary and - * make cleanMe slot available. This differs from similar - * code in unsplice() because we must check that pred still - * points to a matched node that can be unspliced -- if not, - * we can (must) clear cleanMe without unsplicing. This can - * loop only due to contention. - */ - Node pred; - while ((pred = cleanMe) != null) { - Node s = pred.next; - Node n; - if (s == null || s == pred || !s.isMatched()) - casCleanMe(pred, null); // already gone - else if ((n = s.next) != null) { - if (n != s) - pred.casNext(s, n); - casCleanMe(pred, null); - } - else + private void sweep() { + for (Node p = head, s, n; p != null && (s = p.next) != null; ) { + if (!s.isMatched()) + // Unmatched nodes are never self-linked + p = s; + else if ((n = s.next) == null) // trailing node is pinned break; - } - return pred; - } - - /** - * Main implementation of Iterator.remove(). Finds - * and unsplices the given data node. - * - * @param possiblePred possible predecessor of s - * @param s the node to remove - */ - final void findAndRemoveDataNode(Node possiblePred, Node s) { - // assert s.isData; - if (s.tryMatchData()) { - if (possiblePred != null && possiblePred.next == s) - unsplice(possiblePred, s); // was actual predecessor - else { - for (Node pred = null, p = head; p != null; ) { - if (p == s) { - unsplice(pred, p); - break; - } - if (p.isUnmatchedRequest()) - break; - pred = p; - if ((p = p.next) == pred) { // stale - pred = null; - p = head; - } - } - } + else if (s == n) // stale + // No need to also check for p == s, since that implies s == n + p = head; + else + p.casNext(s, n); } } @@ -1158,7 +1156,11 @@ public class LinkedTransferQueue extends AbstractQueue * @return {@code true} if this queue contains no elements */ public boolean isEmpty() { - return firstOfMode(true) == null; + for (Node p = head; p != null; p = succ(p)) { + if (!p.isMatched()) + return !p.isData; + } + return true; } public boolean hasWaitingConsumer() { @@ -1252,8 +1254,8 @@ public class LinkedTransferQueue extends AbstractQueue objectFieldOffset(UNSAFE, "head", LinkedTransferQueue.class); private static final long tailOffset = objectFieldOffset(UNSAFE, "tail", LinkedTransferQueue.class); - private static final long cleanMeOffset = - objectFieldOffset(UNSAFE, "cleanMe", LinkedTransferQueue.class); + private static final long sweepVotesOffset = + objectFieldOffset(UNSAFE, "sweepVotes", LinkedTransferQueue.class); static long objectFieldOffset(sun.misc.Unsafe UNSAFE, String field, Class klazz) { @@ -1266,5 +1268,4 @@ public class LinkedTransferQueue extends AbstractQueue throw error; } } - } diff --git a/src/share/classes/java/util/concurrent/Phaser.java b/src/share/classes/java/util/concurrent/Phaser.java index 7bf643cdfdb8267469c0e3dacf0f84b3fd887a7a..623f46e41ed4d700dddb1f45905bb9d801f9e800 100644 --- a/src/share/classes/java/util/concurrent/Phaser.java +++ b/src/share/classes/java/util/concurrent/Phaser.java @@ -898,7 +898,7 @@ public class Phaser { boolean doWait() { if (thread != null) { try { - ForkJoinPool.managedBlock(this, false); + ForkJoinPool.managedBlock(this); } catch (InterruptedException ie) { } } diff --git a/src/share/classes/java/util/spi/LocaleNameProvider.java b/src/share/classes/java/util/spi/LocaleNameProvider.java index b54195932e012a68722c2cd2febb3c7c446af6df..8c3639e171a41699994c83d1af40480c039904ce 100644 --- a/src/share/classes/java/util/spi/LocaleNameProvider.java +++ b/src/share/classes/java/util/spi/LocaleNameProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -44,22 +44,23 @@ public abstract class LocaleNameProvider extends LocaleServiceProvider { } /** - * Returns a localized name for the given ISO 639 language code and the - * given locale that is appropriate for display to the user. + * Returns a localized name for the given + * IETF BCP47 language code and the given locale that is appropriate for + * display to the user. * For example, if languageCode is "fr" and locale * is en_US, getDisplayLanguage() will return "French"; if languageCode * is "en" and locale is fr_FR, getDisplayLanguage() will return "anglais". * If the name returned cannot be localized according to locale, * (say, the provider does not have a Japanese name for Croatian), * this method returns null. - * @param languageCode the ISO 639 language code string in the form of two + * @param languageCode the language code string in the form of two to eight * lower-case letters between 'a' (U+0061) and 'z' (U+007A) * @param locale the desired locale * @return the name of the given language code for the specified locale, or null if it's not * available. * @exception NullPointerException if languageCode or locale is null * @exception IllegalArgumentException if languageCode is not in the form of - * two lower-case letters, or locale isn't + * two or three lower-case letters, or locale isn't * one of the locales returned from * {@link java.util.spi.LocaleServiceProvider#getAvailableLocales() * getAvailableLocales()}. @@ -68,22 +69,52 @@ public abstract class LocaleNameProvider extends LocaleServiceProvider { public abstract String getDisplayLanguage(String languageCode, Locale locale); /** - * Returns a localized name for the given ISO 3166 country code and the - * given locale that is appropriate for display to the user. + * Returns a localized name for the given + * IETF BCP47 script code and the given locale that is appropriate for + * display to the user. + * For example, if scriptCode is "Latn" and locale + * is en_US, getDisplayScript() will return "Latin"; if scriptCode + * is "Cyrl" and locale is fr_FR, getDisplayScript() will return "cyrillique". + * If the name returned cannot be localized according to locale, + * (say, the provider does not have a Japanese name for Cyrillic), + * this method returns null. + * @param scriptCode the four letter script code string in the form of title-case + * letters (the first letter is upper-case character between 'A' (U+0041) and + * 'Z' (U+005A) followed by three lower-case character between 'a' (U+0061) + * and 'z' (U+007A)). + * @param locale the desired locale + * @return the name of the given script code for the specified locale, or null if it's not + * available. + * @exception NullPointerException if scriptCode or locale is null + * @exception IllegalArgumentException if scriptCode is not in the form of + * four title case letters, or locale isn't + * one of the locales returned from + * {@link java.util.spi.LocaleServiceProvider#getAvailableLocales() + * getAvailableLocales()}. + * @see java.util.Locale#getDisplayScript(java.util.Locale) + * @since 1.7 + */ + public abstract String getDisplayScript(String scriptCode, Locale locale); + + /** + * Returns a localized name for the given + * IETF BCP47 region code (either ISO 3166 country code or UN M.49 area + * codes) and the given locale that is appropriate for display to the user. * For example, if countryCode is "FR" and locale * is en_US, getDisplayCountry() will return "France"; if countryCode * is "US" and locale is fr_FR, getDisplayCountry() will return "Etats-Unis". * If the name returned cannot be localized according to locale, * (say, the provider does not have a Japanese name for Croatia), * this method returns null. - * @param countryCode the ISO 3166 country code string in the form of two - * upper-case letters between 'A' (U+0041) and 'Z' (U+005A) + * @param countryCode the country(region) code string in the form of two + * upper-case letters between 'A' (U+0041) and 'Z' (U+005A) or the UN M.49 area code + * in the form of three digit letters between '0' (U+0030) and '9' (U+0039). * @param locale the desired locale * @return the name of the given country code for the specified locale, or null if it's not * available. * @exception NullPointerException if countryCode or locale is null * @exception IllegalArgumentException if countryCode is not in the form of - * two upper-case letters, or locale isn't + * two upper-case letters or three digit letters, or locale isn't * one of the locales returned from * {@link java.util.spi.LocaleServiceProvider#getAvailableLocales() * getAvailableLocales()}. diff --git a/src/share/classes/java/util/spi/LocaleServiceProvider.java b/src/share/classes/java/util/spi/LocaleServiceProvider.java index 567e89271a8247e2d6b72bd3cb903399545baa74..02626e35e953ec82ef957d7415f7fb1446186abf 100644 --- a/src/share/classes/java/util/spi/LocaleServiceProvider.java +++ b/src/share/classes/java/util/spi/LocaleServiceProvider.java @@ -86,18 +86,19 @@ import java.util.Locale; * Otherwise, they call the getAvailableLocales() methods of * installed providers for the appropriate interface to find one that * supports the requested locale. If such a provider is found, its other - * methods are called to obtain the requested object or name. If neither - * the Java runtime environment itself nor an installed provider supports - * the requested locale, a fallback locale is constructed by replacing the - * first of the variant, country, or language strings of the locale that's - * not an empty string with an empty string, and the lookup process is - * restarted. In the case that the variant contains one or more '_'s, the - * fallback locale is constructed by replacing the variant with a new variant - * which eliminates the last '_' and the part following it. Even if a - * fallback occurs, methods that return requested objects or name are - * invoked with the original locale before the fallback.The Java runtime - * environment must support the root locale for all locale sensitive services - * in order to guarantee that this process terminates. + * methods are called to obtain the requested object or name. When checking + * whether a locale is supported, the locale's extensions are ignored. + * If neither the Java runtime environment itself nor an installed provider + * supports the requested locale, the methods go through a list of candidate + * locales and repeat the availability check for each until a match is found. + * The algorithm used for creating a list of candidate locales is same as + * the one used by ResourceBunlde by default (see + * {@link java.util.ResourceBundle.Control#getCandidateLocales getCandidateLocales} + * for the details). Even if a locale is resolved from the candidate list, + * methods that return requested objects or names are invoked with the original + * requested locale including extensions. The Java runtime environment must + * support the root locale for all locale sensitive services in order to + * guarantee that this process terminates. *

    * Providers of names (but not providers of other objects) are allowed to * return null for some name requests even for locales that they claim to @@ -124,6 +125,11 @@ public abstract class LocaleServiceProvider { /** * Returns an array of all locales for which this locale service provider * can provide localized objects or names. + *

    + * Note: Extensions in a Locale are ignored during + * service provider lookup. So the array returned by this method should + * not include two or more Locale objects only differing in + * their extensions. * * @return An array of all locales for which this locale service provider * can provide localized objects or names. diff --git a/src/share/classes/javax/sound/midi/MidiDevice.java b/src/share/classes/javax/sound/midi/MidiDevice.java index 0a8fed47855384e7c6d0d834169d669cf661be78..1e9f225d94324848279322903010f9eb72f9b6d7 100644 --- a/src/share/classes/javax/sound/midi/MidiDevice.java +++ b/src/share/classes/javax/sound/midi/MidiDevice.java @@ -204,6 +204,9 @@ public interface MidiDevice extends AutoCloseable { * MIDI data. The returned receiver must be closed when the application * has finished using it. * + *

    Usually the returned receiver implements + * the {@code MidiDeviceReceiver} interface. + * *

    Obtaining a Receiver with this method does not * open the device. To be able to use the device, it has to be * opened explicitly by calling {@link #open}. Also, closing the @@ -223,6 +226,10 @@ public interface MidiDevice extends AutoCloseable { * connected with this MidiDevice. * A receiver can be removed * from the device by closing it. + * + *

    Usually the returned receivers implement + * the {@code MidiDeviceReceiver} interface. + * * @return an unmodifiable list of the open receivers * @since 1.5 */ @@ -234,6 +241,9 @@ public interface MidiDevice extends AutoCloseable { * MIDI data The returned transmitter must be closed when the application * has finished using it. * + *

    Usually the returned transmitter implements + * the {@code MidiDeviceTransmitter} interface. + * *

    Obtaining a Transmitter with this method does not * open the device. To be able to use the device, it has to be * opened explicitly by calling {@link #open}. Also, closing the @@ -253,6 +263,10 @@ public interface MidiDevice extends AutoCloseable { * connected with this MidiDevice. * A transmitter can be removed * from the device by closing it. + * + *

    Usually the returned transmitters implement + * the {@code MidiDeviceTransmitter} interface. + * * @return an unmodifiable list of the open transmitters * @since 1.5 */ diff --git a/src/share/classes/com/sun/media/sound/MidiDeviceReceiver.java b/src/share/classes/javax/sound/midi/MidiDeviceReceiver.java similarity index 78% rename from src/share/classes/com/sun/media/sound/MidiDeviceReceiver.java rename to src/share/classes/javax/sound/midi/MidiDeviceReceiver.java index 9c76783db60da09f2b9061cc01dd49d435510408..9ea1df7c38e0c829321fda725c78494fed290540 100644 --- a/src/share/classes/com/sun/media/sound/MidiDeviceReceiver.java +++ b/src/share/classes/javax/sound/midi/MidiDeviceReceiver.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,20 +22,18 @@ * or visit www.oracle.com if you need additional information or have any * questions. */ -package com.sun.media.sound; -import javax.sound.midi.MidiDevice; -import javax.sound.midi.Receiver; +package javax.sound.midi; /** - * A Receiver with reference to it's MidiDevice object. + *

    {@code MidiDeviceReceiver} is a {@code Receiver} which represents + * a MIDI input connector of a {@code MidiDevice} + * (see {@link MidiDevice#getReceiver()}). * - * @author Karl Helgason + * @since 1.7 */ public interface MidiDeviceReceiver extends Receiver { - - /** Obtains the MidiDevice object associated with this Receiver. + /** Obtains a MidiDevice object which is an owner of this Receiver. */ public MidiDevice getMidiDevice(); - } diff --git a/src/share/classes/javax/sound/midi/MidiDeviceTransmitter.java b/src/share/classes/javax/sound/midi/MidiDeviceTransmitter.java new file mode 100644 index 0000000000000000000000000000000000000000..b6a827188e27e49e9faac7a411cf7d7cf3c6160c --- /dev/null +++ b/src/share/classes/javax/sound/midi/MidiDeviceTransmitter.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.sound.midi; + + +/** + *

    {@code MidiDeviceTransmitter} is a {@code Transmitter} which represents + * a MIDI input connector of a {@code MidiDevice} + * (see {@link MidiDevice#getTransmitter()}). + * + * @since 1.7 + */ +public interface MidiDeviceTransmitter extends Transmitter { + + /** Obtains a MidiDevice object which is an owner of this Transmitter. + */ + public MidiDevice getMidiDevice(); +} diff --git a/src/share/classes/javax/sound/midi/MidiSystem.java b/src/share/classes/javax/sound/midi/MidiSystem.java index f865d92eb8ae18c1e4fb61f55cbf7d3908cc4cee..8c0b5d9ebb5fa1dd98b106f5bbc055128de89255 100644 --- a/src/share/classes/javax/sound/midi/MidiSystem.java +++ b/src/share/classes/javax/sound/midi/MidiSystem.java @@ -47,6 +47,8 @@ import javax.sound.midi.spi.MidiDeviceProvider; import com.sun.media.sound.JDK13Services; import com.sun.media.sound.ReferenceCountingDevice; import com.sun.media.sound.AutoConnectSequencer; +import com.sun.media.sound.MidiDeviceReceiverEnvelope; +import com.sun.media.sound.MidiDeviceTransmitterEnvelope; /** @@ -225,6 +227,8 @@ public class MidiSystem { /** * Obtains a MIDI receiver from an external MIDI port * or other default device. + * The returned receiver always implements + * the {@code MidiDeviceReceiver} interface. * *

    If the system property * javax.sound.midi.Receiver @@ -261,6 +265,9 @@ public class MidiSystem { } else { receiver = device.getReceiver(); } + if (!(receiver instanceof MidiDeviceReceiver)) { + receiver = new MidiDeviceReceiverEnvelope(device, receiver); + } return receiver; } @@ -268,6 +275,8 @@ public class MidiSystem { /** * Obtains a MIDI transmitter from an external MIDI port * or other default source. + * The returned transmitter always implements + * the {@code MidiDeviceTransmitter} interface. * *

    If the system property * javax.sound.midi.Transmitter @@ -301,6 +310,9 @@ public class MidiSystem { } else { transmitter = device.getTransmitter(); } + if (!(transmitter instanceof MidiDeviceReceiver)) { + transmitter = new MidiDeviceTransmitterEnvelope(device, transmitter); + } return transmitter; } diff --git a/src/share/classes/javax/sound/sampled/AudioFormat.java b/src/share/classes/javax/sound/sampled/AudioFormat.java index 02f96577126e1420e433867f16bd35522b66eab2..7457f8220eb0deb2f03945dac673a768bb5c83dc 100644 --- a/src/share/classes/javax/sound/sampled/AudioFormat.java +++ b/src/share/classes/javax/sound/sampled/AudioFormat.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999, 2007, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1999, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -431,34 +431,36 @@ public class AudioFormat { /** - * Indicates whether this format matches the one specified. To match, - * two formats must have the same encoding, the same number of channels, - * and the same number of bits per sample and bytes per frame. - * The two formats must also have the same sample rate, - * unless the specified format has the sample rate value AudioSystem.NOT_SPECIFIED, - * which any sample rate will match. The frame rates must - * similarly be equal, unless the specified format has the frame rate - * value AudioSystem.NOT_SPECIFIED. The byte order (big-endian or little-endian) - * must match if the sample size is greater than one byte. + * Indicates whether this format matches the one specified. + * To match, two formats must have the same encoding, + * and consistent values of the number of channels, sample rate, sample size, + * frame rate, and frame size. + * The values of the property are consistent if they are equal + * or the specified format has the property value + * {@code AudioSystem.NOT_SPECIFIED}. + * The byte order (big-endian or little-endian) must be the same + * if the sample size is greater than one byte. * * @param format format to test for match - * @return true if this format matches the one specified, - * false otherwise. - */ - /* - * $$kk: 04.20.99: i changed the semantics of this. + * @return {@code true} if this format matches the one specified, + * {@code false} otherwise. */ public boolean matches(AudioFormat format) { - - if (format.getEncoding().equals(getEncoding()) && - ( (format.getSampleRate() == (float)AudioSystem.NOT_SPECIFIED) || (format.getSampleRate() == getSampleRate()) ) && - (format.getSampleSizeInBits() == getSampleSizeInBits()) && - (format.getChannels() == getChannels() && - (format.getFrameSize() == getFrameSize()) && - ( (format.getFrameRate() == (float)AudioSystem.NOT_SPECIFIED) || (format.getFrameRate() == getFrameRate()) ) && - ( (format.getSampleSizeInBits() <= 8) || (format.isBigEndian() == isBigEndian()) ) ) ) + if (format.getEncoding().equals(getEncoding()) + && (format.getChannels() == AudioSystem.NOT_SPECIFIED + || format.getChannels() == getChannels()) + && (format.getSampleRate() == (float)AudioSystem.NOT_SPECIFIED + || format.getSampleRate() == getSampleRate()) + && (format.getSampleSizeInBits() == AudioSystem.NOT_SPECIFIED + || format.getSampleSizeInBits() == getSampleSizeInBits()) + && (format.getFrameRate() == (float)AudioSystem.NOT_SPECIFIED + || format.getFrameRate() == getFrameRate()) + && (format.getFrameSize() == AudioSystem.NOT_SPECIFIED + || format.getFrameSize() == getFrameSize()) + && (getSampleSizeInBits() <= 8 + || format.isBigEndian() == isBigEndian())) { return true; - + } return false; } @@ -552,14 +554,14 @@ public class AudioFormat { * which is simply a linear (proportional) representation of the sound * waveform. With PCM, the number stored in each sample is proportional * to the instantaneous amplitude of the sound pressure at that point in - * time. The numbers are frequently signed or unsigned integers. + * time. The numbers may be signed or unsigned integers or floats. * Besides PCM, other encodings include mu-law and a-law, which are nonlinear * mappings of the sound amplitude that are often used for recording speech. *

    * You can use a predefined encoding by referring to one of the static * objects created by this class, such as PCM_SIGNED or * PCM_UNSIGNED. Service providers can create new encodings, such as - * compressed audio formats or floating-point PCM samples, and make + * compressed audio formats, and make * these available through the {@link AudioSystem} class. *

    * The Encoding class is static, so that all @@ -589,6 +591,13 @@ public class AudioFormat { */ public static final Encoding PCM_UNSIGNED = new Encoding("PCM_UNSIGNED"); + /** + * Specifies floating-point PCM data. + * + * @since 1.7 + */ + public static final Encoding PCM_FLOAT = new Encoding("PCM_FLOAT"); + /** * Specifies u-law encoded data. */ diff --git a/src/share/classes/javax/sql/CommonDataSource.java b/src/share/classes/javax/sql/CommonDataSource.java index 8d06bd63745aa16d03100664904dc3b1f20e0e77..25b40686d87bb785700e1527ca24d7c9a34d6e08 100644 --- a/src/share/classes/javax/sql/CommonDataSource.java +++ b/src/share/classes/javax/sql/CommonDataSource.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -27,6 +27,8 @@ package javax.sql; import java.sql.SQLException; import java.io.PrintWriter; +import java.sql.SQLFeatureNotSupportedException; +import java.util.logging.Logger; /** * Interface that defines the methods which are common between DataSource, @@ -35,79 +37,93 @@ import java.io.PrintWriter; */ public interface CommonDataSource { - /** - *

    Retrieves the log writer for this DataSource - * object. - * - *

    The log writer is a character output stream to which all logging - * and tracing messages for this data source will be - * printed. This includes messages printed by the methods of this - * object, messages printed by methods of other objects manufactured - * by this object, and so on. Messages printed to a data source - * specific log writer are not printed to the log writer associated - * with the java.sql.DriverManager class. When a - * DataSource object is - * created, the log writer is initially null; in other words, the - * default is for logging to be disabled. - * - * @return the log writer for this data source or null if - * logging is disabled - * @exception java.sql.SQLException if a database access error occurs - * @see #setLogWriter - * @since 1.4 - */ - java.io.PrintWriter getLogWriter() throws SQLException; + /** + *

    Retrieves the log writer for this DataSource + * object. + * + *

    The log writer is a character output stream to which all logging + * and tracing messages for this data source will be + * printed. This includes messages printed by the methods of this + * object, messages printed by methods of other objects manufactured + * by this object, and so on. Messages printed to a data source + * specific log writer are not printed to the log writer associated + * with the java.sql.DriverManager class. When a + * DataSource object is + * created, the log writer is initially null; in other words, the + * default is for logging to be disabled. + * + * @return the log writer for this data source or null if + * logging is disabled + * @exception java.sql.SQLException if a database access error occurs + * @see #setLogWriter + * @since 1.4 + */ + java.io.PrintWriter getLogWriter() throws SQLException; - /** - *

    Sets the log writer for this DataSource - * object to the given java.io.PrintWriter object. - * - *

    The log writer is a character output stream to which all logging - * and tracing messages for this data source will be - * printed. This includes messages printed by the methods of this - * object, messages printed by methods of other objects manufactured - * by this object, and so on. Messages printed to a data source- - * specific log writer are not printed to the log writer associated - * with the java.sql.DriverManager class. When a - * DataSource object is created the log writer is - * initially null; in other words, the default is for logging to be - * disabled. - * - * @param out the new log writer; to disable logging, set to null - * @exception SQLException if a database access error occurs - * @see #getLogWriter - * @since 1.4 - */ - void setLogWriter(java.io.PrintWriter out) throws SQLException; + /** + *

    Sets the log writer for this DataSource + * object to the given java.io.PrintWriter object. + * + *

    The log writer is a character output stream to which all logging + * and tracing messages for this data source will be + * printed. This includes messages printed by the methods of this + * object, messages printed by methods of other objects manufactured + * by this object, and so on. Messages printed to a data source- + * specific log writer are not printed to the log writer associated + * with the java.sql.DriverManager class. When a + * DataSource object is created the log writer is + * initially null; in other words, the default is for logging to be + * disabled. + * + * @param out the new log writer; to disable logging, set to null + * @exception SQLException if a database access error occurs + * @see #getLogWriter + * @since 1.4 + */ + void setLogWriter(java.io.PrintWriter out) throws SQLException; - /** - *

    Sets the maximum time in seconds that this data source will wait - * while attempting to connect to a database. A value of zero - * specifies that the timeout is the default system timeout - * if there is one; otherwise, it specifies that there is no timeout. - * When a DataSource object is created, the login timeout is - * initially zero. - * - * @param seconds the data source login time limit - * @exception SQLException if a database access error occurs. - * @see #getLoginTimeout - * @since 1.4 - */ - void setLoginTimeout(int seconds) throws SQLException; + /** + *

    Sets the maximum time in seconds that this data source will wait + * while attempting to connect to a database. A value of zero + * specifies that the timeout is the default system timeout + * if there is one; otherwise, it specifies that there is no timeout. + * When a DataSource object is created, the login timeout is + * initially zero. + * + * @param seconds the data source login time limit + * @exception SQLException if a database access error occurs. + * @see #getLoginTimeout + * @since 1.4 + */ + void setLoginTimeout(int seconds) throws SQLException; - /** - * Gets the maximum time in seconds that this data source can wait - * while attempting to connect to a database. A value of zero - * means that the timeout is the default system timeout - * if there is one; otherwise, it means that there is no timeout. - * When a DataSource object is created, the login timeout is - * initially zero. - * - * @return the data source login time limit - * @exception SQLException if a database access error occurs. - * @see #setLoginTimeout - * @since 1.4 - */ - int getLoginTimeout() throws SQLException; + /** + * Gets the maximum time in seconds that this data source can wait + * while attempting to connect to a database. A value of zero + * means that the timeout is the default system timeout + * if there is one; otherwise, it means that there is no timeout. + * When a DataSource object is created, the login timeout is + * initially zero. + * + * @return the data source login time limit + * @exception SQLException if a database access error occurs. + * @see #setLoginTimeout + * @since 1.4 + */ + int getLoginTimeout() throws SQLException; + //------------------------- JDBC 4.1 ----------------------------------- + + /** + * Return the parent Logger of all the Loggers used by this data source. This + * should be the Logger farthest from the root Logger that is + * still an ancestor of all of the Loggers used by this data source. Configuring + * this Logger will affect all of the log messages generated by the data source. + * In the worst case, this may be the root Logger. + * + * @return the parent Logger for this data source + * @throws SQLFeatureNotSupportedException if the data source does not use java.util.logging. + * @since 1.7 + */ + public Logger getParentLogger() throws SQLFeatureNotSupportedException; } diff --git a/src/share/classes/javax/sql/rowset/CachedRowSet.java b/src/share/classes/javax/sql/rowset/CachedRowSet.java index c03d5534345035a1b7430e6cf05c933bb0800bb6..2a60bb3b7e7a20ecf71a3da0b7f73e228a54fcf8 100644 --- a/src/share/classes/javax/sql/rowset/CachedRowSet.java +++ b/src/share/classes/javax/sql/rowset/CachedRowSet.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -644,10 +644,10 @@ public interface CachedRowSet extends RowSet, Joinable { * of execute that takes a ResultSet object. * * @param data the ResultSet object containing the data - * to be read into this CachedRowSet object + * to be read into this CachedRowSet object * @throws SQLException if a null ResultSet object is supplied - * or this CachedRowSet object cannot - * retrieve the associated ResultSetMetaData object + * or this CachedRowSet object cannot + * retrieve the associated ResultSetMetaData object * @see #execute * @see java.sql.ResultSet * @see java.sql.ResultSetMetaData @@ -674,10 +674,10 @@ public interface CachedRowSet extends RowSet, Joinable { * to commit outstanding updates, those updates are lost. * * @param conn a standard JDBC Connection object with valid - * properties + * properties * @throws SQLException if an invalid Connection object is supplied - * or an error occurs in establishing the connection to the - * data source + * or an error occurs in establishing the connection to the + * data source * @see #populate * @see java.sql.Connection */ @@ -736,8 +736,8 @@ public interface CachedRowSet extends RowSet, Joinable { * * @throws SQLException if the cursor is on the insert row * @throws SyncProviderException if the underlying - * synchronization provider's writer fails to write the updates - * back to the data source + * synchronization provider's writer fails to write the updates + * back to the data source * @see #acceptChanges(java.sql.Connection) * @see javax.sql.RowSetWriter * @see javax.sql.rowset.spi.SyncFactory @@ -807,8 +807,8 @@ public interface CachedRowSet extends RowSet, Joinable { * @param con a standard JDBC Connection object * @throws SQLException if the cursor is on the insert row * @throws SyncProviderException if the underlying - * synchronization provider's writer fails to write the updates - * back to the data source + * synchronization provider's writer fails to write the updates + * back to the data source * @see #acceptChanges() * @see javax.sql.RowSetWriter * @see javax.sql.rowset.spi.SyncFactory @@ -867,7 +867,7 @@ public interface CachedRowSet extends RowSet, Joinable { * the rowset's Java VM resources. * * @throws SQLException if an error occurs flushing the contents of this - * CachedRowSet object + * CachedRowSet object * @see javax.sql.RowSetListener#rowSetChanged * @see java.sql.ResultSet#close */ @@ -948,9 +948,9 @@ public interface CachedRowSet extends RowSet, Joinable { * * @param idx an int identifying the column to be checked for updates * @return true if the designated column has been visibly updated; - * false otherwise + * false otherwise * @throws SQLException if the cursor is on the insert row, before the first row, - * or after the last row + * or after the last row * @see java.sql.DatabaseMetaData#updatesAreDetected */ public boolean columnUpdated(int idx) throws SQLException; @@ -963,9 +963,9 @@ public interface CachedRowSet extends RowSet, Joinable { * @param columnName a String object giving the name of the * column to be checked for updates * @return true if the column has been visibly updated; - * false otherwise + * false otherwise * @throws SQLException if the cursor is on the insert row, before the first row, - * or after the last row + * or after the last row * @see java.sql.DatabaseMetaData#updatesAreDetected */ public boolean columnUpdated(String columnName) throws SQLException; @@ -1003,7 +1003,7 @@ public interface CachedRowSet extends RowSet, Joinable { *

    * * @return a Collection object that contains the values in - * each row in this CachedRowSet object + * each row in this CachedRowSet object * @throws SQLException if an error occurs generating the collection * @see #toCollection(int) * @see #toCollection(String) @@ -1030,10 +1030,10 @@ public interface CachedRowSet extends RowSet, Joinable { * @param column an int indicating the column whose values * are to be represented in a Collection object * @return a Collection object that contains the values - * stored in the specified column of this CachedRowSet - * object + * stored in the specified column of this CachedRowSet + * object * @throws SQLException if an error occurs generating the collection or - * an invalid column id is provided + * an invalid column id is provided * @see #toCollection * @see #toCollection(String) */ @@ -1059,10 +1059,10 @@ public interface CachedRowSet extends RowSet, Joinable { * @param column a String object giving the name of the * column whose values are to be represented in a collection * @return a Collection object that contains the values - * stored in the specified column of this CachedRowSet - * object + * stored in the specified column of this CachedRowSet + * object * @throws SQLException if an error occurs generating the collection or - * an invalid column id is provided + * an invalid column id is provided * @see #toCollection * @see #toCollection(int) */ @@ -1100,7 +1100,7 @@ public interface CachedRowSet extends RowSet, Joinable { * @return the SyncProvider object that was set when the rowset * was instantiated, or if none was was set, the default provider * @throws SQLException if an error occurs while returning the - * SyncProvider object + * SyncProvider object * @see #setSyncProvider */ public SyncProvider getSyncProvider() throws SQLException; @@ -1127,7 +1127,7 @@ public interface CachedRowSet extends RowSet, Joinable { * @param provider a String object giving the fully qualified class * name of a SyncProvider implementation * @throws SQLException if an error occurs while attempting to reset the - * SyncProvider implementation + * SyncProvider implementation * @see #getSyncProvider */ public void setSyncProvider(String provider) throws SQLException; @@ -1152,9 +1152,9 @@ public interface CachedRowSet extends RowSet, Joinable { * object to the rowset. * * @param md a RowSetMetaData object containing - * metadata about the columns in this CachedRowSet object + * metadata about the columns in this CachedRowSet object * @throws SQLException if invalid metadata is supplied to the - * rowset + * rowset */ public void setMetaData(RowSetMetaData md) throws SQLException; @@ -1183,7 +1183,7 @@ public interface CachedRowSet extends RowSet, Joinable { * @return a ResultSet object that contains the original value for * this CachedRowSet object * @throws SQLException if an error occurs producing the - * ResultSet object + * ResultSet object */ public ResultSet getOriginal() throws SQLException; @@ -1217,7 +1217,7 @@ public interface CachedRowSet extends RowSet, Joinable { * A call to setOriginalRow is irreversible. * * @throws SQLException if there is no current row or an error is - * encountered resetting the contents of the original row + * encountered resetting the contents of the original row * @see #getOriginalRow */ public void setOriginalRow() throws SQLException; @@ -1326,7 +1326,7 @@ public interface CachedRowSet extends RowSet, Joinable { * as this CachedRowSet object and that has a cursor over * the same data * @throws SQLException if an error occurs or cloning is not - * supported in the underlying platform + * supported in the underlying platform * @see javax.sql.RowSetEvent * @see javax.sql.RowSetListener */ @@ -1344,10 +1344,10 @@ public interface CachedRowSet extends RowSet, Joinable { * established must be maintained. * * @return a new RowSet object that is a deep copy - * of this CachedRowSet object and is - * completely independent of this CachedRowSet object + * of this CachedRowSet object and is + * completely independent of this CachedRowSet object * @throws SQLException if an error occurs in generating the copy of - * the of this CachedRowSet object + * the of this CachedRowSet object * @see #createShared * @see #createCopySchema * @see #createCopyNoConstraints @@ -1396,10 +1396,10 @@ public interface CachedRowSet extends RowSet, Joinable { * in the copy. * * @return a new CachedRowSet object that is a deep copy - * of this CachedRowSet object and is - * completely independent of this CachedRowSet object + * of this CachedRowSet object and is + * completely independent of this CachedRowSet object * @throws SQLException if an error occurs in generating the copy of - * the of this CachedRowSet object + * the of this CachedRowSet object * @see #createCopy * @see #createShared * @see #createCopySchema @@ -1445,7 +1445,7 @@ public interface CachedRowSet extends RowSet, Joinable { * @return true if deleted rows are visible; * false otherwise * @throws SQLException if a rowset implementation is unable to - * to determine whether rows marked for deletion are visible + * to determine whether rows marked for deletion are visible * @see #setShowDeleted */ public boolean getShowDeleted() throws SQLException; @@ -1467,7 +1467,7 @@ public interface CachedRowSet extends RowSet, Joinable { * @param b true if deleted rows should be shown; * false otherwise * @exception SQLException if a rowset implementation is unable to - * to reset whether deleted rows should be visible + * to reset whether deleted rows should be visible * @see #getShowDeleted */ public void setShowDeleted(boolean b) throws SQLException; @@ -1523,9 +1523,12 @@ public interface CachedRowSet extends RowSet, Joinable { * set to false, the changes will not be committed until one of the * CachedRowSet interface transaction methods is called. * + * @deprecated Because this field is final (it is part of an interface), + * its value cannot be changed. * @see #commit * @see #rollback */ + @Deprecated public static final boolean COMMIT_ON_ACCEPT_CHANGES = true; /** @@ -1562,10 +1565,10 @@ public interface CachedRowSet extends RowSet, Joinable { * @param startRow the position in the ResultSet from where to start * populating the records in this CachedRowSet * @param rs the ResultSet object containing the data - * to be read into this CachedRowSet object + * to be read into this CachedRowSet object * @throws SQLException if a null ResultSet object is supplied - * or this CachedRowSet object cannot - * retrieve the associated ResultSetMetaData object + * or this CachedRowSet object cannot + * retrieve the associated ResultSetMetaData object * @see #execute * @see #populate(ResultSet) * @see java.sql.ResultSet @@ -1620,3 +1623,4 @@ public interface CachedRowSet extends RowSet, Joinable { public boolean previousPage() throws SQLException; } + diff --git a/src/share/classes/javax/sql/rowset/RowSetFactory.java b/src/share/classes/javax/sql/rowset/RowSetFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..71d2ec531cf7f3d56e1fafd522d2a0886015a4e8 --- /dev/null +++ b/src/share/classes/javax/sql/rowset/RowSetFactory.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.sql.rowset; + +import java.sql.SQLException; + +/** + * An interface that defines the implementation of a factory that is used + * to obtain different types of {@code RowSet} implementations. + * + * @author Lance Andersen + * @since 1.7 + */ +public interface RowSetFactory{ + + /** + *

    Creates a new instance of a CachedRowSet.

    + * + * @return A new instance of a CachedRowSet. + * + * @throws SQLException if a CachedRowSet cannot + * be created. + * + * @since 1.7 + */ + public CachedRowSet createCachedRowSet() throws SQLException; + + /** + *

    Creates a new instance of a FilteredRowSet.

    + * + * @return A new instance of a FilteredRowSet. + * + * @throws SQLException if a FilteredRowSet cannot + * be created. + * + * @since 1.7 + */ + public FilteredRowSet createFilteredRowSet() throws SQLException; + + /** + *

    Creates a new instance of a JdbcRowSet.

    + * + * @return A new instance of a JdbcRowSet. + * + * @throws SQLException if a JdbcRowSet cannot + * be created. + * + * @since 1.7 + */ + public JdbcRowSet createJdbcRowSet() throws SQLException; + + /** + *

    Creates a new instance of a JoinRowSet.

    + * + * @return A new instance of a JoinRowSet. + * + * @throws SQLException if a JoinRowSet cannot + * be created. + * + * @since 1.7 + */ + public JoinRowSet createJoinRowSet() throws SQLException; + + /** + *

    Creates a new instance of a WebRowSet.

    + * + * @return A new instance of a WebRowSet. + * + * @throws SQLException if a WebRowSet cannot + * be created. + * + * @since 1.7 + */ + public WebRowSet createWebRowSet() throws SQLException; + +} \ No newline at end of file diff --git a/src/share/classes/javax/sql/rowset/RowSetProvider.java b/src/share/classes/javax/sql/rowset/RowSetProvider.java new file mode 100644 index 0000000000000000000000000000000000000000..1a84161ce15af66a186c5733bf57ea9b4234879b --- /dev/null +++ b/src/share/classes/javax/sql/rowset/RowSetProvider.java @@ -0,0 +1,305 @@ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package javax.sql.rowset; + +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.sql.SQLException; +import java.util.ServiceLoader; +import javax.sql.rowset.RowSetFactory; + +/** + * A factory API that enables applications to obtain a + * {@code RowSetFactory} implementation that can be used to create different + * types of {@code RowSet} implementations. + *

    + * Example: + *

    + *
    + * RowSetFactory aFactory = RowSetProvider.newFactory();
    + * CachedRowSet crs = aFactory.createCachedRowSet();
    + * ...
    + * RowSetFactory rsf = RowSetProvider.newFactory("com.sun.rowset.RowSetFactoryImpl", null);
    + * WebRowSet wrs = rsf.createWebRowSet();
    + * 
    + *

    + * Tracing of this class may be enabled by setting the System property + * {@code javax.sql.rowset.RowSetFactory.debug} to any value but {@code false}. + *

    + * + * @author Lance Andersen + * @since 1.7 + */ +public class RowSetProvider { + + private static final String ROWSET_DEBUG_PROPERTY = "javax.sql.rowset.RowSetProvider.debug"; + private static final String ROWSET_FACTORY_IMPL = "com.sun.rowset.RowSetFactoryImpl"; + private static final String ROWSET_FACTORY_NAME = "javax.sql.rowset.RowSetFactory"; + /** + * Internal debug flag. + */ + private static boolean debug = true; + + + static { + // Check to see if the debug property is set + String val = getSystemProperty(ROWSET_DEBUG_PROPERTY); + // Allow simply setting the prop to turn on debug + debug = val != null && !"false".equals(val); + } + + + protected RowSetProvider () { + } + + /** + *

    Creates a new instance of a RowSetFactory + * implementation. This method uses the following + * look up order to determine + * the RowSetFactory implementation class to load:

    + *
      + *
    • + * The System property {@code javax.sql.rowset.RowsetFactory}. For example: + *
        + *
      • + * -Djavax.sql.rowset.RowsetFactory=com.sun.rowset.RowSetFactoryImpl + *
      • + *
      + *
    • + * The ServiceLocator API. The ServiceLocator API will look + * for a classname in the file + * {@code META-INF/services/javax.sql.rowset.RowSetFactory} + * in jars available to the runtime. For example, to have the the RowSetFactory + * implementation {@code com.sun.rowset.RowSetFactoryImpl } loaded, the + * entry in {@code META-INF/services/javax.sql.rowset.RowSetFactory} would be: + *
        + *
      • + * {@code com.sun.rowset.RowSetFactoryImpl } + *
      • + *
      + *
    • + *
    • + * Platform default RowSetFactory instance. + *
    • + *
    + * + *

    Once an application has obtained a reference to a {@code RowSetFactory}, + * it can use the factory to obtain RowSet instances.

    + * + * @return New instance of a RowSetFactory + * + * @throws SQLException if the default factory class cannot be loaded, + * instantiated. The cause will be set to actual Exception + * + * @see ServiceLoader + * @since 1.7 + */ + public static RowSetFactory newFactory() + throws SQLException { + // Use the system property first + RowSetFactory factory = null; + String factoryClassName = null; + try { + trace("Checking for Rowset System Property..."); + factoryClassName = getSystemProperty(ROWSET_FACTORY_NAME); + if (factoryClassName != null) { + trace("Found system property, value=" + factoryClassName); + factory = (RowSetFactory) getFactoryClass(factoryClassName, null, true).newInstance(); + } + } catch (ClassNotFoundException e) { + throw new SQLException( + "RowSetFactory: " + factoryClassName + " not found", e); + } catch (Exception e) { + throw new SQLException( + "RowSetFactory: " + factoryClassName + " could not be instantiated: " + e, + e); + } + + // Check to see if we found the RowSetFactory via a System property + if (factory == null) { + // If the RowSetFactory is not found via a System Property, now + // look it up via the ServiceLoader API and if not found, use the + // Java SE default. + factory = loadViaServiceLoader(); + factory = + factory == null ? newFactory(ROWSET_FACTORY_IMPL, null) : factory; + } + return (factory); + } + + /** + *

    Creates a new instance of a RowSetFactory from the + * specified factory class name. + * This function is useful when there are multiple providers in the classpath. + * It gives more control to the application as it can specify which provider + * should be loaded.

    + * + *

    Once an application has obtained a reference to a RowSetFactory + * it can use the factory to obtain RowSet instances.

    + * + * @param factoryClassName fully qualified factory class name that + * provides an implementation of javax.sql.rowset.RowSetFactory. + * + * @param cl ClassLoader used to load the factory + * class. If null current Thread's context + * classLoader is used to load the factory class. + * + * @return New instance of a RowSetFactory + * + * @throws SQLException if factoryClassName is + * null, or the factory class cannot be loaded, instantiated. + * + * @see #newFactory() + * + * @since 1.7 + */ + public static RowSetFactory newFactory(String factoryClassName, ClassLoader cl) + throws SQLException { + + trace("***In newInstance()"); + try { + Class providerClass = getFactoryClass(factoryClassName, cl, false); + RowSetFactory instance = (RowSetFactory) providerClass.newInstance(); + if (debug) { + trace("Created new instance of " + providerClass + + " using ClassLoader: " + cl); + } + return instance; + } catch (ClassNotFoundException x) { + throw new SQLException( + "Provider " + factoryClassName + " not found", x); + } catch (Exception x) { + throw new SQLException( + "Provider " + factoryClassName + " could not be instantiated: " + x, + x); + } + } + + /* + * Returns the class loader to be used. + * @return The ClassLoader to use. + * + */ + static private ClassLoader getContextClassLoader() throws SecurityException { + return (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() { + + public Object run() { + ClassLoader cl = null; + + cl = Thread.currentThread().getContextClassLoader(); + + if (cl == null) { + cl = ClassLoader.getSystemClassLoader(); + } + + return cl; + } + }); + } + + /** + * Attempt to load a class using the class loader supplied. If that fails + * and fall back is enabled, the current (i.e. bootstrap) class loader is + * tried. + * + * If the class loader supplied is null, first try using the + * context class loader followed by the current class loader. + * @return The class which was loaded + */ + static private Class getFactoryClass(String factoryClassName, ClassLoader cl, + boolean doFallback) throws ClassNotFoundException { + try { + if (cl == null) { + cl = getContextClassLoader(); + if (cl == null) { + throw new ClassNotFoundException(); + } else { + return cl.loadClass(factoryClassName); + } + } else { + return cl.loadClass(factoryClassName); + } + } catch (ClassNotFoundException e) { + if (doFallback) { + // Use current class loader + return Class.forName(factoryClassName, true, RowSetFactory.class.getClassLoader()); + } else { + throw e; + } + } + } + + /** + * Use the ServiceLoader mechanism to load the default RowSetFactory + * @return default RowSetFactory Implementation + */ + static private RowSetFactory loadViaServiceLoader() { + RowSetFactory theFactory = null; + trace("***in loadViaServiceLoader()"); + for (RowSetFactory factory : ServiceLoader.load(javax.sql.rowset.RowSetFactory.class)) { + trace(" Loading done by the java.util.ServiceLoader :" + factory.getClass().getName()); + theFactory = factory; + break; + } + return theFactory; + + } + + /** + * Returns the requested System Property. If a {@code SecurityException} + * occurs, just return NULL + * @param propName - System property to retreive + * @return The System property value or NULL if the property does not exist + * or a {@code SecurityException} occurs. + */ + static private String getSystemProperty(final String propName) { + String property = null; + try { + property = (String) AccessController.doPrivileged(new PrivilegedAction() { + + public Object run() { + return System.getProperty(propName); + } + }); + } catch (SecurityException se) { + if (debug) { + se.printStackTrace(); + } + } + return property; + } + + /** + * Debug routine which will output tracing if the System Property + * -Djavax.sql.rowset.RowSetFactory.debug is set + * @param msg - The debug message to display + */ + private static void trace(String msg) { + if (debug) { + System.err.println("###RowSets: " + msg); + } + } +} diff --git a/src/share/classes/javax/sql/rowset/package.html b/src/share/classes/javax/sql/rowset/package.html index 41d00d565b52fa77d9c649e98e4406a546284d92..8b905cd63aa86d3c3f4c8096450938c919b49209 100644 --- a/src/share/classes/javax/sql/rowset/package.html +++ b/src/share/classes/javax/sql/rowset/package.html @@ -5,7 +5,7 @@