ushidayの日記

主に「IBMi」のメモに・・・

iSeriesAccess for Linux の インストール

以前、Debian系にiSeriesAccess for Linuxをインストールした時に上手くいかず、投げ出したままだったので、CentOSで試した時のメモです。
IBMからは、rpmで提供されているので、環境としてはRHEL系を前提としている様です。

sudo rpm -hUv iSeriesAccess-5.4.0-1.6.i386.rpm

libodbcinst.so.1 は iSeriesAccess-5.4.0-1.6.i386 に必要とされています
libodbc.so.1 は iSeriesAccess-5.4.0-1.6.i386 に必要とされています
libstdc++.so.5 は iSeriesAccess-5.4.0-1.6.i386 に必要とされています
libstdc++.so.5(CXXABI_1.2) は iSeriesAccess-5.4.0-1.6.i386 に必要とされています
libstdc++.so.5(GLIBCPP_3.2) は iSeriesAccess-5.4.0-1.6.i386 に必要とされています
libstdc++.so.5(GLIBCPP_3.2.2) は iSeriesAccess-5.4.0-1.6.i386 に必要とされています
libXm.so.3 は iSeriesAccess-5.4.0-1.6.i386 に必要とされています
libXp.so.6 は iSeriesAccess-5.4.0-1.6.i386 に必要とされています

と足りないパッケージのオンパレードなので、

yum provides xxxxxxx.so.x

で結局たりないパッケージは以下の4つでした。

    • unixODBC-2.2.11-7.1.i386
    • compat-libstdc++-33-3.2.3-61.i386
    • openmotif22-2.2.3-18.i386
    • libXp-1.0.0-8.1.el5.i386
sudo yum install unixODBC-2.2.11-7.1.i386 compat-libstdc++-33-3.2.3-61.i386 openmotif22-2.2.3-18.i386 libXp-1.0.0-8.1.el5.i386

sudo rpm -hUv iSeriesAccess-5.4.0-1.6.i386.rpm

上記でインストールは完了しました。すんなり行きましたね。ibm5250というコマンドが/usr/binに出来ます。※ClientAccess(今は呼び方違うと思いますが)のライセンスが必要なようなのでご注意を。

5035の場合 : ibm5250 192.168.1.254 -LANG ja_JP -USE_CP5035
5026の場合 : ibm5250 192.168.1.254 -LANG ja_JP

コードページの設定は「-USE_CP5035」オプションで行えるようです。

Zend_Authでユーザー認証&DB2のencryption

Zend_Authを使った、ユーザー認証をやってみたので、メモをしておきます。
IBM i(AS/400)の既存データベースに存在している社員マスターのユーザーIDとパスワード情報で認証を行います。
Zend_Authは、データベース認証用に”Zend_Auth_Adapter_DbTable”というクラスが用意されています。このクラスは、コストラクタに”Zend_Db_Adapter_xxxxxx”のインスタンスを渡す必要があり、今回のケースでは”Zend_Db”を使っていなかったので、”Zend_Auth_Adapter_Interface”を実装して、オリジナルの”Zend_Auth_Adapter”を作成して認証してみました。

まず、社員マスターの認証をチェックするメソッドを実装します。認証の判定としては

    • 認証成功
    • ユーザーが存在しない
    • 廃止ユーザーで認証不可
    • パスワードのアンマッチ
    • その他のエラー

の5つとします。

encryptionを使ったパスワードの暗号化

話はソレるのですが、先日発表されたPower7のIBM i OS 7.1では、データベース列における暗号化が可能になるそうです。しかし、自分が使っている環境のV5R4では、それが実現出来ませんので、DB2のEncryptを使って暗号化したいと思います。
暗号化したデータを保存する為には、DDLで下の様に”varchar for bit”を定義するか、DDSで”H”タイプのフィールド定義にする必要があります。

--- DDLの場合 ---
ALTER TABLE EMPL ALTER COLUMN PWD SET
DATA TYPE VARCHAR ( 80) FOR BIT DATA NOT NULL

--- DDSの場合 ---
A            PWD           80H         COLHDG(' パスワード ') 

フィールドのLengthについては、マニュアルに以下の様な記載がありました。

パスワードストリングが指定されているが、ヒント・ストリングは
指定されていない場合は、データ・ストリングの長さ属性に16 を足し、
それに次の8バイト境界までのバイト数を足したものになります。

それ以外の場合は、データ・ストリングの長さ属性に48を足し、それに次の8
バイト境界までのバイト数を足したものになります。

だそうです。

次に暗号化から復号化までの手順です。

    • 暗号化パスワード&ヒントの設定「SET ENCRYPTION PASSWORD 'hoge' WITH HINT 'fuga'」これをやると以降のSQLセッションで、グローバル設定になります。若しくは、SQL発行時に列毎にencrypt_rc2やdecrypt_charの第二引数にパスワードを取る事も出来ます。
    • 暗号化処理「encrypt_rc2('暗号化したい値')」又は「encrypt_tdes('暗号化したい値')」
    • 復号化処理「decrypt_char(復号対象列)」

暗号化パスワードは、暗号化する時と復号化する時に必要となります。ヒントはパスワードを忘れた時のヒントになります。ヒントは「select gethint(pwd) from テーブル 」の様にして、見ることが出来ます。
気を付けるのは、暗号化する際に書き込むフィールドは、無変換(CCSID65535)なので、書き込むジョブのCCSIDに左右されると言う事です。パスワード認証を行う際に、”入力パスワードを暗号化したモノ”とDBの暗号化されたパスワードを比較すると、現行のCCSIDと暗号化した時のCCSIDが違うと、当然不一致になります。しかし、”decrypt_char”を使うと、現行ジョブのCCSIDを反映した状態で復号化されるので、DBの暗号化パスワードを復号化した上で、入力パスワードと比較した方が、ジョブのCCSIDに影響される事がなさそうです。
■暗号化されたパスワード

認証処理の実装

■社員のパスワードを認証するクラス

<?php

class Employee {
    const AUTH_SUCCESS = 1;
    const AUTH_NOTFOUND_USER= 2;
    const AUTH_INACTIVE_USER= 3;
    const AUTH_UNMATCH_PASS = 4;
    const AUTH_ERROR = 9;
    
    /**
     * 認証をする
    */
    public function checkAuth($code,$password){
        
	$db =  ←DB2接続処理
        // 認証
        $sql="select"
                ." code,rtrim(name) as name ,del"
                ." ,(case when decrypt_char(pwd)=? then 1 else 0 end) as auth_flag"
                ." from empl"
                ." where code = ?";
        $result = db2_prepare($db,$sql);
        db2_bind_param($result ,1 ,"password",DB2_PARAM_IN);
        db2_bind_param($result ,2 ,"code",DB2_PARAM_IN);
        if(!db2_execute($result)){
	    $flag =  self::AUTH_ERROR;    //エラー
	    return $flag;
	}
        $row = db2_fetch_assoc($result);
        //認証判定
        if ($row) {
            if ($row["del"] == "D"){
                $flag =  self::AUTH_INACTIVE_USER;  //廃止ユーザ
            }elseif ($row["auth_flag"] == 0){
                $flag = self::AUTH_UNMATCH_PASS;    //パスワード不一致
            }else{
                $flag =  self::AUTH_SUCCESS;    //成功
            }
        }else{
            $flag =  self::AUTH_NOTFOUND_USER;  //ユーザーなし
        }
        return $flag;
    }
}
?>
Zend_Auth_Adapterの実装

”Zend_Auth_Adapter”では”authenticate”メソッドを実装して、”Zend_Auth_Result”のインスタンスを返して、例外に”Zend_Auth_Exception”をスローする必要があります。前述のクラスのチェックメソッド(checkAuth)を使って、こんな感じで実装します。

<?php 
require_once 'Employee.php';
require_once 'Zend/Auth/Adapter/Interface.php';
require_once 'Zend/Auth/Adapter/Exception.php';
require_once 'Zend/Auth/Result.php';

class EmployeeAuthAdapter implements Zend_Auth_Adapter_Interface
{
    private $id;
    private $password;
    private $name;  
    public function __construct($id, $password){
        $this->id = $id;
        $this->password = $password;
    }  
    /**
     * 認証
    */
    public function authenticate() {
        $id = $this->id;
        $password = $this->password;
        $code = null;
        $message = "";
    try {
        //社員インスタンス
        $empl = new Employee();
        $ret = $empl->checkAuth($id,$password);
        $msgCode = $ret;
        //結果
        switch ($msgCode) {
        case Employee::AUTH_SUCCESS:
             $code = Zend_Auth_Result::SUCCESS;
             $message =  "認証OK";
             break;
        case Employee::AUTH_NOTFOUND_USER:
             $code = Zend_Auth_Result::FAILURE_IDENTITY_NOT_FOUND;
             $message =  "ユーザーが見つかりません";
             break;
        case Employee::AUTH_INACTIVE_USER:
             $code = Zend_Auth_Result::FAILURE_UNCATEGORIZED;
             $message =  "ユーザーが廃止されてます";
             break;
        case Employee::AUTH_UNMATCH_PASS:
             $code = Zend_Auth_Result::FAILURE_CREDENTIAL_INVALID;
             $message =  "パスワードが違います";
             break;
        default:
             $code = Zend_Auth_Result::FAILURE_UNCATEGORIZED;
             $message =  "その他のエラー";
             break;
        }
    } catch (Exception $e) {
	throw new Zend_Auth_Exception("予期せぬ例外で認証エラー");
    }
    $result = new Zend_Auth_Result($code,$id,array($message));
    return $result;
    }  
}
?>
AuthAdapterの使用

実際に使用する時は、先ほど実装した”authenticate”メソッドで、Zend_Auth_Resultを取得して、”isValid”メソッドで認証します。trueが返れば成功です。認証結果のメッセージは”getMessages”で、メッセージのリストが返されます。

<?php
    require_once 'EmployeeAuthAdapter.php';
    //認証アダプタ
    $adapter = new EmployeeAuthAdapter($id,$password,$autologin);
    //認証
    $ret = $adapter->authenticate();
    if($ret->isValid()){
         //成功の処理
    }else{
         //失敗の処理
         $message = $ret->getMessages();
         echo $message[0] ;
    }
?>

認証画面は、こんな感じに出来ました。

暗黙スキーマの挙動の違い

PCからお手軽に、既存のIBM i(AS/400)のプログラムを呼び出す方法に、SQLのCALLを投げる方法があります。こんな感じで
■PC系の文法

CALL スキーマ名.プログラム名 ('引数','引数2')

AS/400の対話式SQL

CALL スキーマ名/プログラム名 ('引数','引数2')

通常自分は、ユーザーによって実行ライブラリが異なる可能性があるので、ライブラリ名(スキーマ名)を指定しません。スキーマ名を変数にしても良いのですが、AS/400の伝統的に暗黙のオープンというのがあります。暗黙オープンの方法としては...

  1. ユーザージョブの”ライブラリーリスト”に入れる。
  2. ”set schema ライブラリ名”をする。

が、考えられます。前者は、本番運用向きとは思いますが、開発の場合は環境毎にユーザーが変わるので、自分は後者の「set schema」を使っています。
この”暗黙のスキーマ”なのですが、PHPDB2関数で、微妙にAS/400とPCで挙動の違いが有ったので、メモをしておきます。

AS/400(Zend Core for i5 2.6.1)の場合は、”set schema”でスキーマを指定すれば、暗黙でプログラムをcall出来ます。しかし開発用PC(Zend Server CE 4.0)から発行されたSQLの場合には、次の様なメッセージが返されました。

call hoge ('fuga') SQL0204N  "ユーザ名.hoge" は未定義の名前です。  SQLSTATE=42704

PHPDB2関数かDB2 Connectなのか分かりませんが、暗黙のスキーマが見つからない場合は、自動でユーザー名を明示的スキーマに置き換えます。(間違って存在していて勝手にプログラムが動かれても困りますが...。)
今回は対応方法として、AS/400のプログラムをストアドプロシージャ定義する事にしました。方法は以下の通りです。

    • OPMプログラムならばILE化する。(OPMだとストアド情報の保管・復元が出来ないため)
    • ”create procedure”で、プログラムをストアド定義する。

■ストアド定義SQLの例(※CLプログラムで5バイト文字列の引数を1つ取る場合)

CREATE PROCEDURE スキーマ名/プロシージャ名(IN 引数名 CHAR (5 )) LANGUAGE CL
NOT DETERMINISTIC CONTAINS SQL EXTERNAL NAME ライブラリ名/プログラム名
PARAMETER STYLE GENERAL       
    • 実行時に”set path スキーマ名”で関数パスを指定して、プロシージャ名をcallで呼びだす。

OPMをILE化するのは、SAVLIBやRSTLIBでストアド定義の情報が反映されないからです。検証してわかったのですが、プログラムのリネームや削除、ライブラリのリネームをしてもストアド情報と同期はされません。プログラムをSAVOBJ,RSTOBJしても同様に、ストアド情報は同期されませんでした。但し、ライブラリを削除した時だけは、ストアド情報も消えるようです。ライブラリを保管→ライブラリ削除→ライブラリ復元だと、ストアド情報も同期された状態で、復元しました。この事から、ILEオブジェクトの何処かに、ストアドに関するメタ情報を持っていると思うのですが、何処にあるか見つける事が出来ませんでした。(DSPPGMなどには無かった)
※追記:その後、DMPOBJコマンドでメタ情報を確認出来ました。SQL文が丸ごと残ってます。
ちなみに、OPMでストアド定義すると、プロシージャは作成されますが、その旨の警告メッセージが出力されました。
■注意書き(※DB2 Universal Database for iSeries SQL 解説書より)

*PGM オブジェクトが保管された後、このシステムや別のシステムに復元すると、カタログはそれらの属性を使用して自動的に更新されます。
外部プロシージャーの場合は、次の制約の範囲内で属性を保管することができます。
・ 外部プログラム・ライブラリーは、QSYS であってはなりません。
・ 外部プログラムは、CREATE PROCEDURE ステートメントの発行時に存在していなければなりません。
・ 外部プログラムは、ILE *PGM オブジェクトか*SRVPGM オブジェクトにする必要があります。
オブジェクトを更新できない場合でも、それにかかわらず、プロシージャーは作成されます。

これでプロシージャ名を呼び出せば、AS/400でもPCでも暗黙的にプログラムを呼び出す事が出来ます。
但しこの場合は、プログラム名の呼出と違って、AS/400でも”set path”で関数パスを指定する必要があります。ちなみに、ジョブのライブラリーリストに入っていれば、”set path”は不要です。本番運用ならばライブラリーリストでやるのが、一番楽チンかもしれませんネ。

ストアドプロシージャ定義の一覧を確認するには、次のSQLを投げると確認出来ます。

 select * from sysroutines where specific_schema = 'スキーマ名'

トランザクション分離レベル・省略値の違い

以前は、IBM i(AS/400)上のZend Coreにデプロイしてテストやリモートデバッグをしていたのですが、最近は、Windowsのローカル環境に立てた、Zend Server for Windowsでテストやデバッグをしてから、AS/400にデプロイしています。一人チーム開発なので、人のソースとマージしたりする事もないですし、この程度の事でも、実はソースコードのバックアップの代わりにもなったりします。(※ちゃんと正規のバックアップはしていますよ。モチロン...。)

Zend Server for Windowsを使っていて、気づいた違いについてメモを残しておきます。(「今頃、遅いよ!」と言うツッコミは無しの方向で...(-_-;)ゞ)

普段AS/400をメインで使っていると、STRSQLやSQLRPGも省略のトランザクション分離レベルが「*NC(即時コミット)」です。Zend Core for i5を使っていても省略は即時コミットです。たまーに、VBやらでODBC使うときは、「*CS(コミットされていない読取)」と言うことを思い出して、ODBCドライバの設定を「*NC」にするか、ジャーナル処理を「STRJRNPF」で開始しておきます。そうしないと、追加更新時に、”ジャーナル処理されてないお。”エラーで怒られます。ちなみにADOだと意識しなくても追加更新できるので省略が「*NC」と言うことになってるのだと思います。
ODBCの省略

本題ですが、Zend Core i5からZend Server for Windowsに移しただけで、特に動作なども問題のないアプリだったので、何も考えずテストしていたら、エントリー系のレコード追加更新で何処かで見たようなメッセージで怒られました。こんな感じのメセージです。

 [IBM][CLI Driver][AS] SQL7008N REXX 変数 "TABLE NAME " に矛盾したデータが入っています。 SQLSTATE=55019 SQLCODE=-7008

「ジャーナル処理されてないよ。」というストレートなメッセージにして欲しいのですが、どうやらこのメッセージが”ジャーナル処理されてないよ”という事みたいで、DB2 ConnectのCLI接続もODBCと同様に省略が「*NC」ではなく「*CS」の様です。

  • トランザクション分離レベルの設定で考えられそうな対処方法
    1. ジャーナル処理を開始(STRJRNPF)する。
    2. DB2 Connectの「C:\Program Files\IBM\SQLLIB\db2cli.ini」で分離レベルを指定する。
    3. SQLの「SET TRANSACTION ISOLATION LEVEL NO COMMIT」で分離レベルを指定する。
    4. SQLの「WITH句」を使って「WITH NC」で分離レベルを指定する。

まず(1)の方法ですが、対処としはジャーナルを作る事も、それはそれでアリですが、通常それ程クリティカルな要件で無い場合に、ジャーナル処理はレシーバーのオモリやリソースの消費も含めて考えると、必要では無いケースもあります。方法としては「CRTJRNRCV」→「CRTJRN」→「STRJRNPF」でジャーナルを開始します。確かにジャーナルがあると、万が一の時に、ある時点に戻る事が出来るので、トラブル時には威力を発揮してくれる事は確かでしょうが...。
■STRJRNPF

次に(2)の「db2cli.ini」でオプションを追加して、データベースカタログ単位で、分離レベルの省略を設定する事が可能です。以下が設定例です。これが一番簡単で、i5とも同期が取れるので、今回はこれを採用しました。
■「db2cli.ini」

; TXNISOLATION=1|2|4|8|32(UR|CS|RS|RR|NC 省略はCS。32はDB2 for iの即時コミット。)
[カタログ名]
DBALIAS=カタログ別名
TXNISOLATION=32

テキストファイルを直接変えてもいいですし、DB2の構成アシスタントで、選択(S)メニューのCLI設定でも変更する事が出来そうです。(自分は調子悪くてDB2GUI画面は砂時計のまま動きませんでした。)

(3)と(4)は直接SQLの発行で、指定する方法ですが、(3)に関しては上手くいくかと思いきや、駄目でした。AS/400のSTRSQL等では勿論実行できますし、i5版ですとそういった解説もあるので、もしかしたらWindows版のDB2 Connectを使っているなど、実行環境による差異が幾分ありそうです。ちなみに返されたメッセージは以下の通り、

[IBM][CLI Driver][AS] SQL0525N セクション = "1" パッケージ = "SYSSH200 " 整合性トークン = X"NULLID" のバインド時にエラーが発生したため、SQL ステートメントは実行できません。 SQLSTATE=51015 SQLCODE=-525

(4)は問題なく実行されました。ちなみに「php.ini」の”ibm_db2.i5_allow_commit ”は”0(no commit)”になっていますが、(※「i5_allow_commit」は、OS/400上のZend CoreやZend Serverにしか有効でない設定だからでした。)(2)も(4)でコミットもロールバックも出来ているので、DB2 ConnectのCLI設定が優先されている模様です。(トランザクション処理を使う場合は、autocommitオプションをOFFする必要があります。)
db2_connect()関数のオプションに”i5_commit=DB2_I5_TXN_NO_COMMIT”がありますが、これを指定してもジャーナルが開始されていないと、エラーが返されました。まだ色々と試す時間が無かったので、検証しきれていませんが、今後トライしてみて、さらに実態がつかめればと思います。

ZendServer CEの環境変数

ローカルになんちゃって、IBM i(AS/400)の環境を作りたくて(個人では買えないですから...)、DB2 for WindowsとZend Sever CE 5.0 for Windowsを入れました。
そもそも、イケない感じはしていたのですが、DB2の入れた順序が悪いらしく、

  1. DB2 Express-C V9.7 for Windows
  2. DB2 Connect V9.1 (iSeries付属)
  3. DB2 Runtime V9.5 (ZendServer付属)

の順で入れたら、変な挙動をして、一旦アンインストールと思い実行したのですが、待てど暮らせど終了せず、途中で失敗してからは、それはもう最悪の状況になりました。仕方なくレジストリを、地道に修正して、ようやく復帰しました。

今度は、バージョンの古い順に入れたら、おかしな動きはなくなりました。
DB2 V9以降は、複数のDB2を入れても、DB2_COPYという状態で、複数のインストールが出来ます。
DB2には、デフォルトDB2コピーの切り替えが可能な”デフォルトDB2選択ウィザード”([プログラム]→[IBM DB2]→[セットアップ・ツール]→[デフォルトDB2選択ウィザード])が、付属していて、システム環境変数レジストリを書き換えてくれるようです。
無事切り替えてから、ヤレヤレとホットしたのもつかの間、PHPのアプリケーションでやりたかった、ローカルのDB2 Winの参照が出来ず、DB2 iを参照してしまいます。再起動しても何をしても結果は変わらず、システム環境変数の”PATH”も”DB2INSTANCE”もDB2 Winに変わっているのに...。

”var_dump($_SERVER)”で、環境変数を調べてみると、”PATH”がDB2 iのモジュールに通っている。プロセスブラウザで”Apacheのプロセス(httpd.exe)”を見てももちろん同じ、他のプロセスを見ると、現在のシステム環境変数になっているのにと、そんな状況。
httpd.confに何かあるのか?SetEnvとかしなきゃ駄目?→何もない..。
zend.confに何かあるのか?→何もない..。
php.iniに何か有るのか?→何もない..。
ZendEnablerConf.xml?→Coreには、なかったUTF-8でBOMがエラーで、インストール時のデフォルトでコケたぐらいのファイル。自分が認識が甘かった...。orz
ここにモロに、環境変数が記述してあります。こんな感じで...。

<Environment>
	<Variable name="DB2INSTANCE" value="DB2" />
	<Variable name="PHP_FCGI_MAX_REQUESTS" value="10000" />
	<Variable name="TEMP" value="C:\DOCUME~1\ushida\LOCALS~1\Temp" />
	<Variable name="PHPRC" value="C:\Program Files\Zend\ZendServer\etc" />
	<Variable name="PATH" value="...;C:\Program Files\IBM\SQLLIB\BIN;..." />
</Environment>

”DB2INSTANCE”と”PATH”を変更したら、無事ローカルのDB2 WinとDB iの切り替えが出来ました。これで同じカタログのデータベースを作成すれば、ローカルだけで開発とデモが出来そうです。(X60が頑張りすぎで、ソロソロやばいかもしれません)

あとはデータをDB2 iからDB2 Winへ簡単にレプリケーション出来ないかなぁ?
手動でエクスポート&インポートって何だか原始的だし

Zend Server CE 4.0.6 for Winを入れてみた

Zend Server CE(Community Edition)4.0.6をWindows環境に入れて、IBM i(AS/400)のDB2とゴタゴタやったので、メモしておきます。

■動作環境

DB2 Connectのセットアップ
  • DB2ランタイムで躓き...

これは、結果論で分かったのですが、Zend Server(以下ZS)には、DB2のランタイムがバンドルされていて、ZSインストール時に導入されます。しかしランタイム版は”Unix,Linux,Windows”のDB2を対象としている様で、”DB2 for i”は対象外の様です。以下のエラーが投げられました。

---- 接続文字列 ----
「DRIVER={IBM DB2 ODBC DRIVER};DATABASE=カタログ名;HOSTNAME=ホストIP;PORT=446;PROTOCOL=TCPIP;UID=ユーザー名;PWD=パスワード;」は接続出きません。

---- db2_conn_errormsg()の内容 ----
[IBM][CLI Driver] SQL8002N Connect processing failed; a valid product license was not found. SQLSTATE=42968 SQLCODE=-8002

AS/400と同時提供されている”DB2 Connect Personal Edtion”を入れる必要がありました。
IBMよりCDが提供され入るので、32bit Windwos版のインストールを行ないます。
インストール自体は、フルインストールにして、特に難しいところはありません。

  • カタログの作成

[スタート]→[プログラム]のDB2から、[セット・アップツール]→[構成アシスタント]を選び、カタログを構成します。

構成を追加するか?訪ねられるので”はい”を選びます。ウィザードを以下スナップショットの手順で、進めます。
ちなみにデータベース名は「WRKRDBDIREコマンド」で確認して、”*LOCAL”になっている項目です。










以下の設定が終わると、[管理ツール]の[データソース]でシステムDSNに、カタログが作成されています。

  • db2cli.lstのバインド

DB2のコマンドウィンドウでcliのバインド事前にしておく必要があります。しておかないと、DB2接続で以下のエラーが発生しました。

-------------------------------------------
[IBM][CLI Driver][AS] SQL0805N
パッケージ "NULLID .SYSSH200"
が見つかりませんでした。 SQLSTATE=51002
-------------------------------------------

cliのバインド
※参考(https://www.ibm.com/support/knowledgecenter/ja/SSEPGG_11.1.0/com.ibm.db2.luw.qb.dbconn.doc/doc/c0005595.html

C:\Program Files\IBM\SQLLIB\BIN> db2 connect to カタログ名 user ユーザ名 using パスワード
C:\Program Files\IBM\SQLLIB\BIN> cd ../bnd

※DB2 LUWの場合は以下を実行
C:\Program Files\IBM\SQLLIB\bnd> db2 bind @db2cli.lst blocking all grant public
C:\Program Files\IBM\SQLLIB\bnd> db2 bind @db2ubind.lst blocking all grant public 

※IBMiの場合は以下を実行
db2 bind @ddcs400.lst blocking all grant public
(実行後に”NULLID”の中にSYSオブジェクトが作成される)

DB2クライアントのエンコーディングが、ローカル環境がデフォルトになるようで、DB2関数で返される結果が、SJISになって文字化けしました。以下のコマンドをDB2のコマンドウィンドウで実行して、エンコーディングUTF-8(CCSID 1208)にします。

db2set db2codepage=1208
---- 確認 ----
db2set -all

[構成アシスタント]の[DB2レジストリ]でも設定する事が可能です。


Zend Server CEのセットアップ

Zend.comより「Zend Server CE 4.0.6」のWindowsインストーラより導入します。
フルインストールをしないと、IBM系のExtension(ibm_db2,pdo_ibm)はインストールされないので、フルインストールします。最後に前述で入れたDB2 Connectについて、「既知のDB2 Clientがあるよ」と言われます。

  • ZendEnablerConf.xmlの修正

”C:\Program Files\Zend\ZendServer\etc\ZendEnablerConf.xml”の先頭に壊れたデータが入っています。この壊れたデータを除去します。この壊れたデータの影響で、http://localhost:89/ZendServer/ の表示が500 Internal Server Error になります。id:satoruyoshidaさんのこちらの日記で解決させて頂きました。助かりました。

新しい管理用UIは、設定変更時のリスタートもその場で出来るなど、使い易くて良い感じです。
php.iniやhttpd.confを適宜直して、実行すればWindwos Zend Serverの出来上がり?

DB2関数の接続は、カタログ接続の場合は、i5と全く同等です。「*LOCAL」を使わずカタログ名にしておけば、そのままソースを変更する事なく動作します。以下の様な感じです。

<?php
/*
// 非カタログ接続
$database = 'CSC2008';
$user = 'name';
$password = '****';
$hostname = '192.168.xxx.xxx';
$port = 446; // WRKRDBDIREで確認。デフォルトの*DRDAは「446」

$conn_string = "DRIVER={IBM DB2 ODBC DRIVER};DATABASE=$database;" .
  "HOSTNAME=$hostname;PORT=$port;PROTOCOL=TCPIP;UID=$user;PWD=$password;";
$db = db2_connect($conn_string, '', '');
*/

// カタログ接続
$conn_string = "CSC2008"; //カタログ名
$db = db2_connect($conn_string, 'name', '****');

しかし、「ibm_db2.i5_dbcs_alloc=1」を設定してやっても、i5だと半角カナが切れないのに、Windowsだと半角カナが切れてしまいます。(UTF-8で3バイトで認識されて、フィールドのバイト数にあたる文字数になってしまう)これは未解決のままです。

一応ベンチマーク

以前i5でやったベンチマーク同じ条件でやったら、若干遅くなりました。
DB2関数

 time indexex time%
Start1266922322.48437600-0.00%
DB2 Start select1266922322.484397000.0000210.00%
DB2 End select1266922323.986329001.50193223.22%
DB2 Start insert1266922323.986358000.0000290.00%
DB2 End insert1266922328.558094004.57173670.68%
DB2 Start update1266922328.558126000.0000320.00%
DB2 End update1266922328.952449000.3943236.10%
Stop1266922328.952471000.0000220.00%
total-6.468095100.00%

■PDO

 time indexex time%
Start1266922241.96875100-0.00%
PDO Start select1266922241.968781000.0000300.00%
PDO End select1266922243.733597001.76481626.03%
PDO Start insert1266922243.733623000.0000260.00%
PDO End insert1266922248.343330004.60970767.98%
PDO Start update1266922248.343354000.0000240.00%
PDO End update1266922248.749533000.4061795.99%
Stop1266922248.749565000.0000320.00%
total-6.780814100.00%

先日行って来た、OS協議会のお話ですと、これからAS/400にもZend Serverがプリインストールされるみたいです。

Zend Framework Controllerメモ

Zend_Controllerを使ってみた時のメモを残しておきます。

httpd.confの設定

Zend Framework(ZF)では、URL変換に”mod_rewrite”モジュールを使用しているので、「/usr/local/Zend/apache2/conf/httpd.conf」を以下の様に書き換えます。

#LoadModule rewrite_module modules/mod_rewrite.so
↓
LoadModule rewrite_module modules/mod_rewrite.so

ちなみに、”Zend Core 2.6.1”はデフォルトでモジュールが有効になってました。”.htaccess”を有効にする為に、””以下に追記します。

# ルートの設定
<Directory />
    Options FollowSymLinks
    AllowOverride None
</Directory>

# ドキュメントルートの設定
<Directory "/www/zendcore/htdocs">
    Options Indexes FollowSymLinks

#    AllowOverride None
    ↓
    AllowOverride All

    Order deny,allow
        Deny from all
    Allow from 127.0.0.1
</Directory>
.htaccessの設定

Zend StudioZend Frameworkプロジェクトで作成すると以下の構造になり、コントローラ、ビューの雛形や.htaccessもデォルトで用意されますので、それを適宜変更します。

■デフォルトの.htaccess

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} -s [OR]
RewriteCond %{REQUEST_FILENAME} -l [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [NC,L]
RewriteRule ^.*$ index.php [NC,L]
Controllerを使う
    • フロントコントローラの準備。(e.g. PROJECT_HOME/public/index.php
    • Zend_Controller_Actionのサブクラスのアクションコントローラを準備。(e.g. PROJECT_HOME/application/controllers/IndexController.php
    • 継承されるメソッド
      • init : コントローラの初期化の実装をオーバーライド
      • preDispatch : Actionメソッド実行前のフックの実装をオーバーライド。
      • postDispatch : Actionメソッド実行後のフックの実装をオーバーライド。
      • _forward(アクション, クラス=null, モジュール, 引数) : アクション実行後にフォワード。preDispatchで行った場合は、アクションがキャンセルされる。(e.g. $this->_forward("hogehoge","hoge")<=> "/hoge/hogehoge" )
      • _redirect(URI) : URIへリダイレクトする。(e.g. $this->_redirect("/hoge/hogehoge"))
    • Viewを準備。(e.g. PROJECT_HOME/application/views/scripts/index/index.phtml)
    • 命名規則
      • コントローラクラス : 「HogeController.php」(MSD1が大文字)
      • アクションメソッド : 「hogehogeAction」(小文字名+Action)
      • ビュー : 「hogehoge.phtml」(小文字名)
    • URIにコントローラとメソッドを指定しない場合のデフォルト
      • アクションコントローラ : 「IndexController.php」(setDefaultControllerNameで変更可)
      • アクションメソッド : 「indexAction」(setDefaultActionで変更可)

■フロントコントローラの例

<?php
require_once 'Zend/Controller/Front.php';

/*
Zend_Controller_Front::run("../application/controllers");
↑は↓と同意
$front = Zend_Controller_Front::getInstance();
$front->setControllerDirectory("../application/controllers");
$front->dispatch();
*/
$front = Zend_Controller_Front::getInstance();
$front->setControllerDirectory("../application/controllers");

// 独自のデフォルトコントローラとデフォルトメソッドを指定する場合 
/*
$front->setDefaultControllerName("hoge");
$front->setDefaultAction("hogehoge");
*/

$front->dispatch();

■アクションコントローラの例

<?php

require_once 'Zend/Controller/Action.php';
require_once 'Zend/Date.php';

class hogeController extends Zend_Controller_Action
{

    public function init()
    {
        /* 初期化処理をここに記述する。コンストラクタの後に実行される */
        $this->view->assign("message","初期化を行いました。");
        $this->view->assign("message3","forwardするとコントローラの初期化で消える");
    }

    public function preDispatch(){
    /* アクションの呼出前に実行する処理 */
    }

    public function postDispatch(){
        /* アクションの呼出後に実行する処理 */
    	$this->view->assign("message4","アクションを実行しました。");
    }
  
    public function indexAction()
    {
        $this->view->assign("actionname","indexアクションです。");
        $this->view->assign("message3","forwardするとこのメッセージは消される。");
        $date = new Zend_Date();
        $this->view->assign("dt",$date->get(Zend_Date::TIMES));

        if(($date->compare("09:00:00",Zend_Date::TIMES) < 0)
           || ($date->compare("17:00:00",Zend_Date::TIMES) > 0)){
            $this->view->assign("message2","9〜17時以外は稼働時間外です");
            // 条件によって、別のアクションにフォワードする例
            // forward(アクション名,コントローラ名,モジュール名,パラメータ)
            $this->_forward("hogehoge");
        }else{
            $this->view->assign("message2","システム稼働中");               
        }        
    }
    
    public function hogehogeAction()
    {
        $this->view->assign("actionname","hogehogeアクションです。");        
    }    
}

■ビューの例

<html>
<head>
<title>タイトル</title>
</head>
<body>
アクション名:「<?php echo $this->escape($this->actionname);?><br/>
現在の時刻は「<?php echo $this->escape($this->dt);?>」です。<br/>

<?php echo $this->escape($this->message);?><br/>
<?php echo $this->escape($this->message2);?><br/>
<?php echo $this->escape($this->message3);?><br/>
<?php echo $this->escape($this->message4);?><br/>

</body>
</html>