Payara micro触る(楽しようとしたらハマった話)
buildはgradle
普段使ってるのはmavenなので
よく分からんのでこの辺はコピペ
(opengl氏の記事多め。ありがたや~)
uberJarを作る方法はこちらから~。
qiita.com
下記記事のソースを参考にしつつ書いていきます。
qiita.com
とりあえず以下のようなソースを書いて動かしてみます。
// Resourceクラス @Path("/example") public class Resource { @GET @Path("hello") public String hello(){ return "test"; } @GET @Path("user") @Produces(MediaType.APPLICATION_JSON) public User get(){ return new User("xx"); } } //Applicationクラス @ApplicationPath("/api") public class MyApplication extends Application { } //Userクラス(ただのDTO) @Value @AllArgsConstructor public class User{ String name; }
とりあえずここではuberJarにして
java -jar ROOT.jar
で実行しました。
http://localhost:8080/api/example/hello
にアクセスすると想定通り"test"の文字が表示されます。
http://localhost:8080/api/example/user
にアクセスするとコンソールにMessageBodyWriterが見つからない、と言ってエラーが出ます。
lombokによって吐かれているファイルを見てみると以下のようになっています。
(Decompiler PluginによってDecompileした物なので少し違う部分があるかもしれません)
public final class User{ private final String name; public String getName() { return this.name; } public boolean equals(Object o) { if (o == this) { return true; } else if (!(o instanceof User)) { return false; } else { User other = (User) o; String this$name = this.getName(); String other$name = other.getName(); if (this$name == null) { if (other$name != null) { return false; } } else if (!this$name.equals(other$name)) { return false; } return true; } } public int hashCode() { boolean PRIME = true; byte result = 1; String $name = this.getName(); int result1 = result * 59 + ($name == null ? 43 : $name.hashCode()); return result1; } public String toString() { return "User(name=" + this.getName() + ")"; } @ConstructorProperties({ "name" }) public User(String name) { this.name = name; } }
するとConstructorPropertiesなるAnnotationが付与されており
以下のissueを見てみると、jackson 2.7でサポートされているように見えます。
以下の記事を見るとJackson 2.7で、この機能が追加された際に
JsonPropertyのアノテーションとConstructorPropertiesのフィールド名が競合した際の場合のようです。
少し今回やりたいこととは違います。
一応、最新のPayaraのリリースを見ると内部ではJackson 2.8が使われているとのことなので
このConstructorPropertiesの機能は使えるはずです。
github.com
とりあえずWorkaroundとして解決案2つと分からなかった解決案1つを書いておきます。
解決方法1
@Dataアノテーションを使う
@Dataアノテーションを使うと古典的なPOJOが生成されるので
使い方としては
Prisoner prisoner=new Prisoner(); prisoner.setName("hogehoge");
といったような形になります。
ソースを追記して、書いてみます。
// 新しくクラスを追加 // Accessorsはmethod chainするために追加してます。 @Data @Accessors(chain=true) public class Prisoner { private String name; } // Resourceクラスに追加 @GET @Path("prisoner") @Produces(MediaType.APPLICATION_JSON) public Prisoner getPrisoner(){ return new Prisoner().setName("orekyuu"); }
http://localhost:8080/api/example/prisoner
にアクセスすると
{"name":"orekyuu"}
がちゃんと返却されます。
解決方法2
MessageBodyWriterを自分で書いてお茶を濁す。
@javax.ws.rs.ext.Provider @Produces(MediaType.APPLICATION_JSON) public class Provider implements MessageBodyWriter<User> { ObjectMapper mapper=new ObjectMapper(); @Override public long getSize(User user, Class<?> clazz, Type type, Annotation[] annotations, MediaType mediaType) { // TODO Auto-generated method stub return -1; } @Override public boolean isWriteable(Class<?> clazz, Type type, Annotation[] annotations, MediaType mediaType) { // TODO Auto-generated method stub return clazz.isAssignableFrom(User.class); } @Override public void writeTo(User user, Class<?> clazz, Type type, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> multivaluedMap, OutputStream outputStream) throws IOException, WebApplicationException { mapper.writeValue(outputStream, user); } }
こちらは
http://localhost:8080/api/example/userにアクセスすると
エラーを吐かずに想定通り、JSONで返却されました。
解決方法3
内部で使われてるJacksonの動きを変える。
こちらはいくつか内部のソースを追ってみましたが、分からず諦めました。