Writing Tools and Services¶
When we want to write new analysis tools and services with Kieker and Teetime, a lot of boilerplate code to
parse command line parameters,
read configuration files,
check parameters from both sources,
assemble a pipe and filter configuration,
provide shutdown hooks for services,
and execute everything.
Therefore, we created an AbstractService
class providing the
necessary code and also ensuring certain basic functions. Alongside this
class, we envision a specific architecture. It comprises
a main class, e.g.,
MyToolMain
, extendingAbstractService
,a settings class for the parameters utilizing JCommander for all command line properties and Kieker configuration file settings processed by the Kieker configuration file parser. It can also include additional attributes that are not set by Jcommander and Kieker configuration file parser.
and a
TeetimeConfiguration
class, e.g.,MyTeetimeConfiguration
.
For smaller applications you can place all command line parameters in your main class and use it as both settings and main class.
To support checking of parameters and configuration file options, we
provide a helper class ParameterEvaluation
to further analyze
properties.
A typical main class looks like:
public MyToolMain extends AbstractService<MyTeetimeConfiguration,
MyParameterSettings> {
/*
* This is a simple main class which does not need to be instantiated.
*/
private MyToolMain() {
}
/*
* main functions.
*
* @param args arguments are ignored
*/
public static void main(final String[] args) {
java.lang.System.exit(new MyToolMain().run("Application Title",
"Logger Label", args, new MyParameterSettings()));
}
@Override
protected MyTeetimeConfiguration createTeetimeConfiguration() throws
ConfigurationException {
/* do some preparatory stuff. */
return new MyTeetimeConfiguration(...);
}
@Override
protected boolean checkParameters(final JCommander commander) throws
ConfigurationException {
return true; // only if all parameters check out
}
@Override
protected void shutdownService() {
// empty, no special shutdown required
}
@Override
protected File getConfigurationFile() {
return this.settings.getConfigurationFile();
}
@Override
protected boolean checkConfiguration(final Configuration configuration,
final JCommander commander) {
return true; // only if every configuration file option checks out
}
}
A example Teetime configuration looks like:
public class MyTeetimeConfiguration extends Configuration {
public MyTeetimeConfiguration(...) {
SomeStange stage1 = new SomeStage();
SomeOtherStage stage2 = new SomeOtherStage();
this.connectPorts(stage1.getOutputPort(), stage2.getInputPort());
}
}
For parameter settings, Kieker utilizes JCommander for command line
parameter processing. The parameters set by JCommander require an
annotation @Parameter
. In addition Kieker can utilize a standard
Java property file with name value pairs. In addition, Kieker comes
since 2.0.0 with a facility to process the Java properties and fill in
settings in the parameter settings object.
In the following, we will give a brief description how to use all three facilities for parameters.
JCommander - Command Line Parameters¶
The documentation of JCommander can be found here. A typical setup with JCommander may look like:
public class Settings {
@Parameter(names = { "-e", "--experiment-id" }, required = true,
description = "The experiment id")
private String experimentId;
public String getExperimentId() {
return this.experimentId;
}
}
The parameter is defined with a simple private attribute annoteded with
@Parameter
. In this example we follow the GNU command line scheme.
That is, there is a long parameter name and a short version. It is
common to give more often used parameters also short parameter names,
whie others may only have a long name.
The POSIX and GNU guidelines can be found here.
Typical names are:
Short |
Long |
Description |
---|---|---|
-i |
–input |
Main input file or source |
-o |
–output |
Main output file or source |
-V |
–version |
Version of the tool |
-h |
–help |
Show help information |
-v |
–verbose |
Print more information about progress |
Note these are suggestions and may be used differently in your context.
Using a Configuration File¶
The Kieker Configuration
is an extension of the java.lang.Properties
facility of Java. It allows to store name value pairs. The Kieker
Configuration is utilized on the monitoring side of Kieker in Java to
set up probes and logging. It can also be used for analysis tools.
In case a tool requires many settings and the command line may look
too complex, users would rather use configuration files to provide
parameters than specifying all of it on command line.
To be able to specify a configuration file, we need a command line parameter. Therefore, we add one parameter to the settings object (remember this can also just be the main class).
public class Settings {
@Parameter(names = { "-c", "--configuration" }, required = true,
description = "Configuration file.",
converter = PathConverter.class)
private Path configurationPath;
public Path getConfigurationPath() {
return this.configurationPath;
}
}
The specified converter automatically converts the command line string
into a Path
object in the settings. The Kieker framework automatically
reads a configuration when a path is specified. To connect both you
have to implement the getConfigurationPath
method in your main
class. In case you use the main class also for settings, this would
look like:
@Override
public Path getConfigurationPath() {
return this.configurationPath;
}
In case you have a separate settings class:
@Override
public Path getConfigurationPath() {
return this.settings.configurationPath;
}
The tool behavior-analysis is a good example in code how to use a configuration file with a separate settings class, while the `collector <https://github.com/kieker-monitoring/kieker/tree/master/kieker-tools/collector/src/kieker/tools/collector>’_ is a good example for a tool that uses the main class also to store the settings.
Checking Configuration Options¶
To check and process configuration parameters from the Kieker
Configuration
, you add checks to the checkConfiguration
method
in the main class.
@Override
protected boolean checkConfiguration(final Configuration configuration,
final JCommander commander) {
...
return true; // only if every configuration file option checks out
}
The class ParameterEvaluationUtils can be used to check whether files and directories, are readable and perform conversion operation.
Alternatively, you can use the new parameter parsing API of Kieker, as described in the next section.
Parsing Configuration File Parameter¶
Kieker supports a new API to parse configuration settings. In future, it will become the default way to handle configuration files.
Analog to JCommander’s @Parameter
annotation, Kieker supports
an @Setting
annotation which can be used similarily to their
JCommander counterparts, but designed for configuration files. To use
this facility, you implement the checkConfiguration
in your main
class method as follows:
@Override
protected boolean checkConfiguration(final kieker.common.configuration.Configuration configuration,
final JCommander commander) {
final ConfigurationParser parser =
new ConfigurationParser(ConfigurationKeys.PREFIX, this.settings);
try {
parser.parse(configuration);
} catch (final ParameterException e) {
this.logger.error(e.getLocalizedMessage());
return false;
}
...
}
The configuration parser runs over the settings object and fills in all settings from the Kieker configuration object. Like JCommander it automatically converts string values to the correct settings type using convertes from JCommander and validates their content with validators.
An example settings class from the behavior-analysis
tool looks like
public final class BehaviorAnalysisSettings {
@Parameter(names = { "-c", "--configuration" }, required = true, description = "Configuration file")
private File configurationFile;
@Setting(converter = PathConverter.class, validators = ParentPathValueValidator.class)
private Path clusterOutputPath;
...
}
in the example, you can see one parameter, which will be handled by
JCommander, and the clusterOutputPath
which is handled by the
ConfigurationParser
. The latter applies the PathConverter
and
on validator to ensure the parent directory for the output file exists.
As it may be necessary to add more checks after the processing of configuration settings, these can be applied below the try catch block.
Note: future versions of the Kieker AbstractService
will
automatically run the ConfigurationParser
.