CDI extension, altering processed type - cdi

Using Weld 1.1.13.Final in test with Arquillian....
Let's say I inject into a field something volatile. Something like a property subject to change that I want the bean owning the injection point to receive change events. Thought about creating a CDI extension.
Caught ProcessAnnotatedType event and looking for all fields that have an custom annotation on field injection points:
<T> void pat(#Observes ProcessAnnotatedType<T> event, BeanManager bm) {
final AnnotatedType<T> target = event.getAnnotatedType();
for (AnnotatedField<? super T> field : target.getFields())
if (field.isAnnotationPresent(Value.class)) { // ignore that I don't check #Inject here for the moment
CtClass wrapper = pool.get(target.getJavaClass().getName());
ConstPool cp = wrapper.getClassFile().getConstPool();
CtMethod m = CtNewMethod.make(....)
....
wrapper.addMethod(m);
event.setAnnotatedType(bm.createAnnotatedType(wrapper.toClass()));
}
}
Had even grabbed thereafter all the injection points for fields and replaced the underlying WeldField with a new Field corresponding the "wrapper" type. Otherwise bean validation fails.
But this only works for stuff setup during startup not when for example Arquillian uses the Bean Manager to initialize a class that injects one of my "wraps". Things fail since the Bean Resolver uses the Type as a hash key to find beans.
Basically I don't think I can "mask" a class that is annotated (made into a bean) by the CDI with an extra method to receive custom events. Would have been cool but a Type is a Type (i.e. no idea how to proxy or fake the equals/hashCode).

Got it. Turns out the compute value function (google extension) inside the TypeSafeBeanResolver resolver (at least the CDI Weld implementation) is smart. If I just extend the class:
CtClass wrapper = pool.makeClass(target.getJavaClass().getName()+"Proxy");
wrapper.setSuperclass(pool.get(target.getJavaClass().getName()));
.....
final AnnotatedType<T> other = bm.createAnnotatedType(wrapper
.toClass());
then everything works fine. Tested capturing an event in a bean. Will post the code on a Gist with a comment.

Related

How to properly create an instance of AnnotatedControllerConfigurer

I'm trying to define a configuration to create an ExecutionGraphQlService to wire into an existing application as a proof of concept, but I'm a bit confused about how to create an instance of a AnnotatedControllerConfigurer. Here is what I currently have settled upon.
AnnotatedControllerConfigurer annotatedControllerConfigurer = new AnnotatedControllerConfigurer();
annotatedControllerConfigurer.setApplicationContext(applicationContext);
annotatedControllerConfigurer.afterPropertiesSet();
annotatedControllerConfigurer.configure(runtimeWiringBuilder);
AnnotatedControllerConfigurer implements ApplicationContextAware, InitializingBean so it seems to expect to be initialized as a bean, but attempts to autowire an instance of it fail due to Could not autowire. No beans of 'AnnotatedControllerConfigurer' type found.
I've attempted constructor injection public ExecutionGraphQlService defaultExecutionGraphQlService(AnnotatedControllerConfigurer annotatedControllerConfigurer) as well as manual instantiation autowireCapableBeanFactory.autowireBean(annotatedControllerConfigurer) (which should be basically the same thing).
It seems like this class is not a bean, but expects to be managed by Spring's bean lifecycle? Is my approach the expected approach?
The AnnotatedControllerConfigurer type is meant to be a bean in the Spring container, and is using the bean lifecycle to setup the infrastructure.
Creating it as a bean (given other pieces are contributed as beans as well). The runtime wiring should be configured on the GraphQlSource directly.
#Configuration(proxyBeanMethods = false)
class GraphQlConfiguration {
#Bean
public AnnotatedControllerConfigurer annotatedControllerConfigurer() {
return new AnnotatedControllerConfigurer();
}
}

Override beans from an external library (Quarkus)

I want to override a bean that's used by Quarkus to disable authentication/authorization.
With the following implementation, it works that REST endpoints can be configured at start time to not be secured:
#Alternative
#Priority(1)
#ApplicationScoped
public class CustomOidcAuthController extends TestAuthController {
private static final Logger LOGGER = Logger.getLogger(CustomOidcAuthController.class);
#ConfigProperty(name = "quarkus.oidc.enabled", defaultValue = "false")
boolean quarkusOidcEnabled;
#PostConstruct
public void check() {
LOGGER.info("isAuthorizationEnabled(): " + isAuthorizationEnabled());
}
#Override
public boolean isAuthorizationEnabled() {
return quarkusOidcEnabled;
}
}
This is with the Bean residing in the same Quarkus module.
However, I want to externalize this class into a separate library and if I do this, it no longer works.
Noteworthy:
Yes, the #Priority of my bean (1) is higher than the default (3000)
The beans are discovered, if I explicitly inject them.
They are however not used, if I inject the subtype that Quarkus uses internally (either TestAuthController or AuthorizationController).
Therefore the endpoints are always secured
As can be seen here from the IntelliJ debugger
Currently I have an empty beans.xml, but with building a Jandex Index I also observe the same behavior (related How to create a Jandex index in Quarkus for classes in a external module)
I can get the behavior I want, if I use quarkus.arc.selected-alternatives=com.xxx.CustomOidcAuthController, however this is not preferable, since each Service using the library would need to configure this and this will certainly cause problems, because it can be easily forgotten.
Well, the priority of the TestAuthController is indeed 3000 and therefore it takes precedence. Injection of CustomOidcAuthController works because there's no other bean that has CustomOidcAuthController in its set of bean types.
In other words, it works as expected (and defined by the spec).
Yes, the #Priority of my bean (1) is higher than the default (3000)
According to CDI specification, an alternative with highest priority is selected. See this part of the CDI 2.0 specification.
Here is a CDI TCK test asserting that higher priority 'wins' during typesafe resolution.
Therefore, your approach is correct and you just need to make sure your custom bean is an alternative with priority value higher than that of TestAuthController.

How can I programmatically add a producer method to a CDI container during AfterBeanDiscovery?

I know how to add a Bean to a CDI container during AfterBeanDiscovery. My problem is that what I really need to do is the equivalent of adding a new producer method with the equivalent of a particularly qualified parameter.
That is, I'd like to somehow programmatically create several of these:
#Produces
#SomeQualifier("x")
private Foo makeFoo(#SomeQualifier("x") final FooMaker fm) {
return fm.makeFoo();
}
...where the domain over which SomeQualifier's value element ranges is known only at AfterBeanDiscovery time. In other words, some other portable extension has installed two FooMaker instances into the container: FooMaker-qualified-by-#SomeQualifier("x") and FooMaker-qualified-by-#SomeQualifier("y"). Now I need to do the equivalent of making two producer methods to "match" them.
Nonbinding is not an option; I want this resolution to take place at container startup, not at injection time.
I am aware of BeanManager's getProducerFactory method, but the dozens if not hundreds of lines of gymnastics I'd have to go through to add the right qualifier annotation on each AnnotatedParameter "reachable" from the AnnotatedMethod I'd have to create by hand (to avoid generics issues) make me think I'm way off the beaten path here.
Update: So in my extension, I have created a private static method that returns a Foo, and has a FooMaker parameter. I've wrapped this in a hand-tooled AnnotatedMethod that reports SomeQualifier("x") etc. in its getAnnotations() method, and also reports SomeQualifier("x") etc. from its AnnotatedParameter's getAnnotations() method. Then I got a ProducerFactory from the BeanManager and feed that into a new Bean that I create, where I use it to implement the create and destroy methods. Everything compiles and so forth just fine.
(However, Weld (in particular) blows up with this usage, which leads me to think that I'm doing Really Bad Thingsā„¢.)

Seed value in Weld CDI custom scope

Coming from a Guice background, I know that it is possible to seed an object value from a scope using.
scope.seed(Key.get(SomeObject.class), someObject);
I suppose one could do this by registering a Bean that gets a value from an AbstractBoundContext, but examples just seeding one value from a Custom Scope seem hard to find. How do I create a custom scope that seeds a value that can be injected elsewhere?
Edit:
I am currently using the following workaround, that can be injected in an interceptor to set the Configuration when entering the scope, and can then be injected through its thread local provider. I am still looking for options that feel less hacky / are more integrated with the scope/scope context system in Weld though.
#Singleton
public class ConfigurationProducer {
private final InheritableThreadLocal<Configuration> threadLocalConfiguration =
new InheritableThreadLocal<>();
#Produces
#ActiveDataSet
public ConfigurationConfiguration() {
return threadLocalConfiguration.get()
}
public void setConfiguration(Configuration configuration) {
threadLocalConfiguration.set(configuration);
}
}
The answer is to register a custom bean with the AfterBeanDiscovery event, like so:
event.addBean()
.createWith(ctx -> commandContext.getCurrentCommandExecution())
.addType(CommandExecution.class)
.addQualifier(Default.Literal.INSTANCE)
.scope(CommandScoped.class)
.beanClass(CommandExtension.class);
There is a quite sophisticated example available at https://github.com/weld/command-context-example

How can a custom tag have impact on every processing of its subtree

I aim to setup the jsf thread to a specific state that I derive from the fact if a given tag is in the subtree of my custom component. This custom component declares a custom cdi scope.
<my:scope area="info">
<rich:tree selectionChangeListener="#{taskTreeManager.selectionChanged}"
<my:scope>
<my:scope area="work">
<rich:tree selectionChangeListener="#{taskTreeManager.selectionChanged}"
<my:scope>
HERE is my idea: the cdi bean 'taskTreeManager' is different for the two rich:trees, since it comes from a custom cdi scope:
#TaskScoped
public TaskTreeManager { .... }
Now I created the jsf tag .
What is missing is a way that this tag has impact on its children, i a generic way (ie. without making assupmtions and without having to change the children).
The needs to activate the cdi scope on each action happening on any tag in its subtree.
I also failed to find a listener for that, especially since Ajax calls go quite directly into the listeners defined on the very component ( the rich:tree). The hierarchy of the component as can be seen in the xhtml is not so relevant, so it seem: when taskTreeManager.selectionChanged gets called, my:scope tag does not know and thus cannot switch the cdi scope properly.
Then I tried to setup listeners programmatically on each component that resides in the subtree.
In the meantime I turned around: what I am trying to achieve is that each java callback finds the right scope active. This can be reduced to: each EL expression evaluation finds its cdi beans in the right scope.
So my way was to introduce a new ELResolver with the sole purpose of discovering if the current component resides within tag.
Fortunately that is possible:
private String computeViewArea(UIComponent cc) {
String retval = EMPTY;
while (null != cc) {
// attention: cc.toString() causes stackoverflow !! so don't log cc!
if (cc instanceof TaskScopedComponent) {
TaskScopedComponent c = (TaskScopedComponent) cc;
retval = (String) c.getAttributes().get("viewArea");
break;
}
cc = cc.getParent();
}
return retval;
}

Resources