当ブログに掲載しているサンプルは、すべて利用者の自己責任という形でお願いします。
ただし、明らかな不具合がある場合、ご連絡いただければ、訂正記事を出します。
また、こちらのサンプルは、別のサイト等への公開、転載は一切禁止しています。
どうしてもと言う場合は、筆者にあらかじめご連絡ください。

テクてく Lotus 技術者 Slack に参加しよう!

2016年9月6日火曜日

LS2JでREST APIを実行してみよう

皆さん、こんにちは。

9月に入ってしまいました。
だんだんと日が落ちる時刻が早くなってきてますね。18時を過ぎると暗くなってきてます。
こうやって夏は過ぎていき、秋が来るんですね。

さて、今日もREST APIについてのお話です。
ただし、今回はXPagesではなく、Notesクライアントからの実行ができるように進めていきます。

REST APIはHTTP通信を行いますので、Notesクライアントからはそのまま(@関数やLotusScriptを使う)では実行できません。

そこで、Javaプログラムを組んでそれを実行することになります。
NotesクライアントからJavaプログラムを実行するにはいくつかの方法があります。

  1. Webページを表示させて、その中でJavaプログラムを実行する
  2. Javaエージェントを作成して、LotusScriptから呼び出す
  3. 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の私が承ります!
お問い合わせはこちらから→Lotus Notes/Domino カスタマイズとセキュリティ強化 - 株式会社エフ

0 件のコメント: