Interface ExpressionLanguage
- All Known Implementing Classes:
MvelExpressionLanguage
The engine compiles each rule list with a new ExpressionCompiler, which keeps whatever the language caches
for that list. The engine enforces the rest of the rule contract itself, whatever the language: rule names are
unique, conditions and actions aren't blank, rules run in priority order, a condition evaluates to a
Boolean, no fact has a blank name or a name one of the engine's languages reserves (see
reservedFactNames() and reservesForEveryRuleList()), and failures are reported as
RuleCompilationException or
RuleExecutionException and to listeners.
Implementations must be thread-safe: an engine can compile rule lists on several threads.
Implemented by expression languages. A method added to this interface in a later 2.x release is a
default method, so an existing language keeps compiling and working.
- See Also:
-
Method Summary
Modifier and TypeMethodDescriptionname()Returns the language's name, such as"mvel".newCompiler(CompileContext context) Creates the compiler for one rule list.default voidprepare()Initializes the classes with a static initializer that the language's first compile or run would otherwise be the first to use: the language's own and those of any library it runs on.Returns the fact names this language reserves for itself, such as the name its actions see the output object by.default booleanTells whether the namesreservedFactNames()returns are reserved for every rule list the engine loads, or only for a rule list that uses this language.
-
Method Details
-
name
String name()Returns the language's name, such as"mvel". It must return the same name every time. The engine reads it once, when the language is given to the builder or found withServiceLoader, and knows the language by that name from then on.- Returns:
- The name, never
nullor blank
-
newCompiler
Creates the compiler for one rule list.- Parameters:
context- The imports and class loader the rule list is compiled with- Returns:
- A new compiler, used for this rule list only
-
prepare
default void prepare()Initializes the classes with a static initializer that the language's first compile or run would otherwise be the first to use: the language's own and those of any library it runs on. A rule list may be loaded, or a rule run, deep in another run's stack, for example from an action, and aStackOverflowErrorinside a class's static initializer leaves the class unusable for the life of the JVM: every later use of it, by any engine, throwsNoClassDefFoundError. Initializing them here leaves a later load or run nothing to initialize. A warm-up that runs the library once, and so may itself overflow deep in a stack, should skip itself only for whatThrowables.isOverflow(java.lang.Throwable)reads as an overflow, which such aNoClassDefFoundErrorisn't, and rethrow anything else, a fatal error (Throwables.isFatal(java.lang.Throwable)) included.The engine calls it before anything is compiled with the language, having first checked that the thread has room on its stack for it if no engine has prepared a language of the same class yet. It calls it while an engine is built for each language the builder names, by giving it, naming it the default, or giving it options or imports, on every build of such an engine. It calls it for another language the engine has, one found with
ServiceLoader, even the only one found, which is then the default language, when a rule list first uses it, once for the language's class, unless several rule lists first use it at the same time. No lock is taken around these calls, so engines built, or rule lists first loaded or validated, on several threads at once may call it at the same time. So it must be safe to call concurrently, and cheap once it has done its work, as initializing a class that is already initialized is. It should do nothing else, but for running its own library's public API once, such as evaluating a trivial expression, so that a first run deep in a stack has fewer classes to load: it creates no state an engine uses, and may be called on a language that never compiles anything. Anything it throws while an engine is built fails the build, unchanged; anything it throws when a rule list first uses the language fails the language for that rule list, as whatnewCompiler(CompileContext)throws does, reported as the language failing to prepare, and the language's next use prepares it again. By default it does nothing. -
reservedFactNames
Returns the fact names this language reserves for itself, such as the name its actions see the output object by. A run's facts reach every rule, so by default the engine rejects a name that any of its languages reserves, whatever language the rules are written in:build()rejects a fact declared with one, andrun()a fact supplied with one, before any language checks the name withExpressionCompiler.checkFactName(String). A language whosereservesForEveryRuleList()returnsfalsehas its names rejected only for a rule list that uses it, as that method describes.The engine calls it once, when it's built, for every language it has, those found with
ServiceLoaderincluded, before the engine prepares any language and whether or not a rule list ever uses the language. So it must not needprepare()to have been called, and should initialize no class of the language's: return a constant. The engine keeps a copy of the set, so a later change to it changes nothing. It must return the same names every time, beforeprepare()and after. By default, "output", so a language written before this method existed keeps the engine's earlier rule; a language that binds the output object to no fact name can return an empty set. A language whose expressions bind a name to something other than a fact, such asoutputto the output object, or a name to a context or helper object of its own, must return that name, or reject it inExpressionCompiler.checkFactName(String): otherwise the engine accepts a fact by that name, and a rule reads whichever of the two the language finds first.- Returns:
- The names, never
nulland holding nonull; an empty set if the language reserves none
-
reservesForEveryRuleList
default boolean reservesForEveryRuleList()Tells whether the namesreservedFactNames()returns are reserved for every rule list the engine loads, or only for a rule list that uses this language. By default,true: no fact may have one of the names, whatever language the rules are written in, so a language that reserves a name fails the runs that supply a fact by that name to rules written in another, even while no rule is written in it.A language that returns
falsehas its names rejected only where a rule of its own could see the fact.run()rejects a fact supplied with one only when the rule list the run uses has a rule in the language, as it asks only those languages tocheck a name; and a fact declared with one is rejected when such a rule list is compiled, rather than bybuild():load()throws aRuleCompilationExceptionfor it, andvalidate()returns one. A rule list without rules is the default language's. For such a rule list, the names are still rejected for every fact, whatever rules read it (seeExpressionCompiler.factNamesRead()), before any language checks the name, and with the same message. A run rejects them after the engine's own checks of its facts, that each value is of the type its fact was declared with and, withrequireDeclaredFacts(), that it supplied every declared fact and nothing else; names reserved for every rule list are rejected before those checks.The engine calls it once, when it's built, for every language it has, as it calls
reservedFactNames(), so it must not needprepare()to have been called either: return a constant. Anything it throws fails the build, unchanged.- Returns:
trueif the names are reserved for every rule list,falseif only for the rule lists that use this language
-