Create a custom step for rules
This topic covers the creation of rules in Deploy, which define the steps to be included in a deployment plan. Each rule in the xl-rules.xml
file specifies a number of steps to add to the deployment plan.
The available step primitives determine what kind of steps can be used. A step primitive is a definition of a piece of functionality that Deploy may execute as part of the deployment plan. For more information about Deploy rules, see Getting started with Deploy rules.
Deploy and its plugins include predefined steps such as noop
and os-script
. You can define custom deployment step primitives in Java. To create a custom step that is available for rules, you must declare its name and parameters by providing annotations.
Authoring a step primitive
For Deploy to recognize your class as a step primitive:
- It must implement the Java interface
com.xebialabs.deployit.plugin.api.flow.Step
. - It must be annotated with
@com.xebialabs.deployit.plugin.api.rules.StepMetadata(name = "step-name")
. - It must have a non-parameterized constructor.
The step-name
you assign in the annotation will be used as the XML tag name. Ensure that it is XML-compatible.
Example: With the following Java code, you can use the UsefulStep
class by specifying my-nifty-step
inside your xl-rules.xml
:
@StepMetadata(name = "my-nifty-step")
class UsefulStep implements Step {
...
}
Your XML file:
<?xml ... ?>
<rules ...>
<rule ...>
<conditions>...</conditions>
<steps>
<my-nifty-step>
...
</my-nifty-step>
</steps>
</rule>
</rules>
You can parameterize your step primitives with parameters that are required, optional, and/or auto-calculated.
Deploy supports String
class and all Java primitives, including int
and boolean
and so on.
Using the Step
interface
Deploy uses the com.xebialabs.deployit.plugin.api.flow.Step
interface to determine:
- At what order the step should be executed
- The description of the step that should appear in the deployment plan
- What actions to execute for the step
For this, the Step
interface declares these methods:
int getOrder();
String getDescription();
StepExitCode execute(ExecutionContext ctx) throws Exception;
The execute
method is where you define the business logic for your step primitive. The ExecutionContext
that is passed in allows you to access the repository using the credentials of the user executing the deployment plan.
Your implementation returns a StepExitCode
to indicate if the execution of the step was successful.
For more information about Step
, see Javadoc.
Defining parameters in a step primitive
Deploy has a dependency injection mechanism that allows values from xl-rules.xml
to be injected into your class. This is how you can set the step description or other parameters using XML.
To receive values from a rule, define a field in your class and annotate it with the @com.xebialabs.deployit.plugin.api.rules.StepParameter
annotation. This annotation has the following attributes:
Attribute | Description |
---|---|
name | Defines the XML tag name of the parameter. Camel-case names (such as myParam ) are represented with dashes in XML (my-param ) or underscores in Jython (my_param=... ). The content of the resulting XML tags are interpreted as Jython expressions and must result in a value of the type of the private field. |
required | Controls whether Deploy verifies that the parameter contains a value after the post-construct logic has run. Note: Setting required=true does not imply that the parameter must be set from within the rules XML. You can use the post-construct logic to provide a default value. |
calculated | Indicates that a value can be automatically calculated in the step's post-construct logic. The setting does not influence the behavior of the step parameter or of the step itself. |
description | Use this to provide a description of the step parameter. Example: You can use this description to automatically generate documentation. It does not influence the behavior of the step parameter or of the step itself. |
Example: The manual
step primitive has:
@StepParameter(name = "freemarkerContext", description = "Dictionary that contains all values available in the template", required = false, calculated = true)
private Map<String, Object> vars = new HashMap<>();
The following XML sets the value of the vars
field:
<?xml ... ?>
<rules ...>
<rule ...>
<conditions>...</conditions>
<steps>
<manual>
...
<freemarker-context>...</freemarker-context>
...
</manual>
</steps>
</rule>
</rules>
For more information about StepParameter
, see the Javadoc.
Implementing post-construct logic
You can add additional logic to your step that will be executed after all field values have been injected into your step. This logic may include defining or calculating default parameters of your step, applying complex validations, and so on.
To define post-construct logic:
- Define a method with signature
void myMethod(com.xebialabs.deployit.plugin.api.rules.StepPostConstructContext ctx)
. - Annotate your method with
@com.xebialabs.deployit.plugin.api.rules.RulePostConstruct
.
There can be multiple post-construct methods in your class chain. Each of these will be invoked in alphabetical order by name.
The StepPostConstructContext
contains references to the DeployedApplication
, the Scope
, the scoped object (Delta
, Deltas
, or Specification
), and the repository.
Example: The following step tries to find a value for defaultUrl
in the repository if it is not specified in the rules XML. The planning will fail if it is not found.
@StepParameter(name="defaultHostURL", description="The URL to contact first", required=true, calculated=true)
private String defaultUrl;
@RulePostConstruct
private void lookupDefaultUrl(StepPostConstructContext ctx) {
if (defaultUrl==null || defaultUrl.equals("")) {
Repository repo = ctx.getRepository();
Delta delta = ctx.getDelta();
defaultUrl = findDefaultUrl(delta, repo); // to be implemented yourself
}
}
For more information about StepPostConstructContext
, see the Javadoc.
Compiling step primitives
To compile your own step primitives, you depend on the following plugins, located in XL_DEPLOY_SERVER_HOME/lib
:
base-plugin-x.y.z.jar
udm-plugin-api-x.y.z.jar
Making step primitives available to Deploy
After writing the code for your step primitive, you make it available to Deploy by compiling it into a JAR file and placing the file in XL_DEPLOY_SERVER_HOME/plugins
.
Custom step example
This is an example of the implementation of a new type of step:
import com.xebialabs.deployit.plugin.api.flow.Step;
import com.xebialabs.deployit.plugin.api.rules.StepMetadata;
import com.xebialabs.deployit.plugin.api.rules.StepParameter;
@StepMetadata(name = "my-step")
public class MyStep implements Step {
@StepParameter(label = "My parameter", description = "The foo's bar to baz the quuxes", required=false)
private FooBarImpl myParam;
@StepParameter(label = "Order", description = "The execution order of this step")
private int order;
public int getOrder() { return order; }
public String getDescription() { return "Performing MyStep..."; }
public StepExitCode execute(ExecutionContext ctx) throws Exception {
/* ...perform deployment operations, using e.g. myParam...*/
}
}
To refer to this rule in xl-rules.xml
:
<rule ...>
...
<steps>
<my-step>
<order>42</order>
<my-param expression="true">deployed.foo.bar</myParam>
</my-step>
</steps>
</rule>
The script variant:
<rule ...>
<steps>
<script><![CDATA[
context.addStep(steps.my_step(order=42, my_param=deployed.foo.bar))
]]></script>
</steps>
</rule>
A step type is represented by a Java class with a non-parameterized constructor implementing
the Step
interface. The resulting class file must be placed in the standard Deploy classpath.
The order
represents the execution order of the step and the description
is the description of this step, which will appear in the Plan Analyzer and the deployment execution plan. The execute
method is executed when the step runs. The ExecutionContext
interface that is passed to the execute
method allows you to access the repository and the step logs and allows you to set and get attributes, so steps can communicate data.
The step class must be annotated with the StepMetadata
annotation, which has only a name
String member. This name translates directly to a tag inside the steps
section of xl-rules.xml
, so the name must be XML-compliant.
In this example, @StepMetadata(name="my-step")
corresponds to the my-step
tag.
Passing data to the step class is done using dependency injection. You annotate the private fields that you want to receive data with the StepParameter
annotation.
In xl-rules.xml
, you fill these fields by adding tags based on the field name.
For more information about interfaces and annotations, see the Javadoc.