Quarkus: Unsupported bean type: UNRESOLVED_TYPE_VARIABLE, K When using generics

Created on 29 May 2020  路  7Comments  路  Source: quarkusio/quarkus

Hey,

I'm trying my first Quarkus application and are moving from Spring to Quarkus.

In Spring I gererated an helper library where I defined an generic standardservice.

The Service class looks like this:

@Service
public abstract class AbstractService<M extends AbstractModel<K>, K extends Serializable, R extends JpaRepository<M, K>> {

    protected R repository;

    public AbstractService() {

    }

    public Optional<M> get(K id) {
        return repository.findById(id);
    }

    public Optional<M> exists(M model) {
        return repository.findOne(Example.of(model));
    }

    public List<M> getAll() {
        return repository.findAll();
    }

    public M addNew(M newModel) {
        return repository.saveAndFlush(newModel);
    }

    public boolean delete(K id) {
        try {
            repository.deleteById(id);
            return true;
        } catch (Exception ex) {
            return false;
        }
    }

    public Optional<M> update(M updateModel) {

        Optional<M> mOptional = repository.findById(updateModel.getId());

        if (mOptional.isPresent())
            repository.saveAndFlush(updateModel);

        return mOptional;
    }

}

The model class looks like this:

@Getter
@Setter
@MappedSuperclass
@NoArgsConstructor
@AllArgsConstructor
public abstract class AbstractModel<K extends Serializable> {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private K id;
}

This is the controller:

@Slf4j
@RestController
@RequestMapping("service.api.name")
public abstract class AbstractController<M extends AbstractModel<K>, S extends AbstractService<M, K, R>, K extends Serializable, AMA extends AbstractModelAssembler<M, K>, R extends JpaRepository<M, K>> {

    @Value("service.api.name")
    protected String apiName;

    protected S service;

    protected AMA assembler;

    public AbstractController() {

    }

    @GetMapping("/{id}")
    @ResponseBody
    public Response get(@Context UriInfo uriInfo, @PathVariable(value = "id") K id) {

        Optional<M> optionalModel = service.get(id);

        if (optionalModel.isPresent()) {
            return assembler.singleObject(uriInfo, optionalModel.get(), Response.Status.OK);
        } else {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
    }

    @GetMapping("/getall")
    @ResponseBody
    public Response getall(@Context UriInfo uriInfo) {
        return assembler.collection(uriInfo, service.getAll(), Response.Status.OK);
    }

    @PostMapping("/addNew")
    @ResponseBody
    public Response addNew(@Context UriInfo uriInfo, @RequestBody M newModel) {

        Optional<M> tmpModel = service.exists(newModel);

        if (tmpModel.isPresent()) {
            return assembler.singleObject(uriInfo, tmpModel.get(), Response.Status.SEE_OTHER);
        } else {
            M model = service.addNew(newModel);
            return assembler.singleObject(uriInfo, model, Response.Status.CREATED);
        }
    }

    @DeleteMapping("/{id}")
    @ResponseBody
    public Response delete(@PathVariable(value = "id") K id) {
        if (service.delete(id))
            return Response.noContent().build();
        else
            return Response.status(Response.Status.NOT_FOUND).build();
    }

    @PutMapping("/update")
    @ResponseBody
    public Response update(@Context UriInfo uriInfo, @RequestBody M updateModel) {

        Optional<M> mOptional = service.update(updateModel);

        if (mOptional.isPresent()) {
            return assembler.singleObject(uriInfo, mOptional.get(), Response.Status.OK);
        } else {
            return Response.status(Response.Status.NOT_FOUND).build();
        }
    }

}

and this is my Assembler:

@Component
public abstract class AbstractModelAssembler<M extends AbstractModel<K>, K extends Serializable> {

  @Value("service.api.name")
  protected String apiName;

  public Response singleObject(@Context UriInfo uriInfo, M model, Response.Status status) {

    List<Link> links = initLinks(model, uriInfo);

    GenericEntity<M> genericEntity =
        new GenericEntity<>(model) {
        };

    Link self = Link.fromUriBuilder(uriInfo.getAbsolutePathBuilder())
        .rel("self").build();

    return Response.status(status).entity(genericEntity).links(self).build();
  }

  public Response collection(@Context UriInfo uriInfo, List<M> models, Response.Status status) {

    List<Link> links = new ArrayList<>();
    models.forEach(m -> links.addAll(initLinks(m, uriInfo)));

    GenericEntity<List<M>> genericEntity =
        new GenericEntity<>(models) {
        };

    Link self = Link.fromUriBuilder(uriInfo.getAbsolutePathBuilder())
        .rel("self").build();

    return Response.status(status).entity(genericEntity).links(self).build();
  }

  private List<Link> initLinks(M model, UriInfo uriInfo) {
    UriBuilder uriBuilder = uriInfo.getRequestUriBuilder();
    uriBuilder = uriBuilder.path(model.getId().toString());
    Link.Builder linkBuilder = Link.fromUriBuilder(uriBuilder);
    Link selfLink = linkBuilder.rel("self").build();//.toString().replace("${application.api.name}", apiName);
    return Arrays.asList(selfLink);
    //model.setLinks(Arrays.asList(selfLink));
  }
}

When trying to build the native executable, I get this message:

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  9.984 s
[INFO] Finished at: 2020-05-29T10:52:22+02:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal io.quarkus:quarkus-maven-plugin:1.5.0.Final:build (default) on project QuarkusTemplatePlugin: Failed to build quarkus application: io.quarkus.builder.BuildException: Build failure: Build failed due to errors
[ERROR]     [error]: Build step io.quarkus.arc.deployment.ArcProcessor#generateResources threw an exception: java.lang.IllegalArgumentException: Unsupported bean type: UNRESOLVED_TYPE_VARIABLE, K
[ERROR]     at io.quarkus.arc.processor.Types.getTypeHandle(Types.java:141)
[ERROR]     at io.quarkus.arc.processor.Types.getTypeHandle(Types.java:91)
[ERROR]     at io.quarkus.arc.processor.Types.getTypeHandle(Types.java:77)
[ERROR]     at io.quarkus.arc.processor.Types.getTypeHandle(Types.java:91)
[ERROR]     at io.quarkus.arc.processor.BeanGenerator.initConstructor(BeanGenerator.java:657)
[ERROR]     at io.quarkus.arc.processor.BeanGenerator.createConstructor(BeanGenerator.java:555)
[ERROR]     at io.quarkus.arc.processor.BeanGenerator.generateClassBean(BeanGenerator.java:297)
[ERROR]     at io.quarkus.arc.processor.BeanGenerator.generate(BeanGenerator.java:116)
[ERROR]     at io.quarkus.arc.processor.BeanProcessor.generateResources(BeanProcessor.java:199)
[ERROR]     at io.quarkus.arc.deployment.ArcProcessor.generateResources(ArcProcessor.java:393)
[ERROR]     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
[ERROR]     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
[ERROR]     at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
[ERROR]     at java.base/java.lang.reflect.Method.invoke(Method.java:566)
[ERROR]     at io.quarkus.deployment.ExtensionLoader$2.execute(ExtensionLoader.java:932)
[ERROR]     at io.quarkus.builder.BuildContext.run(BuildContext.java:277)
[ERROR]     at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
[ERROR]     at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:2046)
[ERROR]     at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1578)
[ERROR]     at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1452)
[ERROR]     at java.base/java.lang.Thread.run(Thread.java:834)
[ERROR]     at org.jboss.threads.JBossThread.run(JBossThread.java:479)
[ERROR] -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR] 
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException

Am I missing something inside my code or is this a limitation of Quarkus? In Spring I can compile and use it inside services.

arearc arespring

All 7 comments

cc @mkouba @manovotn

I'm not an expert on Spring (@geoand is ;-) but all the classes from the snippets are abstract. How does the real component look like?

I admit that the error message is not very clear. We should add some more info...

I don't really know how the integration layer is looking so I am not clear in what is this supposed to turn into in CDI terms :-D
I suppose a similar issue could be reproduced with simple CDI beans/annotations and not just through spring.

P.S. Sometimes I truly wonder what is the mysterious force causing all the juicy generics issues to pop up specifically on Fridays...

I'm not an expert on Spring (@geoand is ;-) but all the classes from the snippets are abstract. How does the real component look like?

I admit that the error message is not very clear. We should add some more info...

Yeah, I pinged you guys because of the CDI aspect of the issue :)

Hey, thx for the answers :).

@manovotn
In my case the force for this juicy generic is that I tried the whole week to solve it myself till I lost all sanity yesterday ;)

@mkouba
I added also -e and -X flag to eventuelly determine what exactly or where the fault is, but it just gave me a hole lot of more text with not really usefull informations.

The implementations would look like this:

Assembler:
'@Component
public class TemplateModelAssembler extends AbstractModelAssembler {
}'

Controller:
@RestController public class TemplateController extends AbstractController<TemplateModel, TemplateAbService, Long, TemplateModelAssembler, TemplateRepository> { }

Model:
`@Table(name = "templatetable", schema = "public")
@Entity
public class TemplateModel extends AbstractModel {

}
`

Repository:
@Repository public interface TemplateRepository extends JpaRepository<TemplateModel, Long> { }

Service:
@Service public class TemplateAbService extends AbstractService<TemplateModel, Long, TemplateRepository> { }

How you can see, there are all empty and just take the Classes which extend the abstracts.

When you start this, you get an Service which has standard entpoints for get , getall, update, delete and post. It uses the database which you defined inside your application properties and creates everything needed. This was just an project I made because I'm to lazy to write everytime all classes a new when I just need a service which should handle data ;).

Hopefully this can help.

We should know more once https://github.com/quarkusio/quarkus/pull/9680 is merged.

@Cryptorious could you share a minimal app to reproduce the issue?

Sure, here is the helper project. When you try to compile it native, it gives you the error. I can give you also an template project which imports the jar from your local maven repo and uses it (this is the normal usecase). Just tell me what you need :)

helper.zip

Was this page helpful?
0 / 5 - 0 ratings