Introduction: Where Does Your Code Actually Run?
When you write a Java web application, you create classes, controllers, services, and HTML templates. But have you ever wondered: what actually runs your code? When a user visits your website, something needs to:
- Listen for incoming HTTP requests on a network port
- Parse the HTTP protocol and extract the request details
- Route the request to your code
- Execute your code and get the response
- Send the HTTP response back to the user's browser
This "something" is an Application Server (or a simpler variant called a Web Server or Servlet Container). It's the runtime environment that hosts your application and handles all the low-level networking, protocol parsing, and lifecycle management so you can focus on writing business logic.
A Simple Analogy: The Restaurant
Think of deploying a web application like opening a restaurant:
- Your Application (The Menu & Recipes): This is what YOU create—the dishes, recipes, and dining experience. In software terms, your controllers, services, business logic.
- The Application Server (The Restaurant Building): The physical infrastructure—kitchen equipment, tables, plumbing, electricity, ventilation. You don't build these from scratch; you rent or buy a space that has them.
- HTTP Requests (Customers): People come to the door wanting food. The building receives them.
- The Server's Role: The building provides everything needed to serve customers—you just provide the food and service.
An Application Server is software that provides the runtime environment for applications. For Java web applications, it:
- Listens for incoming network connections (typically HTTP on port 80/443 or 8080)
- Manages the lifecycle of your application (loading, starting, stopping)
- Provides services like connection pooling, transactions, security, and more
- Executes your code when requests arrive
- Handles multiple concurrent users efficiently
Web Server vs Application Server vs Servlet Container
These terms are often used interchangeably, but they have distinct meanings. Understanding the differences helps you choose the right tool.
Web Server
A Web Server serves static content (HTML, CSS, JavaScript, images) and handles HTTP protocol basics. It's like a file server that speaks HTTP.
// Web servers serve STATIC content
// They read files from disk and send them over HTTP
User requests: GET /index.html
Web server: Reads /var/www/index.html from disk
Web server: Sends file content back with HTTP headers
// Examples of pure web servers:
// - Apache HTTP Server (httpd)
// - Nginx
// - Microsoft IIS (for static content)
// Web servers are FAST for static files
// But they can't run your Java code!
Servlet Container (Web Container)
A Servlet Container extends the web server concept by adding the ability to run Java Servlets. It implements the Java Servlet specification, allowing dynamic content generation.
// Servlet containers can run Java code!
// They implement the Servlet specification
User requests: GET /api/users
Servlet container:
1. Parses HTTP request
2. Creates HttpServletRequest object
3. Finds matching Servlet (@WebServlet or mapping in web.xml)
4. Calls servlet.service(request, response)
5. Your Java code executes!
6. Sends HttpServletResponse back to user
// Examples of Servlet containers:
// - Apache Tomcat (most popular)
// - Eclipse Jetty
// - Undertow (used in WildFly)
// Servlet containers implement:
// - Jakarta Servlet specification
// - Jakarta Server Pages (JSP)
// - Jakarta WebSocket
Application Server (Full Jakarta EE)
A Full Application Server includes everything a servlet container has, PLUS additional enterprise services defined by the Jakarta EE (formerly Java EE) specification.
// Application servers provide the FULL Jakarta EE stack
// Everything in a Servlet Container, PLUS:
// - EJB (Enterprise JavaBeans) container
// - JPA (Java Persistence API) integration
// - JTA (Java Transaction API)
// - JMS (Java Message Service)
// - CDI (Contexts and Dependency Injection)
// - JAX-RS (RESTful Web Services)
// - Jakarta Security
// - And much more...
// Examples of full application servers:
// - WildFly (formerly JBoss)
// - Payara (GlassFish fork)
// - IBM WebSphere Liberty
// - Oracle WebLogic
// - Apache TomEE (Tomcat + EE)
| Feature | Web Server | Servlet Container | Application Server |
|---|---|---|---|
| Static Content | Yes | Yes | Yes |
| Servlets/JSP | No | Yes | Yes |
| EJB | No | No | Yes |
| JMS | No | No | Yes |
| JTA Transactions | No | No | Yes |
| Examples | Nginx, Apache | Tomcat, Jetty | WildFly, WebLogic |
| Complexity | Low | Medium | High |
| Resource Usage | Low | Medium | High |
What About Spring Boot?
If you've used Spring Boot, you might be confused—you don't deploy to an external server; you just run a JAR file! This is because Spring Boot takes a different approach: embedded servers.
Traditional Deployment (External Server)
// Traditional approach: Deploy WAR to external server
// 1. You build a WAR file
mvn package
// Output: myapp.war
// 2. You have a server installed separately
// /opt/tomcat/
// 3. You deploy by copying WAR to server
cp myapp.war /opt/tomcat/webapps/
// 4. Server detects and deploys your app
// Available at: http://localhost:8080/myapp
// The server exists OUTSIDE your application
┌─────────────────────────────────────────┐
│ TOMCAT SERVER │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ myapp │ │ other │ │ another │ │
│ │ .war │ │ .war │ │ .war │ │
│ └─────────┘ └─────────┘ └─────────┘ │
│ │
│ One server hosts MULTIPLE applications │
└─────────────────────────────────────────┘
Spring Boot (Embedded Server)
// Spring Boot approach: Server is INSIDE your application
// 1. You build an executable JAR
mvn package
// Output: myapp.jar (includes Tomcat inside!)
// 2. You just run it!
java -jar myapp.jar
// Available at: http://localhost:8080
// The server is EMBEDDED in your application
┌─────────────────────────────────────────┐
│ myapp.jar │
│ ┌─────────────────────────────────┐ │
│ │ YOUR APPLICATION CODE │ │
│ │ (Controllers, Services, etc.) │ │
│ └─────────────────────────────────┘ │
│ ┌─────────────────────────────────┐ │
│ │ EMBEDDED TOMCAT │ │
│ │ (Inside your JAR file!) │ │
│ └─────────────────────────────────┘ │
│ │
│ Self-contained, runs anywhere with JVM │
└─────────────────────────────────────────┘
Why Embedded Servers Became Popular
| Aspect | External Server | Embedded Server (Spring Boot) |
|---|---|---|
| Deployment | Copy WAR to server | Just run the JAR |
| Configuration | Server + App configured separately | All in one place |
| Server Version | Whatever's installed on server | You control it (in pom.xml) |
| Multiple Apps | Many apps on one server | One app per process |
| Docker Friendly | More complex | Very easy |
| Cloud Native | Traditional | Modern, 12-factor |
| Startup Time | Server already running | Server starts with app |
Today, most new Java applications use embedded servers (Spring Boot, Quarkus, Micronaut). External application servers are still used in large enterprises with existing infrastructure, or when you need full Jakarta EE features that embedded servers don't provide.
How an Application Server Handles Requests
Let's trace what happens when a user requests a page from your Java web application:
// User types: http://myapp.com/products/123
// Here's what happens inside the application server:
┌─────────────────────────────────────────────────────────────────────┐
│ APPLICATION SERVER │
│ │
│ 1. CONNECTOR (Listens on port 8080) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ - Accepts TCP connection from browser │ │
│ │ - Reads raw bytes from network │ │
│ │ - Parses HTTP protocol │ │
│ │ - Creates internal request object │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 2. ENGINE (Request Processing) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ - Determines which Host (virtual host) handles request │ │
│ │ - Determines which Context (application) handles it │ │
│ │ - Determines which Servlet handles the URL pattern │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 3. FILTER CHAIN │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Security Filter → Logging Filter → CORS Filter → ... │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 4. YOUR SERVLET (or Spring Controller) │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ @GetMapping("/products/{id}") │ │
│ │ public Product getProduct(@PathVariable Long id) { │ │
│ │ return productService.findById(id); │ │
│ │ } │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ 5. RESPONSE (travels back through filters, connector, to browser) │
│ │
└─────────────────────────────────────────────────────────────────────┘
Key Components Explained
- Connector: The network listener. Handles TCP connections and HTTP parsing. You can have multiple connectors (HTTP on 8080, HTTPS on 8443, etc.)
- Engine: The request processor. Routes requests to the correct application and servlet.
- Host: Virtual host support. One server can handle requests for multiple domains (app1.com, app2.com).
- Context: Your application. Each WAR file becomes a context.
- Servlet: Your code that handles specific URL patterns.
Services Provided by Application Servers
Application servers do much more than just route HTTP requests. They provide many services that your application can use:
1. Connection Pooling
// Problem: Creating database connections is SLOW (100-500ms each)
// Solution: Server maintains a pool of ready connections
// Without pooling:
public List<User> getUsers() {
Connection conn = DriverManager.getConnection(url, user, pass); // SLOW!
// ... query ...
conn.close(); // Connection destroyed, next request creates new one
}
// With pooling (configured in server):
@Resource
DataSource dataSource; // Server manages the pool
public List<User> getUsers() {
Connection conn = dataSource.getConnection(); // FAST! Gets existing connection
// ... query ...
conn.close(); // Returns to pool, doesn't destroy
}
// Server configuration (context.xml in Tomcat):
<Resource name="jdbc/mydb"
type="javax.sql.DataSource"
maxTotal="100" <!-- Max 100 connections -->
maxIdle="30" <!-- Keep 30 ready -->
maxWaitMillis="10000"/> <!-- Wait max 10s for connection -->
2. Thread Management
// Server manages a thread pool for handling requests
// Each request gets a thread from the pool
// You don't create threads manually!
┌─────────────────────────────────────────┐
│ THREAD POOL │
│ ┌───┐ ┌───┐ ┌───┐ ┌───┐ ┌───┐ │
│ │ T1│ │ T2│ │ T3│ │...│ │T200│ │
│ └───┘ └───┘ └───┘ └───┘ └───┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ Req1 Req2 Req3 (requests assigned) │
└─────────────────────────────────────────┘
// Tomcat default: 200 threads max
// If 201 requests come simultaneously, one waits
// Configuration (server.xml):
<Connector port="8080"
maxThreads="200"
minSpareThreads="10"
acceptCount="100"/>
3. Session Management
// Server tracks user sessions across requests
@GetMapping("/cart")
public Cart getCart(HttpSession session) {
// Server automatically:
// 1. Reads JSESSIONID cookie from request
// 2. Looks up session data in memory
// 3. Provides it to your code
Cart cart = (Cart) session.getAttribute("cart");
return cart;
}
// Session configuration (web.xml):
<session-config>
<session-timeout>30</session-timeout> <!-- 30 minutes -->
<cookie-config>
<http-only>true</http-only>
<secure>true</secure>
</cookie-config>
</session-config>
4. Security
// Server can handle authentication and authorization
// web.xml security configuration:
<security-constraint>
<web-resource-collection>
<url-pattern>/admin/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>FORM</auth-method>
<form-login-config>
<form-login-page>/login.html</form-login-page>
<form-error-page>/error.html</form-error-page>
</form-login-config>
</login-config>
5. JNDI (Java Naming and Directory Interface)
// Server provides a registry for looking up resources
// Your app doesn't hardcode connection strings!
// In your code - just look up by name:
@Resource(name = "jdbc/productionDB")
DataSource dataSource;
// Server configures the ACTUAL connection:
// Development server → points to dev database
// Production server → points to production database
// Same code, different configuration!
Popular Application Servers
Servlet Containers (Lightweight)
| Server | Description | Best For |
|---|---|---|
| Apache Tomcat | Most popular, reference implementation | Most Java web apps, Spring Boot default |
| Eclipse Jetty | Lightweight, embeddable, fast startup | Microservices, embedded scenarios |
| Undertow | High performance, non-blocking | High-concurrency applications |
Full Application Servers (Jakarta EE)
| Server | Description | Best For |
|---|---|---|
| WildFly | Free, open source, formerly JBoss AS | Full Jakarta EE, enterprise apps |
| Payara | GlassFish fork, production-focused | Jakarta EE with support options |
| IBM WebSphere Liberty | Commercial, modular, fast | Enterprise, IBM ecosystem |
| Oracle WebLogic | Commercial, full-featured | Large enterprise, Oracle ecosystem |
| Apache TomEE | Tomcat + Jakarta EE | Familiar Tomcat with EE features |
Choosing the Right Server
Use a Servlet Container (Tomcat/Jetty) when:
- Building with Spring Boot (embedded server)
- You don't need EJB or JMS
- You want lightweight and simple
- Building microservices
- Deploying to containers (Docker, Kubernetes)
Use a Full Application Server when:
- Required by company policy/infrastructure
- Need full Jakarta EE features (EJB, JMS, etc.)
- Multiple applications sharing resources
- Enterprise support contracts required
- Migrating legacy Java EE applications
Use Embedded Server (Spring Boot) when:
- Building new applications
- Want simple deployment (just run JAR)
- Building for the cloud
- Want to control server version
- Doing microservices architecture
Summary
- Application Server: Runtime environment that hosts and executes your Java web applications
- Web Server: Serves static content only (Nginx, Apache HTTP Server)
- Servlet Container: Runs Servlets/JSP (Tomcat, Jetty)
- Full Application Server: Complete Jakarta EE implementation (WildFly, WebLogic)
- Embedded Server: Server packaged inside your application (Spring Boot approach)
- Services: Connection pooling, thread management, sessions, security, JNDI
- Modern Trend: Embedded servers for cloud-native, microservices applications