Affects: 6.0.0

I have a simple controller that goes like

    @PostMapping
    public ResponseEntity<?> insert(
            @RequestBody @Valid CustomerApiDTO customerDTO) {

        var customerEntity = customerMapper.map(customerDTO);

        customerRepository.saveAndFlush(customerEntity);

        var uri = MvcUriComponentsBuilder.fromMethodCall(
                MvcUriComponentsBuilder.on(CustomerApiController.class)
                        .findByCustomerNumber(customerDTO.getCustomerNumber())  
        ).build();
        return ResponseEntity.created(uri.toUri()).build(); 
    }

With AOT generating the proxy for the controller this fails with:

java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1
        at de.eiswind.training.spring.rest.customer.responseentity.CustomerApiController$$SpringCGLIB$$1.setCallbacks(<generated>)
        at org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder$ControllerMethodInvocationInterceptor.initProxy(MvcUriComponentsBuilder.java:814)
        at org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.controller(MvcUriComponentsBuilder.java:383)

Please let me know if I can provide any further help with this.

Please forgive: I am not sure where to file this issue as it goes with AOT.

Comment From: sbrannen

Can you provide the full stack trace?

Also, have you tried this with Spring Framework 6.0.0 (GA)?

Comment From: eiswind

I updated everything to spring boot 3-SNAPSHOT which pulls in 6.0.0. Still get the same error. Full stacktrace is

 Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed: java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1] with root cause

java.lang.ArrayIndexOutOfBoundsException: Index 1 out of bounds for length 1
        at de.eiswind.training.spring.rest.customer.responseentity.CustomerApiController$$SpringCGLIB$$1.setCallbacks(<generated>)
        at org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder$ControllerMethodInvocationInterceptor.initProxy(MvcUriComponentsBuilder.java:814)
        at org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.controller(MvcUriComponentsBuilder.java:383)
        at org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.on(MvcUriComponentsBuilder.java:358)
        at de.eiswind.training.spring.rest.customer.responseentity.CustomerApiController.insert(CustomerApiController.java:105)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
        at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:699)
        at de.eiswind.training.spring.rest.customer.responseentity.CustomerApiController$$SpringCGLIB$$0.insert(<generated>)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:207)
        at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:152)
        at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:891)
        at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:804)
        at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
        at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1080)
        at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:973)
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1003)
        at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:906)
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:731)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:880)
        at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:814)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:223)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
        at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
        at org.springframework.web.filter.ServerHttpObservationFilter.doFilterInternal(ServerHttpObservationFilter.java:108)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:185)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:158)
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:197)
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
        at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:542)
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:119)
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:357)
        at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:400)
        at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
        at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:861)
        at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1739)
        at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
        at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
        at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
        at java.base/java.lang.Thread.run(Thread.java:833)

2022-11-16T15:15:15.722+01:00 ERROR 58498 --- [           main] o.s.t.w.reactive.server.ExchangeResult   : Request details for assertion failure:

> POST http://localhost:43657/api/customer
> accept-encoding: [gzip]
> user-agent: [ReactorNetty/1.1.0]
> host: [localhost:43657]
> accept: [*/*]
> WebTestClient-Request-Id: [1]
> Content-Type: [application/json]
> Content-Length: [64]

{"customerNumber":"C001","firstname":"Test","lastname":"Mensch"}

< 500 INTERNAL_SERVER_ERROR Internal Server Error
< Content-Type: [application/json]
< Transfer-Encoding: [chunked]
< Date: [Wed, 16 Nov 2022 14:15:15 GMT]
< Connection: [close]

{"timestamp":"2022-11-16T14:15:15.695+00:00","status":500,"error":"Internal Server Error","path":"/api/customer"}

Comment From: eiswind

Please give me a minute, I'll try to set up a reproducible example.

Comment From: eiswind

That was a ride. I found that the failing proxy starts to be generated as soon as I have starter-hateoas on the classpath. removing it ends up with a different error, as the MvcUriComponentsBuilder then tries to generate a class at runtime.

Please have a quick look at

https://github.com/eiswind/mvcuri-native

where both failures are reproducible.

I did not find a way to register a cglib proxy manually yet. Is there one?

Comment From: odrotbohm

So, the non-HATEOAS arrangement basically stumbles over the fact that the controller class and the return type of the dummy method invocation need to be proxied, and no CGLib proxy classes have been registered for these types. I guess that's due to Spring MVC not yet providing any facilities to do that.

In the HATEOAS-world we have an AOT extension for WebMvcContollerLinkBuilder to work properly (which predates MUCB, basically does the same as that but produces Link instances eventually). So, I assume that we now register e.g. a CGLib proxy with a certain proxy definition (declaring LastInvocationAware as sole interface, a HATEOAS specific interceptor). This is done through a ProxyFactory instance that, under the covers, uses the same APIs that MUCB uses. When the code path in MUCB is now hit, the HATEOAS-specific CGLib proxy is used, but in a different setup (registering MethodInvocationInfo as interface and another dedicated interceptor), which then ultimately fails as it seems to try to add a second interceptor to the array already sized to 1.

I guess we'll have to consult the AOT team to avoid such conflicting proxy declarations.

Comment From: snicoll

@eiswind thanks for the report. The problem is that those proxies should be generated at build-time and the code in the middle of that controller method cannot be introspected. I've tried to trigger the creation of the proxy at build-time but this lead to:

Exception in thread "main" java.lang.IllegalStateException: No current ServletRequestAttributes
    at org.springframework.util.Assert.state(Assert.java:76)
    at org.springframework.web.servlet.support.ServletUriComponentsBuilder.getCurrentRequest(ServletUriComponentsBuilder.java:179)
    at org.springframework.web.servlet.support.ServletUriComponentsBuilder.fromCurrentServletMapping(ServletUriComponentsBuilder.java:155)
    at org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.getBaseUrlToUse(MvcUriComponentsBuilder.java:559)
    at org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.fromMethodInternal(MvcUriComponentsBuilder.java:539)
    at org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.fromMethodCall(MvcUriComponentsBuilder.java:321)
    at com.example.mcvuri.MvcuriApplicationAotContribution$AotContribution.applyTo(MvcuriApplicationAotContribution.java:28)

At this stage, I don't know how this can be improved. We'll have to investigate.