Category Archives: Maximo tech tips

Maximo to AWS S3 Document Attachment

Lot of Lift and Shifts from on-prem to Cloud are happening these days as organizations catch up to the benefits of moving from limited on-prem resources to “un-limited” cloud potential and pay as you go and only pay for what makes you go.

Maximo on cloud for any organization which relies on their EAM systems to run their business needs to be fault tolerant, highly available and scalable.

We design architectures like Horizontal/Vertical or even Hybrid Application clusters but document attachments still are files on disk.

That disk now needs to be available to Multiple Nodes/Servers, Availability Zones (AWS terminology) and regions (if you have global presense).

Ok enough, read below how to get Maximo Document Attachments stored in AWS S3 bucket and served from there when user wants to view.

S3 buckets are cheap storage (glacier is cheaper but thats for less frequently accessed data, so more like archive), can be cross replicated across Regions, versioned, encrypted and has 99.9% availability SLA from AWS.

In picture the steps are below:



The thing that makes it most easy is the abstract class in Maximo specifically designed to aid in custom/bespoke Document Storage solutions:


I have extended this class and implemented the required abstract methods and one of the most important one is “createAttachment”. This is the method that will be called when any attachment is added to a record in Maximo.

There is a system property (if not there ADD it), “mxe.attachmentstorage”, this property holds the fully qualified class name of our custom implementation of AttachmentStorage Abstract class.

That’s how our custom class will be called when any attachment is added to maximo records.

Methods implemented if you just want to add attachments:


public abstract String getAttachmentQualifiedName(MboRemote paramMboRemote, String paramString)
throws RemoteException, MXException

public abstract void createAttachment(String paramString1, byte[] paramArrayOfByte, String paramString2)
throws RemoteException, MXException


We need to add a few system properties if we want to make our implementation configurable, below:

mxe.s3.accesskey – Access Key for AWS IAM user, with S3 bucket access

mxe.s3.secretkey – Shhhh…its the secret key, like a password

mxe.s3.bucket – like a folder where files will be stored

mxe.s3.region – Region, bucket resides,cross replication can put it anywhere

I am putting this working solution in a GITHUB repo (below), if interested just let me know.

There are a few prerequisites:

  1. Need all the library jars which AWS Java SDK needs
  2. IAM User and Bucket creation

Next Steps (wishlist):

  • Use TransferManager in AWS SDK so large files are transfered more efficiently and fast
  • Implement Aspera FASP file uploads ( to S3 – Did it for a bespoke application where-in large (GB) files were transferred 10x faster from local to S3 buckets
  • SharePoint Integration with Maximo attached documents
  • Implement for IBM CLoud and Google Cloud
  • Implement streaming (there is a method streamAttachment for that or maybe not) using Amazon Managed Streaming for Kafka for large files like video streams from Drones gone out for some inspections
  • Anything else???


Site Specific MBO creation


I had the requirement from a Custom Cron task and I came across this so thought of sharing.

I wanted to add Workorders from my Cron based on the SiteID I was passing as a parameter. So, for each site I have a Cron instance.

What I did was use the setInsertSite, setInsertOrg method on the MboSet class.

So, before calling add I set the relevant SITE and ORG and then called add so it makes sure that only the appropriate sequence or autokey (based on site and org passed on MboSet) is updated.

Can be very useful in some scenarios so thought of sharing.


Run a Birt Report on click of a Button


I was asked this question by one of the visitor on my blog.

How do I run and view a report using bean class?

So, here is the way to execute any report on button click in maximo.

Tis method will be in a bean class and the mxevent value of that button will be runassetreport.

The only part which I think I need to explain in this is:“runtype”, “BIRT”);“appname”, “ASSET”);“reportname”, “asset_detail.rptdesign”);
String pageName = “reportd” + “1010”;

Because this solution talks about a BIRT Report the runtype is BIRT.

Appname is ASSET cause I am running azn asset detail report and this number 1010 is the reportnum from Report table.

As I was just doing a proof of concept I have hardcoded these values but ideally you will be using Report Mbo to get the relevant values.


    public int runassetreport() throws MXException, RemoteException, IOException, ServletException

        ReportAdminServiceRemote reportAdminServiceRemote = (ReportAdminServiceRemote)getMXSession().lookup(“BIRTREPORT”);
        int engineState = reportAdminServiceRemote.getReportEngineState();
        if (engineState == 2)
          this.clientSession.showMessageBox(this.clientSession.getCurrentEvent(), “Reports Overloaded”, getMessage(“reports”, “previewoverload”), 2);

          return 1;
        if (engineState == 1)
          this.clientSession.showMessageBox(this.clientSession.getCurrentEvent(), “Zero Concurrent Run”, this.clientSession.getMaxMessage(“reports”, “zeroConcurrentRun”).getMessage(), 2);

          return 1;
“runtype”, “BIRT”);
“appname”, “ASSET”);
“reportname”, “asset_detail.rptdesign”);
          String pageName = “reportd” + “1010”;
          ControlHandler lookup = RequestHandler.findDialog(this.sessionContext, pageName);
          if (lookup == null)
            String[] params1 = { pageName };
            Utility.showMessageBox(this.sessionContext.getCurrentEvent(), “reports”, “repappnoxml”, params1);
            return 1;
          RequestHandler.showDialog(this.sessionContext, pageName);
        catch (MXException e)
        return 1;


A private util method to get the maxmessages used in the showMessageBox method:

private String getMessage(String msgGrp, String msgKey) throws RemoteException, MXException
        return MXServer.getMXServer().getMaxMessageCache().getMessage(msgGrp, msgKey, this.sessionContext.getUserInfo()).getMessage();






Setting field values to null from interface tables or flat files data sources


In some cases we need to set some values in maximo tables to null using maximo integration framework.


Normally, if you just leave the field blank in the incoming flat file maximo does not update the field with null just because the incoming data has null.


You need to set the incoming null value as ~NULL~ as per the article from IBM below:



Parameters to improve Maximo performance using WebSphere

IBM WebSphere changed parameters in the JDK in IBM JDK1.4.2 SR3. Any WebSphere version using IBM JDK 1.4.2 SR3 or greater is affected.

The change may cause WebSphere to hold onto unused objects for up to ten minutes after they are no longer needed. In high usage environments, this may act as a memory leak so that the application is starved for memory. There is a parameter to control this behavior, ackTimeout.

Some networks may also benefit from a parameter change in the JDK to set the default TCP/IP stack to V4. This can improve network performance by since the default behavior will be for the standard IP V4 and WebSphere will not need to determine the IP type programmatically. This parameter is


Rapid deployment in Websphere environment


I am sure most of us have gone through this.

Consider the development environment or your local machine has WebSphere as the application server.

To rebuild and redeploy on WS environment takes up lot of time and effort.

Ofcourse the easiest and one of the faster ways to deploy is running sync-websphere-maximo.cmd.

What I use is an even faster approach. I just copy paste any files in maximouiweb.war directory in installedApps directory.

In case of business objects, I unzip businessobjects.jar in a folder by the same name “businessobjects.jar”, and then on just copy paste changed class files and restart Websphere.

Using this approach I do not have to redeploy, rebuild any jar or war. Just copy changed classes from Eclipse bin folder.

It saves lot of time and during the development phase you can focus more on coding rather than the tedious task of deployment.


Note: This approach is only recommended for dev environments and that too only for developers.

Using Commtemplate to send out email in custom Java Code

Hi Friends,

Consider the following scenario, you want to send out an email whenever the Owner Group field is changed on a workorder to everyone in the new owner group.

One option is to use MXServer.sendEMail with some hardcoded content or with just a few lines without any kind of formatting.

Better option is to design a Commtemplate and send out a very well formatted email with dynamic content.

There are just a few additional steps in code if you want to do that.

Step 1: Get the CommTemplate Mbo from MXserver

MboSetRemote commSetRemote = getMboSet(“$anylogicalnae”, “COMMTEMPLATE”, ” templateid=’YOUR_TEMPLATE’ “);

Step 2: Get the content:

if(commTemplateRemote.count() > 0)


CommTemplate commRemote = (CommTemplate)commTemplateRemote.getMbo(0);

// mboRemote should be that object whose content wil be replaced in commtemplate. So, if your commtemplate sends out workorder information nand you have used :wonum in your template make sure mboRemote is that object whose information will be resolved/replaced

SqlFormat sqf = new SqlFormat(mboRemote, commRemote.getString(“subject”));

//Set IgnoreUnresolved to true if you want to ignore any errors in commtemplate, errors for stuff like if you have used :wonum and it cannot resolve this attribute.


//Resolve Content method below converts template in to the dynamic content, by resolving I mean instead of :wonum it will put the current records wonum.

String subject = sqf.resolveContent();

//Similarly do it for the message cotent
sqf = new SqlFormat(mboRemote, commRemote.getString(“message”));


String message = sqf.resolveContent();

if (message.length() > 0)


                message = message + “rn”;


//Once you have resolved From, to everything else send out the email

MXServer.sendEMail(sendTO, null, null, sendFrom,subject, message, sendFrom, null, null);


A good combination can be if you also attach a report of the workorder with this email. For that you can read my post on attaching a birt report dynamically in java code.


Conditional Domains explained………


Consider the following scenario:

You want to add a new status for Workorder which is a Synonym for an existing one. This status is only visible based on a certain Work Type.

This looks very simple but there is just one small trick we need to achieve this.

First create the condition in Conditional Expression application as below:


Go to Domains application and add the new status in WOSTATUS domain. Lets call this as “REVW”.

In View Modify conditions select the condition which checks the worktype as per below screenshot. When you test you will get an error “WorkType” attribute not found. The trick is to add a field WORKTYPE to WOCHANGESTATUS table. Because, when this condition is used on selecting the status ABC it also tries to validate on the dialog table which in this case is a non-persistent table called WOCHANGESTATUS. Let me know if you have any questions.