Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

Java開発者のためのCurl入門-クライアントサイドCurlとサーバサイドJavaの通信

第2回

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加
2008/10/28 14:00

 前回は、Curlの簡単な説明と、HelloWorldを実行するまでを説明しました。今回は、Javaでサーバーサイドを作成し、クライアントサイドのCurlアプリケーションと通信を行います。

はじめに

 前回はCurlの簡単な説明とHelloWorldを実行するまでを説明しました。今回はJavaでサーバーサイドを作成し、クライアントサイドのCurlアプリケーションと通信を行います。

前回の記事

Curlアプリケーションのアーキテクチャ

クライアントサイドとサーバーサイド

 Curlアプリケーションは、クライアントマシンにインストールされたCurl RTE上で実行されます。

 業務アプリケーションでは、サーバーサイドのデータベースを利用するのが一般的ですが、Curlアプリケーションはデータベースサーバに対して直接アクセスすることはしません。通常、サーバーサイドにもCurl以外の言語でアプリケーションを構築する必要があります。

 Curlアプリケーションからデータベースに対してCRUD操作を行うには、サーバーサイドのアプリケーションを経由します。Curlアプリケーションから必要なパラメータをサーバーサイドのアプリケーションに送信し、サーバーサイドアプリケーションが必要な処理を行い、それに対するレスポンスをCurlアプリケーションに返します。

 JSPを用いたHTMLアプリケーションでは、サーバーサイドから返されるデータはレイアウト情報が含まれており、クライアントのブラウザはそれを表示するだけです。一方、Curlアプリケーションでは、サーバーサイドから返されたデータを元に、クライアントサイドでレイアウトを生成し、画面を表示します。

通信プロトコル

 Curlアプリケーションとサーバとの通信には、次のプロトコルとデータ形式が利用できます。HTTPとXMLを用いたSOAP通信なども可能です。

プロトコル
  • HTTP/HTTPS
  • ソケット通信
データ形式
  • テキスト
  • バイナリ
  • XML
  • JSON
  • CSV
通信例
通信例

アーキテクチャ

 Javaを用いたWebアプリケーションの場合、表示に関するロジックとビジネスロジックは、プレゼンテーション層とそれ以外で分離するのが一般的です。

 一方、Curlアプリケーションの場合は、サーバーサイドはデータを返すのみで複雑なロジックは持たず、クライアントサイド、つまりCurl言語を用いてビジネスロジックを作成するのが一般的です。

 クライアントサイドにロジックを多く持てば、CPUやメモリといったリソースをクライアントサイドに分散できるというメリットがあります。クライアント数が多いようなシステムでは、Curlアプリケーションのようなアーキテクチャのメリットをより多く享受できると考えます。

 また、サーバーサイドでビジネスロジックを作成し、クライアントサイドは表示のみに徹するということも可能です。

JSON

 Curlは、バージョン6.0からJSONをサポートするようになりました。そこで今回は、サーバからデータをJSON形式でレスポンスを返し、クライアントサイドでデータの表示を行うプログラムの説明をします。

JSONとは

 JSON(ジェイソン、JavaScript Object Notation)は軽量なデータ記述言語で、主にAjaxの分野で利用されています。

 JSON形式で記述した例は次のようになり、

{
"name":"Foo",
"price":1980
}

 という文字列は,nameが文字列"Foo",priceが数値1980を持つデータを表します。

JavaのJSONライブラリ

 Javaはライブラリを使用することで、JSON形式のデータを簡単に扱うことができるようになります。JavaオブジェクトとJSONを相互変換するためのライブラリには次のようながあります。

 JSONICにはWebServiceServletというサーブレットが付属しており、別途フレームワークなどを用意しなくてもJSONを利用したWebサービスを実現できます。今回はJSONICを利用してアプリケーションを作成します。

アプリケーションの作成 1

サーバーサイドの作成

 JSONICには、SeasarやSpringと連携する機能があります。そこで今回は、Seasar、JSONICとTomcatを使用してサーバーサイドを作成します。

 まず、JSONICからコールされるServiceやDaoを作成します。インターフェースやDaoについては省略しますが、Serviceの実装クラスのコードは次のようになります。

EmpServiceImpl
package sample.service.impl;

...中略...

public class EmpServiceImpl implements EmpService {

	public List<Emp> find(EmpQuery query) {
		return empDao.getDatas(query);
	}

	public void create(Emp emp) {
		empDao.insert(emp);
	}

	public void update(Emp emp) {
		empDao.update(emp);
	}

	public void delete(Emp emp) {
		empDao.delete(emp);
	}

	...中略...

}

 上記のコードは複雑なロジックを記述していないので、Webアプリケーションを作成した事のあるJava技術者であれば理解できると思います。

 作成したServiceや、Daoをdiconファイルに記述します(注:今回は、理解しやすさを優先して、app.diconに直接記述しています)。

app.dicon
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE components PUBLIC "-//SEASAR//DTD S2Container 2.4//EN" 
	"http://www.seasar.org/dtd/components24.dtd">
<components>
	<include path="aop.dicon"/>
	<include path="dao.dicon"/>

	<component class="sample.service.impl.EmpServiceImpl" >
		<aspect>aop.traceThrowsInterceptor</aspect>
		<aspect>aop.traceInterceptor</aspect>
		<aspect>j2ee.requiredTx</aspect>
	</component>
	<component class="sample.dao.EmpDao" >
		<aspect>dao.interceptor</aspect>
	</component>
</components>

 そして、JSONICとSeasarを連携させるためには、web.xmlを次のように記述します。

web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app
	xmlns="http://java.sun.com/xml/ns/j2ee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
	version="2.4">

	<filter>
		<filter-name>s2filter</filter-name>
		<filter-class>org.seasar.framework.container.filter.S2ContainerFilter</filter-class>
	</filter>

	<filter-mapping>
		<filter-name>s2filter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<servlet>
		<servlet-name>s2servlet</servlet-name>
		<servlet-class>org.seasar.framework.container.servlet.S2ContainerServlet</servlet-class>
		<init-param>
			<param-name>debug</param-name>
			<param-value>true</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet>
		<description>JSON-WebService</description>
		<servlet-name>json-ws</servlet-name>
		<servlet-class>net.arnx.jsonic.web.WebServiceServlet</servlet-class>
		<init-param>
			<param-name>config</param-name>
			<param-value>
				container: 'net.arnx.jsonic.web.S2Container'
				debug: true
				mappings: {
					'/[package]/[class]/[id].[ext]': 'sample.web.${package}.service.${class}Service'
					'/[package]/[class].[ext]': 'sample.web.${package}.service.${class}Service'
					'/[class].[ext]': 'sample.service.${class}Service'
				}
			</param-value>
		</init-param>
		<load-on-startup>2</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>s2servlet</servlet-name>
		<url-pattern>/s2servlet</url-pattern>
	</servlet-mapping>

	<servlet-mapping>
		<servlet-name>json-ws</servlet-name>
		<url-pattern>*.json</url-pattern>
	</servlet-mapping>
</web-app>

 JSON-WebServiceのinit-paramに注目してください。containerに、'net.arnx.jsonic.web.S2Container'と記述することで、Seasarとの連携が可能になります('net.arnx.jsonic.web.SpringContainer'と記述すればSpringと連携できます)。また、mappingsには、URLとDIコンテナに登録されたコンポーネントのマッピングを記述します。この記述の場合、例えば、/emp.jsonというURLでアクセスした場合、sample.service.EmpServiceがコールされます。

 RESTスタイルの場合、コールされるメソッドは、次のようにHTTPのメソッドによって決定されます。

  • GET

     findメソッドが呼び出される。引数はURLのクエリ文字列で指定

  • POST

     createメソッドが呼び出される。メソッドの引数はリクエストボディにJSONとして指定

  • PUT

     updateメソッドが呼び出される。メソッドの引数はリクエストボディにJSONとして指定

  • DELETE

     deleteメソッドが呼び出される。メソッドの引数はリクエストボディにJSONとして指定

 動作確認をするには、Tomcatを起動し、ブラウザに次のURLを入力しアクセスします(注:URLはTomcatの設定により変わります)。

  • http://localhost:8080/curl_server/emp.json

 すると、次のようなリクエストが返ってきます。

リクエスト
[
	{
		"deptno": 20,
		"empno": 7369,
		"ename": "SMITH",
		"hiredate": 345826800000,
		"job": "CLERK",
		"mgr": 7902,
		"sal": 800.0
	},
	{
		"deptno": 30,
		"empno": 7499,
		"ename": "ALLEN",
		"hiredate": 351442800000,
		"job": "SALESMAN",
		"mgr": 7698,
		"sal": 1600.0
	},
	
	...中略...

]

 Service、Daoを作成して、それをSeasarに登録し、web.xmlを記述するだけで、サーバーサイドの準備は完了です。

アプリケーションの作成 2

クライアントサイドの作成

 まずは、実行結果を見てください。

 JSONのAPIを利用するためには、CURL.IO.JSONパッケージのインポートが必要です。そして、JsonValue-parseプロシージャを使用してアクセス先のURLデータを受け取り、JsonValueオブジェクトへ変換します。

JSONデータの受信
{import * from CURL.IO.JSON}

|| サーバからのJSONデータの受信
{let empJsonValue: JsonValue =
    {JsonValue-parse
       {url "http://localhost:8080/curl_server/emp.json"}
    }
}

 実行結果のようなグリッドテーブル表示を行うには、RecordGridを使用します。

 RecordGridを使用するには、まず、各列に対する名前と型、それにcaptionで見出し(実行結果のヘッダー)を宣言します。

RecordGridの宣言
{let employees: RecordSet =
    {RecordSet
        {RecordFields
              {RecordField "empno", caption = "empno", domain = int}
            , {RecordField "ename", caption = "ename",  domain = String}
            , {RecordField "job", caption = "job",  domain = String}
            , {RecordField "mgr", caption = "mgr",  domain = int}
            , {RecordField "hiredate", caption = "hiredate",  domain = double}
            , {RecordField "sal", caption = "sal",  domain = double}
            , {RecordField "deptno", caption = "deptno",  domain = int}
        }
    }
}

 サーバから受信したJsonValueオブジェクトの値を、RecordSetにセットして最後に表示を行えば、先ほどの実行結果を表示できます。

JsonValueオブジェクトの値をRecordSetにセット
|| Jsonデータをcustomersに追加する
{define-proc public {add-rec
                        empno:int
                        , ename: String
                        , job: String
                        , mgr: int
                        , hiredate: double
                        , sal: double
                        , deptno: int
                    }: void
    let new-rec: Record = {employees.new-record}
    {new-rec.set "empno", empno}
    {new-rec.set "ename", ename}
    {new-rec.set "job", job}
    {new-rec.set "mgr", mgr}
    {new-rec.set "hiredate", hiredate}
    {new-rec.set "sal", sal}
    {new-rec.set "deptno", deptno}
    {employees.append new-rec}
}

{for emp: JsonObject in empJsonValue do
    let found?: bool = false
    let empno: int = 0
    let ename: String = {String}
    let job: String = {String}
    let mgr: int = 0
    let hiredate: double = 0
    let sal: double = 0
    let deptno: int = 0
    set (empno, found?) = {emp.get-if-exists "empno"}
    set (ename, found?) = {emp.get-if-exists "ename"}
    set (job, found?) = {emp.get-if-exists "job"}
    set (mgr, found?) = {emp.get-if-exists "mgr"}
    set (hiredate, found?) = {emp.get-if-exists "hiredate"}
    set (sal, found?) = {emp.get-if-exists "sal"}
    set (deptno, found?) = {emp.get-if-exists "deptno"}
    {if found? then
        {add-rec empno, ename, job, mgr, hiredate, sal, deptno}
    }
}
データを表示するコード
{value
    {RecordGrid
        width=700
        , record-source = employees
    }
}

 以上で、DBのデータをそのまま表示するというシンプルなアプリケーションを作成しました。非常に簡単なコーディングでグリッドテーブルの表示ができています。また、クライアントサイドでヘッダーをクリックすれば、ソートをすることも可能です。

 最後に、start.curlのコード全体を掲載します。

start.curlのコード全体
{curl 6.0 applet}
{curl-file-attributes character-encoding = "shift-jis"}

{import * from CURL.IO.JSON}

|| サーバからのJSONデータの受信
{let empJsonValue: JsonValue =
    {JsonValue-parse
       {url "http://localhost:8080/curl_server/emp.json"}
    }
}

{let employees: RecordSet =
    {RecordSet
        {RecordFields
              {RecordField "empno", caption = "empno", domain = int}
            , {RecordField "ename", caption = "ename",  domain = String}
            , {RecordField "job", caption = "job",  domain = String}
            , {RecordField "mgr", caption = "mgr",  domain = int}
            , {RecordField "hiredate", caption = "hiredate",  domain = double}
            , {RecordField "sal", caption = "sal",  domain = double}
            , {RecordField "deptno", caption = "deptno",  domain = int}
        }
    }
}

|| Jsonデータをcustomersに追加する
{define-proc public {add-rec
                        empno:int
                        , ename: String
                        , job: String
                        , mgr: int
                        , hiredate: double
                        , sal: double
                        , deptno: int
                    }: void
    let new-rec: Record = {employees.new-record}
    {new-rec.set "empno", empno}
    {new-rec.set "ename", ename}
    {new-rec.set "job", job}
    {new-rec.set "mgr", mgr}
    {new-rec.set "hiredate", hiredate}
    {new-rec.set "sal", sal}
    {new-rec.set "deptno", deptno}
    {employees.append new-rec}
}

{for emp: JsonObject in empJsonValue do
    let found?: bool = false
    let empno: int = 0
    let ename: String = {String}
    let job: String = {String}
    let mgr: int = 0
    let hiredate: double = 0
    let sal: double = 0
    let deptno: int = 0
    set (empno, found?) = {emp.get-if-exists "empno"}
    set (ename, found?) = {emp.get-if-exists "ename"}
    set (job, found?) = {emp.get-if-exists "job"}
    set (mgr, found?) = {emp.get-if-exists "mgr"}
    set (hiredate, found?) = {emp.get-if-exists "hiredate"}
    set (sal, found?) = {emp.get-if-exists "sal"}
    set (deptno, found?) = {emp.get-if-exists "deptno"}
    {if found? then
        {add-rec empno, ename, job, mgr, hiredate, sal, deptno}
    }
}

{value
    {RecordGrid
        width=700
        , record-source = employees
    }
}

まとめ

 Curlはバージョン6.0から、JSONをサポートするようになりました。その紹介も兼ねて、今回はJavaで作成したサーバーサイドとJSON形式で通信を行うアプリケーションの作成を行いました。

 次回はJSON形式ではなく、「Curl ORB for Java」というOSSのライブラリを使用して、CurlアプリケーションからPOJO(Plain Old Java Object)のメソッドをコールし、戻り値をCurlアプリケーションで受け取るアプリケーションを作成する手順を紹介する予定です。

  • ブックマーク
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • 木村聡(キムラサトシ)

    2000年からJavaによるWeb開発に携わる。Seasarプロジェクトコミッタであり、コミッタとしての経験をもとに現在は仕事としてフレームワークの開発を行っている。 著書に、「Javaフレームワーク開発入門」や「Eclipseで学ぶはじめてのJava」等がある。

All contents copyright © 2005-2020 Shoeisha Co., Ltd. All rights reserved. ver.1.5