-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Consider adding support of InterceptionFactory #3532
Comments
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you! |
We could just ignore this method and throw a runtime exception if called at runtime... |
Giving this some more thought:
|
+1
Yes, we can't do that but there are use cases where configuration is not needed, i.e. the class of the produced bean declares interceptor bindings. We could try to introduce a non-portable declarative approach, e.g. something like:
I agree that this is not a good idea...
Yes, it wouldn't be portable but at least "doable" ;-)
|
@Singleton
class SimpleProducer {
@BindTxToGet(@Transactional(TxType.REQUIRED))
@Produces
List<String> interceptedList(InterceptionFactory<List<String>> factory) {
return factory.createInterceptedInstance(List.of());
}
@InterceptionFactoryBinding("get")
@Retention(RUNTIME)
public @interface BindTxToGet {
Transactional value();
}
}
// metaannotation used to identify the binding annotations
@Target(ElementType.ANNOTATION_TYPE)
@Retention(RUNTIME)
public @interface InterceptionFactoryBinding {
// regex to match methods
String value() default "*";
} Which is far from practical. |
Is there any solution to this issue? A workaround is very welcome. Thanks |
No solution or workaround ATM. I don't think we came up with a viable way to do this. This feature is very much runtime oriented (as dictated by spec) and anything we could do would be Quarkus-specific as we'd need an upfront information on what classes to generate at build time. |
Copying my comment from #34436, because it really belongs here. I was thinking we could maybe support something like this: @Produces
@RequestScoped
@SupportInterceptionOf(MyClass.class) // name subject to bike shedding of course
public MyClass produceMyClass(InterceptionFactory factory) {
return factory.createInterceptedInstance(new MyClass());
} That would mean:
Alternatively, we could ditch CDI's @Produces
@RequestScoped
@SupportInterceptionOf(MyClass.class) // name subject to bike shedding of course
public MyClass produceMyClass(ArcInterceptedBeanFactory factory) { // name intentionally ugly
// could also accept arguments to pass to the constructor, probably?
return factory.createInterceptedInstance(MyClass.class);
} Looking at the |
Now, the comments in this issue also got me thinking about interceptor bindings. Clearly we can't support If it doesn't, I'd suggest we just use "annotation mixins". Say for example public class MyClass {
public int doSomething() { ... }
} Now, if I want to create a producer bean for this class, and make the public class MyClassInterceptorBindings {
@Transactional
public int doSomething() {
return 0; // doesn't matter, this method is never executed
}
} Then, I create the producer, on which I declare that a return value of @Produces
@RequestScoped
@SupportInterceptionOf(value = MyClass.class, bindingsFrom = MyClassInterceptorBindings.class)
public MyClass produceMyClass(InterceptionFactory factory) {
return factory.createInterceptedInstance(new MyClass());
} The meaning would be as follows:
WDYT? |
Hm, why do we need the
I'm not sure about this one. Wouldn't the aforementioned |
That's an interesting idea and it could be used for synth beans as well. I'd probably steer away from using Note that the mixin class should probably extend the target class so that we avoid some validation hell in terms of matching methods? The target class should be non-final anyway as we need to subclass it.
@mkouba IIUIC, this still won't allow you to declare annotation values whereas Ladislav's approach will. |
That would get out of hands fairly quickly with interfaces like, I don't know,
Great point! |
@manovotn Not really. Binding does not have to a class but annotation instead...
@Ladicek well, the value of |
Not sure I understand. I was talking about trying to declare an interceptor binding that would be something like |
I'm not sure what
|
And, more generally, the annotation mixin approach has received a lot of criticism elsewhere, for good reasons. I still prefer it over other approaches because it just clicks with my brain. I know my brain is not typical 😆 |
It means add the binding to all methods with the name that matches the regex in the
For sure, but if you need to add the binding to 40 methods? |
Ah I see. Well, your approach only works for memberless annotations. If we want to support bindings with members, we need something more powerful. I think we could mix the two, actually: class MyClassBindings {
@ApplyToMethods("add|remove|doSomething")
@Transactional
void whatever() {
}
} Or something like that. |
You're right, we would need a special annotation for each binding... |
Or even meta-annotations! 🙈 @Retention(RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Association { }
@Association
@Retention(RUNTIME)
public @interface AssociateTransactional {
Transactional binding();
String to();
} and usage: @Produces
@AssociateTransactional(binding = @Transactional(TxType.REQUIRES_NEW), to = "foo")
public MyClass produceMyClass(InterceptionFactory factory) {
return factory.createInterceptedInstance(new MyClass());
} |
If you need to intercept all methods, class level annotation is there.
Hm, possible but quite confusing since you are now using some methods to map it 1:1 and some dummy methods as templates to apply to multiple other methods... |
Yeah, I wouldn't exactly be a fan of |
@manovotn Unless you modify the original class which is something that does not happen very often, right? 😄
Very true. |
Would be great, if the solution could also work for non-managed pojos, one does not have control over their lifecycle. Type and interceptors are known in advance, it just happens that they are created and destroyed on demand programmatically. So I would suggest we rather annotate a This way we also easily distinguish between interceptors for bean creation and interceptors for bean method invocation, cause otherwise the producer method is potential overloaded with annotations. |
I thought about something like: @ApplicationScoped
class MyFactory {
@Inject
@LogInvocations(LogLevel.INFO)
@BindInterceptors(mode=mode.EXCLUDE, interceptors={LogInvocations.class}, methods={"method1", "method2"})
private InterceptionFactory<MyClass> factory;
@Inject
private MyClassRestClient client;
public MyClass create(String key) {
return factory.createInterceptedInstance(client.create(key));
}
} Some notes:
Open question regards the scope of interception:
|
This was fixed like this:
This is documented in more detail in https://quarkus.io/version/main/guides/cdi-reference (not yet, as the PR was just merged; hopefully tomorrow?). Looking forward for feedback of any users! |
@Ladicek Nice work! Just wonder if it can support |
That would in theory be possible, but I have no idea how much work that would entail. There's a feature request in CDI for a standardized solution, but there's been some reluctance from the implementation side. Do you have a concrete usecase? |
Hmm, I have a quarkus-pooled-jms extension which can wrap a And for quarkus-qpid-jms, there is an issue to add the pooling capability provied by quarkus-pooled-jms. ConnectionFactoryWrapperBuildItem had been introduced to solve the similar issue around interception. You can check all the discusses before. Then if we can support |
https://docs.jboss.org/cdi/spec/2.0/cdi-spec.html#interception_factory
The problematic part is
InterceptionFactory#configure()
.The text was updated successfully, but these errors were encountered: