ローカルPCとサーバーのどちらでも動作するWebサービス(API)を作る


# 概要

手元でさくっと動作確認
確認できたらサーバー（tomcatなど）にデプロイすればいいじゃない？
という事で、両対応できるようにしてみた。

しかもローカルでの起動が超早い特典付き！


# IntelliJ コミュニティ版（無料）を使う

`Create New Project`を押す

<img width="470" alt="スクリーンショット 2016-01-02 13.56.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/37624/f4b84f82-efa1-832c-d150-33f473fe1f8d.png">

# プロジェクト名を投入

 ![スクリーンショット 2016-01-02 14.20.05.png](https://qiita-image-store.s3.amazonaws.com/0/37624/1d6e8a61-fd74-fc7e-db09-73ffb315ed6e.png)

# Maven を選ぶ

左側の `Maven` を選択して `Next` を押す
Project SDKが何も無いときは `New...` を押してJDKを設定

 ![スクリーンショット 2016-01-02 14.20.24.png](https://qiita-image-store.s3.amazonaws.com/0/37624/136f1a94-b009-afd7-e9fb-0916bd982fc1.png)


# プロジェクトのフォルダ名を設定

![スクリーンショット 2016-01-02 14.20.38.png](https://qiita-image-store.s3.amazonaws.com/0/37624/f298cb8b-9aff-6f3e-ee86-ee31f41f1cf5.png)

# 最初の画面

右上の `Enable Auto-import` を押す

![スクリーンショット 2016-01-02 14.22.02.png](https://qiita-image-store.s3.amazonaws.com/0/37624/c83c232d-0213-5d6a-61e4-58d0f7bf47dd.png)

# プロジェクトのファイル一覧を表示

左側の `1:Project` を押すと出てくる
左側に何も表示されていない場合は、左下のアイコンをクリックすると出てくる

<img width="149" alt="スクリーンショット 2016-01-02 14.22.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/37624/bb813b2e-9c62-7496-0b41-57958424e4b0.png">

左下のアイコン
<img width="137" alt="スクリーンショット 2016-01-02 14.22.48.png" src="https://qiita-image-store.s3.amazonaws.com/0/37624/f6214cc0-c696-2870-0482-220bc2511fb6.png">

プロジェクトのファイル一覧
<img width="424" alt="スクリーンショット 2016-01-02 15.28.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/37624/0f1b9ba6-0064-8128-67a5-34220cb41111.png">


# 必要なライブラリの設定

最初の画面には `pom.xml` が表示されている。
そこに必要なライブラリを書き足すと、自動的にライブラリをダウンロードして設定してくれる。

/pom.xml ファイル
http://camel-cap.blogspot.jp/2016/01/apache-camel-hello-rest.html?view=sidebar

# サーバーを使わなくても動作確認できるようにしとく


クラス `AppMain` の作成

<img width="719" alt="スクリーンショット 2016-01-02 15.44.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/37624/cc8610e4-a5d3-19ad-d224-3cd73205808d.png">


<img width="382" alt="スクリーンショット 2016-01-02 14.30.00.png" src="https://qiita-image-store.s3.amazonaws.com/0/37624/81bb555c-eb25-32ee-1fe5-43e593bf41ae.png">


<img width="251" alt="スクリーンショット 2016-01-02 15.48.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/37624/1d039faa-7ab3-f4a9-4766-be4b4cb4d3ff.png">


```java:src/main/java/example/api/AppMain.java
package example.api;

import org.apache.camel.spring.Main;

public class AppMain {

    public static void main(String[] args) throws Exception {
        System.out.println("-- start! --");
        Main main = new Main();
        main.run();
    }
}
```

このまま起動するとエラーになるので、spring用ファイルを準備する必要がある

# サーバー（tomcat）にデプロイしても動くようにする

```xml:/src/main/resources/WEB-INF/web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app>

    <!-- spring.xml -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/classes/META-INF/spring/*.xml</param-value>
    </context-param>

    <!-- Start Spring -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener
        </listener-class>
    </listener>

</web-app>
```

# httpリクエストを受け付けるようにspring用ファイルを作成

<img width="493" alt="スクリーンショット 2016-01-02 16.53.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/37624/1c52f3d6-cf23-e1be-d8a7-32ad67209088.png">

<img width="427" alt="スクリーンショット 2016-01-02 16.58.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/37624/8013d8fc-38b9-b175-017b-74216a2719e7.png">

```xml:src/main/resources/META-INF/spring/spring-camel.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
       http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring-2.16.1.xsd
       http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
       ">

    <!-- ルートの自動登録（サブディレクトリも検索される） @Componentが対象 -->
    <context:component-scan base-package="example.api"/>

    <!--ヘッダフィルタを登録-->
    <bean name="AllFilter" class="example.api.AllFilter"/>

    <!-- camelルート -->
    <camelContext xmlns="http://camel.apache.org/schema/spring">
        <contextScan/>
        <restConfiguration component="netty4-http" contextPath="/api/1" host="0.0.0.0"
                           port="8888" bindingMode="json">
            <!-- httpクエリをexchange.getIn()のheaderに入れる -->
            <endpointProperty key="mapHeaders" value="true"/>
            <!-- httpクエリをurlデコード-->
            <endpointProperty key="urlDecodeHeaders" value="true"/>
            <!-- 独自のhttpヘッダを返信しない -->
            <endpointProperty key="headerFilterStrategy" value="AllFilter"/>
        </restConfiguration>
    </camelContext>

</beans>
```

これで `http://localhost:8888/api/1` でhttp受信できる

# REST受信

REST受信用のクラスを作成

<img width="753" alt="スクリーンショット 2016-01-02 20.17.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/37624/58fd4547-3902-e568-6d68-fa67e681d512.png">

<img width="382" alt="スクリーンショット 2016-01-02 20.17.44.png" src="https://qiita-image-store.s3.amazonaws.com/0/37624/a1f0f60b-d376-496e-6293-c7e5ba4f36da.png">


```java:/src/main/java/example/api/rest/HelloRest.java
package example.api.rest;

import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;

@Component
public class HelloRest extends RouteBuilder{
    @Override
    public void configure() throws Exception {
        rest("/hello")
                .get().to("direct:hello");
    }
}
```

これで `http://localhost:8888/api/1/hello` でREST (get)受信できる

# 処理の部分を作成

```java:/src/main/java/example/api/route/HelloRoute.java
package example.api.route;

import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;

import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

@Component
public class HelloRoute extends RouteBuilder{

    @Override
    public void configure() throws Exception {
        from("direct:hello")
                .process(exchange -> {
                    Map map = new HashMap();
                    map.put("hello", "world");
                    map.put("one", 1);
                    map.put("time", Calendar.getInstance());
                    map.put("aa", new int[]{1,2,3});
                    exchange.getIn().setBody(map);
                });
    }
}
```

# httpの独自ヘッダを誤って返信しないようにフィルタを設定

簡単にhttpヘッダを設定できるので、誤って設定した場合でも自動的に削除する設定を入れる。

```java:/src/main/java/example/api/AllFilter.java
package example.api;

import org.apache.camel.Exchange;
import org.apache.camel.spi.HeaderFilterStrategy;

public class AllFilter implements HeaderFilterStrategy {
    @Override
    public boolean applyFilterToCamelHeaders(String headerName, Object headerValue, Exchange exchange) {
        // サーバから出力するほうはフィルタする
        return true;
    }

    @Override
    public boolean applyFilterToExternalHeaders(String headerName, Object headerValue, Exchange exchange) {
        // 入ってくる方のデータはフィルタリングしない
        return fall;
    }
}
```

# 全てのファイル一覧

<img width="423" alt="スクリーンショット 2016-01-03 11.51.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/37624/b63f5220-473d-7525-00af-abfcce314049.png">


# ローカルで実行

先ほど作成した `AppMain` を開くと5行目と7行目にアイコンが付いている
<img width="544" alt="スクリーンショット 2016-01-02 21.35.16.png" src="https://qiita-image-store.s3.amazonaws.com/0/37624/d8876483-ef65-52e1-8d41-a9ab493560a0.png">

5行目のアイコンを押す
<img width="570" alt="スクリーンショット 2016-01-02 21.35.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/37624/38f436cf-3d38-0302-bb32-3249bc56266a.png">

動いているっぽい
<img width="837" alt="スクリーンショット 2016-01-02 21.36.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/37624/558394c1-bacf-87f9-07b4-58215c3dc504.png">

chromeでAPIにリクエストしてみた結果
<img width="1154" alt="スクリーンショット 2016-01-02 21.37.01.png" src="https://qiita-image-store.s3.amazonaws.com/0/37624/4520fe77-58f3-75d8-8e1a-31f5fee7431c.png">


# サーバーにデプロイする

warファイルをそのまま持って行くと大変でかいので、ソースコードだけ持っていく。

サーバーで実行

```
git pull
mvn clean package
mv /helloapi/target/*.war ~/tomcat/webapps/
```

毎回実行するのですら面倒なので、jenkinsなどで自動化すればいい。



# 参考

http://camel.apache.org/rest-dsl.html
http://camel.apache.org/netty4-http.html