/ Development  

A secret (custom) call in the XPath editor exposed

Hi there AppWorks fans,

Welcome to a new installment of AppWorks tips.

This time a very nice feature (inspired by this forum topic) that makes it possible to call some custom Java code from the XPath editor! XPath?…Yes, also known as the expression editor that we use in the message mapping for our activities in a BPM. We know it is possible to add all kinds of nice expressions OOTB, but wouldn’t it be nice to call some function we’ve created ourselves?…Well…


Let get right into it…

First…Start your VM and login to you most favorite platform…Our “AppWorks”. We start with a clean workspace and project which looks like this:

xpath_001

In the ‘bpms’ folder we create a new ‘test’ bpm with just a ‘’(Green) Init’’ -> “Activity” -> “(Red) End” flow. From this BPM we jump into the message map where we have the possibility to drag & drop a process property into the ‘Assignments’ panel in the middle. We only do this action, so we are able to open the ‘Advanced’ XPath editor.

xpath_002

In this editor we can call all kind of advanced functions like these examples:

1
2
3
4
5
6
7
8
9
boolean(instance:instanceProperties/instance:processName/text() != '') //returns TRUE
concat('Hello ', 'world') //returns 'Hello world'
normalize-space(" Hello world again !!") //returns 'Hello world again !!'
count(instance:instanceProperties) //returns 1
date(2021, 2, 15) //returns 737836
sprintf("%D(%yyyy-%MM-%dd)T%T(%HH:%mm:%ss.0)", utc(),utc()) //returns 2021-02-15T10:33:10.0
1+1 //returns 2...duh!
9 mod 2 //returns 1
10 > 2 //returns TRUE

All those functions and operators are available for consumption in the left bottom panel, but that’s not what I would like to dive further into for now.

What we do want to dive into is a call like this one:

com.eibus.util.Base64.encode("Hello world!") which results into SGVsbG8gd29ybGQh

The opposite call looks like this: com.eibus.util.Base64.decode("SGVsbG8gd29ybGQh") and reverts it back to Hello world!

Hmmm…interesting!? as this function is not directly available from the UI!…So, where does it retrieve that value from?

xpath_003


The search for our magic call

A search on the server brings me to this JAR file /opt/opentext/AppWorksPlatform/defaultInst/components/basicutil/basicutil.jar

The search command was: sudo find /opt/opentext/AppWorksPlatform/defaultInst/ -type f -name '*.jar' -print0 | xargs -0 -I '{}' sh -c 'jar tf {} | grep Base64.class && echo {}'

With a view on this file with JD-GUI I see more interesting “public static” methods available to consume from…Here a set of examples:

1
2
3
4
5
6
7
8
9
10
11
12
13
com.eibus.util.UserProfile.encryptAndEncode("awdev", "admin") //returns encoded string
com.eibus.util.UserProfile.decodeAndDecrypt("awdev", "e0pBVkEtQUVTL0dDTS9Ob1BhZGRpbmd9ZQhJfnaG0A7z7XrisU3Sw+zqugagpudDjj9eQu6BapPxfEkQDkScbjyA") //returns decoded string
com.eibus.util.UserProfile.isEncrypted("e0pBVkEtQUVTL0dDTS9Ob1BhZGRpbmd9ZQhJfnaG0A7z7XrisU3Sw+zqugagpudDjj9eQu6BapPxfEkQDkScbjyA") //returns TRUE
com.eibus.util.Util.GetIPAddressByHostName("appworks.mydomain.com") //return in my case '192.168.56.107'
com.eibus.util.UTC.getUTCDate() //returns Tue Feb 16 09:19:31 CET 2021
com.eibus.util.RandomString.getString() //returns in my case 'becuaiej'
com.eibus.version.Version.getDetailedInformation() //returns
// AppWorks Platform
// Copyright (c) 2001-2021 Open Text. All rights reserved. Trademarks owned by Open Text.
// Version: 21.1
// Build: 13
// Revision: 406108
com.cordys.basicutil.util.NetworkUtil.isURLreachable("http://www.google.nl") //returns TRUE

Hmmm…nice stuff, but what else?

Let’s pick another JAR: /opt/opentext/AppWorksPlatform/defaultInst/components/esbserver/esbserver.jar

We came to these examples to call in our XPath editor:

1
2
com.eibus.license.Monitor.getLicenseStatus() //returns the number of days left before license expiration
com.eibus.license.Admin.getCurrentLicense() //returns XML data about the license

What else?

Another one: /opt/opentext/AppWorksPlatform/defaultInst/components/bpmengine/bpmengine.jar

1
2
com.cordys.bpm.utils.Utilities.writeToConsole("Hello world...") //tail -f /opt/tomee/latest/logs/catalina.out
com.cordys.bpm.utils.Utilities.isStringEmpty("") //returns TRUE

Nice!…The final one in the ‘components’ folder: /opt/opentext/AppWorksPlatform/defaultInst/components/entityruntime/utilities.jar

1
com.opentext.utilities.StringHelper.stringsEqualIgnoreCase("Hello world", "Hello") //returns FALSE

Great….What about a JAR outside the ‘components’ folder…Like the ‘ext’ folder: /opt/opentext/AppWorksPlatform/defaultInst/ext/commons-lang3.jar

1
2
3
4
org.apache.commons.lang3.text.WordUtils.capitalizeFully("i aM.fine") //returns I Am.fine
org.apache.commons.lang3.time.DateFormatUtils.format(100000000000, "yyyy/MM/dd") //returns 1973/03/03
org.apache.commons.lang3.SystemUtils.getJavaHome() //returns /usr/lib/jvm/java-11-openjdk-11.0.10.0.9-0.el7_9.x86_64
org.apache.commons.lang3.SystemUtils.getEnvironmentVariable("PATH", "") //returns /opt/opentext/AppWorksPlatform/defaultInst/bin:/usr/lib/jvm/java-11-openjdk-11.0.10.0.9-0.el7_9.x86_64/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin

Ohw yeah!!….Now we’re getting somewhere…If this is possible…How about calling some custom method?

Check the next section…


Create some Java code

Maybe you already noticed…Maybe not, but all calls above have one thing in common!

What might that be?

The method should be public, and it should be static!

The ‘public’ keyword is an access modifier used for classes, attributes, methods, and constructors making them accessible by any other class.

A ‘static’ method can be accessed without creating an object of the class first

So, with those requirements in mind we can create a Java class looking like this:

1
2
3
4
5
6
7
8
9
10
11
package nl.bos.bpm;

public class HelpMethods {
public static String toUpperCase(String input) {
return input.toUpperCase();
}

public static boolean startsWith(String input, String prefix) {
return input.startsWith(prefix);
}
}

You can use a fancy IDE, but for this demo we just use Notepad++ as we will compile this source into a JAR file with the platform features in next section.


The power of JAD

To have our Java class compiled, generated into a JAR, and deployed on the server in a specific location we will use a ‘Java Archive Definition’ document in our project.

Make sure you have a folder structure like this in the ‘java’ folder of our project:

xpath_004

In the ‘crosscontext’ folder we create a new ‘Java Archive Definition’ with the sample same ‘bpm_jad’. We give the ‘JAR File Name’ a logical name like for example ‘BPMMethods’, and we make sure to point to the ‘src’ directory where we will upload our just crafted Java source file. This is how it should look like:

xpath_005

To upload our Java source we create a new ‘Java Source’ document in the ‘src/nl/bos/bpm’ folder where we copy & paste our crafted Java code from the previous section!

The end-results:

xpath_006

Finally, we need to set the ‘java’ folder as ‘Set start point of qualified name’ which makes our ‘relative’ path on the server to this location:

/opt/opentext/AppWorksPlatform/defaultInst/. As we all know this is the location where the ‘crosscontext’ folder lives…So, that’s a wisely chosen folder name!

Now, let’s publish our ‘bpm_jad’, and you will start to see a nicely generated JAR uploaded to the server in this location:

/opt/opentext/AppWorksPlatform/defaultInst/crosscontext/BPMMethods.jar

Now we need to do a restart of TomEE with systemctl restart tomee and after a refresh of the designer we will be able to call our own custom function:

1
nl.bos.bpm.HelpMethods.toUpperCase("test") //returns TEST

xpath_007

Ohhhh YEAH…That’s nice! 💪


With that final call we can also give this post a “DONE” where we learned how to call a custom method from the XPath editor. Nice to know how this works, and it will probably solve some well-though features from our analyst-guy in the future! Leaves me with one last thing…Cheers, have a great week-end, and I see you in a new installment; next week…

Don’t forget to subscribe to get updates on the activities happening on this site. Have you noticed the quiz where you find out if you are also “The AppWorks guy”?