How to customize Jetty in Spring - Custom Error Handler
Tags: java, jetty, spring, web
Published: 2022-06-21 13:51:45

---
title: How to customize Jetty in Spring - Custom Error Handler
published: true
date: 2022-06-21 13:41:00 UTC
tags: java, jetty, spring, web
canonical_url: https://banach.net.pl/posts/2022/how-to-customize-jetty-in-spring-custom-error-handler/
---

Last week we found out that in some cases our application is showing redundant data on error - for example stacktrace. It happens when there was an error which was handled by the application container which was Jetty. For example, sending a header like `X-FORWARDED-PORT: some-not-numeric-value` causes`NumberFormatException` and shows a full stacktrace.

We’ve looked over the documentation and see that we could hide stacktrace in default error handler. Instead of that, we decided to replace it with a custom one. That allows us to put an additional logging logic and customize the output. And this is a short description of how to do it. ;-)

To implement the custom error handler we can extend `org.eclipse.jetty.server.handler.AbstractHandler` and override `handle` method. On the code snippet below there is an example handler. For sake of clarity it returns always simple text (content-type `text/plain`) with code, error message and URL used.

We can extend it by adding additional logic like different responses based on headers from the client, additional logging and so on. :-)

```java
package pl.net.banach.customizeJetty;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.nio.BufferOverflowException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.eclipse.jetty.http.MimeTypes;
import org.eclipse.jetty.io.ByteBufferOutputStream;
import org.eclipse.jetty.server.Dispatcher;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.StringUtil;

public class CustomJettyErrorHandler extends AbstractHandler {

    @Override
    public void handle(String target, Request baseRequest, 
                       HttpServletRequest request,
                       HttpServletResponse response) 
                       throws IOException, ServletException {


        try {
            // Get error message, sanitize it, just in case.
            String message = StringUtil.sanitizeXmlString(
                (String) request.getAttribute(Dispatcher.ERROR_MESSAGE)
            );
            
            // Get error code that will returned
            int code = response.getStatus();
            
            var charset = StandardCharsets.UTF_8;

            // Get writer used
            var buffer = baseRequest.getResponse().getHttpOutput().getBuffer();
            var out = new ByteBufferOutputStream(buffer);
            var writer = new PrintWriter(new OutputStreamWriter(out, charset));

            // Set content type, encoding and write response
            response.setContentType(MimeTypes.Type.TEXT_PLAIN.asString());
            response.setCharacterEncoding(charset.name());
            writer.print("HTTP ERROR ");
            writer.print(code);
            writer.print("\nMessage: ");
            writer.print(message);
            writer.print("\nURI: ");
            writer.print(request.getRequestURI());


            writer.flush();
        } catch (BufferOverflowException e) {
            baseRequest.getResponse().resetContent();
        }

        baseRequest.getHttpChannel().sendResponseAndComplete();
    }
}
```

When we have the error handler ready then it registering it is easy. To do this we will create a custom configuration which implements `WebServerFactoryCustomizer` with `@Configuration` annotation. In a `customize` method we will create a customizer and add a newly created error handler.

Seems simple, but be sure to put this configuration class in a package that is scanned by Spring `@ComponentScan` annotation!

```java
package pl.net.banach.customizeJetty;

import org.springframework.boot.web.embedded.jetty.JettyServerCustomizer;
import org.springframework.boot.web.embedded.jetty.JettyServletWebServerFactory;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.context.annotation.Configuration;

@Configuration
public class JettyConfiguration
            implements WebServerFactoryCustomizer<JettyServletWebServerFactory> {

    @Override
    public void customize(JettyServletWebServerFactory factory) {
        JettyServerCustomizer customizer = server -> {
            server.setErrorHandler(new CustomJettyErrorHandler());
        };
        factory.addServerCustomizers(customizer);
    }
}
```

And that’s all - from now all errors that go to Jetty (application server) will be handled by our custom handler. :-)