-
Notifications
You must be signed in to change notification settings - Fork 10
Java plugin development guide
Plugin context is implemented as asВ [spring configuration bean][1]
Plugin context should be annotated with @GenesisPlugin
with mandatory id field.
It's main responsibility is to provide step coordination factories and step builder factories to genesis workflow context.
It's advised for plugin context not to export other types of beans to global genesis spring context
@Configuration
@GenesisPlugin(id = "ping", description = "simple ping plugin")
public class PingPluginContex {
}
To introduce new steps definition to genesis template dsl one should provide:
- new step class extends
com.griddynamics.genesis.plugin.adapter.AbstractStep
class - step definition to be processed by appropriate executors - custom
com.griddynamics.genesis.plugin.adapter.AbstractStepBuilder
abstract class implementation - the class is respobsible for collecting attribute values from template and creating Step definition - custom
com.griddynamics.genesis.plugin.StepBuilderFactory
interface implementaion - the class responsible for connectingStepBuilder
with step name representation in template DSL
Lets say you want to introduce new ping step with one attibute URL:
ping \{
url = "http://google.com"
\}
To support this syntax the followin code is needed:
public class PingStep extends AbstractStep {
private String url;
public PingStep(String url) {
this.url = url;
}
public String stepDescription() {
return "Ping signal";
}
}
class PingStepBuilderFactory extends com.griddynamics.genesis.plugin.StepBuilderFactory {
@Override
public final String stepName() {
return "ping"; //the value should be exactly the same as it's expected to be in template
}
@Override
public StepBuilder newStepBuilder() {
return new AbstractStepBuilder() {
private String url; //the name of the variable should be exactly the same as the name of the parameter in template
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
@Override
public Step getDetails(){
return new PingStep(url);
}
};
}
}
In order for genesis workflow system to support this new step PingStepBuilderFactory
should be exported in plugin context
@Configuration
@GenesisPlugin(id = "ping", description = "simple ping plugin")
public class PingPluginContex {
@Bean
public PingStepBuilderFactory pingStepBuilderFactory() {
return new PingStepBuilderFactory();
}
}
Action is the tiniest peace of work workflow engine can execute. Each workflow step can consist of several actions.
public class PingAction extends AbstractAction {
private String url;
public PingAction(String url) {
this.url = url;
}
}
Actions have no representations in template DSL. Information of how to execute each action is coded in action executor. Information how to split step into actions and coordinate action executors is coded in step coordinator.
Actions are data objects, to provide appropriate handling of each action one should provide specific action executor. Genesis provide several abstract classes that should be used to implement asynchronous or synchronous executors:
-
AbstractActionExecutor
- base executor class -
AbstractSimpleAsyncActionExecutor
- for async execution -
AbstractSimpleSyncActionExecutor
- for sync execution
public class PingResult extends AbstractActionResult {
public PingResult(Action action) {
super(action);
}
}
public class PingResultFailed extends AbstractActionFailed {
public PingResultFailed(Action action) {
super(action);
}
}
class PingActionExecutor extends AbstractSimpleSyncActionExecutor {
public PingActionExecutor(Action action) {
super(action);
}
public ActionResult startSync() {
PingResult result;
try {
new Url(getAction().getUrl()).openConnection();
result = new PingResult(getAction());
} catch (Exception e) {
result = new PingResult(false);
} finally {
return result;
}
}
}
To coordinate execution of several actions while executing particular step one should implement specific com.griddynamics.genesis.workflow.StepCoordinator
Genesis provide several template classes for different variations, like com.griddynamics.genesis.plugin.adapter.AbstractActionOrientedStepCoordinator
Because in this sample step consist of only one action coordinator looks simplified
public class PingStepCoordinator extends AbstractActionOrientedStepCoordinator {
private boolean failed = false;
public PingStepCoordinator(StepExecutionContext context, PingStep step) {
super(context, step);
}
@Override
public ActionExecutor getActionExecutor(Action action) {
if (action instanceof PingAction) {
return new PingActionExecutor(action);
} else {
throw new RuntimeException("Invalid action type");
}
}
@Override
public Seq onActionFinish(ActionResult result) {
if (result instanceof PingResult) {
failed = false;
} else if (result instanceof PingResultFailed) {
failed = true;
}
return ScalaUtils.list();
}
@Override
public Seq onStepInterrupt(Signal signal) {
return ScalaUtils.list();
}
@Override
public Seq onStepStart() {
return ScalaUtils.list(new PingAction(step.getUrl())); // list of actions to be executed to finish step. only one action in our case
}
@Override
protected boolean isFailed() {
return failed;
}
}
The last peace of the puzzle is a factory class that should map specific step(constructed from template) to appropriate step coordinator.В To provide such class one should implement com.griddynamics.genesis.plugin.adapter.AbstractPartialStepCoordinatorFactory
.
public class PingStepCoordinatorFactory extends AbstractPartialStepCoordinatorFactory {
public NotificationStepCoordinatorFactory(String pluginId, PluginConfigurationContext pluginConfiguration) {
super(pluginId, pluginConfiguration);
}
@Override
public boolean isDefinedAt(Step step) {
return step instanceof NotificationStep;
}
@Override
public StepCoordinator apply(Step step, StepExecutionContext context) {
return new PingStepCoordinator(context, (PingStep) step);
}
}
This implementation should be exported by plugin context
@Configuration
@GenesisPlugin(id = "ping", description = "simple ping plugin")
public class PingPluginContex {
@Bean
public PingStepBuilderFactory pingStepBuilderFactory() {
return new PingStepBuilderFactory();
}
@Bean
public PingStepCoordinatorFactory pingStepCoordinatorFactory() {
return new PingStepCoordinatorFactory();
}
}
Plugin can use genesis main configuration subsystem. To get access to it plugin must conform to following rules:
- All properties must start with
genesis.plugin.<plugin_id>.
prefix - Plugin should provid genesis-plugin.properties in package root directory with all properties with default values
If the plugin have no mean of runtime reconfiguration it's possible to use spring @Value
annotation for easy configuration properties access
@Value("${genesis.plugin.ping.timeout}") Integer timeout;
Genesis provide several services that can be discovered and used by plugins. The definition of services can be found in internal-api
module in com.griddynamics.genesis.service
package.
Discovery of the services is implemented via @Autowired
spring feature
@Autowired SshService sshService;