MBOs in Automation Scripts: Exception Handling

In our previous posts in this series, we talked about how an MboSet is a collection of Mbo objects. In this article, we’ll analyze the different types of exceptions that can be thrown and how each can be utilized.

Most methods that can be called through the Business Components can throw either a RemoteException and/or an MXException.

A RemoteException is thrown by the Java RMI system when an error occurs during remote communication. The exact nature of the error may be determined by checking the Exception object’s class – which may be one of seventeen subclasses of RemoteException.

For more details of these exceptions, refer to the documentation in the Java Development Kit.

The MXException is similar to the RemoteException in that the MXException is a super class for all related exceptions. The actual exceptions thrown will be instances of one of the following subclasses:

  • This is usually a fatal error from which the client cannot recover. It is usually a problem with either the server configuration or the database configuration. Examples of errors causing this exception are “Out of Memory on Server” or a SQL error. Two subclasses of this class have also been introduced:
    • Thrown when a client or server asks for a Mbo that is not known to the system. This is a fatal error that normally points to a bug in the system or an incorrectly configured data dictionary.
    • Thrown when the server, which should have an object running, does not respond. This is usually caused by an incorrect configuration of the remote configuration file or indicates that the server running the object is unavailable for some reason.
  • This is thrown as a result of any of the many possible application-level errors, most of which are not fatal. Such exceptions should be caught by the client and rectified by the logic of the calling program. Examples of reasons why this exception might be thrown are:
    • Trying to set the value of a date attribute to the characters “ABC”.
    • Trying to set the “eqnum” attribute in a WO object to an invalid equipment number.
  • Thrown when an attempt is made to set an attribute that is read only or access a method that is currently not available. Methods may be unavailable because of security restrictions or because the object is not currently in the correct state.

More information about an exception can be retrieved by accessing the getMessage(), getErrorGroup() and getErrorKey() methods in the thrown object. These methods constitute two different approaches to identifying the exact cause of the exception.

The getMessage() method returns a string which contains a message describing the nature of the error. A message from a nested exception, if there was one, is included here as well. Such messages are intended to be for the benefit of human users. If the intention is that the code itself should handle the recovery from the error, then it is more appropriate to call the other two methods.

The error group, returned by getErrorGroup(), usually corresponds to the name of a text file in the RESOURCES\DEFAULTS directory. For example, if the error group is “workorder”, then the textual error description is in resources\default\workorder.txt.

The error key, returned by getErrorKey(), identifies an exact line in the text file that describes the error. For example, “addstatus” identifies a line in workorder.txt that has the line of text “addstatus=WorkOrder status cannot be added without a work order”.

When the intention is that the code itself should recover from an error, it is far easier to use an error key as input for a condition statement than a message. Note that the error description pointed to by the error key is not necessarily the same as the message returned by getMessage().

Exceptions may also provide greater detail in the form of parameters. These are used to supply information for a more general type of exception. For example, if an attempt is made to call the setValue() method using an invalid attribute name then the error group “system” and the error key “noattribute” would be returned. The exception thrown would also contain as part of its parameter list the name of the attribute that could not be found. To access these parameters, use the methods hasParameters() and getParameters().

An MXException may be thrown as the result of another exception occurring in the system. An example of this is a SQLException. These exceptions are not thrown directly from the methods because it would make method signatures complicated. The programmer can gain access to the nested exception by referencing the member “detail”, which is public, of the thrown exception.

A code sample demonstrating the use of an MXException follows:

MXSession s = MXSession.getSession(); s.setHost(hostname); s.setUserName(username); s.setPassword(password); // Display any problems with the connect to the user. try { System.out.println(“Connecting to (” + s.getHost() + “/”+ s.getServer() + “)” ); s.connect(); } catch(MXAccessException ma) { String group = ma.getErrorGroup(); String key = ma.getErrorKey(); System.out.println(“Group : ” + group + “ Key : ” + key + “ Message :” + ma.getMessage()); }

MBOs in Automation Scripts: Adding New Objects to the Collection

In our previous posts in this series, we talked about how an MboSet is a collection of Mbo objects. This is analogous to a spreadsheet of data representing an MboSet, and a single row within that spreadsheet representing an Mbo. This article will discuss how to update single attributes on an Mbo, or in our spreadsheet analogy, update data within a single cell within a row.

If there is a need to add a new object to a collection, the add() method may be on an MboSet, as in the following example:

newWo = woSet.add()

This returns a reference to the newly added object and sets the current index in the collection to point to it. The location of the new object within the collection is not guaranteed, so no assumptions should be made as to where it has been placed relative to the previous current index. You can use the getString() method to fetch default or autokey values for particular attributes. One of the best examples of an autokey value is the “wonum” attribute, which must be unique as the principal identifier of a work order. These values are set automatically by Maximo as part of the add() method.

Once a new object has been created, the attributes of the object can be altered by using the setValue() method. This method call can be performed directly on the newly added object:

newWo.setValue("assetnum", "11430")

As mentioned previously, changing an attribute value may result in other attribute values being updated.

Once all values have been set, the save() method should then be called:

woSet.save()

The save() method may throw an exception, as was discussed in the section on modifying objects. The most common exception thrown by the saving of a newly added object results from a required attribute not being set. The exception returned in this case is of type MXApplicationException with an error group and key of “system” and “null”, respectively.

Our next article will focus on exception handling with Mbos and MboSets.

MBOs in Automation Scripts: Update Attributes of an Mbo

In our previous posts in this series, we talked about how an MboSet is a collection of Mbo objects. This is analogous to a spreadsheet of data representing an MboSet, and a single row within that spreadsheet representing an Mbo. This article will discuss how to update single attributes on an Mbo, or in our spreadsheet analogy, update data within a single cell within a row.

To modify an object in the collection, the object must first be found. Most of the time, the launch points in automation scripts will supply an implicit variable called mbo that refers to the current record. Alternatively, you can get a handle to an Mbo from an MboSet as described in previous articles of this series – remember the setWhere() and moveFirst() methods. Once you have an Mbo, changes are made using the setValue() method. As is the case with other methods, setValue() is a member of both the Mbo and MboSet classes.

Our first example changes the description of the current Mbo (a work order) by calling the mbo variable’s version of the setValue() method.

mbo.setValue("DESCRIPTION", "Changed the description")

In the setValue() method, passing the second argument as a string works for all attributes, regardless of their underlying data types. For example, if an attribute is a numeric type, passing the string “1” is allowed. There are also setValue() method calls that take the long, int, Date, boolean, float, or double data types as the second argument.

This call will throw an exception if the underlying data type does not match the type of the value passed in the call. For example the following code does not work.

mbo.setValue("description", 1.0)

A call to set the value of a particular attribute may result in the values of other attributes changing. For example, setting the “assetnum” attribute of a work order may result in the “location” attribute being updated as well. To check the new value, getString() could be used on the “location” attribute.

Once all values have been modified, the save() method can then be called.

mbo.getThisMboSet().save()

This saves and commits all changes to the database. In most cases, an explicit call to save() is not necessary. In cases where the automation script fires before the save event, the save event will trigger automatically after the execution of the script.

If there is an error during the save, an exception is thrown and the changes are not saved. In that case, the collection is left in exactly the same state as prior to the save() call. This allows appropriate action to be taken and the save() call to be resubmitted.

There is a third argument that can be passed to the setValue() method, which is the Access Modifier. This should only be used when you are certain of the implications of using it. In essence, standard application logic can be bypassed by using access modifiers, which is why they should be used only when necessary. Standard access modifiers are:

  1. NOACCESSCHECK: Suppresses the “Field is Read Only” message and performs the update regardless of whether the field is editable or not.
  2. NOVALIDATION: Update the field without validating the data being entered.
  3. NOACTION: Do not perform related system actions when updating. For example, do not update the location on a work order when updating the asset.

These access modifiers can be used on an update by themselves or combined together. For example, to update a field even though Maximo has the field marked as read only:

mbo.setValue("DESCRIPTION", "Update a read only field", mbo.NOACCESSCHECK)

To update a field and not perform the validation and action events:

mbo.setValue("DESCRIPTION", "Update a field without validation or action", mbo.NOVALIDATION|mbo.NOACTION)

Putting all three together:

mbo.setValue("DESCRIPTION", "Please be careful doing this", mbo.NOACCESSCHECK|mbo.NOVALIDATION|mbo.NOACTION)

A list of attributes whose values may be modified and the objects to which they belong can be found in the MAXATTRIBUTE table in the MAXIMO database. The OBJECTNAME column of this table corresponds to the object name. The ATTRIBUTENAME column corresponds to the attribute name.

Our next article will focus on how to add new Mbo records into an MboSet.

MBOs in Automation Scripts: Reading Multiple Attributes of an MboSet

In our previous posts in this series, we talked about how an MboSet is a collection of Mbo objects. This is analogous to a spreadsheet of data representing an MboSet, and a single row within that spreadsheet representing an Mbo. This article will discuss how to read multiple attributes from an Mbo, or in our spreadsheet analogy, look at data within multiple cells within a single row.

Because most of the objects in Maximo possess many attributes, a long series of getString() method calls might be required to retrieve all the necessary information in a given situation. To make such retrieval more convenient, a method called getMboValueData() has been provided. It allows the programmer to fetch many data items in a single operation by specifying a list of attributes and, if desired, a set of rows in the collection. The result of such a call is a one- or two-dimensional array of MboValueData objects. Another advantage of this approach is that it speeds up processing over a network. When a number of data items are retrieved in a single call over a network rather than many, the efficiency advantages are self-evident.

The getMboValueData() method is available in several versions; as with many other methods it is a member of both the MboSet and the Mbo classes. The general syntax of the most-used version is as follows:

valueDataList = mbo.getThisMboSet().getMboValueData(attributes[])

or

valueDataList = mbo.getMboValueData(attributes[])

The attributes variable above is an array of strings containing a list of attribute names (e.g. “wonum”, “description”, “assetnum”, or “asset.description”). The “asset.description” attribute is an example of “dot notation” using Maximo relationships (more on relationships in a subsequent article). Dot notation is a shortcut that allows you to retrieve data from a related object without accessing the related object itself. For example, if a Work Order object has a value entered in its “assetnum” attribute, passing “asset.description” to either getString() or getMboValueData() will fetch the specified Asset’s description. Thus, by combining dot notation with getMboValueData(), data can be simultaneously retrieved in one method call even from objects that are in different Maximo Business Objects. This is an important part of the convenience that using getMboValueData() confers.

The return from the getMboValueData() method is an array of MboValueData objects. Each one contains the value of the attribute specified in the corresponding position in the attributes parameter. There are several methods which allow retrieval of this data value; the most often used, getData(), returns the data as a string. Not only can the data value be recovered; there are also methods which return information about the attribute itself, such as its maximum length, whether it is read-only, etc. This additional information is very useful in many situations.

A code sample follows:

attributeList = [ "wonum", "description", "assetnum", "asset.description" ]
// Assume the mbo is a Work Order and that the Work Order object has an Asset associated with it.
valueData = mbo.getMboValueData(attributeList);
print "The description of Work Order " + valueData[0].getData() + " is " + valueData[1].getData()
print "The Asset associated with Work Order " + valueData[0].getData() + " has identifier " + valueData[2].getData() + " and description " + valueData[3].getData()

If any of the attributes specified in the array of attribute names (attributeList in the above example) does not exist, a null value is placed in the corresponding position of the returned array. Similarly, if any of the dot notation entries in the list refer to nonexistent related objects, nulls are returned in those positions as well. In the above example, if the mbo variable has no value specified in its “assetnum” attribute, then null would be returned in the position where “asset.description” is entered in attributeList.

There are two other versions of the getMboValueData() method. One is a special case of the version described above. The syntax is:

valueData = mbo.getThisMboSet().getMboValueData(attribute)

or

valueData = mbo.getMboValueData(attribute)

The argument is a string which is a single attribute name and the return is a single MboValueData object. As above, this method is a member of both the MboSet and Mbo classes. Even though it returns less data than the more general versions, it may still be preferable in certain situations. For example, if the desired result is information about a single attribute (e.g. read-only, length, etc.) of some object, then it is sufficient to apply this method to that single attribute rather than many.

The remaining version is more general in nature. This is a member of only the MboSet class and allows the retrieval of data not only for multiple attributes but for multiple objects as well. It is used when many objects are to be processed at once, such as during the generation of a report or the filling of a table. The syntax is:

valueDataList = mbo.getThisMboSet().getMboValueData(firstRow, rowCount, attributes[])

The firstRow and rowCount variables are integer arguments; they specify the first row and the number of rows in the row set to be retrieved, respectively. The most commonly used values are 0 and mbo.getThisMboSet().count(), which fetch data for the entire collection.

The attributes variable is an array of strings containing a list of attribute names, as above. The return is a two-dimensional array of MboValueData objects. A code sample follows.

attributeList[] = [ "wonum", "description", "statusdate" ]
// Assume the woSet collection is non-empty
woCount = mbo.getThisMboSet().count()
valueData = mbo.getThisMboSet().getMboValueData(0, woCount, attributeList)
for i in range(len(valueData)):
    print "The description of Work Order " + valueData[i][0].getData() + " is " + valueData[i][1].getData()
    print "The Status Date of Work Order " + valueData[i][0].getData() + " is " + valueData[i][2].getData()
}

That’s it for now. Our next article will focus on how to modify attribute values of an Mbo in an MboSet.

MBOs in Automation Scripts: Reading Single Attributes of an MboSet

In our last post in this series, we talked about how an MboSet is a collection of Mbo objects. This is analogous to a spreadsheet of data representing an MboSet, and a single row within that spreadsheet representing an Mbo. This article will discuss how to read single attributes from an Mbo, or in our spreadsheet analogy, look at data within a single cell within a row.

Once an MboSet has been fetched, data associated with the individual objects it contains can be extracted. Attributes that are associated with character data types, such as ALN, UPPER, and LOWER, can be retrieved by using the getString() method.

The getString() method is a member of both the Mbo and MboSet classes. The Mbo version fetches data from the Mbo object itself; the MboSet version fetches data from the current member of the MboSet.

For example, here moveFirst() is called to set the current member pointer to the first object in the woSet collection. Since woSet was newly restricted from a new setWhere() call, moveFirst() also triggers the retrieval of the record from the server. The getString() method of the woSet object is called and the value of the description attribute of the current member is retrieved.

woSet = mbo.getThisMboSet()
woSet.setWhere("wonum='1100'")
woSet.moveFirst()
woDesc = woSet.getString("DESCRIPTION")

In the next example the moveFirst() call, in addition to the above functions, also returns the work order object which is the new current member of woSet. The getString() method is called through the object wo and the value of the description attribute is retrieved.

woSet = mbo.getThisMboSet()
woSet.setWhere("wonum='1100'")
wo = woSet.moveFirst()
woDesc = wo.getString("DESCRIPTION")

Next, moveFirst() is called to make the first object the current member. A reference to that member is then retrieved by a getMbo() call. The getString() method is then called as before.

woSet = mbo.getThisMboSet()
woSet.setWhere("wonum='1100'")
woSet.moveFirst()
wo = woSet.getMbo()
woDesc = wo.getString("DESCRIPTION")

Note that the result is the same in each of these three examples. Which combination of calls should be used to reach the result depends entirely on what is most convenient in a given situation.

The getString() method may be used on all attributes regardless of their data types.

There are also getDate(), getInt(), getLong(), getByte(), getBoolean(), getFloat() and getDouble() methods, which return the value of an attribute as something other than a string. An attempt to use these methods on an underlying attribute not of the correct type results in an exception being thrown. For example the following does not work because the attribute is not of type DATE or DATETIME.

woDate = woSet.getDate("DESCRIPTION")

A list of the attributes whose values may be retrieved and the objects to which they belong is listed in the MAXATTRIBUTE table in the Maximo database. The OBJECTNAME column of this table corresponds to the table name, while the ATTRIBUTENAME column corresponds to the column name.

For a complete description of all methods available, refer to the Mbo and MboSet JavaDocs on the IBM Asset Management Developer Center.

Our next article will focus on reading multiple attributes from the current record in an MboSet.