Skip to content

Commit 22bb56c

Browse files
rajat-gargrajatgarg
andauthored
[BAEL-7412] Add code for running multiple tomcat instances (#18899)
* [BAEL-7412] Add code for running multiple tomcat instances * [BAEL-7412] Remove Symbol * [BAEL-7412] Add new module * [BAEL-7412] Add unit tests * [BAEL-7412] Refactor and fix naming * [BAEL-7412] Fix public keyword --------- Co-authored-by: rajatgarg <rajatgarg@adobe.com>
1 parent 58f790b commit 22bb56c

File tree

11 files changed

+427
-0
lines changed

11 files changed

+427
-0
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<modelVersion>4.0.0</modelVersion>
6+
<parent>
7+
<groupId>com.baeldung</groupId>
8+
<artifactId>server-modules</artifactId>
9+
<version>1.0.0-SNAPSHOT</version>
10+
</parent>
11+
12+
<artifactId>apache-tomcat-2</artifactId>
13+
14+
<dependencies>
15+
<dependency>
16+
<groupId>org.apache.tomcat</groupId>
17+
<artifactId>tomcat-catalina</artifactId>
18+
<version>${tomcat.version}</version>
19+
</dependency>
20+
21+
<dependency>
22+
<groupId>org.apache.tomcat</groupId>
23+
<artifactId>tomcat-juli</artifactId>
24+
<version>${tomcat.version}</version>
25+
</dependency>
26+
27+
<dependency>
28+
<groupId>jakarta.servlet</groupId>
29+
<artifactId>jakarta.servlet-api</artifactId>
30+
<version>6.0.0</version>
31+
</dependency>
32+
33+
<!-- Test Dependencies -->
34+
<dependency>
35+
<groupId>org.junit.jupiter</groupId>
36+
<artifactId>junit-jupiter</artifactId>
37+
<version>5.10.0</version>
38+
<scope>test</scope>
39+
</dependency>
40+
</dependencies>
41+
42+
<build>
43+
<plugins>
44+
<plugin>
45+
<groupId>org.apache.maven.plugins</groupId>
46+
<artifactId>maven-compiler-plugin</artifactId>
47+
<version>3.11.0</version>
48+
<configuration>
49+
<source>11</source>
50+
<target>11</target>
51+
</configuration>
52+
</plugin>
53+
<plugin>
54+
<groupId>org.apache.maven.plugins</groupId>
55+
<artifactId>maven-surefire-plugin</artifactId>
56+
<version>3.0.0</version>
57+
</plugin>
58+
</plugins>
59+
</build>
60+
61+
<properties>
62+
<tomcat.version>10.1.24</tomcat.version>
63+
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
64+
</properties>
65+
66+
</project>
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.baeldung.tomcat;
2+
3+
import org.apache.catalina.startup.Catalina;
4+
import java.io.InputStream;
5+
import java.net.URL;
6+
import java.nio.file.*;
7+
8+
public class AppServerXML {
9+
10+
public static void main(String[] args) throws Exception {
11+
AppServerXML app = new AppServerXML();
12+
Catalina catalina = app.startServer();
13+
catalina.getServer().await();
14+
}
15+
16+
public Catalina startServer() throws Exception {
17+
URL staticUrl = getClass().getClassLoader().getResource("static");
18+
if (staticUrl == null) {
19+
throw new IllegalStateException("Static directory not found in classpath");
20+
}
21+
Path staticDir = Paths.get(staticUrl.toURI());
22+
23+
Path baseDir = Paths.get("target/tomcat-base").toAbsolutePath();
24+
Files.createDirectories(baseDir);
25+
26+
String config;
27+
try (InputStream serverXmlStream = getClass().getClassLoader().getResourceAsStream("server.xml")) {
28+
if (serverXmlStream == null) {
29+
throw new IllegalStateException("server.xml not found in classpath");
30+
}
31+
config = new String(serverXmlStream.readAllBytes())
32+
.replace("STATIC_DIR_PLACEHOLDER", staticDir.toString());
33+
}
34+
35+
Path configFile = baseDir.resolve("server.xml");
36+
Files.writeString(configFile, config);
37+
38+
System.setProperty("catalina.base", baseDir.toString());
39+
System.setProperty("catalina.home", baseDir.toString());
40+
41+
Catalina catalina = new Catalina();
42+
catalina.load(new String[]{"-config", configFile.toString()});
43+
catalina.start();
44+
45+
System.out.println("\nTomcat started with multiple connectors!");
46+
System.out.println("http://localhost:8081");
47+
System.out.println("http://localhost:7081");
48+
49+
return catalina;
50+
}
51+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.baeldung.tomcat;
2+
3+
import jakarta.servlet.http.HttpServlet;
4+
import jakarta.servlet.http.HttpServletRequest;
5+
import jakarta.servlet.http.HttpServletResponse;
6+
import org.apache.catalina.connector.Connector;
7+
import java.io.File;
8+
import java.io.IOException;
9+
10+
import org.apache.catalina.Context;
11+
import org.apache.catalina.startup.Tomcat;
12+
13+
public class DualPort {
14+
15+
public static void main(String[] args) throws Exception {
16+
DualPort dualPort = new DualPort();
17+
Tomcat tomcat = dualPort.startServer();
18+
tomcat.getServer().await();
19+
}
20+
21+
public Tomcat startServer() throws Exception {
22+
Tomcat tomcat = new Tomcat();
23+
tomcat.setBaseDir(new File("tomcat-temp").getAbsolutePath());
24+
25+
tomcat.setPort(7080);
26+
tomcat.getConnector();
27+
28+
Connector secondConnector = new Connector();
29+
secondConnector.setPort(8080);
30+
tomcat.getService().addConnector(secondConnector);
31+
32+
Context ctx = tomcat.addContext("", new File(".").getAbsolutePath());
33+
Tomcat.addServlet(ctx, "portServlet", new HttpServlet() {
34+
@Override
35+
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
36+
int port = req.getLocalPort();
37+
resp.setContentType("text/plain");
38+
resp.getWriter().write("Port: " + port + "\n");
39+
}
40+
});
41+
ctx.addServletMappingDecoded("/", "portServlet");
42+
43+
tomcat.start();
44+
System.out.println("Tomcat running on ports 8080 and 7080");
45+
46+
return tomcat;
47+
}
48+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.baeldung.tomcat;
2+
3+
import jakarta.servlet.http.HttpServlet;
4+
import jakarta.servlet.http.HttpServletRequest;
5+
import jakarta.servlet.http.HttpServletResponse;
6+
import java.io.IOException;
7+
8+
public class PortServlet extends HttpServlet {
9+
@Override
10+
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
11+
int port = req.getLocalPort();
12+
resp.setContentType("text/plain");
13+
resp.getWriter().write("port number: " + port);
14+
}
15+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Server port="8005" shutdown="SHUTDOWN">
3+
<Service name="Catalina">
4+
<Connector port="8081" protocol="HTTP/1.1" />
5+
<Connector port="7081" protocol="HTTP/1.1" />
6+
7+
<Engine name="Catalina" defaultHost="localhost">
8+
<Host name="localhost" appBase="/tmp/tomcat-dummy" unpackWARs="false" autoDeploy="false">
9+
<Context path="" docBase="STATIC_DIR_PLACEHOLDER" reloadable="false" />
10+
</Host>
11+
</Engine>
12+
</Service>
13+
</Server>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<web-app xmlns="https://jakarta.ee/xml/ns/jakartaee"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee
5+
https://jakarta.ee/xml/ns/jakartaee/web-app_6_0.xsd"
6+
version="6.0">
7+
8+
<display-name>Static HTML Application</display-name>
9+
10+
<!-- Default servlet for serving static content -->
11+
<servlet>
12+
<servlet-name>default</servlet-name>
13+
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
14+
<init-param>
15+
<param-name>listings</param-name>
16+
<param-value>false</param-value>
17+
</init-param>
18+
<load-on-startup>1</load-on-startup>
19+
</servlet>
20+
21+
<servlet-mapping>
22+
<servlet-name>default</servlet-name>
23+
<url-pattern>/</url-pattern>
24+
</servlet-mapping>
25+
26+
<!-- Welcome files -->
27+
<welcome-file-list>
28+
<welcome-file>index.html</welcome-file>
29+
</welcome-file-list>
30+
</web-app>
31+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head><title>Dual Port Test</title></head>
4+
<body>
5+
<h1>Tomcat is running!</h1>
6+
<p>Port: <script>document.write(window.location.port)</script></p>
7+
</body>
8+
</html>
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
package com.baeldung.tomcat;
2+
3+
import org.apache.catalina.startup.Catalina;
4+
import org.junit.jupiter.api.*;
5+
6+
import java.nio.file.Files;
7+
import java.nio.file.Path;
8+
import java.nio.file.Paths;
9+
10+
import static com.baeldung.tomcat.HttpConnection.getContent;
11+
import static com.baeldung.tomcat.HttpConnection.getResponseCode;
12+
import static org.junit.jupiter.api.Assertions.*;
13+
14+
public class AppServerXMLIntegrationTest {
15+
16+
private static AppServerXML app;
17+
private static Catalina catalina;
18+
private static final int HTTP_PORT_1 = 8081;
19+
private static final int HTTP_PORT_2 = 7081;
20+
21+
@BeforeAll
22+
static void setUp() throws Exception {
23+
app = new AppServerXML();
24+
catalina = app.startServer();
25+
Thread.sleep(2000);
26+
}
27+
28+
@AfterAll
29+
static void shutDown() throws Exception {
30+
if (catalina != null && catalina.getServer() != null) {
31+
catalina.stop();
32+
Thread.sleep(1000);
33+
}
34+
}
35+
36+
@Test
37+
void givenMultipleConnectors_whenServerStarts_thenContainsMultiplePorts() {
38+
assertNotNull(catalina.getServer(), "Server should be initialized");
39+
40+
Path configFile = Paths.get("target/tomcat-base/server.xml");
41+
assertTrue(Files.exists(configFile), "Generated server.xml should exist");
42+
43+
assertDoesNotThrow(() -> {
44+
String config = Files.readString(configFile);
45+
assertTrue(config.contains("port=\"8081\""), "Config should have port 8081");
46+
assertTrue(config.contains("port=\"7081\""), "Config should have port 7081");
47+
assertFalse(config.contains("STATIC_DIR_PLACEHOLDER"), "Placeholder should be replaced");
48+
});
49+
}
50+
51+
@Test
52+
void givenMultipleConnectors_whenResponds_thenReturns200() {
53+
assertDoesNotThrow(() -> {
54+
int response1 = getResponseCode(HTTP_PORT_1);
55+
int response2 = getResponseCode(HTTP_PORT_2);
56+
57+
assertEquals(200, response1, "Port 8081 should respond with 200 OK");
58+
assertEquals(200, response2, "Port 7081 should respond with 200 OK");
59+
});
60+
}
61+
62+
@Test
63+
void givenMultipleConnectors_whenResponds_thenReturnsIdenticalContent() {
64+
assertDoesNotThrow(() -> {
65+
String content1 = getContent(HTTP_PORT_1);
66+
String content2 = getContent(HTTP_PORT_2);
67+
68+
assertNotNull(content1, "Content from port 8081 should not be null");
69+
assertNotNull(content2, "Content from port 7081 should not be null");
70+
71+
assertTrue(content1.contains("Tomcat is running"), "Content should contain expected text");
72+
assertEquals(content1, content2, "Both ports should serve identical content");
73+
});
74+
}
75+
}
76+
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package com.baeldung.tomcat;
2+
3+
import org.apache.catalina.connector.Connector;
4+
import org.apache.catalina.startup.Tomcat;
5+
import org.junit.jupiter.api.*;
6+
7+
import static com.baeldung.tomcat.HttpConnection.getContent;
8+
import static com.baeldung.tomcat.HttpConnection.getResponseCode;
9+
import static org.junit.jupiter.api.Assertions.*;
10+
11+
public class DualPortIntegrationTest {
12+
13+
private static DualPort app;
14+
private static Tomcat tomcat;
15+
private static final int PORT_1 = 8080;
16+
private static final int PORT_2 = 7080;
17+
18+
@BeforeAll
19+
static void setUp() throws Exception {
20+
app = new DualPort();
21+
tomcat = app.startServer();
22+
Thread.sleep(2000);
23+
}
24+
25+
@AfterAll
26+
static void tearDown() throws Exception {
27+
if (tomcat != null && tomcat.getServer() != null) {
28+
tomcat.stop();
29+
tomcat.destroy();
30+
Thread.sleep(1000);
31+
}
32+
}
33+
34+
@Test
35+
void givenMultipleConnectors_whenServerStarts_thenContainsMultiplePorts() {
36+
assertNotNull(tomcat, "Tomcat instance should not be null");
37+
assertNotNull(tomcat.getServer(), "Server should be initialized");
38+
39+
Connector[] connectors = tomcat.getService().findConnectors();
40+
assertEquals(2, connectors.length, "Should have exactly 2 connectors");
41+
42+
int[] ports = new int[]{connectors[0].getPort(), connectors[1].getPort()};
43+
assertTrue(contains(ports, 8080), "Should have connector on port 8080");
44+
assertTrue(contains(ports, 7080), "Should have connector on port 7080");
45+
}
46+
47+
@Test
48+
void givenMultipleConnectors_whenResponds_thenReturns200() {
49+
assertDoesNotThrow(() -> {
50+
int response1 = getResponseCode(PORT_1);
51+
int response2 = getResponseCode(PORT_2);
52+
53+
assertEquals(200, response1, "Port 8080 should respond with 200 OK");
54+
assertEquals(200, response2, "Port 7080 should respond with 200 OK");
55+
});
56+
}
57+
58+
@Test
59+
void givenMultipleConnectors_whenResponds_thenReturnsCorrectPort() {
60+
assertDoesNotThrow(() -> {
61+
String content1 = getContent(PORT_1);
62+
String content2 = getContent(PORT_2);
63+
64+
assertNotNull(content1, "Content from port 8080 should not be null");
65+
assertNotNull(content2, "Content from port 7080 should not be null");
66+
67+
assertTrue(content1.contains("Port: 8080"), "Port 8080 should report 'Port: 8080', but got: " + content1);
68+
assertTrue(content2.contains("Port: 7080"), "Port 7080 should report 'Port: 7080', but got: " + content2);
69+
assertNotEquals(content1, content2, "Each port should report its own port number - content should differ");
70+
});
71+
}
72+
73+
private boolean contains(int[] array, int value) {
74+
for (int i : array) {
75+
if (i == value) return true;
76+
}
77+
return false;
78+
}
79+
}
80+

0 commit comments

Comments
 (0)