Good Practices for Analysis Plugins

If developing a new plugin, extend either the existing plugins or inherit directly from AbstractFilterPlugin, AbstractReaderPlugin or AbstractRepository. Do not inherit from AbstractPlugin or AbstractAnalysisComponent.

Mark the new plugin with the @Plugin or the @Repository annotation, even if no (new) output/repository ports or properties are added.

Make sure that the plugins are thread-safe. In other words: Assume that the plugin could receive events from different threads at the same time.

Filter Constants for Input, Output, and Repository Port Names, and Property names/values

The names (and values) should be provided as public static final String constants. Use the following prefixes for variable names and values:

  • Input port names:
    • Variable name prefix: INPUT_PORT_NAME_
    • No variable value prefix, such as "input"
  • Output port names:
    • Variable prefix: OUTPUT_PORT_NAME_
    • No variable value prefix, such as "output"
  • Repository port name:
    • Variable prefix: REPOSITORY_PORT_NAME_
    • No variable value prefix, such as "repository" (while such postfix might be OK here)
  • Configuration property name:
    • Variable prefix: CONFIG_PROPERTY_NAME_
    • No variable value prefix, such as "config"
  • Configuration property values:
    • Variable prefix: CONFIG_PROPERTY_VALUE_

Moreover,

  • Name components in the variables values should be written in camelCase.
  • The name should include information about the respective semantics, e.g., "monitoringRecords", rather than generic values, such as "defaultOutput".

Example:

public static final String INPUT_PORT_NAME_EVENTS = "receivedEvents";

public static final String OUTPUT_PORT_NAME_RELAYED_EVENTS = "relayedEvents";

public static final String CONFIG_PROPERTY_NAME_STREAM = "stream";
public static final String CONFIG_PROPERTY_NAME_ENCODING = "characterEncoding";

public static final String CONFIG_PROPERTY_VALUE_STREAM_STDOUT = "STDOUT";
public static final String CONFIG_PROPERTY_VALUE_STREAM_STDERR = "STDERR";
public static final String CONFIG_PROPERTY_VALUE_STREAM_STDLOG = "STDLOG";
public static final String CONFIG_PROPERTY_VALUE_DEFAULT_ENCODING = "UTF-8";

Input Ports

  • Use public constants as names:
    • Use:
      @InputPort(name = CountingFilter.INPUT_PORT_NAME, eventTypes = { Object.class }, description = "incoming objects are counted and forwarded")
      
    • Rather than name="received-events"
  • Object as accepted eventType should be made explicit:
    • Use:
      @InputPort(..., eventTypes = { Object.class }, ...)
      public final void inputEvent(final Object event) {
      
    • rather than: eventTypes = {}
  • Most concrete type matching event type(s) should be used in the method signature:
    • ...
  • Meaningful descriptions:
    @InputPort(..., description = "Receives incoming objects to be counted and forwarded")
    
  • Method name should have prefix input:
    @InputPort(...)
    public final void inputEvent(final Object object) { ... }
    

Output Ports

  • Use public constants as names:
    • Use:
      @Plugin(outputPorts = {
              @OutputPort(name = TimestampFilter.OUTPUT_PORT_NAME_WITHIN_PERIOD, description = "Provides records within the timeperiod", eventTypes = { IMonitoringRecord.class }),
              @OutputPort(name = TimestampFilter.OUTPUT_PORT_NAME_OUTSIDE_PERIOD, description = "Provides records out of the timeperiod", eventTypes = { IMonitoringRecord.class })
      })
      
    • Rather than name="recordsWithinTimePeriod"

Repository Ports

  • Use public constants as names:
    • Use:
      @Plugin(repositoryPorts = @RepositoryPort(name = AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, repositoryType = SystemModelRepository.class))
      
    • Rather than name="systemModelRepository"

 

Constructor

There must be one constructor accepting an instance of Configuration and IProjectContext. The constructor must use the inherited super constructor before extracting any information from the given parameters.

  • Use:
    public EventRecordTraceCounter(final Configuration configuration, final IProjectContext projectContext) {
            super(configuration, projectContext);
    
            this.logInvalidTraces = configuration.getBooleanProperty(CONFIG_PROPERTY_NAME_LOG_INVALID);
    }
    

Configuration

The configuration given by the constructor should be stored somehow (it is recommended to do this with final variables) so that it can later be delivered by the getCurrentConfiguration method.

  • Use public constants as names and as values:
    • Use:
      configuration = {
              @Property(name = TimestampFilter.CONFIG_PROPERTY_NAME_TIMEUNIT, defaultValue = TimestampFilter.CONFIG_PROPERTY_VALUE_TIMEUNIT),
              @Property(name = TimestampFilter.CONFIG_PROPERTY_NAME_IGNORE_BEFORE_TIMESTAMP, defaultValue = TimestampFilter.CONFIG_PROPERTY_VALUE_MIN_TIMESTAMP),
              @Property(name = TimestampFilter.CONFIG_PROPERTY_NAME_IGNORE_AFTER_TIMESTAMP, defaultValue = TimestampFilter.CONFIG_PROPERTY_VALUE_MAX_TIMESTAMP)
      })
      

getCurrentConfiguration

The method must deliver a fully filled configuration object, which must be enough to instantiate the class. Keep in mind that the getCurrentConfiguration method should deliver a new Configuration object for each call.

  • Use:
    public final Configuration getCurrentConfiguration() {
    	final Configuration configuration = new Configuration();
    	configuration.setProperty(CONFIG_PROPERTY_NAME_TIMEUNIT, this.timeunit.name());
    	configuration.setProperty(CONFIG_PROPERTY_NAME_IGNORE_BEFORE_TIMESTAMP, Long.toString(this.ignoreBeforeTimestamp));
    	configuration.setProperty(CONFIG_PROPERTY_NAME_IGNORE_AFTER_TIMESTAMP, Long.toString(this.ignoreAfterTimestamp));
    	return configuration;
    }