Maven ことはじめ (Java プロジェクトを作成して、外部ライブラリをまとめてひとつの実行可能 JAR を生成するまで)

## 概要

- 公式のチュートリアル資料等を参考に Maven を導入する
- Maven で Java プロジェクトを作成する
- 外部ライブラリを導入し、ひとつの実行可能 JAR を生成する

## 今回の環境

- macOS Mojave
- OpenJDK 11.0.2
- Apache Maven 3.6.1

## Maven をインストールする

今回は macOS Mojave なので Homebrew でインストールした。

[maven — Homebrew Formulae](https://formulae.brew.sh/formula/maven)

```
$ brew install maven
```

## Java プロジェクトを作成する

### mvn archetype:generate で Java プロジェクトを作成する

公式ドキュメントの [Maven – Maven in 5 Minutes](https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html) を参考にする。

mvn archetype:generate コマンドでプロジェクトを作成できる。
自動生成されるファイルを決定する Archetype を選択する。[Maven Quickstart Archetype](https://maven.apache.org/archetypes/maven-archetype-quickstart/) がシンプルでわかりやすい。

```
$ mvn archetype:generate \
  -DgroupId=com.mycompany.app \
  -DartifactId=my-app \
  -DarchetypeArtifactId=maven-archetype-quickstart \
  -DarchetypeVersion=1.4 \
  -DinteractiveMode=false
```

### 生成されたファイル一覧

```
└── my-app
    ├── pom.xml
    └── src
        ├── main
        │   └── java
        │       └── com
        │           └── mycompany
        │               └── app
        │                   └── App.java
        └── test
            └── java
                └── com
                    └── mycompany
                        └── app
                            └── AppTest.java
```

### pom.xml

自動生成された pom.xml は不要な項目が多いので必要な箇所以外は削除する。
また、maven.compiler.source と maven.compiler.target が Java 1.7 用になっているので Java 11 用に値を変更する。

```xml
<?xml version="1.0" encoding="UTF-8"?>

<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>
  <version>1.0-SNAPSHOT</version>

  <name>my-app</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

</project>
```

### Java プロジェクトをビルドする

mvn package コマンドでテストを実行し、JAR ファイルを生成することができる。

```
$ mvn package
[INFO] Scanning for projects...
[INFO] 
[INFO] ----------------------< com.mycompany.app:my-app >----------------------
[INFO] Building my-app 1.0-SNAPSHOT
(中略)
-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running com.mycompany.app.AppTest
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.126 sec

Results :

Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
```

java コマンドで JAR ファイルをクラスパスに指定して実行できる。

```
$ java -cp target/my-app-1.0-SNAPSHOT.jar com.mycompany.app.App
Hello World!
```

## Assembly Plugin を導入して実行可能な JAR ファイルを作成する

### pom.xml に Assembly Plugin の jar-with-dependencies を導入する

Assembly Plugin の jar-with-dependencies という assembly descriptor を導入する。
これで依存ライブラリも含めてひとつの実行可能な JAR ファイルを生成できるようになる。

[Apache Maven Assembly Plugin – Usage](https://maven.apache.org/plugins/maven-assembly-plugin/usage.html)

> Getting started with the Assembly Plugin is pretty simple. If you want to use one of the prefabricated assembly descriptors, you configure which descriptor to use with the <descriptorRefs>/<descriptorRef> parameter. If you want to use a custom assembly descriptor, you configure the path to your descriptor using the <descriptors>/<descriptor> parameter.

[Apache Maven Assembly Plugin – Predefined Assembly Descriptors](https://maven.apache.org/plugins/maven-assembly-plugin/descriptor-refs.html)

> jar-with-dependencies
> Use jar-with-dependencies as the descriptorRef of your assembly-plugin configuration in order to create a JAR which contains the binary output of your project, along its the unpacked dependencies. This built-in descriptor produces an assembly with the classifier jar-with-dependencies using the JAR archive format.

以下の内容を pom.xml の project 要素以下に追加する。

```xml
<build>
  <plugins>
    <!-- http://maven.apache.org/plugins/maven-assembly-plugin/ -->
    <plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>3.1.1</version>
    <executions>
      <execution>
      <phase>package</phase>
      <goals>
        <goal>single</goal>
      </goals>
      </execution>
    </executions>
    <configuration>
      <descriptorRefs>
      <descriptorRef>jar-with-dependencies</descriptorRef>
      </descriptorRefs>
      <archive>
      <manifest>
        <mainClass>com.mycompany.app.App</mainClass>
      </manifest>
      </archive>
    </configuration>
    </plugin>
  </plugins>
</build>
```

### JAR ファイルを作成する

mvn package コマンドで JAR ファイルが生成される。

```
$ mvn package
```

```
$ ls target/*.jar
target/my-app-1.0-SNAPSHOT-jar-with-dependencies.jar
target/my-app-1.0-SNAPSHOT.jar
```

java -jar コマンドで JAR ファイル指定して実行できる。

```
$ java -jar target/my-app-1.0-SNAPSHOT-jar-with-dependencies.jar 
Hello World!
```

## 外部ライブラリを導入する

ここでは JSON を操作できる外部ライブラリ [Jackson Core](https://github.com/FasterXML/jackson-core) を導入する。

### pom.xml に外部ライブラリを追加する

[Maven Repository: com\.fasterxml\.jackson\.core » jackson\-core » 2\.9\.9](https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core/2.9.9) に Maven 用の設定情報が載っているのでそれを利用する。

```xml
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.9.9</version>
</dependency>
```

pom.xml の全体はこうなる。

```xml
<?xml version="1.0" encoding="UTF-8"?>

<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>
  <version>1.0-SNAPSHOT</version>

  <name>my-app</name>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

  <dependencies>
    <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.9</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <!-- http://maven.apache.org/plugins/maven-assembly-plugin/ -->
      <plugin>
      <artifactId>maven-assembly-plugin</artifactId>
      <version>3.1.1</version>
      <executions>
        <execution>
        <phase>package</phase>
        <goals>
          <goal>single</goal>
        </goals>
        </execution>
      </executions>
      <configuration>
        <descriptorRefs>
        <descriptorRef>jar-with-dependencies</descriptorRef>
        </descriptorRefs>
        <archive>
        <manifest>
          <mainClass>com.mycompany.app.App</mainClass>
        </manifest>
        </archive>
      </configuration>
      </plugin>
    </plugins>
  </build>

</project>
```

### Java ソースコードを修正する

Jackson Core を使用するように src/main/java/com/mycompany/app/App.java を修正する。

```java
package com.mycompany.app;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonGenerator;

public class App {

    public String getGreeting() {
        try {
            Writer out = new StringWriter();
            JsonFactory factory = new JsonFactory();
            JsonGenerator generator = factory.createGenerator(out);
            generator.writeStartObject();
            generator.writeStringField("message", "Hello World!");
            generator.writeEndObject();
            generator.flush();
            generator.close();
            return out.toString();
        } catch (IOException e) {
            return e.toString();
        }
    }

    public static void main(String[] args) {
        System.out.println(new App().getGreeting());
    }
}
```

### 外部ライブラリを含めたひとつの JAR ファイルを作成する

mvn package コマンドで JAR ファイルを生成できる。

```
$ mvn package
```

java -jar コマンドで JAR ファイルを指定して実行する。
外部ライブラリもこのひとつの JAR ファイルにまとめられている。

```
$ java -jar target/my-app-1.0-SNAPSHOT-jar-with-dependencies.jar 
{"message":"Hello World!"}
```

## 参考資料

- [Maven – Maven in 5 Minutes](https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html)
- [Maven Quickstart Archetype](https://maven.apache.org/archetypes/maven-archetype-quickstart/)
- [Apache Maven Assembly Plugin – Usage](https://maven.apache.org/plugins/maven-assembly-plugin/usage.html)
- [Apache Maven Assembly Plugin – Predefined Assembly Descriptors](https://maven.apache.org/plugins/maven-assembly-plugin/descriptor-refs.html)