Skip to content

Java plugin development guide

bugzmanov edited this page Aug 2, 2012 · 1 revision

Genesis project : Java plugin development guide

Plugin context

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 {

}

Step and step builders

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 connecting StepBuilder 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();
    }
}

Actions

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.

Action executors

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;
        }
    }
}

Step coordinator

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;
  }
}

Step coordinator factory

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 configuration

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 services

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;
Clone this wiki locally