Skip to content

Commit

Permalink
Initial proposal for invokable methods: introduce invoker builder
Browse files Browse the repository at this point in the history
This commit introduces an advanced concept for CDI invokable methods,
the `InvokerBuilder`. It can be used to create more complex invokers
that can:

- automatically lookup the target instance or arguments;
- transform the target instance and arguments before invocation;
- transform the return value and thrown exception after invocation;
- wrap the invoker into a custom piece of code for maximum flexibility.

An `InvokerBuilder` can be used during deployment and the outcome
must be registered under certain key (which is a `Class` object).
The built invoker can later be obtained from `InvokableMethod`,
or it can be stored and used directly.
  • Loading branch information
Ladicek committed Dec 2, 2022
1 parent d324ab0 commit b90941d
Show file tree
Hide file tree
Showing 4 changed files with 415 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@

package jakarta.enterprise.inject.build.compatible.spi;

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

/**
* Invokable method that belongs to some managed bean. Allows obtaining the direct invoker,
* the bean class and method metadata.
* registered invokers, the bean class and method metadata. Also allows
* {@linkplain #registerInvoker(Class) registering} a new invoker.
* <p>
* Note that multiple managed beans may inherit an invokable method from a common
* supertype. In that case, each bean has its own invokable method, as can be observed
Expand Down Expand Up @@ -50,4 +52,30 @@ public interface InvokableMethodInfo {
* @return an {@linkplain InvokerInfo opaque token} for the direct invoker, never {@code null}
*/
InvokerInfo directInvoker();

/**
* Returns an invoker for this invokable method that was previously registered under
* given {@code registrationKey}. Such invoker may transform inputs and outputs
* of the method.
*
* @param registrationKey the key under which the invoker was previously registered,
* must not be {@code null}
* @return an {@linkplain InvokerInfo opaque token} for the registered invoker,
* or {@code null} if no invoker was registered under given key
*/
// TODO maybe we shouldn't expose this method -- if someone registers an invoker,
// they get an instance from the builder, so they shouldn't need to obtain it again,
// and they likely don't want anyone else to obtain it
// TODO the method name here is dangerously close to `registerInvoker`
InvokerInfo registeredInvoker(Class<?> registrationKey);

/**
* Registers a new invoker for this invokable method. The returned builder should be used
* to configure transformations that the invoker should apply. The builder eventually
* produces an opaque representation of the invoker.
*
* @param registrationKey the key under which the invoker is to be registered, must not be {@code null}
* @return the invoker builder, never {@code null}
*/
InvokerBuilder<InvokerInfo> registerInvoker(Class<?> registrationKey);
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@
*/
package jakarta.enterprise.inject.spi;

import jakarta.enterprise.invoke.InvokableMethod;
import jakarta.enterprise.invoke.Invoker;
import jakarta.enterprise.invoke.InvokerBuilder;

/**
* <p>
* The container fires an event of this type for each enabled managed bean, before registering the
Expand All @@ -39,4 +43,7 @@ public interface ProcessManagedBean<X> extends ProcessBean<X> {
* @throws IllegalStateException if called outside of the observer method invocation
*/
public AnnotatedType<X> getAnnotatedBeanClass();

// TODO if we introduce `ProcessInvokableMethod`, this method would be moved there
public InvokerBuilder<Invoker<X, ?>> registerInvoker(InvokableMethod<X, ?> invokableMethod, Class<?> registrationKey);
}
21 changes: 20 additions & 1 deletion api/src/main/java/jakarta/enterprise/invoke/InvokableMethod.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

/**
* Invokable method that belongs to some managed bean. Allows obtaining the direct invoker,
* the bean class and method metadata.
* previously registered invokers, the bean class and method metadata.
* <p>
* Note that multiple managed beans may inherit an invokable method from a common
* supertype. In that case, each bean has its own invokable method, as can be observed
Expand Down Expand Up @@ -47,4 +47,23 @@ public interface InvokableMethod<T, R> {
* @return the direct invoker, never {@code null}
*/
Invoker<T, R> getDirectInvoker();

/**
* Returns an invoker for this invokable method that was previously registered under
* given {@code registrationKey}. Such invoker may transform inputs and outputs
* of the method.
*
* @param registrationKey the key under which the invoker was previously registered,
* must not be {@code null}
* @return the registered invoker for this invokable method, or {@code null} if
* no invoker was registered under given key
* @param <TT> target instance type of the invoker; may be different from the bean class
* of this invokable method if the invoker applies a target instance transformation
* @param <TR> return type of the invoker; may be different from the return type of this
* invokable method if the invoker applies a return value transformation
*/
// TODO maybe we shouldn't expose this method -- if someone registers an invoker,
// they get an instance from the builder, so they shouldn't need to obtain it again,
// and they likely don't want anyone else to obtain it
<TT, TR> Invoker<TT, TR> getRegisteredInvoker(Class<?> registrationKey);
}
Loading

0 comments on commit b90941d

Please sign in to comment.