Rhino javascript runtime and IBM API Connect

Rhino javascript runtime and IBM API Connect

When we started looking at DataPower a few years back XSLT was the only way to write translations.

Recently (and maybe someone can tell me when), gateway script became an option for developing DataPower translations.

We have had support for the XSLT that is used by DataPower and by the ACE/IIB/WMB XSLTransform node.?

Adding support for gateway script should also useful for the teams we support that make use of APIC/DataPower.

Also, I did find this really interesting LinkedIn post about the energy cost and impact for different programming languages.

In the case of gateway script, DataPower will be running JavaScript. It's 4x more costly then C, so anything we can do to help improve the code quality and performance?of gateway script is good for the environment. Also, with a big move to cloud and charge by usage, better code that is more efficient can help you save money in the long term on CPU costs,?as well as the intrinsic cost benefits of lowering your technical debt.

Taking into account the SonarQube ecosystem, SonarQube has support for Typescript and JavasSript as first class languages - meaning no additional plugins are needed and you can run the analysis straight out of the box.

The issue is, that when we analyze the a APIC file that contains gateway script code, we are starting with yaml file. So there's no current way to?leverage SonarQube's in built support for JavaScript as we don't have a JavaScript file (we have like a yaml file with some JavaScript and XSLT all merged together).

It would be interesting if some from SonarQube comes across this post and sends me the details of an API that does this. That would be a good read.?

So without first class support and without re-engineering the entire JavaScript plugin, I was looking for some way of?having some static code analysis without re-engineering the whole SonarQube offering.


What I thought would useful that would add value for developers is to be at least able to :

  1. do some checks on the code - ideally ones that are more closely aligned to DataPower deployment?
  2. check that the code is syntactically valid
  3. check that the code is referencing the inbuilt gateway script classes?


For (1), code checks, we have some already in place, such as APIC7 - Console.debug used in gatewayscript (APIC)

For (2) and (3) we can might need some coding magic.

JavaScript doesn't have a compiler per-se. Typescript has. So with Typescript you can have classes and interfaces that you can validate your code against at compile time to pick up errors.

But gateway script is JavaScript. So there is not compiler to validate the code.

There are some frameworks for working JavaScript code from java.

The first attempt was to embed Graalvm?

And then use the Graalvm library to parse and validate the JavaScript code that we extract from the yaml file.

It looked promising, but there are some quirks around how SonarQube loads classes and how Graalvm wants to be loaded.

SonarQube has something called a ClassRealm, which Graalvm can't load classes out of. So all the test cases worked perfectly, but then when it ran with a different classloader it failed.

I wasn't able to work out how to get around that issue. So I looked at another project - Rhino?

Rhino is a slightly older framework that has been super-seceded by Graalvm and probably others.

Again, I was able to use Rhino to to develop logic that would check the gateway script was syntactically correct using context.compileString()

To validate that the object references were at least valid signatures, I used script.exec()

This basically ran the logic and mocked out the gateway objects that were called.

The idea being if a mocked object was called then that suggests that at least the signatures were correct, but if the object didn't exist (no mock) or the method on the mock was missing,?then the signatures were incorrect. I was also able to run this from a test case.

But then again once I used the different class loader, Rhino broke.

So I created some dirty hacky code to get around this issue and made use of a separate classloader of a single jar that contained the Rhino classes.

File jarFile = new File("rhino-1.7.15-BCT-2-jar-with-dependencies.jar")

URL url = jarFile.toURI().toURL();

URL[] urls = {url};

ClassLoader cl = new URLClassLoader(urls);;        


From there I could call Rhino from that separate classloader. Sort of.

Outside of the classloader and calls from inside the classloader involve different classes. To you and me they look the same but the JVM treats them as separate types.

So I couldn't use any classes to make calls when using the new custom classloader. I think it was because the compiled class that was being casted was different.

I ended up having to use reflection to instantiate new objects:


Class cls = cl.loadClass("org.mozilla.javascript.Context")

System.out.println(cls.getClass().getCanonicalName());

Object obj = cls.newInstance();        

Even casting to these newly created instances had classloading issues.

So when to call any methods I again had to use reflection to find the method and invoke it with the appropriate arguments:


Method setMethod = mozillaJsContext.getClass().getMethod("setApplicationClassLoader", ClassLoader.class);			
Object[] classLoaderArgs = {cl};
setMethod.invoke(mozillaJsContext, classLoaderArgs);        

Far from ideal, error prone, type unsafe and slow. But it worked. So progress.

Next I had to create some mocks to represent gateway objects that scripts interact with:

So for the "apim" object, I created a


public class APIMWrapper extends ScriptableObject        


and added methods such as?


  @JSFunction
??public void setvariable(ScriptableObject thisObj)        


and


@JSFunction

public Object getvariable(ScriptableObject thisObj)        


Again, I hit some class cast issues, as the ScriptableObject that was created wasn't from the same classloader.

So I used a GSON trick and converted the object that I had into JSON and then recreated the same object with the running classloader.

I still had to know the class name I was working with, so there's a bit ugly if statement that does that, but again it works. Again, progress.


if (createName != null && createName.equals("au.com.bettercodingtools.sonar.messagebrokersonarplugin.apiconnect.wrappers.APIMWrapper")
{
??	proto = (Scriptable)?gson.fromJson(gson.toJson(o1), APIMWrapper.class);???????

})        


Then when I called script.execute() I could see my mock object being created and the methods being called.

I did have a few other places where the class casting and instance of across the loaders failed.

It didn't throw an exception which was worse. The logic dropped out. But after some debugging it's working.

I ended creating a patched Rhino, with all the little tweaks and extra checks that lets it work. Which you can see here if you are interested.


The rule that we ended up adding to help with APIC validation is:

APIC18 - Failed to parse gateway script (APIC)


More information on our products and on pricing can be found on our website:

https://bettercodingtools.com

You can also reach me via email at:

[email protected]

Or contact me via the contact page on our website:

www.bettercodingtools.com/contact

Regards

Richard

Erol Gurcinar

I help lead and protect Australian / New Zealand businesses and their customers | Leadership | Mentor | Speaker | Architect

2 年

Awesome work Richard! keep these tutorials coming through

回复

要查看或添加评论,请登录

Richard Huegill的更多文章

  • Opaque parsing and performance

    Opaque parsing and performance

    A while back I was asked what our tooling could do to help improve performance for ACE/IIB code. Whether that means…

    2 条评论
  • Embedding DrawIO IIB/WMB/ACE flow diagrams in Confluence

    Embedding DrawIO IIB/WMB/ACE flow diagrams in Confluence

    Happy New Year My last demonstration was all the way back in time, 2024, almost 3 months and 4 hangovers ago. Or 3…

  • Apologies for the broken webinar

    Apologies for the broken webinar

    So last week we attempted to do a webinar on creating Confluence pages to summarize WMB/IIB/ACE code. Unfortunately…

  • Something I don't know too much about

    Something I don't know too much about

    It's ACE and Java classLoader's (but I'm sure friends will be able to point out many others). Actually this is only…

  • Confluence page generation for IIB/WMB/ACE applications

    Confluence page generation for IIB/WMB/ACE applications

    The larger an organization becomes, the more challenging that it is for an organization to manage is combined knowledge…

    2 条评论
  • WMB / IIB / ACE GitLab pages (with sound this time)

    WMB / IIB / ACE GitLab pages (with sound this time)

    The recent version 17 release of GitLab has added GitLab pages. Last week we did a live demo on using GitLab Pages to…

    2 条评论
  • GitLab Pages and IIB

    GitLab Pages and IIB

    The recent version 17 release of GitLab has added some enhancements and fixed some issues. On the of the newer features…

  • SonarQube 10.6 released

    SonarQube 10.6 released

    SonarSource recently a new version of their Sonarqube platform - SonarQube 10.6 For us, we have to update some of build…

  • Squid's (not the game)

    Squid's (not the game)

    When I develop code, I have never gotten it write first time. This is compounded when you work integration tooling.

  • Information radiators for IIB/ACE/WMB projects managed in GitLab

    Information radiators for IIB/ACE/WMB projects managed in GitLab

    Some of the functionality that we have in our plugin is more the "art" side of software engineering then the hard…

社区洞察

其他会员也浏览了