From 2456ae4ea94e36d352478c480b1445c1bd576fb7 Mon Sep 17 00:00:00 2001
From: Gavin King
Date: Thu, 24 Oct 2024 20:12:30 +0200
Subject: [PATCH] first rough proposal for events
see #373
Signed-off-by: Gavin King
---
.../jakarta/data/event/LifecycleEvent.java | 29 ++++++++++++++++++
.../jakarta/data/event/PostDeleteEvent.java | 30 +++++++++++++++++++
.../jakarta/data/event/PostInsertEvent.java | 30 +++++++++++++++++++
.../jakarta/data/event/PostUpdateEvent.java | 30 +++++++++++++++++++
.../jakarta/data/event/PreDeleteEvent.java | 30 +++++++++++++++++++
.../jakarta/data/event/PreInsertEvent.java | 30 +++++++++++++++++++
.../jakarta/data/event/PreUpdateEvent.java | 30 +++++++++++++++++++
.../java/jakarta/data/repository/Delete.java | 5 ++++
.../java/jakarta/data/repository/Insert.java | 5 ++++
.../java/jakarta/data/repository/Update.java | 5 ++++
spec/src/main/asciidoc/jakarta-ee.adoc | 7 +++++
11 files changed, 231 insertions(+)
create mode 100644 api/src/main/java/jakarta/data/event/LifecycleEvent.java
create mode 100644 api/src/main/java/jakarta/data/event/PostDeleteEvent.java
create mode 100644 api/src/main/java/jakarta/data/event/PostInsertEvent.java
create mode 100644 api/src/main/java/jakarta/data/event/PostUpdateEvent.java
create mode 100644 api/src/main/java/jakarta/data/event/PreDeleteEvent.java
create mode 100644 api/src/main/java/jakarta/data/event/PreInsertEvent.java
create mode 100644 api/src/main/java/jakarta/data/event/PreUpdateEvent.java
diff --git a/api/src/main/java/jakarta/data/event/LifecycleEvent.java b/api/src/main/java/jakarta/data/event/LifecycleEvent.java
new file mode 100644
index 000000000..6ceb08e10
--- /dev/null
+++ b/api/src/main/java/jakarta/data/event/LifecycleEvent.java
@@ -0,0 +1,29 @@
+package jakarta.data.event;
+
+/**
+ * Abstract supertype of events relating to lifecycle methods.
+ * In Jakarta EE, a bean may observe such events via CDI:
+ *
+ * void onInsertBook(@Observes PostInsertEvent<Book> bookInsertion) {
+ * Book book = bookInsertion.entity();
+ * ...
+ * }
+ *
+ *
+ * @param the entity type
+ */
+public abstract class LifecycleEvent {
+ private final E entity;
+
+ public LifecycleEvent(E entity) {
+ this.entity = entity;
+ }
+
+ /**
+ * The entity instance which was passed as an argument to
+ * the lifecycle method.
+ */
+ public E entity() {
+ return entity;
+ }
+}
diff --git a/api/src/main/java/jakarta/data/event/PostDeleteEvent.java b/api/src/main/java/jakarta/data/event/PostDeleteEvent.java
new file mode 100644
index 000000000..0d1d65e13
--- /dev/null
+++ b/api/src/main/java/jakarta/data/event/PostDeleteEvent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023,2024 Contributors to the Eclipse Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package jakarta.data.event;
+
+/**
+ * An event that occurs when an {@link jakarta.data.repository.Delete}
+ * method is called, and after the record was deleted from the datastore.
+ *
+ * @param the entity type
+ */
+public class PostDeleteEvent extends LifecycleEvent {
+ public PostDeleteEvent(E entity) {
+ super(entity);
+ }
+}
diff --git a/api/src/main/java/jakarta/data/event/PostInsertEvent.java b/api/src/main/java/jakarta/data/event/PostInsertEvent.java
new file mode 100644
index 000000000..d86689364
--- /dev/null
+++ b/api/src/main/java/jakarta/data/event/PostInsertEvent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023,2024 Contributors to the Eclipse Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package jakarta.data.event;
+
+/**
+ * An event that occurs when an {@link jakarta.data.repository.Insert}
+ * method is called, and after the record was inserted into the datastore.
+ *
+ * @param the entity type
+ */
+public class PostInsertEvent extends LifecycleEvent {
+ public PostInsertEvent(E entity) {
+ super(entity);
+ }
+}
diff --git a/api/src/main/java/jakarta/data/event/PostUpdateEvent.java b/api/src/main/java/jakarta/data/event/PostUpdateEvent.java
new file mode 100644
index 000000000..173f82814
--- /dev/null
+++ b/api/src/main/java/jakarta/data/event/PostUpdateEvent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023,2024 Contributors to the Eclipse Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package jakarta.data.event;
+
+/**
+ * An event that occurs when an {@link jakarta.data.repository.Update}
+ * method is called, and after the entity was updated in the datastore.
+ *
+ * @param the entity type
+ */
+public class PostUpdateEvent extends LifecycleEvent {
+ public PostUpdateEvent(E entity) {
+ super(entity);
+ }
+}
diff --git a/api/src/main/java/jakarta/data/event/PreDeleteEvent.java b/api/src/main/java/jakarta/data/event/PreDeleteEvent.java
new file mode 100644
index 000000000..0e4668a3e
--- /dev/null
+++ b/api/src/main/java/jakarta/data/event/PreDeleteEvent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023,2024 Contributors to the Eclipse Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package jakarta.data.event;
+
+/**
+ * An event that occurs when an {@link jakarta.data.repository.Delete}
+ * method is called, but before the record is deleted from the datastore.
+ *
+ * @param the entity type
+ */
+public class PreDeleteEvent extends LifecycleEvent {
+ public PreDeleteEvent(E entity) {
+ super(entity);
+ }
+}
diff --git a/api/src/main/java/jakarta/data/event/PreInsertEvent.java b/api/src/main/java/jakarta/data/event/PreInsertEvent.java
new file mode 100644
index 000000000..68cfabedc
--- /dev/null
+++ b/api/src/main/java/jakarta/data/event/PreInsertEvent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023,2024 Contributors to the Eclipse Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package jakarta.data.event;
+
+/**
+ * An event that occurs when an {@link jakarta.data.repository.Insert}
+ * method is called, but before the record is inserted into the datastore.
+ *
+ * @param the entity type
+ */
+public class PreInsertEvent extends LifecycleEvent {
+ public PreInsertEvent(E entity) {
+ super(entity);
+ }
+}
diff --git a/api/src/main/java/jakarta/data/event/PreUpdateEvent.java b/api/src/main/java/jakarta/data/event/PreUpdateEvent.java
new file mode 100644
index 000000000..3e4b5adae
--- /dev/null
+++ b/api/src/main/java/jakarta/data/event/PreUpdateEvent.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2023,2024 Contributors to the Eclipse Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ */
+package jakarta.data.event;
+
+/**
+ * An event that occurs when an {@link jakarta.data.repository.Update}
+ * method is called, but before the entity is updated in the datastore.
+ *
+ * @param the entity type
+ */
+public class PreUpdateEvent extends LifecycleEvent {
+ public PreUpdateEvent(E entity) {
+ super(entity);
+ }
+}
diff --git a/api/src/main/java/jakarta/data/repository/Delete.java b/api/src/main/java/jakarta/data/repository/Delete.java
index f2d1a8e36..c3ca93355 100644
--- a/api/src/main/java/jakarta/data/repository/Delete.java
+++ b/api/src/main/java/jakarta/data/repository/Delete.java
@@ -59,6 +59,11 @@
* if the entity with a matching identifier does not have a matching version, the annotated method must raise
* {@link jakarta.data.exceptions.OptimisticLockingFailureException}.
*
+ *
+ * An event of type {@link jakarta.data.event.PreDeleteEvent} must be raised by the annotated method before the record
+ * is deleted. An event of type {@link jakarta.data.event.PostDeleteEvent} must be raised by the annotated method after
+ * the record was successfully deleted.
+ *
*
* Alternatively, the {@code Delete} annotation may be used to annotate a repository method with no parameter of
* entity type. Then the repository method is interpreted as a parameter-based automatic query method. The entity type
diff --git a/api/src/main/java/jakarta/data/repository/Insert.java b/api/src/main/java/jakarta/data/repository/Insert.java
index 5112adf7c..e40f961f5 100644
--- a/api/src/main/java/jakarta/data/repository/Insert.java
+++ b/api/src/main/java/jakarta/data/repository/Insert.java
@@ -66,6 +66,11 @@
* then the annotated method must raise {@link jakarta.data.exceptions.EntityExistsException}.
* If the database follows the BASE model, or uses an append model to write data, this exception is not thrown.
*
+ *
+ * An event of type {@link jakarta.data.event.PreInsertEvent} must be raised by the annotated method before the record
+ * is inserted. An event of type {@link jakarta.data.event.PostInsertEvent} must be raised by the annotated method after
+ * the record was successfully inserted.
+ *
* Annotations such as {@code @Find}, {@code @Query}, {@code @Insert}, {@code @Update}, {@code @Delete}, and
* {@code @Save} are mutually-exclusive. A given method of a repository interface may have at most one {@code @Find}
* annotation, lifecycle annotation, or query annotation.
diff --git a/api/src/main/java/jakarta/data/repository/Update.java b/api/src/main/java/jakarta/data/repository/Update.java
index 3045ec97f..f8315ef0d 100644
--- a/api/src/main/java/jakarta/data/repository/Update.java
+++ b/api/src/main/java/jakarta/data/repository/Update.java
@@ -67,6 +67,11 @@
* If the database follows the BASE model, or uses an append model to write data, the annotated method behaves the same
* as the {@code @Insert} method.
*
+ *
+ * An event of type {@link jakarta.data.event.PreUpdateEvent} must be raised by the annotated method before the record
+ * is updated. An event of type {@link jakarta.data.event.PostUpdateEvent} must be raised by the annotated method after
+ * the record was successfully updated.
+ *
* Annotations such as {@code @Find}, {@code @Query}, {@code @Insert}, {@code @Update}, {@code @Delete}, and
* {@code @Save} are mutually-exclusive. A given method of a repository interface may have at most one {@code @Find}
* annotation, lifecycle annotation, or query annotation.
diff --git a/spec/src/main/asciidoc/jakarta-ee.adoc b/spec/src/main/asciidoc/jakarta-ee.adoc
index bc51e27b8..8d2b6da91 100644
--- a/spec/src/main/asciidoc/jakarta-ee.adoc
+++ b/spec/src/main/asciidoc/jakarta-ee.adoc
@@ -67,6 +67,8 @@ This section discusses interoperability with related Jakarta EE footnote:[Jakart
Contexts and Dependency Injection footnote:[Jakarta Contexts and Dependency Injection 4.1, https://jakarta.ee/specifications/cdi/4.1/] (CDI) is a specification in the Jakarta EE Core profile that provides a powerful and flexible dependency injection framework for Java applications. CDI provides a programming model based around decoupled components with container-managed lifecycles and container-injected dependencies, enabling loose coupling and promoting modular and reusable code.
+==== CDI Dependency Injection
+
In the Jakarta EE environment, CDI allows implementations of Jakarta Data repositories to be made available for injection via the `@Inject` annotation.
The following example illustrates this integration:
@@ -115,6 +117,11 @@ This fragment shows how the application might request injection of a `CarReposit
This integration between CDI and Jakarta Data allows for seamless management of repository instances within Jakarta EE applications.
+==== CDI Events
+
+A repository implementation may raise CDI events.
+In the Jakarta EE environment, the repository implementation is required the raise the event types defined in the package `jakarta.data.event` when lifecycle methods annotated `@Insert`, `@Update`, or `@Delete` are invoked, as specified by the API documentation of these annotations.
+
==== CDI Extensions for Jakarta Data providers
In environments where CDI Full or CDI Lite is available, Jakarta Data providers can make use of a CDI extension--an implementation of `jakarta.enterprise.inject.spi.Extension` or `jakarta.enterprise.inject.build.compatible.spi.BuildCompatibleExtension`--to discover interfaces annotated with `@Repository` and make their implementations available for injection.