3

Having class like so:

public class A {
   @Inject B b;
   @Inject C c;
}

is it possible to tell Weld NOT to inject into c? I can veto A class using event:

<T> void processAnnotatedType(@Observes  ProcessAnnotatedType<T> pat) 

but then also B object would not be injected. I am searching for sth like: "if class name is A and field type is C then omit injection."

To be more specific I want HK2 engine to inject into the "C" field and the problem is that HK2 and Weld are both using @Inject annotation.

Siliarus solution:

I gave a try to Siliarus solution. I found the type that I want add my custom injection implementation to like:

<T> void processIT(@Observes ProcessInjectionTarget<T> pat, BeanManager beanManager) {      
            Set<InjectionPoint> injectionPoints = pat.getInjectionTarget().getInjectionPoints();
            for (InjectionPoint injectionPoint : injectionPoints) {
                if (injectionPoint.getType().equals(B.class)) {
                    l.info("Adding CustomInjection to  {}", pat.getAnnotatedType().getJavaClass());
                    pat.setInjectionTarget(new CustomInjection<T>(pat.getInjectionTarget(), beanManager));
                }
            }
        }
    }
}

and after I added overrided inject(...) in CustomInjection

public CustomInjection(InjectionTarget<T> originalInjectionTarget, BeanManager beanManager) {
    this.wrappedInjectionTarget = originalInjectionTarget;
    this.beanManager = beanManager;
}

like:

@Override
public void inject(T instance, CreationalContext<T> ctx) {

    l.trace("Injecting into {}", instance);
//....create my own HK2 object. Can it be just new B() for example ?!       
    locator =ServiceLocatorUtilities.createAndPopulateServiceLocator();  
    B b = locator.createAndInitialize(B.class);

    l.trace("First injecting standard dependencies {}", instance);
    wrappedInjectionTarget.inject(instance, ctx);
    // dispose created by CDI B type object ?! - seems messy but works 
    manageBViaReflection((x, y, z) -> x.set(y, z), instance, b);
}

In manageBViaReflection I just set the object B - b to field X of type B and name b on instance Y - instance.

The delicate inaccuracy is that line:

wrappedInjectionTarget.inject(instance, ctx);

performs and CDI injection on B. I have producer to type B but I want to create it on my own in this particular class - not using a producer. Object B must be disposed and when I override its value using manageBViaReflection then I must dispose it first - its a bit messy but generally that idea works.

Siliarus, jwells131313 - maybe U have any further suggestions ?

maciek
  • 83
  • 7
  • Who is creating class A, Weld (CDI) or HK2? If you don't know you can print a stack trace (or put a breakpoint) in the constructor of A to figure it out – jwells131313 Nov 15 '16 at 13:14
  • A is created by CDI. – maciek Nov 16 '16 at 10:39
  • @maciek I am not sure what is the problem now? Firstly, why do you need `manageBViaReflection`? Secondly, `I have producer to type B` - why? You dont wan't CDI to produce this, you do it yourself, don't you? If its the CDI hierarchy confusing you here (`InjectionTarget extends Producer`) then beware, in this case `Producer` is not a classical `@Produces`, see its [javadoc](https://github.com/cdi-spec/cdi/blob/1.2/api/src/main/java/javax/enterprise/inject/spi/Producer.java#L38) for more info. – Siliarus Nov 16 '16 at 11:48
  • Ok forget Producer of B - I do want to produce it myself. Then how do I set the field B b to customly produced object in class A (I want to set it there, right?) As I understand `public void inject(T instance, CreationalContext ctx)` is invoked when there is @Inject annotation present above B b reference? This implies that if @Inject is present then Weld checks wheather it is able to perform injection in deployment phase. If I dont have @Inject B b then `inject(T instance, CreationalContext ctx)` will not be invoked and I am not able to produce and set my own object. – maciek Nov 16 '16 at 12:01
  • Could U provide sample pseudocode implementation of method `public void inject(T instance, CreationalContext ctx)` that follows your idea? Lets assume object B is somehow already created at the beginnig of inject(...). How should I perform injection of object B into T instance in this inject(...) method? Maybe I miss some obvious feature? – maciek Nov 16 '16 at 13:57
  • @Siliarus I am really curious of your implementation. – maciek Nov 16 '16 at 14:05
  • Oh, I can see where you are headed now, I probably read it way too hastily before. I don't have it implemented, as I said, I don't know much if anything, about HK2. This looks like a tougher kind of nut, I suggest we take this discussion over to [Weld forums](https://developer.jboss.org/en/weld), where me and my colleagues can take a closer look at this (and please include a link back here to SO as well) and try to help you further. – Siliarus Nov 16 '16 at 14:58
  • @Siliarus - Ok lets move - I will create new post on Weld Forum shortly but its not really about HK2 - as I said, assume obj B is already created for example: `locator.createAndInitialize(B.class)`, no matter how actually. The clue is how to set it to T instance. – maciek Nov 16 '16 at 15:08
  • @Siliarus - post waiting for moderation:https://developer.jboss.org/thread/273039 – maciek Nov 16 '16 at 16:02
  • In hk2 there is a ServiceLocator method called inject in which you can take an already injected object and cause injection to happen. However, it would very likely try to inject all injection fields, not just some specific set. hk2 has a lot of the same principals as CDI so one thing you could do is tell hk2 about the injection point it doesn't know with a constant (a pre-created object) or with a descriptor that can do it's lookup in the BeanManager of CDI (this is how the guice and spring bridges are implemented). I hope this helps! – jwells131313 Nov 17 '16 at 14:17

1 Answers1

2

Alright, from Weld/CDI point of view, here is how to disable injection into those fields. Note that I don't know HK2 so I don't know how you then want to link it there, but from CDI perspective you need to have the bean as @Dependent (to avoid proxying where things would get nastier). You haven't specified the version for CDI, so I'll make notes for both, 1.x and 2.0.

Actually what comes to me are two things, first, its the ProcessAnnotatedType phase, where you could remove the @Inject annotation so that when CDI takes that annotated type (which it turns into bean), it will no longer see it as injection point. You would do that as follows:

void processAnnotatedType(@Observes  ProcessAnnotatedType<T> pat) {
  pat.configureAnnotatedType().remove(Inject.class); // CDI 2.0 solution
  // for CDI 1.x you need to implement your own AT, which 
  // will do just the same, the call this:
  // pat.setAnnotatedType(yourOwnAT);
}

Second though takes into consideration ProcessInjectionTarget. You would need to wrap InjectionTarget with your own implementation. The strength of this approach is that you can hook HK2 internals here directly. The main idea is to override javax.enterprise.inject.spi.InjectionTarget.inject(T, CreationalContext<T>) and put the HK2 code here, so when CDI actually tries to make injection, it would use HK2.

void processIT(@Observes  ProcessInjectionTarget<T> pat) {
  pat.setInjectionTarget(myITImpl); // just set your wrapped impl here
  // there is no diff here in CDI 1.x and 2.0, no configurator here
}

Whichever way you choose, bear in mind that CDI has a nice huge set of TCK tests which cover all this stuff and hence can be used as an example to see how to implement such wrapper.

Siliarus
  • 6,393
  • 1
  • 14
  • 30