November 12, 2013

Automatically stop workflow on closed work orders

When using a workflow to drive the status changes of an object in Maximo you shouldn't allow the users to change the status directly through the 'Change Status' action. This could be easily done removing the 'Change Status' sigoption from relevant users groups.
When users have both options enabled, it may happen that a closed work order has an active workflow on it.

In such cases you can create an escalation to automatically stop workflow on closed work orders.

First of all you have to create an action like this.
  • Action: STOPWF
  • Object: (leave blank)
  • Type: Custom
  • Value: psdi.workflow.StopWorkflowAction
Unfortunately the StopWorkflowAction class is included in Maximo 7.5.

Then you can create the escalation.
  • Escalation: STOPWF
  • Applies to: WFINSTANCE
  • Condition:active=1 and OWNERTABLE='WORKORDER' and exists (select 1 from workorder where status in ('CAN', 'CLOSE') and workorderid=ownerid)
  • Schedule: as you like
  • Action: STOPWF

Refer to this IBM TechNote for more details.

November 8, 2013

IBM Maximo Anywhere 7.5 leaked

Most of you already knows that IBM is going to release the long awaited next-gen mobile solution for Maximo. It will be called IBM Maximo Anywhere.
An IBM whitepaper has just been published and the announcement letter will follow soon.
While waiting for the announcement letter here are some leaked screenshots.



The most promising features are:
  1. Online/Offline mode
  2. Integrated barcode scanning capabilities
  3. Maps integration
  4. Android support (iOS and WP will follow)


Looks promising right?

October 30, 2013

Analyze groups to users association

The following SQL query lists all the security groups and the associated users.

SELECT maxgroup.groupname, maxgroup.description, maxuser.userid, maxuser.loginid, maxuser.defsite
FROM maxgroup
JOIN groupuser ON groupuser.groupname=maxgroup.groupname
JOIN maxuser ON maxuser.userid=groupuser.userid
ORDER BY maxgroup.groupname, maxuser.userid;

The best approach to analyze the results is to load them into Excel and create a pivot table.

October 21, 2013

Automatically refresh StartCenter

Users that heavily rely on StartCenters to check for incoming tickets or work orders sometimes ask if the StartCenter can automatically refresh itself.

To accomplish this you can inject a small JavaScript into the StartCenter JSP code.
Backup [SMPDIR]\maximo\applications\maximo\maximouiweb\webmodule\webclient\components\startcenter-options.jsp file and open it with a text editor.
Search for a row like this

<table ...

Add the following code just before that row.

<!-- BEGIN StartCenter automatic refresh -->
<script type="text/javascript">
  setTimeout("location.reload(true)", 60000);
</script>
<!-- END StartCenter automatic refresh -->

The refresh time in the previous script is in milliseconds so 60000 means 1 minute.

Rebuild and redeploy Maximo EAR file.

Now your start center will be automatically refreshed every 2 minutes.

Maximo 7.5 has introduced an exit warning so you may be prompted with a warning like this every minute.


To avoid this you can disable the exit warning as described in this TechNote.

Be careful when deciding how to set the refresh interval because setting it too low may impact overall system performances.

If this technique is not working you could look at this thread.

Enable autorefresh for one user group

A fellow Maximo consultant has applied this technique only to one user group using the following code.

<%@ page import="psdi.webclient.beans.startcntr.*" %>
<% 

try 
{
  SessionContext sessionContext = new SessionContext(wcs);
  StartCenterAppBean startcenter = (StartCenterAppBean)sessionContext.getCurrentApp().getAppBean();
  psdi.mbo.MboRemote mbo = startcenter.getMbo();
  String where = "groupname = '[GROUPNAME]' and userid in (select userid from maxuser where personid = '" + mbo.getUserInfo().getPersonId() + "')";
  psdi.mbo.MboSetRemote groups = mbo.getMboSet("$GROUPUSER", "GROUPUSER", where);
  groups.moveFirst(); 

  if ( !groups.isEmpty() ) 
  {
%>
    <script type="text/javascript"> setTimeout('window.location=window.location' ,10000); </script>
<% 
  } 
} 

catch (Exception e) 
{
  e.printStackTrace(); 
}
%>

The [GROUPNAME] must be replaced with the security group for which you want to enable the start center autorefresh.

Update
JedrekWiehas suggested to modify the script to set the session id in order to avoid duplication os user's sessions.

String refreshUrl = wcs.getMaximoRequestURI()
                  + "?event=loadapp&value=startcntr&"
                  + wcs.getUISessionUrlParameter()
                  + wcs.getCSRFTokenParameter();
%>
<script type="text/javascript"> setTimeout('window.location=window.location' ,10000); </script>
<% 



October 18, 2013

Object Quick Summary

One of the customer that I'm working with is heavily relying on work logs. Unfortunately, the work logs are displayed in a standard table so there is no way of quickly displaying all the logs entered by the users.



The solution I have designed is a QuickSummary tab in the Work Order Tracking application that displays the main attributes of the selected work order together with the historical work logs in one single text box.



The described solution relies on the Rich Text editor control available in TPAE 7.5 but can be easily adapted to a standard multiline text box available in previous version of Maximo.
Here are the configuration steps.


Database Configuration

The first step is to define a virtual (non persistent) attribute that will be used to display the work order summary text.

Open WORKORDER object in Database Configuration and add the following attribute.
  • Attribute: WOSUMMARY
  • Title: Summary
  • Type: CLOB
  • Persistent: false
Save and apply database changes.


Application Designer

The second step is to add a new tab to the WOTRACK application that contains just a large texbox that will display the attribute defined above.

Open WOTRACK application in Application Designer and export application XML. Open the wotrack.xml file with a text editor and add the following lines just before the </tabgroup></clientarea> tags.
Here is how the file should look like (the text in bold is what needs to be added).

  ...
  <tab id="summary" label="QuickSummary">
    <section id="summary_s1">
      <richtexteditor dataattribute="WOSUMMARY" height="400" id="summary_s1_wosummary" plugins="[]" extra_plugins="[]"/>
    </section>
  </tab>
  </tabgroup>
  </clientarea>
  <include id="pageFooter"/>
</page>
...


Script

The last configuration steps is to create the script that will fill the WOSUMMARY field and attach it to the rich text control. The sample script retrieves all the necessary information from the work order and builds an HTML string that it is used to set the field value.

Goto System Configuration > Platform Configuration > Automation Scripts.
Select Action > Create Script and enter this information:
  • Script: WOSUMMARY
  • Description: Init Workorder summary
  • Script language: jython
  • Source code: paste the code below

from java.lang import StringBuilder
from psdi.mbo import MboConstants


val = StringBuilder()

# retrieve attributes and writes them in HTML format in the buffer
val.append("ID: " + mbo.getString("WONUM") + "<br>")
val.append("Description: " + mbo.getString("DESCRIPTION") + "<br>")
val.append("Location: " + mbo.getString("LOCATION") + "<br>")
val.append("Asset: " + mbo.getString("ASSETNUM") + "<br><br>") 

# retrieve work log records and writes them in the buffer
worklogSet = mbo.getMboSet("MODIFYWORKLOG")

val.append("<hr>")
val.append("<h2>WORK LOG</h2>")
val.append("<hr>")

currMbo=worklogSet.moveFirst()

while currMbo is not None:
    
        val.append("<h3>" + currMbo.getString("DESCRIPTION") + "</h3><br>")
        val.append(currMbo.getString("DESCRIPTION_LONGDESCRIPTION") + "<br>")
        val.append("<hr>")
        currMbo=worklogSet.moveNext()
    

#  sets the formatted string in the attributes value
mbo.getMboValue("WOSUMMARY").setValue(val.toString(), MboConstants.NOACCESSCHECK | MboConstants.NOVALIDATION)

Click on the Create button.
Now you have to attach the script to the WOSUMMARY field. Select Action > Create Script with Object Launch Point and enter the following information:

  • Launch Point: WOSUMMARY
  • Descriptions: Fills the workorder summary
  • Object: WORKORDER
  • Active: True
  • Events: Initialize
  • Script: WOSUMMARY
Accept defaults on the other dialogs and create the launch point.

Now you are done. Open a sample Workorder and go the the QuickSummary tab and look at the results.


Further improvements

There are many ways to improve the QuickSummary feature.

Obviously you can extend the example to include your own fields and formatting to better fit your needs.

Look at how HTML tags have been used to format the output in the script. This means that you can improve the formatting of the summary to enhance readability.


Thanks to my colleague Dirk Hillmer who has helped me with TPAE scripting.

October 17, 2013

Implementing a Smart Search feature

Using Maximo search functionality can be sometimes complex or even frustrating.
Several customers have asked me if there is a simple Google-like functionality to search for all the objects that contains a specific set of words.
This article describes how to implement such feature in the Work Order Tracking application.

The solution described in this tutorial allows to display a simple search dialog when the user clicks on a specific button on the toolbar. When the user presses the 'Search' button the application will list the work orders containing all the keywords in any of the following fields: description, long description, location, asset, work logs.
Note that this is not achievable using the Advanced Search dialog since this will search with an AND logic instead of an OR logic implemented here.

In this screenshot you can see the custom search button and the very simple SmartSearch dialog.




The basic theory of how to create a custom dialog box to ask for user input is described in details in this article.


Create the custom Dialog Box

First of all we need to create the SmartSearch dialog box.
  1. Launch the Application Designer and open the WOTRACK application.
  2. Export the XML application definition to a local file.
  3. Open the exported file with a text editor.
  4. Scroll all the way down to the bottom of the file.
  5. Just before the </presentation> end tag, insert the XML code below.
  6. Save your changes.
  7. Go back to the Application Designer application.
  8. On the List tab, click on the Import Application Definition toolbar button to import your modified file.

Here is the dialog box definition.

<dialog beanclass="mxdev.webclient.beans.workorder.SmartSearchAppBean" id="ssearch" label="SmartSearch">
  <section border="false" id="ssearch_s1" mboname="smartsearch">
    <textbox dataattribute="searchtext" label="Text" id="ssearch_s1_1"/>
    <checkbox dataattribute="searchhist" label="Include historical records" id="ssearch_s1_2"/>
    <blankline id="ssearch_s1_3"/>
  </section>
  <buttongroup id="ssearch_buttons_1">
    <pushbutton default="true" id="ssearch_btn1" label="Search" mxevent="dialogok"/>
    <pushbutton id="ssearch_btn2" label="Cancel" mxevent="dialogcancel"/>
  </buttongroup>
</dialog>



Define the sigoption

Follow these steps to define the sigoption for the custom dialog box.
    1. Launch the Application Designer and open the WOTRACK application.
    2. On the Select Action menu, click on Add/Modify Signature Options.
    3. Click on New Row to add a new row:
      Option: SSEARCH
      Description: SmartSearch
    4. Exit out the Application Designer application.
    5. Launch the Security Groups application and open "EVERYONE" group.
    6. Go to the Applications tab.
    7. Search fo Work Order Tracking application and then for the 'SmartSearch' the new signature option. Grant access to it and save your changes.
    8. Done.


    Create toolbar button


    Follow these steps to create a custom toolbar button to launch your dialog box.
    1. Launch the Application Designer application.
    2. Retrieve the application which you modified on step 2.
    3. On the Select Action menu, click on Add/Modify Toolbar Menu.
    4. Click on New Row to add a new row. Use the following values:
      Element Type = OPTION
      Key Value = SSEARCH
      Position = 80
      Subposition = 0
      Image = find.gif
      Visible = Yes
      Tabs = LIST
    5. Click OK..
    6. Logout and login back.

    Checkpoint
    Launch the Work Order Tracking application and check that the new button and that by clicking on it the new SmartSearch dialog is displayed. Note that at this point you will still see an 'Invalid Binding' error.


    Create SMARTSEARCH object

    Follow these steps to create a non persistent object to support the application bean.
    • Launch Database Designer application.
    • Create an new object and enter the following information
      Object: SMARTSEARCH
      Description: Non persistent bean for SmartSearch
      Persistent: False
    • Save the record and go the the Attributes tab.
    • Create the following attribute:
      Attribute: SEARCHTEXT
      Title: Search text
      Type: ALN
      Length: 100
    • Create the following attribute:
      Attribute: SEARCHHIST
      Title: Include historical records
      Type: YORN
      Default: 0
    • Save and apply database changes


    SmartSearch AppBean class

    Follow these steps to create and deploy the AppBean class.

    1. Download SmartSearchAppBean.java file.
    2. Open your development environment and copy SmartSearchAppBean.java into  mxdev.webclient.beans.workorder package.
    3. Compile and copy SmartSearchAppBean.class file in
      [SMPDIR]\applications\maximo\maximouiweb\webmodule\WEB-INF\classes\mxdev\webclient\beans\workorder
    4. Re-build and deploy Maximo EAR file.

    You can easily modify the set of fields in which the text is searched by modifying how the where clause string is built.


    References

    Custom Dialog Box tutorial (from Daniel NG)
    Call Java method on action menu or toolbar button click

    October 16, 2013

    Display a summary of an object

    NOTE: This techniques requires Java programming. A similar article describes how to achieve the same results with scripting.

    One of the customer that I'm working with is heavily relying on work logs. Unfortunately, the work logs are displayed in a standard table so there is no way of quickly displaying all the logs entered by the users.



    The solution I have designed is a Summary tab in the Work Order Tracking application that displays the main attributes of the selected work order together with the historical work logs in one single text box.



    The described solution relies on the Rich Text editor control available in TPAE 7.5 but can be easily adapted to a standard multiline text box available in previous version of Maximo.
    Here are the configuration steps.


    Database Configuration

    The first step is to define a virtual (non persistent) attribute that will be used to display the work order summary text.

    Open WORKORDER object in Database Configuration and add the following attribute.
    • Attribute: WOSUMMARY
    • Title: Summary
    • Type: CLOB
    • Class: mxdev.app.wo.FldWoSummary
    • Persistent: false
    Save and apply database changes.


    Application Designer

    The second step is to add a new tab to the WOTRACK application that contains just a large texbox that will display the attribute defined above.

    Open WOTRACK application in Application Designer and export application XML. Open the wotrack.xml file with a text editor and add the following lines just before the </tabgroup></clientarea> tags.
    Here is how the file should look like (the text in bold is what needs to be added).

      ...
      <tab id="summary" label="Summary">
        <section id="summary_s1">
          <richtexteditor dataattribute="WOSUMMARY" height="400" id="summary_s1_wosummary" plugins="[]" extra_plugins="[]"/>
        </section>
      </tab>
      </tabgroup>
      </clientarea>
      <include id="pageFooter"/>
    </page>
    ...


    Java class

    The last configuration steps is to write the Java class that has been attached to the WORKORDER.WOSUMMARY field. This piece of code will retrieve all the necessary information from the work order and build an HTML string that will be assigned to the field string value.

    Open your development environment and create a mxdev.app.wo.FldWoSummary class like this.

    package mxdev.app.wo;
    
    import java.rmi.RemoteException;
    
    import psdi.mbo.Mbo;
    import psdi.mbo.MboRemote;
    import psdi.mbo.MboSetRemote;
    import psdi.mbo.MboValue;
    import psdi.mbo.MboValueAdapter;
    import psdi.util.MXException;
    
    public class FldWoSummary extends MboValueAdapter
    {
      public FldWoSummary(MboValue mbv) throws MXException
      {
        super(mbv);
      }
    
      public void initValue() throws MXException, RemoteException
      {
        super.initValue();
    
        MboRemote wo = this.getMboValue().getMbo();
        StringBuilder val = new StringBuilder(); 
    
        // retrieve attributes and writes them in HTML format in the buffer
        val.append("ID: " + wo.getString("WONUM") + "<br>");
        val.append("Description: " + wo.getString("DESCRIPTION") + "<br>");
        val.append("Location: " + wo.getString("LOCATION") + "<br>");
        val.append("Asset: " + wo.getString("ASSETNUM") + "<br><br>");
    
        // retrieve work log records and writes them in the buffer
        MboSetRemote worklogSet = wo.getMboSet("MODIFYWORKLOG");
    
        val.append("<hr>");
        val.append("<h2>WORK LOG</h2>");
        val.append("<hr>");
      
        for(MboRemote currMbo=worklogSet.moveFirst(); currMbo!=null; currMbo=worklogSet.moveNext())
        {
          val.append("<h3>" + currMbo.getString("DESCRIPTION") + "</h3><br>");
          val.append(currMbo.getString("DESCRIPTION_LONGDESCRIPTION") + "<br>");
          val.append("<hr>");
        }
    
        // sets the formatted string in the attributes value
        getMboValue().setValue(val.toString(), Mbo.NOACCESSCHECK | Mbo.NOVALIDATION);
      }
    
    }

    Deploy the java class in Maximo EAR file.

    You can see how HTML tags have been used to format the output.
    Obviously you can extend the example to include your own fields and formatting to better fit your needs.

    October 15, 2013

    How to implement stateful MBOs

    This entry is part of the Maximo Java Development series.

    Many objects in Maximo has a STATUS attribute that can be changed using the status icon or the change status menu option. The STATUS attribute is always associated with a synonym domain that has the valid statuses for the object. Examples of these domains are:
    • POSTATUS for the PO (Purchase Order) object
    • WOSTATUS for the WORKORDER object


    The objects with changes of status must inherit from the StatefulMbo class which provides the basic functionality to change the status. For example the WORKORDER object has the following class hierarchy:
    • Mbo > StatefulMbo > WORKORDER

    The synonym domain is associated with the STATUS attribute, and this domain has an internal and external value. The value stored in the database and displayed on the screen is the external value for an attribute with a synonym domain.



    The most important methods available in the StatefulMbo class are:
    • canChangeStatus(String changeToStatus, long accessModifier)
      Identifies whether the status of the object be changed to the specified value.
      If status change is not allowed, an exception is thrown.
    • changeStatus (String status, Date date, String memo)
      Changes the status of the object.
      This method compares the requested status and the current status by calling canChangeStatus before the change is performed. If the status change is allowed, the status change is performed and a status history record is written. If the status change is not allowed, an exception is thrown.
    • getInternalStatus()
      Returns the Internal value for the current status of the object
    • getStatusListName()
      Return the name of the domain associated with the status attribute.

    Passing the NOACCESSCHECK flag in the accessModifier parameter can be used to indicate that the status can be changed even if it is invalid.
    Two similar methods that can be used to manipulate internal status values are: canChangeMaxStatus and changeMaxStatus.

    When the user selects the 'Change Status' option, the framework displays a dialog box that lists the valid statuses to which the object can change.



    The list is the result of calls to the canChangeStatus method for each of the possible statuses from the synonym domain. If the canChangeStatus method throws an exception, the status is not listed.
    When the user selects the new status, the framework calls the changeStatus method that first call the the canChangeStatus method (again) to validate the change and then update the status and status history.

    October 13, 2013

    MBOs attribute current, previous, initial values

    When implementing business logic in Java code it may be useful to compare the value of an attribute to it's previous or initial value.
    This is particularly useful when overriding MboValueAdapter.action() method to validate the new value of an attribute as soon as it changes.

    Look at how the three values are retrieved in the following example.

    @Override
    public void action() throws MXException, RemoteException
    {
      super.action();
    
      String currVal = getMboValue().getString();
      String oldVal = getMboValue().getPreviousValue().asString();
      String initialVal = getMboValue().getInitialValue().asString();
    
      if (!currVal.equals(oldVal))
      {
        ...
      }
    }

    October 6, 2013

    System requirements for IBM Maximo and SmartCloud Control Desk

    Sometimes is a bit hard to find an exact list of system requirements and prerequisites for IBM Maximo and SmartCloud Control Desk (SCCD) products.

    If you want to know what are the supported operating systems and middleware versions here are the most important sources of information:

    September 30, 2013

    Translate workflow messages and labels

    Translated messages and labels are not enabled by default on Maximo workflows. This means that if you have a multilanguage environment all the users will see the same task descriptions interaction messages when using workflows.
    To be able to insert translated labels and messages for workflows you have to enable the multi-language option for the following attributes:
    • WFACTION.INSTRUCTION
    • WFASSIGNMENT.DESCRIPTION
    • WFINTERACTION.DIRECTIONS
    • WFINTERACTION.DIRECTIONS_LONGDESCRIPTION


    To set multi-language option go to System Configuration > Platform Configuration > Database Configuration find each attribute and set the 'Multilanguage in use?' flag. Enable the admin-mode and apply database changes.
    After having done this you should be able to login as MAXADMIN with a different language and set different labels for actions and tasks using the standard 'Workflow Designer'.



    Reference IBM TechNote: Setting up Multilanguage for Workflow

    September 21, 2013

    How to disable the 7.5 Rich Text editor

    TPAE 7.5 has introduced a nice Rich Text editor control for editing and formatting long descriptions. This applies to both Maximo and SmartCloud Control Desk starting from version 7.5.
    This feature is very cool in some cases but may cause some troubles. I personally do not like the effects of pasting text into such RTF text boxes.

    To disable the formatting features of these controls you can just add the following attributes to the richtexteditor element in the application XML definition: plugins="[]" extra_plugins="[]"

    Here are the steps.
    1. Open the Application Designer.
    2. Open the application you want to modify.
    3. Export the XML definition.
    4. Open the XML with a text editor.
    5. For each richtexteditor element add the plugins and extra_plugins attributes (look at the example below).
    6. Save the XML file.
    7. Update the application definition importing the XML in the Application Designer.

    Example

    Before
    <richtexteditor dataattribute="description_longdescription" id="main_grid3a_col1_2"/>

    After
    <richtexteditor dataattribute="description_longdescription" id="main_grid3a_col1_2" plugins="[]" extra_plugins="[]"/>


    References


    September 11, 2013

    Automatically start Maximo or SCCD server

    To automatically start Maximo or SmartCloud Control Desk (SCCD) when the hosting server starts you need to configure the WebSphere Node Agent to start automatically start at server boot.

    Automatically start WebSphere Node Agent

    This can be achieved on Windows setting up the WebSphere Node Agent as a Windows service using the WASService command located in the [APPSRVDIR]\bin directory.

    WASService -add "Node Agent"
    -servername nodeagent -profilePath "[APPSRVDIR]\profiles\[PROFILENAME]"
    -wasHome "[APPSRVDIR]"
    -logRoot "[APPSRVDIR]\profiles\[PROFILENAME]\logs\nodeagent"
    -logFile "[APPSRVDIR]\profiles\[PROFILENAME]\logs\nodeagent\startNode.log"
    -restart true -startType automatic

    If WebSphere is installed on D:\IBM\WebSphere and the profile is named ctgAppServer the command will look like this:

    cd D:\IBM\Websphere\Appserver\bin
    D:
    WASService -add "Node Agent" -servername nodeagent -profilePath "D:\IBM\WebSphere\AppServer\profiles\ctgAppSrv01" -wasHome "D:\IBM\Websphere\Appserver" -logRoot "D:\IBM\WebSphere\AppServer\profiles\ctgAppSrv01\logs\nodeagent" -logFile "D:\IBM\WebSphere\AppServer\profiles\ctgAppSrv01\logs\nodeagent\startNode.log" -restart true -startType automatic 


    Automatically start Maximo application server

    The Maximo application server can now be configured to automatically start when the Node Agent is started using WebSphere Node Monitoring as described in this article.
    To implement Node Monitoring use the following steps:
    1. Log into WebSphere Integrated Solutions Console.
    2. From the left navigation select Servers > Application Servers.
    3. Click the appropriate Maximo Application Server (default name MXServer).
    4. Expand Java and Process Management and click on Monitoring Policy.
    5. Change the Node restart state to RUNNING
    6. Click Apply
    7. Click Save to save the master configuration
    8. Repeat for each server in the cluster.

    An alternative approach is to configure the application server to be started as a windows service as described in this IBM TechNote.

    August 5, 2013

    Is an MBO change originated by user?

    In some cases you are in the middle of an MBO Java code and you need to know if the current method has been triggered by an interactive UI session or by background process like MIF or escalation.
    This is what you need.

    boolean isGUI = getUserInfo().isInteractive();

    The getUserInfo() method is available on both Mbo and MboSet classes.

    July 19, 2013

    Custom Java Action Class Example

    This entry is part of the Maximo Java Development series.

    In a previous post I have described how create a custom action class using Java.

    Here is a full example that can help to understand how to navigate objects and set values.

    package cust.psdi.app.workorder;
    
    import java.rmi.RemoteException;
    
    import psdi.common.action.ActionCustomClass;
    import psdi.mbo.MboConstants;
    import psdi.mbo.MboRemote;
    import psdi.mbo.MboSetRemote;
    import psdi.util.MXException;
    
    /**
     * Sets the Workorder description with a new one obtained concatenating the
     * following related descriptions:
     * - :JOBPLAN.DESCRIPTION
     * - :PM.LOCATIONS.DESCRIPTION
     * - :PM.ASSET.DESCRIPTION
     * 
     * @author Bruno Portaluri
     */
    public class ActionSetDescriptionFromPm implements ActionCustomClass
    {
      public ActionSetDescriptionFromPm()
      {
        super();
      }
    
      public void applyCustomAction(MboRemote mbo, Object[] params) throws MXException, RemoteException
      {
        String desc = "";
        
        // Get the description of the Job Plan
        MboSetRemote jp = mbo.getMboSet("WO_JOBPLAN");
        if (jp.getMbo(0)!=null)
        {
          desc = desc + jp.getMbo(0).getString("DESCRIPTION");
        }
    
        
        // Get the description of the Job Plan
        MboSetRemote pm = mbo.getMboSet("WO_PM");
        
        if (pm.getMbo(0)!=null)
        {
          // Get the description of the PM's Location
          MboSetRemote locations = pm.getMbo(0).getMboSet("LOCATIONS");
          if (locations.getMbo(0)!=null)
          {
            desc = desc + " - " + locations.getMbo(0).getString("DESCRIPTION");
          }
          
          // Get the description of the PM's Asset
          MboSetRemote asset = pm.getMbo(0).getMboSet("ASSET");
          if (asset.getMbo(0)!=null)
          {
            desc = desc + " - " + asset.getMbo(0).getString("DESCRIPTION");
          }
        }
    
        // Truncates to fit the field length
        desc = desc.substring (0, 100);
        
        System.out.println("Setting WO's " + mbo.getString("WONUM") + " description:" + desc);
        
        mbo.setValue("DESCRIPTION", desc, MboConstants.NOACCESSCHECK|MboConstants.NOVALIDATION_AND_NOACTION);
      }
    }


    July 13, 2013

    How to make record readonly based on an attribute's value

    This entry is part of the Conditional Expressions HowTo.

    A very typical customization task is to make readonly some records of a specific table based on the value of an attribute.
    For example you may wish prevent the editing of purchase orders that have a total costs higher than 1000$ for a specific group. Here is a small tutorial about how you can achieve this.

    Open Administration - Conditional Expression Manager application and create the following conditional expression:

    • Condition: POCOST
    • Description: Total cost is more than 1000
    • Type: EXPRESSION
    • Expression: totalcost>1000



    Now go to the Security - Security Groups application. Choose the desired group and open the Data Restrictions tab.
    Create the following Object Restriction:

    • Object: PO
    • Type: READONLY
    • Reevaluate: true
    • Condition: POCOST


    If you now logon with a user belonging to the chosen group and open the Purchase Orders application you will see that POs with a total cost of more than 1000 cannot be modified.

    July 9, 2013

    Best practices for Maximo projects

    Today I have received the following questions from one of our customers.
    • Are there any best practices or recommendations on how to develop and maintain different versions of a Maximo based solution?
    • How can we do source control?
    • Are there any compare tool/utility to find differences in various version of application (available in different environments)?
    Expanding a little, in this article I will present my own recipes to cook successful Maximo projects.


    Development Methodology

    I am a strong fan of Agile philosophy and less enthusiastic adopter of Agile software development methodologies like Scrum, XP or Crystal.
    I really believe that the right way to seek success and perfection in software development project is to strive to align your processes, organization and culture to the four value statements of the Agile manifesto whatever methodology you are using.

    Individuals and interactions over processes and tools
    Working software over comprehensive documentation
    Customer collaboration over contract negotiation
    Responding to change over following a plan

    There is no silver bullet here. Every project is different. Every customer is different. You have to experiment and improve keeping in mind those principles.
    My own motto is: Start Small, Grow Fast. I always use this as a very simple guiding principle.





    Workspace structure

    I suggest to keep all the files and documents in a single place. There is no need to create boundaries between development, test or project management people. Everybody is on the same ship and a great collaboration is fundamental for an efficient and productive team. If possible, give access to the customer to entire set of files and documents. Foster mutual trust and establish a partnership.
    This is a common set of directories in a project folder.
    • Mbo: Home of an Eclipse project for Mbo Java code (aka businessobjects).
    • Web: Home of an Eclipse project for UI Java code (aka maximouiweb).
    • Applications: Applications definitions exported in XML files. Any time a change is made, the app definition must be exported and stored here.
    • Scripts: TPAE 7.5 scripts.
    • Database: Custom database triggers and SQL scripts.
    • Docs: Requirements, plans and procedures as well as architectural and design documents. All the team members should use this folder to collaborate for a lean workgroup.
    I know that some of these files are stored in a safe place in the Maximo database. However, I think it is better to keep together all the custom code in a common repository where you can also search for previous versions of a specific file.


    Collaboration, Backup, Versioning

    The workspace described above must be shared across all the team members including specialists, architects, developers, project managers and stakeholders.
    Avoid using email to exchange documents. This could quickly become a nightmare. Break old habits. Share a list of tasks on Google Docs and ask everyone to keep it updated. Create a design document and edit it in real time during a review session. Many times we don't need strict rules and processes.

    Regarding the technology for storage and versioning of the workspace it really depends on the size of the team. As a general rule:
    • 1-3 members: A shared folder with a weekly backup is enough. Cloud services like Google Drive, SkyDrive, DropBox, etc. are great and free. Google platform enable a powerful collaboration platform with chat, email, real time document collaboration and sharing.
    • 4-6 members : Here you will need some versioning system like CVS, SVN or Git. You may decide to setup your own server or to go cloud.
    • 7 or more: You need some requirement/bug management system. Those systems are typically called ALM (Application Lifecycle Management). IBM Rational Team Concert is a great and powerful tool but can be a little complex to administer. There are tons of ALM services on the cloud


    Tools

    First of all, share a common set of productivity tools. Look at Maximo specialist Swiss Army Knife for my selection of tools.
    If you have to develop Java customization you have to set some coding standards.
    In real world scenarios you will always face some challenges to keep in sync development, test and production environments. Unfortunately, there is no tool to compare two different Maximo environments. That's why I have develop my own method.
    An effective technique is to periodically clone the production environment to the test and development environments with a procedure like this.
    On the other side it is important to use Migration Manager to move packages to be released from dev to test to production.

    July 7, 2013

    Commodity Codes in Maximo

    Commodity codes are standard classification codes for products and services used to detail where money is spent within a company.

    In Maximo commodity codes are used to define similar types of items, tools, or services. Commodity codes are also grouped together in commodity groups. A commodity group contains a grouping of individual commodity codes.
    Commodity groups and codes can be created and managed selecting the Add/Modify Commodity Codes action in the one of following applications: Companies, Item Master, Purchase Orders, Service Items, Tools. This IBM TechNote describes how to manually enter commodity codes.

    Once you have defined commodities you can associate them with company, contract, and item records. You can also associate commodities with individual purchase order or requisition lines for items, service items, or tools that do not have commodity codes assigned to them, including special order materials or services.


    Classifying items, tools, services with commodity groups and commodity codes provides a way to analyze and optimize spending by product type. To support such spending review process, Maximo provides the Commodity Analysis report in the Item Master application.


    The Commodity Analysis report shows a summary list of items received during a given time period grouped by commodity groups and vendors. The first part of the report shows the received cost by commodity groups presented as a bar chart.



    The second part of the report shows data in a table format. For each commodity group, the cost summary for each vendor is computed and shown as subtotal.


    This report is useful in identifying what commodities made up the bulk of purchase costs and thus, are critical for cost controls. For large organizations it can be used to compare price points from different vendors in order to restructure and optimize expenditures across sites and divisions.


    There are several standards for commodities classifications like UNSPSC, HS, NIGP. Many clients define their own classifications codes and groups in Maximo.
    The UNSPSC (United Nations Standard Products and Services Code) standard is well known and is translated into many languages. Codes can be downloaded for free in PDF format or purchased in Excel format. Is it also possible to search and browse codes with a simple search engine.

    It may be also interesting to hear something from you.
    What is your preferred commodity codes structure in Maximo?
    Do you use standard codes or have you defined you own structure?

    July 5, 2013

    How to import Failure Codes from Excel into Maximo

    This article is outdated! Checkout MxLoader tool.

    This entry is part of the Maximo Integration Framework series.

    Creating and maintaining the hierarchy of failure codes can be quite complex using the standard Failure Codes application. Unfortunately there is no common solution to import Maximo failure codes. This evening I have worked on creating an Excel spreadsheet that can help in such cases.

    This is an experimental tool. Use with care on development environments.
    Leave a comment on this page both if you have problem (I'll try to fix them) and if you are able to use it successfully in your environment.


    Data Model

    Before going on, we need to understand how Maximo stores failure codes and their hierarchy of problems, causes and remedies.
    The FAILURELIST table stores the hierarchy of problems, causes and remedies. The PARENT field holds the parent-child relationship while the FAILURECODE field points to another table called FAILURECODE that stores the descriptions of the failure codes in the various languages.
    It is important to understand that a specific failure code can be attached to several places of the failure list hierarchy. This makes quite complex to manage this structure.


    Export Failure List

    A simple way of exporting failure codes and list is through SQL queries.

     
    select orgid, failurecode, description, langcode from failurecode;


    select f.orgid, f.failurelist classid, f.failurecode class, p.failurelist problemid, p.failurecode problem, c.failurelist causeid, c.failurecode cause, r.failurelist remedyid, r.failurecode remedy
    from failurelist f
    left outer join failurelist p on p.parent=f.failurelist and p.type='PROBLEM'
    left outer join failurelist c on c.parent=p.failurelist and c.type='CAUSE'
    left outer join failurelist r on r.parent=c.failurelist and r.type='REMEDY'
    where f.parent is null
    order by f.failurecode, p.failurecode, c.failurecode, r.failurecode;


    Import

    Create the following Object Structures.
    Object Structure: FLL-FAILURECODE
    • Consumed By: Integration
    • Object: FAILURECODE
    Object Structure: FLL-FAILURELIST
    • Consumed By: Integration
    • Object: FAILURELIST

    Now download the FailureCodesLoader Excel spreadsheet and open it. Go on the Config sheet and set the correct hostname of your Maximo server. Clicking of the link the browser will open and should display the following message: Servlet is running. Please use HTTP POST to post data.
    Now you are ready.

    The Failure Codes and Failure List sheets allow to upload data into the two tables described before.
    The Failure Codes sheet is straightforward to use. Just fill the worksheet with your failure codes descriptions and click on the little arrow on the toolbar.


    This will execute a VBA macro that will call the FLL-FAILURECODE Object Service through an HTTP POST request.

    The Failure List sheets represents the the failure codes hierarchy in a flat table with the same structure as exported by the above SQL query.
    The Excel macro will handle the necessary logic to manage the failure list hierarchy. Once the objects are created and the hierarchy is populated, the macro will fill the 'ID' columns with the correct values. Once the ID values (greyed cells) are filled, the failure node is created in the hierarchy and shouldn't be changed.

    At first it may seem a little tricky at the beginning, but after having played a little bit with the tool, it will be easy to create a complete failure list hierarchy of 1000 codes and more.

    June 26, 2013

    Automatically set Everyplace start center for mobile users

    Maximo Everyplace is the easiest solution to go mobile with Maximo however there is one little thing that always annoys me (and mobile users).

    After having created mobile versions of the Maximo apps, the administrator has to define a mobile start center with a Favorite Applications portlet to access them. Unfortunately, there is no way to set this as a 'mobile' Start Center so each user have to click on the Display Settings link and select the Is Mobile check box for the specific start center.

    In the real world this means that after having set up Maximo Everyplace I have to send an email to all the mobile users with the instructions about how to setup their mobile start center.

    Here is a simple escalation to automatically set the Is Mobile checkbox for the designated start center for all the users.

    First of all we need an Action to set the flag:
    • Action: SETMSC
    • Description: Set Mobile Start Center
    • Object: SCCONFIG
    • Type: Set Value
    • Attribute: ISMOBILE
    • Value: 1
    • Accessible From: ESCALATION

    Then we can define the Escalation to run the action:
    • Name: SETMSC
    • Description: Set Mobile Start Cente
    • Condition: groupname in ('[EVGROUP]') and ismobile=0
    • Schedule: hourly
    • Create Successful Execution Entry?: No
    • Escalation point: Empty
    • Action: SETMSC

    The [EVGROUP] should match the name of your Security Group(s) with the mobile start center assigned.

    June 24, 2013

    Opening of Maximo Summer Series!

    Italy may not be the strongest economy in the EU but certainly offers some benefits.
    I will stay for the next three weeks in a wonderful place in the south of Italy where I can enjoy some sun and sandy beaches like this.


    Maximo and EAM are both my job and my hobby so I will continue to post new articles and tips from my beach chair.

    Stay tuned!

    June 20, 2013

    Embed Maximo user interface in a web page


    This was a tough week but I have learned many things. I have been two days in Florence and other two days in Montebelluna near Venice. Now I am in the train going back home for a well deserved vacation at the beach.

    Today I have discovered a nice way to embed Maximo UI in a custom web portal. The customer wants to provide a very simple self service center in its own intranet web site.
    The trick to achieve this is to use an undocumented parameter called portalmode. Let's see how to do it.


    Download the sample mymx.html file. This HTML file is a simple web page that has a very classical structure. A title bar at the top, a left navigation portfolio and a content page on the right. Open mymx1.html with a text editor and update the two URLs with your Maximo server hostname to match your environment. Note how the portalmode parameter is passed in the URLs and how the IFRAME element is used to embed the frame in the web page.
    Save the file, open it with a web browser. If you click on one of the two links on the left you will be prompted with the Maximo login page and then taken to your SR application. Here is how it will look like.



    You can see some good and bad things.
    1. The Go To menu, graphics and other stuff on the top bar are hidden (good)
    2. Playing with URLs you can easily define shortcusts to Maximo apps (good)
    3. The buttons are somewhat scrambled on the right side (bad)
    4. The application is truncated on the righ side (bad)
    5. The login page is displayed (bad)

    Now we have to keep the good things and avoid the bad ones. Issues 3 and 4 can be solved customizing the SR app. Issue 5 can be solved implementing SSO.

    Simplify the SR application

    Open the Application designer and create a new Power App based on SR object.
    • Application: MYSR
    • Description: Simple SR app for the Intranet
    • Main Object: SR
    • Key Attribute: TICKETID
    • Module Name: SD

    Remove unneeded fields from the MYSR app. Remove the pageHeader, pageFooter and menubar rows in the XML file to remove the headers and footers. Import back the MYSR app and test it. The simplified app will not allow any navigation but that's exactly what we are looking for.
    For your convenience, here is my streamlined MYSR app definition.


    Avoid the login page


    In your intranet portal you probably already have your login page and it is not fair nor elegant to ask the users to login again into Maximo.
    Here you need to implement SSO (Single Sign On). It's not the goal of this article to explain how to implement SSO between a web site and Maximo so we will simulate it. If you need more details about implementing SSO for Maximo you look at this forum thread.
    As decribed in this article, login credentials can be passed in the URL so all you have to do to do the trick is to pass those credentials in the links.



    Putting all together

    Change the link in the HTML file to load the new MYSR app and pass the user credentials as described above. You will have links like this:
    http://[MXHOST]/maximo/ui/maximo.jsp?event=loadapp&value=mysr&additionalevent=insert&portalmode=true&login=true&username=adams&password=adams


    And here is the final result!



    June 18, 2013

    Maximo Integration Framework features

    This entry is part of the Maximo Integration Framework series.

    How many times you have asked yourself: Should I upgrade?
    Maximo Integration Framework has been improved a lot in the recent versions and fixpacks. Look at this list of features and you may find a good reason to upgrade.

    7.5.0.3
    • OSLC - Link and share data by using OSLC integration (Open Services for Lifecycle Collaboration)
    • EMail interaction - Enabling email updates for objects and workflow assignments

    7.5.0.1
    • REST API - Support for Object Structures and MBOs as REST Resources
    • Text JMS messages - Support for messages in a TEXT format

    7.5.0.0
    • Publish Channel Eventing - Improved support for capturing events from a child object within an object structure
    • Application-based Importing and Exporting - Provide importing and exporting capabilities directly from an application such as WO, PO, Asset etc.
    • Support for Application Authorization - Inbound message processing using authorization from an application
    • Web Services via the Application Server - Support for JAX-WS leveraging the application server’s container.  Allows for support of WS policies such as WS-SECURITY, WS-ADDRESSING, etc.
    • BIDI Support - Support for Bi-Directional content within integration messages

    7.1.1.6
    • Attachments - Support for including attachments on integration messages
    • WorkFlow - Support for initiating/waking up a work flow process via integration
    • Nulling Fields - Support for nulling out fields from files and/or interface tables
    • View/Delete Queue - UI dialog to view/delete queue messages
    • Web Service Interactions - Provide wizard-based UI to build interaction from Maximo to external web service

    7.1.1.4
    • Message Reprocessing - Data Import of large files offers ability to manage messages in error via a file
    • Data Import - Enhanced features for Preview Mode functionality
    • Action Class to support initiating an invocation channel - Pre-built Action class provided to support invocation channel execution
    • Integration Query - Support of a SQL WHERE clause

    June 17, 2013

    Syncing data with an external system using a Publish Channel

    This entry is part of the Maximo Integration Framework series.

    Publish Channels in MIF allows to send daya to external systems using a variety of protocols. In this article I will demonstrate how to automatically export new or updated assets into CSV/XML files. Those files can then be processed by an external application to perform additional logic or to replicate the assets.


    Create an Integration Object Structure MYASSET and add the ASSET object as source.


    Remember to select 'Support Flat Structure' if you need CSV export.

    Now create the Publish Channel MYASSETS.


    Create an External System (or reuse your own) and add the MYASSETS Publish Channel to it.


    Enable Publish Channel and test that everything is ok using the 'Data Export' button. A CSV file should appear in few minutes in your MIF output folder (see mxe.int.globaldir system property). If you have problems check the JMS queues, JMSQSEQCONSUMER crontask and anything that may block the publish channel functionality.


    To automate of the XML/CSV file when an asset is created or updated, you have to enable the event listener on the Publish Channel. Open the MYASSETS Publish Channel and select the Enable Event Listener menu action.
    Now open an asset, change its description and save it. This should automatically trigger the invocation of the Publish Channel and you will see a new file in the MIF directory with the updated asset.

    June 11, 2013

    SCCD 7.5 and Maximo 7.6 user interface System Properties

    SmartCloud Control Desk 7.5 and Maximo 7.6 have introduced new UI skins and navigation behavior.



    There are four new System Properties which control the left navigation pane and other related visual attributes.
    • mxe.webclient.homeButtonHeaders - Moves Goto menu to left of the screen and adds Start Center button to the header
    • mxe.webclient.systemNavBar - Enables the lefthand navigation
    • mxe.webclient.tabBreadCrumbs - Removes tabs from list view
    • mxe.webclient.verticalLabels - Sets label above each input field
    The skin can be selected setting the mxe.webclient.skin property: tivoli09 is the default 7.5 skin, tivoli13 is new skin.

    June 10, 2013

    Set a field as required after save

    This entry is part of the Maximo Java Development series.

    You may have noticed that there are some fields that cannot be modified once an object is saved. An example of this behavior is the ASSET.ASSETNUM field. When you create a new asset you can set it's ID but once you have saved it it cannot be modified.
    In this little post I'll show you how to implement this behavior for other fields.

    Let's pretend we want to avoid any modification to the original asset serial number once the asset is created.
    The Mbo.toBeAdded() method allows to detect if the current Mbo has been already saved or not. The Mbo.setFieldFlag() method allows to set a field as required. You just have to override the init() method of the Mbo put the appropriate logic.

    public void init() throws MXException
    {
      super.init();
    
      if (!toBeAdded())
        setFieldFlag("SERIALNUM", READONLY, true);
    }


    The Mbo.setFieldFlag() method allows to set many fields in one shot so a very common technique is to define two arrays of strings to define the lists of attributes to be set as readonly. Look at the following example.

    static String alwaysReadOnlyFields[] = {"ORGID", "SITEID"};
    static String readOnlyAfterSaveFields[] = {"SERIALNUM", "PRIORITY"};
    
    public void init() throws MXException
    {
      super.init();
    
      setFieldFlag(alwaysReadOnlyFields, READONLY, true);
    
      if (!toBeAdded())
        setFieldFlag(readOnlyAfterSaveFields, READONLY, true);
    }
    

    June 9, 2013

    How to set the date format in Maximo


    To set the date format in Maximo, you use the setting.DISPLAYDATE system property from the System Properties application.
    When you enter a date format for this property, it overrides any system settings for displaying date formats in Maximo

    If you do not set the system property with DISPLAYDATE, then the display format is based on the profile information (under Profile - Default Information). This setting determines the display date format for this specific user.
    This setting is used only if the system property setting.DISPLAYDATE is not set.

    If the display date format is not set within Maximo, the format is based on the language setting of the web browser that is used to access Maximo.


    http://publib.boulder.ibm.com/infocenter/ieduasst/tivv1r0/topic/com.ibm.iea.mam/mam/7.1/sysappmanagement/set_display_date_format.pdf

    June 7, 2013

    Maximo add-ons, plugins and extensions on ISM Library


    ISM Library is an IBM website where you can find addons, plugins and extensions available for all IBM products including Maximo.

    I really think it is worth spending 5 minutes of your time to look at what extensions are available. Here is the link that will take you directly to the list of solutions available for Enterprise Asset management (EAM): https://www-304.ibm.com/software/brandcatalog/ismlibrary/search#rc=AssetManagement:Asset%20Management
    I also suggest you to sort results by 'most viewed' to find the most popular solutions.

    You will see my Android App for IBM Maximo Everyplace solution at 15th ranking.

    June 6, 2013

    Return vs. Move Asset - When to use which

    There is a common misconception in Maximo on when to use the return item transaction or a move asset transaction.

    The return item transaction should be used when an item has been consumed by a GL entity, but for one reason or another, the item was not necessary and therefore needs to be returned to the storeroom at its full issued value. Again, a return is to negate an issue transaction.

    The move asset functionality should be used in the case where the user wishes to capture the fact that an asset has moved from one location to another. This means a transfer of value.
    In the case of a traditional equipment asset, the value of the asset literally moves from the GL of one location to the GL of another. In the case of rotating assets this still holds true, however the inventory costs associated with that asset are also tracked within the Rotating Suspense account. At the time the rotating asset is moved into an inventory location, any transactions that were written to the Rotating Suspense account will then be transferred to the storeroom’s control account. Additionally, MAXIMO will adjust the average cost.

    This has been extracted from Rotating A​sset Accou​nting by Kevin Buonagurio.

    June 5, 2013

    MaximoDev Blog Facts and Stats (May 2013)

    After more than 3 years of blogging I think it's worth sharing some statistics about my blog.



    May 26, 2013

    How to rename objects

    You have probably noticed that once you have saved an object you are not allowed to change it's ID. This is basically because each object stored on the database needs an immutable unique identifier that can be used to reference that object from other tables. Maximo prevents updating such identifiers to avoid loosing links between records referencing each other.

    However there is a little trick you can apply to update the ID of an object in the main table and all the related records. The technique consists in using the Data Dictionary stored in MAXATTRIBUTE table to find all the possible tables and fields where references to the renamed object could be stored.

    Let's pretend we have to rename an object stored in the [TABLE] table and whose ID is stored in attribute [IDATTR]. The following SQL query will generate a set of update statements to update both the main record and all its potential references.

    SELECT 'update ' || a.objectname ||
           ' set ' || a.attributename || '=''[NEWVALUE]''' ||
           ' where ' || a.attributename || '=''[OLDVALUE]'';'
    FROM maxattribute a
    JOIN maxobject o ON o.objectname=a.objectname
    WHERE a.persistent=1 AND o.isview=0
      AND ((a.sameasobject='[TABLE]' AND a.sameasattribute='[IDATTR]') OR
           (a.objectname='[TABLE]' AND a.attributename='[IDATTR]'))
    ORDER BY a.objectname, a.attributename;


    Example

    For example, if we need to rename a security group called 'MAINGRP1' to 'NEWGROUP' you will need to run the following query.

    SELECT 'update ' || a.objectname ||
           ' set ' || a.attributename || '=''NEWGROUP''' ||
           ' where ' || a.attributename || '=''MAINGRP1'';'
    FROM maxattribute a
    JOIN maxobject o ON o.objectname=a.objectname
    WHERE a.persistent=1 AND o.isview=0
      AND ((a.sameasobject='MAXGROUP' AND a.sameasattribute='GROUPNAME') OR
           (a.objectname='MAXGROUP' AND a.attributename='GROUPNAME'))
    ORDER BY a.objectname, a.attributename;

    The result of the select will be something similar to this.

    update APPLICATIONAUTH set GROUPNAME='NEWGROUP' where GROUPNAME='MAINGRP1';
    update COLLECTIONAUTH set GROUPNAME='NEWGROUP' where GROUPNAME='MAINGRP1';
    update CTRLGROUP set GROUPNAME='NEWGROUP' where GROUPNAME='MAINGRP1';
    update GLAUTH set GROUPNAME='NEWGROUP' where GROUPNAME='MAINGRP1';
    update GROUPUSER set GROUPNAME='NEWGROUP' where GROUPNAME='MAINGRP1';
    update GRPREASSIGNAUTH set GROUPNAME='NEWGROUP' where GROUPNAME='MAINGRP1';
    update LABORAUTH set GROUPNAME='NEWGROUP' where GROUPNAME='MAINGRP1';
    update LIMITTOLERANCE set GROUPNAME='NEWGROUP' where GROUPNAME='MAINGRP1';
    update LOCAUTH set GROUPNAME='NEWGROUP' where GROUPNAME='MAINGRP1';
    update MAXGROUP set GROUPNAME='NEWGROUP' where GROUPNAME='MAINGRP1';
    update PMSCCATSEC set GROUPNAME='NEWGROUP' where GROUPNAME='MAINGRP1';
    update REPORTAPPAUTH set GROUPNAME='NEWGROUP' where GROUPNAME='MAINGRP1';
    update REPORTAUTH set GROUPNAME='NEWGROUP' where GROUPNAME='MAINGRP1';
    update SECURITYRESTRICT set GROUPNAME='NEWGROUP' where GROUPNAME='MAINGRP1';
    update SITEAUTH set GROUPNAME='NEWGROUP' where GROUPNAME='MAINGRP1';

    Now run this set of update statements in a single transaction against your database and you are done.


    Important notes

    1. It is better to execute the update statements after having stopped the Maximo application server.
    2. Store the update statements in a text file you can revert your changes back if something goes wrong.
    3. This is not a supported procedure so you must be very careful when using it. Test it in a dev/test environment before using it in production.
    4. Backup your database first.


    May 24, 2013

    Find Roles with assignments in a Workflow

    Roles in Maximo are used to represent a specific person or a group of people that can be used in workflows, communication templates and escalations.

    If you need to find which roles are used in a specific workflow you can use an SQL query like this.

    SELECT wfnode.title, wfnode.description, wfa.roleid, r.description
    FROM wfnode
    JOIN wfassignment wfa ON wfa.nodeid=wfnode.nodeid AND wfa.processrev=wfnode.processrev AND wfa.assigncode IS NULL
    JOIN maxrole r ON r.maxrole=wfa.roleid
    WHERE wfnode.processname='[PNAME]' AND wfnode.processrev=[PREV] AND wfnode.nodetype='WFTASK'
    ORDER BY wfnode.title;

    Just replace the [PNAME] and [PVER] tags with the correct process name and revision.

    May 19, 2013

    Link Parent-Child records in Maximo

    In a previous post I have described how to correctly manage a child table in an application using the Application Designer. However, there are better ways of achieving the same goals so I will analyze all the available options to correctly link records in a parent-child relationship.


    If you are a creating custom objects and applications in TPAE you may need at some point to create a child table like the Subassemblies in the Assets application.


    Let's pretend we have a parent table called TB1 and a child table called TB2.
    • TB1: Parent table
      • TB1ID: Identifier of records in TB1
      • ...
    • TB2: Child table
      • TB2ID: Identifier of records in TB2
      • TB1ID: Reference to the parent record in TB1 table
      • ...
    The fundamental concept is to have a field in the child table that points to a specific row in the parent table. In our example such field is TB2.TB1ID.

    In order to have the child table to behave correctly is to 'link' in some way the child rows to the parent object. If not doing this the child records will 'disappear' as soon as you save the record.

    The known techniques for linking parent-child records are described in the following table.

    Technique Pros Cons
    Using Application Designer Very simpleNot working with MIF
    Setting defaults in APPFIELDDEFAULTS table Quite simpleDo not manage deletes
    Using Java Mbo Works perfectlyRequires Java customization
    Using scriptingQuite simpleNone (BTW the example does not manage deletes)
    Using Database Configuration Built in feature Do not manage deletes
    Cannot be used once the tables are created


    Parent-Child database relationship

    Not all the techniques described hereafter requires this configuration but I think it is important to define a relationship from the TB1 table to the TB2 records. In Database Configuration define the following relationship in object TB1:
    • Relationship: TB2
    • Child Object: TB2
    • Where Clause: TB1ID=:TB1ID

    Using Application Designer

    In the Application Designer you have to use the TB2 relationship defined before to link the child table.
    The last important step is to initialize the TB2.TB1ID field on child records. To achieve this, add a Default Value control with the following configuration:
    • Attribute: TB1ID
    • From Data Source ID: results_showlist
    • From Attribute: TB1ID
    This will set the 'link' between parent and child tables.


    Using APPFIELDDEFAULTS

    Another possibility is to default the key values of your child table using the APPFIELDDEFAULTS table.
    The following INSERT statement will put a default value in TB2.TB1ID field to link the child records to the parent one.

    INSERT INTO APPFIELDDEFAULTS
    (APP, DEFAULTVALUE, OBJECTNAME, ATTRIBUTENAME, APPFIELDDEFAULTSID)
    ('[APPNAME]', ':OWNEROBJECT.TB1ID', 'TB2', 'TB1ID', APPFIELDDEFAULTSSEQ.NEXTVAL );


    Using Java

    An alternative method is to initialize this link in the child Mbo using Java. Here is how the child Mbo class should look like.

    public class Tb2Mbo extends Mbo implements MboRemote
    {
      public Tb2Mbo(MboSet ms) throws MXException, RemoteException
      {
        super(ms);
      }
    
      public void add() throws MXException, RemoteException
      {
        super.add();
    
        MboRemote ownerMbo = getOwner();
        if(ownerMbo != null)
        {
          // retrieves the TB1ID value from the parent Mbo
          String tb1id = ownerMbo.getString("TB1ID");
          // sets the TB1ID value in the child Mbo
          setValue("TB1ID", tb1id, NOACCESSCHECK|NOVALIDATION_AND_NOACTION);
        }
      }
    }

    To correctly manage deletes of child records you should also override the delete method of the Tb1Mbo class.

    public void delete(long accessModifier) throws MXException, RemoteException
    {
      super.delete(accessModifier);
    
      (((Mbo)this).getMboSet("TB2")).deleteAll();
    }

    This method has only one limitation (as far as I know). If the primary key of the parent table is updated before saving the record, it will create zombie child records. This is because the changes of TB1.TB1ID fieald are not propagated to child records. This is also a problem when duplicating objects.
    To solve this problem you can implement the action() method of the TB1 field class or setting the primary key of the parent table as described in the Using Database Configuration method.


    Using Scripting

    Adapted from John's comment (thank you)

    Create a table and include attributes for: {OWNERTABLE, OWNERID}
    Create a script with an Object Launch Point. The launch point needs to be set to fire on Initiate only.

    from psdi.mbo import MboConstants
    mbo.setValue('TB1ID', mbo.getOwner().getString("TB1ID"), MboConstants.NOACCESSCHECK))


    Using Database Configuration

    The last technique was suggested by Scott Dickerson. I haven't tested it but it should work.

    All you have to do is make sure your parent and child records have the same attribute names, and a unique primary index defined on the child table that's column order matches the unique primary index on the parent record.

    Make sure that the field names in the child table match the same field names from the key columns in the parent table. For instance, your child table MYASSETCHILD's field names must exactly match the field names of the parent table, in your case ASSETNUM,SITEID. The MYASSETCHILD field will also need it's own unique key field, let's say MYASSETCHILDID.
    So the unique key of the MYASSETCHILD table is ASSETNUM,SITEID,MYASSETCHILDID right?
    Now if you set the primarykeycolseq field in the maxattribute table in this order
    1. MYASSETCHILD:ASSETNUM: 1
    2. MYASSETCHILD:SITEID:2
    3. MYASSETCHILD:MYASSETCHILDID:3
    (you can set the primarykeycolseq by creating a unique index on these 3 columns and flagging it as the primary index for that table).
    As long as the order of the primarykeys in your child table match the order of the primary keys in the parent table, the TPAE framework will automatically set these child attributes whenever new child MBOs are added underneath the parent.

    May 13, 2013

    Java Logging and Tracing in Maximo

    This entry is part of the Maximo Java Development series.

    A good logging (tracing) is always a lifesaver when you have problems in a production environment. I will never stop telling to my fellow programmers how much is important to fill code with meaningful log calls.

    Maximo has a good and flexible logging subsystem. This IBM TechNote describes in detail how logging works in Maximo. Let's now see hot to use Maximo logging in your custom Java code.

    To be able to write entries to the standard Maximo log file you first have to get an instance of psdi.util.logging.MXLogger class. The most common way to achieve this is to use the MXLogger.getLogger(key) method.

    The following example describes a typical use of this technique and how to write a log entry in Maximo logs.

    public class MyClass extends Mbo implements MboRemote
    {
      private MXLogger log = MXLoggerFactory.getLogger("maximo.service.WORKORDER");
    
      ...
    
      public void sampleMethod()
      {
        log.info("Log this to INFO level");
        ...
        log.debug("Log this to DEBUG level");
      }
    }

    As you can see, the log attribute can be initialized in the declaration and used in any method.

    If you are extending an Mbo, you can retrieve a standard logger calling the psdi.mbo.Mbo.getMboLogger() method. The name of the returned logger be
    maximo.service.[service name].[business object name].

    public class MyClass extends Mbo implements MboRemote
    {
      ...
    
      public void sampleMethod()
      {
        getMboLogger().info("This is a log");
      }
    }


    References

    Logging in Maximo
    Understand Maximo 7.5 logging (IBM Education Assistant)
    Understand Maximo 7.5 logging (PDF)

    May 8, 2013

    TPAE Java coding standard

    This entry is part of the Maximo Java Development series.

    What is a coding standard and why it is important?
    A coding standard is a set of guidelines, rules and regulations on how to write code which will help developers quickly read and understand source code conforming to the style as well as helping to avoid introducing faults and misunderstanding.
    Coding standards are important because they provide greater consistency and uniformity in writing code between programmers. This ultimately leads to the code that is easier to understand and maintain which reduces the overall cost of the project.

    In this article I will go through some best practices that should be adopted when writing Java code within the TPAE framework.


    Java Packages

    Package names should be single lowercase words and must follow the existing format.
    All packages containing custom classes must start with a common package that is unique to that TPAE instance. I typically use 'cust' as a general prefix or a short name (no more than 6 letters) of the customer.


    For example, if you are extending the psdi.app.workorder.WO class, your custom class you should be cust.psdi.app.workorder.WO

    The positive effect of this is that you can easily find all the custom code in your classes tree.
    Here are some common examples of packages:

    Java class type Package Directory
    Business Objects (MBOs) cust.app.xxx [SPMDIR]\applications\maximo\businessobjects\classes\cust\app
    Interface processing cust.iface [SPMDIR]\applications\maximo\businessobjects\classes\cust\iface
    Maximo UI AppBeans and DataBeans cust.webclient.beans [SPMDIR]\applications\maximo\maximouiweb\webmodule\WEBINF\classes\cust\webclient\beans


    Class Names

    Classes should use natural descriptive names, begin with a capital, and have mixed case.
    If you extend an existing Mbo you can use the same name as the base class.

    package cust.app.asset;
    
    public class Asset extends psdi.app.asset.Asset implements psdi.app.asset.AssetRemote
    {
      ...
    }


    Alternative standard

    Always prefix the class name with Cust whether creating a new or extending an existing one.

    package cust.app.asset;
    
    public class CustAsset extends psdi.app.asset.Asset implements psdi.app.asset.AssetRemote
    {
      ...
    }

    Examples
    • CustMyTable
    • CustMyTableSet
    • CustMyTableRemote
    • CustFldMyTableMyField
    NOTE: I personally prefer the other naming standard (without the Cust prefix) because the custom classes are already clearly separated from the standard ones using the package naming convention described above.


    Code formatting

    You have many 'religions' here. The most important thing is to be consistent.
    Here is a sample of my preferred code formatting rules.

    public void add() throws MXException, RemoteException
    {
      super.add();
    
      MboRemote ownerMbo = getOwner();
      if (ownerMbo != null)
      {
        setValue("CREWCALNUM", "newval", NOACCESSCHECK|NOVALIDATION_AND_NOACTION);
      }
    }

    Don't criticize me I know it is not the 'standard' one  :-)


    Logging

    Do not use SystemOut! Use the TPAE logging subsystem instead. Logger messages should have a well descriptive debug message.

    MXLogger mxLogger = MXLoggerFactory.getLogger("maximo.application");
    ...
    mxLogger.debug("This will be loged in SystemOut log file");


    Comments

    Class header
    Insert a comment like this at the very beginning of all Java classes.

    /*
     * Any copyright disclaimer
     *
     * Revision History
     * Change Date  Changed By        Request#      Comment
     * ----------------------------------------------------------------------------
     * YYYY-MM-DD   Bruno Portaluri   Defect 1234   Performance improvements
     *
     */


    Class JavaDoc
    Use JavaDocs comments and tags.

    /**
    * My version of Asset class.
    *
    * @author Bruno Portaluri
    */
    public class Asset extends psdi.app.asset.Asset implements psdi.app.asset.AssetRemote
    {
      ...
    }


    Method JavaDoc

    /**
    * Delete object and its childs.
    *
    * @param accessModifier Flag to bypass normal security checks.
    * @return None
    * @see #someOtherMethod
    */
    public void delete(long accessModifier) throws MXException, RemoteException
    {
      ...
    }


    Constants

    Use constants in the psdi.mbo.MboConstants interface.
    Since many base classes like psdi.mbo.Mbo and psdi.mbo.MboSet implements this interface, you can use this constantants without prefixing them with MboConstant.

    The most important constants are listed below. Look at the complete list here.

    • DISCARDABLE - Bit for discardable mbos
    • NOACCESSCHECK - Suppress access control checks
    • NOACTION - Suppress action of a field
    • NOVALIDATION - Suppress validation of a field
    • REQUIRED - Set the field required
    Example

    setFieldFlag("siteid",READONLY, true);

    Few other important constants are defined in psdi.webclient.system.beans.WebClientBean class.
    • EVENT_STOP_ALL - stops the current event and all other events in the EventQueue from being handled
    • EVENT_HANDLED - stops all other controls from handling the event
    • EVENT_CONTINUE - allows event to be handled by other controls


    Annotations

    @Override

    Use it every time you override a method for two benefits.
    • Take advantage of the compiler checking to make sure you actually are overriding a method when you think you are. This way, if you make a common mistake of misspelling a method name or not correctly matching the parameters, you will be warned that you method does not actually override as you think it does.
    • Makes your code easier to understand because it is more obvious when methods are overwritten.

    @Deprecated

    If you want to remove an obsolete method that is called multiple times you could use the @deprecated annotation instead of deleting it. When you deprecate a method you should always explain in the annotation what is the new method to be called.