0001: /*
0002:
0003: Derby - Class org.apache.derby.impl.drda.DDMWriter
0004:
0005: Licensed to the Apache Software Foundation (ASF) under one or more
0006: contributor license agreements. See the NOTICE file distributed with
0007: this work for additional information regarding copyright ownership.
0008: The ASF licenses this file to You under the Apache License, Version 2.0
0009: (the "License"); you may not use this file except in compliance with
0010: the License. You may obtain a copy of the License at
0011:
0012: http://www.apache.org/licenses/LICENSE-2.0
0013:
0014: Unless required by applicable law or agreed to in writing, software
0015: distributed under the License is distributed on an "AS IS" BASIS,
0016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: See the License for the specific language governing permissions and
0018: limitations under the License.
0019:
0020: */
0021:
0022: package org.apache.derby.impl.drda;
0023:
0024: import java.io.OutputStream;
0025: import java.io.InputStream;
0026: import java.io.BufferedInputStream;
0027: import java.io.BufferedOutputStream;
0028: import org.apache.derby.iapi.services.sanity.SanityManager;
0029: import java.sql.SQLException;
0030: import java.sql.DataTruncation;
0031: import java.math.BigDecimal;
0032: import org.apache.derby.iapi.error.ExceptionSeverity;
0033: import java.util.Arrays;
0034: import org.apache.derby.iapi.reference.Property;
0035: import org.apache.derby.iapi.services.property.PropertyUtil;
0036:
0037: import java.io.IOException;
0038:
0039: /**
0040: The DDMWriter is used to write DRDA protocol. The DRDA Protocol is
0041: described in the DDMReader class.
0042: For more details, see DRDA Volume 3 (Distributed Data Management(DDM)
0043: Architecture (DDS definition)
0044: */
0045: class DDMWriter {
0046:
0047: // number of nesting levels for collections. We need to mark the length
0048: // location of the collection so that we can update it as we add more stuff
0049: // to the collection
0050: private final static int MAX_MARKS_NESTING = 10;
0051:
0052: // Default buffer size
0053: private final static int DEFAULT_BUFFER_SIZE = 32767;
0054:
0055: static final BigDecimal ZERO = BigDecimal.valueOf(0L);
0056:
0057: // output buffer
0058: private byte[] bytes;
0059:
0060: // offset into output buffer
0061: private int offset;
0062:
0063: // A saved mark in the stream is saved temporarily to revisit the location.
0064: private int[] markStack = new int[MAX_MARKS_NESTING];
0065:
0066: // top of the stack
0067: private int top;
0068:
0069: // CCSID manager for translation of strings in the protocol to EBCDIC
0070: private CcsidManager ccsidManager;
0071:
0072: // DRDA connection thread for this writer
0073: private DRDAConnThread agent;
0074:
0075: // This Object tracks the location of the current
0076: // Dss header length bytes. This is done so
0077: // the length bytes can be automatically
0078: // updated as information is added to this stream.
0079: private int dssLengthLocation;
0080:
0081: // Current correlation ID
0082: private int correlationID;
0083:
0084: // Next correlation ID
0085: private int nextCorrelationID;
0086:
0087: // is this DRDA protocol or CMD protocol
0088: private boolean isDRDAProtocol;
0089: // trace object of the associated session
0090: private DssTrace dssTrace;
0091:
0092: // Location within the "bytes" array of the start of the header
0093: // of the DSS most recently written to the buffer.
0094: private int prevHdrLocation;
0095:
0096: // Correlation id of the last DSS that was written to buffer.
0097: private int previousCorrId;
0098:
0099: // Chaining bit of the last DSS that was written to buffer.
0100: private byte previousChainByte;
0101:
0102: // Whether or not the current DSS is a continuation DSS.
0103: private boolean isContinuationDss;
0104:
0105: // In situations where we want to "mark" a buffer location so that
0106: // we can "back-out" of a write to handle errors, this holds the
0107: // location within the "bytes" array of the start of the header
0108: // that immediately precedes the mark.
0109: private int lastDSSBeforeMark;
0110:
0111: // Constructors
0112: DDMWriter(int minSize, CcsidManager ccsidManager,
0113: DRDAConnThread agent, DssTrace dssTrace) {
0114: this .bytes = new byte[minSize];
0115: this .ccsidManager = ccsidManager;
0116: this .agent = agent;
0117: this .prevHdrLocation = -1;
0118: this .previousCorrId = DssConstants.CORRELATION_ID_UNKNOWN;
0119: this .previousChainByte = DssConstants.DSS_NOCHAIN;
0120: this .isContinuationDss = false;
0121: this .lastDSSBeforeMark = -1;
0122: reset(dssTrace);
0123: }
0124:
0125: DDMWriter(CcsidManager ccsidManager, DRDAConnThread agent,
0126: DssTrace dssTrace) {
0127: this .bytes = new byte[DEFAULT_BUFFER_SIZE];
0128: this .ccsidManager = ccsidManager;
0129: this .agent = agent;
0130: this .prevHdrLocation = -1;
0131: this .previousCorrId = DssConstants.CORRELATION_ID_UNKNOWN;
0132: this .previousChainByte = DssConstants.DSS_NOCHAIN;
0133: this .isContinuationDss = false;
0134: this .lastDSSBeforeMark = -1;
0135: reset(dssTrace);
0136: }
0137:
0138: /**
0139: * reset values for sending next message
0140: *
0141: */
0142: protected void reset(DssTrace dssTrace) {
0143: offset = 0;
0144: top = 0;
0145: dssLengthLocation = 0;
0146: nextCorrelationID = 1;
0147: correlationID = DssConstants.CORRELATION_ID_UNKNOWN;
0148: isDRDAProtocol = true;
0149: this .dssTrace = dssTrace;
0150: }
0151:
0152: /**
0153: * set protocol to CMD protocol
0154: */
0155: protected void setCMDProtocol() {
0156: isDRDAProtocol = false;
0157: }
0158:
0159: /**
0160: * Create DSS reply object
0161: */
0162: protected void createDssReply() {
0163: beginDss(DssConstants.DSSFMT_RPYDSS, true);
0164: }
0165:
0166: /**
0167: * Create DSS request object
0168: * NOTE: This is _ONLY_ used for testing the protocol
0169: * (via the TestProto.java file in this package)!
0170: * We should never create a DSS request in normal
0171: * DRDA processing (we should only create DSS replies
0172: * and DSS objects).
0173: */
0174: protected void createDssRequest() {
0175: beginDss(DssConstants.DSSFMT_RQSDSS, true);
0176: }
0177:
0178: /**
0179: * Create DSS data object
0180: */
0181: protected void createDssObject() {
0182: beginDss(DssConstants.DSSFMT_OBJDSS, true);
0183: }
0184:
0185: /**
0186: * Mark the DSS that we're currently writing as
0187: * a continued DSS, which is done by setting
0188: * the high-order bit to "1", per DDM spec.
0189: * This means:
0190: *
0191: * 1. One or more continuation DSSes will immediately
0192: * follow the current (continued) DSS.
0193: * 2. All continuation DSSes will have a 2-byte
0194: * continuation header, followed by data; in
0195: * other words, chaining state, correlation
0196: * id, dss format info, and code point will
0197: * NOT be included. All of that info is
0198: * present ONLY in the FIRST DSS in the
0199: * list of continued DSSes.
0200: *
0201: * NOTE: A DSS can be a "continuation" DSS _and_
0202: * a "continued" DSS at the same time. However,
0203: * the FIRST DSS to be continued canNOT be
0204: * a continuation DSS.
0205: */
0206: private void markDssAsContinued(boolean forLob) {
0207:
0208: if (!forLob) {
0209: // continuation bit defaults to '1' for lobs, so
0210: // we only have to switch it if we're not writing
0211: // lobs.
0212: bytes[dssLengthLocation] |= 0x80;
0213: }
0214:
0215: // We need to set the chaining state, but ONLY
0216: // IF this is the FIRST DSS in the continuation
0217: // list (only the first one has chaining state
0218: // in it's header; the others do not).
0219: if (!isContinuationDss)
0220: endDss(!forLob);
0221:
0222: }
0223:
0224: /**
0225: * End DSS header by writing the length in the length location
0226: * and setting the chain bit. Unlike the other two endDss
0227: * methods, this one overrides the default chaining byte
0228: * (which is set in beginDss) with the chaining byte that
0229: * is passed in. NOTE: This method is only used in
0230: * association with createDssRequest, and thus is for
0231: * TESTING purposes only (via TestProto.java). No calls
0232: * should be made to this method in normal DRDA processing
0233: * (because for normal processing, chaining must be
0234: * determined automatically based on DSS requests).
0235: */
0236: protected void endDss(byte chainByte) {
0237:
0238: // Do regular endDss processing.
0239: endDss(true);
0240:
0241: // Now override default chain state.
0242: bytes[dssLengthLocation + 3] &= 0x0F; // Zero out default
0243: bytes[dssLengthLocation + 3] |= chainByte;
0244: previousChainByte = chainByte;
0245:
0246: }
0247:
0248: /**
0249: * End DSS header by writing the length in the length location
0250: * and setting the chain bit.
0251: */
0252: protected void endDss() {
0253: endDss(true);
0254: }
0255:
0256: /**
0257: * End DSS header by writing the length in the length location
0258: * and setting the chain bit.
0259: */
0260: private void endDss(boolean finalizeLength) {
0261:
0262: if (finalizeLength)
0263: finalizeDssLength();
0264:
0265: if (isContinuationDss) {
0266: // no chaining information for this DSS; so we're done.
0267: isContinuationDss = false;
0268: return;
0269: }
0270:
0271: previousCorrId = correlationID;
0272: prevHdrLocation = dssLengthLocation;
0273: previousChainByte = DssConstants.DSSCHAIN_SAME_ID;
0274:
0275: }
0276:
0277: /**
0278: * End final DDM and DSS header by writing the length in the length location
0279: *
0280: */
0281: protected void endDdmAndDss() {
0282: endDdm(); // updates last DDM object
0283: endDss();
0284: }
0285:
0286: /**
0287: * Copy Data to End
0288: * Create a buffer and copy from the position given to the end of data
0289: *
0290: * Note that the position given is treated as relative to the
0291: * current DSS, for there may be other DSS blocks (chained, presumably)
0292: * which are sitting unwritten in the buffer. The caller doesn't
0293: * know this, though, and works only with the current DSS.
0294: *
0295: * getDSSLength, copyDSSDataToEnd, and truncateDSS work together to
0296: * provide a sub-protocol for DRDAConnThread to use in its
0297: * implementation of the LMTBLKPRC protocol. They enable the caller
0298: * to determine when it has written too much data into the current
0299: * DSS, to reclaim the extra data that won't fit, and to truncate
0300: * that extra data once it has been reclaimed and stored elsewhere.
0301: * Note that this support only works for the current DSS. Earlier,
0302: * chained DSS blocks cannot be accessed using these methods. For
0303: * additional background information, the interested reader should
0304: * investigate bugs DERBY-491 and 492 at:
0305: * http://issues.apache.org/jira/browse/DERBY-491 and
0306: * http://issues.apache.org/jira/browse/DERBY-492
0307: *
0308: * @param start
0309: */
0310: protected byte[] copyDSSDataToEnd(int start) {
0311: start = start + dssLengthLocation;
0312: int length = offset - start;
0313: byte[] temp = new byte[length];
0314: System.arraycopy(bytes, start, temp, 0, length);
0315: return temp;
0316: }
0317:
0318: // Collection methods
0319:
0320: /**
0321: * Mark the location of the length bytes for the collection so they
0322: * can be updated later
0323: *
0324: */
0325: protected void startDdm(int codePoint) {
0326: // save the location of the beginning of the collection so
0327: // that we can come back and fill in the length bytes
0328: markStack[top++] = offset;
0329: ensureLength(4); // verify space for length bytes and code point
0330: offset += 2; // move past the length bytes before writing the code point
0331: bytes[offset] = (byte) ((codePoint >>> 8) & 0xff);
0332: bytes[offset + 1] = (byte) (codePoint & 0xff);
0333: offset += 2;
0334: }
0335:
0336: /**
0337: * Erase all writes for the current ddm and reset the
0338: * top
0339: */
0340: protected void clearDdm() {
0341: offset = markStack[top--];
0342: }
0343:
0344: /**
0345: * Clear the entire send buffer
0346: *
0347: */
0348: protected void clearBuffer() {
0349: offset = 0;
0350: top = 0;
0351: dssLengthLocation = 0;
0352: correlationID = DssConstants.CORRELATION_ID_UNKNOWN;
0353: nextCorrelationID = 1;
0354: isDRDAProtocol = true;
0355: }
0356:
0357: /**
0358: * End the current DDM
0359: *
0360: */
0361: protected void endDdm() {
0362: // remove the top length location offset from the mark stack
0363: // calculate the length based on the marked location and end of data.
0364: int lengthLocation = markStack[--top];
0365: int length = offset - lengthLocation;
0366:
0367: // determine if any extended length bytes are needed. the value returned
0368: // from calculateExtendedLengthByteCount is the number of extended length
0369: // bytes required. 0 indicates no exteneded length.
0370: int extendedLengthByteCount = calculateExtendedLengthByteCount(length);
0371: if (extendedLengthByteCount != 0) {
0372: // ensure there is enough room in the buffer for the extended length bytes.
0373: ensureLength(extendedLengthByteCount);
0374:
0375: // calculate the length to be placed in the extended length bytes.
0376: // this length does not include the 4 byte llcp.
0377: int extendedLength = length - 4;
0378:
0379: // shift the data to the right by the number of extended
0380: // length bytes needed.
0381: int extendedLengthLocation = lengthLocation + 4;
0382: System.arraycopy(bytes, extendedLengthLocation, bytes,
0383: extendedLengthLocation + extendedLengthByteCount,
0384: extendedLength);
0385:
0386: // write the extended length
0387: int shiftSize = (extendedLengthByteCount - 1) * 8;
0388: for (int i = 0; i < extendedLengthByteCount; i++) {
0389: bytes[extendedLengthLocation++] = (byte) ((extendedLength >>> shiftSize) & 0xff);
0390: shiftSize -= 8;
0391: }
0392:
0393: // adjust the offset to account for the shift and insert
0394: offset += extendedLengthByteCount;
0395:
0396: // the two byte length field before the codepoint contains the length
0397: // of itself, the length of the codepoint, and the number of bytes used
0398: // to hold the extended length. the 2 byte length field also has the first
0399: // bit on to indicate extended length bytes were used.
0400: length = extendedLengthByteCount + 4;
0401: length |= DssConstants.CONTINUATION_BIT;
0402: }
0403:
0404: // write the 2 byte length field (2 bytes before codepoint).
0405: bytes[lengthLocation] = (byte) ((length >>> 8) & 0xff);
0406: bytes[lengthLocation + 1] = (byte) (length & 0xff);
0407:
0408: }
0409:
0410: /**
0411: * Get the length of the current DSS block we're working on. This is
0412: * used by the LMTBLKPRC protocol, which does its own conversational
0413: * blocking protocol above the layer of the DRDA blocking. The LMTBLKPRC
0414: * implementation (in DRDAConnThread) needs to be able to truncate a
0415: * DSS block when splitting a QRYDTA response.
0416: *
0417: * @return current DSS block length
0418: */
0419: protected int getDSSLength() {
0420: return offset - dssLengthLocation;
0421: }
0422:
0423: /**
0424: * Truncate the current DSS. Before making this call, you should ensure
0425: * that you have copied the data to be truncated somewhere else, by
0426: * calling copyDSSDataToEnd
0427: *
0428: * @param value DSS length
0429: */
0430: protected void truncateDSS(int value) {
0431: offset = dssLengthLocation + value;
0432: }
0433:
0434: // Write routines
0435:
0436: /**
0437: * Write byte
0438: *
0439: * @param value byte to be written
0440: */
0441: protected void writeByte(int value) {
0442: if (SanityManager.DEBUG) {
0443: if (value > 255)
0444: SanityManager.THROWASSERT("writeByte value: " + value
0445: + " may not be > 255");
0446: }
0447:
0448: ensureLength(1);
0449: bytes[offset++] = (byte) (value & 0xff);
0450: }
0451:
0452: /**
0453: * Write network short
0454: *
0455: * @param value value to be written
0456: */
0457: protected void writeNetworkShort(int value) {
0458: ensureLength(2);
0459: bytes[offset] = (byte) ((value >>> 8) & 0xff);
0460: bytes[offset + 1] = (byte) (value & 0xff);
0461: offset += 2;
0462: }
0463:
0464: /**
0465: * Write network int
0466: *
0467: * @param value value to be written
0468: */
0469: protected void writeNetworkInt(int value) {
0470: ensureLength(4);
0471: bytes[offset] = (byte) ((value >>> 24) & 0xff);
0472: bytes[offset + 1] = (byte) ((value >>> 16) & 0xff);
0473: bytes[offset + 2] = (byte) ((value >>> 8) & 0xff);
0474: bytes[offset + 3] = (byte) (value & 0xff);
0475: offset += 4;
0476: }
0477:
0478: /**
0479: * Write byte array
0480: *
0481: * @param buf byte array to be written
0482: * @param length - length to write
0483: */
0484: protected void writeBytes(byte[] buf, int length) {
0485: writeBytes(buf, 0, length);
0486: }
0487:
0488: /**
0489: * Write byte array
0490: *
0491: * @param buf byte array to be written
0492: * @param start - starting position
0493: * @param length - length to write
0494: */
0495: protected void writeBytes(byte[] buf, int start, int length) {
0496:
0497: if (SanityManager.DEBUG) {
0498: if (buf == null && length > 0)
0499: SanityManager.THROWASSERT("Buf is null");
0500: if (length + start - 1 > buf.length)
0501: SanityManager.THROWASSERT("Not enough bytes in buffer");
0502:
0503: }
0504: ensureLength(length);
0505: System.arraycopy(buf, start, bytes, offset, length);
0506: offset += length;
0507: }
0508:
0509: /**
0510: * Write byte array
0511: *
0512: * @param buf byte array to be written
0513: **/
0514: protected void writeBytes(byte[] buf) {
0515: writeBytes(buf, buf.length);
0516: }
0517:
0518: protected void writeLDBytes(byte[] buf) {
0519: writeLDBytes(buf, 0);
0520: }
0521:
0522: protected void writeLDBytes(byte[] buf, int index) {
0523:
0524: int length = buf.length;
0525: int writeLen = buf.length;
0526:
0527: writeShort(writeLen);
0528:
0529: writeBytes(buf, 0, writeLen);
0530: }
0531:
0532: /**
0533: * Write code point and 4 bytes
0534: *
0535: * @param codePoint - code point to write
0536: * @param value - value to write after code point
0537: */
0538: void writeCodePoint4Bytes(int codePoint, int value) {
0539: ensureLength(4);
0540: bytes[offset] = (byte) ((codePoint >>> 8) & 0xff);
0541: bytes[offset + 1] = (byte) (codePoint & 0xff);
0542: bytes[offset + 2] = (byte) ((value >>> 8) & 0xff);
0543: bytes[offset + 3] = (byte) (value & 0xff);
0544: offset += 4;
0545: }
0546:
0547: /**
0548: * Write scalar 1 byte object includes length, codepoint and value
0549: *
0550: * @param codePoint - code point to write
0551: * @param value - value to write after code point
0552: */
0553: void writeScalar1Byte(int codePoint, int value) {
0554: ensureLength(5);
0555: bytes[offset] = 0x00;
0556: bytes[offset + 1] = 0x05;
0557: bytes[offset + 2] = (byte) ((codePoint >>> 8) & 0xff);
0558: bytes[offset + 3] = (byte) (codePoint & 0xff);
0559: bytes[offset + 4] = (byte) (value & 0xff);
0560: offset += 5;
0561: }
0562:
0563: /**
0564: * Write scalar 2 byte object includes length, codepoint and value
0565: *
0566: * @param codePoint - code point to write
0567: * @param value - value to write after code point
0568: */
0569: protected void writeScalar2Bytes(int codePoint, int value) {
0570: ensureLength(6);
0571: bytes[offset] = 0x00;
0572: bytes[offset + 1] = 0x06;
0573: bytes[offset + 2] = (byte) ((codePoint >>> 8) & 0xff);
0574: bytes[offset + 3] = (byte) (codePoint & 0xff);
0575: bytes[offset + 4] = (byte) ((value >>> 8) & 0xff);
0576: bytes[offset + 5] = (byte) (value & 0xff);
0577: offset += 6;
0578: }
0579:
0580: protected void writeScalar2Bytes(int value) {
0581: ensureLength(2);
0582: bytes[offset] = (byte) ((value >>> 8) & 0xff);
0583: bytes[offset + 1] = (byte) (value & 0xff);
0584: offset += 2;
0585: }
0586:
0587: /**
0588: * Write length and codepoint
0589: *
0590: * @param length - length of object
0591: * @param codePoint - code point to write
0592: */
0593: protected void startDdm(int length, int codePoint) {
0594: ensureLength(4);
0595: bytes[offset] = (byte) ((length >>> 8) & 0xff);
0596: bytes[offset + 1] = (byte) (length & 0xff);
0597: bytes[offset + 2] = (byte) ((codePoint >>> 8) & 0xff);
0598: bytes[offset + 3] = (byte) (codePoint & 0xff);
0599: offset += 4;
0600: }
0601:
0602: /**
0603: * Write scalar byte array object includes length, codepoint and value
0604: *
0605: * @param codePoint - code point to write
0606: * @param buf - value to write after code point
0607: * @param length - number of bytes to write
0608: */
0609: protected void writeScalarBytes(int codePoint, byte[] buf,
0610: int length) {
0611: if (SanityManager.DEBUG) {
0612: if (buf == null && length > 0)
0613: SanityManager.THROWASSERT("Buf is null");
0614: if (length > buf.length)
0615: SanityManager.THROWASSERT("Not enough bytes in buffer");
0616: }
0617: ensureLength(length + 4);
0618: bytes[offset] = (byte) (((length + 4) >>> 8) & 0xff);
0619: bytes[offset + 1] = (byte) ((length + 4) & 0xff);
0620: bytes[offset + 2] = (byte) ((codePoint >>> 8) & 0xff);
0621: bytes[offset + 3] = (byte) (codePoint & 0xff);
0622: System.arraycopy(buf, 0, bytes, offset + 4, length);
0623: offset += length + 4;
0624: }
0625:
0626: protected void writeScalarStream(boolean chainedWithSameCorrelator,
0627: int codePoint, EXTDTAInputStream in, boolean writeNullByte)
0628: throws DRDAProtocolException {
0629:
0630: // Stream equivalent of "beginDss"...
0631: int spareDssLength = prepScalarStream(
0632: chainedWithSameCorrelator, codePoint, writeNullByte);
0633:
0634: // write the data
0635: int bytesRead = 0;
0636: int totalBytesRead = 0;
0637:
0638: try {
0639:
0640: OutputStream out = placeLayerBStreamingBuffer(agent
0641: .getOutputStream());
0642:
0643: boolean isLastSegment = false;
0644:
0645: while (!isLastSegment) {
0646:
0647: int spareBufferLength = bytes.length - offset;
0648:
0649: if (SanityManager.DEBUG) {
0650:
0651: if (PropertyUtil
0652: .getSystemProperty("derby.debug.suicideOfLayerBStreaming") != null)
0653: throw new IOException();
0654: }
0655:
0656: bytesRead = in.read(bytes, offset, Math.min(
0657: spareDssLength, spareBufferLength));
0658:
0659: totalBytesRead += bytesRead;
0660: offset += bytesRead;
0661: spareDssLength -= bytesRead;
0662: spareBufferLength -= bytesRead;
0663:
0664: isLastSegment = peekStream(in) < 0;
0665:
0666: if (isLastSegment || spareDssLength == 0) {
0667:
0668: flushScalarStreamSegment(isLastSegment, out);
0669:
0670: if (!isLastSegment)
0671: spareDssLength = DssConstants.MAX_DSS_LENGTH - 2;
0672:
0673: }
0674:
0675: }
0676:
0677: out.flush();
0678:
0679: } catch (IOException e) {
0680: agent.markCommunicationsFailure(
0681: "DDMWriter.writeScalarStream()", "",
0682: e.getMessage(), "*");
0683: }
0684:
0685: }
0686:
0687: /**
0688: * Begins a DSS stream (for writing LOB data).
0689: */
0690: private void beginDss(boolean chainedToNextStructure, int dssType) {
0691: beginDss(dssType, false); // false => don't ensure length.
0692:
0693: // always turn on continuation flags... this is helpful for lobs...
0694: // these bytes will get rest if dss lengths are finalized.
0695: bytes[dssLengthLocation] = (byte) 0xFF;
0696: bytes[dssLengthLocation + 1] = (byte) 0xFF;
0697:
0698: // Set whether or not this DSS should be chained to
0699: // the next one. If it's chained, it has to be chained
0700: // with same id (that's the nature of EXTDTA chaining).
0701: if (chainedToNextStructure) {
0702: dssType |= DssConstants.GDSCHAIN_SAME_ID;
0703: }
0704:
0705: bytes[dssLengthLocation + 3] = (byte) (dssType & 0xff);
0706: }
0707:
0708: /**
0709: * prepScalarStream does the following prep for writing stream data:
0710: * 1. Flushes an existing DSS segment, if necessary
0711: * 2. Determines if extended length bytes are needed
0712: * 3. Creates a new DSS/DDM header and a null byte indicator, if applicable
0713: *
0714: * If value of length was less than 0, this method processes streaming as Layer B Streaming.
0715: * cf. page 315 of specification of DRDA, Version 3, Volume 3
0716: *
0717: */
0718: private int prepScalarStream(boolean chainedWithSameCorrelator,
0719: int codePoint, boolean writeNullByte)
0720: throws DRDAProtocolException {
0721:
0722: ensureLength(DEFAULT_BUFFER_SIZE - offset);
0723:
0724: final int nullIndicatorSize = writeNullByte ? 1 : 0;
0725:
0726: // flush the existing DSS segment ,
0727: // if this stream will not fit in the send buffer or
0728: // length of this stream is unknown.
0729: // Here, 10 stands for sum of headers of layer A and B.
0730:
0731: try {
0732: // The existing DSS segment was finalized by endDss; all
0733: // we have to do is send it across the wire.
0734: sendBytes(agent.getOutputStream());
0735: } catch (java.io.IOException e) {
0736: agent.markCommunicationsFailure(
0737: "DDMWriter.writeScalarStream()",
0738: "OutputStream.flush()", e.getMessage(), "*");
0739: }
0740:
0741: // buildStreamDss should not call ensure length.
0742: beginDss(chainedWithSameCorrelator, DssConstants.GDSFMT_OBJDSS);
0743:
0744: writeLengthCodePoint(0x8004, codePoint);
0745:
0746: // write the null byte, if necessary
0747: if (writeNullByte)
0748: writeByte(0x0);
0749:
0750: //Here, 6 stands for header of layer A and
0751: //4 stands for header of layer B.
0752: return DssConstants.MAX_DSS_LENGTH - 6 - 4 - nullIndicatorSize;
0753:
0754: }
0755:
0756: // method to determine if any data is in the request.
0757: // this indicates there is a dss object already in the buffer.
0758: protected boolean doesRequestContainData() {
0759: return offset != 0;
0760: }
0761:
0762: // Writes out a scalar stream DSS segment, along with DSS continuation
0763: // headers if necessary.
0764: private void flushScalarStreamSegment(boolean lastSegment,
0765: OutputStream out) throws DRDAProtocolException {
0766:
0767: // either at end of data, end of dss segment, or both.
0768: if (!lastSegment) {
0769:
0770: // 32k segment filled and not at end of data.
0771: try {
0772: // Mark current DSS as continued, set its chaining state,
0773: // then send the data across.
0774: markDssAsContinued(true); // true => for lobs
0775: sendBytes(out, false);
0776:
0777: } catch (java.io.IOException ioe) {
0778: agent.markCommunicationsFailure(
0779: "DDMWriter.flushScalarStreamSegment()", "", ioe
0780: .getMessage(), "*");
0781: }
0782:
0783: // Prepare a DSS continuation header for next DSS.
0784: dssLengthLocation = offset;
0785: bytes[offset++] = (byte) (0xff);
0786: bytes[offset++] = (byte) (0xff);
0787: isContinuationDss = true;
0788: } else {
0789: // we're done writing the data, so end the DSS.
0790: endDss();
0791:
0792: }
0793:
0794: }
0795:
0796: private void writeExtendedLengthBytes(int extendedLengthByteCount,
0797: long length) {
0798: int shiftSize = (extendedLengthByteCount - 1) * 8;
0799: for (int i = 0; i < extendedLengthByteCount; i++) {
0800: bytes[offset + i] = (byte) ((length >>> shiftSize) & 0xff);
0801: shiftSize -= 8;
0802: }
0803: offset += extendedLengthByteCount;
0804: }
0805:
0806: // insert a 4 byte length/codepoint pair into the buffer.
0807: // total of 4 bytes inserted in buffer.
0808: // Note: the length value inserted in the buffer is the same as the value
0809: // passed in as an argument (this value is NOT incremented by 4 before being
0810: // inserted).
0811: void writeLengthCodePoint(int length, int codePoint) {
0812: ensureLength(4);
0813: bytes[offset] = (byte) ((length >>> 8) & 0xff);
0814: bytes[offset + 1] = (byte) (length & 0xff);
0815: bytes[offset + 2] = (byte) ((codePoint >>> 8) & 0xff);
0816: bytes[offset + 3] = (byte) (codePoint & 0xff);
0817: offset += 4;
0818: }
0819:
0820: /**
0821: * Write scalar object header includes length and codepoint
0822: *
0823: * @param codePoint - code point to write
0824: * @param dataLength - length of object data
0825: */
0826: protected void writeScalarHeader(int codePoint, int dataLength) {
0827: ensureLength(dataLength + 4);
0828: bytes[offset] = (byte) (((dataLength + 4) >>> 8) & 0xff);
0829: bytes[offset + 1] = (byte) ((dataLength + 4) & 0xff);
0830: bytes[offset + 2] = (byte) ((codePoint >>> 8) & 0xff);
0831: bytes[offset + 3] = (byte) (codePoint & 0xff);
0832: offset += 4;
0833: }
0834:
0835: /**
0836: * Write scalar string object includes length, codepoint and value
0837: * the string is converted into the appropriate codeset (EBCDIC)
0838: *
0839: * @param codePoint - code point to write
0840: * @param string - string to be written
0841: */
0842: void writeScalarString(int codePoint, String string) {
0843: int stringLength = string.length();
0844: ensureLength((stringLength * 2) + 4);
0845: bytes[offset] = (byte) (((stringLength + 4) >>> 8) & 0xff);
0846: bytes[offset + 1] = (byte) ((stringLength + 4) & 0xff);
0847: bytes[offset + 2] = (byte) ((codePoint >>> 8) & 0xff);
0848: bytes[offset + 3] = (byte) (codePoint & 0xff);
0849: offset = ccsidManager
0850: .convertFromUCS2(string, bytes, offset + 4);
0851: }
0852:
0853: /**
0854: * Write padded scalar string object includes length, codepoint and value
0855: * the string is converted into the appropriate codeset (EBCDIC)
0856: *
0857: * @param codePoint - code point to write
0858: * @param string - string to be written
0859: * @param paddedLength - length to pad string to
0860: */
0861: void writeScalarPaddedString(int codePoint, String string,
0862: int paddedLength) {
0863: int stringLength = string.length();
0864: int fillLength = paddedLength - stringLength;
0865: ensureLength(paddedLength + 4);
0866: bytes[offset] = (byte) (((paddedLength + 4) >>> 8) & 0xff);
0867: bytes[offset + 1] = (byte) ((paddedLength + 4) & 0xff);
0868: bytes[offset + 2] = (byte) ((codePoint >>> 8) & 0xff);
0869: bytes[offset + 3] = (byte) (codePoint & 0xff);
0870: offset = ccsidManager
0871: .convertFromUCS2(string, bytes, offset + 4);
0872: Arrays.fill(bytes, offset, offset + fillLength,
0873: ccsidManager.space);
0874: offset += fillLength;
0875: }
0876:
0877: /**
0878: * Write padded scalar string object value
0879: * the string is converted into the appropriate codeset (EBCDIC)
0880: *
0881: * @param string - string to be written
0882: * @param paddedLength - length to pad string to
0883: */
0884: protected void writeScalarPaddedString(String string,
0885: int paddedLength) {
0886: int stringLength = string.length();
0887:
0888: int fillLength = paddedLength - stringLength;
0889: ensureLength(paddedLength);
0890: offset = ccsidManager.convertFromUCS2(string, bytes, offset);
0891: Arrays.fill(bytes, offset, offset + fillLength,
0892: ccsidManager.space);
0893: offset += fillLength;
0894: }
0895:
0896: /**
0897: * Write padded scalar <code>DRDAString</code> object value. The
0898: * string is converted into the appropriate codeset.
0899: *
0900: * @param drdaString string to be written
0901: * @param paddedLength length to pad string to
0902: */
0903: protected void writeScalarPaddedString(DRDAString drdaString,
0904: int paddedLength) {
0905: int stringLength = drdaString.length();
0906: int fillLength = paddedLength - stringLength;
0907: ensureLength(paddedLength);
0908: System.arraycopy(drdaString.getBytes(), 0, bytes, offset,
0909: stringLength);
0910: offset += stringLength;
0911: Arrays.fill(bytes, offset, offset + fillLength,
0912: ccsidManager.space);
0913: offset += fillLength;
0914: }
0915:
0916: /**
0917: * Write padded scalar byte array object includes length, codepoint and value
0918: *
0919: * @param codePoint - code point to write
0920: * @param buf - byte array to be written
0921: * @param paddedLength - length to pad string to
0922: * @param padByte - byte to be used for padding
0923: */
0924: protected void writeScalarPaddedBytes(int codePoint, byte[] buf,
0925: int paddedLength, byte padByte) {
0926: int bufLength = buf.length;
0927: ensureLength(paddedLength + 4);
0928: bytes[offset] = (byte) (((paddedLength + 4) >>> 8) & 0xff);
0929: bytes[offset + 1] = (byte) ((paddedLength + 4) & 0xff);
0930: bytes[offset + 2] = (byte) ((codePoint >>> 8) & 0xff);
0931: bytes[offset + 3] = (byte) (codePoint & 0xff);
0932: offset += 4;
0933: System.arraycopy(buf, 0, bytes, offset, bufLength);
0934: offset += bufLength;
0935: int fillLength = paddedLength - bufLength;
0936: Arrays.fill(bytes, offset, offset + fillLength, padByte);
0937: offset += fillLength;
0938: }
0939:
0940: /**
0941: * Write padded scalar byte array object value
0942: *
0943: * @param buf - byte array to be written
0944: * @param paddedLength - length to pad string to
0945: * @param padByte - byte to be used for padding
0946: */
0947: protected void writeScalarPaddedBytes(byte[] buf, int paddedLength,
0948: byte padByte) {
0949: int bufLength = buf.length;
0950: int fillLength = paddedLength - bufLength;
0951: ensureLength(paddedLength);
0952: System.arraycopy(buf, 0, bytes, offset, bufLength);
0953: offset += bufLength;
0954: Arrays.fill(bytes, offset, offset + fillLength, padByte);
0955: offset += fillLength;
0956: }
0957:
0958: /**
0959: * Write scalar byte array object includes length, codepoint and value
0960: *
0961: * @param codePoint - code point to write
0962: * @param buf - byte array to be written
0963: */
0964: protected void writeScalarBytes(int codePoint, byte[] buf) {
0965: int bufLength = buf.length;
0966: ensureLength(bufLength + 4);
0967: bytes[offset] = (byte) (((bufLength + 4) >>> 8) & 0xff);
0968: bytes[offset + 1] = (byte) ((bufLength + 4) & 0xff);
0969: bytes[offset + 2] = (byte) ((codePoint >>> 8) & 0xff);
0970: bytes[offset + 3] = (byte) (codePoint & 0xff);
0971: System.arraycopy(buf, 0, bytes, offset + 4, bufLength);
0972: offset += bufLength + 4;
0973: }
0974:
0975: /**
0976: * Write scalar byte array object includes length, codepoint and value
0977: *
0978: * @param codePoint - code point to write
0979: * @param buf - byte array to be written
0980: * @param start - starting point
0981: * @param length - length to write
0982: */
0983: protected void writeScalarBytes(int codePoint, byte[] buf,
0984: int start, int length) {
0985: if (SanityManager.DEBUG) {
0986: if (buf == null && length > start)
0987: SanityManager.THROWASSERT("Buf is null");
0988: if (length - start > buf.length)
0989: SanityManager.THROWASSERT("Not enough bytes in buffer");
0990: }
0991: int numBytes = length - start;
0992: ensureLength(numBytes + 4);
0993: bytes[offset] = (byte) (((numBytes + 4) >>> 8) & 0xff);
0994: bytes[offset + 1] = (byte) ((numBytes + 4) & 0xff);
0995: bytes[offset + 2] = (byte) ((codePoint >>> 8) & 0xff);
0996: bytes[offset + 3] = (byte) (codePoint & 0xff);
0997: offset += 4;
0998: System.arraycopy(buf, start, bytes, offset, numBytes);
0999: offset += numBytes;
1000: }
1001:
1002: // The following methods write data in the platform format
1003: // The platform format was indicated during connection time as ASC since
1004: // JCC doesn't read JVM platform (yet)
1005:
1006: /**
1007: * Write platform short
1008: *
1009: * @param v value to be written
1010: */
1011: protected void writeShort(int v) {
1012: writeNetworkShort(v);
1013: }
1014:
1015: /**
1016: * Write boolean as short
1017: * @param b boolean value true = 1 false = 0
1018: *
1019: */
1020: protected void writeShort(boolean b) {
1021: writeNetworkShort(b ? 1 : 0);
1022: }
1023:
1024: /**
1025: * Write platform int
1026: *
1027: * @param v value to be written
1028: */
1029: protected void writeInt(int v) {
1030: writeNetworkInt(v);
1031: }
1032:
1033: /**
1034: * Write platform long
1035: *
1036: * @param v value to be written
1037: */
1038: protected void writeLong(long v) {
1039: ensureLength(8);
1040: bytes[offset] = (byte) ((v >>> 56) & 0xff);
1041: bytes[offset + 1] = (byte) ((v >>> 48) & 0xff);
1042: bytes[offset + 2] = (byte) ((v >>> 40) & 0xff);
1043: bytes[offset + 3] = (byte) ((v >>> 32) & 0xff);
1044: bytes[offset + 4] = (byte) ((v >>> 24) & 0xff);
1045: bytes[offset + 5] = (byte) ((v >>> 16) & 0xff);
1046: bytes[offset + 6] = (byte) ((v >>> 8) & 0xff);
1047: bytes[offset + 7] = (byte) ((v >>> 0) & 0xff);
1048: offset += 8;
1049: }
1050:
1051: /**
1052: * Write platform float
1053: *
1054: * @param v value to be written
1055: */
1056: protected void writeFloat(float v) {
1057: writeInt(Float.floatToIntBits(v));
1058: }
1059:
1060: /**
1061: * Write platform double
1062: *
1063: * @param v value to be written
1064: */
1065: protected void writeDouble(double v) {
1066: writeLong(Double.doubleToLongBits(v));
1067: }
1068:
1069: /**
1070: * Write big decimal to buffer
1071: *
1072: * @param v value to write
1073: * @param precision Precison of decimal or numeric type
1074: * @param scale declared scale
1075: * @exception SQLException thrown if number of digits > 31
1076: */
1077: protected void writeBigDecimal(java.math.BigDecimal v,
1078: int precision, int scale) throws SQLException {
1079: int length = precision / 2 + 1;
1080: ensureLength(offset + length);
1081: bigDecimalToPackedDecimalBytes(v, precision, scale);
1082: offset += length;
1083: }
1084:
1085: /**
1086: * Write platform boolean
1087: *
1088: * @param v value to be written
1089: */
1090: protected void writeBoolean(boolean v) {
1091: ensureLength(1);
1092: bytes[offset++] = (byte) ((v ? 1 : 0) & 0xff);
1093: }
1094:
1095: /**
1096: * Write length delimited string
1097: *
1098: * @param s value to be written with integer
1099: *
1100: * @exception DRDAProtocolException
1101: */
1102: protected void writeLDString(String s) throws DRDAProtocolException {
1103: writeLDString(s, 0);
1104: }
1105:
1106: /**
1107: * Write length delimited string
1108: *
1109: * @param s value to be written with integer
1110: * @param index column index to put in warning
1111: * @exception DRDAProtocolException
1112: */
1113: protected void writeLDString(String s, int index)
1114: throws DRDAProtocolException {
1115: try {
1116: byte[] byteval = s
1117: .getBytes(NetworkServerControlImpl.DEFAULT_ENCODING);
1118: int origLen = byteval.length;
1119: boolean multiByteTrunc = false;
1120: int writeLen = java.lang.Math.min(
1121: FdocaConstants.LONGVARCHAR_MAX_LEN, origLen);
1122: /*
1123: Need to make sure we truncate on character boundaries.
1124: We are assuming
1125: http://www.sun.com/developers/gadc/technicalpublications/articles/utf8.html
1126: To find the beginning of a multibyte character:
1127: 1) Does the current byte start with the bit pattern 10xxxxxx?
1128: 2) If yes, move left and go to step #1.
1129: 3) Finished
1130: We assume that NetworkServerControlImpl.DEFAULT_ENCODING remains UTF-8
1131: */
1132:
1133: if (SanityManager.DEBUG) {
1134: if (!(NetworkServerControlImpl.DEFAULT_ENCODING
1135: .equals("UTF8")))
1136: SanityManager
1137: .THROWASSERT("Encoding assumed to be UTF8, but is actually"
1138: + NetworkServerControlImpl.DEFAULT_ENCODING);
1139: }
1140:
1141: if (writeLen != origLen)
1142: // first position on the first byte of the multibyte char
1143: while ((byteval[writeLen - 1] & 0xC0) == 0x80) {
1144: multiByteTrunc = true;
1145: writeLen--;
1146: // Then subtract one more to get to the end of the
1147: // previous character
1148: if (multiByteTrunc == true) {
1149: writeLen = writeLen - 1;
1150: }
1151: }
1152:
1153: writeShort(writeLen);
1154: writeBytes(byteval, writeLen);
1155: } catch (Exception e) {
1156: //this should never happen
1157: agent.agentError("Encoding "
1158: + NetworkServerControlImpl.DEFAULT_ENCODING
1159: + " not supported");
1160: }
1161: }
1162:
1163: /**
1164: * Write string with default encoding
1165: *
1166: * @param s value to be written
1167: *
1168: * @exception DRDAProtocolException
1169: */
1170: protected void writeString(String s) throws DRDAProtocolException {
1171: try {
1172: writeBytes(s
1173: .getBytes(NetworkServerControlImpl.DEFAULT_ENCODING));
1174: } catch (Exception e) {
1175: //this should never happen
1176: agent.agentError("Encoding "
1177: + NetworkServerControlImpl.DEFAULT_ENCODING
1178: + " not supported");
1179: }
1180: }
1181:
1182: /**
1183: * Write string with default encoding and specified length
1184: *
1185: * @param s value to be written
1186: * @param length number of bytes to be written
1187: *
1188: * @exception DRDAProtocolException
1189: */
1190: protected void writeString(String s, int length)
1191: throws DRDAProtocolException {
1192: byte[] bs = null;
1193: try {
1194: bs = s.getBytes(NetworkServerControlImpl.DEFAULT_ENCODING);
1195: } catch (Exception e) {
1196: //this should never happen
1197: agent.agentError("Encoding "
1198: + NetworkServerControlImpl.DEFAULT_ENCODING
1199: + " not supported");
1200: }
1201: int len = bs.length;
1202: if (len >= length)
1203: writeBytes(bs, length);
1204: else {
1205: writeBytes(bs);
1206: padBytes(NetworkServerControlImpl.SPACE_CHAR, length - len);
1207: }
1208: }
1209:
1210: /**
1211: * Write pad bytes using spaceChar
1212: *
1213: * @param val value to be written
1214: * @param length length to be written
1215: */
1216: protected void padBytes(byte val, int length) {
1217: Arrays.fill(bytes, offset, offset + length, val);
1218: offset += length;
1219: }
1220:
1221: /**
1222: * Flush buffer to outputstream
1223: *
1224: *
1225: * @exception IOException
1226: */
1227: protected void flush() throws java.io.IOException {
1228: flush(agent.getOutputStream());
1229: }
1230:
1231: /**
1232: * Flush buffer to specified stream
1233: *
1234: * @param socketOutputStream
1235: *
1236: * @exception IOException
1237: */
1238: protected void flush(OutputStream socketOutputStream)
1239: throws java.io.IOException {
1240: try {
1241: socketOutputStream.write(bytes, 0, offset);
1242: socketOutputStream.flush();
1243: } finally {
1244: if ((dssTrace != null) && dssTrace.isComBufferTraceOn()) {
1245: dssTrace.writeComBufferData(bytes, 0, offset,
1246: DssTrace.TYPE_TRACE_SEND, "Reply", "flush", 5);
1247: }
1248: reset(dssTrace);
1249: }
1250: }
1251:
1252: // private methods
1253:
1254: /**
1255: * Write DSS header
1256: * DSS Header format is
1257: * 2 bytes - length
1258: * 1 byte - 'D0' - indicates DDM data
1259: * 1 byte - DSS format
1260: * |---|---------|----------|
1261: * | 0 | flags | type |
1262: * |---|---------|----------|
1263: * | 0 | 1 2 3 | 4 5 6 7 |
1264: * |---|---------|----------|
1265: * bit 0 - '0'
1266: * bit 1 - '0' - unchained, '1' - chained
1267: * bit 2 - '0' - do not continue on error, '1' - continue on error
1268: * bit 3 - '0' - next DSS has different correlator, '1' - next DSS has
1269: * same correlator
1270: * type - 1 - Request DSS
1271: * - 2 - Reply DSS
1272: * - 3 - Object DSS
1273: * - 4 - Communications DSS
1274: * - 5 - Request DSS where no reply is expected
1275: */
1276: private void beginDss(int dssType, boolean ensureLen) {
1277:
1278: // save length position, the length will be written at the end
1279: dssLengthLocation = offset;
1280:
1281: // Should this really only be for non-stream DSSes?
1282: if (ensureLen)
1283: ensureLength(6);
1284:
1285: // Skip past length; we'll come back and set it later.
1286: offset += 2;
1287:
1288: // write gds info
1289: bytes[offset] = (byte) 0xD0;
1290:
1291: // Write DSS type, and default chain bit to be
1292: // DssConstants.DSSCHAIN_SAME_ID. This default
1293: // will be overridden by calls to "finalizeChain()"
1294: // and/or calls to "beginDss(boolean, int)" for
1295: // writing LOB data.
1296: bytes[offset + 1] = (byte) dssType;
1297: bytes[offset + 1] |= DssConstants.DSSCHAIN_SAME_ID;
1298:
1299: // save correlationID for use in error messages while processing
1300: // this DSS
1301: correlationID = getCorrelationID();
1302:
1303: // write the reply correlation id
1304: bytes[offset + 2] = (byte) ((correlationID >>> 8) & 0xff);
1305: bytes[offset + 3] = (byte) (correlationID & 0xff);
1306: offset += 4;
1307: }
1308:
1309: /**
1310: * Finish a DSS Layer A object.
1311: * The length of dss object will be calculated based on the difference between the
1312: * start of the dss, saved on the beginDss call, and the current
1313: * offset into the buffer which marks the end of the data. In the event
1314: * the length requires the use of continuation Dss headers, one for each 32k
1315: * chunk of data, the data will be shifted and the continuation headers
1316: * will be inserted with the correct values as needed.
1317: */
1318: private void finalizeDssLength() {
1319: // calculate the total size of the dss and the number of bytes which would
1320: // require continuation dss headers. The total length already includes the
1321: // the 6 byte dss header located at the beginning of the dss. It does not
1322: // include the length of any continuation headers.
1323: int totalSize = offset - dssLengthLocation;
1324: int bytesRequiringContDssHeader = totalSize
1325: - DssConstants.MAX_DSS_LENGTH;
1326:
1327: // determine if continuation headers are needed
1328: if (bytesRequiringContDssHeader > 0) {
1329: // the continuation headers are needed, so calculate how many.
1330: // after the first 32767 worth of data, a continuation header is
1331: // needed for every 32765 bytes (32765 bytes of data + 2 bytes of
1332: // continuation header = 32767 Dss Max Size).
1333: int contDssHeaderCount = bytesRequiringContDssHeader / 32765;
1334: if (bytesRequiringContDssHeader % 32765 != 0)
1335: contDssHeaderCount++;
1336:
1337: // right now the code will shift to the right. In the future we may want
1338: // to try something fancier to help reduce the copying (maybe keep
1339: // space in the beginning of the buffer??).
1340: // the offset points to the next available offset in the buffer to place
1341: // a piece of data, so the last dataByte is at offset -1.
1342: // various bytes will need to be shifted by different amounts
1343: // depending on how many dss headers to insert so the amount to shift
1344: // will be calculated and adjusted as needed. ensure there is enough room
1345: // for all the conutinuation headers and adjust the offset to point to the
1346: // new end of the data.
1347: int dataByte = offset - 1;
1348: int shiftSize = contDssHeaderCount * 2;
1349: ensureLength(shiftSize);
1350: offset += shiftSize;
1351:
1352: // Notes on the behavior of the Layer B segmenting loop below:
1353: //
1354: // We start with the right most chunk. For a 3-segment object we'd
1355: // shift 2 segments: shift the first (rightmost) one 4 bytes and
1356: // the second one 2. Note that by 'first' we mean 'first time
1357: // through the loop', but that is actually the last segment
1358: // of data since we are moving right-to-left. For an object
1359: // of K segments we will pass through this loop K-1 times.
1360: // The 0th (leftmost) segment is not shifted, as it is
1361: // already in the right place. When we are done, we will
1362: // have made room in each segment for an additional
1363: // 2 bytes for the continuation header. Thus, each
1364: // segment K is shifted K*2 bytes to the right.
1365: //
1366: // Each time through the loop, "dataByte" points to the
1367: // last byte in the segment; "dataToShift" is the amount of
1368: // data that we need to shift, and "shiftSize" is the
1369: // distance that we need to shift it. Since dataByte points
1370: // at the last byte, not one byte beyond it (as with the
1371: // "offset" variable used elsewhere in DDMWriter), the start
1372: // of the segement is actually at (dataByte-dataToShift+1).
1373: //
1374: // After we have shifted the segment, we move back to the
1375: // start of the segment and set the value of the 2-byte DSS
1376: // continuation header, which needs to hold the length of
1377: // this segment's data, together with the continuation flag
1378: // if this is not the rightmost (passOne) segment.
1379: //
1380: // In general, each segment except the rightmost will contain
1381: // 32765 bytes of data, plus the 2-byte header, and its
1382: // continuation flag will be set, so the header value will
1383: // be 0xFFFF. The rightmost segment will not have the
1384: // continuation flag set, so its value may be anything from
1385: // 0x0001 to 0x7FFF, depending on the amount of data in that
1386: // segment.
1387: //
1388: // Note that the 0th (leftmost) segment also has a 2-byte
1389: // DSS header, which needs to have its continuation flag set.
1390: // This is done by resetting the "totalSize" variable below,
1391: // at which point that variable no longer holds the total size
1392: // of the object, but rather just the length of segment 0. The
1393: // total size of the object was written using extended length
1394: // bytes by the endDdm() method earlier.
1395: //
1396: // Additional information about this routine is available in the
1397: // bug notes for DERBY-125:
1398: // http://issues.apache.org/jira/browse/DERBY-125
1399:
1400: // mark passOne to help with calculating the length of the final (first or
1401: // rightmost) continuation header.
1402: boolean passOne = true;
1403: do {
1404: // calculate chunk of data to shift
1405: int dataToShift = bytesRequiringContDssHeader % 32765;
1406: if (dataToShift == 0)
1407: dataToShift = 32765;
1408: int startOfCopyData = dataByte - dataToShift + 1;
1409: System.arraycopy(bytes, startOfCopyData, bytes,
1410: startOfCopyData + shiftSize, dataToShift);
1411: dataByte -= dataToShift;
1412:
1413: // calculate the value the value of the 2 byte continuation dss
1414: // header which includes the length of itself. On the first pass,
1415: // if the length is 32767
1416: // we do not want to set the continuation dss header flag.
1417: int twoByteContDssHeader = dataToShift + 2;
1418: if (passOne)
1419: passOne = false;
1420: else {
1421: if (twoByteContDssHeader == DssConstants.MAX_DSS_LENGTH)
1422: twoByteContDssHeader = (twoByteContDssHeader | DssConstants.CONTINUATION_BIT);
1423:
1424: }
1425:
1426: // insert the header's length bytes
1427: bytes[dataByte + shiftSize - 1] = (byte) ((twoByteContDssHeader >>> 8) & 0xff);
1428: bytes[dataByte + shiftSize] = (byte) (twoByteContDssHeader & 0xff);
1429:
1430: // adjust the bytesRequiringContDssHeader and the amount to shift for
1431: // data in upstream headers.
1432: bytesRequiringContDssHeader -= dataToShift;
1433: shiftSize -= 2;
1434:
1435: // shift and insert another header for more data.
1436: } while (bytesRequiringContDssHeader > 0);
1437:
1438: // set the continuation dss header flag on for the first header
1439: totalSize = (DssConstants.MAX_DSS_LENGTH | DssConstants.CONTINUATION_BIT);
1440:
1441: }
1442:
1443: // insert the length bytes in the 6 byte dss header.
1444: bytes[dssLengthLocation] = (byte) ((totalSize >>> 8) & 0xff);
1445: bytes[dssLengthLocation + 1] = (byte) (totalSize & 0xff);
1446: }
1447:
1448: protected void writeExtendedLength(long size) {
1449: int numbytes = calculateExtendedLengthByteCount(size);
1450: if (size > 0)
1451: writeInt(0x8000 | numbytes);
1452: else
1453: writeInt(numbytes);
1454: }
1455:
1456: /**
1457: * Calculate extended length byte count which follows the DSS header
1458: * for extended DDM.
1459: *
1460: * @param ddmSize - size of DDM command
1461: * @return minimum number of extended length bytes needed. 0 indicates no
1462: * extended length needed.
1463: */
1464: private int calculateExtendedLengthByteCount(long ddmSize) {
1465: if (ddmSize <= 0x7fff)
1466: return 0;
1467: // JCC does not support 2 at this time, so we always send
1468: // at least 4
1469: // else if (ddmSize <= 0xffff)
1470: // return 2;
1471: else if (ddmSize <= 0xffffffffL)
1472: return 4;
1473: else if (ddmSize <= 0xffffffffffffL)
1474: return 6;
1475: else if (ddmSize <= 0x7fffffffffffffffL)
1476: return 8;
1477: else
1478: // shouldn't happen
1479: // XXX - add sanity debug stuff here
1480: return 0;
1481: }
1482:
1483: /**
1484: * Ensure that there is space in the buffer
1485: *
1486: * @param length space required
1487: */
1488: private void ensureLength(int length) {
1489: length += offset;
1490: if (length > bytes.length) {
1491: if (SanityManager.DEBUG) {
1492: agent.trace("DANGER - Expensive expansion of buffer");
1493: }
1494: byte newBytes[] = new byte[Math.max(bytes.length << 1,
1495: length)];
1496: System.arraycopy(bytes, 0, newBytes, 0, offset);
1497: bytes = newBytes;
1498: }
1499: }
1500:
1501: /**
1502: * Write a Java <code>java.math.BigDecimal</code> to packed decimal bytes.
1503: *
1504: * @param b BigDecimal to write
1505: * @param precision Precision of decimal or numeric type
1506: * @return length written.
1507: *
1508: * @exception SQLException Thrown if # digits > 31
1509: */
1510: private int bigDecimalToPackedDecimalBytes(java.math.BigDecimal b,
1511: int precision, int scale) throws SQLException {
1512: int declaredPrecision = precision;
1513: int declaredScale = scale;
1514:
1515: // packed decimal may only be up to 31 digits.
1516: if (declaredPrecision > 31) // this is a bugcheck only !!!
1517: {
1518: clearDdm();
1519: throw new java.sql.SQLException(
1520: "Packed decimal may only be up to 31 digits!");
1521: }
1522:
1523: // get absolute unscaled value of the BigDecimal as a String.
1524: String unscaledStr = b.unscaledValue().abs().toString();
1525:
1526: // get precision of the BigDecimal.
1527: int bigPrecision = unscaledStr.length();
1528:
1529: if (bigPrecision > 31) {
1530: clearDdm();
1531: throw new SQLException(
1532: "The numeric literal \""
1533: + b.toString()
1534: + "\" is not valid because its value is out of range.",
1535: "42820", -405);
1536: }
1537: int bigScale = b.scale();
1538: int bigWholeIntegerLength = bigPrecision - bigScale;
1539: if ((bigWholeIntegerLength > 0) && (!unscaledStr.equals("0"))) {
1540: // if whole integer part exists, check if overflow.
1541: int declaredWholeIntegerLength = declaredPrecision
1542: - declaredScale;
1543: if (bigWholeIntegerLength > declaredWholeIntegerLength) {
1544: clearDdm();
1545: throw new SQLException(
1546: "Overflow occurred during numeric data type conversion of \""
1547: + b.toString() + "\".", "22003", -413);
1548: }
1549: }
1550:
1551: // convert the unscaled value to a packed decimal bytes.
1552:
1553: // get unicode '0' value.
1554: int zeroBase = '0';
1555:
1556: // start index in target packed decimal.
1557: int packedIndex = declaredPrecision - 1;
1558:
1559: // start index in source big decimal.
1560: int bigIndex;
1561:
1562: if (bigScale >= declaredScale) {
1563: // If target scale is less than source scale,
1564: // discard excessive fraction.
1565:
1566: // set start index in source big decimal to ignore excessive fraction.
1567: bigIndex = bigPrecision - 1 - (bigScale - declaredScale);
1568:
1569: if (bigIndex < 0) {
1570: // all digits are discarded, so only process the sign nybble.
1571: bytes[offset + (packedIndex + 1) / 2] = (byte) ((b
1572: .signum() >= 0) ? 12 : 13); // sign nybble
1573: } else {
1574: // process the last nybble together with the sign nybble.
1575: bytes[offset + (packedIndex + 1) / 2] = (byte) (((unscaledStr
1576: .charAt(bigIndex) - zeroBase) << 4) + // last nybble
1577: ((b.signum() >= 0) ? 12 : 13)); // sign nybble
1578: }
1579: packedIndex -= 2;
1580: bigIndex -= 2;
1581: } else {
1582: // If target scale is greater than source scale,
1583: // pad the fraction with zero.
1584:
1585: // set start index in source big decimal to pad fraction with zero.
1586: bigIndex = declaredScale - bigScale - 1;
1587:
1588: // process the sign nybble.
1589: bytes[offset + (packedIndex + 1) / 2] = (byte) ((b.signum() >= 0) ? 12
1590: : 13); // sign nybble
1591:
1592: for (packedIndex -= 2, bigIndex -= 2; bigIndex >= 0; packedIndex -= 2, bigIndex -= 2)
1593: bytes[offset + (packedIndex + 1) / 2] = (byte) 0;
1594:
1595: if (bigIndex == -1) {
1596: bytes[offset + (packedIndex + 1) / 2] = (byte) ((unscaledStr
1597: .charAt(bigPrecision - 1) - zeroBase) << 4); // high nybble
1598:
1599: packedIndex -= 2;
1600: bigIndex = bigPrecision - 3;
1601: } else {
1602: bigIndex = bigPrecision - 2;
1603: }
1604: }
1605:
1606: // process the rest.
1607: for (; bigIndex >= 0; packedIndex -= 2, bigIndex -= 2) {
1608: bytes[offset + (packedIndex + 1) / 2] = (byte) (((unscaledStr
1609: .charAt(bigIndex) - zeroBase) << 4) + // high nybble
1610: (unscaledStr.charAt(bigIndex + 1) - zeroBase)); // low nybble
1611: }
1612:
1613: // process the first nybble when there is one left.
1614: if (bigIndex == -1) {
1615: bytes[offset + (packedIndex + 1) / 2] = (byte) (unscaledStr
1616: .charAt(0) - zeroBase);
1617:
1618: packedIndex -= 2;
1619: }
1620:
1621: // pad zero in front of the big decimal if necessary.
1622: for (; packedIndex >= -1; packedIndex -= 2)
1623: bytes[offset + (packedIndex + 1) / 2] = (byte) 0;
1624:
1625: return declaredPrecision / 2 + 1;
1626: }
1627:
1628: /***
1629: * Prepend zeros to numeric string
1630: *
1631: * @param s string
1632: * @param precision - length of padded string
1633: *
1634: * @return zero padded string
1635: */
1636: public static String zeroPadString(String s, int precision) {
1637:
1638: if (s == null)
1639: return s;
1640:
1641: int slen = s.length();
1642: if (precision == slen)
1643: return s;
1644: else if (precision > slen) {
1645: char[] ca = new char[precision - slen];
1646: Arrays.fill(ca, 0, precision - slen, '0');
1647: return new String(ca) + s;
1648: } else {
1649: // Shouldn't happen but just in case
1650: // truncate
1651: return s.substring(0, precision);
1652: }
1653:
1654: }
1655:
1656: private void sendBytes(java.io.OutputStream socketOutputStream)
1657: throws java.io.IOException {
1658:
1659: sendBytes(socketOutputStream, true);
1660:
1661: }
1662:
1663: private void sendBytes(java.io.OutputStream socketOutputStream,
1664: boolean flashStream) throws java.io.IOException {
1665: resetChainState();
1666: try {
1667: socketOutputStream.write(bytes, 0, offset);
1668: if (flashStream)
1669: socketOutputStream.flush();
1670: } finally {
1671: if ((dssTrace != null) && dssTrace.isComBufferTraceOn()) {
1672: dssTrace.writeComBufferData(bytes, 0, offset,
1673: DssTrace.TYPE_TRACE_SEND, "Reply", "flush", 5);
1674: }
1675: clearBuffer();
1676: }
1677: }
1678:
1679: protected String toDebugString(String indent) {
1680: String s = indent + "***** DDMWriter toDebugString ******\n";
1681: int byteslen = 0;
1682: if (bytes != null)
1683: byteslen = bytes.length;
1684: s += indent + "byte array length = " + bytes.length + "\n";
1685: return s;
1686: }
1687:
1688: /**
1689: * Reset any chaining state that needs to be reset
1690: * at time of the send
1691: */
1692: protected void resetChainState() {
1693: prevHdrLocation = -1;
1694: }
1695:
1696: /**
1697: * Looks at chaining info for previous DSS written, and use
1698: * that to figure out what the correlation id for the current
1699: * DSS should be. Return that correlation id.
1700: */
1701: private int getCorrelationID() {
1702:
1703: int cId;
1704: if (previousCorrId != DssConstants.CORRELATION_ID_UNKNOWN) {
1705: if (previousChainByte == DssConstants.DSSCHAIN_SAME_ID)
1706: // then we have to use the last correlation id we sent.
1707: cId = previousCorrId;
1708: else
1709: // get correlation id as normal.
1710: cId = nextCorrelationID++;
1711: } else {
1712: // must be the case that this is the first DSS we're
1713: // writing for this connection (because we haven't
1714: // called "endDss" yet). So, get the corr id as
1715: // normal.
1716: cId = nextCorrelationID++;
1717: }
1718:
1719: return cId;
1720:
1721: }
1722:
1723: /**
1724: * Finalize the current DSS chain and send it if
1725: * needed.
1726: *
1727: * Updates the chaining state of the most recently-written-
1728: * to-buffer DSS to correspond to the most recently-read-
1729: * from-client request. If that chaining state indicates
1730: * we've reached the end of a chain, then we go ahead
1731: * and send the buffer across the wire.
1732: * @param socketOutputStream Output stream to which we're flushing.
1733: */
1734: protected void finalizeChain(byte currChainByte,
1735: OutputStream socketOutputStream)
1736: throws DRDAProtocolException {
1737:
1738: // Go back to previous DSS and override the default
1739: // chain state (WITH_SAME_ID) with whatever the last
1740: // request dictates.
1741:
1742: if (prevHdrLocation != -1) {
1743: // Note: == -1 => the previous DSS was already sent; this
1744: // should only happen in cases where the buffer filled up
1745: // and we had to send it (which means we were probably
1746: // writing EXTDTA). In such cases, proper chaining
1747: // should already have been handled @ time of send.
1748: bytes[prevHdrLocation + 3] &= 0x0F; // Zero out old chain value.
1749: bytes[prevHdrLocation + 3] |= currChainByte;
1750: }
1751:
1752: // previousChainByte needs to match what we just did.
1753: previousChainByte = currChainByte;
1754:
1755: if (currChainByte != DssConstants.DSS_NOCHAIN)
1756: // then we're still inside a chain, so don't send.
1757: return;
1758:
1759: // Else, we just ended the chain, so send it across.
1760:
1761: if ((SanityManager.DEBUG) && (agent != null))
1762: agent.trace("Sending data");
1763:
1764: resetChainState();
1765: if (doesRequestContainData()) {
1766: try {
1767: flush(socketOutputStream);
1768: } catch (java.io.IOException e) {
1769: agent.markCommunicationsFailure(
1770: "DDMWriter.finalizeChain()",
1771: "OutputStream.flush()", e.getMessage(), "*");
1772: }
1773: }
1774:
1775: }
1776:
1777: /**
1778: * Takes note of the location of the most recently completed
1779: * DSS in the buffer, and then returns the current offset.
1780: * This method is used in conjunction with "clearDSSesBackToMark"
1781: * to allow for DRDAConnThread to "back-out" DSSes in the
1782: * event of errors.
1783: */
1784: protected int markDSSClearPoint() {
1785:
1786: lastDSSBeforeMark = prevHdrLocation;
1787: return offset;
1788:
1789: }
1790:
1791: /**
1792: * Does a logical "clear" of everything written to the buffer after
1793: * the received mark. It's assumed that this method will be used
1794: * in error cases when we've started writing one or more DSSes,
1795: * but then hit an error and need to back out. After backing out,
1796: * we'll always need to write _something_ back to the client to
1797: * indicate an error (typically, we just write an SQLCARD) but what
1798: * exactly gets written is handled in DRDAConnThread. Here, we
1799: * just do the necessary prep so that whatever comes next will
1800: * succeed.
1801: */
1802: protected void clearDSSesBackToMark(int mark) {
1803:
1804: // Logical clear.
1805: offset = mark;
1806:
1807: // Because we've just cleared out the most recently-
1808: // written DSSes, we have to make sure the next thing
1809: // we write will have the correct correlation id. We
1810: // do this by setting the value of 'nextCorrelationID'
1811: // based on the chaining byte from the last remaining
1812: // DSS (where "remaining" means that it still exists
1813: // in the buffer after the clear).
1814: if (lastDSSBeforeMark == -1)
1815: // we cleared out the entire buffer; reset corr id.
1816: nextCorrelationID = 1;
1817: else {
1818: // last remaining DSS had chaining, so we set "nextCorrelationID"
1819: // to be 1 greater than whatever the last remaining DSS had as
1820: // its correlation id.
1821: nextCorrelationID = 1 + (int) (((bytes[lastDSSBeforeMark + 4] & 0xff) << 8) + (bytes[lastDSSBeforeMark + 5] & 0xff));
1822: }
1823:
1824: }
1825:
1826: private static int peekStream(InputStream in) throws IOException {
1827:
1828: in.mark(1);
1829:
1830: try {
1831: return in.read();
1832:
1833: } finally {
1834: in.reset();
1835:
1836: }
1837: }
1838:
1839: private static int getLayerBStreamingBufferSize() {
1840: return PropertyUtil.getSystemInt(
1841: Property.DRDA_PROP_STREAMOUTBUFFERSIZE, 0);
1842: }
1843:
1844: private static OutputStream placeLayerBStreamingBuffer(
1845: OutputStream original) {
1846:
1847: int size = getLayerBStreamingBufferSize();
1848:
1849: if (size < 1)
1850: return original;
1851: else
1852: return new BufferedOutputStream(original, size);
1853:
1854: }
1855:
1856: }
|