Payara MicroでJacksonを有効にする。そしてAsync Responseを使う
Javaで非同期処理書きたい。それだけです。
あと前回分かったMessageBodyWriterでMoxyが使われているのをJacksonに切り替えます。
これが動かしたい。
@GET @Path("async") @Produces(MediaType.APPLICATION_JSON) public void asyncTest(@Suspended AsyncResponse response) { System.out.println("log1"); CompletableFuture.runAsync(() -> { response.resume(new Test("test")); System.out.println("log3"); }, executor); System.out.println("log2"); } @Resource private ManagedExecutorService executor;
【Jackson使う】
まずはJacksonを有効にします。Lombokを気持ちよく使いたいからです。
jerseyだとResourceConfigとかいうApplicationクラスを拡張した素敵なクラスがあるのですが
今回はApplicationクラスをいじる形で実装したかったため、そこについては記述いたしません。
今回はJacksonFeatureというJerseyで用意されているクラスを使ってJacksonの機能を有効にします。
めっちゃ簡単です。
@Provider public class ApplicationFeature implements Feature { @Override public boolean configure(FeatureContext ctx) { ctx.register(JacksonFeature.class); return true; } }
ね、簡単でしょう? これだけだと動きません。
これでいけると思ったんじゃー!!!
はい。ソースを見ます。
github.com
ここの行見るとわかるのですが
何やら、ConfigurationからPropertiesを取り出して設定を見ているようです。
手元だとこんな感じになってました。
/* */ public class JacksonFeature implements Feature { /* */ private static final String JSON_FEATURE = JacksonFeature.class.getSimpleName(); /* */ /* */ public boolean configure(FeatureContext context) { /* 71 */ Configuration config = context.getConfiguration(); /* */ /* 73 */ String jsonFeature = (String)CommonProperties.getValue(config.getProperties(), config.getRuntimeType(), "jersey.config.jsonFeature", JSON_FEATURE, String.class); /* */ /* */ /* 76 */ if(!JSON_FEATURE.equalsIgnoreCase(jsonFeature)) { /* 77 */ return false; /* */ /* */ /* */ } else { /* 81 */ context.property(PropertiesHelper.getPropertyNameForRuntime("jersey.config.jsonFeature", config.getRuntimeType()), JSON_FEATURE); /* */ /* */ /* */
デバッグして76行目で止めてjsonFeatureを見るとMoxyに関する名前が入っていたので
これをjersey.config.jsonFeatureに変えればええんかーと思いました。
で、どうやって変更すんの?
・案1 JacksonFeatureを登録している、ApplicationFeatureで設定上書きする。
残念!context.getConfiguration().getProperties()して
クラス名確認したところ、UnmodifiableMapちゃんでした!!!
まぁそんな作りになっているわけがないですよね。
・案2 Applicationクラスで設定する。
これで動きました。
@ApplicationPath("/api") public class MyApplication extends Application { private final Map<String, Object> properties; public MyApplication() { Map<String, Object> properties=new HashMap<>(); properties=new HashMap<>(); properties.put(CommonProperties.MOXY_JSON_FEATURE_DISABLE, true); this.properties=Collections.unmodifiableMap(properties); } @Override public Map<String, Object> getProperties() { return properties; } }
Moxyを無効にするだけでいいみたいですね。
ドキュメント的にはこの辺に載ってます。
Appendix A. Configuration Properties
一応MoxyJsonFeatureのソースを覗くと
JacksonFeatureみたいにJSONFeatureは何を使うのか確認する処理の手前に
Disableかどうか確認して使うかどうか確認しているみたいですね。
非同期Response扱いたい。
ManagedExecutorService使いたい。
@wreulicke 使えるよー https://t.co/SXtRPYTlg7
— うらがみ⛄️ (@backpaper0) 2016年11月26日
@wreulicke Full PlatformとPayara MicroはOKで、Web ProfileとPayara MicroprofileはNGです。
— HASUNUMA Kenji (@khasunuma) 2016年11月26日
なるほど。使えるそうです。
うらがみさんから頂いたソースで
@Injectしてるからそのまま書いたら動かないぞーと思ってたらResourceにしたら動いた。
JNDIリソース周りの知識が足りないっぽい。
あと、CDI管理下にあるからなのかScopeアノテーションを付けたら動いた。
@RequestScoped @Path("/example") public class ApplicationResource {
Profile回りの知識がないと呟いたら
蓮沼さんからスライドを頂きました。
@wreulicke っ https://t.co/mTabrqDYtA
— HASUNUMA Kenji (@khasunuma) 2016年11月26日
なるほど。ここを見る限りManagedExecutorServiceはConcurrencyに入る?ので
Web Profileだと本来使えない機能のようですね。
【番外編】ServletContainerInitializerの話
JavadocのonStartupの引数から
Parameters:
c - the Set of application classes that extend, implement, or have been annotated with the class types specified by the HandlesTypes annotation, or null if there are no matches, or this ServletContainerInitializer has not been annotated with HandlesTypes
ctx - the ServletContext of the web application that is being started and in which the classes contained in c were found
ふむふむ。ServletContainerInitializerにつけられたHandleTypesアノテーションがない場合はnull
HandleTypesで「指定されたアノテーションで注釈付けされたクラス」もしくは「指定されたクラスをextendsもしくはimplementした型」のSetが入ってくる模様
で、Jerseyで使われてるContainerInitializerを見る。
github.com
PathとProviderとApplicationとApplicationPathが指定されている。
ここのスライドにかかれている、以下の動きに繋がるんですかねー
Java EE環境下では @Path で注釈されたクラスをリソースクラス、 @Provider で注釈されたクラスをプロバイダーと認識して自動で登録してくれる
うらがみさんによる細かい解説
2)そのServletContainerInitializer実装クラスをインスタンス化してonStartupメソッドを実行してくれる。この時、ServletContainerInitializer実装クラスに付けた[at]HandlesTypesアノテーションの
— うらがみ⛄️ (@backpaper0) 2016年11月26日
4)で、JerseyはServletContainerInitializer実装クラスとしてJerseyServletContainerInitializerというのがあって、こいつがServletContainer(これはJerseyアプリケーションを
— うらがみ⛄️ (@backpaper0) 2016年11月26日
6)登録した状態でJerseyアプリケーションを起動してくれる。ちなみにResourceConfigはJerseyが持っているApplicationサブクラス。
— うらがみ⛄️ (@backpaper0) 2016年11月26日
【小話】ボケてた。
@wreulicke アノテーションを継承???
— かずひら (@kazuhira_r) 2016年11月26日
アノテーションは継承できません。そうでした。
@Inheritで引き継ぐのと勘違いしてました。
ちなみに一応Annotationはインターフェースなので
一応言っておくけど、アノテーションってextendsは出来ないけどimplementsは出来るからな!
— うらがみ⛄️ (@backpaper0) 2016年11月26日
おまけ:Java EEの中にはこんなのもいたりする。CDI Extension書くときとかにお世話になったイメージ
AnnotationLiteral (Java(TM) EE 7 Specification APIs)