What's?
Open LibertyのJakarta RESTful Web Services(JAX-RS)のリファレンスを見ていて、JSONをどうやって処理しているのかがわからなかったので確認してみようかなと。
結果としては、Jakarta JSON Binding(JSON-B)でした。
環境
$ java --version
openjdk 21.0.4 2024-07-16
OpenJDK Runtime Environment (build 21.0.4+7-Ubuntu-1ubuntu222.04)
OpenJDK 64-Bit Server VM (build 21.0.4+7-Ubuntu-1ubuntu222.04, mixed mode, sharing)
$ mvn --version
Apache Maven 3.9.9 (8e8579a9e76f7d015ee5ec7bfcdc97d260186937)
Maven home: /home/charon/.sdkman/candidates/maven/current
Java version: 21.0.4, vendor: Ubuntu, runtime: /usr/lib/jvm/java-21-openjdk-amd64
Default locale: ja_JP, platform encoding: UTF-8
OS name: "linux", version: "5.15.0-124-generic", arch: "amd64", family: "unix"
Open Libertyプロジェクトを作成する
Get started(Create a starter application)を使って、Open LibertyのMavenプロジェクトを作成。
$ curl -G https://start.openliberty.io/api/start \
-o jaxrs-sample.zip \
-d a=jaxrs-sample \
-d b=maven \
-d e=10.0 \
-d g=com.example \
-d j=21 \
-d m=6.1
zipを展開。
$ unzip -d jaxrs-sample jaxrs-sample.zip
Archive: jaxrs-sample.zip
creating: jaxrs-sample/src/main/java/com/example/
inflating: jaxrs-sample/src/main/liberty/config/server.xml
inflating: jaxrs-sample/Dockerfile
inflating: jaxrs-sample/.dockerignore
inflating: jaxrs-sample/src/main/java/com/example/rest/RestApplication.java
inflating: jaxrs-sample/README.txt
inflating: jaxrs-sample/.mvn/wrapper/maven-wrapper.jar
inflating: jaxrs-sample/.mvn/wrapper/maven-wrapper.properties
inflating: jaxrs-sample/mvnw
inflating: jaxrs-sample/mvnw.cmd
inflating: jaxrs-sample/pom.xml
inflating: jaxrs-sample/.gitignore
inflating: jaxrs-sample/src/main/resources/META-INF/microprofile-config.properties
pom.xml
とserver.xml
を少し修正して、JAX-RSソースコードを作成。
Application
のサブクラスは、@ApplicationPath
のパスだけ修正しました。
src/main/java/com/example/rest/RestApplication.java
package com.example.rest;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.core.Application;
@ApplicationPath("/")
public class RestApplication extends Application {
}
JSONを使ったリクエスト、レスポンスに対応するクラス。
src/main/java/com/example/rest/CalcRequest.java
package com.example.rest;
public class CalcRequest {
private Integer x;
private Integer y;
public CalcRequest() {
System.out.println("CalcRequest@constructor");
Thread.dumpStack();
}
public Integer getX() {
return x;
}
public void setX(Integer x) {
System.out.println("CalcRequest@setY");
Thread.dumpStack();
this.x = x;
}
public Integer getY() {
return y;
}
public void setY(Integer y) {
this.y = y;
}
}
src/main/java/com/example/rest/CalcResponse.java
package com.example.rest;
public class CalcResponse {
private Integer result;
public CalcResponse(Integer result) {
this.result = result;
}
public Integer getResult() {
System.out.println("CalcResponse@getX");
Thread.dumpStack();
return result;
}
public void setResult(Integer result) {
this.result = result;
}
}
呼び出し確認ができるように、Thread#dumpStack
を仕込んでいます。
JAX-RSリソースクラス。
src/main/java/com/example/rest/CalcResource.java
package com.example.rest;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/calc")
public class CalcResource {
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public CalcResponse calc(CalcRequest request) {
return new CalcResponse(request.getX() + request.getY());
}
}
JSONを処理しているライブラリを確認する
ビルド、起動。
$ mvn compile liberty:dev
確認。
$ curl -X POST -H 'Content-Type: application/json' localhost:9080/calc -d '{"x": 10, "y": 15}'
{"result":25}
この時に得られたスタックトレース。
[INFO] CalcRequest@constructor
[INFO] [err] java.lang.Exception: Stack trace
[INFO] [err] at java.base/java.lang.Thread.dumpStack(Thread.java:2209)
[INFO] [err] at com.example.rest.CalcRequest.<init>(CalcRequest.java:9)
[INFO] [err] at java.base/jdk.internal.reflect.DirectConstructorHandleAccessor.newInstance(DirectConstructorHandleAccessor.java:62)
[INFO] [err] at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:502)
[INFO] [err] at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:486)
[INFO] [err] at org.eclipse.yasson.internal.ReflectionUtils.createNoArgConstructorInstance(ReflectionUtils.java:282)
[INFO] [err] at [internal classes]
[INFO] [err] at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:413)
[INFO] [err] at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:385)
[INFO] [err] at [internal classes]
[INFO] [err] at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:240)
[INFO] [err] at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:154)
[INFO] [err] at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:385)
[INFO] [err] at [internal classes]
[INFO] [err] at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614)
[INFO] [err] at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1266)
[INFO] [err] at [internal classes]
[INFO] [err] at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
[INFO] [err] at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
[INFO] [err] at java.base/java.lang.Thread.run(Thread.java:1583)
[INFO] CalcRequest@setY
[INFO] [err] java.lang.Exception: Stack trace
[INFO] [err] at java.base/java.lang.Thread.dumpStack(Thread.java:2209)
[INFO] [err] at com.example.rest.CalcRequest.setX(CalcRequest.java:18)
[INFO] [err] at org.eclipse.yasson.internal.deserializer.ValueSetterDeserializer.deserialize(ValueSetterDeserializer.java:37)
[INFO] [err] at [internal classes]
[INFO] [err] at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:413)
[INFO] [err] at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:385)
[INFO] [err] at [internal classes]
[INFO] [err] at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:240)
[INFO] [err] at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:154)
[INFO] [err] at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:385)
[INFO] [err] at [internal classes]
[INFO] [err] at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614)
[INFO] [err] at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1266)
[INFO] [err] at [internal classes]
[INFO] [err] at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
[INFO] [err] at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
[INFO] [err] at java.base/java.lang.Thread.run(Thread.java:1583)
[INFO] CalcResponse@getX
[INFO] [err] java.lang.Exception: Stack trace
[INFO] [err] at java.base/java.lang.Thread.dumpStack(Thread.java:2209)
[INFO] [err] at com.example.rest.CalcResponse.getResult(CalcResponse.java:12)
[INFO] [err] at org.eclipse.yasson.internal.serializer.ValueGetterSerializer.serialize(ValueGetterSerializer.java:39)
[INFO] [err] at org.eclipse.yasson.internal.serializer.ObjectSerializer.lambda$serialize$0(ObjectSerializer.java:41)
[INFO] [err] at java.base/java.util.LinkedHashMap.forEach(LinkedHashMap.java:986)
[INFO] [err] at org.eclipse.yasson.internal.serializer.ObjectSerializer.serialize(ObjectSerializer.java:38)
[INFO] [err] at [internal classes]
[INFO] [err] at org.jboss.resteasy.core.interception.jaxrs.ServerWriterInterceptorContext.lambda$writeTo$1(ServerWriterInterceptorContext.java:74)
[INFO] [err] at org.jboss.resteasy.plugins.server.servlet.Servlet3AsyncHttpRequest$Servlet3ExecutionContext.executeBlockingIo(Servlet3AsyncHttpRequest.java:392)
[INFO] [err] at [internal classes]
[INFO] [err] at org.jboss.resteasy.core.interception.jaxrs.ServerWriterInterceptorContext.lambda$getStarted$0(ServerWriterInterceptorContext.java:68)
[INFO] [err] at org.jboss.resteasy.core.interception.jaxrs.ServerWriterInterceptorContext.aroundWriteTo(ServerWriterInterceptorContext.java:87)
[INFO] [err] at [internal classes]
[INFO] [err] at org.jboss.resteasy.core.ServerResponseWriter.lambda$writeNomapResponse$3(ServerResponseWriter.java:216)
[INFO] [err] at org.jboss.resteasy.core.interception.jaxrs.ContainerResponseContextImpl.filter(ContainerResponseContextImpl.java:420)
[INFO] [err] at [internal classes]
[INFO] [err] at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:240)
[INFO] [err] at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:154)
[INFO] [err] at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:385)
[INFO] [err] at [internal classes]
[INFO] [err] at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:614)
[INFO] [err] at com.ibm.ws.webcontainer.servlet.ServletWrapper.service(ServletWrapper.java:1266)
[INFO] [err] at [internal classes]
[INFO] [err] at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144)
[INFO] [err] at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642)
[INFO] [err] at java.base/java.lang.Thread.run(Thread.java:1583)
Eclipse Yassonなので、JSON-Bですね。
なお、使用するフィーチャーを絞り込む場合は、restfulWS-x.y
だけではなくjsonb-x.y
も必要です。
<featureManager>
<feature>restfulWS-3.1</feature>
<feature>jsonb-3.0</feature>
</featureManager>
jsonb-x.y
がない場合は、JSONのリクエストを送信しても受け付けてもらえません
[INFO] RESTEASY003200: Could not find message body reader for type: class com.example.rest.CalcRequest of content type: application/json
pom.xmlとserver.xmlの変更箇所
このあたりを少し変更しました。
$ diff -u pom.xml*
--- pom.xml 2024-10-18 12:44:34.376505859 +0900
+++ pom.xml.org 2024-10-18 03:34:46.000000000 +0900
@@ -6,14 +6,13 @@
<groupId>com.example</groupId>
<artifactId>jaxrs-sample</artifactId>
- <version>0.0.1-SNAPSHOT</version>
+ <version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
- <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
@@ -52,15 +51,6 @@
<plugin>
<groupId>io.openliberty.tools</groupId>
<artifactId>liberty-maven-plugin</artifactId>
- <version>3.11.1</version>
- <configuration>
- <runtimeArtifact>
- <groupId>io.openliberty</groupId>
- <artifactId>openliberty-kernel</artifactId>
- <version>24.0.0.10</version>
- <type>zip</type>
- </runtimeArtifact>
- </configuration>
</plugin>
</plugins>
</build>
$ diff -u src/main/liberty/config/server.xml*
--- src/main/liberty/config/server.xml 2024-10-18 12:44:28.900481784 +0900
+++ src/main/liberty/config/server.xml.org 2024-10-18 03:34:46.000000000 +0900
@@ -35,7 +35,7 @@
<applicationManager autoExpand="true"/>
<!-- Configures the application on a specified context root -->
- <webApplication contextRoot="/" location="jaxrs-sample.war" />
+ <webApplication contextRoot="/jaxrs-sample" location="jaxrs-sample.war" />
<!-- Default SSL configuration enables trust for default certificates from the Java runtime -->
<ssl id="defaultSSLConfig" trustDefaultCerts="true" />