event-scheduler


License

License

GroupId

GroupId

nl.stokpop
ArtifactId

ArtifactId

event-scheduler
Last Version

Last Version

3.0.2
Release Date

Release Date

Type

Type

jar
Description

Description

event-scheduler
event-scheduler
Project URL

Project URL

https://github.com/stokpop/event-scheduler.git
Source Code Management

Source Code Management

https://github.com/stokpop/event-scheduler.git

Download event-scheduler

How to add to project

<!-- https://jarcasting.com/artifacts/nl.stokpop/event-scheduler/ -->
<dependency>
    <groupId>nl.stokpop</groupId>
    <artifactId>event-scheduler</artifactId>
    <version>3.0.2</version>
</dependency>
// https://jarcasting.com/artifacts/nl.stokpop/event-scheduler/
implementation 'nl.stokpop:event-scheduler:3.0.2'
// https://jarcasting.com/artifacts/nl.stokpop/event-scheduler/
implementation ("nl.stokpop:event-scheduler:3.0.2")
'nl.stokpop:event-scheduler:jar:3.0.2'
<dependency org="nl.stokpop" name="event-scheduler" rev="3.0.2">
  <artifact name="event-scheduler" type="jar" />
</dependency>
@Grapes(
@Grab(group='nl.stokpop', module='event-scheduler', version='3.0.2')
)
libraryDependencies += "nl.stokpop" % "event-scheduler" % "3.0.2"
[nl.stokpop/event-scheduler "3.0.2"]

Dependencies

compile (1)

Group / Artifact Type Version
com.github.stephenc.jcip : jcip-annotations jar 1.0-1

test (2)

Group / Artifact Type Version
junit : junit jar 4.13.2
org.mockito : mockito-core jar 3.9.0

Project Modules

There are no modules declared in this project.

event-scheduler

Add this java library to your project to generate timed events.

For instance during a performance test to dynamically increase response times over time.

usage

Create an EventScheduler using the builder with an EventSchedulerConfig:

EventLogger eventLogger = EventLoggerStdOut.INSTANCE;

String scheduleScript1 =
    "PT600S|scale-down|{ 'replicas':1 }\n" +
    "PT660S|heapdump|server=myserver.example.com;port=1567";

String scheduleScript2 =
    "PT1S|restart(restart to reset replicas)|{ 'server':'myserver' 'replicas':2, 'tags': [ 'first', 'second' ] }";

TestConfig testConfig = TestConfig.builder()
    .workload("testType")
    .testEnvironment("testEnv")
    .testRunId("testRunId")
    .buildResultsUrl("http://url")
    .version("version")
    .rampupTimeInSeconds(10)
    .constantLoadTimeInSeconds(300)
    .annotations("annotation")
    .variables(Collections.emptyMap())
    .tags(Arrays.asList("tag1","tag2"))
    .build();

// this class really needs to be on the classpath, otherwise: runtime exception, not found on classpath
String factoryClassName = "nl.stokpop.eventscheduler.event.EventFactoryDefault";

List<EventConfig> eventConfigs = new ArrayList<>();
eventConfigs.add(EventConfig.builder().name("myEvent1").eventFactory(factoryClassName).scheduleScript(scheduleScript2).build());
eventConfigs.add(EventConfig.builder().name("myEvent2").eventFactory(factoryClassName).build());
eventConfigs.add(EventConfig.builder().name("myEvent3").eventFactory(factoryClassName).build());

EventSchedulerConfig eventSchedulerConfig = EventSchedulerConfig.builder()
    .schedulerEnabled(true)
    .debugEnabled(false)
    .continueOnAssertionFailure(false)
    .failOnError(true)
    .keepAliveIntervalInSeconds(120)
    .testConfig(testConfig)
    .eventConfigs(eventConfigs)
    .scheduleScript(scheduleScript1)
    .build();

EventScheduler scheduler = EventSchedulerBuilder.of(eventSchedulerConfig, eventLogger);

Note that a lot of properties of the builders have decent defaults and do not need to be called, such as the retry and keep alive properties.

The TestConfig describes high level properties of a test run.

A TestConfig can be set at the EventSchedulerConfig and at the EventConfig. When set at the EventSchedulerConfig, no EventConfig are allowed to have an TestConfig. If one EventConfig has a TestConfig, that one will be used as top level TestConfig. It is not allowed to have multiple EventConfigs with a TestConfig.

When adding events, each event can have its own values. That makes it possible to configure multiple Wiremock event that use different Wiremock urls for instance.

Then call these methods at the appropriate time:

scheduler.beforeTest()

Call when the load test starts.

scheduler.afterTest()

Call when the load test stops.

test events

During a test run this Event Scheduler emits events. You can put your own implementation of the EventFactory and Event interface on the classpath and add your own code to these events.

For event specific properties create a subclass of EventConfig and add the properties. Use this XxxEventConfig in the generics of the XxxEventFactory. Override the getEventFactory() method in the XxxEventConfig class to define the factory class to use:

@Override
public String getEventFactory() {
    return XxxEventFactory.class.getName();
}

Events triggers available, with example usage:

  • before test - use to restart servers or setup/cleanup environment
  • after test - start generating reports, clean up environment
  • keep alive calls - send keep alive calls to any remote API
  • check result - after a test check results for the event, if failures are present the CI build can fail
  • abort test - abort a running test, do not run to end
  • custom events - any event you can define in the event scheduler, e.g. failover, increase stub delay times or scale-down events
  • custom events - any event you can define in the event scheduler, e.g. failover, increase stub delay times or scale-down events

The keep alive is scheduled each 15 seconds during the test. The keep-alive schedule can also be changed.

custom events

You can provide custom events via a list of

<duration,eventName(description),eventSettings> 

tuples, one per line.

The eventName can be any unique name among the custom events. You can use this eventName in your own implementation of the Event interface to select what code to execute.

The description can be any text to explain what the event at that time is about. It will be sent to remote systems and is for instance shown in the graphs as an event marker. If no description is provided, the description is 'eventName-duration'.

You can even send some specific settings to the event, using the eventSettings String. Decide for your self if you want this to be just one value, a list of key-value pairs, json snippet or event base64 encoded contents.

Example:

EventSchedulerConfig.builder().scheduleScript(eventSchedule).build()

And as input:

String eventSchedule =
        "PT5S|restart(restart with 2 replicas)|{ server:'myserver' replicas:2 tags: [ 'first', 'second' ] }\n" +
        "PT10M|scale-down\n" +
        "PT10M45S|heapdump(generate heapdump on port 1567)|server=myserver.example.com;port=1567\n" +
        "PT15M|scale-up|{ replicas:2 }\n";

Note the usage of ISO-8601 duration or period format, defined as PT(n)H(n)M(n)S. Each period is from the start of the test, so not between events!

Above can be read as:

  • send restart event 5 seconds after start of test run.
  • send scale-down event 10 minutes after start of the test run.
  • send heapdump event 10 minutes and 45 seconds after start of test run.
  • send scale-up event 15 minutes after start of test run.

The setting will be send along with the event as well, for your own code to interpret.

When no settings are present, like with de scale-down event in this example, the settings event will receive null for settings.

event-scheduler maven plugins

To use the events via the event-scheduler-maven-plugin, the jar with the implementation details must be on the classpath of the plugin.

You can use the dependencies element inside the plugin element.

For example, using the test-events-hello-world event-scheduler plugin (yes, a plugin of a plugin):

<plugin>
    <groupId>nl.stokpop</groupId>
    <artifactId>event-scheduler-maven-plugin</artifactId>
    <version>1.1.0</version>
    <configuration>
        <eventSchedulerConfig>
            <debugEnabled>false</debugEnabled>
            <schedulerEnabled>true</schedulerEnabled>
            <failOnError>true</failOnError>
            <continueOnEventCheckFailure>true</continueOnEventCheckFailure>
            <eventConfigs>
                <eventConfig implementation="nl.stokpop.helloworld.event.StokpopEventConfig">
                    <name>StokpopHelloEvent1</name>
                    <testConfig>
                        <systemUnderTest>my-application</systemUnderTest>
                        <version>1.2.3</version>
                        <workload>stress-test</workload>
                        <testEnvironment>loadtest</testEnvironment>
                        <testRunId>my-test-123</testRunId>
                        <buildResultsUrl>http://localhost:4000/my-test-123</buildResultsUrl>
                        <rampupTimeInSeconds>1</rampupTimeInSeconds>
                        <constantLoadTimeInSeconds>4</constantLoadTimeInSeconds>
                        <annotations>${annotation}</annotations>
                        <tags>
                            <tag>tag1-value</tag>
                            <tag>tag2-value</tag>
                        </tags>
                    </testConfig>
                    <scheduleScript>
                        PT1S|restart(restart with 2 replicas)|{ server:'myserver' replicas:2 tags: [ 'first', 'second' ] }
                    </scheduleScript>
                    <myRestService>https://my-rest-api</myRestService>
                    <myCredentials>${env.SECRET}</myCredentials>
                    <helloMessage>Hello, Hello World!</helloMessage>
                    <helloInitialSleepSeconds>1</helloInitialSleepSeconds>
                </eventConfig>
            </eventConfigs>
        </eventSchedulerConfig>
    </configuration>
    <dependencies>
        <dependency>
            <groupId>nl.stokpop</groupId>
            <artifactId>test-events-hello-world</artifactId>
            <version>1.1.0</version>
        </dependency>
    </dependencies>
</plugin>

Note that the <eventConfig implementation="..."> implementation field is mandatory, it defines the EventConfig subtype to use. The name of an event, here StokpopHelloEvent1, should a unique event name. The event name is used in the logging.

custom events generator

Create your own event by implementing the nl.stokpop.eventscheduler.api.EventFactory interface.

Create your own event generator by implementing the nl.stokpop.eventscheduler.api.EventGeneratorFactory interface.

Add the @generatorFactoryClass and settings to the customEvents tag to have an eventSchedule generated instead of an explicit list of timestamps and events.

<customEvents>
    @generatorFactoryClass=com.stokpop.event.StokpopEventGeneratorFactory
    events-file=${project.basedir}/src/test/resources/events.json
    foo=bar
</customEvents>

The class defined by @generatorFactoryClass should be available on the classpath. The foo=bar is an example of properties for the event generator. You can use multiple lines for multiple properties.

Properties that start with @-sign are so-called "meta" properties and should properties with @-sign should preferably not be used as custom properties inside the implementation class.

class loaders

If classes are not available on the default classpath of the Thread, you can provide your own ClassLoader via nl.stokpop.eventscheduler.api.EventSchedulerBuilder.of(EventSchedulerConfig, ClassLoader). Useful when running with Gradle instead of Maven.

event logging

Two convenience logger implementations are provided for the nl.stokpop.eventscheduler.api.EventLogger interface.

  • ...log.EventLoggerStdOut.INSTANCE logs to standard out (debug disabled)
  • ...log.EventLoggerStdOut.INSTANCE_DEBUG logs to standard out (debug enabled)

kill switch

The keep-alive call can receive data from remote systems and decide to throw a KillSwitchException based on that data. If a KillSwitchException is thrown and passed on to the user of the event-scheduler, running tests can for instance be aborted based on the received data.

A SchedulerExceptionHandler implementation can be set on the EventScheduler. It contains a kill and abort method that you can implement to handle these exceptions.

An example is that the analysis tool in use discovers too high response times and decides to kill the running test.

Versions

Version
3.0.2
3.0.1
3.0.0
2.2.2
2.2.1
2.2.0
2.1.1
2.1.0
2.0.0
1.0.1
1.0.0