Generic DAO with CDI error = cannot be cast to java.lang.reflect.Parameterizedtype

Asked

Viewed 755 times

1

I’m trying to implement a generic CDI DAO. Apparently I’m doing it right, but I’m getting the following error:

cannot be cast to java.lang.reflect.Parameterizedtype

This error is pointing me to this line of my Repository class:

Class<T> clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];

Irepository interface code

package br.com.amarildo.repository;

import java.util.List;

public interface IRepository<T> {

    public T porId(Long id);

    public T guardar(T entity);

    public void alterar(T entity);

    public void remover(T entity);

    public List<T> todos();

}

Repository class code

package br.com.amarildo.repository;

import java.lang.reflect.ParameterizedType;
import java.util.List;

import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;

public class Repository<T> implements IRepository<T> {

    protected final EntityManager manager;
    private final Class<T> clazz;

    @SuppressWarnings("unchecked")
    @Inject
    public Repository(EntityManager manager) {
        this.manager = manager;
        this.clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
    }

    public T porId(Long id) {
        return manager.find(clazz, id);
    }

    public T guardar(T entity) {
        return this.manager.merge(entity);
    }

    public void alterar(T entity) {
        this.manager.merge(entity);
    }

    public void remover(T entity) {
        this.manager.remove(entity);
    }

    public List<T> todos() {
        TypedQuery<T> query = manager.createQuery("from " + clazz.getName(), clazz);
        List<T> resultList = query.getResultList();
        return resultList;
    }

}

class Testerepository code

package br.com.amarildo.repository;

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

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;

import br.com.amarildo.model.FuncionarioModel;


public class TesteRepository extends Repository<FuncionarioModel> implements Serializable {
    private static final long serialVersionUID = 1L;

    public TesteRepository(EntityManager manager) {
        super(manager);
    }

    public List<String> cargosQueContem(String cargo) {
        TypedQuery<String> query = super.manager.createQuery("select distinct cargo from FuncionarioModel " + "where upper(cargo) like upper(:cargo)", String.class);
        query.setParameter("cargo", "%" + cargo + "%");
        return query.getResultList();
    }

}

class code Functioncontroller where I run Testerepository

package br.com.amarildo.controller;

import java.io.Serializable;

import javax.inject.Inject;

import br.com.amarildo.model.FuncionarioModel;
import br.com.amarildo.repository.TesteRepository;
import br.com.amarildo.util.NegocioException;
import br.com.amarildo.util.Transactional;

public class FuncionarioController implements Serializable {
    private static final long serialVersionUID = 1L;

    @Inject 
    private TesteRepository funcionarioRepository;

    @Transactional
    public FuncionarioModel salvar(FuncionarioModel funcionarioModel) throws NegocioException {
        if (funcionarioModel.getCargo().isEmpty() ) {
            throw new NegocioException("Não é Possivel Salvar Funcionario sem Cargo");
        }
        return this.funcionarioRepository.guardar(funcionarioModel);
    }

    @Transactional
    public void atualizar(FuncionarioModel funcionarioModel) throws NegocioException {
        if (funcionarioModel.getCargo().isEmpty()) {
            throw new NegocioException("Não é possível fazer a Alteração campo cargo está vazio !");
        }
        this.funcionarioRepository.alterar(funcionarioModel);
    }

    @Transactional
    public void excluir(FuncionarioModel funcionarioModel) throws NegocioException {
        funcionarioModel = this.funcionarioRepository.porId(funcionarioModel.getCodigo());
        if (funcionarioModel.getCargo()== null) {
            throw new NegocioException("Não é possível excluir um Funcionario Demitido!");
        }
        this.funcionarioRepository.remover(funcionarioModel);
    }

}

How to solve this?

Follow the error I’m getting:

Caused by: org.jboss.weld.exceptions.DeploymentException: WELD-001408: Unsatisfied dependencies for type TesteRepository with qualifiers @Default
  at injection point [BackedAnnotatedField] @Inject private br.com.amarildo.controller.FuncionarioController.funcionarioRepository
  at br.com.amarildo.controller.FuncionarioController.funcionarioRepository(FuncionarioController.java:0)

    at org.jboss.weld.bootstrap.Validator.validateInjectionPointForDeploymentProblems(Validator.java:359)
    at org.jboss.weld.bootstrap.Validator.validateInjectionPoint(Validator.java:281)
    at org.jboss.weld.bootstrap.Validator.validateGeneralBean(Validator.java:134)
    at org.jboss.weld.bootstrap.Validator.validateRIBean(Validator.java:155)
    at org.jboss.weld.bootstrap.Validator.validateBean(Validator.java:518)
    at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:68)
    at org.jboss.weld.bootstrap.ConcurrentValidator$1.doWork(ConcurrentValidator.java:66)
    at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:63)
    at org.jboss.weld.executor.IterativeWorkerTaskFactory$1.call(IterativeWorkerTaskFactory.java:56)
    ... 4 more

jun 29, 2017 10:26:45 AM org.apache.catalina.core.ContainerBase startInternal
GRAVE: A child container failed during start
java.util.concurrent.ExecutionException: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost]]
    at java.util.concurrent.FutureTask.report(Unknown Source)
    at java.util.concurrent.FutureTask.get(Unknown Source)
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:916)
    at org.apache.catalina.core.StandardEngine.startInternal(StandardEngine.java:262)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147)
    at org.apache.catalina.core.StandardService.startInternal(StandardService.java:441)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147)
    at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:787)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147)
    at org.apache.catalina.startup.Catalina.start(Catalina.java:629)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:351)
    at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:485)
Caused by: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost]]
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:153)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1408)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1398)
    at java.util.concurrent.FutureTask.run(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
    at java.lang.Thread.run(Unknown Source)
Caused by: org.apache.catalina.LifecycleException: A child container failed during start
    at org.apache.catalina.core.ContainerBase.startInternal(ContainerBase.java:924)
    at org.apache.catalina.core.StandardHost.startInternal(StandardHost.java:871)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147)

Thanks to all the error It was solved as below: I had to do the injection in my Testerepository Builder of the Testerepository class was just that.

@Inject
public TesteRepository(EntityManager manager) {
    super(manager);
} 

2 answers

2

Let’s see what that line does:

Class<T> clazz = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];

First, we get the superclass, including the generic type:

getClass().getGenericSuperclass()

And then we cast a cast ParameterizedType, that is, a type that contains generic parameters.

Then we get the generic guy:

[...].getActualTypeArguments()[0];

And finally, we forced him with a cast for a class:

Class<T> clazz = (Class<T>) [...]

That is, if you have one BananaRepository extends Repository<Banana>:

  • The getClass() will give you the object representing the class BananaRepository.

  • The .getGenericSuperclass() var give you Repository<Banana> - Note that this is a parameterized generic type, ie a ParameterizedType, then we can cast.

  • The .getActualTypeArguments[0] will catch the generic type of Repository<Banana>, that is to say Banana. That’s a class, so the cast works.

So far so good, but things can go wrong in other scenarios:

  1. If you have a MyListRepository extends Repository<List<Banana>>, the getActualTypeArguments[0] will bring List<Banana>, which is a parameterized type, not a class. The result of this will be a ClasscastException.

  2. If you have a RawRepository extends Repository, without the generic type, the getGenericSuperclass() will return an object of the type Class, and not a ParameterizedType. Other ClassCastException here.

  3. If you have a BananaPrataRepository extends BananaRepository and BananaRepository extends Repository<Banana>, by instantiating the BananaPrataRepository, the getGenericSuperclass() will bring BananaRepository instead of Repository<Banana>, which will give you a ClassCastExcepotion.

  4. If you instantiate the Repository<T> directly, other than through a subclass, the getGenericSuperclass() will bring Object, and when trying to cast ParameterizedType, will give ClassCastException.

  5. If you have class StrangeRepository2 extends StrangeRepository1<String> and class StrangeRepository1<A> extends Repository<Banana>, the code will think that the generic type is the class String instead of class Banana. This case in practice does not make sense, but it is important to know that in theory it can occur, and if it occurs, the wrong generic type will be found.

The conclusion is that the class Repository may be used only under the following conditions:

  • It can only be used through a subclass, never directly.

  • Subclasses must declare the generic type of the superclass (Repository) in the extends.

  • Subclasses shall not contain other subclasses.

Now let’s see the top of your stacktrace:

java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
    at br.com.amarildo.repository.Repository.<init>(Repository.java:21)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
    at java.lang.reflect.Constructor.newInstance(Unknown Source)
    at org.jboss.weld.injection.ConstructorInjectionPoint.newInstance(ConstructorInjectionPoint.java:119)

It shows that Weld (responsible for CDI in Jboss) invoked the class constructor Repository directly through Reflection. This will make you fall in case 4 of the above list.

That is, in the place where you are injecting a Repository<AlgumaCoisa>, you must do this:

  • Inject a AlgumaCoisaRepository.

  • Work with @Qualifiers.

  • Specify the correct bean to be injected into beans.xml.

In addition, subclasses of Repository can themselves have subclasses through the following:

Class<?> directSubclass = getClass();
while (directSubclass.getSuperclass() != Repository.class) {
    directSubclass = directSubclass.getSuperclass();
}
Type genericSuper = directSubclass.getGenericSuperclass();
if (!(genericSuper instanceof ParameterizedType)) throw new UnsupportedOperationException("A superclasse definida deve conter os tipos genéricos adequados.");
Type generic = ((ParameterizedType) genericSuper).getActualTypeArguments()[0];
if (!(generic instanceof Class<?>)) throw new UnsupportedOperationException("O tipo genérico do repositório deve ser uma classe que não tenha ela memsma outro tipo genérico.");
Class<T> clazz = (Class<T>) generic;

In addition, the class Repository must be abstract.

  • Thank you very much Victor. I’ll check every step you’ve given me .

  • I posted the whole sequence of my code above I still could not identify the error if someone can tell me ta complicated. vlw

  • am receiving the following error now = WELD-001408: Unsatisfied dependencies for type Testerepository with Qualifiers @Default

0

Pass the generic class by parameter in the constructor

public Repository(EntityManager manager, Class<T> entityClass) {
    this.manager = manager;
    this.clazz = entityClass;
}
  • Thank you very much Marquezani I’m going to test.

  • I did the test the way below

  • ' @Suppresswarnings("unchecked") @Inject public Repository(Entitymanager manager, Class<T> entityClass) { this.manager = manager; this.clazz = entityClass = (Class<T>) ((Parameterizedtype) getClass().getGenericSuperclass()). getActualTypeArguments()[0]; }'

  • however returns me the following error = WELD-001408: Unsatisfied dependencies for type Class<T> with Qualifiers Default at Injection point [Backedannotatedparameter] Parameter 2 of [Backedannotatedconstructor] Inject public br.com.Amarildo.repository.Repository(Entitymanager, Class<T>) at br.com.Amarildo.repository.Repository. <init>(Repository.java:17)

  • This error is already caused by the Bean you passed by parameter. Try adding a Beans.xml in WEB-INF as in the link https://stackoverflow.com/questions/23561836/weld-001408-unsatisfied-dependencies-for-type-with-qualifiers-default-at

  • I did as mentioned most unfortunately the error percisti

  • xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee http://jboss.org/schema/beans_1_0.xsd

  • Thank you all for the error It was solved as below: I had to do the injection in my Testerepository method of the Testerepository class was just that. @Inject public Testerepository(Entitymanager manager) { super(manager); }

Show 3 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.