DEV Community

Yogeth.C.R
Yogeth.C.R

Posted on

Automating Java Servlet Deployments with Gradle and Bash

Step 1: Configuring Gradle for Web Archives (.war)

To create standard web archives, ensure the war plugin is applied in build.gradle. Additionally, we will configure a predictable output directory and file name so our automation script always knows where to locate the generated binary.

Add the following to build.gradle:

plugins {
    id 'java'
    id 'war'
}

group = 'com.demo'
version = '1.0.0'

repositories {
    mavenCentral()
}

dependencies {
    // Jakarta Servlet API (or javax.servlet for older Tomcat versions)
    providedCompile 'jakarta.servlet:jakarta.servlet-api:6.0.0'
}

// Ensure predictable archive naming and destination
war {
    archiveFileName = 'app.war'
    destinationDirectory = file("$buildDir/dist")
}
Enter fullscreen mode Exit fullscreen mode

By explicitly setting archiveFileName = 'app.war' and directing it to $buildDir/dist, we prevent dynamic version numbers (e.g., app-1.0.0.war) from breaking our downstream bash script.

Test the build configuration:

./gradlew war
Enter fullscreen mode Exit fullscreen mode

You should see your compiled package at build/dist/app.war.


Step 2: Creating the Deployment Script (deploy.sh)

Now, we will write a Bash script to orchestrate the deployment workflow:

  1. Validate and build the .war package.
  2. Stop the local Tomcat instance safely.
  3. Clean old deployments from Tomcat's webapps/ directory.
  4. Copy the new .war file.
  5. Restart Tomcat and display process logs.

Create deploy.sh in your project root:

#!/usr/bin/env bash

# Exit immediately if a command exits with a non-zero status
set -e

# Configuration Paths - Adjust TOMCAT_HOME to your installation path
TOMCAT_HOME="${TOMCAT_HOME:-/opt/tomcat}"
WAR_SOURCE="build/dist/app.war"
DEPLOY_DIR="$TOMCAT_HOME/webapps"
APP_NAME="app"

# Terminal Formatting Colors
GREEN=' [0;32m'
BLUE=' [0;34m'
RED=' [0;31m'
NC=' [0m' # No Color

echo -e "${BLUE}[1/5] Building project archive with Gradle...${NC}"
./gradlew war

if [ ! -f "$WAR_SOURCE" ]; then
    echo -e "${RED}[ERROR] Archive build failed. $WAR_SOURCE not found.${NC}"
    exit 1
fi

echo -e "${BLUE}[2/5] Stopping Tomcat...${NC}"
if [ -f "$TOMCAT_HOME/bin/shutdown.sh" ]; then
    "$TOMCAT_HOME/bin/shutdown.sh" || true
fi

echo -e "${BLUE}[3/5] Cleaning previous deployments...${NC}"
rm -rf "$DEPLOY_DIR/$APP_NAME"
rm -f "$DEPLOY_DIR/$APP_NAME.war"

echo -e "${BLUE}[4/5] Deploying new WAR file...${NC}"
cp "$WAR_SOURCE" "$DEPLOY_DIR/$APP_NAME.war"

echo -e "${BLUE}[5/5] Starting Tomcat...${NC}"
"$TOMCAT_HOME/bin/startup.sh"

echo -e "${GREEN} Successfully deployed $APP_NAME.war to Tomcat!${NC}"
echo -e "Access application at: http://localhost:8080/$APP_NAME/"
Enter fullscreen mode Exit fullscreen mode

Make the script executable:

chmod +x deploy.sh
Enter fullscreen mode Exit fullscreen mode

Step 3: Running and Verifying the Pipeline

Execute your single-command deployment:

./deploy.sh
Enter fullscreen mode Exit fullscreen mode

Script Execution Flow

  1. Gradle compiles class files and packages build/dist/app.war.
  2. Tomcat is safely stopped to release file locks.
  3. Legacy context folders and stale .war files are removed from webapps/.
  4. The fresh build is placed into webapps/app.war.
  5. Tomcat restarts and auto-expands the package.

Verify by running:

curl -I http://localhost:8080/app/
Enter fullscreen mode Exit fullscreen mode

Key Takeaways & Conclusion

  • Predictable Outputs: Setting deterministic output filenames in build.gradle simplifies automation scripts.
  • Idempotency: Cleaning stale files (rm -rf) before deploying prevents Tomcat from serving stale code or holding file locks.
  • Portability: This Bash pattern can easily be modified for remote servers over SSH or integrated into CI/CD pipelines like GitHub Actions or Jenkins.

Did you find this approach helpful? Check out my open-source projects on GitHub or connect with me on DEV Community.

Top comments (0)