Skip to content

Commit

Permalink
Programmatic lookup improvements, introduce Handle interface.
Browse files Browse the repository at this point in the history
  • Loading branch information
manovotn committed Sep 27, 2021
1 parent 602f89c commit 786b223
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 0 deletions.
85 changes: 85 additions & 0 deletions api/src/main/java/jakarta/enterprise/inject/Instance.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

import jakarta.enterprise.inject.spi.Bean;
import jakarta.enterprise.util.AnnotationLiteral;
import jakarta.enterprise.util.TypeLiteral;
import jakarta.inject.Provider;
Expand Down Expand Up @@ -227,4 +228,88 @@ default boolean isResolvable() {
*/
void destroy(T instance);

/**
* Obtains an initialized contextual reference handle for a bean that has the required type and qualifiers and is
* eligible for injection. Throws exceptions if there is no such bean or more than two beans.
*
* <p>
* The contextual reference is obtained lazily, i.e. when first needed.
* </p>
*
* @return a new {@link Handle} instnace
* @throws UnsatisfiedResolutionException if there is no bean with given type and qualifiers
* @throws AmbiguousResolutionException if there is more than one bean given type and qualifiers
*/
Handle<T> getHandle();

/**
* Allows iterating over contextual reference handles for all beans that have the required type and required qualifiers and are eligible
* for injection.
*
* <p>
* Note that the returned {@link Iterable} is stateless. Therefore, each {@link Iterable#iterator()} produces a new set of handles.
* </p>
*
* @return a new iterable
*/
Iterable<Handle<T>> handles();

/**
* Returns stream of {@link Handle} objects.
*
* @return a new stream of contextual reference handles
*/
default Stream<Handle<T>> handlesStream() {
return StreamSupport.stream(handles().spliterator(), false);
}

/**
* This interface represents a contextual reference handle.
* <p>
* Allows to inspect the metadata of the relevant bean before resolving its contextual reference and also to destroy
* the underlying contextual instance.
* </p>
*
* @author Matej Novotny
* @param <T> the required bean type
*/
interface Handle<T> extends AutoCloseable {

/**
* The contextual reference is obtained lazily, i.e. when first needed.
*
* @return the contextual reference
* @see Instance#get()
* @throws IllegalStateException If the producing {@link Instance} does not exist
*/
T get();

/**
*
* @return the bean metadata
*/
Bean<?> getBean();

/**
* Destroy the contextual instance.
*
* It's a no-op if:
* <ul>
* <li>called multiple times</li>
* <li>if the producing {@link Instance} does not exist</li>
* <li>if the handle does not hold a contextual reference, i.e. {@link #get()} was never called</li>
* </ul>
*
* @see Instance#destroy(Object)
*/
void destroy();

/**
* Delegates to {@link #destroy()}.
*/
@Override
void close();

}

}
10 changes: 10 additions & 0 deletions api/src/test/java/org/jboss/cdi/api/test/ClosableCDIProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@ public void destroy(Object instance) {

}

@Override
public Handle<Object> getHandle() {
return null;
}

@Override
public Iterable<Handle<Object>> handles() {
return null;
}

@Override
public Iterator<Object> iterator() {
return null;
Expand Down
10 changes: 10 additions & 0 deletions api/src/test/java/org/jboss/cdi/api/test/DummyCDIProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,16 @@ public void destroy(Object instance) {

}

@Override
public Handle<Object> getHandle() {
return null;
}

@Override
public Iterable<Handle<Object>> handles() {
return null;
}

@Override
public Iterator<Object> iterator() {
return null;
Expand Down
10 changes: 10 additions & 0 deletions api/src/test/java/org/jboss/cdi/api/test/DummyCDIProvider2.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,16 @@ public void destroy(Object instance) {

}

@Override
public Handle<Object> getHandle() {
return null;
}

@Override
public Iterable<Handle<Object>> handles() {
return null;
}

@Override
public Iterator<Object> iterator() {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,16 @@ public void destroy(Object instance) {

}

@Override
public Handle<Object> getHandle() {
return null;
}

@Override
public Iterable<Handle<Object>> handles() {
return null;
}

@Override
public Iterator<Object> iterator() {
return null;
Expand Down
50 changes: 50 additions & 0 deletions spec/src/main/asciidoc/core/injectionandresolution.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,9 @@ public interface Instance<T> extends Iterable<T>, Provider<T> {
void destroy(T instance);
Handle<T> getHandle();
Iterable<Handle<T>> handles();
Stream<Handle<T>> handlesStream();
}
----
Expand Down Expand Up @@ -687,6 +690,53 @@ The bean instance passed to `destroy()` should be a dependent scoped bean instan
Applications are encouraged to always call `destroy()` when they no longer require an instance obtained from `Instance`. All built-in normal scoped contexts support destroying bean instances.
An `UnsupportedOperationException` is thrown if the active context object for the scope type of the bean does not support destroying bean instances.

The `getHandle()` method must:

* Return an initialized contextual reference `Handle<T>` for a bean that has the required type and qualifiers and is eligible for injection. The contextual reference must be resolved lazily, i.e. when first needed. <<handle_interface>> is described in a separate paragraph.
* Throw `UnsatisfiedResolutionException` if there is no bean with given type and qualifiers
* Throw `AmbiguousResolutionException` if there is more than one bean given type and qualifiers

The `handles()` method must:

* Allow iterating over contextual reference handles for all beans that have the required type and required qualifiers and are eligible for injection.
* Return stateless `Iterable`. Therefore, each `Iterable#iterator()` produces a new set of handles.

The `handlesStream()` is a `Stream` equivalent of the aforementioned `handles()` method.

[[handle_interface]]
===== The `Handle` interface

`Handle` is an interface representing a contextual reference handle.
It is an abstraction allowing inspection of bean metadata via `Bean<?>` objects.
Handles have to resolve their contextual references lazily, i.e. when their `get()` method is invoked.
Last but not least, this interface can be used to destroy the contextual instance once not needed.

[source, java]
----
public interface Handle<T> extends AutoCloseable {
T get();
Bean<?> getBean();
void destroy();
void close();
}
----

The `get()` method returns a contextual reference object. The contextual reference is resolved lazily.

The `getBean()` method returns the `Bean` object representing metadata of the given contextual instance.

The `destroy()` method destroys the contextual instance and is a no-op if:

* called multiple times
* the producing `Instance` does not exist
* the handle does not hold a contextual reference, i.e. `get()` was never called

The rules for destroying instances are the same as with `Instance#destroy()`.

The `close()` method delegates to the aforementioned `destroy()` method.

[[builtin_instance]]

==== The built-in `Instance`
Expand Down

0 comments on commit 786b223

Please sign in to comment.