Maven Getting Started Guide

오픈소스 비즈니스 컨설팅
둘러보기로 가기 검색하러 가기

MavenMaven Getting Started Guide를 번역한다.

처음 Maven을 사용하는 사람은 이 가이드의 각 단계에 따라 진행하세요. 잠재적으로 이 가이드는 사용자가 Maven을 다운로드하여 설치하였다고 가정 합니다. 만일 설치하지 않았다면 Maven을 다운로드하여 설치 하세요.
Maven은 소규모 프로젝트에서 프로젝트 팀이 요구사항에 집중할 수 있도록 도와주는 도구 입니다.

Maven은?

첫 눈에 Maven은 여러 형태가 될 수 있을 것처럼 보이지만, 간단히 요약하자면 Maven은 프로젝트의 빌드 구조에 다양한 패턴들을 적용하기 위한 (Attempt이지만 effort 노력의 결과)이다. 실전 경험을 바탕으로 한 명확한 수단들을 제공함으로써 프로젝트에 대한 생산성과 이해도를 향상시킬 수 있다. Maven은 기본적으로 프로젝트 관리와 이해 도구이며, 관리적인 측면에서 도움을 주기위해 다음의 기능들을 제공한다.

  • 빌드
  • 문서화
  • 보고서
  • 의존성
  • SCMs
  • 릴리즈
  • 배포

만일 Maven에 대해서 더 상세한 정보를 원한다면 Maven의 철학Maven의 역사를 참고하라. 이제 사용자가 Maven을 사용해서 어떤 장점을 얻을 수 있는지 살펴보도록 하자.

Maven은 어떻게 개발 프로세스에 장점이 될 수 있는가?

Maven은 개발 주기를 가속화시키는 표준적인 내용과 방법을 채택하여 빌드 프로세스에 대한 장점을 제공할 수 있으며, 동시에 더 높은 성공율을 이루도록 도와준다. 개발 프로세스에서 Maven이 어떻게 도움을 주는지에 대해서 더 자세한 정보는 Maven 사용의 장점를 참조하기 바란다.

이제 Maven의 역사와 목적에 대해서 조금 다루었으므로, Maven을 사용해서 실행하도록 하는 몇가지 실제 예제를 살펴보도록 하자.

Maven의 설정방법

Maven이 제공하는 기본 설정값으로도 충분하지만, 캐시 장소를 변경하거나 HTTP 프록시 서버를 사용하고자 한다면, 별도의 설정을 만들 필요가 있다. 더 자세한 정보는 Maven 설정 가이드를 참고하라.

어떻게 첫번째 Maven 프로젝트를 만들것인가?

첫번째 Maven 프로젝트를 만들어보자. 첫번째 Maven 프로젝트를 만들기 위해 Maven의 archetype 메커니즘을 사용할 것이다. archetype은 동을한 유형의 모든 다른 것으로 만들어진 것으로부터 원래의 패턴 혹은 모델로 정의된다. Maven에서 archetype은 사용자의 요구사항에 맞춰진 작업하려는 Maven 프로젝트를 만들어내기 위해 사용자의 입력값과 같이 조합된 프로젝트의 템플릿이다. 여기서 archetype 메커니즘이 어떻게 동작하는지를 보여주겠지만, archetype에 대해서 더 많은 것을 알기 원한다면 Archetype에 대한 소개를 참고하라.

첫번째 프로젝트를 생성해보자. Maven 프로젝트 중에서 가장 간단한 것을 생성하기 위해서 커맨드 행에 다음과 같이 실행한다.

mvn archetype:create \
  -DarchetypeGroupId=org.apache.maven.archetypes \
  -DgroupId=com.mycompany.app \
  -DartifactId=my-app

위의 명령을 일단 샐행하면, 몇가지 일이 발생되는 것을 볼 수 있을 것이다. 먼저 새로운 프로젝트를 위해 my-app 이라는 디렉토리가 생성되는 것을 볼 수 있으며, 이 디렉토리는 다음과 같은 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.mycompany.app</groupId>
 <artifactId>my-app</artifactId>
 <packaging>jar</packaging>
 <version>1.0-SNAPSHOT</version>
 <name>Maven Quick Start Archetype</name>
 <url>http://maven.apache.org</url>
 <dependencies>
   <dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>3.8.1</version>
     <scope>test</scope>
   </dependency>
 </dependencies>
</project>

pom.xml 은 위 프로젝트에 대한 프로젝트 객체 모델(Project Object Model, POM)을 포함한다. POM은 Maven의 기본적인 단위 작업이다. Maven이 원래 프로젝트의 개념에 대해 관련된 모든 것들에 프로젝트 중심이기 때문에 이는 중요하다. 간략하게 말해서 POM은 프로젝트에 대한 모든 중요한 개개의 정보를 포함하며 프로젝트와 관련된 모든 것을 찾는데 기본적으로 원-스탑 장소이다. POM을 이해하는 것은 중요하며 새로운 사용자는 POM에 대한 소개를 참고하여 이해할 수 있다.

위의 예제는 매우 간단한 POM이지만 POM이 포함하는 주요 요소를 보여주고 있으며, 다음과 같은 POM의 기본적인 내용에 익숙해지기 위해서 각각에 대해 세부적으로 살펴보겠다.

* project 모든 Maven의 pom.xml 파일에서 최상위 요소임.
* modelVersion 이 요소는 해당 POM이 사용중인 객체 모델의 버전을 가르킴. 모델 버전 
그 자체는 그렇게 자주 바뀌지는 않지만 Maven 개발자가 모델을 바꿀 피요가 있는 경우 
사용에 대한 안정성을 보장하기 위해서 필요함.
* groupId 이 요소는 프로젝트를 생성한 조직 혹은 단체에 대한 유일한 식별자를 가르킴.
groupId는 프로젝트에 대한 주요 식별자 중의 하나이며 보통 조직의 전체 도메인 명을 기반으로 함.
예를 들어, org.apache.maven.plugins는 모든 Maven 플러그인에 대한 groupId를 의미함.
* artifactId 이 요소는 해당 프로젝트에 의해서 생성되는 산출물의 유일한 기본명을 가르킴.
프로젝트에 대한 기본 산출물은 일반적으로 JAR 파일임. 소스 파일과 같은 부수적인 산출물들도
최종명의 일부분으로 artifactId를 사용함. Maven에 의해서 만들어지는 전형적인 산출물은
<artifactId>-<version>.<extension> 형태를 띰. (예를 들어, myapp-1.0.jar)
* packaging 이 요소는 해당 산출물 (JAR, WAR, EAR 등)에 이해서 사용되는 패키지 유형을 가르킴.
만들어진 산출물이 JAR, WAR, EAR인 경우에만을 의미할 뿐만 아니라, 빌드 프로세스의 부분으로 사용되는
특정 생명주기도 가르킴. (생명주기는 이후 가이드 내에서 다룰 내용임. 지금은 프로젝트의 주어진
packaging은 빌드 생명주기를 수정하는 역할을 담당한다고만 이해해야 함.) packaging 요소에 대한 기본값은
JAR이며 대부분의 프로젝트에 대해 이를 지정할 필요가 없음.
* version 이 요소는 프로젝트에 의해 생성된 산출물의 버전을 지칭함. 
Maven은 버전 관리와 같이 오랫동안 도움을 주며 SNAPSHOT 형태를 종종 보게되는데, 
이는 프로젝트가 개발 상태임을 나타냄. 이 지침서에서 snapshot 사용과 어떻게 동작하는지를 설명함.
* name 이 요소는 프로젝트에 대해서 사용되는 이름을 나타냄. 이는 Maven의 생성된 문서에서 종종 사용됨.
* url 이 요소는 프로젝트의 사이트가 위치된느 지점을 가르킴. 이것은 Maven의 생성된 문서에서 종종 사용됨.
* description 이 요소는 프로젝트에 대한 기본적인 설명을 제공함. 이것은 Maven의 생성된 문서에서 종종 사용됨.

POM에서 어떤 요소가 사용되는지에 대한 완전한 내용은 POM 참조 문서를 참고하라. 이제 주어진 프로젝트로 다시 돌아가보자.

첫번재 프로젝트의 archetype 생성 후에 다음과 같은 디렉토리 구조가 생성되었음을 역시 볼 수 있을 것이다.

my-app
|-- pom.xml
`-- src
    |-- main
    |   `-- java
    |       `-- com
    |           `-- mycompany
    |               `-- app
    |                   `-- App.java
    `-- test
        `-- java
            `-- com
                `-- mycompany
                    `-- app
                        `-- AppTest.java

보는 바와 같이, archetype으로부터 생성된 프로젝트는 POM, 어플리케이션의 소스에 대한 소스 트리, 테스트 소스에 대한 소스 트리를 가지고 있다. 이것은 Maven 프로젝트에 대한 표준 형태이다. (어플리케이션 소스는 ${basedir}/src/main/java에 위치하며 테스트 소스는 ${basedir}/src/test/java에 위치함. 여기서 where ${basedir}는 pom.xml을 포함하는 디렉토리를 나타냄.)

만일 수동으로 Maven 프로젝트를 생성하려고 한다면 사용하기를 권하는 디렉토리기 구조가 위와 같다. 이것은 Maven의 관행이며 더 자세히 알고 싶으면 표준 디렉토리 형태에 대한 개요를 읽기 바란다.

이제 의문이 될 수 있는 POM과, 어플리케이션 소스, 테스트 소스를 가졌다.

어떻게 어플리케이션 소스를 컴파일하는가?

archetype:create로 생성된 pom.xml 이 위치한 디렉토리로 가서 어플리케이션 소스를 컴파일하기 위해 다음의 명령을 실행하라.

mvn compile

이 명령이 실행되는 동안에 다음과 같은 형태의 결과를 볼 수 있을 것이다.

[INFO] ----------------------------------------------------------------------------
[INFO] Building Maven Quick Start Archetype
[INFO]    task-segment: [compile]
[INFO] ----------------------------------------------------------------------------
[INFO] artifact org.apache.maven.plugins:maven-resources-plugin: \
  checking for updates from central
...
[INFO] artifact org.apache.maven.plugins:maven-compiler-plugin: \
  checking for updates from central
...
[INFO] [resources:resources]
...
[INFO] [compiler:compile]
Compiling 1 source file to <dir>/my-app/target/classes
[INFO] ----------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ----------------------------------------------------------------------------
[INFO] Total time: 3 minutes 54 seconds
[INFO] Finished at: Fri Sep 23 15:48:34 GMT-05:00 2005
[INFO] Final Memory: 2M/6M
[INFO] ----------------------------------------------------------------------------

먼저 이 명령을 시행하고 나면 Maven은 명령을 실행하는데 필요한 모든 플러그인과 관련 의존들을 다운로드할 필요가 있다. Maven을 맨처음 설치 후에 이는 시간이 걸릴 수도 있다. (위의 결과에서 거의 4분 가량이 소요되었다.) 만일 다시 명령을 실행하면, Maven은 이제 필요한 것을 가지고 있어서 새로운 것을 다운로드 할피요가 없으며 훨씬 더 빨리 명령을 실행할 수 있을 것이다.

As you can see from the output, the compiled classes were placed in ${basedir}/target/classes , which is another standard convention employed by Maven. So, if you're a keen observer, you'll notice that by using the standard conventions the POM above is very small and you haven't had to tell Maven explicitly where any of your sources are or where the output should go. By following the standard Maven conventions you can get a lot done with very little effort! Just as a casual comparison, let's take a look at what you might have had to do in Ant to accomplish the same thing .

Now, this is simply to compile a single tree of application sources and the Ant script shown is pretty much the same size as the POM shown above. But we'll see how much more we can do with just that simple POM!

How do I compile my test sources and run my unit tests?

Now you're successfully compiling your application's sources and now you've got some unit tests that you want to compile and execute (because every programmer always writes and executes their unit tests *nudge nudge wink wink*).

Execute the following command:

mvn test

Upon executing this command you should see output like the following:

[INFO] ----------------------------------------------------------------------------
[INFO] Building Maven Quick Start Archetype
[INFO]    task-segment: [test]
[INFO] ----------------------------------------------------------------------------
[INFO] artifact org.apache.maven.plugins:maven-surefire-plugin: \
  checking for updates from central
...
[INFO] [resources:resources]
[INFO] [compiler:compile]
[INFO] Nothing to compile - all classes are up to date
[INFO] [resources:testResources]
[INFO] [compiler:testCompile]
Compiling 1 source file to C:\Test\Maven2\test\my-app\target\test-classes
...
[INFO] [surefire:test]
[INFO] Setting reports dir: C:\Test\Maven2\test\my-app\target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
[surefire] Running com.mycompany.app.AppTest
[surefire] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0 sec

Results :
[surefire] Tests run: 1, Failures: 0, Errors: 0

[INFO] ----------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ----------------------------------------------------------------------------
[INFO] Total time: 15 seconds
[INFO] Finished at: Thu Oct 06 08:12:17 MDT 2005
[INFO] Final Memory: 2M/8M
[INFO] ----------------------------------------------------------------------------

Some things to notice about the output:

   * Maven downloads more dependencies this time. These are the dependencies and plugins necessary for executing the tests (it already has the dependencies it needs for compiling and won't download them again).
   * Before compiling and executing the tests Maven compiles the main code (all these classes are up to date because we haven't changed anything since we compiled last).

If you simply want to compile your test sources (but not execute the tests), you can execute the following:

mvn test-compile

Now that you can compile your application sources, compile your tests, and execute the tests, you'll want to move on to the next logical step so you'll be asking ...

How do I create a JAR and install it in my local repository?

Making a JAR file is straight forward enough and can be accomplished by executing the following command:

mvn package

If you take a look at the POM for your project you will notice the packaging element is set to jar . This is how Maven knows to produce a JAR file from the above command (we'll talk more about this later). You can now take a look in the ${basedir}/target directory and you will see the generated JAR file.

Now you'll want to install the artifact you've generated (the JAR file) in your local repository (~/.m2/repository is the default location). For more information on repositories you can refer to our Introduction to Repositories but let's move on to installing our artifact! To do so execute the following command:

mvn install

Upon executing this command you should see the following output:

[INFO] ----------------------------------------------------------------------------
[INFO] Building Maven Quick Start Archetype
[INFO]    task-segment: [install]
[INFO] ----------------------------------------------------------------------------
[INFO] [resources:resources]
[INFO] [compiler:compile]
Compiling 1 source file to <dir>/my-app/target/classes
[INFO] [resources:testResources]
[INFO] [compiler:testCompile]
Compiling 1 source file to <dir>/my-app/target/test-classes
[INFO] [surefire:test]
[INFO] Setting reports dir: <dir>/my-app/target/surefire-reports
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
[surefire] Running com.mycompany.app.AppTest
[surefire] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.001 sec 

Results :
[surefire] Tests run: 1, Failures: 0, Errors: 0

[INFO] [jar:jar]
[INFO] Building jar: <dir>/my-app/target/my-app-1.0-SNAPSHOT.jar
[INFO] [install:install]
[INFO] Installing <dir>/my-app/target/my-app-1.0-SNAPSHOT.jar to \
   <local-repository>/com/mycompany/app/my-app/1.0-SNAPSHOT/my-app-1.0-SNAPSHOT.jar
[INFO] ----------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ----------------------------------------------------------------------------
[INFO] Total time: 5 seconds
[INFO] Finished at: Tue Oct 04 13:20:32 GMT-05:00 2005
[INFO] Final Memory: 3M/8M
[INFO] ----------------------------------------------------------------------------

Note that the surefire plugin (which executes the test) looks for tests contained in files with a particular naming convention. By default the tests included are:

   * **/*Test.java
   * **/Test*.java
   * **/*TestCase.java

And the default excludes are:

   * **/Abstract*Test.java
   * **/Abstract*TestCase.java

You have walked through the process for setting up, building, testing, packaging, and installing a typical Maven project. This is likely the vast majority of what projects will be doing with Maven and if you've noticed, everything you've been able to do up to this point has been driven by an 18-line file, namely the project's model or POM. If you look at a typical Ant build file that provides the same functionality that we've achieved thus far you'll notice it's already twice the size of the POM and we're just getting started! There is far more functionality available to you from Maven without requiring any additions to our POM as it currently stands. To get any more functionality out of our example Ant build file you must keep making error-prone additions.

So what else can you get for free? There are a great number of Maven plug-ins that work out of the box with even a simple POM like we have above. We'll mention one here specifically as it is one of the highly prized features of Maven: without any work on your part this POM has enough information to generate a web site for your project! You will most likely want to customize your Maven site but if you're pressed for time all you need to do to provide basic information about your project is execute the following command:

mvn site

There are plenty of other standalone goals that can be executed as well, for example:

mvn clean

This will remove the target directory with all the build data before starting so that it is fresh.

Perhaps you'd like to generate an IntelliJ IDEA descriptor for the project?

mvn idea:idea

This can be run over the top of a previous IDEA project - it will update the settings rather than starting fresh.

If you are using Eclipse IDE, just call:

mvn eclipse:eclipse

Note: some familiar goals from Maven 1.0 are still there - such as jar:jar , but they might not behave like you'd expect. Presently, jar:jar will not recompile sources - it will simply just create a JAR from the target/classes directory, under the assumption everything else had already been done.

How do I use plug-ins?

Whenever you want to customise the build for a Maven project, this is done by adding or reconfiguring plugins.

Note for Maven 1.0 Users: In Maven 1.0, you would have added some preGoal to maven.xml and some entries to project.properties . Here, it is a little different.

For this example, we will configure the Java compiler to allow JDK 5.0 sources. This is as simple as adding this to your POM:

...
<build>
 <plugins>
   <plugin>
     <groupId>org.apache.maven.plugins</groupId>
     <artifactId>maven-compiler-plugin</artifactId>
     <version>2.0.2</version>
     <configuration>
       <source>1.5</source>
       <target>1.5</target>
     </configuration>
   </plugin>
 </plugins>
</build>
...

You'll notice that all plugins in Maven 2.0 look much like a dependency - and in some ways they are. This plugin will be automatically downloaded and used - including a specific version if you request it (the default is to use the latest available).

The configuration element applies the given parameters to every goal from the compiler plugin. In the above case, the compiler plugin is already used as part of the build process and this just changes the configuration. It is also possible to add new goals to the process, and configure specific goals. For information on this, see the Introduction to the Build Lifecycle .

To find out what configuration is available for a plugin, you can see the Plugins List and navigate to the plugin and goal you are using.

How do I add resources to my JAR?

Another common use case that can be satisfied which requires no changes to the POM that we have above is packaging resources in the JAR file. For this common task, Maven again relies on the Standard Directory Layout , which means by using standard Maven conventions you can package resources within JARs simply by placing those resources in a standard directory structure.

You see below in our example we have added the directory ${basedir}/src/main/resources into which we place any resources we wish to package in our JAR. The simple rule employed by Maven is this: any directories or files placed within the ${basedir}/src/main/resources directory are packaged in your JAR with the exact same structure starting at the base of the JAR.

my-app
|-- pom.xml
`-- src
    |-- main
    |   |-- java
    |   |   `-- com
    |   |       `-- mycompany
    |   |           `-- app
    |   |               `-- App.java
    |   `-- resources
    |       `-- META-INF
    |           `-- application.properties
    `-- test
        `-- java
            `-- com
                `-- mycompany
                    `-- app
                        `-- AppTest.java

So you can see in our example that we have a META-INF directory with an application.properties file within that directory. If you unpacked the JAR that Maven created for you and took a look at it you would see the following:

|-- META-INF
|   |-- MANIFEST.MF
|   |-- application.properties
|   `-- maven
|       `-- com.mycompany.app
|           `-- my-app
|               |-- pom.properties
|               `-- pom.xml
`-- com
    `-- mycompany
        `-- app
            `-- App.class

As you can see, the contents of ${basedir}/src/main/resources can be found starting at the base of the JAR and our application.properties file is there in the META-INF directory. You will also notice some other files there like META-INF/MANIFEST.MF as well as a pom.xml and pom.properties file. These come standard with generation of a JAR in Maven. You can create your own manifest if you choose, but Maven will generate one by default if you don't. (You can also modify the entries in the default manifest. We will touch on this later.) The pom.xml and pom.properties files are packaged up in the JAR so that each artifact produced by Maven is self-describing and also allows you to utilize the metadata in your own application if the need arises. One simple use might be to retrieve the version of your application. Operating on the POM file would require you to use some Maven utilities but the properties can be utilized using the standard Java API and look like the following:

  1. Generated by Maven
  2. Tue Oct 04 15:43:21 GMT-05:00 2005

version=1.0-SNAPSHOT groupId=com.mycompany.app artifactId=my-app

To add resources to the classpath for your unit tests, you follow the same pattern as you do for adding resources to the JAR except the directory you place resources in is ${basedir}/src/test/resources. At this point you would have a project directory structure that would look like the following:

my-app
|-- pom.xml
`-- src
    |-- main
    |   |-- java
    |   |   `-- com
    |   |       `-- mycompany
    |   |           `-- app
    |   |               `-- App.java
    |   `-- resources
    |       `-- META-INF
    |           |-- application.properties
    `-- test
        |-- java
        |   `-- com
        |       `-- mycompany
        |           `-- app
        |               `-- AppTest.java
        `-- resources
            `-- test.properties

In a unit test you could use a simple snippet of code like the following to access the resource required for testing:

...

// Retrieve resource
InputStream is = getClass().getResourceAsStream( "/test.properties" );

// Do something with the resource

...

How do I filter resource files?

Sometimes a resource file will need to contain a value that can only be supplied at build time. To accomplish this in Maven, put a reference to the property that will contain the value into your resource file using the syntax ${<property name>} . The property can be one of the values defined in your pom.xml, a value defined in the user's settings.xml, a property defined in an external properties file, or a system property.

To have Maven filter resources when copying, simply set filtering to true for the resource directory in your 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.mycompany.app</groupId>
 <artifactId>my-app</artifactId>
 <packaging>jar</packaging>
 <version>1.0-SNAPSHOT</version>
 <name>Maven Quick Start Archetype</name>
 <url>http://maven.apache.org</url>
 <dependencies>
   <dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>3.8.1</version>
     <scope>test</scope>
   </dependency>
 </dependencies>
 <build>
   <resources>
     <resource>
       <directory>src/main/resources</directory>
       <filtering>true</filtering>
     </resource>
   </resources>
 </build>
</project>

You'll notice that we had to add the build , resources , and resource elements which weren't there before. In addition, we had to explicitly state that the resources are located in the src/main/resources directory. All of this information was provided as default values previously, but because the default value for filtering is false, we had to add this to our pom.xml in order to override that default value and set filtering to true.

To reference a property defined in your pom.xml, the property name uses the names of the XML elements that define the value, with "pom" being allowed as an alias for the project (root) element. So ${pom.name} refers to the name of the project, ${pom.version} refers to the version of the project, ${pom.build.finalName} refers to the final name of the file created when the built project is packaged, etc. Note that some elements of the POM have default values, so don't need to be explicitly defined in your pom.xml for the values to be available here. Similarly, values in the user's settings.xml can be referenced using property names beginning with "settings" (for example, ${settings.localRepository} refers to the path of the user's local repository).

To continue our example, let's add a couple of properties to the application.properties file (which we put in the src/main/resources directory) whose values will be supplied when the resource is filtered:

# application.properties
application.name=${pom.name}
application.version=${pom.version}

With that in place, you can execute the following command (process-resources is the build lifecycle phase where the resources are copied and filtered):

mvn process-resources

and the application.properties file under target/classes (and will eventually go into the jar) looks like this:

# application.properties
application.name=Maven Quick Start Archetype
application.version=1.0-SNAPSHOT

To reference a property defined in an external file, all you need to do is add a reference to this external file in your pom.xml. First, let's create our external properties file and call it src/main/filters/filter.properties:

# filter.properties
my.filter.value=hello!

Next, we'll add a reference to this new file in the 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.mycompany.app</groupId>
 <artifactId>my-app</artifactId>
 <packaging>jar</packaging>
 <version>1.0-SNAPSHOT</version>
 <name>Maven Quick Start Archetype</name>
 <url>http://maven.apache.org</url>
 <dependencies>
   <dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>3.8.1</version>
     <scope>test</scope>
   </dependency>
 </dependencies>
 <build>
   <filters>
     <filter>src/main/filters/filter.properties</filter>
   </filters>
   <resources>
     <resource>
       <directory>src/main/resources</directory>
       <filtering>true</filtering>
     </resource>
   </resources>
 </build>
</project>

Then, if we add a reference to this property in the application.properties file:

# application.properties
application.name=${pom.name}
application.version=${pom.version}
message=${my.filter.value}

the next execution of the mvn process-resources command will put our new property value into application.properties. As an alternative to defining the my.filter.value property in an external file, you could also have defined it in the properties section of your pom.xml and you'd get the same effect (notice I don't need the references to src/main/filters/filter.properties either):

<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.mycompany.app</groupId>
 <artifactId>my-app</artifactId>
 <packaging>jar</packaging>
 <version>1.0-SNAPSHOT</version>
 <name>Maven Quick Start Archetype</name>
 <url>http://maven.apache.org</url>
 <dependencies>
   <dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>3.8.1</version>
     <scope>test</scope>
   </dependency>
 </dependencies>
 <build>
   <resources>
     <resource>
       <directory>src/main/resources</directory>
       <filtering>true</filtering>
     </resource>
   </resources>
 </build>
 <properties>
   <my.filter.value>hello</my.filter.value>
 </properties>
</project>

Filtering resources can also get values from system properties; either the system properties built into Java (like java.version or user.home) or properties defined on the command line using the standard Java -D parameter. To continue the example, let's change our application.properties file to look like this:

# application.properties
java.version=${java.version}
command.line.prop=${command.line.prop}

Now, when you execute the following command (note the definition of the command.line.prop property on the command line), the application.properties file will contain the values from the system properties.

mvn process-resources "-Dcommand.line.prop=hello again"

How do I use external dependencies?

You've probably already noticed a dependencies element in the POM we've been using as an example. You have, in fact, been using an external dependency all this time, but here we'll talk about how this works in a bit more detail. For a more thorough introduction, please refer to our Introduction to Dependency Management .

The dependencies section of the pom.xml lists all of the external dependencies that our project needs in order to build (whether it needs that dependency at compile time, test time, run time, or whatever). Right now, our project is depending on JUnit only (I took out all of the resource filtering stuff for clarity):

<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.mycompany.app</groupId>
 <artifactId>my-app</artifactId>
 <packaging>jar</packaging>
 <version>1.0-SNAPSHOT</version>
 <name>Maven Quick Start Archetype</name>
 <url>http://maven.apache.org</url>
 <dependencies>
   <dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>3.8.1</version>
     <scope>test</scope>
   </dependency>
 </dependencies>
</project>

For each external dependency, you'll need to define at least 4 things: groupId, artifactId, version, and scope. The groupId, artifactId, and version are the same as those given in the pom.xml for the project that built that dependency. The scope element indicates how your project uses that dependency, and can be values like compile , test , and runtime . For more information on everything you can specify for a dependency, see the Project Descriptor Reference .

For more information about the dependency mechanism as a whole, see Introduction to Dependency Management .

With this information about a dependency, Maven will be able to reference the dependency when it builds the project. Where does Maven reference the dependency from? Maven looks in your local repository (~/.m2/repository is the default location) to find all dependencies. In a previous section , we installed the artifact from our project (my-app-1.0-SNAPSHOT.jar) into the local repository. Once it's installed there, another project can reference that jar as a dependency simply by adding the dependency information to its 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">
 <groupId>com.mycompany.app</groupId>
 <artifactId>my-other-app</artifactId>
 ...
 <dependencies>
   ...
   <dependency>
     <groupId>com.mycompany.app</groupId>
     <artifactId>my-app</artifactId>
     <version>1.0-SNAPSHOT</version>
     <scope>compile</scope>
   </dependency>
 </dependencies>
</project>

What about dependencies built somewhere else? How do they get into my local repository? Whenever a project references a dependency that isn't available in the local repository, Maven will download the dependency from a remote repository into the local repository. You probably noticed Maven downloading a lot of things when you built your very first project (these downloads were dependencies for the various plugins used to build the project). By default, the remote repository Maven uses can be found (and browsed) at http://repo1.maven.org/maven2/ . You can also set up your own remote repository (maybe a central repository for your company) to use instead of or in addition to the default remote repository. For more information on repositories you can refer to the Introduction to Repositories .

Let's add another dependency to our project. Let's say we've added some logging to the code and need to add log4j as a dependency. First, we need to know what the groupId, artifactId, and version are for log4j. We can browse ibiblio and look for it, or use Google to help by searching for "site:www.ibiblio.org maven2 log4j". The search shows a directory called /maven2/log4j/log4j (or /pub/packages/maven2/log4j/log4j). In that directory is a file called maven-metadata.xml. Here's what the maven-metadata.xml for log4j looks like:

<metadata>
 <groupId>log4j</groupId>
 <artifactId>log4j</artifactId>
 <version>1.1.3</version>
 <versioning>
   <versions>
     <version>1.1.3</version>
     <version>1.2.4</version>
     <version>1.2.5</version>
     <version>1.2.6</version>
     <version>1.2.7</version>
     <version>1.2.8</version>
     <version>1.2.11</version>
     <version>1.2.9</version>
     <version>1.2.12</version>
   </versions>
 </versioning>
</metadata>

From this file, we can see that the groupId we want is "log4j" and the artifactId is "log4j". We see lots of different version values to choose from; for now, we'll just use the latest version, 1.2.12 (some maven-metadata.xml files may also specify which version is the current release version). Alongside the maven-metadata.xml file, we can see a directory corresponding to each version of the log4j library. Inside each of these, we'll find the actual jar file (e.g. log4j-1.2.12.jar) as well as a pom file (this is the pom.xml for the dependency, indicating any further dependencies it might have and other information) and another maven-metadata.xml file. There's also an md5 file corresponding to each of these, which contains an MD5 hash for these files. You can use this to authenticate the library or to figure out which version of a particular library you may be using already.

Now that we know the information we need, we can add the dependency to our 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.mycompany.app</groupId>
 <artifactId>my-app</artifactId>
 <packaging>jar</packaging>
 <version>1.0-SNAPSHOT</version>
 <name>Maven Quick Start Archetype</name>
 <url>http://maven.apache.org</url>
 <dependencies>
   <dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>3.8.1</version>
     <scope>test</scope>
   </dependency>
   <dependency>
     <groupId>log4j</groupId>
     <artifactId>log4j</artifactId>
     <version>1.2.12</version>
     <scope>compile</scope>
   </dependency>
 </dependencies>
</project>

Now, when we compile the project (mvn compile ), we'll see Maven download the log4j dependency for us.

How do I deploy my jar in my remote repository?

For deploying jars to an external repository, you have to configure the repository url in the pom.xml and the authentication information for connectiong to the repository in the settings.xml.

Here is an example using scp and username/password authentication:

<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.mycompany.app</groupId>
 <artifactId>my-app</artifactId>
 <packaging>jar</packaging>
 <version>1.0-SNAPSHOT</version>
 <name>Maven Quick Start Archetype</name>
 <url>http://maven.apache.org</url>
 <dependencies>
   <dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>3.8.1</version>
     <scope>test</scope>
   </dependency>
   <dependency>
     <groupId>org.apache.codehaus.plexus</groupId>
     <artifactId>plexus-utils</artifactId>
     <version>1.0.4</version>
   </dependency>
 </dependencies>
 <build>
   <filters>
     <filter>src/main/filters/filters.properties</filter>
   </filters>
   <resources>
     <resource>
       <directory>src/main/resources</directory>
       <filtering>true</filtering>
     </resource>
   </resources>
 </build>
 <distributionManagement>
   <repository>
     <id>mycompany-repository</id>
     <name>MyCompany Repository</name>
     <url>scp://repository.mycompany.com/repository/maven2</url>
   </repository>
 </distributionManagement>

</project>

<settings 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/settings-1.0.0.xsd">
 ...
 <servers>
   <server>
     <id>mycompany-repository</id>
     <username>jvanzyl</username>
     <privateKey>/path/to/identity</privateKey> (default is ~/.ssh/id_dsa)
     <passphrase>my_key_passphrase</passphrase>
   </server>
 </servers>
 ...

</settings>

Note that if you are connecting to an openssh ssh server which has the parameter "PasswordAuthentication" set to "no" in the sshd_confing, you will have to type your password each time for username/password authentication (although you can log in using another ssh client by typing in the username and password). You might want to switch to public key authentication in this case.

How do I create documentation?

To get you jump started with Maven's documentation system you can use the archetype mechanism to generate a site for your existing project using the following command:

mvn archetype:create \

 -DarchetypeGroupId=org.apache.maven.archetypes \
 -DarchetypeArtifactId=maven-archetype-site \
 -DgroupId=com.mycompany.app \
 -DartifactId=my-app-site

Now head on over to the Guide to creating a site to learn how to create the documentation for your project.

How do I build other types of projects?

Note that the lifecycle applies to any project type. For example, back in the base directory we can create a simple web application:

mvn archetype:create \
   -DarchetypeGroupId=org.apache.maven.archetypes \
   -DarchetypeArtifactId=maven-archetype-webapp \
   -DgroupId=com.mycompany.app \
   -DartifactId=my-webapp

Note that these must all be on a single line. This will create a directory called my-webapp containing the following project descriptor:

<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.mycompany.app</groupId>
 <artifactId>my-webapp</artifactId>
 <packaging>war</packaging>
 <version>1.0-SNAPSHOT</version>
 <dependencies>
   <dependency>
     <groupId>junit</groupId>
     <artifactId>junit</artifactId>
     <version>3.8.1</version>
     <scope>test</scope>
   </dependency>
 </dependencies>
 <build>
   <finalName>my-webapp</finalName>
 </build>

</project>

Note the <packaging> element - this tells Maven to build as a WAR. Change into the webapp project's directory and try:

mvn clean package

You'll see target/my-webapp.war is built, and that all the normal steps were executed.

How do I build more than one project at once?

The concept of dealing with multiple modules is built in to Maven 2.0. In this section, we will show how to build the WAR above, and include the previous JAR as well in one step.

Firstly, we need to add a parent pom.xml file in the directory above the other two, so it should look like this:

+- pom.xml
+- my-app
| +- pom.xml
| +- src
|   +- main
|     +- java
+- my-webapp
| +- pom.xml
| +- src
|   +- main
|     +- webapp

The POM file you'll create should contain the following:

<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.mycompany.app</groupId>
 <version>1.0-SNAPSHOT</version>
 <artifactId>app</artifactId>
 <packaging>pom</packaging>
 <modules>
   <module>my-app</module>
   <module>my-webapp</module>
 </modules>
</project>

We'll need a dependency on the JAR from the webapp, so add this to my-webapp/pom.xml :

 ...
 <dependencies>
   <dependency>
     <groupId>com.mycompany.app</groupId>
     <artifactId>my-app</artifactId>
     <version>1.0-SNAPSHOT</version>
   </dependency>
   ...
 </dependencies>

Finally, add the following <parent> element to both of the other pom.xml files in the subdirectories:

<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">
 <parent>
   <groupId>com.mycompany.app</groupId>
   <artifactId>app</artifactId>
   <version>1.0-SNAPSHOT</version>
 </parent>
 ...

Now, try it... from the top level directory, run:

mvn clean install

The WAR has now been created in my-webapp/target/my-webapp.war , and the JAR is included:

$ jar tvf my-webapp/target/my-webapp-1.0-SNAPSHOT.war
   0 Fri Jun 24 10:59:56 EST 2005 META-INF/
 222 Fri Jun 24 10:59:54 EST 2005 META-INF/MANIFEST.MF
   0 Fri Jun 24 10:59:56 EST 2005 META-INF/maven/
   0 Fri Jun 24 10:59:56 EST 2005 META-INF/maven/com.mycompany.app/
   0 Fri Jun 24 10:59:56 EST 2005 META-INF/maven/com.mycompany.app/my-webapp/
3239 Fri Jun 24 10:59:56 EST 2005 META-INF/maven/com.mycompany.app/my-webapp/pom.xml
   0 Fri Jun 24 10:59:56 EST 2005 WEB-INF/
 215 Fri Jun 24 10:59:56 EST 2005 WEB-INF/web.xml
 123 Fri Jun 24 10:59:56 EST 2005 META-INF/maven/com.mycompany.app/my-webapp/pom.properties
  52 Fri Jun 24 10:59:56 EST 2005 index.jsp
   0 Fri Jun 24 10:59:56 EST 2005 WEB-INF/lib/
2713 Fri Jun 24 10:59:56 EST 2005 WEB-INF/lib/my-app-1.0-SNAPSHOT.jar

How does this work? Firstly, the parent POM created (called app ), has a packaging of pom and a list of modules defined. This tells Maven to run all operations over the set of projects instead of just the current one (to override this behaviour, you can use the --non-recursive command line option).

Next, we tell the WAR that it requires the my-app JAR. This does a few things: it makes it available on the classpath to any code in the WAR (none in this case), it makes sure the JAR is always built before the WAR, and it indicates to the WAR plugin to include the JAR in its library directory.

You may have noticed that junit-3.8.1.jar was a dependency, but didn't end up in the WAR. The reason for this is the <scope>test</scope> element - it is only required for testing, and so is not included in the web application as the compile time dependency my-app is.

The final step was to include a parent definition. This is different to the extend element you may be familiar with from Maven 1.0: this ensures that the POM can always be located even if the project is distributed separately from its parent by looking it up in the repository.

Unlike Maven 1.0, it is not required that you run install to successfully perform these steps - you can run package on its own and the artifacts in the reactor will be used from the target directories instead of the local repository.

You might like to generate your IDEA workspace again from the top level directory...

mvn idea:idea