Technical Blog Post
Abstract
Maximo Rest Client Example: Calling an External REST Service (Logo Tiger 3) with a Maximo Client
Body
MOTIVATION:
When I searched for a REST client implemented within Maximo using Automation Scripting, all documents I could find gave information on Maximo REST services which is the reverse of my requirement. Hence, I thought it would be a good idea to post this example which can ease the things for developers looking for a similar requirement.
Maximo is focused on Asset Management, hence most of the time on customer site we need to integrate Maximo with ERP (SAP ERP, Oracle ERP etc.) or billing systems. I will present an example of a Maximo Jython REST client which sends information on company business object of Maximo to create a current (account) object on Logo Tiger (an ERP software from Turkey). Actually, I integrated company, item and purchase order objects for my project but I want to demostrate companies now since it is the simplest among all.
PREPARATION:
Maximo v7.6, which is the up-to-date version, supports Apache HttpClient 4.1 which will be used for our REST operations.
Note on Preliminary Work: Apache HttpComponents 4.5.3 is the latest version of http client and it includes more elegant and simple methods for development. First, I succesfully tested it via an external Java client but when I translated my code into Jython and ran it on Maximo I encountered “java.lang.NoClassDefFoundError”. This root cause of this error is that HttpComponents 4.5.3 library is surpassed by application server library (Websphere 8.5.5). I tried changing the order of class loaders and defining library within both Maximo and Websphere which did not work out. Then, I gave up on the latest version and used the version compatible with Maximo v7.6 and Websphere 8.5.5 which is Apache HttpClient 4.1.3.
You can download the client library and its dependencies from the links below:
httpclient-4.1.3.jar:
https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient/4.1.3
dependencies (httpcore-4.1.4.jar, commons-codec-1.4.jar, commons-logging-1.1.1.jar):
https://mvnrepository.com/artifact/org.apache.httpcomponents/httpcore/4.1.4
https://mvnrepository.com/artifact/commons-codec/commons-codec/1.4
https://mvnrepository.com/artifact/commons-logging/commons-logging/1.1.1
Since ext folder is in the classpath of application server, the most practical way is to copy these jars into the application server ext folder and restart Websphere.
The path for Websphere ext folder in Windows:
…\IBM\WebSphere\AppServer\lib\ext
DEVELOPMENT:
There is an online manual for Logo Tiger Objects REST Services, which can be accessed through the link below:
https://wikidocs.logo.com.tr/display/WUA/Logo+Objects+REST+Servis
According to the manual, we first need to get an access token via an http POST operation with basic authentication and some header parameters. Then, we will process another http POST with the response (access token) of first POST in the header authorization of type Bearer and the rest of the data as JSON body of the request.
First, we need to define a couple of parameters as Maximo system properties. They will be used for calling the REST service and getting access token.
Figure 1 - System Properties for REST Service
Then, we create an Automation Script with action type of launch point. It works on COMPANIES object. The Jython code for the Maximo REST Client is as follows:
# LOGO REST Client - Maximo Company to Logo Tiger Current Account from com.ibm.json.java import JSONObject from java.io import BufferedReader, IOException, InputStreamReader from java.lang import System, Class, String, StringBuffer from java.nio.charset import Charset from java.util import Date, Properties, List, ArrayList from org.apache.commons.codec.binary import Base64 from org.apache.http import HttpEntity, HttpHeaders, HttpResponse, HttpVersion from org.apache.http.client import ClientProtocolException, HttpClient from org.apache.http.client.entity import UrlEncodedFormEntity from org.apache.http.client.methods import HttpPost from org.apache.http.entity import StringEntity from org.apache.http.impl.client import DefaultHttpClient from org.apache.http.message import BasicNameValuePair from org.apache.http.params import BasicHttpParams, HttpParams, HttpProtocolParamBean from psdi.mbo import Mbo, MboRemote, MboSet, MboSetRemote from psdi.security import UserInfo from psdi.server import MXServer from sys import * # method for getting the access token with basic authentication def getAccessToken(): token = "" # get connection properties from System Properties properties = MXServer.getMXServer().getConfig() host = properties.getProperty("logo.rest.host") uri = host + '/api/v1/token' clientid = properties.getProperty("logo.rest.clientid") clientsecurity = properties.getProperty("logo.rest.clientsecurity") username = properties.getProperty("logo.rest.username") password = properties.getProperty("logo.rest.password") firmno = properties.getProperty("logo.rest.firmno") System.out.println('getAccessToken() - Properties fetched') # get authentication header auth = clientid + ":" + clientsecurity encodedAuth = String(Base64.encodeBase64(String.getBytes(auth, 'ISO-8859-1')),"UTF-8") authHeader = "Basic " + str(encodedAuth) # get http parameters params = BasicHttpParams() paramsBean = HttpProtocolParamBean(params) paramsBean.setVersion(HttpVersion.HTTP_1_1) paramsBean.setContentCharset("UTF-8") paramsBean.setUseExpectContinue(True) # get http body entities formparams = ArrayList() formparams.add(BasicNameValuePair("grant_type", "password")) formparams.add(BasicNameValuePair("username", username)) formparams.add(BasicNameValuePair("password", password)) formparams.add(BasicNameValuePair("firmno", firmno)) entity = UrlEncodedFormEntity(formparams, "UTF-8") # get client, http headers and request client = DefaultHttpClient() request = HttpPost(uri) request.setParams(params) request.addHeader(HttpHeaders.CONTENT_TYPE, "application/x-www-form-urlencoded") request.addHeader(HttpHeaders.ACCEPT, "application/x-www-form-urlencoded") request.addHeader(HttpHeaders.AUTHORIZATION, authHeader) request.setEntity(entity) # get client response response = client.execute(request) # return JSON response and access token status = response.getStatusLine().getStatusCode() obj = JSONObject.parse(response.getEntity().getContent()) token = str(obj.get("access_token")) return {'client':client, 'token':token} # method for releasing the open client connection def releaseConnection(client): if(client != None): client.getConnectionManager().shutdown() # method for creating the JSON string which is sent in http POST body def createJSONstring(): jsonStr = "" currCode = {'TL': 0, 'USD': 1, 'EURO': 2} currency = mbo.getString("CURRENCYCODE") obj = JSONObject() obj.put("INTERNAL_REFERENCE", 0) obj.put("RECORD_STATUS", 0) obj.put("CURRENCY", currCode[currency]) obj.put("ACCOUNT_TYPE", 3) obj.put("CODE", mbo.getString("CODE")) obj.put("TITLE", mbo.getString("NAME")) obj.put("ADDRESS1", mbo.getString("ADDRESS1")) obj.put("ADDRESS2", mbo.getString("ADDRESS1")) obj.put("CITY", mbo.getString("ADDRESS3")) obj.put("TOWN", mbo.getString("ADDRESS2")) obj.put("POST_CODE", mbo.getString("ADDRESS4")) obj.put("COUNTRY_CODE", mbo.getString("ADDRESS5")) obj.put("TELEPHONE1", mbo.getString("PHONE")) obj.put("TELEPHONE2", mbo.getString("CELLPHONE")) obj.put("FAX", mbo.getString("FAX")) obj.put("TAX_OFFICE", mbo.getString("TAXOFFICE")) obj.put("TAX_ID", mbo.getString("REGISTRATION2")) jsonStr = obj.serialize(True) return jsonStr # method for http POST using the path, JSON body and token with bearer authorization def httpPost(path, jsonstring, token): reference = None # get connection properties from System Properties properties = MXServer.getMXServer().getConfig() host = properties.getProperty("logo.rest.host") uri = host + path # get authentication header authHeader = "Bearer " + token # get http parameters params = BasicHttpParams() paramsBean = HttpProtocolParamBean(params) paramsBean.setVersion(HttpVersion.HTTP_1_1) paramsBean.setContentCharset("UTF-8") paramsBean.setUseExpectContinue(True) # get http body entities entity = StringEntity(jsonstring, "UTF-8") # get client, http headers and request client = DefaultHttpClient() request = HttpPost(uri) request.setParams(params) request.addHeader(HttpHeaders.CONTENT_TYPE, "application/json") request.addHeader(HttpHeaders.ACCEPT, "application/json") request.addHeader(HttpHeaders.AUTHORIZATION, authHeader) request.setEntity(entity) # get client response response = client.execute(request) # return JSON response and reference number status = response.getStatusLine().getStatusCode() obj = JSONObject.parse(response.getEntity().getContent()) reference = int(obj.get("INTERNAL_REFERENCE")) if obj.get("INTERNAL_REFERENCE") != None else None return reference # method for creating Maximo company record as a current account in Logo def addCompany2Logo(): jsonstr = createJSONstring() client = None try: connection = getAccessToken() client = connection['client'] token = connection['token'] System.out.println( 'TOKEN: ' + token) reference = httpPost("/api/v1/Arps", jsonstr, token) if(reference != None): mbo.setValue("LOGOREFNO", reference, 11L) mbo.getThisMboSet().save() System.out.println( 'REFERENCE: ' + str(reference)) except: System.out.println( 'Error:' + str(exc_info()[0]) + str(exc_info()[1])) finally: releaseConnection(client) System.out.println( 'finally - all connections closed') # Main part addCompany2Logo()
Code 1 - Jython Code for Rest Client
Finally, the action for the script above is substituted in an escalation which runs under a proper condition in an adequate time interval. One can alter this code to their specific needs for any other REST service or use it in a workflow or as an object event instead of an escalation.
UID
ibm11129845