The packaging type is an important aspect of any Maven project. It specifies the type of artifact the project produces. Generally, a build produces a jar, war, pom, or other executable.
Maven offers many default packaging types and also provides the flexibility to define a custom one.
In this tutorial, we’ll take a deep dive into Maven packaging types. First, we’ll look at the build lifecycles in Maven. Then, we’ll discuss each packaging type, what they represent, and their effect on the project’s lifecycle. In the end, we’ll see how to define a custom packaging type.
2. Default Packaging Types
Maven offers many default packaging types that include a jar, war, ear, pom, rar, ejb, and maven-plugin. Each packaging type follows a build lifecycle that consists of phases. Usually, every phase is a sequence of goals and performs a specific task.
Different packaging types may have different goals in a particular phase. For example, in the package phase of jar packaging type, maven-jar-plugin‘s jar goal is executed. Conversely, for a war project, maven-war-plugin‘s war goal is executed in the same phase.
Java archive – or jar – is one of the most popular packaging types. Projects with this packaging type produce a compressed zip file with the .jar extension. It may include pure Java classes, interfaces, resources, and metadata files.
To begin with, let’s look at some of the default goal-to-build-phase bindings for the jar:
- resources: resources
- compiler: compile
- resources: testResources
- compiler: testCompile
- surefire: test
- jar: jar
- install: install
- deploy: deploy
Without delay, let’s define the packaging type of a jar project:
If nothing has been specified, Maven assumes the packaging type is a jar.
Simply put, a web application archive – or war – contains all files related to a web application. It may include Java servlets, JSPs, HTML pages, a deployment descriptor, and related resources. Overall, war has the same goal bindings as a jar, but with one exception —the package phase of the war has a different goal, which is war.
Without a doubt, jar and war are the most popular packaging types in the Java community. A detailed difference between these two might be an interesting read.
Let’s define the packaging type of a web application:
The other packaging types ejb, par, and rar also have similar lifecycles, but each has a different package goal.
ejb:ejb or par:par or rar:rar
Enterprise application archive – or ear – is a compressed file that contains a J2EE application. It consists of one or more modules that can be either web modules (packaged as a war file) or EJB modules (packaged as a jar file) or both of them.
To put it differently, the ear is a superset of jars and wars and requires an application server to run the application, whereas war requires only a web container or webserver to deploy it. The aspects that distinguish a web server from an application server, and what those popular servers are in Java, are important concepts for a Java developer.
Let’s define the default goal bindings for the ear:
- ear: generate-application-xml
- resources: resources
- ear: ear
- install: install
- deploy: deploy
Here’s how we can define the packaging type of such projects:
Among all packaging types, pom is the simplest one. It helps to create aggregators and parent projects.
An aggregator or multi-module project assembles submodules coming from different sources. These submodules are regular Maven projects and follow their own build lifecycles. The aggregator POM has all the references of submodules under the modules element.
A parent project allows you to define the inheritance relationship between POMs. The parent POM shares certain configurations, plugins, and dependencies, along with their versions. Most elements from the parent are inherited by its children — exceptions include artifactId, name, and prerequisites.
Because there are no resources to process and no code to compile or test. Hence, the artifacts of pom projects generate themselves instead of any executable.
Let’s define the packaging type of a multi-module project:
Such projects have the simplest lifecycle that consists of only two steps: install and deploy.
Maven offers a variety of useful plugins. However, there might be cases when default plugins are not sufficient enough. In this case, the tool provides the flexibility to create a maven-plugin, according to project needs.
To create a plugin, set the packaging type of the project:
The maven-plugin has a lifecycle similar to jar‘s lifecycle, but with two exceptions:
- plugin: descriptor is bound to the generate-resources phase
- plugin: addPluginArtifactMetadata is added to the package phase
For this type of project, a maven-plugin-api dependency is required.
Enterprise Java Beans – or ejb – help to create scalable, distributed server-side applications. EJBs often provide the business logic of an application. A typical EJB architecture consists of three components: Enterprise Java Beans (EJBs), the EJB container, and an application server.
Now, let’s define the packaging type of the EJB project:
The ejb packaging type also has a similar lifecycle as jar packaging, but with a different package goal. The package goal for this type of project is ejb:ejb.
The project, with ejb packaging type, requires a maven-ejb-plugin to execute lifecycle goals. Maven provides support for EJB 2 and 3. If no version is specified, then default version 2 is used.
Resource adapter – or rar – is an archive file that serves as a valid format for the deployment of resource adapters to an application server. Basically, it is a system-level driver that connects a Java application to an enterprise information system (EIS).
Here’s the declaration of packaging type for a resource adapter:
Every resource adapter archive consists of two parts: a jar file that contains source code and a ra.xml that serves as a deployment descriptor.
Again, the lifecycle phases are the same as a jar or war packaging with one exception: The package phase executes the rar goal that consists of a maven-rar-plugin to package the archives.
3. Other Packaging Types
So far, we’ve looked at various packaging types that Maven offers as default. Now, let’s imagine we want our project to produce an artifact with a .zip extension. In this case, the default packaging types can’t help us.
Maven also provides some more packaging types through plugins. With the help of these plugins, we can define a custom packaging type and its build lifecycle. Some of these types are:
To define a custom type, we have to define its packaging type and phases in its lifecycle. For this, create a components.xml file under the src/main/resources/META-INF/plexus directory:
<component> <role>org.apache.maven.lifecycle.mapping.LifecycleMapping</role> <role-hint>zip</role-hint> <implementation>org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping</implementation> <configuration> <phases> <process-resources>org.apache.maven.plugins:maven-resources-plugin:resources</process-resources> <package>com.baeldung.maven.plugins:maven-zip-plugin:zip</package> <install>org.apache.maven.plugins:maven-install-plugin:install</install> <deploy>org.apache.maven.plugins:maven-deploy-plugin:deploy</deploy> </phases> </configuration> </component>
Until now, Maven doesn’t know anything about our new packaging type and its lifecycle. To make it visible, let’s add the plugin in the pom file of the project and set extensions to true:
<plugins> <plugin> <groupId>com.baeldung.maven.plugins</groupId> <artifactId>maven-zip-plugin</artifactId> <extensions>true</extensions> </plugin> </plugins>
Now, the project will be available for a scan, and the system will look into plugins and components.xml file, too.
Other than all these types, Maven offers a lot of other packaging types through external projects and plugins. For example, nar (native archive), swf, and swc are packaging types for the projects that produce Adobe Flash and Flex content. For such projects, we need a plugin that defines custom packaging and a repository that contains the plugin.
In this article, we looked at various packaging types available in Maven. Also, we got familiar with what these types represent and how they differ in their lifecycles. In the end, we also learned how to define a custom packaging type and customize the default build lifecycle.
All code examples on Baeldung are built using Maven. Be sure to check out our various Maven configurations over 0n GitHub.