// /// <summary> Throws an AdkMessagingException, optionally logging its message first</summary> // public static void _throw(AdkMessagingException thr, Category log) // { // if ((Adk.Debug & AdkDebugFlags.Exceptions) != 0) // thr.log(log); // throw thr; // } // // /// <summary> Throws an AdkTransportException, optionally logging its message first</summary> // public static void _throw(AdkTransportException thr, Category log) // { // if ((Adk.Debug & AdkDebugFlags.Exceptions) != 0) // thr.log(log); // throw thr; // } /// <summary> Throws a SifException, optionally logging its message first</summary> public static void _throw( SifException thr, ILog log ) { SifException exc = thr; // If exception has a non-success status code and no errors, substitute a // more descriptive exception if ( thr.Ack != null && (!thr.Ack.HasStatusCode( 0 ) && !thr.Ack.HasError()) ) { StringBuilder b = new StringBuilder(); b.Append( "Received non-success status code (" ); SIF_Status s = thr.Ack.SIF_Status; if ( s == null ) { b.Append( "and no SIF_Status element exists" ); } else { b.Append( s.SIF_Code ); } b.Append( ") but no error information" ); exc = new SifException( b.ToString(), thr.Ack, thr.Zone ); } if ( (Adk.Debug & AdkDebugFlags.Exceptions) != 0 ) { exc.Log( log ); } throw exc; }
// /// <summary> Throws an AdkMessagingException, optionally logging its message first</summary> // public static void _throw(AdkMessagingException thr, Category log) // { // if ((Adk.Debug & AdkDebugFlags.Exceptions) != 0) // thr.log(log); // throw thr; // } // // /// <summary> Throws an AdkTransportException, optionally logging its message first</summary> // public static void _throw(AdkTransportException thr, Category log) // { // if ((Adk.Debug & AdkDebugFlags.Exceptions) != 0) // thr.log(log); // throw thr; // } /// <summary> Throws a SifException, optionally logging its message first</summary> public static void _throw(SifException thr, ILog log) { SifException exc = thr; // If exception has a non-success status code and no errors, substitute a // more descriptive exception if (thr.Ack != null && (!thr.Ack.HasStatusCode(0) && !thr.Ack.HasError())) { StringBuilder b = new StringBuilder(); b.Append("Received non-success status code ("); SIF_Status s = thr.Ack.SIF_Status; if (s == null) { b.Append("and no SIF_Status element exists"); } else { b.Append(s.SIF_Code); } b.Append(") but no error information"); exc = new SifException(b.ToString(), thr.Ack, thr.Zone); } if ((Adk.Debug & AdkDebugFlags.Exceptions) != 0) { exc.Log(log); } throw exc; }
/// <summary> /// Attempts to parse attributes out of the source message enough to make a valid /// SIF_Ack with a SIF_Error. This is useful in conditions where the source message cannot /// be parsed by the ADK /// </summary> /// <param name="sourceMessage"> The original message as a string</param> /// <param name="error">The error to place in the SIF_Ack/SIF_Error</param> /// <param name="zone">The zone associated with this message</param> /// <returns></returns> /// <exception cref="AdkMessagingException"></exception> public static SIF_Ack ackError(String sourceMessage, SifException error, ZoneImpl zone) { SifMessageInfo parsed = null; try { StringReader reader = new StringReader(sourceMessage); parsed = SifMessageInfo.Parse(reader, false, zone); reader.Close(); } catch (Exception e) { zone.Log.Error(e, e); } SIF_Ack errorAck = new SIF_Ack(zone.HighestEffectiveZISVersion); if (parsed != null) { // Set SIFVersion, OriginalSourceId, and OriginalMsgId; if (parsed.SifVersion != null) { errorAck.SifVersion = parsed.SifVersion; } errorAck.SIF_OriginalMsgId = parsed.GetAttribute("SIF_MsgId"); errorAck.SIF_OriginalSourceId = parsed.GetAttribute("SIF_SourceId"); } SetRequiredAckValues(errorAck); SIF_Error newErr = new SIF_Error(); newErr.SIF_Category = (int)error.ErrorCategory; newErr.SIF_Code = error.ErrorCode; newErr.SIF_Desc = error.ErrorDesc; newErr.SIF_ExtendedDesc = error.ErrorExtDesc; errorAck.SIF_Error = newErr; return(errorAck); }
/// <summary> /// Attempts to parse attributes out of the source message enough to make a valid /// SIF_Ack with a SIF_Error. This is useful in conditions where the source message cannot /// be parsed by the ADK /// </summary> /// <param name="sourceMessage"> The original message as a string</param> /// <param name="error">The error to place in the SIF_Ack/SIF_Error</param> /// <param name="zone">The zone associated with this message</param> /// <returns></returns> /// <exception cref="AdkMessagingException"></exception> public static SIF_Ack ackError(String sourceMessage, SifException error, ZoneImpl zone) { SifMessageInfo parsed = null; try { StringReader reader = new StringReader(sourceMessage); parsed = SifMessageInfo.Parse(reader, false, zone); reader.Close(); } catch (Exception e) { zone.Log.Error(e, e); } SIF_Ack errorAck = new SIF_Ack(zone.HighestEffectiveZISVersion ); if (parsed != null) { // Set SIFVersion, OriginalSourceId, and OriginalMsgId; if (parsed.SifVersion != null) { errorAck.SifVersion = parsed.SifVersion; } errorAck.SIF_OriginalMsgId = parsed.GetAttribute("SIF_MsgId"); errorAck.SIF_OriginalSourceId = parsed.GetAttribute("SIF_SourceId"); } SetRequiredAckValues(errorAck); SIF_Error newErr = new SIF_Error(); newErr.SIF_Category = (int)error.ErrorCategory; newErr.SIF_Code = error.ErrorCode; newErr.SIF_Desc = error.ErrorDesc; newErr.SIF_ExtendedDesc = error.ErrorExtDesc; errorAck.SIF_Error = newErr; return errorAck; }
private void doBehavior(IZone zone, String reportObjectRefId, IDataObjectOutputStream reportStream) { fWasCalled = true; if ((Behavior & HandlerBehavior.WaitForPulse) != 0) { Console.WriteLine("Signaling..."); fSignalObject.Set(); Console.WriteLine("Waiting..."); fWaitForObject.WaitOne(); Console.WriteLine("Resuming publishing..."); } if ((Behavior & HandlerBehavior.ThrowException) != 0) { AdkException exc = new AdkException("Errors Occurred", zone); throw exc; } if ((Behavior & HandlerBehavior.ThrowADKRetryException) != 0) { AdkException exc = new AdkException("Errors Occurred", zone); exc.Retry = true; throw exc; } if ((Behavior & HandlerBehavior.ThrowSIFRetryException) != 0) { AdkException exc = new SifException(SifErrorCategoryCode.Transport, SifErrorCodes.GENERIC_GENERIC_ERROR_1, "Errors Occurred", zone); exc.Retry = true; throw exc; } if ((Behavior & HandlerBehavior.ThrowNullPointerException) != 0) { throw new NullReferenceException("Bogus!"); } }
/// <summary> Dispatch a message. /// /// </summary> /// <param name="msg">The infrastructure message to dispatch /// </param> public virtual int dispatch(SifMessagePayload msg) { string errTyp = null; int status = 1; try { SifMessageType pload = (SifMessageType)Adk.Dtd.GetElementType(msg.ElementDef.Name); if (pload == SifMessageType.SIF_SystemControl) { IList<SifElement> ch = ((SIF_SystemControl)msg).SIF_SystemControlData.GetChildList(); if (ch != null && ch.Count > 0) { if (ch[0].ElementDef == InfraDTD.SIF_SLEEP) { fZone.ExecSleep(); } else if (ch[0].ElementDef == InfraDTD.SIF_WAKEUP) { fZone.ExecWakeup(); } else if (ch[0].ElementDef == InfraDTD.SIF_PING) { // Notify MessagingListeners... SifMessageInfo msginfo = new SifMessageInfo(msg, fZone); NotifyMessagingListeners_OnMessageProcessed (SifMessageType.SIF_SystemControl, msginfo); if (fZone.IsSleeping(AdkQueueLocation.QUEUE_LOCAL)) { return 8; } return 1; } else { fZone.Log.Warn("Received unknown SIF_SystemControlData: " + ch[0].Tag); throw new SifException (SifErrorCategoryCode.Xml, SifErrorCodes.XML_MISSING_MANDATORY_ELEMENT_6, "SIF_SystemControlData must contain SIF_Ping, SIF_Wakeup, or SIF_Sleep", fZone); } // Notify MessagingListeners... SifMessageInfo msginfo2 = new SifMessageInfo(msg, fZone); NotifyMessagingListeners_OnMessageProcessed (SifMessageType.SIF_SystemControl, msginfo2); } return status; } // If zone is asleep, return status code if (fZone.IsSleeping(AdkQueueLocation.QUEUE_LOCAL)) { return 8; } // Some agents don't want to receive messages - for example, the // SIFSend Adk Example agent. This is very rare but we offer a property // to allow for it if (fZone.Properties.DisableMessageDispatcher) { return status; } switch (pload) { case SifMessageType.SIF_Event: errTyp = "Subscriber.onEvent"; status = dispatchEvent((SIF_Event)msg); break; case SifMessageType.SIF_Request: errTyp = "Publisher.onRequest"; dispatchRequest((SIF_Request)msg); break; case SifMessageType.SIF_Response: errTyp = "QueryResults"; dispatchResponse((SIF_Response)msg); break; default: fZone.Log.Warn ("Agent does not know how to dispatch " + msg.ElementDef.Name + " messages"); throw new SifException (SifErrorCategoryCode.Generic, SifErrorCodes.GENERIC_MESSAGE_NOT_SUPPORTED_2, "Message not supported", msg.ElementDef.Name, fZone); } } catch (LifecycleException) { throw; } catch (SifException se) { // Check if AdkException.setRetry() was called; use transport // error category to force ZIS to resend message if (se.Retry) { se.ErrorCategory = SifErrorCategoryCode.Transport; se.ErrorCode = SifErrorCodes.WIRE_GENERIC_ERROR_1; } logAndRethrow("SIFException in " + errTyp + " message handler for " + msg.ElementDef.Name, se); } catch (AdkZoneNotConnectedException adkznce) { // Received a message while the zone was disconnected. Return a system transport // error so that the message is not removed from the queue SifException sifEx = new SifException( SifErrorCategoryCode.Transport, SifErrorCodes.WIRE_GENERIC_ERROR_1, adkznce.Message, fZone); logAndRethrow("Message received while zone is not connected", sifEx); } catch (AdkException adke) { // Check if ADKException.setRetry() was called; use transport // error category to force ZIS to resent message if (adke.Retry) { logAndThrowRetry(adke.Message, adke); } logAndThrowSIFException("ADKException in " + errTyp + " message handler for " + msg.ElementDef.Name, adke); } catch (Exception uncaught) { logAndThrowSIFException("Uncaught exception in " + errTyp + " message handler for " + msg.ElementDef.Name, uncaught); } return status; }
private void logAndThrowRetry(String shortMessage, Exception exception) { if ( (Adk.Debug & AdkDebugFlags.Exceptions) != 0 ) { fZone.Log.Error( shortMessage, exception ); } SifException exToThrow = new SifException( SifErrorCategoryCode.Transport, SifErrorCodes.WIRE_GENERIC_ERROR_1, shortMessage, exception.StackTrace, fZone ); if ( (Adk.Debug & AdkDebugFlags.Exceptions) != 0 ) { fZone.Log.Error( "Translated to a SIFException that will force a retry", exToThrow ); } throw exToThrow; }
private void logAndThrowSIFException(String shortMessage, Exception exception) { if ( (Adk.Debug & AdkDebugFlags.Exceptions) != 0 ) { fZone.Log.Error( shortMessage, exception ); } SifException exToThrow = new SifException( SifErrorCategoryCode.Generic, SifErrorCodes.GENERIC_GENERIC_ERROR_1, shortMessage, exception.StackTrace, fZone, exception ); if ( (Adk.Debug & AdkDebugFlags.Exceptions) != 0 ) { fZone.Log.Error( "Translated to a SIFException", exToThrow ); } throw exToThrow; }
/** * Sends a SIF_Ack in response to a pulled message * @param sifGetMessageAck The original SIF_Ack from the SIF_GetMessage. This is sometimes null, when * parsing fails * @param pulledMEssage The message delivered inside of the above ack. NOTE that even if parsing fails, * the SIFParser tries to return what it can, and will return this message payload (in getParsed()), * instead of the above container message. * @param ackStatus The status to ack (NOTE: This is ignored if the err property is set) * @param err The error to set in the SIF_Ack */ private void sendPushAck(SIF_Ack sifGetMessageAck, SifMessagePayload pulledMEssage, int ackStatus, SifException err) { try { SIF_Ack ack2 = null; if( fAckAckOnPull && sifGetMessageAck != null){ ack2 = sifGetMessageAck.ackStatus( ackStatus ); } else { ack2 = pulledMEssage.ackStatus(ackStatus); } // If an error occurred processing the message, return // the details in the SIF_Ack if( err != null ) { fZone.Log.Debug( "Handling exception by creating a SIF_Error", err ); SIF_Error newErr = new SIF_Error(); newErr.SIF_Category = (int)err.ErrorCategory; newErr.SIF_Code = err.ErrorCode; newErr.SIF_Desc = err.ErrorDesc; newErr.SIF_ExtendedDesc = err.ErrorExtDesc; ack2.SIF_Error = newErr; // Get rid of the <SIF_Status> SIF_Status status = ack2.SIF_Status; if( status != null ) { ack2.RemoveChild( status ); } } // Send the ack send(ack2); } catch( Exception ackEx ) { fZone.Log.Debug( "Failed to send acknowledgement to pulled message: " + ackEx, ackEx ); } }
/// <summary> Poll the ZIS for messages pending in the agent's queue. /// /// This method is typically called by a ProtocolHandler thread to perform a /// periodic pull when the agent is running in pull mode. It may also be /// called by the framework to force a pull if the framework requires an /// immediate response to a message it has sent to the ZIS, such as a /// request for SIF_ZoneStatus. /// /// /// If a message is retrieved from the ZIS, it is dispatched through the /// Adk's usual message routing mechanism just as pushed messages are. Thus, /// there is no difference between push and pull mode once a message has /// been obtained from the ZIS. Because message routing is asynchronous (i.e. /// the MessageDispatcher will forward the message to the appropriate /// framework or agent code), this method does not return a value. If an /// error is returned in the SIF_Ack, the agent's FaultListener will be /// notified if one is registered. If an exception occurs, it is thrown to /// the caller. /// /// /// Each time this method is invoked it repeatedly sends SIF_GetMessage to /// the ZIS until no more messages are available, effectively emptying out /// the agent's queue. /// /// </summary> /// <returns> zero if no messages were waiting in the agent's queue; 1 if a /// message was pulled from the agent's queue; -1 if the zone is sleeping /// </returns> public virtual int Pull() { // Wait for fPullCanStart to be set to true by the ZoneImpl class once // the zone is connected if (fPullCanStart != null) { lock (fPullCanStart) { try { Monitor.Wait(fPullCanStart); } catch { // Do Nothing } } } while (true) { if ((Adk.Debug & AdkDebugFlags.Messaging_Pull) != 0) { fZone.Log.Debug("Polling for next message..."); } // Send a SIF_GetMessage, get a SIF_Ack SIF_SystemControl sys = new SIF_SystemControl(); SIF_SystemControlData cmd = new SIF_SystemControlData(); cmd.AddChild(new SIF_GetMessage()); sys.SIF_SystemControlData = cmd; SIF_Ack ack = null; try { ack = send(sys, true); } catch (PullMessageParseException pmpe) { // Unable to parse the pulled message. Try sending the proper // Error SIF_Ack to remove the message from the queue if (pmpe.fSourceMessage != null) { fZone.Log.Debug("Handling exception by creating a SIF_Error", pmpe.fParseException); // Try parsing out the SIF_OriginalMsgId so that we can remove the message // from the queue. // Ack either the SIF_Ack or the internal, embedded message, based on our setting int startIndex = fAckAckOnPull ? 0 : 10; int messageStart = pmpe.fSourceMessage.IndexOf("<SIF_Message", startIndex); SifException sourceException = null; if (pmpe.fParseException is SifException) { sourceException = (SifException)pmpe.fParseException; } else { sourceException = new SifException( SifErrorCategoryCode.Xml, SifErrorCodes.XML_GENERIC_ERROR_1, "Unable to parse pulled SIF_Message", pmpe.fParseException.Message, fZone, pmpe.fParseException ); } SIF_Ack errorAck = SIFPrimitives.ackError(pmpe.fSourceMessage.Substring(messageStart), sourceException, fZone); errorAck.SifVersion = sys.SifVersion; send( errorAck ); continue; } } // // Process the response. If status code 9 (no message), no // action is taken. If status code 0 (success), the content of // the SIF_Status / SIF_Data element is parsed and dispatched. // If an error is reported in the ack, the agent's fault // handler is called with a SifException describing the error // if a fault handler has been registered. // if (ack.HasStatusCode(SifStatusCodes.NO_MESSAGES_9)) { if ((Adk.Debug & AdkDebugFlags.Messaging_Pull ) != 0) fZone.Log.Debug( "No messages waiting in agent queue" ); return 0; } if(ack.HasError()) { SifException se = new SifException(ack, fZone); fZone.Log.Debug("Unable to pull the next message from the queue: " + se.ToString()); AdkUtils._throw(se, fZone.Log); } if (ack.HasStatusCode(SifStatusCodes.SUCCESS_0)) { AdkException parseEx = null; SifMessagePayload payload = getPullMessagePayload(ack); if ((Adk.Debug & ( AdkDebugFlags.Messaging | AdkDebugFlags.Messaging_Pull ) ) != 0) { fZone.Log.Debug("Pulled a " + payload.ElementDef.Tag(payload.SifVersion) + " message (SIF " + payload.SifVersion + ")"); } #region Notify MessagingListeners... bool cancelled = false; ICollection<IMessagingListener> msgList = GetMessagingListeners(fZone); if (msgList.Count > 0) { StringWriter tmp = new StringWriter(); SifWriter sifwriter = new SifWriter(tmp); sifwriter.Write(payload); sifwriter.Flush(); tmp.Flush(); StringBuilder xml = new StringBuilder(); xml.Append(tmp.ToString()); // Determine message type before parsing foreach (IMessagingListener listener in msgList) { try { SifMessageType pload = (SifMessageType) Adk.Dtd.GetElementType(payload.ElementDef.Name); MessagingReturnCode code = listener.OnMessageReceived(pload, xml); switch (code) { case MessagingReturnCode.Discard: cancelled = true; break; case MessagingReturnCode.Reparse: { try { // Reparse the XML into a new message payload = (SifMessagePayload) fParser.Parse(xml.ToString(), fZone); } catch (IOException ioe) { parseEx = new AdkException ( "Failed to reparse message that was modified by MessagingListener: " + ioe, fZone); } } break; } } catch (AdkException adke) { parseEx = adke; } } } #endregion if (fQueue != null) { // TODO: put message on agent local queue } else { if (parseEx != null) { throw parseEx; } int ackStatus = SifStatusCodes.IMMEDIATE_ACK_1; SifException err = null; bool acknowledge = true; try { // Dispatch the message if (!cancelled) { ackStatus = dispatch(payload); } } catch (LifecycleException) { throw; } catch (SifException se) { err = se; } catch (AdkException adke) { // TODO: This needs to generate proper category/code based on payload if (adke.HasSifExceptions()) { // Return the first exception err = adke.SIFExceptions[0]; } else { // Build a SifException to describe this AdkException err = new SifException (SifErrorCategoryCode.Generic, SifErrorCodes.GENERIC_GENERIC_ERROR_1, adke.Message, fZone); } } catch (Exception thr) { // Uncaught exception (probably an Adk internal error) string txt = "An unexpected error occurred while processing a pulled message: " + thr; fZone.Log.Debug(txt); // Build a SifException to describe this Throwable err = new SifException (SifErrorCategoryCode.System, SifErrorCodes.SYS_GENERIC_ERROR_1, thr.Message, thr.ToString(), fZone, thr); fZone.Log.Debug(err); } if (acknowledge) { sendPushAck(ack, payload, ackStatus, err); } else return 1; } } else { // We only get to here if there is no error and no success code if (ack.HasStatusCode(SifStatusCodes.SLEEPING_8)) { // Zone is sleeping return -1; } else { // Unknown condition AdkUtils._throw(new SifException(ack, fZone), fZone.Log ); } } } }
private void sendErrorResponse(SIF_Request req, SifException se, SifVersion renderAsVer, int maxBufSize) { DataObjectOutputStreamImpl outStream = DataObjectOutputStreamImpl.NewInstance(); outStream.Initialize(fZone, (IElementDef[])null, req.SourceId, req.MsgId, renderAsVer, maxBufSize); SIF_Error err = new SIF_Error( (int)se.ErrorCategory, se.ErrorCode, se.ErrorDesc); err.SIF_ExtendedDesc = se.ErrorExtDesc; outStream.SetError(err); try { outStream.Close(); } catch (Exception ignored) { fZone.Log.Warn("Ignoring exception in out.close()", ignored); } try { outStream.Commit(); } catch (Exception ignored) { fZone.Log.Warn("Ignoring exception in out.commit()", ignored); } }
/// <summary> Dispatch a SIF_Request. /// /// <b>When ALQ Disabled:</b> The SIF_Request is immediately dispatched to /// the Publisher of the associated topic. Only after the Publisher has /// returned a result does this method return, causing the SIF_Request to /// be acknowledged. The result data returned by the Publisher is handed to /// the zone's ResponseDelivery thread, which sends SIF_Response messages to /// the ZIS until all of the result data has been sent, potentially with /// multiple SIF_Response packets. Note without the ALQ, there is the /// potential for the agent to terminate before all data has been sent, /// causing some results to be lost. In this case the SIF_Request will have /// never been ack'd and will be processed again the next time the agent /// is started. /// /// /// <b>When ALQ Enabled:</b> The SIF_Request is placed in the ALQ where it /// will be consumed by the zone's ResponseDelivery thread at a later time. /// This method returns immediately, causing the SIF_Request to be /// acknowledged. The ResponseDelivery handles dispatching the request to /// the Publisher of the associated topic, and also handles returning /// SIF_Response packets to the ZIS. With the ALQ, the processing of the /// SIF_Request and the returning of all SIF_Response data is guaranteed /// because the original SIF_Request will not be removed from the ALQ until /// both of these activities have completed successfully (even over multiple /// agent sessions). /// /// /// Note that any error that occurs during a SIF_Request should result in a /// successful SIF_Ack (because the SIF_Request was received successfully), /// and a single SIF_Response with a SIF_Error payload. The SIF Compliance /// harness checks for this. /// /// /// </summary> /// <param name="req">The SIF_Request to process /// </param> private void dispatchRequest(SIF_Request req) { SifVersion renderAsVer = null; SIF_Query q = null; SIF_QueryObject qo = null; IElementDef typ = null; int maxBufSize = 0; bool rethrow = false; try { // block thread until Zone.query() has completed in case it is in the // midst of a SIF_Request and the destination of that request is this // agent (i.e. a request of self). This is done to ensure that we don't // receive the SIF_Request from the zone before the ADK and agent have // finished issuing it in Zone.query() fZone.WaitForRequestsToComplete(); // // Check SIF_Version. If the version is not supported by the Adk, // fail the SIF_Request with an error SIF_Ack. If the version is // supported, continue on; the agent may not support this version, // but that will be determined later and will result in a SIF_Response // with a SIF_Error payload. // SIF_Version[] versions = req.GetSIF_Versions(); if (versions == null || versions.Length == 0) { rethrow = true; throw new SifException ( SifErrorCategoryCode.Xml, SifErrorCodes.XML_MISSING_MANDATORY_ELEMENT_6, "SIF_Request/SIF_Version is a mandatory element", fZone); } // SIF_Version specifies the version of SIF that will be used to render // the SIF_Responses // TODO: Add support for multiple SIF_Request versions renderAsVer = SifVersion.Parse(versions[0].Value); if (!Adk.IsSIFVersionSupported(renderAsVer)) { rethrow = true; throw new SifException ( SifErrorCategoryCode.RequestResponse, SifErrorCodes.REQRSP_UNSUPPORTED_SIFVERSION_7, "SIF_Version " + renderAsVer + " is not supported by this agent", fZone); } // Check max buffer size int? maximumBufferSize = req.SIF_MaxBufferSize; if (!maximumBufferSize.HasValue ) { rethrow = true; throw new SifException ( SifErrorCategoryCode.Xml, SifErrorCodes.XML_MISSING_MANDATORY_ELEMENT_6, "SIF_Request/SIF_MaxBufferSize is a mandatory element", fZone); } maxBufSize = maximumBufferSize.Value; if (maxBufSize < 4096 || maxBufSize > Int32.MaxValue) { throw new SifException ( SifErrorCategoryCode.RequestResponse, SifErrorCodes.REQRSP_UNSUPPORTED_MAXBUFFERSIZE_8, "Invalid SIF_MaxBufferSize value (" + maxBufSize + ")", "Acceptable range is 4096 to " + Int32.MaxValue, fZone); } // Check to see if the Context is supported try { IList<SifContext> contexts = req.SifContexts; } catch (AdkNotSupportedException contextNotSupported) { throw new SifException( SifErrorCategoryCode.Generic, SifErrorCodes.GENERIC_CONTEXT_NOT_SUPPORTED_4, contextNotSupported.Message, fZone); } // Lookup the SIF_QueryObject q = req.SIF_Query; if (q == null) { // If it's a SIF_ExtendedQuery or SIF_Example, throw the appropriate error if (req.SIF_ExtendedQuery != null) { throw new SifException( SifErrorCategoryCode.RequestResponse, SifErrorCodes.REQRSP_NO_SUPPORT_FOR_SIF_EXT_QUERY, "SIF_ExtendedQuery is not supported", fZone); } else { throw new SifException ( SifErrorCategoryCode.Xml, SifErrorCodes.XML_MISSING_MANDATORY_ELEMENT_6, "SIF_Request/SIF_Query is a mandatory element", fZone); } } qo = q.SIF_QueryObject; if (qo == null) { rethrow = true; throw new SifException ( SifErrorCategoryCode.Xml, SifErrorCodes.XML_MISSING_MANDATORY_ELEMENT_6, "SIF_Query/SIF_QueryObject is a mandatory element", fZone); } // Lookup the ElementDef for the requested object type typ = Adk.Dtd.LookupElementDef(qo.ObjectName); if (typ == null) { throw new SifException ( SifErrorCategoryCode.RequestResponse, SifErrorCodes.REQRSP_INVALID_OBJ_3, "Agent does not support this object type: " + qo.ObjectName, fZone); } } catch (SifException se) { if (!rethrow) { sendErrorResponse(req, se, renderAsVer, maxBufSize); } // rethrow all errors at this point throw se; // // Capture the SifException so it can be written to the output stream // // and thus returned as the payload of the SIF_Response message later // // in this function. // error = se; // fZone.Log.Error("Error in dispatchRequest that will be put into the SIF_Response", se); } // For now, SIFContext is not repeatable in SIF Requests SifContext requestContext = req.SifContexts[0]; Object target = null; // // Lookup the Publisher for this object type using Topics, // but only if the context is the Default context // if (typ != null && SifContext.DEFAULT.Equals(requestContext)) { ITopic topic = null; topic = fZone.Agent.TopicFactory.LookupInstance(typ, requestContext); if (topic != null) { target = topic.GetPublisher(); } } if (target == null) { target = fZone.GetPublisher(requestContext, typ); if (target == null) { // // No Publisher message handler found. Try calling the Undeliverable- // MessageHandler for the zone or agent. If none is registered, // return an error SIF_Ack indicating the object type is not // supported. // Boolean handled = false; IUndeliverableMessageHandler errHandler = fZone.ErrorHandler; if (errHandler != null) { SifMessageInfo msginfo = new SifMessageInfo(req, fZone); handled = errHandler.OnDispatchError(req, fZone, msginfo); // Notify MessagingListeners... foreach (IMessagingListener ml in GetMessagingListeners(fZone)) { ml.OnMessageProcessed(SifMessageType.SIF_Request, msginfo); } } if (!handled) { fZone.Log.Warn("Received a SIF_Request for " + qo.ObjectName + " (MsgId=" + req.MsgId + "), but no Publisher object is registered to handle it"); SifException sifEx = new SifException( SifErrorCategoryCode.RequestResponse, SifErrorCodes.REQRSP_INVALID_OBJ_3, "Agent does not support this object type", qo.ObjectName, fZone); sendErrorResponse(req, sifEx, renderAsVer, maxBufSize); throw sifEx; } else { #if PROFILED ( BuildOptions.PROFILED ) ProfilerUtils.profileStop(); #endif return; } } } //bool success; DataObjectOutputStreamImpl outStream = null; SifMessageInfo msgInfo = new SifMessageInfo(req, fZone); Query query = null; try { // Convert SIF_Request/SIF_Query into a Query object if (q != null) { query = new Query(q); } msgInfo.SIFRequestObjectType = typ; } catch (Exception thr) { fZone.Log.Debug(thr.ToString()); SifException sifEx = new SifException (SifErrorCategoryCode.Xml, SifErrorCodes.XML_MALFORMED_2, "Could not parse SIF_Query element", thr.Message, fZone, thr); sendErrorResponse(req, sifEx, renderAsVer, maxBufSize); throw sifEx; } try { outStream = DataObjectOutputStreamImpl.NewInstance(); outStream.Initialize ( fZone, query, req.SourceId, req.MsgId, renderAsVer, maxBufSize ); // Call the agent-supplied Publisher, or if we have an error, write // that error to the output stream instead ((IPublisher)target).OnRequest(outStream, query, fZone, msgInfo); // Notify MessagingListeners... NotifyMessagingListeners_OnMessageProcessed (SifMessageType.SIF_Request, msgInfo); } catch (SifException se) { // For a SIF_Request, a SifException (other than a Transport Error) // does not mean to return an error ack but instead to return a // valid SIF_Response with a SIF_Error payload (see the SIF // Specification). Transport Errors must be returned to the ZIS so // that the message will be retried later. // if (se.Retry || se.ErrorCategory == SifErrorCategoryCode.Transport) { //success = false; //retry was requested, so we have to tell the output stream to not send an empty response outStream.DeferResponse(); throw; } outStream.SetError(se.Error); } catch (AdkException adke) { // If retry requested, throw a Transport Error back to the ZIS // instead of returning a SIF_Error in the SIF_Response payload if (adke.Retry) { //success = false; //retry was requested, so we have to tell the output stream to not send an empty response outStream.DeferResponse(); throw; } // Return SIF_Error payload in SIF_Response SIF_Error err = new SIF_Error(); err.SIF_Category = (int)SifErrorCategoryCode.Generic; err.SIF_Code = SifErrorCodes.GENERIC_GENERIC_ERROR_1; err.SIF_Desc = adke.Message; outStream.SetError(err); } catch (Exception thr) { SIF_Error err = new SIF_Error(); err.SIF_Category = (int)SifErrorCategoryCode.Generic; err.SIF_Code = SifErrorCodes.GENERIC_GENERIC_ERROR_1; err.SIF_Desc = "Agent could not process the SIF_Request at this time"; err.SIF_ExtendedDesc = "Exception in " + "Publisher.onRequest" + " message handler: " + thr.ToString(); outStream.SetError(err); } finally { try { outStream.Close(); } catch { // Do Nothing } try { outStream.Commit(); } catch { /* Do Nothing */ } #if PROFILED ProfilerUtils.profileStop(); #endif outStream.Dispose(); } }
public void ProcessRequest(AdkHttpRequestContext context) { if ((Adk.Debug & AdkDebugFlags.Messaging) != 0) { Zone.Log.Debug ("Received push message from " + context.Request.RemoteAddress + " (" + context.Request.Url.Scheme + ")"); } SIF_Ack ack = null; SifMessagePayload parsed = null; //Check request length and type if (!SifIOFormatter.IsValidContentLength(context.Request.ContentLength)) { throw new AdkHttpException (AdkHttpStatusCode.ClientError_400_Bad_Request, "Content length Must be greater than zero"); } if (!SifIOFormatter.IsValidContentMediaType(context.Request.ContentType)) { throw new AdkHttpException(AdkHttpStatusCode.ClientError_415_Unsupported_Media_Type, "Content header does not support the specified media type: " + context.Request.ContentType); } // Read raw content Stream requestStream = context.Request.GetRequestStream(); StringBuilder requestXml = null; // If we need to convert the request stream to a string for either logging or messaging, do so if ((Adk.Debug & AdkDebugFlags.Message_Content) != 0) { requestXml = ConvertRequestToString(requestStream); Zone.Log.Debug ("Received " + context.Request.ContentLength + " bytes:\r\n" + requestXml.ToString()); } TextReader requestReader = new StreamReader(requestStream, SifIOFormatter.ENCODING); Exception parseEx = null; bool reparse = false; bool cancelled = false; int reparsed = 0; do { try { parseEx = null; // Parse content parsed = (SifMessagePayload)CreateParser().Parse(requestReader, Zone); reparse = false; parsed.LogRecv(Zone.Log); } catch (AdkParsingException adke) { parseEx = adke; } catch (Exception ex) { parseEx = ex; } // // Notify listeners... // // If we're asked to reparse the message, do so but do not notify // listeners the second time around. // if (reparsed == 0) { ICollection <IMessagingListener> msgList = MessageDispatcher.GetMessagingListeners(Zone); if (msgList.Count > 0) { // Convert the stream to a string builder if (requestXml == null) { requestXml = ConvertRequestToString(requestStream); } // Determine message type before parsing foreach (IMessagingListener listener in msgList) { try { SifMessageType pload = Adk.Dtd.GetElementType(parsed.ElementDef.Name); MessagingReturnCode code = listener.OnMessageReceived(pload, requestXml); switch (code) { case MessagingReturnCode.Discard: cancelled = true; break; case MessagingReturnCode.Reparse: requestReader = new StringReader(requestXml.ToString()); reparse = true; break; } } catch (AdkException adke) { parseEx = adke; } } } } if (cancelled) { return; } reparsed++; }while (reparse); if (parseEx != null) { // TODO: Handle the case where SIF_OriginalSourceId and SIF_OriginalMsgId // are not available because parsing failed. See SIFInfra // Resolution #157. if (parseEx is SifException && parsed != null) { // Specific SIF error already provided to us by SIFParser ack = parsed.AckError((SifException)parseEx); } else { String errorMessage = null; if (parseEx is AdkException) { errorMessage = parseEx.Message; } else { // Unchecked Throwable errorMessage = "Could not parse message"; } if (parsed == null) { SifException sifError = null; if (parseEx is SifException) { sifError = (SifException)parseEx; } else { sifError = new SifException( SifErrorCategoryCode.Xml, SifErrorCodes.XML_GENERIC_ERROR_1, "Could not parse message", parseEx.ToString(), this.Zone, parseEx); } if (requestXml == null) { requestXml = ConvertRequestToString(requestStream); } ack = SIFPrimitives.ackError( requestXml.ToString( ), sifError, this.Zone); } else { ack = parsed.AckError( SifErrorCategoryCode.Generic, SifErrorCodes.GENERIC_GENERIC_ERROR_1, errorMessage, parseEx.ToString()); } } if ((Adk.Debug & AdkDebugFlags.Messaging) != 0) { Zone.Log.Warn ("Failed to parse push message from zone \"" + Zone + "\": " + parseEx); } if (ack != null) { // Ack messages in the same version of SIF as the original message if (parsed != null) { ack.SifVersion = parsed.SifVersion; } AckPush(ack, context.Response); } else { // If we couldn't build a SIF_Ack, returning an HTTP 500 is // probably the best we can do to let the server know that // we didn't get the message. Note this should cause the ZIS // to resend the message, which could result in a deadlock // condition. The administrator would need to manually remove // the offending message from the agent's queue. if ((Adk.Debug & AdkDebugFlags.Messaging) != 0) { Zone.Log.Debug ("Could not generate SIF_Ack for failed push message (returning HTTP/1.1 500)"); } throw new AdkHttpException (AdkHttpStatusCode.ServerError_500_Internal_Server_Error, "Could not generate SIF_Ack for failed push message (returning HTTP/1.1 500)"); } return; } // Check SourceId to see if it matches this agent's SourceId String destId = parsed.DestinationId; if (destId != null && !destId.Equals(Zone.Agent.Id)) { Zone.Log.Warn ("Received push message for DestinationId \"" + destId + "\", but agent is registered as \"" + Zone.Agent.Id + "\""); ack = parsed.AckError ( SifErrorCategoryCode.Transport, SifErrorCodes.WIRE_GENERIC_ERROR_1, "Message not intended for this agent (SourceId of agent does not match DestinationId of message)", "Message intended for \"" + destId + "\" but this agent is registered as \"" + Zone.Agent.Id + "\""); AckPush(ack, context.Response); return; } // Convert content to SIF message object and dispatch it ack = ProcessPush(parsed); // Send SIF_Ack reply AckPush(ack, context.Response); }