Skip to Content
Author's profile photo Eng Swee Yeoh

Demystifying Custom Adapter Development: Part 4 – Modifying the Adapter’s Functionality

Introduction

Now that we have familiarise ourselves with the various aspects of the custom adapter development, we can proceed with actual changes to modify the adapter’s functionality. As an example, we will use the requirement described in the following post and implement the solution using a custom adapter.

Synchronous retrieval of dynamically specified file from file system/server

In summary, we want to synchronously retrieve the contents of a file from the PI file system by providing an input payload containing the full path to the file. This is currently not supported by the File adapter in either sender or receiver mode. To achieve this, we will modify the receiver functionality of the custom adapter to:-

i) Extract from request payload the full path to the file

ii) Extract the file contents

iii) Generate the synchronous response with payload containing the file contents

Adapter Metadata Changes

We will update the SampleRA.xml file which defines the adapter metadata. Following are the changes required:-

i) Remove old Transport Protocol and replace with new Transport Protocol

ii) Remove old Message Protocol and replace with new Message Protocol

iii) Add new channel attribute and corresponding details

First of all, remove the section for the previous File Transport Protocol in the Outbound section of the file. Replace it with the following:-

  • Transport Protocol Name – NFS
  • Uses Message Protocol SyncGet (to be defined below)
  • Has only one channel attribute xpathToFile (to be defined below)

/wp-content/uploads/2016/05/transport_prot_954856.png

Next, remove the previous JCA10 Message protocol in the Outbound section of the file. Replace it with the following:-

  • Message Protocol name – SyncGet
  • Contains localejbs/ModuleProcessorExitBean in the module sequence with just one parameter JNDIName

/wp-content/uploads/2016/05/message_prot_954875.png

Add details for channel attribute xpathToFile as shown below.

/wp-content/uploads/2016/05/xpath_attribute_954876.png

Update the adapter metadata in ESR by importing this modified SampleRA.xml file and activating the Adapter Metadata object.

Note: For the sake of simplicity, housekeeping is performed on the remaining content of the adapter metadata file even though there are a lot of attributes previously defined which are left unused/orphaned.

Source Code Changes

Next, we will update the source code of the adapter to modify its functionality. Unfortunately, certain parts of the sample adapter’s source code is not written in a way that makes it easy to enhance or modify. Therefore, there will be numerous commenting and deletion of existing codes.

Additionally, we will be using the utility library used in my custom adapter modules in order to reuse some existing logic and simplify the development. So, head over to equalize-xpi-modules Latest Release and download the com.equalize.xpi.util.jar file. Then add it as an External JAR in the build path of the project as shown below.

/wp-content/uploads/2016/05/util_jar_954877.png

i) SPIManagedConnection

First of all, we will comment out some of the private instance attributes that are used by the existing sample adapter logic.

/wp-content/uploads/2016/05/smc_1_954878.png

Next, in the constructor of the class, comment out the trace statement. (Actually there is nothing really wrong with this statement, just that it incorrectly uses one of the commented out attributes above – optionally the statement can be modified accordingly instead of being commented out).

/wp-content/uploads/2016/05/smc_2_954879.png

Still within the constructor, comment out the try-catch block (all the way until the end of the constructor) that accesses the old channel attributes (i.e. fileOutDir, fileOutPrefix, etc). This is replaced with a try-catch block that accesses the new channel attribute xpathToFile.

/wp-content/uploads/2016/05/smc_3_954880.png

Delete the following methods (no screenshot shown):-

  • getOutFile()
  • getAsmaGet()
  • getAsmaError()

Create the following getter method getChannel() for private attribute channel.

/wp-content/uploads/2016/05/smc_4_954881.png

In the destroy() method, comment out the statement as shown below.

/wp-content/uploads/2016/05/smc_5_954882.png

In the cleanup() method, comment out the if-else block as shown below.

/wp-content/uploads/2016/05/smc_6_954883.png

ii) XIConfiguration

Similar to above, comment out the logic that uses the old channel attributes. This part of the logic ensures that the adapter framework is able to read the channel attributes during initialization or when a channel is added. For simplicity, we will not change the name of the variables (dir & name) that store the retrieved values.

As shown below, comment out the old attributes in methods channelAdded() and init() and add logic using xpathToFile.

/wp-content/uploads/2016/05/xicfg_1_954884.png

In method getChannelStatus(), comment out the section that uses the old attributes and add logic using xpathToFile.

/wp-content/uploads/2016/05/xicfg_2_954885.png

iii) CCIInteraction

Delete the following methods (no screenshot shown):-

  • send()
  • call()

Comment out the existing section that executes the send() or call() method. This is where we will be introducing the core part of the new logic to fulfill the requirement above. Add a new statement that uses a new method retrieveFile() as shown below.

/wp-content/uploads/2016/05/cci_954886.png

Subsequently, add logic for method retrieveFile() using the following code:-


    private Record retrieveFile (InteractionSpec ispec, Record input, SPIManagedConnection mc) throws ResourceException {
    Message msg = ((XIMessageRecord) input).getXIMessage();
    MessageKey amk = new MessageKey(msg.getMessageId(), MessageDirection.INBOUND);
        try {
        // Parse the XML input using DOM and evaluate the XPath expression
    ConversionDOMInput domIn = new ConversionDOMInput(msg.getDocument().getText());
    String xpathToFile = mc.getChannel().getValueAsString("xpathToFile");
    String inFile = domIn.evaluateXPathToString(xpathToFile);
    this.audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "XPath expression: " + xpathToFile);
    this.audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "XPath expression value: " + inFile);
    // Retrieve the file contents
    InputStream inpStr = new FileInputStream(new File(inFile));
    ByteArrayOutputStream baos = Converter.toBAOS(inpStr);
    // Create response XI message
    this.audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "File retrieved, constructing response message");
    XIMessageRecordImpl output = new XIMessageRecordImpl(msg.getToParty(),msg.getFromParty(),
      msg.getToService(), msg.getFromService(),
      msg.getAction());
    Message response = output.getXIMessage();
    // Populate payload and attributes of response message
    XMLPayload payload = response.createXMLPayload();
    payload.setContent(baos.toByteArray());
    payload.setName("MainDocument");
    payload.setDescription("EQ Adapter Synchronous Response");
    payload.setContentType("application/xml");
    response.setDocument(payload);
    response.setRefToMessageId(msg.getMessageId());
    this.audit.addAuditLogEntry(amk, AuditLogStatus.SUCCESS, "Response message construction completed");
    return (Record) output;
    } catch (Exception e) {
    this.audit.addAuditLogEntry(amk, AuditLogStatus.ERROR, e.getMessage());
    ResourceException re = new ResourceException(e.getMessage());
  TRACE.throwing("retrieveFile (InteractionSpec ispec, Record input, SPIManagedConnection mc)", re);
  throw re;
    }
    }






Ensure that any missing import statements are added accordingly.

In a nutshell, the above logic will be executed when the receiver channel processes a message. It determines the dynamic file that needs to be retrieved based on the evaluated value of Xpath expression in the input payload. It then retrieves the file content and constructs the response payload based on it.

Deployment Descriptor Changes

The following is an optional change to the ra.xml file. As described in previous part, this does not affect functionality.

/wp-content/uploads/2016/05/raxml_954887.png

Manifest File Changes

Prior to deployment, it is recommended to change the version number of the deployment in the SAP_MANIFEST.MF file. In the example below, it is changed to 1.0.0 to indicate the first major release of this adapter based on Semantic Versioning.

/wp-content/uploads/2016/05/sap_mani_954888.png

Note: If there is a need for multiple trial and error deployments prior to a proper release, we can supplement the versioning with an additional fourth digit(s) and increment it at each new deployment, i.e. 1.0.0.1, 1.0.0.2, 1.0.0.3.

Export and Deployment

Once this is all done, we are nearly ready for deployment. Before that, we need to ensure that the External JAR file for the utility library is also included in the RAR project. Copy and paste the file into the connectorModule folder of the RAR project as shown below.

/wp-content/uploads/2016/05/ext_jar_954895.png

Proceed to complete the steps required for deployment (refer to steps in Part 1)

  • Export JAR file of adapter project to connectorModule folder of RAR project
  • Export RAR file of RAR project
  • Undeploy previous version if already deployed so that there is no version clash during deployment
  • Deploy RAR file to PI server

Configuration and Testing

We are now ready to configure the adapter and test it out.

Configure a receiver channel using the custom adapter. The new transport protocol and message protocol will be reflected during selection of the adapter type.

/wp-content/uploads/2016/05/channel_1_954902.png

Subsequently, the new channel attribute will be available for input. Enter the XPath expression as follows:-

/wp-content/uploads/2016/05/channel_2_954903.png

After the channel has been activated, configure an integration scenario (classical, ICO or IFlow) that uses the receiver channel. This step will not be covered in this post.

For testing, I will try to retrieve a file from the home directory of the PI admin user. The screenshot below shows the CSV file that I intend to retrieve.

/wp-content/uploads/2016/05/inputfile_954920.png

Using the built-in test functionality, a request message is triggered to the integration scenario. The message contains an XML payload with the full path to the CSV file.

/wp-content/uploads/2016/05/test_msg_954921.png

After the message has been triggered and processed successfully, we can view the following log entries generated by the adapter logic in the message log.

/wp-content/uploads/2016/05/log_954922.png

Finally, we are able to view the content of the retrieved file within the response message’s payload.

/wp-content/uploads/2016/05/payload_954926.png

Source Code

Both Eclipse projects (adapter and RAR) are available in the following GitHub repository.

GitHub equalize-xpi-adapter-sample repository

Conclusion

Phew! Finally, after so many elaborate steps, we have managed to customise the adapter’s functionality according to the requirement.

As demonstrated, although custom adapter development might seem overwhelmingly complex, once we dissect it into bite-size pieces, it is not impossible to gain an understanding of it and develop an adapter to meet a particular integration requirement.

It is important to note that this series is not meant to cover every aspect of custom adapter development, but to introduce the basic and key concepts in order to lower the entry barrier for such developments. As such, the source code modifications are done in a simple manner with the aim of introducing the concepts. Therefore, it is by no means the definitive way to develop a production-ready adapter. Once one is familiar with adapter development, it is recommended that the source code be enhanced via the usual techniques of modularisation, refactoring or usage of design patterns.

Next up in this series, we will be looking at changing the sender side functionality to an HTTP poller with OAuth 2.0 authentication.

Other Parts of this Series

Demystifying Custom Adapter Development: Part 1a – Cloning the Sample JCA Adapter

Demystifying Custom Adapter Development: Part 1b – Cloning the Sample JCA Adapter

Demystifying Custom Adapter Development: Part 2 – Examining the Adapter’s Key Classes and Methods

Demystifying Custom Adapter Development: Part 3 – Examining the Deployment and Metadata Files

Demystifying Custom Adapter Development: Part 5 – Creating an HTTP Poller with OAuth 2.0 authentication

Assigned Tags

      6 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Vadim Klimov
      Vadim Klimov

      Eng Swee, thank you for such gradual and detailed examples and clarifications that you brought across this blog series. I enjoyed reading all parts and definitely found a lot of interesting and extremely helpful material there. I can only imagine how much work has been done by you behind the scenes when writing down these blogs, and this inspires respect for this work done. Thank you for continuous sharing of useful information with the community.

      Best regards,

      Vadim

      Author's profile photo Eng Swee Yeoh
      Eng Swee Yeoh
      Blog Post Author

      Hi Vadim

      Thank you for the positive feedback and encouraging comments.

      Like you, writing down the details in blog posts helps me make sure that my own understanding of the subject is accurate. It also opens up an avenue for constructive feedback from the community if any area can be further improved. I'm really glad to know that you have found this series useful 🙂

      Regards

      Eng Swee

      Author's profile photo Former Member
      Former Member

      Hi Eng Swee,

                   Your blog is very lucid and descriptive. You have really explained a complex topic in a very detailed and simple manner.

      Thanks

      Ravijeet

      Author's profile photo Kanniyappan Rajakumar
      Kanniyappan Rajakumar

      Hi Eng,

      I have created a custom adapter by following your blog and works perfectly, I am looking for Custom Sender channel which will act as Soap/REST sender, where channel will wait for HTTP request and process the message. could you provide your suggestions to implement the same.

      Regards,

      Rajakumar

       

       

      Author's profile photo Eng Swee Yeoh
      Eng Swee Yeoh
      Blog Post Author

      Hi Rajakumar

       

      A listening sender is a more complex development, and unfortunately I do not have an example to share. If it's just an HTTP request, I suggest that you use existing SOAP/REST adapter as a listener and perform customisation via other means (adapter module, mapping, etc).

       

      Regards

      Eng Swee

      Author's profile photo Kanniyappan Rajakumar
      Kanniyappan Rajakumar

      Hi Eng Swee,

      Thanks for the suggestion, we will use REST adapter as a workaround.

      Regards,

      Rajakumar