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 heavily on direct database access, custom Java MBOs, or database triggers, your MAS migration will become harder to support and harder to upgrade. This blog shows you the safer path forward.
The Customization Reset
Dev: "I'll extend the WORKORDER MBO like always."
Architect: "That pattern is much harder to support in MAS. Start with automation scripts."
Dev: "But I need database access!"
Architect: "Plan around supported APIs instead of direct database access."
Dev: "This will take 3x longer!"
Architect: "It may take more redesign upfront, but it usually ages better."
The tension: Legacy patterns were fast and familiar, but MAS rewards more supportable patterns.
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](#)



