Updated

  1. Added support to group by for ResourceSet
  2. Added support to sync for ResourceSet
  3. Added support to bulk for ResourceSet
  4. Added new examples about new API in TestOSLCAPI.java.
  5. Fixed bugs
  6. Removed references to javax.xml.bind.DatatypeConverter.printBase64Binary because that API is not available on Android. It now uses commons-codec to get base64 support.

Maximo Rest Client 1.0 Released!

  1. Added support to arbitrary parameters for Resource/ResourceSet
  2. Added support to arbitrary headers for get/post/patch/merge/delete
  3. Added support to order by for ResourceSet
  4. Added support to invoke action with properties for Resource
  5. Added new examples about new API in TestOSLCAPI.java.
  6. Fixed bugs

I. Introduction


The Maximo REST client library provides a set of driver APIs that can be consumed by a Java-based web component that wants to interface with a Maximo instance. The client APIs use the Maximo NextGen REST/JSON APIS, which were originally inspired by Linked Data principles. By using this API, you are able to create, update, delete, and query Maximo business objects by using Maximo integration framework object structures.

The following main components are included in this client library:

Currently the only supported data format is JSON, and we have two flavors of JSON: the lean and the namespaced. The lean format is supported starting in Maximo Asset Management version 7.6.0.1 and is the recommended format to use, because it uses less bandwidth.

II. Install


2.1 As a Maven dependency

2.1.1 Central repository

Maximo REST Client is available in the Maven Central repository as an open source artifact. It can be included in a Maven project easily by adding the dependency to the project. The Maven will automatically handle the maven transitive dependencies.

  1. Create a new Maven project.
  2. Add following dependency to your pom.xml file.

Latest Release

<dependency>
    <groupId>com.ibm.maximo</groupId>
    <artifactId>maximo-restclient</artifactId>
    <version>1.0</version>
</dependency>

Last Release

<dependency>
    <groupId>com.ibm.maximo</groupId>
    <artifactId>maximo-restclient</artifactId>
    <version>0.1</version>
</dependency>

2.1.2 Local repository

You can use a local repository if the Internet is unavailable or it is difficult to access the central repository for some reason. The client can be installed locally. After the installation, it can be included in a Maven project as well.

  1. Run mvn clean install -Dgpg.skip at the dictionary of library.
  2. Create a new Maven project
  3. Add following dependency to your pom.xml file.
<dependency>
    <groupId>com.ibm.maximo</groupId>
    <artifactId>maximo-restclient</artifactId>
    <version>VERSION</version>
</dependency>

Where VERSION is the version you gave this artifact in the pom.xml.

2.2 As a Java library

If the Maven environment is unavailable, the Maximo REST Client can be used as a regular reference library in the Java project. Because the client depends on javax-json, the javax-json and commons-codec libraries are also needed.

You can get it from http://repo1.maven.org/maven2/org/glassfish/javax.json/1.0.4/ or use the Maven dependency as shown in the following code:

<dependency>
    <groupId>org.glassfish</groupId>
    <artifactId>javax.json</artifactId>
    <version>1.0.4</version>
</dependency>
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>1.1</version>
</dependency>

When the javax.json-1.0.4.jar, commons-codec-1.1.jar, and maximo-restclient-0.1.jar files are ready, add them to the Java project as common reference libraries.

III. Usage


Maximo Resources, or object structures, represent a graph of related Maximo business objects (Mbos) that provides an atomic view/object to create, update, delete, and query the releated set of Mbos.

We will use the Work Order, Purchase Order and Companies Resources as examples to show you how to use the Maximo REST Client.

Note: The use cases can be found at TestOSLCApi.java.

3.1 Query a work order for work order set (mxwodetail)

The following instruction shows how to query a work order from Maximo Asset Management by using the Maximo RET Client Library.

3.1.1 Connect to Maximo Asset Management

Before you connect, it is necessary to set up the authentication and environment information in Options.

Options option = new Options().user("maxadmin").password("maxadmin").auth("maxauth");

Note: For Maximo Asset Management Multitenancy, take the tenant code = "00", as an example, in the following Options.

Options option = new Options().user("maxadmin").password("maxadmin").auth("maxauth").mt(true).tenantCode("00");
option.host("host").port(7001).lean(true);
MaximoConnector mc=new MaximoConnector(option).debug(true);
mc.connect();
MaximoConnector mc = new MaximoConnector(new Options().user("maxadmin").password("maxadmin").lean(true).auth("maxauth").host("host").port(7001));
mc.connect();

3.1.2 Query the work orders

By object structure name:

ResourceSet rs = mc.resourceSet("mxwodetail").select("wonum","status").where((new QueryWhere()).where("status").equalTo("APPR")).fetch();

By RESTful URI :

ResourceSet rs = mc.resourceSet(new URL("http://host:port/maximo/oslc/os/mxwodetail")).select("wonum","status").where((new QueryWhere()).where("status").equalTo("APPR")).fetch();
ResourceSet rs = mc.resourceSet("mxwodetail").select("wonum","status").where((new QueryWhere()).where("status").equalTo("APPR")).pageSize(10).fetch();
ResourceSet rs = mc.resourceSet("mxwodetail").select("wonum","status").where((new QueryWhere()).where("status").equalTo("APPR")).paging(true).fetch();
ResourceSet rs = mc.resourceSet("mxwodetail").select("wonum","status").where((new QueryWhere()).where("status").equalTo("APPR")).stablePaging(true).fetch();
rs.nextPage();
rs.previousPage();

For stable paging where currently only scrolling forward is supported, a call to previousPage() results in an error.

JsonObject jo = rs.toJSON(); 

Note: we support JSON output in byte array. Try the following code:

byte[] jodata = rs.toJSONBytes();

By specific URI:

String woUri = "http://host:port/maximo/oslc/os/mxwodetail/_QkVERk9SRC8xMDAw";

By using the ResourceSet

Resource re = rs.fetchMember(woUri);

Or by using MaximoConnector:

MaximoConnector mc = new MaximoConnector(new Options().user("maxadmin").password("maxadmin").lean(true).auth("maxauth").host("host").port(7001));
mc.connect();
Resource re = mc.resource(woUri);

By index, which will query the member resource from the resourceset collection and will not make a trip to the server:

Resource re = rs.member(0);
re.reload("wonum","status","assetnum","location","wplabor.craft");

OR simply

re.reload("*");
JsonObject jo = re.toJSON();
byte[] joBytes = re.toJSONBytes();

3.1.3 Traverse the Work Orders

In some case, you might need to traverse some or all work orders. There are some helpful API in the Maximo REST Client.

MaximoConnector mc = new MaximoConnector(new Options().user("maxadmin").password("maxadmin").lean(true).auth("maxauth").host("host").port(7001));
mc.connect();
ResourceSet rs = mc.resourceSet("mxwodetail").pageSize(10).
for(int i=0;i<rs.count();i++){
    Resource re = rs.member(i);
    ...//other operations
}
for(int i=0;i<rs.totalCount();)
{   
    for(int j=0;j<rs.count();j++)
    {
        Resource re = rs.member(j);
    }
    i+=rs2.count();
    if(!rs2.hasNextPage())
    {
        break;
    }
    rs2.nextPage();
}

3.1.4 Disconnect from Maximo Asset Management

mc.disconnect();

3.2 Create a new work order (mxwodetail)

The following instruction shows how to create a new work order by Maximo Rest Client.

3.2.1 Get the work order set

MaximoConnector mc = new MaximoConnector(new Options().user("maxadmin").password("maxadmin").lean(true).auth("maxauth").host("host").port(7001));
mc.connect();
ResourceSet rs = mc.resourceSet("mxwodetail");

3.2.2 Create a new work order

For non-lean, add the prefix before the attribute:

JsonObject jo = Json.createObjectBuilder().add("spi:siteid", "BEDFORD").add("spi:description", "test").build();
Resource re = rs.create(jo);

For lean, skip the prefix, and use the attribute directly:

JsonObject jo = Json.createObjectBuilder().add("siteid", "BEDFORD").add("description", "test").build();
Resource re = rs.create(jo);
JsonObject wplJo = Json.createObjectBuilder().add("skilllevel", "FIRSTCLASS").add("craft", "ELECT").build();
JsonArray wpLaborArray = Json.createArrayBuilder().add(wplJo).build();
jo.add("wplabor",wpLaborArray );

Note: The sample uses the lean format.

3.2.3 Return with the properties

By default, the create operation will not return any content of the newly created work order. Because many attributes use default values or calues that auto-generated at the server based on Maximo business logic, it often makes sense to get the final representation of the newly created resource.

Instead of reselecting the work order, which makes a round-trip to the server, it is easy to get the resource content in response information when a new work order is created by the Maximo REST Client.

For non-lean:

Resource re = rs.create(jo,"spi:wonum", "spi:status","spi:statusdate","spi:description");

or simply:

Resource re = rs.create(jo,"*");

For lean:

Resource re = rs.create(jo,"wonum", "status","statusdate", "description");

or simply:

Resource re = rs.create(jo,"*");

3.3 Update a purchase order (mxpo)

To update a resource, you can use either the update() or the merge() APIs. The difference between these APIs how they handle the related child objects in the Resource. An example that uses the PO Resource (mxpo) best illustrates this difference. This example will reference two of the Maximo business object that is contained in the Resource: the PO(parent) and POLINE(child).

In this scenario, you have an existing purchase order that has one PO Line child objects. If you wanted to update the PO with a new PO Line entry, which is the second line, you use the merge() api. The merge process goes through the request "poline" array of objects and matches them up with the existing set of polines, which is the current one, and it will compare the rdf:about, if it is present in the new poline, to determine if this line is an existing poline or a new one. If it determines that it is a new one, it will proceed with creating this new poline, and you have two polines. If it finds a match, it will proceed with updating the matched one with the requested poline content, and you have one updated poline. If there are other existing polines, they won't be updated by the process.

If you use the update() API instead of the merge() API for this scenario, have only one PO Line. If there are any other PO Lines, they will be deleted. This deletion is because the update process treats the "poline" element as an atomic object and will update it as a complete replacement. Processing will insert the new PO Line or update the matching PO Line and delete all the other existing PO Lines for that PO.

The update() and merge() behavior applies only for child objects and not for the root object. The root object is always updated by using either API, assuming some attributes of that object have been changed.

In another scenario, you have an exsiting PO that has three polines(1,2,3) and you want to complete the following actions:

  1. delete poline#1
  2. update poline#3
  3. create a new poline#N

You need to do the following tasks:

Use the update() API and send three polines (2,3,4).

The update API will see that the request does not contain PO Line 1 and so it will delete it. It will skip the update of PO Line 2 because no attributes were change, update PO Line 3, and add a new line, PO Line 4.

After the update, the PO has lines 2,3 and 4.

So if you used the merge() API instead, the only difference is that PO Line 1 is not deleted. The PO has lines 1,2,3 and 4.

3.3.1 Update the poline in the purchase order

You can create a new PO Line on a purchase order and then update this purchase order by using the update() API to update the existing PO Line or replace the existing one by the a new PO Line.

If the polinenum(s) is matched, Maximo Asset Management will update the existing poline with a new array.

If the polinenum(s) is not matched, Maximo Asset Management will delete the existing poline array and create a new one with the new array. The array size will be equal to the new array.

MaximoConnector mc = new MaximoConnector(new Options().user("maxadmin").password("maxadmin").lean(true).auth("maxauth").host("host").port(7001));
mc.connect();
ResourceSet reSet = mc.resourceSet("MXPO").fetch();
Resource poRes  = reSet.member(0);
JsonObject polineObjIn = Json.createObjectBuilder().add("polinenum", 1).add("itemnum", "560-00").add("storeloc", "CENTRAL").build();
JsonArray polineArray = Json.createArrayBuilder().add(polineObjIn).build();
JsonObject poObj = Json.createObjectBuilder().add("poline", polineArray).build();
poRes.update(poObj);
JsonObject polineObjIn2 = Json.createObjectBuilder().add("polinenum", 2).add("itemnum", "0-0031").add("storeloc", "CENTRAL").build();
JsonArray polineArray2 = Json.createArrayBuilder().add(polineObjIn2).build();
JsonObject poObj2 = Json.createObjectBuilder().add("poline", polineArray2).build();
poRes.update(polineObj2);

At the end of it, you have a PO with 1 POLINE. The steps below explains how it happens:

The server-side framework will attempt to locate a POLINE that has the polinenum 2 and will not find any, because there is only a POLINE with polinenum 1).

3.3.2 Merge the poline in the purchase order

You can create a new poline on a purchase order and then merge this purchase order by using another poline object. You can create a new PO Line on a purchase order and then merge this purchase order by using the merge() API to update the existing line or add an additional line.

If the poline(s) is matched, Maximo Asset Management will update the existing poline with new array, which is similar to using the update() method.

If the poline(s) is not matched, Maximo Asset Management will add the new poline array to the existing poline array.

MaximoConnector mc = new MaximoConnector(new Options().user("maxadmin").password("maxadmin").lean(true).auth("maxauth").host("host").port(7001));
mc.connect();
ResourceSet reSet = mc.resourceSet("MXPO").fetch();
Resource poRes  = reSet.member(1);
JsonObject polineObjIn = Json.createObjectBuilder().add("polinenum", 1).add("itemnum", "560-00").add("storeloc", "CENTRAL").build();
JsonArray polineArray = Json.createArrayBuilder().add(polineObjIn).build();
JsonObject poObj = Json.createObjectBuilder().add("poline", polineArray ).build();
poRes.update(poObj);//this creates a POLINE with polinenum 1.
JsonObject polineObjIn3 = Json.createObjectBuilder().add("polinenum", 2).add("itemnum", "0-0031").add("storeloc", "CENTRAL").build();
JsonArray polineArray3 = Json.createArrayBuilder().add(polineObjIn3).build();
JsonObject polineObj3 = Json.createObjectBuilder().add("poline", polineObjIn3).build();
poRes.merge(polineObj3);//this will create a POLINE with polinenum 2.

At the end of it, you have a PO that has 2 POLINEs. The steps below explains how it happens:

The server-side framework will attempt to locate a POLINE that has the polinenum 2 and will not find any, because there is only a POLINE that has polinenum 1.

3.4 Delete a service request (mxsr)

You can delete an existing work order by using the Maximo REST Client.

3.4.1 Get an existing service request

MaximoConnector mc = new MaximoConnector(new Options().user("maxadmin").password("maxadmin").lean(true).auth("maxauth").host("host").port(7001));
mc.connect();
ResourceSet rs = mc.resourceSet("mxsr").

By using a specific URI:

String venUri = "http://localhost:7001/maximo/oslc/os/mxsr/_U1IvMTE3Mw--";

By using ResourceSet:

Resource re = rs.fetchMember(srUri);

Or byusing MaximoConnector:

Resource re = mc.resource(srUri);

By index:

Resource re = rs.member(0);

3.4.2 Delete the service request

mc.deleteResource(srUri);
re.delete();

3.5 Attachments

Attachments in Maximo Asset Management are documents, files, or images that are attached to a resource, such as a work order or service request. The following example shows how to add and delete an attachment on a work order. In the resource definition, the DOCLINKS object, which is a child of the work order object, supports the attachment data.

3.5.1 Create an attachment for an existing work order.

MaximoConnector mc = new MaximoConnector(new Options().user("maxadmin").password("maxadmin").lean(true).auth("maxauth").host("host").port(7001));
mc.connect();
ResourceSet rs = mc.resourceSet("mxwodetail").

By specific URI:

String woUri = "http://host:port/maximo/oslc/os/mxwodetail/_QkVERk9SRC8xMDAw";

By using ResourceSet:

Resource re = rs.fetchMember(woUri);

By using MaximoConnector:

Resource re = mc.resource(woUri);

By index (be careful with the range):

Resource re = rs.member(0);
AttachmentSet ats = re.attachmentSet();
String str = "hello world @ "+ Calendar.getInstance().getTime().toString();
byte[] data = str.getBytes("utf-8");
Attachment att = new Attachment().name("attachment.txt").description("test").data(data).meta("FILE", "Attachments");

By default,

att = ats.create(att);

Note: For the custom property name for doclinks such as "customdoclink",

att = ats.create("customdoclink",att);

3.5.2 Get the data from the attachment

Attachment att = ats.member(0);
byte[] data = att.toDoc();
JsonObject attMeta = att.toDocMeta();
JsonObject attMeta = att.fetchDocMeta();
String attUri = "http://host/maximo/oslc/os/mxwodetail/_QkVERk9SRC8xMDAw/DOCLINKS/28";
Attachment att = mc.attachment(attUri);
byte[] data = mc.attachedDoc(attUri);
JsonObject attMeta = mc.attachmentDocMeta(attUri);

3.5.3 Delete an attachment

By specific URI:

String woUri = "http://host:port/maximo/oslc/os/mxwodetail/_QkVERk9SRC8xMDAw";

By using ResourceSet:

Resource re = rs.fetchMember(woUri);

Or by using MaximoConnector:

Resource re = mc.resource(woUri);

By index (be careful with the range):

Resource re = rs.member(0);
AttachmentSet ats = re.attachmentSet();

By index:

Attachment att = ats.member(0);

By using a specific URI:

String attUri = "http://host/maximo/oslc/os/mxwodetail/_QkVERk9SRC8xMDAw/DOCLINKS/28";

By using AttachmentSet:

Attachment att = ats.fetchMember(attUri);

Or by using MaximoConnector:

Attachment att  = mc.attachment(attUri);

By using the attachment, which is useful when you already have the attachment:

att.delete();

By using MaximoConnector, which is useful when you have only the URI:

mc.deleteAttachment(attUri);

3.6 Saved query

Maximo Asset Management supports a feature that is called a Saved Query that is a pre-built query for an application, such as the Work Order Tracking application, which allows a user to easily retrieve a common set of data, for example, a list of approved work orders. Assuming public saved queries are present for an application, you can use the savedQuery() API to query records based on defined filter criterion. You also must ensure that the authorized application is set accordingly for the object structure you plan to use.

Take the "OWNER IS ME" query for the Work Order Tracking (WOTRACK) application as an example, which assumes that the MXWODETAIL object structure is setup with "WOTRACK" as the authorized application.

ResourceSet rs = mc.resourceSet("mxwodetail").savedQuery(new SavedQuery().name("WOTRACK:OWNER IS ME")).select("*").fetch();

The select("*") statement queries all attributes for the filtered set of mxwodetail. As mentioned earlier, you can do a partial resource selection, such as select("wonum","status").

You can also do further filtering with the saved query.

ResourceSet rs = mc.resourceSet("mxwodetail").savedQuery(new SavedQuery().name("WOTRACK:OWNER IS ME")).where(new QueryWhere().where("status").in("APPR","WAPPR")).select("wonum","status","statusdate").fetch();

3.7 Terms search

This feature is used mostly for text search. This feature needs the server-side object structure to be set up with the searchable attributes. For example, if it is setup with "description", you can use the hasTerms() API to set the searchable terms.

For example, you can select all the resources from oslcmxsr whose description contains email or finance.

MaximoConnector mc = new MaximoConnector(new Options().user("maxadmin").password("maxadmin").auth("maxauth").host("host").port(7001));
mc.connect();
ResourceSet res = mc.resourceSet("OSLCMXSR").hasTerms("email", "finance").select("spi:description", "spi:ticketid").pageSize(5).fetch();

3.8 Action

Actions are functional components of a resource that perform specific tasks, such as changing the status of a resource or moving a resource. This example uses the changeStatus action as an example (the changeStatus method is annotated marked as a WebMethod in the WorkOrder Service),

MaximoConnector mc = new MaximoConnector(new Options().user("maxadmin").password("maxadmin").lean(true).auth("maxauth").host("host").port(7001));
mc.connect();
ResourceSet reSet = mc.resourceSet("MXWODETAIL").where((new QueryWhere()).where("status").equalTo("WAPPR")).fetch();
Resource re = reSet.member(0);
JsonObject jo = Json.createObjectBuilder().add("status","APPR").add("memo","approval").build();
re.invokeAction("wsmethod:changeStatus", jo);

References

Java API