Stop Customizing Maximo the Old Way: Replace Third-Party Tools, Java, and Database Tricks
Series: Modern Maximo - Transforming from Legacy 7.x to MAS 9, Cloud-Native, and AI-Driven EAM | Part 4 of 12
Read Time: 12-15 minutes
Who this is for: Maximo developers, customization architects, and technical leads who currently rely on Java MBOs, database triggers, direct SQL, or third-party customization tools -- and need to understand what replaces them in MAS 9.
The bottom line: If your customization strategy depends on direct database access, custom Java MBOs, or database triggers, your MAS migration will fail. This blog shows you the path forward.
The Customization Crisis
Dev: "I'll extend the WORKORDER MBO like always."
Architect: "That won't work in MAS. Use automation scripts."
Dev: "But I need database access!"
Architect: "No direct database access. Use the API."
Dev: "This will take 3x longer!"
Architect: "Yes. And work 3x better."
The tension: Legacy patterns worked and were fast--but they're wrong for MAS.
Quick Reference: Legacy to MAS-Safe
Legacy Pattern — MAS Replacement
Custom Java MBOs — Automation Scripts (Python/JS)
Database Triggers — Automation Scripts + Events
Direct SQL Queries — MboSet API + REST/GraphQL
JDBC Integrations — REST API + OAuth
File System Access — DOCLINKS + Object Storage
Hardcoded Config — System Properties + K8s Secrets
Why Database-Level Tools Are Dead
MAS locks down the database. SaaS model, container immutability, and operator-managed upgrades mean no more direct schema modifications.
The Five Deadly Anti-Patterns
Anti-Pattern 1: Custom MBO Extensions with Direct SQL
The Legacy 7.6.x Way:
public class MyWorkOrderSet extends WorkOrderSet {
public void customValidation() throws MXException {
// Direct SQL query - DANGEROUS!
SqlFormat sql = new SqlFormat(
"SELECT COUNT(*) FROM MAXIMO.ASSET WHERE ASSETNUM = :1"
);
sql.setObject(1, "ASSETNUM", "STRING", getString("ASSETNUM"));
MXResultSet rs = getDatabase().executeQuery(sql);
// validation logic...
}
}Why This Fails in MAS:
- Direct database access is blocked in containerized environments
- MBO extensions don't survive upgrades cleanly
- Doesn't scale in microservices architecture
- Completely broken in SaaS (zero database access)
- Bypasses caching, security, and optimization layers
The MAS Way: Automation Script
# Object Launch Point: WORKORDER, Save Event, Before Save
if mbo.toBeAdded() or mbo.isModified("ASSETNUM"):
assetnum = mbo.getString("ASSETNUM")
# Use MboSet instead of SQL - respects security, caching, everything
assetSet = mbo.getMboSet("ASSET")
if assetSet.isEmpty():
errorgroup = 'asset'
errorkey = 'assetnotfound'Anti-Pattern 2: Database Triggers for Business Logic
The Legacy 7.6.x Way:
CREATE TRIGGER update_asset_repair_date
AFTER UPDATE ON MAXIMO.WORKORDER
FOR EACH ROW
BEGIN
IF :NEW.STATUS = 'COMP' THEN
UPDATE MAXIMO.ASSET
SET LASTREPAIREDDATE = SYSDATE
WHERE ASSETNUM = :NEW.ASSETNUM;
END IF;
END;Why This Fails in MAS:
- Database is sealed--you literally cannot add triggers
- Triggers bypass all application logic and security
- Invisible to API consumers and audit trails
- Cannot be version controlled properly
- 100% broken in SaaS
The MAS Way: Automation Script with Launch Point
# Object Launch Point: WORKORDER, Status Change, After Save
if mbo.getString("STATUS") == "COMP":
assetnum = mbo.getString("ASSETNUM")
if assetnum:
assetSet = mbo.getMboSet("ASSET")
if not assetSet.isEmpty():
asset = assetSet.getMbo(0)
asset.setValue("LASTREPAIREDDATE", mbo.getDate("ACTFINISH"))Anti-Pattern 3: Direct Database Integration
The Legacy 7.6.x Way:
# External reporting tool connecting directly to Maximo DB
import cx_Oracle
connection = cx_Oracle.connect('maximo/password@maxdb')
cursor = connection.cursor()
cursor.execute("""
SELECT WONUM, DESCRIPTION, STATUS
FROM MAXIMO.WORKORDER
WHERE SITEID = 'MAIN' AND STATUS IN ('WAPPR', 'APPR')
""")Why This Fails in MAS:
- No external database connections allowed
- Bypasses authentication, authorization, and encryption
- No audit trail for data access
- Schema changes break everything
- Completely impossible in SaaS
The MAS Way: REST API Integration
import requests
# Proper API-based integration
base_url = "https://mas-instance.example.com/maximo/oslc/os/mxwo"
headers = {
"apikey": os.getenv("MAXIMO_API_KEY"),
"Content-Type": "application/json"
}
params = {
"oslc.where": 'siteid="MAIN" and status in ["WAPPR","APPR"]',
"oslc.select": "wonum,description,status"
}
response = requests.get(base_url, headers=headers, params=params)
work_orders = response.json()["member"]Key insight: The three most common anti-patterns -- custom MBOs with direct SQL, database triggers, and direct database integrations -- share a common flaw: they bypass the application layer entirely. In MAS, the application layer IS the only access path. Every pattern that skips it is fundamentally incompatible, not just inconvenient.
Anti-Pattern 4: File System Dependencies
The Legacy 7.6.x Way:
public void exportReport() {
String filePath = "/opt/IBM/SMP/maximo/reports/output/report.pdf";
File reportFile = new File(filePath);
// Write report to filesystem
}Why This Fails in MAS:
- Containers have no persistent file system
- Pods can be destroyed and recreated at any moment
- Hardcoded paths don't exist in cloud environments
- Doesn't scale horizontally
- Broken in SaaS
The MAS Way: Object Storage
# Use Maximo's DOCLINKS for attachments
doclinks = mbo.getMboSet("DOCLINKS")
doc = doclinks.add()
doc.setValue("DOCUMENT", "ReportOutput")
doc.setValue("DOCTYPE", "Attachments")
doc.setValue("URLNAME", "report_" + mbo.getString("WONUM") + ".pdf")
# Or use external object storage (S3, Azure Blob, etc.)
import boto3
s3 = boto3.client('s3')
s3.upload_file('local_report.pdf', 'maximo-reports', f'reports/{wonum}.pdf')Anti-Pattern 5: Hardcoded Configuration
The Legacy 7.6.x Way:
public class IntegrationService {
private static final String ENDPOINT = "http://erp.company.com/api";
private static final String API_KEY = "sk-12345abcde"; // Security nightmare!
}Why This Fails in MAS:
- Can't change config without recompiling
- Credentials in source code (audit failure waiting to happen)
- Different values needed per environment
- Doesn't work with secrets management
- Major security and compliance risk
The MAS Way: Property/Secret Management
# Automation Script using System Properties
from psdi.server import MXServer
endpoint = MXServer.getMXServer().getProperty("custom.erp.endpoint")
username = MXServer.getMXServer().getProperty("custom.erp.username")
# For sensitive data, use encrypted properties or external secrets
import os
api_key = os.getenv("ERP_API_KEY") # Injected by Kubernetes secretsKey insight: Every anti-pattern has a MAS-safe replacement. The shift is consistent: move from infrastructure-level access (filesystem, database, hardcoded config) to application-level access (APIs, DOCLINKS, system properties, Kubernetes secrets). The MAS way is more work upfront but dramatically more maintainable, scalable, and upgrade-safe.
MAS Customization Toolbox
Automation Scripts
Replaces 80% of custom Java. Use for validation, defaults, workflow logic, and integrations.
Launch Point — Use Case
Object — Business logic on records
Attribute — Field-level validation
Action — Toolbar buttons, escalations
Library — Reusable modules
Domains & Conditions
No-code logic for dynamic dropdowns and field restrictions.
Conditional UI Expressions
Show/hide fields, set read-only, or require fields based on conditions.
Integration Framework
Outbound: REST, GraphQL, Kafka, Webhooks | Inbound: REST, Kafka subscriptions
MAS-Safe Checklist
- [ ] No direct database access or triggers
- [ ] API-first integrations
- [ ] No file system dependencies
- [ ] No hardcoded values or credentials
- [ ] Works in SaaS (stateless, scalable)
- [ ] Upgrade-safe
ROI Summary
Short-term: 2-3x initial effort for redesign
Long-term: Quarterly upgrades (vs. 6-12 month projects), 40-60% support cost reduction, SaaS-ready, audit-compliant
Key insight: The 2-3x initial redesign investment pays for itself within 12-18 months. Organizations that modernize their customizations report 40-60% reduction in ongoing support costs, plus the ability to adopt quarterly MAS updates without the multi-month upgrade projects that plagued 7.6.x.
Key Takeaways
- Legacy patterns must go -- database triggers, custom Java, direct SQL are all incompatible
- Automation scripts replace 80% of custom Java with better maintainability
- API-first everything -- REST/GraphQL for integrations, no direct DB access
- Configuration over code -- domains, conditions, and no-code tools first
Resources for Your Journey
IBM Official
- MAS 9.0 Documentation
- Automation Scripts Guide
- REST API Reference
- Integration Framework Guide
- GraphQL API Documentation
Community
Training
- MAX4337G - Automation Scripting
- MAX0151G - MAS Manage Fundamentals
- MAX0152G - MAS Manage Configuration
Previous: The 7.6 to MAS Migration Playbook
Next: Integration Modernization
Series: THINK MAS -- Modern Maximo | Part 4 of 12
About TheMaximoGuys: We help Maximo developers and teams make the transition from 7.6.x thinking to MAS mastery. We've been through the journey ourselves--and we're here to make yours smoother.
Part of the "THINK MAS" Series | Published by TheMaximoGuys | [Subscribe for Updates](#)



