LotusScript のBackend クラスと Notes Javaクラスはよく似ている

2008年11月05日 00:39

 今日は、Notes Javaクラスについて話そうと思います。サポートとしては、Notes Javaクラスの仕組みを理解しようと思ったのであれば一度は以下の文章を読んでほしいです。

Lotus Notes/Domino における Java 実行環境とその問題判別について (#729912)

ここで仕組みの部分でよく理解してほしいのは、Notes Javaクラス、COM、LotusScriptが並列に並んで、Lotus Script Backengine を経由してNotesのNativeオブジェクトにアクセスしていることです。
 これがタイトルでも書いている通り、「Notes JavaクラスとLotusScript Backengine クラスはよく似ている」と言う事です。DIIOP接続などJARファイルを使っていると忘れがちですが、これらの処理は全てnlsxbe.dll (WindowsでのDLL名)を使用してNotesに接続している、と言う点では同じなのです。

もう1つ重要な点は、Notes Javaクラスも LotusScriptも直接Nativeオブジェクトを介してアクセスしているわけではない点です。これらの処理は共通のインターフェースを使用しているのでほぼ同等の結果を得ることが出来る反面、どちらもLSXBEの制限に縛られますし、同じ問題に遭遇する可能性があります。

 例えばHTTPタスクの場合は、このようなインターフェースを介さず直接Nativeオブジェクトにアクセスします。だからNotes JavaクラスとLotusScriptの機能拡張は同時に行われる事が多いのですが(8.0からの未読文書へのアクセスなど)、これとWebアクセスの拡張はお互いに独立なのです。

このような仕組みを理解する事ははサポートのシーンでも重要になります。
Notes Javaクラスの問題は、LotusScciptでも問題になる事が多いので、調査の際にはこの視点を最大限に生かす必要があるのです。

1. 一般的に再現環境の構築や試行は、Javaのリモートアクセスアプリケーション > Java エージェント > LotusScript の順で容易になる。繰り返しテストが必要な場合には、LotusScript で再現させる事も大事

2. LotusScriptの問題はほとんどLSXBEの問題である可能性が高いが、JavaエージェントではJVMの問題が起因であることがある。LotusScriptで問題が再現出来ないことが分かると、JVM の問題、メモリの使用状況、コーディングの問題などを疑う必要が出てくる。(Javaの recycle()の問題などはこの典型ではないかと思います。)

3. Javaアプリケーションの問題調査を行うときには、LotusScriptでの問題発生事例についても、ヒットしている可能性を考慮する必要がある


アプリケーション開発者の視点で考えると、たとえば 、Javaでコードを書いている人にしてみれば、「どのような条件で問題が起きるか」と言う事には詳細について知りたいと思うのではないかと思うのですが、1. のように「同じ問題がLotusScriptでも起きるかどうか?」と言うのは何の役にも立たない情報なので多くの場合興味を持っていないことが多いのではないかと思うので、これはサポート固有の視点ではないかと思います。
 もちろん条件を絞る事によって十分な情報が得られる事も多いので、自分自身このような観点での調査までするのは限られたケースだけです。

なんか今日は堅い話になってしまったので、明日はアプリ開発には役に立たないかもしれないけど、「DIIOPとか聞くとNotes開発者なのにJava使わないといけなくなりそうで怖いんだけど、どんなものなのかは知りたいから触ってみたい」と言う人向けの簡単DIIOPテストコードを紹介します。
繰り返しますが、リモートアクセス用の本格的アプリ開発には何の役にも立たないと思います。 。

DIIOPの動作確認用サンプルコード

2008年11月05日 22:06

 テンプレートのデザイン変えてみました。こだわりはないのですが、どうしても2カラムのものに変えたかったので。。やっぱり3カラムだと横幅の狭さが読んでて気になります。。

昨日も書いたとおり、今日はDIIOPのサンプルコードと簡単な動かし方です。
本格的な開発ではEclipseのようなGUIの開発環境があると便利ですが、ちょっとDIIOPの動きを知りたいだけであれば、JDKを使って手でコードを書いたほうが楽なのではないかな、と個人的には思います。

■ 準備
 ノーツデータベースを作ったらいきなり使えるわけではないのでクライアントとサーバーについて以下の準備だけは最低限必要なのでお願いします。

1) DIIOPタスクの稼動
 ドミノサーバーはDIIOPタスクとHTTPタスクを稼動しておいてください。
必要に応じて、Load DIIOP コマンドで起動してもよいですし、Notes.ini のServerTasks行に加えておいても構いません。あとDIIOPはインターネットパスワードでつなぐので、接続するNotesユーザーにはインターネットパスワードを設定しておく必要があります。

2) JDKの導入
 接続を行うクライアント環境ではJDKを導入しておく必要があります。
コンパイルの必要があるため、JREだけではなく、JDKを導入しておく必要があります。

JDKはどこのものでもいいのですが、一応SunのJDKダウンロードサイトと、IBMのJDKダウンロードサイトのリンクをつけておきます。
IBM JDK Download
SUN JDK Download

今回のサンプルくらいではJDKのバージョンはそれほどこだわる事は無いのですが、Notes 8でも 1.5.0 なので、ちゃんとした開発を行う際には最新のJava 6 ではなく Lotus Notes/Dominoの JRE のバージョンにあわせてください。
 現在利用しているNotes のJREのバージョンが知りたい場合にはNotesのJVMディレクトリの下にあるbin\java.exeを使用して確認をする事が出来ます。

C:\lotus\notes\jvm\bin>java -fullversion
java 完全バージョン "J2RE 1.5.0 IBM Windows 32 build pwi32devifx-20071025 (SR6b)"


3) NCSO.jarのコピー
 Notesクライアントの入っている環境下であれば直接そこをサーチパスに指定すればよいので問題ないのですが、本来リモートアクセスの Java アプリケーション(DIIOPを使用した接続を行うもの)ではNotesクライアントが導入されていない端末で接続を行うことが前提なので、その場合にはNCSO.jarを事前に端末上にコピーしておく必要があります。
NCSO.jarは <データディレクトリ>\domino\java にあります。


■ サンプルコード

サンプルデータベースを用意するのも面倒だと思うので、サーバー上のNames.nsf につないでサーバー名とデータベースタイトルだけを出力するものです。

NABTest.java (青字は適当に変えて下さい。)

import lotus.domino.* ;

public class NABTest
{
static String host = "xxxx.xxx.com" ; // server address
static String cName = "notes admin" ; //username
static String dbname = "names.nsf"; // database (e.g. mail\\hnagashi.nsf)
static String password = "password" ; // internet password


public static void main(String args[]){
try{
Session s = NotesFactory.createSession(host,cName,password) ;
Database db = s.getDatabase("", dbname );
if(!db.isOpen()){
db.open();
}
String title = db.getTitle();
String uname = s.getUserName();

System.out.println("UserName: " + uname );
System.out.println("ServerName: " + db.getServer());
System.out.println("Title: " + title);

}catch(NotesException ne){
System.out.println(ne.id + ne.text);
ne.printStackTrace() ;
}catch (Exception e){e.printStackTrace() ;}
}
}


■ コンパイルと実行
 恐らく初めてDIIOPアプリケーションを作るときに一番緊張するのはコンパイル作業ではないでしょうか。言葉で説明すれば、「NCSO.jarをクラスパスに指定してコンパイルして下さい」と言うだけの話なのですが毎回混乱するので僕は以下のようなバッチファイルを用意しています。
(以下、作業ディレクトリをC:\MyJava としています。)

Compile.bat (青字は編集してください。)
set Java_HOME=C:\SUN\Java\jdk1.6.0_06\bin
set PATH=%Java_HOME%;%PATH%
set CLASSPATH=C:\lotus\notes\data\domino\java\NCSO.jar;C:\myjava
javac -target 1.5 NABTest.java

Java_HOME: JDKのbinディレクトリへのパス
CLASSPATH: NCSO.jarへのパスとNABTest.javaを置いているディレクトリ
8.xのJREはjava 1.5なので -target を1.5にしていますが、7.xのNCSO.jarなどを使っているのであれば1.4などにしましょう。
 僕の環境ではNotesクライアントが入っているのでNCSO.jarのパスがいかにも、と言うところになっていますが、これもどこに置いても構いません。ClassNotFoundException になるのはほとんどの場合NCSO.jarへのパスが通っていない場合です。CLASSPATHが色々通り過ぎていると逆に変なjarファイルを見てエラーになってしまう事もあるのでこのバッチの例のように新たにセットしてしまった方が簡単な気がします。

僕は実行のコマンドも考えるの面倒なので以下のような一行のバッチファイルを作っています。
こっちはJava_HOMEへのパスが通っていることが前提なので、そうでない場合は上の例を参考にパスを加えて下さい。

run.bat
java -cp c:\MyJava;c:\lotus\notes\data\domino\java\NCSO.jar NABTest

■ 実行結果

C:\MyJava>run
C:\MyJava>java -cp c:\MyJava;D:\lotus\notes\data\domino\java\NCSO.jar NABTest
UserName: CN=notes admin/O=LTS
ServerName: CN=xxxx/O=LTS
Title: LTS's Directory


コマンドプロンプトからcompile.batとrun.batを実行して、ユーザー名、サーバー名、データベース名(Names.nsf)のタイトルが表示できたら成功です。
 DIIOPの最大のメリットは、NCSO.jarさえあればクライアント環境が無くてもNotesデータベースにアクセスできる事なのですが、何となく概要がつかめたのではないでしょうか。

 僕はこういうコードをHello World みたいな感じで使ってDIIOP環境がセットアップできた事を確認するのに使っています。

 こういう安っぽいコードはなかなかTechnoteとして公開することも難しいし、上級者にはあまり役に立たない情報なのではないかと思うのですが、このような場所で共有してDIIOPやNotes Javaクラスの勉強の入り口になる事が出来たら幸いです。

サンプルコードの補足

2008年11月06日 23:01

 初心者向けに書いたのに何の説明もしていませんでした。。。
解説するほどのコードでは無いのですが、リモートアクセスアプリケーションでは以下のような行が特徴です。

Session s = NotesFactory.createSession(host,cName,password) ;

よくよく考えると当たり前なのですが、リモートアクセスアプリケーションの場合にはリモートに対してセッションを張るので、createSession()メソッドではhostを指定しないといけないのです。
ローカルアクセスアプリケーションでは、以下のようにhost無しでcreateSession() を呼びます。

Session s = NotesFactory.createSession() ;

それと、あのサンプルを見て、「定数宣言しているhostやユーザーネームの値が格好悪い」て思った人は多いと思います。
 接続先を変える度にコンパイルしなくてもいいように引数にして処理すればいいのに・・・て言うのはごもっともな意見なのですが、この手のテストコードでは僕はあまり気にしていないです。
引数の順番とか数を覚えるの面倒くさいし、コンパイル環境が整っていないと十分な調査はどうせ出来ないので効率よくコンパイルできるようにしておいたほうがデバッグコードとかを入れる上でも都合がいいし、この手のコードは出来るだけコードを短くしたいので、引数チェックや引数処理のコードを入れるのがどうしても好きになれない、て理由なのですが好みの問題だと思って流してください。。


ところで、リモートアクセスアプリケーションではNCSO.jarしか使いません。
Notes JavaクラスはLSXBEを使う、と言っていたのですがnlsxbe.dllやliblsxbe.so などはどのようにして使われるのでしょうか。
 この場合、LSXBE はサーバー上でDIIOPタスクが代理で呼び出すような形式になります。当然DIIOPタスクには通信の媒介となる役目もあるので、サーバー上でDIIOPタスク上で作られたORBスタブとクライアントのORBスタブ同士で通信を行う訳ですが、DIIOPタスク上ではこのために、以下の二つのオブジェクトが存在する事になります。

1. 主にクライアント通信のために必要になるJavaオブジェクト
2. LSXBEのために必要になる C++ オブジェクト

1.と2.のオブジェクトは当然相関があるのですが、Garbage Collectionの対象になるのは1.だけなのでここで2.のC++ オブジェクトだけが浪費されてしまう事があります。(もちろんこれは一時的な浪費で、エージェントが終了すれば解放されるべきものです。)
 これが、いわゆるNotes Javaクラス特有の recycle() が必要になる背景です。
この話も以下のTechnoteの4章に詳細が述べられています。LotusScriptではあまり意識する必要が無いのですが、Javaアプリケーションで大きなループをするときには必ずこの点について一度は考慮した方がよいと思います。たまにこういう問題に遭遇するとNotes のJVMはGCがちゃんと動かないような理由があるのではないか?と考えて、System.gc() を呼んでみようと考える人もいますが、これは予期しない結果を引き起こすのでしないようにして下さい。

Lotus Notes/Domino における Java 実行環境とその問題判別について (#729912)

DIIOP サンプル: ループ処理も加えてみる

2008年11月07日 21:19

やはりNotes のエージェントで一番典型的にはビューにアクセスして文書を取得したりアイテムを書き換えたりする処理なのではないかと思います。

 せっかくだから以前に使ったサンプルをちょっとだけ拡張して PeopleビューにアクセスしてFullNameフィールドの値をコンソールに表示するようにしてみると少し成長した気分になれるのではないかと思います。

私のテスト環境では10ユーザー程度しかいないので全ユーザー出力もたいした事無いですが、数千ユーザーいる環境でこんなことをすると大変なので、あくまでサンプルコードだ、という事をご理解いただけると助かります。。



NABLoop.java
import lotus.domino.* ;

public class NABLoop
{
static String host = "xxx.xxxx.com" ; // server address
static String cName = "notes admin" ; //username
static String dbname = "names.nsf"; // database (e.g. mail\\hnagashi.nsf)
static String password = "password" ; // internet password

public static void main(String args[]){
try{
Session s = NotesFactory.createSession(host,cName,password) ;
Database db = s.getDatabase("", dbname );

if(!db.isOpen()){
db.open();
}
View view = db.getView("People");
Document doc = view.getFirstDocument();
Document temp = null;
String title = db.getTitle();
String uname = s.getUserName();

System.out.println("Session User Name: " + uname );
System.out.println("ServerName: " + db.getServer());
System.out.println("Title: " + title);

while (doc != null)
{
System.out.println("User Name: " + doc.getItemValueString("FullName"));
temp = view.getNextDocument(doc);
doc.recycle();
doc = null;
doc = temp;
}

}catch(NotesException ne){
System.out.println(ne.id + ne.text);
ne.printStackTrace() ;
}catch (Exception e){e.printStackTrace() ;}
}
}

ローカルアクセスアプリケーションに書き換える

2008年11月28日 08:27

NotesでJavaを利用している際に発生した問題を調査する場合、始めに切り分けないといけないのは、Java アプリケーションがローカルアクセスアプリケーションかリモートアクセスアプリケーションか、と言う点です。

 リモートアクセスアプリケーションと言うのはいわゆるDIIOP経由でのアクセス、ローカルアクセスアプリケーションと言うのはNotes.jarを介してノーツクライアントのモジュールを利用してアクセスする方式です。 したがって、ローカルアクセスアプリケーションを動かすには、ローカルにNotesクライアントが導入されている必要があります。よく、「私のアプリケーションはリモートのドミノサーバーにつなぐのでローカルアクセスではありません」と思ってしまう方も多いのですが、Sessionクラスのオブジェクトをリモートに持つかローカルに持つかの違いで、どちらもリモートのデータベースを開いたり処理する事が出来ます。

 以前このブログでDIIOPのサンプルを紹介しましたが、あのNABにアクセスするだけのサンプルをローカルアクセスに変えてみましょう。

 変更したコードでは、createSession()の呼び方が少し変わった(引数がなくなっています)のと、sinitThread()やstermThread() と言うおまじないのような処理が入っているのに気付いて頂けたら十分です。他にもローカルアクセスアプリケーションを使う方法はあるのですが、それは後日紹介します。

# そろそろ「このくらいのサンプルはデザイナーヘルプにあるじゃないか・・・」とか言われそう
# ですが、チュートリアル目的のエントリなので。。
# 一応コンパイル方法までフォローしているので。。。。

NABTest1.java

import lotus.domino.* ;

public class NABTest1
{
static String domsrv = "ServerName/Org" ; // server name
static String dbname = "names.nsf"; // database (e.g. mail\\hnagashi.nsf)


public static void main(String args[]){
try{
NotesThread.sinitThread();
Session s = NotesFactory.createSession() ;
Database db = s.getDatabase(domsrv, dbname );
if(!db.isOpen()){
db.open();
}
String title = db.getTitle();
String uname = s.getUserName();

System.out.println("UserName: " + uname );
System.out.println("ServerName: " + db.getServer());
System.out.println("Title: " + title);


}catch(NotesException ne){
System.out.println(ne.id + ne.text);
ne.printStackTrace() ;
}catch (Exception e){e.printStackTrace() ;
}finally { NotesThread.stermThread();}
}

}



コンパイル用バッチファイル (C:\MyJava を作業フォルダと仮定しています。)
compile.bat

set Java_HOME=D:\Java\JDK6.0\bin
set PATH=%Java_HOME%;%PATH%
set CLASSPATH=C:\lotus\Notes\jvm\lib\ext\Notes.jar;C:\myjava
javac -target 1.5 NABTest1.java

# 7.x をご利用の場合は、-target を 1.4にして下さい。。

run.bat
java -cp c:\MyJava;C:\lotus\Notes\jvm\lib\ext\Notes.jar NABTest1

ローカルアクセスアプリケーション: NotesThreadクラスの継承

2009年06月25日 00:17

 ずいぶん間が空いてしまいましたが、Javaを利用したローカルアクセスアプリケーションについての続きです。

前回はsintThread()/stermThread() と言う静的メソッドを利用した方法を紹介しましたが、今回はNotesThreadクラスを継承してマルチスレッド化することまで意識した方法で書き直してみましょう。

前回のトピック
ローカルアクセスアプリケーションに書き換える

クラス名が同じなので、コンパイルや実行用のバッチファイルは以前のまま使えます。
前回同様この方法はローカルアクセス(Notes.jarを使う方法。クライアントがインストールされている必要があります)でしか使用できません。

import lotus.domino.* ;

public class NABTest1 extends NotesThread
{
static String domsrv = "ServerName/Org" ; // server name
static String dbname = "names.nsf"; // database (e.g. mail\\hnagashi.nsf)


public static void main(String args[]){
NABTest1 t = new NABTest1();
t.start();
}
public void runNotes(){
try{
Session s = NotesFactory.createSession() ;
//Session s = NotesFactory.createSessionWithFullAccess();
Database db = s.getDatabase(domsrv, dbname );
if(!db.isOpen()){
db.open();
}
String title = db.getTitle();
String uname = s.getUserName();

System.out.println("UserName: " + uname );
System.out.println("ServerName: " + db.getServer());
System.out.println("Title: " + title);


}catch(NotesException ne){
System.out.println(ne.id + ne.text);
ne.printStackTrace() ;
}catch (Exception e){e.printStackTrace() ;
}

}

}



NotesThreadクラスを継承した場合、Notes関連の処理は、runNotes() と言うメソッドをオーバーライドすることによって処理を書くことが出来ます。main関数ではインスタンス化したあとに、start()メソッドを実行するとここで記述した処理が実行されるのが何となくコードを見ても分かるのではないかと思います。

このように書くと今までの簡単なサンプルコードよりはずっとJavaっぽくなってきますよね。
まだmain関数とNotes関連の処理を行うクラスを分けたりするともっと本格的になると思うのですが、そこは各自の興味にお任せいたします。。。

ローカルアクセスアプリケーション: Runnable インターフェースの継承

2009年07月10日 00:02

なんかおかしいな、と思ってたら水曜日にこの記事をアップし忘れていたようです。
ちょっとネタ切れっぽかったのでちょうどよかったところもありますが。。

ローカルアクセスアプリケーションの最後のサンプルは、Runnable インターフェースを実装する方法で書くサンプルです。
 前の例との違いをよく確認してください。

NABTest1.java
import lotus.domino.* ;
public class NABTest1 implements Runnable{
// server name
static String domsrv = "ServerName/Org";
// database (e.g. mail\\hnagashi.nsf)
static String dbname = "names.nsf";


public static void main(String args[]){
NABTest1 t = new NABTest1();
NotesThread nt = new NotesThread((Runnable)t);
nt.start();
}
public void run(){
try{
Session s = NotesFactory.createSession() ;
//Session s = NotesFactory.createSessionWithFullAccess();
Database db = s.getDatabase(domsrv, dbname );
if(!db.isOpen()){ db.open();
} String title = db.getTitle();
String uname = s.getUserName();
System.out.println("UserName: " + uname );
System.out.println("ServerName: " + db.getServer());
System.out.println("Title: " + title);
}catch(NotesException ne){ System.out.println(ne.id + ne.text);
ne.printStackTrace() ;
}catch (Exception e){e.printStackTrace() ;
}
}
}


extends が implements になったのと runNotes() が run() になったところ以外は目新しいところも無いと思うのですが、それでOKです。このような例はJavaが多重継承を許可していないので、色々なクラスを継承したい場合にはどれかをインターフェースにして、キーとなるメソッドを実装していく形にする必要があるためです。(この辺の説明はあんまり自信ないのですが、合っているでしょうか?)

実際に必要になったときに何も知らない状態で1から書くのは面倒だと思うので、そんなときにちょっと思い出して参考にしてもらえればと思います。


ちなみにコンパイル用のバッチファイルは以下の通りです。
例によってC:\MyJava にサンプルコードを置く事を前提にしています。

compile.bat
set JAVA_HOME=C:\Program Files\Java\jdk1.6.0_11\bin
set PATH=%JAVA_HOME%;%PATH%
set CLASSPATH=C:\lotus\Notes\jvm\lib\ext\Notes.jar;C:\myjava
javac -target 1.5 NABTest1.java

run.bat
set JAVA_HOME=C:\Program Files\Java\jdk1.6.0_11\bin
set PATH=%JAVA_HOME%;%PATH%
set CLASSPATH=C:\lotus\Notes\jvm\lib\ext\Notes.jar;C:\myjava
java NABTest1


dW: Writing Java to build applications using IBM Lotus Domino Designer

2009年09月03日 00:13

developerWorks でこんな記事がありました。

Writing Java to build applications using IBM Lotus Domino Designer

正直言ってこの手の資料にいわゆるJavaの説明みたいな内容を入れているのはやや中途半端に感じるのであまり好きではないのですが、Javaエージェントの手軽なサンプルなどもあって斜め読みしてみても面白いのかな、て思います。

一部の内容でもこのブログで今度取り上げようかな、と思います。
是非興味がある方は一度入手してみてください


最新記事