Skip to content

Commit

Permalink
Initial specification text for method invokers
Browse files Browse the repository at this point in the history
  • Loading branch information
Ladicek committed Jan 29, 2024
1 parent 45b911d commit ec24b34
Show file tree
Hide file tree
Showing 11 changed files with 284 additions and 124 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@

import java.util.Collection;

import jakarta.enterprise.invoke.InvokerBuilder;
import jakarta.enterprise.lang.model.AnnotationInfo;
import jakarta.enterprise.lang.model.declarations.ClassInfo;
import jakarta.enterprise.lang.model.declarations.FieldInfo;
Expand Down Expand Up @@ -164,25 +163,6 @@ public interface BeanInfo {
*/
Collection<InjectionPointInfo> injectionPoints();

/**
* Returns a new {@link InvokerBuilder} for given method. The builder eventually produces
* an opaque representation of the invoker for the given method.
* <p>
* The {@code method} must be declared on the bean class or inherited from a supertype
* of the bean class of this bean, otherwise an exception is thrown.
* <p>
* If an invoker may not be obtained for given {@code method} as described
* in {@link jakarta.enterprise.invoke.Invoker Invoker}, an exception is thrown.
* <p>
* If this method is called outside the {@code @Registration} phase, an exception is thrown.
*
* @param method method of this bean, must not be {@code null}
* @return the invoker builder, never {@code null}
* @since 4.1
*/
// TODO we may want to introduce another entrypoint for this operation
InvokerBuilder<InvokerInfo> createInvoker(MethodInfo method);

// ---

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package jakarta.enterprise.inject.build.compatible.spi;

import jakarta.enterprise.invoke.InvokerBuilder;
import jakarta.enterprise.lang.model.declarations.MethodInfo;

/**
* Factory for {@link InvokerBuilder}s.
*
* @since 4.1
*/
public interface InvokerFactory {
/**
* Returns a new {@link InvokerBuilder} for given method of given bean. The builder
* eventually produces an opaque representation of the generated invoker.
* <p>
* If an invoker may not be built for given {@code bean} or for given {@code method},
* an exception is thrown.
*
* @param bean target bean of the invoker, must not be {@code null}
* @param method target method of the invoker, must not be {@code null}
* @return the invoker builder, never {@code null}
*/
InvokerBuilder<InvokerInfo> createInvoker(BeanInfo bean, MethodInfo method);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@
package jakarta.enterprise.inject.build.compatible.spi;

import jakarta.enterprise.invoke.Invoker;
import jakarta.enterprise.invoke.InvokerBuilder;
import jakarta.enterprise.lang.model.declarations.MethodInfo;

/**
* Opaque token that stands in for an invoker registered using {@link BeanInfo#createInvoker(MethodInfo)}.
* It can only be used to materialize an {@link Invoker} in a synthetic component; see
* {@link SyntheticBeanBuilder#withParam(String, InvokerInfo) SyntheticBeanBuilder.withParam()} or
* Opaque token that stands in for an invoker registered using {@link InvokerFactory#createInvoker(BeanInfo, MethodInfo)}
* and {@link InvokerBuilder#build()}. It can only be used to materialize an {@link Invoker} in a synthetic component;
* see {@link SyntheticBeanBuilder#withParam(String, InvokerInfo) SyntheticBeanBuilder.withParam()} or
* {@link SyntheticObserverBuilder#withParam(String, InvokerInfo) SyntheticObserverBuilder.withParam()}.
*
* @since 4.1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
* <p>
* Additionally, methods annotated {@code @Registration} may declare parameters of these types:
* <ul>
* <li>{@link InvokerFactory}</li>
* <li>{@link Messages}</li>
* <li>{@link Types}</li>
* </ul>
Expand Down
125 changes: 29 additions & 96 deletions api/src/main/java/jakarta/enterprise/invoke/Invoker.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,122 +11,55 @@
package jakarta.enterprise.invoke;

/**
* Allows indirectly invoking a method that belongs to a managed bean (the <em>target method</em>).
* To invoke the method, the caller must provide all the arguments that the target method accepts,
* as well as the instance on which the target method is to be invoked, if it is not {@code static}.
* An invoker allows indirect invocation of its target method on an instance of its target
* bean.
* <p>
* Whenever a direct invocation of a method is a business method invocation, an indirect invocation
* of that method through an invoker is also a business method invocation.
* CDI-based frameworks are expected to use invokers when they need to invoke application
* methods. Applications are not supposed to use invokers, as they can invoke their own
* methods directly.
* <p>
* Invoker implementations must be thread-safe. It is possible to use a single invoker instance
* to perform multiple independent invocations of the target method, possibly on different instances
* and with different arguments.
*
* <h2>Obtaining an invoker</h2>
*
* The CDI container allows {@linkplain InvokerBuilder building} an invoker for non-private
* methods declared on a managed bean class or inherited from a supertype. Attempting to build
* an invoker for a private method or a constructor of a managed bean class leads to a deployment
* problem. Attempting to build an invoker for a method of a class that is not a managed bean class
* or that is an interceptor or decorator class leads to a deployment problem.
* <p>
* Multiple managed beans may inherit a method from a common supertype. In that case, each bean
* conceptually has its own method and an invoker obtained for one bean may not be used to invoke
* the method on the other bean.
* <p>
* Using the {@link InvokerBuilder} is the only way to obtain an invoker. An {@code InvokerBuilder}
* can only be obtained in CDI portable extensions and build compatible extensions.
*
* <h2>Example</h2>
*
* To illustrate how invokers work, let's take a look at an example. Say that the following bean
* exists and has a method that you want to invoke indirectly:
* For example, assume the following managed bean exists:
*
* <pre>
* &#64;Dependent
* class MyService {
* String hello(String name) {
* public class MyService {
* public String hello(String name) {
* return "Hello " + name + "!";
* }
* }
* </pre>
*
* When you obtain an {@code InvokerBuilder} for the {@code hello()} method, you can
* immediately build a direct invoker. In a portable extension, this results in an invoker:
*
* <pre>
* InvokerBuilder&lt;Invoker&lt;MyService, String&gt;&gt; builder = ...;
* Invoker&lt;MyService, String&gt; invoker = builder.build();
* </pre>
*
* In a build compatible extension, this results in an opaque token that later
* materializes as an invoker:
* Further, assume that {@code invoker} is an invoker for the {@code hello()} method
* of the {@code MyService} bean and {@code myService} is a contextual reference for the bean.
* Then, to invoke the {@code hello()} method indirectly, a framework would call
*
* <pre>
* InvokerBuilder&lt;InvokerInfo&gt; builder = ...;
* InvokerInfo invoker = builder.build();
* invoker.invoke(myService, new Object[] { "world" })
* </pre>
*
* To call the {@code hello()} method through this invoker, call
* {@code invoker.invoke(myService, new Object[] {"world"})}.
* The return value is {@code "Hello world!"}.
* <p>
* An implementation of the direct invoker above is equivalent to the following class:
* The return value would be {@code "Hello world!"}.
*
* <pre>
* class TheInvoker implements Invoker&lt;MyService, String&gt; {
* String invoke(MyService instance, Object[] arguments) {
* return instance.hello((String) arguments[0]);
* }
* }
* </pre>
*
* @param <T> type of the target instance
* @param <R> return type of the method
* @param <T> type of the target bean
* @param <R> return type of the target method
* @since 4.1
* @see #invoke(Object, Object[])
*/
public interface Invoker<T, R> {
/**
* Invokes the target method of this invoker on given {@code instance}, passing given
* {@code arguments}. If the target method is {@code static}, the {@code instance} is ignored;
* by convention, it should be {@code null}. If the target method returns normally, this
* method returns its return value, unless the target method is declared {@code void},
* in which case this method returns {@code null}. If the target method throws an exception,
* this method rethrows it directly.
* <p>
* If some parameter of the target method declares a primitive type, the corresponding element of
* the {@code arguments} array must be of the corresponding wrapper type. No type conversions are
* performed, so if the parameter is declared {@code int}, the argument must be an {@code Integer}
* and may not be {@code Short} or {@code Long}. If the argument is {@code null}, the default value
* of the primitive type is used. Note that this does not apply to arrays of primitive types;
* if a parameter is declared {@code int[]}, the argument must be {@code int[]} and may not be
* {@code Integer[]}.
* <p>
* If the target method is not {@code static} and {@code instance} is {@code null},
* a {@link NullPointerException} is thrown. If the target method is not {@code static} and
* the {@code instance} is not assignable to the class of the bean to which the method belongs,
* a {@link ClassCastException} is thrown.
* <p>
* If the target method declares no parameter, {@code arguments} are ignored. If the target method
* declares any parameter and {@code arguments} is {@code null}, {@link NullPointerException} is
* thrown. If the {@code arguments} array has fewer elements than the number of parameters of
* the target method, {@link ArrayIndexOutOfBoundsException} is thrown. If the {@code arguments}
* array has more elements than the number of parameters of the target method, the excess elements
* are ignored. If some of the {@code arguments} is not assignable to the declared type of
* the corresponding parameter of the target method, {@link ClassCastException} is thrown.
*
* TODO the previous 2 paragraphs refer to "assignability", which needs to be defined somewhere!
*
* TODO when the `InvokerBuilder` applies transformations, some of the requirements above
* are no longer strictly necessary, should reflect that in this text somehow (it is already
* mentioned in `InvokerBuilder`, but that likely isn't enough)
* Invokes the target method on the given {@code instance} of the target bean, passing
* given {@code arguments}. If the target method returns normally, this method returns
* its return value, unless the target method is declared {@code void}, in which case
* this method returns {@code null}. If the target method throws an exception, it is
* rethrown directly.
*
* @param instance the instance on which the target method is to be invoked, may only be {@code null}
* if the method is {@code static}
* @param arguments arguments to be supplied to the target method, may only be {@code null}
* if the method declares no parameter
* @return return value of the target method, or {@code null} if the method is declared {@code void}
* @param instance the instance of the target bean on which the target method is to be invoked;
* may only be {@code null} if the target method is {@code static}
* @param arguments arguments to be passed to the target method; may only be {@code null}
* if the target method declares no parameter
* @return return value of the target method, or {@code null} if the target method
* is declared {@code void}
* @throws RuntimeException when {@code instance} or {@code arguments} are incorrect
* @throws Exception when the target method throws an exception
*/
R invoke(T instance, Object[] arguments); // TODO throws Exception ?
R invoke(T instance, Object[] arguments) throws Exception;
}
Original file line number Diff line number Diff line change
Expand Up @@ -360,9 +360,8 @@ public interface InvokerBuilder<T> {
InvokerBuilder<T> setInvocationWrapper(Class<?> clazz, String methodName);

/**
* Returns the built {@link Invoker} or some represention of it. Implementations are allowed
* but not required to reuse already built invokers for the same target method with the same
* configuration.
* Returns the built {@link Invoker} or some representation of it. Implementations are allowed
* but not required to reuse already built invokers when possible.
*
* @return the built invoker
*/
Expand Down
4 changes: 4 additions & 0 deletions spec/src/main/asciidoc/cdi-spec.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ include::core/interceptors.asciidoc[]

include::core/events.asciidoc[]

include::core/invokers.asciidoc[]

include::core/beanmanager_lite.asciidoc[]

include::core/spi_lite.asciidoc[]
Expand All @@ -79,6 +81,8 @@ include::core/decorators.asciidoc[]

include::core/events_full.asciidoc[]

include::core/invokers_full.asciidoc[]

include::core/spi_full.asciidoc[]

include::core/packagingdeployment_full.asciidoc[]
Expand Down

0 comments on commit ec24b34

Please sign in to comment.