JAX-RS の HelloWorld を LibertyとtWASでためす

# 目的

LibertyとtWASのJAX-RS環境を構築して HelloWorld のような簡単なプログラムでテストします。

# Libertyテスト環境の構築

用意したGitレポジトリをクローンします。
```
git clone http://github.com/pdprof/jaxrs.git
cd jaxrs/jaxrs-docker
```

Libertyのイメージをビルドして開始します。
```
./setup-docker.sh
./start.sh
```

Libertyの起動を確認します。jaxrs.clientにアクセスします。

http://localhost:9080/jaxrs.client/


# tWASテスト環境の構築

Libetyを起動している場合には停止します。

```
docker stop jaxrs
```

tWASのイメージをビルドして開始します。
```
./setup-was90.sh
./was90-start.sh
```

Liberty, tWAS どちらかを起動しテストします。


# 構成済みのユーザー

tWASの管理ユーザーは 

|User | Password|
|---|---|
|wsadmin|passw0rd|

になっています。Liberty はセキュリティが無効になっています。構成は以下のファイルでされています。



# テストコード

実行しているHelloWorldの説明をしつつ、テストします。

## JAX-RS Provider

JAX-RS Provider 側は

https://github.com/pdprof/jaxrs/blob/master/jaxrs/src/main/java/pdprof/jaxrs/PdprofApplication.java
```PdprofApplication.java
package pdprof.jaxrs;

import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;

@ApplicationPath("pdprof")
public class PdprofApplication extends Application {

}
```

https://github.com/pdprof/jaxrs/blob/master/jaxrs/src/main/java/pdprof/jaxrs/SleepService.java
```SleepService.java
package pdprof.jaxrs;

import javax.ws.rs.FormParam;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("sleep")
public class SleepService {
    
	@GET
    public String hello() {
        return "Hello World!!";
    }
	
	@POST
    @Produces(MediaType.TEXT_PLAIN)
	public String sleep(@FormParam("time") String t) {
		int i = 10000;
		try { i = Integer.parseInt(t); } catch (Exception e) {}
		try {
			Thread.sleep(i);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		return "Sleep " + t + " milliseconds";
	}
	
}
```

の2つを WARにパッケージすることで動作します。

```
@ApplicationPath("pdprof")
@Path("sleep")
@GET
```

でブラウザで 

http://localhost:9080/jaxrs/pdprof/sleep

にアクセスすることで

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262366/5b1cad84-5190-42b1-96af-ca400cad2d7e.png)

になります。

# JAX-RS Client

クライアントの用意はいくつかあります。確認は以下のフォルダの javaファイルを見ます。

https://github.com/pdprof/jaxrs/tree/master/jaxrs.client/src/main/java/pdprof/jaxrs/client


ここでは ReceiveClientServlet をもとに説明します。

https://github.com/pdprof/jaxrs/blob/master/jaxrs.client/src/main/java/pdprof/jaxrs/client/ReceiveClientServlet.java
```ReceiveClientServlet.java
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		System.out.println("*** ReceiveClientServlet START ***");
		String timeout = request.getParameter("timeout");
		String sleep = request.getParameter("sleep");
		String port = request.getParameter("port");
		try { Integer.parseInt(timeout); } catch (Exception e) { timeout = "10000";}
		try { Integer.parseInt(sleep); } catch (Exception e) { sleep = "20000";}
		try { Integer.parseInt(port); } catch (Exception e) { port = "9080";}
		String host = request.getParameter("host");
		if (host == null) { host = "localhost"; }
		ClientBuilder cb = ClientBuilder.newBuilder().register(new PdprofResponseFilter()).property("com.ibm.ws.jaxrs.client.receive.timeout", timeout);

		Client c = cb.build();
		String res = null;
		Entity<Form> entity = Entity.entity(new Form().param("time", sleep).param("method", "post"),
			    MediaType.APPLICATION_FORM_URLENCODED_TYPE);
		
		MultivaluedMap<String, Object> headers = new MultivaluedHashMap<>();
		headers.putSingle("Expect", "100-continue");
		
		try {
			res = c.target("http://"+ host + ":" + port + "/jaxrs/pdprof/sleep").request().headers(headers).post(entity, String.class);
		} catch (Exception e) {
			res = "[Error]:" + e.toString();
			System.out.println("*** ReceiveClientServlet ***");
			e.printStackTrace();
		} finally {
			c.close();
		}
		response.getWriter().append(res);
	
```

ほかのクライアントもあわせて、JAX-RSの処理として覚えておくと良いのは

```java
ClientBuilder cb = ClientBuilder.newBuilder().register(new PdprofResponseFilter()).property("com.ibm.ws.jaxrs.client.connection.timeout", timeout);

ClientBuilder cb = ClientBuilder.newBuilder().register(new 
PdprofResponseFilter()).property("com.ibm.ws.jaxrs.client.receive.timeout", timeout);

Entity<Form> entity = Entity.entity(new Form().param("time", sleep).param("method", "post"),
			    MediaType.APPLICATION_FORM_URLENCODED_TYPE);

res = c.target("http://"+ host + ":" + port + "/jaxrs/pdprof/sleep").request().headers(headers).post(entity, String.class);
```

- Connect Timeout : com.ibm.ws.jaxrs.client.connection.timeout
- Response Timeout : com.ibm.ws.jaxrs.client.receive.timeout
- ClientResponseFilter : PdprofResponseFilter()
- POSTするデータの登録とリクエスト送信

やりたいことに合わせて、コードを変更します。


# JAX-RS クライアントのテスト

コードの説明をしたのでテストします。LibertyやtWASが起動できていれば、http://localhost:9080/jaxrs.client/ にアクセスできます。

http://localhost:9080/jaxrs.client/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262366/e972dad0-7736-4a02-bb17-f45dc8c63c5d.png)


## Connect timeout

相手先がみつからず、TCPIPの接続完了がタイムアウトするテストです。10.10.10.10 などネットワークにいないIPを指定して submit です。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262366/277a570f-7f53-42c4-8a97-bcfc7ae396f9.png)

```txt
[Error]:javax.ws.rs.ProcessingException: java.net.SocketTimeoutException: SocketTimeoutException invoking http://10.10.10.10:9080/jaxrs/pdprof/sleep: connect timed out
```

Libertyテスト時のログです。
```console.log
*** ConnectClientServlet ***
[err] javax.ws.rs.ProcessingException: java.net.SocketTimeoutException: SocketTimeoutException invoking http://10.10.10.10:9080/jaxrs/pdprof/sleep: connect timed out
[err] 	at org.apache.cxf.jaxrs.client.AbstractClient.checkClientException(AbstractClient.java:632)
[err] 	at [internal classes]
[err] 	at pdprof.jaxrs.client.ConnectClientServlet.doGet(ConnectClientServlet.java:57)
[err] 	at javax.servlet.http.HttpServlet.service(HttpServlet.java:686)
[err] 	at javax.servlet.http.HttpServlet.service(HttpServlet.java:791)
[err] 	at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1260)
[err] 	at [internal classes]
[err] 	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
[err] 	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
[err] 	at java.base/java.lang.Thread.run(Thread.java:839)
[err] Caused by: 
[err] java.net.SocketTimeoutException: SocketTimeoutException invoking http://10.10.10.10:9080/jaxrs/pdprof/sleep: connect timed out
[err] 	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
[err] 	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
[err] 	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
[err] 	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
[err] 	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.mapException(HTTPConduit.java:1458)
[err] 	at [internal classes]
[err] 	... 34 more
[err] Caused by: 
[err] java.net.SocketTimeoutException: connect timed out
[err] 	at java.base/java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:412)
[err] 	at java.base/java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:255)
[err] 	at java.base/java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:237)
[err] 	at java.base/java.net.Socket.connect(Socket.java:609)
[err] 	at java.base/sun.net.NetworkClient.doConnect(NetworkClient.java:177)
[err] 	at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:507)
[err] 	at java.base/sun.net.www.http.HttpClient.openServer(HttpClient.java:602)
[err] 	at java.base/sun.net.www.http.HttpClient.<init>(HttpClient.java:275)
[err] 	at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:374)
[err] 	at java.base/sun.net.www.http.HttpClient.New(HttpClient.java:395)
[err] 	at java.base/sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:1253)
[err] 	at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(HttpURLConnection.java:1232)
[err] 	at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:1081)
[err] 	at java.base/sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:1015)
[err] 	at java.base/sun.net.www.protocol.http.HttpURLConnection.getOutputStream0(HttpURLConnection.java:1367)
[err] 	at java.base/sun.net.www.protocol.http.HttpURLConnection.getOutputStream(HttpURLConnection.java:1342)
[err] 	at org.apache.cxf.transport.http.URLConnectionHTTPConduit$URLConnectionWrappedOutputStream.setupWrappedStream(URLConnectionHTTPConduit.java:334)
[err] 	at [internal classes]
[err] 	... 41 more
*** ConnectClientServlet END ***

```

## Receive timeout

相手先にリクエストは送信できたが、応答が戻らずにタイムアウトするテストです。Sleep time を Receive timeout より長くして submit です。hostname の調整で、tWAS環境にアクセスさせることもできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262366/4d6841ec-f6ff-4bea-8e56-8a91012324e2.png)

```
[Error]:javax.ws.rs.ProcessingException: java.net.SocketTimeoutException: SocketTimeoutException invoking http://localhost:9080/jaxrs/pdprof/sleep: Read timed out
```

Libertyテスト時のログです。
```console.log
*** ReceiveClientServlet START ***
*** ReceiveClientServlet ***
[err] javax.ws.rs.ProcessingException: java.net.SocketTimeoutException: SocketTimeoutException invoking http://localhost:9080/jaxrs/pdprof/sleep: Read timed outJAX-RS
[err] 	at org.apache.cxf.jaxrs.client.AbstractClient.checkClientException(AbstractClient.java:632)
[err] 	at [internal classes]
[err] 	at pdprof.jaxrs.client.ReceiveClientServlet.doGet(ReceiveClientServlet.java:55)
[err] 	at javax.servlet.http.HttpServlet.service(HttpServlet.java:686)
[err] 	at javax.servlet.http.HttpServlet.service(HttpServlet.java:791)
[err] 	at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1260)
[err] 	at [internal classes]
[err] 	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
[err] 	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
[err] 	at java.base/java.lang.Thread.run(Thread.java:839)
[err] Caused by: 
[err] java.net.SocketTimeoutException: SocketTimeoutException invoking http://localhost:9080/jaxrs/pdprof/sleep: Read timed out
[err] 	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
[err] 	at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
[err] 	at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
[err] 	at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
[err] 	at org.apache.cxf.transport.http.HTTPConduit$WrappedOutputStream.mapException(HTTPConduit.java:1458)
[err] 	at [internal classes]
[err] 	... 33 more
[err] Caused by: 
[err] java.net.SocketTimeoutException: Read timed out
[err] 	at java.base/java.net.SocketInputStream.socketRead(SocketInputStream.java:115)
[err] 	at java.base/java.net.SocketInputStream.read(SocketInputStream.java:168)
[err] 	at java.base/java.net.SocketInputStream.read(SocketInputStream.java:140)
[err] 	at java.base/java.io.BufferedInputStream.fill(BufferedInputStream.java:252)
[err] 	at java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:292)
[err] 	at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:351)
[err] 	at java.base/sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:787)
[err] 	at java.base/sun.net.www.http.HttpClient.parseHTTPHeader(HttpClient.java:925)
[err] 	at java.base/sun.net.www.http.HttpClient.parseHTTP(HttpClient.java:722)
[err] 	at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1615)
[err] 	at java.base/sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1520)
[err] 	at java.base/java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:527)
[err] 	at org.apache.cxf.transport.http.URLConnectionHTTPConduit$URLConnectionWrappedOutputStream$2.run(URLConnectionHTTPConduit.java:441)
[err] 	at [internal classes]
[err] 	at java.base/java.security.AccessController.doPrivileged(AccessController.java:746)
[err] 	at org.apache.cxf.transport.http.URLConnectionHTTPConduit$URLConnectionWrappedOutputStream.getResponseCode(URLConnectionHTTPConduit.java:437)
[err] 	at [internal classes]
[err] 	... 40 more
*** ReceiveClientServlet END ***
```

## Simple JAX-RS client

最後にエラーなしのクライアントです。http://localhost:9080/jaxrs.client/get をクリックすると以下のページが出力されます。クライアントからの送信されたリクエストヘッダーが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/262366/24ea508f-373e-45d8-af5b-d5ab0c7e6586.png)


# トラブルシューティング

毎度のことですが、よくつかう dockerコマンドを書いておきます。この文書でもpodman, dockerとまとまりがないですが、Linux では dockerがpodmanを呼び出すようになっていました。

```
docker ps # 動作中のContainer確認
docker logs -f jaxrs # ログ確認
docker restart jaxrs # 再起動
docker inspect jaxrs # container 確認
docker exec -it jaxrs bash # Container内状況確認
```

# まとめ

LibertyとtWASの環境を構築して、JAX-RSをテストしました。