📄 ddmwriter.java
字号:
/* Derby - Class org.apache.derby.impl.drda.DDMWriter Copyright 2001, 2004 The Apache Software Foundation or its licensors, as applicable. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */package org.apache.derby.impl.drda;import java.io.OutputStream;import org.apache.derby.iapi.services.sanity.SanityManager;import java.sql.SQLException;import java.sql.DataTruncation;import java.math.BigDecimal;import org.apache.derby.iapi.error.ExceptionSeverity;import java.util.Arrays;/** The DDMWriter is used to write DRDA protocol. The DRDA Protocol is described in the DDMReader class. For more details, see DRDA Volume 3 (Distributed Data Management(DDM) Architecture (DDS definition)*/class DDMWriter{ // number of nesting levels for collections. We need to mark the length // location of the collection so that we can update it as we add more stuff // to the collection private final static int MAX_MARKS_NESTING = 10; // Default buffer size private final static int DEFAULT_BUFFER_SIZE = 32767; static final BigDecimal ZERO = BigDecimal.valueOf(0L); // output buffer private byte[] bytes; // offset into output buffer private int offset; // A saved mark in the stream is saved temporarily to revisit the location. private int[] markStack = new int[MAX_MARKS_NESTING]; // top of the stack private int top; // CCSID manager for translation of strings in the protocol to EBCDIC private CcsidManager ccsidManager; // DRDA connection thread for this writer private DRDAConnThread agent; // This Object tracks the location of the current // Dss header length bytes. This is done so // the length bytes can be automatically // updated as information is added to this stream. private int dssLengthLocation; // Current correlation ID private int correlationID; // Next correlation ID private int nextCorrelationID; // is this DRDA protocol or CMD protocol private boolean isDRDAProtocol; // trace object of the associated session private DssTrace dssTrace; // Location within the "bytes" array of the start of the header // of the DSS most recently written to the buffer. private int prevHdrLocation; // Correlation id of the last DSS that was written to buffer. private int previousCorrId; // Chaining bit of the last DSS that was written to buffer. private byte previousChainByte; // Whether or not the current DSS is a continuation DSS. private boolean isContinuationDss; // In situations where we want to "mark" a buffer location so that // we can "back-out" of a write to handle errors, this holds the // location within the "bytes" array of the start of the header // that immediately precedes the mark. private int lastDSSBeforeMark; // Constructors DDMWriter (int minSize, CcsidManager ccsidManager, DRDAConnThread agent, DssTrace dssTrace) { this.bytes = new byte[minSize]; this.ccsidManager = ccsidManager; this.agent = agent; this.prevHdrLocation = -1; this.previousCorrId = DssConstants.CORRELATION_ID_UNKNOWN; this.previousChainByte = DssConstants.DSS_NOCHAIN; this.isContinuationDss = false; this.lastDSSBeforeMark = -1; reset(dssTrace); } DDMWriter (CcsidManager ccsidManager, DRDAConnThread agent, DssTrace dssTrace) { this.bytes = new byte[DEFAULT_BUFFER_SIZE]; this.ccsidManager = ccsidManager; this.agent = agent; this.prevHdrLocation = -1; this.previousCorrId = DssConstants.CORRELATION_ID_UNKNOWN; this.previousChainByte = DssConstants.DSS_NOCHAIN; this.isContinuationDss = false; this.lastDSSBeforeMark = -1; reset(dssTrace); } /** * reset values for sending next message * */ protected void reset(DssTrace dssTrace) { offset = 0; top = 0; dssLengthLocation = 0; nextCorrelationID = 1; correlationID = DssConstants.CORRELATION_ID_UNKNOWN; isDRDAProtocol = true; this.dssTrace = dssTrace; } /** * set protocol to CMD protocol */ protected void setCMDProtocol() { isDRDAProtocol = false; } /** * Create DSS reply object */ protected void createDssReply() { beginDss(DssConstants.DSSFMT_RPYDSS, true); } /** * Create DSS request object * NOTE: This is _ONLY_ used for testing the protocol * (via the TestProto.java file in this package)! * We should never create a DSS request in normal * DRDA processing (we should only create DSS replies * and DSS objects). */ protected void createDssRequest() { beginDss(DssConstants.DSSFMT_RQSDSS, true); } /** * Create DSS data object */ protected void createDssObject() { beginDss(DssConstants.DSSFMT_OBJDSS, true); } /** * Mark the DSS that we're currently writing as * a continued DSS, which is done by setting * the high-order bit to "1", per DDM spec. * This means: * * 1. One or more continuation DSSes will immediately * follow the current (continued) DSS. * 2. All continuation DSSes will have a 2-byte * continuation header, followed by data; in * other words, chaining state, correlation * id, dss format info, and code point will * NOT be included. All of that info is * present ONLY in the FIRST DSS in the * list of continued DSSes. * * NOTE: A DSS can be a "continuation" DSS _and_ * a "continued" DSS at the same time. However, * the FIRST DSS to be continued canNOT be * a continuation DSS. */ private void markDssAsContinued(boolean forLob) { if (!forLob) { // continuation bit defaults to '1' for lobs, so // we only have to switch it if we're not writing // lobs. bytes[dssLengthLocation] |= 0x80; } // We need to set the chaining state, but ONLY // IF this is the FIRST DSS in the continuation // list (only the first one has chaining state // in it's header; the others do not). if (!isContinuationDss) endDss(!forLob); } /** * End DSS header by writing the length in the length location * and setting the chain bit. Unlike the other two endDss * methods, this one overrides the default chaining byte * (which is set in beginDss) with the chaining byte that * is passed in. NOTE: This method is only used in * association with createDssRequest, and thus is for * TESTING purposes only (via TestProto.java). No calls * should be made to this method in normal DRDA processing * (because for normal processing, chaining must be * determined automatically based on DSS requests). */ protected void endDss(byte chainByte) { // Do regular endDss processing. endDss(true); // Now override default chain state. bytes[dssLengthLocation + 3] &= 0x0F; // Zero out default bytes[dssLengthLocation + 3] |= chainByte; previousChainByte = chainByte; } /** * End DSS header by writing the length in the length location * and setting the chain bit. */ protected void endDss() { endDss(true); } /** * End DSS header by writing the length in the length location * and setting the chain bit. */ private void endDss (boolean finalizeLength) { if (finalizeLength) finalizeDssLength(); if (isContinuationDss) { // no chaining information for this DSS; so we're done. isContinuationDss = false; return; } previousCorrId = correlationID; prevHdrLocation = dssLengthLocation; previousChainByte = DssConstants.DSSCHAIN_SAME_ID; } /** * End final DDM and DSS header by writing the length in the length location * */ protected void endDdmAndDss () { endDdm(); // updates last DDM object endDss(); } /** * Copy Data to End * Create a buffer and copy from the position given to the end of data * * Note that the position given is treated as relative to the * current DSS, for there may be other DSS blocks (chained, presumably) * which are sitting unwritten in the buffer. The caller doesn't * know this, though, and works only with the current DSS. * * getDSSLength, copyDSSDataToEnd, and truncateDSS work together to * provide a sub-protocol for DRDAConnThread to use in its * implementation of the LMTBLKPRC protocol. They enable the caller * to determine when it has written too much data into the current * DSS, to reclaim the extra data that won't fit, and to truncate * that extra data once it has been reclaimed and stored elsewhere. * Note that this support only works for the current DSS. Earlier, * chained DSS blocks cannot be accessed using these methods. For * additional background information, the interested reader should * investigate bugs DERBY-491 and 492 at: * http://issues.apache.org/jira/browse/DERBY-491 and * http://issues.apache.org/jira/browse/DERBY-492 * * @param start */ protected byte [] copyDSSDataToEnd(int start) { start = start + dssLengthLocation; int length = offset - start; byte [] temp = new byte[length]; System.arraycopy(bytes,start,temp,0,length); return temp; } // Collection methods /** * Mark the location of the length bytes for the collection so they * can be updated later * */ protected void startDdm (int codePoint) { // save the location of the beginning of the collection so // that we can come back and fill in the length bytes markStack[top++] = offset; offset += 2; // move past the length bytes before writing the code point bytes[offset] = (byte) ((codePoint >>> 8) & 0xff); bytes[offset + 1] = (byte) (codePoint & 0xff); offset += 2; } /** * Erase all writes for the current ddm and reset the * top */ protected void clearDdm () { offset = markStack[top--]; } /** * Clear the entire send buffer * */ protected void clearBuffer() { offset = 0; top = 0; dssLengthLocation = 0; correlationID = DssConstants.CORRELATION_ID_UNKNOWN; nextCorrelationID = 1; isDRDAProtocol = true; } /** * End the current DDM * */ protected void endDdm () { // remove the top length location offset from the mark stack // calculate the length based on the marked location and end of data. int lengthLocation = markStack[--top]; int length = offset - lengthLocation; // determine if any extended length bytes are needed. the value returned // from calculateExtendedLengthByteCount is the number of extended length // bytes required. 0 indicates no exteneded length. int extendedLengthByteCount = calculateExtendedLengthByteCount (length); if (extendedLengthByteCount != 0) { // ensure there is enough room in the buffer for the extended length bytes. ensureLength (extendedLengthByteCount); // calculate the length to be placed in the extended length bytes. // this length does not include the 4 byte llcp. int extendedLength = length - 4; // shift the data to the right by the number of extended // length bytes needed. int extendedLengthLocation = lengthLocation + 4; System.arraycopy (bytes, extendedLengthLocation, bytes, extendedLengthLocation + extendedLengthByteCount, extendedLength); // write the extended length int shiftSize = (extendedLengthByteCount -1) * 8; for (int i = 0; i < extendedLengthByteCount; i++) { bytes[extendedLengthLocation++] = (byte) ((extendedLength >>> shiftSize ) & 0xff); shiftSize -= 8; } // adjust the offset to account for the shift and insert offset += extendedLengthByteCount; // the two byte length field before the codepoint contains the length // of itself, the length of the codepoint, and the number of bytes used // to hold the extended length. the 2 byte length field also has the first // bit on to indicate extended length bytes were used. length = extendedLengthByteCount + 4; length |= DssConstants.CONTINUATION_BIT; } // write the 2 byte length field (2 bytes before codepoint). bytes[lengthLocation] = (byte) ((length >>> 8) & 0xff); bytes[lengthLocation+1] = (byte) (length & 0xff); } /** * Get the length of the current DSS block we're working on. This is * used by the LMTBLKPRC protocol, which does its own conversational * blocking protocol above the layer of the DRDA blocking. The LMTBLKPRC * implementation (in DRDAConnThread) needs to be able to truncate a * DSS block when splitting a QRYDTA response. * * @return current DSS block length */ protected int getDSSLength() { return offset - dssLengthLocation; } /** * Truncate the current DSS. Before making this call, you should ensure * that you have copied the data to be truncated somewhere else, by * calling copyDSSDataToEnd * * @param desired DSS length */ protected void truncateDSS(int value) { offset = dssLengthLocation + value; } // Write routines /** * Write byte * * @param value byte to be written */ protected void writeByte (int value) { if (SanityManager.DEBUG) { if (value > 255) SanityManager.THROWASSERT( "writeByte value: " + value + " may not be > 255"); } ensureLength (1); bytes[offset++] = (byte) (value & 0xff); } /** * Write network short * * @param value value to be written */ protected void writeNetworkShort (int value) { ensureLength (2); bytes[offset] = (byte) ((value >>> 8) & 0xff); bytes[offset + 1] = (byte) (value & 0xff); offset += 2; } /** * Write network int * * @param value value to be written */ protected void writeNetworkInt (int value) { ensureLength (4); bytes[offset] = (byte) ((value >>> 24) & 0xff); bytes[offset + 1] = (byte) ((value >>> 16) & 0xff); bytes[offset + 2] = (byte) ((value >>> 8) & 0xff); bytes[offset + 3] = (byte) (value & 0xff);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -