78

I have Spring Boot application version 1.5.x, which uses org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory, I'm trying to migrate it to Spring Boot 2, but the app does not compile, although a have a dependency to org.springframework.boot:spring-boot-starter-tomcat. The compiler issues the error below:

error: package org.springframework.boot.context.embedded.tomcat
Brian Clozel
  • 56,583
  • 15
  • 167
  • 176
Anton Krosnev
  • 3,964
  • 1
  • 21
  • 37
  • Possible duplicate of [Unable to start ServletWebServerApplicationContext due to missing ServletWebServerFactory bean](https://stackoverflow.com/questions/46463908/unable-to-start-servletwebserverapplicationcontext-due-to-missing-servletwebserv) – Alex R Oct 10 '18 at 21:18

3 Answers3

84

In Spring boot 2.0.0.RELEASE you can replace with following code::

@Bean
public ServletWebServerFactory servletContainer() {
    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
        @Override
        protected void postProcessContext(Context context) {
            SecurityConstraint securityConstraint = new SecurityConstraint();
            securityConstraint.setUserConstraint("CONFIDENTIAL");
            SecurityCollection collection = new SecurityCollection();
            collection.addPattern("/*");
            securityConstraint.addCollection(collection);
            context.addConstraint(securityConstraint);
        }
    };
    tomcat.addAdditionalTomcatConnectors(redirectConnector());
    return tomcat;
}

private Connector redirectConnector() {
    Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
    connector.setScheme("http");
    connector.setPort(8080);
    connector.setSecure(false);
    connector.setRedirectPort(8443);
    return connector;
}
kryger
  • 12,906
  • 8
  • 44
  • 65
Rajim
  • 949
  • 6
  • 4
  • 4
    This should be the accepted answer. Exactly what I was looking for. The only thing I did differently is this line: `Connector connector = new Connector(TomcatServletWebServerFactory.DEFAULT_PROTOCOL);` Thanks for the answer. – leventunver Mar 12 '18 at 12:51
  • 8
    I am getting `IllegalStateException: No ServletContext set` if I do that - any idea what I'm missing? – Stefan Falk May 06 '18 at 17:30
  • I did as you mentioned but when I am hitting my application on http, it is still giving me error : Bad Request This combination of host and port requires TLS. Ideally this error comes when there is no connector. Could you please let me know if I need to do anything else. I am hitting the URL : http://localhost:8443/ – Anurag Oct 16 '19 at 05:19
  • 1
    Hi @displayname, for the `No ServletContext set`, i did a simple demo to understand and shared my finding [here](https://stackoverflow.com/questions/48971937/ugrade-spring-boot-2-0-0-rc2-exception-no-servletcontext-set/61140479#61140479). If you are interested :) – jumping_monkey Apr 10 '20 at 12:58
  • Hi @Anurag, you need to hit http://localhost:8080, or the http port you have set in connector.setPort(your_http_port). If you are hitting, https://localhost:8443, make sure you have at least the self-signed certificate in place, and the required properties in application.properties to tell Tomcat where the certificate is, what the password is and so on. – jumping_monkey Apr 11 '20 at 01:16
57

The class has been removed and replaced by org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory For more info check: Spring-Boot-2.0-Migration-Guide, which says:

In order to support reactive use cases, the embedded containers package structure has been refactored quite extensively. EmbeddedServletContainer has been renamed to WebServer and the org.springframework.boot.context.embedded package has been relocated to org.springframework.boot.web.server. Correspondingly, EmbeddedServletContainerCustomizer has been renamed to WebServerFactoryCustomizer.

For example, if you were customizing the embedded Tomcat container using the TomcatEmbeddedServletContainerFactory callback interface, you should now use TomcatServletWebServerFactory and if you were using an EmbeddedServletContainerCustomizer bean, you should now use a WebServerFactoryCustomizer bean.

I had the problem that I needed to sent bigger request, then the default size allowed:

@Bean
    public TomcatServletWebServerFactory containerFactory() {
        return new TomcatServletWebServerFactory() {
            protected void customizeConnector(Connector connector) {
                int maxSize = 50000000;
                super.customizeConnector(connector);
                connector.setMaxPostSize(maxSize);
                connector.setMaxSavePostSize(maxSize);
                if (connector.getProtocolHandler() instanceof AbstractHttp11Protocol) {

                    ((AbstractHttp11Protocol <?>) connector.getProtocolHandler()).setMaxSwallowSize(maxSize);
                    logger.info("Set MaxSwallowSize "+ maxSize);
                }
            }
        };

    }
Anton Krosnev
  • 3,964
  • 1
  • 21
  • 37
5

Great Thx! I came from this article: https://blog.swdev.ed.ac.uk/2015/06/24/adding-embedded-tomcat-ajp-support-to-a-spring-boot-application/

using spring boot 2.1.3:

@Configuration
@Data
public class TomcatConfiguration {

@Value("${tomcat.ajp.port}")
int ajpPort;

@Value("${tomcat.ajp.remoteauthentication}")
String remoteAuthentication;

@Value("${tomcat.ajp.enabled}")
boolean tomcatAjpEnabled;

@Bean
public TomcatServletWebServerFactory servletContainer() {

    TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();
    if (tomcatAjpEnabled)
    {
        Connector ajpConnector = new Connector("AJP/1.3");
        ajpConnector.setPort(ajpPort);
        ajpConnector.setSecure(false);
        ajpConnector.setAllowTrace(false);
        ajpConnector.setScheme("https");
        tomcat.addAdditionalTomcatConnectors(ajpConnector);
    }

    return tomcat;
  }

}
womd
  • 3,077
  • 26
  • 20
  • 1
    In Spring Boot 2.3.2 I'm getting: `java.lang.IllegalArgumentException: The AJP Connector is configured with secretRequired="true" but the secret attribute is either null or "". This combination is not valid.` – Dima Korobskiy Jan 29 '21 at 17:27