/// <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(); } }
private void SetRequestPolicy( SIF_Request request, IZone zone ) { SIF_Query query = request.SIF_Query; if( query == null ) { // SIF_ExtendedQuery and SIF_Example are not supported by ADK Policy yet return; } // // Object Request Policy // // Determine if there is policy in effect for this Query // String objectName = query.SIF_QueryObject.ObjectName; ObjectRequestPolicy requestPolicy = fPolicyFactory.GetRequestPolicy( zone, objectName ); if( requestPolicy != null ){ // // SIF_Request/SIF_Version policy // String requestVersions = requestPolicy.RequestVersion; if( requestVersions != null ){ if( (Adk.Debug & AdkDebugFlags.Policy ) > 0 ){ zone.Log.Info( "POLICY: Setting SIF_Request/SIF_Version to " + requestVersions ); } // Clear the list of SIF Versions foreach( SIF_Version existingVersion in request.GetSIF_Versions() ){ request.RemoveChild( existingVersion ); } // The version will be a comma-delimited list. Set each of these // as SIF_Version elements, but also try to derive the most logical // version element to set the SIF Message/@Version attribute to // NOTE: Someone could theoretically set versions incorrectly, such // as "1.1,1.5r1". Multiple SIF_Version elements are not supported in // SIF 1.x, but we won't bother with validating incorrect settings. Policy // is power in the configurator's hands to use or abuse. String[] versions = requestVersions.Split( ',' ); String lowestVersion = versions[0]; foreach( String version in versions ){ String ver = version.Trim(); request.AddSIF_Version(new SIF_Version(ver)); if (lowestVersion.CompareTo(ver) > 0) { lowestVersion = ver; } } // Determine how the SIF_Message/@Version should be set to // * If the policy is set to a single version, use it // * If a list, use the lowest // * If *, ignore // * if [major].*, use the lowest version supported if( lowestVersion.Length > 0 ){ SifVersion newMsgVersion = null; if( lowestVersion.EndsWith( "*" ) ){ try { // 2.*, requests go out with a message version of 2.0r1 int major = int.Parse( lowestVersion.Substring( 0, 1 ) ); newMsgVersion = SifVersion.GetEarliest( major ); } catch( FormatException iae ){ zone.Log.Warn( "POLICY: Error parsing ObjectRequestPolicy version '" + requestVersions + "' : " + iae.Message, iae ); } } else { try { newMsgVersion = SifVersion.Parse( lowestVersion ); } catch( FormatException iae ){ zone.Log.Warn( "POLICY: Error parsing ObjectRequestPolicy version '" + requestVersions + "' : " + iae.Message, iae ); } } if( newMsgVersion != null ){ if( (Adk.Debug & AdkDebugFlags.Policy ) > 0 ){ zone.Log.Info( "POLICY: Setting SIF_Messaage/@Version to " + newMsgVersion ); } request.SifVersion = newMsgVersion; } } } // // SIF_DestinationID policy // String requestSourceId = requestPolicy.RequestSourceId ; if( requestSourceId != null ){ if( (Adk.Debug & AdkDebugFlags.Policy) > 0 ){ zone.Log.Info( "POLICY: Setting SIF_Request SIF_DestinationID to " + requestPolicy.RequestSourceId ); } request.SIF_Header.SIF_DestinationId = requestSourceId; } } }