Documentation for a newer release is available. View Latest

Conceptos

Esta sección contiene explicaciones y ayuda sobre cómo lograr las siguientes tareas con los eventos de IPF:

  • Suscribirse a eventos

  • Usar implementaciones de la API de eventos de IPF

  • Definir tus propios eventos para publicarlos usando la API de eventos

Jerarquía de eventos

Todos los eventos extienden el nivel superior IPFSystemEvent<T>, que tiene los siguientes miembros (y los getters asociados):

public abstract class IPFSystemEvent<T> {
    private static final EventVersion DEFAULT_VERSION = new EventVersion(1, 0, 0);

    private String name;
    private EventLevel level;
    private Instant createdAt;
    private EventVersion version;
    private EventType type;
    private ProcessingContext processingContext;
    private String source;
    private Map<String, String> metadata;
    private T payload;

El IPFSystemEvent está parametrizado, y el tipo T contiene la carga útil del evento que extiende esta clase.

Generar y suscribirse a eventos

Las dos subsecciones siguientes asumen que se ha creado una implementación del bus. La API de eventos y la implementación están en dos módulos diferentes para permitir la capacidad de conectar diferentes implementaciones de bus y procesador.

Para usar la implementación DefaultEventBus que se utiliza en los ejemplos a continuación, declara las siguientes dependencias en pom.xml:

<dependencies>
    <dependency>
        <groupId>com.iconsolutions.ipf.core.systemevents</groupId>
        <artifactId>ipf-system-events-api</artifactId>
        <version>${ipf-system-events.version}</version>
    </dependency>
    <dependency>
        <groupId>com.iconsolutions.ipf.core.systemevents</groupId>
        <artifactId>ipf-system-events-impl</artifactId>
        <version>${ipf-system-events.version}</version>
    </dependency>
</dependencies>

Los ejemplos a continuación asumen un bus con el nombre eventBus que ya ha sido creado así:

    private final EventBus eventBus = DefaultEventBus.getInstance();

Suscribirse a eventos

Ten en cuenta que los eventos publicados en un bus antes de que se reconozca una suscripción no se entregarán a ese suscriptor.

Hay dos métodos subscribe como se describe en la interfaz. Aquí está el primero:

    <T extends IPFSystemEvent> boolean subscribe(Class<T> clazz, EventProcessor<? extends T> eventProcessor);

Esta llamada permite que el eventProcessor proporcionado se suscriba al bus, pero también da una Class opcional para denotar un nivel específico en la jerarquía de clases en el que suscribirse. Si consideramos la siguiente jerarquía, por ejemplo:

|- IPFSystemEvent
|--> FunctionalSystemEvent
|---> TestSpecEvent

Podemos emitir una llamada a subscribe como esta:

        eventBus.subscribe(TestSpecEvent.class, eventProcessor);

Esto garantizará que este eventProcessor solo reciba eventos del tipo TestSpecEvent y sus subclases, y ningún otro tipo de IPFSystemEvent.

Usa esta llamada cuando solo estés interesado en un conjunto específico de eventos.

Aquí está la segunda forma de suscribirse:

    default boolean subscribe(EventProcessor<IPFSystemEvent<?>> eventProcessor) {

Esta es una forma abreviada de suscribirse de la siguiente manera:

        var subscribed = eventBus.subscribe(IPFSystemEvent.class, eventProcessor);

Usa esta llamada si potencialmente te interesan todos los posibles eventos que puede generar este sistema.

Reducir el enfoque con EventProcessor

Si observamos la definición de la interfaz EventProcessor, podemos ver el siguiente método de interfaz (con una implementación predeterminada), además del notify esperado:

    /**
     * A predicate to filter which events to accept for processing
     *
     * @return whether this {@link EventProcessor} should process the given {@link IPFSystemEvent}.
     */
    default Predicate<T> predicate() {
        return a -> true;
    }

Este predicado permite que el procesador inspeccione cada mensaje entrante y decida si está interesado en ese mensaje en particular.

La implementación predeterminada - a → true - permite el paso de todos los eventos. Sobrescribir este método permite un control más detallado sobre los eventos que se pasan al método notify.

Consulta a continuación diferentes ejemplos de uso de cómo se pueden usar los predicados para reducir el enfoque de un procesador.

Generar eventos

Supongamos que queremos generar un evento que indique el inicio de un flujo de IPF. Para hacerlo podemos llamar al método raise así:

        var testSpecEvent = new TestSpecEvent(PROCESSING_CONTEXT, "B");
        eventBus.raise(testSpecEvent);

Definir eventos personalizados

Para definir eventos personalizados, crea una clase de evento como subclase de IPFSystemEvent<T>, donde el tipo T es el tipo de carga útil que se publicará. Dependiendo del uso del nuevo evento, la carga útil no necesariamente necesita ser serializable, pero se recomienda que sea el caso ya que los eventos generalmente eventualmente necesitarán ser serializados sobre algún medio (cola de mensajes, archivo, RPC).

Documentar eventos

Existe una utilidad en el módulo ipf-system-events-api que recupera todas las subclases de IPFSystemEvent en el classpath y puede listar sus:

  • Nombre (el getSimpleName() de la clase)

  • Descripción (de la anotación @EventDescription)

  • Abstracto/no abstracto

  • Tipo de carga útil

Semántica del bus

Esta sección define el comportamiento de la API de eventos en situaciones específicas:

Situación Comportamiento

Se genera un evento antes de que un suscriptor se suscriba correctamente

El evento no se entrega al suscriptor

El suscriptor pierde una entrega de evento

El suscriptor no es notificado de nuevo (entrega como máximo una vez)

Semántica predeterminada del bus de eventos

Todos los eventos tienen un "source", definido de forma laxa como el nodo o la aplicación desde la cual se publicó un evento del sistema. El bus de eventos predeterminado de IPF busca un valor de Config (HOCON) llamado ipf.system-events.source, que en sí mismo es un marcador de posición para ipf.application.name.

Si ipf.system-events.source no se resuelve a un valor en tiempo de ejecución, entonces se usará el valor predeterminado, que será la salida de InetAddress.getLocalHost().getHostName(). Si eso lanza una excepción, el origen será UNKNOWN.

En la mayoría de los casos no necesitarás definir ipf.system-events.source, pero querrás asegurarte de que tu aplicación defina ipf.application.name. Si es necesario que el origen del evento del sistema sea diferente del nombre de la aplicación, deberás definir explícitamente ipf.system-events.source.

Tabla 1. Tabla que describe cómo se determina el origen del evento del sistema:
Valor de ipf.system-events.source Valor de ipf.application.name Hostname actual Origen

El valor predeterminado: ${?ipf.application.name}

application-name

host-name

application-name

explicitly-defined-source

application-name

host-name

explicitly-defined-source

El valor predeterminado: ${?ipf.application.name}

<does not exist>

host-name

host-name

El valor predeterminado: ${?ipf.application.name}

<does not exist>

<unknown or failed to get>

UNKNOWN

Escribir una implementación personalizada del bus de eventos

Si el DefaultEventBus en memoria existente definido en el paquete -impl es insuficiente, es posible implementar tu propio bus.

Hay un kit de compatibilidad (TCK) en el proyecto -api que permite a futuros implementadores del bus probar la corrección de su implementación.

La prueba está disponible en una versión test-jar del módulo -api. Crea una dependencia como esta:

<dependencies>
    <dependency>
        <groupId>com.iconsolutions.ipf.core.systemevents</groupId>
        <artifactId>ipf-system-events-api</artifactId>
        <version>${ipf-system-events.version}</version>
        <type>test-jar</type>
        <scope>test</scope>
    </dependency>
</dependencies>

El TCK es una clase de prueba JUnit abstracta - EventBusTestSpec - con el siguiente método abstracto:

    abstract EventBus getEventBus();

Haz que tu prueba extienda esta clase e implementa este método abstracto para permitir que la prueba se ejecute. También puedes agregar tus propios métodos @Test en esta clase (y otras) si deseas probar otros escenarios no cubiertos por el TCK.

Recuerda que tu clase de implementación del TCK debe terminar con la palabra Test para que sea detectada automáticamente por Maven Surefire Plugin