반응형

Spring Boot (스프링 부트) - undertow의 에러페이지 처리 문제

 

<< 환경 >>

Java           Open JDK 1.8

Spring Boot 1.5.2.RELEASE

 

NestedServletException이 Undertow의 에러처리를 요구하여 WAS container에 도달하기 전에 Spring에서 먼저 처리하는 것으로 보이는 현상 - 404에러 발생시 관련 로그없이 바로 에러페이지를 호출

 

관련 로그를 확인하기 위하여 에러처리 템플릿(error template) 404.html, 500.html 등을 모두 제거 후 테스트 진행

 

 

2020-12-19 03:19:04.721 ERROR 11852 --- [  XNIO-2 task-6] org.thymeleaf.TemplateEngine             : [THYMELEAF][XNIO-2 task-6] Exception processing template "error": Error resolving template "error", template might not exist or might not be accessible by any of the configured Template Resolvers
2020-12-19 03:19:04.722 ERROR 11852 --- [  XNIO-2 task-6] io.undertow.request                      : UT005023: Exception handling request to /error
 
java.lang.RuntimeException: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template "error", template might not exist or might not be accessible by any of the configured Template Resolvers
    at io.undertow.servlet.spec.HttpServletResponseImpl.doErrorDispatch(HttpServletResponseImpl.java:159)
    at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:295)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
    at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
    at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
    at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
    at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
    at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
    at io.undertow.server.Connectors.executeRootHandler(Connectors.java:211)
    at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:809)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template "error", template might not exist or might not be accessible by any of the configured Template Resolvers
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85)
    at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:81)
    at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
    at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.servlet.handlers.SessionRestoringHandler.handleRequest(SessionRestoringHandler.java:119)
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:274)
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchToPath(ServletInitialHandler.java:209)
    at io.undertow.servlet.spec.RequestDispatcherImpl.error(RequestDispatcherImpl.java:480)
    at io.undertow.servlet.spec.RequestDispatcherImpl.error(RequestDispatcherImpl.java:405)
    at io.undertow.servlet.spec.HttpServletResponseImpl.doErrorDispatch(HttpServletResponseImpl.java:157)
    ... 14 common frames omitted
Caused by: org.thymeleaf.exceptions.TemplateInputException: Error resolving template "error", template might not exist or might not be accessible by any of the configured Template Resolvers
    at org.thymeleaf.TemplateRepository.getTemplate(TemplateRepository.java:246)
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1104)
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1060)
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1011)
    at org.thymeleaf.spring4.view.ThymeleafView.renderFragment(ThymeleafView.java:335)
    at org.thymeleaf.spring4.view.ThymeleafView.render(ThymeleafView.java:190)
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1282)
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
    ... 30 common frames omitted
 
2020-12-19 03:19:04.723 ERROR 11852 --- [  XNIO-2 task-6] io.undertow.servlet.request              : UT015012: Failed to generate error page /error for original exception: null. Generating error page resulted in a 500.
 
java.lang.RuntimeException: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template "error", template might not exist or might not be accessible by any of the configured Template Resolvers
    at io.undertow.servlet.spec.HttpServletResponseImpl.doErrorDispatch(HttpServletResponseImpl.java:159)
    at io.undertow.servlet.handlers.ServletInitialHandler.handleFirstRequest(ServletInitialHandler.java:295)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$100(ServletInitialHandler.java:81)
    at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:138)
    at io.undertow.servlet.handlers.ServletInitialHandler$2.call(ServletInitialHandler.java:135)
    at io.undertow.servlet.core.ServletRequestContextThreadSetupAction$1.call(ServletRequestContextThreadSetupAction.java:48)
    at io.undertow.servlet.core.ContextClassLoaderSetupAction$1.call(ContextClassLoaderSetupAction.java:43)
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:272)
    at io.undertow.servlet.handlers.ServletInitialHandler.access$000(ServletInitialHandler.java:81)
    at io.undertow.servlet.handlers.ServletInitialHandler$1.handleRequest(ServletInitialHandler.java:104)
    at io.undertow.server.Connectors.executeRootHandler(Connectors.java:211)
    at io.undertow.server.HttpServerExchange$1.run(HttpServerExchange.java:809)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
    at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.thymeleaf.exceptions.TemplateInputException: Error resolving template "error", template might not exist or might not be accessible by any of the configured Template Resolvers
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
    at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
    at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:85)
    at io.undertow.servlet.handlers.FilterHandler.handleRequest(FilterHandler.java:81)
    at io.undertow.servlet.handlers.security.ServletSecurityRoleHandler.handleRequest(ServletSecurityRoleHandler.java:62)
    at io.undertow.servlet.handlers.ServletDispatchingHandler.handleRequest(ServletDispatchingHandler.java:36)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.server.handlers.PredicateHandler.handleRequest(PredicateHandler.java:43)
    at io.undertow.servlet.handlers.SessionRestoringHandler.handleRequest(SessionRestoringHandler.java:119)
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchRequest(ServletInitialHandler.java:274)
    at io.undertow.servlet.handlers.ServletInitialHandler.dispatchToPath(ServletInitialHandler.java:209)
    at io.undertow.servlet.spec.RequestDispatcherImpl.error(RequestDispatcherImpl.java:480)
    at io.undertow.servlet.spec.RequestDispatcherImpl.error(RequestDispatcherImpl.java:405)
    at io.undertow.servlet.spec.HttpServletResponseImpl.doErrorDispatch(HttpServletResponseImpl.java:157)
    ... 14 common frames omitted
Caused by: org.thymeleaf.exceptions.TemplateInputException: Error resolving template "error", template might not exist or might not be accessible by any of the configured Template Resolvers
    at org.thymeleaf.TemplateRepository.getTemplate(TemplateRepository.java:246)
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1104)
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1060)
    at org.thymeleaf.TemplateEngine.process(TemplateEngine.java:1011)
    at org.thymeleaf.spring4.view.ThymeleafView.renderFragment(ThymeleafView.java:335)
    at org.thymeleaf.spring4.view.ThymeleafView.render(ThymeleafView.java:190)
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1282)
    at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1037)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:980)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
    ... 30 common frames omitted
 

 

RuntimeException, NotFoundException, Internal Server Error 발생시 Undertow가 thymeleaf template을 찾는 것을 확인

 

에러 발생시 Undertow는 ServletInitialHandler에서 

1. 맵핑된 에러 탐색

2. 기본 에러페이지 호출

3. 지정된 에러페이지 호출

를 하고 있는 것으로 추정

 

이를 우회하기 위한 방법으로

 

1. application 실행 파일에 Bean객체 포함

1
2
3
4
5
6
7
8
9
10
    @Bean
    EmbeddedServletContainerCustomizer errorPageCustomizer() {
        return (container -> {
            container.addErrorPages(
                new ErrorPage(RuntimeException.class"/error"),
                new ErrorPage(HttpStatus.NOT_FOUND, "/error"),
                new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error")
            );
        });
    }
cs

 

 

또는

 

 

1. CustomServerProperties extends ServerProperties 파일 생성 후 ServletContextInitializer, EmbeddedServletContainerCustomizer를 구현하는 설정 파일에 @Bean객체로 추가

 

[ CustomTestServerProperties.java ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import java.util.HashSet;
import java.util.Set;
 
import org.springframework.boot.autoconfigure.web.ServerProperties;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.web.servlet.ErrorPage;
import org.springframework.http.HttpStatus;
 
/**
 * Created on 2020-12-18.
 */
public class CustomTestServerProperties extends ServerProperties {
    
    @Override
    public void customize(ConfigurableEmbeddedServletContainer container) {
        super.customize(container);
        Set<ErrorPage> errorPageSet = new HashSet<>();
        errorPageSet.add(new ErrorPage(RuntimeException.class"/error"));
        errorPageSet.add(new ErrorPage(HttpStatus.NOT_FOUND, "/error"));
//        errorPageSet.add(new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error"));
        
        container.setErrorPages(errorPageSet);
    }
 
}
cs

 

 

[ WebConfigurer.java ] - ServletContextInitializer, EmbeddedServletContainerCustomizer를 구현

1
2
3
4
    @Bean
    public CustomTestServerProperties customTestServerProperties() {
        return new CustomTestServerProperties();
    }
cs

 

 

2. CustomErrorController 생성 후 위에서 mapping된 ErrorController를 찾을 수 있도록 유도

 

[ CustomErrorController.java ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import javax.servlet.http.HttpServletRequest;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.web.ErrorController;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseStatus;
 
/**
 * Created on 2020-12-18.
 * 
 * @return error template
 */
@Controller
public class CustomErrorController implements ErrorController {
    
    private final Logger log = LoggerFactory.getLogger(CustomErrorController.class);
    
    private static final String ERROR_PATH = "/error";
 
    @Override
    public String getErrorPath() {
        return ERROR_PATH;
    }
    
    @ResponseStatus(HttpStatus.FOUND)    // status code : 302
    @RequestMapping(value = "/error", method = RequestMethod.GET, produces = "text/html")
    public String handleErrorPage(HttpServletRequest request, Model model) {
        log.info("---------------------------------------------------");
        log.info("Controller: " + this.getClass().getName().toString());
        log.info("---------------------------------------------------");
        return "error/error";
    }
 
}
cs

 

 

3. CustomErrorController에서 지정한 error page를 찾을 수 있도록 thymeleaf 설정 추가

 

[ application.yml ]

1
2
3
4
5
spring:
    thymeleaf:
        cache: false
        prefix: /
        suffix: .html
cs

 

 

 

참고

https://github.com/spring-projects/spring-boot/issues/2692

 

반응형