Module org.apache.sis.storage
Package org.apache.sis.storage.event

# Class StoreListeners

Object
StoreListeners

All Implemented Interfaces:
  `Localized`

public class StoreListeners extends Object implements Localized
Holds a list of `StoreListener` instances and provides convenience methods for sending events. This is a helper class for `DataStore` and `Resource` implementations.

Observers can add listeners for being notified about events, and producers can invoke one of the `warning(…)` and other methods for emitting events.

## Warning events

All warnings are given to the listeners as `LogRecord` instances (this allows localizable messages and additional information like stack trace, timestamp, etc.). This `StoreListeners` class provides convenience methods like `warning(String, Exception)`, which build `LogRecord` from an exception or from a string. But all those `warning(…)` methods ultimately delegate to `warning(LogRecord, Filter)`, thus providing a single point that subclasses can override. When a warning is emitted, the default behavior is:

- Notify all listeners that are registered for a given `WarningEvent` type in this `StoreListeners` and in the parent resource or data store. Each listener will be notified only once, even if the same listener is registered in two or more places.
- If previous step found no listener registered for `WarningEvent`, then log the warning in the first logger found in following choices:
  1. The logger specified by `LogRecord​.getLoggerName()` if non-null.
  2. Otherwise the logger specified by `DataStoreProvider​.getLogger()` if the provider can be found.
  3. Otherwise a logger whose name is the source `DataStore` package name.

## Thread safety

The same `StoreListeners` instance can be safely used by many threads without synchronization on the part of the caller. Subclasses should make sure that any overridden methods remain safe to call from multiple threads.

Since:
  1.0

- ## Constructor Summary

  Constructors
  Constructor
  Description
  `StoreListeners(StoreListeners parent, Resource source)`
  Creates a new instance with the given parent and initially no listener.

- ## Method Summary

  Modifier and Type
  Method
  Description
  `<E extends StoreEvent>
  void`
  `addListener(Class<E> eventType, StoreListener<? super E> listener)`
  Registers a listener to notify when the specified kind of event occurs.
  `void`
  `close()`
  Sends a `CloseEvent` to all listeners registered for that kind of event, then discards listeners in this instance (but not in parents).
  `<E extends StoreEvent>
  boolean`
  `fire(Class<E> eventType, E event)`
  Sends the given event to all listeners registered for the given type or for a super-type.
  `Locale`
  `getLocale()`
  Returns the locale used by this manager, or `null` if unspecified.
  `Logger`
  `getLogger()`
  Returns the logger where to send warnings when no other destination is specified.
  `Optional<StoreListeners>`
  `getParent()`
  Returns the parent set of listeners that are notified in addition to this set of listeners.
  `Resource`
  `getSource()`
  Returns the source of events.
  `String`
  `getSourceName()`
  Returns a short name or label for the source.
  `<E extends StoreEvent>
  boolean`
  `hasListener(Class<E> eventType, StoreListener<? super E> listener)`
  Returns `true` if the given listener is registered for the given type or a super-type.
  `boolean`
  `hasListeners(Class<? extends StoreEvent> eventType)`
  Returns `true` if at least one listener is registered for the given type or a super-type.
  `<E extends StoreEvent>
  void`
  `removeListener(Class<E> eventType, StoreListener<? super E> listener)`
  Unregisters a listener previously added for the given type of events.
  `void`
  `setUsableEventTypes(Class<?>... permitted)`
  Notifies this `StoreListeners` that only events of the specified types will be fired.
  `String`
  `toString()`
  Returns a string representation for debugging purposes.
  `void`
  `useReadOnlyEvents()`
  Notifies this `StoreListeners` that it will fire only `WarningEvent`s and `CloseEvent`.
  `void`
  `warning(Exception exception)`
  Reports a warning described by the given exception.
  `void`
  `warning(String message)`
  Reports a warning described by the given message.
  `void`
  `warning(String message, Exception exception)`
  Reports a warning described by the given message and exception.
  `void`
  `warning(Level level, String message, Exception exception)`
  Reports a warning at the given level represented by the given message and exception.
  `void`
  `warning(LogRecord description)`
  Reports a warning described by the given log record.
  `void`
  `warning(LogRecord description, Filter onUnhandled)`
  Reports a warning described by the given log record.

  ### Methods inherited from class Object

  `clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait`

- ## Constructor Details

  - ### StoreListeners

    public StoreListeners(StoreListeners parent, Resource source)
    Creates a new instance with the given parent and initially no listener. The parent is typically the listeners of the `DataStore` that created a resource. When an event is fired, listeners registered in the parent will be notified as well as listeners registered in this `StoreListeners`. Each listener will be notified only once even if it has been registered in two places.

    #### Permitted even types

    If the parent restricts the usable event types to a subset of `StoreEvent` subtypes, then this `StoreListeners` inherits those restrictions. The list of usable types can be rectricted more but cannot be relaxed.
    Parameters:
      `parent` - the manager to notify in addition to this manager, or `null` if none.
      `source` - the source of events. Cannot be null.

- ## Method Details

  - ### getParent

    public Optional<StoreListeners> getParent()
    Returns the parent set of listeners that are notified in addition to this set of listeners. This is the value of the `parent` argument given to the constructor.
    Returns:
      parent set of listeners that are notified in addition to this set of listeners.
    Since:
      1.3

  - ### getSource

    public Resource getSource()
    Returns the source of events. This value is specified at construction time.
    Returns:
      the source of events (never `null`).

  - ### getSourceName

    public String getSourceName()
    Returns a short name or label for the source. It may be the name of the file opened by a data store. The returned name can be useful in warning messages for identifying the problematic source.

    The default implementation fetches a name from the data store, or returns an arbitrary name if no better name is found.

    Returns:
      a short name of label for the source (never `null`).
    See Also:
      - `DataStore​.getDisplayName()`

  - ### getLocale

    public Locale getLocale()
    Returns the locale used by this manager, or `null` if unspecified. That locale is typically inherited from the `DataStore` locale and can be used for formatting messages.
    Specified by:
      `getLocale` in interface `Localized`
    Returns:
      the locale for messages (typically specified by the data store), or `null` if unknown.
    See Also:
      - `DataStore​.getLocale()`
      - `StoreEvent​.getLocale()`

  - ### getLogger

    public Logger getLogger()
    Returns the logger where to send warnings when no other destination is specified. This method tries to get the logger from `DataStoreProvider​.getLogger()`. If that logger cannot be found, then this method infers a logger name from the package name of the source data store. The returned logger is used when:
    - no listener has been registered for the `WarningEvent` type, and
    - the `LogRecord` does not specify a logger.
    Returns:
      the logger where to send the warnings when there is no other destination.
    Since:
      1.1
    See Also:
      - `DataStoreProvider​.getLogger()`

  - ### warning

    public void warning(String message)
    Reports a warning described by the given message.

    This method is a shortcut for `warning(Level​.WARNING, message, null)`.

    Parameters:
      `message` - the warning message to report.

  - ### warning

    public void warning(Exception exception)
    Reports a warning described by the given exception. The exception stack trace will be omitted at logging time for avoiding to pollute console output (keeping in mind that this method should be invoked only for non-fatal warnings). See below for more explanation.

    This method is a shortcut for `warning(Level​.WARNING, null, exception)`.

    Parameters:
      `exception` - the exception to report.

  - ### warning

    public void warning(String message, Exception exception)
    Reports a warning described by the given message and exception. At least one of `message` and `exception` arguments shall be non-null. If both are non-null, then the exception message will be concatenated after the given message. If the exception is non-null, its stack trace will be omitted at logging time for avoiding to pollute console output (keeping in mind that this method should be invoked only for non-fatal warnings). See below for more explanation.

    This method is a shortcut for `warning(Level​.WARNING, message, exception)`.

    Parameters:
      `message` - the warning message to report, or `null` if none.
      `exception` - the exception to report, or `null` if none.

  - ### warning

    public void warning(Level level, String message, Exception exception)
    Reports a warning at the given level represented by the given message and exception. At least one of `message` and `exception` arguments shall be non-null. If both are non-null, then the exception message will be concatenated after the given message.

    #### Stack trace omission

    If there are no registered listeners for the `WarningEvent` type, then the `LogRecord` will be sent to a `Logger` but without the stack trace. This is done that way because stack traces consume lot of space in the logging files, while being considered implementation details in the context of `StoreListeners` (on the assumption that the logging message provides sufficient information).
    Parameters:
      `level` - the warning level.
      `message` - the message to log, or `null` if none.
      `exception` - the exception to log, or `null` if none.

  - ### warning

    public void warning(LogRecord description)
    Reports a warning described by the given log record. Invoking this method is equivalent to invoking `warning(LogRecord, Filter)` with a null filter.
    Parameters:
      `description` - warning details provided as a log record.

  - ### warning

    public void warning(LogRecord description, Filter onUnhandled)
    Reports a warning described by the given log record. The default implementation forwards the given record to one of the following destinations, in preference order:
    1. `StoreListener​.eventOccured(new WarningEvent(source, record))` on all listeners registered for this kind of event.
    2. `onUnhandled​.isLoggable(description)` if above step found no listener and the `onUnhandled` filter is non-null.
    3. `Logger​.getLogger(record​.loggerName).log(record)` if the filter in above step returned `true` (or if the filter is null). In that case, `loggerName` is one of the following:
       - `record.getLoggerName()` if that value is non-null.
       - Otherwise the value of `DataStoreProvider​.getLogger()` if the provider is found.
       - Otherwise the source `DataStore` package name.
    Parameters:
      `description` - warning details provided as a log record.
      `onUnhandled` - filter invoked if the record has not been handled by a `StoreListener`, or `null` if none. This filter determines whether the record should be sent to the logger returned by `getLogger()`.
    Since:
      1.2

  - ### fire

    public <E extends StoreEvent> boolean fire(Class<E> eventType, E event)
    Sends the given event to all listeners registered for the given type or for a super-type. This method first notifies the listeners registered in this `StoreListeners`, then notifies listeners registered in parent `StoreListeners`s. Each listener will be notified only once even if it has been registered many times.

    If one or many `StoreListener​.eventOccured(StoreEvent)` implemetations throw a `RuntimeException`, those exceptions will be collected and reported in a single log record. Runtime exceptions in listeners do not cause this method to fail.

    Type Parameters:
      `E` - compile-time value of the `eventType` argument.
    Parameters:
      `eventType` - the type of the event to be fired.
      `event` - the event to fire.
    Returns:
      `true` if the event has been sent to at least one listener.
    Throws:
      `IllegalArgumentException` - if the given event type is not one of the types of events that this `StoreListeners` can fire.
    Since:
      1.3
    See Also:
      - `close()`

  - ### addListener

    public <E extends StoreEvent> void addListener(Class<E> eventType, StoreListener<? super E> listener)
    Registers a listener to notify when the specified kind of event occurs. Registering a listener for a given `eventType` also register the listener for all event sub-types. The same listener can be registered many times, but its `StoreListener​.eventOccured(StoreEvent)` method will be invoked only once per event. This filtering applies even if the listener is registered on different resources in the same tree, for example a parent and its children.

    #### Warning events

    If `eventType` is assignable from `WarningEvent.class`, then registering that listener turns off logging of warning messages for this manager. This side-effect is applied on the assumption that the registered listener will handle warnings in its own way, for example by showing warnings in a widget.
    Type Parameters:
      `E` - compile-time value of the `eventType` argument.
    Parameters:
      `eventType` - type of `StoreEvent` to listen (cannot be `null`).
      `listener` - listener to notify about events.
    See Also:
      - `Resource​.addListener(Class, StoreListener)`

  - ### removeListener

    public <E extends StoreEvent> void removeListener(Class<E> eventType, StoreListener<? super E> listener)
    Unregisters a listener previously added for the given type of events. The `eventType` must be the exact same class than the one given to the `addListener(…)` method; this method does not remove listeners registered for subclasses and does not remove listeners registered in parent manager.

    If the same listener has been registered many times for the same even type, then this method removes only the most recent registration. In other words if `addListener(type, ls)` has been invoked twice, then `removeListener(type, ls)` needs to be invoked twice in order to remove all instances of that listener. If the given listener is not found, then this method does nothing (no exception is thrown).

    #### Warning events

    If `eventType` is `WarningEvent.class` and if, after this method invocation, there are no remaining listeners for warning events, then this `StoreListeners` will send future warnings to the loggers.
    Type Parameters:
      `E` - compile-time value of the `eventType` argument.
    Parameters:
      `eventType` - type of `StoreEvent` which were listened (cannot be `null`).
      `listener` - listener to stop notifying about events.
    See Also:
      - `Resource​.removeListener(Class, StoreListener)`

  - ### hasListener

    public <E extends StoreEvent> boolean hasListener(Class<E> eventType, StoreListener<? super E> listener)
    Returns `true` if the given listener is registered for the given type or a super-type. This method may unconditionally return `false` if the given type of event is never fired by this `StoreListeners`, because calls to `addListener(eventType, …)` are free to ignore the listeners for those types.
    Type Parameters:
      `E` - compile-time value of the `eventType` argument.
    Parameters:
      `eventType` - type of `StoreEvent` to check (cannot be `null`).
      `listener` - listener to check for registration.
    Returns:
      `true` if this object contains the specified listener for given event type, `false` otherwise.
    Since:
      1.3

  - ### hasListeners

    public boolean hasListeners(Class<? extends StoreEvent> eventType)
    Returns `true` if at least one listener is registered for the given type or a super-type. This method may unconditionally return `false` if the given type of event is never fired by this `StoreListeners`, because calls to `addListener(eventType, …)` are free to ignore the listeners for those types.
    Parameters:
      `eventType` - the type of event for which to check listener presence.
    Returns:
      `true` if this object contains at least one listener for given event type, `false` otherwise.

  - ### setUsableEventTypes

    public void setUsableEventTypes(Class<?>... permitted)
    Notifies this `StoreListeners` that only events of the specified types will be fired. With this knowledge, `StoreListeners` will not retain any reference to listeners that are not listening to events of those types or to events of a parent type. This restriction allows the garbage collector to dispose unnecessary listeners.

    The argument shall enumerate all permitted types, including sub-types (they are not automatically accepted). All types given in argument must be types that were accepted before the invocation of this method. In other words, this method can be invoked for reducing the set of permitted types but not for expanding it.

    #### Example

    an application may unconditionally register listeners for being notified about additions of new data. If a `DataStore` implementation is read-only, then such listeners would never receive any notification. As a slight optimization, the `DataStore` constructor can invoke this method for example as below:

        listeners.setUsableEventTypes(WarningEvent.class);

    With this configuration, calls to `addListener(DataAddedEvent​.class, foo)` will be ignored, thus avoiding this instance to retain a never-used reference to the `foo` listener.
    Parameters:
      `permitted` - type of events that are permitted. Permitted sub-types shall be explicitly enumerated as well.
    Throws:
      `IllegalArgumentException` - if one of the given types was not permitted before invocation of this method.
    Since:
      1.2
    See Also:
      - `useReadOnlyEvents()`

  - ### useReadOnlyEvents

    public void useReadOnlyEvents()
    Notifies this `StoreListeners` that it will fire only `WarningEvent`s and `CloseEvent`. This method is a shortcut for `setUsableEventTypes(WarningEvent​.class, CloseEvent​.class)}`, provided because frequently used by read-only data store implementations.

    Declaring a root resource (typically a `DataStore`) as read-only implies that all children (e.g. components of an aggregate) are also read-only.

    Since:
      1.3
    See Also:
      - `setUsableEventTypes(Class...)`
      - `WarningEvent`
      - `CloseEvent`

  - ### close

    public void close()
    Sends a `CloseEvent` to all listeners registered for that kind of event, then discards listeners in this instance (but not in parents). Because listeners are discarded, invoking this method many times on the same instance has no effect after the first invocation.

    If one or many `StoreListener​.eventOccured(StoreEvent)` implementations throw a `RuntimeException`, those exceptions will be collected and reported in a single log record. Runtime exceptions in listeners do not cause this method to fail.

    Since:
      1.3
    See Also:
      - `fire(Class, StoreEvent)`
      - `DataStore​.close()`
      - `CloseEvent`

  - ### toString

    public String toString()
    Returns a string representation for debugging purposes.
    Overrides:
      `toString` in class `Object`
    Returns:
      a debug string.