Skip to content

Commit

Permalink
Merge pull request #876 from gavinking/events
Browse files Browse the repository at this point in the history
first rough proposal for events
  • Loading branch information
otaviojava authored Nov 21, 2024
2 parents 6ee0c6d + fd91006 commit a8fda6c
Show file tree
Hide file tree
Showing 11 changed files with 291 additions and 0 deletions.
89 changes: 89 additions & 0 deletions api/src/main/java/jakarta/data/event/LifecycleEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*
* Copyright (c) 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;

/**
* <p>Abstract supertype of events relating to lifecycle methods.</p>
* <p>In Jakarta EE, a bean may observe such events via CDI:</p>
* <pre>
* void onInsertBook(&#64;Observes PostInsertEvent&lt;Book&gt; bookInsertion) {
* Book book = bookInsertion.entity();
* ...
* }
* </pre>
* <p>As usual for a CDI event, an observer of a {@code LifecycleEvent}
* is notified synchronously and immediately by default. An observer may
* elect to receive notifications during a phase of the transaction
* completion cycle by explicitly specifying a {@code TransactionPhase},
* for example:</p>
* <ul>
* <li>{@code @Observes(during=BEFORE_COMPLETION)} to be notified just
* before transaction completion, or</li>
* <li>{@code @Observes(during=AFTER_SUCCESS)} to be notified after
* successful completion of the transaction.</li>
* </ul>
* <p>An observer may choose to be notified asynchronously using
* {@code @ObservesAsync}. However, the mutable state held by the
* {@link #entity} is not in general safe for concurrent access,
* and so portable applications must not use {@code @ObservesAsync}
* to observe a {@code LifecycleEvent}. If the state of an entity is
* accessed from an asynchronous observer method for a lifecycle
* event, the resulting behavior is undefined and unportable.</p>
*
*
* @param <E> the entity type
*/
public abstract class LifecycleEvent<E> {
private final E entity;

public LifecycleEvent(E entity) {
this.entity = entity;
}

/**
* The entity which is being processed by the lifecycle method.
* <ul>
* <li>For a {@code Pre} event, this is always the instance
* which was passed as an argument to the lifecycle
* method, and its state reflects the state of the
* entity before execution of the lifecycle method.</li>
* <li>For a {@code Post} event, it may or may not be
* identical to the object passed as an argument
* to the lifecycle method, and it may or may not be
* identical to the instance returned by the lifecycle
* method, if any. If the state of the entity changes
* as a result of execution of the lifecycle method,
* those changes may or may not be reflected in the
* entity returned by this method.</li>
* </ul>
* <p>
* Thus, a portable application should not assume that the
* state of the entity in a {@code Post} event faithfully
* reflects the current state of the corresponding record
* in the database.
* <p>
* A portable application must not mutate the state of the
* entity instance returned by this method. If the state
* of the entity instance is mutated while event listeners
* are being notified, the resulting behavior is undefined
* and unportable.
*/
public E entity() {
return entity;
}
}
30 changes: 30 additions & 0 deletions api/src/main/java/jakarta/data/event/PostDeleteEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 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 a {@link jakarta.data.repository.Delete}
* lifecycle method is called, after each record is deleted from the datastore.
*
* @param <E> the entity type
*/
public class PostDeleteEvent<E> extends LifecycleEvent<E> {
public PostDeleteEvent(E entity) {
super(entity);
}
}
30 changes: 30 additions & 0 deletions api/src/main/java/jakarta/data/event/PostInsertEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 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}
* lifecycle method is called, after each record is inserted into the datastore.
*
* @param <E> the entity type
*/
public class PostInsertEvent<E> extends LifecycleEvent<E> {
public PostInsertEvent(E entity) {
super(entity);
}
}
30 changes: 30 additions & 0 deletions api/src/main/java/jakarta/data/event/PostUpdateEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 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}
* lifecycle method is called, after each entity is updated in the datastore.
*
* @param <E> the entity type
*/
public class PostUpdateEvent<E> extends LifecycleEvent<E> {
public PostUpdateEvent(E entity) {
super(entity);
}
}
30 changes: 30 additions & 0 deletions api/src/main/java/jakarta/data/event/PreDeleteEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 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 a {@link jakarta.data.repository.Delete}
* lifecycle method is called, but before each record is deleted from the datastore.
*
* @param <E> the entity type
*/
public class PreDeleteEvent<E> extends LifecycleEvent<E> {
public PreDeleteEvent(E entity) {
super(entity);
}
}
30 changes: 30 additions & 0 deletions api/src/main/java/jakarta/data/event/PreInsertEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 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}
* lifecycle method is called, but before each record is inserted into the datastore.
*
* @param <E> the entity type
*/
public class PreInsertEvent<E> extends LifecycleEvent<E> {
public PreInsertEvent(E entity) {
super(entity);
}
}
30 changes: 30 additions & 0 deletions api/src/main/java/jakarta/data/event/PreUpdateEvent.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 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}
* lifecycle method is called, but before each entity is updated in the datastore.
*
* @param <E> the entity type
*/
public class PreUpdateEvent<E> extends LifecycleEvent<E> {
public PreUpdateEvent(E entity) {
super(entity);
}
}
5 changes: 5 additions & 0 deletions api/src/main/java/jakarta/data/repository/Delete.java
Original file line number Diff line number Diff line change
Expand Up @@ -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}.
* </p>
* <p>
* An event of type {@link jakarta.data.event.PreDeleteEvent} must be raised by the annotated lifecycle
* method before each record is deleted. An event of type {@link jakarta.data.event.PostDeleteEvent}
* must be raised by the annotated lifecycle method after each record is successfully deleted.
* </p>
*
* <p>Alternatively, the {@code Delete} annotation may be used to annotate a repository method with no parameter of an
* entity type. Then the repository method is interpreted as a parameter-based automatic query method. The entity type
Expand Down
5 changes: 5 additions & 0 deletions api/src/main/java/jakarta/data/repository/Insert.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* </p>
* <p>
* An event of type {@link jakarta.data.event.PreInsertEvent} must be raised by the annotated lifecycle
* method before each record is inserted. An event of type {@link jakarta.data.event.PostInsertEvent}
* must be raised by the annotated lifecycle method after each record is successfully inserted.
* </p>
* <p>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.
Expand Down
5 changes: 5 additions & 0 deletions api/src/main/java/jakarta/data/repository/Update.java
Original file line number Diff line number Diff line change
Expand Up @@ -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.
* </p>
* <p>
* An event of type {@link jakarta.data.event.PreUpdateEvent} must be raised by the annotated lifecycle
* method before each record is updated. An event of type {@link jakarta.data.event.PostUpdateEvent}
* must be raised by the annotated lifecycle method after each record is successfully updated.
* </p>
* <p>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.
Expand Down
7 changes: 7 additions & 0 deletions spec/src/main/asciidoc/jakarta-ee.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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 to 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.
Expand Down

0 comments on commit a8fda6c

Please sign in to comment.