From 7a0c932c343c0c581062bb78b71ef9b17b830778 Mon Sep 17 00:00:00 2001
From: Martin Kouba
Date: Tue, 29 Nov 2016 10:41:17 +0100
Subject: [PATCH] CDI-650 Introduce asynchronous event notification options
(#337)
---
.../java/javax/enterprise/event/Event.java | 6 +-
.../event/ImmutableNotificationOptions.java | 76 ++++++++++++++++
.../enterprise/event/NotificationOptions.java | 86 +++++++++++++++++++
.../test/event/NotificationOptionsTest.java | 44 ++++++++++
spec/src/main/asciidoc/core/events.asciidoc | 5 +-
5 files changed, 213 insertions(+), 4 deletions(-)
create mode 100644 api/src/main/java/javax/enterprise/event/ImmutableNotificationOptions.java
create mode 100644 api/src/main/java/javax/enterprise/event/NotificationOptions.java
create mode 100644 api/src/test/java/org/jboss/cdi/api/test/event/NotificationOptionsTest.java
diff --git a/api/src/main/java/javax/enterprise/event/Event.java b/api/src/main/java/javax/enterprise/event/Event.java
index e330f3bda..a0e09044e 100644
--- a/api/src/main/java/javax/enterprise/event/Event.java
+++ b/api/src/main/java/javax/enterprise/event/Event.java
@@ -20,6 +20,7 @@
import java.lang.annotation.Annotation;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.Executor;
+
import javax.enterprise.util.TypeLiteral;
/**
@@ -129,7 +130,7 @@ public interface Event {
*
*
* @param event the event object
- * @param executor a custom executor to execute asynchronous event
+ * @param options the notification options
* @return a {@link CompletionStage} allowing further pipeline composition on the asynchronous operation.
* Default asynchronous execution facility is container specific.
* If any observer notified by this event throws an exception
@@ -140,7 +141,7 @@ public interface Event {
*
* @since 2.0
*/
- public CompletionStage fireAsync(U event, Executor executor);
+ public CompletionStage fireAsync(U event, NotificationOptions options);
/**
*
@@ -181,4 +182,5 @@ public interface Event {
* is not a qualifier type
*/
public Event select(TypeLiteral subtype, Annotation... qualifiers);
+
}
diff --git a/api/src/main/java/javax/enterprise/event/ImmutableNotificationOptions.java b/api/src/main/java/javax/enterprise/event/ImmutableNotificationOptions.java
new file mode 100644
index 000000000..784b095e8
--- /dev/null
+++ b/api/src/main/java/javax/enterprise/event/ImmutableNotificationOptions.java
@@ -0,0 +1,76 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2016, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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.
+ */
+package javax.enterprise.event;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+/**
+ * The immutable implementation of {@link NotificationOptions}.
+ *
+ * @author Martin Kouba
+ *
+ */
+class ImmutableNotificationOptions implements NotificationOptions {
+
+ private final Executor executor;
+
+ private final Map options;
+
+ private ImmutableNotificationOptions(Executor executor, Map options) {
+ this.executor = executor;
+ this.options = new HashMap<>(options);
+ }
+
+ @Override
+ public Executor getExecutor() {
+ return executor;
+ }
+
+ @Override
+ public Object get(String name) {
+ return options.get(name);
+ }
+
+ static class Builder implements javax.enterprise.event.NotificationOptions.Builder {
+
+ private Executor executor;
+
+ private Map options;
+
+ Builder() {
+ this.options = new HashMap<>();
+ }
+
+ public Builder setExecutor(Executor executor) {
+ this.executor = executor;
+ return this;
+ }
+
+ public Builder set(String name, Object value) {
+ options.put(name, value);
+ return this;
+ }
+
+ public ImmutableNotificationOptions build() {
+ return new ImmutableNotificationOptions(executor, options);
+ }
+
+ }
+
+}
diff --git a/api/src/main/java/javax/enterprise/event/NotificationOptions.java b/api/src/main/java/javax/enterprise/event/NotificationOptions.java
new file mode 100644
index 000000000..f2a8c63f7
--- /dev/null
+++ b/api/src/main/java/javax/enterprise/event/NotificationOptions.java
@@ -0,0 +1,86 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2016, Red Hat, Inc., and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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.
+ */
+package javax.enterprise.event;
+
+import java.util.concurrent.Executor;
+
+/**
+ * Notification options are used to configure observer notification.
+ *
+ * @author Martin Kouba
+ * @see Event#fireAsync(Object, NotificationOptions)
+ * @since 2.0
+ */
+public interface NotificationOptions {
+
+ /**
+ *
+ * @return the executor used to execute an asynchronous event
+ */
+ Executor getExecutor();
+
+ /**
+ *
+ * @param optionName
+ * @return the value of an option or null
if no option for the given name exists
+ */
+ Object get(String optionName);
+
+ /**
+ *
+ * @param executor
+ * @return an immutable holder of an executor
+ */
+ static NotificationOptions ofExecutor(Executor executor) {
+ return builder().setExecutor(executor).build();
+ }
+
+ /**
+ *
+ * @param optionName
+ * @param optionValue
+ * @return an immutable holder of a single option
+ */
+ static NotificationOptions of(String optionName, Object optionValue) {
+ return builder().set(optionName, optionValue).build();
+ }
+
+ /**
+ *
+ * @return the options builder
+ */
+ static Builder builder() {
+ return new ImmutableNotificationOptions.Builder();
+ }
+
+ /**
+ * Notification options builder.
+ *
+ * @author Martin Kouba
+ * @since 2.0
+ */
+ interface Builder {
+
+ Builder setExecutor(Executor executor);
+
+ Builder set(String optionName, Object optionValue);
+
+ NotificationOptions build();
+
+ }
+
+}
diff --git a/api/src/test/java/org/jboss/cdi/api/test/event/NotificationOptionsTest.java b/api/src/test/java/org/jboss/cdi/api/test/event/NotificationOptionsTest.java
new file mode 100644
index 000000000..758c3d99f
--- /dev/null
+++ b/api/src/test/java/org/jboss/cdi/api/test/event/NotificationOptionsTest.java
@@ -0,0 +1,44 @@
+package org.jboss.cdi.api.test.event;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+import static org.testng.Assert.assertNull;
+
+import java.time.Duration;
+import java.util.concurrent.Executor;
+
+import javax.enterprise.event.NotificationOptions;
+
+import org.testng.annotations.Test;
+
+/**
+ *
+ * @author Martin Kouba
+ */
+public class NotificationOptionsTest {
+
+ @Test
+ public void testBuilder() {
+ Duration timeout = Duration.ofDays(1);
+ NotificationOptions options = NotificationOptions.of("timeout", timeout);
+ assertEquals(timeout, options.get("timeout"));
+ assertNull(options.getExecutor());
+ assertNull(options.get("alpha"));
+ options = NotificationOptions.builder().set("foo", "bar").setExecutor(new Executor() {
+ @Override
+ public void execute(Runnable command) {
+ }
+ }).build();
+ assertEquals("bar", options.get("foo"));
+ assertNotNull(options.getExecutor());
+ options = NotificationOptions.ofExecutor(new Executor() {
+ @Override
+ public void execute(Runnable command) {
+ }
+ });
+ Executor executor = options.getExecutor();
+ assertNotNull(executor);
+ assertNull(options.get("timeout"));
+ }
+
+}
diff --git a/spec/src/main/asciidoc/core/events.asciidoc b/spec/src/main/asciidoc/core/events.asciidoc
index e51bb636d..1d4d03efb 100644
--- a/spec/src/main/asciidoc/core/events.asciidoc
+++ b/spec/src/main/asciidoc/core/events.asciidoc
@@ -122,7 +122,7 @@ public interface Event {
public void fire(T event);
public CompletionStage fireAsync(U event);
- public CompletionStage fireAsync(U event, Executor executor);
+ public CompletionStage fireAsync(U event, NotificationOptions options);
public Event select(Annotation... qualifiers);
public Event select(Class subtype, Annotation... qualifiers);
@@ -165,7 +165,8 @@ If an instance of an annotation that is not a qualifier type is passed to `selec
The methods `fire()` and `fireAsync()` fire an event with the specified qualifiers and notify observers, as defined by <>.
If the container is unable to resolve the parameterized type of the event object, it uses the specified type to infer the parameterized type of the event types.
-The method `fireAsync()` may be called with a specific `Executor` object to be used to invoke observers method.
+The method `fireAsync()` may be called with a `NotificationOptions` object to configure the observer methods notification, e.g. to specify an `Executor` object to be used for asynchronous delivery.
+The container is permitted to define other non-portable notification options.
The following elements are container specific: