Sunday, December 11, 2016

JMS Connectors and Queues

Goal of this exercise is to design a web service solution wherein we are hosting the service and it’s consumed by a SaaS solution. In this POC I am exposing/hosting a simple SOAP based XML web service. 
I am testing this out on PeopleSoft HCM 9.1 running PT 8.53.26.

Transaction data flow is as follows:
  • Expose web service which takes in the request message
  • Request XML message is written to the request queue on the JMS server
  • A listener process reads the request queue and fetches the message
  • A process/program processes the request message and populates the result or response XML message on the response queue.
  • The web service reads the response queue and sends the response back to the caller
JMS Correlation ID is used as a key to fetch/write the appropriate message from/to the queue.

JMS Server and Queue Setup

Following are the steps to setup a JMS Server, Connection Factory and three queues, Request queue, Response queue and Error queue via the weblogic admin console. I am running Weblogic version 10.3.6.0.13.

Open the Weblogic Console and navigate to Services > Messaging > JMS Modules, Click Lock&Edit, then New, then enter a name for the JMS Module and click "Next”:






























Select PIA as Target and click "Next": 
Uncheck "Would you like to add resources to this JMS system module?" and click "Finish"








Navigate to Services > Messaging > JMS Servers and Click "new", enter the name of the JMS Server and click "Next":













Navigate to Services > Messaging > JMS Modules and select the JMS Module created in previous steps.














Now create a queue as below. 
















Similarly create two more queues, the ResponseQueue and ErrorQ












PeopleSoft setup

Nodes:
Create two nodes, of type external, one for listening on the request queue and another one to write back to the response queue. Setup is pretty much identical except for the queue name. Use the JMSTARGET connector ID for both the nodes and key in properties as follows:
JMSFactory = MyConnectionFactory (this is the JNDI name defined via the weblogic console)
JMSMessageType = Text
JMSProvider = Weblogic
JMSQueue = RequestQueue for one node and ResponseQueue for the other node
JMSUrl = Weblogic JMS Server in the form t3://server:port

Messages:

Create two non-rowset based messages, again one for request and the other for response.
Added a schema to each message (this is optional, you can manage it via peoplecode too). Request message for this POC has the following structure:

<?xml version='1.0'?>
<person>
    <emplid>string</emplid>
</person>

and response message would be as follows:

<?xml version='1.0'?>
<person>
    <name>string</name>

</person>

Service Operation:

Created a service and assigned two service operations to it. Assigned security to both the service operations. The listener service operation has handler application package onNotify peoplecode defined, nothing for the response service operation. 
So in case of the listening service operation the routing would be:
Sender Node = newly created JMS listening node (above)
Receiver Node = local default node 

In case of response service operation the routing would be:
Sender Node = local default node
Receiver Node =  newly created JMS response node (above)

Handler Peoplecode:

method onNotify
   /+ &MSG as Message +/
   /+ Extends/implements PS_PT:Integration:INotificationHandler.OnNotify +/
  
   Local XmlDoc &xmldoc = &MSG.GetXmlDoc();
   Local XmlNode &xmlNode = &xmldoc.DocumentElement;
   Local string &emplid = %This.FetchDataValue(&xmldoc, 1, "emplid");
   Local string &name;
  
   SQLExec("select ISNULL((SELECT NAME FROM PS_PERSONAL_DATA WHERE EMPLID = :1),'No Name')", &emplid, &name);
  
/* create the reponse message */
Local Message &ResponseMSG = CreateMessage(Operation.JMS_RESP_SO, %IntBroker_Request);
   Local string &xmldata = "<?xml version='1.0'?><person/>";
   Local XmlDoc &ResponseDOC = CreateXmlDoc(&xmldata);
   &xmlNode = &ResponseDOC.DocumentElement;
  
   Local XmlNode &NameNode = &xmlNode.AddElement("name");
   &NameNode.NodeValue = &name;
  
/* create the reponse message */
   &ResponseMSG.SetXmlDoc(&ResponseDOC);
   %IntBroker.Publish(&ResponseMSG);
  
end-method;

method FetchDataValue
   /+ &xmldoc as XmlDoc, +/
   /+ &row as Number, +/
   /+ &dataTag as String +/
   /+ Returns String +/
  
   Local string &dataValue;
   Local array of XmlNode &dataList;
  
   &dataList = &xmldoc.DocumentElement.FindNodes(&dataTag);
   If &dataList.Len = 0 Then
      &dataValue = "";
   Else
      &dataValue = &dataList [&row].NodeValue;
   End-If;

   Return &dataValue;

end-method;

IB changes:

Integrationgateway.properties file can be edited via the gateway setup properties link from the gateway component. Updated that as follows for the local gateway.

Showing only the pieces that I changed updated, under the JMS configuration section. need to only define the listening queue here. Error queue is optional but good to have. 

ig.jms.Queues=1

ig.jms.Queue1=RequestQueue
ig.jms.Queue1.Provider=Weblogic
ig.jms.Queue1.JMSFactory=MyConnectionFactory
ig.jms.Queue1.Url=t3://server:port
# following service operation details are required only for a listening operation.  
ig.jms.Queue1.OperationName=JMS_REQ_SO.v1
ig.jms.Queue1.OperationVersion=v1
ig.jms.Queue1.RequestingNode=JMS_LISTENER_NODE
ig.jms.Queue1.DestinationNode=PSFT_HR


ig.jms.ErrorQueue=ErrorQueue
ig.jms.ErrorQueue-Provider=Weblogic
ig.jms.ErrorQueue-JMSFactory=MyConnectionFactory
ig.jms.ErrorQueue-Url=t3://server:port 


Testing:


Create a message via the weblogic console in MyQueue1 or the RequestQueue and verify via integration broker monitor that the message is picked up processed and written to MyQueue2 or the ResponseQueue.

Any issues or errors will be written to errorLog.html and/or msgLog.html files on the webserver.