// // Copyright (c)1998-2011 Pearson Education, Inc. or its affiliate(s). // All rights reserved. // package openadk.library; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import openadk.library.impl.ADKUtils; import openadk.library.infra.*; import org.apache.log4j.Category; /** * A specialization of SIFElement for SIF infrastructure messages such as * SIF_Register, SIF_Request, and SIF_Event.<p> * * SIFMessagePayload provides methods specific to infrastructure messages, such * as retrieving the SIF_Header or its individual fields. When an instance of * this class is constructed, it is done so without its SIF_Header element. The * <code>getHeader</code> method adds a SIF_Header child if one does not exist, * and the class framework takes care of assigning values to the header prior to * sending messages. * <p> * * For consistency the ADK employs the same SIFElement class hierarchy and * conventions for SIF Infrastructure messages as it does for SIF Data Objects. * Some inherited methods of SIFElement, such as setChanged and setEmpty, have * no effect for infrastructure messages. * <p> * * @author Eric Petersen * @version ADK 1.0 */ public abstract class SIFMessagePayload extends SIFElement { /** * The namespace (e.g. "http://www.sifinfo.org/v1.0r1/message", * "http://www.sifinfo.org/infrastructure/1.x", etc) */ protected String fXmlns; /** * The version attribute. SIF 1.1 uses a separate version attribute to indicate * the version of SIF to which a message conforms; SIF 1.0 encodes the version * in the namespace. If SIF 1.1 or later is in use and this value is empty, it * is assumed to be the latest version of the SIF Specification. */ protected String fVersionAttr; /** * Constructor * @param metadata The ElementDef representing this object in the ADK metadata */ public SIFMessagePayload( ElementDef metadata ) { super(metadata); setSIFVersion( ADK.getSIFVersion() ); } /** * Constructor * @param version The SIFVersion to render this message in * @param metadata The ElementDef representing this object in the ADK metadata */ public SIFMessagePayload( SIFVersion version, ElementDef metadata ) { super(metadata); setSIFVersion( version ); } /** * Returns the SIF_Header element. A new SIF_Header is created for this * object if one does not already exist.<p> * @return The infrastructure message header */ public SIF_Header getHeader() { SIF_Header hdr = (SIF_Header)getChild( InfraDTD.SIF_HEADER ); if( hdr == null ) { hdr = new SIF_Header(); addChild(hdr); } return hdr; } /** * Returns the XML namespace of this message * @return The value that was set in the "xmlns" attribute of the message */ public String getXmlns() { return fXmlns; } /** * Sets the XML namespace of this message * @param xmlns The value to set for the "xmlns" attribute. */ public void setXmlns( String xmlns ) { fXmlns = xmlns; } /** * Returns the Version attribute of this SIF_Message.<p> * * The Version attribute was introduced in SIF 1.1, which uses the attribute * to identify the version of SIF to which a message conforms. Prior * versions of SIF encode the version in the namespace. * * @return The value of the Version attribute. If a null value is returned, * the caller should assume "1.1" * * @since SIF 1.1 */ public String getVersionAttribute() { return fVersionAttr; } /** * Sets the Version attribute of this SIF_Message.<p> * * The Version attribute was introduced in SIF 1.1<p> * * @param version The text for the Version attribute * @since SIF 1.1 */ public void setVersionAttribute( String version ) { fVersionAttr = version; } /** * Gets the SIF Version to which this message conforms.<p> * * The SIF Version is determined by first inspecting the Version attribute * of the SIF_Message. If present, that attribute identifies the version of * SIF to which the version conforms. If not present, the namespace is * inspected; if it is in the form "http://www.sifinfo.org/v1.0r1/messages", * it is parsed to obtain the version of SIF. If it is in the form * "http://www.sifinfo.org/infrastructure/1.x" and no Version attribute is * present, the version of SIF is assumed to be "1.1" * * @return A SIFVersion encapsulating the version of SIF to which the * message conforms, or null if the message does not provide a valid * xmlns or Version attribute from which to determine the SIF Version */ public SIFVersion getSIFVersion() { if( fVersionAttr != null ) { // SIF 1.1 or later return SIFVersion.parse( fVersionAttr ); } else if( fXmlns != null ) { return SIFVersion.parseXmlns( fXmlns ); } return null; } /** * Sets the SIF Version to which this SIF_Message conforms.<p> * * If the supplied version if SIF 1.1 or later, the Version attribute is * assigned a value and the xmlns attribute is set to "http://www.sifinfo.org/infrastructure/<i>major</i>.x". * Otherwise, the Version attribute is not set and the xmlns attribute is set * to "http://www.sifinfo.org/v<i>version</i>/messages" * * @param A SIFVersion instance encapsulating the version of the SIF * Specification to which this message conforms */ public void setSIFVersion( SIFVersion ver ) { fXmlns = ver.getXmlns(); if( ver.compareTo( SIFVersion.SIF11 ) >= 0 ){ fVersionAttr = ver.toString(); } else { fVersionAttr = null; } } /** * Gets the SIF_MsgId value from this message's header.<p> * * If the message does not have a SIF_Header element, one is created.<p> * * @return The SIF_MsgId value */ public String getMsgId() { SIF_Header h = getHeader(); return ( h != null ? h.getSIF_MsgId() : null ); } /** * Gets the SIF_SourceId value from this message's header.<p> * * If the message does not have a SIF_Header element, one is created.<p> * * @return The SIF_SourceId value */ public String getSourceId() { SIF_Header h = getHeader(); return ( h != null ? h.getSIF_SourceId() : null ); } /** * Gets the SIF_DestinationId value from this message's header.<p> * * If the message does not have a SIF_Header element, one is created.<p> * * @return The SIF_DestinationId value */ public String getDestinationId() { SIF_Header h = getHeader(); return ( h != null ? h.getSIF_DestinationId() : null ); } /** * Gets the timestamp of this message from the SIF_Timestamp element in the header.<p> * * If the message does not have a SIF_Header element, one is created with * a timestamp equal to the current time.<p> * * @return The message timestamp */ public Calendar getTimestamp() { SIF_Header h = getHeader(); Calendar c = null; if( h != null ) { c = h.getSIF_Timestamp(); } if( c == null ){ c = Calendar.getInstance(); } return c; } /** * Gets the list of SIFContexts associated with this message.<p> * * @return The list of SIFContexts that are associated with this message * @throws ADKException If one of the contexts in the message is not defined * by this agent instance */ public List<SIFContext> getSIFContexts() throws ADKNotSupportedException { List<SIFContext> contexts = new ArrayList<SIFContext>(); SIF_Contexts allContexts = getHeader().getSIF_Contexts(); if( allContexts == null || allContexts.getChildCount() == 0 ){ contexts.add( SIFContext.DEFAULT ); } else { for( SIF_Context context : allContexts.getSIF_Contexts() ){ SIFContext definedContext = SIFContext.isDefined( context.getValue() ); if( definedContext == null ){ throw new ADKNotSupportedException( "SIFContext \"" + context.getValue() + "\" is not supported" ); } contexts.add( definedContext ); } } return contexts; } /** * Create an Immediate SIF_Ack for this message.<p> * @return A new SIF_Ack instance where the SIF_Status/SIF_Code value is * set to a value of "1" and SIF_Ack header values are derived from this * message's header values */ public SIF_Ack ackImmediate() { return ackStatus(1); } /** * Create an Intermediate SIF_Ack for this message.<p> * @return A new SIF_Ack instance where the SIF_Status/SIF_Code value is * set to a value of "2" and SIF_Ack header values are derived from this * message's header values */ public SIF_Ack ackIntermediate() { return ackStatus(2); } /** * Create a Final SIF_Ack for this message.<p> * @return A new SIF_Ack instance where the SIF_Status/SIF_Code value is * set to a value of "3" and SIF_Ack header values are derived from this * message's header values */ public SIF_Ack ackFinal() { return ackStatus(3); } /** * Create a SIF_Ack for this message.<p> * @param code The SIF_Status/SIF_Code value * @return A new SIF_Ack instance where the SIF_Status/SIF_Code value is * set to the specified value and SIF_Ack header values are derived * from this message's header values */ public SIF_Ack ackStatus( int code ) { SIF_Ack ack = new SIF_Ack(); SIF_Status status = new SIF_Status( code ); ack.setSIF_Status(status); ack.setSIF_OriginalMsgId(getMsgId()); ack.setSIF_OriginalSourceId(getSourceId()); SIFVersion msgVersion = getSIFVersion(); if( code == 8 /* Receiver is sleeping */ ) { if( msgVersion.getMajor() == 1 ){ // SIF 1.x used SIF_Data for text SIF_Data d = new SIF_Data(); d.setTextValue("Receiver is sleeping"); status.setSIF_Data( d ); } else { status.setSIF_Desc( "Receiver is sleeping" ); } } ack.message = this; // Ack using the same version of SIF as this message ack.setSIFVersion( msgVersion ); return ack; } /** * Create an error SIF_Ack for this message.<p> * @deprecated Please use the overload of this method that accepts a * SIFErrorCategory as the first parameter * @param category The value of the SIF_Error/SIF_Category element * @param code The value of the SIF_Error/SIF_Code element * @param desc The value of the SIF_Error/SIF_Desc element * @return A new SIF_Ack instance with a SIF_Error element and SIF_Ack * header values derived from this message's header values */ public SIF_Ack ackError( int category, int code, String desc ) { return ackError(category , code, desc, null ); } /** * Create an error SIF_Ack for this message. * @param sifEx The SIFException that is the cause of the error * @return */ public SIF_Ack ackError( SIFException sifEx ){ SIF_Ack ack = new SIF_Ack(); ack.message = this; ack.setSIF_OriginalMsgId(getMsgId()); ack.setSIF_OriginalSourceId(getSourceId()); SIF_Error error = new SIF_Error( sifEx.getSIFErrorCategory(), sifEx.getErrorCode(), sifEx.getErrorDesc(), sifEx.getErrorExtDesc() ); ack.setSIF_Error( error ); // Ack using the same version of SIF as this message ack.setSIFVersion( getSIFVersion() ); return ack; } /** * Create an error SIF_Ack for this message.<p> * @param category The value of the SIF_Error/SIF_Category element * @param code The value of the SIF_Error/SIF_Code element * @param desc The value of the SIF_Error/SIF_Desc element * @return A new SIF_Ack instance with a SIF_Error element and SIF_Ack * header values derived from this message's header values */ public SIF_Ack ackError( SIFErrorCategory category, int code, String desc ) { return ackError(category,code,desc,null); } /** * Create an error SIF_Ack for this message.<p> * @deprecated Please use the overload of this method that accepts a SIFErrorCategory * as the first parameter * @param category The value of the SIF_Error/SIF_Category element * @param code The value of the SIF_Error/SIF_Code element * @param desc The value of the SIF_Error/SIF_Desc element * @param extDesc The value of the SIF_Error/SIF_ExtendedDesc element * @return A new SIF_Ack instance with a SIF_Error element and SIF_Ack * header values derived from this message's header values */ public SIF_Ack ackError( int category, int code, String desc, String extDesc ) { return ackError( SIFErrorCategory.lookup( category ), code, desc, extDesc ); } /** * Create an error SIF_Ack for this message.<p> * @param category The value of the SIF_Error/SIF_Category element * @param code The value of the SIF_Error/SIF_Code element * @param desc The value of the SIF_Error/SIF_Desc element * @param extDesc The value of the SIF_Error/SIF_ExtendedDesc element * @return A new SIF_Ack instance with a SIF_Error element and SIF_Ack * header values derived from this message's header values */ public SIF_Ack ackError( SIFErrorCategory category, int code, String desc, String extDesc ) { SIF_Ack ack = new SIF_Ack(); ack.setSIF_OriginalMsgId(getMsgId()); ack.setSIF_OriginalSourceId(getSourceId()); SIF_Error error = new SIF_Error( category, code, desc == null ? "" : desc, extDesc ); ack.setSIF_Error( error ); // Ack using the same version of SIF as this message ack.setSIFVersion( getSIFVersion() ); return ack; } /** * Utility method called by the class framework to log this SIF_Message * prior to sending it to a zone.<p> * @param log The Log4j Category instance representing the destination Zone */ public void LogSend( Category log ) { if( ( ADK.debug & ADK.DBG_MESSAGING ) != 0 ) log.debug("Send "+fElementDef.name()); if( ( ADK.debug & ADK.DBG_MESSAGING_DETAILED ) != 0 ) { String id = getMsgId(); log.debug(" MsgId: " + ( id == null ? "<none>" : id ) ); } } /** * Utility method called by the class framework to log this SIF_Message * upon receipt from a zone.<p> * @param log The Log4j Category instance representing the source Zone */ public void LogRecv( Category log ) { if( ( ADK.debug & ADK.DBG_MESSAGING ) != 0 ) log.debug("Receive "+fElementDef.name()); if( ( ADK.debug & ADK.DBG_MESSAGING_DETAILED ) != 0 ) { String id = getMsgId(); log.debug(" MsgId: " + ( id == null ? "<none>" : id ) ); } } }