Search code examples
genericscdinested-generics

Decide what implementation to use in @Inject


I have a GenericCrudController<E> that encapsulates an instance of my GenericCrudBO<E>, and my GenericCrudBO<E> encapsulates an instance of my GenericCrudDAO<E>. So, what I want to do is to make, for example, my GenericCrudController<User> have by default the implementation of GenericCrudBO<User>, if there is any, of course. If not, I want to stick with the generic implementation. I am using CDI on the project and I think thats the way to go but I don't know what annotations to use. The structure and the scenario abovementioned comes next:

public abstract class GenericController<T extends BaseEntity> implements Serializable {
    private static final long serialVersionUID = 1L;

    protected List<T> list;
    protected T entity;
    protected T filter;

    @Inject
    @Named("crudBO")
    protected CrudBO<T> bo;

    /*IRRELEVANT CODE GOES THERE */
}

@Named("crudBO")
public class GenericCrudBO<E extends BaseEntity> implements CrudBO<E>{
    private static final long serialVersionUID = 1L;

    @Inject
    @Named("crudDAO")
    protected GenericCrudDAO<E> dao;

    @Override
    @SuppressWarnings("unchecked")
    public List<E> search(E filter) {
        Class<E> clazz = (Class<E>) filter.getClass();
        return dao.search(clazz, filter);
    }

    /*IRRELEVANT CODE GOES THERE*/
}

@Named("crudDAO")
public class GenericCrudDAO<E extends BaseEntity> implements CrudDAO<E>{/*IRRELEVANT CODE GOES THERE*/}

Those are some examples of implementation of the generic structure:

public class UserBO extends GenericCrudBO<User>{
    /*IRRELEVANT CODE GOES THERE*/
    @Override
    public List<User> search(User filter) {
        /*SPEFIFIC CODE*/
    }
}

@Named(value="userController")
@ViewScoped
public class UserController extends GenericController<User>{ 

    @Inject private UserBO userBO;

    @Override
    public void search() {
        if(filter != null) {
            list = userBO.search(filter);
        } else throw new RuntimeException();
    }
}

Now notice that I have to manually inject an instance of UserBO and override the search method to be able to achieve my goal of call the UserBO's search method instead of GenericCrudBO one. What I am asking is if its possible to do it with some CDI annotation, forcing the processor to inject an instance of the right implementation based on the generic type of the controller. Thanks


Solution

  • I solved the problem using an javax.enterprise.inject.Instance object to encapsulate my bo's and dao's. This way:

    @Inject
    private Instance<CrudBO<T>> bo;
    
    public CrudBO<T> getBo() {
        return bo.get();
    }
    

    Just to make things clear, CrudBO is the interface that GenericCrudBO implements, so, knowing that by default the GenericCrudBO is annotated with @Default, I annotated the specialized classes with @Alternative:

    @Alternative
    public class SpecializedCrudBO extends GenericCrudBO<SpecificClass>{
    }
    

    I also declared the alternatives BO's and DAO's in beans.xml:

    <beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
    
        <alternatives>
            <class>br.com.logtec.business.SpecializedCrudBO</class>
            ...
        </alternatives>
    
    </beans>
    

    And now CDI can handle my generic java beans at runtime, this is very good also because I dont have to create tons of boilerplate classes.