gnodet commented on PR #13011:
URL: https://github.com/apache/maven/pull/13011#issuecomment-5561988438
## API Design Suggestion: Event/Listener Hierarchy
The current PR introduces `RepositoryListener` / `RepositoryEvent` as
standalone types with separate registration methods on `Session`. This works,
but it creates an inconsistency with the existing `Listener` / `Event` API
(build lifecycle events) — two parallel, unrelated listener systems on the same
`Session` with different design patterns:
- `Listener` uses `@FunctionalInterface` single-dispatch (`onEvent(Event)`)
— consumers must switch on `EventType`
- `RepositoryListener` uses typed `default` callbacks
(`artifactDownloading()`, etc.) — much better ergonomics
**Proposed hierarchy** for 4.1.0: introduce `Event` and `Listener` as base
marker types, with `ExecutionEvent`/`ExecutionListener` (build lifecycle) and
`RepositoryEvent`/`RepositoryListener` (repository operations) as typed
specializations.
### Event hierarchy
```java
@Experimental @Immutable
public interface Event {
@Nonnull Session session();
}
@Experimental @Immutable
public interface ExecutionEvent extends Event {
@Nonnull ExecutionEventType type();
@Nonnull Optional<Project> project();
@Nonnull Optional<MojoExecution> mojoExecution();
@Nonnull Optional<Exception> exception();
}
@Experimental @Immutable
public interface RepositoryEvent extends Event {
@Nonnull RepositoryEventType type();
@Nonnull Optional<Artifact> artifact();
@Nonnull Optional<RepositoryMetadata> metadata();
@Nonnull Optional<Path> path();
@Nonnull Optional<Repository> repository();
@Nonnull Optional<Exception> exception();
@Nonnull List<Exception> exceptions();
@Nonnull Optional<RequestTrace> trace();
}
```
### Listener hierarchy
```java
@Experimental @Consumer
public interface Listener {
/** @deprecated Implement ExecutionListener or RepositoryListener
instead. */
@Deprecated
default void onEvent(@Nonnull Event event) {}
}
@Experimental @Consumer
public interface ExecutionListener extends Listener {
default void sessionStarted(@Nonnull ExecutionEvent event) {}
default void sessionEnded(@Nonnull ExecutionEvent event) {}
default void projectDiscoveryStarted(@Nonnull ExecutionEvent event) {}
default void projectStarted(@Nonnull ExecutionEvent event) {}
default void projectSucceeded(@Nonnull ExecutionEvent event) {}
default void projectFailed(@Nonnull ExecutionEvent event) {}
default void projectSkipped(@Nonnull ExecutionEvent event) {}
default void mojoStarted(@Nonnull ExecutionEvent event) {}
default void mojoSucceeded(@Nonnull ExecutionEvent event) {}
default void mojoFailed(@Nonnull ExecutionEvent event) {}
default void mojoSkipped(@Nonnull ExecutionEvent event) {}
default void forkStarted(@Nonnull ExecutionEvent event) {}
default void forkSucceeded(@Nonnull ExecutionEvent event) {}
default void forkFailed(@Nonnull ExecutionEvent event) {}
default void forkedProjectStarted(@Nonnull ExecutionEvent event) {}
default void forkedProjectSucceeded(@Nonnull ExecutionEvent event) {}
default void forkedProjectFailed(@Nonnull ExecutionEvent event) {}
}
@Experimental @Consumer
public interface RepositoryListener extends Listener {
default void artifactDescriptorInvalid(@Nonnull RepositoryEvent event) {}
default void artifactDescriptorMissing(@Nonnull RepositoryEvent event) {}
default void metadataInvalid(@Nonnull RepositoryEvent event) {}
default void artifactResolving(@Nonnull RepositoryEvent event) {}
default void artifactResolved(@Nonnull RepositoryEvent event) {}
// ... etc (19 typed callbacks, as in this PR)
}
```
### Session impact
Single registration point — no overloaded methods needed:
```java
// Session keeps ONE set of listener methods for both types:
void registerListener(@Nonnull Listener listener);
void unregisterListener(@Nonnull Listener listener);
Collection<Listener> getListeners();
```
The dispatcher routes via `instanceof ExecutionListener` / `instanceof
RepositoryListener`. A listener can even implement both.
### Benefits
- **Uniform design** — both event families use typed `default` callbacks, no
more `@FunctionalInterface` single-dispatch
- **Single registration** — no separate
`registerListener(RepositoryListener)` / `getRepositoryListeners()` on `Session`
- **Extensible** — future event categories (transfer, toolchain) just add
`XxxEvent extends Event` + `XxxListener extends Listener`, no `Session` changes
- **Backward compatible** — old `Listener.onEvent()` stays as a `@Deprecated
default`, existing consumers keep compiling
- **Noun-style accessors** on new types (consistent with #13036),
`@Immutable` events
- `EventType` → `ExecutionEventType` for symmetry with `RepositoryEventType`
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]