//
// Copyright (c)1998-2011 Pearson Education, Inc. or its affiliate(s).
// All rights reserved.
//
package openadk.library;
import java.io.IOException;
import openadk.library.common.YesNo;
import openadk.library.impl.DataObjectOutputStreamImpl;
import openadk.library.infra.*;
/**
* Helper class to send SIF_Response messages when an agent defers automatic
* sending of responses in its implementation of the Publisher.onRequest
* message handler. This class is designed to be used when the {@link openadk.library.DataObjectOutputStream#deferResponse}
* method is called during SIF_Request processing, and must be used outside of the
* Publisher.onRequest message handler.<p>
*
* By design, the ADK automatically sends one or more SIF_Response messages to
* the zone when control is returned from the Publisher.onRequest
* message handler. SIF_Responses are sent in a background thread without any
* further intervention on the agent's part. The ADK takes care of proper data
* object rendering and packetizing based on the parameters of the SIF_Request
* message, and includes a SIF_Error element in the payload if an exception is
* thrown from the onRequest method.<p>
*
* Although this behavior is convenient and very appropriate for most agent
* implementations, there are cases when agents require greater control over SIF
* Request & Response messaging. Specifically, agents sometimes need the
* ability to decouple SIF_Response processing from SIF_Request processing.
* For example, implementing the SIF 1.5 StudentLocator message choreography may
* require that SIF_Response messages are sent hours or days after the initial
* SIF_Request is received. Or, an agent might queue requests in a database
* table and work on them later when it has available resources. Both of these
* scenarios can be achieved by calling the {@link openadk.library.DataObjectOutputStream#deferResponse}
* method on the DataObjectOutputStream passed to the Publisher.onRequest
* message handler. This method signals the ADK to ignore any SIFDataObjects
* written to the stream and to defer the sending of SIF_Response messages. The
* agent must send its own SIF_Response messages at a later time by using this
* SIFResponseSender helper class<p>
*
* To use this class,<p>
*
* <ul>
* <li>
* When the agent is ready to send SIF_Response messages for a SIF_Request
* that was received at an earlier time, instantiate a SIFResponseSender.
* </li>
* <li>
* Call the {@link #open} method and pass it the Zone you wish to send
* SIF_Response messages to. You must also pass the SIF_Version and
* SIF_MaxBufferSize value from the original SIF_Request message. (Be sure
* to obtain these values from the <code>SIFMessageInfo</code> parameter
* in your Publisher.onRequest implementation so you can pass them to
* the {@link #open} method when using this class.)
* </li>
* <li>
* Repeatedly call the {@link #write(SIFDataObject)} method, once for each
* SIFDataObject that should be included in the SIF_Response stream
* </li>
* <li>
* Call the {@link #write(SIF_Error)} method to include a SIF_Error element
* in the SIF_Response stream
* </li>
* <li>
* When finished, call the {@link #close} method. The ADK will automatically
* package the objects into one or more SIF_Response packets in the same way it
* does for normal request/response processing via the Publisher.onRequest
* method.
* </li>
* </ul>
*
* @since ADK 1.5.1
*/
public class SIFResponseSender
{
protected Zone fZone;
protected DataObjectOutputStreamImpl fOut = null;
/**
* Open the SIFResponseSender to send SIF_Response messages to a specific zone.
*
* @param zone The zone to send messages to
*
* @param sifRequestMsgId The SIF_MsgId from the original SIF_Request message.
* You can obtain this by calling {@link openadk.library.SIFMessageInfo#getMsgId}
* on the SIFMessageInfo parameter passed to the Publisher.onRequest
* method. NOTE: Do not call {@link openadk.library.SIFMessageInfo#getSIFRequestMsgId()}
* as it will return a <code>null</code> value and is only intended to be called
* on SIF_Response messages.
*
* @param sifRequestSourceId The SIF_SourceId from the original SIF_Request message.
* You can obtain this by calling {@link openadk.library.SIFMessageInfo#getSourceId()}
* on the SIFMessageInfo parameter passed to the Publisher.onRequest
* method.
*
* @param sifVersion The SIF_Version value from the original SIF_Request message.
* You can obtain this by calling {@link openadk.library.SIFMessageInfo#getSIFRequestVersion}
* on the SIFMessageInfo parameter passed to the Publisher.onRequest
* method.
*
* @param maxBufSize The SIF_MaxBufferSize value from the original SIF_Request
* message. You can obtain this by calling {@link openadk.library.SIFMessageInfo#getMaxBufferSize}
* on the SIFMessageInfo parameter passed to the Publisher.onRequest
* method.
*
* @param fieldRestrictions The Query object representing the SIF_Request that this SIF_Response
* is being generated for
*
* @exception IllegalArgumentException is thrown if any of the parameter are invalid
*
* @exception ADKException is thrown if an error occurs preparing the output stream
*/
public void open(
Zone zone,
String sifRequestMsgId,
String sifRequestSourceId,
SIFVersion sifVersion,
int maxBufferSize,
Query srcQuery )
throws ADKException
{
fZone = zone;
fOut = DataObjectOutputStreamImpl.newInstance();
fOut.initialize( zone, srcQuery, sifRequestSourceId, sifRequestMsgId, sifVersion, maxBufferSize );
}
/**
* Open the SIFResponseSender to send SIF_Response messages to a specific zone.
*
* @param zone The zone to send messages to
*
* @param sifRequestMsgId The SIF_MsgId from the original SIF_Request message.
* You can obtain this by calling {@link openadk.library.SIFMessageInfo#getMsgId}
* on the SIFMessageInfo parameter passed to the Publisher.onRequest
* method. NOTE: Do not call {@link openadk.library.SIFMessageInfo#getSIFRequestMsgId()}
* as it will return a <code>null</code> value and is only intended to be called
* on SIF_Response messages.
*
* @param sifRequestSourceId The SIF_SourceId from the original SIF_Request message.
* You can obtain this by calling {@link openadk.library.SIFMessageInfo#getSourceId()}
* on the SIFMessageInfo parameter passed to the Publisher.onRequest
* method.
*
* @param sifVersion The SIF_Version value from the original SIF_Request message.
* You can obtain this by calling {@link openadk.library.SIFMessageInfo#getSIFRequestVersion}
* on the SIFMessageInfo parameter passed to the Publisher.onRequest
* method.
*
* @param maxBufSize The SIF_MaxBufferSize value from the original SIF_Request
* message. You can obtain this by calling {@link openadk.library.SIFMessageInfo#getMaxBufferSize}
* on the SIFMessageInfo parameter passed to the Publisher.onRequest
* method.
*
* @param fieldRestrictions An optional array of ElementDef constants, obtained or
* reconstructed from the {@link openadk.library.Query#getFieldRestrictions}
* method from the original SIF_Request, that identify the subset of SIF elements
* to include in the data objects written to SIF_Response messages. If this array
* is provided, data objects will only have those elements specified; otherwise
* data objects contain all of their elements.
*
* @exception IllegalArgumentException is thrown if any of the parameter are invalid
*
* @exception ADKException is thrown if an error occurs preparing the output stream
*/
public void open(
Zone zone,
String sifRequestMsgId,
String sifRequestSourceId,
SIFVersion sifVersion,
int maxBufferSize,
ElementDef[] fieldRestrictions )
throws ADKException
{
fZone = zone;
fOut = DataObjectOutputStreamImpl.newInstance();
fOut.initialize( zone, fieldRestrictions, sifRequestSourceId, sifRequestMsgId, sifVersion, maxBufferSize );
}
/**
* Write a SIFDataObject to the output stream
*/
public void write( SIFDataObject sdo )
throws ADKException
{
_checkOpen();
fOut.write( sdo );
}
/**
* Write a SIF_Error to the output stream
* @param error A SIF_Error instance
*/
public void write( SIF_Error error )
throws ADKException
{
_checkOpen();
fOut.setError( error );
}
/**
* Close the stream and send one or more SIF_Response packets to the zone.
*/
public void close()
throws ADKException
{
_checkOpen();
try {
fOut.close();
} catch( IOException ioe ) {
throw new ADKException( "Failed to close SIFResponseSender stream: " + ioe, fZone );
}
fOut.commit();
}
private void _checkOpen() {
if( fOut == null )
throw new IllegalStateException( "SIFResponseSender is not open" );
}
/**
* Allows the starting packet number for SIF_Responses to be set. <p>
*
* By default, the SIFResponseSender class will automatically set the starting
* packet number to 1 and increment the number automatically for each packet.
* However, some agents may need to respond to SIF_Requests with multiple, asynchronous
* responses. In that case, the agent developer is responsible for keeping track of the
* packet numbers that were previously sent and setting the correct starting packet number
* for the next set of SIF_Response packtets.
*
* @see #getSIF_PacketNumber()
* @param packetNumber The SIF_PacketNumber value that should be set for the next SIF_Response
* packet
*
* @exception IllegalStateException thrown if this property is set after objects have
* already been written
*/
public void setSIF_PacketNumber( int packetNumber) {
_checkOpen();
fOut.setSIF_PacketNumber( packetNumber );
}
/**
* Allows the SIF_MorePackets value for SIF_Responses to be set.<p>
*
* By default, the SIFResponseSender class will automatically close out the
* SIF_Response stream when SIFResponseSender is closed by setting the
* SIF_MorePackets value to "No" on the final packet. However, if the SIF_Response
* stream should be kept open, this value will be used on the last SIF_Response
* packet that is generated by the SIFResponseSender class.
*
* @param morePacketsValue
*/
public void setSIF_MorePackets(YesNo morePacketsValue) {
_checkOpen();
fOut.setSIF_MorePackets( morePacketsValue );
}
/**
* Gets the SIF_PacketNumber of the current SIF_Response packet
* @return
*/
public int getSIF_PacketNumber() {
_checkOpen();
return fOut.getSIF_PacketNumber();
}
/**
* Gets the value that will be set on the final SIF_Response packet
* when the SIFResponseSender is closed.
* @return
*/
public YesNo getSIF_MorePackets() {
_checkOpen();
return fOut.getSIF_MorePackets();
}
}