Skip to main contentCarbon Design System

Scripting best practices

Scripting best practices

Scripting allows you to extend Maximo business logic using Python/JS or any other JSR223 compliant scripting language. All the script code gets compiled to Java bytecode and are cached as part of Maximo runtime caches. When the script is invoked, it is the cached bytecode that is executed by the JVM using the JSR223 bridge. Since the script code executes in the same thread as other Maximo business logic (written in Java), a poorly written script code can impact the performance of the system negatively. In general you should follow the Maximo Performance guidelines because scripting in the end is equivalent to Maximo custom code, to avoid common mistakes.

Choosing the right launch point and event

Launch points are script trigger points. Often, choosing the right launch point can help avoid certain performance issues in scripting. For example, in Maximo 7.5 scripting release, there was no support for attribute value initialization. This led many script developers to use the Object Launch Point (OLP) Init event to initialize the MBO attribute values. Though functionally was not affected, it potentially could lead to performance issues when selecting a bunch of MBOs (in List tab or APIs/MIF, Escalations). The OLP init event script gets executed for every MBO that is selected in the MboSet, even though the attribute whose value was getting initialized by the script was not even used or shown. You can avoid this by changing the Object launch point to Attribute launch point for the Initialize Value event. The following sample script code shows thisvalue is the current attribute init value:

if priority is not None:
thisvalue=2*priority

The MBO framework will invoke this script only when this attribute is referred to by the code or the UI.

Another example of Launch point choice is in the Integration skipping events use case. Often, you would use the user exit scripting to determine if you need to skip an outbound integration message. However, at this point the system has already entailed the cost of serializing the MBOs. Instead, you should use the Publish Channel Event Filter scripting which gets invoked right when the event is triggered and much before any serialization of MBOs happen. The following sample shows the Event Filter scripting which 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 may want to do costly Object init scripts only when the object is initialized from the Main tab (UI) and not from the List tab. This is because in such cases, the following sample code bis helpful:

from psdi.common.context import UIContext
if UIContext.getCurrentContext() is not None and UIContext.isFromListTab()==False:
..costly initlization..

Watch out for conflicting launch point event scripts

Scripting framework allows attaching multiple scripts to the same launch point event. This poses a problem if the script code expects to execute it in a certain order before or after certain other scripts in the same launch point event. Since the Maximo event topic is an un-ordered 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 if 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 middle of a transaction

This is a common coding pattern seen in scripts that can cause problems to Maximo transactions and event firing. Ideally, when a Maximo 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

It is a common programming mistake in the scripts when checking the count of an MboSet multiple times. The count() call ends up firing an SQL every time it is called. An optimal approach would be to invoke it once, store the value in a var, and reuse that var for subsequent code flow. The following example demonstrate this: Good code:

cnt = mboset.count()
if cnt<=1:
service.log(“skipping this as count is+cnt)

Bad code:

if mboset.count()<=1:
service.log(“skipping this as count is+mboset.count())

Closing the MboSet

Maximo MBO framework always releases the MboSets created after a transaction is complete. This is true as long as all the MboSets were created as a related set to launch point MBO or any of it related MBOs. If however the MboSet is created using the MXServer.getMXServer().getMboSet(..) API, the script code is responsible for closing and clearing that MboSet. You should do the following try finally block to achieve this:

try:
..some code..
finally:
mboset.cleanup()

If this is not done, it tends to start building up and may result in OOM errors.

Avoid the Mozilla Compatibility Script for Nashorn

Moving from Rhino (Java 7) to Nashorn (Java 8) is recommended for performance reasons. Nashorn performs better in Java 8 compared to Rhino. Leveraging the Mozilla compatibility script with Nashorn can result in poor performance in Java 8.

Check if logging in 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 would unfortunately result in mboset.count() being called, even though the script logging is disabled.

from psdi.util.logging import MXLoggerFactory
logger = MXLoggerFactory.getLogger("maximo.script");
debugEnabled = logger.isDebugEnabled()
if debugEnabled:
service.log("count of mbos "+mboset.count())

Starting from 7612, the following function will be added in the “service” variable that will allow you to check it easily:

if service.isLoggingEnabled():
service.log(“count of mbos “+mboset.count())