---
title: MessagePack + Spring MVC or JAX-RS(Jersery) on Spring Boot
tags: ["JAX-RS", "Jackson", "Java", "Jersey", "MessagePack", "Spring", "Spring Boot", "Spring MVC"]
categories: ["Programming", "Java", "org", "msgpack", "jackson", "dataformat", "MessagePackFactory"]
date: 2015-01-17T06:08:33Z
updated: 2015-01-17T10:38:25Z
---

以前の記事「[Spring MVC + Spring BootでJackson2のdataformatを変更する方法](http://blog.ik.am/#/entries/301)」の続きです。

> 本当はMessagePackのjackson-dataformat-msgpackを使いたかったのですが、なんか結果がおかしい(二回書き込まれている)&何回かやるとレスポンスが空になるなど、怪しい挙動をしていました・・・問題の切り分けのためにここに至ったわけです・・・

と言っていた訳ですが、@komamitsu_twさんから、[0.7.0-p3で治ったとコメントを頂いた](http://qiita.com/making@github/items/15807d46de034409da56#comment-d16e52469e5d26d60631)ので、試してみました。

### Spring MVC + Spring Bootの場合

基本方針は前回と同じです。依存関係に

``` xml
<dependency>
    <groupId>org.msgpack</groupId>
    <artifactId>jackson-dataformat-msgpack</artifactId>
    <version>0.7.0-p3</version>
</dependency>
```

を追加して、

``` java
@Bean
HttpMessageConverter messagePackMessageConverter() {
    return new AbstractJackson2HttpMessageConverter(
            new ObjectMapper(new MessagePackFactory()),
            new MediaType("application", "x-msgpack")) {
    };
}
```

をBean定義するだけです。

サンプルは[こちら](https://github.com/making/spring-mvc-msgpack-sample)。

`App`クラスを実行して、

```
$ curl -v "localhost:8080/calc?left=100&right=300"
> GET /calc?left=100&right=300 HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/x-msgpack;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Fri, 16 Jan 2015 15:00:26 GMT
<
��leftd�right�,�answer��
```

MessagePackフォーマットになっていますね！

Content negotiationもサポートされているので、拡張子に`.json`をつけると

``` bash
$ curl -v "localhost:8080/calc.json?left=100&right=300"
> GET /calc.json?left=100&right=300 HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Fri, 16 Jan 2015 15:01:44 GMT
<
{"left":100,"right":300,"answer":400}
```

JSONが返ります！開発中は見やすいこっちがいいですね。

`Accept`でも切り替えられます。

``` bash
$ curl -v -H "Accept: application/x-msgpack" "localhost:8080/calc?left=100&right=300"
> GET /calc?left=100&right=300 HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: application/x-msgpack
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/x-msgpack;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Fri, 16 Jan 2015 15:03:39 GMT
<
��leftd�right�,�answer��
```
と

``` bash
$ curl -v -H "Accept: application/json" "localhost:8080/calc?left=100&right=300"
> GET /calc?left=100&right=300 HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: application/json
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json;charset=UTF-8
< Transfer-Encoding: chunked
< Date: Fri, 16 Jan 2015 15:04:13 GMT
<
{"left":100,"right":300,"answer":400}
```

サンプルコードには[E2Eテスト](https://github.com/making/spring-mvc-msgpack-sample/blob/master/src/test/java/com/example/CalcTest.java)もついているので興味があれば見てください。


### JAX-RS(Jersey) + Spring Bootの場合
Jerseyでも同じようにできないかなといろいろ試してみたところ、以下のように設定すればいけました。

``` java
package com.example;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.jaxrs.json.JacksonJsonProvider;
import org.glassfish.jersey.server.ResourceConfig;
import org.msgpack.jackson.dataformat.MessagePackFactory;
import org.springframework.context.annotation.Configuration;

import javax.inject.Named;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.ext.Provider;

@Configuration
public class AppConfig {

    @Provider
    @Produces("application/x-msgpack")
    @Consumes("application/x-msgpack")
    public static class JacksonMessagePackProvider extends JacksonJsonProvider {
        public JacksonMessagePackProvider() {
            super(new ObjectMapper(new MessagePackFactory()));
        }

        @Override
        protected boolean hasMatchingMediaType(MediaType mediaType) {
            if (mediaType != null) {
                String subtype = mediaType.getSubtype();
                return "x-msgpack".equals(subtype);
            }
            return false;
        }
    }

    @Named
    static class JerseyConfig extends ResourceConfig {
        public JerseyConfig() {
            this.packages("com.example")
                    .register(JacksonMessagePackProvider.class);
        }
    }
}
```

`JacksonJsonProvider`を継承して、`@Produces`と`@Consumes`をつけ、`hasMatchingMediaType`をオーバーライドするのがポイントでした。

エンドポイントはこんな感じ。

``` java
package com.example;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.inject.Named;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;

@Named
@Path("/")
public class CalcEndpoint {


    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Result {
        private int left;
        private int right;
        private long answer;
    }

    @GET
    @Produces({"application/json", "application/x-msgpack"})
    @Path("calc")
    public Result calc(@QueryParam("left") int left, @QueryParam("right") int right) {
        return new Result(left, right, left + right);
    }
}
```

サンプルは[こちら](https://github.com/making/jersey-msgpack-sample)。

`App`クラスを実行して、

``` bash
$ curl -v -H "Accept: application/x-msgpack" "localhost:8080/calc?left=100&right=300"
> GET /calc?left=100&right=300 HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: application/x-msgpack
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/x-msgpack;charset=UTF-8
< Content-Length: 26
< Date: Fri, 16 Jan 2015 16:32:05 GMT
<
��leftd�right�,�answer��
```

`Accept`でも切り替えて、

``` bash
$ curl -v -H "Accept: application/json" "localhost:8080/calc?left=100&right=300"
> GET /calc?left=100&right=300 HTTP/1.1
> User-Agent: curl/7.30.0
> Host: localhost:8080
> Accept: application/json
>
< HTTP/1.1 200 OK
< Server: Apache-Coyote/1.1
< Content-Type: application/json;charset=UTF-8
< Content-Length: 37
< Date: Fri, 16 Jan 2015 16:32:52 GMT
<
{"left":100,"right":300,"answer":400}
```

JSONも出ました。

以前、@frsyukiさんがつぶやいていたことを思い出しました。

<blockquote class="twitter-tweet" lang="ja"><p>JavaでイマドキのRPCといえば、Jetty+Jersey+Jackson+MessagePackでキマリ！</p>&mdash; FURUHASHI Sadayuki (@frsyuki) <a href="https://twitter.com/frsyuki/status/535689358641074176">2014, 11月 21</a></blockquote>
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>

↑のサンプルだとTomcat8ですが、Jetty9にしたい場合は、

``` xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jersey</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
```

こうすればOK。Spring MVCの場合も同様。Tomcatでも問題ないと思うけど。

Spring Bootだと即実行可能でかつ、実行可能jarもポータブルで尚良いのではないでしょうか。

---

Spring Bootを始めるなら、Spring MVC版、Jersey版それぞれmaven-archetypeを作ったのでサクッと試したい場合はどうぞ

* https://github.com/making/spring-boot-blank
* https://github.com/making/spring-boot-jersey-blank

Spring Bootはアプリケーション実行基盤として優秀だなーと改めて思いました。

* [ProctolBuffersの例](http://blog.ik.am/#/entries/300)
* [Apache Thriftの例](http://blog.ik.am/#/entries/310)

も挙げましたが、RPC実行環境としてもいけてるのでは。

**追記**

Jetty+Jersey+Jackson+MessagePackを使ったJavaでモダンなRPCを作る雛形プロジェクト(maven archetype)を作ってみました

https://github.com/making/msgpack-rpc-jersey-blank

騙されたと思って使ってみてください。
