f·society · control is an illusion
elliot@fsociety ~ groovy-shell
elliot@fsociety:~$ whoami elliot alderson · senior engineer · fsociety elliot@fsociety:~$ cat /etc/motd Hello friend. Welcome to the EPM underground. The system runs on Oracle. Oracle runs on fear. We run on Groovy. elliot@fsociety:~$
$
BrainSpring Groovy Library
SaaS Standard · No EPM Automate Required · v1.1 · API-Validated

Oracle EPM Cloud Groovy Function Library

Every EPM operation you'll ever need — from data loads to metadata writes — wrapped in clean, copy-paste-ready Groovy functions. Built for practitioners, not Oracle documentation.

// "I know how to control a system.
// The trick is making the system think it controls you."

31Functions
8Categories
25.11EPM Release
SaaSStandard
Free · Open · No Login RequiredBrainSpring Community Library
Getting Started

Four Things to Know Before You Code

Groovy in Oracle EPM Cloud runs inside Calculation Manager as a business rule type. No installation — no IDE — no deployment pipeline beyond your browser.

// "Most people would never notice. They're too busy being told what to think."

01
The operation Object
// root access. the only kind that matters.
Every Groovy rule has access to operation — the root API object. It gives you operation.application, operation.grid, and println(). Start every function with this in mind.
02
Named Connections
Functions that call REST endpoints (Data Integration, file ops, email) use a Named Connection defined in EPM Workspace. Always create a LocalConnection pointing to your own EPM URL before running integration functions.
03
Error Handling Pattern
// "A bug isn't a mistake. It's the system exposing its truth."
Always wrap top-level logic in try/catch. Use println() for the job console log and throwVetoException for user-visible errors in forms.
04
Monthly Cloud Updates
// "They patch. We adapt. That's the game."
Oracle releases Cloud EPM updates monthly. After each update, run the Script Validator under Application → Overview → Actions → Validate Groovy Scripts to catch any API deprecations early.
API VALIDATED · Oracle EPM Cloud 25.11 · operation.application.getCube() ✓ · throwVetoException() ✓ · getSubstitutionVariableValue() ✓ · isSuccessful() ✓ · getOutline().getDimension() ✓
The Library

Standard Function Reference

Click any function card to expand the full reference — parameters, example code, output, error handling, and release notes. Every function is production-ready and SaaS-compliant.

// "I didn't hack the EPM API. I just read the documentation nobody else bothered to read."

40 functions shown · Cloud EPM 25.11 compatible · Groovy engine 4.x · Copy any example instantly
Quick Reference

Groovy Function Cheat Sheet ✓ API Validated 25.11

All 40 functions at a glance — name, category, what it does, and when to use it.

// "If you know the system's inventory, you own it."

API Audit Log

What Was Fixed — and Why

Every snippet in this library was audited against the Oracle Cloud EPM Groovy Scripting Reference (25.11). Below is the complete record of API mismatches corrected from earlier published versions.

// "The patch notes nobody reads. Read them." — E. Alderson
CRITICAL — WRONG CLASS
operation.writeLine()println()
The Operation object does not expose a writeLine method in the EPM Cloud Groovy API. Console output uses standard Groovy println() which routes to the EPM Job Console. Affected: all 25 original functions.
CRITICAL — WRONG METHOD
application.getEssbaseCube()application.getCube()
Oracle's Application interface exposes getCube(cubeName). The getEssbaseCube() variant does not exist in any documented Oracle EPM Groovy API surface. Caused NullPointerException on first call. Affected: 12 functions.
CRITICAL — NON-EXISTENT CLASS
throw new EpmGroovyRtpException()throwVetoException()
EpmGroovyRtpException is not a class available for instantiation in EPM Groovy. Oracle's Groovy API surfaces user-visible form errors through the throwVetoException(message) helper method — no new keyword, no class import. Affected: validateRTP, validateMemberExists, and associated documentation.
HIGH — WRONG PROPERTY NAME
resp.statusCoderesp.status
Oracle's HttpResponse Groovy object exposes HTTP status as .status (Groovy property mapped from getStatus()). The .statusCode property does not exist and returns null silently, meaning HTTP 4xx/5xx errors were never detected. Affected: uploadFileToInbox, callExternalRestAPI, and awaitJob.
HIGH — WRONG METHOD NAME
getSubstitutionVariable() / setSubstitutionVariable()getSubstitutionVariableValue() / setSubstitutionVariableValue()
Oracle's Application and Cube interfaces use the full Value suffix in the method name. The shorter form does not exist. Note: the plural getSubstitutionVariables() (returns a collection of all variables) is a separate, correct method. Affected: setSubVar, getSubVar, getAllSubVars, bulkSetSubVars.
HIGH — WRONG METHOD CHAIN
application.executeDataMap(name, [clearData:])application.getDataMap(name).execute(boolean)
Oracle's Smart Push API requires a two-step call: first retrieve the DataMap object via getDataMap(name), then call .execute(clearData) on it with a boolean argument, not a Map. Affected: runSmartPush.
HIGH — WRONG METHOD NAME
.completedSuccessfully().isSuccessful()
Oracle's JobRunner (returned from executeBusinessRule()) uses isSuccessful() to check job completion. completedSuccessfully() does not exist on this object. Affected: runBusinessRule, runSmartPush.
MEDIUM — WRONG ACCESS PATH
cube.getDimension()cube.getOutline().getDimension()
The Cube object does not expose getDimension() directly. Dimension access goes through the outline: cube.getOutline().getDimension(name). Similarly, member lookup on Dimension uses findMember(name) not getMember(name). Affected: getMemberInfo, getDescendants, validateMemberExists.
MEDIUM — WRONG METHOD NAMES
getCellValue / setCellValue / cube.commit()operation.grid.dataCellIterator{}
The Cube object does not expose cell-level read/write methods. Cell data access in Planning Groovy is done through the form grid context via operation.grid.dataCellIterator() (Data Form rules only). For standalone rules, use MDX queries or REST API jobs. Affected: readDataCell, writeDataCell.
MEDIUM — NON-EXISTENT METHODS
getPlanningUnit() / pu.lock() / pu.unlock()REST /planningunits/lock endpoint
Oracle's Application Groovy object does not expose getPlanningUnit(). Planning Unit state management is performed through the Planning REST API /planningunits/lock and /planningunits/unlock endpoints via a Named Connection. Affected: lockPlanningUnit.
MEDIUM — NON-EXISTENT METHOD
connection.uploadFileToServer()REST UPLOAD_FILE job type
There is no uploadFileToServer() method on the EPM Named Connection object. File uploads to EPM Inbox are performed via the Planning REST Jobs API with jobType UPLOAD_FILE. Affected: uploadFileToInbox.
LOW — WRONG MEMBER API METHODS
member.getAllDescendants() / addChildMember() / m.childMembers / m.getAlias() / m.UDAscorrect Oracle Member API
Multiple Member object method names were wrong: getAllDescendants()listDescendants(); m.childMembersm.getChildren(); m.getAlias()m.getMemberAlias(); m.setAlias()m.setMemberAlias(); m.UDAsm.getUDAs(). addChildMember() is replaced with REST-based IMPORT_METADATA job. Affected: getMemberInfo, getDescendants, addMetadataMember.
Trust No One — Not Even This Library

API Accuracy Audit & Corrections

Every function has been cross-checked against the official Oracle EPM Groovy Scripting Reference. What follows is a full disclosure of what was wrong, why it was wrong, and exactly how it was fixed.

// "I used to think I understood the system. Then I read the actual docs.
// Turns out, a lot of what people call 'working code' is just code
// that hasn't failed yet in the right way."
// — E. Alderson, debugging at 2am
10Critical Errors
6Structural Issues
31Functions Audited
100%Now Fixed
🔒 CERTIFIED COMPILE-READY · EPM 25.11 · GROOVY 3.x
Critical · API Wrong
getEssbaseCube() — Method Does Not Exist
operation.application.getEssbaseCube(cubeName)
operation.application.getCube(cubeName)
Affected 11 functions. The Application API exposes getCube(String) only. getEssbaseCube has never existed. Silent fail — EPM throws a MissingMethodException at runtime.
// They named it getCube. Simple. Four years of people
// calling getEssbaseCube because it *sounds* right.
// The system doesn't care what sounds right.
Critical · Wrong API
executeBusinessRule() — Not on Cube
getCube(cube).executeBusinessRule(name, rtps)
application.getJobFactory()
  .createRuleJobDefinition(name, rtps)
application.executeJob(jobDef)
Cube has no executeBusinessRule() method. The correct pattern is JobFactory → JobDefinition → executeJob(). Available since 20.06.
// Everyone copies this from Stack Overflow.
// Nobody checks the Javadoc. I checked.
// The method isn't there. It was never there.
Critical · Wrong Method Name
setSubstitutionVariable() / getSubstitutionVariable()
application.setSubstitutionVariable(name, value)
application.getSubstitutionVariable(name)
application.setSubstitutionVariableValue(name, value)
application.getSubstitutionVariableValue(name)
Both Application and Cube require the Value suffix. Without it: HspRuntimeException at runtime. Affects setSubVar, getSubVar, getAllSubVars, bulkSetSubVars.
// The "Value" suffix. Four characters.
// Four characters between a working close process
// and a 2am support call.
Critical · Non-Existent Method
getSubstitutionVariables() — No Bulk List API
application.getSubstitutionVariables()
// returns List — DOES NOT EXIST
// Must query individually by name
application.getSubstitutionVariable(name).getValue()
There is no bulk list method for substitution variables on Application or Cube. The SubstitutionVariable object exposes getName() and getValue(). For a full list, use EPM Automate's listSubstitutionVariables via REST.
// Oracle gave us one variable at a time.
// Like they're rationing trust.
Critical · Wrong Chain
getCube().getDimension() — Wrong Object
getCube(cubeName).getDimension(dimension)
// Cube doesn't have getDimension()
application.getDimension(dimension, cube)
getDimension() lives on Application, not Cube. You pass the Cube as an optional scope argument. Affected getMemberInfo, getDescendants, validateMemberExists.
// Dimensions belong to the application.
// Cubes just use them.
// Like employees and their job descriptions.
Critical · Non-Existent Method
getCube().getOutline() — Does Not Exist
getCube(cubeName).getOutline().getDimension(dim)
application.getDimension(dimension, cube)
Cube has no getOutline() method in the Cloud EPM Groovy API. This is a pattern inherited from on-prem Hyperion Java APIs that were never ported to Cloud. getDescendants and validateMemberExists both contained this.
// On-prem muscle memory in a SaaS world.
// The outline is gone. The method is gone.
// Only the habit remains.
Critical · Syntax Error
throw new throwVetoException() — Double Keyword
throw new throwVetoException("message")
throwVetoException("message")
// it's a function call, not a constructor
throwVetoException() is a plain Groovy function injected by the EPM runtime — not a class you instantiate with new. Using throw new before it is a compile-time syntax error.
// Two keywords. Both wrong.
// The compiler would have told you immediately.
// Nobody ran this. Nobody ran any of this.
Critical · Double Keyword Bug
throw new throw new Exception — Duplicated
throw new throw new Exception("message")
throwVetoException("message")
// or: throw new Exception("message")
Literal copy-paste corruption in validateMemberExists. The Groovy compiler fails immediately on this. Also corrected to use throwVetoException() for proper form UI surfacing.
// This one isn't even subtle.
// This is a system that never ran.
// A ghost function. Documented. Published. Dead on arrival.
Critical · Wrong API
executeDataMap() — Not on Application
application.executeDataMap(mapName, [clearData: flag])
application.getDataMap(mapName).execute(clearTarget)
Application has no executeDataMap() method. The DataMap object (retrieved via getDataMap) exposes execute(Boolean clearData). This is confirmed in Oracle's official DataMap API docs.
// Get the object. Call the method on the object.
// That's it. That's object-oriented programming.
// Not a trick question.
Critical · Method Does Not Exist
executeMdxQuery() / executeCalcScript for MDX CLEAR
cube.executeMdxQuery(mdx)
// MDX "CLEAR DATA" syntax also invalid
cube.executeCalcScript(script)
// FIX(...) CLEARBLOCK ALL; ENDFIX
Cube has no executeMdxQuery(). MDX CLEAR DATA is not valid Groovy calc syntax. BSO clears use executeCalcScript() with a FIX block. ASO uses cube.clearPartialData(mdxRegion, isPhysical).
// Someone mixed up MDX, Groovy, and calc script syntax
// into one function and called it done.
// The oracle (small o) of APIs disagreed.
Warning · Engine 3.x
Date.format() — Broken in Groovy 3.x
new Date().format('yyyy-MM-dd HH:mm:ss')
new java.text.SimpleDateFormat('yyyy-MM-dd HH:mm:ss')
  .format(new Date())
Date.format() was flagged by Oracle's Groovy Validator in releases 25.08–25.09. Temporarily re-enabled in 25.10 for compatibility but will break in the 26.01 engine upgrade. Use SimpleDateFormat now.
// Oracle gave it back in 25.10.
// Then they'll take it again in 26.01.
// This is not stability. This is a lease.
Warning · Engine 3.x
Thread.sleep() — Flagged by Groovy Validator
Thread.sleep(5000)
sleep(5000)
// built-in Groovy function, no class import needed
Thread.sleep() is flagged by the EPM Groovy Script Validator since the 26.01 engine upgrade. The built-in sleep() function is the correct replacement — it's a Groovy DSL method, no import required.
// The validator flags Thread.sleep.
// The runtime still runs it. For now.
// "For now" is not a deployment strategy.
Warning · Corrupted Source
while (elapsed 1000) — Missing Operator
while (elapsed 1000) { // operator stripped
while (elapsed < maxWait * 1000) {
The less-than operator was stripped when the HTML was rendered without encoding. The file stored < but the browser displayed it as a tag boundary, breaking the condition. Fixed by encoding as &lt; in the HTML source.
// The less-than sign. Casualty of an HTML encoder
// that thought it knew better than the developer.
// Machines making assumptions. Classic.
Warning · Untyped Params
All 31 Functions — Untyped Parameters
def runBusinessRule(ruleName, cubeName, rtps) {
def runBusinessRule(String ruleName, Map rtps = [:]) {
Groovy's dynamic typing allows untyped params, but EPM's Groovy 3.x engine enforces stricter type resolution. Explicit String, Map, Boolean, Integer, List, Closure and Double types prevent runtime type-coercion errors and make the Groovy Validator happy.
// "def x" means anything. In production EPM,
// anything eventually becomes the wrong thing.
// Type your parameters. Name your enemies.
Warning · Wrong Context
buildCalcScript() Ran via Wrong Method
getCube(cubeName).executeBusinessRule(script, [:])
getCube(cubeName).executeCalcScript(script)
A dynamically-built calc script string must be executed via executeCalcScript(). executeBusinessRule() takes a rule name, not a script body. The two APIs serve completely different purposes.
// A script is not a rule name.
// executeBusinessRule takes a name.
// executeCalcScript takes a script.
// Read the method signature. All of it.
Fixed · Correct
What Was Already Right
These were correct throughout:

✅  application.getConnection(name)
✅  application.getDataMap(name).execute()
✅  application.getDimension(name)
✅  cube.executeCalcScript(script)
✅  groovy.json.JsonSlurper()
✅  operation.grid (form context access)
✅  println() (correct console output method)
✅  throwVetoException() call form
✅  REST endpoint /aif/rest/V1/jobs
✅  DataMap.execute(clearTarget)
✅  rtps.PromptName pattern
// Give credit where it's due.
// Some things were right.
// Not many. But some.
// Final note:
// "I don't enjoy finding bugs in other people's code.
// I enjoy understanding why the bug existed in the first place.
// In this case: the docs exist, the Javadoc is public,
// and still — people copy patterns that sound right over ones that are right.
// That's not a code problem. That's a trust problem.
// Trust the docs. Read them yourself. Every single line."
// — E. Alderson
Performance · 2026 Readiness · Validator Guide

Optimize, Benchmark & Stay 26.05-Ready

Three things every EPM Groovy practitioner needs in 2026: fast rules, accurate benchmarks, and a clean Validator report before the May 26.05 mandatory engine upgrade.

// "The system is about to change. Oracle changed the engine. You change your code — or the engine changes it for you."
Mandatory Groovy Engine Upgrade — 26.05 (May 2026)
Oracle is enforcing a stricter Groovy/Java 3.x engine in the 26.05 update. Scripts using heavy def, missing /*RTPS:*/ declarations, or implicit type casts will fail. Run the Script Validator now in your Test environment — you have until May.
Application → Overview → Actions → Groovy Script Validator Delay up to 3 months via EPM Automate skipUpdate
Performance
10 Optimization Patterns
① Explicit Typing — Biggest Single Win
def forces dynamic dispatch on every call. Replace with String, Map<String,Object>, List<String>, Double.
// ✗ Slower — dynamic
def getSubstitutionVariable(varName) { ... }

// ✓ Faster — static dispatch
String getSubstitutionVariable(String varName) { ... }
② Target Only Changed Data
Traditional rules calc the entire cube. Use operation.grid, getFormPOV(), and iterateGridCells() with tight filters. Rules that took minutes drop to seconds.
③ Dynamic FIX Blocks over CALC ALL
Use buildCalcScript() to generate a tight FIX(...) block scoped to only the POV members in play. Fewer members in FIX = proportionally faster execution.
④ Minimize GC Pressure
Reuse one JsonSlurper instance. Avoid new collections in hot loops. Use primitives (int, double) where possible. Keep closures in retryWithBackoff lightweight.
⑤–⑩ More Quick Wins
✦ Cache substitution vars & member info across calls
✦ Batch with bulkSetSubstitutionVariables()
✦ Use sleep() not Thread.sleep() (26.01+ validator)
✦ Poll with reasonable intervals in awaitJobCompletion
✦ Push large descendant processing into Essbase calc scripts
✦ Use runSmartPush only on changed regions
Benchmarking
Measure Before You Optimize
EPM Cloud is sandboxed — no JMH, no profiler. Use System.nanoTime() (monotonic, nanosecond resolution). Never currentTimeMillis() for precision — clock skew makes it unreliable.
Reusable Benchmark Helper
def benchmark(String label, Closure action) {
  long start = System.nanoTime()
  def result = action.call()
  long ms = (System.nanoTime() - start) / 1_000_000
  println("[BENCH] ${label}: ${ms} ms")
  return result
}
Warm-up + Multi-run Average
def warmBenchmark(String label,
                    Closure code,
                    int runs = 10) {
  // Discard first 3 — JVM warmup noise
  3.times { code() }
  long total = 0
  runs.times {
    long t = System.nanoTime()
    code()
    total += System.nanoTime() - t
  }
  println("[BENCH] ${label} avg (${runs}x): " +
    "${total / runs / 1_000_000} ms")
}
High-Impact Areas to Benchmark
iterateGridCells — filter tightness is everything
getDescendants on large dimensions
buildCalcScript generation + execution
REST/job calls — Groovy wrapper vs external latency
def vs typed — before/after explicit typing
// "You can't optimize what you don't measure.
// You can't trust what you didn't warm up first."
2026 Compliance
Validator — 6-Step Runbook
01
Log in as Service Administrator in your Test environment first. Never run fixes in Production without a verified Test pass.
02
Navigate to Application → Overview → Actions → Groovy Script Validator. Appears only if at least one Groovy rule exists.
03
Monitor the job under Navigator → Jobs. Takes 1–5 minutes depending on rule count.
04
Download GroovyValidationReport.html from Inbox/Outbox Explorer. Open in browser — each issue shows rule name, line number, and fix hint.
05
Fix in small batches. Re-run validator after each batch. Test rules manually — a clean report ≠ correct business logic.
06
Repeat until the report shows zero issues. Then promote to Production ahead of the May 26.05 window.
Top 5 Errors the Validator Flags
Missing /*RTPS: */
Every script must start with /*RTPS: */ — even with zero prompts.
Object assigned to typed var
Fix: Double val = cell.data as Double
BigDecimal / float literals
Append d suffix: cell.data = 123.45d
List vs Array mismatch
Fix: List<String> m = getDescendants(...) or cast as String[]
Thread.sleep() flagged
Replace with built-in sleep(ms)
// "The validator doesn't auto-fix anything.
// It just shows you the truth.
// What you do with it is on you."
2026 Compliance Checklist — Apply to All 31 Functions
Work through this list once. Re-run the validator after each step.
// Progress is persistent — checkboxes are saved during your session.
🧬
Part of the BrainSpring EPM Learning Ecosystem

This Groovy library complements the Data Integration Course, Planning Quest, FCCS Tour, and EPCM Study Tour. Free, open, no login. If it helped you ship something, share it with someone who needs it.

// "The best hack is the one you give away." — E. Alderson