Spring Bootを試す１(環境構築 ~ Tomcat起動)

# はじめに

Spring Bootでサンプルプログラムを書きたいと思います。
参考にするのは、[Spring徹底入門](https://www.amazon.co.jp/Spring%E5%BE%B9%E5%BA%95%E5%85%A5%E9%96%80-Spring-Framework%E3%81%AB%E3%82%88%E3%82%8BJava%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E9%96%8B%E7%99%BA-%E6%A0%AA%E5%BC%8F%E4%BC%9A%E7%A4%BENTT%E3%83%87%E3%83%BC%E3%82%BF/dp/4798142476)です。
こちらで紹介されている会議室予約システムを作っていきます。

# Javaのインストール

Javaをインストールしましょう。
バージョンはいろいろありますが、今回はOpenJDK 8でいきます。
WSL2を利用している方は、以下のコマンドでダウンロードできます。

```:WSL2
$ sudo apt install openjdk-8-jdk
```
次に環境変数を設定します。
まずは、どこにJavaがインストールされたか確認しましょう。

```:WSL2
$ dpkg -L openjdk-8-jdk

*** 略
/usr/lib/jvm/java-8-openjdk-amd64
*** 略
```
いっぱい出ますが、`JAVA_HOME`に指定するのは`/usr/lib/jvm/java-8-openjdk-amd64`でOKです。
パスが分かったので、環境変数を設定します。

```:WSL2
$ sudo vi /etc/profile

/etc/profileに以下を追記。
export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
export PATH=$PATH:$JAVA_HOME/bin
export CLASSPATH=.:$JAVA_HOME/jre/lib:$JAVA_HOME/lib:$JAVA_HOME/lib/tools.jar
```
次のコマンドでbashの設定内容を反映し、インストールできていることを確認しましょう。

```:WSL2
$ source /etc/profile
$ java -version
openjdk version "1.8.0_265"
OpenJDK Runtime Environment (build 1.8.0_265-8u265-b01-0ubuntu2~20.04-b01)
OpenJDK 64-Bit Server VM (build 25.265-b01, mixed mode)
```
これでJavaが使えるようになりました。

# Mavenのインストール

続いてMavenのインストールを行います。
ビルドツールとしてはGradleのほうが人気が出ているらしいですが、大人の事情で古き良きMavenを使います。
コマンドはなんとこれだけ。

```:WSL2
$ sudo apt install maven
```
インストールの確認をしましょう。

```:WSL2
$ mvn -v
Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 1.8.0_265, vendor: Private Build, runtime: /usr/lib/jvm/java-8-openjdk-amd64
Default locale: en, platform encoding: UTF-8
OS name: "linux", version: "4.19.104-microsoft-standard", arch: "amd64", family: "unix"
```

# Spring-Bootのサンプルを起動する

では、SpringBootのサンプルプロジェクトを作成しましょう。
はじめに適当なディレクトリを作成します。筆者の場合は次のようにしました。

```:WSL2
$ cd ~
$ mkdir web-app
$ cd web-app
```

次のコマンドでサンプルプロジェクトを作成します。

```:WSL2
$ mvn archetype:generate

*** 中略 ***

Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): 1672:
```

いかがでしょう。たくさん英語が出てきてビビりますが、無視して大丈夫です。
`mvn archetype:generate`は対話的にプロジェクトを作成するコマンドです。
今回利用するサンプルを選択しましょう。`spring-boot`と入力してみましょう。

```:WSL2
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : spring-boot
```

さっきよりも選択肢が減りました。(筆者の環境では78個)
今回は`org.springframework.boot:spring-boot-sample-tomcat-archetype (Spring Boot Tomcat Sample)`を使ってみましょう。
このサンプルだとTomcatが組み込まれているので、Jarをそのまま実行できます。
筆者の環境では`68:`と表示されていたので、`68`と入力。（名前を指定してもできるはず）

```:WSL2
*** 略
68: remote -> org.springframework.boot:spring-boot-sample-tomcat-archetype (Spring Boot Tomcat Sample)
*** 略
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 68
```
またたくさん英語が出ますが、めげないでください。無視でOKです。
次に以下のことを聞かれます。

```:WSL2
*** 略
Define value for property 'groupId': com.rocorock
Define value for property 'artifactId': mvn-spring
Define value for property 'version' 1.0-SNAPSHOT: :
Define value for property 'package' com.rocorock: :
Confirm properties configuration:
groupId: com.rocorock
artifactId: mvn-spring
version: 1.0-SNAPSHOT
package: com.rocorock
 Y: : y
```
Mavenでは`groupId`、`artifactId`、`version`、`package`の指定が必要です。
パッケージ名とかで利用されます。本質的にはなんでもOKだと思います。
`version`、`package`はそのままEnterを押せば、勝手に反映してくれます。
今回Mavenの設定項目の説明は掲載しませんが、Mavenについて知りたい方はこちらの本をお勧めします。
[Javaビルドツール入門](https://www.amazon.co.jp/Java%E3%83%93%E3%83%AB%E3%83%89%E3%83%84%E3%83%BC%E3%83%AB%E5%85%A5%E9%96%80-Maven-Gradle-SBT-Bazel%E5%AF%BE%E5%BF%9C/dp/4798049387)
コンソールに以下の表示が出たら成功です。

```:WSL2
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: spring-boot-sample-tomcat-archetype:1.0.2.RELEASE
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: com.rocorock
[INFO] Parameter: artifactId, Value: mvn-spring
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: com.rocorock
[INFO] Parameter: packageInPathFormat, Value: com/rocorock
[INFO] Parameter: package, Value: com.rocorock
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: groupId, Value: com.rocorock
[INFO] Parameter: artifactId, Value: mvn-spring
[INFO] Project created from Archetype in dir: /home/tomoya/web-app/mvn-spring
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  02:26 min
[INFO] Finished at: 2020-09-06T16:51:46+09:00
[INFO] ------------------------------------------------------------------------
```
できたファイルをパッケージ化してみましょう。

```:WSL2
$ cd mvn-spring
$ mvn package

*** 略

Results :

Failed tests:
  SampleTomcatApplicationTests.testHome:53 expected:<Hello [World]> but was:<Hello [DESKTOP-F147IF8]>
  NonAutoConfigurationSampleTomcatApplicationTests.testHome:82 expected:<Hello [World]> but was:<Hello [DESKTOP-F147IF8]>

Tests run: 2, Failures: 2, Errors: 0, Skipped: 0

[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  4.461 s
[INFO] Finished at: 2020-09-06T16:52:06+09:00
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.15:test (default-test) on project mvn-spring: There are test failures.
[ERROR]
[ERROR] Please refer to /home/tomoya/web-app/mvn-spring/target/surefire-reports for the individual test results.
[ERROR] -> [Help 1]
[ERROR]
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]
[ERROR] For more information about the errors and possible solutions, please read the following articles:
[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException
```
`BUILD FAILURE`と出ました。コンソールを確認してみましょう。

```
Failed tests:
  SampleTomcatApplicationTests.testHome:53 expected:<Hello [World]> but was:<Hello [DESKTOP-F147IF8]>
  NonAutoConfigurationSampleTomcatApplicationTests.testHome:82 expected:<Hello [World]> but was:<Hello [DESKTOP-F147IF8]>
```

どうやらテストに失敗しているようです。`expected:<Hello [World]> but was:<Hello [DESKTOP-F147IF8]>`となっているので、'Hello World'と返すようにコードを直してあげましょう。（なぜサンプルなのにエラーを吐く…）
まずはテストコードを確認します。

```SampleTomcatApplicationTests.java
/*
 * Copyright 2012-2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.rocorock.tomcat;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.IntegrationTest;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.boot.test.TestRestTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;

import static org.junit.Assert.assertEquals;

/**
 * Basic integration tests for demo application.
 * 
 * @author Dave Syer
 */
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = SampleTomcatApplication.class)
@WebAppConfiguration
@IntegrationTest("server.port:0")
@DirtiesContext
public class SampleTomcatApplicationTests {

	@Value("${local.server.port}")
	private int port;

	@Test
	public void testHome() throws Exception {
		ResponseEntity<String> entity = new TestRestTemplate().getForEntity(
				"http://localhost:" + this.port, String.class);
		assertEquals(HttpStatus.OK, entity.getStatusCode());
		assertEquals("Hello World", entity.getBody());
	}

}
```
` assertEquals("Hello World", entity.getBody())`がエラーとなっているテストなので、ここのテストが通るように修正します。

```HelloWorldService.java
/*
 * Copyright 2012-2013 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.rocorock.tomcat.service;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class HelloWorldService {

	@Value("${name:World}")
	private String name;

	public String getHelloMessage() {
		return "Hello World"; //修正箇所
	}

}
```

今回はテストを回避するために"Hello World"とベタ打ちしました。
本質的には解決していませんが、とりあえず動くようにならないと始まらないのでこのまま進めます。
もう一度パッケージ化してみましょう。

```:WSL2
$ mvn package

*** 略
[INFO] Building jar: /home/tomoya/web-app/mvn-spring/target/mvn-spring-1.0-SNAPSHOT.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:1.0.2.RELEASE:repackage (default) @ mvn-spring ---
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  4.288 s
[INFO] Finished at: 2020-09-06T17:00:47+09:00
[INFO] ------------------------------------------------------------------------
```
今度は成功しました。成功すると新しくtargetフォルダが作成されます。
ここに実行可能なjarが保存されています。早速動かしてみましょう。

```:WSL2
$ ls
pom.xml  src  target
$ java -jar target/mvn-spring-1.0-SNAPSHOT.jar

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.0.2.RELEASE)

2020-09-06 17:01:19.928  INFO 5988 --- [           main] c.r.tomcat.SampleTomcatApplication       : Starting SampleTomcatApplication on DESKTOP-F147IF8 with PID 5988 (/home/tomoya/web-app/mvn-spring/target/mvn-spring-1.0-SNAPSHOT.jar started by tomoya in /home/tomoya/web-app/mvn-spring)
2020-09-06 17:01:19.951  INFO 5988 --- [           main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@414be896: startup date [Sun Sep 06 17:01:19 JST 2020]; root of context hierarchy
```
楽しい起動スクリプトが表示されます。Tomcatはデフォルトではポート`8080`で起動するので、アクセスしてみましょう。
ブラウザで`http://localhost:8080`と入力してください。`Hello World`と表示されたら成功です。
コンソール上で`Ctrl + C`を押してTomcatを止めれば、デモ終了です。

# 終わりに

会議室予約アプリを作成するといいながら、Tomcatのサンプルを動かして終わりました…
次回以降このサンプルを拡張して開発したいと思います！（挫折したら記事にならないかも）