Skip to main contentCarbon Design System

Object structure scripting

The Object Structure provides the message definition and content for other integration components like the Publish Channel. In addition, the object structure by itself can support the exchange of Maximo data with external applications/clients using for example, REST. Using a script on the object structure applies to all uses as shown in the following diagram.

OS use cases

A script on an object structure such as MXITEM, can impact the processing of item data through REST, application Import, Enterprise Service and Invocation/Publish Channel.

Creating a script

The following four screen shots identify the steps to create a new script for use with integration. This example will create a script for the MXITEM object structure that would run during the inbound processing.

OS script OS script name OS script code

Object structure inbound script processing

The framework that supports the inbound processing of an object structure provides methods, or hooks, where custom Java code can be implemented to alter or enhance the processing of the data through the object structure to the Maximo Business Objects (MBOs). With the introduction of scripting, these same functions, or hooks, are available for use by an automation script to perform similar customizations that can be done using Java. In the case of the processing of Object Structures, a custom Java class and a custom automation script can be implemented on one object structure at the same time. However, this does not apply to all integration components.

The inbound processing of an object structure provides a number of predefined methods, or functions, that must be extended by an automation script in order to implement custom processing using a script. These functions support implementing logic at different points in the processing of message into Maximo.

Prior to any MBO processing

Function
beforeProcess(ctx)

This function would be processed once for each noun in the inbound message. If the inbound message had five items, each item would be considered a noun.

During the processing of MBOSets and MBOs

Function
beforeCreateMboSet(ctx)
afterCreateMboSet(ctx)
mboRules(ctx)
beforeMboData(ctx)
afterMboData(ctx)

These functions would be processed sequentially for each object within every noun in the inbound message. If the object structure for an item contained the Item object and the itemspec object, the functions above would process sequentially for item and then itemspec.

After the MBO processing

Function
preSaveRules(ctx)
changeStatus(ctx)

These functions would be processed once for each noun in the inbound message.

There is a context (ctx) that is passed between the processing of the object structure and the script in order for the script to implement custom code. This context is supported bi-directionally. There are following pre-defined APIs available on the context:

Context (ctx) APIs

The following matrix outlines the APIs (column on left) and their most common uses within the available functions (row across the top).

OS script

The ctx variable is explained in the following table. Some cases are applicable to both inbound and outbound and some of them are specific to either inbound or outbound.

Context functionDescriptionApplicability
ctx.getMosDetailInfo()This provides the integration data dictionary cache information for the object in the the object structure being processed.Inbound and outbound
ctx.skipMbo()This supports the skipping of a MBO, which is not to process, on the inbound/outbound messageInbound and outbound
ctx.skipTxn()This supports skipping an entire transaction or message from processingInbound and outbound
ctx.complete()This supports ending the processing at the point of execution. It will not process the children data of the messageInbound and outbound
ctx.process()This supports continuing the processing at the point of execution, likely used as part of conditional logicInbound and outbound
ctx.getParentMbo()Retrieves the parent MBO, which can be used from a child that does not yet have a MBO created.Inbound
ctx.isPrimary()Identifies if the current MBO is the root level MBO of the object structureInbound
ctx.getUserInfo()Supports the retrieval of the user information.This would be needed for a script to create a new MBO.Inbound and outbound
ctx.bypassMbo()Supports by-passing the MBO creation and continuing to the next MBO to be processed. This could be used when there is a case where one MBO creates a child MBO, so the integration processing wants to bypass the creation of that child MBO.Inbound
ctx.getMsgType()Provides access to the message type (for example, Sync, Create).Inbound
ctx.setMsgType(String msgType)Provides support to set the message type (for example, Sync, Create ). It can be used before processing onlyInbound
ctx.getData()Provides access to StructureData (XML message).Inbound
ctx.getMboSet()Gets the mboset in context for the level that is getting processed now.Inbound
ctx.setMboSet()Sets the mboset for the current level. This is leveraged for cases where you would want to create the mboset for that level in a custom way.Inbound
ctx.setMbo(MboRemote mbo)Sets the MBO for the level. This is done when you want to create or find the MBO in a custom way.Inbound
ctx.getPrimaryMboSet()This gets the primary mboset (the root object) for this object structure.Inbound
ctx.getPrimaryMbo()Provides support for retrieving the primary (root) MBOs of an object structure when processing a child MBO.Inbound
ctx.setProcessTable(String processTable)Used for setting the table for a process, for example, when you have a non-persistent MBO such as MXRECEIPT, it has processing logic to determine the table(MBO) to be updated (either MATRECTRANS or SERVRECTRANS). This API should be used in the beforeProcess(ctx) function.Inbound
ctx.setSkipBaseAdditionalRules()Used in the preSaveRules(ctx) function, this can be used to inject custom logic to operate on a completed object structure that is now prepared at this point in the processing.Inbound
ctx.processAsUpdate()Sets the processing action of a MBO to be an Update (versus an Add or Delete).Inbound
ctx.processAsAdd()Sets the processing action of a MBO to be an Add (versus an Update or Delete).Inbound
ctx.processAsAtEnd()Sets the processing action of a MBO to be an Add (versus an Update or Delete) and will create the MBO at the end of the collection rather than by default it would be created at the top of the MBO collection.Inbound

Inbound script examples using the Functions and APIs

Many of the following examples were done by enabling Application Import and Export in the Item Master application using the MXITEM object structure. An item was exported and the XML was modified to a smaller set of attributes. This XML was first imported with no script to ensure it would successfully create an Item. On subsequent tests, the Item Number was changed to ensure that a new record could be created (avoid duplicate errors).

?xml version="1.0" encoding="UTF-8"?>
<SyncMXITEM xmlns="http://www.ibm.com/maximo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" creationDateTime="2014-10-03T15:36:12-04:00" transLanguage="EN" baseLanguage="EN" messageID="1412364918445435277" maximoVersion="7 6 20140925-2230 V7600-211">
<MXITEMSet>
<ITEM>
<DESCRIPTION>Centrifugal Pump 100-1 GPM, 60 FT-HD</DESCRIPTION>
<HIERARCHYPATH>PUMP \ CNTRFGL</HIERARCHYPATH>
<ITEMNUM>PUMP100-1</ITEMNUM>
<ITEMSETID>SET1</ITEMSETID>
<ITEMTYPE>ITEM</ITEMTYPE>

Function: beforeProcess(ctx)

This function will process prior to any MBOs being retrieved or created by the object structure processing. If you are using the object structure (OS) with application import, REST/OSLC api or calling the OS using SOAP or XML over HTTP, the inbound processing class is the only placeholder to inject custom code (if you were using an Enterprise Service you would have additional processing points for custom code).

If external applications are calling the object structure but that application cannot do any filtering of messages, you could implement filtering in the function using the ctx.skipTxn() api. Implementing filtering here is a good choice since there has been no extensive processing of the data at this point. A skipped transaction would avoid unnecessary processing. This script will filter out any items where the LOTTYPE = NOLOT.

function beforeProcess(ctx)
{
var lot=ctx.getData().getCurrentData("LOTTYPE")
if (lot=="NOLOT")
{
ctx.skipTxn();
}
}

In the script, the ctx.getData().getCurrentData uses the structureData API to retrieve the Lot Type value from XML message and sets it to the ‘lot’ variable.
Then a condition tests the value of the variable and calls the ctx.skipTxn() API for the framework to skip the processing of the message.
The response of processing is still ‘success’, which is the same as if the record was created in Maximo since no exception was raised.
When testing a scenario like this, one way to verify the behavior of your script is to load the record with the script as active and then load it again when the script is configured as not active. In this test, with the script active, there was no item added. With the script not active, a new item was created.

Function: mboRules(ctx)

The mboRules(ctx) function [tied to the checkBusinessRules() method of the object structure processing class] operates prior the creation of each MBO. This can be used to skip processing of a specific MBO, transaction or continue processing. An MBO can also be created at this point.

The following example is for the MXINVISSUE object structure that accepts Issue transactions of type Issue and Return but does not support type Invoice. This script will filter out the types that are Invoice.

function mboRules(ctx)
{
if (ctx.getMosDetailInfo().getObjectName() == "MATUSETRANS")
{
var type=ctx.getData().getCurrentData("ISSUETYPE")
if (type NOT IN ("ISSUE","RETURN"))
{
ctx.skipMbo();

Functions: beforeMboData(ctx) and afterMboData(ctx)

The beforeMBOData function [tied to the presetMboRulesSet() method of the object structure processing class] is available as the MBO is being created but before MIF processing sets values to the MBO. The afterMboData(ctx), which is tied to the setAdditionalData() method of the object structure processing class is available after the MBO is created and the values are set to the MBO by the MIF. The MBO data is pre-save and processing could be implemented to create related MBOs that would be included in the transaction or change data in MBOs using custom logic. Since the MBO columns are set by the MIF at this point, by default script processing could not set the field values.
If your requirement is to set the field values at this point in the processing, then you would need to configure the field as Set Restricted in the object structure so that the MIF does not set this and then provide logic to set the value in your script. The primary difference between the ‘before’ and ‘after’ function is that the ‘before’ allows your code to set a field value conditionally and when that condition is not met, the MIF will set the field using the value in the incoming message. The ‘after’ function approach will use the value your code implements and the MIF will not set the field, since the field will need to be specified as Set Restricted.

beforeMboData(ctx) example The following XML message and script uses the ‘before’ function as an example. The XML is a simple MXITEM xml to create a new Item in Maximo.

<?xml version="1.0" encoding="UTF-8"?>
<SyncMXITEM xmlns="http://www.ibm.com/maximo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MXITEMSet>
<ITEM>
<DESCRIPTION>New Item</DESCRIPTION>
<HIERARCHYPATH>PUMP \ CNTRFGL</HIERARCHYPATH>
<ITEMNUM>NEWITEM01</ITEMNUM>
<COMMODITYGROUP>MOTOR</COMMODITYGROUP>
<ITEMSETID>SET1</ITEMSETID>

The following script is required to:

  • Determine that the MBO being processed is ITEM
  • Set the HIERARCHYPATH field from the message to a variable (hier).
  • Evaluate the value of the variable hier to be equal to ‘PUMP \ CNTRFLG’
    • If equal, it defaults the value PUMP to the Commodity Group field in the MBO. The value is set with DELAYVALIDATION so that any processing that occurs after this script executes cannot set this field value.
    • If not equal, the script does not set the value and the value will be set to MOTOR from the incoming message by the MIF.
function beforeMboData(ctx)
{
if (ctx.getMosDetailInfo().getObjectName() == "ITEM")
{
var hier=ctx.getData().getCurrentData("HIERARCHYPATH")
if (hier == "PUMP \\ CNTRFGL")
{
var mbo = ctx.getMbo();
var MboConstants = Java.type("psdi.mbo.MboConstants");

afterMboData(ctx) example The following XML message and script uses the ‘after’ function as an example. The XML is a simple MXITEM xml to create a new Item in Maximo.

<?xml version="1.0" encoding="UTF-8"?>
<SyncMXITEM xmlns="http://www.ibm.com/maximo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<MXITEMSet>
<ITEM>
<DESCRIPTION>New Item</DESCRIPTION>
<HIERARCHYPATH>PUMP \ CNTRFGL</HIERARCHYPATH>
<ITEMNUM>NEWITEM01</ITEMNUM>
<COMMODITYGROUP>MOTOR</COMMODITYGROUP>
<ITEMSETID>SET1</ITEMSETID>

The following script logic is identical to the logic used in the ‘before’ example. An additional configuration was that the Commodity Code was configured as Set Restricted to prevent the MIF from setting the field value. This is needed since the MIF default processing will always set fields with ‘DELAYVALIDATION’, which prevents any subsequent processing from overriding the value set by the MIF.

The script takes care of the following points:

  • Determines that the MBO being processed is ITEM
  • Sets the HIERARCHYPATH field from the message to a variable (hier).
  • Evaluates the value of the variable hier to be equal to ‘PUMP \ CNTRFLG’
    • If equal, it defaults the value PUMP to the Commodity Group field in the MBO. The value is set with DELAYVALIDATION so that the any processing that occurs after this script executes cannot set to this field value.
    • If not equal, the script does not set the value and the field will not be set when saved unless the MBO provides a default value.
function afterMboData(ctx)
{
if (ctx.getMosDetailInfo().getObjectName() == "ITEM")
{
var hier=ctx.getData().getCurrentData("HIERARCHYPATH")
if (hier == "PUMP \\ CNTRFGL")
{
var mbo = ctx.getMbo();

If you need to use the afterMboData function and cannot Set Restrict the field in the object structure, there is an option to ‘reset’ the mbo field flag that will allow you to change the field value after it has been set by either the MIF or the MBO.
The script code snippet would look like this:

.
.
mbo.setFieldFlag("commoditycode", MboConstants.NOSETVALUE, false);
mbo.setValue("commoditygroup","PUMP",MboConstants.DELAYVALIDATION);
.
.

Functions: changeStatus(ctx)

The changeStatus(ctx) function [tied to the changeStatus () method] is available to provide additional capability beyond what is provided by the MIF’s base class for change status (statefulMicSetIn). Using this function requires that the processing class of the object structure has either statefulMicSetIn or a class that extends statefulMicSetIn to be registered.

The following examples are where this function might be used:

  • The processing needs to support more than the three default parameters (status, memo, date) of change status.
  • The processing needs to set the status date field rather than using the default system datetime. Note: The MBO business logic may prevent using past or future dates, or some MBOs do not support accepting the date for a changestatus, as it always uses the system datetime. The following sample was used with the MXPO object structure since the status date of the PO can be set to something other than the system date.

The following Java Script example will set the Memo field related to the Change Status to a string value when there is no memo value (NP_STATUSMEMO) passed in the inbound integration message. Additionally, it will set the date field to the date passed in the STATUSDATE element of the inbound message (rather than the MBO using the default system datetime).

function changeStatus(ctx)
{
var mbo = ctx.getMbo();
var struc = ctx.getData();
var stat = struc.getCurrentData("STATUS");
var statdate = struc.getCurrentDataAsDate("STATUSDATE");
var memo = struc.getCurrentData("NP_STATUSMEMO");
if(struc.isCurrentDataNull("NP_STATUSMEMO"))

When the script executes changeStatus, this will prevent the processing class on the object structure from executing it as well (executes only once).

Object structure outbound script processing

It may be required to manipulate the XML/JSON data serialization in the Integration Object structure layer. For example, in outbound messages where the Object structure processing would serialize the MBO structure into a XML or json message. The following common use cases are listed:

  • To override certain values in the XML or json based on the current MBO state.
  • To skip some MBOs or attributes based on certain conditions.

You may think that you can do it in exits. However, exits are further down the line in outbound processing and it may not be very efficient to let the data get serialized to json/xml and then get dropped. The following example in py shows the three functions you can define to interface with the Object structure serialization process:

FunctionPurpose
overrideValues(ctx)Used for the purpose of overriding the MBO attribute values as they get serialized to json/xml
skipMbo(ctx)Used for the purpose of skipping the MBOs before serialization. These skipped MBOs will not be part of the xml/json generated
skipCols(ctx)Used for skipping MBO attributes before serialization.

All these functions take in a common parameter, the ctx, whicch is an object of type psdi.iface.mos.OSDefnScriptContext. This object contains the state of the serialization and helps the script code to figure out the level in the Object structure hierarchy, you are at, in the call back point.

The following code can be applied to the MXPO outbound object structure.

def overrideValues(ctx):
if ctx.getMboName()=='PO' and ctx.getMbo().isNull("description")==True:
ctx.overrideCol("DESCRIPTION","PO "+ctx.getMbo().getString("ponum"))
def skipMbo(ctx):
if ctx.getMboName()=='PO':
if ctx.getMbo().getMboSet("poline").count()==0:
ctx.skipTxn()
elif ctx.getMboName()=='POLINE':

The points to note here are:

  • In the override cols note that you are overriding the description only when the object is PO and the description is null. This will not change the PO MBO description. It will just have the description in the json/xml.
  • In the skipMbo, you will skip the POLINE processing if the line is a service line. You are also going to skip the PO itself if there are no lines for it.
  • In the skipCols, you check for the MBO in process to be POLINE and if taxed is set to false, you skip all the tax attributes.
  • These ctx vars all extend the “service” global var and hence all APIs available there can be leveraged here, for example, to throw error, call workflow, or to log real time.

You can write any combination of these three functions in your script code. At least one is required. Also note that if we wanted to overrride an attribute of POLINE, along with the existing code, we ust need to add another if block to check the context mbo to be POLINE.

def overrideValues(ctx):
if ctx.getMboName()=='PO' and ctx.getMbo().isNull("description")==True:
ctx.overrideCol("DESCRIPTION","PO "+ctx.getMbo().getString("ponum"))
elif ctx.getMboName()=='POLINE' and ctx.getMbo().getString("itemnum")=="ITM1":
ctx.overrideCol("REMARK","Speacial Item")

To create this script you must use the action, Create Integration Scripts from the Automation Scripting application. You should choose the Object structure name, in this case MXPO, and the fact that you are using the outbound processing. All you need to do is create the script code and save it. When you save the script code, the script is automatically attached to the Object Structure processing.