皆さん、こんにちは。
9月に入ってしまいました。
だんだんと日が落ちる時刻が早くなってきてますね。18時を過ぎると暗くなってきてます。
こうやって夏は過ぎていき、秋が来るんですね。
さて、今日もREST APIについてのお話です。
ただし、今回はXPagesではなく、Notesクライアントからの実行ができるように進めていきます。
REST APIはHTTP通信を行いますので、Notesクライアントからはそのまま(@関数やLotusScriptを使う)では実行できません。
そこで、Javaプログラムを組んでそれを実行することになります。
NotesクライアントからJavaプログラムを実行するにはいくつかの方法があります。
- Webページを表示させて、その中でJavaプログラムを実行する
- Javaエージェントを作成して、LotusScriptから呼び出す
- LS2Jを使ってLotusScriptからJavaを実行する
1.はNotesクライアントはWebブラウザも内包されているのを利用しているものです。
2.はソルクシーズの吉田さんもブログ記事にしている(
Notes から MQTT を使って Publish する(Quickstart : Watson Internet of Things Platform))ものですね。
もっとも、こちらの記事ではRESTではなく、IoTを使うためのMQTTをJavaエージェントで実行していますが。
3.は古い記事ではありますが、「
Notesサポートのつぶやき」というブログの
LS2J : LotusScript でJavaのクラスを呼び出してみるにあるLotusScriptを拡張してJavaプログラムを呼び出せるという方法をつかっています。
いずれもJVMを使うことになるので、メモリ使用量に気を付けなければいけませんが、それでもNotesクライアントから実行できるというのはなかなか面白いものだと思います。
今日は、これらのうち、「
3.LS2Jを使ってLotusScriptからJavaを実行する」という方法でREST APIを使ったプログラムを実装してみます。
実装するのはお決まりの郵便番号検索です。
郵便番号を入力したら、郵便番号データ格納したNotes DBにREST APIで接続して、該当する住所情報を取得してくるというものにします(XPagesでのREST APIの紹介時に使用したものと同等です
Notes/Domino でREST APIを使ってみよう-後半-を参照)。
今回のサンプル画面はこんな感じです。
 |
Notes DBの郵便番号検索画面 |
郵便番号フィールドに郵便番号を入力して、[検索]ボタンをクリックすると該当する住所情報が「都道府県名」「市区町村名」「町域名」に入ってくるという形です。
では、早速プログラムの中身を見ていきましょう。
※実装順序は無視しています。
1.[検索]ボタン
Option Declare
Use "LibRESTAPI"
Sub Click(Source As Button)
Dim uiws As New NotesUIWorkspace
Dim uidoc As NotesUIDocument
Dim udoc As NotesDocument
Dim adrs As TypeAddress
Set uidoc = uiws.CurrentDocument
Set udoc = uidoc.Document
If SearchAddress( udoc.ZIPNO(0), adrs ) = False Then
Msgbox "住所の検索に失敗しました。", 0 + 16, "郵便番号検索"
Exit Sub
Else
Call udoc.ReplaceItemValue( "PREFECTURES", adrs.Prefectures )
Call udoc.ReplaceItemValue( "CITIES", adrs.Cities )
Call udoc.ReplaceItemValue( "ADDRESS", adrs.Address )
End If
End Sub
フォーム上に細々とプログラムを書くのが好きではないので、最小限の形にしています。
NotesUIDocumentクラスのメソッドを使用して、入力した郵便番号を取得して、それを引数にしてSearchAddress()という関数を呼び出しています。
この関数の戻り値が成否と住所情報で、成功していたら住所情報を画面上の文書にセットしています(計算結果フィールドにしているので、NotesDocumentクラスのメソッドを使ってセットしています)。
ここは、よくある?LotusScriptのプログラムだと思いますので、そんなに難しくはないと思います。
2.LS2Jの関数
Option Public
Option Declare
UseLSX "*javacon"
Use "NotesREST"
Use "ClassEnvironment"
Type TypeAddress
Prefectures As String '都道府県名
Cities As String '市区町村名
Address As String '町域名
End Type
Function SearchAddress( zipno As String, adrs As TypeAddress ) As Boolean
SearchAddress = False
On Error GoTo ErrProc
Dim mySession As New JavaSession
Dim myClass As JavaClass
Dim varAddress As Variant
Dim endpoint As String
Dim cEnv As cEnvironment
'入力チェック
If zipno = "" Then
Exit Function
End If
'環境設定文書オブジェクトの取得
Set cEnv = New cEnvironment()
'Javaクラスの取得
Set myClass = mySession.GetClass( "jp.co.effectforce.NotesREST" )
endpoint = cEnv.EndPoint
varAddress = myClass.searchAddress( endpoint, zipno )
If varAddress(0) = "" Then
Exit Function
End If
adrs.Prefectures = varAddress(0)
adrs.Cities = varAddress(1)
adrs.Address = varAddress(2)
SearchAddress = True
Exit Function
ErrProc:
MsgBox _
"エラー行数:" & CStr(Erl) & Chr$(10) & _
"エラー番号:" & CStr(Err) & Chr$(10) & _
"エラー内容:" & Error, 0 + 16, "LS2JによるREST API"
Exit Function
End Function
こちらがLS2Jのプログラムです。
ポイントはいくつかありますが、まずは
UseLSX "*javacon"という記述が必要なこと。これはLotusScriptからJavaの関数を実行できるようにするためのものです。
次に、下記変数の定義。
Dim mySession As New JavaSession
Dim myClass As JavaClass
変数名は任意ですが、型は決まり事ですので、このまま覚えても差し支えないでしょう。
JavaSessionはJVMにアクセスするためのクラスです。NotesSessionと同等のものと考えてよいかと思います。
JavaClassはその名の通り、Javaのクラスです。既存のJavaのメソッドを実行することもできますし、自分でJavaクラスを作成して(後述)、そのメソッドを実行することもできます。
java.lang.*やjava.util.*クラスの関数であれば、そのままLotusScriptに記述して使えそうです。
(どこまでが標準のままで使えるかについてはわかってません。ごめんなさい・・・)
JavaClassに定義したのは、自作した郵便番号検索用のREST APIのクラスです。
それがこの文になります。
Set myClass = mySession.GetClass( "jp.co.effectforce.NotesREST" )
実際に、REST APIで郵便番号を検索しているのが、下記のメソッドです。
endpointには、郵便番号を取得するためのURLが格納されています。
varAddress = myClass.searchAddress( endpoint, zipno )
3.自作したJavaクラス
スクリプトライブラリは、LotusScript以外にもJavaやJavaScriptも作成することができます。
LS2Jなので、Javaのスクリプトライブラリを作成します。
 |
スクリプトライブラリ作成画面 |
名前を入力して、タイプを「Java」にして、[OK]をクリックすると、
Javaライブラリコンテンツの画面になるので、Untitle.javaをダブルクリックして、ソースのエディタ画面を開きます。
すると、
public class Untitled {
}
だけが表示されたさびしい画面になるので、適宜変更します。
なお、今回のソースは以下のような形にしてみました。
package jp.co.effectforce;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.*;
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonToken;
public class NotesREST {
public static String[] searchAddress( String endpoint, String zipno ) throws Exception {
String[] adrs = new String[3]; // Output用住所情報格納変数
try {
// Initialize
adrs[0] = "";
adrs[1] = "";
adrs[2] = "";
// REST API
String strUrl = endpoint + "?search=FIELD%20zip7%20CONTAINS%20" + zipno;
URL url = new URL( strUrl );
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.setRequestMethod( "GET" );
con.addRequestProperty( "Content-Type", "application/json; charset=utf-8" );
con.setInstanceFollowRedirects( false );
con.setDoInput( true );
con.connect();
int rc = con.getResponseCode();
if (200 != rc) throw new Exception( "ErrorCode:" + rc + "/ErrMessage:" + con.getResponseMessage() );
BufferedReader reader = new BufferedReader( new InputStreamReader(con.getInputStream()) );
StringBuilder buffer = new StringBuilder(2048);
String line = null;
while (null != (line = reader.readLine())) {
buffer.append( line ).append('\n');
}
String result= buffer.toString();
// JsonFactoryをもとにJsonParserの取得
JsonFactory factory = new JsonFactory();
JsonParser parser = factory.createParser( result );
// 配列で返ってくるのでそれを見越した処理とする
if ( parser.nextToken() == JsonToken.START_ARRAY ) {
while ( parser.nextToken() != JsonToken.END_ARRAY ) {
// 各オブジェクトの処理
if ( parser.getCurrentToken() == JsonToken.START_OBJECT ) {
while ( parser.nextToken() != JsonToken.END_OBJECT ) {
String pname = parser.getCurrentName();
parser.nextToken();
// Prefectures or Cities or Addressの場合のみ処理をする
if ( "Prefectures".equals( pname ) ) {
adrs[0] = parser.getText();
} else if ( "Cities".equals( pname ) ) {
adrs[1] = parser.getText();
} else if ( "Address".equals( pname ) ) {
adrs[2] = parser.getText();
} else {
parser.skipChildren();
}
}
} else {
parser.nextToken();
}
}
}
// disconnect
con.disconnect();
return adrs;
} catch( Exception e ) {
System.out.println( "getMessage= " + e.getMessage() );
adrs[0] = "";
adrs[1] = "";
adrs[2] = "";
return adrs;
}
}
}
上記のソースの中で
URL url = new URL( strUrl );
・・・
String result= buffer.toString();
この辺りがREST APIになります。
endpointに指定したURLに対して、「GET」でhttp通信をしているくらいです。
その後、返ってきた結果を行単位で読み込んで、resultという文字列変数に格納しています。
この格納された文字列はJSON形式のデータです。
そこから下の行はREST APIで取得したJSONデータをJavaで解析をしています。
ここで、一つ問題になるのが、JavaでJSONを扱うには、そのままではできないということです(Java8で実装予定らしいけど、どうなったんだろ?)。
そこで、JacksonというJavaのライブラリを利用することにします。
今回はJacksonのうち、coreである「jackson-core-2.3.5.jar」をダウンロードして使用しています。
※Jasksonライブラリの使い方についてはGoogleで検索してみてください。
JSONデータをJavaで解析していき、必要な情報(Prefectures、Cities、Address)を配列に格納して、返しています。
このようにLS2Jを使えば、NotesクライアントからもJavaプログラムを実行することができます。
さらにJavaプログラムを作成すれば、NotesクライアントからもREST APIを実行することがわかっていただけたかと思います。
Notes DBをXPagesするのは大変なんだけど、Webサービスを使ってみたい!という方はこういったことにもチャレンジしてみてはいかがでしょうか?
なお、LS2Jについては、Domino Designerのヘルプに詳しい解説が出ていますので、熟読することをお勧めします。こちらを参照->
LS2J の概要
それでは今日はこの辺で・・・
Notes/Dominoで困ったことがあれば、弊社にお問い合わせください。
IBM Championの私が承ります!
お問い合わせはこちらから→