Package org.openjdk.jmc.flightrecorder.parser

Parser extensions are a way to modify events read from a Flight Recording. It is possible to add new attributes to the events and to add completely new types of events. It is also possible to modify the values of existing attributes but we don't recommend that since it may break Mission Control functionality.

To create a new parser extension you start by implementing the IParserExtension interface. This is a (normally stateless) factory class that can create implementations of IEventSinkFactory. For each loading of a Flight Recording exactly one instance of IEventSinkFactory will be created for each implementation of IParserExtension that is passed to the loader.

Parser extensions are loaded using the Java ServiceLoader framework. To register an implementation, add its fully qualified class name to a resource file named org/openjdk/jmc/flightrecorder/parser/IParserExtension that should be included in the same jar file as the implementation.

The actual reading of the Flight Recording is then split into multiple threads that read different parts of the recording. For every event type that is encountered a call to IEventSinkFactory.create is made to get an IEventSink instance. For every event of that type IEventSink.addEvent is called with the actual event values. Note that the IEventSinkFactory instance will be shared by all read threads so the create method must be thread safe. However, each thread will have its own IEventSink instance for every event that it has found. This means that there will most probably be multiple instances of IEventSink for the same event type, each one owned by a separate read thread.

When all events have been read and passed through the sinks (all read threads are finished), a call to IEventSinkFactory.flush will be made. This allows the factory to create any additional events that it may need to create.

The IParserExtension.getEventSinkFactory method takes another IEventSinkFactory instance as an argument. This subfactory is the next factory to pass event types and events to. This allows for creating a chain of extensions that modify the events in different ways. The last factory in this chain is an internal implementation that takes care of the events for use in the Mission Control.

Basically, for every call to IEventSinkfactory.create you will get the metadata for an event type. Inspect this metadata and call the create method of the subfactory with the metadata, either modified or unmodified. This will give you an IEventSink instance that you can return immediately if you don't want to do anything with this event, or use in your own IEventSink implementation. If you want to create additional "synthetic" event types based on this type, then you can call create on the subfactory multiple times.

For every call to IEventSink.addEvent you will get the event values. You can look at these values and do whatever you want with them. Examples are generating additional attributes and calculating statistics. Once you have your new values you call addEvent on the correct sink from the subfactory. Note that the arguments to the addEvent call (the event values) must match the attributes specified when the IEventSink was created with the create call.

If you want to create events like statistics that should be created after all other events have been read, then you can use the IEventSinkFactory.flush method. Create a sink for your new event type when you get the create call for the type that you want to calculate statistics for. Collect data during addEvent calls and when you get the flush call, add the statistics events by calling addEvent on the statistics event sink.