Interface ExpressionLanguage

All Known Implementing Classes:
MvelExpressionLanguage

public interface ExpressionLanguage
A language that rule conditions and actions are written in, such as MVEL.

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 Type
    Method
    Description
    Returns the language's name, such as "mvel".
    Creates the compiler for one rule list.
    default void
    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.
    default Set<String>
    Returns the fact names this language reserves for itself, such as the name its actions see the output object by.
    default boolean
    Tells whether the names reservedFactNames() 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 with ServiceLoader, and knows the language by that name from then on.
      Returns:
      The name, never null or blank
    • newCompiler

      ExpressionCompiler newCompiler(CompileContext context)
      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 a StackOverflowError inside a class's static initializer leaves the class unusable for the life of the JVM: every later use of it, by any engine, throws NoClassDefFoundError. 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 what Throwables.isOverflow(java.lang.Throwable) reads as an overflow, which such a NoClassDefFoundError isn'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 what newCompiler(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

      default Set<String> 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, and run() a fact supplied with one, before any language checks the name with ExpressionCompiler.checkFactName(String). A language whose reservesForEveryRuleList() returns false has 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 ServiceLoader included, before the engine prepares any language and whether or not a rule list ever uses the language. So it must not need prepare() 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, before prepare() 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 as output to the output object, or a name to a context or helper object of its own, must return that name, or reject it in ExpressionCompiler.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 null and holding no null; an empty set if the language reserves none
    • reservesForEveryRuleList

      default boolean reservesForEveryRuleList()
      Tells whether the names reservedFactNames() 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 false has 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 to check a name; and a fact declared with one is rejected when such a rule list is compiled, rather than by build(): load() throws a RuleCompilationException for it, and validate() 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 (see ExpressionCompiler.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, with requireDeclaredFacts(), 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 need prepare() to have been called either: return a constant. Anything it throws fails the build, unchanged.

      Returns:
      true if the names are reserved for every rule list, false if only for the rule lists that use this language