-
1. Re: Weld CDI extension to extend AnnotatedType or add method
young_matthewd Nov 21, 2013 6:31 AM (in response to young_matthewd)What I have done so far is rather than recreating the managed bean (sort of a headache) I capture the processed annotated type first and try to add a method there is Javassist then with the help of the BeanManager create a new annotated type which is set to the event. Seeing my new method later on in the ProcessManagedBean managed bean event. Code is:
private ClassPool pool = ClassPool.getDefault();
....
<T> void processAnnotatedType(@Observes ProcessAnnotatedType<T> event, BeanManager bm) {
final AnnotatedType<T> target = event.getAnnotatedType();
// check only fields for annotation
for (AnnotatedField<? super T> field : target.getFields())
if (field.isAnnotationPresent(Value.class)) {
CtClass wrapper = pool.get(target.getJavaClass().getName());
CtMethod m = CtNewMethod.make(....);
wrapper.addMethod(m);
event.setAnnotatedType(bm.createAnnotatedType(wrapper.toClass()));
}
}
<T> void processManagedBean(@Observes ProcessManagedBean<T> event) {
// check that the method is there
}
But I get a Deployment Exception for the managed bean that is to be injected into another bean when validating. Any ideas?
-
2. Re: Weld CDI extension to extend AnnotatedType or add method
mkouba Nov 21, 2013 7:40 AM (in response to young_matthewd)Hi Matthew,
what deployment exception do you actually get (message, stack trace)? If I understand it correctly you would like to automatically add an observer method to a bean with a specific injection point (@Inject @Value...)?
-
3. Re: Weld CDI extension to extend AnnotatedType or add method
young_matthewd Nov 21, 2013 8:51 AM (in response to mkouba)@Martin,
Getting the following exception:
org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [Taker] with qualifiers [@Default] at injection point [[field] @Inject se.bolagsverket.afg.cdi.Sample.taker]
where Taker looks like:
public class Taker {
@Inject @Value("stuff")
public String whatever;
}
And yes I want to add my Javassist method to Taker. Have been debugging for the past hour. The code diverges (from the normal case when not creating a new annotated type from the Taker class with Javassist) inside TypeSafeBeanResolver when calling "getBeans(type)". The following line returns null in that method:
List<T> beansForType = beansByType.get().get(type);
The signature for the passed type and what is contained in the beansForType property is identical (in both cases with the Javassist annotated type and the non-Javaassist). The only thing that differs is the existence of a method. Which leads me into a gray area in my head. When Java does the Map look-up by Type for LazyValueHolder how are the types deemed equal?
-
4. Re: Weld CDI extension to extend AnnotatedType or add method
young_matthewd Nov 21, 2013 9:12 AM (in response to young_matthewd)Type ID number differs of course. When I come into the extension method watching the ProcessAnnotatedType event I have a Taker class with a Java type that has id say equal to 139. Then I modify the class with Javassist and get a similar Type but the id is different, say 249. Thus the look is done on the original 139 which has no Managed Bean corresponding to Taker (it exists but with a Map key compare of 249).
Is there anyway to register my modified Taker managed bean with the original Type id?
-
5. Re: Weld CDI extension to extend AnnotatedType or add method
mkouba Nov 21, 2013 9:13 AM (in response to young_matthewd)1 of 1 people found this helpfulI guess this might be some class loader issue. CtClass.toClass() uses the context class loader of the current thread which might be inappropriate here.
-
6. Re: Weld CDI extension to extend AnnotatedType or add method
young_matthewd Nov 21, 2013 9:40 AM (in response to mkouba)toClass uses the class loader of the current thread. Not sure this is the problem rather that Javassist likely has to generate a "new" Type since a new method is added. Working on assumptions here.
Can get at the resolver (TypeSafeBeanResolver) through the bean manager but no seeing a way to "cross map" the original Type id to the managed bean keyed of the adjust Type made by Javassist.
-
7. Re: Weld CDI extension to extend AnnotatedType or add method
young_matthewd Nov 22, 2013 1:50 AM (in response to young_matthewd)Got an idea, just haven't implemented it...
The problem is the binding in the InjectionPoint (in my class a Field Injection Point). The Java Type contained within the InjectionPoint is the "old" (before the Javassist modification). I can create a map of "old" to "new" annotated types. Then when processing the ProcessInjectionTarget events switch out the injection target that use my "new" beans which I can find in the InjectionPoint set associated with the InjectionTarget. If I can create my own InjectionPoint and InjectionTarget then I can set the event with the target.
-
8. Re: Weld CDI extension to extend AnnotatedType or add method
young_matthewd Nov 22, 2013 5:02 AM (in response to young_matthewd)Okej. One step forward 2 back.
Did the following. On ProcessInjectionTarget events I get the InjectionTarget and loop through the InjectionPoint associated. For each InjectionPoint that is a FieldInjectionPoint I get the WeldField and modify the underlying Field with my "new" Type objects (saved in a map when handling ProcessAnnotatedType events):
InjectionTarget<T> target = event.getInjectionTarget();
Set<InjectionPoint> points = target.getInjectionPoints();
for (InjectionPoint point : points) {
if (point instanceof FieldInjectionPoint) {
FieldInjectionPoint fip = (FieldInjectionPoint) point;
WeldField wf = fip.getWeldField();
Type current = wf.getBaseType();
Type wrapper = switching.get(current); // pulling from the map local to my extension
Field field = wf.getJavaMember();
Field underlyingType = field.getClass().getDeclaredField("type");
underlyingType.setAccessible(true);
if (wrapper != null) {
underlyingType.set(field, wrapper);
WeldField wfOther = WeldFieldImpl.of(field, wf.getDeclaringType(), new ClassTransformer(new TypeStore()));
FieldInjectionPoint fipOther = FieldInjectionPoint.of(fip.getBean(), fip.getInjectionTargetClass(), wfOther);
}
}
.... // save the new InjectionTarget
}
Works fine add I get through validation. BUT. This doesn't help when somebody else uses the BeanManager to create an annotated type then a managed bean like Arquillien does with it's JUnit tests and uses the real Field.
So I got to apply my changes inside the BeanManager if this is really going to work.
-
9. Re: Weld CDI extension to extend AnnotatedType or add method
young_matthewd Dec 18, 2013 3:42 AM (in response to young_matthewd)Basically nothing can be done since outside of writing a java agent it isn't possible to "replace" a class in a classloader (doesn't matter if one uses ASM or Javassist or CGLIB). Stuck with fiddling around with the CDI implementation. Not a great option but.....
Best place I can think of starting at is the bean resolver in the bean manager. TypeSafeBeanResolver. Specifically proxying the getBeans by type method.
Wondering if my corner case is potentially something that ought to exist in the CDI extension? Allowing somebody to switch out types of resolved injection point on certain conditions (like only adding methods and so forth).
-
10. Re: Weld CDI extension to extend AnnotatedType or add method
young_matthewd Dec 18, 2013 7:49 AM (in response to young_matthewd)After adjusting the TypeSafeBeanResolver by intercepting the getAllBeans method and replacing the Resolvable rawType, typeClosure with my proxied annotated type I ran into the next problem. A problem I should have seen coming. Weld is going to take the resolved bean and do a reflection set on the target's field. That field is coded to the "old" type not the one I make with Javassist. Fail. ;-)
Short of the long: this approach won't work. Back to square one.
-
11. Re: Weld CDI extension to extend AnnotatedType or add method
young_matthewd Dec 20, 2013 1:29 AM (in response to young_matthewd)TypeSafeBeanResolver is smart. Just by extending the target class the super type is enough to resolve:
CtClass wrapper = pool.makeClass(target.getJavaClass().getName() + "Proxy");
wrapper.setSuperclass(pool.get(target.getJavaClass().getName()));
This has lead me to another problem BUT all beans generated are of type proxy and their injection points are valid.