Showing posts with label Jetty. Show all posts
Showing posts with label Jetty. Show all posts

Wednesday, December 19, 2018

5. Replacing tomcat with jetty

By default when we run spring boot application, it runs a tomcat server. I have personally found tomcat too heavyweight and I prefer to use Jetty for writing a webservices backend.
2018-12-19 10:54:08.403  INFO 16029 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)

2018-12-19 10:54:08.424  INFO 16029 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]

2018-12-19 10:54:08.424  INFO 16029 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet Engine: Apache Tomcat/9.0.12

To replace tomcat with Jetty, we need to make a couple of changes.  The first step is to add Jetty dependencies in the pom.xml.
  
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
  </dependency>
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-jetty</artifactId>
  </dependency>
  <dependency>
   <groupId>org.eclipse.jetty</groupId>
   <artifactId>jetty-deploy</artifactId>
  </dependency>
  <dependency>
   <groupId>org.eclipse.jetty</groupId>
   <artifactId>jetty-rewrite</artifactId>
  </dependency>
  <dependency>
   <groupId>org.eclipse.jetty</groupId>
   <artifactId>jetty-util</artifactId>
  </dependency>

Since springframework boot bom automatically includes a dependency on tomcat in spring-boot-starter-web, we specifically need to exclude it. So we change
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
  </dependency>

to
  <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-web</artifactId>
   <exclusions>
    <exclusion>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-tomcat</artifactId>
    </exclusion>
   </exclusions>
  </dependency>

package in.springframework.blog.tutorials;
import org.apache.logging.log4j.LoggingException;
import org.eclipse.jetty.server.*;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.server.handler.RequestLogHandler;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.springframework.beans.factory.annotation.Value;
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.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.File;
/**
* Created by vinay on 2/3/16.
*/
@Configuration
public class HelloWorldJettyCustomizer {
@Bean
WebServerFactoryCustomizer<JettyServletWebServerFactory> containerCustomizer(
@Value("${server.port:8081}") final int port,
@Value("${jetty.acceptqueuesize:5000}") final String acceptQueueSize,
@Value("${jetty.threadPool.maxThreads:2000}") final String maxThreads,
@Value("${jetty.threadPool.minThreads:8}") final String minThreads,
@Value("${jetty.threadPool.idleTimeout:60000}") final String idleTimeout,
@Value("${logging.path:/tmp}") final String loggingPath)
throws Exception {
// This is boiler plate code to setup https on embedded Tomcat
// with Spring Boot:
return container -> {
container.addServerCustomizers((JettyServerCustomizer) server -> {
// Enable logs
File logFile = new File(loggingPath + "/product-yyyy_mm_dd.access.log");
String logFilename = logFile.getAbsolutePath();
if (!logFile.getParentFile().exists() && !logFile.getParentFile().mkdirs()) {
throw new LoggingException(String.format("%s could not be created.", logFile.getParentFile().getAbsolutePath()));
}
NCSARequestLog requestLog = new NCSARequestLog(logFilename);
requestLog.setAppend(true);
requestLog.setExtended(false);
requestLog.setLogTimeZone("UTC");
RequestLogHandler rlh = new RequestLogHandler();
rlh.setRequestLog(requestLog);
Handler[] handlers = server.getHandlers();
if (handlers == null || handlers.length == 0) {
server.setHandler(rlh);
}
else {
HandlerCollection handlerCollection = new HandlerCollection();
for (int i = 0; i < handlers.length; ++i) {
handlerCollection.addHandler(handlers[i]);
}
handlerCollection.addHandler(rlh);
server.setHandler(handlerCollection);
}
// Tweak the connection pool used by Jetty to handle incoming HTTP connections
final QueuedThreadPool threadPool = server.getBean(QueuedThreadPool.class);
threadPool.setMaxThreads(Integer.valueOf(maxThreads));
threadPool.setMinThreads(Integer.valueOf(minThreads));
threadPool.setIdleTimeout(Integer.valueOf(idleTimeout));
threadPool.setName("Jetty-Threadpool");
for (Connector connector : server.getConnectors()) {
if (connector instanceof ServerConnector) {
ServerConnector serverConnector = (ServerConnector)connector;
serverConnector.setPort(port);
serverConnector.setAcceptQueueSize(Integer.valueOf(acceptQueueSize));
serverConnector.setIdleTimeout(Integer.valueOf(idleTimeout));
HttpConnectionFactory connectionFactory = connector.getConnectionFactory(HttpConnectionFactory.class);
connectionFactory.getHttpConfiguration().setSendServerVersion(false);
}
}
});
};
}
}

The next step is to add a customizer for jetty which will allow us to set some parameters related to Jetty.
As we can see the customizer uses a set of variables that we can override in application.properties file to modify the behavior of the server.

2018-12-19 11:06:44.577  INFO 16219 --- [           main] o.e.jetty.server.AbstractConnector       : Started ServerConnector@42ebece0{HTTP/1.1,[http/1.1]}{0.0.0.0:8081}
2018-12-19 11:06:44.580  INFO 16219 --- [           main] o.s.b.web.embedded.jetty.JettyWebServer  : Jetty started on port(s) 8081 (http/1.1) with context path ''
Now the logs clearly show that in place of tomcat, it is running jetty.