db-derby-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Army <a...@golux.com>
Subject Re: Regarding DERBY-35...
Date Thu, 28 Oct 2004 00:05:08 GMT
I am posting the following patch as a fix for DERBY-35.  These changes should correct some
DRDA chaining protocol errors 
in the Derby Network Server code.

Please see my comments below for more details about what I _planned_ to do (which I described
in an email to this list 
several weeks ago) and what I actually did.  Assuming the patch is acceptable, please submit
when possible...

Army wrote:
> 
> 
> Regarding DERBY-35: DRDA Chaining in Network Server is incorrect
> 
> It seems to me like a good fix to this bug should do three things:
> 
> 1) Make it so that the chaining bit in the DSS reply is set as part of 
> the DDMWriter.endDss() method, instead of waiting until the beginDss() 
> call for the _next_ DSS.

I have indeed changed the code so that we no longer rely on beginDss() to finalize DSS replies.
 However, the setting of 
chain bits (which _must_ be based on the chaining state of the _request_ DSS) does not happen
in "endDss", as originally 
planned.   Instead, I created a new method called "finalizeChain", and it is inside _that_
method that the chaining bits 
are set.  This new method is then called whenever there is the possibility that a reply chain
might be ending and/or we 
have to transmit the current buffer to the client.

The reason we can't set the chaining bits inside "endDss" is because there are cases where
we have to send multiple DSS 
replies for a single DSS request.  In such cases, all reply DSS'es must be "chained with same
id", and only the _last_ 
DSS reply should have its chaining bits set to match the request DSS.  Thus, if we were to
set the chaining bit in 
"endDss", we would end up setting _every_ DSS reply in the chain to match the request DSS,
which would be incorrect.  By 
pushing things back to the "finalizeChain" method, we can write all of the reply DSSes first
(using "chained with same 
id"), and then call "finalizeChain" to only set the chaining state for the last reply DSS
in the chain.

> 2) Allow setting of the "reuseCorrId" (and thus, indirectly, the setting 
> of the reply chain bits) in DDMWriter to be controlled by the DDMWriter 
> itself, instead of taking the flag as a parameter from DRDAConnThread.
>  [ snip ]

The "reuseCorrId" variable has been removed entirely, as correlation ids for the replies are
now chosen entirely based 
on chaining flags.  If the chaining flag for some reply DSS A is "chain with same id", then
the reply DSS that 
immediately follows A will have the same correlation id as A.  If the chaining flag for A
is "chain with diff id", then 
the reply DSS that immediately follows A will have a correlation that is 1 greater than that
of A.  And if the chaining 
flag for A is "no chaining", then there won't be any DSS'es immediately following A (the buffer
will be sent as soon as 
DSS A is written), so we don't have to worry about subsequent correlation ids.

That said, though, the ultimate goal here was still met: DDMWriter takes care of all of the
correlation id processing; 
DRDAConnThread doesn't know anything about it.

> 
> 3) If #1 and #2 can be successfully implemented, then this one probably 
> isn't so important, but still: it'd be nice if we could minimize the 
> calls to "send()" in DRDAConnThread so that they're only used at the end 
> of the chain.
> [ snip ]

All calls to "send()" have been removed from DRDAConnThread, as has the send() method itself.
 Where possible, the calls 
were removed entirely, but in most cases, they were replaced by calls to the new "finalizeChain()"
method.  That method 
is better than the old "send()" method because it will transmit data IF and ONLY IF we've
reached the end of the reply 
chain (which is determined based on the chaining state of DSS request to which we're replying).
 This means that we 
don't have to add "if chaining" logic around all of the calls, and simplifies the code in
DRDAConnThread.  All of the 
logic is now handled in DDMWriter, which seems more intuitive.

Okay, that's my (perhaps-too-lengthy?) description of what this patch does...

Thanks,
Army

----

Index: DDMReader.java
===================================================================
--- DDMReader.java	(revision 55731)
+++ DDMReader.java	(working copy)
@@ -1368,6 +1368,8 @@
  	protected void clearBuffer() throws DRDAProtocolException
  	{
  		skipBytes(java.lang.Math.min(dssLength, count - pos));
+    	dssIsChainedWithSameID = false;
+    	dssIsChainedWithDiffID = false;
  	}

  	/**
@@ -1751,4 +1753,20 @@
  	   s += indent + "Reader buffer length = " + buffer.length + "\n";
  	   return s;
  	}
+
+	/**
+	 * Return chaining bit for current DSS.
+	 */
+	protected byte getCurrChainState() {
+
+		if (!dssIsChainedWithSameID && !dssIsChainedWithDiffID)
+			return DssConstants.DSS_NOCHAIN;
+
+		if (dssIsChainedWithSameID)
+			return DssConstants.DSSCHAIN_SAME_ID;
+
+		return DssConstants.DSSCHAIN;
+
+	}
+
  }
Index: DDMWriter.java
===================================================================
--- DDMWriter.java	(revision 55731)
+++ DDMWriter.java	(working copy)
@@ -55,8 +55,6 @@
  	// top of the stack
  	private int top;

-	private boolean simpleDssFinalize = false;
-
  	// CCSID manager for translation of strings in the protocol to EBCDIC
  	private CcsidManager ccsidManager;

@@ -80,14 +78,29 @@
  	// 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;
+	
  	// 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;
  		reset(dssTrace);
  	}

@@ -96,6 +109,10 @@
  		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;
  		reset(dssTrace);
  	}

@@ -127,56 +144,126 @@
  	 */
  	protected void createDssReply()
  	{
-		// finish off previous DSS
-    	if (offset != 0)
-    		finalizePreviousChainedDss(false);
-		beginDss(DssConstants.DSSFMT_RPYDSS, nextCorrelationID++);
-		simpleDssFinalize = false;
+		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(int corrID)
+	protected void createDssRequest()
  	{
-		// finish off previous DSS
-    	if (offset != 0)
-    		finalizePreviousChainedDss((correlationID == corrID));
-		beginDss(DssConstants.DSSFMT_RQSDSS, corrID);
-		simpleDssFinalize = false;
+		beginDss(DssConstants.DSSFMT_RQSDSS, true);
  	}
+
  	/**
  	 * Create DSS data object
  	 */
-	protected void createDssObject(boolean reuseCorrID)
+	protected void createDssObject()
  	{
-		// finish off previous DSS - objects are always part of a previous
-		// DSS reply - so correlation id should be the same
-    	if (offset != 0)
-    		finalizePreviousChainedDss (reuseCorrID);
-		beginDss(DssConstants.DSSFMT_OBJDSS, (reuseCorrID ? correlationID : nextCorrelationID++));
-		simpleDssFinalize = false;
+		beginDss(DssConstants.DSSFMT_OBJDSS, true);
  	}
+
  	/**
-	 * Create DSS data object
+	 * 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.
  	 */
-	protected void createDssObject()
+	private void markDssAsContinued(boolean forLob)
  	{
-		createDssObject(true);
+
+		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 ()
+	protected void endDss(byte chainByte)
  	{
-		int val = offset - dssLengthLocation;
-		bytes[dssLengthLocation] = (byte) ((val >>> 8) & 0xff);
-		bytes[dssLengthLocation + 1] = (byte) (val & 0xff);
+
+		// 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
  	 *
  	 */
@@ -236,6 +323,7 @@
  		top = 0;
  		dssLengthLocation = 0;
  		correlationID = DssConstants.CORRELATION_ID_UNKNOWN;
+		nextCorrelationID = 1;
  		isDRDAProtocol = true;
  	}

@@ -536,21 +624,21 @@
  	}


-	protected int  writeScalarStream (boolean chained,
-									  boolean chainedWithSameCorrelator,
+	protected int  writeScalarStream (boolean chainedWithSameCorrelator,
  									  int codePoint,
  									  int length,
  									  java.io.InputStream in,
  									  boolean writeNullByte)
  		throws DRDAProtocolException
  	{
+
+		// Stream equivalent of "beginDss"...
  		int leftToRead = length;
-		int bytesToRead = prepScalarStream (chained,
-											chainedWithSameCorrelator,
+		int bytesToRead = prepScalarStream (chainedWithSameCorrelator,
  											codePoint,
  											writeNullByte,
  											leftToRead);
-		
+
  		if (length == 0)
  			return 0;

@@ -577,7 +665,7 @@
  					leftToRead -= bytesRead;
  				}
  			} while (bytesToRead > 0);
-			
+
  			bytesToRead = flushScalarStreamSegment (leftToRead, bytesToRead);
  		} while (leftToRead > 0);
  		
@@ -595,60 +683,35 @@
  		return totalBytesRead;
  	}
  	
-	
-	private void beginDss (boolean dssHasSameCorrelator,
-						   boolean chainedToNextStructure,
-						   boolean nextHasSameCorrelator,
-						   int dssType,
-						   int corrId,
-						   boolean simpleFinalizeBuildingNextDss)
-  {
-	  if (doesRequestContainData()) {
-		  if (simpleDssFinalize)
-		  {
-			  finalizeDssLength();
+	/**
+	 * Begins a DSS stream (for writing LOB data).
+	 */
+	private void beginDss (boolean chainedToNextStructure,
+						   int dssType)
+	{
+		beginDss(dssType, false);	// false => don't ensure length.

-		  }
-		  else
-			  finalizePreviousChainedDss (dssHasSameCorrelator);
-	  }
+		// always turn on continuation flags... this is helpful for lobs...
+		// these bytes will get rest if dss lengths are finalized.
+  		bytes[dssLengthLocation] = (byte) 0xFF;
+  		bytes[dssLengthLocation + 1] = (byte) 0xFF;

-	  ensureLength (6);
+		// Set whether or not this DSS should be chained to
+		// the next one.  If it's chained, it has to be chained
+		// with same id (that's the nature of EXTDTA chaining).
+		if (chainedToNextStructure) {
+			dssType |= DssConstants.GDSCHAIN_SAME_ID;
+		}

-	  // save the length position and skip
-	  // note: the length position is saved so it can be updated
-	  // with a different value later.
-	  dssLengthLocation = offset;
-	  // always turn on chaining flags... this is helpful for lobs...
-	  // these bytes will get rest if dss lengths are finalized.
-	  bytes[offset] = (byte) 0xFF;
-	  bytes[offset + 1] = (byte) 0xFF;
+		bytes[dssLengthLocation + 3] = (byte) (dssType & 0xff);
+	}

-	  // insert the manditory 0xD0 and the dssType
-	  bytes[offset + 2] = (byte) 0xD0;

-    if (chainedToNextStructure) {
-      dssType |= DssConstants.GDSCHAIN;
-      if (nextHasSameCorrelator)
-        dssType |= DssConstants.GDSCHAIN_SAME_ID;
-    }
-    bytes[offset + 3] = (byte) (dssType & 0xff);
-
-    // write the request correlation id
-    // use method that writes a short !!!
-    bytes[offset + 4] = (byte) ((corrId >>> 8) & 0xff);
-    bytes[offset + 5] = (byte) (corrId & 0xff);
-	offset +=6;
-    simpleDssFinalize = simpleFinalizeBuildingNextDss;
-  }
-
-
    // prepScalarStream does the following prep for writing stream data:
    // 1.  Flushes an existing DSS segment, if necessary
    // 2.  Determines if extended length bytes are needed
    // 3.  Creates a new DSS/DDM header and a null byte indicator, if applicable
-  protected int prepScalarStream  (boolean chained,
-                                   boolean chainedWithSameCorrelator,
+  protected int prepScalarStream  (boolean chainedWithSameCorrelator,
                                     int codePoint,
                                     boolean writeNullByte,
                                     int leftToRead) throws DRDAProtocolException
@@ -663,10 +726,8 @@
      // flush the existing DSS segment if this stream will not fit in the send buffer
      if (10 + extendedLengthByteCount + nullIndicatorSize + leftToRead + offset > DssConstants.MAX_DSS_LENGTH)
{
        try {
-        if (simpleDssFinalize)
-          finalizeDssLength();
-        else
-          finalizePreviousChainedDss (true);
+	    // The existing DSS segment was finalized by endDss; all
+	    // we have to do is send it across the wire.
          sendBytes(agent.getOutputStream());
        }
        catch (java.io.IOException e) {
@@ -677,12 +738,7 @@
      }

      // buildStreamDss should not call ensure length.
-    beginDss (true,
-			  chained,
-			  chainedWithSameCorrelator,
-			  DssConstants.GDSFMT_OBJDSS,
-			  correlationID,
-			  true);
+	beginDss(chainedWithSameCorrelator, DssConstants.GDSFMT_OBJDSS);

      if (extendedLengthByteCount > 0) {
        // method should never ensure length
@@ -728,32 +784,49 @@
  	protected int flushScalarStreamSegment (int leftToRead,
  											int bytesToRead)
  		throws DRDAProtocolException
-  {
-	  int newBytesToRead = bytesToRead;
+	{
+		int newBytesToRead = bytesToRead;

-	  // either at end of data, end of dss segment, or both.
-	  if (leftToRead != 0) {
-		  // 32k segment filled and not at end of data.
-		  if ((Math.min (2 + leftToRead, 32767)) > (bytes.length - offset)) {
-        try {
-          sendBytes (agent.getOutputStream());
-        }
-        catch (java.io.IOException ioe) {
-			agent.markCommunicationsFailure ("DDMWriter.flushScalarStreamSegment()",
+		// either at end of data, end of dss segment, or both.
+		if (leftToRead != 0) {
+		// 32k segment filled and not at end of data.
+
+			if ((Math.min (2 + leftToRead, 32767)) > (bytes.length - offset)) {
+				try {
+				// Mark current DSS as continued, set its chaining state,
+				// then send the data across.
+					markDssAsContinued(true); 	// true => for lobs
+					sendBytes (agent.getOutputStream());
+				}
+				catch (java.io.IOException ioe) {
+					agent.markCommunicationsFailure ("DDMWriter.flushScalarStreamSegment()",
                                                 "",
                                                 ioe.getMessage(),
                                                 "*");
-        }
-      }
-      dssLengthLocation = offset;
-      bytes[offset++] = (byte) (0xff);
-      bytes[offset++] = (byte) (0xff);
-      newBytesToRead = Math.min (leftToRead,32765);
-    }
+				}
+			}
+			else {
+			// DSS is full, but we still have space in the buffer.  So
+			// end the DSS, then start the next DSS right after it.
+				endDss(false);		// false => don't finalize length.
+			}

-    return newBytesToRead;
-  }
+			// Prepare a DSS continuation header for next DSS.
+			dssLengthLocation = offset;
+			bytes[offset++] = (byte) (0xff);
+			bytes[offset++] = (byte) (0xff);
+			newBytesToRead = Math.min (leftToRead,32765);
+			isContinuationDss = true;
+  		}
+		else {
+		// we're done writing the data, so end the DSS.
+			endDss();
+		}

+		return newBytesToRead;
+
+	}
+
    // the offset must not be updated when an error is encountered
    // note valid data may be overwritten
    protected void padScalarStreamForError (int leftToRead, int bytesToRead) throws DRDAProtocolException
@@ -1193,12 +1266,20 @@
  	 */
  	protected void flush () throws java.io.IOException
  	{
-		OutputStream socketOutputStream = agent.getOutputStream();
+		flush(agent.getOutputStream());
+	}
+
+	/**
+	 * Flush buffer to specified stream
+	 *
+	 * @param socketOutputStream
+	 *
+	 * @exception IOException
+	 */
+	protected void flush(OutputStream socketOutputStream)
+		throws java.io.IOException
+	{
  		try {
-			if (isDRDAProtocol)
-			{
-				finalizeDssLength();
-			}
  			socketOutputStream.write (bytes, 0, offset);
  			socketOutputStream.flush();
  		}
@@ -1215,18 +1296,6 @@
  			reset(dssTrace);
  		}
  	}
-	/**
-	 * Flush buffer to specified stream
-	 *
-	 * @param socketOutputStream
-	 *
-	 * @exception IOException
-	 */
-	protected void flush(OutputStream os) throws java.io.IOException
-	{
-		os.write(bytes, 0, offset);
-		os.flush();
-	}

  	// private methods

@@ -1252,41 +1321,41 @@
  	 *			 - 4 - Communications DSS
  	 *			 - 5 - Request DSS where no reply is expected
  	 */
-	private void beginDss (int dssType, int corrID)
+	private void beginDss (int dssType, boolean ensureLen)
  	{
-		// save correlationID for use in error messages while processing
-		// this DSS
-		correlationID = corrID;
+
  		// save length position, the length will be written at the end
  		dssLengthLocation = offset;

-		ensureLength(6);
+		// Should this really only be for non-stream DSSes?
+		if (ensureLen)
+			ensureLength(6);
+
+		// Skip past length; we'll come back and set it later.
  		offset += 2;

  		// write gds info
  		bytes[offset] = (byte) 0xD0;
+
+		// Write DSS type, and default chain bit to be
+		// DssConstants.DSSCHAIN_SAME_ID.  This default
+		// will be overridden by calls to "finalizeChain()"
+		// and/or calls to "beginDss(boolean, int)" for
+		// writing LOB data.
  		bytes[offset + 1] = (byte) dssType;
+		bytes[offset + 1] |= DssConstants.DSSCHAIN_SAME_ID;

-		// write the request correlation id
-		bytes[offset + 2] = (byte) ((corrID >>> 8) & 0xff);
-		bytes[offset + 3] = (byte) (corrID & 0xff);
+		// save correlationID for use in error messages while processing
+		// this DSS
+		correlationID = getCorrelationID();
+
+		// write the reply correlation id
+		bytes[offset + 2] = (byte) ((correlationID >>> 8) & 0xff);
+		bytes[offset + 3] = (byte) (correlationID & 0xff);
  		offset += 4;
  	}

  	/**
-	 * finish the DSS in the buffer by updating the length and chaining bits
-	 *
-	 * @param nextDssHasSameCorrelator - how to set chaining bits
-	 */
-  	private void finalizePreviousChainedDss (boolean nextDssHasSameCorrelator)
-	{
-		finalizeDssLength();
-		bytes[dssLengthLocation + 3] |= 0x40;
-		if (nextDssHasSameCorrelator)
-			bytes[dssLengthLocation + 3] |= 0x10;
-		
-	}
-	/**
       * Finish a DSS Layer A object.
  	 * The length of dss object will be calculated based on the difference between the
  	 * start of the dss, saved on the beginDss call, and the current
@@ -1616,6 +1685,7 @@

    private void sendBytes (java.io.OutputStream socketOutputStream) throws java.io.IOException
    {
+	resetChainState();
      try {
        socketOutputStream.write (bytes, 0, offset);
        socketOutputStream.flush();
@@ -1635,43 +1705,6 @@
    }


-private void buildStreamDssObject (boolean chainedToNextStructure,
-								   boolean nextHasSameCorrelator,
-								   int corrID)
-  {
-	int dssType =   DssConstants.GDSFMT_OBJDSS;
-    if (offset != 0) {
-        finalizePreviousChainedDss (true);
-	}
-
-    ensureLength (6);
-
-    // save the length position and skip
-    // note: the length position is saved so it can be updated
-    // with a different value later.
-    dssLengthLocation = offset;
-    // always turn on chaining flags... this is helpful for lobs...
-    // these bytes will get rest if dss lengths are finalized.
-    bytes[offset] = (byte) 0xFF;
-    bytes[offset + 1] = (byte) 0xFF;
-
-    // insert the manditory 0xD0 and the dssType
-    bytes[offset + 2] = (byte) 0xD0;
-    if (chainedToNextStructure) {
-      dssType |= DssConstants.GDSCHAIN;
-      if (nextHasSameCorrelator)
-        dssType |= DssConstants.GDSCHAIN_SAME_ID;
-    }
-    bytes[offset + 3] = (byte) (  dssType & 0xff);
-
-    // write the request correlation id
-    // use method that writes a short !!!
-    bytes[offset + 4] = (byte) ((corrID >>> 8) & 0xff);
-    bytes[offset + 5] = (byte) (corrID & 0xff);
-	offset += 6;
-	}
-
-
  	private static int min (int i, int j)
  	{
  		return (i < j) ? i : j;
@@ -1687,8 +1720,97 @@
  		return s;
  	}

-}
+	/**
+	 * Reset any chaining state that needs to be reset
+	 * at time of the send
+	 */
+	protected void resetChainState()
+	{
+		prevHdrLocation = -1;
+	}

+	/**
+	 * Looks at chaining info for previous DSS written, and use
+	 * that to figure out what the correlation id for the current
+	 * DSS should be.  Return that correlation id.
+	 */
+	private int getCorrelationID() {

+		int cId;
+		if (previousCorrId != DssConstants.CORRELATION_ID_UNKNOWN) {
+			if (previousChainByte == DssConstants.DSSCHAIN_SAME_ID)
+			// then we have to use the last correlation id we sent.
+				cId = previousCorrId;
+			else
+			// get correlation id as normal.
+				cId = nextCorrelationID++;
+		}
+		else {
+		// must be the case that this is the first DSS we're
+		// writing for this connection (because we haven't
+		// called "endDss" yet).  So, get the corr id as
+		// normal.
+			cId = nextCorrelationID++;
+		}

+		return cId;

+	}
+
+	/**
+	 * Finalize the current DSS chain and send it if
+	 * needed.
+	 *
+	 * Updates the chaining state of the most recently-written-
+	 * to-buffer DSS to correspond to the most recently-read-
+	 * from-client request.  If that chaining state indicates
+	 * we've reached the end of a chain, then we go ahead
+	 * and send the buffer across the wire.
+	 * @param socketOutputStream Output stream to which we're flushing.
+	 */
+	protected void finalizeChain(byte currChainByte,
+		OutputStream socketOutputStream) throws DRDAProtocolException
+	{
+
+		// Go back to previous DSS and override the default
+		// chain state (WITH_SAME_ID) with whatever the last
+		// request dictates.
+
+		if (prevHdrLocation != -1) {
+		// Note: == -1 => the previous DSS was already sent; this
+		// should only happen in cases where the buffer filled up
+		// and we had to send it (which means we were probably
+		// writing EXTDTA).  In such cases, proper chaining
+		// should already have been handled @ time of send.
+			bytes[prevHdrLocation + 3] &= 0x0F;	// Zero out old chain value.
+			bytes[prevHdrLocation + 3] |= currChainByte;
+		}
+
+		// previousChainByte needs to match what we just did.
+		previousChainByte = currChainByte;
+
+		if (currChainByte != DssConstants.DSS_NOCHAIN)
+		// then we're still inside a chain, so don't send.
+			return;
+
+		// Else, we just ended the chain, so send it across.
+
+		if ((SanityManager.DEBUG) && (agent != null))
+			agent.trace("Sending data");
+
+		resetChainState();
+		if (offset != 0) {
+			try {
+				flush(socketOutputStream);
+			} catch (java.io.IOException e) {
+				agent.markCommunicationsFailure(
+					"DDMWriter.finalizeChain()",
+					"OutputStream.flush()",
+					e.getMessage(),"*");
+			}
+		}
+
+	}
+
+}
+
Index: DRDAConnThread.java
===================================================================
--- DRDAConnThread.java	(revision 55743)
+++ DRDAConnThread.java	(working copy)
@@ -572,10 +572,8 @@
  								writeNullSQLCARDobject();
  							}
  							// Send any warnings if JCC can handle them
-							checkWarning(null, null, stmt.getResultSet(), true, 0, false, sendWarningsOnCNTQRY);
+							checkWarning(null, null, stmt.getResultSet(), 0, false, sendWarningsOnCNTQRY);
  						}
-						send();
-
  					}
  					catch(SQLException e)
  					{
@@ -595,12 +593,12 @@
   										trace("Warning: Error closing statement");
  								}
  								writeABNUOWRM();
-								writeSQLCARD(true,e,CodePoint.SVRCOD_ERROR,0,0);
+								writeSQLCARD(e,CodePoint.SVRCOD_ERROR,0,0);
  							}
  						}
  						else
  						{
-							writeSQLCARDs(e, false, 0);
+							writeSQLCARDs(e, 0);
  						}
  						errorInChain(e);
  					}
@@ -618,18 +616,17 @@
  						// builtin method to check(expensive)
  						// For now we will assume that every execute immediate
  						// does an update (that is the most conservative thing)
-						boolean reuseCorrID = false;
  						if (database.RDBUPDRM_sent == false)
  						{
  							writeRDBUPDRM();
-							reuseCorrID = true;
  						}

  						// we need to set update count in SQLCARD
-						checkWarning(null, database.getDefaultStatement().getStatement(), null, reuseCorrID,
updateCount, true, true);
+						checkWarning(null, database.getDefaultStatement().getStatement(),
+							null, updateCount, true, true);
  					} catch (SQLException e)
  					{
-						writeSQLCARDs(e, false, 0);
+						writeSQLCARDs(e, 0);
  						errorInChain(e);
  					}
  					break;
@@ -638,21 +635,15 @@
  					try {
  						if (parseEXCSQLSET())
  						// all went well.
-							writeSQLCARDs(null,true,0);
-						writer.endDss();
-						send();
+							writeSQLCARDs(null,0);
  					}
  					catch (SQLWarning w)
  					{
-						writeSQLCARD(true, w, CodePoint.SVRCOD_WARNING, 0, 0);
-						writer.endDss();
-						send();
+						writeSQLCARD(w, CodePoint.SVRCOD_WARNING, 0, 0);
  					}
  					catch (SQLException e)
  					{
-						writeSQLCARDs(e,false,0);
-						writer.endDss();
-						send();
+						writeSQLCARDs(e, 0);
  						errorInChain(e);
  					}
  					break;
@@ -668,11 +659,11 @@
  										 (sqldaType ==  CodePoint.TYPSQLDA_LIGHT_OUTPUT),
  										 database.getConnection().getWarnings());
  						else
-							checkWarning(database.getConnection(), null, null, false, 0, true, true);
+							checkWarning(database.getConnection(), null, null, 0, true, true);

  					} catch (SQLException e)
  					{
-						writeSQLCARDs(e, false, 0, true);
+						writeSQLCARDs(e, 0, true);
  						PRPSQLSTTfailed = true;
  						errorInChain(e);
  					}
@@ -681,15 +672,11 @@
  					PreparedStatement ps = null;
  					try {
  						if (PRPSQLSTTfailed) {
-							reader.skipBytes();
  							// read the command objects
  							// for ps with parameter
-							// Skip parameters too
-							if (reader.isChainedWithSameID())
-							{
-								correlationID = reader.readDssHeader();
-								reader.skipDss();
-							}
+							// Skip objects/parameters
+							skipRemainder(true);
+
  							// If we failed to prepare, then we fail
  							// to open, which  means OPNQFLRM.
  							writeOPNQFLRM(null);
@@ -703,7 +690,7 @@
  							ps.clearWarnings();
  							stmt.execute();
  							writeOPNQRYRM(false, stmt);
-							checkWarning(null, ps, null, true, 0, false, true);
+							checkWarning(null, ps, null, 0, false, true);

  							writeQRYDSC(stmt, false);
  							// We could send QRYDTA here if there's no LOB data
@@ -729,7 +716,6 @@
  						catch (SQLException pse) {}
  						errorInChain(e);
  					}
-					send();
  					break;
  				case CodePoint.RDBCMM:
  					try
@@ -741,7 +727,7 @@
  							database.getConnection().clearWarnings();
  							database.commit();
  							writeENDUOWRM(COMMIT);
-							checkWarning(database.getConnection(), null, null, true, 0, true, true);
+							checkWarning(database.getConnection(), null, null, 0, true, true);
  						}
  						// we only want to write one of these per transaction
  						// so set to false in preparation for next command
@@ -751,7 +737,7 @@
  					{
  						// Even in case of error, we have to write the ENDUOWRM.
  						writeENDUOWRM(COMMIT);
-						writeSQLCARDs(e, true, 0);
+						writeSQLCARDs(e, 0);
  						errorInChain(e);
  					}
  					break;
@@ -763,7 +749,7 @@
  						database.getConnection().clearWarnings();
  						database.rollback();
  						writeENDUOWRM(ROLLBACK);
-						checkWarning(database.getConnection(), null, null, true, 0, true, true);
+						checkWarning(database.getConnection(), null, null, 0, true, true);
  						// we only want to write one of these per transaction
  						// so set to false in preparation for next command
  						database.RDBUPDRM_sent = false;
@@ -772,7 +758,7 @@
  					{
  						// Even in case of error, we have to write the ENDUOWRM.
  						writeENDUOWRM(ROLLBACK);
-						writeSQLCARDs(e, true, 0);
+						writeSQLCARDs(e, 0);
  						errorInChain(e);
  					}
  					break;
@@ -780,11 +766,11 @@
  					try{
  						stmt = parseCLSQRY();
  						stmt.rsClose();
-						writeSQLCARDs(null, false, 0);
+						writeSQLCARDs(null, 0);
  					}
  					catch (SQLException e)
  					{
-						writeSQLCARDs(e, false, 0);
+						writeSQLCARDs(e, 0);
  						errorInChain(e);
  					}
  					break;
@@ -797,24 +783,25 @@
  				 */
  				case CodePoint.BGNBND:
  					reader.skipBytes();
-					writeSQLCARDs(null, false, 0);
+					writeSQLCARDs(null, 0);
  					break;
  				case CodePoint.BNDSQLSTT:
  					reader.skipBytes();
  					parseSQLSTTDss();
-					writeSQLCARDs(null, false, 0);
+					writeSQLCARDs(null, 0);
  					break;
  				case CodePoint.SQLSTTVRB:
+					// optional
  					reader.skipBytes();
  					break;
  				case CodePoint.ENDBND:
  					reader.skipBytes();
-					writeSQLCARDs(null, false, 0);
+					writeSQLCARDs(null, 0);
  					break;
  				case CodePoint.DSCSQLSTT:
  					if (PRPSQLSTTfailed) {
  						reader.skipBytes();
-						writeSQLCARDs(null, false, 0);
+						writeSQLCARDs(null, 0);
  						break;
  					}
  					try {
@@ -832,18 +819,12 @@
  						}
  						errorInChain(e);
  					}
-					send();
  					break;
  				case CodePoint.EXCSQLSTT:
  					if (PRPSQLSTTfailed) {
-						reader.skipBytes();
  						// Skip parameters too if they are chained Beetle 4867
-						while(reader.isChainedWithSameID())
-						{
-							correlationID = reader.readDssHeader();
-							reader.skipDss();
-						}
-						writeSQLCARDs(null, false, 0);
+						skipRemainder(true);
+						writeSQLCARDs(null, 0);
  						break;
  					}
  					try {
@@ -858,16 +839,22 @@
  						{
  							server.consoleExceptionPrint(e);
  						}
-						writeSQLCARDs(e, false, 0);
+						writeSQLCARDs(e, 0);
  						errorInChain(e);
  					}
  					break;
  				default:
  					codePointNotSupported(codePoint);
  			}
+
+			// Set the correct chaining bits for whatever
+			// reply DSS(es) we just wrote.  If we've reached
+			// the end of the chain, this method will send
+			// the DSS(es) across.
+			finalizeChain();
+
  		}
  		while (reader.isChainedWithSameID() || reader.isChainedWithDiffID());
-		send();
  	}

  	/**
@@ -890,11 +877,7 @@
  		if (reader.terminateChainOnErr() && (getExceptionSeverity(e) > CodePoint.SVRCOD_ERROR))
  		{
  			if (SanityManager.DEBUG)  trace("terminating the chain on error...");
-			while(reader.isChainedWithSameID() || reader.isChainedWithDiffID())
-			{
-				correlationID = reader.readDssHeader();
-				reader.skipDss();
-			}
+			skipRemainder(false);
  		}
  	}

@@ -935,7 +918,7 @@
  		appRequester = new AppRequester();
  		parseEXCSAT();
  		writeEXCSATRD();
-		send();
+		finalizeChain();

  		//we may have to do the access security more than once if we don't
  		//provide the requested security mechanism or we run into errors
@@ -981,7 +964,6 @@
  		//at this point if the security check failed, we're done, the session failed
  		if (securityCheckCode != 0)
  		{
-			send();
  			return false;
  		}

@@ -1000,30 +982,46 @@
  				|| failureType == CodePoint.RDBATHRM)
  			{
  				writeRDBfailure(failureType);
-				writeSQLCARD(true,databaseAccessException,
+				writeSQLCARD(databaseAccessException,
  					CodePoint.SVRCOD_ERROR,0,0);
  			}
  			else
  			{
  				writeRDBfailure(CodePoint.RDBAFLRM);
+
  				// RDBAFLRM requires TYPDEFNAM and TYPDEFOVR
+				writer.createDssObject();
  				writer.writeScalarString(CodePoint.TYPDEFNAM,
  										 CodePoint.TYPDEFNAM_QTDSQLASC);
  				writeTYPDEFOVR();
-				writeSQLCARD(true,databaseAccessException,
+				writer.endDss();
+
+				// Finally, per DDM spec, "an SQLCARD always follows
+				// the RDBAFLRM".
+				writeSQLCARD(databaseAccessException,
  							 CodePoint.SVRCOD_ERROR,0,0);
  			}

-			send();
+			// Ignore anything that was chained to the ACCRDB.
+			skipRemainder(false);
+
+			// Finalize chain state for whatever we wrote in
+			// response to ACCRDB.
+			finalizeChain();
  			return false;
  		}
  		else if (database.accessCount > 1 )	// already in conversation with database
  		{
  			writeRDBfailure(CodePoint.RDBACCRM);
-			send();
+
+			// Ignore anything that was chained to the ACCRDB.
+			skipRemainder(false);
+
+			// Finalize chain state for RDBACCRM
+			finalizeChain();
  			return false;
  		}
-		else // everything is fine
+		else // everything is fine
  			writeACCRDBRM(svrcod);

  		// compare this application requester with previously stored
@@ -2131,7 +2129,7 @@
  		if (stmt.rsIsClosed())
  		{
  			writeQRYNOPRM(CodePoint.SVRCOD_ERROR);
-			skipRemainder();
+			skipRemainder(true);
  			return null;
  		}
  		stmt.setQueryOptions(blksize,qryrelscr,qryrownbr,qryrfrtbl,nbrrow,maxblkext,
@@ -2145,12 +2143,17 @@
  	/**
  	 * Skip remainder of current DSS and all chained DSS'es
  	 *
+	 * @param onlySkipSameIds True if we _only_ want to skip DSS'es
+	 *   that are chained with the SAME id as the current DSS.
+	 *   False means skip ALL chained DSSes, whether they're
+	 *   chained with same or different ids.
  	 * @exception DRDAProtocolException
  	 */
-	private void skipRemainder() throws DRDAProtocolException
+	private void skipRemainder(boolean onlySkipSameIds) throws DRDAProtocolException
  	{
  		reader.skipDss();
-		while (reader.isChainedWithSameID())
+		while (reader.isChainedWithSameID() ||
+			(!onlySkipSameIds && reader.isChainedWithDiffID()))
  		{
  			reader.readDssHeader();
  			reader.skipDss();
@@ -2390,7 +2393,7 @@
  	 *
  	 * @exception DRDAProtocolException
  	 */
-	private void writeACCSECRD(int securityCheckCode)
+	private void writeACCSECRD(int securityCheckCode)
  		throws DRDAProtocolException
  	{
  		writer.createDssReply();
@@ -2415,14 +2418,14 @@
  		}
      	writer.endDdmAndDss ();

-		/* The chaining status in reader is for the latest request DSS ACCSEC.
-		 * There's a difference between JCC and CCC here.  CCC chains ACCSEC, SECCHK,
-		 * and ACCRDB together, whereas JCC sends ACCSEC separately.  DRDA spec
-		 * requires that if requests are chained, replies must be chained.  When we
-		 * call "send", we terminate the chain.  So can't do it if ACCSEC is chained.
-		 */
-		if (! (reader.isChainedWithSameID() || reader.isChainedWithDiffID()))
-			send();
+		if (securityCheckCode != 0) {
+		// then we have an error and so can ignore the rest of the
+		// DSS request chain.
+			skipRemainder(false);
+		}
+
+		finalizeChain();
+
  	}
  	/**
  	 * Parse security check
@@ -2592,8 +2595,16 @@
  		writer.startDdm(CodePoint.SECCHKRM);
  		writer.writeScalar2Bytes(CodePoint.SVRCOD, svrcodFromSecchkcd(securityCheckCode));
  		writer.writeScalar1Byte(CodePoint.SECCHKCD, securityCheckCode);
-
      	writer.endDdmAndDss ();
+
+		if (securityCheckCode != 0) {
+		// then we have an error and are going to end up ignoring the rest
+		// of the DSS request chain.
+			skipRemainder(false);
+		}
+
+		finalizeChain();
+
  	}
  	/**
  	 * Calculate SVRCOD value from SECCHKCD
@@ -2840,7 +2851,7 @@
  								 CodePoint.TYPDEFNAM_QTDSQLASC);
  		writeTYPDEFOVR();
  		writer.endDdmAndDss ();
-		send();
+		finalizeChain();
  	}
  	
  	private void writeTYPDEFOVR() throws DRDAProtocolException
@@ -3439,7 +3450,7 @@
  			database.getConnection().clearWarnings();
  			CallableStatement cs = (CallableStatement) stmt.prepare(prepareString);
  		}
-		
+
  		stmt.ps.clearWarnings();

  		boolean hasResultSet = stmt.execute();
@@ -3501,7 +3512,7 @@
  			//indicate that we are going to return data
  			stmt.setQryrtndta(true);
  			if (! isProcedure)
-				checkWarning(null, ps, null, true, -1, true, true);
+				checkWarning(null, ps, null, -1, true, true);
  			if (rsNum == 0)
  				writeSQLRSLRD(stmt);
  			writeOPNQRYRM(true, stmt);
@@ -3520,21 +3531,14 @@
  		else  if (! sendSQLDTARD)
  		{
  			int updateCount = ps.getUpdateCount();
-			boolean reuseCorrID = false;			// RESOLVE:  We should send this but get protocol err if
-			// we do!!!
  			if (false && (database.RDBUPDRM_sent == false) &&
  				! isProcedure)
  			{
  				writeRDBUPDRM();
-				//following SQLCARD has to be in a continued DSS
-				reuseCorrID = true;
  			}

-
-			checkWarning(database.getConnection(), stmt.ps, null, false, updateCount, true, true);
+			checkWarning(database.getConnection(), stmt.ps, null, updateCount, true, true);
  		}
-		else
-			writer.endDss();

  		} while(hasResultSet && (++rsNum < numResults));
  		
@@ -3723,14 +3727,7 @@
  		}
  		catch (SQLException se)
  		{
-			reader.skipDss();
-			while (reader.isChainedWithSameID() ||
-				   reader.isChainedWithDiffID())
-			{
-				correlationID = reader.readDssHeader();
-				reader.skipDss();
-				
-			}
+			skipRemainder(false);
  			throw se;
  		}
  	}
@@ -3924,7 +3921,7 @@
  				else
  					ps.setShort(i+1, paramVal);
  				break;
-		}
+			}
  			case  FdocaConstants.DRDA_TYPE_NINTEGER:
  			{
  				int paramVal = reader.readInt(getByteOrder());
@@ -4792,20 +4789,20 @@

  	}

-	private void writeSQLCARDs(SQLException e, boolean reuseCorrID, int updateCount)
+	private void writeSQLCARDs(SQLException e, int updateCount)
  									throws DRDAProtocolException
  	{
-		writeSQLCARDs(e, reuseCorrID, updateCount, false);
+		writeSQLCARDs(e, updateCount, false);
  	}

-	private void writeSQLCARDs(SQLException e, boolean reuseCorrID, int updateCount, boolean
sendSQLERRRM)
+	private void writeSQLCARDs(SQLException e, int updateCount, boolean sendSQLERRRM)
  									throws DRDAProtocolException
  	{

  		int severity = CodePoint.SVRCOD_INFO;
  		if (e == null)
  		{
-			writeSQLCARD(reuseCorrID, e,severity, updateCount, 0);
+			writeSQLCARD(e,severity, updateCount, 0);
  			return;
  		}

@@ -4817,9 +4814,8 @@
  		if (sendSQLERRRM || (severity > CodePoint.SVRCOD_ERROR))
  		{
  			writeSQLERRRM(severity);
-			reuseCorrID = true;
  		}
-		writeSQLCARD(reuseCorrID, e,severity, updateCount, 0);
+		writeSQLCARD(e,severity, updateCount, 0);
  	}

  	private int getSqlCode(int severity)
@@ -4832,10 +4828,10 @@
  			return -1;
  	}

-	private void writeSQLCARD(boolean reuseCorrID, SQLException e,int severity,
+	private void writeSQLCARD(SQLException e,int severity,
  		int updateCount, long rowCount ) throws DRDAProtocolException
  	{
-		writer.createDssObject(reuseCorrID);
+		writer.createDssObject();
  		writer.startDdm(CodePoint.SQLCARD);
  		writeSQLCAGRP(e, getSqlCode(severity), updateCount, rowCount);
  		writer.endDdmAndDss();
@@ -5388,7 +5384,7 @@
  				numElems = pmeta.getParameterCount();
  		}

-		writer.createDssObject(false);
+		writer.createDssObject();

  		// all went well we will just write a null SQLCA
  		writer.startDdm(CodePoint.SQLDARD);
@@ -5671,7 +5667,7 @@

  			if (stmt.getExtDtaObjects() != null)
  			{
-				writer.endDdm();
+				writer.endDdmAndDss();
  				writeEXTDTA(stmt);
  				getMoreData=false;
  				sentExtData = true;
@@ -5930,7 +5926,7 @@
  		{
  			// finish off query block and send
  			writer.endDdmAndDss();
-			send();
+			finalizeChain();
  			// read CNTQRY - not sure why JCC sends this
  			correlationID = reader.readDssHeader();
  			int codePoint = reader.readLengthAndCodePoint();
@@ -6331,25 +6327,6 @@
  		
  	}

-	
-	/**
-	 * Send data to application requester
-	 *
-	 * @exception DRDAProtocolException
-	 */
-	private void send() throws DRDAProtocolException
-	{
-		//check if there is data to send
-		if (writer.getOffset() == 0)
-			return;
-		if (SanityManager.DEBUG) trace("Sending data");
-		try {
-			writer.flush();
-		} catch (IOException ioe) {
-			markCommunicationsFailure("flush","","","");
-		}
-	}
-
    /**
     * Write Fdoca Value to client
     * @param index     Index of column being returned
@@ -6732,7 +6709,7 @@
  				server.consoleExceptionPrintTrace(e);
  				reader.clearBuffer();
  				de.write(writer);
-				send();
+				finalizeChain();
  				closeSession();
  				close();
  			}
@@ -6778,7 +6755,7 @@
  		
  			reader.clearBuffer();
  			unExpDe.write(writer);
-			send();
+			finalizeChain();
  		}
  		catch (DRDAProtocolException nde)
  		{
@@ -7014,8 +6991,7 @@
          if (o instanceof Blob) {
  			Blob b = (Blob) o;
  			long blobLength = b.length();
-			writer.writeScalarStream (chainFlag,
-									  chainedWithSameCorrelator,
+			writer.writeScalarStream (chainedWithSameCorrelator,
  									  CodePoint.EXTDTA,
  									  (int) Math.min(blobLength,
  													 Integer.MAX_VALUE),
@@ -7028,8 +7004,7 @@
  			long[] outlen = {-1};
  			ByteArrayInputStream  unicodeStream =
  				convertClobToUnicodeStream(c, outlen);
-			writer.writeScalarStream (chainFlag,
-									  chainedWithSameCorrelator,
+			writer.writeScalarStream (chainedWithSameCorrelator,
  									  CodePoint.EXTDTA,
  									  (int) Math.min(outlen[0],
  													 Integer.MAX_VALUE),		
@@ -7038,8 +7013,7 @@
  		}
  		else if (o instanceof  byte[]) {
  			byte[] b = (byte []) o;
-			writer.writeScalarStream (chainFlag,
-									  chainedWithSameCorrelator,
+			writer.writeScalarStream (chainedWithSameCorrelator,
  									  CodePoint.EXTDTA,
  									  (int) b.length,
  									  new ByteArrayInputStream(b),
@@ -7095,7 +7069,6 @@
  	 * @param conn 		connection to check
  	 * @param stmt 		statement to check
  	 * @param rs 		result set to check
-	 * @param reuseCorrID 	whether send SQLCARD using previous correlation ID
  	 * @param updateCount 	update count to include in SQLCARD
  	 * @param alwaysSend 	whether always send SQLCARD regardless of
  	 *						the existance of warnings
@@ -7104,7 +7077,7 @@
  	 * @exception DRDAProtocolException
  	 */
  	private void checkWarning(Connection conn, Statement stmt, ResultSet rs,
-						  boolean reuseCorrID, int updateCount, boolean alwaysSend, boolean sendWarn)
+						  int updateCount, boolean alwaysSend, boolean sendWarn)
  		throws DRDAProtocolException, SQLException
  	{
  		// instead of writing a chain of sql warning, we send the first one, this is
@@ -7152,7 +7125,7 @@


  		if ((alwaysSend || reportWarning != null) && sendWarn)
-			writeSQLCARDs(reportWarning, reuseCorrID, updateCount);
+			writeSQLCARDs(reportWarning, updateCount);
  	}


@@ -7166,6 +7139,18 @@
  		s += "\n";
  		return s;
  	}
+
+	/**
+	 * Finalize the current DSS chain and send it if
+	 * needed.
+	 */
+	private void finalizeChain() throws DRDAProtocolException {
+
+		writer.finalizeChain(reader.getCurrChainState(), getOutputStream());
+		return;
+
+	}
+
  }


Index: DssConstants.java
===================================================================
--- DssConstants.java	(revision 55731)
+++ DssConstants.java	(working copy)
@@ -29,13 +29,14 @@
    protected static final int DSS_ID = 0xD0;

    // DSS chaining bit.
+  protected static final int DSS_NOCHAIN = 0x00;
    protected static final int DSSCHAIN = 0x40;

    // DSS chaining bit for continuation on error
    protected static final int DSSCHAIN_ERROR_CONTINUE = 0x20;

    // DSS chaining bit where next DSS has same correlation ID.
-  protected static final int DSSCHAIN_SAME_ID = 0x10;
+  protected static final int DSSCHAIN_SAME_ID = 0x50;

    // DSS formatter for an OBJDSS.
    protected static final int DSSFMT_OBJDSS = 0x03;
@@ -59,7 +60,7 @@
    static final int GDSCHAIN = 0x40;

    // GDS chaining bits where next DSS has different correlation ID.
-  static final int GDSCHAIN_SAME_ID = 0x10;
+  static final int GDSCHAIN_SAME_ID = 0x50;

    // GDS formatter for an OBJDSS.
    static final int GDSFMT_OBJDSS = 0x03;
Index: TestProto.java
===================================================================
--- TestProto.java	(revision 55731)
+++ TestProto.java	(working copy)
@@ -310,7 +310,6 @@
  	private void processCommand()
  		throws IOException, DRDAProtocolException
  	{
-		int correlationId;
  		Integer icmd  = (Integer)commandTable.get(tkn.sval.toLowerCase(Locale.ENGLISH));
  		if (icmd == null)
  		{
@@ -330,8 +329,7 @@
  				processIncludeFile();
  				break;
  			case CREATE_DSS_REQUEST:
-				correlationId = getInt();
-				writer.createDssRequest(correlationId);
+				writer.createDssRequest();
  				break;
  			case CREATE_DSS_OBJECT:
  				writer.createDssObject();
@@ -340,7 +338,14 @@
  				writer.createDssReply();
  				break;
  			case END_DSS:
-				writer.endDss();
+				tkn.nextToken();
+				tkn.pushBack();
+				if ((tkn.sval != null) && tkn.sval.startsWith("0x"))
+				// use specified chaining.
+					writer.endDss((getBytes())[0]);
+				else
+				// use default chaining
+					writer.endDss();
  				break;
  			case END_DDM:
  				writer.endDdm();
@@ -448,7 +453,7 @@
  				checkIntOrCP(val);
  				break;
  			case FLUSH:
-				writer.flush(monitorOs);
+				writer.finalizeChain(reader.getCurrChainState(), monitorOs);
  				writer.reset(null);
  				break;
  			case DISPLAY:
@@ -622,7 +627,7 @@
  	 * in hex format or it can just be a string, in which case each char is
  	 * interpreted as  2 byte UNICODE
  	 *
-	 * @param byte array
+	 * @return byte array
  	 */
  	private byte []  getBytes() throws IOException
  	{

Mime
View raw message