Forráskód Böngészése

Servlet 3.1 with Async I/O implementation (#3319)

* Restore servlet3 JSON and Plaintext

* Some changes

* Add async IO to plaintext servlet

* Add source encoding in pom.xml, tweak compilation parameters

* Change project name

* Make variables final

* Use the new way to start Resin

* Add Apache Tomcat 9.0.4 installation

* Download and compile OpenSSL 1.0.2 and Apache APR 1.6.3

* Add Tomcat Native compilation and installation

* Compile and configure Tomcat Native library

* Add custom minimal config for Tomcat

* Enable servlet3 project in Travis

* add in-progress changes

* Add Sync and Async versions of Plaintext and JSON

* Add benchmark configuration

* Add sync setup script

* Update Tomcat to 9.0.5

* Update README.md

* Move servlet3 to sub-folder in servlet directory

* Fix sub-folder link

* Fix setup script names

* Remove servlet3 from Travis build matrix

* Modify openssl.sh to call gpg with keyserver argument in order to receive the OpenSSL developer's singing keys
Radoslav Petrov 7 éve
szülő
commit
507872eadc

+ 2 - 0
frameworks/Java/servlet/README.md

@@ -2,6 +2,8 @@
 
 This is the Java Servlet portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
 
+There is a Servlet 3.1 based tests implementation in the [servlet3](./servlet3) sub-folder. It is using Tomcat 9 as Servlet containter.
+
 ### Plaintext and JSON
 
 * [Plaintext test source](src/main/java/hello/PlaintextServlet.java)

+ 38 - 0
frameworks/Java/servlet/benchmark_config.json

@@ -102,6 +102,44 @@
       "display_name": "servlet",
       "notes": "",
       "versus": "servlet-raw"
+    },
+    "async-tomcat": {
+      "setup_file": "servlet3-async",
+      "json_url": "/servlet3/json",
+      "plaintext_url": "/servlet3/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "None",
+      "framework": "None",
+      "language": "Java",
+      "orm": "Raw",
+      "platform": "Servlet",
+      "webserver": "Tomcat",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "servlet3",
+      "notes": "Servlet 3.1 Async I/O",
+      "versus": "servlet"
+    },
+    "sync-tomcat": {
+      "setup_file": "servlet3-sync",
+      "json_url": "/servlet3/json",
+      "plaintext_url": "/servlet3/plaintext",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification" : "Platform",
+      "database": "None",
+      "framework": "None",
+      "language": "Java",
+      "orm": "Raw",
+      "platform": "Servlet",
+      "webserver": "Tomcat",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "servlet3",
+      "notes": "",
+      "versus": "servlet"
     }
   }]
 }

+ 9 - 0
frameworks/Java/servlet/servlet3-async.sh

@@ -0,0 +1,9 @@
+#!/bin/bash
+
+fw_depends java tomcat maven
+
+cd servlet3
+mvn clean compile war:war
+rm -rf $CATALINA_HOME/webapps/*
+cp target/servlet3.war $CATALINA_HOME/webapps
+$CATALINA_HOME/bin/startup.sh

+ 9 - 0
frameworks/Java/servlet/servlet3-sync.sh

@@ -0,0 +1,9 @@
+#!/bin/bash
+
+fw_depends java tomcat maven
+
+cd servlet3
+mvn clean compile war:war -P sync
+rm -rf $CATALINA_HOME/webapps/*
+cp target/servlet3.war $CATALINA_HOME/webapps
+$CATALINA_HOME/bin/startup.sh

+ 34 - 0
frameworks/Java/servlet/servlet3/README.md

@@ -0,0 +1,34 @@
+# Servlet 3.1 API benchmarking test
+
+This is Framework permutation based on the following technology stack
+
+* Java
+* Tomcat 9
+* Servlet 3.1 with Async I/O
+* Jackson 2 for JSON processing
+
+Currently implemented test types are Plaintext and JSON. Their implementation comes in two flavors: PO (plain old) Servlets and Servlets 3.1 with Async I/O.
+
+### Plaintext
+
+* [Async](src/main/java/com/gitlab/zloster/tfb/servlet3/async/Plaintext.java)
+* [Sync](src/main/java/com/gitlab/zloster/tfb/servlet3/sync/Plaintext.java)
+
+### JSON
+
+* [Async](src/main/java/com/gitlab/zloster/tfb/servlet3/async/JSON.java)
+* [Sync](src/main/java/com/gitlab/zloster/tfb/servlet3/sync/JSON.java)
+
+## Test URLs
+
+### Default Maven profile
+
+The `async` profile is activated by default.
+
+* Plaintext - `http://localhost:8080/servlet3/plaintext`
+* JSON - `http://localhost:8080/servlet3/json`
+
+### `sync` Maven profile
+
+* Plaintext - `http://localhost:8080/servlet3/plaintext`
+* JSON - `http://localhost:8080/servlet3/json`

+ 97 - 0
frameworks/Java/servlet/servlet3/pom.xml

@@ -0,0 +1,97 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>com.gitlab.zloster.tfb.servlet3</groupId>
+	<artifactId>servlet3</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<packaging>war</packaging>
+	<name>servlet3</name>
+
+	<properties>
+		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+		<java.version>1.8</java.version>
+		<slf4j.version>1.7.10</slf4j.version>
+	</properties>
+
+	<profiles>
+		<profile>
+			<id>sync</id>
+			<properties>
+				<exludeSources>**/async/*.java</exludeSources>
+			</properties>
+		</profile>
+		<profile>
+			<id>async</id>
+			<activation>
+				<activeByDefault>true</activeByDefault>
+			</activation>
+			<properties>
+				<exludeSources>**/sync/*.java</exludeSources>
+			</properties>
+		</profile>
+	</profiles>
+
+	<build>
+		<finalName>servlet3</finalName>
+
+		<plugins>
+
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-compiler-plugin</artifactId>
+				<version>3.7.0</version>
+				<configuration>
+					<source>${java.version}</source>
+					<target>${java.version}</target>
+					<optimize>true</optimize>
+					<debug>false</debug>
+					<excludes>
+						<exclude>${exludeSources}</exclude>
+					</excludes>
+				</configuration>
+			</plugin>
+
+			<plugin>
+				<groupId>org.apache.maven.plugins</groupId>
+				<artifactId>maven-war-plugin</artifactId>
+				<version>3.1.0</version>
+				<configuration>
+					<failOnMissingWebXml>false</failOnMissingWebXml>
+				</configuration>
+			</plugin>
+
+		</plugins>
+
+	</build>
+
+	<dependencies>
+
+		<dependency>
+			<groupId>javax.servlet</groupId>
+			<artifactId>javax.servlet-api</artifactId>
+			<version>3.1.0</version>
+		</dependency>
+
+		<dependency>
+			<groupId>javax.servlet</groupId>
+			<artifactId>jstl</artifactId>
+			<version>1.2</version>
+			<scope>runtime</scope>
+		</dependency>
+
+		<dependency>
+			<groupId>com.fasterxml.jackson.core</groupId>
+			<artifactId>jackson-databind</artifactId>
+			<version>2.9.3</version>
+		</dependency>
+
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+			<version>${slf4j.version}</version>
+			<scope>compile</scope>
+		</dependency>
+
+	</dependencies>
+
+</project>

+ 12 - 0
frameworks/Java/servlet/servlet3/src/main/java/com/gitlab/zloster/tfb/servlet3/HelloMessage.java

@@ -0,0 +1,12 @@
+package com.gitlab.zloster.tfb.servlet3;
+
+public class HelloMessage {
+  private String message = "Hello, World!";
+
+  public HelloMessage() {
+  }
+
+  public String getMessage() {
+    return message;
+  }
+}

+ 7 - 0
frameworks/Java/servlet/servlet3/src/main/java/com/gitlab/zloster/tfb/servlet3/Helper.java

@@ -0,0 +1,7 @@
+package com.gitlab.zloster.tfb.servlet3;
+
+public class Helper {
+	public static final String MEDIATYPE_APPLICATION_JSON = "application/json";
+	public static final String MEDIATYPE_TEXT_PLAIN = "text/plain";
+	public static final byte[] CONTENT = "Hello, World!".getBytes();
+}

+ 19 - 0
frameworks/Java/servlet/servlet3/src/main/java/com/gitlab/zloster/tfb/servlet3/World.java

@@ -0,0 +1,19 @@
+package com.gitlab.zloster.tfb.servlet3;
+
+public class World {
+	private int id;
+	private int randomNumber;
+
+	public World(int id, int randomNumber) {
+		this.id = id;
+		this.randomNumber = randomNumber;
+	}
+
+	public int getId() {
+		return id;
+	}
+
+	public int getRandomNumber() {
+		return randomNumber;
+	}
+}

+ 65 - 0
frameworks/Java/servlet/servlet3/src/main/java/com/gitlab/zloster/tfb/servlet3/async/JSON.java

@@ -0,0 +1,65 @@
+package com.gitlab.zloster.tfb.servlet3.async;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.gitlab.zloster.tfb.servlet3.HelloMessage;
+import com.gitlab.zloster.tfb.servlet3.Helper;
+
+@SuppressWarnings("serial")
+@WebServlet(name = "JSON", urlPatterns = "/json", asyncSupported = true)
+public class JSON extends HttpServlet {
+	private static final Logger LOGGER = LoggerFactory.getLogger(JSON.class);
+	private static final ObjectMapper mapper = new ObjectMapper();
+
+	/**
+	 * The JSON serialization is performed on the request processing thread and
+	 * the response is sent back asynchronously.
+	 */
+	@Override
+	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
+			IOException {
+		final AsyncContext async = req.startAsync();
+		final ServletOutputStream out = resp.getOutputStream();
+		LOGGER.debug("JSON sync start");
+		resp.setContentType(Helper.MEDIATYPE_APPLICATION_JSON);
+		byte[] content = mapper.writeValueAsBytes(new HelloMessage());
+		out.setWriteListener(new WriteListener() {
+			@Override
+			public void onWritePossible() throws IOException {
+				byte[] buffer = new byte[32];
+				ByteArrayInputStream is = new ByteArrayInputStream(content);
+
+				while (out.isReady()) {
+					int len = is.read(buffer);
+
+					if (len < 0) {
+						async.complete();
+						LOGGER.debug("JSON async end");
+						return;
+					}
+					out.write(buffer, 0, len);
+				}
+			}
+
+			@Override
+			public void onError(Throwable t) {
+				LOGGER.error("JSON async error", t);
+				async.complete();
+			}
+		});
+	}
+}

+ 62 - 0
frameworks/Java/servlet/servlet3/src/main/java/com/gitlab/zloster/tfb/servlet3/async/Plaintext.java

@@ -0,0 +1,62 @@
+package com.gitlab.zloster.tfb.servlet3.async;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import javax.servlet.AsyncContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.WriteListener;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitlab.zloster.tfb.servlet3.Helper;
+
+/**
+ * Web Framework Benchmarks Test type 6: Plaintext
+ *
+ */
+@SuppressWarnings("serial")
+@WebServlet(name = "Plaintext", urlPatterns = "/plaintext", asyncSupported = true)
+public class Plaintext extends HttpServlet {
+	private static final Logger LOGGER = LoggerFactory.getLogger(Plaintext.class);
+
+	@Override
+	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
+			IOException {
+		final AsyncContext async = req.startAsync();
+		final ServletOutputStream out = resp.getOutputStream();
+		LOGGER.debug("plaintext async start");
+		resp.setContentType(Helper.MEDIATYPE_TEXT_PLAIN);
+
+		out.setWriteListener(new WriteListener() {
+			@Override
+			public void onWritePossible() throws IOException {
+				byte[] buffer = new byte[32];
+				ByteArrayInputStream is = new ByteArrayInputStream(Helper.CONTENT);
+
+				while (out.isReady()) {
+					int len = is.read(buffer);
+
+					if (len < 0) {
+						async.complete();
+						LOGGER.debug("plaintext async end");
+						return;
+					}
+					out.write(buffer, 0, len);
+				}
+			}
+
+			@Override
+			public void onError(Throwable t) {
+				LOGGER.error("plaintext async error", t);
+				async.complete();
+			}
+		});
+	}
+}

+ 36 - 0
frameworks/Java/servlet/servlet3/src/main/java/com/gitlab/zloster/tfb/servlet3/sync/JSON.java

@@ -0,0 +1,36 @@
+package com.gitlab.zloster.tfb.servlet3.sync;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.gitlab.zloster.tfb.servlet3.HelloMessage;
+import com.gitlab.zloster.tfb.servlet3.Helper;
+
+/**
+ * Web Framework Benchmarks Test type 1: JSON serialization
+ *
+ */
+@SuppressWarnings("serial")
+@WebServlet("/json")
+public class JSON extends HttpServlet {
+	private static final Logger LOGGER = LoggerFactory.getLogger(JSON.class);
+	private static final ObjectMapper mapper = new ObjectMapper();
+
+	@Override
+	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
+			IOException {
+		LOGGER.debug("JSON sync start");
+		resp.setContentType(Helper.MEDIATYPE_APPLICATION_JSON);
+		mapper.writeValue(resp.getOutputStream(), new HelloMessage());
+		LOGGER.debug("JSON sync end");
+	}
+}

+ 33 - 0
frameworks/Java/servlet/servlet3/src/main/java/com/gitlab/zloster/tfb/servlet3/sync/Plaintext.java

@@ -0,0 +1,33 @@
+package com.gitlab.zloster.tfb.servlet3.sync;
+
+import java.io.IOException;
+
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.gitlab.zloster.tfb.servlet3.Helper;
+
+/**
+ * Web Framework Benchmarks Test type 6: Plaintext
+ *
+ */
+@SuppressWarnings("serial")
+@WebServlet(name = "Plaintext", urlPatterns = "/plaintext")
+public class Plaintext extends HttpServlet {
+	private static final Logger LOGGER = LoggerFactory.getLogger(Plaintext.class);
+
+	@Override
+	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
+			IOException {
+		LOGGER.debug("plaintext sync start");
+		resp.setContentType(Helper.MEDIATYPE_TEXT_PLAIN);
+		resp.getOutputStream().write(Helper.CONTENT);
+		LOGGER.debug("plaintext sync end");
+	}
+}

+ 16 - 0
frameworks/Java/servlet/servlet3/src/main/resources/logback.xml

@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+        <file>servlet3.log</file>
+        <append>true</append>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{35} - %msg%n</pattern>
+        </encoder>
+    </appender>
+
+    <logger name="com.gitlab.zloster.tfb.servlet3" level="ERROR"/>
+
+    <root level="INFO">
+        <appender-ref ref="FILE" />
+    </root>
+</configuration>

+ 17 - 0
frameworks/Java/servlet/servlet3/src/main/webapp/jsp/error.jsp

@@ -0,0 +1,17 @@
+<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
+<%@ page session="false" %>
+<%
+    if(request.getAttribute("statusCode") != null)
+        response.setStatus((Integer)request.getAttribute("statusCode"));
+    else
+        response.setStatus(500);
+%>
+<html>
+<head>
+<title>error</title>
+</head>
+<body>
+<h1>error</h1>
+${message}
+</body>
+</html>

+ 42 - 0
toolset/setup/linux/systools/apache-apr.sh

@@ -0,0 +1,42 @@
+#!/bin/bash
+
+fw_depends gcc-6 openssl
+
+fw_installed apache-apr && return 0
+
+APR_VERSION=1.6.3
+TEMP_DIRECTORY="$(mktemp -d)"
+
+# Ask Apache about the preferred mirror for our connection using JSON response. Source: https://stackoverflow.com/a/39670213
+APR_MIRROR="$(wget -qO - http://www.apache.org/dyn/closer.lua?as_json=1 | grep -P '"preferred": "' | cut -d \" -f4)"
+APR_FILENAME=apr-${APR_VERSION}
+APR_URL=${APR_MIRROR}apr/${APR_FILENAME}.tar.gz
+APR_SIGNATURE=http://www.apache.org/dist/apr/${APR_FILENAME}.tar.gz.asc
+
+APACHE_KEYS=https://people.apache.org/keys/group/apr.asc
+
+# Download the files at temporal storage
+wget -P ${TEMP_DIRECTORY} ${APACHE_KEYS}
+wget -P ${TEMP_DIRECTORY} ${APR_SIGNATURE}
+wget -P ${TEMP_DIRECTORY} ${APR_URL} 
+
+# It's highly unlikely Apache to change the filename of the GPG keys file
+gpg --import ${TEMP_DIRECTORY}/apr.asc
+
+# Verify the downloaded file using the signature file
+gpg --verify ${TEMP_DIRECTORY}/${APR_FILENAME}.tar.gz.asc ${TEMP_DIRECTORY}/${APR_FILENAME}.tar.gz
+
+#TODO below
+# Compile the project
+pushd "${TEMP_DIRECTORY}"
+fw_untar "${TEMP_DIRECTORY}/${APR_FILENAME}.tar.gz"
+pushd "$APR_FILENAME"
+# We need the newer custom OpenSSL
+./configure --with-ssl=/usr/local/ssl
+make
+sudo make install
+sudo ldconfig
+popd
+popd
+
+echo "# Apache APR should be in /usr/local/ssl" > $IROOT/apache-apr.installed

+ 36 - 0
toolset/setup/linux/systools/openssl.sh

@@ -0,0 +1,36 @@
+#!/bin/bash
+
+fw_depends gcc-6
+
+fw_installed openssl && return 0
+
+OPENSSL_VERSION=1.0.2n
+TEMP_DIRECTORY="$(mktemp -d)"
+OPENSSL_FILENAME=openssl-${OPENSSL_VERSION}
+OPENSSL_URL=https://www.openssl.org/source/${OPENSSL_FILENAME}.tar.gz
+OPENSSL_SIGNATURE=https://www.openssl.org/source/${OPENSSL_FILENAME}.tar.gz.asc
+
+# Import the PGP keys of OpenSSL team. See the website for updates
+gpg --keyserver keys.gnupg.net --recv-keys 8657ABB260F056B1E5190839D9C4D26D0E604491 5B2545DAB21995F4088CEFAA36CEE4DEB00CFE33 C1F33DD8CE1D4CC613AF14DA9195C48241FBF7DD 7953AC1FBC3DC8B3B292393ED5E9E43F7DF9EE8C E5E52560DD91C556DDBDA5D02064C53641C25E5D D099684DC7C21E02E14A8AFEF23479455C51B27C
+
+# Download the files at temporal storage
+wget -P ${TEMP_DIRECTORY} ${OPENSSL_SIGNATURE}
+wget -P ${TEMP_DIRECTORY} ${OPENSSL_URL} 
+
+# Verify the downloaded file using the signature file
+gpg --verify ${TEMP_DIRECTORY}/${OPENSSL_FILENAME}.tar.gz.asc ${TEMP_DIRECTORY}/${OPENSSL_FILENAME}.tar.gz
+
+# Compile the project
+pushd "${TEMP_DIRECTORY}"
+fw_untar "${TEMP_DIRECTORY}/${OPENSSL_FILENAME}.tar.gz"
+pushd "$OPENSSL_FILENAME"
+./config --shared
+make
+sudo make install
+sudo ln -sf /usr/local/ssl/bin/openssl `which openssl`
+openssl version -v
+sudo ldconfig
+popd
+popd
+
+echo "# OpenSSL should be in /usr/local/ssl" > $IROOT/openssl.installed

+ 74 - 0
toolset/setup/linux/webservers/tomcat/server.xml

@@ -0,0 +1,74 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  Licensed to the Apache Software Foundation (ASF) under one or more
+  contributor license agreements.  See the NOTICE file distributed with
+  this work for additional information regarding copyright ownership.
+  The ASF licenses this file to You under the Apache License, Version 2.0
+  (the "License"); you may not use this file except in compliance with
+  the License.  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<Server port="8005" shutdown="SHUTDOWN">
+  <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
+
+  <!--APR library loader. Documentation at /docs/apr.html -->
+  <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
+
+  <!-- Prevent memory leaks due to use of particular java/javax APIs-->
+  <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
+  <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
+  <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
+
+  <!-- Global JNDI resources
+       Documentation at /docs/jndi-resources-howto.html
+  -->
+  <GlobalNamingResources>
+    <!-- Editable user database that can also be used by
+         UserDatabaseRealm to authenticate users
+    -->
+    <Resource name="UserDatabase" auth="Container"
+              type="org.apache.catalina.UserDatabase"
+              description="User database that can be updated and saved"
+              factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
+              pathname="conf/tomcat-users.xml" />
+  </GlobalNamingResources>
+
+  <Service name="Catalina">
+    <!--The connectors can use a shared executor, you can define one or more named thread pools
+    <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
+        maxThreads="250" minSpareThreads="4"/>-->
+
+    <!-- A "Connector" using the shared thread pool
+    <Connector executor="tomcatThreadPool"
+               port="8080" protocol="HTTP/1.1"
+               connectionTimeout="20000"
+               redirectPort="8443"
+               server="Tomcat"/>
+    -->
+    <!-- Connector with NIO2 without too much tweaks -->
+    <Connector port="8080" protocol="org.apache.coyote.http11.Http11Nio2Protocol"
+               connectionTimeout="30000"
+               keepAliveTimeout="30000"
+               URIEncoding="UTF-8"
+               redirectPort="8443"
+               server="Tomcat"/>
+
+    <Engine name="Catalina" defaultHost="localhost">
+      <Realm className="org.apache.catalina.realm.LockOutRealm">
+        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
+               resourceName="UserDatabase"/>
+      </Realm>
+
+      <Host name="localhost"  appBase="webapps"
+            unpackWARs="true" autoDeploy="true">
+      </Host>
+    </Engine>
+  </Service>
+</Server>

+ 56 - 0
toolset/setup/linux/webservers/tomcat/tomcat.sh

@@ -0,0 +1,56 @@
+#!/bin/bash
+
+fw_depends java openssl apache-apr
+
+fw_installed tomcat && return 0
+
+TOMCAT_VERSION=9.0.5
+TEMP_DIRECTORY="$(mktemp -d)"
+
+# Ask Apache about the preferred mirror for our connection using JSON response. Source: https://stackoverflow.com/a/39670213
+APACHE_MIRROR="$(wget -qO - http://www.apache.org/dyn/closer.lua?as_json=1 | grep -P '"preferred": "' | cut -d \" -f4)"
+TOMCAT_FILENAME=apache-tomcat-${TOMCAT_VERSION}
+TOMCAT_URL=${APACHE_MIRROR}tomcat/tomcat-9/v${TOMCAT_VERSION}/bin/${TOMCAT_FILENAME}.tar.gz
+
+TOMCAT_SIGNATURE=https://www.apache.org/dist/tomcat/tomcat-9/v${TOMCAT_VERSION}/bin/apache-tomcat-${TOMCAT_VERSION}.tar.gz.asc
+
+APACHE_KEYS=https://www.apache.org/dist/tomcat/tomcat-9/KEYS
+
+# Download the files at temporal storage
+wget -P ${TEMP_DIRECTORY} ${APACHE_KEYS}
+wget -P ${TEMP_DIRECTORY} ${TOMCAT_SIGNATURE}
+wget -P ${TEMP_DIRECTORY} ${TOMCAT_URL} 
+
+# It's highly unlikely Apache to change the filename of the GPG keys file
+gpg --import ${TEMP_DIRECTORY}/KEYS
+
+# Verify the downloaded file using the signature file
+gpg --verify ${TEMP_DIRECTORY}/${TOMCAT_FILENAME}.tar.gz.asc ${TEMP_DIRECTORY}/${TOMCAT_FILENAME}.tar.gz
+
+tar -xzf ${TEMP_DIRECTORY}/${TOMCAT_FILENAME}.tar.gz -C $IROOT
+
+# Build Tomcat Native adapter - Apache APR
+pushd "${IROOT}/${TOMCAT_FILENAME}/bin"
+mkdir -p tomcat-native
+tar -xzf tomcat-native.tar.gz -C tomcat-native --strip-components=1
+pushd "tomcat-native/native"
+./configure --with-apr=/usr/local/apr --with-ssl=/usr/local/ssl
+make
+sudo make install
+popd
+popd
+
+# Configure default server.xml
+pushd "${IROOT}/${TOMCAT_FILENAME}/conf/"
+mv server.xml server.xml.orig
+cat $FWROOT/toolset/setup/linux/webservers/tomcat/server.xml > server.xml
+popd
+
+# Configure Tomcat to use compiled native adapter
+echo -e "LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/apr/lib:/usr/local/ssl/lib\n
+export LD_LIBRARY_PATH" > ${IROOT}/${TOMCAT_FILENAME}/bin/setenv.sh
+
+echo "export CATALINA_HOME=$IROOT/$TOMCAT_FILENAME" > $IROOT/tomcat.installed
+echo -e "export PATH=\$IROOT/$TOMCAT_FILENAME/bin:\$PATH" >> $IROOT/tomcat.installed
+
+source $IROOT/tomcat.installed