Core Functionalities

This part of the reference documentation covers all the technologies that are absolutely integral to the Rarog Platform.

1. Background tasks

Most applications does some things in background. It may be simple timer tick, that updates displayed lock. It may be long-running computing, or periodic data send operation. For example metrics are periodically saved as snapshots and sent to external services. Single application may easily start hundreds of even thousands such operation. Problems arise when all of these action try to execute at the same time. It can easily result in thread saturation state - state when there is more threads started than system can handle without performance issues. Also, starting new thread has their costs, which makes performance problems bigger.

And this situation may become problem quite fast, if each component and each plugin, will start its own threads pool. Java offers common thread pool, but it comes with its own problems: it can become saturated by tasks that spend time waiting or even become blocked (deadlock) and is not configurable at runtime. So we need some middle ground. Some common pool to not over saturate system, but make it configurable and monitorable, so admin can take decision how big pool should be for his instance.

1.1. Task manager

Task manager is designed to be main point for starting background tasks. It has 2 major functions:

  1. adds monitoring to started threads and tasks

  2. limits max number of concurrently running tasks, so they will do not hinder of foreground tasks (like handling requests)

It comes with handful API, which allows to use TaskManager directly, or via ScheduledExecutorService or via ThreadFactory.

Note that TaskManager#createThread and ThreadFactory adapter do not limit number of concurrent threads. It only adds monitoring.

Example usage

Refer to javadoc for API reference.

void schedule(TaskManager taskManager) {
    Runnable beeper = () -> System.out.println("beep");
    taskManager.scheduleTask(beeper, ScheduleConfig.builder()
        .period(Duration.ofSeconds(10))
         .repeatCount(360)
         .createSchedulerConfig());
}

Metrics

TaskManager wraps executed tasks and started threads into monitoring, so it is easy for instance administrator to identify problems with over saturation or stale tasks. Visit TaskManager metrics reference for more details.

1.2. BackgroundTaskManager

TBA

2. Events

To decouple application components sometimes it’s wise to use events. In Rarog, each plugin usign Spring has a separate Spring Context. However, even this is not certain because plugins are not forced to use Spring at all. Given that, it’s not possible to send Spring events which should be received by other plugins. To solve this problem, Rarog has an API separate from Spring’s native ApplicationEvents.

We can split the API in two parts, the one for event producers and for listeners.

2.1. Event Producer API

To create an event producer in Rarog, you can just use the EventService like below:

import eu.rarogsoftware.rarog.platform.api.plugins.ComponentImport;
import eu.rarogsoftware.rarog.platform.api.plugins.events.EventService;

public class ExampleEventPublisher {
    private final EventService eventService;

    public ExampleEventPublisher(@ComponentImport EventService eventService) {
        this.eventService = eventService;
    }

    public void sendExampleEvent() {
        eventService.publishSyncEvent(new SomeArbitraryObject());
    }
}

Any object can be an event. In addition to that there are two main ways events can be handled.

Events can be processed:
  • Synchronously - using publishSyncEvent method. The method returns when all the listeners process the event.

  • Asynchronously - there are two flavours: you can use either publishAsyncEvent or publishParallelAsyncEvent methods. First one runs all the listeners sequentially in one separate thread, whereas the second one uses a separate thread for each handler.

For the asynchronous events Rarog’s TaskManager is utilized under the hood, so the thread count is limited according to its settings.

When an exception is thrown within one of the listeners, this fact is just logged and processing is not stopped.

2.2. Event Listener API

Event listener in Rarog are classes implementing eu.rarogsoftware.rarog.platform.api.plugins.events.EventListener interface and declaring single parameter methods annotated with eu.rarogsoftware.rarog.platform.api.plugins.events.EventHandler annotation.

Example listener:

@Component
public class FuncTestEventListener implements EventListener {
    private final Logger logger = LoggerFactory.getLogger(FuncTestEventListener.class);

    @EventHandler
    public void onFuncTestEvent(SomeEvent event) {
        logger.debug("Received SomeEvent yay. Count: {}");
    }

    @EventHandler
    public void onStringEvent(String event) {
        logger.debug("Received String yay. Count: {}");
    }
}

A method annotated with EventHandler will be invoked each time a matching event is published to EventService. Matching event means the object is either an exact type of parameter, its subclass, or an implementation if parameter type is an interface.

Valid handler method has only one parameter - if there is more, or less - such method will be skipped when registering the class, and such fact will be logged. However, if an EventListener will not have any valid RarogHandler an exception will be thrown.

To enable event listener, you have to register all EventListener classes, e.g. by defining the following Bean in your plugin’s configuration, Spring will automagically find all EventListener implementations:

@Configuration
public class ExamplePluginConfiguration {
    @Bean
    public EventListenersDescriptor eventListenersDescriptor(List<EventListener> eventListeners) {
        return new EventListenersDescriptor(new HashSet<>(eventListeners));
    }
}

3. Logging

Rarog Platform as most ready to use software comes with logging capability, that allow to detect and investigate problems.

3.1. Logging API

Rarog Platform comes with SLF4J which is "frontend" for logging.It is the only logging library that can be considered API and will be always available in Rarog.Any other logging libraries may be added or removed without any warning.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyComponent {
    private final Logger logger = LoggerFactory.getLogger(MyComponent.class);
}

3.2. Logger behaviour

Current logging backend is Log4j2, which is configured to log to console and to file in logs home directory. It uses format %d\{yyyy-MM-dd HH:mm:ss.SSS} [%p] ${hostName}: [%t] %c{1.}: %m%n%throwable with produces logs like 2022-12-12 12:12:12.1212} [INFO] hostname: [process-1] logger.class: Message and stack trace. The same format is applied to logs printed to console, but it uses console formatting.

3.3. Good practices

TBA

4. Metrics

A software metric is a measure of software characteristics that are quantifiable or countable. Like performance, users way of using it, etc. This information is useful for admins and other developers to understand how application is behaving and if there are any actions that need to be taken, to improve it or prevent some potential harmful situations. That’s why software should have meaningful metrics.

Rarog Platform comes with ready to use API for metrics, that have minimal performance impact. You can use this API in all OSGi based plugins.

4.1. Usage code example

class ExampleClass {
    Counter invocationCounter;
    Histogram durationHistogram;

    ExampleClass(MetricsService metricsService,
                                         AdvancedScheduledExecutorService delegate) {
        this.invocationCounter = metricsService.createCounter(MetricSettings.settings()
                .name("example_counter")
                .description("Example of counter")
        );
        this.durationHistogram = metricsService.createHistogram(HistogramSettings.settings()
                .name("example_histogram")
                .description("Example of histogram")
                .buckets(0.001, 0.01, 0.1, 1, 10, 60, 600, 3600)
        );
    }

    void method(long repeatCount) {
        invocationCounter.increment();
        try (var ignored = durationHistogram.startTimer()) {
            for(int i = 0; i <repeatCount;i++) {
                // do something here
            }
        } catch (Exception e) {
            //ignore because thrown by timer, or log it
        }
    }
}

4.2. Usage in plugins

  1. Add dependency on eu.rarogsoftware.rarog.platform:rarog-platform-api to your plugin.

  2. Make sure package eu.rarogsoftware.rarog.platform.api.metrics is imported to your plugin by OSGi (happens automatically, when using default configuration, otherwise you need to update configuration of your maven-bundle-plugin).

  3. Add eu.rarogsoftware.rarog.platform.api.metrics.MetricsService to your component and import it from OSGi (use @ComponentImport on constructor parameter or import it manually using OSGi API)

  4. Use MetricsService to create new instance of you metric. You can read javadoc to learn more about each metric.

  5. Use metric objects to monitor some actions (For example Counter#increment).

  6. Enable OpenMetrics page. See metrics reference for info how do it.

  7. Access <base_url>/rest/telemetry/1.0/expose/openmetrics/ and look for your metric.

5. Monitoring

Rarog Platform as enterprise ready software comes with various ways to monitor its behaviour and quality aspects like: performance, security related actions and issues, stability issues, etc. So admins can easily detect and fix various problems and fine tune instance for the best outcomes. And as each high quality tool all these way are compatible with industry standards.

5.1. Logs file

The most detailed information is available in main log file logs/rarog.log in Rarog home directory. It contains all kinds of information, which make it hard to read, but using tools like Splunk or Datadog Log Explorer, it may be possible to do meaningful analysis and set alarms. Visit logging reference for details on logging.

5.2. Metrics

Rarog Platform measures multiple aspects of its works like performance, functionality usage, etc. All this information is exposed in OpenMetrics format for consumption by various tools like Prometheus. It can be used to detect performance issues, monitor resource usage, detect faulty components or see effectiveness of fine-tuning operations.

Detailed guide about available metrics and ways to access it can be found in metrics reference. You can visit metrics reference for guide on add your own metrics.

5.3. Audit log

TBA