- はじめに
- Java SE 環境における JAX-RS アプリケーションブートストラップ
- マルチパート・フォーム用APIの標準化
- ContextResolver
- その他の変更点
- module-info.java
はじめに
Jakarta EE 10 で Jakarta RESTful Web Services は 3.0 から 3.1 へバージョンアップします。
大きな変更点は、Java SE 環境向けのサポートとマルチパート・フォーム用APIの標準化になります。
Java SE 環境における JAX-RS アプリケーションブートストラップ
Java SE環境で JAX-RS アプリケーションをブートストラップする標準的なAPIが提供されました。
javax.ws.rs.JAXRS
インターフェースが追加され、このインターフェース経由でJAX-RSアプリケーションを起動できます。
以下のような JAX-RS アプリケーション が定義されていた場合、
@ApplicationPath("helloworld") @Path("hello") public class HelloWorld extends Application { @Override public Set<Class<?>> getClasses() { return Collections.singleton(HelloWorld.class); } @GET public String sayHello() { return "Hello, World!"; } }
以下のようにブートストラップできます。
public static final void main(final String[] args) throws InterruptedException { final Application application = new HelloWorld(); final JAXRS.Configuration requestedConfiguration = JAXRS.Configuration.builder().build(); JAXRS.start(application, requestedConfiguration).thenAccept(instance -> { Runtime.getRuntime().addShutdownHook(new Thread(() -> instance.stop().thenAccept(stopResult -> System.out.printf("Stop result: %s [Native stop result: %s].%n", stopResult, stopResult.unwrap(Object.class))))); final Configuration actualConfigurarion = instance.configuration(); final URI uri = UriBuilder.newInstance() .scheme(actualConfigurarion.protocol().toLowerCase()) .host(actualConfigurarion.host()).port(actualConfigurarion.port()) .path(actualConfigurarion.rootPath()).build(); System.out.printf("Instance %s running at %s [Native handle: %s].%n", instance, uri, instance.unwrap(Object.class)); System.out.println("Send SIGKILL to shutdown."); }); Thread.currentThread().join(); }
ごちゃごちゃ書かれていますが、JAXRS.Configuration.builder().build()
で JAXRS.Configuration
を作成し、JAXRS.start()
するだけです。
マルチパート・フォーム用APIの標準化
RESTful Web Services では multipart/form-data
を扱うための標準的なAPIが定義されていませんでした。
そのため、Jersey, Apache CFX, Resteasy などは、それぞれ独自にマルチパートをサポートしており、移植性の無い状況でした。
今回のバージョンアップにて、jakarta.ws.rs.core.EntityPart
インターフェースが追加されました。
EntityPart
は複数のエンティティを単一のエンティティとして送受信するマルチパートリクエストに対応するものです。
@POST @Consumes(MediaType.MULTIPART_FORM_DATA) public Response postWidget(List<EntityPart> parts) { for (EntityPart part : parts) { String name = part.getName(); Optional<String> fileName = part.getFileName(); InputStream is = part.getContent(); MultivaluedMap<String, String> partHeaders = part.getHeaders(); MediaType mediaType = part.getMediaType(); doSomethingWithPart(name, fileName, is, partHeaders, mediaType); } return Response.ok().build(); }
@FormParam
により以下のように受けることもできます。
@POST @Consumes(MediaType.MULTIPART_FORM_DATA) public Response postWidget( @FormParam("part1Name") String part1, @FormParam("part2Name") InputStream part2, @FormParam("part3Name") EntityPart part3) {...}
EntityPart
を使うことで、それぞれのパートのヘッダにアクセスできます。
クライアントからの POST は以下のように行います。
List<EntityPart> parts = ... WebTarget target = ClientBuilder.newClient().target("http://..."); Entity<List<EntityPart>> entity = Entity.entity(parts, MediaType.MULTIPART_FORM_DATA); Response response = target.request().post(entity);
EntityPart の構築は以下のように行います。
EntityPart.withName(filename)
.content(filename, Files.newInputStream(file))
.mediaType("application/pdf")
.build()
ContextResolver
JSON のシリアライズ・デシリアライズをカスタマイズするには、ContextResolver<>
で @Provider
を使い、例えば以下のようにプライベートフィールドをシリアル化することができます。
@Provider public class JsonConfiguration implements ContextResolver<Jsonb> { @Override public Jsonb getContext(Class<?> aClass) { return JsonbBuilder.newBuilder() .withConfig(new JsonbConfig() .withPropertyVisibilityStrategy(new PrivateVisibilityJsonbStrategy())) .build(); } }
しかし、ContextResolver<Jsonb>
の提供は、仕様上義務付けられていないため、RI である Glassfish/Jersey では機能するものの、他の実装では機能しない状況でした。
今回のバージョンアップにて、仕様上ContextResolver<Jsonb>
の提供が義務付けられ、他の実装でも Jsonb インスタンス が提供されるようになります。
その他の変更点
リクエストパラメータを指定する
@CookieParam
,@FormParam
,@HeaderParam
,@MatrixParam
,@QueryParam
などで、List<T>
,Set<T>
,SortedSet<T>
に加え、配列型(T[]
)を指定可能となったJAX-RSの実装では、特定のサービスプロバイダの拡張が自動ロードされるようになった(
ServiceLoader#load
による)Link.JaxbLink
とLink.JaxbAdapter
が非推奨かされたContainerRequestContext
Configuration
ClientRequestContext
InterceptorContext
にhasProperty(String name)
メソッドが(デフォルトメソッドとして)追加されたResponse.created(URI)
が、(ベースURIに対する)相対URIを絶対URIに解決するようになった(リクエストURIではなく)@jakarta.ws.rs.core.Context
は、将来CDIによる@Inject
に置き換わることが明記された(現時点では Deprecated になっている訳ではない)Cookie
クラスのコンストラクタが非推奨となり、Cookie.Builder
を使うようになったNewCookie
についても同様にNewCookie.Builder
を使用
リクエストに
Content-Type
やAccept
が指定されていない場合のリソースマッチングが明確化された- 仕様書上は 3.5. Declaring Media Type Capabilities の後半部
JAX-RS実装はデフォルトの Exception Mapper を提供する要件が追加された
module-info.java
Jakarta RESTful Web Services では、旧来から module-info.java が用意されていましたので、変更ではないのですが、以下のような定義になっています。
module jakarta.ws.rs {
requires static jakarta.xml.bind;
requires java.logging;
exports jakarta.ws.rs;
exports jakarta.ws.rs.client;
exports jakarta.ws.rs.container;
exports jakarta.ws.rs.core;
exports jakarta.ws.rs.ext;
exports jakarta.ws.rs.sse;
uses jakarta.ws.rs.client.ClientBuilder;
uses jakarta.ws.rs.ext.RuntimeDelegate;
uses jakarta.ws.rs.sse.SseEventSource.Builder;
opens jakarta.ws.rs.core to jakarta.xml.bind;
}