We’re going to briefly change things up a little bit for this blog post and switch from Java to Jython. Normal Java service will be resumed shortly! I'm somewhat new to Jython myself, so this article shares some of the basics needed to get scripts to work. I plan to create a follow-up article to covered some more advanced topics once I've figured out how things fit together.
A capability of Atrium Orchestrator that has been sadly overlooked for many years is its ability to execute Jython scripts from within workflows. AO’s workflow activities and its use of XPath and XSLT allow a reasonable amount of flexibility, and I'm often impressed by what’s possible with some clever manipulation of XML.
Unfortunately this does mean that sometimes tasks that ought to be simple become an exercise in futility; I think anyone who has wanted a loop that repeats infinitely until a condition is met will relate to this!
One of the great strengths of AO is that you don’t need a programming background to create simple workflows, but there are going to be times when some good old-fashioned code is just the best way to get something done. You have a number of options if you go down this route:
- Call an out-of-process script using the command-line adapter. This means just about anything you can run from the command-line is available to you. Running out-of-process has its disadvantages, though, as there is an overhead in memory and process setup time.
- Call a Perl script using the Script adapter. This also uses an external out-of-process Perl instance, with the disadvantages as above.
- Call a Jython script using the Script adapter. This executes in-process and so is very quick, but Jython is an interesting beast.
Prior to the 20.14.01 content release, Jython scripts were somewhat crippled by our inclusion of the out-of-date Jython 2.1 library (from 2001!). We've now moved to version 2.5.3, which is of 2012 vintage and the most recent stable release as at this writing.
Ignoring the somewhat out-of-date version of Jython used previously, why aren't more people using these scripts in their workflows? There are, of course, those people who just are not familiar with Python/Jython syntax, and I'm not intending to teach anyone the basics of the language here. I think for those who do know Jython, the issue has been one of documentation: the script adapter documentation is fairly high-level and code examples in the community are few and far between. So that’s what I aim to fix here with some practical examples of things you can achieve with Jython in AO.
Firstly, let’s do some basic grid configuration. Initially we’re going to use the script activity, and this means you must have an instance of the ro-adapter-script adapter active on your grid and it MUST be called ScriptAdapter. This name is a restriction of the script activity itself; if we call the script adapter directly we can give the adapter a different name, which is essential if you want to use both Jython and Perl via this adapter.
The configuration of the script adapter is a little, well… let’s call it idiosyncratic. It asks for the path to a Perl executable, which of course is not relevant to Jython. You do, however, have to enter the path to a valid file here and it must include the string “jython”. I created an empty file called /opt/bmc/jythonDummyFile on my CDP and used this in the configuration. You DON’T need to point to a valid Python or Jython interpreter as this is provided with the script adapter. The adapter configuration will look something like this:
So now let’s start with something simple to demonstrate passing variables into and out of a Jython script. We will convert a string to upper-case; even though there is a basic transform in AO that can do this already, it’s a useful exercise.
We need an assign activity to set up the initial string, a script activity to run the Jython code, and we’ll have another assign activity that allows us to do some logging and other post-execution trickery. It looks like this:
The Set Variables activity is as follows:
It is very important to note the quotes around the string. Imagine this string will be placed directly into a statement in this way:
var1 = This is a simple string
This will generate an error in Jython. So by quoting the string, the actual assignment is:
var1 = "This is a simple string"
Of course, when passing numeric values, these don’t need quotes.
The Script activity is setup thusly:
And last, but by no means least, we have the awesomeness that is our Jython script. We’re using an embedded script here, so click the “View/Edit Script” button and paste this in, ensuring that the scripting language drop-down is set to Jython:
var2 = var1.upper()
Yes, that’s it. You can see that we take the context item inputString and assign it to the Jython variable var1. The script performs an upper() operation on this string, assigning the result to var2. And upon completing execution, AO will stick var2 into our outputString context item which we can dump out using the logging tab and see:
[outputString=THIS IS A SIMPLE STRING]
Numerics and Multiple Values
This time we’ll make things a little more complex. Set three context items:
valueA = 7 valueB = 3 myString = "Multiplying the supplied values gives: "
The Jython script is this:
returnValue = var3 + str(var1 * var2)
And you configure the Script activity in the following way:
Running this will get you the output:
[outputString=Multiplying the supplied values gives: 21]
Hopefully this is enough for you to understand the basic method used for passing variables into and out of a Jython script. So far we have only covered simple string and numeric values; how do we pass out an array or list, for example?
Stick in this script code, leaving everything else the same as it was:
returnValue = [ "Tomato", "Orange", "Lychee" ] returnValue.append("Watermelon") returnValue.append("Kumquat")
Any idea what we’ll get in our outputString context item?
[outputString=['Tomato', 'Orange', 'Lychee', 'Watermelon', 'Kumquat']]
Unfortunately not the most useful output in AO terms as we need to tokenize it. Far more useful would be an XML document that we could process; however, so far I've been unable to get AO to recognize a returned XML document as XML.
Well here’s the really bad news. At the moment, the Script Adapter doesn't include the standard libraries that are available for Jython, so standard Python imports don't work. You can import standard Java classes, so we could use the Java Random class to generate a pseudo-random password:
from java.util import Random charList = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!#." rgen = Random() generatedPassword = "" for _ in range(int(passLength)): num = rgen.nextInt(len(charList)) generatedPassword = generatedPassword + charList[num:num+1]
I'm hoping at this stage you can set up your input and output context items without too much assistance. If you need a hint, you need to pass a number to "passLength" as the input, and "generatedPassword" is your output variable.
Wouldn't it be nice if you could use the standard Python libraries from your Jython scripts? Well, you can. Unofficially and in a non-supported fashion for the moment. Grab the Jython 2.5.3 installer and run it (you'll need a JRE installed). Select the Standalone install and this will generate a jython.jar file. Upload this to the adapter folder containing the script adapter on each peer for which it's enabled (under server/grids/<gridname>/library/adapters/implementations/ro-adapter-script_20.14.01.00_1 most likely). Rename the existing jython-2.5.3.jar to jython-2.5.3.old.jar and rename the new file from jython.jar to jython-2.5.3.jar. Restart the script adapter.
Magically, scripts like this one should now work:
import random charList = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890!#." generatedPassword = "" for _ in range(int(passLength)): num = random.randint(0, len(charList)) generatedPassword = generatedPassword + charList[num:num+1]
Did I mention that doing this is currently totally unsupported by BMC? If you'd like to see this implemented in the product, please go vote for the idea on this subject!
Debugging Your Jython Script
Debugging a script by running it in Dev Studio is something you only want to do if you’re a masochist and really enjoy digging for syntax errors in the grid.log. You are far better off writing some code against a Jython interpreter and simulating the inputs you expect from AO. Once you’ve squashed your bugs, then paste it into the script window (or link to it as a file if you prefer). At the moment, using this method means you can't use any Python imports, hence either using Java libs or sticking to some pretty basic scripting.
If you have a convenient Linux machine, your distro should have a Jython package that you can install with "apt-get install jython" or "yum install jython" depending on your Debian or Redhat leanings. Crazy Gentoo users can figure it out for themselves . On Windows, you should be able to download the installer from the Jython.org downloads page.
This will allow you to test your scripts from the command-line first and move them to AO when you have them functionally complete.
If you choose to upload the standalone Jython jar that includes the standard libs, you can also do your testing using Python (just bear in mind you can't import Java libs in Python!).