A generic Web Bean for injecting entities

Posted by    |       CDI

Here's another usecase for the injection point metadata API that we're considering adding to Web Beans. I've always thought it would be nice to be able to inject entity instances by role, instead of passing references around the system. (In Seam, you can use home objects to achieve this.)

Of course, we need a way to distinguish which instance of a particular entity type we're interested in. For each conversation, there are certain roles. For example, in an user administration screen, there is the current user, and the user that is being administered.

In a Web Beans world, we would represent a role using an annotation. For example, we would refer to the @Administered User. What I would like to be able to do is associate an identifier with the role, at the beginning of a conversation, and then simply inject the entity with that identifier into any Web Bean that is invoked during that conversation. Of course, I could write a Web Bean with a producer method to do this:

@ConversationScoped
public class AdministeredUser {

    private @PersistenceContext EntityManager em;
    private String userId;
    
    public void setUserId(String userId) { 
        this.userId = userId; 
    }
    
    @Produces @Administered User getUser() {
        return userId==null ? null : em.find(userId);
    }
    
}

However, I don't really want to have to write a new Web Bean for every role! So let's generify this code. Instead of making @Administered a binding annotation, we'll use a generic binding type @Persistent, and use InjectionPoint to determine the role that was specified at the injection point:

@ConversationScoped
public class EntityRoles {

    private @PersistenceContext EntityManager em;
    private Map<Key, Object> idByRole = new HashMap<Key, Object>();
    
    public void setId(Class entity, Annotation role, Object Id) { 
        idByRole.put(new Key(entity, role), id);
    }
    
    @Produces @Persistent Object getInstance(InjectionPoint ij) {
        Object id = idByRole.get(new Key(ij.getType(), ij.getAnnotations()));
        return id==null ? null : em.find(id);
    }
    
}

(I'm leaving out the boring implementation of Key, and the binding annotation @Persistent.)

Now, the following code may be used to set the identifier, at the beginning of a conversation:

@Current EntityRoles er;

...

er.setId(User.class, Administered.class, userId);

And the User may be injected as follows:

@Administered @Persistent User user

We probably also want to make it possible to refer to the User by a name in Unified EL, but for that we'll need to write a ELResolver (which is also too boring for this post).


Back to top