まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:ibm

IBM Domino 10 の新機能の1つで、個人的にすごく楽しみにしていた domino-db パッケージを遅ればせながら使ってみました。

このパッケージについて簡単に説明します。IBM Domino サーバーのアプリケーションデータベースにアクセスする方法は何通りかありますが、一般的にはこれまで以下の3通りありました:

(1) 専用クライアント(IBM Notes)を使う
(2) サーバー側で HTTP タスクを有効にした上で、ブラウザなどの HTTP クライアントからアクセスする
(3) IBM Notes が導入されたシステムから C/C++ や Java、ActiveX でプログラミングを行ってアクセスする

(1) はごくごく一般的な、いわゆる「ノーツ」を使ってアクセスする方法で、(2) はブラウザを使ってアクセスします。この2つはプログラミングというよりは、普通に提供されている機能です。(3) はプログラミングによってアプリケーションやマクロを作成してアクセスする方法なのですが、そのための SDK やインターフェースは同システム内にノーツが導入されている前提で提供されているものでした。つまりプログラミングで IBM Domino サーバーにアクセスする場合、IBM Notes クライアントがインストールされていることが前提条件であり、IBM Notes がインストールされていない(またはインストールできない)環境からはプログラミングしてもネイティブコードが存在しないので実行できない、という制約があったのでした。


このたび IBM Domino 10.0 から提供された App Dev Pack という拡張機能によって、この制約がなくなりました。つまり、

(4) App Dev Pack で提供される domino-db パッケージを使ってプログラミングし、Node.js からアクセスする

という4つ目の選択肢が新たに与えられたことになります。なお App Dev Pack は IBM Domino 10.0.1 と同時に提供が開始されました。またこの段階では Linux 版の App Dev Pack のみが提供されています。詳しくは以下でも紹介しますが、2019/01/14 時点では Linux (CentOS7/RHEL7)版の IBM Domino 10.0.1 サーバーと、Linux 上の Node.js 環境で動作します(2019/01/18 修正 domino-db パッケージは Linux 以外でも動くようです。少なくとも Windows からは動きました)

この App Dev Pack を実際に使って試してみました。今回は以下のような2台の Linux システム間で実際に動かしています:
2019011302


【IBM Domino サーバー】
OS: CentOS 7
Domino: 10.0.1

【クライアント】
OS: Ubuntu 16.04
Notes: インストールせず
Node.js: 8.11

今回は同一ネットワーク上に上記2台の Linux マシンを構築していますが、実際にはクライアントから IBM Domino サーバーに(名前とポート番号指定だけで)アクセスすることが可能であれば、インターネット越しでも可能だと思っています。


【環境準備】
CentOS 7 上に IBM Domino 10.0.1 環境を構築します。こちらの手順はすぐ下に記載した部分以外は省略します(私がこちらを参照したわけではないのですが、詳しくはこちらに記載された方法で導入できるようです)。なお Linux 版の Domino 10.x は CentOS/RHEL 7 以上でないとインストールできません。

ただ上記リンク先に書かれていなくて、ちと注意が必要な点があるのでそこだけ追記しておきます。

後述の環境では(Proton の動作ポートが動的な設定のままでも動くように)インフラ部分のセキュリティレベルをかなり緩めに作りました。具体的には SELinux を無効にし、更にファイアウォール(firewalld)も止めている、という点をコメントしておきます。具体的なコマンドとしては IBM Domino インストール後に root でログインして以下を実行しました:
(firewalld を停止)
# systemctl stop firewalld

(firewalld の無効化)
# systemctl disable firewalld

(SELinux の設定ファイルを編集)
# vi /etc/selinux/config

(SELINUX=enforcing とあった行を以下のように更新して、保存)
SELINUX=disabled

(システム再起動)
# shutdown -r now

これでファイアウォールや実行ポート番号を意識せずに以下の記述内容が実行できる環境にしています。



そして上記の環境を使い、CentOS 7側には Proton というサーバータスクを、Ubuntu 側には domino-db パッケージをそれぞれ追加導入しています。つまりシステム的にはこんな感じで Proton と domino-db パッケージを経由して通信しています:
2019011303


以下、それぞれの追加モジュールについて紹介します。

まずクライアント側に domino-db という npm パッケージを用意します。このパッケージが IBM Domino とのアクセスを提供しており、Node.js を使ったプログラム内で IBM Domino のデータベースを読み書きすることができるようになります。なお 2019/01/14 時点で提供されている domino-db パッケージのバージョンは 1.1.0 であり、Linux 版のみが提供されているようでした。

またサーバー側にも Proton というアプリケーション(正確にはサーバータスク・アプリケーション)が必要です。この Proton が前述の domino-db を使ったアプリケーションからのリクエストやレスポンスに対応します。なお 2019/01/14 時点で提供されている Proton のバージョンは 0.2.2 であり、こちらも Linux 版のみが提供されているようでした。

これら2つのモジュールが互いに通信しあって Node.js プログラムからの IBM Domino へのリクエスト制御やデータベースの読み書きに対応します。


実際にこれらをインストールする手順を紹介します。まず Domino 10 のインストールモジュールをダウンロードした IBM サイトなどから IBM Domino AppDev Pack をダウンロードします。2019/01/15 時点でのバージョンは 1.0 で Linux English 版のみ、 DOMINO_APPDEV_PACK_1.0_LNX_EN.tgz というファイル名でした:
2019011502


このアーカイブファイルを展開すると、以下のようなファイル群が表れます。なお、今回以下で紹介するものの中で使っているのは青のファイルのみで、これらのファイルが /tmp 以下に展開されているものとします:
- LICENSE
- NOTICE
- domino-appdev-docs-site.tgz : ドキュメント
- domino-domino-db-1.1.0.tgz : domino-db パッケージ
- domino-iam-service-1.0.15.tgz : IAM 認証用モジュール
- domino-node-iam-client-1.0.22.tgz : IAM 認証用の Node.js クライアントモジュール
- node-demo.nsf : デモ用アプリケーション
- oauth-dsapi-0.2.2.tgz : OAuth 用モジュール
- proton-addin-0.2.2.tgz : Proton 一式

なお、以下で紹介する内容は domino-appdev-docs-site.tgz を展開したドキュメント内にかかれている内容をベースに独自にカスタマイズしたものです。以下ではこのファイルを参照することはありませんが、domino-db パッケージを使ってアプリケーション開発をする場合の API リファレンスなども含まれているので、こちらは必ず手元で参照できるようにしておきましょう:
2019011501


まずはサンプルで使うドミノデータベース node-demo.nsf をデータフォルダに移動させます。いったん IBM Domino サーバーをシャットダウンし、notes ユーザー(Linux 内で IBM Domino を実行するユーザー)でログインします。そして node-demo.nsf をデータフォルダ直下にコピーしておきます:
$ cd /local/notesdata

$ cp /tmp/node-demo.nsf .

$ chown notes.notes node-demo.nsf

なお、このデータベースに特別な設計が含まれているわけではありません。ただし、今回の設定では Anonymous ユーザーでこのデータベースにアクセスして文書を作成することになるので、Anonymous ユーザーで文書を読み書きができるような権限設定が必要です。他のデータベースを使う場合も同様です。

次に IBM Domino 10 サーバーに Proton を導入します。notes ユーザーで以下のコマンドを実行してファイルコピー&セットアップを行います:
Domino バイナリのあるフォルダに移動
$ cd /opt/ibm/domino/notes/latest/linux

libnotes.so が存在していることを確認
$ ls -la libnotes.so

Proton ファイルをこのフォルダ内に展開
$ sudo tar -xzvf /tmp/proton-addin-0.2.2.tgz

setup_proton.sh を使ってセットアップ
$ sudo sh -v ./setup_proton.sh

この時点で Proton を起動することは可能ですが、デフォルト状態のままだと同一システムからのリクエストのみ受け付けます。今回は外部の別マシンから Node.js アプリケーションを実行したいので、外部リクエストを受け付けるための設定を追加する必要があります。

そのため、データフォルダ(/local/notesdata/)以下の notes.ini ファイルをテキストエディタで開き、以下の1行を追加します:
PROTON_LISTEN_ADDRESS=0.0.0.0

PROTON_LISTEN_ADDRESS は Proton へのリクエストを受け付けるホストの IP アドレスです。デフォルトは 127.0.0.1 なので同一システムからのリクエストのみ受け付けます。上記の 0.0.0.0 は全ての外部ホストからのリクエストを受け付ける、という設定です。

ここで1つ注意を。実は Proton の実行ポート番号は動的に決まるのですが notes.ini に以下の一行を追加することで実行ポート番号を指定できることになっています(この例の場合は 6000 番ポート):
PROTON_LISTEN_PORT=6000

が、私が試した限りでは、この1行を追加すると Proton 自身が起動に失敗するようになってしまいました。原因はよくわからないのですが、先に進めるため、今回は上記の設定はしていません。つまり Proton は起動時に空きポートを探して動的なポートで起動されるようにしています。

ここまでの設定ができたら IBM Domino サーバーを起動し、更に起動後のサーバーコンソールに以下を実行して Proton サーバータスクを実行します:
> load proton

するとコマンドに続いて以下のような実行ログが表示されます:
> load proton

PROTON> Build 0.2.2
PROTON> Listening on 0.0.0.0:1217, INSECURE
PROTON> Note: Requested port was 0, Actual listen port is 1217
PROTON> Server initialized
PROTON> Server allows Anonymous access only.
  :

この場合の例では Proton は 1217 番ポートが空いていることを確認し、1217 番ポートでリクエストを待ち受けている状態になりました。この番号は後で使うので覚えておきましょう。

これで IBM Domino サーバー側の設定は終わりです。実際には通信に SSL を使ったり、IAM による認証や権限管理を行ったりする設定もできるようですが今回は試していません。興味ある方はドキュメントを参照して挑戦してみてください。


次にクライアント側である Ubuntu システムのセットアップに移ります。まず Ubuntu に Node.js を導入します。ドキュメントによると Node.js V8.x で動作確認を行っている模様なので、今回も Node.js V8 を導入することにします(V10.x でも動くらしい、とは書かれていました、念の為)。多くの環境で Node.js を導入するとデフォルトで V8.x になると思っていますが、6以下だったり10以上だったりする場合は nvm などを使って Node.js V8.x (と npm)が動く環境を用意してください。

そして AppDev Pack 内に含まれている domino-db パッケージを転送するなどして、Ubuntu 側にコピーしておきます(以下の紹介ではカレントフォルダ内に domino-domino-db-1.1.0.tgz が存在しているものとします)。これでアプリケーション開発の準備は完了です。

チュートリアルをベースに、以下のような Node.js プログラムコード(test01.js)を記述します:
//. test01.js

//. domino-db のロード
const { useServer } = require( '@domino/domino-db' );

//. Domino サーバー(192.168.1.100)と Proton の実行ポート(1217)を指定
const serverConfig = {
  hostName: '192.168.1.100',
  connection: {
    port:1217
  }
};

//. 対象とするデータベース
const databaseConfig = {
  filePath: 'node-demo.nsf'
};

//. データベースに新規に作成する文書の内容
const createOptions = {
  documents: [
    {
      Form: 'Contact',
      Firstname: 'Kei',
      LastName: 'Kimura',
      City: 'Funabashi',
      State: 'Chiba'
    }
  ]
};

//. domino-db を使って Proton に接続
useServer( serverConfig ).then( async server => {
  //. 接続に成功したら処理対象データベースを指定
  const database = await server.useDatabase( databaseConfig );

  //. 設定した内容で文書を作成
  const response = await database.bulkCreateDocuments( createOptions );

  //. 作成した文書の unid を取得
  const unids = response.documents.map( doc => doc['@unid'] );

  //. 作成した文書の unid をコンソールに表示して終了
  console.log( `Documents created: ${unids}` );
});

Node.js に慣れている人であればなんとな~く処理内容は理解できると思います。1点だけコメントすると、上記の青字部分で指定した内容でデータベース内に新規文書を作成します。上記例では documents は配列になっていますが、その要素は1つだけです(つまり1文書だけ作成します)。配列要素の中身は作成する文書の各フィールドとその値を指定します。つまり今回の例では以下のようなフィールドとその値を指定してノーツの文書を新規に作成することになります:
フィールド名フィールド値
FormContact
FirstNameKei
LastNameKimura
CityFunabashi
StateChiba


また documents は配列なので、ここに2つ以上のオブジェクトを指定することも可能です。その場合は2つの文書がバルクインサートで作成され、結果の unid も2つ得ることができます。 ただ今回は1文書のみ作成する前提で上記コードが記述されている点にご注意ください。

このコードを実行してみます。まず上記で作成した test01.js ファイルがあるフォルダで domino-db パッケージを npm install します。domino-db はまだ npmjs に登録されているわけではなく、あくまでインストールモジュールから導入する必要があります。したがって以下のようなコマンドでローカルファイルシステムの同一フォルダにある domino-domino-db-1.1.0.tgz を指定してインストールします:
$ npm install ./domino-domino-db-1.1.0.tgz --save

domino-db のインストールができれば test01.js を実行することができます。また実行結果には作成された文書の UNID が表示されます:
$ node test01

Documents created: 5F29E1B7FD62450649258383005083FE

実際に Domino 10 サーバー上の node-demo.nsf ファイルを開いてみると、All Names ビューから Name が Kei Kimura となった文書が追加されていることが確認できます:
2019011301


この文書を開いてみると、入力した通りの内容で文書が作成されていることが確認できます。本当に Notes/Domino の導入されていないシステムから domino-db パッケージを使ってデータベース内に文書を作成することができました:
2019011503


本当に domino-db パッケージ(と Node.js)だけでリモートの IBM Domino データベースに文書を作成することができちゃいました。今はサーバー側もクライアントも Linux 環境でないとできない(2019/01/18 修正 サーバー側が Linux 環境でないとできない)、という制約がありますが、いずれ他のシステムでもこの機能が提供される(勝手にそう思ってますが・・)と超便利だと思いました。

1点だけ、Proton の実行ポート番号を notes.ini で指定すると動かなくなる、という現象は自分だけなのでしょうか?この辺りはまだ情報があまりなく、うまい回避方法があるといちいち実行ポートをコンソールで確認する必要がなくなって便利なんだけどな・・・ 情報求む。


(この記事は IBM Cloud アドベントカレンダー 2018 に参加しています。3日目の記事です)

IBM Cloudant (Apache CouchDB) にあまり詳しくない人が他のデータベースと同じ感覚でデータを扱っている時に、特に既存データを更新している時にふと気づくことがあります。例えば以下のような現象を目の当たりにした時、何が起こっているのか正しく理解できるでしょうか?


IBM Cloudant のダッシュボード画面にアクセスし、今回は "testdb" という名称のデータベースを IBM Cloudant 上に新規に作成しました。以下の手順はすべてこのデータベースを対象に行います(CouchDB でも同様の結果になります)。作成したばかりなのでまだドキュメント数はゼロです:
2018100201


testdb データベースを選択した画面です。普通はここで testdb 内のドキュメント一覧が表示されますが、まだ1つも存在していないので "No Documents Found" と表示されています。ここでドキュメントを新規に作成するため "Create Document" ボタンをクリックします:
2018100202


新規に JSON ドキュメントを作成する画面に切り替わります。Cloudant(CouchDB) のドキュメントは "_id" というユニーク ID を含める必要があります(API 経由で _id を含めずに作ると自動的に割り振られます)。自動的に設定された "_id" 以外に "name" というキーを作り、適当な値(下図では "kkimura")を設定して "Create Document" ボタンをクリックします(JSON ドキュメントなので "_id" キーの最後にカンマをつけることを忘れずに):
2018100203


先程のドキュメントが作成され、ドキュメント一覧に1つのドキュメントが表示されるようになりました:
2018100204


ちなみに、この段階でデータベース一覧に戻ると testdb データベースのドキュメント数もゼロから 1 に変わっていることが確認できます:
2018100205


またドキュメント一覧からこのドキュメントを選択するとドキュメントの確認/編集画面になります。"_rev" という先ほど指定しなかったキーと値が追加されていますが、こちらは後で説明します:
2018100206


ここまでは特別におかしな所はないと思います。この文書を編集するあたりから Cloudant 特有のクセというか、「あれ?」と感じる所が出てくるようになってきます。

この画面から JSON ドキュメントを編集してみます。試しに "name" の値を(下図では "Kei Kimura" に)変更し、"Save Changes" ボタンをクリックします:
2018100207


変更内容が保存されて、ドキュメント一覧に戻ります。既存文書を編集して保存したので文書数は変わらずに1つのままです。ではこの文書を選択して開いてみます:
2018100208


"name" の値が "Kei Kimura" になった文書が開きました。が、よく見ると "_rev" の値が先程と異なっています。最初に作った直後は "1-" で始まる値だったのが、 "2-" で始まる値になっています。ここは変更しなかったはずなんですが・・・:
2018100209


また、このタイミングでデータベース一覧の画面に戻ると、testdb の文書数は1のままなんですが、データベースサイズが微妙に増えています。これほどの差がでるような変更をしたつもりはないのですが・・・:
2018100210


更にこの文書を開いて、再度 "name" 値を "kkimura" に変更して(元に戻して)みます。値を変更して "Save Changes" ボタンをクリックします:
2018100211


すると(中を開いて確認してもいいのですが)また "_rev" の値が変わっていることが一覧からもわかります。今度は "3-" で始まる値になっていました:
2018100212


この辺りから「???」と感じることが増えてきました。では最後にこの文書を削除してみます。一覧からチェックをつけてゴミ箱ボタンをクリックします:
2018100213


削除すると一覧からは文書は消えて、元通りの "No Documents Found" が表示されます:
2018100214


しかしデータベース一覧に戻って testdb を見ると、文書数は "0" ですが、横に!マークが付いています。また文書を削除した割にはデータベースサイズがあまり減っていないように見えます:
2018100215


この!マーク部分にマウスカーソルをあわせると、"This database has just 0 docs and 1 deleted docs" と表示されます。このメッセージの意味はいったい・・・:
2018100216


ドキュメントに勝手に "_rev"(と "_id")が付与されること、編集して保存すると "_rev" の値が勝手に変更されること、文書を削除してもデータベースサイズが減らないこと、文書を削除した時の謎のメッセージ、・・・ と、この辺りが Cloudant(CouchDB) を始めて使うと戸惑う点でしょうか? 前置きが長くなってしまいましたが、以下にこの謎を解くための説明を記載します。


上記の振る舞いを理解するには、まず自動付与される2つの値 "_id" と "_rev" の意味と役割を正しく理解する必要があります。

"_id" はいわゆる「文書 ID」です。この値はデータベース内でユニークな値をなっており、各文書を一意に取得することができるキー値となっています。正しい ID 値が与えられるだけで(他の絞り込み条件がなくても)データベース内から目的の文書を特定して取得することができます。ID 値については普通のデータベースでも扱うものなので、あまり難しくないと思っています。

一方、もうひとつの "_rev" 、こちらは IBM Cloudant(CouchDB) の特徴的な予約語となっており、「文書のリビジョン」を管理する値となっています。「リビジョン」は「バージョン」と読み替えていただいてもいいです。

上記の例だと、最初に "name" = "kkimura" という値で文書を作成しました。この時点ではこの文書のリビジョン(バージョン)は 1 で、"_rev" 値は "1-" で始まる値になっていました:
2018100204


次に同じ文書を "name" = "Kei Kimura" と変更して保存しました。この時点でこの文書のリビジョンは 2 となり、"_rev" 値も "2-" で始まる値に更新されました:
2018100208


更に同じ文書を "name" = "kkimura" に戻して保存しました。この時点でこの文書のリビジョンは 3 となり、"_rev" 値も "3-" で始まる値に更新されました:
2018100212


つまり "_rev" 値は "_id" 値で決まる文書のバージョンを管理する役割を持って自動的に更新されるシステム値ということになります。ただ Cloudant(CouchDB) でドキュメントが更新される際にはもう1つの特徴があります。

実は Cloudant(CouchDB) ではドキュメントが更新されることはほぼなく、「新しいドキュメントが新しい "_rev" 値を持って新規作成」されます。つまり厳密には同じ "_id" 値を持った複数のドキュメントがデータベース内には存在しているが、その中で最も大きな "_rev" 値を持ったドキュメントだけが有効になります。論理的にドキュメントを更新したつもりでいても、物理的には古いドキュメントは消えずに残っていて、新しいドキュメントが同じ "_id" 値&新しい "_rev" 値で作成されるのでした。なお最新でないリビジョンのドキュメントは _id 値を指定してドキュメントを取得する時に { revs_info: true } というオプションを指定することで取得することができます(このオプションをつけない限り、最新 _rev のものだけで取得できます):
http://docs.couchdb.org/en/stable/api/document/common.html


上記で Cloudant(CouchDB) のドキュメントが更新されることは「ほぼ」ないと書いたのですが、厳密にはあります。それが文書削除時です。Cloudant(CouchDB) の文書削除はいわゆる「ソフトデリート(論理削除)」であって、「ハードデリート(物理削除)」ではありません。文書に削除フラグ( { _deleted: true } )をつけて更新し、最新 "_rev" の文書が削除されているようにすることで、論理的に文書が削除されたことにしています。そしてこの論理削除を行う際には _id 値だけではなく、_rev 値と合わせて指定して、「この ID 値の、このリビジョンの文書を削除する」ことを明示的に指定する必要があります。論理的には _id 値だけで削除できそうな感覚を持ってしまいますが、その場合はまずその _id 値を持ったドキュメントの最新リビジョンを取得し、取得したドキュメントから _rev 値を取り出し、改めて _id 値と _rev 値を指定して論理削除する、という流れになります。


これらの部分を理解していると、文書を更新したり、削除した時にデータベースサイズが増える謎が理解できると思います。要は物理的に書き換えたり、物理的に削除しているわけではなく、新リビジョンのドキュメントを追加したり、削除フラグをつけたりしているだけなので、(別途物理削除するまでは)データベースサイズという観点では減ることがないのでした。








 

(この記事は IBM Cloud アドベントカレンダー 2018 に参加しています。2日目の記事です)

先日、以下のブログエントリを公開しました。本エントリはその技術的な補足と、試験的な意味も含めてアプリ URL を一般公開する内容になっています。
お絵かき LIFF アプリを作ってみた

2018112200


技術的な説明の前に、まずは一度使ってみていただきたく、このアプリを(一時的に?)公開することにしました。少し面倒ではありますが、以下の手順で実行できるようにしたので、まずは一度使ってみてください。

①スマホに LINE アプリをインストール(お絵かきアプリは PC 版 LINE からは使えません)

②LINE を開いて誰かとのメッセージ画面(グループ LINE 可)の中に以下の文字を入力してリンクメッセージとして送信する(この作業だけは PC の LINE アプリから行ってもよい)
line://app/1624220123-mbERyVgb

2018120101


③スマホの LINE 画面内から②で送信したリンクメッセージをタップ
2018120102


④LINE 内でお絵かきアプリが起動します。ペンの色と太さを変えながら指でお絵かきします。
2018120103


⑤間違えたら reset か、一度終了してから再度リンクメッセージをタップしてやり直し。
2018120104



⑥描き終わったら post でお絵かきが画像として LINE にメッセージ送信されます(右上の×を押してアプリを終了する)
2018120105


・・・というものです。LINE はスタンプ文化が有名ですが、スタンプを持っていなくてもその場でお絵かきして送るとか、他には地図を書いて送るとか、いろいろな使い方が考えられると思っています。現状、上記のリンクを LINE のメッセージとして一度送っておく必要があり、そのリンクをタップした時に起動する、という使い方になってしまいます。技術的に興味がある人もない人も是非お試しいただきたいと思っています。


以下、このアプリの技術的な解説になります。

このアプリは LINE の新しい開発フレームワークである LIFF(LIne Front-end Framework) を使って開発しています:
https://developers.line.biz/ja/docs/liff/overview/


LIFF は LINE 内で動作するウェブアプリケーションのプラットフォームです。「LINE 内で動作する」という点と「ウェブアプリケーション」であることがキーワードです。上記のように LINE 内で特定リンクをタップすることで起動し、その中身は HTML5 と JavaScript で記述されたウェブアプリケーションです。LIFF の詳しい仕様等は上記の開発者向けページを参照いただきたいのですが、要するにパブリックインターネット上に HTML5 ベースのウェブアプリを作って、そこを参照するような LINE リンクを作っておくことで LINE アプリ内でその HTML5 ページを起動することができます。また LIFF SDK の JavaScript API を使うことで、その HTML5 アプリの情報を起動元である LINE 側に(メッセージとして)送信することも可能です。

今回作ったアプリの場合は HTML5 の Canvas を使ってお絵かきアプリを作成し、その中に描かれた絵を動的に画像に変換して、画像を貼り付ける形でメッセージを送ったことにしています。その結果、HTML5 Canvas 内に描かれた絵を LINE 上で画像添付したかのように送信させることを実現しています。

この仕組の HTML5 ウェブアプリケーション部分と、画像添付部分を IBM Cloud 上の Node.js ランタイムと IBM Cloudant を使って実現しています。HTML5 Canvas 内に描いた画像を動的に PNG 画像へ変換し、Node.js ランタイム上の REST API を使って取得し、(LIFF の場合、画像はオリジナル以外にサムネイルが必要なので)サムネイル画像を生成して IBM Cloudant データベース内に添付ファイルとして保存します:

2018120106


画像が添付できたら、今度はその画像を外部から参照できる URL を用意します。具体的には Node.js ランタイム内に Cloudant 内に格納した画像ファイルを参照できるような URL(REST API) を用意します。そして HTML5 内の LIFF SDK を使って、画像ファイルの URL をメッセージとして送信して、添付画像を付与したメッセージ送信と同じ処理を行います。これでお絵かきで作成した画像が添付されたかのような振る舞いを実現できます:
2018120107


という形で現在は実現しています。 つまり現在はこのアプリを使って作成した画像は IBM Cloudant 内に保存されるように実装されています。現在の運用環境では、このデータベースの容量にも制約があるので、しばらくはデータベースをリセットしながら運用することになります。もしうまく送信できない場合は僕がデータベースのメンテナンスを忘れていて、容量オーバーになっている可能性があることをお伝えしておきます(気付いたタイミングでリセットするようにします)。

また、この公開アプリのソースコードはこちらで(MIT ライセンスで)公開しています。IBM Cloud を使う前提で記述されたコードですが、自分なりに改良したい人がいればうまく活用してください:
https://github.com/dotnsf/line_liff_doodle


これまで LINE の API といえば Messaging API で、bot のようなバックエンド側を操作するアプリを作るには有用だったのですが、やっとフロントエンド側を操作できる API(というかフレームワーク)が出てきてくれました。公開ページ上で HTML5 アプリを置いて運用することになるので、実質的にクラウドを活用する形態が多くなると思います。そんな時に IBM Cloud の上記構成程度であれば無料のライトアカウントでも運用できるので、公開したソースコードを是非色んな人に使ってみたり改良してみてほしいです。

LINE の新しい開発フレームワークである LIFF(LIne Front-end Framework)の存在を教えていただいたので、試しに使ってみました。

この LIFF 最大の特徴はスマホの LINE アプリ内で HTML5 の Web アプリケーションを動かすことができる、という点です。この Web アプリケーション内で LIFF の SDK を併用すると、アプリケーションからLINE にメッセージや画像(やスタンプ)を送ることも可能です。

この LIFF を使って指で画面にお絵かきをするような HTML5 アプリを作り、その絵を LINE アプリに送信する、というアプリケーションを作ってみました。LINE はスタンプを送信する文化がメジャーですが、その延長で自分でその場でお絵かきした画像を送る、という使い方を想定したアプリケーションです。ソースコードは github で公開したので、興味ある方は実際に試してみてください:
2018112200



なお、今回提供しているソースコードでは IBM Cloud の NoSQL マネージド・データベースである Cloudant を使っているので、IBM Cloud のアカウントも必要です。無料のライトアカウントもあるので IBM Cloud のアカウントをお持ちでない場合はあわせて取得してください。


【IBM Cloudant の準備】
今回紹介するアプリケーションでは IBM Cloundant のインスタンスが必要です。IBM Cloud にログインし、インスタンスを1つ作成して、あらかじめ画像格納用のデータベースを用意しておきます。なお無料のライトプランの場合、Cloudant には 1GB までのデータしか格納することができないという点をご了承ください。

まず IBM Cloud にログインし、「リソースの作成」から IBM Cloudant を追加します。IBM Cloudant は「データベース」カテゴリ内に存在しています:
2018112201


作成時のロケーションはどこでも構いません(下図では「シドニー」を使っています)。ただ認証方法は従来の方式が利用できる方("Use both legacy credentials and IAM")を選択しておく必要があります:
2018112202


また価格プランも任意で構いませんが、Lite プランの場合は無料です。ただし 1GB までしかデータを格納することはできません。最後に「作成」をクリックしてインスタンスを作成します:
2018112203


Cloudant インスタンス作成直後の画面です。まずここから目的の(画像格納用の)データベースを作成するため、Cloudant のダッシュボードに移動します。下図の緑のボタンをクリック:
2018112204


Cloudant のダッシュボード画面に移動しました。データベース一覧が表示されていますが、作成直後の場合は何も存在してません。ここで「Create Database」をクリックして、データベースを作成します:
2018112205


作成するデータベースの名前を指定します。デフォルトでは "doodledb" を使います(後述の settings.js 内で指定されている名称と一致している必要があります)ので、とりあえず doodledb と入力して「Create」します:
2018112206


doodledb データベースが作成され、doodledb データベース内の文書一覧画面に移動しました(当然中身はありません)。左上の "<" 印をクリックしてデータベース一覧に戻ります:
2018112207


データベース一覧に戻りました。先程とは変わって、"doodledb" が一覧に含まれているはずです。これでデータベースの準備ができました:
2018112208


次にこのデータベースへ接続するための情報を確認します。IBM Cloud の画面に戻り、「サービス資格情報」タブを選択します。サービス資格情報は(この時点では)存在していないはずなので、「新規資格情報」ボタンをクリックして作成します:
2018112201


設定項目は変更せずに「追加」します:
2018112202


資格情報が追加されると先程の画面内に「資格情報の表示」メニューが追加されているはずです。ここをクリックして内容を確認します:
2018112203


資格情報の内容が JSON テキストで表示されます。今回必要なのは "username" と "password" の値です。これらの情報を後で利用するので、メモしておくか、コピペできるようにしておきます:
2018112204


これで IBM Cloudant の準備はできました。


【ランタイム(Web アプリケーション・サーバー)の準備】
次に LIFF が参照する Web アプリケーションサーバーを作成します。このサンプルは Node.js 上で動作するサンプルですが、今回は IBM Cloud 上にアプリケーション・サーバーを作成することにします(これもライトプランの無料枠内で作成することができます)。

Cloudant の時と同様に IBM Cloud にログイン後にリソース作成で「コンピュート」カテゴリの「SDK for Node.js」を選択します:
2018112201


ロケーションは「ダラス」を選択します(するとドメインは "mybluemix.net" となります)。そして「アプリ名」にアプリケーション名称を指定します。下図の例では "linedoodle" というアプリケーション名称としており、この場合のエンドポイント URL は https://linedoodle.mybluemix.net/ となります。なおアプリ名は他で使われていないものを指定する必要があります。自分の名前や日付を指定するなどして、ユニークなアプリケーション名称を指定してください。最後に「作成」をクリックします:
2018112202


しばらく待つとアプリケーションサーバーが起動します。これで LIFF アプリからアクセスできるパブリッククラウド上にアプリケーションサーバーが用意できました:
2018112203


【LINE Developers の準備】
LINE の LIFF アプリを作成するには LINE Developers でのチャネル登録が必要です。LINE Developer において新規にチャネルを作成します:
2018112204


チャネルの種類は「Messaging API」を選択します:
2018112205


アプリ名とプラン(Developer Trial)を選択し、作成します:
2018112206


作成すると、このチャネルにアクセストークンが割り当てられます。「アクセストークン(ロングターム)」と書かれた項目の値をこの後で使うことになるのでメモするか、コピペできるようにしておきます:
2018112207


また、ここで LIFF アプリとしてのアプリ URL を作成しておきましょう。LIFF タブを選んで「追加」ボタンをクリックします:
2018112209


名称、サイズ(Full または Tall のいずれかを選択)、そしてエンドポイント URL(上述の Node.js アプリケーションのエンドポイント URL)を入力して「保存する」をクリックします:
2018112210


すると LIFF アプリが登録され、LIFF URL が確認できるようになります。この LIFF URL は実際に LINE からアプリケーションを起動する際に利用します:
2018112208


これで LIFF アプリの登録も完了しました。あと少し。


【アプリケーションの準備】
上記で作成したアプリケーション・サーバーに HTML5 アプリケーションをデプロイします。まずはソースコードを入手します。github のリポジトリからソースコードをダウンロードまたは git clone して入手します:
https://github.com/dotnsf/line_liff_doodle

ソースコードがダウンロードできたら settings.js ファイルを環境にあわせて編集して保存します:
exports.db_username = '(Cloudant の username)';
exports.db_password = '(Cloudant の password)';
exports.db_name = 'doodledb';
exports.app_port = 0;

exports.base_url = 'https://(アプリケーション名).(ドメイン)/';

exports.line_access_token = '(LINE Developer で取得したロングタームアクセストークン)';

  :
  :

最後にこのアプリを上記で作成したアプリケーションサーバーにデプロイします。IBM Cloud では cf コマンドを使ってデプロイするので、未導入の場合は cf コマンドをダウンロード&インストールしてください:
https://github.com/cloudfoundry/cli/releases


そして cf コマンドを使って IBM Cloud へ IBM ID でログインし、アプリケーションをプッシュ(デプロイ)します:
$ cd (ソースコードのあるフォルダ)
$ cf login -a https://api.ng.bluemix.net/ -u (IBM ID)
$ cf push (アプリケーション名)

ここまでの作業が全て成功すると LIFF アプリが IBM Cloud 上にデプロイされ、LINE のチャット上で LIFF URL をクリックすると、LINE 上で同アプリケーションを呼び出すことができるようになります。


【LINE で動作確認】
では実際に LINE を使って動作確認してみます。上述で取得した LIFF URL ("line://app/" で始める文字列)をコピー&ペーストするなどして LINE のメッセージとして表示させます:
2018112201


この LIFF URL 部分をタップすると画面下部から LIFF アプリである HTML5 の Web アプリケーションの画面が表れます。今回の Web アプリはお絵かきアプリとなっていて、指でキャンバス上をドラッグして絵を描くアプリとなっています。色やフォントの太さを変えたり、失敗したら "reset" ではじめからやり直すこともできます:
2018112202


たとえばこんな絵を描いてみました。書き終わったら "post" をタップします:
2018112203


描いた絵が LINE の会話内に画像として表れます。その場でステッカーを作って送るような感覚です:
2018112204


・・・というアプリを作ってみたのでした。いかがでしょう? ちなみに iPhone 版の LINE でのみ動作確認しています。Android 版 LINE でうまく動かなかったらごめんなさい。


以前から似たようなアプリを Twitter 向けに作ってみたりしていて、同じようなのを LINE 向けにも作れたらいいなあ、と思っていました。が、LINE にはクライアント側を操作できる API が提供されておらず作ることができずに諦めていました。 が、今回 LIFF の存在を教えていただいて、「これならできるかも・・」と期待してがんばってみたら、なんとか実現できた、という経緯です。アプリ実装の詳細についてはまた別の機会にでも。

ある意味で LINE のステッカービジネスを根底から否定(苦笑)するアプリなので、あまり LINE 受けはよくないかもしれません。(^^; でも教えていただいたおかげで実現することができました。この場をお借りしてお礼申し上げます。


そうそう。繰り返しになりますが、IBM Cloud の(無料の)ライトアカウントを使って作成した場合、データベースには 1GB ぶんの画像しか格納できません。定期的にデータを削除するとか、データベースを消して作り直すとかして対応してください。



(参考)
LIFF API リファレンス


IBM Watson をはじめとした IBM Cloud から提供されているサービスの認証方法に IAM が採用され始めています。以前に IBM Watson サービスで IAM を使う方法についてはこちらのブログエントリでも紹介しました:
IBM Watson のアプリケーションを IAM(API Key) 認証/認可に移行する


今回は IBM Cloudant を IAM で利用する方法を紹介します。なお 2018/11/22 時点で IBM Cloudant が IAM でないと認証できなくなるというアナウンスがされているわけではないことを書き添えておきます(つまり現時点では従来の認証方法と併用されており、すぐに移行作業が必要になるわけではありません)。


まずは IAM に対応した IBM Cloudant のサービスインスタンスを用意する必要があります。現在 IBM Cloudant のサービスインスタンスを作成すると IAM のみで利用するタイプか、従来の認証と IAM の両方が利用できるタイプかを選択して作成することができます。どちらでもいいのですが IAM 対応のインスタンスを用意します。

まず IBM Cloud のダッシュボードからリソースの追加を行い、データベースカテゴリ内の "Cloudant" を選択します:
2018110701


作成時の認証方法において "Use both legacy credentails and IAM(従来の方法と IAM)" を選択して作成します:
2018110702


これでどちらの方法でも認証できるインスタンスが作成できました:
2018110703


クレデンシャル情報を確認するため、サービス資格情報を参照します。資格情報が作成されていない場合はあらたに1つ作成します:
2018110704


作成した資格情報を選択して参照します:
2018110706


JSON テキスト内に "apikey" と書かれたキー文字列が含まれていることを確認します。この値は後ほど利用します:
2018110707


では IAM で IBM Cloudant を使う Node.js アプリケーションを作成してみます。ソースコードはこんな感じになります:
var fs = require( 'fs' );

//. https://www.npmjs.com/package/@cloudant/cloudant
var CloudantLib = require( '@cloudant/cloudant' );

var options = {
  url: "https://xxxxxx-bluemix.cloudant.com",    //. Cloudant URL
  plugins: {
    iamauth: {
      iamApiKey: "(上記 apikey 文字列)"
    }
  }
};
var cloudant = new CloudantLib( options );
var db = cloudant.db.use( "mydb" );

db.list( { include_docs: true }, function( err, body ){
  if( err ){
    console.log( JSON.stringify( err, null, 2 ) );
  }else{
    console.log( JSON.stringify( body, null, 2 ) );
  }
});

Cloudant へのアクセスには @cloudant/cloudant パッケージを利用しています。このパッケージが既に IAM 対応済みで、上記のように URL と apikey 文字列を指定することでデータベースに接続することができ、データベースインスタンス生成後は従来通りの各種関数が利用できるようになります。


このページのトップヘ