Spring Hateoas Causing an Exception "Cannot subclass final class void"

2k Views Asked by At

I have the following controller logic (business garbage removed) that is generating an IllegalArgumentException with the message "Cannot subclass final class void".

package org.ghc.services.medicalsystem.controller

import com.google.common.collect.Maps
import org.springframework.http.HttpEntity
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestMethod
import org.springframework.web.bind.annotation.RestController

import static org.springframework.hateoas.mvc.ControllerLinkBuilder.linkTo
import static org.springframework.hateoas.mvc.ControllerLinkBuilder.methodOn


@RestController
class ConsumerIdentificationController extends RestfulController {


    @RequestMapping (value  = '/identification/consumer/{consumerId}',
                     method = RequestMethod.GET)
    HttpEntity<HateoasResponse> consumerIdentificationRetrieval (@PathVariable final String consumerId) {
        def bogusResponse = Maps.newHashMapWithExpectedSize (1)
        bogusResponse.put ("bogus", "entry")
        def realResponse = new HateoasResponse (bogusResponse)
        realResponse.add (linkTo(methodOn (this.class).consumerIdentificationRetrieval (consumerId)).withSelfRel ())
        new ResponseEntity (realResponse, HttpStatus.OK)
    }

}

class HateoasResponse extends ResourceSupport {

    private Map<String,String> content


    @JsonCreator
    HateoasResponse (final Map<String, String> content) {
        this.content = content
    }
}

The non-HATEOAS portion of the method is working fine. Of note is that the controller is written in Groovy.

The stack trace for the error is:

java.lang.IllegalArgumentException: Cannot subclass final class void
    at org.springframework.cglib.proxy.Enhancer.generateClass(Enhancer.java:446)
    at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
    at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
    at org.springframework.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
    at org.springframework.cglib.proxy.Enhancer.createClass(Enhancer.java:317)
    at org.springframework.hateoas.core.DummyInvocationUtils.getProxyWithInterceptor(DummyInvocationUtils.java:170)
    at org.springframework.hateoas.core.DummyInvocationUtils.access$100(DummyInvocationUtils.java:38)
    at org.springframework.hateoas.core.DummyInvocationUtils$InvocationRecordingMethodInterceptor.intercept(DummyInvocationUtils.java:100)
    at org.springframework.hateoas.core.DummyInvocationUtils$InvocationRecordingMethodInterceptor.invoke(DummyInvocationUtils.java:109)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
    at com.sun.proxy.$Proxy57.initialize(Unknown Source)
    at org.codehaus.groovy.vmplugin.v7.Selector$MethodSelector.getMetaClass(Selector.java:531)
    at org.codehaus.groovy.vmplugin.v7.Selector$MethodSelector.setCallSiteTarget(Selector.java:942)
    at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:211)
    at org.ghc.services.medicalsystem.controller.ConsumerIdentificationController.consumerIdentificationRetrieval(ConsumerIdentificationController.groovy:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:483)
    at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
    at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
    at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:110)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:781)
    at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:721)
    at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:943)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:877)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:966)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:857)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:618)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:842)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:725)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:291)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.codehaus.groovy.vmplugin.v7.IndyInterface.selectMethod(IndyInterface.java:215)
    at org.ghc.cloudscale.logging.filter.CloudScaleLoggingFilter.doFilter(CloudScaleLoggingFilter.groovy:66)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at com.netflix.hystrix.contrib.requestservlet.HystrixRequestContextServletFilter.doFilter(HystrixRequestContextServletFilter.java:53)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:239)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:219)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:106)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:506)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:142)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:79)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:88)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:537)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1081)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:658)
    at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:222)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1566)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1523)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.lang.Thread.run(Thread.java:745)

The error is being thrown from the linkTo() method. Beyound that it gets into dynamic proxies that I'm not familiar with. Any suggestions are welcome as always!

1

There are 1 best solutions below

0
On BEST ANSWER

You are running into a known bug with Spring's HATEAOS and Groovy. The only known workaround is to make your controller class statically compiled.