Skip to content

Rules Java based Rule Structure

sgilda edited this page Sep 3, 2015 · 26 revisions

Java-based Rule Structure

RuleProvider

Windup rules are based on OCPsoft Rewrite, an open source routing and URL rewriting solution for Servlets, Java Web Frameworks, and Java EE. The rewrite framework allows you to create rule providers and rules in an easy to read format.

Windup rule add-ons can implement the RuleProvider interface or they can extend the AbstractRuleProvider class, the SingleRuleProvider class, or the IteratingRuleProvider class.

The following is an example of a simple Java-based rule add-on.

public class ExampleRuleProvider extends AbstractRuleProvider
{
    @Override public RulePhase getPhase(){
        return RulePhase.DISCOVERY;
    }

    // @formatter:off
    @Override
    public Configuration getConfiguration(GraphContext context)
    {
        return ConfigurationBuilder.begin()
        .addRule()
        .when(
            // Some implementation of GraphCondition.
            Query.find(...)....
        )
       .perform(
            ...
       );
    }
    // @formatter:on
    // (@formatter:off/on prevents Eclipse from formatting the rules.)
}

Add Rule Code Structure

As mentioned above, individual rules are added to a ruleset in the getConfiguration(GraphContext context) method using the OCPsoft Rewrite ConfigurationBuilder class.

Like most rule-based frameworks, Windup rules consist of the following:

  • Condition: This is the when(condition) that determines if the rule should execute.

  • Operation: This defines what to perform() if the condition is met.

  • Otherwise: The when(condition) is not met

  • Operation: This defines what to perform() if the condition is not met.

Rules must define the condition, or when, and an operation, or perform. However, the otherwise and remainder are optional.

when()
.when(Query.fromType(XmlMetaFacetModel.class))

The .when() clause of a rule typically queries the graph using the Query API. Results of the query are put on variables stack (Variables), many times indirectly through the querying API.

The .when() clause can also subclass GraphCondition. The Query class extends GraphCondition and is a convenient way to create a condition. You can also use multiple conditions within one when() call using and().

Example:

.when(Query.fromType(XmlMetaFacetModel.class).and(Query...))

One last but important feature is the ability to use Gremlin queries. See the Gremlin Documentation reference manual for more information.

perform()
.perform(
   new AbstractIterationOperation<XmlMetaFacetModel>(XmlMetaFacetModel.class, "xml")
   {
      public void perform(GraphRewrite event, EvaluationContext context, XmlMetaFacetModel model)
      {
         // for each model, do something
      }
   }
)

The .perform() clause of the rule typically iterates over the items of interest, such as Java and XML files and querying services, processes them, and writes the findings into the graph.

For that, various operations are available, which are subclasses of GraphOperation. You can also implement your own operations.

There are several convenient implementations for constructs like iteration (Iteration).

Iteration
.perform(
    Iteration.over(XmlMetaFacetModel.class, "xmlModels").as("xml")
    .when(...)
    .perform(
        new AbstractIterationOperation<XmlMetaFacetModel>(XmlMetaFacetModel.class, "xml"){
            public void perform(GraphRewrite event, EvaluationContext context, XmlMetaFacetModel xmlFacetModel)
            {
            }
        })
    .otherwise(
        new AbstractIterationOperation<XmlMetaFacetModel>(XmlMetaFacetModel.class, "xml"){
            public void perform(GraphRewrite event, EvaluationContext context, XmlMetaFacetModel payload)
                { ... }
        })
    .endIteration()
Nested iterations

An iteration is also an operation, so anywhere an operation is expected, you can use the Iteration. If the Iteration is placed within the perform method of another Iteration, it is called nested iteration.

The following is an example of a nested iteration.

.addRule()
     .when(...)
     .perform(
     Iteration.over("list_variable").as("single_var")
       .perform(
         Iteration.over("single_var") //second iteration
           .perform(...).endIteration()
     )
     .endIteration()
);
otherwise

As previously mentioned, Windup rules are based on OCPsoft Rewrite. The .otherwise() clause allows you to perform something if the condition specified in .when() clause is not matched. For more information, see OCP Rewrite web.

The following is an example of an otherwise operation.

.otherwise(
   new AbstractIterationOperation<XmlMetaFacetModel>(XmlMetaFacetModel.class, "xml")
   {
      public void perform(GraphRewrite event, EvaluationContext context, XmlMetaFacetModel model)
      {
         // for each model, do something altenate
      }
   }
)
Where

The where() clause is used to provide information about used parameters within the rule. So for example if you have used a parameter in some condition like for example JavaClass.references("{myMatch}"), you may use the where clause to specify what the myMatch is like .where("myMatch").matches("java.lang.String.toString\(.*\)").

The following is an example

.when(JavaClass.references("{myMatch}").at(TypeReferenceLocation.METHOD))
.perform(...)
.where("myMatch").matches("java.lang.String.toString\(.*\)")

+ Please note that within the where clause the regex is used in contrast to JavaClass.references() where a windup specific syntax is expected.

Metadata

Rules can specify metadata. Currently, the only appearing in some rules, and not actually used, is RuleMetadata.CATEGORY.

.withMetadata(RuleMetadata.CATEGORY, "Basic")

.withMetadata() is basically putting key/value to a Map<Object, Object>.

Available utilities

For a list of what key services and constructs can be used in the rule, see Available Rules Utilities.

Variable stack

The communication between the conditions and operations is done using the variable stack that is filled with the output of the condition/s and then given to the Iteration to be processed. Within conditions, you can specify the name of the result iterable that is saved in the stack using as() method, the iteration can specify the iterable to iterate over using the over() method and even specify the name of for each processed single model of the result being processed. Example:

.addRule()
     .when(Query...as("result_list"))
     .perform(
     Iteration.over("result_list").as("single_var")
          ...
     )
);

The varstack may be accesed even from the second condition in order to narrow the result of the previous one. After that the iteration may choose which result it wants to iterate over (it is even possible to have multiple iterations listed in the perform, each of which may access different result saved in the variable stack).

.addRule()
     .when(Query...as("result_list").and(Query.from("result_list")....as("second_result_list")))
     .perform(
     Iteration.over("second_result_list")
          ...
     )
);
Clone this wiki locally