maven-jar-plugin maven-war-plugin

In the Maven build lifecycle, packaging represents one of the most critical phases for Java projects. Depending on the project type, the two most common packaging formats are JAR (Java Archive) and WAR (Web Archive). Maven provides dedicated plugins to handle each format: maven-jar-plugin and maven-war-plugin. Understanding their default behaviors, configuration options, and appropriate use cases enables developers to tailor build outputs for diverse deployment and distribution scenarios.

maven-jar-plugin

The maven-jar-plugin is responsible for packaging compiled class files and associated resources into a JAR archive. It defaults to binding during the package phase and executing the jar:jar goal. For standard Java libraries or executable applications, this plugin serves as the default packaging mechanism and typically requires no explicit declaration to function.

Common Goals

  • jar:jar – Creates a JAR file from the project's output.
  • jar:test-jar – Generates a JAR containing only test-classes.
  • jar:manifest – Outputs a MANIFEST.MF file (rarely used in isolation).

The default output path follows the convention target/${project.build.finalName}.jar.

Configuration: Executable JAR

To produce a JAR that can be launched directly via java -jar, the manifest must declare the main class and classpath settings. A typical configuration appears as follows:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-jar-plugin</artifactId>
  <version>3.3.0</version>
  <configuration>
    <archive>
      <manifest>
        <mainClass>com.example.Main</mainClass>
        <addClasspath>true</addClasspath>
        <classpathPrefix>lib/</classpathPrefix>
      </manifest>
    </archive>
  </configuration>
</plugin>

This setup writes Main-Class and Class-Path entries into the manifest, facilitating standalone execution. The addClasspath flag automatically calculates relative paths, while classpathPrefix prefixes classpath entries, which is especially useful when dependencies reside in a lib directory.

Including and Excluding Content

The plugin processes files located under target/classes. Resources from source directories are handled by maven-resources-plugin prior to JAR packaging. However, the content within the classes directory can be further filtered using <excludes> and <includes>:

<configuration>
  <excludes>
    <exclude>**/*.properties</exclude>
    <exclude>**/test/**</exclude>
  </excludes>
</configuration>

Such configurations allow fine-grained control over which resources make it into the final archive, helping reduce size and avoid unnecessary inclusion of test or locale files.

maven-war-plugin

For web applications, maven-war-plugin packages the project into a WAR file. It similarly binds to the package phase and runs the war:war goal by default. A WAR archive follows the standard Java EE/web application structure, containing WEB-INF/classes, WEB-INF/lib, static assets, and optionally WEB-INF/web.xml.

Common Goals

  • war:war – Produces a standard WAR file.
  • war:exploded – Generates a directory-based representation, useful for development and hot deployment.
  • war:inplace – Creates the exploded structure directly within the source tree.

The default output path is target/${project.build.finalName}.war.

Configuration: Omitting web.xml

Starting with Servlet 3.0, a web application can operate without a deployment descriptor. However, the plugin will fail by default if web.xml is absent. Disabling this check is straightforward:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-war-plugin</artifactId>
  <version>3.4.0</version>
  <configuration>
    <failOnMissingWebXml>false</failOnMissingWebXml>
  </configuration>
</plugin>

With this setting, the build proceeds even when the application relies on annotations and programmatic configuration rather than XML.

Adding External Web Resources

Frontend assets may not always live under the conventional src/main/webapp directory. The webResources element enables inclusion of arbitrary directories:

<configuration>
  <webResources>
    <resource>
      <directory>src/main/frontend/dist</directory>
      <targetPath>static</targetPath>
      <filtering>false</filtering>
    </resource>
  </webResources>
</configuration>

This configuration copies the contents of dist into the WAR’s static path, preserving the original file structure and disabling Maven resource filtering to avoid unintended variable substitution.

Custom MANIFEST and File Exclusion

Like its JAR counterpart, the WAR plugin supports <archive> customization for the manifest and file filtering:

<configuration>
  <archive>
    <manifest>
      <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
    </manifest>
  </archive>
  <packagingExcludes>**/*.log, WEB-INF/lib/old-*.jar</packagingExcludes>
</configuration>

The packagingExcludes pattern is particularly helpful for stripping log files or removing conflicting third-party JARs during the build, resulting in a leaner deployment artifact.

Comparison and Selection Criteria

Aspect maven-jar-plugin maven-war-plugin
Primary Purpose Packaging Java libraries or executable programs Packaging web applications for servlet container deployment
Typical Structure Package-level classes and resources WEB-INF/classes, WEB-INF/lib, static resources, optional web.xml
Default Binding package phase, jar:jar goal package phase, war:war goal
Dependency Handling Does not include dependencies by default (unless shaded/assembled) Automatically includes compile and runtime scope dependencies into WEB-INF/lib
Key Configuration Focus Main-Class, Class-Path, resource excludes/includes failOnMissingWebXml, webResources, packagingExcludes, archive manifest entries
Common Use Case JAR distribution, standalone execution, module dependencies WAR deployment to Tomcat, Jetty, WildFly, or other application servers

Choosing between the two depends primarily on the nature of the artifact: a JAR suits standalone logic, reusable libraries, or Spring Boot fat JARs, while a WAR is the correct format for any application intended to run inside a container that manages the servlet lifecycle.

Best Practices and Common Considerations

  • Spring Boot Projects: Most Spring Boot applications do not use these plugins directly. Instead, the spring-boot-maven-plugin re-packages a JAR or WAR into an executable format with embedded server support.
  • Multi-module Projects: A parent POM can declare plugin versions and baseline configurations, while child modules override specific settings as needed. This pattern centralizes plugin management and ensures consistency across modules.
  • Clean Builds: Running mvn clean package before a final build helps eliminate stale artifacts and ensures the output reflects the current source state.
  • Executable JARs with External Dependencies: For JARs that rely on external libraries not shaded into the archive, the maven-dependency-plugin can copy dependencies into a lib folder, and the JAR manifest’s Class-Path element can reference them by relative path.
  • Development Workflow: The war:exploded goal is frequently used during development. It produces a directory structure that can be directly deployed to a running container, enabling hot-reloading and faster iteration cycles.
  • Resource Filtering: Be mindful of filtering settings, especially when including properties files or configuration artifacts. Disabling filtering (<filtering>false</filtering>) prevents Maven from replacing ${...} placeholders, which is often desired for binary or pre-processed assets.

By thoughtfully configuring maven-jar-plugin and maven-war-plugin, project teams can precisely control the contents, structure, and launchability of their build artifacts. Whether the goal is distributing a reusable Java library, deploying a web application to a production container, or streamlining a development workflow, these plugins provide the granular control necessary to meet a wide spectrum of project requirements.