-
Notifications
You must be signed in to change notification settings - Fork 128
Rules Java based Rule Structure
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.
-
If the rule should run in a phase other than the default MIGRATION_PHASE, you must implement the getPhase() method and specify in which Windup lifecycle phase the rule should be executed. For more information about rule phases, see Rule Execution Lifecycle.
-
Rules are added in the getConfiguration(GraphContext context) method. This method is inherited from the OCPsoft Rewrite interface org.ocpsoft.rewrite.config.ConfigurationProvider. Rules are discussed in more detail below under Add Rule Code Structure
-
If other rules must execute after or before the rules in this provider, you must provide the list of RuleProvider classes using the getExecuteBefore() or http://windup.github.io/windup/docs/latest/javadoc/org/jboss/windup/config/RuleProvider.html#getExecuteAfter%28%29getExecuteAfter()* methods.
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.)
}
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(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(
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
).
.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()
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()
);
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
}
}
)
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.
For a list of what key services and constructs can be used in the rule, see Available Rules Utilities.
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")
...
)
);