Difference between revisions of "Asynchronous Web Services"
(→Basic Definition (without asynchronous respond)) |
(→Basic Definition (without asynchronous respond)) |
||
| Line 33: | Line 33: | ||
With above basic pattern, message is processed asynchronously, result is generated (and stored in AsyncResult field). But result is not delivered anywhere. It is up to remote client to ask agility web service (with separate request) and retrieve result. Unfortunately there is no helper to support this pattern. Separate web service must be defined which will work on ImportExportQueueBO and query agility database for particular message status. | With above basic pattern, message is processed asynchronously, result is generated (and stored in AsyncResult field). But result is not delivered anywhere. It is up to remote client to ask agility web service (with separate request) and retrieve result. Unfortunately there is no helper to support this pattern. Separate web service must be defined which will work on ImportExportQueueBO and query agility database for particular message status. | ||
| − | + | Sample definition: [[#Simple_Asynchronous_Web_Service|Simple Asynchronous Web Service Example]]. | |
===Asynchronous Respond Delivery=== | ===Asynchronous Respond Delivery=== | ||
Revision as of 07:24, 26 August 2016
Contents
Introduction
Base principal of asynchronous processing is to store request data (from web service client) in queue and immediately respond with simple acknowledgement. From client point of view acknowledge means that his message is delivered and pending processing. Web service host process (import) received data in separate thread. Advantage of asynchronous processing is that client do not have to wait for time consuming processing (big data volume). Network connection can be easily broken and it is important to keep it as short as possible. Action after processing of message depend on agreement. Possible scenarios:
- Client don’t expect processing results (send ad forget)
- Client will periodically call sever and asks for results (until processed)
- Client expects that server will always send back result.
- Client expects that server will send back result only when processing fail.
Currently supported are only first two patterns.
Asynchronous Web Service Definition
Basic Definition (without asynchronous respond)
To enable Asynchronous Import setup Storage Type as "AsyncWebService".
Asynchronous import definition consists of regular import and additional section <ack>…<ack>. Inside ack element defined is additional separate import definition (full featured) which is responsible for generating acknowledgement. Import form ack section is processed first and import result is responded to client immediately. After respond main import is processed asynchronously (in separate thread). Result is stored in syImpExpQueue.AsyncResult.
With above basic pattern, message is processed asynchronously, result is generated (and stored in AsyncResult field). But result is not delivered anywhere. It is up to remote client to ask agility web service (with separate request) and retrieve result. Unfortunately there is no helper to support this pattern. Separate web service must be defined which will work on ImportExportQueueBO and query agility database for particular message status.
Sample definition: Simple Asynchronous Web Service Example.
Asynchronous Respond Delivery
Delivery help content to be written.
Import option element
Option element allows to exchange data between code and all stages of particular queue item (Import Export Queue Item). In code Options are stored in flat list of variables:
Option element can be defined in mappings section. Option definition is similar like field element. Option element is implemented only for xml file import, and it must be defined inside table element.
<mappings>
<table name="ImportedAssets" xpath="ImportFile/assets/asset">
<option name="WSRemoteMessageID" xpath="//header/MessageID"></option>
<field name="Description">#Import.Field3 + #Option.WSRemoteMessageID</field>
...
</table>
</mappings>
In string expression reference to option is made with #Option prefix: #Option.WSRemoteMessageID. Options can be accessed in result export without declaring any context.
Messages Identification
In asynchronous processing it is important to allow identification of particular message conversation. Our interfaces are defined in generic way, and there is no automatic mechanism to handle messages identification. Each interface must take carte of identification itself. Import Options functionality is developed to help handle this problem.
Queue is extended with two additional string fields: WSMessageID & WSRemoteMessageID. Message Identification fields has corresponding import options: WSMessageID & WSRemoteMessageID. Be aware that both fields represent particular message conversation (not just single queue element or packet send over the network!).
MessageID can be set by client but also by server. For example client can assign message id in request and will expect that any respond will carry same messageid (so client can match which respond belongs to which request). But client can leave this task for server for example client create message without id but expect that server will assign id and acknowledgement contains message id. Client will reuse provided id to periodically ask for processing result. Both patterns are supported.
- WSMessageID
- Assigned automatically in code for each import which is handled by queue (Web Service Import). It is unique GUID generated during creating of item in syImpExpQueue.
- WSRemoteMessageID
- Represent ID assigned by remote client. It is optional and depend on import definition (in most cases declared in ack-import, but it can be defined in regular import too). When in web service import definition, one of options is named WSRemoteMessageID then result of option evaluation is automatically saved to appropriate queue item (syImpExpQueue.WSRemoteMessageID).
<import>
<ack>
<namespaces>
<namespace name="wo" uri="http://www.fastnetasp.com/GeneralImport/WO"></namespace>
<namespace name="soap" uri="http://www.w3.org/2003/05/soap-envelope"></namespace>
</namespaces>
<mappings classname="Control.GeneralInterface.XMLFileImport">
<table name="JobList" xpath="soap:Envelope/soap:Body/wo:UpdateJob/wo:JobList/wo:woJob">
<option name="WSRemoteMessageID" xpath="wo:JobCode"></option>
<table name="JobTask" xpath="wo:woJobTask">
<field name="TaskCode" xpath="wo:TaskCode"></field>
<option name="WSRemoteMessageID">#Option.WSRemoteMessageID + "-" + #Import.TaskCode</option>
<option name="ScopeTest">"I'm in scope"</option>
</table>
</table>
</mappings>
<resultexport>
<contents>
<data type="template-xml"><ack WSMessageID="{#Option.WSMessageID}" WSRemoteMessageID="{#Option.WSRemoteMessageID}">OK</ack></data>
</contents>
</resultexport>
</ack>
<namespaces>
<namespace name="wo" uri="http://www.fastnetasp.com/GeneralImport/WO"></namespace>
<namespace name="soap" uri="http://www.w3.org/2003/05/soap-envelope"></namespace>
</namespaces>
<mappings classname="Control.GeneralInterface.XMLFileImport">
<table name="JobList" xpath="soap:Envelope/soap:Body/wo:UpdateJob/wo:JobList/wo:woJob">
<field name="JobCode" xpath="wo:JobCode"></field>
<field name="Description">#Option.ScopeTest</field>
<field name="JobStatus" xpath="wo:JobStatus"></field>
<field name="JobType" xpath="wo:JobType"></field>
<field name="justwait">System.Threading.Thread.Sleep(20000)</field>
<table name="JobTask" xpath="wo:woJobTask">
<field name="TaskCode" xpath="wo:TaskCode"></field>
<field name="TaskDescription" xpath="wo:Description"></field>
<field name="TaskType" xpath="wo:TaskType"></field>
<field name="TaskStatus" xpath="wo:TaskStatus"></field>
</table>
</table>
</mappings>
<assign classname="Control.GeneralInterface.StandardImportProcessor">
<businessobject classname="DataBO.ProcessMngt.JobBO">
<table name="woJob" sourceTable="JobList">
<identification>
woJob.JobCode = #Import.JobCode
</identification>
<mode>
<update/>
</mode>
<set>
<field name="JobStatusID">Lookup("DataBO.SystemData.JobStatusBO","syJobStatus","Code",#Import.JobStatus,"JobStatusID")</field>
<field name="FullDescription">#Import.Description + "\r\n" + #Option.WSMessageID +"\r\n" + #Option.WSRemoteMessageID</field>
</set>
</table>
</businessobject>
</assign>
<resultexport>
<contents defaulttype="template-xml">
<data>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Body>
<UpdateJobResponse xmlns="http://www.fastnetasp.com/GeneralImport/WO">
<UpdateJobResult>
{:include "jobresults"}
</UpdateJobResult>
</UpdateJobResponse>
</soap:Body>
</soap:Envelope>
</data>
<partial name="jobresults">
<foreach table="JobList" datacontext="mappings" classname="DataBO.ProcessMngt.JobBO">
<data>
<woJobResult WSMessageID="{#Option.WSMessageID}" WSRemoteMessageID="{#Option.WSRemoteMessageID}">
<JobCode>{woJob.JobCode}</JobCode>
<StatusCode>
{:if #Result.IsImported}OK{:else}ERROR{:endif}
</StatusCode>
<ScopeTest>
{#Option.ScopeTest}
</ScopeTest>
<StatusDescription>{#Result.Message}</StatusDescription>
</woJobResult>
</data>
</foreach>
</partial>
</contents>
</resultexport>
</import>
Examples
Web Service (Server Side)
Simple Asynchronous Web Service
In this example client asynchronously request update of particular job. In simplest scenario ack import consists with result generator (result export):
<?xml version="1.0" encoding="utf-8"?>
<import>
<!-- =======================================================
ACKnowledgement hander
============================================================ -->
<ack>
<resultexport>
<contents>
<data type="template-xml">
<ack>OK</ack>
</data>
</contents>
</resultexport>
</ack>
<!-- =======================================================
Standard import processed asynchronously in separate thread
============================================================ -->
<namespaces>
<namespace name="wo" uri="http://www.fastnetasp.com/GeneralImport/WO"/>
<namespace name="soap" uri="http://www.w3.org/2003/05/soap-envelope"/>
</namespaces>
<mappings classname="Control.GeneralInterface.XMLFileImport">
<table name="JobList" xpath="soap:Envelope/soap:Body/wo:UpdateJob/wo:JobList/wo:woJob">
<field name="JobCode" xpath="wo:JobCode"/>
<field name="Description" xpath="wo:Description"/>
<field name="JobStatus" xpath="wo:JobStatus"/>
<field name="JobType" xpath="wo:JobType"/>
<table name="JobTask" xpath="wo:woJobTask">
<field name="TaskCode" xpath="wo:TaskCode"/>
<field name="TaskDescription" xpath="wo:Description"/>
<field name="TaskType" xpath="wo:TaskType"/>
<field name="TaskStatus" xpath="wo:TaskStatus"/>
</table>
</table>
</mappings>
<assign classname="Control.GeneralInterface.StandardImportProcessor">
<businessobject classname="DataBO.ProcessMngt.JobBO">
<table name="woJob" sourceTable="JobList">
<identification>
woJob.JobCode = #Import.JobCode
</identification>
<mode>
<update/>
</mode>
<set>
<field name="JobStatusID">Lookup("DataBO.SystemData.JobStatusBO","syJobStatus","Code",#Import.JobStatus,"JobStatusID")</field>
<field name="FullDescription">#Import.Description</field>
</set>
</table>
</businessobject>
</assign>
<resultexport>
<contents defaulttype="template-xml">
<data>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Body>
<UpdateJobResponse xmlns="http://www.fastnetasp.com/GeneralImport/WO">
<UpdateJobResult>
{:include "jobresults"}
</UpdateJobResult>
</UpdateJobResponse>
</soap:Body>
</soap:Envelope>
</data>
<partial name="jobresults">
<foreach table="JobList" datacontext="mappings" classname="DataBO.ProcessMngt.JobBO">
<data>
<woJobResult>
<JobCode>{woJob.JobCode}</JobCode>
<StatusCode>
{:if #Result.IsImported}OK{:else}ERROR{:endif}
</StatusCode>
<StatusDescription>{#Result.Message}</StatusDescription>
</woJobResult>
</data>
</foreach>
</partial>
</contents>
</resultexport>
</import>
Asynchronous Web Service With Message Identification Handling
<?xml version="1.0" encoding="utf-8"?>
<import>
<ack>
<namespaces>
<namespace name="wo" uri="http://www.fastnetasp.com/GeneralImport/WO"></namespace>
<namespace name="soap" uri="http://www.w3.org/2003/05/soap-envelope"></namespace>
</namespaces>
<mappings classname="Control.GeneralInterface.XMLFileImport">
<table name="JobList" xpath="soap:Envelope/soap:Body/wo:UpdateJob/wo:JobList/wo:woJob">
<option name="WSRemoteMessageID" xpath="wo:JobCode"></option>
<table name="JobTask" xpath="wo:woJobTask">
<field name="TaskCode" xpath="wo:TaskCode"></field>
<option name="WSRemoteMessageID">#Option.WSRemoteMessageID + "-" + #Import.TaskCode</option>
<option name="ScopeTest">"I'm in scope"</option>
</table>
</table>
</mappings>
<resultexport>
<contents>
<data type="template-xml"><ack WSMessageID="{#Option.WSMessageID}" WSRemoteMessageID="{#Option.WSRemoteMessageID}">OK</ack></data>
</contents>
</resultexport>
</ack>
<namespaces>
<namespace name="wo" uri="http://www.fastnetasp.com/GeneralImport/WO"></namespace>
<namespace name="soap" uri="http://www.w3.org/2003/05/soap-envelope"></namespace>
</namespaces>
<mappings classname="Control.GeneralInterface.XMLFileImport">
<table name="JobList" xpath="soap:Envelope/soap:Body/wo:UpdateJob/wo:JobList/wo:woJob">
<field name="JobCode" xpath="wo:JobCode"></field>
<field name="Description">#Option.ScopeTest</field>
<field name="JobStatus" xpath="wo:JobStatus"></field>
<field name="JobType" xpath="wo:JobType"></field>
<field name="justwait">System.Threading.Thread.Sleep(20000)</field>
<table name="JobTask" xpath="wo:woJobTask">
<field name="TaskCode" xpath="wo:TaskCode"></field>
<field name="TaskDescription" xpath="wo:Description"></field>
<field name="TaskType" xpath="wo:TaskType"></field>
<field name="TaskStatus" xpath="wo:TaskStatus"></field>
</table>
</table>
<validation>
<error message="WO not allowed to update">#Import.JobCode='13605'</error>
</validation>
</mappings>
<assign classname="Control.GeneralInterface.StandardImportProcessor">
<businessobject classname="DataBO.ProcessMngt.JobBO">
<table name="woJob" sourceTable="JobList">
<identification>
woJob.JobCode = #Import.JobCode
</identification>
<mode>
<update/>
</mode>
<set>
<field name="JobStatusID">Lookup("DataBO.SystemData.JobStatusBO","syJobStatus","Code",#Import.JobStatus,"JobStatusID")</field>
<field name="FullDescription">#Import.Description + "\r\n" + #Option.WSMessageID +"\r\n" + #Option.WSRemoteMessageID</field>
</set>
</table>
</businessobject>
</assign>
<resultexport>
<contents defaulttype="template-xml">
<variable name="globalVarTest" type="stringexpression">"Hello I'm Global Var"</variable>
<data>
<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
<soap:Body>
<UpdateJobResponse xmlns="http://www.fastnetasp.com/GeneralImport/WO">
<UpdateJobResult>
{:include "jobresults"}
</UpdateJobResult>
</UpdateJobResponse>
</soap:Body>
</soap:Envelope>
</data>
<partial name="jobresults">
<foreach table="JobList" datacontext="mappings" classname="DataBO.ProcessMngt.JobBO">
<data>
<woJobResult WSMessageID="{#Option.WSMessageID}" WSRemoteMessageID="{#Option.WSRemoteMessageID}">
<JobCode>{woJob.JobCode}</JobCode>
<StatusCode>
{:if #Result.IsImported}
OK
{:else}
ERROR
{:endif}
</StatusCode>
<ScopeTest>
{#Option.ScopeTest}
</ScopeTest>
<StatusDescription>{#Result.Message}</StatusDescription>
</woJobResult>
</data>
</foreach>
</partial>
</contents>
</resultexport>
</import>
Remote Client
Request Job Update on Asynchronous WebService (Export)
ex
Receive Job Update Respond (WebService Import)
ex
Receive Job Update Error (WebService Import)
ex



