Server.appとFileMaker Server 14を共存させる

FileMaker Serverのアップデータが途中で止まる件は、こちらのページにまとめました。FileMaker Serverは、自身でhttpdコマンドのプロセスを起動するため、アップデータを適用するためにプロセスを止めた状態でないと処理しないのはいいとしても、FileMaker Server以外のサービスがhttpdを起動していても、やはりアップデートは進まず、そこで単なるシェルスクリプトによる繰り返しでhttpdのプロセスがなくなるまで待ってしまい、フリーズのようになってしまうという状況です。

OS X 10.11にServer.app Ver.5を入れた状態で、一度Webサービスを起動し、その後停止した状態を見ることができたので、記録しておきます。FileMaker Serverのプロセスを完全に落としても、以下のような2種類のhttpdのプロセスが残っています。-fオプションで指定されている設定ファイルを見れば、だいたいどんな機能のものなのかはわかります。

$ ps -ef|grep httpd
0 235 1 0 4:14PM ?? 0:00.47 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/Proxy/apache_serviceproxy.conf -E /private/var/log/apache2/service_proxy_error.log
70 358 235 0 4:14PM ?? 0:00.02 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/Proxy/apache_serviceproxy.conf -E /private/var/log/apache2/service_proxy_error.log
70 359 235 0 4:14PM ?? 0:00.06 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/Proxy/apache_serviceproxy.conf -E /private/var/log/apache2/service_proxy_error.log
70 360 235 0 4:14PM ?? 0:00.11 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/Proxy/apache_serviceproxy.conf -E /private/var/log/apache2/service_proxy_error.log
70 361 235 0 4:14PM ?? 0:00.06 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/Proxy/apache_serviceproxy.conf -E /private/var/log/apache2/service_proxy_error.log
70 362 235 0 4:14PM ?? 0:00.02 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/Proxy/apache_serviceproxy.conf -E /private/var/log/apache2/service_proxy_error.log
0 472 1 0 4:14PM ?? 0:00.35 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/apache2/services/ACSServer.conf -E /var/log/apache2/services/ACSServer_error_log
70 476 472 0 4:14PM ?? 0:00.02 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/apache2/services/ACSServer.conf -E /var/log/apache2/services/ACSServer_error_log
70 20919 235 0 5:36PM ?? 0:00.01 /usr/sbin/httpd -D FOREGROUND -f /Library/Server/Web/Config/Proxy/apache_serviceproxy.conf -E /private/var/log/apache2/service_proxy_error.log
1026 28116 27955 0 5:57PM ttys001 0:00.00 grep httpd

apache_serviceproxy.confは文字通りプロキシで、通常のWebサービスやWikiなどのサービスを受け付けてプロセスに流すような設定になっています。つまり、リバースプロキシでしょうか。

ACSServer.confの方は、いろいろ見ていると、/AccountsConfigService/api/とかいったパスが出てくるので、ACSはAccounts Config Serviceだと思われます。いろいろな設定を追いかけると、/Applications/Server.app/Contents/ServerRoot/usr/libexec/scsdのプロセスがデーモンとして起動しているようで、それに対するWebインタフェースをApacheを使って稼働させている模様です。man scsdはきちんとドキュメントが表示され、「Accounts Config Service Daemon」とタイトルにあります。細かなことはわかりませんが、Webアプリケーションとして動くサービスとなっています。

何れにしても、これらのサービスが稼働していれば、FileMaker Serverのプロセスは起動しません。httpdを利用するサービスを全てなくすには、次のようなコマンドを打ち込みました。

$ cd /Applications/Server.app/Contents/ServerRoot/System/Library/LaunchDaemons/
$ sudo launchctl unload -w com.apple.serviceproxy.plist
$ sudo launchctl unload -w com.apple.service.ACSServer.plist

Server.appはいろいろな機能がありますが、Webサービスはもちろん、事実上はWebアプリケーションであるカレンダーやWikiなどの機能を一切使わないのなら、上記のようにしてリバースプロキシとACSServerはなくてもおそらくは問題ないと思われます。Server.appを、AFP/SMBのファイルサーバーとして、そしてTime Machineのサービスとして、さらにOpen DirectoryのMasterとして使う限りでは、上記のサービスを落とした状態でも利用できています。

ただ、これらのサービスは単純に「落としても構わない」とは言い切れないですし、皆さん自身が判断してください。ともかく、Server.appとFileMaker Serverを同時に使う場合には、Server.appのWebあるいはApacheを使っている一切の機能を使わないでおくようにするしかないと思います。ちなみに、聞くところによると、FileMaker社のサポートに問い合わせると「Server.appはアンインストールしろ」と言われるらしいです。それもどうかと思います。事実上、同じ会社の製品なんですけどね。

[IM] Ver.5.3よりサポートのsourceキー

Ver.5.3より、コンテキスト定義(定義ファイルのIM_Entry関数の第一引数の配列の要素)に、sourceキーを入れるようになっています。データベース上のエンティティ名に関連するキーとしては、name、view、tableがありますが、それにsourceが加わります。データベースから読み出しを行うときのFROM句に指定されるビューやテーブルの名前は、viewキーがあればその値、なければnameキーの値が使われます。CREATE/UPDATE/DELETコマンドの対象は、tableキーの値で、それがなければnameキーが使わます。例えば、nameキーにテーブル名があれば、そのテーブル名を指定しての読み書きはviewとtableキーの指定はしなくてもできます。しかしながら、表示は何かのビューを使い、更新はそのビューではない特定のテーブルを指定したいような場合に対処できるように、viewとtableキーを用意しました。

しかし、これだけでは問題があります。あるテーブルのあるレコードのあるフィールドが、ページ上の複数のリンクノードに展開された場合、それが例えば両方ともテキストフィールドであれば、一方の値を変えると、フィールド更新のためのサーバー通信が行われるとともに、クライアントサイドで同一フィールドの別リンクノードの値を更新できるようになっています。その時、何を手掛かりにして反映させるコンテキストを探しているかといえば、「viewないしはnameキーの値が同じだが異なるコンテキスト」です。例えば、住所録的なpeopleテーブルがあり、同一ページに全レコードの一覧と、その中で男性の一覧の両方があったとします。前者のコンテキストは、name=allmembers, view=people、後者のコンテキストはname=malemembers, view=peopleとして適切なqueryキーの条件を与えればいいでしょう。こうすれば、同一テーブルから異なるコンテキストを生成して、内容が異なる一覧を1ページ内で生成できます。

この時、男性の一覧にあるレコードは、必ず全員のレコードの一覧にもあります。どちらもviewキーがpeopleなので、男性のレコードの1つのフィールドを変更すると、対応する全員の一覧にあるレコードのフィールド値も更新されます。以下、この動作は「連動」と呼びます。同一のviewキーの値を持つコンテキスト同士なので、INTER-Mediatorにとっての手がかりがあります。

しかしながら、SQLのレベルで、peopleをもとにしたビューeveryoneとsomeoneがあったとします。それらで表を作るとしたら、nameやviewを使うにしても、everyoneとsomeoneがそれぞれのコンテキストに登場はしますが、コンテキストの情報から各々が同一のpeopleテーブルから導出されていることはわかりません。そうなると、同一のレコードがそれぞれの一覧に見えていて、一方の値を変更したとしても、その変更結果を伝達する手がかりがなく、連動はできません。

このような時には、一方のコンテキストを、name=everyone, source=people、もう一方はname=someone, source=peopleと定義します。ページファイル側は、everyoneあるいはsomeoneをdata-im属性に利用する点は変わりありません。このsourceの設定により、2つのコンテキスト定義から得られるコンテキストは、同一のテーブルから来ているものということがINTER-Mediatorに伝わるので、同一フィールド値がユーザーインタフェース上で連動するようになります。

FileMaker Serverのアンインストール

FileMaker Serverをどうしてもアンインストールしたい場合があります。インストーラが「先にアンインストールしないといけません」とメッセージを表示して、それ以上進めないとなると、仕方ありません。インストーラのExtraフォルダに前のバージョンのアンインストーラがありますが、こんな場所にも、現在インストールされているFileMaker Serverのアンインストーラがあることを知っていれば、便利かもしれません。

shot3216

ディスクのルートから、ライブラリ>Application Support>FileMaker>FileMaker Serverという場所にも、アンインストーラがあります。ご参考までに。

FileMaker Serverのアップデータがフリーズする理由

FileMaker Server 14.0.4bのアップデータがリリースされました。このところ、アップデートが頻繁にありますが、いざアップデートしようとすると、最初の部分でフリーズしてしまうことがあります。フリーズと言っても、マウスカーソルがレインボーになってしまう現象です(画面ショットではレインボー状態は撮影できないので、以下の図は普通のポインタになっています)。

shot3215

ちなみに、この現象は、14.0.4bのアップデータに限らず、13のアップデータでも発生することがあります。もちろん、いつまで経ってもこのまま進みません。さて、なんででしょうか? アクティビティモニタを見ると、パッチアプリケーションのCPU稼働率が100%近くになっています。ということは、本当にフリーズか、適切でない処理待ちをしていることが考えられます。

そこで、ターミナルを起動して、プロセスを見てみます。パッチアプリケーションのプロセスには「Patch」という特徴的な文字があります。以下のコマンドで、プロセスIDは27530だとわかります。(以下、コマンドラインの出力が長いので折り返して見えるようにしておきますが、見づらい点はご容赦ください。)

$ ps jax|grep Patch
root 27530 1 27523 0 0 S ?? 0:15.60 /Volumes/FileMaker Server 14.0.4b Patch 1/FileMaker Server 14.0.4b Patch.app/Contents/MacOS/FileMaker Server 14.0.4b Patch

次に、このプロセスが別のプロセスを起動していないかをチェックします。すると、以下のように、「/bin/sh /Users/msyk/Library/Application Support/1358455 0」なるプロセスがあります。最後の方の数値は起動ごとに違うものがつけられ、正常終了すると消されているファイルです。

$ ps jax|grep 27530
root 27530 1 27523 0 0 S ?? 0:15.60 /Volumes/FileMaker Server 14.0.4b Patch 1/FileMaker Server 14.0.4b Patch.app/Contents/MacOS/FileMaker Server 14.0.4b Patch
root 27545 27530 27523 0 0 S ?? 0:00.05 /bin/sh /Users/msyk/Library/Application Support/1358455 0

これは/bin/shつまり引数をシェルスクリプトとして実行しているということです。catコマンドで、ファイルパスにスペースがあるのでダブルクォートで囲むなどの対処をして、中身を読んでみます。パッチアプリケーションは、このプロセスが終了するのを、おそらく単なるループで待っているためにフリーズするのでしょう。psコマンドである種のプロセスが存在するかどうかを2秒ごとにチェックし、調べているプロセスがなくなれば、スクリプトを終了します。

$ cat “/Users/msyk/Library/Application Support/1358455”
#!/bin/sh

cd /Library/Application\ Support/FileMaker/FileMaker\ Server
./launcher -stop

while [ “`ps auwwx | grep -e fmserver_helperd -e fmsib -e fmslogtrimmer -e fmxdbc_listener -e fmsased -e httpd | grep -v grep`” ] ; do
sleep 2
done

whileの行を見ると、psコマンドの結果に、fmserver_helperd、fmsib、fmslogtrimmer、fmxdbc_listener、fmsasedという文字列があるかを調べています。つまり、FileMaker Serverの様々なプロセスが起動した状態なのかを調べています。さらに、httpdというプロセスまで存在するかを調べています。アップデートは、FileMaker Serverを停止させて行うようにということで、FileMaker関連のプロセスがなくなっているという条件はまあいいでしょう。ちなみに、FileMaker Serverのプロセスを事前に全部止めておくには、以下のコマンドでできます。

sudo launchctl unload /Library/LaunchDaemons/com.filemaker.fms.plist

しかし、httpdとなると、何らかのきっかけで動かしてしまっているかもしれません。あ!そういえば、Server.appで色々試したぞと思ってチェックすると、Server.appのソフトウエアアップデート、キャッシュ、Xcode Serverあたりがhttpを使っていました。しかし、Server.appは、それらのプロセスをオフにしても、httpdの稼働を止めません。仕方ないので、sudo launchctl unload <plistファイル>で順次落として、やっとFileMaker Serverのアップデータが動くようになりました。ちなみに、こうしたデーモンは、launchctlで起動しているので、killしてもすぐに再起動されます。そこで、/System/Library/LaunchDaemonsや、/Library/LaunchDaemonsに該当するファイルを探すわけですが、Server.appについては、/Applications/Server.app/Contents/ServerRoot以下の、/System/Library/LaunchDaemonsや、/Library/LaunchDaemonsにあるplistファイルを利用してデーモンを起動しています。これらのファイルから該当するhttpdを起動しているものを探して落とさないといけません。チェックすべきplistファイルのある場所は文中に記載しましたが、見づらいでしょうから以下に列挙します。

/System/Library/LaunchDaemons
/Library/LaunchDaemons
/Applications/Server.app/Contents/ServerRoot/System/Library/LaunchDaemons
/Applications/Server.app/Contents/ServerRoot/Library/LaunchDaemons

しかし、一番の問題は、「httpdが起動していたらアップデートをしない」という仕様です。つまり、結果的にFileMaker Server以外のhttpdの起動を許さないという仕様に問題があります。Server.app自体が色々なサービスでhttpdを使うのですが、80番ポートの取り合いになるのは確かにまずいでしょう。しかしながら、FileMaker Serverのインストーラーは、自分自身のhttpdのポート番号を指定することで、他のサービスとの同居の可能性を探っています。しかし、このスクリプトを見る限りは、「FileMaker Serverが起動したhttpd」ではなく、単に「名前がhttpd」のプロセスを探しているにすぎません。つまり、FileMaker Server以外でhttpdを使いっていればトラブルが発生する仕様なのです。これでは仕様の上で矛盾があると言えるでしょう。

もちろん、特定のサーバーではFileMaker Serverしか動かさないという前提でもそれはそれで構わないと思いますが、だったら、LinuxのVMで動かすようにして欲しいというのが総意ではないでしょうか。OS XとWindowsで、アプリケーションみたいなインストーラでセットアップするサーバーはもはや古さを感じます。現在のシステム構築環境にマッチしない仕様を早く改善しないと、先頭集団からさらに引き離されるでしょう。

Xcode 7.3で出るSwitf 3でなくなる機能

iPhone SEがラインナップに加わり、Xcode 7.3がリリースされました。Swift 2.2になったのですが、Swift 3でなくなる機能が、deprecatedとして出てくるようになりました。以前のように、いきなりコードに赤いエラーが出て動かなくなるより、はるかに気持ちの良い対応ですね。手元のいくつかのプロジェクトを早速開いてみました。

まずは、x++やx–演算子の廃止です。こちらに詳細がありますが、まとめると初心者にとってわかりにくく、トリッキーなプログラムになりがちということで、廃止すべきという意見が通ったということです。x++と++xの違いとか、C言語のクイズとしては面白いかもしれませんが、読みやすいコードにするには、解釈しづらい言語仕様の排除というところでしょうか。x++は、x += 1 に変換しろということになっていますが、++の前後の位置を利用している場合や、x++の返り値を利用している場合には、単に置き換えで済まないかもしれません。

shot3198

__FUNCTION__が使えなくなり、#functionに置き換えるように出てきています。プロポーザルはこちらですが、アンダーラインを使うC言語由来の方法ではなく、#をコンパイラによる置き換えを記述できるようにするということでの変化ということになります。

shot3199

タイマー等で使っていたセレクタ型はSelector(“メソッド名:”)みたいに書いていましたが、#selector(クラス名.メソッド名(引数))のように記述するようになります。メソッド名を文字列で記述するとなると、間違ったメソッド名かどうかの判断ができないので、記述を見直すのは確かに必要です。こちらにプロポーザルがあります。

shot3200

オープンソースとなったSwiftですが、早速多数のプロポーザルが行われ、それが言語仕様となり、Xcodeに反映されるというダイナミズムがこのようにすでに動き始めています。Swiftは過去に例を見ない速度で進化し始めているといえるでしょう。

FileMakerのWebビューアが、WebDirectで文字化け

最近ブログ更新していないので、小さなネタですが書いておきます。私がWebDirectの話をするのは予想外かもしれませんが、ちょっとハマったことがあったので書いておきます。FileMakerでのソリューション内で、複数の文書を並べるような機能が必要だったので、Webビューアを使いました。全文字を平面上に成り行きでレイアウト出来るので便利です。Webビューアには、dataスキームを利用して、フィールドの値などを合成したHTMLを与えてWebビューアに表示しました。FileMaker Pro上ではもちろん問題ありません。しかし、WebDirectでWebビューア部分が文字化けしました。理由はフォントではなく、文字セット指定がなかったからです。そこで、

data:text/html;charset=utf8,<!DOCTYPE html><html><head>
<meta http-equiv='X-UA-Compatible' content='IE=Edge,chrome=1'/>....

のように、charsetを入れればブラウザ上では文字化けは無くなりました。charsetを省略すると、us-asciiと同じになるそうですが、それはブラウザ上だからかと思われます。FileMaker Proでは指定なしでも文字化けないようにしているということでしょうね。

FileMakerのWebビューアは本当に「IE」なのか?

追加情報:社本さん、ありがとうございます。解決できました。ヘッダに「<meta http-equiv=’X-UA-Compatible’ content=’IE=Edge,chrome=1’/>」というタグを入れれば、Webビューア上でもflexの機能が動き出します。ということは、MS EdgeはこのmetaタグなくてもEdge動作をするのだけど、FileMakerはmetaタグがないとIE9互換動作をするという罠ってことですね。(以下の原稿公開後にこのパラグラフを追加しました)

最近、CSSのflexを理解したので、FileMakerのWebビューアでも使おうとしたら、思いがけない落とし穴がありました。Macは問題なくレイアウトされますが、Windows 10 + FileMaker 14.0.4では次の図のように、flexが機能しません。「ある記事」と「塚口…」の部分は、それぞれDIVタグ要素になっており、その2つのDIVをさらにまとめたDIVがあって、そこにdisplay: flexを記述してあります。上半分は、FileMaker 14のWebビューア、下半分はMicrosoft Edgeでの表示結果です。同じソースなのですが、このように、Edgeでは正しくflexの動作をしてDIV要素を横方向に並べますが、Webビューアでは、flex動作をしないで、単にDIVとして表示します。

スクリーンショット 2015-12-16 16.00.30

FileMakerのWebビューアコンポーネントは、Internet Explorerのものと言われていますが、もしかして、FlexboxはVer.10から対応なので、Ver.9相当のコードで構築されたコンポーネントが入っているということでしょうか?何れにしても、IE/Edgeのバージョンとは連動しないということかと思われます。

HTMLのソースは、IEの画面のURLから取得できるはずですが、貼り付けておきます。

<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<style>
.titlearea{font-size: 12pt; display: flex; display: -ms-flexbox;align-items: baseline; margin: 3px 0 3px 0;}
.title{font-weight: bold; flex: 10 0 auto;-ms-flex: 10;}
.credit{font-size: 10pt; flex: 1 1 auto;-ms-flex: 1;}
.content{font-size: 10pt; border-bottom: 1px dotted black;}
BODY{border: none;}
DIV{font-family: Arial, Roboto, “Droid Sans”, “游ゴシック”, YuGothic, “ヒラギノ角ゴ ProN W3”, “Hiragino Kaku Gothic ProN”, “メイリオ”, Meiryo, sans-serif;}
</style>
</head>
<body>
  <div class='titlearea'>
    <div class='title'>ある記事</div>
    <div class='credit'>塚口 三春[2015/12/15]</div>
  </div>
  <div class='content'>いいことばかりです。いいことばかりです。…いいことばかりです。いいことばかりです。いいことばかりです。いいことばかりです。いいことばかりです。</div>
</div></body></html>

[IM]リピーター判別の汎用化

INTER-Mediatorに関して、1つ重要な仕様変更を考えています。エンクロージャー、リピーターの識別に関することです。現状は次のようなルールになっています。

(1)ターゲット指定(data-im属性)のあるノードを含む、TR、OPTION、LIタグ要素は自動的にリピーターとなる
(2)自動的にリピーターとなったノードの親ノードが自動的にエンクロージャーとなる
(3)DIV、SPANに限り、data-im-control属性を指定して、リピーターとエンクロージャーを明示的に指定できる

この3つ目のルールを廃止して、次のようなルールにしようかと思っています。ようするに汎用化です。(1)(2)のルールはそのまま継続させます。結果的に、(2)は(3′)に対しても適用されることになります。

(3′)data-im-control属性の値が「repeater」の場合、そのノード及び兄弟ノードのリピーターを集めてレコードの数だけ繰り返す。
(2′)repater群の1つ上位のノードは自動的にエンクロジャーであると識別する。

OL/ULだけでなく、DLなどのタグや、その他、SECTIONタグなど、エンクロージャー/リピーターとして使用したいタグがある点は、以前から指摘されています。SAPN/DIVが対応していれば、「なんとかなる」とは言え、時に不便なこともあります。結果的に、なんでもリピーターにしていいのではないかというのが考えた結論です。そうすれば、DIV内部にINPUTタグのリピートみたいなこともできます。

FileMakerのスクリプトで実現するインジェクションもしくはアスペクト指向

アスペクト指向プログラミング(AOP)はオブジェクト指向の世界での手法の1つですが、FileMakerでのスクリプトでそれを実現する方法が見つかったので、記事で残しておきます。AOPについては後ろの方でまとめて、まず、何を実現したかを具体的に説明しましょう。

マスターテーブルからの選択結果を記録する

データベースに次のようなリレーションシップを定義しています。フィールドmemoとtermはテキストで、後のフィールドは数値型です。_idの付くフィールドは連番数値を自動入力して、主キーフィールドととします。

shot3094

あるレイアウト(User Inteface)は、テーブルオカレンスのUI_Mainのレコードを表示させるように定義しました。そして、リレーションシップの先の2つのテーブルオカレンスを、3つのポータルに表示しています。このデカルト積を使うリレーションシップにより、Selectionsテーブルのすべてのレコードがポータルに表示されます。FileMaker 13からサポートされたポップオーバーボタンで選択入力をするときに、このようなリレーションシップを利用するのが1つの典型的なパターンではないでしょうか? ポータルには3つのボタンがありますが、それぞれ概ね同じ目的です。例えば、最初のポータルでは、ポータル内でのボタンを押したレコードに対応するselection_idフィールドの値が、select1フィールドに入力されます。その動作を3通り考えることで、AOPに迫るというのが概要です。当然、実際のソリューションでは主キー値となる連番はユーザーに見せないように作りますが、ここでは動作が一目瞭然となるように意識的に通常のソリューションでは見せない内容をレイアウトに表示して動作をチェックします。Memoフィールドの左側のClearボタンにより、Memoフィールドの文字列を消去します。

shot3123

ここでSelectAボタンがどうなっているのかを説明しましょう。SelectAボタンは一番簡単なというか、理解しやすい方法です。以下のように、ボタンをクリックすると、「Select Button 1」というスクリプトが実行されます。2つ目のポータルのボタンは「Select Button 2」、3つ目のポータルでは「Select Button 3」というスクリプトが動くように指定をします。ボタン名の番号と、スクリプト名の番号は対応していません。ちょっとわかりにくいことをやってしまってごめんなさい。

shot3124

3つのスクリプトは以下のようになります。つまり、ポータル内で選択したレコードのselection_idの値を、UI_Mainテーブルオカレンスのselect*フィールドに代入をしています。それぞれ、自分自身のポータルと、対応するUI_Mailのフィールドがスクリプトの設定に見えています。

shot3129

shot3130

shot3131

スクリプトの共通化

このような「同じようなスクリプト」は、共通化したいですね。昔のFileMakerなら、共通化は難しい上に、個別に記述できるから分かりやすいといった議論もあったわけですが、今時の複雑なソリューションでは、スクリプトの数が爆発する傾向にあり、1つのスクリプトにまとめる意義は高まっていると言えるでしょう。そういうわけで、「SelectB」ボタンでは、SelectAボタンの手法を1つのスクリプトにまとめてみました。3つのポータルにある各SelectBボタンは、「Select Button B」という共通のスクリプトを呼び出しています。

shot3125

この時、最初のポータルのSelectBボタンは、スクリプト引数として、次のような設定を行います。つまり、値を入力するフィールド名と、値を取り出すフィールド名を、改行で区切って指定をしておきます。それぞれのポータルのSelectBボタンは同一のスクリプトを呼び出すものの、スクリプト引数を異なるものにするということです。

shot3127

Select Button Bスクリプトは、次のようなものです。引数から値を取り出したものは、フィールド名の文字列です。その文字列のフィールド名で指定したフィールドに値を設定するには「フィールドを名前で設定」スクリプトを利用します。そして、文字列で与えたフィールドから値を取り出すのは、GetField関数を使います。引数を変数に入れると同時に、スクリプトステップは異なるものとなり、関数の利用も行わないといけません。

shot3132

共通化したことによるデメリット

ここまでのような単純なスクリプトで済ませられるのであれば、それはそれでいいのですが、実際の開発では複雑な要求が発生します。ここまでは、選択ボタンは同じ動作をするという前提でしたが、現実にはそういうことはあまりありません。ボタンごとに固有の要求が出てくることがあります。具体例としては次のようなことです。

  • あるポータルでのボタン選択をするときにだけ、別の選択肢は空欄にしたり、場合によっては初期値的な設定を強制的に行う必要がある
  • ある選択の場合に、ある条件が成り立つ場合にだけ、選択していいかどうかをダイアログで問い合わせる
  • ある選択肢の選択後に、テーブルの更新など、その選択肢に固有の処理を追加しないと要求が実現できない

こうなってくると、「そら見たことか、スクリプトはボタンごとに設定するのが王道だ」と思わず口走ってしまうかもしれませんが、未だに1ファイルに1データベースみたいな作り方をする発想が抜けきれないとしか言いようがありません。ここの例では、共通なステップは1つだけですが、複雑な処理は共通化しておかないとデバッグやメンテナンスコストが増大するのは言うまでもなく、システム構築の常識です。

もちろん、FileMakerでは、プログラミング言語的な柔軟性を犠牲にする分、ツールを使った開発がやりやすいとか、学習コストが低いという長所を得ています。しかし、なんとかならないかとも考えるわけです。

以前だと、「共通部分があるものの、違う部分もある」という場合にどういった手法をやっていたかといえば、まずは、スクリプトが分かれてしまうのは仕方ないとして、共通部分を「スクリプトの実行」ステップで呼び出すということです。もちろん、この方法は汎用的かつ共通部分の抽出ができるという面ではメリットですが、「違う部分」が発生するごとにスクリプトが増えることになります。また、ありがちですが、「違う部分がない」にもかかわらずスクリプトが別々とか、「違う部分がある」のにシステムの全然違う箇所からスクリプトを共通に使うなどの、後からの不具合発見を阻害する作りに往往にしてなりがちです。

1つの苦肉の索は、共通のスクリプト内に、IF〜ELSE IF〜END IFを利用して、例えば、押したボタンごとに分岐を作り、ボタンごとに違う処理を組み込む方法です。これだと、1つのスクリプトで共通に使えますが、一方で、ボタンが増えればIFの分岐が増えてしまいます。もちろん、ちゃんと作れば動作しますが、あるボタンの動作を追う時には無視するスクリプトが大量になってしまうなど、可読性が低くなります。また、自分が関係してるところだけをちゃんと書けばいいということは理屈の上では成り立つことですが、現実には変数を共通に使ったことでのバグの発生などが懸念されます。分離されていないことでの気づかない悪影響は、当然ながら気づくののコストがかかります。したがって、IFで分岐を入れるのはいいような悪いようなということになります。

共通スクリプトと個別スクリプトの分離

そこで、オブジェクト指向の世界でのDI(Dependency Injection)の手法により、関心ごとを分離して実装することができないかと考えました。例えば、ボタンごとにニーズが違うのであれば、ボタンごとに関心ごとが異なると位置づければ、これはAOPの実現でもあります。

まず、レイアウト上にある「SelectC」ボタンは、いずれも、「Select Button C」スクリプトを呼び出すようにしています。3つのポータル上のいずれも同様な設定になっています。

shot3126

以下のスクリプト指定は、1つ目のポータルのSelectCボタンのものです。スクリプト引数はSelectBボタンと同様な指定です。スクリプトは同一でも、ポータルごとに、設定するフィールドやポータルが参照するテーブルオカレンスが違うので、それらをスクリプト引数で指定をします。

shot3128

そして、「Select Button C」スクリプトは次のようなものです。コメントの「前処理スクリプトの呼び出し」部分が追加されています。「URLを開く」ステップと併せて示します。

shot3133

shot3108

ここで、このボタンをクリックしたスクリプトの関心ごとを、選択した結果を保存するフィールドで区別するということを考えます。この前提が上記のスクリプトの動作に必要となります。

ここで、例えば、1つ目のSelect3ボタンをクリックしたとします。すると、「Select Button C」スクリプトの最初の2つのステップで、次のように変数が設定されます。

  • $destField ← UI_Main::select1
  • $srcField ← UI_First_Selections::selection_id

そして、「URLを開く」オプションで、次の方なURLが生成されて、そのURLを開こうとします。FileMakerのURLスキーマは、ご存知ない場合には、どこかのサイト等で調べてください。

fmp://$/AOScriptTest?script=SelectButton_UI_Main_select1&param=any_parameters&$srcField=UI_First_Selections::selection_id&$destField=UI_Main::select1

最初の$で自分自身を参照します。URLのscriptパラメータは、その直前のscriptName変数の計算式をみてください。スクリプト名は、「SelectButton_<値を設定するフィールド名>」となります。値を設定するフィールド名は、ボタンのスクリプト呼び出しのスクリプト引数で指定されています。なお、コロンがフィールド名に含まれますが、無難にするためにアンダーラインに置き換えます。つまり、1つ目のポータルのSelectCボタンをクリックすると、自分自身の「SelectButton_UI_Main_select1」スクリプトを呼び出すということになります。ここで、「スクリプトの実行」ステップが使えない理由は、そのステップではスクリプトの選択はできても、変数等でのスクリプト名の指定ができないからです。なので、ここではURLスキームの利用を行います。「SelectButton_UI_Main_select1」スクリプトは例えば、以下のようなものを作成しました。select2とselect3フィールドを空白にします。ここで、レイアウトを指定する必要がない点は注目点の1つです。

shot3114

2つ目のポータルのSelectCボタンを押した時に呼び出されるURLでは、スクリプト名は「SelectButton_UI_Main_select2」となりますが、このためのスクリプトは作っていません。つまり、2つ目のポータルのSelect3ボタンをクリックした時、「URLを開く」では存在しないスクリプトをパラメーターに指定します。しかしながら、エラーとして出ないように「エラー処理」ステップを入れているので、要するに、存在しないスクリプトを実行しようとしても、スルーするだけです。

「SelectButton_UI_Main_select3」スクリプトは次のようなものです。SelectButton_UI_Main_select1スクリプトとSelectButton_UI_Main_select3スクリプトの最初の1行は同じで、パラメーターに指定した内容をmemoフィールドに入力することです。式が途中で切れていますが、結果を見れば明白だと思うので、式はここには記述していません。

shot3134

選択した時の動作を確認する

実際に動かしてみます。まず、Clearボタンを押してmemoフィールドを空白にして、3つ目のポータルのSelectCボタンをクリックしてください。すると、選択結果がselect3フィールドに入り、memoには何かの表示が見えています。つまり、Select Button Cスクリプトがボタンクリックにより呼び出され、その中で、SelectButton_UI_Main_select3スクリプトが呼び出されたということです。

shot3136

Clearボタンを押してmemoフィールドを空白にして、2つ目のポータルのSelectCボタンをクリックしてください。すると、選択結果がselect2フィールドに入りますが、memoは空白のままです。つまり、Select Button Cスクリプトがボタンクリックにより呼び出されただけの結果になります。

shot3137

Clearボタンを押してmemoフィールドを空白にして、1つ目のポータルのSelectCボタンをクリックしてください。すると、選択結果がselect1フィールドに入り、select2とselect3は空白となり、memoには何かの表示が見えています。つまり、Select Button Cスクリプトがボタンクリックにより呼び出され、その中で、SelectButton_UI_Main_select1スクリプトが呼び出されたということです。

shot3138

Select Button Cスクリプトでは、IF〜Else IFを使うことなく、クリックしたボタンに応じて処理を分岐させています。ここで、もし、新しくselect4フィールドを用意して、同様にポータルから選択できるようにしたとします。もちろん、ボタン等にSelect Button Cスクリプトを設定して、引数にフィールド名を指定します。そして、select4フィールドに特有の処理を、「SelectButton_UI_Main_select4」を記述して追加します。ここに、選択肢を設定する前に行いたい処理を記述します。こうして、フィールドが増えてスクリプトの利用元が増えたとしても、Select Button Cスクリプト自体に一切の変更を加えることなく、select4フィールド特有の処理を追加できることになります。実装のパタンとしては、GoFのパターンのStateに近いことをやっていると考えていいかと思います。

実開発への展開に向けて

まず、FileMaker的な考慮点を考えましょう。ポイントとなるのは、fmp://$/… スキームにより、変数で与えたスクリプト名で実行できる点です。この時、本当に通信するのではなく、同一のスクリプト実行環境で実行を行っているようです。その証拠として、「URLを開く」ステップの先で、「全スクリプトの終了」ステップを実行すると、呼び出した元のスクリプトも途中で中止してしまいます。また、このことは、「URLを開く」ステップの先で呼び出したスクリプトで、状況を判定して、これ以上の処理はキャンセルするという動作を組み込むことができることも言えるのです。また、グローバル変数を「URLを開く」ステップの先で設定して、それを呼び出し元でも参照できます。つまり、同一のグローバル変数が使えるので、「URLを開く」ステップは同一のユーザーセッション上で稼働していると言えるのです。この辺りの検証スクリプトの残骸が、コメントで残っているので、参考にしてください。

ここのスクリプトでは、前処理だけを実装しましたが、後処理、あるいはある特定の「中処理」を必要に応じて組み込むことも可能です。これは、呼び出すスクリプトの性質上、自由に決めることができるでしょう。一方、FileMakerの場合、その「仕様記述」に困難さんがある点も見逃せません。コメントに書くというのは曖昧かつ、記述者により違いや間違いの混入など、問題点は多いでしょう。開発のマネージメント上、状況に応じたベターな方法を見つける必要があります。

アスペクト指向スクリプトプログラミング

アスペクト指向とは、オブジェクト指向でのクラスの考え方では解決しにくい問題を扱います。クラスは単一の仕組みを持つのが理想ではありますが、いろいろな機能を組み込んだ結果、幾つかのクラスに共通の処理を実装してしまったというようなことがあるかもしれません。機能の実装においては、ある基準でまとめた結果、別の基準で見た時にはまとまってないということがあります。よくあるのがログ記録機能です。いくつかのクラスの実装ではログの記録があるものの、そのログの処理はクラスが違っても共通のことです。そうであれば、ログの記録ということ自体をその他のクラスとは別の独立したことと考える方が、明確にその機能だけに集中して検討が可能です。つまり、物事のある側面(Aspect)に注目すると、その側面は必ずしもクラスとしての分解に馴染まないことが出てきます。しかしながら、側面として検討した昨日をうまく実装する方法が必要になります。それがアスペクト指向プログラミングです。

一方で、アスペクト指向はオブジェクト指向を否定するものではなく、オブジェクト指向プログラミングの拡張の1つとして捉えるべきです。ただし、オブジェクト指向ではないプログラミングでも適用できる手法です。その代表的な実装方法がDIであり、DI(Dependency Injection)はプログラムのあるポイントに別のプログラムを挿入することです。ただし、オブジェクト指向の世界においては、インタフェースや抽象化クラスなどのいろいろなテクニックを使って、実装上の振る舞いや制約をきちんとしたルールに落とし込んでいると言えます。ある側面を定義したものをDIによって別のクラスに埋め込みをするということでの、アスペクト指向の実現できているということになります。アスペクト指向とDIは同一のものではありませんが、結果的には表裏一体な関係であると言えるでしょう。FileMakerでのInjection、つまり処理の注入は、若干明示的にはなってしまいますが、「URLを開く」でできることが以上のようにわかりました。しかしながら、あえてタイトルにインジェクトションだけを書いたのかというと、依存性つまりDependencyのコントロールが事実上できないという点があります。1つの分かりやすい側面は、名前さえ一致すればどんなスクリプトも呼び出せてしまうという点は、依存を無視しているともいえるわけです。

ここで示した例は、スクリプトにより選択肢の選択ができるという共通点があります。その意味では、Select Button Cスクリプトが1つのクラスだと思っていいかと思います。一方で、選択する先によって関心事が違うという状況を想定しました。つまり、あるボタンは「別のフィールドも更新する」という側面を持ち、別のボタンは「他には何もしない」という共通点の薄い別の側面があったわけです。しかしながら、「URLを開く」を応用した手法により、ボタンごとに異なる側面の実装を独立したスクリプトできるようになりました。したがって、この手法は、FileMakerのスクリプト実装におけるアスペクト指向プログラミングであると言えます。

[IM]INTER-Mediatorはなぜ“会社”にしないのか?

このところ、INTER-Mediatorはなぜ会社にしないのかという主旨の質問を聞かれることが何度かありました。INTER-Mediatorに対して、「なぜ、会社にしないのですか?」「これだったら投資してもらえるのでは?」といった疑問を持たれるようです。ちょうどいい機会なので、新居が考えることを書いておきたいと思います。

まず、INTER-Mediatorもそこそこ出来上がってきており、これを元に仕事ができるレベルになっているのは確かです。新居自身、既にコンスタントにINTER-Mediatorを使った開発案件を抱えるくらいになりつつあります。としたら、一般的に考えられるのは、会社を作ってよりビジネスを広げることでしょう。また、投資が得られたら、ツールの開発などに取り組むことも現実的な作業として考えられるでしょう。しかし、新居自身は「INTER-Mediatorの会社」を作る意思は今の所ありません。

考えられる理想的な状況は、会社を作ってビジネスが軌道に乗るということではなく、次のようなことです。INTER-Mediatorはあくまで素材として存在し、それをベースにビジネスを行う会社が複数共存(連立か?)できるような状況が思想的だと考えます。1社の製品として、1社がコントロールする場合、まず、一定のシェアを確保しないと、ビジネスが存続しません。それは、言い換えれば他者を排除することにもつながります。もちろん、多大なシェアを目指すのも1つの手法ですが、既に数度の飽和をしているIT市場であり、いくつものビッグプレイヤーに1社で臨むのはかなり無理があると考えます。また、そうした一種の賭けをやると、終わりも視野に入ってきます。ビジネスの表舞台に立てるというのは魅力である一方、ダメになった時にはゼロ以下になってしまうということもあります。

INTER-Mediatorが目指すものは、システム開発プロセスの変革であり、その結果、情報システムを広げることでITのメリットをたくさんの人が、より多くの場面で享受できるようにすることです。もちろん、そうした意思はINTER-Mediator特有のものではなく、多くの人たちが様々な手法でチャレンジし続けています。INTER-Mediatorはその流れのほんの小さなインパクトにしか過ぎないものですが、理想を目指して継続させることこそが、何らかの貢献になるのではないかと信じています。そのためには、ビジネスのフィールドに軸足を突っ込むことは、リスクを増やすだけと考えています。

ただし、その一方で、INTER-Mediatorでビジネスをすることには肯定的に捉えています。自分も、業務システムの発注を受けていますし、エミックさんのようにFMPublisherというビジネス展開の一翼をINTER-Mediatorベースに進めていらっしゃる会社もあります。新居自身は、他の皆さんがビジネス展開する上で、必要であれば支援は行います。また、特定の会社だけでなく、原則として複数の会社に対しても支援しています。INTER-Mediatorのコアは「コミュニティ」だと考えています。そのコミュニティで培われたものを持ってビジネスフィールドに進出する人や会社は複数あって、それぞれが独自にアイデアを持ち、協業し、あるいは競合して、世界が広がればいいのではないかと考えています。コミュニティを中心にして、ビジネスフィールドが広がるというイメージです。そして、コミュニティの主体を、新居個人ではなく、Committeeにしたのも、そういう願いの1つの現れでもあります。

こうした手法は、オープンソースを主体にした団体の1つの生きる道だと考えます。例えば、Apache Foundationも、ある意味同様な立ち位置ではないかと考えますが、法人格云々となるといろいろなやり方があるかもしれません。しかしながら、コアコミュニティモデルとも言うべき、コンセプトとそれを実現する素材としてソフトウエア開発をコミュニティで行い、ビジネスを行う主体とは別に存在させることで、多彩な展開が実現することを願っています。したがって、INTER-Mediator株式会社を作る意思は今の所はありません。