👚

Jersey 2.14でパラメータの受け取りにOptionalが使えるようになった

2014/12/15に公開

Jersey 2.14がリリースされたようです!

https://eclipse-ee4j.github.io/jersey.github.io/release-notes/2.14.html

で、注目はJERSEY-2612です。
この対応のおかげで@QueryParamなどのパラメータをOptionalで定義する事が可能になります。
ただしParamConverterを書く必要はありますが。

ParamConverterってなんやねん!って方は

https://zenn.dev/backpaper0/articles/2013_07_17_jaxrs_parameter

の後半を読んでくださいませー。

リクエストパラメータをOptionalで受け取るコード例

適当ですがサクッちょとサンプル書きました。

https://github.com/backpaper0/sandbox/tree/77d7f587077a741973057d236cc3f6582d10bd38/jersey-optional-example

リソースクラスをこちらにも掲載します。
ちょー簡単な例ですが、こんな感じでリソースメソッドの引数にOptionalを使えるようになります。

package example;

import java.util.Optional;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

@Path("hello")
public class HelloWorld {

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String say(@QueryParam("name") Optional<String> name) {
        return "Hello, " + name.orElse("world") + "!";
    }
}

注意点としては先ほども書きましたがParamConverter実装クラスとParamConverterProvider実装クラスも自前で準備しなくてはならない事です。
まあ、一度書いたら使い回せるはずなのでサクッと書いておきましょー。

今までこれが出来なかった理由

@QueryParamなどで注釈された引数へ渡される値はSingleValueExtractorextractメソッドを通るんですが、Jersey 2.13までのextractメソッドは「値がnullでなければParamConverterなどで変換、nullなら@DefaultValueで設定された値を返す」という感じの実装になっていました。

その部分を抜粋します。

@Override
public T extract(MultivaluedMap<String, String> parameters) {
    String v = parameters.getFirst(getName());
    if (v != null) {
        try {
            return fromString(v);
        } catch (WebApplicationException ex) {
            throw ex;
        } catch (ProcessingException ex) {
            throw ex;
        } catch (Exception ex) {
            throw new ExtractorException(ex);
        }
    } else {
        return defaultValue();
    }
}

このロジックが原因でOptionalParamConverterを書いてもnullが渡ってくるアレっぷりでした。

しかしこのextractメソッドはJersey 2.14で次のように修正されました。

@Override
public T extract(MultivaluedMap<String, String> parameters) {
    String v = parameters.getFirst(getName());
    try {
        return fromString((v == null && isDefaultValueRegistered()) ? getDefaultValueString() : v);
    } catch (WebApplicationException ex) {
        throw ex;
    } catch (ProcessingException ex) {
        throw ex;
    } catch (IllegalArgumentException ex) {
        return defaultValue();
    } catch (Exception ex) {
        throw new ExtractorException(ex);
    }
}

ご覧の通り「値がnullかつデフォルト値が設定されていればデフォルト値を、そうでなければ値をParamConverterなどに渡す」という風になっています。

これで値がnullの場合でもParamConverterを通るようになり、Optionalへの変換が可能になりました。

まとめ

  • Jerseyでリクエストパラメータなどの受け取りにOptional使えるようになって嬉しい
  • ハイテンションでブログ書いたら文章やばい

そんな感じでー

Discussion