Instructor: Nelson Dias
Github: https://github.com/nelsonandredias/wso2_course
- Pull the code and save it to WSO2 workspace (using WSO2 IS v8.0.0)
mkdir ~/WSO2
cd ~/WSO2
git clone https://github.com/wwangsa/learnWSO2.git- Open Integration Studio
- Choose WSO2 workspace above
- Once Integration Studio is opened, go to File > "Open Projects from File System"
- Select the import source directory to the ~/WSO2
- Select All Folder, click on "Close newly imported projects upon completion" then click finish.
- When doing google search, use the following keywords. Most of the time, google search will point to old article so specifying version would help narrow down the latest result. WSO2 Enterprise Integrator (EI) was introduced on v6.
- ESB: WSO2 EI ESB {version}
- Data service server: WSO2 EI DSS {version}
- When dragging and drop mediators, sometimes the mediators keep dragging the previous mediator. This is the IDE bug. You have to click the new mediator until it got highlighted (may have to click multiple times), then you drag it.
- To stop the console run, there is a maximize icon on top right of the console window. When it's click, you will see the stop button
- On WSO2 integration studio, to deselect project on Project Explorer, use ctrl+shift+click on the folder that is currently selected. If a folder is selected in the Project Explorer window, it will create new integration project underneath that folder.
- To learn more different types of core mediators and what it could do, here is the link to the v 7.2.0 with Micro Integrator.
- This 6.4.0 documentation has core mediators' description summarized as a table
-
Create the project Zero (Project folder: ProjectZero)
- When creating new integration project, use the "New Integration Project". This will create a folder that contains with ESBConfigs and CompositeExporter folders undeneath it.
- To add REST API Project, click on the ESBConfigs folder > New> REST API > Create A New API Artifact
Create an endpoint where name is passed as path param and it will return a greeting message "Welcome {name}".
Call GET greetings/{name}
curl -v GET "http://localhost:8290/greetings/Nelson" -w "\n"
Note on Mediators:
- API: Where the it defines the part of url. In the ex above, ie. greetings
- Resource: Where the endpoints path are defined. In the ex above, ie. name
- Log: To generate message in the log file
- Property: It is used to manipulate messages. A formula can be used to concatenate the values from query strings or path params
- Payload Factory: Transforms or replaces the contents of a message in other words set how the output looks like based on message created by property
- Inflow and Outflow Mediators: inflow refers to request sequence while outflow refers to response sequence. Each sequence has childs in it such as log, property, payload factory and so on.
- To run the project, right click on the "ProjectZeroCompositeExporter" and "Export Projects Artifact and Run"
-
Intro
curl -v GET "http://localhost:8290/" -w "\n"
When no resource specified, the curl will return 404 while the log mediator will report "main sequence executed for call to non-existent". This message comes can be customizedfrom {IntegrationStudio_Home}/runtime/microesb/repository/deployment/server/synapse-configs/default/sequences/main.xml and add the following underneath the first custom log
<log descripton="LOG CUSTOM MAIN SEQUENCE" level="custom"> <property name="LOG CUSTOM DEFAULT MESSAGE" value="this messages is part of the default main sequence"/> </log>
With the changes above, when we run the curl without resource specified, we will get 405 Methods not allowed when doing curl. While the logmediator will report the xml message above.
-
REST APIs - Default API Resource (Project folder: ECommerceAPI)
Using Log mediator to generate log message.
# GET orders curl -v GET "http://localhost:8290/orders" -w "\n"
# On Micro Integrator Server Log, it generates the following line when the curl is executed 2022-02-09 23:36:51,656] INFO {LogMediator} - {api:EcommerceAPI} LOG MESSAGE = this is the default resource -
REST APIs - URI Template
Resource's URL Style: URI_TEMPLATE
Allowing path on the resource url
Message flows:
- On the new API Resource, define the URI_TEMPLATE so we can use the path params (
$ctx:uri.var.currentMonth) and query strings ($ctx:query.param.mindayand$ctx:query.param.maxday). - Log Message is used to log and capture the path params and query strings then hold them into variables.
- Set Payload is used to define the message output based on variables passed onto the output
- Loop Back mediator is used to move the message from inflow (request path) to outflow (response path) sequence. All the configuration included in the in sequence that appears after the Loopback mediator is skipped.
- Respond mediator is used to send back the result back to the client
Call
GET orders/month/{currentMonth}#Before adding the query strings in the log message curl -v GET "http://localhost:8290/orders/month/January" -w "\n"
Result
GET orders/month/{currentMonth}{ "month":January }Call
GET orders/month/{currentMonth}?*#After adding the query strings in the log message curl -v GET "http://localhost:8290/orders/month/January?minday=1&maxday=20" -w "\n"
Result
GET orders/month/{currentMonth}?*{ "month":January, "minday":1, "max":20, "totalOrders": 200 } - On the new API Resource, define the URI_TEMPLATE so we can use the path params (
-
REST APIs - URL Mapping
Resource's URL style - URI_MAPPING
Request that match the mapping pattern will be processed. There are 3 kinds of url mapping:
- Extension mapping. Ex:
/.jsp - Exact mapping. Ex:
/test - Path mapping. Ex:
/test/*
Be careful when constructing JSON. Missing double quote will compiled ok but would not show result
Call
GET orders/list#After adding the query strings in the log message curl -v GET "http://localhost:8290/orders/list" -w "\n"
Result
GET orders/month/{currentMonth}?*{ "orders":{ "order":{ "country":"Portugal", "total":394 }, "order":{ "country":"Spain", "total":212 }, "order":{ "country":"Brazil", "total":690 } } } - Extension mapping. Ex:
-
REST APIs - Swagger files (Project folder: SwaggerFile)
- After the project is created and project artifacts exported, the Swagger Editor tab will shows up next to Design and Source tabs for the xml file you edit. This is due to built in Swagger processor in WSO2.
- The processors setting can be found in {Integration_Home}/runtime/microesb/conf/carbon.xml.
- To access the project's swagger, go to either urls:
- We can download the yaml file and upload it to the Postman so we don't have to set it up in Postman manually. One thing to note that Postman will use https url.
Below is to test manually using curl Call
GET api/pets#After adding the query strings in the log message curl -v GET "http://localhost:8290/api/pets" -w "\n"
Result
GET api/pets{ "pet":{ "name":"Kuky", "animal":"Dog" }, "pet":{ "name":"Kuky", "animal":"Cat" }, "pet":{ "name":"Star", "animal":"Cat" }, "pet":{ "name":"Gems", "animal":"Mouse" } }Call
GET api/pets/{name}#After adding the query strings in the log message curl -v GET "http://localhost:8290/api/pets/Kuky" -w "\n"
Result
GET api/pets{name}{ "pet":{ "name":"Kuky", "animal":"Dog" }, "pet":{ "name":"Kuky", "animal":"Cat" } } -
Proxy services (Project folder: ProxyService)
The instructor use a SOAP service for the Proxy services example. The proxy services used to perform transformation and introduce functionality without changing the existing service. He provided 2 files:
- Java based SOAP service. The webservice can be accessed at http://localhost:9000/services/SimpleStockQuoteService
#Prerequisites. The instructor uses Java 1.8 or Java 8 (OpenJDK). It would not work with 11 or higher. Run the following to install java 8. sudo apt install openjdk-8-jdk #create symbolic link so we can execute it by calling java8 sudo ln -s /usr/lib/jvm/java-8-openjdk-amd64/bin/java /usr/bin/java8 #To run the web services, execute this with java 8. java8 -jar Resources/Chapter_17_Proxy_Services/stockquote_service.jar
-
Import WSDL file using Postman, SoapUI or Curl
The instructor is using SoapUI. Below is the instruction to use Postman or curl
a. Under Postman's colection, expand SimpleStockQuoteService > SimpleStockQuoteServiceHttpSoap12Endpoint/getQuote then go to the Body and change the value under the symbol tag to
IBM.b. Using Curl
- Using xml file as data reference. Due to relative path, make sure to run it on the workspace folder.
curl --location --request POST 'http://localhost:9000/services/SimpleStockQuoteService' \ --header 'Content-Type: text/xml; charset=utf-8' \ --header 'SOAPAction: urn:getQuote' \ --data @Resources/Ch_17_Proxy_Services/requestBody.xml
- Without using xml file
curl --location --request POST 'http://localhost:9000/services/SimpleStockQuoteService' \ --header 'Content-Type: text/xml; charset=utf-8' \ --header 'SOAPAction: urn:getQuote' \ --data-raw '<?xml version="1.0" encoding="utf-8"?> <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <getQuote xmlns="http://services.samples"> <request> <symbol>string</symbol> </request> </getQuote> </soap:Body> </soap:Envelope> '
Once the test successful, we are creating the proxy services where we register the WSDL file into the Registry Resources
-
Create new integration project called
ProxyServiceon the workspace folder but this time 3 Modules need to be created by checking the checkboxes for:ESB Configs,Composite Exporter, andRegistry Resources. -
On
ProxyServiceConfigs,- create new >
Proxy Servicesartifact calledCustomProxyServiceStockQuoteService
- create new >
-
On
ProxyServiceRegistryResources,- create new > Registry Resource . Click next the import from the file system.
- Click on browse file then locate the wsdl file in the Resources/Chapter_17_Proxy_Services/sample_proxy_1.wsdl
-
Go back to the
CustomProxyServiceStockQuoteServiceand click on its property- scroll down to the WSDL Type and select
REGISTRY_KEY - Click WSDL Key and select workspace > Carbon Application Registry Resources > ProxyServiceRegistryResources > sample_proxy_1_wsdl >
/_system/governance/custom/sample_proxy_1.wsdl
- scroll down to the WSDL Type and select
-
Now create the proxy service flow
- Add Log Start mediator
- Add Send mediator (allowing sending request to the backend)
- Go back
src/main/synapse-config/endpoints,- create new
Endpointartifact calledSimpleStockQuote - add the address
http://localhost:9000/services/SimpleStockQuoteServicethen click Finish - Change the Address Endpoint Format to
SOAP 1.2
- create new
- Go back to the
CustomProxyServiceStockQuoteServiceartifact and add the Defined Endpoints >SimpleStockQuotenext to the Send mediator - Add Log END mediator in the output flow and add another Send mediator
-
To run the Proxy Service, we need to package by right click on
ProxyServiceCompositeExporterthen Export Project artifacts and run then select the following then click Finish- ProxyServiceRegistryResources
- ProxyServiceConfigs
-
Make sure the stockquote_service.jar is running
#To run the web services, execute this with java 8. java8 -jar Resources/Chapter_17_Proxy_Services/stockquote_service.jar -
Once exported, the WSDL file can be viewed and downloaded from http://localhost:8290/services/CustomProxyServiceStockQuoteService?wsdl into either POSTMAN, SOAP UI or curl
- For SOAP UI
- Create SOAP Project
- Project Name:
CustomProxyService - Initial WSDL:
http://localhost:8290/services/CustomProxyServiceStockQuoteService?wsdl
- Project Name:
- Go to any CustomProxyBinding, edit the getQuote and replace question mark in the
<xsd:symbol>?</xsd:symbol>with eitherIBMorMSFT. It should return the value while the WSO2 will logs the following
[2022-02-16 00:51:32,795] INFO {LogMediator} - {proxy:CustomProxyServiceStockQuoteService} LOG MESSAGE = LOG START [2022-02-16 00:51:32,904] INFO {TimeoutHandler} - This engine will expire all callbacks after GLOBAL_TIMEOUT: 120 seconds, irrespective of the timeout action, after the specified or optional timeout [2022-02-16 00:51:34,395] INFO {LogMediator} - {proxy:CustomProxyServiceStockQuoteService} LOG MESSAGE = LOG END - Create SOAP Project
- For SOAP UI
-
Inbound Endpoints - Listening Inbound Endpoints (Project folder: InboundEndpoint)
An inbound endpoint is a message entry point that can inject messages directly from the transport layer to the mediation layer, without going through the Axis engine. One of the advantages of using Inbound Endpoints is in its ability to create inbound messaging channels dynamically. There are three types of inbound endpoints:
- Listening Inbound Endpoints (Bidirectional) - HTTP/HTTPS, HL7, CXF WS-RM or Websocket
- Polling Inbound Endpoints (One directional) - File, JMS or Kafka
- Event-based Inbound Endpoints (Pulled once connection established) - MQTT or RabbitMQ
Note:
- Port 8285, 8290 and 8295 are being used on this project. Make sure the ports are not taken already. Otherwise, it will give error.
- This projects used a lot of sequence artifacts and mediation sequences / sequence mediator. The sequence mediator contains mediators that would be use repeatedly. Think of it as desigining a class and its methods in object oriented.
Steps (mixing design and code view for quicker note):
-
Create Integration Project called
InboundEndpointwith ESB Configs and CompositeExporter -
Create RestAPI artifact called
DictionaryAPIwith context/api. We're creatinghttp://localhost:8290/api/dictionary/{word}endpoint.a. Set Resource properties -> URI Style:
URI_Template, Uri Template:/dictionary/{word}, Protocol:httpb. InSequence - Set Log Description
<log description="LOG MESSAGE" level="custom"> <property expression="$ctx:uri.var.word" name="LOG MESSAGE"/> </log>
c. InSequence - Set property from the log word mediator
<property description="SET PROPERTY" expression="$ctx:uri.var.word" name="wordProperty" scope="default" type="STRING"/>
d. InSequence - Set payloadFactory that display the word Property
<payloadFactory description="SET PAYLOAD" media-type="json"> <format> { "word":$1 } </format> <args> <arg evaluator="xml" expression="$ctx:wordProperty"/> </args> </payloadFactory>
e. InSequence - Add Loopback to move to OutSequence
<loopback/>f. OutSequence - Add Respond back to the client
<respond description="SEND OUT RESPONSE"/>
-
Create inbound endpoints artifact called
ExposeRestApiIEP. We are creatinghttp://localhost:8285/api/dictionary/{word}endpoint that forwards the{word}query path tohttp://localhost:8290/api/dictionary/{word}a. On the Inbound EP,
- set the Dispatch Filter Pattern to
/api/dictionary/.*. The.*means it only work as filter dispatcher and it will ignore the log inside the sequence and forward the response. - Then change the Inbound Http Port:
8285
b. Since we could not add log and other mediators in the Inbound Endpoint, we have to add sequence. Drag the sequence mediator to the sequence box and double click it. This create
Sequence.xmlfile under sequences folder. The codes behind the sequence.xml is the following<sequence name="Sequence" trace="disable" xmlns="http://ws.apache.org/ns/synapse"/>
c. Double clicking sequence mediator and add log mediator in it. This will create
Sequence.xmlfile<log description="LOG MESSAGE" level="custom"> <property name="LOG MESSAGE" value="THIS SEQUENCE HAS BEEN EXECUTED"/> </log>
- set the Dispatch Filter Pattern to
-
Create inbound endpoints artifact called
HttpTestIEPwithout dispatcher. This means sequence will actually be called and transformed the message. We are creatinghttp://localhost:8295endpoint that will generatecovidresult by passing the query path tohttp://localhost:8290/api/dictionary/covida. On the Inbound EP, set the Port to
8295b. Create an endpoint artifact called
DictionaryApiEP-
Endpoint Type:
HTTP Endpoint -
URI Template:
http://localhost:8290/api/dictionary/{uri.var.wordIEP}
c. Create another sequence artifact called
SequenceIN- Drag Log mediator inside the sequence box
<log description="LOG MESSAGE" level="custom"> <property name="LOG MESSAGE" value="SEQUENCE IN HAS BEEN EXECUTED"/> </log>
- Add property mediator after that
<property description="SET PROPERTY" name="uri.var.wordIEP" scope="default" type="STRING" value="covid"/>
- Add Send mediator then add
DictionaryApiEPdefined endpoints
<send> <endpoint key="DictionaryApiEP"/> </send>
d. Create another sequence artifact to end the loop. It's called
SequenceOUT- Add log mediator
<log description="LOG MESSAGE" level="custom"> <property name="LOG MESSAGE" value="SEQUENCE OUT HAS BEEN EXECUTED"/> </log>
- Add send mediator inside the sequence box
<send/>e. Back to the
HttpTestIEP.xmlinbound-endpoints- Drag
SequenceINfrom Defined Sequences into sequence
f. Back to
SequenceIN.xml-
change the Receiving Sequence Type:
Static -
set SequenceOUT directly on the code
<send receive="SequenceOUT"> <endpoint key="DictionaryApiEP"/> </send>
-
-
Package artifacts of the first draft by going to Exporter
Export Artificats Project and Run. The application is successfully deployed when you see the listeners are ready in the console
[2022-02-18 00:13:20,149] INFO {HTTPEndpointManager} - Listener is already started for port : 8295 [2022-02-18 00:13:20,150] INFO {HTTPEndpointManager} - Listener is already started for port : 8285-
Open terminal window to test out
a. Calling
DictionaryAPIREST API projectcurl -v GET "http://localhost:8290/api/dictionary/Portugal"Response
{ "word":Portugal }Log Message
[2022-02-18 00:18:29,431] INFO {LogMediator} - {api:DictionaryAPI} LOG MESSAGE = Portugalb. Calling
ExposeRestApiIEPInbound endpoint - through dispatch filter pattern exposing the API via http endpoint.Since we're using dispatch filter pattern, the sequence is being ignored.curl -v GET "http://localhost:8285/api/dictionary/wso2" -w "\n"
Response
{ "word":wso2 }Log Message
[2022-02-18 00:21:42,147] INFO {LogMediator} - {api:DictionaryAPI} LOG MESSAGE = wso2c. Calling
HttpTestIEPInbound endpoint - Since there is no dispatch filter, the sequencesIN is usedcurl -v GET "http://localhost:8295" -w "\n"
Response
{ "word":covid }Log Message
[2022-02-18 00:23:57,526] INFO {LogMediator} - {inboundendpoint:HttpTestIEP} LOG MESSAGE = SEQUENCE IN HAS BEEN EXECUTED [2022-02-18 00:23:57,552] INFO {TimeoutHandler} - This engine will expire all callbacks after GLOBAL_TIMEOUT: 120 seconds, irrespective of the timeout action, after the specified or optional timeout [2022-02-18 00:23:57,629] INFO {LogMediator} - {api:DictionaryAPI} LOG MESSAGE = covid [2022-02-18 00:23:57,697] INFO {LogMediator} - {inboundendpoint:HttpTestIEP} LOG MESSAGE = SEQUENCE OUT HAS BEEN EXECUTED
-
Inbound Endpoints - Polling Inbound Endpoints (Project folder: InboundEndpoint)
A polling inbound endpoint polls periodically for data and when data is available the data is injected to a given sequence. Continuing from previous project, this chapter will check when different file types are dropped to the certain folders and it will get copied over to different folder.
- Create folders structures for this project
mkdir -p /tmp/WSO2/Ch19/CSV/{In,Original,Out} mkdir -p /tmp/WSO2/Ch19/JSON/{In,Original,Out} mkdir -p /tmp/WSO2/Ch19/XML/{In,Original,Out} cp ~/IntegrationStudio/8.0.0/workspace/Resources/Ch_19_Inbound_Endpoints_Polling/sample.csv /tmp/WSO2/Ch19/CSV/ cp ~/IntegrationStudio/8.0.0/workspace/Resources/Ch_19_Inbound_Endpoints_Polling/sample.json /tmp/WSO2/Ch19/JSON/ cp ~/IntegrationStudio/8.0.0/workspace/Resources/Ch_19_Inbound_Endpoints_Polling/test.xml /tmp/WSO2/Ch19/XML/ cd ~/IntegrationStudio/8.0.0/workspace/-
Create new sequence artifacts called
a.
CSVDataFileProcessSeq- Drag Log to the sequence box
<log description="LOG CSV FILE CONTENT" level="full"/>
b.
XMLDataFileProcessSeq-
Drag Log mediator to the sequence box
<log description="LOG XML FILE CONTENT" level="full"/>
-
Drag another Log mediator to the sequence box. This log uses expression to read the country data inside the xml file.
<log description="LOG MESSAGE" level="custom"> <property expression="$body//orders//order//country" name="LOG COUNTRY"/> </log>
c. Create new sequence artifact called
JSONDataFileProcessSeq-
Drag Log mediator to the sequence box
<log description="LOG FILE JSON CONTENT" level="full"/>
-
Drag another Log mediator to the sequence sbox. This log uses expression to read the country data inside the xml file.
<log description="LOG MESSAGE" level="custom"> <property expression="json-eval($.id)" name="LOG ID"/> </log>
d. Create new sequence artifact called
ErrorProcessSeq.This will monitor the error during the 3 sequences above
- Drag Log mediator to the sequence box
<log description="LOG ERROR DETAILS" level="full"> <property name="MESSAGE" value="An unexpected error occured!"/> <property expression="$ctx:ERROR_CODE" name="ERROR_CODE"/> <property expression="$ctx:ERROR_MESSAGE" name="ERROR_MESSAGE"/> <property expression="$ctx:ERROR_DETAIL" name="ERROR_DETAIL"/> <property expression="$ctx:ERROR_EXCEPTION" name="ERROR_EXCEPTION"/> </log>
- Drag Log to the sequence box
-
Create 3 different inbound endpoints artifacts called
a.
CSVDataFileProcessIEP- Inbound Endpoint Creation Type:
File - Sequence:
CSVDataFileProcessSeq - Error Sequence:
ErrorProcessSeq
Click Finish
- Change InboundEP Mediator Properties:
<parameters> <parameter name="interval">1000</parameter> <parameter name="sequential">true</parameter> <parameter name="coordination">true</parameter> <parameter name="transport.vfs.ContentType">text/plain</parameter> <parameter name="transport.vfs.LockReleaseSameNode">false</parameter> <parameter name="transport.vfs.AutoLockRelease">false</parameter> <parameter name="transport.vfs.ActionAfterFailure">MOVE</parameter> <parameter name="transport.vfs.FailedRecordsFileName">vfs-move-failed-records.properties</parameter> <parameter name="transport.vfs.FailedRecordsFileDestination">repository/conf/</parameter> <parameter name="transport.vfs.MoveFailedRecordTimestampFormat">dd-MM-yyyy HH:mm:ss</parameter> <parameter name="transport.vfs.FailedRecordNextRetryDuration">3000</parameter> <parameter name="transport.vfs.ActionAfterProcess">MOVE</parameter> <parameter name="transport.vfs.FileURI">file:///tmp/WSO2/Ch19/CSV/In</parameter> <parameter name="transport.vfs.MoveAfterFailure">/tmp/WSO2/Ch19/CSV/Original</parameter> <parameter name="transport.vfs.ReplyFileName">response.xml</parameter> <parameter name="transport.vfs.MoveTimestampFormat">yyy.MMMMM.dd.hh.mm.ss aaa</parameter> <parameter name="transport.vfs.DistributedLock">false</parameter> <parameter name="transport.vfs.FileNamePattern">.*\.csv</parameter> <parameter name="transport.vfs.MoveAfterProcess">/tmp/WSO2/Ch19/CSV/Out</parameter> <parameter name="transport.vfs.Locking">disable</parameter> <parameter name="transport.vfs.SFTPUserDirIsRoot">false</parameter> <parameter name="transport.vfs.FileSortAttribute">none</parameter> <parameter name="transport.vfs.FileSortAscending">true</parameter> <parameter name="transport.vfs.CreateFolder">true</parameter> <parameter name="transport.vfs.Streaming">false</parameter> <parameter name="transport.vfs.Build">false</parameter> <parameter name="transport.vfs.UpdateLastModified">true</parameter> </parameters>
b.
XMLDataFileProcessIEP- Inbound Endpoint Creation Type:
File - Sequence:
XMLDataFileProcessSeq - Error Sequence:
ErrorProcessSeq
Click Finish
- Change InboundEP Mediator Properties:
<parameters> <parameter name="interval">1000</parameter> <parameter name="sequential">true</parameter> <parameter name="coordination">true</parameter> <parameter name="transport.vfs.ContentType">text/xml</parameter> <parameter name="transport.vfs.LockReleaseSameNode">false</parameter> <parameter name="transport.vfs.AutoLockRelease">false</parameter> <parameter name="transport.vfs.ActionAfterFailure">MOVE</parameter> <parameter name="transport.vfs.FailedRecordsFileName">vfs-move-failed-records.properties</parameter> <parameter name="transport.vfs.FailedRecordsFileDestination">repository/conf/</parameter> <parameter name="transport.vfs.MoveFailedRecordTimestampFormat">dd-MM-yyyy HH:mm:ss</parameter> <parameter name="transport.vfs.FailedRecordNextRetryDuration">3000</parameter> <parameter name="transport.vfs.ActionAfterProcess">MOVE</parameter> <parameter name="transport.vfs.FileURI">file:///tmp/WSO2/Ch19/XML/In</parameter> <parameter name="transport.vfs.MoveAfterFailure">/tmp/WSO2/Ch19/XML/Original</parameter> <parameter name="transport.vfs.ReplyFileName">response.xml</parameter> <parameter name="transport.vfs.MoveTimestampFormat">yyy.MMMMM.dd.hh.mm.ss aaa</parameter> <parameter name="transport.vfs.DistributedLock">false</parameter> <parameter name="transport.vfs.FileNamePattern">.*\.xml</parameter> <parameter name="transport.vfs.MoveAfterProcess">/tmp/WSO2/Ch19/XML/Out</parameter> <parameter name="transport.vfs.Locking">disable</parameter> <parameter name="transport.vfs.SFTPUserDirIsRoot">false</parameter> <parameter name="transport.vfs.FileSortAttribute">none</parameter> <parameter name="transport.vfs.FileSortAscending">true</parameter> <parameter name="transport.vfs.CreateFolder">true</parameter> <parameter name="transport.vfs.Streaming">false</parameter> <parameter name="transport.vfs.Build">false</parameter> <parameter name="transport.vfs.UpdateLastModified">true</parameter> </parameters>
c.
JSONDataFileProcessIEP- Inbound Endpoint Creation Type:
File - Sequence:
JSONDataFileProcessSeq - Error Sequence:
ErrorProcessSeq
Click Finish
- Change InboundEP Mediator Properties:
<parameters> <parameter name="interval">1000</parameter> <parameter name="sequential">true</parameter> <parameter name="coordination">true</parameter> <parameter name="transport.vfs.ContentType">application/json</parameter> <parameter name="transport.vfs.LockReleaseSameNode">false</parameter> <parameter name="transport.vfs.AutoLockRelease">false</parameter> <parameter name="transport.vfs.ActionAfterFailure">MOVE</parameter> <parameter name="transport.vfs.FailedRecordsFileName">vfs-move-failed-records.properties</parameter> <parameter name="transport.vfs.FailedRecordsFileDestination">repository/conf/</parameter> <parameter name="transport.vfs.MoveFailedRecordTimestampFormat">dd-MM-yyyy HH:mm:ss</parameter> <parameter name="transport.vfs.FailedRecordNextRetryDuration">3000</parameter> <parameter name="transport.vfs.ActionAfterProcess">MOVE</parameter> <parameter name="transport.vfs.FileURI">file:///tmp/WSO2/Ch19/JSON/In</parameter> <parameter name="transport.vfs.MoveAfterFailure">/tmp/WSO2/Ch19/JSON/Original</parameter> <parameter name="transport.vfs.ReplyFileName">response.xml</parameter> <parameter name="transport.vfs.MoveTimestampFormat">yyy.MMMMM.dd.hh.mm.ss aaa</parameter> <parameter name="transport.vfs.DistributedLock">false</parameter> <parameter name="transport.vfs.FileNamePattern">.*\.json</parameter> <parameter name="transport.vfs.MoveAfterProcess">/tmp/WSO2/Ch19/JSON/Out</parameter> <parameter name="transport.vfs.Locking">disable</parameter> <parameter name="transport.vfs.SFTPUserDirIsRoot">false</parameter> <parameter name="transport.vfs.FileSortAttribute">none</parameter> <parameter name="transport.vfs.FileSortAscending">true</parameter> <parameter name="transport.vfs.CreateFolder">true</parameter> <parameter name="transport.vfs.Streaming">false</parameter> <parameter name="transport.vfs.Build">false</parameter> <parameter name="transport.vfs.UpdateLastModified">true</parameter> </parameters>
- Inbound Endpoint Creation Type:
-
Package the artifacts that are created in this project by going to Exporter
Export Artificats Project and Run. Deselect All previous artifacts and select the ones that we created above by Expanding Inbound EndpointConfigs:- com.example.sequence_._ErrorProcessSeq
- com.example.sequence_._XMLDataFileProcessSeq
- com.example.inbound-endpoint_._CSVDataFileProcessIEP
- com.example.inbound-endpoint_._XMLDataFileProcessIEP
- com.example.sequence_._JSONDataFileProcessSeq
- com.example.sequence_._CSVDataFileProcessSeq
- com.example.inbound-endpoint_._JSONDataFileProcessIEP
Click Finish
The application is successfully deployed when you see the listeners are ready in the console
[2022-02-22 23:21:12,901] INFO {AbstractQuartzTaskManager} - Task scheduled: [ESB_TASK][CSVDataFileProcessIEP-FILE--SYNAPSE_INBOUND_ENDPOINT]. [2022-02-22 23:21:12,916] INFO {AbstractQuartzTaskManager} - Task scheduled: [ESB_TASK][XMLDataFileProcessIEP-FILE--SYNAPSE_INBOUND_ENDPOINT]. [2022-02-22 23:21:12,977] INFO {AbstractQuartzTaskManager} - Task scheduled: [ESB_TASK][JSONDataFileProcessIEP-FILE--SYNAPSE_INBOUND_ENDPOINT].-
Do the following tests for each file type. See how the file copied to the
Infolder got moved toOutwithin 1 second (polling time):Use Windows Explorer/Finder/Files app to the following folder
/tmp/WSO2/Ch19/a. Go to CSV folder and copy
sample.csvtoInfolder. The file will be moved toOutfolder[2022-02-22 23:23:19,219] INFO {LogMediator} - {inboundendpoint:CSVDataFileProcessIEP} To: , MessageID: urn:uuid:DD330314E1061987CE1645590199208, Direction: request, Envelope: <?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Body><text xmlns="http://ws.apache.org/commons/ns/payload">1,Nelson,Dias,Portugal,Developer
 2,Romeo,Santos,RepDominicana,Singer</text></soapenv:Body></soapenv:Envelope>b. Go to XML folder and copy
test.xmltoInfolder. The file will be moved toOutfolder[2022-02-22 23:31:28,932] INFO {LogMediator} - {inboundendpoint:XMLDataFileProcessIEP} To: , MessageID: urn:uuid:DD330314E1061987CE1645590688920, Direction: request, Envelope: <?xml version='1.0' encoding='utf-8'?><soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:wsa="http://www.w3.org/2005/08/addressing"><soapenv:Body> <orders> <order> <country>Portugal</country> </order> </orders> </soapenv:Body></soapenv:Envelope> [2022-02-22 23:31:28,960] INFO {LogMediator} - {inboundendpoint:XMLDataFileProcessIEP} LOG COUNTRY = Portugalc. Go to JSON folder and copy
sample.jsontoInfolder. The file will be moved toOutfolder[2022-02-22 23:35:49,978] INFO {LogMediator} - {inboundendpoint:JSONDataFileProcessIEP} To: , MessageID: urn:uuid:DD330314E1061987CE1645590949979, Direction: request, Payload: { "id": 12345, "id_str": "12345", "array": [ 1, 2, [ [], [{"inner_id": 6789}] ] ], "name": null, "object": {}, "$schema_location": "unknown", "12X12": "image12x12.png" } [2022-02-22 23:35:49,992] INFO {LogMediator} - {inboundendpoint:JSONDataFileProcessIEP} LOG ID = 12345
-
Scheduled Tasks (Project folder: ScheduledTask)
Create task that runs periodically. It can inject messages, define endpoints, proxy services, and so on. This project uses scheduled task to pass the country and location in xml message to a proxy. From proxy, it passed on to the Endpoint then, from Endpoint, it passed to the WeatherAPI.
-
Create new integration studio project called
ScheduledTask -
Create new REST API artifacts called
WeatherAPIwith contextAPIa. On the Resource, set the following
<resource methods="GET" protocol="http" uri-template="/weather/{country}/{city}">
b. Add the following mediators to the inSequence
<inSequence> <log description="LOG START" level="custom"> <property name="LOG MESSAGE" value="LOG START API"/> </log> <payloadFactory description="SET PAYLOAD" media-type="json"> <format>{ "country":$1, "city": $2, "weather": "sunny" }</format> <args> <arg evaluator="xml" expression="$ctx:uri.var.country"/> <arg evaluator="xml" expression="$ctx:uri.var.city"/> </args> </payloadFactory> <log description="LOG END" level="custom"> <property name="LOG MESSAGE" value="LOG END API"/> </log> <loopback/> </inSequence>
c. Add the following mediator to the outSequence
<outSequence> <respond description="SEND RESPONSE"/> </outSequence>
-
Create an Endpoint artifact called
WeatherApiEPa. Endpoint Type:
HTTP Endpoint(This always bite back and easily forgotten to change)b. URI Template:
http://localhost:8290/api/weather/{uri.var.countryProxy}/{uri.var.cityProxy} -
Create a Proxy Service artifact called
CustomWeatherApiProxya. Add the following mediators to the outSequence
<inSequence> <property description="SET COUNTRY PROXY" expression="//request/location/country" name="uri.var.countryProxy" scope="default" type="STRING"/> <property description="SET CITY PROXY" expression="//request/location/city" name="uri.var.cityProxy" scope="default" type="STRING"/> <log description="LOG LOCATION" level="custom"> <property expression="$ctx:uri.var.countryProxy" name="LOG COUNTRY"/> <property expression="$ctx:uri.var.cityProxy" name="LOG CITY"/> </log> <send> <endpoint key="WeatherApiEP"/> </send> </inSequence>
b. Add the following mediator to the outSequence
<outSequence> <log description="LOG OUTPUT" level="full"/> </outSequence>
-
Create Scheduled Task artifact called
InjectRequestWeatherApiTask. We keep itSimple, not using cron method and set the count to2and Interval to10seconds. This will run the proxy twice within 10 secondsa. Once artifact is created, click on
Task Implementation Properties, set-
injectTo with value
Proxy -
message to
XMLwith value (if there is any problem, try linearize the xml into single line)
<request> <location> <city>Lisbon</city> <country>Portugal</country> </location> </request>
- proxyName with value
CustomWeatherApiProxy
-
-
Export project artifacts and Run
[2022-02-27 00:46:36,721] INFO {AbstractQuartzTaskManager} - Task scheduled: [ESB_TASK][InjectRequestWeatherApiTask]. [2022-02-27 00:46:37,279] INFO {LogMediator} - {proxy:CustomWeatherApiProxy} LOG COUNTRY = Portugal, LOG CITY = Lisbon [2022-02-27 00:46:37,467] INFO {TimeoutHandler} - This engine will expire all callbacks after GLOBAL_TIMEOUT: 120 seconds, irrespective of the timeout action, after the specified or optional timeout [2022-02-27 00:46:37,674] INFO {LogMediator} - {api:WeatherAPI} LOG MESSAGE = LOG START API [2022-02-27 00:46:37,745] INFO {LogMediator} - {api:WeatherAPI} LOG MESSAGE = LOG END API [2022-02-27 00:46:37,895] INFO {LogMediator} - {proxy:CustomWeatherApiProxy} To: http://www.w3.org/2005/08/addressing/anonymous, WSAction: , SOAPAction: , MessageID: urn:uuid:cabb9503-19ea-4610-94e0-488512a2473a, correlation_id: c6ff23f0-9363-4c78-a0da-4a3acb1c472c, Direction: response, Payload: { "country":Portugal, "city": Lisbon, "weather": "sunny" } [2022-02-27 00:46:39,519] INFO {AuthenticationHandlerAdapter} - User admin logged in successfully [2022-02-27 00:46:39,605] INFO {ServiceComponent} - Initializing Security parameters [2022-02-27 00:46:46,732] INFO {LogMediator} - {proxy:CustomWeatherApiProxy} LOG COUNTRY = Portugal, LOG CITY = Lisbon [2022-02-27 00:46:46,743] INFO {LogMediator} - {api:WeatherAPI} LOG MESSAGE = LOG START API [2022-02-27 00:46:46,744] INFO {LogMediator} - {api:WeatherAPI} LOG MESSAGE = LOG END API [2022-02-27 00:46:46,767] INFO {LogMediator} - {proxy:CustomWeatherApiProxy} To: http://www.w3.org/2005/08/addressing/anonymous, WSAction: , SOAPAction: , MessageID: urn:uuid:0ca99bd0-f233-4cfd-b9a4-21a999b4e3ae, correlation_id: 11d316fe-31c4-4687-b361-0547b434468a, Direction: response, Payload: { "country":Portugal, "city": Lisbon, "weather": "sunny" } -
-
Send Mediator (Project folder: HealthcareProject)
This project is allowing healthcare patients to search for specialists by providing the category and return the list back to the patient. One thing to note that send mediator's role is to send the request message to the healthcare service endpoint. It used to send messages to synapse engine. It also copies any message's contexts and properties from the current message context to the replied message received on the execution of Send operation. Since we're not defining the receiving sequence, the default behavior will use outSequence to handle the reponse.
-
Create New Integration Project called
HealthcareProjectwith the following modules: ESB Configs, Composite Exporter, Registry Resources, Connector Exporter. -
Start the web services provided for this chapter. We can use java decompiler app to view the
Hospital-Service-JDK11-2.0.0.jarcode that run these backend services. The instructor uses JD-GUI app.# Go to the resources folder java8 -jar Resources/Ch_22_Send_Mediator/Hospital-Service-JDK11-2.0.0.jar # To test out, run this in another terminal curl -v GET "http://localhost:9090/healthcare/surgery" curl -v GET "http://localhost:9090/healthcare/ent"
-
Back to WSO2, create Endpoints artifacts that point to the java services
a. Endpoint Type:
HTTP Endpointb. URI Template:
http://localhost:9090/healthcare/{uri.var.category} -
Create REST API artifacts called
HealthcareAPIwith context/healthcare. The API handles client request. Add the following mediators to the API.<resource methods="GET" uri-template="/querydoctor/{category}"> <inSequence> <log description="LOG WELCOME" level="custom"> <property name="LOG MESSAGE" value="WELCOME TO HEALTHCARE SERVICE"/> </log> <log description="LOG BEFORE" level="custom"> <property name="LOG MESSAGE" value="LOG BEFORE SEND"/> </log> <send> <endpoint key="QueryDoctorEP"/> </send> </inSequence> <outSequence> <log description="LOG END" level="custom"> <property name="LOG MESSAGE" value="LOG END"/> </log> <send/> </outSequence> <faultSequence/> </resource>
-
Export Project Artifacts and Run (check all if haven't)
curl -v GET "http://localhost:8290/healthcare/querydoctor/surgery" #WSO2 Logs 2022-02-28 00:58:29,035] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = WELCOME TO HEALTHCARE SERVICE [2022-02-28 00:58:29,036] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = LOG BEFORE SEND [2022-02-28 00:58:29,052] INFO {TimeoutHandler} - This engine will expire all callbacks after GLOBAL_TIMEOUT: 120 seconds, irrespective of the timeout action, after the specified or optional timeout [2022-02-28 00:58:29,108] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = LOG END
-
-
Switch Mediator (Project folder: HealthcareProject)
Creating message routing logic that will forward to relevant hospital backend service depends on the content of the json payloads.
-
Create 3 new endpoints called
a. GrandOaks
- method:
POST - uri-template:
http://localhost:9090/grandoaks/categories/{uri.var.category}/reserve
b. Clemency
- method:
POST - uri-template:
http://localhost:9090/clemency/categories/{uri.var.category}/reserve
c. PineValley
- method:
POST - uri-template:
http://localhost:9090/pinevalley/categories/{uri.var.category}/reserve
- method:
-
Create another API Resource inside the HealthcareAPI
json-evalis used in property mediator to read the hospital name from the payload. Switch mediator is used for logic (EnterSource XPathto define the logic).<resource methods="POST" uri-template="/categories/{category}/reserve"> <inSequence> <log description="LOG START" level="custom"> <property name="LOG MESSAGE" value=""LOG RESERVATION START""/> </log> <property description="SET HOSPITAL" expression="json-eval($.hospital)" name="hospitalProperty" scope="default" type="STRING"/> <switch source="$ctx:hospitalProperty"> <case regex="grand oak community hospital"> <log description="LOG HOSPITAL" level="custom"> <property expression="fn:concat("Routing to ", $ctx:hospitalProperty)" name="LOG MESSAGE"/> </log> <send> <endpoint key="GrandOakEP"/> </send> </case> <case regex="clemency medical center"> <log description="LOG HOSPITAL" level="custom"> <property expression="fn:concat("Routing to ", $ctx:hospitalProperty)" name="LOG MESSAGE"/> </log> <send> <endpoint key="ClemencyEP"/> </send> </case> <case regex="pine valley community hospital"> <log description="LOG HOSPITAL" level="custom"> <property expression="fn:concat("Routing to ", $ctx:hospitalProperty)" name="LOG MESSAGE"/> </log> <send> <endpoint key="PineValleyEP"/> </send> </case> <default> <log description="LOG DEFAULT" level="custom"> <property expression="fn:concat("Invalid Hospital ", $ctx:hospitalProperty)" name="LOG MESSAGE"/> </log> <respond description="SEND OUT RESPONSE"/> </default> </switch> </inSequence> <outSequence> <send/> </outSequence> <faultSequence/> </resource>
-
Export Project Artifacts and Run (check all if haven't)
-
If the java service is not running,
# Go to the resources folder java8 -jar Resources/Ch_22_Send_Mediator/Hospital-Service-JDK11-2.0.0.jar -
To test it out
curl -v POST "http://localhost:8290/healthcare/categories/surgery/reserve" \ --header "Content-Type:application/json" \ --data @Resources/Ch_23_Switch_Mediator/PatientExample.json -w "\n"
[2022-03-10 01:08:40,627] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = "LOG RESERVATION START" [2022-03-10 01:08:40,629] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = Routing to grand oak community hospital [2022-03-10 01:14:37,285] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = "LOG RESERVATION START" [2022-03-10 01:14:37,287] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = Routing to clemency medical centerResult
{ "appointmentNumber": 7, "doctor": { "name": "thomas collins", "hospital": "grand oak community hospital", "category": "surgery", "availability": "9.00 a.m - 11.00 a.m", "fee": 7000.0 }, "patient": { "name": "Nelson Dias", "dob": "1940-03-19", "ssn": "234-23-525", "address": "Lisbon", "phone": "8770586755", "email": "nelson.dias@wso2.com" }, "fee": 7000.0, "confirmed": false }Change the hospital on PatientExample.json to
clemency medical center{ "status": "Doctor thomas collins is not available in clemency medical center" }
-
-
Data Mapper (Project folder: HealthcareProject)
Transforming request message to the format that the reservation system accepted. Data mapper is used to map the fields. The example is transforming the request from
{ "name": "Nelson Dias", "dob": "1940-03-19", "ssn": "234-23-525", "address": "Lisbon", "phone": "8770586755", "email": "nelson.dias@wso2.com", "doctor": "thomas collins", "hospital": "grand oak community hospital", "cardNo": "7844481124110331", "appointment_date": "2017-04-02" }to
{ "patient": { "name": "Nelson Dias", "dob": "1940-03-19", "ssn": "234-23-525", "address": "Lisbon", "phone": "8770586755", "email": "nelson.dias@wso2.com" }, "doctor": "thomas collins", "hospital": "grand oak community hospital", "appointment_date": "2025-04-02" }-
On the /categories/{category}/reserve resource, add
Data Mappermediator in between theSET HOSPITALproperty mediator and Switch mediator. -
Double click the data mapper mediator and give the configuration name
RequestMappingthen click Finish. It will open up RequestMapping.datamapper_diagram
-
Click on Load Input File, set Resource Type to
JSON, click on file system and findPatientClient.jsonunder Resources/Ch_24_Data_Mapper folder -
Click on Load Output File, set Resource Type to
JSON, click on file system and findPatientExample.jsonunder Resources/Ch_24_Data_Mapper folder -
You can use
AI generated data mappings availableto map the input and output automatically. If you prefer to do it manually, you can drag the field from input and to the field on output.
-
Once all fields mapped, go tback to the
HealthcareAPIand change the following Data Mapper's properties- Input Type:
JSON - Output Type:
JSON
- Input Type:
-
Export Project Artifacts and Run by checking the
HealthCareProjectConfigsandHealthCareProjectRegistryResourcesartifacts -
If the java service is not running,
# Go to the resources folder java8 -jar Resources/Ch_22_Send_Mediator/Hospital-Service-JDK11-2.0.0.jar -
To test,
curl -v POST "http://localhost:8290/healthcare/categories/surgery/reserve" \ --header "Content-Type:application/json" \ --data @Resources/Ch_24_Data_Mapper/PatientClient.json -w "\n"
[2022-03-12 01:55:20,963] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = "LOG RESERVATION START" [2022-03-12 01:55:21,004] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = Routing to grand oak community hospitalResult
{ "appointmentNumber": 8, "doctor": { "name": "thomas collins", "hospital": "grand oak community hospital", "category": "surgery", "availability": "9.00 a.m - 11.00 a.m", "fee": 7000.0 }, "patient": { "name": "Nelson Dias", "dob": "1940-03-19", "ssn": "234-23-525", "address": "Lisbon", "phone": "8770586755", "email": "nelson.dias@wso2.com" }, "fee": 7000.0, "confirmed": false } -
To try another test, change the hospital to
clemency medical centerinPatientClient.jsonthen run the curl command again[2022-03-12 02:00:41,679] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = "LOG RESERVATION START" [2022-03-12 02:00:41,703] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = Routing to clemency medical centerResult
{ "status": "Doctor thomas collins is not available in clemency medical center" }
-
-
Sequence Template (Project folder: HealthcareProject) Allowing a template (the same workflow) which can be used in several mediation flows and can be invoked multiple times. Do not confused with mediation sequences. A mediation sequence is a set of mediators organized into a logical flow, allowing you to implement pipes and filter patterns. The mediators in the sequence will perform the necessary message processing and route the message to the required destination.
Typically, the required mediation sequences are defined within the proxy service or the REST API. This includes an In sequence, an Out sequence, and a default fault sequence.
-
Create new template called
HospitalRoutingSQwith Template TypeSequence Template -
Right click on the Template design and add parameter then add log and property mediators
<parameter isMandatory="false" name="setHospital"/> <sequence> <log description="LOG HOSPITAL" level="custom"> <property expression="fn:concat('Routing to:', $func:setHospital)" name="LOG MESSAGE"/> </log> </sequence>
-
Change the hardcoded hospital name in URI Template to
{uri.var.hospital}for ClemencyCP, GrandOakEP, and PineValeyEP endpoints. -
Inside the switch mediators, add Call Template mediators between Log and Send mediators for each switch case (except default)
-
Once Call Template mediators are added, removed the log mediator because we created earlier in the sequence
-
Export Project Artifacts and Run (check all if haven't)
-
If the java service is not running,
# Go to the resources folder java8 -jar Resources/Ch_22_Send_Mediator/Hospital-Service-JDK11-2.0.0.jar -
To test it out
curl -v POST "http://localhost:8290/healthcare/categories/surgery/reserve" \ --header "Content-Type:application/json" \ --data @Resources/Ch_24_Data_Mapper/PatientClient.json -w "\n"
[2022-03-15 01:00:15,655] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = "LOG RESERVATION START" [2022-03-15 01:00:18,708] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = Routing to:clemencyResult
{ "status": "Doctor thomas collins is not available in clemency medical center" }
-
-
Call mediator (Project folder: HealthcareProject)
How to use call mediator to expose several services as single services using chaining method.
When the Send mediator is used to send a message, the response goes to the OutSequence (or to the specified receiving sequence). When the Call mediator is used to send a message, the response goes to the next mediator, allowing to continue the transform process.-
Create new Endpoint called
ChannelingEP- Endpoint Type:
HTTP Endpoint - Method:
GET - URI Template:
http://localhost:9090/{uri.var.hospital}/categories/appointments/{uri.var.appointmentId}/fee
- Endpoint Type:
-
Create new Endpoint called
SettlePaymentEP- Endpoint Type:
HTTP Endpoint - Method:
POST - URI Template:
http://localhost:9090/healthcare/payments
- Endpoint Type:
-
Removed previous send mediators and add call mediators
-
Add log mediator and 3 properties mediators
- GET DOCTOR DETAILS >
json-eval($.doctor) - GET APPOINTMENT ID >
json-eval($.appointmentNumber) - GET PATIENT DETAILS >
json-eval($.patient)DETAIL
- GET DOCTOR DETAILS >
-
Add Payload Factory
<payloadFactory description="SET PAYLOAD MESSAGE" media-type="json"> <format>{ "appointmentNumber": $1, "doctor": $2, "patient": $3, "fee": $4, "confirmed": "false", "cardNumber": "1234567890" }</format> <args> <arg evaluator="xml" expression="$ctx:uri.var.appointmentId"/> <arg evaluator="xml" expression="$ctx:doctorDetails"/> <arg evaluator="xml" expression="$ctx:patientDetails"/> <arg evaluator="xml" expression="$ctx:actualFee"/> </args> </payloadFactory>
-
Add another call mediator to
SettlePaymentEPthen add Respond mediator at the end -
Export Project Artifacts and Run (check all if haven't)
-
If the java service is not running,
# Go to the resources folder java8 -jar Resources/Ch_22_Send_Mediator/Hospital-Service-JDK11-2.0.0.jar -
To test it out
curl -v POST "http://localhost:8290/healthcare/categories/surgery/reserve" \ --header "Content-Type:application/json" \ --data @Resources/Ch_24_Data_Mapper/PatientClient.json -w "\n"
[2022-03-20 01:22:22,524] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = Routing to:grandoaks [2022-03-20 01:22:22,551] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = {"appointmentNumber":9,"doctor":{"name":"thomas collins","hospital":"grand oak community hospital","category":"surgery","availability":"9.00 a.m - 11.00 a.m","fee":7000.0},"patient":{"name":"Nelson Dias","dob":"1940-03-19","ssn":"234-23-525","address":"Lisbon","phone":"8770586755","email":"nelson.dias@wso2.com"},"fee":7000.0,"confirmed":false} [2022-03-20 01:22:22,598] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = {"patientName":"nelson dias","doctorName":"thomas collins","actualFee":"7000.0"}Result
{ "patient": "Nelson Dias", "actualFee": 7000.0, "discount": 20, "discounted": 5600.0, "paymentID": "5ce473f6-7b1a-4d81-a196-390516cb8a8c", "status": "Settled" }
-
-
Install RabbitMQ Using RabbitMQ as middleware. Leverage Docker instead of installing in computer. Default cred is guest/guest
docker pull rabbitmq:3-management # Replace the brackets docker run -d --hostname my-rabbit --name wso2-rabbit -p 15672:15672 -p 5672:5672 rabbitmq:3-managementTo test it out, go to browser and go to http://localhost:15672. If login is an issue, use private browsing.

-
Message Store and Processor (Project folder: HealthcareProject) Still related to the HealthcareProject, besides calling the
POST /healthcare/payments, it submitting to RabbitMQ server to process the payment which would not happen instantly. "A Message Store is used by a mediation sequence to temporarily store messages before they are delivered to their destination. This approach is useful for serving traffic to back-end services that can only accept messages at a given rate, whereas incoming traffic arrives at different rates.The Store Mediator in a mediation sequence is used to store incoming messages in the message store. The Message Processor retrieves the messages from the message store and delivers them to the back-end service at a given rate."
-
Create new message-stores with the following configs:
- Message Store Name:
PaymentRequestMS - Message Store type:
RabbitMQ Message Store - RabbitMQ Server Host Name:
localhost - RabbitMQ Server Host Port:
5672 - SSL Enabled:
false - RabbitMQ Queue Name:
myQueue - RabbitMQ Exchange Name:
myExchange - Routing Key:
myKey - User Name:
guest - Password:
guest
- Message Store Name:
-
Create sequences artifact called
PaymentRequestProcessingSeqand add logs, call, defined endpoints and drop mediators. Drop mediator is used to exit the API immediately without continuing the flow (end of message flow).<?xml version="1.0" encoding="UTF-8"?> <sequence name="PaymentRequestProcessingSeq" trace="disable" xmlns="http://ws.apache.org/ns/synapse"> <log description="LOG START" level="custom"> <property name="LOG MESSAGE" value="LOG BEFORE SETTLE PAYMENT EP CALL"/> </log> <call> <endpoint key="SettlePaymentEP"/> </call> <log description="LOG END" level="custom"> <property name="LOG MESSAGE" value="LOG AFTER SETTLE PAYMENT EP CALL"/> <property expression="json-eval($)" name="LOG APPOINTMENT"/> </log> <drop/> </sequence>
-
Create Message Processor artifact with the following configs: It's responsible to execute the sequence we created above
- Message processor type:
Message Sampling Processor - Message processor Name:
PaymentRequestMP - Message Store:
PaymentRequestMS - Processor state:
Activate - Sequence:
PaymentRequestProcessingSeq - Sampling interval (Millis):
1000 - Sampling concurrency: 1
- Message processor type:
-
Go back to the HealthcareAPI remove the Call SettlementEP
<call> <endpoint key="SettlePaymentEP"/> </call>
and replace with Store and Payload Factory mediators
<store description="PUBLISH TO QUEUE" messageStore="PaymentRequestMS"/> <payloadFactory description="SET RESPONSE PAYLOAD" media-type="json"> <format> { "message": "Payment Request published to myQueue. Confirmation will be sent via Email" } </format> <args/> </payloadFactory>
-
Go to RabbitMQ Dashboard page (http://localhost:15672)
- Go to Queues tab and click on "Add queue" and put the name
myQueuethen click "Add queue". - Go to Exchanges tab and click on "Add a new exchange" then put the name
myExchangethen click "Add exchange" - Click on
myExchangethat were created above and click on bindings with the following:
- On To queue:
myQueue - Routing Key:
myKey - Click on Bind
- Go to Queues tab and click on "Add queue" and put the name
-
Go back to WSO2 HealthcareAPI and click on "Server configuration (Embedded Micro Integrator)" {The 6th icon from the left shortcut bar on the top} and modify the embedded
deployment.tomlfile by going to line 60 (it could be different) from# [transport.rabbitmq] # sender_enable = true # listener_enable = true # [[transport.rabbitmq.sender]] # name = "AMQPConnectionFactory" # parameter.hostname = "localhost" # parameter.port = 5672 # parameter.username = "guest" # parameter.password = "guest" # [[transport.rabbitmq.listener]] # name = "AMQPConnectionFactory" # parameter.hostname = "localhost" # parameter.port = 5672 # parameter.username = "guest" # parameter.password = "guest"
to
[transport.rabbitmq] sender_enable = true # listener_enable = true [[transport.rabbitmq.sender]] name = "CachedRabbitMQConnectionFactory" parameter.hostname = "localhost" parameter.port = 5672 parameter.username = "guest" parameter.password = "guest" [[transport.rabbitmq.listener]] name = "AMQPConnectionFactory" parameter.hostname = "localhost" parameter.port = 5672 parameter.username = "guest" parameter.password = "guest"
-
Export Project Artifacts and Run (check all if haven't)
-
If the java service is not running,
# Go to the resources folder java8 -jar Resources/Ch_22_Send_Mediator/Hospital-Service-JDK11-2.0.0.jar -
To test it out
curl -v POST "http://localhost:8290/healthcare/categories/surgery/reserve" \ --header "Content-Type:application/json" \ --data @Resources/Ch_24_Data_Mapper/PatientClient.json -w "\n"
[2022-03-23 00:46:11,106] INFO {PassThroughListeningIOReactorManager} - Pass-through HTTP Listener started on 0.0.0.0:8290 [2022-03-23 00:46:11,115] INFO {PassThroughListeningIOReactorManager} - Pass-through HTTPS Listener started on 0.0.0.0:8253 [2022-03-23 00:46:11,116] INFO {RabbitMQListener} - RABBITMQ listener started [2022-03-23 00:46:11,119] INFO {StartupFinalizer} - WSO2 Micro Integrator started in 25.00 seconds [2022-03-23 00:46:11,588] INFO {PassThroughListeningIOReactorManager} - Pass-through EI_INTERNAL_HTTP_INBOUND_ENDPOINT Listener started on 0.0.0.0:9201 [2022-03-23 00:46:11,750] INFO {PassThroughListeningIOReactorManager} - Pass-through EI_INTERNAL_HTTPS_INBOUND_ENDPOINT Listener started on 0.0.0.0:9164 [2022-03-23 00:46:12,021] INFO {AbstractQuartzTaskManager} - Task scheduled: [ESB_TASK][MSMP_PaymentRequestMP_0]. [2022-03-23 00:46:12,022] INFO {ScheduledMessageProcessor} - Started message processor. [PaymentRequestMP]. [2022-03-23 00:46:12,248] INFO {RabbitMQStore} - Store [PaymentRequestMS] Successfully connected to RabbitMQ Broker [2022-03-23 00:46:15,572] INFO {AuthenticationHandlerAdapter} - User admin logged in successfully [2022-03-23 00:46:15,592] INFO {ServiceComponent} - Initializing Security parameters [2022-03-23 00:47:22,449] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = "LOG RESERVATION START" [2022-03-23 00:47:24,862] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = Routing to:grandoaks [2022-03-23 00:47:24,925] INFO {TimeoutHandler} - This engine will expire all callbacks after GLOBAL_TIMEOUT: 120 seconds, irrespective of the timeout action, after the specified or optional timeout [2022-03-23 00:47:25,182] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = {"appointmentNumber":11,"doctor":{"name":"thomas collins","hospital":"grand oak community hospital","category":"surgery","availability":"9.00 a.m - 11.00 a.m","fee":7000.0},"patient":{"name":"Nelson Dias","dob":"1940-03-19","ssn":"234-23-525","address":"Lisbon","phone":"8770586755","email":"nelson.dias@wso2.com"},"fee":7000.0,"confirmed":false} [2022-03-23 00:47:25,228] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = {"patientName":"nelson dias","doctorName":"thomas collins","actualFee":"7000.0"} [2022-03-23 00:47:26,039] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = LOG BEFORE SETTLE PAYMENT EP CALL [2022-03-23 00:47:26,074] INFO {LogMediator} - {api:HealthcareAPI} LOG MESSAGE = LOG AFTER SETTLE PAYMENT EP CALL, LOG APPOINTMENT = {"patient":"Nelson Dias","actualFee":7000.0,"discount":20,"discounted":5600.0,"paymentID":"9bb72945-29ac-4274-97f7-93d631b68c18","status":"Settled"}Result
{ "message": "Payment Request published to myQueue. Confirmation will be sent via Email" }
-
-
Aggregate Mediator (Project folder: AggregateMediator) Combining messages as a whole that are saved into state filter using Aggregate Mediator

-
Create 3 APIs
- Name:
Station1API, Context:/station1. In the InSequence, add the following
<log description="LOG START" level="custom"> <property name="LOG MESSAGE" value="STATION 1 LOG START"/> </log> <payloadFactory description="SET PAYLOAD" media-type="json"> <format>{ "station":{ "short_name": "St1", "name": "Station 1", "no_of_launches": 100 } }</format> <args/> </payloadFactory> <respond description="SEND OUT RESPONSE"/>
- Create the last 2 APIs similar to above but change the number
- Name:
-
Create 3 endpoints for the 3 APIs above respectively
- Endpoint Name:
Station1EP, Endpoint Type:HTTP Endpoint, URI Template:http://localhost:8290/station1 - Endpoint Name:
Station2EP, Endpoint Type:HTTP Endpoint, URI Template:http://localhost:8290/station2 - Endpoint Name:
Station3EP, Endpoint Type:HTTP Endpoint, URI Template:http://localhost:8290/station3
- Endpoint Name:
-
Create another API that is responsible to combine the messages into a single message
Name:
StationAPI, Context:/stationInSequence:
- add typical log start message
- add clone mediator by right clicking then choose
Add/Remove Targetthen enter3. It is used clone the same messages. - Once branched out, add defined endpoints to each clone cell.
- assigned the clone ID
aggregate1 - Change all the clone target properties with Sequence Type:
NONEand Endpoint Type:ANONYMOUS. This means no further mediation will be performed for the cloned messages and they will send back to endpoint. Not sure why, The anonymous value will help dump the value to the log
OutSequence:
- Add property mediator called
SET AGGREGATED RESPONSEwith property nameAggregated_Responsesand set Property Data Type:OM. The data type converts XML to OM. - Add aggregate mediator and set
- Aggregate ID to
aggregate1 - Enclosing Element Property to
Aggregated_Responses - Click on Correlation Expression, change it to Expression and type
json-eval($) - Click on Aggregate Expression, change it to Expression and type
json-eval($.station.name)
- Aggregate ID to
- Add Log inside the agggregate mediator and set
- Log Level: Full
- Add new element -> Property Name:
AGGREGATED STATIONS ======>, Property Value Type:Expression, change it to Expression and type$ctx:Aggregated_Responses
- Add Respond called
Send Out Response
The InSequence and OutSequence codes
<inSequence> <log description="LOG START" level="custom"> <property name="LOG MESSAGE" value="STATION MAIN LOG START"/> </log> <clone id="aggregate1"> <target> <endpoint key="Station1EP"/> </target> <target> <endpoint key="Station2EP"/> </target> <target> <endpoint key="Station3EP"/> </target> </clone> </inSequence> <outSequence> <property description="SET AGGREGATED RESPONSE" name="Aggregated_Responses" scope="default"> <value xmlns=""/> </property> <aggregate id="aggregate1"> <correlateOn expression="json-eval($)"/> <completeCondition> <messageCount max="-1" min="-1"/> </completeCondition> <onComplete aggregateElementType="root" enclosingElementProperty="Aggregated_Responses" expression="json-eval($.station.name)"> <log description="LOG AGGREGATE" level="full"> <property expression="$ctx:Aggregated_Responses" name="AGGREGATED STATIONS =======>"/> </log> <respond description="SEND OUT RESPONSE"/> </onComplete> </aggregate> </outSequence>
-
Export Project Artifacts and Run
-
Before, we were using CURL to trigger the API. We can use the embedded http client of WSO2 to do the same thing by going to "HTTP Client" tab in next to "Console" tab. If it doesn't show up, go to Window menu > Show View > Other and search for "HTTP Client"
- On the GET box, type:
http://localhost:8290/stationand click on green ► button
- On the GET box, type:
[2022-04-11 01:10:14,224] INFO {LogMediator} - {api:Station2API} LOG MESSAGE = STATION 2 LOG START [2022-04-11 01:10:14,224] INFO {LogMediator} - {api:Station1API} LOG MESSAGE = STATION 1 LOG START [2022-04-11 01:10:14,229] INFO {LogMediator} - {api:Station3API} LOG MESSAGE = STATION 3 LOG START [2022-04-11 01:10:14,342] INFO {LogMediator} - {api:StationAPI} To: http://www.w3.org/2005/08/addressing/anonymous, WSAction: , SOAPAction: , MessageID: urn:uuid:eddd8fb1-32ea-4a7a-bb84-481517e5ecec, correlation_id: a056e21d-e7e3-48e0-ad50-91221d8430fb_e7386574-233d-4a11-bda0-052807f703f9_1d23d99d-6589-4e79-8d51-a675123c4ab9, Direction: response, AGGREGATED STATIONS =======> = <value/>, Payload: {"value":["Station 1","Station 3","Station 2"]} -
-
Fault Mediator (Project folder: ErrorHandling) This is error handling in WSO2 that typically happens at the endpoint that resulted in timeout.

-
Create new integration project called
ErrorHandling -
Create new REST API Project called
GenderAPIwith context/genderand add 2 logs on theThenandElsesections- Add new filter mediator with Regular Expression set to
Maleand Source to$trp:GenderId. This filter acts like if statement and it uses to filter messages based on jsonpath, xpath or regular expression. $trp is used to read transport headers
- Add new filter mediator with Regular Expression set to
-
Create new sequence called
processingMaleSeqthat will executed when the transport header is male. This sequence is created to generate error explicitly by calling undefined endpoint- Create send mediator
- Add NamedEndPoint to
unexistentMaleEP
-
Add the
processingMaleSeqto theGenderAPI's filter on theThensection. -
On the
Elsesection of the filter, add send mediator and a NamedEndpoint tounexistentFemaleEPto generate runtime error -
Add another sequence to log all error messages and called it
logErrorHandlerSeq<sequence name="logErrorHandlerSeq" trace="disable" xmlns="http://ws.apache.org/ns/synapse"> <log description="LOG FULL ERROR" level="custom"> <property name="text" value="An unexpected error occured"/> <property expression="get-property('ERROR_MESSAGE')" name="message"/> <property expression="get-property('ERROR_CODE')" name="code"/> <property expression="get-property('ERROR_DETAIL')" name="detail"/> <property expression="get-property('ERROR_EXCEPTION')" name="exception"/> </log> </sequence>
-
Create another sequence called
faultSeq-
Add
logErrorHandlerSeqthat was created above -
Add PayloadFactory called
SET ERROR PAYLOADand set mediate type tojsonwith payload{ "error":{ "message":$1, "detail": $2, "exception" :$3 } }Under Args, add 3 arguments with the following expressions
- get-property('ERROR_MESSAGE')
- get-property('ERROR_DETAIL')
- get-property('ERROR_EXCEPTION')
-
Add property to customize the axis2 engine configuration called
SET HTTP STATUS CODE BAD REQUEST- Property Scope:
axis2 - Property Name:
HTTP_SC - Value:
404
- Property Scope:
-
Add respond mediator to send the message back to the client
-
-
Create another sequence called
customErrorHandlerSeq. The send message needs to be invoked to send error messages. By default, it doesn't send error message- Add
logErrorHandlerSeqfrom defined sequences - Add fault mediator called
CUSTOM FAULT MESSAGE- SOAP version:
soap11 - SOAP11:
Server - Actor:
WSO2 - Detail > value:
Wrong Gender. Try with Female!!! - Reason > value:
Wrong Gender
- SOAP version:
- Add log mediator
<log description="LOG FAULT" level="custom"> <property expression="$body" name="LOG FAULT MESSAGE"/> </log>
- Add Payload Factory mediator called
SET ERROR PAYLOAD<payloadFactory description="SET ERROR PAYLOAD" media-type="json"> <format>{ "stacktrace":$1 }</format> <args> <arg evaluator="xml" expression="$body"/> </args> </payloadFactory>
- Add Respond mediator called
SEND OUT ERROR MESSAGE
- Add
-
Go back to the GenderAPI.xml, in the API Resource, change the Fault Sequence Type to
Named Referenceand enterfaultSeqin the Fault Sequence Name. -
Go back to
processingMaleSeq.xml, click on the sequence box to access its property. On Error:customErrorHandlerSeq -
Export Project Artifacts and Run
-
Get data:
-
GET Method:
http://localhost:8290/gender- Using curl:
curl --location --request GET 'http://localhost:8290/gender' --header 'GenderId: Female' - Using WSO2 HTTP Client Headers:
GenderId=Femalethen click on green ► button
[2022-05-10 00:49:33,835] INFO {LogMediator} - {api:GenderAPI} GENDER IS =====> = Female [2022-05-10 00:49:33,836] INFO {LogMediator} - {api:GenderAPI} [2022-05-10 00:49:33,847] INFO {LogMediator} - {api:GenderAPI} text = An unexpected error occured, message = Couldn't find the endpoint with the key : unexistentFemaleEP, code = 305100, detail = Couldn't find the endpoint with the key : unexistentFemaleEP, exception = Couldn't find the endpoint with the key : unexistentFemaleEP- HTTP Response:404
{ "error":{ "message":Couldn't find the endpoint with the key : unexistentFemaleEP, "detail": Couldn't find the endpoint with the key : unexistentFemaleEP, "exception" :Couldn't find the endpoint with the key : unexistentFemaleEP } } - Using curl:
-
GET Method:
http://localhost:8290/gender- Using curl:
curl --location --request GET 'http://localhost:8290/gender' --header 'GenderId: Male' - Using WSO2 HTTP Client Headers:
GenderId=Malethen click on green ► button
[2022-05-16 00:53:08,482] INFO {LogMediator} - {api:GenderAPI} GENDER IS =====> = Male [2022-05-16 00:53:08,484] INFO {LogMediator} - {api:GenderAPI} LOG MALE = MALE [2022-05-16 00:53:08,500] INFO {LogMediator} - {api:GenderAPI} LOG MESSAGE = PROCESSING MALE SEQ START [2022-05-16 00:53:08,509] INFO {LogMediator} - {api:GenderAPI} text = An unexpected error occured, message = Couldn't find the endpoint with the key : unexistentMaleEP, code = 305100, detail = Couldn't find the endpoint with the key : unexistentMaleEP, exception = Couldn't find the endpoint with the key : unexistentMaleEP [2022-05-16 00:53:08,517] INFO {LogMediator} - {api:GenderAPI} LOG FAULT MESSAGE = <soapenv:Body xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"><soapenv:Fault><faultcode xmlns:soap11Env="http://schemas.xmlsoap.org/soap/envelope/">soap11Env:Server</faultcode><faultstring>Wrong Gender</faultstring><faultactor>WSO2</faultactor><detail>Wrong Gender. Try with Female!!!</detail></soapenv:Fault></soapenv:Body>- HTTP Response:500 Internal server error
{ "stacktrace": { "Body": { "Fault": { "faultcode": "soap11Env:Server", "faultstring": "Wrong Gender", "faultactor": "WSO2", "detail": "Wrong Gender. Try with Female!!!" } } } } - Using curl:
-
-







