RestTemplate による HTTP 通信の際に発生する様々なエラー (例外) を処理する

## 概要

- Spring Framework の RestTemplate クラスを利用して HTTP 通信をする
- サンプルプログラムを実行して HTTP 通信の際に発生する様々な例外を確認する

## 例外クラス RestClientException の継承関係ツリー

Spring Framework のドキュメントを見ると、RestTemplate は org.springframework.web.client.RestClientException を投げることが記載されている。
例外を catch して中身を確認すると、実際には RestClientException のサブクラスが throw されているのがわかる。

- RestClientException (RestTemplate が投げる例外のベースとなるクラス)
    - RestClientResponseException (HTTPレスポンスデータを含む例外のベースとなるクラス)
        - HttpStatusCodeException (ステータスコードに基づく例外の抽象クラス)
            - HttpClientErrorException (HTTP 4xx系を受信したとき)
                - 4xx系ステータスコードのそれぞれの例外クラス(複数)
            - HttpServerErrorException (HTTP 5xx系を受信したとき)
                - 5xx系ステータスコードのそれぞれの例外クラス(複数)
        - UnknownHttpStatusCodeException (不明なステータスコードのとき)
    - ResourceAccessException (I/Oエラーが発生したとき)

## サンプルプログラムで発生した例外の一覧

### HTTP 404 Not Found の場合
- 発生した例外: org.springframework.web.client.HttpClientErrorException$NotFound
- HttpClientErrorException.NotFound は org.springframework.web.client.HttpClientErrorException のサブクラス

### HTTP 500 Internal Server Error の場合
- 発生した例外: org.springframework.web.client.HttpServerErrorException$InternalServerError
- HttpServerErrorException.InternalServerError は org.springframework.web.client.HttpServerErrorException のサブクラス

### 不明なステータスコードの場合
- 発生した例外: org.springframework.web.client.UnknownHttpStatusCodeException

### URL文字列ではない場合
- 発生した例外: java.lang.IllegalArgumentException

### URL文字列として解析できない場合
- 発生した例外: java.net.URISyntaxException

### URLのスキーム名が不明の場合
- 発生した例外: org.springframework.web.client.ResourceAccessException
- 原因となる例外: java.net.MalformedURLException

### ホスト名からIPアドレスが引けない場合
- 発生した例外: org.springframework.web.client.ResourceAccessException
- 原因となる例外: java.net.UnknownHostException

### Webサーバが起動していないポートへアクセスした場合 (Connection refused)
- 発生した例外: org.springframework.web.client.ResourceAccessException
- 原因となる例外: java.net.ConnectException

### 信頼できないSSLサーバ証明書 (自己署名証明書, オレオレ証明書) の場合
- 発生した例外: org.springframework.web.client.ResourceAccessException
- 原因となる例外: javax.net.ssl.SSLHandshakeException

### 接続タイムアウト (Connection Timeout) の場合 (マシンが存在しないIPアドレスなど)
- 発生した例外: org.springframework.web.client.ResourceAccessException
- 原因となる例外: java.net.SocketTimeoutException

### 読み取りタイムアウト (Read Timeout) の場合
- 発生した例外: org.springframework.web.client.ResourceAccessException
- 原因となる例外: java.net.SocketTimeoutException

## 動作確認環境

- macOS Mojave
- OpenJDK 11.0.2
- Spring Framework 5.1.8

## サンプルプログラムのソースコード

### ソースコード一覧

```
├── pom.xml
└── src
    └── main
        └── java
            └── com
                └── example
                    └── MyHttpClient.java
```

### Maven のビルド用ファイル pom.xml

今回はサンプルプログラムを Maven でビルドする。
RestTemplate クラスは Spring Framework の Spring Web に含まれているため、dependencies に spring-web を追加する。

```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.example</groupId>
  <artifactId>my-http-client</artifactId>
  <version>1.0-SNAPSHOT</version>

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

  <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.example.MyHttpClient</mainClass>
            </manifest>
          </archive>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-web -->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-web</artifactId>
      <version>5.1.8.RELEASE</version>
    </dependency>
  </dependencies>

</project>
```

### MyHttpClient.java

HTTP 通信の際に発生する様々な例外を確認するためのクラス。

```java
package com.example;

import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestClientResponseException;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.net.URISyntaxException;

public class MyHttpClient {

  public static void main(String[] args) {

    RestTemplate restTemplate = new RestTemplate();

    // タイムアウト値を設定
    if(restTemplate.getRequestFactory() instanceof SimpleClientHttpRequestFactory) {
      SimpleClientHttpRequestFactory rf = (SimpleClientHttpRequestFactory)restTemplate.getRequestFactory();
      rf.setConnectTimeout(1000); // 接続タイムアウトを1000ミリ秒に設定
      rf.setReadTimeout(1000); // 読み取りタイムアウトを1000ミリ秒に設定
    }

    // RestTemplate が内部で使用している ClientHttpRequestFactory のクラス名を出力
    System.out.println("ClientHttpRequestFactory: " + restTemplate.getRequestFactory().getClass().getName());

    // いろいろなパターンを試す
    get("HTTP 200 OK", restTemplate, "http://localhost:8000/");
    get("HTTP 301 Moved Permanently (リダイレクトは自動で処理されるはず)", restTemplate, "http://localhost:8000/?status=301&headers=%7B%22location%22%3A%22http%3A%2F%2Flocalhost%3A8000%2F%22%7D");
    get("HTTP 404 Not Found", restTemplate, "http://localhost:8000/?status=404");
    get("HTTP 500 Internal Server Error", restTemplate, "http://localhost:8000/?status=500");
    get("不明なステータスコード", restTemplate, "http://localhost:8000/?status=599");
    get("URL文字列ではない", restTemplate, "foo");
    get("URL文字列として解析できない", restTemplate, "http://local host:8000/");
    get("URLのスキーム名が不明", restTemplate, "foo://localhost:8000/");
    get("ホスト名からIPアドレスが引けない", restTemplate, "http://localhostlocalhost:8000/");
    get("Webサーバが起動していないポート", restTemplate, "http://localhost:1234/");
    get("信頼できないSSLサーバ証明書 (自己署名証明書, オレオレ証明書)", restTemplate, "https://localhost:8443/");
    get("接続タイムアウト (Connection Timeout) マシンが存在しないIPアドレス", restTemplate, "http://127.0.0.2/");
    get("読み取りタイムアウト (Read Timeout)", restTemplate, "http://localhost:8000/?wait=10");
  }

  /**
   * 指定した URL にアクセスして情報を出力する。
   *
   * @param title        タイトル (どういうパターンかの説明)
   * @param restTemplate HTTP 通信に利用する RestTemplate オブジェクト
   * @param url          アクセスする URL
   */
  private static void get(String title, RestTemplate restTemplate, String url) {
    try {
      System.out.println("************************************************************");
      System.out.println(title);
      System.out.println("URL: " + url);
      URI uri = new URI(url);
      RequestEntity req = RequestEntity.get(uri).build();
      ResponseEntity<String> res = restTemplate.exchange(req, String.class);
      HttpStatus status = res.getStatusCode();
      HttpHeaders headers = res.getHeaders();
      String body = res.getBody();
      System.out.println("Status: " + status);
    } catch (RestClientResponseException e) {
      System.out.println("Class: " + e.getClass().getName());
      int statusCode = e.getRawStatusCode();
      String statusText = e.getStatusText();
      System.out.println("StatusCode: " + statusCode);
      System.out.println("StatusText: " + statusText);
      e.printStackTrace();
    } catch (ResourceAccessException e) {
      System.out.println("Class: " + e.getClass().getName());
      e.printStackTrace();
    } catch (RestClientException e) {
      System.out.println("Class: " + e.getClass().getName());
      e.printStackTrace();
    } catch (URISyntaxException e) {
      System.out.println("Class: " + e.getClass().getName());
      e.printStackTrace();
    } catch (Exception e) {
      System.out.println("Class: " + e.getClass().getName());
      e.printStackTrace();
    }
  }
}
```

## ソースコードのビルド

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

```
$ mvn package
```

## 実行結果

サンプルプログラムを実行してそれぞれのパターンでどのような例外が発生しているかを確認する。

```
$ java -jar target/my-http-client-1.0-SNAPSHOT-jar-with-dependencies.jar 
ClientHttpRequestFactory: org.springframework.http.client.SimpleClientHttpRequestFactory
************************************************************
HTTP 200 OK
URL: http://localhost:8000/
Status: 200 OK
************************************************************
HTTP 301 Moved Permanently (リダイレクトは自動で処理されるはず)
URL: http://localhost:8000/?status=301&headers=%7B%22location%22%3A%22http%3A%2F%2Flocalhost%3A8000%2F%22%7D
Status: 200 OK
************************************************************
HTTP 404 Not Found
URL: http://localhost:8000/?status=404
Class: org.springframework.web.client.HttpClientErrorException$NotFound
StatusCode: 404
StatusText: Not Found
org.springframework.web.client.HttpClientErrorException$NotFound: 404 Not Found
	at org.springframework.web.client.HttpClientErrorException.create(HttpClientErrorException.java:85)
	at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:122)
	at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:102)
	at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
	at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:778)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:736)
	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:637)
	at com.example.MyHttpClient.get(MyHttpClient.java:62)
	at com.example.MyHttpClient.main(MyHttpClient.java:35)
************************************************************
HTTP 500 Internal Server Error
URL: http://localhost:8000/?status=500
Class: org.springframework.web.client.HttpServerErrorException$InternalServerError
StatusCode: 500
StatusText: Internal Server Error
org.springframework.web.client.HttpServerErrorException$InternalServerError: 500 Internal Server Error
	at org.springframework.web.client.HttpServerErrorException.create(HttpServerErrorException.java:79)
	at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:124)
	at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:102)
	at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
	at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:778)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:736)
	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:637)
	at com.example.MyHttpClient.get(MyHttpClient.java:62)
	at com.example.MyHttpClient.main(MyHttpClient.java:36)
************************************************************
不明なステータスコード
URL: http://localhost:8000/?status=599
Class: org.springframework.web.client.UnknownHttpStatusCodeException
StatusCode: 599
StatusText: null
org.springframework.web.client.UnknownHttpStatusCodeException: Unknown status code [599] null
	at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:100)
	at org.springframework.web.client.ResponseErrorHandler.handleError(ResponseErrorHandler.java:63)
	at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:778)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:736)
	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:637)
	at com.example.MyHttpClient.get(MyHttpClient.java:62)
	at com.example.MyHttpClient.main(MyHttpClient.java:37)
************************************************************
URL文字列ではない
URL: foo
Class: java.lang.IllegalArgumentException
java.lang.IllegalArgumentException: URI is not absolute
	at java.base/java.net.URL.fromURI(URL.java:674)
	at java.base/java.net.URI.toURL(URI.java:1116)
	at org.springframework.http.client.SimpleClientHttpRequestFactory.createRequest(SimpleClientHttpRequestFactory.java:145)
	at org.springframework.http.client.support.HttpAccessor.createRequest(HttpAccessor.java:87)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:731)
	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:637)
	at com.example.MyHttpClient.get(MyHttpClient.java:62)
	at com.example.MyHttpClient.main(MyHttpClient.java:38)
************************************************************
URL文字列として解析できない
URL: http://local host:8000/
Class: java.net.URISyntaxException
java.net.URISyntaxException: Illegal character in authority at index 7: http://local host:8000/
	at java.base/java.net.URI$Parser.fail(URI.java:2915)
	at java.base/java.net.URI$Parser.parseAuthority(URI.java:3249)
	at java.base/java.net.URI$Parser.parseHierarchical(URI.java:3160)
	at java.base/java.net.URI$Parser.parse(URI.java:3116)
	at java.base/java.net.URI.<init>(URI.java:600)
	at com.example.MyHttpClient.get(MyHttpClient.java:60)
	at com.example.MyHttpClient.main(MyHttpClient.java:39)
************************************************************
URLのスキーム名が不明
URL: foo://localhost:8000/
Class: org.springframework.web.client.ResourceAccessException
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "foo://localhost:8000/": unknown protocol: foo; nested exception is java.net.MalformedURLException: unknown protocol: foo
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:744)
	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:637)
	at com.example.MyHttpClient.get(MyHttpClient.java:62)
	at com.example.MyHttpClient.main(MyHttpClient.java:40)
Caused by: java.net.MalformedURLException: unknown protocol: foo
	at java.base/java.net.URL.<init>(URL.java:634)
	at java.base/java.net.URL.fromURI(URL.java:701)
	at java.base/java.net.URI.toURL(URI.java:1116)
	at org.springframework.http.client.SimpleClientHttpRequestFactory.createRequest(SimpleClientHttpRequestFactory.java:145)
	at org.springframework.http.client.support.HttpAccessor.createRequest(HttpAccessor.java:87)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:731)
	... 3 more
************************************************************
ホスト名からIPアドレスが引けない
URL: http://localhostlocalhost:8000/
Class: org.springframework.web.client.ResourceAccessException
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhostlocalhost:8000/": localhostlocalhost; nested exception is java.net.UnknownHostException: localhostlocalhost
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:744)
	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:637)
	at com.example.MyHttpClient.get(MyHttpClient.java:62)
	at com.example.MyHttpClient.main(MyHttpClient.java:41)
Caused by: java.net.UnknownHostException: localhostlocalhost
	at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:220)
	at java.base/java.net.Socket.connect(Socket.java:591)
	at java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:177)
	at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:474)
	at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:569)
	at java.base/sun.net.www.http.HttpClient.<init>(HttpClient.java:242)
	at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:341)
	at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:362)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1242)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1181)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1075)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:1009)
	at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:76)
	at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
	at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:735)
	... 3 more
************************************************************
Webサーバが起動していないポート
URL: http://localhost:1234/
Class: org.springframework.web.client.ResourceAccessException
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:1234/": Connection refused (Connection refused); nested exception is java.net.ConnectException: Connection refused (Connection refused)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:744)
	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:637)
	at com.example.MyHttpClient.get(MyHttpClient.java:62)
	at com.example.MyHttpClient.main(MyHttpClient.java:42)
Caused by: java.net.ConnectException: Connection refused (Connection refused)
	at java.base/java.net.PlainSocketImpl.socketConnect(Native Method)
	at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:399)
	at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:242)
	at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:224)
	at java.base/java.net.Socket.connect(Socket.java:591)
	at java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:177)
	at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:474)
	at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:569)
	at java.base/sun.net.www.http.HttpClient.<init>(HttpClient.java:242)
	at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:341)
	at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:362)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1242)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1181)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1075)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:1009)
	at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:76)
	at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
	at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:735)
	... 3 more
************************************************************
信頼できないSSLサーバ証明書 (自己署名証明書, オレオレ証明書)
URL: https://localhost:8443/
Class: org.springframework.web.client.ResourceAccessException
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "https://localhost:8443/": PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target; nested exception is javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:744)
	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:637)
	at com.example.MyHttpClient.get(MyHttpClient.java:62)
	at com.example.MyHttpClient.main(MyHttpClient.java:43)
Caused by: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:128)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:321)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:264)
	at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:259)
	at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:642)
	at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.onCertificate(CertificateMessage.java:461)
	at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.consume(CertificateMessage.java:361)
	at java.base/sun.security.ssl.SSLHandshake.consume(SSLHandshake.java:392)
	at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:444)
	at java.base/sun.security.ssl.HandshakeContext.dispatch(HandshakeContext.java:421)
	at java.base/sun.security.ssl.TransportContext.dispatch(TransportContext.java:178)
	at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:164)
	at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1152)
	at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1063)
	at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:402)
	at java.base/sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:567)
	at java.base/sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
	at java.base/sun.net.www.protocol.https.HttpsURLConnectionImpl.connect(HttpsURLConnectionImpl.java:163)
	at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:76)
	at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
	at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:735)
	... 3 more
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385)
	at java.base/sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:290)
	at java.base/sun.security.validator.Validator.validate(Validator.java:264)
	at java.base/sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:321)
	at java.base/sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:221)
	at java.base/sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:129)
	at java.base/sun.security.ssl.CertificateMessage$T12CertificateConsumer.checkServerCerts(CertificateMessage.java:626)
	... 20 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
	at java.base/sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
	at java.base/sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
	at java.base/java.security.cert.CertPathBuilder.build(CertPathBuilder.java:297)
	at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380)
	... 26 more
************************************************************
接続タイムアウト (Connection Timeout) マシンが存在しないIPアドレス
URL: http://127.0.0.2/
Class: org.springframework.web.client.ResourceAccessException
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://127.0.0.2/": connect timed out; nested exception is java.net.SocketTimeoutException: connect timed out
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:744)
	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:637)
	at com.example.MyHttpClient.get(MyHttpClient.java:62)
	at com.example.MyHttpClient.main(MyHttpClient.java:44)
Caused by: java.net.SocketTimeoutException: connect timed out
	at java.base/java.net.PlainSocketImpl.socketConnect(Native Method)
	at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:399)
	at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:242)
	at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:224)
	at java.base/java.net.Socket.connect(Socket.java:591)
	at java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:177)
	at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:474)
	at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:569)
	at java.base/sun.net.www.http.HttpClient.<init>(HttpClient.java:242)
	at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:341)
	at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:362)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1242)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1181)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1075)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:1009)
	at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:76)
	at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
	at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:735)
	... 3 more
************************************************************
読み取りタイムアウト (Read Timeout)
URL: http://localhost:8000/?wait=10
Class: org.springframework.web.client.ResourceAccessException
org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http://localhost:8000/": Read timed out; nested exception is java.net.SocketTimeoutException: Read timed out
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:744)
	at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:637)
	at com.example.MyHttpClient.get(MyHttpClient.java:62)
	at com.example.MyHttpClient.main(MyHttpClient.java:45)
Caused by: java.net.SocketTimeoutException: Read timed out
	at java.base/java.net.SocketInputStream.socketRead0(Native Method)
	at java.base/java.net.SocketInputStream.socketRead(SocketInputStream.java:115)
	at java.base/java.net.SocketInputStream.read(SocketInputStream.java:168)
	at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140)
	at java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:252)
	at java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:292)
	at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:351)
	at java.base/sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:746)
	at java.base/sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:689)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1604)
	at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1509)
	at java.base/java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:527)
	at org.springframework.http.client.SimpleBufferingClientHttpRequest.executeInternal(SimpleBufferingClientHttpRequest.java:82)
	at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
	at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:53)
	at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:735)
	... 3 more
```

## 参考資料
- [spring\-framework/spring\-web at master · spring\-projects/spring\-framework · GitHub](https://github.com/spring-projects/spring-framework/tree/master/spring-web)
- [RestTemplate \(Spring Framework 5\.1\.8\.RELEASE API\)](https://docs.spring.io/spring/docs/5.1.8.RELEASE/javadoc-api/org/springframework/web/client/RestTemplate.html)
- [RestClientException \(Spring Framework 5\.1\.8\.RELEASE API\)](https://docs.spring.io/spring/docs/5.1.8.RELEASE/javadoc-api/org/springframework/web/client/RestClientException.html)
- [SimpleClientHttpRequestFactory \(Spring Framework 5\.1\.8\.RELEASE API\)](https://docs.spring.io/spring/docs/5.1.8.RELEASE/javadoc-api/org/springframework/http/client/SimpleClientHttpRequestFactory.html)
- [5\.2\. RESTクライアント（HTTPクライアント） — TERASOLUNA Server Framework for Java \(5\.x\) Development Guideline 5\.5\.1\.RELEASE documentation](http://terasolunaorg.github.io/guideline/5.5.1.RELEASE/ja/ArchitectureInDetail/WebServiceDetail/RestClient.html)
- [【Spring】RestTemplateが投げる例外クラスまとめ \- Qiita](https://qiita.com/shotana/items/88b120432e694c9b63f6)