はじめに
前回は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の実装クラスのコードは次のようになります。
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に直接記述しています)。
<?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を次のように記述します。
<?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オブジェクトへ変換します。
{import * from CURL.IO.JSON} || サーバからのJSONデータの受信 {let empJsonValue: JsonValue = {JsonValue-parse {url "http://localhost:8080/curl_server/emp.json"} } }
実行結果のようなグリッドテーブル表示を行うには、RecordGridを使用します。
RecordGridを使用するには、まず、各列に対する名前と型、それにcaptionで見出し(実行結果のヘッダー)を宣言します。
{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にセットして最後に表示を行えば、先ほどの実行結果を表示できます。
|| 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のコード全体を掲載します。
{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アプリケーションで受け取るアプリケーションを作成する手順を紹介する予定です。