IEEE1888 SDKをMacで使う: Javaプログラミング篇
前回はSDKの構成について確認しましたが、今回は実際にWRITEするプログラムを作成してみます。
環境構築
基本的にはSDKに付属する「IEEE1888プログラミング・スタートアップ・マニュアル」の説明通りに進めます。
まず、Axis2のダウンロードをおこないます。マニュアルでは1.5系を使用するように指定されているので、その中で最新版を選択します。
今回は、axis2-1.5.6-bin を利用します。
ダウンロードしたら、以下のディレクトリ構成に展開します。
app/ # 開発のトップ + axis2-1.5.6/ # zipを展開してできるディレクトリ
Mac に java が入っているかどうかは環境によります。java や javac コマンドを起動してみて、動作すれば良いですし、無ければ「インストールしますか?」と聞いてくるので、そこでインストールを選択します。(OSX 10.8.1の場合)
聞いてこなければ http://support.apple.com/downloads/#java からダウンロードしてインストールします。
次に WSDL からStub等のファイルを生成します。
$ cd app $ export JAVA_HOME=/System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home $ ./axis2-1.5.6/bin/wsdl2java.sh -uri http://192.168.11.12/axis2/services/FIAPStorage?wsdl
これで、以下のような構成になります。準備OKです。
app/ + axis2-1.5.6/ + build.xml + src/org/fiap/soap/FIAPStorageStub.java + src/org/fiap/soap/FIAPStorageCallbackHandler.java
コード
次にテストソースを記述します。ファイルは以下の場所に作成します。
app/ + .... + src/kahnn/SampleFIAPApp.java
コードは以下のようになります。
package kahnn; import org.fiap.soap.FIAPStorageStub; import org.apache.axis2.databinding.types.URI; import java.util.Calendar; import java.util.Date; import java.text.SimpleDateFormat; public class SampleFIAPApp { static private String USAGE_MSG = "SampleFIAPApp id value [time [target_endpoint]]\n" + " time: yyyy-MM-dd'T'HH:mm:ss.SSSZ\n" + " target_endpoint: http://x.x.x.x/axis2/services/FIAPStorage/"; /** WRITE */ public static void write(String id, String data, Calendar dtime, String endpoint) throws Exception { // Pointの作成 (1つだけ). Point[]を取り扱うことも可. FIAPStorageStub.Point point = new FIAPStorageStub.Point(); { FIAPStorageStub.Value value = new FIAPStorageStub.Value(); point.setId(new URI(id)); value.setTime(dtime); value.setString(data); point.addValue(value); } // PointSetは使用しないが使う場合は以下のようになる. 配列でなくても可. //FIAPStorageStub.PointSet[] ps = new FIAPStorageStub.PointSet[n]; //ps[0] = new FIAPStorageStub.PointSet(); //ps[0].setId(new URI("http://.... pointset id ..../")); //ps[0].setPoint(point); // .... // Body FIAPStorageStub.Body body = new FIAPStorageStub.Body(); body.addPoint(point); // -or- setPoint(points[]) // body.setPointSet(ps); // Transport FIAPStorageStub.Transport transport = new FIAPStorageStub.Transport(); transport.setBody(body); // DataRQ FIAPStorageStub.DataRQ dataRQ = new FIAPStorageStub.DataRQ(); dataRQ.setTransport(transport); // Stub & WRITE REQUEST FIAPStorageStub server = ((null == endpoint) ? new FIAPStorageStub() : new FIAPStorageStub(endpoint)); FIAPStorageStub.DataRS dataRS = server.data(dataRQ); // Get Header transport = dataRS.getTransport(); FIAPStorageStub.Header header = transport.getHeader(); if (null == header) { exitErr("Fatal Error: Header object was not found."); } // Check OK -or- NG FIAPStorageStub.OK ok = header.getOK(); if (ok != null) { System.out.println("Successfully uploaded " + "\'" + id + "\'=\'" + data + "\' ..."); } else { FIAPStorageStub.Error error = header.getError(); if (error != null) { exitErr("Error: type=\'" + error.getType() + "\'" + "; message=\'" + error.getString() + "\'"); } else { exitErr("Fatal Error: Neither OK nor Error object was not found."); } } } ////////////////////////////////////////////////////////// public static void usage() { System.err.println(USAGE_MSG); System.exit(-1); } public static void exitErr(String msg) { System.err.println(msg); System.exit(-2); } public static void main(String[] args) throws Exception { if (args.length < 2) { usage(); } // Get pointID String id = args[0]; // Get value data String data = args[1]; // Set calender Calendar dtime = Calendar.getInstance(); if (3 <= args.length) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); dtime.setTime(sdf.parse(args[2])); } // Set endpoint String endpoint = null; if (4 <= args.length) { endpoint = args[3]; } write(id, data, dtime, endpoint); } }
Ant を使用して build します。Mac には標準でインストールされているので、何も考えずに利用できます。
$ export AXIS2_HOME=/Users/kahnn/Desktop/IEEE1888/app/axis2-1.5.6 $ ant ..... jar.client: [jar] Building jar: /Users/kahnn/Desktop/IEEE1888/app/build/lib/FIAPStorage-test-client.jar BUILD SUCCESSFUL Total time: 3 seconds
これで、Client 用の FIAPStorage-test-client.jar ができました。
実行
さっそく、テストプログラムを実行してみます。axis2 に含まれる axis2.sh を利用すると必要なクラスパスの設定が楽になります。
まずは USAGE の確認。
$ axis2-1.5.6/bin/axis2.sh -cp build/lib/FIAPStorage-test-client.jar kahnn.SampleFIAPApp Using AXIS2_HOME: /Users/kahnn/Desktop/IEEE1888/app/axis2-1.5.6 Using JAVA_HOME: /System/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home SampleFIAPApp id value [time [target_endpoint]] time: yyyy-MM-dd'T'HH:mm:ss.SSSZ target_endpoint: http://x.x.x.x/axis2/services/FIAPStorage/
大丈夫のようなので、いよいよデータ投入。
$ axis2-1.5.6/bin/axis2.sh -cp build/lib/FIAPStorage-test-client.jar kahnn.SampleFIAPApp http://test.kahnn/ps/point_int_1 100 2012-09-05T21:20:56.001+0900 .... Successfully uploaded 'http://test.kahnn/ps/point_int_1'='100' ...
例のごとく、「Sensors in this FIAPStorage」の画面で確認します。上手く登録できたようです。
ここまでできれば FETCH の方も簡単ですね。後は実際に使用する際に色々とやれば良さそうです。
PointID登録の仕様について
色々とさわっていたら、pointSetTree テーブルに存在しないPointIDを指定しても、データが登録できることに気づきました。PointIDが未登録でWRITEした場合、error(POINT_NOT_FOUNDとか)が返されると思っていたのですが、そうでは無いようです。
あらためて、ドキュメントを見直しても、このあたりの仕様についてはハッキリとしません。(やはり正式な仕様が必要なのでしょうかね)
それでも、SDK (IEEE1888SDK-20120617) に付属のドキュメント「IEEE1888 Storageとの通信仕様.pdf」(2011-09-04) を確認してみますと、「第6章 エラーの種類」に以下のように記載されています。
6.3. type=”POINT_NOT_FOUND” key 要素で id 属性により指定していたポイントが、サーバ側に存在しないことを示す。 一点でも該当するケースがあれば、このエラーが返ることがある(その id 属性の読出 しがトライされるまでは、必ずしもこのエラーが返るとは限らない)。 6.4. type=”FORBIDDEN” 指定されたポイントへのアクセスが禁止されていることを示す。FETCH の場合であれば、 key 要素の id 属性で指定したポイントの読出しが禁止されていることを意味し、WRITE の場合であれば、point の id 属性で指定したポイントへの書込みが禁止されている ことを 意味する。通常は、クライアントが認証された上で、ポイントへの読出し・書込み 権限の 検証が行われるため、現バージョンの東大版 IEEE1888 ストレージでは、この エラーが返ることはない。将来のために用意されている。
この説明からは、POINT_NOT_FOUND は query の場合に返される可能性のあるエラーで、WRITE で PointID の登録までは許さない場合は(将来的には)FORBIDDENが使用できるように思えます。
SDKで使っているStorageの参照実装では、使い勝手を考えて、特別な指定なしに任意のIDが登録可能なようにしているという事なのでしょう。
実際に参照実装(fiap-reference-201205)を確認しても、POINT_NOT_FOUND(に対応するPointNotFoundException) は、query時に指定された id が pointsettree テーブルに無い場合に返されるようですし、FORBIDDEN(に対応するForbiddenException)は定義されているけど使用されていないようです。org.fiap.storage 内のソースでは pointsettree に登録する部分も確認できました。やはり、無ければ追加する処理となっています。
データのクリーンアップ
まちがって登録してしまったポイントやデータを削除する場合は、SQL を使うしか無いようです。
pointsettree 以外のテーブルについては、pointeettree を親とする参照制約がついています。ですから、あるidとそれに紐づくデータを全部消したい場合は、子供から順番に削除する必要があります。 (テーブル作成時の REFERENCES 指定に ON DELETE CASCADE があれば、一気に消せるはずなんですが、残念ながらそうはなっていません。まあ、ALTERで制約をつけ直したり、最初からやり直す場合は、テーブル定義を最初から変更しても良いかもしれません)
試しに psql を使用して http://test.kahnn/ps/point_int_X を削除してみます。(これは Pointsetは存在しません)
gut_storage=# \set PID '\'http://test.kahnn/ps/point_int_X\'' gut_storage=# \set ... PID = ''http://test.kahnn/ps/point_int_X'' ... gut_storage=# delete from pointValueLatest where id = :PID; DELETE 0 gut_storage=# delete from pointValue where id = :PID; DELETE 1 gut_storage=# delete from pointAttribute where id = :PID; DELETE 0 gut_storage=# delete from pointSetTree where id = :PID; DELETE 1 gut_storage=# select count(*) from pointvalue where id = 'http://test.kahnn/ps/point_int_X'; count ------- 0 gut_storage=# select count(*) from pointsettree where id = 'http://test.kahnn/ps/point_int_X'; count ------- 0
この要領で既存のデータを全て削除して、新しく追加した Point にデータを追加してみます。
このようにすっきりします。