Javaのログ出力ライブラリメモ

知識がlog4jで止まっていたので調査。

Javaのログ出力: 道具と考え方
javaのロガーが多すぎて訳が解らないので整理してみました - 文系プログラマによるTIPSブログ

・・・迷路で遊ぶ暇は無いんだけどな、、、
とりあえず「SLF4J+Logback」でそんなに問題にはならんだろう。

JAX-RS 返却用クラスはデフォルトコンストラクタ必須

タイトルの通り。JAXBの仕様?らしい。

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public User getIt() {
        return new User(1);
    }

この場合、Userクラスが以下だとダメ。

public class User {
    public User(int id) {}
}

ちゃんとデフォルトコンストラクタ作るべし。

public class User {
    public User() {}
    public User(int id){}
}

JSON・XMLを返すけどエラー時はステータスコードやメッセージを選びたい

JAX-RSで200とか404とか返すだけなら、メソッド戻り値にResponseを指定してResponseを操作すればいい。

@RequestScoped
@Path("myresource")
public class MyResource {
    @GET
    public Response getIt() {
        return Response.ok().build();    // 200が返る
    }
}

JSONとかXMLとか返したいなら、戻り値にオブジェクトを指定して@ProducesでMIME(MediaType)を指定する。

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public User getIt() {
        User user = userService.getUser(1);
        return user;    // JSONが返る
    }


本題。
通常はJSONXMLを返したいけど、データベースなどにデータが無い場合は404にしたい。
参考:Chapter 7. Representations and Responses

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public User getIt() {
        try {
            User user = userService.getUser(1);
            return user;    // JSONが返る(ステータスコード:200)
        } catch (NoResultException e) {
            throw new WebApplicationException(404);    // 404が返る
        }
    }

WebApplicationExceptionをthrowする。ステータスコード返すだけならコード番号を渡す。
メッセージも変えたいならResponseを渡す。

throw new WebApplicationException(
    Response.status(Status.NOT_FOUND).entity("error. error. error.").build()
);


長すぎるし。。。
多分、WebApplicationExceptionを継承したクラス作るほうが幸せになれる。

public class CustomException extends WebApplicationException {
    public CustomException() {
        super(Response.status(Status.NOT_FOUND).build());
    }

    public CustomException(String message) {
        this(Status.NOT_FOUND, message);
    }

    public CustomException(Status status, String message) {
        this(status.getStatusCode(), message);
    }

    public CustomException(int code, String message) {
        super(
            Response.status(code).entity(message).build()
        );
    }
}

他にもExceptionMapperを使ってExceptionとレスポンスをマッピングさせる方法もある。
NoResultException -> 404 とかやってもいいけど、エラーの切り分けが面倒になりそう
(データが無いのか、単純にパスが間違っているのか、etc...)
なのでやめておく。


余談
このままだとtext/plainだけど、JSON(XML)返すサービスならエラーもJSON(XML)で返すべき?
まあMediaType指定するだけでいいから何とでもなるんだけど。

DeltaSpike(Data)を諦める

DeltaSpikeもひと通り調べ終わり、実際にコーディングを進めていたがDeltaSpike Dataを使うのを諦めることにした。

理由は原因不明のOutOfMemoryError (heap space)が発生して、どうしても発生源が突き止められなかったから。
JAX-RSの1リクエストでRepositoryでデータを取得してデータを返す分には問題無いが、せいぜい同時5リクエスト程度でOutOfMemoryになる。
テストデータは20件程度でメモリを食う要素も無いはず。
Tomcatのヒープを増やそうが、あらゆる場所にEntityManager.clear()を入れようが解決せず。

Repositoryを使わずEntityManagerからDtoデータを引っ張ってくるようにコードを書き直すと問題なく動いたので、仕方なくEntityManagerを使ってカリカリ書くことにする。
とりあえずJSONを返せればいいので、膨大に手間が増えることはないだろうし、一週間以上やっているので正直これ以上深入りする時間が無い。
(し、これ以上精神が持たない。ほんとにツラい。)

Tomcat8 + Java8 + DeltaSpike1.5.1

Jersey + CDIで@Contextが動かない

Jersey + CDIの構成で、ContainerRequestFilterの中でUriInfoを使おうとしてハマった。
構成は JAX-RS(Jersey) + JPA (on CDI) / on Tomcat - edgegram がベースでDeltaspikeを少々追加。

public class FormTypeFilter implements ContainerRequestFilter {
    @Context
    private UriInfo uriInfo;

    public void filter(ContainerRequestContext requestContext) throws IOException {
        System.out.println(uriInfo);
    }
}

UriInfoがnullにしかならない。ResourceInfoも試したが同様。
調べてもそんな事例なさそうだし(そもそもJersey + CDI自体情報少ないし)。


他のライブラリが影響しているのかと、色々抜き差しした結果、

  • 単純なJersey(jersey-container-servletだけ)にするとInjectされる
  • CDIを使おうとする(jersey-cdi1xを入れる)とnullになる

という所まで何とかこぎつける。
ただ、jersey-cdi1xがダメとなるとCDIが使えなくなる。

何か無いのか? とmarvenリポジトリを検索してみると、jersey-cdi1x-servletというのを発見。
jersey-cdi1xの代わりにjersey-cdi1x-servletを入れて試すと

・・・

UriInfo入った。ちゃんとInjectされている。
他の自前クラスも@Injectできているし、CDIコンテナも問題なく動いている様子。
正解かどうか分からないが、とりあえず動くのでいいことにする。

ハマり時間:8時間

PostgreSQLのbyteaカラムに手動でデータを入れる

初期データ用にbyteaデータを入れようと調べてみたが、psqlからのINSERTはできない?
仕方なくJDBCで放り込むことにした。(その時点で既に手動ではないが)
Chapter 7. Storing Binary Data

Class.forName("org.postgresql.Driver");
Connection con = DriverManager.getConnection("jdbc:postgresql://dbhost:5432/test", "test", "test");

File file = new File("C:/image.png");
FileInputStream fis = new FileInputStream(file);

PreparedStatement ps = con.prepareStatement("INSERT INTO binary_tbl (value) VALUES (?)");
ps.setBinaryStream(1, fis, (int)file.length());
ps.executeUpdate();
ps.close();
fis.close();
con.close();

JDBCなんて使うの数年前に研修で教えて以来だわ。
余談だけど、今時の開発者の中にはJDBC学ばずに、O/Rとかライブラリ使うしか分からない人もいたりするのかなぁ。

バッテリー上がり

車のバッテリーが上がったので交換した。
あまり動かさないのと、駐車場が家から自転車で5分強と少々距離があり、こまめにエンジンをかけに行けない。

停めているのが知り合いの所なので駐車場代無料なのは魅力。
近くに借りると軽く1.5万/月はするので、毎月バッテリー3個分払うようなもの。

そう考えれば仮に毎年バッテリー変えることになっても全然安い。
けど、もうすこしマメに充電するようにしたい。
(交換する度に毎度思う)