/// <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 SIF_Ack SifSystemControl(SifElement command, ZoneImpl zone) { SIF_SystemControl msg = new SIF_SystemControl(zone.HighestEffectiveZISVersion); SIF_SystemControlData cmd = new SIF_SystemControlData(); cmd.AddChild(command); msg.SIF_SystemControlData = cmd; return zone.Dispatcher.send(msg); }