Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NoSuchMethodError: org.springframework.boot.web.servlet.error.ErrorController.getErrorPath() in latest spring-cloud-starter-netflix-zuul

My application use spring-boot version 2.5.0 and spring-cloud-starter-netflix-zuul 2.2.8.RELEASE

With latest spring-boot version 2.5.0, getErrorPath() API is removed from ErrorController, but latest spring-cloud-starter-netflix-zuul 2.2.8.RELEASE still calls this API and causes this error

Has anyone got the same issue and resolved already

2021-06-23 19:36:31 o.a.c.c.C.[.[localhost] [ERROR] Exception Processing ErrorPage[errorCode=0, location=/error]
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoSuchMethodError: 'java.lang.String org.springframework.boot.web.servlet.error.ErrorController.getErrorPath()'
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1078)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:626)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:733)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:327)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:106)
    at org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:81)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:121)
    at org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:115)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:87)
    at org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:81)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
    at org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:105)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
    at org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:149)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
    at org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:63)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:103)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:89)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:110)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:80)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:336)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:211)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:183)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:358)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:271)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:119)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:103)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
    at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:710)
    at org.apache.catalina.core.ApplicationDispatcher.processRequest(ApplicationDispatcher.java:459)
    at org.apache.catalina.core.ApplicationDispatcher.doForward(ApplicationDispatcher.java:384)
    at org.apache.catalina.core.ApplicationDispatcher.forward(ApplicationDispatcher.java:312)
    at org.apache.catalina.core.StandardHostValve.custom(StandardHostValve.java:398)
    at org.apache.catalina.core.StandardHostValve.status(StandardHostValve.java:257)
    at org.apache.catalina.core.StandardHostValve.throwable(StandardHostValve.java:352)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:177)
    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:374)
    at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
    at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:893)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1707)
    at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
    at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
    at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.NoSuchMethodError: 'java.lang.String org.springframework.boot.web.servlet.error.ErrorController.getErrorPath()'
    at org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping.lookupHandler(ZuulHandlerMapping.java:87)
    at org.springframework.web.servlet.handler.AbstractUrlHandlerMapping.getHandlerInternal(AbstractUrlHandlerMapping.java:152)
    at org.springframework.web.servlet.handler.AbstractHandlerMapping.getHandler(AbstractHandlerMapping.java:498)
    at org.springframework.web.servlet.DispatcherServlet.getHandler(DispatcherServlet.java:1257)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039)
    ... 72 common frames omitted
like image 773
Khoa Phung Avatar asked Jun 23 '21 13:06

Khoa Phung


People also ask

What is wrong with the geterrorpath() API in errorcontroller?

Since the author is using the latest spring-boot 2.5.5 version, the getErrorPath () API was removed from ErrorController, but spring-cloud-starter-netflix-zuul 2.2.7.RELEASE still calls this API and causes this error. Method 1: Use a lower version of spring boot to be compatible with spring-cloud-starter-netflix-zuul

What happened to geterrorpath () in Spring Boot?

getErrorPath () in org.springframework.boot.web.servlet.error.ErrorController has been deprecated Compilation failure [ERROR] /.../.../CustomErrorController.java: warnings found and -Werror specified

Is it possible to build projects with-Werror flag in spring?

@scottfrederick After 1caca6e we can't build projects with "-Werror" flag, because we must implement getErrorPath (), but method is deprecated getErrorPath () in org.springframework.boot.web.servlet.error.ErrorController has been deprecated


3 Answers

The problem is the Spring version conflict between org.springframework.cloud:spring-cloud-starter-netflix-zuul:2.2.x and Spring Boot 2.5.x

This workaround / fix of aldobongio mentioned here worked for me: https://gist.github.com/aldobongio/6a22f49863c7a777612f7887bbb8fd1d

This code creates a new BeanPostProcessor that wraps a custom proxy around the original ZuulHandlerMapping bean. This proxy intercepts the invocation of the lookupHandler method preventing the execution of the code leading to "NoSuchMethodError".

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.cglib.proxy.Callback;
import org.springframework.cglib.proxy.CallbackFilter;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.cglib.proxy.NoOp;
import org.springframework.cloud.netflix.zuul.filters.RouteLocator;
import org.springframework.cloud.netflix.zuul.web.ZuulController;
import org.springframework.cloud.netflix.zuul.web.ZuulHandlerMapping;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Fix for Zuul configuration with Spring Boot 2.5.x + Zuul - "NoSuchMethodError: ErrorController.getErrorPath()":
 */
@Configuration
public class ZuulConfiguration {
  /**
   * The path returned by ErrorController.getErrorPath() with Spring Boot < 2.5
   * (and no longer available on Spring Boot >= 2.5).
   */
  private static final String ERROR_PATH = "/error";
  private static final String METHOD = "lookupHandler";

  /**
   * Constructs a new bean post-processor for Zuul.
   *
   * @param routeLocator    the route locator.
   * @param zuulController  the Zuul controller.
   * @param errorController the error controller.
   * @return the new bean post-processor.
   */
  @Bean
  public ZuulPostProcessor zuulPostProcessor(@Autowired RouteLocator routeLocator,
                                             @Autowired ZuulController zuulController,
                                             @Autowired(required = false) ErrorController errorController) {
    return new ZuulPostProcessor(routeLocator, zuulController, errorController);
  }

  private enum LookupHandlerCallbackFilter implements CallbackFilter {
    INSTANCE;

    @Override
    public int accept(Method method) {
      if (METHOD.equals(method.getName())) {
        return 0;
      }
      return 1;
    }
  }

  private enum LookupHandlerMethodInterceptor implements MethodInterceptor {
    INSTANCE;

    @Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
      if (ERROR_PATH.equals(args[0])) {
        // by entering this branch we avoid the ZuulHandlerMapping.lookupHandler method to trigger the 
        // NoSuchMethodError 
        return null;
      }
      return methodProxy.invokeSuper(target, args);
    }
  }

  private static final class ZuulPostProcessor implements BeanPostProcessor {

    private final RouteLocator routeLocator;
    private final ZuulController zuulController;
    private final boolean hasErrorController;

    ZuulPostProcessor(RouteLocator routeLocator, ZuulController zuulController, ErrorController errorController) {
      this.routeLocator = routeLocator;
      this.zuulController = zuulController;
      this.hasErrorController = (errorController != null);
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
      if (hasErrorController && (bean instanceof ZuulHandlerMapping)) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(ZuulHandlerMapping.class);
        enhancer.setCallbackFilter(LookupHandlerCallbackFilter.INSTANCE); // only for lookupHandler
        enhancer.setCallbacks(new Callback[] {LookupHandlerMethodInterceptor.INSTANCE, NoOp.INSTANCE});
        Constructor<?> ctor = ZuulHandlerMapping.class.getConstructors()[0];
        return enhancer.create(ctor.getParameterTypes(), new Object[] {routeLocator, zuulController});
      }
      return bean;
    }
  }
}
like image 59
electrobabe Avatar answered Oct 11 '22 12:10

electrobabe


I faced the same issue because spring cloud did not support zuul since 2020.
spring-cloud and netflix-zuul will work properly as following versions.

spring-boot-starter-parent: 2.1.3.RELEASE
java.version: 1.8
spring-cloud.version: Greenwich.RELEASE
spring-cloud-starter-netflix-zuul: 2.1.0.RELEASE
spring-cloud-starter-netflix-eureka-client: 2.1.0.RELEASE
jackson-dataformat-xml: 2.9.9
spring-cloud-starter-netflix-eureka-server: 2.1.0.RELEASE
like image 24
Myo Min Thein Avatar answered Oct 11 '22 12:10

Myo Min Thein


Why this issue occurs?

  • This issue occurs because zuul and ribbon were removed from the springboot main channel in 2.5.x.

How can we solve this issue?

  1. Replacing: Zuul (Api gateway) by Spring Cloud Gateway and Ribbon(Load balancer) by Spring Cloud Loadbalancer. You can go through this blog to get instructions: https://spring.io/blog/2018/12/12/spring-cloud-greenwich-rc1-available-now#spring-cloud-netflix-projects-entering-maintenance-mode

  2. Creating a Bean using @Configuration annotation:

  • Create a ZuulConfiguration.java class and use this code:
@Configuration
public class ZuulConfiguration {

    /** The path returned by ErrorContoller.getErrorPath() with Spring Boot < 2.5 (and no longer available on Spring Boot >= 2.5). */
    private static final String ERROR_PATH = "/error";

    /**
     * Constructs a new bean post-processor for Zuul.
     * 
     * @param routeLocator
     *            the route locator.
     * @param zuulController
     *            the Zuul controller.
     * @param errorController
     *            the error controller.
     * @return the new bean post-processor.
     */
    @Bean
    public ZuulPostProcessor zuulPostProcessor(@Autowired RouteLocator routeLocator, @Autowired ZuulController zuulController,
            @Autowired(required = false) ErrorController errorController) {
        return new ZuulPostProcessor(routeLocator, zuulController, errorController);
    }

    private static final class ZuulPostProcessor implements BeanPostProcessor {

        private final RouteLocator routeLocator;

        private final ZuulController zuulController;

        private final boolean hasErrorController;

        ZuulPostProcessor(RouteLocator routeLocator, ZuulController zuulController, ErrorController errorController) {
            this.routeLocator = routeLocator;
            this.zuulController = zuulController;
            this.hasErrorController = (errorController != null);
        }

        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            if (hasErrorController && (bean instanceof ZuulHandlerMapping)) {
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(ZuulHandlerMapping.class);
                enhancer.setCallbackFilter(LookupHandlerCallbackFilter.INSTANCE); // only for lookupHandler
                enhancer.setCallbacks(new Callback[] { LookupHandlerMethodInterceptor.INSTANCE, NoOp.INSTANCE });
                Constructor<?> ctor = ZuulHandlerMapping.class.getConstructors()[0];
                return enhancer.create(ctor.getParameterTypes(), new Object[] { routeLocator, zuulController });
            }
            return bean;
        }

    }

    private static enum LookupHandlerCallbackFilter implements CallbackFilter {

        INSTANCE;

        @Override
        public int accept(Method method) {
            if ("lookupHandler".equals(method.getName())) {
                return 0;
            }
            return 1;
        }

    }

    private static enum LookupHandlerMethodInterceptor implements MethodInterceptor {

        INSTANCE;

        @Override
        public Object intercept(Object target, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            if (ERROR_PATH.equals(args[0])) {

                /* by entering this branch we avoid the ZuulHandlerMapping.lookupHandler method to trigger the NoSuchMethodError */
                return null;
            }
            return methodProxy.invokeSuper(target, args);
        }

    }

}
  • solution link https://gist.github.com/aldobongio/6a22f49863c7a777612f7887bbb8fd1d
like image 2
Md. Shahariar Hossen Avatar answered Oct 11 '22 12:10

Md. Shahariar Hossen