From derby-commits-return-14422-apmail-db-derby-commits-archive=db.apache.org@db.apache.org Fri May 20 09:50:15 2011 Return-Path: X-Original-To: apmail-db-derby-commits-archive@www.apache.org Delivered-To: apmail-db-derby-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id 68287687D for ; Fri, 20 May 2011 09:50:15 +0000 (UTC) Received: (qmail 39179 invoked by uid 500); 20 May 2011 09:50:15 -0000 Delivered-To: apmail-db-derby-commits-archive@db.apache.org Received: (qmail 39139 invoked by uid 500); 20 May 2011 09:50:14 -0000 Mailing-List: contact derby-commits-help@db.apache.org; run by ezmlm Precedence: bulk list-help: list-unsubscribe: List-Post: Reply-To: "Derby Development" List-Id: Delivered-To: mailing list derby-commits@db.apache.org Received: (qmail 39132 invoked by uid 99); 20 May 2011 09:50:14 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 20 May 2011 09:50:14 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 20 May 2011 09:50:09 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 3394E2388906; Fri, 20 May 2011 09:49:47 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1125299 - in /db/derby/code/trunk/java: client/org/apache/derby/client/net/ testing/org/apache/derbyTesting/functionTests/tests/derbynet/ Date: Fri, 20 May 2011 09:49:47 -0000 To: derby-commits@db.apache.org From: kahatlen@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20110520094947.3394E2388906@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org Author: kahatlen Date: Fri May 20 09:49:46 2011 New Revision: 1125299 URL: http://svn.apache.org/viewvc?rev=1125299&view=rev Log: DERBY-5068: Investigate increased CPU usage on client after introduction of UTF-8 CcsidManager Make the CcsidManager implementations encode strings directly into the ByteBuffer instead of going via an intermediate byte array. Modified: db/derby/code/trunk/java/client/org/apache/derby/client/net/CcsidManager.java db/derby/code/trunk/java/client/org/apache/derby/client/net/EbcdicCcsidManager.java db/derby/code/trunk/java/client/org/apache/derby/client/net/NetConnection.java db/derby/code/trunk/java/client/org/apache/derby/client/net/NetPackageRequest.java db/derby/code/trunk/java/client/org/apache/derby/client/net/Request.java db/derby/code/trunk/java/client/org/apache/derby/client/net/Utf8CcsidManager.java db/derby/code/trunk/java/testing/org/apache/derbyTesting/functionTests/tests/derbynet/Utf8CcsidManagerClientTest.java Modified: db/derby/code/trunk/java/client/org/apache/derby/client/net/CcsidManager.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/net/CcsidManager.java?rev=1125299&r1=1125298&r2=1125299&view=diff ============================================================================== --- db/derby/code/trunk/java/client/org/apache/derby/client/net/CcsidManager.java (original) +++ db/derby/code/trunk/java/client/org/apache/derby/client/net/CcsidManager.java Fri May 20 09:49:46 2011 @@ -21,6 +21,11 @@ package org.apache.derby.client.net; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import org.apache.derby.client.am.Agent; +import org.apache.derby.client.am.SqlException; + // Performs character conversions as required to send and receive PROTOCOL control data. // User data uses the JVM's built in converters, i18n.jar, @@ -57,26 +62,6 @@ public abstract class CcsidManager { // @return A new byte array representing the String in a particular ccsid. public abstract byte[] convertFromJavaString(String sourceString, org.apache.derby.client.am.Agent agent) throws org.apache.derby.client.am.SqlException; - - // Convert a Java String into bytes for a particular ccsid. - // The String is converted into a buffer provided by the caller. - // - // @param sourceString A Java String to convert. - // @param buffer The buffer to convert the String into. - // @param offset Offset in buffer to start putting output. - // @return An int containing the buffer offset after conversion. - public abstract int convertFromJavaString(String sourceString, - byte[] buffer, - int offset, - org.apache.derby.client.am.Agent agent) throws org.apache.derby.client.am.SqlException; - - // Convert a byte array representing characters in a particular ccsid into a Java String. - // - // @param sourceBytes An array of bytes to be converted. - // @return String A new Java String Object created after conversion. - abstract String convertToJavaString(byte[] sourceBytes); - - // Convert a byte array representing characters in a particular ccsid into a Java String. // // @param sourceBytes An array of bytes to be converted. @@ -85,19 +70,35 @@ public abstract class CcsidManager { // @return A new Java String Object created after conversion. abstract String convertToJavaString(byte[] sourceBytes, int offset, int numToConvert); - - /** - * - * @return Maximum number of bytes per character + * Initialize this instance for encoding a new string. This method resets + * any internal state that may be left after earlier calls to + * {@link #encode()} on this instance. For example, it may reset the + * internal {@code java.nio.charset.CharsetEncoder}, if the implementation + * uses one to do the encoding. */ - abstract int maxBytesPerChar(); + public abstract void startEncoding(); /** - * Get length in bytes for string s - * @param s The string from which to obtain the length - * @return The length of s in bytes + * Encode the contents of a {@code CharBuffer} into a {@code ByteBuffer}. + * The method will return {@code true} if all the characters were encoded + * and copied to the destination. If the receiving byte buffer is too small + * to hold the entire encoded representation of the character buffer, the + * method will return {@code false}. The caller should then allocate a + * larger byte buffer, copy the contents from the old byte buffer to the + * new one, and then call this method again to get the remaining characters + * encoded. + * + * @param src buffer holding the characters to encode + * @param dest buffer receiving the encoded bytes + * @param agent where to report errors + * @return {@code true} if all characters were encoded, {@code false} if + * the destination buffer is full and there still are more characters to + * encode + * @throws SqlException if the characters cannot be encoded using this + * CCSID manager's character encoding */ - abstract int getByteLength(String s); + public abstract boolean encode( + CharBuffer src, ByteBuffer dest, Agent agent) throws SqlException; } Modified: db/derby/code/trunk/java/client/org/apache/derby/client/net/EbcdicCcsidManager.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/net/EbcdicCcsidManager.java?rev=1125299&r1=1125298&r2=1125299&view=diff ============================================================================== --- db/derby/code/trunk/java/client/org/apache/derby/client/net/EbcdicCcsidManager.java (original) +++ db/derby/code/trunk/java/client/org/apache/derby/client/net/EbcdicCcsidManager.java Fri May 20 09:49:46 2011 @@ -21,6 +21,9 @@ package org.apache.derby.client.net; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import org.apache.derby.client.am.Agent; import org.apache.derby.client.am.SqlException; import org.apache.derby.client.am.ClientMessageId; import org.apache.derby.shared.common.reference.SQLState; @@ -127,41 +130,41 @@ public class EbcdicCcsidManager extends } public byte[] convertFromJavaString(String sourceString, org.apache.derby.client.am.Agent agent) throws SqlException { - byte[] bytes = new byte[sourceString.length()]; - convertFromJavaString(sourceString, bytes, 0, agent); - return bytes; - } - - public int convertFromJavaString(String sourceString, - byte[] buffer, - int offset, - org.apache.derby.client.am.Agent agent) throws SqlException { - for (int i = 0; i < sourceString.length(); i++) { - char c = sourceString.charAt(i); - if (c > 0xff) - // buffer[offset++] = (byte) 63; - { - throw new SqlException(agent.logWriter_, - new ClientMessageId(SQLState.CANT_CONVERT_UNICODE_TO_EBCDIC)); + CharBuffer src = CharBuffer.wrap(sourceString); + ByteBuffer dest = ByteBuffer.allocate(sourceString.length()); + startEncoding(); + encode(src, dest, agent); + return dest.array(); + } + + public void startEncoding() { + // We don't have a CharsetEncoder instance to reset, or any other + // internal state associated with earlier encode() calls. Do nothing. + } + + public boolean encode(CharBuffer src, ByteBuffer dest, Agent agent) + throws SqlException { + // Encode as many characters as the destination buffer can hold. + int charsToEncode = Math.min(src.remaining(), dest.remaining()); + for (int i = 0; i < charsToEncode; i++) { + char c = src.get(); + if (c > 0xff) { + throw new SqlException(agent.logWriter_, + new ClientMessageId( + SQLState.CANT_CONVERT_UNICODE_TO_EBCDIC)); } else { - buffer[offset++] = (byte) (conversionArrayToEbcdic[c]); + dest.put((byte) conversionArrayToEbcdic[c]); } - ; } - return offset; - } - String convertToJavaString(byte[] sourceBytes) { - int i = 0; - char[] theChars = new char[sourceBytes.length]; - int num = 0; - - for (i = 0; i < sourceBytes.length; i++) { - num = (sourceBytes[i] < 0) ? (sourceBytes[i] + 256) : sourceBytes[i]; - theChars[i] = (char) conversionArrayToUCS2[num]; + if (src.remaining() == 0) { + // All characters have been encoded. We're done. + return true; + } else { + // We still have more characters to encode, but no room in + // destination buffer. + return false; } - - return new String(theChars); } String convertToJavaString(byte[] sourceBytes, int offset, int numToConvert) { @@ -176,18 +179,4 @@ public class EbcdicCcsidManager extends } return new String(theChars); } - - - /* (non-Javadoc) - * @see org.apache.derby.client.net.CcsidManager#maxBytesPerChar() - */ - int maxBytesPerChar() { - return 1; - } - - public int getByteLength(String s) { - return s.length(); - } - } - Modified: db/derby/code/trunk/java/client/org/apache/derby/client/net/NetConnection.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/net/NetConnection.java?rev=1125299&r1=1125298&r2=1125299&view=diff ============================================================================== --- db/derby/code/trunk/java/client/org/apache/derby/client/net/NetConnection.java (original) +++ db/derby/code/trunk/java/client/org/apache/derby/client/net/NetConnection.java Fri May 20 09:49:46 2011 @@ -20,6 +20,8 @@ */ package org.apache.derby.client.net; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; import java.sql.SQLException; import org.apache.derby.client.am.CallableStatement; import org.apache.derby.client.am.DatabaseMetaData; @@ -38,6 +40,7 @@ import org.apache.derby.jdbc.ClientDrive import org.apache.derby.client.ClientPooledConnection; import org.apache.derby.shared.common.reference.SQLState; +import org.apache.derby.shared.common.sanity.SanityManager; public class NetConnection extends org.apache.derby.client.am.Connection { @@ -127,8 +130,7 @@ public class NetConnection extends org.a // non unicode ccsids. this is done when the server doesn't recoginze the // unicode ccsids). // - - byte[] prddta_; + private ByteBuffer prddta_; // Correlation Token of the source sent to the server in the accrdb. // It is saved like the prddta in case it is needed for a connect reflow. @@ -837,7 +839,7 @@ public class NetConnection extends org.a netAgent_.netConnectionRequest_.writeAccessDatabase(databaseName_, false, crrtkn_, - prddta_, + prddta_.array(), netAgent_.typdef_); } @@ -1321,41 +1323,62 @@ public class NetConnection extends org.a } private void constructPrddta() throws SqlException { - int prddtaLen = 1; - if (prddta_ == null) { - prddta_ = new byte[NetConfiguration.PRDDTA_MAXSIZE]; + prddta_ = ByteBuffer.allocate(NetConfiguration.PRDDTA_MAXSIZE); } else { - java.util.Arrays.fill(prddta_, (byte) 0); + prddta_.clear(); + java.util.Arrays.fill(prddta_.array(), (byte) 0); } + CcsidManager ccsidMgr = netAgent_.getCurrentCcsidManager(); + for (int i = 0; i < NetConfiguration.PRDDTA_ACCT_SUFFIX_LEN_BYTE; i++) { - prddta_[i] = netAgent_.getCurrentCcsidManager().space_; + prddta_.put(i, ccsidMgr.space_); } - prddtaLen = netAgent_.getCurrentCcsidManager().convertFromJavaString(NetConfiguration.PRDID, - prddta_, - prddtaLen, - netAgent_); - - prddtaLen = netAgent_.getCurrentCcsidManager().convertFromJavaString(NetConfiguration.PRDDTA_PLATFORM_ID, - prddta_, - prddtaLen, - netAgent_); + // Start inserting data right after the length byte. + prddta_.position(NetConfiguration.PRDDTA_LEN_BYTE + 1); + + // Register the success of the encode operations for verification in + // sane mode. + boolean success = true; + + ccsidMgr.startEncoding(); + success &= ccsidMgr.encode( + CharBuffer.wrap(NetConfiguration.PRDID), prddta_, agent_); + + ccsidMgr.startEncoding(); + success &= ccsidMgr.encode( + CharBuffer.wrap(NetConfiguration.PRDDTA_PLATFORM_ID), + prddta_, agent_); + + int prddtaLen = prddta_.position(); int extnamTruncateLength = Math.min(extnam_.length(), NetConfiguration.PRDDTA_APPL_ID_FIXED_LEN); - netAgent_.getCurrentCcsidManager().convertFromJavaString(extnam_.substring(0, extnamTruncateLength), - prddta_, - prddtaLen, - netAgent_); + ccsidMgr.startEncoding(); + success &= ccsidMgr.encode( + CharBuffer.wrap(extnam_, 0, extnamTruncateLength), + prddta_, agent_); + + if (SanityManager.DEBUG) { + // The encode() calls above should all complete without overflow, + // since we control the contents of the strings. Verify this in + // sane mode so that we notice it if the strings change so that + // they go beyond the max size of PRDDTA. + SanityManager.ASSERT(success, + "PRDID, PRDDTA_PLATFORM_ID and EXTNAM exceeded PRDDTA_MAXSIZE"); + } + prddtaLen += NetConfiguration.PRDDTA_APPL_ID_FIXED_LEN; prddtaLen += NetConfiguration.PRDDTA_USER_ID_FIXED_LEN; - prddta_[NetConfiguration.PRDDTA_ACCT_SUFFIX_LEN_BYTE] = 0; + // Mark that we have an empty suffix in PRDDTA_ACCT_SUFFIX_LEN_BYTE. + prddta_.put(NetConfiguration.PRDDTA_ACCT_SUFFIX_LEN_BYTE, (byte) 0); prddtaLen++; + // the length byte value does not include itself. - prddta_[NetConfiguration.PRDDTA_LEN_BYTE] = (byte) (prddtaLen - 1); + prddta_.put(NetConfiguration.PRDDTA_LEN_BYTE, (byte) (prddtaLen - 1)); } private void initializePublicKeyForEncryption() throws SqlException { Modified: db/derby/code/trunk/java/client/org/apache/derby/client/net/NetPackageRequest.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/net/NetPackageRequest.java?rev=1125299&r1=1125298&r2=1125299&view=diff ============================================================================== --- db/derby/code/trunk/java/client/org/apache/derby/client/net/NetPackageRequest.java (original) +++ db/derby/code/trunk/java/client/org/apache/derby/client/net/NetPackageRequest.java Fri May 20 09:49:46 2011 @@ -57,49 +57,59 @@ public class NetPackageRequest extends N // Relational Database Name (RDBNAM) // RDB Package Identifier (PKGID) int maxIdentifierLength = NetConfiguration.PKG_IDENTIFIER_MAX_LEN; + CcsidManager ccsidMgr = netAgent_.getCurrentCcsidManager(); + + byte[] dbnameBytes = ccsidMgr.convertFromJavaString( + netAgent_.netConnection_.databaseName_, netAgent_); + + byte[] collectionToFlowBytes = ccsidMgr.convertFromJavaString( + collectionToFlow, netAgent_); + + byte[] pkgNameBytes = ccsidMgr.convertFromJavaString( + section.getPackageName(), netAgent_); boolean scldtalenRequired = false; scldtalenRequired = checkPKGNAMlengths(netAgent_.netConnection_.databaseName_, + dbnameBytes.length, maxIdentifierLength, NetConfiguration.PKG_IDENTIFIER_FIXED_LEN); if (!scldtalenRequired) { scldtalenRequired = checkPKGNAMlengths(collectionToFlow, + collectionToFlowBytes.length, maxIdentifierLength, NetConfiguration.PKG_IDENTIFIER_FIXED_LEN); } if (!scldtalenRequired) { scldtalenRequired = checkPKGNAMlengths(section.getPackageName(), + pkgNameBytes.length, maxIdentifierLength, NetConfiguration.PKG_IDENTIFIER_FIXED_LEN); } // the format is different depending on if an SCLDTALEN is required. if (!scldtalenRequired) { - writeScalarPaddedString(netAgent_.netConnection_.databaseName_, - NetConfiguration.PKG_IDENTIFIER_FIXED_LEN); - writeScalarPaddedString(collectionToFlow, - NetConfiguration.PKG_IDENTIFIER_FIXED_LEN); - writeScalarPaddedString(section.getPackageName(), - NetConfiguration.PKG_IDENTIFIER_FIXED_LEN); + byte padByte = ccsidMgr.space_; + writeScalarPaddedBytes(dbnameBytes, + NetConfiguration.PKG_IDENTIFIER_FIXED_LEN, padByte); + writeScalarPaddedBytes(collectionToFlowBytes, + NetConfiguration.PKG_IDENTIFIER_FIXED_LEN, padByte); + writeScalarPaddedBytes(pkgNameBytes, + NetConfiguration.PKG_IDENTIFIER_FIXED_LEN, padByte); } else { - buildSCLDTA(netAgent_.netConnection_.databaseName_, NetConfiguration.PKG_IDENTIFIER_FIXED_LEN); - buildSCLDTA(collectionToFlow, NetConfiguration.PKG_IDENTIFIER_FIXED_LEN); - buildSCLDTA(section.getPackageName(), NetConfiguration.PKG_IDENTIFIER_FIXED_LEN); + buildSCLDTA(dbnameBytes, NetConfiguration.PKG_IDENTIFIER_FIXED_LEN); + buildSCLDTA(collectionToFlowBytes, NetConfiguration.PKG_IDENTIFIER_FIXED_LEN); + buildSCLDTA(pkgNameBytes, NetConfiguration.PKG_IDENTIFIER_FIXED_LEN); } } - private void buildSCLDTA(String identifier, int minimumLength) throws SqlException { - int length = netAgent_.getCurrentCcsidManager().getByteLength(identifier); - - if (length <= minimumLength) { - write2Bytes(minimumLength); - writeScalarPaddedString(identifier, minimumLength); - } else { - write2Bytes(length); - writeScalarPaddedString(identifier, length); - } + private void buildSCLDTA(byte[] identifier, int minimumLength) + throws SqlException { + int length = Math.max(minimumLength, identifier.length); + write2Bytes(length); + byte padByte = netAgent_.getCurrentCcsidManager().space_; + writeScalarPaddedBytes(identifier, length, padByte); } @@ -152,9 +162,9 @@ public class NetPackageRequest extends N // throws an exception if lengths exceed the maximum. // returns a boolean indicating if SLCDTALEN is required. private boolean checkPKGNAMlengths(String identifier, + int length, int maxIdentifierLength, int lengthRequiringScldta) throws SqlException { - int length = netAgent_.getCurrentCcsidManager().getByteLength(identifier);; if (length > maxIdentifierLength) { throw new SqlException(netAgent_.logWriter_, new ClientMessageId(SQLState.LANG_IDENTIFIER_TOO_LONG), Modified: db/derby/code/trunk/java/client/org/apache/derby/client/net/Request.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/net/Request.java?rev=1125299&r1=1125298&r2=1125299&view=diff ============================================================================== --- db/derby/code/trunk/java/client/org/apache/derby/client/net/Request.java (original) +++ db/derby/code/trunk/java/client/org/apache/derby/client/net/Request.java Fri May 20 09:49:46 2011 @@ -35,6 +35,7 @@ import java.io.IOException; import java.io.ObjectOutputStream; import java.io.UnsupportedEncodingException; import java.nio.ByteBuffer; +import java.nio.CharBuffer; public class Request { @@ -1106,48 +1107,51 @@ public class Request { /* Grab the current CCSID MGR from the NetAgent */ CcsidManager currentCcsidMgr = netAgent_.getCurrentCcsidManager(); - int stringByteLength = currentCcsidMgr.getByteLength(string); + // We don't know the length of the string yet, so set it to 0 for now. + // Will be updated later. + int lengthPos = buffer.position(); + writeLengthCodePoint(0, codePoint); + + int stringByteLength = encodeString(string); if (stringByteLength > byteLengthLimit) { throw new SqlException(netAgent_.logWriter_, new ClientMessageId(sqlState), string); } - writeScalarHeader(codePoint, Math.max(byteMinLength, stringByteLength)); - - buffer.position( - currentCcsidMgr.convertFromJavaString( - string, buffer.array(), buffer.position(), netAgent_)); - // pad if we don't reach the byteMinLength limit if (stringByteLength < byteMinLength) { padBytes(currentCcsidMgr.space_, byteMinLength - stringByteLength); + stringByteLength = byteMinLength; } - } - - - // this method inserts ddm character data into the buffer and pad's the - // data with the ccsid manager's space character if the character data length - // is less than paddedLength. - // Not: this method is not to be used for String truncation and the string length - // must be <= paddedLength. - // This method assumes that the String argument can be - // converted by the ccsid manager. This should be fine because usually - // there are restrictions on the characters which can be used for ddm - // character data. This method also assumes that the string.length() will - // be the number of bytes following the conversion. - final void writeScalarPaddedString(String string, int paddedLength) throws SqlException { - ensureLength(paddedLength); - - /* Grab the current CCSID MGR from the NetAgent */ - CcsidManager currentCcsidMgr = netAgent_.getCurrentCcsidManager(); - - int stringLength = currentCcsidMgr.getByteLength(string); - - buffer.position(currentCcsidMgr.convertFromJavaString( - string, buffer.array(), buffer.position(), netAgent_)); + // Update the length field. The length includes two bytes for the + // length field itself and two bytes for the codepoint. + buffer.putShort(lengthPos, (short) (stringByteLength + 4)); + } - padBytes(currentCcsidMgr.space_, paddedLength - stringLength); + /** + * Encode a string and put it into the buffer. A larger buffer will be + * allocated if the current buffer is too small to hold the entire string. + * + * @param string the string to encode + * @return the number of bytes in the encoded representation of the string + */ + private int encodeString(String string) throws SqlException { + int startPos = buffer.position(); + CharBuffer src = CharBuffer.wrap(string); + CcsidManager ccsidMgr = netAgent_.getCurrentCcsidManager(); + ccsidMgr.startEncoding(); + while (!ccsidMgr.encode(src, buffer, netAgent_)) { + // The buffer was too small to hold the entire string. Let's + // allocate a larger one. We don't know how much more space we + // need, so we just tell ensureLength() that we need more than + // what we have, until we manage to encode the entire string. + // ensureLength() typically doubles the size of the buffer, so + // we shouldn't have to call it many times before we get a large + // enough buffer. + ensureLength(buffer.remaining() + 1); + } + return buffer.position() - startPos; } // this method writes a 4 byte length/codepoint pair plus the bytes contained @@ -1228,6 +1232,7 @@ public class Request { } final void maskOutPassword() { + int savedPos = buffer.position(); try { String maskChar = "*"; // construct a mask using the maskChar. @@ -1236,14 +1241,16 @@ public class Request { mask.append(maskChar); } // try to write mask over password. - netAgent_.getCurrentCcsidManager().convertFromJavaString( - mask.toString(), buffer.array(), passwordStart_, netAgent_); + buffer.position(passwordStart_); + encodeString(mask.toString()); } catch (SqlException sqle) { // failed to convert mask, // them simply replace with 0xFF. for (int i = 0; i < passwordLength_; i++) { buffer.put(passwordStart_ + i, (byte) 0xFF); } + } finally { + buffer.position(savedPos); } } @@ -1457,12 +1464,7 @@ public class Request { // ccsid manager or typdef rules. should this method write ddm character // data or fodca data right now it is coded for ddm char data only final void writeDDMString(String s) throws SqlException { - CcsidManager currentCcsidManager = netAgent_.getCurrentCcsidManager(); - - ensureLength(currentCcsidManager.getByteLength(s)); - - buffer.position(currentCcsidManager.convertFromJavaString( - s, buffer.array(), buffer.position(), netAgent_)); + encodeString(s); } private void buildLengthAndCodePointForLob(int codePoint, Modified: db/derby/code/trunk/java/client/org/apache/derby/client/net/Utf8CcsidManager.java URL: http://svn.apache.org/viewvc/db/derby/code/trunk/java/client/org/apache/derby/client/net/Utf8CcsidManager.java?rev=1125299&r1=1125298&r2=1125299&view=diff ============================================================================== --- db/derby/code/trunk/java/client/org/apache/derby/client/net/Utf8CcsidManager.java (original) +++ db/derby/code/trunk/java/client/org/apache/derby/client/net/Utf8CcsidManager.java Fri May 20 09:49:46 2011 @@ -22,6 +22,12 @@ package org.apache.derby.client.net; import java.io.UnsupportedEncodingException; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.CharacterCodingException; +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; import org.apache.derby.client.am.Agent; import org.apache.derby.client.am.ClientMessageId; @@ -31,6 +37,10 @@ import org.apache.derby.shared.common.re public class Utf8CcsidManager extends CcsidManager { + private final static String UTF8 = "UTF-8"; + private final static Charset UTF8_CHARSET = Charset.forName(UTF8); + private final CharsetEncoder encoder = UTF8_CHARSET.newEncoder(); + public Utf8CcsidManager() { super((byte) ' ', // 0x40 is the ebcdic space character (byte) '.', @@ -56,14 +66,27 @@ public class Utf8CcsidManager extends Cc } public byte[] convertFromJavaString(String sourceString, Agent agent) - throws SqlException { - byte[] bytes = new byte[getByteLength(sourceString)]; - convertFromJavaString(sourceString, bytes, 0, agent); - return bytes; - } - - public String convertToJavaString(byte[] sourceBytes) { - return convertToJavaString(sourceBytes, 0, sourceBytes.length); + throws SqlException { + try { + ByteBuffer buf = encoder.encode(CharBuffer.wrap(sourceString)); + + if (buf.limit() == buf.capacity()) { + // The length of the encoded representation of the string + // matches the length of the returned buffer, so just return + // the backing array. + return buf.array(); + } + + // Otherwise, copy the interesting bytes into an array with the + // correct length. + byte[] bytes = new byte[buf.limit()]; + buf.get(bytes); + return bytes; + } catch (CharacterCodingException cce) { + throw new SqlException(agent.logWriter_, + new ClientMessageId(SQLState.CANT_CONVERT_UNICODE_TO_UTF8), + cce); + } } /** @@ -71,7 +94,10 @@ public class Utf8CcsidManager extends Cc */ public String convertToJavaString(byte[] sourceBytes, int offset, int numToConvert) { try { - return new String(sourceBytes, offset, numToConvert, "UTF-8"); + // Here we'd rather specify the encoding using a Charset object to + // avoid the need to handle UnsupportedEncodingException, but that + // constructor wasn't introduced until Java 6. + return new String(sourceBytes, offset, numToConvert, UTF8); } catch (UnsupportedEncodingException e) { // We don't have an agent in this method if (SanityManager.DEBUG) { @@ -81,37 +107,30 @@ public class Utf8CcsidManager extends Cc return null; } - public int convertFromJavaString(String sourceString, byte[] buffer, - int offset, Agent agent) throws SqlException { - try { - byte[] strBytes = sourceString.getBytes("UTF-8"); - - for(int i=0; i