Annotation Interface AuditedConditional
Audited. Method-level annotation that asks
AuditedAspect to emit an audit event of the declared
AuditEventType only when the when() SpEL predicate
evaluates to true on the post-invocation context.
Motivated by AUDIT_PHASE_C_RECCE.md bucket 2c: a cluster of
imperative auditTrailService.addUpdateEvent(...) callers sit
behind an early-return guard (e.g.
if ( dimension.getCellTypeAssignments().stream().noneMatch( CellTypeAssignment::isPreferred ) ) {
return PreferredCellTypeAssignmentChangeOutcome.UNCHANGED;
}
...
auditTrailService.addUpdateEvent( ee, FooEvent.class, "..." );
Plain @Audited would unconditionally fire on every successful
return, which is wrong (the guard branch is a no-op). This annotation
lets the aspect skip the no-op branch by evaluating a SpEL predicate
against the same context used for Audited.messageSpel():
method arguments resolve by name, the return value is exposed as
#result, and positional access via #root.args[i] also
works.
Common predicate shapes:
when = "!#result.isEmpty()"— fire only when a collection-returning method actually produced something.when = "#result.name() != 'UNCHANGED'"— fire only when an enum-returning outcome method actually changed state.when = "#result > 0"— fire only when a count-returning method did non-zero work.when = "!#toRemove.isEmpty() || !#toAdd.isEmpty()"— fire only when the input deltas would actually result in a change (parameters resolved by name).
Semantics:
@AfterReturningonly — throwing methods record nothing (same asAudited).- If the
when()SpEL fails to evaluate or returnsnull, no audit row is written and the failure is logged atERROR. This is the safe default: when the predicate is unclear, err on the side of not emitting (no false positives in the audit log). Contrast withAudited.messageSpel(), where a SpEL failure falls back toAudited.message()because dropping the row would be worse than a bad note. - Otherwise the emission path is identical to
Audited: locate the firstAuditableargument, optionalAuditEventPayload, resolve note viamessage()/messageSpel(), delegate toAuditTrailService.addUpdateEventWithPayload, publish anAuditedEvent.
A method may carry either Audited or @AuditedConditional
but not both — the aspect's two @AfterReturning advices match on
the respective annotation and would otherwise double-fire.
-
Optional Element Summary
Optional ElementsModifier and TypeOptional ElementDescriptionOptional plain (non-SpEL) note string.Optional SpEL expression for the note, evaluated only whenwhen()istrue.Class<? extends AuditEventType> The concreteAuditEventTypesubclass to record.Optional SpEL expression that resolves to aClass<? extends AuditEventType>at runtime.SpEL predicate evaluated against the post-invocation context.
-
Element Details
-
value
Class<? extends AuditEventType> valueThe concreteAuditEventTypesubclass to record. Must be instantiable via a public no-arg constructor.May be left at the default (
AuditEventType.class — the abstract base) whenvalueSpel()chooses the event class at runtime. SeeAudited.value()for the parallel rule.- Default:
ubic.gemma.model.common.auditAndSecurity.eventType.AuditEventType.class
-
valueSpel
String valueSpelOptional SpEL expression that resolves to aClass<? extends AuditEventType>at runtime. MirrorsAudited.valueSpel(): when set, it overridesvalue()(with a WARN if both are non-default). When evaluation fails or resolves tonull/non-Class, the aspect falls back tovalue(); ifvalue()is also at the default the audit row is skipped.- Default:
""
-
when
String whenSpEL predicate evaluated against the post-invocation context. The audit row is emitted only when this expression evaluates toBoolean.TRUE.The evaluation context is identical to
Audited.messageSpel(): method parameters resolve by name (e.g.#ee,#dimension) — requires-parameterscompile flag, enabled project-wide in the parent pom; positional access via#root.args[i]; the method's return value is exposed as#result.The default
"true"makes the annotation a strict superset ofAudited(an always-fire conditional). Practical usage will always supply a non-trivial predicate.- Default:
"true"
-
message
String messageOptional plain (non-SpEL) note string. Stored verbatim inAUDIT_EVENT.NOTE. Empty default = no note. SeeAudited.message().- Default:
""
-
messageSpel
String messageSpelOptional SpEL expression for the note, evaluated only whenwhen()istrue. Semantics matchAudited.messageSpel(): failure to evaluate falls back tomessage(); the audit row is still written. (Thewhenpredicate is the only gate on emission.)- Default:
""
-