Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
Lionel_Hu
Explorer
Dear CPI folks,

We used to write Groovy scripting in our CPI integration projects. But if you are a big fan of JavaScript like me, why not to use JavaScript to write the script in your next CPi integration project?

Unfortunately by searching on SAP community, it is very hard to find any resource on how to do that. With some research on this topic, I managed to test successfully with JS script powerned iFlow. That's the reason why I want to share my findings in the community.

Background


Based on SAP Help Portal document: https://help.sap.com/docs/cloud-integration/sap-cloud-integration/define-local-script-step. The scripting engine that was used in CPI runtime environment is so called 'Rhino'.








JavaScript Engine (Rhino)



1.7 R4



It is very easy to find the home of this Rhino engine on the Internet: https://github.com/mozilla/rhino/. The README document is showing very clear information that the Rhino is "an implementation of JavaScript in Java".

The API document is here: https://javadoc.io/doc/org.mozilla/rhino/1.7R4/index.html

Local Test Environment


It is always joyful that we can play around with scripting locally before using it officially in our project.

To prepare a playground on your local environment. Download the jar file from https://mvnrepository.com/artifact/org.mozilla/rhino/1.7R4, put it in somewhere on your PC. And then use the command line "java -jar rhino-1.7R4.jar -debug -version 180" to start the shell to test.


Rhino Shell in Command Line


I still haven't figure out what is the difference between these possibile version no: -version 100|110|120|130|140|150|160|170|180. But from the screenshot above you can see, "help()" is very useful to get some idea on how to setup the shell environment and version() will give you exactly what you set for the version.

This is very similar to the Node.JS shell that can be used to test some js function/class before adding to your productive js file.



To test the local script file, you can use "-f" parameter like following:

java -jar rhino-1.7R4.jar -f <filename of your rhino js file>





CPI iFlow Implementation:


Now let's implement some simple CPI scripting features in the iFlow:

When you select "JavaScript" as the scripting language in CPI iFlow design, you will see the template that SAP has prepared for you, so you don't have to build the wheel again.


overview of the iFlow design


In my test iFlow design,

  • The iFlow will be triggered only once by Timer so that the test can be triggered/managed by deployment on demand.

  • The XML payload was generated by a SFAPI (SOAP) query to the Compound Employee entity of sandbox SFSF tenant.

  • I chained two scripts (JS and Groovy) in order to get the logging message to compare the results.

  • The XML Modifier is used to remove the XML head from the payload so that your JS script can work without any issue.


Note:


I tried to use Regular Expression in JS to replace(remove) the XML head but unfortunately that was failed with the error "The choice of Java method java.lang.String.replace matching JavaScript argument types (function,string) is ambiguous; candidate methods are: class java.lang.String replace(char,char)"). I guess that was caused by the supportability of the RegEx in Rhino. As long as the XML modifier can do the trick, I don't care about it anymore.
payload = payload.replace(/<\?xml[^>]*\?>/, "");

The script itself is very straight forward. The only thing that I want to mention is that the Rhino JS engine supports the ECMAScript for XML (E4X) which is an extension to provide the native support of XML in Rhino.

So you can easily access the XML payload with single line of code, but please bear in mind if you use "typeof parsedPayload" to check the object type, it is "xml" instead of "object".
const parsedPayload = new XML(body);

you can get more details about this topic from here: https://svn.wso2.org/repos/wso2/tags/carbon/0.1alpha/mashup/java/xdocs/e4xquickstart.html#modifying

Source Code of my script:
/* Refer the link below to learn more about the use cases of script.
https://help.sap.com/viewer/368c481cd6954bdfa5d0435479fd4eaf/Cloud/en-US/148851bf8192412cba1f9d2c17f...

If you want to know more about the SCRIPT APIs, refer the link below
https://help.sap.com/doc/a56f52e1a58e4e2bac7f7adbf45b2e26/Cloud/en-US/index.html */
importClass(com.sap.gateway.ip.core.customdev.util.Message);

function processData(message) {
//Body
var body = message.getBody(java.lang.String);
const payload = new XML(body);

const counter = payload.CompoundEmployee.length();

const firstElement = payload.CompoundEmployee[0];

const messageLog = messageLogFactory.getMessageLog(message);
if(messageLog != null){
messageLog.setStringProperty("JS Logger", "Logger")
messageLog.addAttachmentAsString("JavaScript MessageCounter", counter, "text/plain");
messageLog.addAttachmentAsString("JavaScript First CompoundEmployee", firstElement , "text/plain");
}

return message;
}

After deployment of the iFlow, I got the successfully result as expected. The total number of Compound Employee records and the first employee payload appear in the attachment.


Counter:


First Employee Payload:


 

Simple like that !

With the power of JavaScript native JSON support, you may get more from the JSON based integration scenarios with JSON.parse and JSON.stringify.

I hope this blog can inspire you on using Java Script as the scripting language in your next project. Enjoy it. 🙂

Thank you

Best Regards
Lionel
10 Comments
dhruv_mehta
Active Contributor

This is cool! 🙂 Will try it. Is it possible for CPI on Cloud Foundry?

MortenWittrock
Active Contributor
0 Kudos
Yes; JavaScript is supported both in Neo and Cloud Foundry.

Have fun with Cloud Integration!

Regards,

Morten
Lionel_Hu
Explorer
Hi Dhruvin,

Yes, it works the same way as Neo. I did a test in my CF trial account and it runs fine without any problem.

To make it simple, I fetch data from Northwind and generate a csv with ProductID and ProductName only.



victorgonzalez
Explorer
0 Kudos
Thanks Lionel, your blog is very useful.

Do you know if it is posible import libreries, for example from https://jwt.io/#libraries-io, for token management?

 

Thanks!

Victor
Lionel_Hu
Explorer
0 Kudos
Hi Victor,

I'm afraid that it is impossible. As Rhino is only a JS engine which is used to run JS script based on Java environment. It did not implement the full feature of modularization and other advanced techniques from JS world.

However it still can use the library class from Java just like Groovy. e.g. when you create the skeleton JS script file, it already includes these two imports:
importClass(com.sap.gateway.ip.core.customdev.util.Message);
importClass(java.util.HashMap);

Unfortunately I'm not a Java guy so I have no idea how these importClass work.

In Rhino command line mode, there're some modularization functions like following, but I haven't test them yet. I will do some test once I have some free time in the next few weeks.
defineClass(className) Define an extension using the Java class
named with the string argument.
Uses ScriptableObject.defineClass().
load(["foo.js", ...]) Load JavaScript source files named by
string arguments.
loadClass(className) Load a class named by a string argument.
The class must be a script compiled to a
class file.

Thank you

Best Regards
Lionel
otto_frost4
Participant
0 Kudos

Why can not Nashorn be used as script engine? Nashorn is included in sap java 8 so it would be trivial for sap to enable it.

We use alot of nashorn javascript in sap po and we do not want to backport from nashorn to rhino.

pwedemann
Discoverer
0 Kudos
Do you know any plans/time frame to update the outdated engine (Rhino 1.7R4, released 2012-06-18), to a newer version which contains fixes for the 64K byte limit which we currently get when heavily using the JS engine with large scripts?
Lionel_Hu
Explorer
0 Kudos
Hi Otto,

Thank you for your reply.

Unfortunately I'm not in a good position to answer this question as I'm not part of the product management of CPI. As a generic recommendation, you may consider to deploy the Nashorn scripts to BTP platform on top of Java application with Cloud SDK, just like in the SAP PO on top of NW Java platform.

But again, I'm not a Java guy with Nashorn expertise at all.

Thank you

Best Regards
Lionel
Lionel_Hu
Explorer
0 Kudos
Hi Philip,

Thank you for your reply.

This is true that the Rhino engine is quite old and outdated in CPI platform. I'm not sure if there's any further plan to upgrade the component or not. But if you are running the Integration Suite on Cloud Foundry. As an alternative workaround, you may consider to wrap the APIs in APIM to expose it to your CPI iFlows. The js engine can be deployed on CF platform as part of the web application with SDK.

Yes, some homework is required to get it done.

Thank you

Best Regards
Lionel
pwedemann
Discoverer
0 Kudos
Thanks for the update, but we switched to Java (or more specific Kotlin/JVM) by uploading custom Java archives containing all of our dependencies.

We need custom (Kotlin Multiplatform) dependencies to share/reuse existing code and I wasn't aware you can upload custom jar files, so I tried the JS workaround. The workaround uses Kotlin/JS translated to one big JS file containing all Kotlin dependencies. Unfortunately, this JS file was too big for SAP CI resulting in a runtime crash due the 64K limit caused by the outdated engine, newer versions contain the fix.

But this workaround isn't need anymore, I missed the part of uploading custom Java archives at the beginning...

Java/Kotlin JVM and the Script API and uploading our dependencies is all we need 🙂

Best regards

Philip