Tuesday, September 11, 2012

Setting up custom loggers in Maximo

Using out of box loggers to debug Maximo customizations is not a good idea as developers end up leaving the debug on which will impact the overall system performance. On creation of a custom logger, we can only turn the debug on for the custom logger and leave the other loggers in their current state.

Here is a quick and easy way to create and use custom loggers for Maximo customizations.This logger can be used only for the custom Java code that developers write in Maximo.


1.Navigate to System Configuration -> Platform Configuration -> Logging
2.Click on New Row under Root Logger
3.Enter the following:   
           Logger: Customization
           Log Level: DEBUG
           Key: log4j.logger.maximo.Customization
           Inherited Appenders: Console,Rolling
           Enable Active checkbox.
4.Save the logging properties
5.Click on Select Action -> Apply Settings



Here is a sample of how to use the custom logger in a Custom class:

/** Import the following classes to use a custom logger */
import psdi.util.logging.MXLogger;
import psdi.util.logging.MXLoggerFactory;

public class ExPO extends PO implements PORemote
{

    /** Declare the logger as an instance of MXLogger */
    MXLogger myLogger;
   

    public ExPO (MboSet mboset) throws MXException, RemoteException
    {
            super(mboset);

            /**Get the instance of the custom logger using the key maximo.Customization which is            defined in Step 3 of the logger configuration */

            myLogger = MXLoggerFactory.getLogger("maximo.Customization");
            myLogger.debug("**Inside the constructor of custom.app.po.ExPO**");          
    }

}
    


Thursday, September 6, 2012

How to set default value in a field using the Application Designer

There are three ways in Maximo through which a default value can be set for a persistent field:

1. Using Field level custom class
2. Using Database configuration
3. Using Application Designer

The first method of using field level custom class requires someone to write a custom Java code and it involves the cost of maintenance and support.

The second method of using Database configuration is quite simple. However it is more heavy from the system perspective as it is directly forced on the database.

The third method is to use an Application Designer to set a field default value.

Here is an example to default the Quantity to 5.00 whenever a new item / material is associated to Workorder -> Plans -> Materials tab

1.  Navigate to Application Designer
2. Search for the application WOTRACK
3. Navigate to Plans -> Materials subtab
4. Click on Control Palette
5. Enable the "Hide" radio button
6. Drag the "Default Value" control to the section where the Quantity field is present



7. Open up the Properties for the "Default Value" control
8. Specify attribute as ITEMQTY (belonging to Object WPMATERIAL) and Value as 5.00 (The default value that needs to be set for the attribute)
9. Save changes in the Application Designer
10. To test - navigate to Workorder -> Workorder Tracking -> Plans -> Materials subtab
11.Click on New Row in Materials subtab. The default value of 5.00 is populated in the screen.

NOTE: To view an existing Default Value control, click on Select Action -> Toggle Show All Controls.

Wednesday, July 6, 2011

Stop and Restart Workflow through Action Class

There are several scenarios where a Maximo application object (workorder / jobplan / pm) which is currently in a workflow needs to be stopped and restarted. I faced one such instance recently where the workflow design was modified to fix a flaw and there were several workorders which were tagged to the old workflow version.  Hence I had to stop workorders in the old workflow and restart them in the new workflow. As the count was huge, it was impossible to do it manually using the Workflow administrator to stop the workflow and restart them using the Workorder application. Hence I wrote an action class and tagged it to an escalation which will have a condition to pull all the workorders that needs to be stopped and restarted.

I am explaining this example with WORKORDER as the object which is in the old workflow. The workorder needs to be stopped from the old workflow and re-routed to the newly revised workflow

Here is what you need to do....
1. Create a Relationship to find the active WFINSTANCE records for the workorder.
   
Name: ACTIVEWFINSTANCE
Child Object: WFINSTANCE
Where Clause: ownerid=:workorderid and ownertable=’WORKORDER’ and active=1
Remarks: Gets all the Active Workorders from workorder table which has pending assignments

2. Create a new Action Class and associate it to a Custom Action.
3. Here is the action class.

package custom.escalation.action;

import java.rmi.RemoteException;
import psdi.mbo.MboRemote;
import psdi.mbo.SqlFormat;
import psdi.util.MXException;
import psdi.workflow.WFInstanceSetRemote;
import psdi.workflow.WFInstance;
import psdi.util.logging.MXLogger;
import psdi.util.logging.MXLoggerFactory;
import psdi.workflow.WFProcess;
import psdi.workflow.WFProcessSetRemote;




public class StopWorkFlow implements
psdi.common.action.ActionCustomClass
{

     private static final MXLogger log = MXLoggerFactory.getLogger("maximo");
   
    public void applyCustomAction(MboRemote mbo, Object[] arg)
    throws MXException, RemoteException
    {
    log.debug("Entered applyCustomAction of StopWorkFlow");
    WFInstanceSetRemote wfInstanceSet=(WFInstanceSetRemote) mbo.getMboSet("ACTIVEWFINSTANCE");

   
        if(!wfInstanceSet.isEmpty())
        {
            log.debug("WfInstance is not empty");
            for(int wfInstance=0; wfInstance <wfInstanceSet.count();wfInstance++)
            {
            WFInstance wfInst=(WFInstance) wfInstanceSet.getMbo(wfInstance);
            String processName = wfInst.getString("processname");
            log.debug("processName: "+ processName);           
            WFProcessSetRemote wfProcessSet = (WFProcessSetRemote) mbo.getMboSet("WFPROCESS");
            SqlFormat sqf1 = new SqlFormat(mbo.getUserInfo(), "processname = :1 and active = 1");
             sqf1.setObject(1,"WFPROCESS","PROCESSNAME", processName);
             wfProcessSet.setWhere(sqf1.format());
            int matchingProcess = wfProcessSet.count();
            log.debug("matchingProcess: " + matchingProcess);
            WFProcess wfProcess = null;
            
            if (matchingProcess==1)
            {
                wfProcess = (WFProcess)wfProcessSet.getMbo(0);
            }
           
            // Enter memo for the closed transaction
            wfInst.stopWorkflow("Workflow stopped through escalation");
            log.debug("After stopping workflow");
            // Enter memo for restarted transaction
            wfInst.initiateWorkflow("Initiated workflow through escalation",wfProcess);
            log.debug("After initiating workflow");
            wfInstanceSet.save();
           


            } //end of For
        } //End of if(!wfInstanceSet.isEmpty())
    } //End of public void applyCustomAction
} //End of stopWorkFlow


 4. The action class gets the process name that the workflow is currently in and retrieves the active version of the process. It then stops the workflow of the existing workorder and then restarts it using the latest active version of the workflow.

5. Now create an escalation and associate a condition to pick the relevant workorders which needs to be stopped and restarted.

6. Associate an action to the escalation which has to be tagged to the action class created in Step 3.