[RESOLVIDO] Injetar DAO Genérico via CDI em um DataModelFactory

Bom dia, pessoal.

Me deparei com a seguinte situação-problema: tenho uma classe DAO Genérico e criei um DAOFactory que contém um método anotado com @Produces que retorna o DAO de acordo com o bean que eu informar. Ex:

@Inject
private DAO<Produto> dao;

A injeção ocorre normalmente, inclusive com o EntityManager que eu injeto no DAOFactory:

public class DAOFactory<T extends Serializable> {

	@Inject
	private EntityManager em;

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Produces
	public DAO createDAO(InjectionPoint injectionPoint) throws ClassNotFoundException {
		
		 ParameterizedType type = (ParameterizedType) injectionPoint.getType(); 
		 Class classe = (Class) type.getActualTypeArguments()[0]; 
		 return new DAO(classe, em);
        }
}

Até aí tudo bem.
Tive que expandir o sistema de modo a ter também um DataModel Genérico (do componente DataModel do Primefaces) para não precisar ficar criando vários DataModel para cada bean do meu sistema.

package br.com.caelum.notasfiscais.datamodel;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

import org.primefaces.model.LazyDataModel;
import org.primefaces.model.SortOrder;

import br.com.caelum.notasfiscais.dao.DAO;

public class DataModelGenerico<T extends Serializable> extends LazyDataModel<T> {

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	@SuppressWarnings("unused")
	private Class<? extends Serializable> classe;
	
	private DAO<T> dao;

	public DataModelGenerico(Class<T> classe, DAO<T> dao) {
		this.classe = classe;		
		this.dao = dao;
	}

	@Override
	public List<T> load(int inicio, int quantidade, String campoOrdenacao,
			SortOrder sentidoOrdenacao, Map<String, String> filtros) {

		return (List<T>) dao.listaTodosPaginada(inicio, quantidade);

	}

}

Por usar CDI, também tive que criar um DataModelFactory (no mesmo estilo do DAOFactory):

package br.com.caelum.notasfiscais.datamodel;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;

import javax.enterprise.inject.Produces;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.inject.Inject;

import br.com.caelum.notasfiscais.dao.DAO;

public class DataModelFactory<T extends Serializable> {
	
	@Inject
	private DAO<? extends Serializable> dao;

	@Produces
	public DataModelGenerico createFactory(InjectionPoint injectionPoint) {
		ParameterizedType type = (ParameterizedType) injectionPoint.getType();
		Class classe = (Class) type.getActualTypeArguments()[0];
		
		return new DataModelGenerico(classe, dao);
	}

}

Tive que fazer uma pequena alteração no método createDAO do DAOFactory para verificar o tipo do objeto que está vindo na injeção:

if (type instanceof ParameterizedType) {
			ParameterizedType paramType = (ParameterizedType) type;
			if (paramType.getActualTypeArguments()[0] instanceof TypeVariable<?>)
				dataType = (Class<T>) paramType.getClass().asSubclass(Serializable.class);
				//dataType = Class.forName(paramType.getClass().getName()).asSubclass(Serializable.class);
			else
				dataType = (Class<T>) paramType.getActualTypeArguments()[0];
		} else if (type instanceof Class) {
			dataType = (Class<T>) type;
		}

Da maneira como está o código até então descrito, me deparo com a seguinte exceção:

java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.WildcardTypeImpl cannot be cast to java.lang.Class
	br.com.caelum.notasfiscais.dao.DAOFactory.createDAO(DAOFactory.java:38)

a linha 38 que fala na exceção acima é esta que está no método createDAO do DAOFactory:

dataType = (Class<T>) paramType.getActualTypeArguments()[0];

Se alguém tiver alguma idéia de como eu poderia resolver isto, ou até mesmo de outra maneira, ficaria grato.
Abraços!

Acho que vc não vai conseguir injetar o dao na DataModelFactory, vai ter q criar manualmente.

Como o dao do DataModelGenerico é sempre do mesmo tipo, por exemplo, DataModelGenerico vai ter q receber um DAO, vc pode criar manualmente invocando o método da DAOFactory ou chamando o new do construtor do dao.

Ficaria assim no final:

public class DAOFactory{

        @Inject
        private EntityManager em;

	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Produces
	public DAO createDAO(InjectionPoint injectionPoint) throws ClassNotFoundException {
		
		 ParameterizedType type = (ParameterizedType) injectionPoint.getType(); 
		 Class classe = (Class) type.getActualTypeArguments()[0]; 
		 return new DAO(classe, em);
        }
}

e a DataModelFactory:

public class DataModelFactory {

        @Inject
        private DAOFactory daoFactory;

	@Produces
	public DataModelGenerico createFactory(InjectionPoint injectionPoint) {
		ParameterizedType type = (ParameterizedType) injectionPoint.getType();
		Class classe = (Class) type.getActualTypeArguments()[0];
                
                //Pode chamar a fábrica
                DAO dao = daoFactory.createDAO(injectionPoint);
                
                // Ou invocar o construtor direto
                DAO dao = new DAO(classe,em);
		
		return new DataModelGenerico(classe, dao);
	}

}

Faz um teste e ve se resolve. Sei que não deveriamos usar a fábrica diretamente, mas como é um código mais de infra, acho que não vamos ter muitos problemas.

PS: As fábricas não precisam ser genéricas :wink:

mario.fts, boa tarde!
Cara, funcionou perfeitamente.
Utilizei a primeira abordagem que você mencionou na resposta, ou seja, chamando o método createDao do DAOFactory.

Abraços! Muito obrigado pela ajuda.