こんにちは開発担当の米原です。
今回は、日時をユリウス日(ジュリアン日)に変換しないといけないケースがありましたので、その時の備忘です。
ユリウス日(Julian Day、JD)とは
ユリウス暦紀元前4713年1月1日の正午(世界時)からの日数です。
ユリウス日は、日付が長期にわたり連続するため、日数計算やさまざまな暦の関連付けなど、幅広く使われています。
サンプルコード(XPages サーバサイドJavascript)
//ジュリアン日変換
function funcToJulianDay(p_year, p_month, p_day) {
//月が1月または2月の場合、前の年の13月または14月とみなすみたい
if (p_month <= 2) {
p_year -= 1;
p_month += 12;
}
var jDay = 0;
var seireki = 0;
var correct = 0;
//指定された年の世紀部分
seireki = Math.floor(p_year / 100);
//400年周期での閏年の補正
correct = 2 - seireki + Math.floor(seireki / 4);
// 年を365.25日と見なし計算
//基準年である紀元前4713年からの年数をプラス
//月の部分を日数に変換(30.6001 を掛けることで、月の平均日数(約30.6日)を使って月数を日数に変換)
//日の部分をそのまま加算 1524.5(基準日(紀元前4713年1月1日正午)との補正)
jDay = Math.floor(365.25 * (p_year + 4716)) +
Math.floor(30.6001 * (p_month + 1)) +
p_day + correct - 1524.5;
return jDay;
}
//呼び出し元サンプル(今日の日付を取得し変換)
function test001(){
try{
var year = 0;
var month = 0;
var day = 0;
var dt:Date = @Today(); // 今日のの日時を取得してみる
year = dt.getFullYear();
month = dt.getMonth() + 1;
day = dt.getDate();
var julianDay = funcToJulianDay(year, month, day);
print("ユリウス日:", julianDay);
}catch(e){
print("Error in test001:"+e)
}
}
参考にどうぞ。
2024年10月18日
XPagesリッチテキストコントロールのカスタマイズ
XPagesのリッチテキストコントロール カスタマイズ
Notes/Dominoエンジニア XPages担当の米原です。
まだまだ現役でXpagesの開発を行っています!
最近行き詰まったところがありましたで備忘としてブログに残すことにしました。
リッチテキストの入力高さを固定(縦スクロール付き)方法
リッチテキストを画面に配置したのですが、文字を入力して改行していくと、
自動で入力枠が縦に広がり結構縦に長くなり1画面の領域を結構占領してしまいます。
どうにか縦の幅を狭くしたいと思いました。
入力をするとどんどん広がって他の項目が見えなくなったりします。
Dojo属性を追加することによりCKEditorのConfig値を変更しました。
autoGrow_maxHeight:縦幅の広がった際の最大値
こんな感じで最大枠を指定できます。長くなる場合は縦のスクロールバーが機能します。
ツールバーを消す方法
今回の場合上のツールバーも不要なので消したいと思います。
同じように、今度は nameに toolbar 、valueに [[]] これで上のツールバーは消えます。
この様にしてすっきりしました!
ここで「tootbar」 の「value」 に表示したいアイコンを選択することも可能です。
詳しくはこちらを参照してください↓
https://www.ktrick.com/xpages-richtext-ckeditor-toolbar-customize/
設定値などは、こちらを参考にしました。
https://docs-old.ckeditor.com/ckeditor_api/symbols/CKEDITOR.config.html
また色々と試したいと思います!
DominoV9→DominoV12 バージョンアップでXpagesエラーが発生した件
こんにちは。Xpages担当の米原です。
本日はXpagesでDominoV9からDominoV12にバージョンアップした際にハマったエラーの回避方法についてのお話です。
V9のサポート期日が迫っていまして、V12に更新されるお客さんが増えてきています。
そう、NotesDominoはどんどん進化してその都度便利な機能が追加されています!
V9で終わりではないですよ!!!
安心してNotes Domino 使い続けましょう。
さてさて本題に戻りまして
今回発生した問題はこちらの機能です。
- ボタン押下でダイアログを表示
- ダイアログ内で処理を実行(入力フィールドの値などセット)
- ダイアログ内で更新したものをダイアログを閉じるときのイベント(onHide)で部分更新
OKボタンで更新するのは、OKボタンのイベントに処理を追加で可能なのですが、
「x」ボタンが押された時にも反映したい場合があり、そのためにonHideイベントに部分更新のIDをセットしそのエリアを更新するように使用していました。
ここで、最後の「×」やOKでダイアログを閉じる際に、onHideが機能するのですが、
何とエラーになってしまってます。。
V9では大丈夫だったのに、V12ではこの使い方はダメなようです。
簡単なサンプルで確認したらこのようなものになります。
ダイアログ起動ボタン押下でダイアログ起動
「×」ボタン押下
エラー発生となります。
設計はこのようなものです
<xe:dialog id="dlgTest" title="入力用ダイアログ">
<xp:panel>
ボタン押下で値がセットされ、×ボタンで値が更新します。<br/>
<xp:button value="値をセット" id="button5">
<xp:eventHandler event="onclick" submit="true">
<xp:this.action><![CDATA[#{javascript:getComponent("testText").setValue("値がセットされました。")}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
<xp:button value="値をクリア" id="button1">
<xp:eventHandler event="onclick" submit="true">
<xp:this.action><![CDATA[#{javascript:getComponent("testText").setValue("値がクリアされました")}]]></xp:this.action>
</xp:eventHandler>
</xp:button>
</xp:panel>
<xp:eventHandler event="onHide" submit="true" refreshMode="partial" refreshId="testPan" disableValidators="true">
</xp:eventHandler>
</xe:dialog>
色々と調べた所、、、
どうも部分更新のエラー処理がV10から変更されているようでその影響の様です。
こちらに情報がありました 参考URL
回避策として、
Xspプロパティに「xsp.error.disable.detection.set01=true」をセットすることで
V9.0.1でのエラー処理に戻すことが可能です。
それでは、Xspプロパティを更新した状態でもう一度実行すると
「値をセット」ボタン押下後に、「×」ボタン押下すると
「値がセットされました。」と文言が更新されました。期待通りの動作です。
似た様な現象が発生した場合は参考になればと思います。
それでは本日はここまでで。
XPagesで同じカスタムコントールを使用する場合のID被りへの対処法
Notes/Dominoエンジニア XPages担当の米原です。
今回もXPagesのお話です!
共通して使用しているカスタムコントロールを同ページで複数使用する必要がある場合、
例えばこんなパターン
<xp:panel>
<xc:ccFormSample02>
<xc:this.rendered><![CDATA[#{javascript:param.get("paramTEST") == "01"}]]></xc:this.rendered>
</xc:ccFormSample02>
<xc:ccFormSample02>
<xc:this.rendered><![CDATA[#{javascript:param.get("paramTEST") == "02"}]]></xc:this.rendered>
</xc:ccFormSample02>
</xp:panel>
「ccFormSample02 」カスタムコントロールをURLパラメータの値で表示/非表示の切り替えを行っています。
今回は設計が複雑ではない簡単なカスタムコントロールを用意しましたが、実際運用される場合はもっと複雑
で複数配置を目的としたカスタムコントロールがあるかと思います。
例えばこのようなときが想像できます。
カスタムコントロールA(共通して使用したい。)
画面の表示をURLパラメータで切り替えている場合
画面Aでは「カスタムコントロールA」情報が重要になるので一番上に表示させたい。
画面Bでは「カスタムコントロースA」情報はあまり重要ではないので一番下に表示させたい。
そのような場合に同じカスタムコントロールを同画面に配置してURLパラメータで切り替えることで実装したりします。
表示・非表示切り替えで対応する場合に問題になるのが、IDの問題です!
今回は共有箇所のInputTestフィールドにIDを付与していてそのIDを元に値をセットしている場合があります。
<xp:tr>
<xp:td styleClass="tblHead">
<xp:label id="label2"
for="orderNumber1">
<xp:this.value><![CDATA[#{javascript:
var ret = "";
ret = "共通箇所"
return ret;}]]></xp:this.value>
</xp:label>
</xp:td>
<xp:td>
<xp:inputText id="test001"></xp:inputText>
</xp:td>
</xp:tr>
id="test001" 箇所ですね。
例えば初期値に値をセットさせたい場合でこのように値をセットしている場合
<xp:this.afterPageLoad><![CDATA[#{javascript:
getComponent("test001").setValue("test");
}]]></xp:this.afterPageLoad>
「 getComponent("test001").setValue("test"); 」このような方法で表示の値をセットすると、
結果的には
paramTEST=01 の場合 は初期値に 「test」がセットされていますが、 paramTEST=02 の場合 は値がセットされていません。
これはIDが被っているので最初に見つけた所のIDにセットされるようになります。
もう少し詳しく説明しますと、 getComponent("test001") .setValue("test") とすると、最初の id="test001"が取得されて、そちらに値がセットされてしまいます。
非表示の方法としてrenderをfalse にしたり、 style に display:none をして非表示にしたりしますが、 getComponent では非表示にしても取得されていまうので、そのような現象になってしまいます。
さてさて、どうしたものかな~と。。。。
そこで活躍するのが「loaded」です!
<xp:panel>
<xc:ccFormSample02>
<xc:this.loaded><![CDATA[${javascript:param.get("paramTEST") == "01"}]]></xc:this.loaded>
</xc:ccFormSample02>
<xc:ccFormSample02>
<xc:this.loaded><![CDATA[${javascript:param.get("paramTEST") == "02"}]]></xc:this.loaded>
</xc:ccFormSample02>
</xp:panel>
loaded は ページ作成時にコントロールを作成するかどうかですので、どちらかのカスタムコントールしか作成されなくなり、IDが被ることが無く期待した動作になるということになります。
無事期待する動作となりました。
今回のパターンでは 「rendered」では都合が悪く、「loaded」を使用しましたが、「loaded」は一度「false」にしてしまうと、ページ全体をロードしなおさないと表示の切り替えができません。「rendered」に関しましては、部分更新で表示/非表示の切り替えが可能となります。
両方使える場合では「 loaded 」が「false」の場合はカスタムコントロールを一切読み込まないので、余分にメモリを消費しないことや処理速度も 「rendered」 に比べて早いのではないかと考えられます。
用途に合わせて使うのがよろしいかと思います。
似た様なことでお困りの際はお試ししてください。
ではでは本日はこの辺で失礼いたします。
Domino DesignerでXPageを開いた際に開くのが遅い時の対応
Xpagesの設計をDesignerで開いた際に遅い件
Notes/Dominoエンジニア XPages担当の米原です
設計をDesigerで開いた際におそい~と思ったことはないでしょうか??
設計が少ない場合はそうでもないのですが、
より複雑になった場合、カスタムコントロールの数が多くなっていきだんだん遅くなりますよね。
回避策としまして
- 冗長な処理をなくす
- DBを分ける
- PCのスペックを上げる。メモリ増幅 ^^;
などが考えられます。
ここでは取り上げる方法は、Desigerで設計を開いた際により早く開くことができるのと、
やりようによっては設計が見やすくもそうでなくもなります。
今回は1つのカスタムコントロールに5個のカスタムコントロールをセットしているもので試してみます。
これくらいなら特に遅くもならないのですが、実運用しているものでしたら、カスタムコントールの中に
カスタムコントロールはいっぱい入っているケースがあり、特にリピートコントロールは遅く成る要員の一つです
上図ではどのようなコントロールが配置されているとかがすぐわかるのでいいのはいいのですが
①のカスタムコントロールを開きまして、プロパティの設計定義を開きます。
そこに、XSPの形式で入力します。テスト的に下図のように設定します。
設定後に再度ccFormMain02を確認すると
test と表示されます。
ん、、、、見にくい
コメントは少し装飾しました。うんいい感じです
マークアップ式はこんな感じです↓
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xc="http://www.ibm.com/xsp/custom"
xmlns:xe="http://www.ibm.com/xsp/coreex">
<div style="border: 2px solid #A9A9A9; padding: 3px; margin: 5px;">
<h1>添付用のリッチテキスト</h1>
<div>
フォームA,Bで使用予定
</div>
</div>
</xp:view>
こんなふうに全て記入すると。。。
見やすくなりましたね、マークアップ箇所は基本のHTMLで記入するだけなのでお好みいに合わせて
カスタマイズできます。
ということで、見やすくなりました。めでたしめでたし!
あれれちょっと待った~表示が変わっただけなのでは、、、、
と思いきや、これで設計を開くときには今回のマークアップの設計定義を読み込むので
設計は格段に速く開くことが可能になります。
さすがに今回のサンプル的なのでは速さは体感できませんが、カスタムコントロールが増えて
複雑化してきたら結構ストレスがたまる位に起動が遅く成りますのでその際には体感できるかと思います。
それでは今回はここまでとします。
Domino OAuth2 Provider Beta Release by KTrick
I would like to introduce our new solution called "Domino OAuth2 Provider (DOAP)".
We developed OAuth2 provider that can run on IBM Domino server.
Our Domino OAuth2 Provider (DOAP) brings true OAuth2 capability to your IBM Domino Servers.
Since we decided to provide DOAP community edition for Non-Commercial Use, everyone can download and use it.
Please visit our DOAP website and get the DOAP installer and free license.
Once after you download our DOAP, please follow the tutorial about how to install DOAP: https://doap.ktrick.com/portfolio/tutorial/
Please try our "Domino OAuth2 Provider (DOAP)". Any feedback is really appreciated.
For more detail, you might want to check our FAQ page: https://doap.ktrick.com/faq/
Open Source Project "DOAP Sample Chat app with Domino"
To feel the power of OAuth2 capability with IBM Domino, we open-sourced node.js application called "DOAP Sample Chat app with Domino".
Please download/clone the code and check the instruction of how to run app from this link: https://doap.ktrick.com/portfolio/domino-nodejs-chat/
This sample chat application is written by Node.js, web-socket and OAuth module to communicate with Domino/DOAP.
After the user login to Domino via OAuth2, they can start chat immediately on the web based node.js application. The chat comments are also synchronized with discussion nsf in the background. To pull/push the comments from/to the documents of discussion nsf, this app uses to Domino Data Service(aka Domino REST service). Since DOAP OAuth accessToken is injected in the request body for every requests, IBM Domino authorizes to access the domino data as user permission which is configured in ACL of database.
Below is the demo movie, please turn on the caption of the movie to understand what's going on.
XPages on bluemixとIoTでBB-8を動かしてみた (2/2)
前回からの続き
こちらのブログは前回の「XPages on bluemixとIoTでBB-8を動かしてみた パート1」の続きです。
前回までで、WindowsでBLEを制御するための以下の手順がおわりました。
- (1)WindowsでBLEを制御するために
- (2)BluetoothのUUIDを取得
(3)GitHubからBB8-Bluemixプロジェクトを入手
BLE接続が出来ればNode.jsのBB-8サンプルコードがあるので、それにドローンの時のようにMQTTを組み込んでIoT対応してやればいいと考えていましたが、なんと既にBB8-Bluemixなるプロジェクトが存在することを発見。今回はありがたく使わせて頂くことにしました。
https://github.com/shamimshossain/bb8-bluemix
こちらからbb8-Bluemixプロジェクトをクローンするなりしてローカルに展開します。
次に、コマンドプロンプトを開き、プロジェクトフォルダのパスにChange Directoryした後、「npm install」を実行します。 依存関係で色々とワーニングが出ましたが自分の環境では実行自体は出来ましたのでそのまま進めます。
インストールが完了したら、プロジェクトフォルダ内にある spheroDemo.js を開き3行目の引数にパート1で取得したBluetooth UUIDを指定します。MAC OSではBLEアドレスは20桁以上であるようですが、Windowsでは12桁のIDでした。
(4)BluemixでInternet of Things Platform Starterアプリを作成
(5)IoT Foundationよりデバイスの追加を行う
- アプリケーションのステージングが完了したら、左メニューから「Internet of Things Platform」を選択します。
- デバイスの接続から「ダッシュボードを起動」をクリックします。
- IBM Watson IoT Platform画面が開いたら「デバイスの追加」をクリック
- デバイスの追加ウィザードが開くので「デバイス・タイプの作成」を選択。次へ
- 「デバイス・タイプの作成」 ‐ 「一般情報」ステップではデバイスの名前を「sphero」にして次へ
- 「デバイス・タイプの作成」 ‐ 「テンプレートの定義」ステップでは特になにも選択せず次へ
- 「デバイス・タイプの作成」 ‐ 「情報の送信」ステップでもなにもせず次へ
- 「デバイス・タイプの作成」 ‐ 「メタデータ(オプション)」ステップもなにもせず「作成」ボタンを押下
- 「デバイス・タイプの選択」でさきほど作成したデバイスタイプを選び次へ
- 「デバイス情報」ではデバイスIDを任意の名前に設定して次へ
- 「メタデータ」ではなにもせず次へ
- 「セキュリティー」もなにもせず次へ
- 「要約」で確認後、追加ボタンを押下
- デバイス資格情報が表示されるので、赤で囲んだ部分をメモしておきます。
- 次に「Internet of Things Platform Starter」のダッシュボードに戻り、apiKeyとapiTokenを確認しメモします。
(6)BB8-Bluemixプロジェクトの設定ファイルにIoT Foundation情報を設定する
(3)で展開したBB8-Bluemixのプロジェクトを開き、sphero-config.properties ファイルを開きます。
以下のようにファイルを編集します。
- deviceid: IBM Watson IoT Platform で登録した BB-8 の Device ID
- authtoken: IBM Watson IoT Platform に BB-8 を登録した際に割り当てられた認証トークン
- apikey, apitoken: IBM Watson IoT Platform の資格情報に記載されたapiKeyとapiToken
(7)MQTTクライアントからIBM IoT Fundationに接続テスト
にこちらのbb8-bluemix プロジェクトの下部に記載されているNode-Redのコードで実行テストを行います。
- bb8-bluemix プロジェクトの下部にあるNode-Redのコードをコピーします。
- Bluemixで作成した「Internet of Things Platform Starter」アプリケーションからプロジェクトURLを開きNode-Redを起動させます。
- メニューからインポートを選択しコピーしたコードを貼り付けます。
- 青色の「IBM IoT App Out」ノードをダブルクリックして以下のように設定を行います。
・Device TypeはIoT Foundationで指定したspheroを指定。
・Device IdにIoT Foundationで追加したデバイスIDを指定。 - Node-Redの「Deploy」ボタンを押し保存します。
ここまで設定が完了したら、コマンドプロンプトからspheroDemo.js を実行してBB-8と繋いでおきます。次にNode-Redから「Change color to Red」などのノードをクリックして実行しBB-8を制御できるか試します。
(8)XPages on Bluemixのアプリを作成
ここでやっとXPAGESの話になります。
- BluemixのカタログからXPAGESランタイムを選択します。
- 任意の名前、ホスト名を入力しアプリケーションを作成します。
- XPagesアプリケーションを作成後、スターターコードをダウンロードします。
(9)加速度センサーによるBB-8の制御アプリをBluemixにデプロイ
BB-8を制御するXPagesアプリケーションをこちらからダウンロードしてください。
ダウンロード: http://ktrick.com/download/xpages-bb8-tpl.zip
自身のXPAGESアプリケーションの設計をこちらのものに置換するか、XPages, スクリプトライブラリ、リソース、ファイル、スタイルシートの設計をコピーして使ってください。
次にスクリプトライブラリのsp_mqtt_bb8.js を開き以下の赤枠の値を置き換えます。
情報はNode-Redの時と同様に資格情報を参照します。
- deviceid: デバイスID
- pubTopic: デバイスタイプがspheroでない場合は書き換えます
- mqtt_host: 資格情報のmqtt_hostを指定
- mqtt_s_port: 資格情報のmqtt_s_portを指定
- org: 資格情報のorgを指定
- apiKey: 資格情報のapiKeyを指定
- apiToken: 資格情報のapiTokenを指定
設計置換後にBluemixにデプロイするとXPAGESアプリケーションのTOPページが以下のようなものに置き換わっていることが確認できます。
(10)XPAGESで動作テスト
Node-Redでテストしたときと同様にspheroDemo.jsを実行しBB-8とIoT Foundationを接続しておきます。
次にXPAGESアプリをスマートフォンで開いてください。
今回のXPAGESアプリではHTML5+Javascriptによってスマホのモーションセンサーを感知し前後、左右にBB-8を制御することが出来るようになっています。
こんな感じで操作できます。
動いた~♪
以上により、フォースはXPAGESとIoTで成り立っていたということが証明できました。
XPages on bluemixとIoTでBB-8を動かしてみた (1/2)
XPagesとIoTでドローンの次はBB-8をプログラムで制御
昨年の2015年11月18日、Notesコンソーシアム「パートナーソリューションセミナー」の1セッションをXPAGESDAYがハックし、「XPagesとIoTでドローンを飛ばそう」という試みを行いました。
今回は、ドローンの代わりにスターウォーズ最新作の映画『スター・ウォーズ/フォースの覚醒(エピソード7)』に登場したドロイド「BB-8」をXPAGESから制御してしまおうという試みです。
なお、ドローン(AR-Drone)の時のシステム構成図がこちらのようになっていました。
スライドはこちら。
ドローンの時と異なる点
ドローンの時はドローン自身が持つ専用WiFiに接続してIoTを経由してコントロールするというものでしたが、今回のBB-8ではWiFiの代わりにBluetoothを利用する点が大きく異なります。
まずはBB-8を入手
実は自分はBB-8は海外のとある方からの頂きもので偶然入手しました。 通常の遊び方は専用のスマートフォンアプリをインストールしラジコン(古い?)のようにBB-8をコントロールして遊びます。
Amazonで調べるとBB-8のオプションアイテムとしてフォースバンドなるものが2016年9月30日に発売されるようです。 これを見て真っ先に「これってIoTのセンサーデバイスとして流用できるのかな?」と考えた自分はちょっと病んでいるなと思っています。
※日本で買うと結構お高いみたいなのですが、英国のAmazonで買うと半値ぐらいなんですよね。送料込みでも十分元が取れるので興味ある方は海外のAmazonも見てみてください。
BB-8の前身はSphero?
さて、なぜBB-8がプログラムにより制御できるかと言いますと、実はSphero Japanが販売しているロボティクスボール「Sphero」というプロダクトが元となっています。
上の写真のような丸いボールのおもちゃでやはりスマートフォンから制御出来たりするようです。 そしてこの時から開発元が色々とSDKを提供しておりプログラム制御が可能な製品であったようです。 Spheroに頭の部分をつけたのがBB-8と考えるとしっくりきますが、どうやら中の構造は全く同じというわけではないようです。 そのせいか、BB-8が制御可能なSDKは発売後しばらく世に出てきていませんでした。
さて、前置きが長くなりましたが、BB-8をXPAGESとIoTを使い制御する解説を行ってまいります。
前提
- Windows OSにnode.jsをインストール済みであること
- Bluemixのアカウント作成済みであること
- CFコマンドラインインターフェースをインストール済みであること
- Bluemixコマンドラインインターフェースをインストール済みであること
- IBM Domino DesignerにBluemixツールをインストール済みであること(設定はこちら)
- BB-8
- Bluetooth 4.0 USB アダプター
システム概略
今回のbb-8の場合はドローン制御よりシンプルな構成です。
(1)WindowsでBLEを制御するために
自分の環境ではVAIOに内蔵のBluetoothデバイスをBLE接続のために利用出来なかったためBluetooth 4.0 USB アダプターを別途購入し、そのアダプターに対してZadig と呼ばれるツールでWinUSB driverの設定をするという手順が必要になりました。
こちらが購入したBluetooth 4.0 USB アダプター
Bluetooth 4.0 USB アダプターがそろえば、あとはこちらの動画の手順に従い作業を進めてください。
https://github.com/sandeepmistry/noble
※これがMacだとこの面倒な手順が必要ないんですよね。Visual Studio、Pythonをインストールしての作業とかなり面倒なのでさすがに次期開発マシンはMacにしようかと心が揺らいでしまいます。
(2)BluetoothのUUIDを取得
上記の設定が整えば、node.jsのパッケージ管理ツールである npmを使い、「cylon-ble」というパッケージをグローバルインストールします。
npm i -g cylon-ble
次に「cylon-sphero-ble」をインストールします。
npm i cylon-sphero-ble
以上の手順でようやくBB-8のBluetooth UUIDを参照するための「cylon-ble-scan」コマンド が使えるようになります。
以下はWindows コマンドプロンプトの実行例ですが、以下からBB-で始まるNameを探しその下の行のUUIDを割り出します。
パート2へ続く
長くなったので、ブログを複数回に分けることにしました。
XPAGESは今のところ全く触れられていませんがパート2で出てきますのでもう少しお付き合いください。
続きは「XPages on bluemixとIoTでBB-8を動かしてみた パート2」へ
XPages SSJSで文字列から関数を動的に呼び出す方法
文字列から動的に関数を呼び出したいケースとは?
関数を文字列から動的に関数を呼び出したいケースってどんな時でしょうか? 例えばHTML GET/POSTメソッドのパラメーター値によって呼び出す関数を変えたい場合などが想定されます。
以下の例では、「http://mydomain.com/ApplyFuncXAgent.xsp?func=callMe」というような呼び出しに対してクライアントサイドJavascript(CSjS)でコールする関数を変更するというコードになります。
これを簡略化して文字列から動的に関数を呼び出したい場合、CSJSでは以下のような書き方ができます。
windowオブジェクトに登録されている関数オブジェクトを取得し関数としてコールしています。しかしながらXPagesのサーバーサイドJavascript(SSJS)ではwindowオブジェクトがありませんので別の方法で関数を呼び出してやる必要があります。
(1)windowの代わりにthisを使用する方法
(2)eval()を利用する方法
(1),(2)どちらでも同じ結果を得ることができます。
パラメーターを渡したい場合はcall()もしくはapply()を使用
SSJSでも文字列から直接関数として呼び出す方法は分かりましたが、次はその関数に引数を与えたい場合にどうするか。この方法はCSJSと同じになります。
例えば、「http://mydomain.com/ApplyFuncXAgent.xsp?func=callMe&args=aa,bb」というようにURLパラメータに[args=aa,bb]を追加して引数も渡す方法を想定します。この場合、以下のコードのようにcall()、もしくはapply()を利用してやることでパラメータを渡すことが可能になります。
apply()の場合はcall()の時の引数をカンマ区切りで指定するのではなく、第2引数にArrayとして指定してやります。
XAgentと組み合わせたサンプル
今回自分が必要であったシチュエーションはAJAXによる非同期通信でビューの値をJSON形式で取得するための汎用的なロジックを作りたかったからでした。以下のサンプルコードはXAgentになっており関数名と引数値をURLパラメータで渡すことによって呼び出し関数を変えて目的のJSONの値を返すようになっています。
想定しているURLの呼び出しは「http://mydomain.com/hoge.nsf/ApplyFuncXAgent.xsp?func=testFunc&args=aa,bb」などです。
上記XAgentから呼び出されているcallFuncByString()を含んだxpCommon.jssのサンプルコードです
複数のビューのJSON値を非同期通信で取得する毎にXAgentを用意しなくて済むといのがこの汎用化コードの利点ですが、関数を動的に呼び出すことそのものはデバッグがしづらくなるという欠点もあるため使用には慎重になったほうがいいケースもあると思います。
※セキュリティーの観点からこのXAgentをそのまま使うとSSJSの標準関数を含む幾多の関数を呼び出すことが可能になるので危険です。実際には機能制限をするなどして意図した関数以外を呼び出されないように注意を払ってください。
IBM Connect 2016 XPAGES 最新動向 Part 3 - XPAGESトラブルシューティング
前回2回に渡りIBM Championがスピーカーを務めるセッションにフォーカスを当てたレポートブログを紹介してきました。
今回はPaul Withers氏の「"Marty, You're Just Not Thinking Fourth Dimensionally": Troubleshooting XPages」のセッションに参加した内容を振り返りたいと思います。
このセッションではStackoverflowで寄せられた質問などをもとにXPAGESでの間違ったアプローチの紹介やトラブルシューティングの方法などノウハウ情報を満載の内容となっていました。
また、このセッションではXPAGESの基礎知識を持っていることが前提になっており「XPAGESを既に使っている人は挙手してください」と質問すると会場のほぼすべての人が手を挙げていたのも印象的でした。 そして前列に座ったIBM Champion達が突っ込みを入れながらさらに話がディープに進んで行くのもIBM Connectのセッションならではの光景です。
documentidプロパティでは常に式言語(EL)を使うこと
このセッション内の数あるトピックの中でも全てのXPAGES開発者が知っておくべきノウハウであり、知らずにやってしまいがちな落とし穴であると思ったのがこの「documentidプロパティなどでは常に式言語(EL)を使う」ということでしたので、私自身でテストしたサンプルコードを交えてこのトピックを掘り下げて紹介したいと思います。
※ちなみに式言語(EL)とは${...}もしくは#{...}で始まるコードの事で#{javascript:...}ではないことと区別しています。
Stackoverflowのこちらのリンク「Why does this code write out TWO documents?」でPaul氏が自ら回答している内容にも絡んでいるのですが、Paul氏が指摘するには、式言語(EL)を使わなければページロード時にdocumentidのプロパティ計算ロジックが4回も呼ばれてしまうと指摘しています。
まずはこちらに用意したサンプルコードを見てください。
Domino文書データソースを定義する箇所でdocumentIdプロパティに対して値の計算をしています。
UNIDを引っ張ってきたいのでViewを取得して文書を取得してそのUNIDを返すというロジックになります。
如何でしょうか? 皆さんもこれに似たロジックを書いた経験はないでしょうか?
このサンプルには1つの致命的な問題と2つの推奨されないコードが含まれています。
1つ目の致命的な問題とはbeforePageLoadにてviewScopeの変数をセットしデータソース内でそのviewScope変数を利用しているという箇所です。
これはデータソースのほうが先にプロセスが走りその後にbeforePageLoadのプロセスが走るため5行目のviewScope.selectedPageが意図したとおりに設定されずサーバーエラーを起こしてしまいます。
そこでひとまず問題を解決するために以下のようにコードを書き換えます。
beforePageLoadでのviewScopeの利用をやめ、8行目にビューの値をハードコードで渡してやりました。これでとりあえずサーバーエラーは回避され、見かけ上は意図したとおりにブラウザでこのコードを表示されるでしょう。
推奨されないコード その1
しかし、このコードにはまだ推奨されないコードが含まれており、試しにブラウザでこのコードを表示してみるとAdministratorのコンソールに以下のような出力があることが確認できます。
HTTP JVM: doc found HTTP JVM: doc found HTTP JVM: doc found HTTP JVM: doc found
このように、documentIdの値の計算の処理が4回も呼び出されています。これこそが今回Paul氏が指摘する問題の箇所になります。
Paul氏によると4回も呼び出される理由は以下のようになります。
式言語(EL) 【${...}もしくは#{...}】を使わなかった場合、documentIdのようなプロパティを
- Panel内に追加した場合、beforePageLoadの後に2回呼び出される
- XPage内に追加した場合、beforePageLoadの前に2回呼び出される。(このときbeforePageLoadで追加したscope変数は使えない)
- 常にrender response時に2回呼び出される
ということで、上記サンプルの場合4回呼び出されるということになります。
そこで回避策として式言語(EL)を利用することでこの問題は回避されます。
以下に1回のみ呼び出される式言語(EL)を利用したサンプルコードを追加します。
このコードをadministratorコンソールで確認すると
HTTP JVM: doc found
のように1回しか呼び出されていないことが確認できます。
また、式言語(EL)で「動的に計算」を利用しているため、beforePageLoadで設定されたscope変数が利用できていることにも注目してください。
documentId="#{viewScope.docId}"
の箇所が
documentId="#{javascript:viewScope.docId}"
では意図した動作になりません。
推奨されないコード その2
Domino文書のデータソースを定義する際にignoreRequestParams="true"を明示的に定義してやらないとURLパラメータのdocumentIdで指定された値が常に優先されるため、このようなロジックを書くときはignoreRequestParams="true"を明示的に定義してやるべきです。
こんな事例が沢山
今回はこのブログを書くにあたってPaul氏の指摘を深堀して実証実験を行ってみましたが、実際にPaul氏のセッションではこれに似た内容がいくつもスライドとデモで紹介され、どれもがものすごいスピードで紹介されるためはっきり言ってついていくのがやっとな状態のかなりレベルの高い内容となっていました。
1年を通してStackoverflowでいくつもの質問に対して回答をしているPaul氏ならではのノウハウの詰まったセッションであっただけに、今回のセッションに参加できただけでもIBM Connectに来た甲斐があったと思える、そんなセッションでした。
なお、こちらのセッションのスライドは既に公開されており、こちらより確認できます。
http://www.idonotes.com/IdoNotes/IdoConnect2013.nsf/dx/1279a-marty-youre-just-not-thinking-fourth-dimensionally-troubleshooting-xpages-2016.htm
Chris Millerさんのサイトでその他のセッションを含めLotusphere 2013からのセッションがタイトル別にまとめられています。既に2016年のセッションもいくつか登録されているようなので確認してみてください。