Injecting generic Beans with CDI/Weld
Asked Answered
V

1

7

I just come from my tiny nice JavaSE/Guice world and am currently discovering the path of "carried by the container"-EE6. After having some trouble with Glassfish3.1, I just switched to JBoss and am now facing a problem that shouldnt be one.

As infrastructural assisting class, im trying to create a generic repository/DAO for any kind of entity. In a very simple manner, this might look like this one.

public class Repository<E, K extends Serializable & Comparable<K>> {

    private final Instance<EntityManager> entityManagerInstance;

    protected final Class<E> getDomainObjectClass() {
        return domainObjectClass;
    }

    private final Class<E> domainObjectClass;

    protected final EntityManager getEntityManager() {
            return entityManagerInstance.get();
    }

    @Inject
    public Repository(Instance<EntityManager> entityManageryProvider, Provider<E> domainObjectProvider) {
            //This is a dirty hack, sadly :(
            domainObjectClass = (Class<E>)domainObjectProvider.get().getClass();
            this.entityManagerInstance = entityManageryProvider;
    }

    public final void persist(E domainObject) {
        final EntityManager em = getEntityManager();
        em.persist(domainObject);
    }

    public final Collection<E> getAllEntities() {
            final EntityManager em = getEntityManager();
            final CriteriaBuilder cb = em.getCriteriaBuilder();
            final CriteriaQuery<E> query = cb.createQuery(getDomainObjectClass());

            final List<E> result = em.createQuery(query).getResultList();
            return Collections.unmodifiableList(result);
    }

    public final E find(K id) {
            Preconditions.checkNotNull(id);
            final EntityManager em = getEntityManager();
            return em.find(getDomainObjectClass(), id);
    }

    // [...]
}

Now there may be a bean that does not require entity-dependent query capabilities but just a repository of a certain entity type, like (might be a test case):

public class DomainObjectARepositoryTest{

    @Inject
    Repository<DomainObjectA, PersistableUUID> domainObjectARepository;


    @Test
    public void testMitarbeitererstellung() {
        for (DomainObjectA a : domainObjectARepository.getAllEntities()) {
            // do cool stuff
        }       
    }
}

Unfortunatly Weld does not seem to like this kind of generic injection. At deployment time, I get the following error:

state=Create: org.jboss.weld.exceptions.DeploymentException: WELD-001408 Unsatisfied dependencies for type [Repository<DomainObjectA , PersistableUUID>] with qualifiers [@Default] at injection point [[field] @Inject sompackage.DomainObjectARepositoryTest.domainObjectARepository]

Am I missing something or did they just forgot to implement generic injects? As far as I understand the generic stuff, it is erasured after compiletime anyway - even this worked so fine in guice3 so far.

Kind regards,

avi

edit: found a comment by garvin king that this behavior is in the spec, but not implemented in weld, (staement was in june 2009)

Verdin answered 21/6, 2011 at 22:31 Comment(4)
Good find, regarding the comment :-)Veach
ty @jan. Well, since this comment is not about two years old, I'd hope weld could have implemented it until nowVerdin
hi @avithan, how did you do this in Guice? Especially the Provider<E> domainObjectProvider part. Do you manually bind Provider<X> for every X?Montsaintmichel
hi @irreputable. If I remember correctly, there is a default provider for every type which uses the default constructor or the constructor annotated with @Inject. So there is no need to bind the entity types.Verdin
V
1

That's rather a long comment than a complete answer to your question, but might point you in the right direction:

I'm following the discussions in seam-dev & weld-dev since quite some time, and do not remember that anything like this ever popped up. So my guess would be that it hasn't been on the agenda ever since Gavin commented about it.

What you can do relatively easy to verify this assumption:

(a) Obtain a reference to the BeanManager and query it for the relevant bean type (or just for Object to be on the save side), of course you will have to remove @Inject in DomainObjectARepositoryTest in order to get the application started.

(b) Register an extension and listen to ProcessBean to what comes up during the deployment. That would be my suggested way to go, you'll find more information here.

With that outcome you should definitely be able to tell if there are any bean types Repository<E, K extends Serializable & Comparable<K>> hanging around :-)

Would be cool if you'd report back here with the results and also considered filing a Jira issue in the negative case.

Veach answered 23/6, 2011 at 5:17 Comment(2)
Hi @jan, thanks for that comment, I'll try the extension way within the next days. Today i just had a talk with another ee6-professional who told me that he is using generic injection out of the box (so seems to be working for him). Maybe its a problem with the deployment (I'm using m2eclipse (+wtp-addon) and the jboss eclipse tools for deployment - maybe he is using some kind of wrong profile there - dunnow)Verdin
Hi @jan, I finally came down to the stupid point I missed out in my example above: The Repository was abstract and there was no concrete implementation for Repository<DomainObjectA,?>. But - if there are multiple implementations of Repository, the dependency went ambigious (even if the type arguments are different), so I manually have to select the right one from an Instance.iterator() with some Class<E> getDomainObjectType()-method.Verdin

© 2022 - 2024 — McMap. All rights reserved.