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: