@Override
    public VirtualConnection finishResponseMessage(WsByteBuffer[] body, InterChannelCallback callback, boolean bForce) throws MessageSentException {
        if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
            Tr.debug(tc, "finishResponseMessage(body,cb)");
        }
        // H2 doesn't support asych writes, if we got here and this is H2, switch over to sync
        if (isH2Connection()) {
            if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                Tr.debug(tc, "finishResponseMessage: This is H2, calling sync finishResponseMessage(body)");
            }
            // Send the error response synchronously for H2
            try {
                finishResponseMessage(body);
                return getVC();
            } catch (IOException e) {
                return null;
            }
        }
        if (!headersParsed()) {
            // request message must have the headers parsed prior to sending
            // any data out (this is a completely invalid state in the channel
            // above)
            IOException ioe = new IOException("Request not read yet");
            FFDCFilter.processException(ioe, CLASS_NAME + ".finishResponseMessage", "1010");
            if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                Tr.debug(tc, "Attempt to send response without a request msg");
            }
            callback.error(getVC(), ioe);
            return null;
        }

        if (isMessageSent()) {
            throw new MessageSentException("Message already sent");
        }

        // if headers haven't been sent and chunked encoding is not explicitly
        // configured, then set this up for Content-Length
        if (!headersSent() && !getResponseImpl().isChunkedEncodingSet()) {
            if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                Tr.debug(tc, "finishMessage(cb) setting partial body false");
            }
            setPartialBody(false);
        }

        if (getHttpConfig().runningOnZOS()) {
            // @311734 - add this to notify the xmem channel of our final write
            getVC().getStateMap().put(HttpConstants.FINAL_WRITE_MARK, "true");
        }
        setForceAsync(bForce);
        setAppWriteCallback(callback);
        VirtualConnection vc = sendFullOutgoing(body, getResponseImpl(), HttpISCWriteCallback.getRef());
        if (null != vc) {
            // Note: if forcequeue is true, then we will not get a VC object as
            // the lower layer will use the callback and return null
            logFinalResponse(getNumBytesWritten());
            HttpInvalidMessageException inv = checkResponseValidity();
            if (null != inv) {
                // invalid response
                callback.error(vc, inv);
                return null;
            }
        }
        return vc;
    }
@ Override public VirtualConnection finishResponseMessage ( WsByteBuffer [ ] body , InterChannelCallback callback , boolean bForce ) throws MessageSentException { if ( TraceComponent . isAnyTracingEnabled ( ) && tc . isDebugEnabled ( ) ) { Tr . debug ( tc , "finishResponseMessage(body,cb)" ) ; } if ( isH2Connection ( ) ) { if ( TraceComponent . isAnyTracingEnabled ( ) && tc . isDebugEnabled ( ) ) { Tr . debug ( tc , "finishResponseMessage: This is H2, calling sync finishResponseMessage(body)" ) ; } try { finishResponseMessage ( body ) ; return getVC ( ) ; } catch ( IOException e ) { return null ; } } if ( ! headersParsed ( ) ) { IOException ioe = new IOException ( "Request not read yet" ) ; FFDCFilter . processException ( ioe , CLASS_NAME + ".finishResponseMessage" , "1010" ) ; if ( TraceComponent . isAnyTracingEnabled ( ) && tc . isDebugEnabled ( ) ) { Tr . debug ( tc , "Attempt to send response without a request msg" ) ; } callback . error ( getVC ( ) , ioe ) ; return null ; } if ( isMessageSent ( ) ) { throw new MessageSentException ( "Message already sent" ) ; } if ( ! headersSent ( ) && ! getResponseImpl ( ) . isChunkedEncodingSet ( ) ) { if ( TraceComponent . isAnyTracingEnabled ( ) && tc . isDebugEnabled ( ) ) { Tr . debug ( tc , "finishMessage(cb) setting partial body false" ) ; } setPartialBody ( false ) ; } if ( getHttpConfig ( ) . runningOnZOS ( ) ) { getVC ( ) . getStateMap ( ) . put ( HttpConstants . FINAL_WRITE_MARK , "true" ) ; } setForceAsync ( bForce ) ; setAppWriteCallback ( callback ) ; VirtualConnection vc = sendFullOutgoing ( body , getResponseImpl ( ) , HttpISCWriteCallback . getRef ( ) ) ; if ( null != vc ) { logFinalResponse ( getNumBytesWritten ( ) ) ; HttpInvalidMessageException inv = checkResponseValidity ( ) ; if ( null != inv ) { callback . error ( vc , inv ) ; return null ; } } return vc ; }
Send the given body buffers for the outgoing response asynchronously.
If chunked encoding is set or if the partialbody flag is set, then
these buffers will be considered a "chunk" and encoded
as such. If the message is Content-Length defined, then the buffers
will simply be sent out with no modifications. This marks the end
of the outgoing message.
<p>
Note: if headers have not already been sent, then the first call to this
method will send the headers. If this was a chunked encoded message, then
the zero-length chunk is automatically appended.
<p>
If the write can be done immediately, the VirtualConnection will be
returned and the callback will not be used. The caller is responsible for
handling that situation in their code. A null return code means that the
async write is in progress.
<p>
The boolean bForce parameter allows the caller to force the asynchronous
action even if it could be handled immediately. The return code will always
be null and the callback always used.

@param body
(last set of body data, null if no body information)
@param callback
@param bForce
@return VirtualConnection
@throws MessageSentException
-- if a finishMessage API was already used