Scripting best practices

Scripting enables you to extend IBM® Maximo® Manage business logic by using Python, JavaScript, or any other JSR223-compliant scripting language. All the script code gets compiled to Java™ bytecode and is cached as part of Maximo Manage runtime caches. When the script is started, it is the cached bytecode that is run by the Java virtual machine by using the JSR223 bridge. Since the script code runs in the same thread as other Maximo Manage business logic written in Java, poorly written script code can negatively impact the performance of the system. Follow the Maximo Manage performance guidelines because scripting is equivalent to Maximo Manage custom code.

Choosing the correct launch point and event

Launch points are script trigger points. Often, choosing the correct launch point can help avoid certain performance issues in scripting. For example, in previous releases, no support was available for attribute value initialization. This led many script developers to use the object launch point (OLP) init event to initialize the Maximo business object (MBO) attribute values. Though functionality was not affected, it might lead to performance issues when you select many MBOs. The OLP initialization event script is run for every MBO that is selected in the MboSet, even though the attribute whose value is getting initialized by the script is not used or shown. You can avoid this issue by changing the object launch point to an attribute launch point for the initialize value event. The following sample script code shows that thisvalue is the current attribute init value:
if priority is not None:
   thisvalue=2*priority

The MBO framework starts this script only when this attribute is referred to by the code or the user interface.

Another example of launch point choice is integration skipping events. Often, developers use the user exit scripting to determine whether they need to skip an outbound integration message. However, at this point the system has already incurred the cost of serializing the MBOs. Instead, you should use the publish channel event filter scripting which gets invoked when the event is triggered and before any serialization of MBOs happens. The following sample shows the event filter scripting that works with the MBOs.
if service.getMbo().getString("status")=="APPR":
  evalresult=False
evalresult=True

Avoid costly object init events if invoked from List tab

You might want to use costly object initialization scripts only when the object is initialized from the Main tab and not from the List tab. In the Main tab, there is only one object. In the List tab, there are many objects. In such cases, the following sample code is helpful:
from psdi.common.context import UIContext
if UIContext.getCurrentContext() is not None and UIContext.isFromListTab()==False:
    ..costly initialization..

Watch out for conflicting launch point event scripts

The scripting framework allows you to attach multiple scripts to the same launch point event. This poses a problem if the script code expects to run it in a certain order before or after certain other scripts in the same launch point event. Since the Maximo event topic is an unordered map, the events are triggered without a fixed order. This can potentially cause issues if the order dependency is not managed properly. You should evaluate the reason to attach multiple scripts for the same launch point event and evaluate whether it makes more sense to combine them into one script. The other option is to make sure there is no dependency between the scripts.

Avoid calling save in the middle of a transaction

This coding pattern can cause problems to Maximo Manage transactions and event firing. Ideally, when a transaction is in progress, the script should try to be part of that encompassing transaction. The MBOs created or updated by a script are automatically part of the encompassing transaction as long as those were created from the script launch point MBO or any related MBO. If you create an MBO using the MXServer.getMXServer().getMboSet(“…”) API, it would be outside the encompassing transaction unless you add it explicitly to the following encompassing transaction:
mbo.getMXTransaction().add(<newly created a mboset>)

Calling MboSet.count() many times

A common mistake when writing scripts is checking the count of an MboSet multiple times. The count() call ends up firing an SQL every time it is called. A better approach is to invoke it once, store the value in a var, and reuse that var for subsequent code flow. The following example demonstrates this approach.
cnt = mboset.count()
if cnt<=1:
  service.log(“skipping this as count is “+cnt)
Do not code it as shown in the following example.
if mboset.count()<=1:
  service.log(“skipping this as count is “+mboset.count())

Closing the MboSet

The MBO framework always releases the MboSets that are created after a transaction is complete. This is true if all the MboSets were created as a related set to the launch point MBO or any of its related MBOs. However, if the MboSet is created using the MXServer.getMXServer().getMboSet(..) API, the script code is responsible for closing and clearing that MboSet. The following example shows how to clear the MboSet.
try:
  ..some code..
finally:
  mboset.cleanup()

If you do not clear the MboSets, out-of-memory errors may result.

Avoid the Mozilla compatibility script for Nashorn

Moving from Rhino and Java 7 to Nashorn and Java 8 is recommended for performance reasons. Nashorn performs better in Java 8 than Rhino does. Using the Mozilla compatibility script with Nashorn can result in poor performance in Java 8.

Check whether logging is enabled before logging

Logging is often done inside the script without checking the log level. The following sample shows how that can impact performance:
service.log("count of mbos "+mboset.count())

This code unfortunately results in mboset.count() being called even though script logging is disabled.

A better approach is to check whether debug is enabled before calling mboset.count().
from psdi.util.logging import MXLoggerFactory
logger = MXLoggerFactory.getLogger("maximo.script");
debugEnabled = logger.isDebugEnabled()

if debugEnabled:
  service.log("count of mbos "+mboset.count())
The following function in the service variable enables you to check whether logging is enabled:
if service.isLoggingEnabled():
  service.log(“count of mbos “+mboset.count())

Avoid accessing script cache

Accessing the script cache from the automation script code results in circular dependency and causes instability when the script cache is being loaded, that is, partial load. When you write a script, use library scripts for modular script development and thereby avoid accessing the script from the cache.