Writing Jython scripts for XL Deploy

You can use Jython scripting to extend or customize XL Deploy actions, events, or components. This topic describes best practices for writing, organizing and packaging your Jython scripts.

Pointing to a Jython script from configuration files

Usually when you attach a Jython script to an XL Deploy action, event, or component, you specify a relative path to it. In this situation, XL Deploy can find this script is by appending the path to each segment of its own classpath and looking there.

If you have a configuration snippet such as ... script="myproject/run.py"..., then XL Deploy can find the script at ext/myproject/run.py because the ext folder is on the classpath.

The script can also be packaged into a JAR and placed in the plugins folder. XL Deploy scans this folder at startup and adds the JARs it finds to the classpath. In this situation, the JAR archive should contain the myproject folder and run.py script.

Creating a JAR

When creating a JAR, verify that the file paths in the plugin JAR do not start with ./. You can check this with jar tf yourfile.jar. If you package two files and a folder, the output should look like this:

file1.xml
file2.xml
web/

And not like this:

./file1.xml
./file2.xml
web/

Splitting your Jython code into modules

You can split your code into modules. Note the following:

  • You have to create an empty __init__.py in each folder that becomes a module (or a segment of a package name).
  • Start the package name with something unique, otherwise it can clash with other extensions or standard Jython modules. For example, myproject.modules.repo is a better name than utils.repo.

Consider an example in which you have the following code in run.py:

# myproject/run.py  
    infrastructureCis = repositoryService.query(None, None, "Infrastructure", None, None, None, 0, -1)
    applicationsCis = repositoryService.query(None, None, "Applications", None, None, None, 0, -1)

# do something with those cis

You can create a class that helps perform queries to the repository and hides unnecessary parameters.

# myproject/modules/repo.py

class RepositoryHelper:

    def __init__(self, repositoryService):
        self._repositoryService = repositoryService

    def get_all_cis(self, parent):
        ci_ids = self._repositoryService.query(None, None, parent, None, None, None, 0, -1)
        return map(lambda ci_id: self._repositoryService.read(ci_id.id), ci_ids)

Then, in run.py, you can import and use RepositoryHelper:

# myproject/run.py

from myproject.modules import repo
repository_helper = repo.RepositoryHelper(repositoryService)
infrastructureCis = repository_helper.get_all_cis("Infrastructure")
applicationsCis = repository_helper.get_all_cis("Applications")

# do something with those cis

The contents of the folder and JAR archive will then be:

myproject
myproject/__init__.py
myproject/run.py
myproject/modules
myproject/modules/__init__.py
myproject/modules/repo.py

Using third-party libraries from scripts

In addition to your own scripts, you can use:

  • third-party Python libraries
  • third-party Java libraries
  • your own Java classes

In each of this cases make sure that they are available on the classpath in the same manner as described for your own Jython modules.

Best practice: Develop in directories, run in JARs

While developing and debugging scripts, you can keep the files open in the editor and change them after every iteration. After you have finished development, it is recommended to package them into a JAR file and place it in the plugins folder.

Best practice: Restarting the server

Normally there is no need to restart the server after changing a Jython script. However, modules are cached by the scripting engine after their first execution. To avoid this effect, you can use built-in reload() function.

from myproject.modules import repo
reload(repo)
# ...

Finding scripting examples

You can find an example of scripting in the UI extension demo plugin, which is available in the samples folder of your XL Deploy installation.