diff --git a/server-modules/apache-tomcat-2/pom.xml b/server-modules/apache-tomcat-2/pom.xml new file mode 100644 index 000000000000..3541efca4e1a --- /dev/null +++ b/server-modules/apache-tomcat-2/pom.xml @@ -0,0 +1,66 @@ + + + 4.0.0 + + com.baeldung + server-modules + 1.0.0-SNAPSHOT + + + apache-tomcat-2 + + + + org.apache.tomcat + tomcat-catalina + ${tomcat.version} + + + + org.apache.tomcat + tomcat-juli + ${tomcat.version} + + + + jakarta.servlet + jakarta.servlet-api + 6.0.0 + + + + + org.junit.jupiter + junit-jupiter + 5.10.0 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 11 + 11 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.0.0 + + + + + + 10.1.24 + UTF-8 + + + \ No newline at end of file diff --git a/server-modules/apache-tomcat-2/src/main/java/com/baeldung/tomcat/AppServerXML.java b/server-modules/apache-tomcat-2/src/main/java/com/baeldung/tomcat/AppServerXML.java new file mode 100644 index 000000000000..14187f7e8c26 --- /dev/null +++ b/server-modules/apache-tomcat-2/src/main/java/com/baeldung/tomcat/AppServerXML.java @@ -0,0 +1,51 @@ +package com.baeldung.tomcat; + +import org.apache.catalina.startup.Catalina; +import java.io.InputStream; +import java.net.URL; +import java.nio.file.*; + +public class AppServerXML { + + public static void main(String[] args) throws Exception { + AppServerXML app = new AppServerXML(); + Catalina catalina = app.startServer(); + catalina.getServer().await(); + } + + public Catalina startServer() throws Exception { + URL staticUrl = getClass().getClassLoader().getResource("static"); + if (staticUrl == null) { + throw new IllegalStateException("Static directory not found in classpath"); + } + Path staticDir = Paths.get(staticUrl.toURI()); + + Path baseDir = Paths.get("target/tomcat-base").toAbsolutePath(); + Files.createDirectories(baseDir); + + String config; + try (InputStream serverXmlStream = getClass().getClassLoader().getResourceAsStream("server.xml")) { + if (serverXmlStream == null) { + throw new IllegalStateException("server.xml not found in classpath"); + } + config = new String(serverXmlStream.readAllBytes()) + .replace("STATIC_DIR_PLACEHOLDER", staticDir.toString()); + } + + Path configFile = baseDir.resolve("server.xml"); + Files.writeString(configFile, config); + + System.setProperty("catalina.base", baseDir.toString()); + System.setProperty("catalina.home", baseDir.toString()); + + Catalina catalina = new Catalina(); + catalina.load(new String[]{"-config", configFile.toString()}); + catalina.start(); + + System.out.println("\nTomcat started with multiple connectors!"); + System.out.println("http://localhost:8081"); + System.out.println("http://localhost:7081"); + + return catalina; + } +} diff --git a/server-modules/apache-tomcat-2/src/main/java/com/baeldung/tomcat/DualPort.java b/server-modules/apache-tomcat-2/src/main/java/com/baeldung/tomcat/DualPort.java new file mode 100644 index 000000000000..972ff4de094c --- /dev/null +++ b/server-modules/apache-tomcat-2/src/main/java/com/baeldung/tomcat/DualPort.java @@ -0,0 +1,48 @@ +package com.baeldung.tomcat; + +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.apache.catalina.connector.Connector; +import java.io.File; +import java.io.IOException; + +import org.apache.catalina.Context; +import org.apache.catalina.startup.Tomcat; + +public class DualPort { + + public static void main(String[] args) throws Exception { + DualPort dualPort = new DualPort(); + Tomcat tomcat = dualPort.startServer(); + tomcat.getServer().await(); + } + + public Tomcat startServer() throws Exception { + Tomcat tomcat = new Tomcat(); + tomcat.setBaseDir(new File("tomcat-temp").getAbsolutePath()); + + tomcat.setPort(7080); + tomcat.getConnector(); + + Connector secondConnector = new Connector(); + secondConnector.setPort(8080); + tomcat.getService().addConnector(secondConnector); + + Context ctx = tomcat.addContext("", new File(".").getAbsolutePath()); + Tomcat.addServlet(ctx, "portServlet", new HttpServlet() { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + int port = req.getLocalPort(); + resp.setContentType("text/plain"); + resp.getWriter().write("Port: " + port + "\n"); + } + }); + ctx.addServletMappingDecoded("/", "portServlet"); + + tomcat.start(); + System.out.println("Tomcat running on ports 8080 and 7080"); + + return tomcat; + } +} diff --git a/server-modules/apache-tomcat-2/src/main/java/com/baeldung/tomcat/PortServlet.java b/server-modules/apache-tomcat-2/src/main/java/com/baeldung/tomcat/PortServlet.java new file mode 100644 index 000000000000..26c270eea7e1 --- /dev/null +++ b/server-modules/apache-tomcat-2/src/main/java/com/baeldung/tomcat/PortServlet.java @@ -0,0 +1,15 @@ +package com.baeldung.tomcat; + +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import java.io.IOException; + +public class PortServlet extends HttpServlet { + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { + int port = req.getLocalPort(); + resp.setContentType("text/plain"); + resp.getWriter().write("port number: " + port); + } +} diff --git a/server-modules/apache-tomcat-2/src/main/resources/server.xml b/server-modules/apache-tomcat-2/src/main/resources/server.xml new file mode 100644 index 000000000000..e7ff58768098 --- /dev/null +++ b/server-modules/apache-tomcat-2/src/main/resources/server.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/server-modules/apache-tomcat-2/src/main/resources/static/WEB-INF/web.xml b/server-modules/apache-tomcat-2/src/main/resources/static/WEB-INF/web.xml new file mode 100644 index 000000000000..af405ef375f3 --- /dev/null +++ b/server-modules/apache-tomcat-2/src/main/resources/static/WEB-INF/web.xml @@ -0,0 +1,31 @@ + + + + Static HTML Application + + + + default + org.apache.catalina.servlets.DefaultServlet + + listings + false + + 1 + + + + default + / + + + + + index.html + + + diff --git a/server-modules/apache-tomcat-2/src/main/resources/static/index.html b/server-modules/apache-tomcat-2/src/main/resources/static/index.html new file mode 100644 index 000000000000..c644aa338438 --- /dev/null +++ b/server-modules/apache-tomcat-2/src/main/resources/static/index.html @@ -0,0 +1,8 @@ + + +Dual Port Test + +

Tomcat is running!

+

Port:

+ + diff --git a/server-modules/apache-tomcat-2/src/test/java/com/baeldung/tomcat/AppServerXMLIntegrationTest.java b/server-modules/apache-tomcat-2/src/test/java/com/baeldung/tomcat/AppServerXMLIntegrationTest.java new file mode 100644 index 000000000000..3edcba0d2c44 --- /dev/null +++ b/server-modules/apache-tomcat-2/src/test/java/com/baeldung/tomcat/AppServerXMLIntegrationTest.java @@ -0,0 +1,76 @@ +package com.baeldung.tomcat; + +import org.apache.catalina.startup.Catalina; +import org.junit.jupiter.api.*; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; + +import static com.baeldung.tomcat.HttpConnection.getContent; +import static com.baeldung.tomcat.HttpConnection.getResponseCode; +import static org.junit.jupiter.api.Assertions.*; + +public class AppServerXMLIntegrationTest { + + private static AppServerXML app; + private static Catalina catalina; + private static final int HTTP_PORT_1 = 8081; + private static final int HTTP_PORT_2 = 7081; + + @BeforeAll + static void setUp() throws Exception { + app = new AppServerXML(); + catalina = app.startServer(); + Thread.sleep(2000); + } + + @AfterAll + static void shutDown() throws Exception { + if (catalina != null && catalina.getServer() != null) { + catalina.stop(); + Thread.sleep(1000); + } + } + + @Test + void givenMultipleConnectors_whenServerStarts_thenContainsMultiplePorts() { + assertNotNull(catalina.getServer(), "Server should be initialized"); + + Path configFile = Paths.get("target/tomcat-base/server.xml"); + assertTrue(Files.exists(configFile), "Generated server.xml should exist"); + + assertDoesNotThrow(() -> { + String config = Files.readString(configFile); + assertTrue(config.contains("port=\"8081\""), "Config should have port 8081"); + assertTrue(config.contains("port=\"7081\""), "Config should have port 7081"); + assertFalse(config.contains("STATIC_DIR_PLACEHOLDER"), "Placeholder should be replaced"); + }); + } + + @Test + void givenMultipleConnectors_whenResponds_thenReturns200() { + assertDoesNotThrow(() -> { + int response1 = getResponseCode(HTTP_PORT_1); + int response2 = getResponseCode(HTTP_PORT_2); + + assertEquals(200, response1, "Port 8081 should respond with 200 OK"); + assertEquals(200, response2, "Port 7081 should respond with 200 OK"); + }); + } + + @Test + void givenMultipleConnectors_whenResponds_thenReturnsIdenticalContent() { + assertDoesNotThrow(() -> { + String content1 = getContent(HTTP_PORT_1); + String content2 = getContent(HTTP_PORT_2); + + assertNotNull(content1, "Content from port 8081 should not be null"); + assertNotNull(content2, "Content from port 7081 should not be null"); + + assertTrue(content1.contains("Tomcat is running"), "Content should contain expected text"); + assertEquals(content1, content2, "Both ports should serve identical content"); + }); + } +} + diff --git a/server-modules/apache-tomcat-2/src/test/java/com/baeldung/tomcat/DualPortIntegrationTest.java b/server-modules/apache-tomcat-2/src/test/java/com/baeldung/tomcat/DualPortIntegrationTest.java new file mode 100644 index 000000000000..cc263724cd46 --- /dev/null +++ b/server-modules/apache-tomcat-2/src/test/java/com/baeldung/tomcat/DualPortIntegrationTest.java @@ -0,0 +1,80 @@ +package com.baeldung.tomcat; + +import org.apache.catalina.connector.Connector; +import org.apache.catalina.startup.Tomcat; +import org.junit.jupiter.api.*; + +import static com.baeldung.tomcat.HttpConnection.getContent; +import static com.baeldung.tomcat.HttpConnection.getResponseCode; +import static org.junit.jupiter.api.Assertions.*; + +public class DualPortIntegrationTest { + + private static DualPort app; + private static Tomcat tomcat; + private static final int PORT_1 = 8080; + private static final int PORT_2 = 7080; + + @BeforeAll + static void setUp() throws Exception { + app = new DualPort(); + tomcat = app.startServer(); + Thread.sleep(2000); + } + + @AfterAll + static void tearDown() throws Exception { + if (tomcat != null && tomcat.getServer() != null) { + tomcat.stop(); + tomcat.destroy(); + Thread.sleep(1000); + } + } + + @Test + void givenMultipleConnectors_whenServerStarts_thenContainsMultiplePorts() { + assertNotNull(tomcat, "Tomcat instance should not be null"); + assertNotNull(tomcat.getServer(), "Server should be initialized"); + + Connector[] connectors = tomcat.getService().findConnectors(); + assertEquals(2, connectors.length, "Should have exactly 2 connectors"); + + int[] ports = new int[]{connectors[0].getPort(), connectors[1].getPort()}; + assertTrue(contains(ports, 8080), "Should have connector on port 8080"); + assertTrue(contains(ports, 7080), "Should have connector on port 7080"); + } + + @Test + void givenMultipleConnectors_whenResponds_thenReturns200() { + assertDoesNotThrow(() -> { + int response1 = getResponseCode(PORT_1); + int response2 = getResponseCode(PORT_2); + + assertEquals(200, response1, "Port 8080 should respond with 200 OK"); + assertEquals(200, response2, "Port 7080 should respond with 200 OK"); + }); + } + + @Test + void givenMultipleConnectors_whenResponds_thenReturnsCorrectPort() { + assertDoesNotThrow(() -> { + String content1 = getContent(PORT_1); + String content2 = getContent(PORT_2); + + assertNotNull(content1, "Content from port 8080 should not be null"); + assertNotNull(content2, "Content from port 7080 should not be null"); + + assertTrue(content1.contains("Port: 8080"), "Port 8080 should report 'Port: 8080', but got: " + content1); + assertTrue(content2.contains("Port: 7080"), "Port 7080 should report 'Port: 7080', but got: " + content2); + assertNotEquals(content1, content2, "Each port should report its own port number - content should differ"); + }); + } + + private boolean contains(int[] array, int value) { + for (int i : array) { + if (i == value) return true; + } + return false; + } +} + diff --git a/server-modules/apache-tomcat-2/src/test/java/com/baeldung/tomcat/HttpConnection.java b/server-modules/apache-tomcat-2/src/test/java/com/baeldung/tomcat/HttpConnection.java new file mode 100644 index 000000000000..479820733778 --- /dev/null +++ b/server-modules/apache-tomcat-2/src/test/java/com/baeldung/tomcat/HttpConnection.java @@ -0,0 +1,38 @@ +package com.baeldung.tomcat; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URI; +import java.net.URL; +import java.util.stream.Collectors; + +public class HttpConnection { + static int getResponseCode(int port) throws Exception { + HttpURLConnection connection = getConnection(port); + try { + return connection.getResponseCode(); + } finally { + connection.disconnect(); + } + } + + static String getContent(int port) throws Exception { + HttpURLConnection connection = getConnection(port); + try (BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) { + return reader.lines().collect(Collectors.joining()); + } finally { + connection.disconnect(); + } + } + + static HttpURLConnection getConnection(int port) throws IOException { + URL url = URI.create("http://localhost:" + port + "/").toURL(); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setRequestMethod("GET"); + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); + return connection; + } +} diff --git a/server-modules/pom.xml b/server-modules/pom.xml index 8bbe42aaeefc..1a299aa3128f 100644 --- a/server-modules/pom.xml +++ b/server-modules/pom.xml @@ -19,6 +19,7 @@ undertow wildfly armeria + apache-tomcat-2 \ No newline at end of file