Interface ExpressionCompiler
- All Superinterfaces:
AutoCloseable
RulesEngine.load(java.util.List) calls the compile methods on one
thread, and then, for an engine built with
copiesAtLoad(n), newSession()
and warmUp(Session) on the same thread. checkFactName(String), newSession() and
convertOutput(Object, Class) are also called by runs of the rule list, possibly on many threads at once, so
they must be thread-safe.
The engine closes the compiler once it no longer needs the rule list, after closing every session the compiler
created: when a later load() has replaced the rule list and no run is still using it, when the engine
is closed, or when the rule list fails to load.
Implemented by expression languages. A method added to this interface is a default method, so an
existing language keeps compiling and working.
- See Also:
-
Method Summary
Modifier and TypeMethodDescriptiondefault voidcheckFactName(String name) Rejects the name of a fact that rules written in this language couldn't refer to, such as a keyword of the language.default voidclose()Releases what the compiler holds.compileAction(Expression source) Compiles an action.compileCondition(Expression source) Compiles a condition.default @Nullable ObjectconvertOutput(Object value, Class<?> target) Converts a value an action returned withActionResult.set(java.util.Map)to the type of a setter of the output object, as this language understands its own values, such as aDouble3.0, which a language whose numbers are all doubles computes for1 + 2, to theInteger3 for a setter taking anint.Returns the names of the facts the conditions and actions this compiler compiled can read, ornullif it can't tell.Creates a session for one copy of the rule list: the state this language's conditions and actions change while they run.default voidPrepares a new session before any run uses it, such as by compiling this compiler's expressions into it, so that the runs that use it first don't pay for that.
-
Method Details
-
compileCondition
Compiles a condition. A condition must evaluate to aBoolean, and can't change facts or declare variables; a compiler that can see such a write rejects the condition here.- Parameters:
source- The condition: the name of its rule,CONDITION, and its text, which isn't blank- Returns:
- The compiled condition, which every run of the rule list shares. Keep what changes while it runs in a
Session. - Throws:
InvalidExpressionException- if the condition breaks a rule the engine enforces, such as assigning to a fact, or has an error the language can point to, such as a syntax error; its issues say where. Anything else is reported as the cause of aRuleCompilationExceptiontoo.
-
compileAction
Compiles an action. When it runs, the action either changes the output object,ActionContext.output(), which a language may bind to a name such as "output", and returnsActionResult.done(), or returnsActionResult.set(java.util.Map)with the properties for the engine to set on the output object.- Parameters:
source- The action: the name of its rule,ACTION, and its text, which isn't blank- Returns:
- The compiled action, which every run of the rule list shares. Keep what changes while it runs in a
Session. - Throws:
InvalidExpressionException- if the action breaks a rule the engine enforces, or has an error the language can point to, such as a syntax error; its issues say where. Anything else is reported as the cause of aRuleCompilationExceptiontoo.
-
newSession
Session newSession()Creates a session for one copy of the rule list: the state this language's conditions and actions change while they run. Every condition and action this compiler compiled is given the session of the run that evaluates it.- Returns:
- A new session, or
Session.none()if the compiled expressions keep no state between runs and are safe to run on several threads at once. Throwing or returningnullfails the run that needed the session with aRuleExecutionException.
-
warmUp
Prepares a new session before any run uses it, such as by compiling this compiler's expressions into it, so that the runs that use it first don't pay for that. By default, does nothing: a session prepares itself the first time each expression runs, whatever that costs.RulesEngine.load(java.util.List)calls it on its own thread, once for each session it creates withnewSession()for a copy made when the rules load (RulesEngineBuilder.copiesAtLoad(int)), after compiling every expression and before any run uses the session. It isn't called forSession.none(), or for a session created during a run, which prepares itself as it's used.- Parameters:
session- A session this compiler'snewSession()returned, which no run has used- Throws:
Exception- if the session can't be prepared. It failsload()with aRuleCompilationExceptionnaming the language, and the rules loaded before stay loaded, except a fatalError, which is rethrown unchanged.
-
checkFactName
Rejects the name of a fact that rules written in this language couldn't refer to, such as a keyword of the language. The engine has already rejectednulland blank names, and the names its languages reserve (seeExpressionLanguage.reservedFactNames()). By default, every other name is accepted.The engine calls it for every fact of a run, and every declared fact, unless
factNamesRead()returns a set: then only for the facts named in it.- Parameters:
name- The fact's name- Throws:
IllegalArgumentException- if rules can't refer to a fact with this name;run()throws it as is, unless a fatalErroris among its causes or their suppressed exceptions. Anything else this method throws is logged and thrown fromrun()as anIllegalArgumentExceptionnaming the fact and the language, except a fatalError, thrown, among the causes of what this method throws or suppressed on one of them, which is rethrown unchanged.
-
factNamesRead
Returns the names of the facts the conditions and actions this compiler compiled can read, ornullif it can't tell. By default,null, and the engine callscheckFactName(String)for every fact, as a fact reaches every rule.When it returns a set, the engine calls
checkFactName(String)only for the facts named in it: a fact that no rule of this compiler reads is never checked by this language, so a keyword of this language can still name a fact that only another language's rules read. It narrows nothing else: the names a language reserves (seeExpressionLanguage.reservedFactNames()) are still rejected for every fact. A name the set holds that is no fact's changes nothing, so a compiler that can tell only roughly returns more names, never fewer: a fact left out is one a rule may read under a name this language can't refer to, and no check says so. A compiler that can't tell for some expression, such as one that reads facts by a name it computes, returnsnull.RulesEngine.load(java.util.List)andRulesEngine.validate(java.util.List)call it once, on their own thread, after every rule of the list has compiled, and before they check the declared facts; not when a rule failed to compile. The engine keeps a copy of the set, so a later change to it changes nothing.- Returns:
- The names, holding no
null, ornullif this compiler can't tell which facts its rules read. Throwing, or returning a set that holdsnull, failsload()with aRuleCompilationExceptionnaming the language, except a fatalError, which is rethrown unchanged.
-
convertOutput
Converts a value an action returned withActionResult.set(java.util.Map)to the type of a setter of the output object, as this language understands its own values, such as aDouble3.0, which a language whose numbers are all doubles computes for1 + 2, to theInteger3 for a setter taking anint. By default, returns the value unchanged: no conversion, so the writer sets only what it takes as it is.The default
OutputWriter,OutputWriter.beansAndMaps(), calls it only when no setter of the property takes the value as it is, boxed primitives widened as Java widens them, and then for each type a setter takes, the most specific setter first, until one takes what this returns: a value the target accepts as it is, or one it accepts widened. A primitive target, such asint.class, takes its wrapper. Return the value unchanged for a target this language has no conversion for, such as a type it doesn't know: the writer tries the next setter, and fails the write as it does without a conversion, with the message that names the setters, when none takes the value. Throw anIllegalArgumentExceptionnaming the value and the target to refuse a conversion that would change the value, such as a fraction,NaN, an infinity or a number too large for anint: the writer still tries the property's other setters, and when none takes the value, it fails the write with the first refusal, which fails the rule naming it and the property. Anything else it throws fails the write at once. A writer of an application's own gets it as anOutputConverter, and may call it as it likes, or not at all. It's never called withnull, which every setter taking a reference accepts.A language whose numbers are doubles converts a finite, whole
Doublewithin the target's range to anint, ashort, abyteor their wrappers, and one whose magnitude is at most 253 to alongor aLong, where every whole double is exact, and refuses every other double. The engine calls it on the thread running the rule, possibly on many threads at once, so it must be thread-safe.- Parameters:
value- The value an action returned, notnulltarget- The type of a setter's parameter, possibly primitive- Returns:
- The converted value, which the target accepts, or
valuewhere this language has no conversion for the target.nullis a value too, which the first setter taking a reference takes, so return it only to set the property tonull, never for a conversion that isn't there. - Throws:
IllegalArgumentException- if this language refuses to convert the value to the target; its message should name the value and the target, as the rule's failure quotes it
-
close
default void close()Releases what the compiler holds. The engine calls it once, after closing every session the compiler created. By default, does nothing. Anything it throws is logged at WARN, unless arun()orload()it started logged it already, and the engine still closes the other compilers. Only a fatalErroris then rethrown, unchanged, unless a fatal error of the call's own came first. AnInterruptedExceptionin the cause chain of what it throws, or suppressed there, puts back the interrupt status of the thread that closes it, which can be a run's thread, for example when a rule's action callsload()orclose().- Specified by:
closein interfaceAutoCloseable
-