Maximo Automation Scripts: From Launch Points to Production Patterns
Maximo Automation Scripts: From Launch Points to Production Patterns
Automation scripts are the most powerful customization mechanism in the Maximo ecosystem. They let you extend and modify system behavior using Jython (Python on the JVM) or JavaScript without touching the core Java codebase. This means your customizations survive upgrades, stay within the supported configuration layer, and can be managed entirely through the Maximo UI.
But power without discipline creates chaos. Every Maximo consultant has encountered a production system littered with undocumented scripts, silent exception handlers, and launch points that fire on every save with no clear purpose. This article is about doing it right: understanding every launch point type, writing production-grade error handling, leveraging the new Bob AI tool for script modernization, and knowing when a script is the wrong answer.
The Launch Point Taxonomy
A launch point is the trigger that fires your script. Maximo provides five distinct launch point types, each with its own execution context, available variables, and appropriate use cases. Understanding the differences is the foundation of effective scripting.
Object Launch Points
Object launch points fire when a record is created, saved, or deleted. They are the most commonly used launch point type and the most frequently misused.
Events available:
- Initialize: Fires when a new record is first created, before any data is populated. Use this to set default values that depend on complex logic.
- Validate: Fires during save, before the record is committed. Use this for cross-field validation that cannot be expressed through standard conditional UI.
- Save: Fires after validation but before the database commit. Use this for derived field calculations, audit logging, or triggering related record updates.
- After Save: Fires after the database commit. Use this for integrations, notifications, or any action that should only occur if the save succeeded.
Available implicit variables:
- mbo: The current MBO (Maximo Business Object) record
- mboName: The object name as a string
- user: The current user name
- app: The current application name (may be null in non-UI contexts)
Example: Auto-generating a follow-up work order from a failed inspection:
# Object Launch Point: INSPFIELDRESULT - Save - After Save
# Generates a follow-up work order when an inspection result fails
from psdi.mbo import MboConstants
from psdi.server import MXServer
def main():
# Only proceed if the inspection result indicates failure
result_value = mbo.getString("RESULTVALUE")
if result_value != "FAIL":
return
# Check if a follow-up work order already exists
existing_wo = mbo.getString("FOLLOWUPWO")
if existing_wo:
return
# Get the parent inspection record
inspection = mbo.getOwner()
if inspection is None:
return
# Create the follow-up work order
wo_set = MXServer.getMXServer().getMboSet(
"WORKORDER",
mbo.getUserInfo()
)
new_wo = wo_set.add()
new_wo.setValue("DESCRIPTION",
"Follow-up: Failed inspection on " + inspection.getString("ASSETNUM"))
new_wo.setValue("ASSETNUM", inspection.getString("ASSETNUM"))
new_wo.setValue("LOCATION", inspection.getString("LOCATION"))
new_wo.setValue("WOPRIORITY", 1)
new_wo.setValue("WORKTYPE", "CM")
new_wo.setValue("REPORTEDBY", user)
# Set the GL account from the asset or location
asset_set = mbo.getMboSet("ASSET")
if not asset_set.isEmpty():
asset = asset_set.getMbo(0)
new_wo.setValue("GLACCOUNT", asset.getString("GLACCOUNT"))
wo_set.save()
# Link back to the inspection
mbo.setValue("FOLLOWUPWO", new_wo.getString("WONUM"))
wo_set.close()
main()
Attribute Launch Points
Attribute launch points fire when a specific field value changes. They are more surgical than object launch points and should be preferred when the logic is scoped to a single field.
Events available:
- Initialize: Fires when the attribute is first displayed. Use for dynamic defaulting.
- Validate: Fires when the attribute value changes. Use for field-level validation.
- Action: Fires on a specific user action tied to the attribute.
Available implicit variables:
- mbo: The current MBO
- app: The current application name
- value: The new value being set (available in Validate event)
Example: Dynamic default for asset priority based on criticality:
# Attribute Launch Point: ASSET.PRIORITY - Initialize
# Sets default priority based on asset criticality field
def main():
criticality = mbo.getString("CRITICALITY")
priority_map = {
"CRITICAL": 1,
"HIGH": 2,
"MEDIUM": 3,
"LOW": 4
}
if criticality in priority_map:
mbo.setValue("PRIORITY", priority_map[criticality])
Action Launch Points
Action launch points are triggered by user-initiated actions: button clicks, select actions, or custom dialog invocations. They are the mechanism for building interactive customizations.
Key characteristics:
- Explicitly invoked by the user, not automatic
- Can be tied to a signature option in Application Designer
- Ideal for custom dialogs, bulk operations, and complex multi-step processes
Example: Custom dialog invocation pattern:
# Action Launch Point: CUSTOMDIALOG.INVOKE
# Handles the OK button on a custom dialog
from psdi.mbo import MboConstants
from psdi.server import MXServer
def main():
# The dialog passes selected values through the MBO
assetnum = mbo.getString("ASSETNUM")
location = mbo.getString("LOCATION")
if not assetnum and not location:
errorgroup = "custom"
errorkey = "noAssetOrLocation"
params = []
raise MXException(errorgroup, errorkey, params)
# Perform the business logic
wo_set = MXServer.getMXServer().getMboSet(
"WORKORDER",
mbo.getUserInfo()
)
new_wo = wo_set.add()
new_wo.setValue("DESCRIPTION", "Custom dialog generated work order")
new_wo.setValue("ASSETNUM", assetnum)
new_wo.setValue("LOCATION", location)
wo_set.save()
wo_set.close()
Custom Condition Launch Points
Custom condition launch points are used within workflows to evaluate complex routing logic that cannot be expressed through SQL-based conditions. They return a boolean value that determines which workflow path to follow.
Available implicit variables:
- mbo: The current MBO being processed by the workflow
- evalresult: A boolean variable you set to True or False to control routing
Example: Complex workflow routing based on multiple conditions:
# Custom Condition Launch Point: WORKORDER.ROUTE_DECISION
# Routes work orders based on priority, work type, and asset criticality
def main():
priority = mbo.getInt("WOPRIORITY")
worktype = mbo.getString("WORKTYPE")
# Get asset criticality
asset_set = mbo.getMboSet("ASSET")
criticality = "LOW"
if not asset_set.isEmpty():
asset = asset_set.getMbo(0)
criticality = asset.getString("CRITICALITY")
# Route to emergency path if priority 1 and critical asset
if priority <= 1 and criticality == "CRITICAL":
evalresult = True
return
# Route to expedited path if priority 2 and CM work type
if priority <= 2 and worktype == "CM":
evalresult = True
return
# Default: standard path
evalresult = False
Integration Launch Points
Integration launch points fire during inbound or outbound integration processing. They allow you to transform data, validate incoming records, or enrich outgoing messages.
Events available:
- Inbound: Fires before an inbound integration message is processed
- Outbound: Fires before an outbound integration message is sent
Available implicit variables:
- mbo: The current MBO (inbound) or the MBO being published (outbound)
- user: The integration user
- app: The integration application name
Production-Grade Error Handling
The single most common failure pattern in Maximo automation scripts is silent exception handling. A script catches an exception, logs nothing, and continues as if nothing happened. The result is data corruption that may not be discovered for weeks or months.
The Anti-Pattern
# DO NOT DO THIS
try:
critical_operation()
except:
pass # Silent failure - data corruption waiting to happen
The Production Pattern
from psdi.util.logging import MXLoggerFactory
import traceback
logger = MXLoggerFactory.getLogger("maximo.script")
def main():
try:
critical_operation()
except Exception as e:
# Log the full stack trace
logger.error("Script CUSTOM_SCRIPT failed: " + str(e))
logger.error(traceback.format_exc())
# Set a user-visible error message
errorgroup = "custom"
errorkey = "scriptExecutionFailed"
params = [str(e)]
# Re-raise as MXException so the transaction rolls back
from psdi.util import MXException
raise MXException(errorgroup, errorkey, params)
Key principles:
- Always log the full stack trace. A one-line error message is not enough to diagnose production issues.
- Use a consistent logger name. The pattern
maximo.scriptormaximo.automation.YOUR_SCRIPT_NAMEmakes log filtering straightforward. - Re-raise as MXException. This ensures the transaction rolls back. A script that catches an exception and continues has silently corrupted data.
- Include context in the error message. The MBO name, key values, and user context help with troubleshooting.
- Never catch bare
except:. Always catchException(or a specific exception type) so system-level exceptions likeKeyboardInterruptare not swallowed.
Structured Error Handling for Integration Scripts
Integration scripts have a special consideration: you may want to reject a single record without aborting the entire integration batch. The pattern for this:
from psdi.iface.mic import MicConstants
def main():
try:
# Validate the incoming record
if not is_valid(mbo):
mbo.setValue("SKIP", True, MicConstants.NO_ACCESSCHECK)
return
except Exception as e:
logger.error("Integration validation failed: " + str(e))
mbo.setValue("SKIP", True, MicConstants.NO_ACCESSCHECK)
# Do not re-raise - allow the batch to continue
Bob: AI-Assisted Script Modernization
In June 2026, IBM introduced Bob, an AI-assisted development tool specifically trained on Maximo coding patterns and best practices. Bob is not a generic code generator. It is a Maximo-specific skill that analyzes existing automation scripts, identifies issues, and suggests improvements.
Bob's workflow follows a five-stage pipeline:
- Discover: Reads existing automation scripts from the Maximo environment, including configuration scripts, object launch points, attribute launch points, and integration scripts.
- Analyze: Reviews each script for outdated patterns, risky constructs, and inefficiencies. Bob looks for repeated code blocks, missing exception handling, hardcoded values, deprecated API calls, and performance bottlenecks.
- Optimize: Suggests cleaner, more maintainable logic. This includes simplifying complex conditionals, reducing duplicate scripts, improving readability, and replacing deprecated patterns with current best practices.
- Validate: Ensures the optimized script produces the same behavior as the original. This step is critical in enterprise environments where a script change can affect thousands of transactions.
- Update: Once reviewed and approved, the changes are written back through a governed process. Bob does not modify production scripts without human review.
Bob is particularly valuable for organizations with large script inventories accumulated over years of Maximo usage. Scripts written for Maximo 7.5 or 7.6 often use patterns that are suboptimal or deprecated in MAS 9.x. Bob can identify these and suggest modern equivalents.
Scripts vs. Workflows vs. Escalations: A Decision Framework
Not every automation need requires a script. Maximo provides three primary automation mechanisms, and choosing the wrong one creates technical debt.
| Criterion | Automation Script | Workflow | Escalation |
|---|---|---|---|
| Trigger | Real-time (save, field change, action) | Real-time (route a record) | Scheduled (cron-based) |
| Complexity | High (arbitrary logic) | Medium (routing, approvals) | Low to medium (batch operations) |
| Performance impact | Per-transaction | Per-route step | Batch, off-peak |
| Maintainability | Requires coding skills | Visual designer, lower skill bar | SQL or simple scripts |
| Transaction context | Full MBO access | Limited to workflow variables | Limited to escalation context |
| Use case | Complex validation, integrations, custom UI | Approvals, status transitions, notifications | Batch updates, periodic sync, cleanup |
When to use an automation script:
- The logic involves complex conditionals or calculations
- You need to interact with multiple related MBOs
- You are building a custom UI interaction (dialog, button)
- You are integrating with an external system via HTTP
- The logic cannot be expressed in the workflow designer
When to use a workflow:
- The process involves human approval steps
- The logic is primarily routing a record through status transitions
- You need an audit trail of who did what and when
- Non-technical users need to modify the process
When to use an escalation:
- The operation runs on a schedule, not in real time
- You are processing records in bulk
- The logic is simple (set a field, send a notification, close stale records)
- You want to offload work to off-peak hours
The most common mistake is using an object launch point on save for logic that should be an escalation. If the operation scans and updates many records, it does not belong in a per-transaction script. Move it to an escalation that runs nightly.
Script Performance Considerations
Automation scripts execute within the Maximo application server's JVM. Poorly written scripts can degrade performance for all users. Here are the patterns to watch:
Avoid N+1 Queries
# BAD: N+1 query pattern
wo_set = mbo.getMboSet("WOCHILDREN")
for i in range(wo_set.count()):
child = wo_set.getMbo(i)
# Each getMboSet call inside the loop is a separate database query
child_assets = child.getMboSet("ASSET")
# ...
# GOOD: Use relationship traversal or batch fetching
wo_set = mbo.getMboSet("WOCHILDREN")
wo_set.setQbe("status", "=WAPPR")
wo_set.reset()
# Process the filtered set without additional queries per record
Cache MXServer References
# BAD: Getting MXServer reference inside a loop
for assetnum in asset_list:
mxserver = MXServer.getMXServer() # Unnecessary repeated call
# ...
# GOOD: Get it once
mxserver = MXServer.getMXServer()
for assetnum in asset_list:
# Use the cached reference
Close MBO Sets
Every getMboSet() call that is not a relationship traversal should be explicitly closed:
wo_set = MXServer.getMXServer().getMboSet("WORKORDER", userInfo)
try:
# Process the set
pass
finally:
wo_set.close() # Always close to release database resources
Practical Implications
Script inventory and governance. Every organization running Maximo should maintain an inventory of all automation scripts, including their launch point type, associated object, last modified date, and business purpose. Without this, script modernization is guesswork.
Bob as a force multiplier. For organizations with dozens or hundreds of scripts, Bob can reduce the modernization effort from months to weeks. The key is to run Bob in a non-production environment first, review its suggestions carefully, and validate before applying changes.
Exception handling is non-negotiable. Every script in production should have explicit exception handling with logging and transaction rollback. This is not a nice-to-have. It is the difference between a script that fails safely and one that silently corrupts data.
Launch point selection matters. Using an object launch point when an attribute launch point would suffice means your script fires on every save, not just when the relevant field changes. This is wasted CPU cycles and increased risk of unintended side effects.
The upgrade argument. One of the strongest selling points for automation scripts over Java customizations is upgrade survivability. Scripts live in the database and are not affected by application binary updates. But this only holds if the scripts use supported APIs. Scripts that reach into internal classes or use reflection may break across releases just like Java customizations.
Bottom Line
Automation scripts are the right answer for most Maximo customizations in 2026. They are upgrade-safe, manageable through the UI, and powerful enough to handle complex business logic. But they require discipline: proper launch point selection, production-grade error handling, performance awareness, and a governance framework that prevents script sprawl.
Bob represents a step change in script maintainability. Organizations that adopt it early will reduce their technical debt and free up developers for higher-value work. Those that ignore it will continue accumulating scripts that become harder to modernize with each passing release.
The decision framework is simple: use scripts for complex real-time logic, workflows for human-driven processes, and escalations for scheduled batch operations. Get the launch point right. Handle every exception. Close every MBO set. And when in doubt, ask Bob.