1. java
  2. /enterprise
  3. /application-servers

Java Application Servers and Deployment

Java Application Servers

Application servers provide the runtime environment for enterprise Java applications, offering services like transaction management, security, messaging, and web services. This guide covers deployment, configuration, and management of popular Java application servers.

Table of Contents

Application Server Overview

Types of Java Servers

Web Servers (Servlet Containers)

  • Apache Tomcat
  • Jetty
  • Undertow

Full Java EE Application Servers

  • WildFly (formerly JBoss)
  • Oracle WebLogic
  • IBM WebSphere
  • GlassFish

Java EE Components

// Servlets and JSPs
@WebServlet("/hello")
public class HelloServlet extends HttpServlet { }

// Enterprise JavaBeans (EJB)
@Stateless
public class UserService { }

// Java Persistence API (JPA)
@Entity
public class User { }

// Message-Driven Beans
@MessageDriven
public class OrderProcessor implements MessageListener { }

// Web Services
@WebService
public class UserWebService { }

Apache Tomcat

Installation and Setup

# Download and extract Tomcat
wget https://downloads.apache.org/tomcat/tomcat-10/v10.1.15/bin/apache-tomcat-10.1.15.tar.gz
tar -xzf apache-tomcat-10.1.15.tar.gz
cd apache-tomcat-10.1.15

# Set environment variables
export JAVA_HOME=/usr/lib/jvm/java-11-openjdk
export CATALINA_HOME=/opt/tomcat
export CATALINA_BASE=/opt/tomcat

# Start Tomcat
./bin/startup.sh

# Stop Tomcat
./bin/shutdown.sh

Server Configuration

<!-- server.xml - Main Tomcat configuration -->
<Server port="8005" shutdown="SHUTDOWN">
  <Service name="Catalina">
    
    <!-- HTTP Connector -->
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443"
               maxThreads="200"
               minSpareThreads="10"
               enableLookups="false"
               compression="on"
               compressionMinSize="2048"/>
    
    <!-- HTTPS Connector -->
    <Connector port="8443" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="150" SSLEnabled="true">
      <SSLHostConfig>
        <Certificate certificateKeystoreFile="conf/localhost-rsa.jks"
                     type="RSA" />
      </SSLHostConfig>
    </Connector>
    
    <!-- AJP Connector for load balancing -->
    <Connector protocol="AJP/1.3"
               address="::1"
               port="8009"
               redirectPort="8443" />
    
    <Engine name="Catalina" defaultHost="localhost">
      
      <!-- Clustering configuration -->
      <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster">
        <Manager className="org.apache.catalina.ha.session.DeltaManager"
                 expireSessionsOnShutdown="false"
                 notifyListenersOnReplication="true"/>
        
        <Channel className="org.apache.catalina.tribes.group.GroupChannel">
          <Membership className="org.apache.catalina.tribes.membership.McastService"
                      address="228.0.0.4"
                      port="45564"
                      frequency="500"
                      dropTime="3000"/>
          
          <Receiver className="org.apache.catalina.tribes.transport.nio.NioReceiver"
                    address="auto"
                    port="4000"
                    autoBind="100"
                    selectorTimeout="5000"
                    maxThreads="6"/>
          
          <Sender className="org.apache.catalina.tribes.transport.ReplicationTransmitter">
            <Transport className="org.apache.catalina.tribes.transport.nio.PooledParallelSender"/>
          </Sender>
          
          <Interceptor className="org.apache.catalina.tribes.group.interceptors.TcpFailureDetector"/>
          <Interceptor className="org.apache.catalina.tribes.group.interceptors.MessageDispatchInterceptor"/>
        </Channel>
        
        <Valve className="org.apache.catalina.ha.tcp.ReplicationValve"/>
        <Valve className="org.apache.catalina.ha.session.JvmRouteBinderValve"/>
        
        <ClusterListener className="org.apache.catalina.ha.session.ClusterSessionListener"/>
      </Cluster>
      
      <Host name="localhost" appBase="webapps"
            unpackWARs="true" autoDeploy="true">
        
        <!-- Access logging -->
        <Valve className="org.apache.catalina.valves.AccessLogValve"
               directory="logs"
               prefix="localhost_access_log"
               suffix=".txt"
               pattern="%h %l %u %t &quot;%r&quot; %s %b" />
      </Host>
    </Engine>
  </Service>
</Server>

Context Configuration

<!-- context.xml - Application context configuration -->
<Context>
  
  <!-- Database connection pool -->
  <Resource name="jdbc/MyDB" 
            auth="Container" 
            type="javax.sql.DataSource"
            maxTotal="20" 
            maxIdle="5"
            maxWaitMillis="10000"
            username="dbuser" 
            password="dbpass" 
            driverClassName="com.mysql.cj.jdbc.Driver"
            url="jdbc:mysql://localhost:3306/mydb"/>
  
  <!-- JMS Connection Factory -->
  <Resource name="jms/ConnectionFactory"
            auth="Container"
            type="org.apache.activemq.ActiveMQConnectionFactory"
            description="JMS Connection Factory"
            factory="org.apache.activemq.jndi.JNDIReferenceFactory"
            brokerURL="tcp://localhost:61616"/>
  
  <!-- Environment entries -->
  <Environment name="maxExemptions" 
               value="15"
               type="java.lang.Integer" 
               override="false"/>
  
  <!-- Session configuration -->
  <Manager className="org.apache.catalina.session.PersistentManager">
    <Store className="org.apache.catalina.session.FileStore"
           directory="sessions"/>
  </Manager>
  
</Context>

User Management

<!-- tomcat-users.xml - User and role configuration -->
<tomcat-users xmlns="http://tomcat.apache.org/xml"
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
              xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
              version="1.0">
  
  <!-- Define roles -->
  <role rolename="manager-gui"/>
  <role rolename="manager-script"/>
  <role rolename="admin-gui"/>
  <role rolename="admin-script"/>
  
  <!-- Define users -->
  <user username="admin" 
        password="adminpass" 
        roles="manager-gui,admin-gui"/>
  
  <user username="deployer" 
        password="deploypass" 
        roles="manager-script"/>
  
</tomcat-users>

WildFly (JBoss)

Installation and Configuration

# Download and extract WildFly
wget https://github.com/wildfly/wildfly/releases/download/30.0.0.Final/wildfly-30.0.0.Final.tar.gz
tar -xzf wildfly-30.0.0.Final.tar.gz
cd wildfly-30.0.0.Final

# Start WildFly
./bin/standalone.sh

# Start with custom configuration
./bin/standalone.sh -c standalone-full.xml

# Start domain mode
./bin/domain.sh

Datasource Configuration

<!-- standalone.xml - WildFly configuration -->
<subsystem xmlns="urn:jboss:domain:datasources:6.0">
  <datasources>
    <datasource jndi-name="java:jboss/datasources/ExampleDS" 
                pool-name="ExampleDS" 
                enabled="true" 
                use-java-context="true">
      <connection-url>jdbc:mysql://localhost:3306/mydb</connection-url>
      <driver>mysql</driver>
      <pool>
        <min-pool-size>10</min-pool-size>
        <max-pool-size>20</max-pool-size>
        <prefill>true</prefill>
      </pool>
      <security>
        <user-name>dbuser</user-name>
        <password>dbpass</password>
      </security>
      <validation>
        <valid-connection-checker class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker"/>
        <background-validation>true</background-validation>
        <background-validation-millis>60000</background-validation-millis>
      </validation>
    </datasource>
    
    <drivers>
      <driver name="mysql" module="com.mysql">
        <driver-class>com.mysql.cj.jdbc.Driver</driver-class>
      </driver>
    </drivers>
  </datasources>
</subsystem>

JMS Configuration

<!-- JMS subsystem configuration -->
<subsystem xmlns="urn:jboss:domain:messaging-activemq:13.0">
  <server name="default">
    <statistics enabled="${wildfly.messaging-activemq.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
    
    <security-setting name="#">
      <role name="guest" send="true" consume="true" create-non-durable-queue="true" delete-non-durable-queue="true"/>
    </security-setting>
    
    <address-setting name="#" 
                     dead-letter-address="jms.queue.DLQ" 
                     expiry-address="jms.queue.ExpiryQueue" 
                     max-size-bytes="10485760" 
                     page-size-bytes="2097152" 
                     message-counter-history-day-limit="10"/>
    
    <http-connector name="http-connector" socket-binding="http" endpoint="http-acceptor"/>
    <http-connector name="http-connector-throughput" socket-binding="http" endpoint="http-acceptor-throughput">
      <param name="batch-delay" value="50"/>
    </http-connector>
    
    <in-vm-connector name="in-vm" server-id="0">
      <param name="buffer-pooling" value="false"/>
    </in-vm-connector>
    
    <http-acceptor name="http-acceptor" http-listener="default"/>
    <http-acceptor name="http-acceptor-throughput" http-listener="default">
      <param name="batch-delay" value="50"/>
      <param name="direct-deliver" value="false"/>
    </http-acceptor>
    
    <in-vm-acceptor name="in-vm" server-id="0">
      <param name="buffer-pooling" value="false"/>
    </in-vm-acceptor>
    
    <jms-queue name="ExampleQueue" entries="java:jboss/exported/jms/queue/ExampleQueue"/>
    <jms-topic name="ExampleTopic" entries="java:jboss/exported/jms/topic/ExampleTopic"/>
  </server>
</subsystem>

CLI Configuration

# Connect to WildFly CLI
./bin/jboss-cli.sh --connect

# Deploy application
deploy ~/myapp.war

# Undeploy application
undeploy myapp.war

# Add datasource
/subsystem=datasources/data-source=MyDS:add(
  jndi-name=java:jboss/datasources/MyDS,
  driver-name=mysql,
  connection-url=jdbc:mysql://localhost:3306/mydb,
  user-name=dbuser,
  password=dbpass
)

# Enable datasource
/subsystem=datasources/data-source=MyDS:enable

# Configure logging
/subsystem=logging/logger=com.mycompany:add(level=DEBUG)

# Set system properties
/system-property=my.property:add(value="my-value")

# Reload configuration
:reload

Oracle WebLogic

Domain Configuration

<!-- config.xml - WebLogic domain configuration -->
<domain xmlns="http://xmlns.oracle.com/weblogic/domain">
  <name>mydomain</name>
  
  <!-- Server configuration -->
  <server>
    <name>AdminServer</name>
    <listen-port>7001</listen-port>
    <listen-address>localhost</listen-address>
    
    <ssl>
      <enabled>true</enabled>
      <listen-port>7002</listen-port>
    </ssl>
    
    <log>
      <file-name>logs/AdminServer.log</file-name>
      <rotation-type>bySize</rotation-type>
      <file-size-limit>5000</file-size-limit>
      <number-of-files-limited>true</number-of-files-limited>
      <file-count>10</file-count>
    </log>
  </server>
  
  <!-- Managed servers -->
  <server>
    <name>ManagedServer1</name>
    <listen-port>8001</listen-port>
    <machine>Machine1</machine>
    <cluster>MyCluster</cluster>
  </server>
  
  <server>
    <name>ManagedServer2</name>
    <listen-port>8002</listen-port>
    <machine>Machine2</machine>
    <cluster>MyCluster</cluster>
  </server>
  
  <!-- Cluster configuration -->
  <cluster>
    <name>MyCluster</name>
    <multicast-address>239.192.0.0</multicast-address>
    <multicast-port>7001</multicast-port>
    <cluster-messaging-mode>unicast</cluster-messaging-mode>
  </cluster>
  
  <!-- Machine definitions -->
  <machine>
    <name>Machine1</name>
    <node-manager>
      <name>NodeManager1</name>
      <listen-address>host1.example.com</listen-address>
      <listen-port>5556</listen-port>
    </node-manager>
  </machine>
  
</domain>

DataSource Configuration

<!-- JDBC DataSource configuration -->
<jdbc-data-source>
  <name>MyDataSource</name>
  <jndi-name>jdbc/MyDS</jndi-name>
  
  <jdbc-driver-params>
    <url>jdbc:oracle:thin:@localhost:1521:xe</url>
    <driver-name>oracle.jdbc.OracleDriver</driver-name>
    <properties>
      <property>
        <name>user</name>
        <value>dbuser</value>
      </property>
    </properties>
    <password-encrypted>{AES}encrypted_password</password-encrypted>
  </jdbc-driver-params>
  
  <jdbc-connection-pool-params>
    <initial-capacity>5</initial-capacity>
    <max-capacity>20</max-capacity>
    <capacity-increment>2</capacity-increment>
    <shrink-frequency-seconds>900</shrink-frequency-seconds>
    <highest-num-waiters>2147483647</highest-num-waiters>
    <connection-creation-retry-frequency-seconds>0</connection-creation-retry-frequency-seconds>
    <connection-reserve-timeout-seconds>10</connection-reserve-timeout-seconds>
    <test-table-name>SQL SELECT 1 FROM DUAL</test-table-name>
    <test-connections-on-reserve>true</test-connections-on-reserve>
    <test-frequency-seconds>120</test-frequency-seconds>
  </jdbc-connection-pool-params>
  
  <jdbc-xa-params>
    <xa-retry-duration-seconds>300</xa-retry-duration-seconds>
    <xa-retry-interval-seconds>60</xa-retry-interval-seconds>
  </jdbc-xa-params>
  
  <target>MyCluster</target>
</jdbc-data-source>

Deployment Strategies

WAR Deployment

<!-- web.xml - Web application configuration -->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
         http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
  
  <display-name>My Web Application</display-name>
  
  <!-- Context parameters -->
  <context-param>
    <param-name>environment</param-name>
    <param-value>production</param-value>
  </context-param>
  
  <!-- Servlet configuration -->
  <servlet>
    <servlet-name>MyServlet</servlet-name>
    <servlet-class>com.example.MyServlet</servlet-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>/WEB-INF/app-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>MyServlet</servlet-name>
    <url-pattern>/api/*</url-pattern>
  </servlet-mapping>
  
  <!-- Filter configuration -->
  <filter>
    <filter-name>AuthenticationFilter</filter-name>
    <filter-class>com.example.AuthenticationFilter</filter-class>
  </filter>
  
  <filter-mapping>
    <filter-name>AuthenticationFilter</filter-name>
    <url-pattern>/secure/*</url-pattern>
  </filter-mapping>
  
  <!-- Resource references -->
  <resource-ref>
    <res-ref-name>jdbc/MyDB</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
  </resource-ref>
  
  <!-- Security constraints -->
  <security-constraint>
    <web-resource-collection>
      <web-resource-name>Secure Area</web-resource-name>
      <url-pattern>/admin/*</url-pattern>
      <http-method>GET</http-method>
      <http-method>POST</http-method>
    </web-resource-collection>
    <auth-constraint>
      <role-name>admin</role-name>
    </auth-constraint>
  </security-constraint>
  
  <login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>MyApp Realm</realm-name>
  </login-config>
  
  <security-role>
    <role-name>admin</role-name>
  </security-role>
  
</web-app>

EAR Deployment

<!-- application.xml - Enterprise application configuration -->
<application xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
             http://xmlns.jcp.org/xml/ns/javaee/application_8.xsd"
             version="8">
  
  <application-name>MyEnterpriseApp</application-name>
  <description>My Enterprise Application</description>
  
  <!-- Web module -->
  <module>
    <web>
      <web-uri>myapp-web.war</web-uri>
      <context-root>/myapp</context-root>
    </web>
  </module>
  
  <!-- EJB module -->
  <module>
    <ejb>myapp-ejb.jar</ejb>
  </module>
  
  <!-- Utility module -->
  <module>
    <java>myapp-common.jar</java>
  </module>
  
  <!-- Security roles -->
  <security-role>
    <role-name>user</role-name>
  </security-role>
  
  <security-role>
    <role-name>admin</role-name>
  </security-role>
  
</application>

Automated Deployment

#!/bin/bash
# Tomcat deployment script

TOMCAT_HOME=/opt/tomcat
WEBAPPS_DIR=$TOMCAT_HOME/webapps
APP_NAME=myapp
WAR_FILE=$APP_NAME.war
BACKUP_DIR=/opt/backups

# Create backup of existing deployment
if [ -d "$WEBAPPS_DIR/$APP_NAME" ]; then
    echo "Creating backup of existing deployment..."
    tar -czf "$BACKUP_DIR/${APP_NAME}_$(date +%Y%m%d_%H%M%S).tar.gz" \
        -C "$WEBAPPS_DIR" "$APP_NAME"
    
    # Stop Tomcat
    echo "Stopping Tomcat..."
    $TOMCAT_HOME/bin/shutdown.sh
    
    # Remove old deployment
    rm -rf "$WEBAPPS_DIR/$APP_NAME"
    rm -f "$WEBAPPS_DIR/$WAR_FILE"
fi

# Deploy new WAR
echo "Deploying new WAR file..."
cp "$WAR_FILE" "$WEBAPPS_DIR/"

# Start Tomcat
echo "Starting Tomcat..."
$TOMCAT_HOME/bin/startup.sh

# Wait for deployment
echo "Waiting for deployment to complete..."
sleep 30

# Verify deployment
if [ -d "$WEBAPPS_DIR/$APP_NAME" ]; then
    echo "Deployment successful!"
else
    echo "Deployment failed!"
    exit 1
fi

Configuration Management

Environment-Specific Configuration

// Configuration service for different environments
@Component
public class ConfigurationService {
    
    @Value("${app.environment:development}")
    private String environment;
    
    @Value("${database.url}")
    private String databaseUrl;
    
    @Value("${database.pool.maxSize:10}")
    private int maxPoolSize;
    
    @Value("${security.jwt.secret}")
    private String jwtSecret;
    
    @PostConstruct
    public void init() {
        System.out.println("Application starting in environment: " + environment);
        
        if ("production".equals(environment)) {
            configureForProduction();
        } else if ("staging".equals(environment)) {
            configureForStaging();
        } else {
            configureForDevelopment();
        }
    }
    
    private void configureForProduction() {
        // Production-specific configuration
        System.setProperty("log.level", "WARN");
        System.setProperty("cache.enabled", "true");
    }
    
    private void configureForStaging() {
        // Staging-specific configuration
        System.setProperty("log.level", "INFO");
        System.setProperty("debug.enabled", "true");
    }
    
    private void configureForDevelopment() {
        // Development-specific configuration
        System.setProperty("log.level", "DEBUG");
        System.setProperty("hot.reload", "true");
    }
}

JNDI Configuration

// Accessing JNDI resources
@Component
public class ResourceManager {
    
    private DataSource dataSource;
    private ConnectionFactory connectionFactory;
    
    @PostConstruct
    public void initResources() {
        try {
            Context initContext = new InitialContext();
            Context envContext = (Context) initContext.lookup("java:/comp/env");
            
            // Get DataSource
            dataSource = (DataSource) envContext.lookup("jdbc/MyDB");
            
            // Get JMS ConnectionFactory
            connectionFactory = (ConnectionFactory) envContext.lookup("jms/ConnectionFactory");
            
        } catch (NamingException e) {
            throw new RuntimeException("Failed to initialize JNDI resources", e);
        }
    }
    
    public Connection getDatabaseConnection() throws SQLException {
        return dataSource.getConnection();
    }
    
    public javax.jms.Connection getJMSConnection() throws JMSException {
        return connectionFactory.createConnection();
    }
}

Monitoring and Management

JMX Management

// Custom MBean for application monitoring
public interface ApplicationMonitorMBean {
    int getActiveUsers();
    long getRequestCount();
    double getAverageResponseTime();
    void resetStatistics();
    String getApplicationStatus();
}

@Component
public class ApplicationMonitor implements ApplicationMonitorMBean {
    
    private final AtomicInteger activeUsers = new AtomicInteger(0);
    private final AtomicLong requestCount = new AtomicLong(0);
    private final AtomicLong totalResponseTime = new AtomicLong(0);
    
    @PostConstruct
    public void registerMBean() {
        try {
            MBeanServer server = ManagementFactory.getPlatformMBeanServer();
            ObjectName name = new ObjectName("com.example:type=ApplicationMonitor");
            server.registerMBean(this, name);
        } catch (Exception e) {
            throw new RuntimeException("Failed to register MBean", e);
        }
    }
    
    @Override
    public int getActiveUsers() {
        return activeUsers.get();
    }
    
    @Override
    public long getRequestCount() {
        return requestCount.get();
    }
    
    @Override
    public double getAverageResponseTime() {
        long requests = requestCount.get();
        return requests > 0 ? (double) totalResponseTime.get() / requests : 0.0;
    }
    
    @Override
    public void resetStatistics() {
        requestCount.set(0);
        totalResponseTime.set(0);
    }
    
    @Override
    public String getApplicationStatus() {
        return "Running - " + getActiveUsers() + " active users";
    }
    
    public void recordRequest(long responseTime) {
        requestCount.incrementAndGet();
        totalResponseTime.addAndGet(responseTime);
    }
    
    public void userLoggedIn() {
        activeUsers.incrementAndGet();
    }
    
    public void userLoggedOut() {
        activeUsers.decrementAndGet();
    }
}

Health Checks

// Application health check endpoint
@RestController
@RequestMapping("/health")
public class HealthController {
    
    @Autowired
    private DataSource dataSource;
    
    @Autowired
    private JmsTemplate jmsTemplate;
    
    @GetMapping
    public ResponseEntity<Map<String, Object>> healthCheck() {
        Map<String, Object> health = new HashMap<>();
        boolean isHealthy = true;
        
        // Check database connectivity
        health.put("database", checkDatabase());
        if (!"UP".equals(health.get("database"))) {
            isHealthy = false;
        }
        
        // Check JMS connectivity
        health.put("jms", checkJMS());
        if (!"UP".equals(health.get("jms"))) {
            isHealthy = false;
        }
        
        // Check memory usage
        health.put("memory", checkMemory());
        
        // Check disk space
        health.put("disk", checkDiskSpace());
        
        health.put("status", isHealthy ? "UP" : "DOWN");
        health.put("timestamp", new Date());
        
        HttpStatus status = isHealthy ? HttpStatus.OK : HttpStatus.SERVICE_UNAVAILABLE;
        return ResponseEntity.status(status).body(health);
    }
    
    private String checkDatabase() {
        try (Connection conn = dataSource.getConnection()) {
            return conn.isValid(5) ? "UP" : "DOWN";
        } catch (Exception e) {
            return "DOWN - " + e.getMessage();
        }
    }
    
    private String checkJMS() {
        try {
            // Try to send a test message
            jmsTemplate.convertAndSend("health.check", "ping");
            return "UP";
        } catch (Exception e) {
            return "DOWN - " + e.getMessage();
        }
    }
    
    private Map<String, Object> checkMemory() {
        Runtime runtime = Runtime.getRuntime();
        long maxMemory = runtime.maxMemory();
        long totalMemory = runtime.totalMemory();
        long freeMemory = runtime.freeMemory();
        long usedMemory = totalMemory - freeMemory;
        
        Map<String, Object> memory = new HashMap<>();
        memory.put("max", maxMemory);
        memory.put("total", totalMemory);
        memory.put("used", usedMemory);
        memory.put("free", freeMemory);
        memory.put("usage", (double) usedMemory / maxMemory);
        
        return memory;
    }
    
    private Map<String, Object> checkDiskSpace() {
        File root = new File("/");
        Map<String, Object> disk = new HashMap<>();
        disk.put("total", root.getTotalSpace());
        disk.put("free", root.getFreeSpace());
        disk.put("used", root.getTotalSpace() - root.getFreeSpace());
        
        return disk;
    }
}

Best Practices

Performance Tuning

# JVM tuning parameters
JAVA_OPTS="-server \
  -Xms2048m \
  -Xmx4096m \
  -XX:NewRatio=2 \
  -XX:SurvivorRatio=8 \
  -XX:+UseG1GC \
  -XX:MaxGCPauseMillis=200 \
  -XX:+UseStringDeduplication \
  -XX:+UnlockExperimentalVMOptions \
  -XX:G1NewSizePercent=20 \
  -XX:G1MaxNewSizePercent=30 \
  -XX:+DisableExplicitGC \
  -Djava.awt.headless=true \
  -Dfile.encoding=UTF-8 \
  -Duser.timezone=UTC"

# Garbage collection logging
GC_OPTS="-Xloggc:logs/gc.log \
  -XX:+PrintGCDetails \
  -XX:+PrintGCTimeStamps \
  -XX:+PrintGCDateStamps \
  -XX:+UseGCLogFileRotation \
  -XX:NumberOfGCLogFiles=5 \
  -XX:GCLogFileSize=100M"

# JMX monitoring
JMX_OPTS="-Dcom.sun.management.jmxremote \
  -Dcom.sun.management.jmxremote.port=9999 \
  -Dcom.sun.management.jmxremote.authenticate=false \
  -Dcom.sun.management.jmxremote.ssl=false"

export JAVA_OPTS="$JAVA_OPTS $GC_OPTS $JMX_OPTS"

Security Configuration

// Security configuration for application server
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeRequests(authz -> authz
                .requestMatchers("/health/**", "/metrics/**").permitAll()
                .requestMatchers("/admin/**").hasRole("ADMIN")
                .requestMatchers("/api/**").hasRole("USER")
                .anyRequest().authenticated()
            )
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .maximumSessions(1)
                .maxSessionsPreventsLogin(false)
            )
            .csrf().disable()
            .headers(headers -> headers
                .frameOptions().deny()
                .contentTypeOptions().and()
                .httpStrictTransportSecurity(hstsConfig -> hstsConfig
                    .maxAgeInSeconds(31536000)
                    .includeSubdomains(true)
                )
            );
        
        return http.build();
    }
    
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(12);
    }
}

Logging Configuration

<!-- logback-spring.xml - Logging configuration -->
<configuration>
  
  <springProfile name="!production">
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
      <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
      </encoder>
    </appender>
    
    <root level="DEBUG">
      <appender-ref ref="CONSOLE"/>
    </root>
  </springProfile>
  
  <springProfile name="production">
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>logs/application.log</file>
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>logs/application.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
        <maxFileSize>100MB</maxFileSize>
        <maxHistory>30</maxHistory>
        <totalSizeCap>3GB</totalSizeCap>
      </rollingPolicy>
      <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
      </encoder>
    </appender>
    
    <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>logs/error.log</file>
      <filter class="ch.qos.logback.classic.filter.LevelFilter">
        <level>ERROR</level>
        <onMatch>ACCEPT</onMatch>
        <onMismatch>DENY</onMismatch>
      </filter>
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
        <fileNamePattern>logs/error.%d{yyyy-MM-dd}.log</fileNamePattern>
        <maxHistory>30</maxHistory>
      </rollingPolicy>
      <encoder>
        <pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
      </encoder>
    </appender>
    
    <root level="WARN">
      <appender-ref ref="FILE"/>
      <appender-ref ref="ERROR_FILE"/>
    </root>
  </springProfile>
  
  <!-- Application-specific loggers -->
  <logger name="com.example" level="INFO"/>
  <logger name="org.springframework.security" level="DEBUG"/>
  <logger name="org.hibernate.SQL" level="DEBUG"/>
  <logger name="org.hibernate.type.descriptor.sql.BasicBinder" level="TRACE"/>
  
</configuration>

Summary

Java Application Servers provide essential infrastructure for enterprise applications:

Key Capabilities:

  • Web container for servlets and JSPs
  • EJB container for enterprise beans
  • Resource management (datasources, JMS)
  • Transaction management
  • Security services
  • Clustering and load balancing

Popular Servers:

  • Tomcat: Lightweight servlet container
  • WildFly: Full Java EE server with modern features
  • WebLogic: Enterprise-grade server with advanced tools

Best Practices:

  • Use environment-specific configurations
  • Implement proper monitoring and health checks
  • Configure appropriate security measures
  • Optimize JVM settings for performance
  • Set up proper logging and error handling
  • Plan for scalability and clustering

Deployment Considerations:

  • Automate deployment processes
  • Use blue-green or rolling deployments
  • Implement proper backup and rollback strategies
  • Monitor application performance and resources
  • Set up proper alerting and notifications

Application servers continue to be crucial for enterprise Java applications, providing the foundation for scalable, secure, and manageable applications in production environments.