Maven & Deployment in Tomcat

Table of contents

1. Introduction to Maven 🚀

What is Maven? 🤔

Maven is a powerful, widely-used project management and comprehension tool in the Java ecosystem. The name "Maven" means "accumulator of knowledge." Maven simplifies the entire build process by providing a standardized structure and a clear lifecycle for project development. It helps developers manage dependencies, execute tests, package code, and deploy applications with minimal configuration. While it’s primarily designed for Java, Maven can be used with other programming languages as well.

Summary of Maven 📚

  • Build Automation 🔄: Maven automates the build process, including compiling the code, running tests, packaging it into a distributable format like a JAR file, and deploying the code to a server or repository. Automation ensures consistency across environments and reduces the likelihood of human error.

  • Dependency Management 📦: One of Maven’s key strengths is its ability to manage project dependencies. Maven automatically downloads required libraries from a central repository (like Maven Central) and includes them in your project, resolving version conflicts and handling transitive dependencies (dependencies of dependencies).

  • Project Management 🛠️: Maven uses a Project Object Model (POM) file (pom.xml) to define the project structure, dependencies, plugins, and other configuration details. The POM file is the core of a Maven project, making it easier to manage projects consistently across different teams and environments.

  • Convention Over Configuration 📐: Maven encourages a standardized project structure (e.g., src/main/java for source code, src/test/java for tests). This convention reduces the need for custom configuration, which speeds up the development process and ensures consistency across projects.

Use Cases of Maven 🛠️

  • Java Project Builds 🧱: Maven is primarily used for building Java applications. It compiles the source code, packages it into a JAR/WAR/EAR, and manages dependencies, all with simple commands.

  • Dependency Management 📦: In complex projects with multiple dependencies, Maven handles the inclusion of external libraries (like Spring, Hibernate) by automatically downloading them from repositories, ensuring your project has everything it needs.

  • Continuous Integration (CI) 🤖: Maven integrates seamlessly with CI/CD pipelines, allowing automated builds and deployments. Tools like Jenkins, GitLab CI, and CircleCI often use Maven to build and test Java projects.

Maven as a Build Tool for DevOps 🔧

Maven is an essential part of the DevOps toolkit, particularly in Java environments:

  • Automating Repetitive Tasks 🔁: aven scripts (mvn install, mvn deploy) can be run automatically by CI/CD tools like Jenkins, Bamboo, or GitLab CI to build, test, and deploy code whenever changes are pushed to the repository.

  • Integrating with CI/CD Pipelines ⚙️: Maven’s lifecycle and plugins can be easily integrated into CI/CD pipelines, enabling automated builds, testing, and deployment, which accelerates the software development lifecycle.

  • Supporting Multi-Module Projects 🗂️: Maven efficiently manages projects with multiple modules (e.g., a large enterprise application with separate modules for web, service, and persistence layers). Each module can have its own POM file, and Maven can build and manage these modules as part of a larger project.

Best Practices for Using Maven 🏅

  1. Use a Consistent Directory Structure 📁: Stick to Maven’s standard directory layout (src/main/java, src/test/java, etc.) to minimize configuration and make it easier for new team members to understand the project structure.

  2. Define Dependencies Clearly 📦: Only include necessary dependencies in your pom.xml to keep the project lightweight. Avoid unnecessary dependencies that could bloat the project and increase the build time.

  3. Keep the POM File Clean and Organized 📝: Comment your pom.xml where necessary, and group related sections together. For example, keep all dependency declarations in one section, and plugin configurations in another.

  4. Use a Repository Manager 🌐: Consider using a repository manager like Nexus or Artifactory to store and manage your build artifacts. This adds a layer of security and control over what dependencies are being used in your builds.

  5. Regularly Update Dependencies 🔄: Stay up to date with the latest versions of your dependencies to benefit from security patches, new features, and performance improvements. Use tools like the Maven Versions plugin to manage this process.

  6. Use Profiles for Environment-Specific Configurations 🌍: Profiles in Maven allow you to customize the build process for different environments (e.g., development, testing, production).

  7. Leverage Plugins ⬆️: Use Maven plugins to extend functionality, such as maven-compiler-plugin for compiling code, maven-surefire-plugin for running tests, and maven-jar-plugin for creating JAR files.

Advantages of Maven 🌟

  • Standardization 🧩: Maven’s conventions provide a standardized way to build and manage projects, making it easier to work across different projects or teams.
  • Ease of Use 🛠️: Once you’re familiar with Maven’s conventions, you can set up new projects and manage dependencies with minimal effort.
  • Extensibility 🔌: Maven’s plugin system allows it to be extended with custom build logic. There are thousands of plugins available for various tasks, from code analysis to deployment.
  • Comprehensive Documentation 📚: Maven has extensive documentation and a large community, making it easy to find help or guidance when needed.

Disadvantages of Maven ⚠️

  • Steep Learning Curve 📈: Maven can be complex for beginners due to its vast array of features and the requirement to understand concepts like POM, lifecycle phases, and dependency scopes.
  • Dependency Conflicts ⚠️: Managing transitive dependencies can be challenging, especially when different libraries require different versions of the same dependency.
  • Slower Builds ⏳: Maven’s comprehensive build process can be slower compared to other tools, particularly when dealing with large projects or extensive dependencies.

2. Maven Lifecycle and Its Stages 🔄

Maven's lifecycle defines the steps that occur when building a project. Each phase in the lifecycle represents a stage in the build process, from validating the project configuration to deploying the final build.

Overview of the Maven Lifecycle 📜

Maven’s build lifecycle consists of three main lifecycles:

  1. Default Lifecycle 🛠️: Handles project deployment, including compile, test, package, and install phases.
  2. Clean Lifecycle 🧹: Handles project cleaning, including removing previous builds.
  3. Site Lifecycle 🌐: Handles the creation of the project's site documentation.

Within the Default Lifecycle, the most commonly used phases are:

  1. validate 🛠️: Validates that the project is correct and all necessary information is available.

    • Example Output:
      [INFO] Scanning for projects...
      [INFO] ----------------------< com.example:my-app >-----------------------
      [INFO] Building my-app 1.0-SNAPSHOT
      [INFO] --------------------------------[ jar ]---------------------------------
      [INFO] 
      [INFO] --- maven-validate-plugin:1.0.0:validate (default) @ my-app ---
      [INFO] 
      [INFO] BUILD SUCCESS
      
  2. compile 🧩: Compiles the source code of the project.

    • Example Output:
      [INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ my-app ---
      [INFO] Changes detected - recompiling the module!
      [INFO] Compiling 10 source files to /target/classes
      [INFO] 
      [INFO] BUILD SUCCESS
      
  3. test 🧪: Runs tests using a testing framework like JUnit.

    • Example Output:
      [INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ my-app ---
      [INFO] Surefire report directory: /target/surefire-reports
      [INFO] 
      [INFO] -------------------------------------------------------
      [INFO]  T E S T S
      [INFO] -------------------------------------------------------
      [INFO] Running com.example.MyAppTest
      [INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.123 s
      [INFO] 
      [INFO] BUILD SUCCESS
      
  4. package 📦: Packages the compiled code into a distributable format, such as a JAR file.

    • Example Output:
      [INFO] --- maven-jar-plugin:3.2.0:jar (default-jar) @ my-app ---
      [INFO] Building jar: /target/my-app-1.0-SNAPSHOT.jar
      [INFO] 
      [INFO] BUILD SUCCESS
      
  5. verify ✔️: Runs checks to ensure the package is valid and meets quality standards.

    • Example Output:
      [INFO] --- maven-verifier-plugin:1.1:verify (default-verify) @ my-app ---
      [INFO] Verifying the project artifacts...
      [INFO] 
      [INFO] BUILD SUCCESS
      
  6. install 💾: Installs the package into the local repository, making it available for other projects on the same machine.

    • Example Output:
      [INFO] --- maven-install-plugin:2.5.2:install (default-install) @ my-app ---
      [INFO] Installing /target/my-app-1.0-SNAPSHOT.jar to ~/.m2/repository/com/example/my-app/1.0-SNAPSHOT/my-app-1.0-SNAPSHOT.jar
      [INFO] 
      [INFO] BUILD SUCCESS
      
  7. deploy 🌐: Copies the final package to a remote repository for sharing with other developers or projects.

    • Example Output:
      [INFO] --- maven-deploy-plugin:2.8.2:deploy (default-deploy) @ my-app ---
      [INFO] Uploading: https://repository.example.com/releases/com/example/my-app/1.0-SNAPSHOT/my-app-1.0-SNAPSHOT.jar
      [INFO] 
      [INFO] BUILD SUCCESS
      

Dependency Between Lifecycle Stages 🔗

Certain lifecycle stages inherently include the execution of previous stages:

  • package 📦: When you run mvn package, Maven first runs validate, compile, and test stages before packaging the code.
  • install 💾: The mvn install command runs all prior stages (validate, compile, test, package, and verify) before installing the artifact to the local repository.
  • deploy 🌐: Running mvn deploy executes all the preceding phases (validate, compile, test, package, verify, install) to ensure the artifact is correctly built and tested before being deployed.

Example Maven Command Execution ⚙️

Let’s run through a simple example of the mvn package command and see what happens.

mvn package

Expected Output 🖥️

  1. validate: Maven checks that the project structure is correct.
    [INFO] --- maven-enforcer-plugin:1.4.1:enforce (enforce-maven) @ my-app ---
    [INFO] Skipping Rule Enforcement.
    
  2. compile: The source code is compiled.
    [INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ my-app ---
    [INFO] Changes detected - recompiling the module!
    [INFO] Compiling 5 source files to /path/to/project/target/classes
    
  3. test: The tests are run.
    [INFO] --- maven-surefire-plugin:2.22.2:test (default-test) @ my-app ---
    [INFO] Surefire report directory: /path/to/project/target/surefire-reports
    [INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.345 s - in TestSuite
    
  4. package: The compiled code is packaged into a JAR file.
    [INFO] --- maven-jar-plugin:3.2.0:jar (default-jar) @ my-app ---
    [INFO] Building jar: /path/to/project/target/my-app-1.0-SNAPSHOT.jar
    

3. Maven Artifacts 📦

Maven builds artifacts as the output of its build lifecycle. An artifact is a file, usually a JAR (Java ARchive), WAR (Web Application Archive), or EAR (Enterprise Archive), that is produced by Maven after executing its lifecycle stages. These artifacts can be shared with other developers, deployed to servers, or published to a repository for other projects to use.

How Maven Creates Artifacts 🛠️

  1. Compilation 🧩: Maven first compiles the source code into bytecode that can be run on the Java Virtual Machine (JVM). This happens during the compile phase.

  2. Packaging 📦: In the package phase, Maven takes the compiled code, along with any resources like configuration files or libraries, and packages them into a JAR, WAR, or EAR file. This file is the final product of the build process.

  3. Installation and Deployment 💾🌐: After packaging, Maven can install the artifact into the local repository (~/.m2/repository) using the install phase or deploy it to a remote repository using the deploy phase. This makes the artifact available for use by other projects or for deployment to a server.

Types of Artifacts Maven Creates 🗂️

  1. JAR (Java ARchive) 📁:

    • Definition: A JAR file is the most common type of artifact. It contains compiled Java classes, metadata, and resources like images and text files. It's used for Java applications or libraries.
    • Usage: JAR files are used when you want to distribute a library or a standalone application.
    • Example Output:
      [INFO] Building jar: /target/my-app-1.0-SNAPSHOT.jar
      
  2. WAR (Web Application Archive) 🌐:

    • Definition: A WAR file is used for web applications. It contains the web components like JSP files, servlets, HTML pages, JavaScript, and other resources necessary for a web application.
    • Usage: WAR files are deployed to web servers like Apache Tomcat, JBoss, or GlassFish.
    • Example Output:
      [INFO] Building war: /target/my-web-app-1.0-SNAPSHOT.war
      
  3. EAR (Enterprise Archive) 🏢:

    • Definition: An EAR file is used for large, enterprise-level applications. It can contain multiple JAR and WAR files along with other resources like XML configuration files.
    • Usage: EAR files are deployed to enterprise servers like IBM WebSphere or Oracle WebLogic.
    • Example Output:
      [INFO] Building ear: /target/my-enterprise-app-1.0-SNAPSHOT.ear
      

Why Multiple Lines of Code Are Packaged into Single Artifacts 🧳

Packaging multiple lines of code and resources into a single artifact has several benefits:

  • Simplicity 🎯: Simplifies deployment by bundling all necessary files into a single archive.
  • Versioning 🗓️: Ensures a consistent version of the application or library is distributed and deployed.
  • Dependency Management 📦: Makes it easier to manage dependencies by packaging all required libraries and resources into one artifact.
  • Security 🔒: Protects the code by compiling it into bytecode, which is more challenging to reverse-engineer than raw source code.

Advantages and Disadvantages of Packaging as a Single Artifact ⚖️

Advantages:

  • Easier Deployment 🚀: A single file is easier to move, copy, and deploy across environments.
  • Consistency 🛡️: Ensures all code, resources, and dependencies are consistent across different deployments.
  • Security 🔐: Protects the source code from being directly viewed or modified, as the code is compiled into bytecode and packaged.

Disadvantages:

  • Size 📏: Artifacts can become large, particularly EAR files that contain multiple modules, increasing storage and transfer times.
  • Lack of Modularity 🧩: A single artifact means that all components are deployed together, even if only a small part of the application has changed, potentially slowing down the deployment process.

Example: Compiling Source Code and Required Plugins into an Artifact 🛠️

Let’s say you have a Maven project for a simple Java application. Here’s a step-by-step process on how Maven compiles and packages the code into a JAR file.

  1. Source Code Structure 📂:

    • src/main/java/com/example/MyApp.java: The main application file.
    • src/main/resources/config.properties: A configuration file.
  2. Maven POM (pom.xml) Configuration 📑:

    • The POM file includes dependencies and plugins required to build and package the application.
      <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.example</groupId>
        <artifactId>my-app</artifactId>
        <version>1.0-SNAPSHOT</version>
        <dependencies>
            <!-- Example dependency -->
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13.1</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
        <build>
            <plugins>
                <!-- Compiler plugin -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.1</version>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
      </project>
      
  3. Running Maven Commands 🏃‍♂️:

    • Compile: Run mvn compile to compile the Java source files.

      mvn compile
      
    • Output:

      [INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ my-app ---
      [INFO] Changes detected - recompiling the module!
      [INFO] Compiling 1 source file to /target/classes
      [INFO] 
      [INFO] BUILD SUCCESS
      
    • Package: Run mvn package to create a JAR file containing the compiled code and resources.

      mvn package
      
    • Output:

      [INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ my-app ---
      [INFO] Copying 1 resource
      [INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ my-app ---
      [INFO] Changes detected - recompiling the module!
      [INFO] Compiling 1 source file to /target/classes
      [INFO] --- maven-jar-plugin:3.2.0:jar (default-jar) @ my-app ---
      [INFO] Building jar: /target/my-app-1.0-SNAPSHOT.jar
      [INFO] 
      [INFO] BUILD SUCCESS
      

The final artifact, my-app-1.0-SNAPSHOT.jar, includes compiled Java classes and the config.properties file, packaged into a single JAR file in the target directory ready for deployment or sharing.


4. Introduction to pom.xml 📝

The pom.xml file (Project Object Model) is the heart of any Maven project. It is an XML file that contains information about the project and configuration details used by Maven to build the project.

Why pom.xml is Needed for Maven 🔍

  • Central Configuration 🔧: pom.xml is the central place for managing all the configurations required to build a project. It defines project dependencies, plugins, goals, and other settings in a structured manner.
  • Plugins / Dependency Management 📦: Specifies all dependencies needed to build, test, and run the project. Maven uses this file to download the necessary libraries from the central or local repository.
  • Build Customization 🛠️: Allows customization of the build process through plugins and profiles, which can be defined directly in pom.xml.
  • Versioning and Compatibility 📜: Helps maintain consistency and compatibility between different project modules or dependencies by specifying explicit versions.

Structure of pom.xml 📋

The structure of a pom.xml file is hierarchical and contains several sections, each serving a specific purpose:

  1. Model Version 🧩: Specifies the version of the POM model. Always set to 4.0.0 for compatibility with Maven 2 and later.

    <modelVersion>4.0.0</modelVersion>
    
  2. Group ID 🏷️: Defines the group or organization that the project belongs to. This is usually a reversed domain name (e.g., com.example).

    <groupId>com.example</groupId>
    
  3. Artifact ID 🆔: Specifies the unique identifier for the project or module within the group.

    <artifactId>my-app</artifactId>
    
  4. Version 📅: Indicates the version of the artifact. Typically includes a version number and can include labels like SNAPSHOT for development versions.

    <version>1.0-SNAPSHOT</version>
    
  5. Dependencies 📦: Lists all external libraries that the project depends on. Maven will download these from the specified repositories.

    <dependencies>
      <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.1</version>
        <scope>test</scope>
      </dependency>
    </dependencies>
    
  6. Build Configuration 🛠️: Configures the build process, including specifying plugins and their configurations.

    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.1</version>
          <configuration>
            <source>1.8</source>
            <target>1.8</target>
          </configuration>
        </plugin>
      </plugins>
    </build>
    
  7. Repositories 🌐: Defines remote repositories where Maven can find artifacts. Maven Central is the default, but you can add others if needed.

    <repositories>
      <repository>
        <id>central</id>
        <url>https://repo.maven.apache.org/maven2</url>
      </repository>
    </repositories>
    
  8. Plugins 🔌: Configures plugins that extend Maven’s capabilities, such as compiling code, running tests, and packaging artifacts.

    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.2.0</version>
          <configuration>
            <archive>
              <manifestEntries>
                <Built-By>${user.name}</Built-By>
                <Built-Date>${maven.build.timestamp}</Built-Date>
              </manifestEntries>
            </archive>
          </configuration>
        </plugin>
      </plugins>
    </build>
    

Maven Central and Maven Repository 🌐

  • Maven Central: The primary repository where most public Maven artifacts are stored. When a dependency is declared in the pom.xml, Maven first checks the local repository, then Maven Central. It is a large collection of build artifacts available for public use.

    • URL: https://repo.maven.apache.org/maven2
  • Maven Repository: Can be local (on your machine), remote (on a server), or central (like Maven Central). Maven searches these repositories in sequence to resolve dependencies.

    • Local Repository: Stores artifacts downloaded from remote repositories or built locally. Typically located at ~/.m2/repository.
    • Remote Repository: A remote server where artifacts can be stored and shared, often used in team environments or for distributing software.

Adding Dependencies from Maven Central 📥

To add a dependency from Maven Central:

  1. Find the Dependency 🔍: Search for the required dependency on Maven Central or a similar repository to get its groupId, artifactId, and version.

  2. Add to pom.xml 📑: Include the dependency information in the <dependencies> section of your pom.xml file.

    <dependencies>
      <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.5.4</version>
      </dependency>
    </dependencies>
    
  3. Maven Resolves the Dependency 🛠️: When you build your project, Maven will download the specified dependency and its transitive dependencies from Maven Central and add them to your project’s classpath.

      mvn compile
    
    • Example Output:
      [INFO] Scanning for projects...
      [INFO] 
      [INFO] --- maven-dependency-plugin:3.1.2:resolve (default-cli) @ my-app ---
      [INFO] Resolving dependencies...
      [INFO] Resolving org.springframework.boot:spring-boot-starter-web:jar:2.5.4
      [INFO] Resolving org.springframework.boot:spring-boot-starter-tomcat:jar:2.5.4
      [INFO] 
      [INFO] BUILD SUCCESS
      

5. Standard Java Directory Structure & Files 📂

The Java directory structure is a standardized format that developers follow to organize their Java projects. This structure helps maintain a clean and predictable organization of files, which is crucial for building and deploying Java applications using tools like Maven.

A typical Java project follows a standard directory structure to keep the source code, resources, and configurations organized. This structure is essential for the project to be correctly processed by build tools like Maven.

Here's a typical directory structure for a Maven-based Java project:

my-java-project/
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/
│   │   │       └── example/
│   │   │           └── MyApp.java
│   │   └── resources/
│   │       └── application.properties
│   └── test/
│       ├── java/
│       │   └── com/
│       │       └── example/
│       │           └── MyAppTest.java
│       └── resources/
├── target/
├── pom.xml
├── README.md
  1. Root Directory 📁

    • The root directory of your project typically contains the pom.xml file and other configuration files like .gitignore.
    • Example:
      my-java-project/
      ├── pom.xml
      ├── src/
      ├── target/
      └── README.md
      
  2. src Directory 🗂️

    • This is the main directory where all the source code and resources are placed.
    • It typically has two subdirectories: main and test.

    • src/main/ Directory 📦

      • Purpose: Contains the application’s main code and resources.

      • Structure:

        src/
        ├── main/
        │   ├── java/
        │   │   └── com/
        │   │       └── example/
        │   │           └── MyApp.java
        │   └── resources/
        │       └── application.properties
        
      • Subfolders:

        • src/main/java/ ☕:

          • Purpose: The directory where all Java source code resides. This is where your .java files (classes, interfaces, enums, etc.) are stored.
          • Structure: Organized by package names (e.g., com/ example). Each package typically represents a logical grouping of related classes.
          • Example: com/example/MyApp.java is where the main application code resides.
        • src/main/resources/ 📜:

          • Purpose: This folder contains non-Java files, such as configuration files, property files, XML files, or any other resources needed by the application.
          • Structure: Files are usually placed directly under the resources directory or within subfolders for better organization.
          • Example: application.properties is a common configuration file for Java applications, particularly when using frameworks like Spring Boot.
        • src/main/webapp/ 🌐: (For web applications) Contains web-related files like JSP files, HTML, CSS, JavaScript, and other web resources.

  • src/test/ Directory 🧪

    • Purpose: Contains the test code and resources.
    • Structure:
      src/
      ├── test/
      ├   ├── java/
      ├   │   └── com/
      ├   │       └── example/
      ├   │           └── MyAppTest.java
      ├   └── resources/
      
    • Subfolders:

      • src/test/java/ ☕:

        • Purpose: Contains the Java source code for test cases. These test cases are used to validate the functionality of the application.
        • Structure: Follows the same package structure as src/main/java to ensure that tests are easily linked to the corresponding source files.
        • Example: com/example/MyAppTest.java might contain unit tests for MyApp.java.
      • src/test/resources/ 📜:

        • Purpose: Stores resources required for testing, such as test configuration files, mock data, or SQL scripts.
        • Structure: Similar to src/main/resources, files can be organized directly under the directory or in subfolders.
        • Example: A test.properties file used for configuring test-specific settings.
  1. target Directory 🎯

    • Purpose: This directory is generated by Maven during the build process. It contains all the compiled classes, packaged artifacts (e.g., JAR, WAR), and any other output generated by Maven.

    • Structure:

      target/
      ├── classes/
      ├── my-app-1.0-SNAPSHOT.jar
      └── test-classes/
      
      • Subfolders:
        • target/classes/ 📚: Contains compiled .class file for the main application.
        • target/test-classes/ 📚: Contains compiled `.class files for test cases.
        • target/[artifact-name].jar 📦: The packaged JAR file or other build artifacts.
  2. pom.xml 📜: The Maven configuration file that describes the project, its dependencies, plugins, and build instructions.

  3. README.md 📄: A markdown file explaining the MyApp application & its related data.

Directories Needed for Apache Maven or Its Plugins 🚀

For Maven to work correctly, it expects the source files to be in the correct directories:

  • src/main/java/ ☕: Maven looks here for all the Java source files to compile to generate the bytecode in the target/classes/ directory.
  • src/main/resources/ 📜: Maven includes all files in this directory in the final artifact. This is crucial for configuration files like application.properties.
  • src/test/java/ ☕: Maven looks here for test code to execute during the test phase.
  • src/test/resources/ 📜: Any test-specific resources are expected to be here, and Maven will include them in the test classpath.
  • target 📦: Created by Maven during the build process to store compiled classes and packaged artifacts.
  • pom.xml 📜: Mandatory for defining the Maven build configuration, dependencies, and plugins.

These directories are crucial because Maven's default conventions depend on this structure. If the structure is not followed, Maven will require additional configuration to locate and compile the files.

application.properties File 📑

The application.properties file is typically located in the src/main/resources/ directory. It’s a key configuration file in Java applications, especially those using frameworks like Spring Boot.

  • Purpose: This file contains application-specific properties such as database configurations, server ports, and other settings. It allows externalizing the configuration from the code, making the application more flexible and easier to manage in different environments.

  • Structure: The file consists of key-value pairs, each representing a specific configuration setting. The format is straightforward and easy to read.

  • Example Content:

    # Server configuration
    server.port=8080
    
    # Database configuration
    spring.datasource.url=jdbc:mysql://localhost:3306/mydb
    spring.datasource.username=root
    spring.datasource.password=password
    
    # Logging configuration
    logging.level.org.springframework=INFO
    
  • Usage:

    • Configuring Application Behavior: Developers use application.properties to customize the behavior of their application based on different environments (development, testing, production).
    • Externalizing Configuration: By keeping settings in an external file, changes can be made without modifying the source code, allowing for more flexible deployments.

6. Installation Requirements: JDK, JRE, JVM ☕️

Before installing Maven, you need to ensure that the Java Development Kit (JDK), Java Runtime Environment (JRE), and Java Virtual Machine (JVM) are properly installed. These components are essential for running Java applications and building Java projects using Maven.

JDK, JRE, and JVM: What Are They? 🤔

  1. JDK (Java Development Kit) 🧰

    • Purpose: The JDK is a software development environment used for developing Java applications. It includes tools for compiling, debugging, and running Java programs.
    • Why Install: Developers need the JDK to write and compile Java programs. It provides all the necessary tools and libraries required for Java development.
    • Components:
      • Java Compiler (javac): Converts Java source code into bytecode.
      • JRE: Included within the JDK for running Java programs.
      • Development Tools: Includes tools like javadoc, javap, etc.
  2. JRE (Java Runtime Environment) 🏃‍♂️

    • Purpose: The JRE is a software package that provides the libraries and other components necessary to run Java applications. It does not include development tools like compilers or debuggers.
    • Why Install: The JRE is essential for running Java applications on end-user systems. It allows users to execute Java programs without needing the full JDK.
    • Components:
      • JVM: The core part of the JRE that executes the Java bytecode.
      • Libraries: A set of class libraries that Java applications rely on.
  3. JVM (Java Virtual Machine) 💻

    • Purpose: The JVM is the virtual machine that runs Java bytecode. It is platform-independent, meaning the same Java code can run on any device with a compatible JVM.
    • Why Install: Integral to both the JDK and JRE, the JVM allows Java applications to be platform-independent. Developers rely on the JVM to ensure that Java applications can run anywhere.
    • Components:
      • Class Loader: Loads Java classes into memory.
      • Bytecode Verifier: Ensures the bytecode is valid and safe to execute.
      • Interpreter/Just-In-Time Compiler: Executes the bytecode, translating it into machine code for the host system.

Software Structure for JDK, JRE, JVM in Tree Format 🌳

Here’s a simplified tree structure that outlines how these components are organized:

JDK (Java Development Kit)
│
├── bin/ (Binaries)
│   ├── javac    (Java compiler)
│   ├── java     (Java interpreter)
│   └── other tools (javadoc, jdb, etc.)
│
├── jre/ (Java Runtime Environment)
│   ├── bin/
│   │   └── java (JVM executable)
│   └── lib/
│       ├── rt.jar (Runtime libraries)
│       └── other runtime libraries
│
└── lib/ (Development Libraries)
    ├── tools.jar (Tools libraries)
    └── other development libraries
  • JDK: Contains everything needed for Java development, including the JRE, compiler, and other tools.
  • JRE: A subset of the JDK that includes just the JVM and libraries needed to run Java applications.
  • JVM: Part of the JRE, responsible for executing Java bytecode.

7. JDK Version Compatibility 🔄

When using Maven for building Java projects, selecting the correct version of the JDK is crucial. Maven relies on the JDK to compile and package Java applications, and not all JDK versions are compatible with every project or environment.

Why Only Selected Versions of JDK Work for Maven Packaging 🧐

  • Java Version Compatibility 📋: Maven projects are configured to use a specific version of Java. This is set in the pom.xml file using the maven-compiler-plugin. If the installed JDK does not match the version specified in the pom.xml, the build may fail.

  • Compiler Differences 🧮: Different JDK versions have different compilers that might produce incompatible bytecode or have different performance characteristics. For example, Java 8 introduced significant changes in the language and compiler, which might not be compatible with earlier versions.

  • Library Dependencies 📚: Some libraries or frameworks used in the project might require a specific JDK version. Using an incompatible version could lead to ClassNotFoundException or NoSuchMethodError.

  • API Changes ⚙️: Different versions of the JDK can introduce or remove APIs, leading to compatibility issues with existing code. For instance, certain deprecated APIs might be removed in newer versions, causing compilation errors.

  • Bytecode Compatibility 📉: The bytecode generated by newer versions of the JDK might not be compatible with older JVMs. This is controlled by the -source and -target options in the maven-compiler-plugin.

  • Language Features 📚: New language features (e.g., lambda expressions in JDK 8, modules in JDK 9) require a specific JDK version. Maven needs to compile your code with the appropriate version that supports these features.

Example of JDK Version Compatibility 🚦

Let’s say your project uses a library that is compatible only with JDK 11. If you try to build this project using JDK 8, you might encounter issues due to changes in the Java language or removed APIs.

  1. Maven Compiler Plugin Configuration in Project’s pom.xml:

    <build>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.1</version>
          <configuration>
            <source>11</source>
            <target>11</target>
          </configuration>
        </plugin>
      </plugins>
    </build>
    
  2. Output When Building with JDK 8:

    • Command: mvn clean install
    • Output:
      [INFO] -------------------------------------------------------------
      [ERROR] COMPILATION ERROR : 
      [INFO] -------------------------------------------------------------
      [ERROR] Source option 11 is not supported. Use 8 or lower.
      [ERROR] Target option 11 is not supported. Use 8 or lower.
      [INFO] 2 errors 
      [INFO] -------------------------------------------------------------
      
  3. Output When Building with JDK 11:

    • Command: mvn clean install
    • Output:
      [INFO] -------------------------------------------------------------
      [INFO] BUILD SUCCESS
      [INFO] -------------------------------------------------------------
      
  4. Solution: In this example, the project fails to build with JDK 8 because it’s configured for JDK 11. However, it builds successfully when the correct JDK version is used. Ensure that the correct JDK version is installed and configured for the project to avoid such compatibility issues.

Difference Between OpenJDK and Oracle JDK ⚖️

  • OpenJDK 🆓: An open-source implementation of the Java Platform, Standard Edition under the GNU General Public License (GPL) with a linking exception. This makes it free to use, modify, and distribute. It’s free to use and has the same codebase as Oracle JDK.
  • Oracle JDK 💼: Oracle’s proprietary implementation of the Java Platform, Standard Edition, built from the OpenJDK project but with additional commercial features.
    • Differences:
      • Licensing: Oracle JDK comes with a commercial license for enterprises, whereas OpenJDK is free under an open-source license.
      • Performance: Oracle JDK might have slight performance optimizations and additional monitoring tools compared to OpenJDK.

8. The .m2 Directory 📁

The .m2 directory is a hidden folder located in the user’s home directory (~/.m2) that Maven uses to store its repository of downloaded dependencies, plugin files, and other build-related information.

What is the .m2 Directory? 🗂️

  • Purpose: The .m2 directory is the default location for Maven’s local repository. It stores all downloaded dependencies, plugins, and cached build information.
  • Location: By default, this directory is located in the user's home directory (~/.m2 on Unix-based systems or C:\Users\<username>\.m2 on Windows).

When is the .m2 Directory Created? 📅

  • The .m2 directory is created automatically the first time Maven is run. Maven will create the necessary subdirectories and configuration files, such as settings.xml and the repository folder. When Maven downloads dependencies or plugins, it caches them in this directory for future use, eliminating the need to re-download them for every build.

Contents of the .m2 Folder 🗄️

  • repository 📦: This is the most crucial subdirectory within .m2. It contains all the downloaded JAR files, libraries, plugins, and other artifacts that Maven retrieves from remote repositories.

    • Structure:
      ~/.m2/
      ├── repository/
      │   ├── com/
      │   │   └── example/
      │   │       └── my-library/
      │   │           └── 1.0/
      │   │               └── my-library-1.0.jar
      │   └── org/
      │       └── apache/
      │       └── commons/
      │           └── commons-lang3/
      │               └── 3.10/
      │                   ├── commons-lang3-3.10.jar
      │                   └── commons-lang3-3.10.pom
      └── settings.xml
      
    • Significance 📥: The repository folder allows Maven to quickly access dependencies that have already been downloaded, significantly speeding up the build process by avoiding repeated downloads.
  • settings.xml ⚙️: A configuration file that allows users to customize Maven's behavior, such as specifying proxy settings, mirror repositories, or different local repository locations.

Where Does Maven Store Its Dependencies? 📦

  • Dependency Storage: Maven stores all dependencies in the ~/.m2/repository folder. Each dependency is stored in a specific path based on its group ID, artifact ID, and version.

    Example:

    • Dependency: org.apache.commons:commons-lang3:3.12.0
    • Stored At: ~/.m2/repository/org/apache/commons/commons-lang3/3.12.0/commons-lang3-3.12.0.jar

Significance of the .m2 Folder for Build Performance 🚀

  • Performance Benefits: By caching all dependencies and plugins locally, the .m2 folder prevents Maven from repeatedly downloading the same files for every build, drastically reducing build times, especially in environments with slow or limited internet access.

    • Example: If a project requires junit:junit:4.13.1, Maven will first check ~/.m2/repository/junit/junit/4.13.1 for the JAR file before attempting to download it from a remote repository.
  • Reduced Build Time: If Maven has already downloaded and stored a dependency in the .m2 directory, it can retrieve it directly from the local repository, speeding up subsequent builds.


9. Where is the Final Artifact Created? 🎁

  • Final Artifact Location: When Maven completes the build process, it places the final artifact (e.g., a JAR or WAR file) in the target directory of the project.

    • Example:

      my-java-project/
      ├── pom.xml
      ├── src/
      ├── target/
      │   ├── my-java-project-1.0-SNAPSHOT.jar
      │   └── ...
      
    • Project Directory: /my-java-project/

    • Artifact: my-java-project-1.0-SNAPSHOT.jar
    • Location: /my-java-project/target/my-java-project-1.0-SNAPSHOT.jar

This target/ directory is where Maven places all output generated during the build process, including compiled classes, packaged archives, and temporary files.

This structure ensures a clean separation between source files, compiled classes, and packaged artifacts, making the Maven build process efficient and organized.


10. Amazon Linux 2023 Environment: Setting Up Java and Maven 🖥️

Setting up a development environment on Amazon Linux 2023 requires careful attention to detail, especially when installing Java and Maven. When setting up Java and Maven in an Amazon Linux 2023 environment, you have two main options for Java Development Kits (JDK): OpenJDK and Oracle JDK.

1. Choosing Between OpenJDK and Oracle JDK ☕️

Java developers can choose between OpenJDK (an open-source implementation of the Java Platform) and Oracle JDK (a commercially supported, proprietary implementation by Oracle) depending on your project's requirements. It's essential to decide which JDK to use based on licensing, support, and specific features required.

Important Note: You should not install both OpenJDK and Oracle JDK simultaneously on the same machine. Doing so can lead to conflicts in the Java runtime environment, causing unexpected behavior in Java applications.

2. Downloading JDKs Using wget 📥

Connect to Your AWS Instance: Log in to your Amazon Linux 2023 instance as the root user or a user with sudo privileges.

Downloading OpenJDK 8 🆓

  1. Open a Terminal Session as Root User:

    • It’s essential to have root privileges to install software in /opt or other system directories.
  2. Navigate to the /opt Directory:

    • The /opt directory is used for installing software packages that are not managed by the system’s package manager. This keeps them isolated from system software and avoids potential conflicts.
      cd /opt
      
  3. Download OpenJDK 8 Using wget:

    • Use the wget command to download the OpenJDK 8 tarball.
    • Use -P /opt: Saves the downloaded file in the /opt directory (optional, if you are in another directory).
      wget https://download.java.net/openjdk/jdk8u44/ri/openjdk-8u44-linux-x64.tar.gz -P /opt
      
    • The above URL is an example; you may need to check for the latest versions or mirrors for OpenJDK 8.
  4. Extract the Downloaded Tarball:

    • Use the tar command to extract the downloaded .tar.gz file.
      tar -xvzf /opt/openjdk-8u44-linux-x64.tar.gz -C /opt
      
    • Use -C /opt: Specifies the directory to extract to (optional, if you are in another directory).

Downloading Oracle JDK 8 💼

  1. Navigate to the /opt Directory:

    cd /opt
    
  2. Download Oracle JDK 8 Using wget:

    • Oracle requires users to accept their license agreement before downloading the JDK. For this, you might need to manually download the file from Oracle's website or use a script with cookies enabled. You can bypass this by using a pre-signed link (only for demonstration purposes).
      wget --no-cookies --no-check-certificate --header "Cookie: oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u131-b11/d54c1d3a095b4ff2b6607d096fa80163/jdk-8u131-linux-x64.tar.gz -P /opt
      
  3. Explanation:

    • --no-cookies and --no-check-certificate: Bypasses restrictions on the Oracle website.
    • --header "Cookie: oraclelicense=accept-securebackup-cookie": Accepts the Oracle license automatically.
  4. Extract the Oracle JDK Tarball:

    tar -xvzf /opt/jdk-8u131-linux-x64.tar.gz -C /opt
    

3. Using yum Package Manager vs. wget 🆚

  • wget for Manual Downloads:

    • Downloads a specific version of JDK directly from the web.
    • Gives you full control over the installation and version management.
    • Suitable when you need a specific version or when managing dependencies manually.
  • yum Package Manager:

    • Uses the default package manager for Red Hat-based systems.
    • Automatically handles dependencies.
    • Downloads the latest available version in the repository, which might not be the version you need.

4. Downloading the Latest Version of Maven Using wget 🌐

  1. Navigate to the /opt Directory:

    cd /opt
    
  2. Download the Latest Maven Version:

    • First, check Maven’s official website for the latest version and download it using wget.
      wget https://dlcdn.apache.org/maven/maven-3/3.8.8/binaries/apache-maven-3.8.8-bin.tar.gz -P /opt
      
  3. Extract the Maven Tarball:

    tar -xvzf /opt/apache-maven-3.8.8-bin.tar.gz -C /opt
    

5. Why Use /opt for External Downloads? 📂

  • Isolation: The /opt directory is designated for installing optional software packages that are not part of the default operating system installation. It’s an ideal location for external software. This keeps them separate from system-managed files in directories like /usr.
  • Control: By installing in /opt, administrators have more control over software versions and configurations, preventing package managers from overwriting custom installations.

6. Verify Installation and Check Versions ✅

  1. Check Java Version:

    • After extracting the JDK, check the installed Java version.
    /opt/openjdk-8u44-linux-x64/bin/java -version  #For OpenJDK
    /opt/jdk1.8.0_131/bin/java -version            #For OracleJDK
    
    • Example Output (For OracleJDK):
      java version "1.8.0_131"
      Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
      Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)
      
  2. Check Maven Version:

    • Verify Maven installation by checking its version (Do this step after configuring the environment variables as mentioned below)
    /opt/apache-maven-3.8.8/bin/mvn -version
    
    • Example Output:
      Apache Maven 3.8.8 (4c87b05d9aedce574290d1acc98575ed5eb6cd39)
      Maven home: /opt/apache-maven-3.8.8
      Java version: 1.8.0_131, vendor: Oracle Corporation, runtime: /opt/jdk1.8.0_131/jre
      Default locale: en, platform encoding: UTF-8
      OS name: "linux", version: "6.1.102-111.182.amzn2023.x86_64", arch: "amd64", family: "unix"
      

7. Configuring Environment Variables 🌐

What is /etc/profile ? 🗒️

  • /etc/profile is a system-wide configuration file used to set environment variables for all users. It’s executed by the shell at login and is ideal for setting up environment variables like PATH, JAVA_HOME, and MAVEN_HOME.
  • This file is ideal for setting up Java and Maven paths because it ensures that all users and processes have access to these settings without needing individual configurations.

What is echo $PATH ? 📢

  • The echo $PATH command displays the current value of the PATH environment variable. The PATH variable tells the shell where to look for executable files when a command is issued.

Adding Environment Variables to /etc/profile ⚙️

  1. Open /etc/profile:

    sudo vi /etc/profile
    
  2. Add the following lines (use any one JDK only) at the end of the file:

    export JAVA_HOME=/opt/openjdk-8u44-linux-x64   #For OpenJDK
    export JAVA_HOME=/opt/jdk1.8.0_131             #For OracleJDK
    export MAVEN_HOME=/opt/apache-maven-3.8.8
    export PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin
    
  3. Explanation:

    • JAVA_HOME and MAVEN_HOME: Specifies the installation directories.
    • M2_HOME: The M2_HOME environment variable points to the Maven installation directory. By adding Maven’s bin directory to PATH, you ensure that Maven commands (mvn) can be run from anywhere on the command line.
    • PATH: Updates the PATH variable to include the Java and Maven bin directories.

Why Use JAVA_HOME and Not Other Parameters? 🧐

  • JAVA_HOME is a standard environment variable used by various Java applications and tools to locate the Java installation directory. Most build tools (like Maven) and IDEs (like Eclipse or IntelliJ) use JAVA_HOME to know where to find the JDK.
  • Alternative Parameters: Other parameters like JRE_HOME or CLASSPATH are less commonly used and might not be recognized by all tools or might not serve the same purpose. JAVA_HOME is the universally recognized standard for pointing to a JDK installation.
  • M2_HOME We can use parameterM2_HOME instead of MAVEN_HOMEwhile exporting the maven path.

Retaining Environment Settings After Reboot 🔄

To ensure that environment settings persist after a session ends or the system restarts, adding them to /etc/profile is effective because this file is executed whenever a login shell starts.

Persisting Settings Across Reboots:

  • Global Changes: Modifications to /etc/profile apply globally to all users and persist across reboots.
  • User-Specific Changes: If you want changes to be user-specific, you can add them to the ~/.bash_profile or ~/.bashrc files in the user's home directory.

To ensure the environment variables are set for all future sessions:

  1. Reload the Profile: Exit the current session & re-login.

  2. Verify the Variables:

    echo $JAVA_HOME
    echo $MAVEN_HOME
    

    You can also verify by entering the Java & Maven commands at any path apart from its bin location,

    java -version  #For JDK
    mvn -version   #For Maven
    

8. What is an Archetype in Maven? 🧩

  • Archetype: A Maven archetype is a template project provided by Maven to help developers quickly set up a new project with a standard directory structure, a basic pom.xml, and other necessary files. This ensures consistency and saves time when starting new projects.

  • Generating a Simple Web App with archetype:generate command 🌐

    • The archetype:generate goal generates a new Maven project based on an archetype.

    • Example Command:

      mvn archetype:generate -DgroupId=com.example -DartifactId=my-webapp -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
      
    • Explanation:

      • archetype:generate: Command to generate a new project from an archetype.
      • -DgroupId, -DartifactId: Specifies the project’s group and artifact IDs.
      • -DarchetypeArtifactId: Specifies the archetype to use (e.g., maven-archetype-webapp for a web application).
      • -DinteractiveMode=false: Runs the command without prompting for input.
  • Linux and Windows: The command is the same for both operating systems when using Maven.

This command initializes a new project in the my-webapp directory, using the maven-archetype-webapp archetype to set up a basic web application.


11. Sample Web Application in Amazon Linux 2023 Environment Deployed in Tomcat 🌐

1. What is Tomcat? 🐱‍💻

Tomcat, also known as Apache Tomcat, is an open-source web server and servlet container that serves as a crucial component in the Java web development ecosystem. It allows developers to deploy and run Java-based web applications, such as Java Servlets and JavaServer Pages (JSPs). Essentially, Tomcat provides an environment for executing Java code in response to web requests, making it a vital tool for hosting and managing Java web applications. 🌐

Why is Tomcat Important? 🤔

  • Open Source: It is freely available and has a strong community supporting it.
  • Java Web Application Hosting: Tomcat is popular for its stability and wide adoption, making it the go-to choice for hosting Java web applications. 💻
  • Integration with Java Frameworks: It integrates seamlessly with Java frameworks like Spring and Hibernate, enabling powerful, scalable web applications. ⚙️
  • Scalability: Tomcat can scale from simple web applications to large enterprise-level applications. 📈
  • Customization and Extensibility: Tomcat is highly customizable, allowing developers to tweak it according to their project needs. 🛠️
  • Lightweight: Compared to full-fledged Java EE application servers like JBoss or WebSphere, Tomcat is lightweight and has a smaller footprint.

In summary, Tomcat acts as a middle layer (middleware) between the web client (browser) and the Java-based server application, handling requests and responses efficiently. 🌟

How it Works:

  1. Client Requests: Receives HTTP / HTTPS requests from clients.
  2. Servlets/JSPs: Processes these requests using servlets and JSPs.
  3. Response: Sends the processed response back to the client.

2. Maven Builds and Deployment in Tomcat 🛠️

Maven is a build automation tool used primarily for Java projects. It manages project builds, dependencies, and documentation. When deploying to Tomcat, Maven can automate the build and deployment process using plugins. 🚀

Maven Build Process:

  1. Project Setup: Maven uses a pom.xml file to manage project dependencies, configurations, and plugins.
  2. Compile Code: Maven compiles Java code using the dependencies listed in pom.xml.
  3. Run Tests: Maven runs any tests to ensure code quality and functionality.
  4. Package Application: Maven packages the compiled code and resources into a WAR (Web Application Archive) file. This WAR file is what gets deployed to Tomcat.

Using Maven to Deploy to Tomcat:

  1. Project Setup: Define your project and dependencies in the pom.xml file.
  2. Build Lifecycle: Maven follows a defined lifecycle to build your project, which includes compiling, testing, packaging, and deploying.
  3. WAR File: For web applications, Maven builds a .war (Web Application Archive) file which contains all the necessary files to deploy a web application.
  4. Deployment Plugin: Use the Maven Tomcat plugin (tomcat-maven-plugin) to deploy the .war file to the Tomcat server.

Example pom.xml Configuration:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/POM/4.0.0">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.example</groupId>
    <artifactId>my-webapp</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <url>http://localhost:8080/manager/text</url>
                    <server>TomcatServer</server>
                    <path>/my-webapp</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
  • <url>: The URL of the Tomcat manager.
  • <server>: The ID of the server credentials defined in settings.xml in the tomcat configuration directory.
  • <path>: The context path where the application will be deployed.

Build the Project: Use Maven to build your project into a WAR (Web Application Archive) file.

  mvn clean package

This command cleans the previous builds and packages the application into a WAR file located in the target directory. 📦

Deploy the WAR: Deploy the WAR file to Tomcat using Maven.

  mvn tomcat7:deploy

Note:

  1. We can run the clean, package & deploy command simultaneously like mvn clean package tomcat7:deploy & it is suggested to package the application before deploying.
  2. Before running the above command we need to configure settings.xml for accessing manager access. In our actual deployment mentioned below we will manually do it, so this process is optional.

    This command uploads the WAR file to Tomcat’s webapps directory and deploys it. 🚀

3. Tomcat Folder Structure and Its Uses 📂

Understanding Tomcat's folder structure is essential for effectively managing and configuring the server. Let's break down the key directories and their purposes. 📁

apache-tomcat-x.x.x/
│
├── bin/               # Contains startup scripts and utility programs.
│   ├── catalina.sh    # Script to start/stop Tomcat (Unix/Linux)
│   ├── catalina.bat   # Script to start/stop Tomcat (Windows)
│   └── tomcat-juli.jar# Utility JAR for logging.
│
├── conf/              # Configuration files for Tomcat.
│   ├── server.xml     # Main configuration file where you define connectors, engines, etc.
│   ├── web.xml        # Global web application configuration.
│   ├── tomcat-users.xml # User roles and permissions.
│   └── context.xml    # Configuration specific to individual applications.
│
├── lib/               # Libraries required by Tomcat itself.
│   ├── catalina.jar   # Core Tomcat classes.
│   ├── tomcat-api.jar # API classes.
│   └── servlet-api.jar # Servlet API.
│
├── logs/              # Log files generated by Tomcat.
│   ├── catalina.out   # Main log file.
│   ├── localhost.log  # Logs specific to the localhost.
│   └── manager.log    # Logs related to the Tomcat Manager.
│
├── webapps/           # Default web applications and where your deployed apps go.
│   ├── ROOT/          # Default web application served at the root URL.
│   └── examples/      # Example applications (useful for testing).
│
├── work/              # Temporary files and compiled JSPs.
│   ├── Catalina/      # Compiled classes for individual web applications.
│   └── localhost/     # Temporary files for the localhost.
│
└── temp/              # Temporary files created by Tomcat.
    └── work/          # Temporary files created by Tomcat for internal use.

Explanation of Key Directories: 📁

  • /bin: 📁

    • Purpose: Contains scripts for starting and stopping Tomcat.
    • Key Files:
      • startup.sh / startup.bat: Starts the Tomcat server.
      • shutdown.sh / shutdown.bat: Stops the Tomcat server.
  • /conf: 📁

    • Purpose: Houses all the configuration files for Tomcat.
    • Key Files:
      • server.xml: Main configuration file for the server.
      • web.xml: Default configuration for web applications.
      • tomcat-users.xml: Manages user roles and access controls.
  • /lib: 📁

    • Purpose: Contains Java libraries (JAR files) needed by Tomcat and web applications.
    • Usage: Place any additional JAR files here to make them available to all web applications.
  • /logs: 📁

    • Purpose: Stores log files for monitoring server activity.
    • Key Logs:
      • catalina.out: Primary log file for Tomcat.
      • localhost_access_log.*: Access logs that record incoming requests.
  • /temp: 📁

    • Purpose: Temporary storage used by Tomcat during operations.
  • /webapps: 📁

    • Purpose: Default deployment directory for web applications.
    • Usage: Deploy WAR files here for automatic deployment by Tomcat.
    • ROOT/: The default web application that is served when accessing the root URL.
    • myapp/: An example directory where your custom web applications are deployed.
  • /work: 📁

    • Purpose: Contains temporary files and compiled JSPs. This directory is used for runtime storage and is usually cleared out automatically.

4. Amazon Linux 2023 Environment Setup 🐧

To deploy a Java web application on Tomcat, we need to set up the environment on Amazon Linux 2023. Here's how you do it step by step:

We will be using,

  • Oracle JDK 8
  • Maven available latest version
  • Tomcat version 8

1. Install Oracle JDK 8 ☕️

The Java Development Kit (JDK) is necessary for compiling and running Java applications.

Command:

  wget --no-cookies --no-check-certificate --header "Cookie: oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u131-b11/d54c1d3a095b4ff2b6607d096fa80163/jdk-8u131-linux-x64.tar.gz -P /opt
  • Purpose: Download Oracle JDK 8 Update 131.
  • Options:
    • --no-cookies and --no-check-certificate: Bypass certificate checks and cookie requirements.
    • --header "Cookie: oraclelicense=accept-securebackup-cookie": Accept Oracle's licensing terms.
    • -P /opt: Save the file in the /opt directory.

Command:

  tar -xvzf /opt/jdk-8u131-linux-x64.tar.gz -C /opt
  • Purpose: Extract the downloaded JDK tarball into /opt.
  • Options:
    • -xvzf: Extract (x), verbose (v), gzip (z), file (f).
    • -C /opt: Change to /opt directory before extracting.

2. Install Apache Maven 🚀

Maven is used to build and manage Java projects.

Command:

  wget https://dlcdn.apache.org/maven/maven-3/3.8.8/binaries/apache-maven-3.8.8-bin.tar.gz -P /opt
  • Purpose: Download Apache Maven 3.8.8.
  • Options:
    • -P /opt: Save the file in the /opt directory.

Command:

  tar -xvzf /opt/apache-maven-3.8.8-bin.tar.gz -C /opt
  • Purpose: Extract the Maven tarball into /opt.
  • Options:
    • -xvzf: Extract (x), verbose (v), gzip (z), file (f).
    • -C /opt: Change to /opt directory before extracting.

3. Install Apache Tomcat 🐱

Tomcat will serve as our servlet container.

  wget https://archive.apache.org/dist/tomcat/tomcat-8/v8.0.1/bin/apache-tomcat-8.0.1.tar.gz -P /opt
  • Purpose: Download Apache Tomcat 8.0.1.
  • Options:

    • -P /opt: Save the file in the /opt directory.
    tar -xvzf /opt/apache-tomcat-8.0.1.tar.gz -C /opt
    
  • Purpose: Extract the Tomcat tarball into /opt.
  • Options:
    • -xvzf: Extract (x), verbose (v), gzip (z), file (f).
    • -C /opt: Change to /opt directory before extracting.

4. Set Up Environment Variables 🌍

To make Java, Maven, and Tomcat accessible system-wide, we set up environment variables.

echo "export JAVA_HOME=/opt/jdk1.8.0_131             #For OracleJDK" >> /etc/profile
echo "export MAVEN_HOME=/opt/apache-maven-3.8.8" >> /etc/profile
echo 'export PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin' >> /etc/profile
  • echo: Adds environment variable settings to /etc/profile.
  • JAVA_HOME: Points to the JDK installation directory.
  • MAVEN_HOME: Points to the Maven installation directory.
  • PATH: Updates the system PATH to include Java and Maven binaries.
  • echo 'export PATH=$PATH:$JAVA_HOME/bin:$MAVEN_HOME/bin': Add the JDK and Maven bin directories to the PATH, ensuring their executables can be run from anywhere.

5. Install tree Utility 🌳

The tree command is useful for displaying directory structures.

yum install tree -y
  • yum install tree -y: Installs the tree utility on Amazon Linux.

6. Verification of installed binaries 🛠️

Once the above binaries are installed, logout of the session or user & login in Terminal again for changes to get effected & check the versions,

1. For Java,

Command:

java -version

Output:

Oracle Java: 
java version "1.8.0_131"
Java(TM) SE Runtime Environment (build 1.8.0_131-b11)
Java HotSpot(TM) 64-Bit Server VM (build 25.131-b11, mixed mode)

2. For Maven

Command:

mvn -version

Output:

Apache Maven 3.8.8 (4c87b05d9aedce574290d1acc98575ed5eb6cd39)
Maven home: /opt/apache-maven-3.8.8
Java version: 1.8.0_131, vendor: Oracle Corporation, runtime: /opt/jdk1.8.0_131/jre
Default locale: en, platform encoding: UTF-8
OS name: "linux", version: "6.1.102-111.182.amzn2023.x86_64", arch: "amd64", family: "unix"

3. For Tomcat,

Command:

/opt/apache-tomcat-8.0.1/bin/version.sh

Output:

Using CATALINA_BASE:   /opt/apache-tomcat-8.0.1
Using CATALINA_HOME:   /opt/apache-tomcat-8.0.1
Using CATALINA_TMPDIR: /opt/apache-tomcat-8.0.1/temp
Using JRE_HOME:        /opt/jdk1.8.0_131
Using CLASSPATH:       /opt/apache-tomcat-8.0.1/bin/bootstrap.jar:/opt/apache-tomcat-8.0.1/bin/tomcat-juli.jar
Server version: Apache Tomcat/8.0.1
Server built:   Jan 29 2014 11:13:21
Server number:  8.0.1.0
OS Name:        Linux
OS Version:     6.1.102-111.182.amzn2023.x86_64
Architecture:   amd64
JVM Version:    1.8.0_131-b11
JVM Vendor:     Oracle Corporation

7. Java Source Code and Application Analysis 💻

Now, let's dive into the actual Java code, directory structure, and how the application is organized and works. 🚀

This Java web application showcases a basic blog management system using Servlets and JSP. It includes setup instructions, Maven configurations, Java code, and JSP pages. The application structure and files are designed to facilitate both development and testing, ensuring that the web app functions as intended.

Project Directory Structure 📂

Create Project directories

mkdir -p ~/advanced-java-webapp/{src/main/{java/com/example/webapp/{servlet,views,model},resources,webapp/WEB-INF/views},src/test/java}
mkdir -p ~/advanced-java-webapp/src/test/java/com/example/webapp/{servlet,model}

Here's how the project is organized:

~/advanced-java-webapp
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           └── webapp
    │   │               ├── model
    │   │               │   └── BlogPost.java
    │   │               ├── servlet
    │   │               │   └── HomeServlet.java
    │   │               └── views
    │   ├── resources
    │   └── webapp
    │       ├── WEB-INF
    │       │   ├── views
    │       │   │   └── home.jsp
    │       │   └── web.xml
    │       └── index.jsp
    └── test
        └── java
            └── com
                └── example
                    └── webapp
                        ├── model
                        │   └── BlogPostTest.java
                        └── servlet
                            └── HomeServletTest.java

Directory Structure Overview

The directory structure of your web application project is carefully organized to follow the Maven Standard Directory Layout, which simplifies project management and builds processes.

Here's a breakdown of your project's directory structure:

  • ~/advanced-java-webapp/: Root directory for the project.
  • src/main/java: Contains the Java source code for your application.
    • com/example/webapp/servlet: Holds the servlets that handle client requests and generate responses.
    • com/example/webapp/views: Would typically hold Java classes for view-related logic (if needed).
    • com/example/webapp/model: Contains Java classes representing the application's data model (e.g., BlogPost).
  • src/main/resources: Contains configuration files and resources such as property files or XML configurations needed at runtime.
  • src/main/webapp: The web application root directory, containing all web-related resources.
    • WEB-INF: Contains the web.xml deployment descriptor and JSP files under WEB-INF/views directory to restrict direct access.
  • src/test/java: Contains Java test classes for unit and integration testing.
    • com/example/webapp/servlet: Tests for servlets.
    • com/example/webapp/model: Tests for model classes.

Detailed Explanation of Each File in ~/advanced-java-webapp/ directory

1. pom.xml

This file is crucial for Maven builds. It specifies the project structure, dependencies, plugins, and configuration settings. The dependencies are essential for developing and running a web application using Servlets and JSP. The plugins manage the build lifecycle and deployment to Tomcat.

Command:

vi ~/advanced-java-webapp/pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>advanced-java-webapp</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <failOnMissingWebXml>false</failOnMissingWebXml>
    </properties>

    <dependencies>
        <!-- Servlet API dependency -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>3.1.0</version>
            <scope>provided</scope>
        </dependency>
        <!-- JSP API dependency -->
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>javax.servlet.jsp-api</artifactId>
            <version>2.3.1</version>
            <scope>provided</scope>
        </dependency>
        <!-- JSTL dependency -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
        <!-- Mockito dependency for testing -->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>3.12.4</version>
            <scope>test</scope>
        </dependency>
        <!-- JUnit dependency for testing -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>advanced-java-webapp</finalName>
        <plugins>
            <!-- Maven WAR Plugin for packaging the application -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <version>3.3.1</version>
                <configuration>
                    <warName>advanced-java-webapp</warName>
                    <failOnMissingWebXml>false</failOnMissingWebXml>
                </configuration>
            </plugin>
            <!-- Tomcat Maven Plugin for deploying to Tomcat -->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <path>/advanced-java-webapp</path>
                </configuration>
            </plugin>
            <!-- Maven Surefire Plugin for running tests -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>3.0.0-M5</version>
            </plugin>
        </plugins>
    </build>
</project>

Explanation:

Content Breakdown:

Certainly! Here’s a more detailed breakdown of the pom.xml file located at ~/advanced-java-webapp/pom.xml:

Root Element

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  • <project>: This is the root element of the POM file, defining the project model.
    • xmlns: XML namespace for the POM schema, specifying the default namespace for elements in the POM.
    • xsi:schemaLocation: Provides the location of the XML Schema Definition (XSD) for validation. This tells XML parsers where to find the schema that defines the structure and constraints of the POM file.

Model Version

<modelVersion>4.0.0</modelVersion>
  • <modelVersion>: Specifies the version of the POM model to use. 4.0.0 is the current version and is required for all POM files.

Project Identification

<groupId>com.example</groupId>
<artifactId>advanced-java-webapp</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
  • <groupId>: Defines the group or organization that the project belongs to. Typically a reversed domain name to ensure uniqueness (e.g., com.example).
  • <artifactId>: The name of the project artifact. This is the name of the JAR or WAR file without the version number (e.g., advanced-java-webapp).
  • <version>: The version of the project. 1.0-SNAPSHOT indicates that this is a snapshot version, meaning it is a work-in-progress and not a final release.
  • <packaging>: Specifies the type of artifact to produce. war stands for Web Application Archive, which is used for web applications.

Properties

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <failOnMissingWebXml>false</failOnMissingWebXml>
</properties>
  • <properties>: Defines project-wide properties that can be referenced elsewhere in the POM.
    • <project.build.sourceEncoding>: Sets the character encoding for source files to UTF-8, ensuring proper handling of text.
    • <maven.compiler.source>: Specifies the version of the Java source code to use (Java 1.8).
    • <maven.compiler.target>: Specifies the version of the Java bytecode to generate (Java 1.8).
    • <failOnMissingWebXml>: Determines whether to fail the build if the web.xml file is missing. Set to false to allow the build to proceed without this file.

Dependencies

<dependencies>
    <!-- Servlet API dependency -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <!-- JSP API dependency -->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>javax.servlet.jsp-api</artifactId>
        <version>2.3.1</version>
        <scope>provided</scope>
    </dependency>
    <!-- JSTL dependency -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>1.2</version>
    </dependency>
    <!-- Mockito dependency for testing -->
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <version>3.12.4</version>
        <scope>test</scope>
    </dependency>
    <!-- JUnit dependency for testing -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>
  • <dependencies>: Lists all the dependencies required for the project.
    • Servlet API:
      • <dependency>:
        • <groupId>: javax.servlet
        • <artifactId>: javax.servlet-api
        • <version>: 3.1.0
        • <scope>: provided – This means the servlet API is expected to be provided by the runtime environment (e.g., Tomcat) and is not included in the WAR file.
    • JSP API:
      • <dependency>:
        • <groupId>: javax.servlet.jsp
        • <artifactId>: javax.servlet.jsp-api
        • <version>: 2.3.1
        • <scope>: provided – Similar to the Servlet API, the JSP API is provided by the server.
    • JSTL:
      • <dependency>:
        • <groupId>: javax.servlet
        • <artifactId>: jstl
        • <version>: 1.2 – Provides the JavaServer Pages Standard Tag Library (JSTL).
    • Mockito:
      • <dependency>:
        • <groupId>: org.mockito
        • <artifactId>: mockito-core
        • <version>: 3.12.4
        • <scope>: test – This dependency is used for writing unit tests and is included only in the test classpath.
    • JUnit:
      • <dependency>:
        • <groupId>: junit
        • <artifactId>: junit
        • <version>: 4.13.2
        • <scope>: test – This dependency is used for writing and running unit tests.

Build Configuration

<build>
    <finalName>advanced-java-webapp</finalName>
    <plugins>
        <!-- Maven WAR Plugin for packaging the application -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.3.1</version>
            <configuration>
                <warName>advanced-java-webapp</warName>
                <failOnMissingWebXml>false</failOnMissingWebXml>
            </configuration>
        </plugin>
        <!-- Tomcat Maven Plugin for deploying to Tomcat -->
        <plugin>
            <groupId>org.apache.tomcat.maven</groupId>
            <artifactId>tomcat7-maven-plugin</artifactId>
            <version>2.2</version>
            <configuration>
                <path>/advanced-java-webapp</path>
            </configuration>
        </plugin>
        <!-- Maven Surefire Plugin for running tests -->
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <version>3.0.0-M5</version>
        </plugin>
    </plugins>
</build>
  • <build>: Configures how the project is built and packaged.

    • <finalName>: Specifies the name of the final WAR file produced by the build. It will be named advanced-java-webapp.war.
    • <plugins>: Lists the plugins used in the build process.
      • Maven WAR Plugin:
        • <plugin>:
          • <groupId>: org.apache.maven.plugins
          • <artifactId>: maven-war-plugin
          • <version>: 3.3.1 – Manages the creation of WAR files.
          • <configuration>:
            • <warName>: Sets the name of the WAR file.
            • <failOnMissingWebXml>: Allows the build to succeed even if the web.xml is missing, which is useful for servlet 3.0+ applications that might not require a web.xml.
      • Tomcat Maven Plugin:
        • <plugin>:
          • <groupId>: org.apache.tomcat.maven
          • <artifactId>: tomcat7-maven-plugin
          • <version>: 2.2 – Facilitates deploying the application to Tomcat.
          • <configuration>:
            • <path>: Sets

    the context path for deployment in Tomcat. The application will be accessible at /advanced-java-webapp.

    • Maven Surefire Plugin:
      • <plugin>:
        • <groupId>: org.apache.maven.plugins
        • <artifactId>: maven-surefire-plugin
        • <version>: 3.0.0-M5 – Used for running unit tests during the build process.

Summary

The pom.xml file for the advanced-java-webapp project defines:

  • Project Metadata: Group ID, artifact ID, version, and packaging type (WAR).
  • Build Properties: Source and target Java versions, character encoding, and handling of missing web.xml.
  • Dependencies: Includes Servlet API, JSP API, JSTL, Mockito (for testing), and JUnit (for testing).
  • Build Configuration: Specifies the final name of the WAR file, and includes plugins for packaging (Maven WAR Plugin), deployment (Tomcat Maven Plugin), and testing (Maven Surefire Plugin).

This setup ensures the project can be properly built, tested, and deployed as a web application.


2. web.xml

This is the deployment descriptor for your web application. It configures your web application and defines servlets, filters, and listeners. The welcome-file-list element is used to define the default page when a user accesses the application.

Command:

vi ~/advanced-java-webapp/src/main/webapp/WEB-INF/web.xml
<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_3_1.xsd"
         version="3.1">
    <display-name>Advanced Java Web Application</display-name>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>

Explanation:

Content Breakdown:

<?xml version="1.0" encoding="UTF-8"?>
<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_3_1.xsd"
         version="3.1">
    <display-name>Advanced Java Web Application</display-name>
    <welcome-file-list>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>
</web-app>
  • XML Namespaces and Schema: The web-app defines the XML schema and namespaces used, ensuring the file conforms to the specifications for a Java EE 7 (Servlet 3.1) application.

  • Display Name: The <display-name> element provides a human-readable name for the application.

  • Welcome File List: The <welcome-file-list> defines the default resource to be loaded (i.e., index.jsp) when the root URL is accessed.


3. HomeServlet.java

The HomeServlet class is a key component of the application, responsible for handling HTTP requests and managing blog posts to /home. It demonstrates a basic CRUD (Create, Read, Update, Delete) operation pattern using Java Servlets. It extends HttpServlet, which is part of the Java Servlet API, and overrides doGet and doPost methods to handle HTTP GET and POST requests, respectively.

Command:

vi ~/advanced-java-webapp/src/main/java/com/example/webapp/servlet/HomeServlet.java
package com.example.webapp.servlet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@WebServlet("/home")
public class HomeServlet extends HttpServlet {
    private List<BlogPost> blogPosts;

    @Override
    public void init() throws ServletException {
        super.init();
        blogPosts = new ArrayList<>();
        blogPosts.add(new BlogPost(1, "The Art of Java Programming", "Discover the beauty of Java and its vast ecosystem.", "John Doe"));
        blogPosts.add(new BlogPost(2, "Web Development with Servlets and JSP", "Learn how to create dynamic web applications using Java EE technologies.", "Jane Smith"));
        blogPosts.add(new BlogPost(3, "Mastering Design Patterns in Java", "Explore common design patterns and their implementation in Java.", "Bob Johnson"));
    }

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String action = request.getParameter("action");
        if (action != null) {
            switch (action) {
                case "delete":
                    deletePost(request);
                    break;
                case "edit":
                    prepareEditPost(request);
                    break;
            }
        }
        request.setAttribute("blogPosts", blogPosts);
        request.getRequestDispatcher("/WEB-INF/views/home.jsp").forward(request, response);
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String action = request.getParameter("action");
        if ("submit".equals(action)) {
            createOrUpdatePost(request);
        } else if ("delete".equals(action)) {
            deletePost(request);
        }
        doGet(request, response);
    }

    private void createOrUpdatePost(HttpServletRequest request) {
        String idStr = request.getParameter("id");
        String title = request.getParameter("title");
        String excerpt = request.getParameter("excerpt");
        String author = request.getParameter("author");

        int id = idStr.isEmpty() ? -1 : Integer.parseInt(idStr);

        BlogPost post = findPostById(id);
        if (post == null) {
            blogPosts.add(new BlogPost(blogPosts.size() + 1, title, excerpt, author));
        } else {
            post.setTitle(title);
            post.setExcerpt(excerpt);
            post.setAuthor(author);
        }
    }

    private void deletePost(HttpServletRequest request) {
        int id = Integer.parseInt(request.getParameter("id"));
        BlogPost post = findPostById(id);
        if (post != null) {
            blogPosts.remove(post);
        }
    }

    private BlogPost findPostById(int id) {
        for (BlogPost post : blogPosts) {
            if (post.getId() == id) {
                return post;
            }
        }
        return null;
    }

    private void prepareEditPost(HttpServletRequest request) {
        int id = Integer.parseInt(request.getParameter("id"));
        BlogPost post = findPostById(id);
        if (post != null) {
            request.setAttribute("editPost", post);
        }
    }

    public static class BlogPost {
        private int id;
        private String title;
        private String excerpt;
        private String author;

        public BlogPost(int id, String title, String excerpt, String author) {
            this.id = id;
            this.title = title;
            this.excerpt = excerpt;
            this.author = author;
        }

        public int getId() {
            return id;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public String getExcerpt() {
            return excerpt;
        }

        public void setExcerpt(String excerpt) {
            this.excerpt = excerpt;
        }

        public String getAuthor() {
            return author;
        }

        public void setAuthor(String author) {
            this.author = author;
        }
    }
}

Explanation:

Content Breakdown:

Package and Imports

package com.example.webapp.servlet;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
  • Package Declaration: The package com.example.webapp.servlet; line defines the package to which this servlet belongs. Packages are used to organize classes and interfaces in a namespace, which helps to avoid naming conflicts and to control access.

  • Imports:

    • java.io.IOException and javax.servlet.ServletException are imported for handling exceptions that can occur during the input/output operations and servlet operations.
    • java.util.ArrayList and java.util.List are used to manage collections of BlogPost objects.
    • javax.servlet.annotation.WebServlet is an annotation used to declare a servlet and map it to a specific URL (/home in this case).
    • javax.servlet.http.HttpServlet, javax.servlet.http.HttpServletRequest, and javax.servlet.http.HttpServletResponse are part of the Servlet API, allowing the servlet to handle HTTP requests and responses.

Servlet Annotation and Class Declaration

@WebServlet("/home")
public class HomeServlet extends HttpServlet {
  • @WebServlet("/home"): This annotation is used to declare the servlet and specify the URL pattern that this servlet will handle. In this case, the servlet will handle requests sent to the /home URL.

  • Class Declaration: public class HomeServlet extends HttpServlet declares a public class HomeServlet that extends HttpServlet. This means HomeServlet is a servlet that inherits capabilities from the HttpServlet class, such as handling HTTP requests (GET, POST, etc.).

Member Variables and Initialization

private List<BlogPost> blogPosts;

@Override
public void init() throws ServletException {
    super.init();
    blogPosts = new ArrayList<>();
    blogPosts.add(new BlogPost(1, "The Art of Java Programming", "Discover the beauty of Java and its vast ecosystem.", "John Doe"));
    blogPosts.add(new BlogPost(2, "Web Development with Servlets and JSP", "Learn how to create dynamic web applications using Java EE technologies.", "Jane Smith"));
    blogPosts.add(new BlogPost(3, "Mastering Design Patterns in Java", "Explore common design patterns and their implementation in Java.", "Bob Johnson"));
}
  • private List<BlogPost> blogPosts;: This is a private member variable of type List<BlogPost> that stores a list of BlogPost objects. It will be used to manage the collection of blog posts.

  • init() Method:

    • This method is called by the servlet container to initialize the servlet when it is first loaded into memory.
    • It initializes the blogPosts list and populates it with three BlogPost objects, each representing a different blog post.
    • The super.init() call ensures that the parent class (HttpServlet) initialization logic is executed.

doGet Method

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String action = request.getParameter("action");
    if (action != null) {
        switch (action) {
            case "delete":
                deletePost(request);
                break;
            case "edit":
                prepareEditPost(request);
                break;
        }
    }
    request.setAttribute("blogPosts", blogPosts);
    request.getRequestDispatcher("/WEB-INF/views/home.jsp").forward(request, response);
}
  • doGet Method:
    • Handles GET requests to the servlet. It is triggered when a user navigates to the /home URL.
    • Retrieves the action parameter from the request to determine what action to take (delete or edit).
    • Depending on the action value, it calls either deletePost or prepareEditPost.
    • Sets the blogPosts list as an attribute on the request object so it can be accessed in the JSP (home.jsp).
    • Forwards the request to the home.jsp page to display the current state of blogPosts.

doPost Method

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    String action = request.getParameter("action");
    if ("submit".equals(action)) {
        createOrUpdatePost(request);
    } else if ("delete".equals(action)) {
        deletePost(request);
    }
    doGet(request, response);
}
  • doPost Method:
    • Handles POST requests to the servlet, typically used when a form is submitted.
    • Checks the action parameter to determine what action to perform. If the action is "submit", it calls createOrUpdatePost to handle the creation or update of a blog post. If the action is "delete", it calls deletePost.
    • Calls doGet to refresh the page after performing the post action to ensure the user sees the updated list of blog posts.

createOrUpdatePost Method

private void createOrUpdatePost(HttpServletRequest request) {
    String idStr = request.getParameter("id");
    String title = request.getParameter("title");
    String excerpt = request.getParameter("excerpt");
    String author = request.getParameter("author");

    int id = idStr.isEmpty() ? -1 : Integer.parseInt(idStr);

    BlogPost post = findPostById(id);
    if (post == null) {
        blogPosts.add(new BlogPost(blogPosts.size() + 1, title, excerpt, author));
    } else {
        post.setTitle(title);
        post.setExcerpt(excerpt);
        post.setAuthor(author);
    }
}
  • createOrUpdatePost Method:
    • Responsible for creating a new blog post or updating an existing one based on the form data submitted.
    • Retrieves parameters from the request (id, title, excerpt, author).
    • If id is empty, it's set to -1, indicating a new post creation.
    • Calls findPostById to see if a post with the given id exists.
    • If the post is null (not found), a new BlogPost is created and added to the blogPosts list. Otherwise, the existing post is updated with the new data (title, excerpt, author).

deletePost Method

private void deletePost(HttpServletRequest request) {
    int id = Integer.parseInt(request.getParameter("id"));
    BlogPost post = findPostById(id);
    if (post != null) {
        blogPosts.remove(post);
    }
}
  • deletePost Method:
    • Handles the deletion of a blog post.
    • Retrieves the id of the post to be deleted from the request parameters.
    • Uses findPostById to locate the post by its id.
    • If the post is found, it is removed from the blogPosts list.

findPostById Method

private BlogPost findPostById(int id) {
    for (BlogPost post : blogPosts) {
        if (post.getId() == id) {
            return post;
        }
    }
    return null;
}
  • findPostById Method:
    • Searches for a BlogPost in the blogPosts list by its id.
    • Iterates through each BlogPost in blogPosts and returns the post if the id matches.
    • Returns null if no post with the given id is found.

prepareEditPost Method

private void prepareEditPost(HttpServletRequest request) {
    int id = Integer.parseInt(request.getParameter("id"));
    BlogPost post = findPostById(id);
    if (post != null) {
        request.setAttribute("editPost", post);
    }
}
  • prepareEditPost Method:
    • Prepares a blog post for editing.
    • Retrieves the id of the post to be edited from the request parameters.
    • Uses findPostById to find the post by its id.
    • If the post is found, it sets the post as an attribute (editPost) on the request object so it can be accessed in the JSP (home.jsp).

BlogPost Inner Class

public static class BlogPost {
    private int id;
    private String title;
    private String excerpt;
    private String author;

    public BlogPost(int id, String title, String excerpt, String author) {
        this.id = id;
        this.title = title;
        this.excerpt = excerpt;
        this.author = author;
    }

    public int getId() {
        return id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getExcerpt() {
        return excerpt;
    }

    public void setExcerpt(String excerpt) {
        this.excerpt = excerpt;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }
}
  • Fields:

    • private int id: Represents the unique identifier of each blog post.
    • private String title: Stores the title of the blog post.
    • private String excerpt: Contains a brief summary or excerpt of the blog post's content.
    • private String author: Holds the name of the author of the blog post.
  • Constructor:

    • public BlogPost(int id, String title, String excerpt, String author): This constructor initializes a new instance of BlogPost with the provided id, title, excerpt, and author.
  • Getter and Setter Methods:

    • getId(): Returns the id of the blog post.
    • getTitle(): Returns the title of the blog post.
    • setTitle(String title): Sets a new title for the blog post.
    • getExcerpt(): Returns the excerpt of the blog post.
    • setExcerpt(String excerpt): Sets a new excerpt for the blog post.
    • getAuthor(): Returns the author of the blog post.
    • setAuthor(String author): Sets a new author for the blog post.

Integration and Dependencies Between Methods and Classes

HomeServlet and BlogPost

  1. HomeServlet Class:

    • Purpose: Serves as a controller in the MVC (Model-View-Controller) pattern, handling HTTP requests and manipulating data before sending it to the view (home.jsp).
    • Main Responsibilities:
      • Initialization (init()): Loads initial data (blog posts).
      • Request Handling (doGet(), doPost()): Manages both GET and POST requests, controlling what happens based on user actions.
      • Data Operations:
        • Creating/Updating Posts (createOrUpdatePost()): Adds new posts or updates existing ones based on user input.
        • Deleting Posts (deletePost()): Removes a blog post based on its id.
        • Editing Preparation (prepareEditPost()): Prepares a blog post for editing by placing it in the request scope.
  2. BlogPost Class:

    • Purpose: Represents a single blog post entity within the application.
    • Main Responsibilities:
      • Data Encapsulation: Holds blog post properties (id, title, excerpt, author) and provides methods to access and modify them.

How Each Part Interacts:

  • Data Initialization:

    • The init() method initializes the blogPosts list with sample BlogPost objects. This setup is crucial for testing and demonstrating the functionality of the servlet without needing an external database.
  • Handling HTTP Requests:

    • doGet() Method:

      • Processes GET requests to display the blog posts.
      • Depending on the action parameter (e.g., delete or edit), it will call methods (deletePost or prepareEditPost) to modify the blogPosts list or set attributes for the view.
      • The blogPosts list is set as a request attribute to be displayed in home.jsp.
    • doPost() Method:

      • Processes POST requests, typically from form submissions.
      • Based on the action parameter (submit or delete), it either calls createOrUpdatePost to modify the blog posts or deletePost to remove a post.
      • After handling a POST request, it delegates to doGet() to refresh the view with the updated data.
  • Modifying Data:

    • createOrUpdatePost():

      • Checks if a blog post already exists using findPostById(). If it does, it updates the post's properties using the setter methods (setTitle(), setExcerpt(), setAuthor()).
      • If the post does not exist (id is -1 or not found), it creates a new BlogPost and adds it to the list.
    • deletePost():

      • Finds the post by id and removes it from the blogPosts list.
    • prepareEditPost():

      • Finds the post by id and sets it as a request attribute (editPost) to be accessed in the JSP page (home.jsp).
  • View Rendering (home.jsp):

    • The JSP file (home.jsp) renders the blogPosts list to the user. It relies on the request attributes set by the doGet() method to display data dynamically based on the current state of blogPosts.

4. BlogPost.java

The BlogPost class in the com.example.webapp.model package is a simple data model class (also known as a POJO—Plain Old Java Object). It is used to represent a blog post entity within the application. The class encapsulates the properties of a blog post, including the title, excerpt, and author, and provides getter methods to access these properties.

Command:

vi ~/advanced-java-webapp/src/main/java/com/example/webapp/model/BlogPost.java
package com.example.webapp.model;

public class BlogPost {
    private String title;
    private String excerpt;
    private String author;

    public BlogPost(String title, String excerpt, String author) {
        this.title = title;
        this.excerpt = excerpt;
        this.author = author;
    }

    // Getters
    public String getTitle() { return title; }
    public String getExcerpt() { return excerpt; }
    public String getAuthor() { return author; }
}

Explanation:

Content Breakdown:

package com.example.webapp.model;
  • Package Declaration:
    • The package statement declares that this class is part of the com.example.webapp.model package. This helps organize related classes together and avoid naming conflicts.
public class BlogPost {
  • Class Declaration:
    • The BlogPost class is declared as public, meaning it can be accessed by any other class in the application. This is important for its usage across different parts of the application, especially in servlets and JSPs.

Private Fields

private String title;
private String excerpt;
private String author;
  • Fields:
    • private String title;: This field holds the title of the blog post. It is marked as private to restrict direct access from outside the class, following the principle of encapsulation.
    • private String excerpt;: This field stores a brief excerpt or summary of the blog post's content. It is also private to ensure that it can only be accessed through the class's methods.
    • private String author;: This field contains the name of the author of the blog post. It is private for the same reasons as the other fields.

Constructor

public BlogPost(String title, String excerpt, String author) {
    this.title = title;
    this.excerpt = excerpt;
    this.author = author;
}
  • Constructor:
    • The constructor is public, allowing any other class to create instances of BlogPost.
    • Parameters:
      • String title: The title of the blog post.
      • String excerpt: A brief summary or excerpt of the blog post.
      • String author: The author's name.
    • this Keyword:
      • this.title = title; assigns the value of the title parameter to the title field of the BlogPost instance being created. This pattern is repeated for the excerpt and author fields. The this keyword is necessary here to differentiate between the class fields and the parameters with the same name.

Getter Methods

// Getters
public String getTitle() { return title; }
public String getExcerpt() { return excerpt; }
public String getAuthor() { return author; }
  • Getter Methods:
    • These methods provide public access to the private fields of the class. They follow a standard naming convention (getFieldName) which is widely recognized in Java for accessing private data.
    • public String getTitle():
      • Returns the title of the blog post. This allows other classes (such as servlets or JSPs) to retrieve the title without directly accessing the private field.
    • public String getExcerpt():
      • Returns the excerpt of the blog post. Similar to getTitle(), it provides controlled access to the private excerpt field.
    • public String getAuthor():
      • Returns the author of the blog post, allowing external classes to access the author's name.

Usage and Dependencies

  • Usage in the Web Application:

    • The BlogPost class is primarily used as a data model within the web application. For example, instances of BlogPost might be created, modified, and stored in a collection (like a List) in servlets such as HomeServlet. The servlets would then use these objects to dynamically generate content for the web pages (like home.jsp).
  • Dependencies:

    • The BlogPost class has no direct dependencies on other classes or libraries. It is a self-contained data model class.
    • However, other classes depend on BlogPost for storing and manipulating blog post data. For instance, in the servlet (HomeServlet), a list of BlogPost objects is created and managed to display blog posts on the homepage.

Integration and Interaction

  • Interaction with Servlets:

    • In the HomeServlet class, the BlogPost model is used to represent the data for blog posts. The servlet creates and manipulates instances of BlogPost using the constructor and getter methods.
    • For example, when initializing the list of blog posts, the servlet creates several BlogPost objects using the constructor:
      blogPosts.add(new BlogPost("The Art of Java Programming", "Discover the beauty of Java...", "John Doe"));
      
    • When displaying the blog posts on a webpage, the servlet accesses each blog post's details using the getter methods:
      request.setAttribute("blogPosts", blogPosts);
      
  • Interaction with JSP (JavaServer Pages):

    • When the servlet forwards the request to a JSP file (like home.jsp), the JSP can use Expression Language (EL) or JSTL to access the properties of the BlogPost objects provided by the servlet. For example:
      <c:forEach var="post" items="${blogPosts}">
          <h2>${post.title}</h2>
          <p>${post.excerpt}</p>
          <p><em>by ${post.author}</em></p>
      </c:forEach>
      
    • This snippet shows how the title, excerpt, and author fields are accessed through the getter methods indirectly via EL expressions (${post.title}, ${post.excerpt}, ${post.author}).

5. index.jsp

The index.jsp file is a well-structured JavaServer Page (JSP) that serves as the entry point for the web application. It is designed to provide a welcoming landing page for users visiting the blog. The page includes a button to navigate to the home page and a modal dialog for users to enter a nickname. It leverages Bootstrap for a modern, responsive design and uses JavaScript for interactive elements, such as the nickname modal and client-side storage. By combining HTML, CSS, and JavaScript with JSP capabilities, the page creates a dynamic and engaging user experience while preparing the user for further navigation within the application.

Command:

vi ~/advanced-java-webapp/src/main/webapp/index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Asthik Blog</title>
    <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>📰</text></svg>">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body {
            background-color: #004643;
            color: white;
        }
        .btn-primary {
            border-radius: 25px; /* Increased corner radius */
        }
    </style>
</head>
<body>
    <div class="container mt-5">
        <h1 class="text-center">Welcome to Asthik's Blog</h1>
        <p class="text-center mt-3">
            <button id="goToHomeBtn" class="btn btn-primary">Go to Home Page</button>
        </p>
    </div>

    <!-- Nickname Modal -->
    <div class="modal fade" id="nicknameModal" tabindex="-1" aria-labelledby="nicknameModalLabel" aria-hidden="true">
        <div class="modal-dialog">
            <div class="modal-content">
                <div class="modal-header">
                    <h5 class="modal-title" id="nicknameModalLabel">Enter Your Nickname</h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
                </div>
                <div class="modal-body">
                    <input type="text" class="form-control" id="nicknameInput" placeholder="Enter your nickname">
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
                    <button type="button" class="btn btn-primary" id="saveNickname">Save</button>
                </div>
            </div>
        </div>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    <script>
        var nicknameModal = new bootstrap.Modal(document.getElementById('nicknameModal'));

        document.getElementById('goToHomeBtn').addEventListener('click', function(e) {
            e.preventDefault();
            nicknameModal.show();
        });

        document.getElementById('saveNickname').addEventListener('click', function() {
            var nickname = document.getElementById('nicknameInput').value || 'Friend';
            nicknameModal.hide();
            // Store the nickname in localStorage
            localStorage.setItem('userNickname', nickname);
            // Redirect to the home page
            window.location.href = '${pageContext.request.contextPath}/home';
        });
    </script>
</body>
</html>

Explanation:

Content Breakdown:

JSP Directives

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
  • Page Directive:
    • The JSP page directive provides configuration settings for the JSP page.
    • language="java": Specifies that the scripting language used in this JSP is Java.
    • contentType="text/html; charset=UTF-8": Sets the MIME type of the response to text/html with a character encoding of UTF-8. This is essential for correctly displaying the text and special characters on the webpage.
    • pageEncoding="UTF-8": Specifies the encoding used for the JSP page itself, ensuring it is read and interpreted as UTF-8.

HTML Document Structure

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Asthik Blog</title>
    <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>📰</text></svg>">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body {
            background-color: #004643;
            color: white;
        }
        .btn-primary {
            border-radius: 25px; /* Increased corner radius */
        }
    </style>
</head>
<body>
    ...
</body>
</html>
  • HTML5 Doctype:

    • The <!DOCTYPE html> declaration defines the document type and version of HTML being used (HTML5 in this case).
  • <html> Element:

    • The root element of the HTML document.
  • <head> Section:

    • <meta charset="UTF-8">: Specifies the character set for the HTML document as UTF-8, ensuring proper handling of text and special characters.
    • <title>Asthik Blog</title>: Sets the title of the web page, which appears in the browser tab.
    • Favicon Link:
      • <link rel="icon" ...>: Embeds a small newspaper emoji as the favicon using an SVG image data URI. This favicon will be displayed in the browser tab or address bar.
    • Bootstrap CSS:
      • <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">: Includes Bootstrap 5 CSS from a CDN for responsive design and pre-styled components.
    • Internal CSS Styles:
      • The <style> block includes internal CSS to customize the page's appearance:
        • body { background-color: #004643; color: white; }: Sets the background color to a dark green (#004643) and text color to white.
        • .btn-primary { border-radius: 25px; }: Modifies Bootstrap’s default primary button style to have a larger border radius for rounded corners.

HTML Body and Main Content

<body>
    <div class="container mt-5">
        <h1 class="text-center">Welcome to Asthik's Blog</h1>
        <p class="text-center mt-3">
            <button id="goToHomeBtn" class="btn btn-primary">Go to Home Page</button>
        </p>
    </div>
    ...
</body>
  • <body> Element:

    • Contains the visible content of the webpage.
  • Main Container:

    • <div class="container mt-5">: A Bootstrap container element centered with a top margin (mt-5). This provides a responsive and aligned structure for the page content.
    • Welcome Header:
      • <h1 class="text-center">Welcome to Asthik's Blog</h1>: Displays a centered heading welcoming users to the blog.
    • Home Button:
      • <p class="text-center mt-3">: A paragraph element with additional top margin (mt-3) to separate it from the header.
      • <button id="goToHomeBtn" class="btn btn-primary">Go to Home Page</button>: A Bootstrap-styled primary button with an ID of goToHomeBtn. Clicking this button triggers the display of the nickname modal.

Modal for Nickname Input

<!-- Nickname Modal -->
<div class="modal fade" id="nicknameModal" tabindex="-1" aria-labelledby="nicknameModalLabel" aria-hidden="true">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title" id="nicknameModalLabel">Enter Your Nickname</h5>
                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
            </div>
            <div class="modal-body">
                <input type="text" class="form-control" id="nicknameInput" placeholder="Enter your nickname">
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
                <button type="button" class="btn btn-primary" id="saveNickname">Save</button>
            </div>
        </div>
    </div>
</div>
  • Modal Component:
    • This section creates a Bootstrap modal component, which is a dialog box/popup window that is displayed on top of the current page.
    • <div class="modal fade" id="nicknameModal" ...>: The outer container for the modal, with Bootstrap classes modal and fade for transition effects.
    • Modal Dialog and Content:
      • <div class="modal-dialog">: Defines the modal's dialog box.
      • <div class="modal-content">: Contains the actual content of the modal.
    • Modal Header:
      • <div class="modal-header">: Header section of the modal, containing the title and a close button.
      • <h5 class="modal-title" id="nicknameModalLabel">Enter Your Nickname</h5>: Displays the title of the modal.
      • Close Button:
        • <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>: A Bootstrap-styled close button to dismiss the modal.
    • Modal Body:
      • <div class="modal-body">: Contains the body of the modal.
      • Nickname Input:
        • <input type="text" class="form-control" id="nicknameInput" placeholder="Enter your nickname">: An input field for the user to enter their nickname. It uses Bootstrap’s form-control class for styling.
    • Modal Footer:
      • <div class="modal-footer">: Footer of the modal with action buttons.
      • Close and Save Buttons:
        • <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>: A secondary button to close the modal.
        • <button type="button" class="btn btn-primary" id="saveNickname">Save</button>: A primary button to save the nickname input by the user. This button is associated with the ID saveNickname for JavaScript event handling.

Scripts and JavaScript Logic

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
    var nicknameModal = new bootstrap.Modal(document.getElementById('nicknameModal'));

    document.getElementById('goToHomeBtn').addEventListener('click', function(e) {
        e.preventDefault();
        nicknameModal.show();
    });

    document.getElementById('saveNickname').addEventListener('click', function() {
        var nickname = document.getElementById('nicknameInput').value || 'Friend';
        nicknameModal.hide();
        // Store the nickname in localStorage
        localStorage.setItem('userNickname', nickname);
        // Redirect to the home page
        window.location.href = '${pageContext.request.contextPath}/home';
    });
</script>
  • Bootstrap JS:
    • <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>: Loads Bootstrap's JavaScript library from a CDN. This library includes all Bootstrap components and functionality, such as mod

als and buttons.

  • JavaScript for Modal and User Interaction:
    • JavaScript Variable Initialization:
      • var nicknameModal = new bootstrap.Modal(document.getElementById('nicknameModal'));: Initializes a new Bootstrap modal instance for the nickname modal element.
    • Button Event Listeners:
      • Go to Home Button:
        • document.getElementById('goToHomeBtn').addEventListener('click', function(e) {...}: Adds a click event listener to the "Go to Home Page" button. When clicked, it prevents the default button behavior and displays the nickname modal by calling nicknameModal.show().
      • Save Nickname Button:
        • document.getElementById('saveNickname').addEventListener('click', function() {...}: Adds a click event listener to the "Save" button in the modal. When clicked:
          • var nickname = document.getElementById('nicknameInput').value || 'Friend';: Retrieves the user-inputted nickname from the input field. If no nickname is entered, it defaults to 'Friend'.
          • nicknameModal.hide();: Closes the modal after the nickname is saved.
          • localStorage.setItem('userNickname', nickname);: Stores the nickname in the browser's localStorage so it can be retrieved later across page loads or sessions.
          • window.location.href = '${pageContext.request.contextPath}/home';: Redirects the user to the home page using JSP's pageContext to construct the correct URL path.

Usage and Integration

  • Client-Side Interactions:

    • This JSP serves as a dynamic front-end page that interacts directly with users. It uses Bootstrap for styling and JavaScript to handle user interactions such as opening the modal and saving user input.
  • Server-Side Context:

    • While this JSP does not interact with server-side Java code directly (e.g., through scriptlets or JSP expressions), it does utilize the JSP pageContext to correctly handle URL paths. This is particularly useful when deploying the web application in different environments where the context path may change.
  • Session Management and User Personalization:

    • The nickname modal provides a simple mechanism for personalizing the user experience. By saving the user's nickname in localStorage, the application can remember the user's choice across different sessions or page loads without the need for server-side state management.

6. home.jsp

The home.jsp file is a key component of the blog application, combining dynamic server-side rendering with interactive client-side features. It displays a list of blog posts and provides options to create, edit, or delete a post. By leveraging JSTL for server-side logic, Bootstrap for styling, and JavaScript for interactivity, the page provides a seamless and engaging user experience. The integration with backend services through forms and dynamic links ensures that users can manage blog content effectively.

Command:

vi ~/advanced-java-webapp/src/main/webapp/WEB-INF/views/home.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Asthik Home</title>
    <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🏠︎</text></svg>">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        body {
            background-color: #2c2a4a;
            color: white;
        }
        .card {
            background-color: #4f518c;
            color: white;
            transition: transform 0.3s;
            border-radius: 15px; /* Increased corner radius */
        }
        .card-body {
            border-radius: 15px; /* Increased corner radius */
        }
        .card:hover {
            transform: translateY(-5px);
        }
        .btn {
            border-radius: 20px;
            transition: all 0.3s ease;
        }
        .btn:hover {
            transform: scale(1.05);
        }
        .btn-warning {
            background-color: #dabfff;
            border-color: #dabfff;
            margin-right: 10px;
        }
        .btn-danger {
            background-color: #907ad6;
            border-color: #907ad6;
        }
        .btn-home {
            background-color: #dd2d4a;
            border-color: #dd2d4a;
            color: white;
        }
        #redirectMessage {
            position: fixed;
            bottom: 20px;
            left: 50%;
            transform: translateX(-50%);
            background-color: rgba(0,0,0,0.7);
            padding: 10px 20px;
            border-radius: 5px;
        }
    </style>
</head>
<body>
    <div class="container mt-5">
        <h1 class="text-center mb-4">Welcome to Asthik's Blog</h1>

        <c:if test="${not empty message}">
            <div class="alert alert-success" role="alert">
                ${message}
            </div>
        </c:if>

        <div class="row mb-5">
            <c:forEach items="${blogPosts}" var="post">
                <div class="col-md-4 mb-4">
                    <div class="card h-100">
                        <div class="card-body">
                            <h5 class="card-title">${post.title}</h5>
                            <p class="card-text">${post.excerpt}</p>
                            <p class="card-text"><small>By ${post.author}</small></p>
                            <a href="${pageContext.request.contextPath}/home?action=edit&id=${post.id}" class="btn btn-warning btn-sm">Edit</a>
                            <a href="${pageContext.request.contextPath}/home?action=delete&id=${post.id}" class="btn btn-danger btn-sm">Delete</a>
                        </div>
                    </div>
                </div>
            </c:forEach>
        </div>

        <!-- Form to create or edit blog post -->
        <div class="row">
            <div class="col-md-6 mx-auto">
                <h2>Create or Edit Post</h2>
                <form action="${pageContext.request.contextPath}/home" method="post">
                    <input type="hidden" name="id" value="${editPost != null ? editPost.id : ''}">
                    <div class="mb-3">
                        <label for="title" class="form-label">Title</label>
                        <input type="text" class="form-control" id="title" name="title" value="${editPost != null ? editPost.title : ''}" required>
                    </div>
                    <div class="mb-3">
                        <label for="excerpt" class="form-label">Excerpt</label>
                        <textarea class="form-control" id="excerpt" name="excerpt" rows="3" required>${editPost != null ? editPost.excerpt : ''}</textarea>
                    </div>
                    <div class="mb-3">
                        <label for="author" class="form-label">Author</label>
                        <input type="text" class="form-control" id="author" name="author" value="${editPost != null ? editPost.author : ''}" required>
                    </div>
                    <button type="submit" class="btn btn-primary" name="action" value="submit">Save Post</button>
                    <a href="${pageContext.request.contextPath}" class="btn btn-home" id="backHome">Back Home</a>
                </form>
            </div>
        </div>
    </div>

    <div id="redirectMessage" style="display: none;">
        <p>Thank you, <span id="nicknamePlaceholder"></span>! Redirecting in <span id="countdown">5</span> seconds.</p>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    <script>
        document.getElementById('backHome').addEventListener('click', function(e) {
            e.preventDefault();
            var nickname = localStorage.getItem('userNickname') || 'Friend';
            document.getElementById('nicknamePlaceholder').textContent = nickname;
            document.getElementById('redirectMessage').style.display = 'block';
            var countdown = 5;
            var countdownElement = document.getElementById('countdown');
            var countdownInterval = setInterval(function() {
                countdown--;
                countdownElement.textContent = countdown;
                if (countdown <= 0) {
                    clearInterval(countdownInterval);
                    window.location.href = '${pageContext.request.contextPath}';
                }
            }, 1000);
        });
    </script>
</body>
</html>

Explanation:

Content Breakdown:

JSP Directives

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
  • Page Directive:

    • language="java": Specifies that the scripting language used is Java.
    • contentType="text/html; charset=UTF-8": Sets the MIME type for the page to text/html with UTF-8 encoding.
    • pageEncoding="UTF-8": Ensures the JSP page is read and interpreted as UTF-8.
  • Taglib Directive:

    • <%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>: Imports the core JSTL tag library, which provides standard tags like <c:if> and <c:forEach> for adding conditional logic and loops within the JSP page.

HTML Document Structure

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Asthik Home</title>
    <link rel="icon" href="data:image/svg+xml,...">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        ...
    </style>
</head>
<body>
    ...
</body>
</html>
  • HTML5 Doctype:

    • Defines the document type as HTML5, ensuring modern standards and browser compatibility.
  • <html> Element:

    • The root element for the HTML document.
  • <head> Section:

    • Meta Tag: Specifies the character encoding for the document as UTF-8.
    • Title Tag: Sets the title of the webpage to "Asthik Home".
    • Favicon: Embeds a small house emoji as the favicon using an SVG image data URI.
    • Bootstrap CSS: Includes Bootstrap 5 CSS from a CDN to provide a responsive layout and pre-styled components.
    • Internal CSS Styles: Contains internal CSS rules that customize the appearance of the page, such as background colors, card styling, button effects, and hover transitions.

Body and Main Content

<body>
    <div class="container mt-5">
        <h1 class="text-center mb-4">Welcome to Asthik's Blog</h1>

        <c:if test="${not empty message}">
            <div class="alert alert-success" role="alert">
                ${message}
            </div>
        </c:if>

        <div class="row mb-5">
            <c:forEach items="${blogPosts}" var="post">
                ...
            </c:forEach>
        </div>

        <div class="row">
            <div class="col-md-6 mx-auto">
                <h2>Create or Edit Post</h2>
                <form action="${pageContext.request.contextPath}/home" method="post">
                    ...
                </form>
            </div>
        </div>
    </div>

    <div id="redirectMessage" style="display: none;">
        <p>Thank you, <span id="nicknamePlaceholder"></span>! Redirecting in <span id="countdown">5</span> seconds.</p>
    </div>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
    <script>
        ...
    </script>
</body>
  • Main Container:

    • Bootstrap Container: A div with the container class is used to center content and add a top margin (mt-5).
    • Welcome Message: A centered heading welcoming users to the blog.
  • Conditional Alert:

    • <c:if test="${not empty message}">: JSTL if tag that checks if the message attribute is not empty.
    • Alert Box: Displays a Bootstrap-styled alert box containing a message when a blog post is successfully saved, edited, or deleted.
  • Blog Post Listing:

    • <c:forEach items="${blogPosts}" var="post">: JSTL loop that iterates over a collection of blogPosts.
    • Post Cards: For each blog post, a Bootstrap card is displayed with the post's title, excerpt, author, and buttons for editing or deleting the post.
      • Edit and Delete Buttons: Use dynamic URLs constructed with JSP expressions and query parameters based on the post ID.
  • Form for Creating or Editing Posts:

    • Form Container: A Bootstrap row containing a form for creating or editing blog posts.
    • Form Fields: Includes input fields for the post ID (hidden), title, excerpt, and author, pre-filled with values if editPost is not null (indicating an edit operation).
    • Form Buttons: Includes buttons to save the post and return to the home page.
  • Redirect Message:

    • <div id="redirectMessage">: A hidden div that shows a countdown and a thank-you message when a user navigates back home.

JavaScript Logic

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
<script>
    document.getElementById('backHome').addEventListener('click', function(e) {
        e.preventDefault();
        var nickname = localStorage.getItem('userNickname') || 'Friend';
        document.getElementById('nicknamePlaceholder').textContent = nickname;
        document.getElementById('redirectMessage').style.display = 'block';
        var countdown = 5;
        var countdownElement = document.getElementById('countdown');
        var countdownInterval = setInterval(function() {
            countdown--;
            countdownElement.textContent = countdown;
            if (countdown <= 0) {
                clearInterval(countdownInterval);
                window.location.href = '${pageContext.request.contextPath}';
            }
        }, 1000);
    });
</script>
  • Bootstrap JS:

    • Loads Bootstrap's JavaScript library to support dynamic components like modals and buttons.
  • JavaScript for Redirect Message and Countdown:

    • Back Home Button Event Listener: Listens for clicks on the "Back Home" button. When clicked:
      • Prevent Default Action: Prevents the default hyperlink behavior.
      • Retrieve Nickname: Gets the user’s nickname from localStorage or defaults to 'Friend'.
      • Show Redirect Message: Displays a message thanking the user and begins a countdown.
      • Countdown Logic: Updates a countdown every second and redirects the user to the home page when the countdown reaches zero.

Usage and Integration

  • Dynamic Content with JSTL:

    • The use of JSTL allows the JSP to dynamically display content based on the server-side state. For example, it can loop through blog posts or conditionally display a success message.
  • Responsive Design with Bootstrap:

    • Bootstrap is used extensively to create a responsive and modern UI, including forms, buttons, alerts, and cards.
  • User Interaction and Personalization:

    • JavaScript enhances the user experience by adding interactive elements, such as showing a redirect message with a countdown. It also allows for some level of personalization, such as greeting users by their saved nickname.
  • Server-Side Integration:

    • The form submissions and dynamic links (Edit and Delete) are constructed to interact with server-side logic. Actions like creating, editing, and deleting posts are handled via HTTP requests to the backend.

7. HomeServletTest.java

The HomeServletTest.java file effectively tests the core functionalities of the HomeServlet class using JUnit and Mockito. By mocking servlet components and simulating different request scenarios, these tests ensure that the servlet correctly handles user interactions and maintains the intended behavior of the web application. This approach is crucial for maintaining code quality and reliability in a Java web application environment. It uses the Mockito framework to mock the HTTP request and response objects, as well as the RequestDispatcher to simulate the servlet environment. The tests verify the servlet's ability to handle various HTTP methods (GET and POST) and actions (submit, delete, and edit) correctly.

Command:

vi ~/advanced-java-webapp/src/test/java/com/example/webapp/servlet/HomeServletTest.java
package com.example.webapp.servlet;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

import static org.mockito.Mockito.*;

public class HomeServletTest {

    @Mock
    private HttpServletRequest request;
    @Mock
    private HttpServletResponse response;
    @Mock
    private RequestDispatcher requestDispatcher;

    private HomeServlet homeServlet;

    @Before
    public void setUp() throws Exception {
        MockitoAnnotations.initMocks(this);
        homeServlet = new HomeServlet();
        homeServlet.init(); // Initialize the servlet
        when(request.getRequestDispatcher("/WEB-INF/views/home.jsp")).thenReturn(requestDispatcher);
    }

    @Test
    public void testDoGet() throws ServletException, IOException {
        homeServlet.doGet(request, response);

        verify(request).setAttribute(eq("blogPosts"), any(List.class));
        verify(requestDispatcher).forward(request, response);
    }

    @Test
    public void testDoPost() throws ServletException, IOException {
        when(request.getParameter("action")).thenReturn("submit");
        when(request.getParameter("id")).thenReturn("");
        when(request.getParameter("title")).thenReturn("Test Title");
        when(request.getParameter("excerpt")).thenReturn("Test Excerpt");
        when(request.getParameter("author")).thenReturn("Test Author");

        homeServlet.doPost(request, response);

        verify(request).setAttribute(eq("blogPosts"), any(List.class));
        verify(requestDispatcher).forward(request, response);
    }

    @Test
    public void testDeletePost() throws ServletException, IOException {
        when(request.getParameter("action")).thenReturn("delete");
        when(request.getParameter("id")).thenReturn("1");

        homeServlet.doPost(request, response);

        verify(request).setAttribute(eq("blogPosts"), any(List.class));
        verify(requestDispatcher).forward(request, response);
    }

    @Test
    public void testEditPost() throws ServletException, IOException {
        when(request.getParameter("action")).thenReturn("edit");
        when(request.getParameter("id")).thenReturn("1");

        homeServlet.doGet(request, response);

        verify(request).setAttribute(eq("blogPosts"), any(List.class));
        verify(requestDispatcher).forward(request, response);
    }
}

Explanation:

Content Breakdown:

Package Declaration and Imports

package com.example.webapp.servlet;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;

import static org.mockito.Mockito.*;
  • Package Declaration: Declares that this class is part of the com.example.webapp.servlet package.
  • JUnit Imports:
    • org.junit.Before: Annotation to specify a method that should run before each test case.
    • org.junit.Test: Annotation to define test methods.
  • Mockito Imports:
    • org.mockito.Mock: Annotation to declare mock objects.
    • org.mockito.MockitoAnnotations: Utility class to initialize annotated mock objects.
  • Servlet Imports:
    • javax.servlet.*: Includes necessary classes for handling servlet requests, responses, and exceptions.
  • Mockito Static Imports:
    • static org.mockito.Mockito.*: Provides access to Mockito methods like when, verify, and any.

Class Declaration

public class HomeServletTest {
  • The HomeServletTest class is a public test class designed to test the functionality of the HomeServlet.

Mock Object Declarations

@Mock
private HttpServletRequest request;
@Mock
private HttpServletResponse response;
@Mock
private RequestDispatcher requestDispatcher;

private HomeServlet homeServlet;
  • Mock Annotations:
    • @Mock: Annotations are used to declare mock objects for HttpServletRequest, HttpServletResponse, and RequestDispatcher.
    • These mocks are initialized in the setUp() method and are used to simulate the behavior of servlet components.
  • HomeServlet Instance:
    • private HomeServlet homeServlet;: The servlet under test, which will be instantiated and initialized in the setUp() method.

@Before Setup Method

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
    homeServlet = new HomeServlet();
    homeServlet.init(); // Initialize the servlet
    when(request.getRequestDispatcher("/WEB-INF/views/home.jsp")).thenReturn(requestDispatcher);
}
  • @Before Annotation:
    • Specifies that the setUp() method should be executed before each test case to initialize the test environment.
  • Mockito Initialization:
    • MockitoAnnotations.initMocks(this);: Initializes the mock objects annotated with @Mock.
  • Servlet Initialization:
    • homeServlet = new HomeServlet();: Instantiates the servlet object to be tested.
    • homeServlet.init();: Calls the init() method to initialize the servlet (if necessary for your servlet setup).
  • RequestDispatcher Mock Setup:
    • when(request.getRequestDispatcher("/WEB-INF/views/home.jsp")).thenReturn(requestDispatcher);: Configures the mock HttpServletRequest to return a mock RequestDispatcher when getRequestDispatcher() is called with the specified path.

Test Methods

Each test method is annotated with @Test and verifies a different aspect of the servlet's behavior.

  1. testDoGet() Method
@Test
public void testDoGet() throws ServletException, IOException {
    homeServlet.doGet(request, response);

    verify(request).setAttribute(eq("blogPosts"), any(List.class));
    verify(requestDispatcher).forward(request, response);
}
  • Purpose: Tests the doGet() method of HomeServlet.
  • Method Call: Invokes the doGet() method on the homeServlet object.
  • Verification:

    • verify(request).setAttribute(eq("blogPosts"), any(List.class));: Ensures that the servlet sets the blogPosts attribute on the request object.
    • verify(requestDispatcher).forward(request, response);: Ensures that the request is forwarded to the correct JSP view (home.jsp).
  • testDoPost() Method

@Test
public void testDoPost() throws ServletException, IOException {
    when(request.getParameter("action")).thenReturn("submit");
    when(request.getParameter("id")).thenReturn("");
    when(request.getParameter("title")).thenReturn("Test Title");
    when(request.getParameter("excerpt")).thenReturn("Test Excerpt");
    when(request.getParameter("author")).thenReturn("Test Author");

    homeServlet.doPost(request, response);

    verify(request).setAttribute(eq("blogPosts"), any(List.class));
    verify(requestDispatcher).forward(request, response);
}
  • Purpose: Tests the doPost() method when creating or submitting a blog post.
  • Mock Setup: Configures the request parameters to simulate a form submission with a "submit" action and input values for title, excerpt, and author.
  • Method Call: Invokes the doPost() method on the homeServlet object.
  • Verification:

    • Checks if the servlet properly sets attributes and forwards the request as expected after handling a post submission.
  • testDeletePost() Method

@Test
public void testDeletePost() throws ServletException, IOException {
    when(request.getParameter("action")).thenReturn("delete");
    when(request.getParameter("id")).thenReturn("1");

    homeServlet.doPost(request, response);

    verify(request).setAttribute(eq("blogPosts"), any(List.class));
    verify(requestDispatcher).forward(request, response);
}
  • Purpose: Tests the doPost() method for the "delete" action.
  • Mock Setup: Mocks request parameters to simulate deleting a blog post with ID "1".
  • Method Call: Invokes the doPost() method on the homeServlet.
  • Verification:

    • Ensures that the appropriate request attributes are set and the request is forwarded correctly.
  • testEditPost() Method

@Test
public void testEditPost() throws ServletException, IOException {
    when(request.getParameter("action")).thenReturn("edit");
    when(request.getParameter("id")).thenReturn("1");

    homeServlet.doGet(request, response);

    verify(request).setAttribute(eq("blogPosts"), any(List.class));
    verify(requestDispatcher).forward(request, response);
}
  • Purpose: Tests the doGet() method for the "edit" action.
  • Mock Setup: Mocks request parameters to simulate an edit action for a blog post with ID "1".
  • Method Call: Invokes the doGet() method on the homeServlet.
  • Verification:
    • Checks that the servlet handles the edit action correctly by setting the necessary attributes and forwarding the request.

Usage and Integration

  • Mocking with Mockito:
    • The use of Mockito enables isolation of the servlet's logic from its dependencies (such as the HTTP request and response), allowing the tests to focus solely on the servlet's behavior.
  • JUnit Annotations:
    • @Before and @Test annotations organize the test lifecycle, ensuring that the servlet and its dependencies are properly initialized before each test and that each method is correctly identified as a test case.
  • Verifying Servlet Behavior:
    • verify() methods are used to ensure that the servlet interacts with the request and response objects as expected, such as setting attributes and forwarding requests.
  • Testing Different Actions:
    • The test cases cover multiple scenarios (GET requests, POST requests with different actions), ensuring comprehensive coverage of the servlet's functionality.

8. BlogPostTest.java

The BlogPostTest.java file provides thorough testing of the BlogPost class nested within the HomeServlet. The tests validate the constructor's ability to initialize fields correctly and the setters' ability to modify fields appropriately. By ensuring that both construction and modification behaviors are tested, this class helps maintain the integrity and reliability of the BlogPost data model within the Java web application.

Command:

vi ~/advanced-java-webapp/src/test/java/com/example/webapp/model/BlogPostTest.java
package com.example.webapp.model;

import org.junit.Test;
import static org.junit.Assert.*;
import com.example.webapp.servlet.HomeServlet;

public class BlogPostTest {

    @Test
    public void testBlogPostCreation() {
        HomeServlet.BlogPost post = new HomeServlet.BlogPost(1, "Test Title", "Test Excerpt", "Test Author");

        assertEquals(1, post.getId());
        assertEquals("Test Title", post.getTitle());
        assertEquals("Test Excerpt", post.getExcerpt());
        assertEquals("Test Author", post.getAuthor());
    }

    @Test
    public void testBlogPostSetters() {
        HomeServlet.BlogPost post = new HomeServlet.BlogPost(1, "Initial Title", "Initial Excerpt", "Initial Author");

        post.setTitle("Updated Title");
        post.setExcerpt("Updated Excerpt");
        post.setAuthor("Updated Author");

        assertEquals("Updated Title", post.getTitle());
        assertEquals("Updated Excerpt", post.getExcerpt());
        assertEquals("Updated Author", post.getAuthor());
    }
}

Explanation:

Content Breakdown:

Package Declaration and Imports

package com.example.webapp.model;

import org.junit.Test;
import static org.junit.Assert.*;
import com.example.webapp.servlet.HomeServlet;
  • Package Declaration: Declares that this class is part of the com.example.webapp.model package.
  • JUnit Imports:
    • org.junit.Test: This annotation is used to indicate that a method is a JUnit test case.
  • JUnit Assertion Imports:
    • static org.junit.Assert.*: Provides access to assertion methods such as assertEquals to validate test outcomes.
  • HomeServlet Import:
    • com.example.webapp.servlet.HomeServlet: Imports the HomeServlet class to access the nested BlogPost class that resides within HomeServlet.

Class Declaration

public class BlogPostTest {
  • The BlogPostTest class is a public test class designed to test the BlogPost data model nested within the HomeServlet class.

Test Methods

Each test method is annotated with @Test and is responsible for verifying different functionalities of the BlogPost class.

  1. testBlogPostCreation() Method
@Test
public void testBlogPostCreation() {
    HomeServlet.BlogPost post = new HomeServlet.BlogPost(1, "Test Title", "Test Excerpt", "Test Author");

    assertEquals(1, post.getId());
    assertEquals("Test Title", post.getTitle());
    assertEquals("Test Excerpt", post.getExcerpt());
    assertEquals("Test Author", post.getAuthor());
}
  • Purpose: Tests the creation of a BlogPost object and verifies that all fields are correctly initialized.
  • Object Instantiation:
    • HomeServlet.BlogPost post = new HomeServlet.BlogPost(1, "Test Title", "Test Excerpt", "Test Author");: Creates a new instance of BlogPost with an ID, title, excerpt, and author.
  • Assertions:
    • assertEquals(1, post.getId());: Verifies that the ID of the BlogPost object is correctly set to 1.
    • assertEquals("Test Title", post.getTitle());: Ensures the title is "Test Title".
    • assertEquals("Test Excerpt", post.getExcerpt());: Ensures the excerpt is "Test Excerpt".
    • assertEquals("Test Author", post.getAuthor());: Ensures the author is "Test Author".
  • Outcome: Confirms that the constructor of BlogPost properly initializes its fields.

  • testBlogPostSetters() Method

@Test
public void testBlogPostSetters() {
    HomeServlet.BlogPost post = new HomeServlet.BlogPost(1, "Initial Title", "Initial Excerpt", "Initial Author");

    post.setTitle("Updated Title");
    post.setExcerpt("Updated Excerpt");
    post.setAuthor("Updated Author");

    assertEquals("Updated Title", post.getTitle());
    assertEquals("Updated Excerpt", post.getExcerpt());
    assertEquals("Updated Author", post.getAuthor());
}
  • Purpose: Tests the setter methods of the BlogPost class to ensure they correctly update the fields.
  • Object Instantiation:
    • HomeServlet.BlogPost post = new HomeServlet.BlogPost(1, "Initial Title", "Initial Excerpt", "Initial Author");: Creates a new BlogPost object with initial values for each field.
  • Method Calls:
    • post.setTitle("Updated Title");: Updates the title of the BlogPost to "Updated Title".
    • post.setExcerpt("Updated Excerpt");: Updates the excerpt to "Updated Excerpt".
    • post.setAuthor("Updated Author");: Updates the author to "Updated Author".
  • Assertions:
    • assertEquals("Updated Title", post.getTitle());: Verifies that the title has been correctly updated.
    • assertEquals("Updated Excerpt", post.getExcerpt());: Verifies that the excerpt has been correctly updated.
    • assertEquals("Updated Author", post.getAuthor());: Verifies that the author has been correctly updated.
  • Outcome: Confirms that the setter methods (setTitle, setExcerpt, setAuthor) correctly modify the fields of the BlogPost object.

Usage and Integration

  • JUnit Annotations:
    • @Test: Indicates that the methods are test cases, allowing JUnit to execute them as part of the test suite.
  • Assertions:
    • assertEquals: Used extensively to check if the actual value matches the expected value. This is critical for verifying the correctness of object initialization and modification.
  • Testing Object State:
    • The test cases ensure that both the creation (constructor) and modification (setters) of a BlogPost object maintain consistent and expected state.
  • Verification of Behavior:
    • By testing both the getters and setters, these tests provide comprehensive coverage of the BlogPost model's behavior, ensuring that any changes to these methods in the future will be immediately detected if they deviate from the expected functionality.

5. Maven Build Process for our application 🚀

First let's dive into the individual Maven build process and how it integrates with Tomcat for your Java application! 😊

  1. mvn clean 🧹

    • Purpose: Cleans up the target directory, removing all files generated by the previous build.
    • Sample Output:
      [INFO] Scanning for projects...
      [INFO] 
      [INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ my-app ---
      [INFO] Deleting /root/advanced-java-webapp/target
      [INFO] 
      [INFO] BUILD SUCCESS
      [INFO] 
      [INFO] Total time: 0.5 s
      [INFO] Finished at: 2024-08-31T14:00:00+00:00
      [INFO] ------------------------------------------------------------------------
      
  2. mvn compile 🔄

    • Purpose: Compiles the source code of the project.
    • Sample Output:
      [INFO] Scanning for projects...
      [INFO] 
      [INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ my-app ---
      [INFO] Compiling 5 source files to /root/advanced-java-webapp/target/classes
      [INFO] 
      [INFO] BUILD SUCCESS
      [INFO] 
      [INFO] Total time: 1.2 s
      [INFO] Finished at: 2024-08-31T14:05:00+00:00
      [INFO] ------------------------------------------------------------------------
      
  3. mvn validate

    • Purpose: Validates the project and checks if all necessary information is available.
    • Sample Output:
      [INFO] Scanning for projects...
      [INFO] 
      [INFO] --- maven-validation-plugin:2.0:validate (default-validate) @ my-app ---
      [INFO] Validation successful
      [INFO] 
      [INFO] BUILD SUCCESS
      [INFO] 
      [INFO] Total time: 0.3 s
      [INFO] Finished at: 2024-08-31T14:10:00+00:00
      [INFO] ------------------------------------------------------------------------
      
  4. mvn package 📦

    • Purpose: Packages the compiled code into a distributable format, like a .jar or .war file.
    • Sample Output:
      [INFO] Scanning for projects...
      [INFO] 
      [INFO] --- maven-jar-plugin:3.2.0:jar (default-jar) @ my-app ---
      [INFO] Building jar: /root/advanced-java-webapp/target/advanced-java-webapp.war
      [INFO] 
      [INFO] BUILD SUCCESS
      [INFO] 
      [INFO] Total time: 2.0 s
      [INFO] Finished at: 2024-08-31T14:15:00+00:00
      [INFO] ------------------------------------------------------------------------
      
  5. mvn test 🧪

    • Purpose: Runs the unit tests using a suitable testing framework.
    • Sample Output:
      [INFO] Scanning for projects...
      [INFO] 
      [INFO] --- maven-surefire-plugin:3.0.0-M5:test (default-test) @ my-app ---
      [INFO] Surefire report directory: /root/advanced-java-webapp/target/surefire-reports
      [INFO] 
      [INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.5 s
      [INFO] 
      [INFO] BUILD SUCCESS
      [INFO] 
      [INFO] Total time: 1.5 s
      [INFO] Finished at: 2024-08-31T14:20:00+00:00
      [INFO] ------------------------------------------------------------------------
      
  6. mvn clean install 🔄🛠️
    • Purpose: Cleans the project and then builds it. It also installs the package into the local repository.
    • Sample Output:
      [INFO] Scanning for projects...
      [INFO] 
      [INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ my-app ---
      [INFO] Deleting /root/advanced-java-webapp/target
      [INFO] 
      [INFO] --- maven-resources-plugin:3.2.0:resources (default-resources) @ my-app ---
      [INFO] Using 'UTF-8' encoding to copy filtered resources.
      [INFO] Copying 2 resources
      [INFO] 
      [INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ my-app ---
      [INFO] Compiling 5 source files to /root/advanced-java-webapp/target/classes
      [INFO] 
      [INFO] --- maven-resources-plugin:3.2.0:testResources (default-testResources) @ my-app ---
      [INFO] Using 'UTF-8' encoding to copy filtered resources.
      [INFO] Copying 1 resource
      [INFO] 
      [INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ my-app ---
      [INFO] Compiling 2 test sources to /root/advanced-java-webapp/target/test-classes
      [INFO] 
      [INFO] --- maven-surefire-plugin:3.0.0-M5:test (default-test) @ my-app ---
      [INFO] Surefire report directory: /root/advanced-java-webapp/target/surefire-reports
      [INFO] 
      [INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.7 s
      [INFO] 
      [INFO] --- maven-jar-plugin:3.2.0:jar (default-jar) @ my-app ---
      [INFO] Building jar: /root/advanced-java-webapp/target/advanced-java-webapp.war
      [INFO] 
      [INFO] --- maven-install-plugin:2.5.0:install (default-install) @ my-app ---
      [INFO] Installing /root/advanced-java-webapp/target/advanced-java-webapp.war to /root/.m2/repository/com/example/advanced-java-webapp/1.0-SNAPSHOT/advanced-java-webapp-1.0-SNAPSHOT.war
      [INFO] 
      [INFO] BUILD SUCCESS
      [INFO] 
      [INFO] Total time: 5.0 s
      [INFO] Finished at: 2024-08-31T14:30:00+00:00
      [INFO] ------------------------------------------------------------------------
      
      In our case, since the application is built first time, we can use mvn package or mvn install or mvn clean package(OR)install

Using Maven Build Process with mvn clean install as per our case

Command:

mvn clean install
  • Explanation: This command performs a series of steps:

    1. clean: Deletes the target directory to ensure a fresh build.
    2. validate: Validates the project’s configuration.
    3. compile: Compiles the source code.
    4. test: Runs unit tests.
    5. package: Packages the code into a JAR/WAR file.
    6. install: Installs the packaged artifact into the local Maven repository (.m2 directory).

Artifact Locations

  • target Directory:

    • This is where Maven places the final WAR file. The default location is advanced-java-webapp/target/advanced-java-webapp.war.
    • Configuration in pom.xml: The <build> section in pom.xml defines the output directory for artifacts.
    • Example configuration:
      <build>
          <finalName>advanced-java-webapp</finalName>
      </build>
      
  • Local Maven Repository (.m2 directory): The artifact & pom is also copied to the local Maven repository for use in other Maven projects.

    • Path:
      ~/.m2/repository/com
      ├── example
      │   └── advanced-java-webapp
      │       ├── 1.0-SNAPSHOT
      │       │   ├── _remote.repositories
      │       │   ├── advanced-java-webapp-1.0-SNAPSHOT.pom
      │       │   ├── advanced-java-webapp-1.0-SNAPSHOT.war
      │       │   └── maven-metadata-local.xml
      │       └── maven-metadata-local.xml
      

    Configuration in pom.xml by which this path in .m2 is created

    <groupId>com.example</groupId>
    <artifactId>advanced-java-webapp</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
    

Final Project Directory Structure with Artifacts 📂

~/advanced-java-webapp
├── pom.xml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── example
│   │   │           └── webapp
│   │   │               ├── model
│   │   │               │   └── BlogPost.java
│   │   │               ├── servlet
│   │   │               │   └── HomeServlet.java
│   │   │               └── views
│   │   ├── resources
│   │   └── webapp
│   │       ├── WEB-INF
│   │       │   ├── views
│   │       │   │   └── home.jsp
│   │       │   └── web.xml
│   │       └── index.jsp
│   └── test
│       └── java
│           └── com
│               └── example
│                   └── webapp
│                       ├── model
│                       │   └── BlogPostTest.java
│                       └── servlet
│                           └── HomeServletTest.java
└── target
    ├── advanced-java-webapp
    │   ├── META-INF
    │   ├── WEB-INF
    │   │   ├── classes
    │   │   │   └── com
    │   │   │       └── example
    │   │   │           └── webapp
    │   │   │               ├── model
    │   │   │               │   └── BlogPost.class
    │   │   │               └── servlet
    │   │   │                   ├── HomeServlet$BlogPost.class
    │   │   │                   └── HomeServlet.class
    │   │   ├── lib
    │   │   │   └── jstl-1.2.jar
    │   │   ├── views
    │   │   │   └── home.jsp
    │   │   └── web.xml
    │   └── index.jsp
    ├── advanced-java-webapp.war
    ├── classes
    │   └── com
    │       └── example
    │           └── webapp
    │               ├── model
    │               │   └── BlogPost.class
    │               └── servlet
    │                   ├── HomeServlet$BlogPost.class
    │                   └── HomeServlet.class
    ├── generated-sources
    │   └── annotations
    ├── generated-test-sources
    │   └── test-annotations
    ├── maven-archiver
    │   └── pom.properties
    ├── maven-status
    │   └── maven-compiler-plugin
    │       ├── compile
    │       │   └── default-compile
    │       │       ├── createdFiles.lst
    │       │       └── inputFiles.lst
    │       └── testCompile
    │           └── default-testCompile
    │               ├── createdFiles.lst
    │               └── inputFiles.lst
    ├── surefire-reports
    │   ├── TEST-com.example.webapp.model.BlogPostTest.xml
    │   ├── TEST-com.example.webapp.servlet.HomeServletTest.xml
    │   ├── com.example.webapp.model.BlogPostTest.txt
    │   └── com.example.webapp.servlet.HomeServletTest.txt
    └── test-classes
        └── com
            └── example
                └── webapp
                    ├── model
                    │   └── BlogPostTest.class
                    └── servlet
                        └── HomeServletTest.class

Detailed explanation of the final directory structure and files after running the Maven build for advanced-java-webapp project:

Root Directory

~/advanced-java-webapp
├── pom.xml
  • pom.xml: This is the Maven Project Object Model (POM) file. It contains configuration information about the project, including dependencies, plugins, and build settings.

Source Code Directory

├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── example
│   │   │           └── webapp
│   │   │               ├── model
│   │   │               │   └── BlogPost.java
│   │   │               ├── servlet
│   │   │               │   └── HomeServlet.java
│   │   │               └── views
│   │   ├── resources
│   │   └── webapp
│   │       ├── WEB-INF
│   │       │   ├── views
│   │       │   │   └── home.jsp
│   │       │   └── web.xml
│   │       └── index.jsp
│   └── test
│       └── java
│           └── com
│               └── example
│                   └── webapp
│                       ├── model
│                       │   └── BlogPostTest.java
│                       └── servlet
│                           └── HomeServletTest.java
  • src/main/java: Contains the main application source code.

    • com/example/webapp/model/BlogPost.java: Defines the BlogPost class.
    • com/example/webapp/servlet/HomeServlet.java: Contains the HomeServlet class.
    • com/example/webapp/views: Contains view-related files (though it’s empty in this structure).
  • src/main/resources: Used for non-Java resources (empty in this case).

  • src/main/webapp: Contains web application resources.

    • WEB-INF: Contains configuration and internal files.
      • views/home.jsp: JSP file for rendering the home page.
      • web.xml: Deployment descriptor for configuring servlets and other web components.
    • index.jsp: The default JSP file served when accessing the web application.
  • src/test/java: Contains test source code.

    • com/example/webapp/model/BlogPostTest.java: Unit tests for the BlogPost class.
    • com/example/webapp/servlet/HomeServletTest.java: Unit tests for the HomeServlet class.

Target Directory

└── target
    ├── advanced-java-webapp
    │   ├── META-INF
    │   ├── WEB-INF
    │   │   ├── classes
    │   │   │   └── com
    │   │   │       └── example
    │   │   │           └── webapp
    │   │   │               ├── model
    │   │   │               │   └── BlogPost.class
    │   │   │               └── servlet
    │   │   │                   ├── HomeServlet$BlogPost.class
    │   │   │                   └── HomeServlet.class
    │   │   ├── lib
    │   │   │   └── jstl-1.2.jar
    │   │   ├── views
    │   │   │   └── home.jsp
    │   │   └── web.xml
    │   └── index.jsp
    ├── advanced-java-webapp.war
    ├── classes
    │   └── com
    │       └── example
    │           └── webapp
    │               ├── model
    │               │   └── BlogPost.class
    │               └── servlet
    │                   ├── HomeServlet$BlogPost.class
    │                   └── HomeServlet.class
    ├── generated-sources
    │   └── annotations
    ├── generated-test-sources
    │   └── test-annotations
    ├── maven-archiver
    │   └── pom.properties
    ├── maven-status
    │   └── maven-compiler-plugin
    │       ├── compile
    │       │   └── default-compile
    │       │       ├── createdFiles.lst
    │       │       └── inputFiles.lst
    │       └── testCompile
    │           └── default-testCompile
    │               ├── createdFiles.lst
    │               └── inputFiles.lst
    ├── surefire-reports
    │   ├── TEST-com.example.webapp.model.BlogPostTest.xml
    │   ├── TEST-com.example.webapp.servlet.HomeServletTest.xml
    │   ├── com.example.webapp.model.BlogPostTest.txt
    │   └── com.example.webapp.servlet.HomeServletTest.txt
    └── test-classes
        └── com
            └── example
                └── webapp
                    ├── model
                    │   └── BlogPostTest.class
                    └── servlet
                        └── HomeServletTest.class
  • target/advanced-java-webapp: The directory structure for the packaged WAR file.

    • META-INF: Contains metadata files.
    • WEB-INF: Contains the compiled classes and other resources.
      • classes: Contains compiled .class files.
        • com/example/webapp/model/BlogPost.class: Compiled class for BlogPost.
        • com/example/webapp/servlet/HomeServlet.class: Compiled class for HomeServlet.
        • com/example/webapp/servlet/HomeServlet$BlogPost.class: Compiled class for the inner BlogPost class within HomeServlet.
      • lib: Contains libraries used by the application (e.g., JSTL).
        • jstl-1.2.jar: The JSTL library.
      • views/home.jsp: JSP file copied from the src/main/webapp directory.
      • web.xml: Deployment descriptor copied from the src/main/webapp/WEB-INF directory.
    • index.jsp: JSP file copied from the src/main/webapp directory.
  • target/advanced-java-webapp.war: The packaged WAR file. This is the final deployable artifact.

  • target/classes: Contains compiled classes for the main source code.

    • com/example/webapp/model/BlogPost.class: Compiled class for BlogPost.
    • com/example/webapp/servlet/HomeServlet.class: Compiled class for HomeServlet.
    • com/example/webapp/servlet/HomeServlet$BlogPost.class: Compiled class for the inner BlogPost class within HomeServlet.
  • target/generated-sources: Contains sources generated during the build (e.g., annotation processing).

    • annotations: Directory for annotation-generated sources.
  • target/generated-test-sources: Contains sources generated for testing.

    • test-annotations: Directory for test-related annotations.
  • target/maven-archiver: Contains metadata about the build.

    • pom.properties: Properties file with information about the POM and project.
  • target/maven-status: Contains status files for Maven plugins.

    • maven-compiler-plugin:
      • compile/default-compile: Contains lists of files created or used during compilation.
      • testCompile/default-testCompile: Contains lists of files created or used during test compilation.
  • target/surefire-reports: Contains reports from test execution.

    • TEST-com.example.webapp.model.BlogPostTest.xml: Test results in XML format for BlogPostTest.
    • TEST-com.example.webapp.servlet.HomeServletTest.xml: Test results in XML format for HomeServletTest.
    • com.example.webapp.model.BlogPostTest.txt: Test results in text format for BlogPostTest.
    • com.example.webapp.servlet.HomeServletTest.txt: Test results in text format for HomeServletTest.
  • target/test-classes: Contains compiled classes for the test source code.

    • com/example/webapp/model/BlogPostTest.class: Compiled class for BlogPostTest.
    • com/example/webapp/servlet/HomeServletTest.class: Compiled class for HomeServletTest.

6. Deploying to Tomcat 🌐

Tomcat Service Management

  1. Stopping Tomcat 🚫

    • Before deploying your .war file, ensure Tomcat is not running.
    • Commands:

      /opt/apache-tomcat-8.0.1/bin/catalina.sh stop
      

      (OR)

      /opt/apache-tomcat-8.0.1/bin/shutdown.sh
      
    • Sample Output:
      Using CATALINA_BASE:   /opt/apache-tomcat-8.0.1
      Using CATALINA_HOME:   /opt/apache-tomcat-8.0.1
      Using CATALINA_TMPDIR: /opt/apache-tomcat-8.0.1/temp
      Using JRE_HOME:       /usr/lib/jvm/java-8-openjdk
      Using CLASSPATH:      /opt/apache-tomcat-8.0.1/bin/bootstrap.jar:/opt/apache-tomcat-8.0.1/bin/tomcat-juli.jar
      Stopping Tomcat server from /opt/apache-tomcat-8.0.1
      
    • Note: If the Tomcat service is not started & when you run stop command, before its started, you might see this error,
      Using CATALINA_BASE:   /opt/apache-tomcat-8.0.1
      Using CATALINA_HOME:   /opt/apache-tomcat-8.0.1
      Using CATALINA_TMPDIR: /opt/apache-tomcat-8.0.1/temp
      Using JRE_HOME:        /opt/jdk1.8.0_131
      Using CLASSPATH:       /opt/apache-tomcat-8.0.1/bin/bootstrap.jar:/opt/apache-tomcat-8.0.1/bin/tomcat-juli.jar
      Sep 01, 2024 1:31:20 PM org.apache.catalina.startup.Catalina stopServer
      SEVERE: Could not contact localhost:8005. Tomcat may not be running.
      Sep 01, 2024 1:31:20 PM org.apache.catalina.startup.Catalina stopServer
      SEVERE: Catalina.stop:
      java.net.ConnectException: Connection refused (Connection refused)
      
  2. Deploy the WAR file:

    • Copy the WAR file to the Tomcat webapps directory.
    • Command:
      cp ~/advanced-java-webapp/target/advanced-java-webapp.war /opt/apache-tomcat-8.0.1/webapps/
      
    • Tomcat automatically deploys the WAR file once copied & the service is started.
  3. Starting Tomcat 🚀

    • Commands:

      /opt/apache-tomcat-8.0.1/bin/catalina.sh start
      

      (OR)

      /opt/apache-tomcat-8.0.1/bin/startup.sh
      
    • Sample Output:
      Using CATALINA_BASE:   /opt/apache-tomcat-8.0.1
      Using CATALINA_HOME:   /opt/apache-tomcat-8.0.1
      Using CATALINA_TMPDIR: /opt/apache-tomcat-8.0.1/temp
      Using JRE_HOME:       /usr/lib/jvm/java-8-openjdk
      Using CLASSPATH:      /opt/apache-tomcat-8.0.1/bin/bootstrap.jar:/opt/apache-tomcat-8.0.1/bin/tomcat-juli.jar
      Starting Tomcat server from /opt/apache-tomcat-8.0.1
      

Accessing Your Web Application

  • Once Tomcat starts, it will deploy your .war file. A folder with the name of your artifact (e.g., my-app) will be created in /opt/apache-tomcat-8.0.1/webapps.
  • Website will be accessible at: http://<hostname>:8080/<artifactname> (e.g., http://localhost:8080/advanced-java-webapp).

Environment Variables

  • $CATALINA_HOME: Points to the Tomcat installation directory (/opt/apache-tomcat-8.0.1). It helps Tomcat locate its configuration, libraries, and scripts.
  • Web Application Folder: Your web application is placed in $CATALINA_HOME/webapps/advanced-java-webapp.

Tomcat Configuration

  1. Access Manager Home URL 🔑

    • Modify /opt/apache-tomcat-8.0.1/conf/tomcat-users.xml to add users and roles for the Tomcat Manager.
    • Example Configuration:
      <tomcat-users>
        <role rolename="manager-gui"/>
        <user username="admin" password="admin" roles="manager-gui"/>
      </tomcat-users>
      
  2. Configuration Changes 🛠️

    • Modify /opt/apache-tomcat-8.0.1/conf/catalina.properties to adjust various properties such as classpath settings, logging levels, etc.
    • Example Configuration:
      # Customize the classpath for additional libraries
      common.loader=${catalina.base}/lib,/path/to/extra/libs/*.jar
      
  3. Access Logs 📜

    • Logs are stored in /opt/apache-tomcat-8.0.1/logs. Check files like catalina.out for application logs, and localhost_access_log.*.txt for access logs.

Summary of build process 🌟

  1. Maven Build:

    • Use commands like mvn clean, mvn compile, mvn validate, mvn package, mvn test, and mvn clean install to manage the build process.
    • mvn clean install builds and installs the artifact into your local Maven repository.
  2. Deployment to Tomcat:

    • Ensure Tomcat is stopped before deploying.
    • Copy the WAR file to the webapps directory.
    • Start Tomcat to deploy your .war file.
    • Access your application at http://<hostname>:8080/<artifactname>.
    • Manage Tomcat using scripts and configuration files found in $CATALINA_HOME.
  3. Configuration and Logs:

    • $CATALINA_HOME defines the Tomcat directory.
    • Manage Tomcat using shutdown.sh and startup.sh.
    • Configure Tomcat Manager access and other settings in tomcat-users.xml and catalina.properties.
    • Check logs in the logs directory.