0
votes

The following code snipset works with "weld-core-1.1.5.AS71.Final.jar" (the one used by JBoss 7.1.1), but doesn't work with "weld-core-impl-2.2.6.Final.jar" (the one used by wildfly 8.2).

public class Client<T> {
    public interface Spi<T> {
        T getSomething();
    }

    @Inject
    private Spi<T> spi; // WELD-001408: Unsatisfied dependencies for type Spi<Object>

}

public class SpiImpl implements Client.Spi<Integer> {
    @Override
    public Integer getSomething() {
        return 5;
    }
}

Why? The CDI 1.2 specification:

A parameterized bean type is considered assignable to a parameterized required type if they have identical raw type and for each parameter:

  1. the required type parameter and the bean type parameter are actual types with identical raw type, and, if the type is parameterized, the bean type parameter is assignable to the required type parameter according to these rules, or
  2. the required type parameter is a wildcard, the bean type parameter is an actual type and the actual type is assignable to the upper bound, if any, of the wildcard and assignable from the lower bound, if any, of the wildcard, or
  3. the required type parameter is a wildcard, the bean type parameter is a type variable and the upper bound of the type variable is assignable to or assignable from the upper bound, if any, of the wildcard and assignable from the lower bound, if any, of the wildcard, or
  4. the required type parameter is an actual type, the bean type parameter is a type variable and the actual type is assignable to the upper bound, if any, of the type variable, or
  5. the required type parameter and the bean type parameter are both type variables and the upper bound of the required type parameter is assignable to the upper bound, if any, of the bean type parameter.

So, according to item #4, the above code should work, shouldn't it?

Edit 1: <-- See Edit 2 before. There I noticed that the CDI implementation is doing what I expected it should do, but I mistakenly thought it didn't.

Antonin Stefanutti answer is right: item #4 of the mentioned specification don't apply. But, if the actual type of the type variable T is known when the inyectión point is resolved, it is possible to get a proper instance of Spi.

Supouse Client class is abstract and an implementation specifies the type of the type parameter in the class definition. In that case, the actual type can be discovered and item #4 of the specification would apply.

How can be resolved? By means of introspection, using Class#getGenericInterfaces() operation, on the actual Class of the Client instance. Like this:

public class Test {

@Inject
private ClientImpl clientImpl;

public abstract static class Client<T> {
    public interface Spi<T> {
        T getSomething();
    }

    // @Inject inject don't work because CDI doesn't require it even when the actual type of T can be discovered. So, instead, I initialized it programaticaly in the postConstruct()
    private Spi<T> spi; // WELD-001408: Unsatisfied dependencies for type Spi<Object> // Not true, later, in "edit 2" I noteced it works fine. Forget all this. Sorry.

    @Inject
    private Instance<Spi<?>> spiFinder;

    /**Initializes the spi instance variable programaticaly */
    @PostConstruct
    private void postConstruct(){
        ParameterizedType clientType = (ParameterizedType)this.getClass().getGenericSuperclass();
        Type clientTypeParamter = clientType.getActualTypeArguments()[0];
        Class<T> clientParameterClass = (Class<T>)clientTypeParamter;

        final Iterator<Spi<?>> iterator = spiFinder.iterator();
        while (iterator.hasNext()) {
            Spi<?> spiCandidate = iterator.next();
            ParameterizedType spiCandidateType = (ParameterizedType)spiCandidate.getClass().getGenericInterfaces()[0];
            Type spiCandidateTypeParameter = spiCandidateType.getActualTypeArguments()[0];
            Class<?> spiCandidateParameterClass = (Class<?>)spiCandidateTypeParameter;
            if( clientParameterClass.isAssignableFrom(spiCandidateParameterClass)) {
                if( spi != null)
                    throw new AmbiguousResolutionException();
                spi = (Spi<T>)spiCandidate;
            }
        }
        if( spi == null)
            throw new UnsatisfiedResolutionException();
    }
}

@Dependent
public static class SpiImpl_1 implements Client.Spi<Integer> {
    @Override
    public Integer getSomething() {
        return 5;
    }
}

@Dependent
public static class SpiImpl_2 implements Client.Spi<Double> {
    @Override
    public Double getSomething() {
        return 5.0;
    }
}

@Dependent
public static class ClientImpl extends Client<Integer> {}

}

The above code works (an instance of SpiImpl_1 is injected into spi instance variable), so, why couldn't CDI do this job for us? With instrospection it's possible to discover the actual type of every type parameter declared in the enclosing class/interface definition when the concrete class specifies the actual type of extended superclass's type parameter.

Edit 2 Sorry, forget everything I have said. CDI does inject the proper instance of Client in this second example without the need of the programatic initialization I added. It doen't throw "WELD-001408: Unsatisfied dependencies for type Spi" where I said it does. I was wrong. Sorry again. Should I delete this question?

1

1 Answers

1
votes

In your example, the required type parameter is a type variable, T, and not an actual type so that item #4 does not apply. Actually, any of the conditions mentioned in the specification for assignability of a parameterized bean type to a parameterized required type are met in your example, which implies conclusively that the dependency is not satisfied.

This is somehow related to CDI-517, though not identical, yet the clarification can explain the behavioral changes between Weld 1.x and Weld 2.x as the specification has been clarified.

As mentioned in that discussion, having Spi<Integer> assignable to Spi<T> would be incorrect from the Java langage standpoint.