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
- Apache Tomcat
- WildFly (JBoss)
- Oracle WebLogic
- Deployment Strategies
- Configuration Management
- Monitoring and Management
- Best Practices
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 "%r" %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.