JDK-4685305 : (cs) Charset.{forName,isSupported} not thread-safe
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.nio
  • Affected Version: unknown,1.4.1_02
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_2000
  • CPU: x86
  • Submitted: 2002-05-15
  • Updated: 2009-11-04
  • Resolved: 2002-11-20
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other Other
1.4.1_03 03Fixed 1.4.2Fixed
Related Reports
Relates :  
Relates :  
Description
This bug happened during Sun ONE Message Queue QA testing after the test running
for > 20hrs (see below original bug report).  The test has multiple threads each
creating connections then sending/receiving messages.  The test run repeatly
again again.   This NPE happened from java code on charset lookup for "UTF8" 
- the call is originated from code like:

sometext.getBytes("UTF8")

which will be called on each connection creation.

 java.lang.NullPointerException
 at java.util.TreeMap.rotateLeft(TreeMap.java:1248)
 at java.util.TreeMap.fixAfterInsertion(TreeMap.java:1315)
 at java.util.TreeMap.put(TreeMap.java:477)
 at sun.nio.cs.AbstractCharsetProvider.lookup(AbstractCharsetProvider.java:87)
 at sun.nio.cs.AbstractCharsetProvider.charsetForName(AbstractCharsetProvider.java:100)
 at java.nio.charset.Charset.lookup(Charset.java:380)
 at java.nio.charset.Charset.isSupported(Charset.java:405)
 at java.lang.StringCoding.lookupCharset(StringCoding.java:79)
 at java.lang.StringCoding.encode(StringCoding.java:351)
 at java.lang.String.getBytes(String.java:586)
 at com.sun.messaging.jmq.util.MD5.getHashString(MD5.java:24)
 at com.sun.messaging.jmq.jmsclient.auth.JMQDigestAuthenticationHandler.h

+++++++++++++++++++++++original bug report++++++++++++++++++++++++++++++++

Client throws Null Pointer Exception while connecting to the broker....
Exception Stack Trace:


JMS Exception
=============


javax.jms.JMSException: [C4003]: Error occurred on connection creation. - caught
 java.lang.NullPointerException
        at com.sun.messaging.jmq.jmsclient.ExceptionHandler.getJMSException(Exce
ptionHandler.java:183)
        at com.sun.messaging.jmq.jmsclient.ExceptionHandler.handleException(Exce
ptionHandler.java:136)
        at com.sun.messaging.jmq.jmsclient.ConnectionImpl.init(ConnectionImpl.ja
va:392)
        at com.sun.messaging.jmq.jmsclient.ConnectionImpl.<init>(ConnectionImpl.
java:228)
        at com.sun.messaging.jmq.jmsclient.UnifiedConnectionImpl.<init>(UnifiedC
onnectionImpl.java:33)
        at com.sun.messaging.jmq.jmsclient.QueueConnectionImpl.<init>(QueueConne
ctionImpl.java:32)
        at com.sun.messaging.ConnectionFactory.createQueueConnection(ConnectionF
actory.java:72)
        at com.sun.messaging.ConnectionFactory.createQueueConnection(ConnectionF
actory.java:57)
        at Longevity.ConnectionsThread.doIt(ConnectionsThread.java:85)
        at Longevity.ConnectionsThread.run(ConnectionsThread.java:65)


Linked Exception
================


java.lang.NullPointerException
        at java.util.TreeMap.rotateLeft(TreeMap.java:1248)
        at java.util.TreeMap.fixAfterInsertion(TreeMap.java:1315)
        at java.util.TreeMap.put(TreeMap.java:477)
        at sun.nio.cs.AbstractCharsetProvider.lookup(AbstractCharsetProvider.jav
a:87)
        at sun.nio.cs.AbstractCharsetProvider.charsetForName(AbstractCharsetProv
ider.java:100)
        at java.nio.charset.Charset.lookup(Charset.java:380)
        at java.nio.charset.Charset.isSupported(Charset.java:405)
        at java.lang.StringCoding.lookupCharset(StringCoding.java:79)
        at java.lang.StringCoding.encode(StringCoding.java:351)
        at java.lang.String.getBytes(String.java:586)
        at com.sun.messaging.jmq.util.MD5.getHashString(MD5.java:24)
        at com.sun.messaging.jmq.jmsclient.auth.JMQDigestAuthenticationHandler.h
andleRequest(JMQDigestAuthenticationHandler.java:58)
        at com.sun.messaging.jmq.jmsclient.ProtocolHandler.authenticate(Protocol
Handler.java:661)
        at com.sun.messaging.jmq.jmsclient.ProtocolHandler.hello(ProtocolHandler
.java:615)
        at com.sun.messaging.jmq.jmsclient.ConnectionImpl.hello(ConnectionImpl.j
ava:232)
        at com.sun.messaging.jmq.jmsclient.ConnectionImpl.openConnection(Connect
ionImpl.java:1463)
        at com.sun.messaging.jmq.jmsclient.ConnectionImpl.init(ConnectionImpl.ja
va:389)
        at com.sun.messaging.jmq.jmsclient.ConnectionImpl.<init>(ConnectionImpl.
java:228)
        at com.sun.messaging.jmq.jmsclient.UnifiedConnectionImpl.<init>(UnifiedC
onnectionImpl.java:33)
        at com.sun.messaging.jmq.jmsclient.QueueConnectionImpl.<init>(QueueConne
ctionImpl.java:32)
        at com.sun.messaging.ConnectionFactory.createQueueConnection(ConnectionF
actory.java:72)
        at com.sun.messaging.ConnectionFactory.createQueueConnection(ConnectionF
actory.java:57)
        at Longevity.ConnectionsThread.doIt(ConnectionsThread.java:85)
        at Longevity.ConnectionsThread.run(ConnectionsThread.java:65)


mathi.manoharan@eng 2002-05-17

Test Scenario
=============


Test that was run on the machin: Longevity 
(http://jpgserv/JMQFalcon/qa/testcases/Longevity_Testsuite.html)

Three brokers running on Windows Linux and Solaris are clustered. The exception 
occured on the client running on the Windows machine trying to connect to the 
broker running on the Linux machine

Machine Configurations
======================
Sparc (Solaris 8)    -   Ultra 60 (hostname: colorodo)
PC Server (Windows Advanced Server)  -   Dual processor (two 1MHz processors), 
with 512 MB RAM (hostname power-edge-2500)
PC Workstation (Redhat Linux 7.1) - Dual Processor PC (two 400 MHz processors), 
with 256 MB RAM
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++


###@###.### 2003-03-07
This happens with both weblogic and websphere when using SUN and performing
Dynamic WebServices. Can this be fixed for 141_03 seems simple enough to get
and something lots of webservice users world wide will see.

Client Exception[with VM 1.4.1] :
=================================
java.lang.NullPointerException
at java.util.TreeMap.rotateLeft(TreeMap.java:1254)
at java.util.TreeMap.fixAfterInsertion(TreeMap.java:1293)
at java.util.TreeMap.put(TreeMap.java:477)
at
sun.nio.cs.AbstractCharsetProvider.lookup(AbstractCharsetProvider.java:102)
at
sun.nio.cs.AbstractCharsetProvider.charsetForName(AbstractCharsetProvider.ja
va:114)
at java.nio.charset.Charset.lookup(Charset.java:380)
at java.nio.charset.Charset.isSupported(Charset.java:405)
at sun.nio.cs.StreamEncoder.forOutputStreamWriter(StreamEncoder.java:67)
at java.io.OutputStreamWriter.<init>(OutputStreamWriter.java:82)
at com.sun.xml.rpc.util.xml.XmlWriter.<init>(XmlWriter.java:85)
at com.sun.xml.rpc.streaming.XMLWriterImpl.<init>(XMLWriterImpl.java:29)
at
com.sun.xml.rpc.streaming.XMLWriterFactoryImpl.createXMLWriter(XMLWriterFact
oryImpl.java:38)
at
com.sun.xml.rpc.streaming.XMLWriterFactoryImpl.createXMLWriter(XMLWriterFact
oryImpl.java:34)
at
com.sun.xml.rpc.client.StreamingSender._writeRequest(StreamingSender.java:38
2)
at com.sun.xml.rpc.client.StreamingSender._send(StreamingSender.java:63)
at
com.sun.xml.rpc.client.dii.CallInvokerImpl.doInvoke(CallInvokerImpl.java:54)
at com.sun.xml.rpc.client.dii.BasicCall.invoke(BasicCall.java:279)
at DynamicWebservicesClient.invokeMethod(DynamicWebservicesClient.java:146)
at DynamicWebservicesClient.run(DynamicWebservicesClient.java:85)

Also getting :
===============
java.lang.NullPointerException
at java.util.TreeMap.rotateRight(TreeMap.java:1265)
at java.util.TreeMap.fixAfterInsertion(TreeMap.java:1298)
at java.util.TreeMap.put(TreeMap.java:477)
at
sun.nio.cs.AbstractCharsetProvider.lookup(AbstractCharsetProvider.java:102)
at
sun.nio.cs.AbstractCharsetProvider.charsetForName(AbstractCharsetProvider.ja
va:114)
at java.nio.charset.Charset.lookup(Charset.java:380)
at java.nio.charset.Charset.isSupported(Charset.java:405)
at sun.nio.cs.StreamEncoder.forOutputStreamWriter(StreamEncoder.java:67)
at java.io.OutputStreamWriter.<init>(OutputStreamWriter.java:82)
at com.sun.xml.rpc.util.xml.XmlWriter.<init>(XmlWriter.java:85)
at com.sun.xml.rpc.streaming.XMLWriterImpl.<init>(XMLWriterImpl.java:29)
at
com.sun.xml.rpc.streaming.XMLWriterFactoryImpl.createXMLWriter(XMLWriterFact
oryImpl.java:38)
at
com.sun.xml.rpc.streaming.XMLWriterFactoryImpl.createXMLWriter(XMLWriterFact
oryImpl.java:34)
at
com.sun.xml.rpc.client.StreamingSender._writeRequest(StreamingSender.java:38
2)
at com.sun.xml.rpc.client.StreamingSender._send(StreamingSender.java:63)
at
com.sun.xml.rpc.client.dii.CallInvokerImpl.doInvoke(CallInvokerImpl.java:54)
at com.sun.xml.rpc.client.dii.BasicCall.invoke(BasicCall.java:279)
at DynamicWebservicesClient.invokeMethod(DynamicWebservicesClient.java:146)
at DynamicWebservicesClient.run(DynamicWebservicesClient.java:85)

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: 1.4.1_03 mantis FIXED IN: 1.4.1_03 mantis INTEGRATED IN: 1.4.1_03 mantis mantis-b08
14-06-2004

EVALUATION This bug is extremely difficult to reproduce -- it requires two threads to modify a data structure at just the right time after a full GC. The fix is to add the synchronization logic to sun.nio.cs.AbstractCharsetProvider that should have been there in the first place. -- ###@###.### 2002/5/21
05-10-0187

SUGGESTED FIX *** /tmp/geta12157 Tue May 21 15:27:27 2002 --- AbstractCharsetProvider.java Tue May 21 15:22:42 2002 *************** *** 10,15 **** --- 10,16 ---- import java.lang.ref.SoftReference; import java.nio.charset.Charset; import java.nio.charset.spi.CharsetProvider; + import java.util.ArrayList; import java.util.TreeMap; import java.util.Iterator; import java.util.Locale; *************** *** 64,73 **** /* Declare support for the given charset */ protected void charset(String name, String className, String[] aliases) { ! put(classMap, name, className); ! for (int i = 0; i < aliases.length; i++) ! put(aliasMap, aliases[i], name); ! put(aliasNameMap, name, aliases); } private String canonicalize(String charsetName) { --- 65,76 ---- /* Declare support for the given charset */ protected void charset(String name, String className, String[] aliases) { ! synchronized (this) { ! put(classMap, name, className); ! for (int i = 0; i < aliases.length; i++) ! put(aliasMap, aliases[i], name); ! put(aliasNameMap, name, aliases); ! } } private String canonicalize(String charsetName) { *************** *** 75,81 **** return (acn != null) ? acn : charsetName; } ! protected Charset lookup(String csn) { // Check cache first SoftReference sr = (SoftReference)cache.get(csn); --- 78,84 ---- return (acn != null) ? acn : charsetName; } ! private Charset lookup(String csn) { // Check cache first SoftReference sr = (SoftReference)cache.get(csn); *************** *** 111,123 **** } public final Charset charsetForName(String charsetName) { ! return lookup(canonicalize(charsetName)); } public final Iterator charsets() { return new Iterator() { ! Iterator i = classMap.keySet().iterator(); public boolean hasNext() { return i.hasNext(); --- 114,133 ---- } public final Charset charsetForName(String charsetName) { ! synchronized (this) { ! return lookup(canonicalize(charsetName)); ! } } public final Iterator charsets() { + final ArrayList ks; + synchronized (this) { + ks = new ArrayList(classMap.keySet()); + } + return new Iterator() { ! Iterator i = ks.iterator(); public boolean hasNext() { return i.hasNext(); *************** *** 135,141 **** } public final String[] aliases(String charsetName) { ! return (String[])aliasNameMap.get(charsetName); } } --- 145,153 ---- } public final String[] aliases(String charsetName) { ! synchronized (this) { ! return (String[])aliasNameMap.get(charsetName); ! } } } This fix has been spot-tested, but it should be tested more thoroughly before being integrated. -- ###@###.### 2002/5/21
05-10-0187

WORK AROUND Early in the program, lookup each charset that you expect to use and create a strong reference to it that will persist for the duration of the program run. For example, in your main class you could do this: public class Main { private static Charset csUTF8 = Charset.forName("UTF-8"); private static Charset csASCII = Charset.forName("ASCII"); private static Charset cs88591 = Charset.forName("ISO-8859-1"); ... } These strong references will prevent these charsets from being removed from the internal cache. -- ###@###.### 2002/5/21
05-10-0187