Category: Automation Script (Page 1 of 2)

How to run SQL query in Maximo without database access?

With the introduction of the Maximo Application Suite, I have had to deal with more and more Maximo environments on the cloud. This often means there is no access to the backend such as the database or the Websphere/Openshift console. Sometimes, to troubleshoot issues, it is critical to be able to run queries on the database. In this post, I will introduce a new approach to accessing the database using Automation Script.

From Maximo version 7.6.0.9, we can now build custom API using automation script. This is a powerful new feature yet it looks to be underutilized by the community.

The first obvious use case is it gives us the freedom to build any API we want without being restricted by the limitations of the Maximo Integration Framework. For example, we can create an API that returns data in CSV or binary format. Or we can use it to upload data and bypass the business layer.

Since it allows us to use the browser to interact with Automation Script, and the script framework has access to all Java functions of the MBO layer, We can exploit it to execute all sorts of weird operations.

https://[MAXIMO_ROOT]/maximo/oslc/script/[SCRIPT_NAME]

In an article I posted a few days ago, I used API script to call a Java function to refresh Maximo sequence and avoid a restart. Using the same approach, we can do a database configuration and deployment without downtime

In this post, I’ll demonstrate how we can use API script to run SELECT, UPDATE, DELETE SQL statements to Maximo database without direct DB access. This can come in handy when DB access is restricted. Of course, we can use MXLoader to achieve the same result. However, this method is a lot more convenient.

Creating an API script is very simple, we just need to create a script without a launch point. Then we can call it by accessing this URL on the browser:

If you’re already logged in and have a session. That is all it takes. Otherwise, to authenticate the request, you can pass in username and password parameters like you normally do when calling the REST API.

https://[MAXIMO_ROOT]/maximo/oslc/script/[SCRIPT_NAME]?_lid=[USERNAME]&_lpwd=[PASSWORD]

To run a SELECT query on the database, I created a script named RUNSQL with the code below:

To use the script to run a query, I typed the SQL query directly in the URL in the sql parameter as below.

https://[MAXIMO_URL]/maximo/oslc/script/runsql?method=SELECT&sql=SELECT top 10 assetnum,description,status FROM asset WHERE status = 'OPERATING'

In this case, the data is returned to the browser in the CSV format.

To execute a query that does not return data (INSERT/UPDATE/DELETE):

https://[MAXIMO_URL]/maximo/oslc/script/runsql?method=DELETE&sql=DELETE maxsession WHERE userid = 'maxadmin'

Note: I have tested this against SQL Server. Haven’t got a chance to test it against DB2 and Oracle database.

Consider Time Zone with automation script

It took me some time to get to this piece of code, but requirement changed and I needed to ditch it. But I’m sure I’ll need to use it at some point in the future, so why not put a note here just in case.

Requirement

– A client has many hotels in Sydney and Brisbane which are in two different time zones. Sydney has daylight saving while Brisbane doesn’t have. In other words, there is one hour difference for half of the year, and no time difference for the other half.

– Client wants to display a warning message to the user that when he/she raised a service request out of normal working hours (7AM to 7PM), if it’s a high priority item, they should make a phone call instead.

Finding:

– User profile has a time zone setting. We can also associate a time zone to a specific site or location

– Time stored in the database is server time. In this case, the server is in Sydney, as such all date/time values are Sydney time. The time zone associated to site or location won’t affect the time values in Work Order (e.g. reporteddate)

– Time displayed on the screen is converted to user’s time zone.

– Therefore, if we use a conditional expression with a standard SQL where clause, or create a saved query using the time part, it can be inaccurate due to the values are all Sydney time

Example:

– During summer time when there is daylight saving in Sydney, there is 1h difference between Brisbane and Sydney. A user in Brisbane creates a ticket at 6:30 PM for a location in Sydney, that would be 7:30 PM. 

– If we consider user’s time zone, that’s still within working hours. If we consider the site’s time zone, that’s off hours. Based on that, we might want to display our warning accordingly.

– The custom conditional expression automation script below works for me:

– In the case we just want to convert time to one specific zone, we can use the java.util.TimeZone class:

from java.util import TimeZone

timeZone = TimeZone.getTimeZone("Australia/Sydney")

Send email from automation script

Simple stuff but I got a few people asked me this same question, so here is how to create an automation script to send email from Maximo:

1 – Create a Communication Template:

  • Template ID: MY_COMM_TEMPLATE
  • Description: Test Communication Template
  • Applies To: ASSET
  • Send From: <your_email@address> (Note: to make this work, you must have setup smtp and able to send email from Maximo first
  • Subject and message: as shown below
  • In the “Recipients” tab, add an Email recipient pointing to your own email:

2 – Create an Automation Script

Create an autoscript with Object Launch Point on the “ASSET” object, on the SAVE (Update) event, choose Language = Python and copy/paste the following sample script:

3 – Test sending email

Open an Asset then change its status to INACTIVE, you should receive an notification in your email inbox:

Process Inbound Twilio SMS message in Maximo

In my previous post, I provided some demos on how we can process inbound text messages to create SR and route Workflow. There are a few people on LinkedIn asked me how to do it. I also like to explore the option of integrating Maximo with Twilio directly without the need for AppConnect. With Maximo 7.6, we can create a simple API using automation script. To prove the concept, the video demonstrates how we can create a simple API with automation script and direct Twilio HTTP requests to Maximo:


Below is the source code for the script in the video:

In this case, for the sake of simplicity, I only include the bare minimum components to make it works. You will probably want to write some scripts to automatically populate the “Reported By” person based on source phone number and populate other details like location/asset number based on the content of the incoming message. Also, it would be nice to respond with a text message for confirmation and provide the users with the new SR number.

With this method, we’ll need Maximo to be accessible from the Internet, it’s is not a problem for a cloud-based system. But if you have an onpremise system, you’ll need to configure port-forwarding to make Maximo accessible from the Internet. For development, since my ADSL router does not support port-forwarding, I used a tool called ‘ngrok’ to forward access of my local Maximo to the web via ngrok.io

The Scripted API is a new feature in Maximo 7.6. Thus this approach won’t work if you have an older Maximo version. It that case, probably using App Connect is an easier option. With App Connect, I used it for two functions:

  • Act as an API gateway to route Twilio request to a local server
  • Quickly transform HTTP request to REST/JSON request which Maximo can easily consume

App Connect is just a fancy name for a newer version of IBM Integration Bus (which IBM now call it App Connect Enterprise) and a bunch of new cloud-enabling features. So you’ll need some basic understanding of IIB in order to set it up. For connecting App Connect (the cloud component) and local Integration server, I followed the tutorial here: link

Send SMS Notification from Maximo using Twilio

Email or SMS Notification?

Occassionally, someone would ask me if we can configure Maximo to send notifications via SMS. With Maximo, the answer to such questions is always Yes. However, with this one, it will cost some money, very cheap though.

Since it is not free, I would say for most Maximo notification scenarios, the user would be ok with email notification which is free. However, there are certain scenarios where SMS notifications can add value such as:

  • Notify a field worker when a new high priority Work Order is assigned to him/her
  • Notify an asset owner when the asset deviates from the normal operating parameter range or when downtime is reported
  • Notify Maximo admin when there are repeated login attempts from an uncommon IP address or when there is a major problem tracked by the Escalation app.

In the section below, I provide the detailed steps on how to set up a trial Twilio account and send notifications from Maximo

A. Setup a Twilio trial account

With your trial account, Twilio will give you a small initial balance to buy a phone number and use the service. I got $15, and it will cost me $6 to buy a phone number, and 5 cents to send a text message.

  1. Create a trial account on Twilio.com: you’ll have to use your real mobile phone number to verify the account.
  2. Login using your trial account, go to Console, then create a new Project. Select “Products”, then choose “Programmable SMS”. Give your project a name, for example: “Maximo”. Then skip the rest of the steps to create the project
  3. On the left menu, click on the icon with the title “Programmable SMS”. In this menu, open the “SMS” submenu, open the “Messaging Services” page, then click on “Create new Messaging Service
  4. Give the messaging service a name, such as “MaximoNotification”. For use case, choose “Notifications, Outbound Only”. Then click on Create
  5. After the messaging service is created, open it. On the left menu, click on Number to open it, then click on “Buy a Number” to purchase a telephone number. On the pop-up, select your country code, then click on “Search”. It will give you a list of numbers to choose from
  6. Choose a number with SMS capability, and click on Buy. Depends on the country you live in, you may have to enter some details for your new number. I’m in Australia, so I have to enter my address.
  7. After purchasing a number, you are ready to send SMS messages from your Twilio account.
    Please note: with Trial account, it can only send SMS messages to a Verified Number. Your mobile number which you entered when registering is automatically added to this list. So you will be able to send SMS to that number. But if you like to send SMS to a different number, you’ll have to add it to the ‘Verified Caller ID’ list first.
  8. Go to “Console Dashboard”, open the “Settings” sub-menu, then open “General” page to take note of your Account SID, and Authentication Token. You will need it to send an SMS from Maximo.

B. Configure Maximo to send REST requests to Twilio

There are different ways to set this up depending on your requirement. In this example, I’ll create an ‘Action’ automation script, and send a REST request using Apache HTTPClient library. Action auto script can be used with Workflows or Escalation. In this example, I’ll send out an SMS when the workflow is initiated on the Service Request application.

The setup steps are as follows:

  1. Go to Automation Script application, create an ‘Action launch point’ automation script. Give the launch point a name, for example: “SENDSMS”. Set the action name as “SENDSMS” too. Set “SR” as the main object. Select “Jython” for script language. For the script, copy/paste the example code below to the “Source Code”
  2. With the code above, replace {YOUR_ACCOUNT_SID}, {YOUR_AUTH_TOKEN}, {YOUR_PURCHASED_PHONE_NUMBER} with actual details of your Twilio account. For testing purposes, replace {RECIPIENT_PHONE_NUMBER} with your phone number. Later on, once it works, you can replace it with a variable retrieved from your record using mbo.getString()
  3. Create a new workflow “SEND_SMS” for the SR object, connect the Start and Stop node with one line. And set the line to use ‘SENDSMS’ action. After that, Enable and Activate the Workflow. And remember to Add Workflow to SR application using the Select Action menu if you haven’t done so.
  4. Open an SR record, then click on the route button to initiate the “SEND_SMS” workflow
  5. After clicking on the Route workflow button, you should be able to receive an SMS message on your phone:

Source Code:

In a later post, I’ll talk about integrating Twilio with Maximo to create new Service Requests via SMS. For now, good luck with spamming your end-users with endless work requests 🙂

MboSet performance, Memory Cache, and DB Call

 

I recently had to look at ways to improve the performance of a custom-built operation in Maximo. Essentially, it is one of the many validation operations take place after a user uploads a PO with a few hundred thousand lines.  The team here already built a high-performance engine to handle the process, but even with it, this particular operation still takes around 15-17 milliseconds to process each line which is too slow for their current standard. Imagine processing 200,000 PO lines, it will take nearly an hour just for this operation alone. There are a few dozen of these operations that need to be executed, plus other standard basic Maximo operations like status change or save, the whole process takes up many hours.
 
With this millisecond operation, many assumptions or standard recommendations for improving performance may not work. In some instances, following the standard recommendations actually make it slower. For example, many Maximo blogs suggest setting a MboSet to DISCARDABLE to reduce memory usage, but if the MboSet is re-used repeatedly, it will need to query from the DB again and again which can be costly. In this case, don’t set the MboSet to DISCARDABLE will allow Maximo to cache data in memory and thus make it faster. 
 
Usually, for performance tuning, a database query is the most expensive operation, and with Maximo, it has something to do with the getMboSet method. Therefore, a better insight into how it works will help us to devise the best strategy for writing code for high-performing operations. In this post, I’ll list out the findings of some of the experiments that I did around MboSet, hopefully will help save you some time if you have to deal with the same problem.

1. mboSet.count() versus getSize() versus isEmpty()
 
If we need to retrieve a MboSet via relationship such as itemSet = asset.getMboSet(“ALL_ITEM”), and we need to check if the result set is empty or not, we can use either itemSet.count() or .getSize() or .isEmpty(). The differences between them are:
  • count() will fire off a “Select count(*) From ITEM Where…” query; isEmpty() will fire off a “Select * From ITEM Where…” query.
  • getSize() will never fire off a DB query. It only returns the size of the mboSet which has been initialised in memory. Thus, if after the itemSet = asset.getMboSet(“ALL_ITEM”), we call cnt = itemSet.getSize(), we will get cnt = 0. After itemSet.moveFirst() is called, getSize will return cnt = 2 (because Maximo initialised one object ahead), and after we have looped through the whole itemSet using itemSet.moveNext(), calling itemSet.getSize() will return the actual size of the whole set.
  • isEmpty(), together with moveFirst(), or moveNext() or getMbo(i) function will call a “Select * From ITEM Where…” query if the itemSet never been initialized. But if the mboSet is already initialised, any subsequent call to those functions will just access the object in memory and thus doesn’t trigger another DB query.

Considering the following piece of code:

With this code, the mboSet.count() method will fire off a “Select count(*)” query. Then, when getMbo() method is called the first time, it will fire off a “Select * From…” query to initialise the mboSet. Although inside DB, the Select count(*) cost much less; from Maximo JVM, with a small table, it takes about the same amount of time (a few milliseconds) to execute the mboSet.count() and the mboSet.getMbo(i) method.  As such, the above piece of code will take about twice the amount of time to execute when compared to a loop that doesn’t use the count() method. This is consistent with the recommendation by Bruno in his blog.

However, considering a scenario when our logic only needs to check if a result set returned is empty or not, with a massive table, calling count() can be much faster than calling isEmpty().

2. Retrieve MboSet via a MXServer object versus via Relationship

When we retrieve a mboSet via relationship, the data will be cached in Maximo’s JVM memory. Thus, if the same relationship on the same mbo object is used again, it doesn’t fire off another DB query, thus improving performance significantly. However, if in our code, instead of using a relationship, we retrieve the mboSet via mxserver.getMboSet(), it will query the DB every time, thus could impact performance if the same mboSet can be re-used (when there is no change to the DB query). Therefore, in this case, it is advisable to retrieve the data via relationship instead. But be aware that in the case the same relationship is used, but with a different where clause, Maximo will have to call the DB to execute a new query, thus there will be no difference in terms of performance.

This leads to a scenario where you don’t want to add a new relationship to the object every time you need some specific operation. In this case, we can define a temporary relationship in our code as in the following sample:

itemSet = asset.getMboSet(“$TEMP_RELATIONSHIP”, “ITEM”, “status = ‘ACTIVE’”)

This will add a new temporary relationship during runtime, and as long as the where clause doesn’t change, the itemSet data is cached and thus the next time you call the getMboSet using this temporary relationship, data will be accessed from memory instead of being queried from the DB.

Sometimes, you will want to shorten your code using the dot notation in mbo.getString(), getInt(), or getBoolean() method etc., Maximo will try to use cached data instead of querying the database, thus considering the following piece of code:

The first getString statement will initialise the mboSet retrieved by the “ASSET_ITEM” relationship. Thus, subsequent getString statements don’t query the DB again. Because of that, in terms of performance, it will be similar to the following piece of code:

item = asset.getMboSet(“ASSET_ITEM”).getMbo(0) if (“ACTIVE”.equals(item.getString(“STATUS”))) { desc = item.getString(“DESCRIPTION”); orderunit = item.getString(“ORDERUNIT”) }

Using a relationship will help cache the data in memory, but if the data is only used once, all it does is increase memory usage and thus could potentially degrade performance instead of giving any benefit. Thus, you should be considering whether data can be re-used. If not, setting the mboSet to DISCARDABLE will ensure memory is freed up after use. So my recommendation for you to deal with performance issues is to always run your own test and make sure the right strategy is used for your scenario.

« Older posts