サポート > Wagby Developer Network(R7) > REST API 活用ガイド > [応用例] CAS 環境で REST API を利用する
ja | en

CAS 環境で REST API を利用する方法を説明します。Windows Active Directory による認証を利用した REST API 呼び出しなどが可能になります。 R7.11.1

CAS を利用した REST API 呼び出し時は、最初に「CAS へのログイン(Ticket Granting Ticket の取得)」を行います。 TGT は後述する ST(Service Ticket) の取得時に利用します。

TGT 取得後は、Wagby アプリケーションにアクセスする度に、アクセスする URL に対応するST(Service Ticket) を取得します。 Wagby アプリケーションにアクセスする際は ST も合わせて送信する必要があります。

ここでは2017年4月時点で最新の HttpClient 4.5 を使ったサンプルコードを紹介します。

CASへのログオン

次のリクエストを送信するようにします。

POST /cas/v1/tickets HTTP/1.1
Content-Type: application/x-www-form-urlencoded
username=admin&password=admin

Wagbyアプリケーションへのログオン

最初に、ログオン画面用の Service Ticket を CAS から取得します。

POST /cas/v1/tickets/{TGT} HTTP/1.1
Content-Type: application/x-www-form-urlencoded
service=http%3A%2F%2Flocalhost%3A8921%2Fwagby%2Frest%2Fsession

Service Ticket (ST) を使って Wagby アプリケーションへログオンします。

PUT /wagby/rest/session?ticket={ST} HTTP/1.1
Content-Type: application/x-www-form-urlencoded

データ取得

最初に、顧客詳細画面用の Service Ticket を CAS から取得します

POST /cas/v1/tickets/{TGT} HTTP/1.1
Content-Type: application/x-www-form-urlencoded
service=http%3A%2F%2Flocalhost%3A8921%2Fwagby%2Frest%2Fcustomer%2Fentry%2F1000

Service Ticket (ST) を使って顧客データを取得します。

GET /wagby/rest/customer/entry/1000?ticket={ST} HTTP/1.1

ログオフ

最初に、ログオフ用の Service Ticket を CAS から取得します。

POST /cas/v1/tickets/{TGT} HTTP/1.1
Content-Type: application/x-www-form-urlencoded
service=http%3A%2F%2Flocalhost%3A8921%2Fwagby%2Frest%2Fsession

Service Ticket (ST) を使ってログオフ処理を行います。

DELETE /wagby/rest/session?ticket={ST} HTTP/1.1

その後、CAS のログオフ処理も行います。

DELETE /cas/v1/tickets/{TGT} HTTP/1.1

サンプルコード

コードは次のとおりです。

/*
 * Copyright (C) Since 2006 JasmineSoft Inc. All Rights Reserved.
 */

package jp.jasminesoft.wagby.sample;

import java.io.IOException;
import java.net.URI;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

/**
 * HttpClient を使って REST API へアクセスを行うサンプルコードです。
 *
 * @author JasmineSoft
 * @version $Revision$ $Date$
 */
public class HttpClientSampleCAS {

    /**
     * メインメソッド
     * @param args コマンドライン引数
     */
    public static void main(String[] args) {

        // CAUTION: 以下のコードはエラーハンドリングを行っていません。
        //          各処理で発生した Exception を適切に処理するようにして下さい。
        BasicCookieStore cookieStore = new BasicCookieStore();
        String baseUrl = "http://localhost:8921/wagby/rest";

        // TGT(Ticket Granting Ticket) を取得します(CAS へログインする)。
        HttpUriRequest request = RequestBuilder.post()
                .setUri("http://localhost:18921/cas/v1/tickets")
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("username", "admin")
                .addParameter("password", "admin")
                .build();
        String content = execute(request);
        String tgtUrl = getTgtURL(content);

        // Wagby アプリケーションのログオン。
        // ログオン画面用の Service Ticket を CAS から取得します。
        String serviceUrl = baseUrl + "/session";
        request = RequestBuilder.post()
                .setUri(tgtUrl)
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("service", serviceUrl)
                .build();
        String serviceTicket = execute(request);
        // ログオン。
        request = RequestBuilder.put()
                .setUri(serviceUrl + "?ticket=" + serviceTicket)
                .setHeader("Content-Type", "application/x-www-form-urlencoded")
                .build();
        content = execute(request, cookieStore);

        // 顧客データを取得(主キー:1000)
        // Service Ticket を CAS から取得します。
        serviceUrl = baseUrl + "/customer/entry/1000";
        request = RequestBuilder.post()
                .setUri(tgtUrl)
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("service", serviceUrl)
                .build();
        serviceTicket = execute(request);
        // 顧客データ取得処理の実行。
        request = RequestBuilder.get()
                .setUri(serviceUrl + "?ticket=" + serviceTicket)
                .build();
        content = execute(request, cookieStore);

        // Wagby アプリケーションのログオフ。
        // Service Ticket を CAS から取得します。
        serviceUrl = baseUrl + "/session";
        request = RequestBuilder.post()
                .setUri(tgtUrl)
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("service", serviceUrl)
                .build();
        serviceTicket = execute(request);
        // Wagby アプリケーションログオフ処理の実行。
        request = RequestBuilder.delete()
                .setUri(serviceUrl + "?ticket=" + serviceTicket)
                .build();
        content = execute(request, cookieStore);

        // CAS のログアウト。
        request = RequestBuilder.delete()
                .setUri(tgtUrl)
                .build();
        content = execute(request);
    }

    /**
     * 取得した HTML から TGT URL (form/@action 属性値)を取得します。
     * @param html HTML
     * @return TGT URL
     */
    private static String getTgtURL(String html) {
        Matcher matcher = Pattern.compile(
                ".*form action=\"([^\"]+)\".*").matcher(html);
        if (!matcher.matches()) {
            throw new IllegalStateException(
                    "Successful request, but no ticket found!");
        }
        return matcher.group(1);
    }

    /**
     * Executes HTTP request.
     * @param request the request to execute
     * @return String containing the content.
     */
    private static String execute(HttpUriRequest request) {
        return execute(request, null);
    }

    /**
     * Executes HTTP request.
     * @param request the request to execute
     * @param cookieStore CookieStore
     * @return String containing the content.
     */
    private static String execute(HttpUriRequest request,
            CookieStore cookieStore) {
        System.out.println("Executing request " + request.getRequestLine());

        try (CloseableHttpClient client = createDefaultClient(cookieStore);
                CloseableHttpResponse response = client.execute(request)) {

            //// Set-Cookie ヘッダをコンソールへ出力する。
            //for (Header header : response.getHeaders("Set-Cookie")) {
            //    System.out.println(
            //            header.getName() + " : " + header.getValue());
            //}
            int status = response.getStatusLine().getStatusCode();
            if (status >= 200 && status < 300) {
                // HTTP ステータスコードが 200 番台の場合のみ処理を行う。
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    String content = EntityUtils.toString(entity);
                    System.out.println(content);
                    return content;
                }
                throw new IllegalStateException(
                        "Successful request, but response is empty!");
            } else {
                throw new ClientProtocolException(
                        "Unexpected response status: " + status);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * create Http client.
     * @param cookieStore CookieStore
     * @return Http client
     */
    private static CloseableHttpClient createDefaultClient(
            CookieStore cookieStore) {
        return HttpClients.custom()
                .setDefaultCookieStore(cookieStore)
                .setRedirectStrategy(new CustomRedirectStrategy()) // リダイレクト時の動作変更
                .build();
    }

    /**
     * リダイレクト時の動作を実装します。
     * - PUT リクエストもリダイレクト対象とします。
     * - 302 Found ( Moved Temporarily ) の PUT/POST/DELETE リクエスト時に
     *   リクエストメソッドを GET メソッドに変更せず、そのままのメソッド
     *   を維持したままリダイレクト処理を行います。
     *
     * @author JasmineSoft
     */
    public static class CustomRedirectStrategy extends LaxRedirectStrategy {

        /**
         * Redirectable methods.
         */
        private static final String[] REDIRECT_METHODS = new String[] {
            HttpPut.METHOD_NAME
        };

        /** {@inheritDoc} */
        @Override
        protected boolean isRedirectable(final String method) {
            boolean isRedirectable = super.isRedirectable(method);
            if (isRedirectable) {
                return true;
            }
            for (final String m: REDIRECT_METHODS) {
                if (m.equalsIgnoreCase(method)) {
                    return true;
                }
            }
            return false;
        }

        /** {@inheritDoc} */
        @Override
        public HttpUriRequest getRedirect(
                final HttpRequest request,
                final HttpResponse response,
                final HttpContext context) throws ProtocolException {
            final String method = request.getRequestLine().getMethod();
            if (method.equalsIgnoreCase(HttpHead.METHOD_NAME)
                    || method.equalsIgnoreCase(HttpGet.METHOD_NAME)) {
                return super.getRedirect(request, response, context);
            }
            final URI uri = getLocationURI(request, response, context);
            return RequestBuilder.copy(request).setUri(uri).build();
        }
    }
}

リダイレクトについて

標準の HttpClient の動作では Http Status: 302 Found ( Moved Temporarily ) 時に PUT/POST/DELETE メソッドのリクエストをリダイレクトしません。そのため、上のサンプルでは CustomRedirectStrategy クラスを作成し、これらの場合もリダイレクト処理を行うようにしています。

また「PUT/POST/DELETE メソッドのリクエストのリダイレクト時に、リクエストメソッドが GET に変更されてしまう」という標準の動作を変更し、そのメソッドを維持したままリダイレクト処理を行うようにもしています。

HttpClient 以外の環境で CAS 経由の REST API へアクセスする場合にも、これらのリダイレクト時の動作は注意して対応してください。
curl コマンドや Postman(Chromeアプリ) では、特別な設定をしなくとも意図通りにリダイレクトできました。(2017.4月時点で確認)

上のサンプルコードをベースに、一覧取得、登録、更新、削除を行う例を紹介します。各手順は「HttpClient を使って Wagby の REST API を呼び出す > 登録、更新、削除」と同じです。

/*
 * Copyright (C) Since 2006 JasmineSoft Inc. All Rights Reserved.
 */

package jp.jasminesoft.wagby.sample;

import java.io.IOException;
import java.net.URI;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

import jp.jasminesoft.jfc.app.JAXBJacksonModule;
import jp.jasminesoft.wagby.model.customer_p.CustomerP;
import jp.jasminesoft.wagby.model.customer_p.Deptname;
import jp.jasminesoft.wagby.model.customer_p.Kananame;
import jp.jasminesoft.wagby.model.customer_p.Name;

import org.apache.http.Consts;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.ProtocolException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CookieStore;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

/**
 * HttpClient を使って REST API へアクセスを行うサンプルコードです。
 *
 * @author JasmineSoft
 */
public class HttpClientSampleCAS {

    /**
     * メインメソッド
     * @param args コマンドライン引数
     */
    public static void main(String[] args) {

        // CAUTION: 以下のコードはエラーハンドリングを行っていません。
        //          各処理で発生した Exception を適切に処理するようにして下さい。
        BasicCookieStore cookieStore = new BasicCookieStore();
        String baseUrl = "http://localhost:8921/wagby/rest";

        // TGT(Ticket Granting Ticket) を取得します(CAS へログインする)。
        HttpUriRequest request = RequestBuilder.post()
                .setUri("http://localhost:18921/cas/v1/tickets")
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("username", "admin")
                .addParameter("password", "admin")
                .build();
        String content = execute(request);
        String tgtUrl = getTgtURL(content);

        // Wagby アプリケーションのログオン。
        // ログオン画面用の Service Ticket を CAS から取得します。
        String serviceUrl = baseUrl + "/session";
        request = RequestBuilder.post()
                .setUri(tgtUrl)
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("service", serviceUrl)
                .build();
        String serviceTicket = execute(request);
        // ログオン。
        request = RequestBuilder.put()
                .setUri(serviceUrl + "?ticket=" + serviceTicket)
                .setHeader("Content-Type", "application/x-www-form-urlencoded")
                .build();
        content = execute(request, cookieStore);

        // ログオン状態の確認。
        // Service Ticket を CAS から取得します。
        serviceUrl = baseUrl + "/session";
        request = RequestBuilder.post()
                .setUri(tgtUrl)
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("service", serviceUrl)
                .build();
        serviceTicket = execute(request);
        // ログオン状態の確認処理の実行。
        request = RequestBuilder.get()
                .setUri(serviceUrl + "?ticket=" + serviceTicket)
                .build();
        content = execute(request, cookieStore);

        // 顧客一覧を取得(検索条件なし)
        // Service Ticket を CAS から取得します。
        serviceUrl = baseUrl + "/customer/list";
        request = RequestBuilder.post()
                .setUri(tgtUrl)
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("service", serviceUrl)
                .build();
        serviceTicket = execute(request);
        // 顧客一覧取得処理の実行。
        request = RequestBuilder.get()
                .setUri(serviceUrl + "?ticket=" + serviceTicket)
                .build();
        content = execute(request, cookieStore);

        // 顧客一覧を取得(顧客名「ジャスミン太郎」で検索)。
        // Service Ticket を CAS から取得します。
        serviceUrl = baseUrl + "/customer/list";
        request = RequestBuilder.post()
                .setUri(tgtUrl)
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("service", serviceUrl)
                .build();
        serviceTicket = execute(request);
        // 顧客一覧取得処理の実行。
        request = RequestBuilder.post()
                .setUri(serviceUrl + "?ticket=" + serviceTicket)
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("customer_cp$002fname", "ジャスミン太郎")
                .build();
        content = execute(request, cookieStore);

        // json:顧客一覧を取得(顧客名「ジャスミン太郎」で検索)。
        // Service Ticket を CAS から取得します。
        serviceUrl = baseUrl + "/customer/list";
        request = RequestBuilder.post()
                .setUri(tgtUrl)
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("service", serviceUrl)
                .build();
        serviceTicket = execute(request);
        // json:顧客一覧取得処理の実行。
        String json = "{ \"name\": {\"content\":\"ジャスミン太郎\"}}";
        request = RequestBuilder.post()
                .setUri(serviceUrl + "?ticket=" + serviceTicket)
                .setHeader("Content-Type", "application/json")
                .setEntity(new StringEntity(json, Consts.UTF_8))
                .build();
        content = execute(request, cookieStore);

        // 顧客データを取得(主キー:1000)
        // Service Ticket を CAS から取得します。
        serviceUrl = baseUrl + "/customer/entry/1000";
        request = RequestBuilder.post()
                .setUri(tgtUrl)
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("service", serviceUrl)
                .build();
        serviceTicket = execute(request);
        // 顧客データ取得処理の実行。
        request = RequestBuilder.get()
                .setUri(serviceUrl + "?ticket=" + serviceTicket)
                .build();
        content = execute(request, cookieStore);

        // 顧客データの新規登録開始(初期値が埋まった状態のデータが取得できる)。
        // Service Ticket を CAS から取得します。
        serviceUrl = baseUrl + "/customer/new";
        request = RequestBuilder.post()
                .setUri(tgtUrl)
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("service", serviceUrl)
                .build();
        serviceTicket = execute(request);
        // 顧客データ新規登録開始処理の実行。
        request = RequestBuilder.get()
                .setUri(serviceUrl + "?ticket=" + serviceTicket)
                .build();
        content = execute(request, cookieStore);

        // 取得した json データの entityp フィールドを CustomerP に変換
        CustomerP customer_p = toObject(
                toJsonNode(content).get("entityp"), CustomerP.class);
        // name 項目に「ジャスミン三郎」を指定
        Name name = new Name();
        name.setContent("ジャスミン三郎");
        customer_p.setName(name);
        //customer_p.setName(new Name());
        //customer_p.getName().setContent("ジャスミン三郎");
        // kananame 項目に「ジャスミンサブロウ」を指定
        Kananame kananame = new Kananame();
        kananame.setContent("ジャスミンサブロウ");
        customer_p.setKananame(kananame);
        //customer_p.setKananame(new Kananame());
        //customer_p.getKananame().setContent("ジャスミンサブロウ");

        // 顧客データの新規登録(保存)。
        // Service Ticket を CAS から取得します。
        serviceUrl = baseUrl + "/customer/new";
        request = RequestBuilder.post()
                .setUri(tgtUrl)
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("service", serviceUrl)
                .build();
        serviceTicket = execute(request);
        // 顧客データ保存処理の実行。
        request = RequestBuilder.post()
                .setUri(serviceUrl + "?ticket=" + serviceTicket)
                .setHeader("Content-Type", "application/json")
                .setEntity(new StringEntity(toJson(customer_p), Consts.UTF_8))
                .build();
        content = execute(request, cookieStore);

        // 登録時に自動採番された主キーを取得。
        // 取得した json データの pkey フィールドが主キー値となっている。
        String pkey = toJsonNode(content).get("pkey").textValue();

        // 顧客データの更新開始。
        // Service Ticket を CAS から取得します。
        serviceUrl = baseUrl + "/customer/edit/" + pkey;
        request = RequestBuilder.post()
                .setUri(tgtUrl)
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("service", serviceUrl)
                .build();
        serviceTicket = execute(request);
        // 顧客データ更新開始処理の実行。
        request = RequestBuilder.get()
                .setUri(serviceUrl + "?ticket=" + serviceTicket)
                .build();
        content = execute(request, cookieStore);

        // 取得した json データの entityp フィールドを CustomerP に変換
        customer_p = toObject(
                toJsonNode(content).get("entityp"), CustomerP.class);
        // deptname 項目に「営業部」を指定
        Deptname deptname = new Deptname();
        deptname.setContent("営業部");
        customer_p.setDeptname(deptname);
        //customer_p.setDeptname(new Deptname());
        //customer_p.getDeptname().setContent("営業部");

        // 顧客データの更新(保存)。
        // Service Ticket を CAS から取得します。
        serviceUrl = baseUrl + "/customer/edit/" + pkey;
        request = RequestBuilder.post()
                .setUri(tgtUrl)
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("service", serviceUrl)
                .build();
        serviceTicket = execute(request);
        // 顧客データ保存処理の実行。
        request = RequestBuilder.put()
                .setUri(serviceUrl + "?ticket=" + serviceTicket)
                .setHeader("Content-Type", "application/json")
                .setEntity(new StringEntity(toJson(customer_p), Consts.UTF_8))
                .build();
        content = execute(request, cookieStore);

        // 顧客データを取得(正しく更新されているか確認)
        // Service Ticket を CAS から取得します。
        serviceUrl = baseUrl + "/customer/entry/" + pkey;
        request = RequestBuilder.post()
                .setUri(tgtUrl)
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("service", serviceUrl)
                .build();
        serviceTicket = execute(request);
        // 処理の実行。
        request = RequestBuilder.get()
                .setUri(serviceUrl + "?ticket=" + serviceTicket)
                .build();
        content = execute(request, cookieStore);

        // 顧客データを削除(主キー:1000)
        // Service Ticket を CAS から取得します。
        serviceUrl = baseUrl + "/customer/edit/" + pkey;
        request = RequestBuilder.post()
                .setUri(tgtUrl)
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("service", serviceUrl)
                .build();
        serviceTicket = execute(request);
        // 処理の実行。
        request = RequestBuilder.delete()
                .setUri(serviceUrl + "?ticket=" + serviceTicket)
                .build();
        content = execute(request, cookieStore);

        // Wagby アプリケーションのログオフ。
        // Service Ticket を CAS から取得します。
        serviceUrl = baseUrl + "/session";
        request = RequestBuilder.post()
                .setUri(tgtUrl)
                //.setHeader("Content-Type", "application/x-www-form-urlencoded") // HttpClient では省略可
                .addParameter("service", serviceUrl)
                .build();
        serviceTicket = execute(request);
        // Wagby アプリケーションログオフ処理の実行。
        request = RequestBuilder.delete()
                .setUri(serviceUrl + "?ticket=" + serviceTicket)
                .build();
        content = execute(request, cookieStore);

        // CAS のログアウト。
        request = RequestBuilder.delete()
                .setUri(tgtUrl)
                .build();
        content = execute(request);
    }

    /**
     * 取得した HTML から TGT URL (form/@action 属性値)を取得します。
     * @param html HTML
     * @return TGT URL
     */
    private static String getTgtURL(String html) {
        Matcher matcher = Pattern.compile(
                ".*form action=\"([^\"]+)\".*").matcher(html);
        if (!matcher.matches()) {
            throw new IllegalStateException(
                    "Successful request, but no ticket found!");
        }
        return matcher.group(1);
    }

    /**
     * Executes HTTP request.
     * @param request the request to execute
     * @return String containing the content.
     */
    private static String execute(HttpUriRequest request) {
        return execute(request, null);
    }

    /**
     * Executes HTTP request.
     * @param request the request to execute
     * @param cookieStore CookieStore
     * @return String containing the content.
     */
    private static String execute(HttpUriRequest request,
            CookieStore cookieStore) {
        System.out.println("Executing request " + request.getRequestLine());

        try (CloseableHttpClient client = createDefaultClient(cookieStore);
                CloseableHttpResponse response = client.execute(request)) {

            //// Set-Cookie ヘッダをコンソールへ出力する。
            //for (Header header : response.getHeaders("Set-Cookie")) {
            //    System.out.println(
            //            header.getName() + " : " + header.getValue());
            //}
            int status = response.getStatusLine().getStatusCode();
            if (status >= 200 && status < 300) {
                // HTTP ステータスコードが 200 番台の場合のみ処理を行う。
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    String content = EntityUtils.toString(entity);
                    System.out.println(content);
                    return content;
                }
                throw new IllegalStateException(
                        "Successful request, but response is empty!");
            } else {
                throw new ClientProtocolException(
                        "Unexpected response status: " + status);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * create Http client.
     * @param cookieStore CookieStore
     * @return Http client
     */
    private static CloseableHttpClient createDefaultClient(
            CookieStore cookieStore) {
        return HttpClients.custom()
                .setDefaultCookieStore(cookieStore)
                .setRedirectStrategy(new CustomRedirectStrategy()) // リダイレクト時の動作変更
                .build();
    }

    /**
     * Java オブジェクトを JSON 文字列に変換します。
     * @param value 変換対象の Java オブジェクト
     * @return Java オブジェクトを変換した JSON 文字列
     */
    private static String toJson(Object value) {
        ObjectMapper mapper = new ObjectMapper();
        try {
            return mapper.writeValueAsString(value);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * JSON 文字列を JsonNode に変換します。
     * @param json JSON 文字列
     * @return JsonNode
     */
    private static JsonNode toJsonNode(String json) {
        try {
            ObjectMapper mapper = new ObjectMapper();
            return mapper.readTree(json);
        } catch (JsonParseException e) {
            throw new RuntimeException(e);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * JsonNode を clazz のインスタンスに変換します。
     * @param <P> 変換後のクラスの型
     * @param node JsonNode
     * @param clazz 変換後のクラス
     * @return clazz のインスタンス
     */
    private static <P> P toObject(JsonNode node, Class<P> clazz) {
        return toObject(node.toString(), clazz);
    }

    /**
     * JSON 文字列を clazz のインスタンスに変換します。
     * @param <P> 変換後のクラスの型
     * @param json JSON 文字列
     * @param clazz 変換後のクラス
     * @return clazz のインスタンス
     */
    private static <P> P toObject(String json, Class<P> clazz) {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerModule(new JAXBJacksonModule()); // JAXB のアノテーションを利用した変換ルール
        try {
            return mapper.readValue(json, clazz);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * リダイレクト時の動作を実装します。
     * - PUT リクエストもリダイレクト対象とします。
     * - 302 Found ( Moved Temporarily ) の PUT/POST/DELETE リクエスト時に
     *   リクエストメソッドを GET メソッドに変更せず、そのままのメソッド
     *   を維持したままリダイレクト処理を行います。
     *
     * @author JasmineSoft
     * @version $Revision$ $Date$
     */
    public static class CustomRedirectStrategy extends LaxRedirectStrategy {

        /**
         * Redirectable methods.
         */
        private static final String[] REDIRECT_METHODS = new String[] {
            HttpPut.METHOD_NAME
        };

        /** {@inheritDoc} */
        @Override
        protected boolean isRedirectable(final String method) {
            boolean isRedirectable = super.isRedirectable(method);
            if (isRedirectable) {
                return true;
            }
            for (final String m: REDIRECT_METHODS) {
                if (m.equalsIgnoreCase(method)) {
                    return true;
                }
            }
            return false;
        }

        /** {@inheritDoc} */
        @Override
        public HttpUriRequest getRedirect(
                final HttpRequest request,
                final HttpResponse response,
                final HttpContext context) throws ProtocolException {
            final String method = request.getRequestLine().getMethod();
            if (method.equalsIgnoreCase(HttpHead.METHOD_NAME)
                    || method.equalsIgnoreCase(HttpGet.METHOD_NAME)) {
                return super.getRedirect(request, response, context);
            }
            final URI uri = getLocationURI(request, response, context);
            return RequestBuilder.copy(request).setUri(uri).build();
        }
    }
}