cffunction

ColdFusion用フレームワーク
Model-Glue & Tartan
2005/11/06
Tomoaki Tanaka
Kagoshima、Japan
Model-Glueについて
Model-Glueとは?
 Model-Glueは

暗黙呼出フレームワーク

Lesser GPLとしてリリース
 ダウンロード、利用、改編がフリー
Model, View, and Controller の分離を促進



 ColdFusionアプリケーション用フレームワーク
 簡単にMVC(Model View Controller)デザインパターンを作成可能
 Tartanなど他のフレームワークと協調動作
他のフレームワークとの本当に簡単な協調動作:
 Tartan(By Paul Kenney)フレームワーク接続用コネクターが付属
Mach-II(他の暗黙呼出、MVCフレームワーク)と比較して
 少し簡略化された機能
 ModelとControllerの境界のより明確に定義
 開発者

Joe Rinehart
 a quasi-popular ColdFusion blogger with an interest in developing
better OO applications in ColdFusion,
 with constant feedback provided by Doug Hughes of Alagad, Inc.
Model-GlueのMVCアキーテクチャ
ViewState
View
Model
event
event
ViewCollection
ViewCollection
event
Default
event
ModelGlue
event
ConfigBean.xml
ViewState
event
ModelGlue.xml
event
event
Controller
Data
Source
Bean
(VO)
Web
Service
View
ModelGlueの概観
ModelGlue
Controller
Model
ModelGlue.xml
Configure
ModelGlue
User
InterAction
Click
html
ModelGlue
Core Files
Include
Views
(.cfm)
Config bean
(.xml)
Configure
Bean
Controllers
(.cfc)
Call &
Return
Models
(.cfc)
ModelGlueの構造設計(シンプルモデル)
views
events
controllers
cfcs
DAOs
Broadcasts
views
results
Broadcasts
views
results
Broadcasts
views
results
Controller-A
Beans
WebServices
DAOs
Controller-B
Beans
WebServices
DAOs
Controller-C
Modelglue.xml
Model-Glue
ColdFusionMX
Beans
WebServices
ModelGlueの構造設計(ConfigBeansモデル)
views
events
controllers
cfcs
DAOs
Broadcasts
views
results
Broadcasts
views
results
Broadcasts
views
results
Controller-A
Beans
WebServices
DAOs
Controller-B
Beans
WebServices
DAOs
Controller-C
Modelglue.xml
Beans
WebServices
ConfigBean-DAO.xml
ConfigBean-Bean.xml
ConfigBean-WS.xml
Model-Glue
ColdFusionMX
ConfigBean-DAO.cfc
ConfigBean-Bean.cfc
ConfigBean-WS.cfc
ModelGlue.xmlの構成パターン
<modelglue>
<config>
【configセクション】
Model-Glue標準項目、ユーザ定義項目(Datasource等)
</config>
<controllers>
<controller>
<messege-listner />
</controller>
</controllers>
<event-handlers>
<event-handler>
<broadcasts>
<message />
</broadcasts>
<views>
<include />
</views>
<results>
<result />
</results>
</event-handler>
</event-handlers>
</modelglue>
【controllersセクション】
※Controllerは、一つでも良いが、ビジネスロジック群に応じて複数に分割して定義しても良い。
【Controllerセクション】
message-listnerを複数定義する。
指定するController.cfc内のfunctionとmessage-listnerのマッピングを行う。
【event-handlersセクション】
※Controllerは、想定されうるユーザイベントに応じて複数に分割して定義する。
【event-handlerセクション】
braodcastsセクション、viewsセクション、resultsセクションから構成される。
[broadcastsセクション]
イベント発生時に起動するリスナーを messageセクション として複数記述する。
[messageセクション]
起動するリスナーを controllerセクションのmessage-listnerセクションに定義した
リスナー名で指定する。<argument>タグで引数を渡すことも可能。
[viewsセクション]
レンダリングするviewファイルを<include>タグで指定する。
[includeセクション]
レンダリング結果をname属性で指定するviewコレクションに格納することができる。
<value>タグで、引数を渡すことも可能。
[resultsセクション]
イベント処理結果のシンボル内容に応じて、イベントコントロールを行う。
例外処理と最終レンダリング処理用イベントを起動する。
Controller.cfcの標準構成パターン
<cfcomponent extends="ModelGlue.Core.Controller">
<cffunction name=“init”>
</cffunction>
<cffunction name=“OnRequestStart”>
</cffunction>
<cffunction name=“OnRequestEnd”>
</cffunction>
<cffunction name=“UserModel1”>
</cffunction>
<cffunction name=“UserModel2”>
ModelGlueの初期化
Beanのインスタンス生成と初期化
Modelのインスタンス生成と初期化
リクエストスタート時の処理
リクエスト終了時の処理
ユーザ処理-1(メッセージリスナー対応)
ユーザ処理-2(メッセージリスナー対応)
</cffunction>
・
・
・
<cffunction name=“UserModelN”>
</cffunction>
</cfcomponent>
ユーザ処理-n(メッセージリスナー対応)
イベントフローの仕組み
model-glue.xml
<modelglue>
<modelglue>
<event-handlers>
<event-handlers>
controllerA.cfc
<modelglue>
<event-handlers>
コントローラーA定義
リスナー1定義
ファンクション1定義
コントローラーB定義
リスナー2定義
ファンクション2定義
</event-handlers>
リスナー3定義
ファンクション3定義
<event-handlers>
リスナー4定義
ファンクション4定義
イベントA定義
メッセージ定義
イベントB定義
ビュー定義
・
・
・
リザルト定義
</event-handlers>
</cfcomponent>
イベントZ定義
</event-handlers>
</modelglue>
<cfcomponent >
</event-handlers>
</modelglue>
</modelglue>
Model-Glueのインストール

サードパーティかビルトインのウェブサーバを使ってColdFusionMXの標準的インストール(デ
フォルトでは、ColdFusion root ="Web root"とします)が終了していたなら、ModelGlue
フォルダをWeb rootにコピーしてください。

ColdFusion rootがWeb rootと異なる場合、ModelGlueフォルダをColdFusion rootにコ
ピーしてください。そして、/ModelGlue/Samples and /ModelGlue/Documentation
フォルダをWeb rootにコピーしてください。この時点で、samplesとdocumentationがブラ
ウジングできるか確認すべきでしょう。

フレームワークそれ自身は、ModelGlue、Bean、Core、MetaData、そしてUtilフォルダ内の
各ファイルから成ります。サーバが必要なのはフレームワークのひとつのコピーだけを必要とし、
/ModelGlue/ModelGlue.cfcのcreateObject("component",
"ModelGlue.ModelGlue")に存在しつづけることを必要とします。
Model-Glueアプリケーションの作成
 新規Model-Glueアプリケーション作成のワン・ツー・スリー
1. /ModelGlue/ModelGlueApplicationTemplateフォルダをWeb rootにコピーしてください。コ
ピーしたフォルダ名を"NewApp"に変更します。
2. NewApp/Config/ModelGlue.xmlファイルを開きます。<config>と<controllers>ブロックの設
定を以下のように変更し、保存します。
<Config> Block:
<setting
<setting
<setting
<setting
name="applicationMapping" value="/newapp" />
name="modelGlueMapping" value="/ModelGlue" />
name="beanMappings" value="/newapp/Config/Beans/" />
name="viewMappings" value="/newapp/views" /
<Controllers> Block:
<controller name="myController" type="newapp.Controller.Controller">
3. NewApp/Applicationファイルを開きます。<cfapplication>タグの中のアプリケーション名を以下
のように変更します。
<cfapplication name="newapp" sessionmanagement="true"/>
これで!できました。http://[host]/NewAppを訪れると、退屈なページがゲットされるはずです。
新規イベントハンドラーの作成



イベントハンドラーは、Model-Glueを操作することです。
どのようなModel-Glueサイトでも、どのページに遷移するかは、URL.Eventによって制御可
能です。
ModelGlue.xmlファイル中のDefaultEventを設定することによって、デフォルトイベントを変
更できます。

新しいイベントハンドラーを作成しましょう。そして次のクイックスタートで、""Hello, World!"を
(もうわかっていると思いますが)表示できるでしょう!

<event-handlers>ブロックの最後に、新しい空のevent-handlerタグを単に追加してくださ
い。
<event-handler name="HelloWorld" />
</event-handlers>

さあ、http://[host]/newApp/?event=HelloWorld を訪れてみましょう。

何も起こらなかった?そうですね・・・ブランク画面?このイベントはまだ何もしていないからです。
新規ビューの作成



Viewは、ユーザに要素を示し、そしてユーザはものをクリックし、キーボード入力したものを見
ます。それがViewの仕事です。それ以上何もありません。
Viewの中にクエリーを置くのは、誤りです(コントローラにクエリーを置く混乱と間違えないよう
に!後ほど詳細について触れます。)。
/NewApp/Viewsフォルダにdsp.helloWorld.cfmファイルを追加してください。内訳は以下
のとおり。
<h1>Hello, World!</h1>


さて、HelloWorld eventにそのViewファイルを含めるようにしてみましょう。Viewファイルを
含める基本的構文は、よく知られているCFIncludeです。唯一の違いは、<include>タグは
Name属性が必須であることです。Viewは他のViewの内部で表示され、最後に表示される
Viewこそがユーザに見せるViewとなります。
最新のイベントハンドラーは、このようになります。
<event-handler name="HelloWorld">
<views>
<include name="content" template="dsp.helloworld.cfm" />
</views>
</event-handler>

さあ、http://[host]/newApp/?event=HelloWorldを訪れてみましょう。ワオ、ハローワー
ルド!
Viewを他のViewで使う-1

さて、Viewをイベントに追加する方法がわかったところで、Viewを他のViewに表示する方法
を考察してみましょう。

Viewでは、2つの変数が利用できます。



ViewCollection
ViewState
 ViewStateは気にしないでください。ViewCollectionで代用できますので・・・
ViewからのHTML結果(同一リクエストで起動されたイベントによってレンダリングされた・・・理
解できない状況なら無視してもいい)は、ViewCollectionによって利用可能です。

ViewCollectionがもつ2つのファンクション
 Exists(viewName)
ViewNameの存在チェック
 GetView(viewName) ViewName内容のゲット
Viewを他のViewで使う-2

それでは、layout.main.cfmによって、"Hello, World"messageを"ラップ"して、同一結果
を得ることができるか見てみましょう。

最初に、イベントハンドラーを修正しましょう。
<event-handler name="HelloWorld">
<views>
<include name="content" template="dsp.helloworld.cfm" />
<include name="main" template="layout.main.cfm" />
</views>
</event-handler>

次に、ViewCollectionの中に"content"があれば、それを表示するようlayout.main.cfmを
修正してみましょう。
<h1>Model-Glue Application</h1>
<cfif ViewCollection.Exists("content")>
<cfoutput>#ViewCollection.GetView("content")#</cfoutput>
</cfif>

ちょっとはよくなった?
既存Viewに追加する

<Include>タグのName属性にはユニークな名前をつけてください。非ユニークな名前をつけ
たければ、その属性にappend="true"を追加してください。これで、既存Viewのコンテンツに
追加できます。これを行わなければ、既存Viewをオーバライドします。このアクションを見るた
めに、イベントハンドラーを修正してみてください。
<event-handler name="HelloWorld">
<views>
<include name="content" template="dsp.helloworld.cfm" />
<include name="content" template="dsp.helloworld.cfm" append="true" />
<include name="main" template="layout.main.cfm" />
</views>
</event-handler>

いつも最初で理解しないように見えるので、人々はいつも物事を二度言います。いつも最初で
理解しないように見えるので、人々はいつも物事を二度言います。ちょうどからかってみまし
た!
messageのブロードキャスト



さて、whiz-bangフレームワークに静的なmessageを表示させる方法を把握できましたか?実際に
はあまり印象的でありませんね。
今度は、HelloWorld イベントハンドラーにmessageをブロードキャストさせる方法を実際に見て見
ましょう。
messageのブロードキャストは、"ハローーーー、だれか気になったら、お願いこれをして!と喋るよ
うにイベントハンドラーにさせることことです。イベントハンドラーにブロードキャスト機能を追加する方
法は以下のとおりです:
<event-handler name="HelloWorld">
<broadcasts>
<message name="DoHelloWorld" />
</broadcasts>
<views>
<include name="content" template="dsp.helloworld.cfm" />
<include name="content" template="dsp.helloworld.cfm" append="true" />
<include name="main" template="layout.main.cfm" />
</views>
</event-handler>


実行しようとすればできたのですが、何も起こりませんでした。
なぜか?DoHelloWorld messageを誰もListenしていないからです。イベントハンドラーは、すべ
て好きなように跳び、喚き、叫ぶことができますが、何も起こそうとしません。なぜなら、誰も気にして
いないからです。次を読んでください。Controller(modelGlue)に気にしてもらう方法を教えませう。
messageのListen-1






さて、今度はModel-Glueの核心部分に触れてみましょう。
ブロードキャストに耳をすますControllerは、すべてのブロードキャストを聞き分けています。
重大なこと、重要なこと・・・それほど重大でないものも。
行うべきことは、<message-listener>タグを希望する<Controllers>セクションに追加す
ることです。
そこには、すでに2つのデフォルトのブロードキャスト(“OnRequestStart” と
“OnRequestEnd”)があるでしょう。
全ての<message-listener>タグは、イベントハンドラーがMESSAGE属性と同じブロード
キャストmessageを放ったとき、FUNCTION属性で指定したCFCファンクションを実行します。
DoHelloWorld messageをリスナーに追加するために、Controllerに新しいCFCファンク
ションを加える必要があります。/NewApp/Controller/Controller.cfc を作成し、
<cfcomponent>タグの終了のまえにこのコードを追加してください:
<cffunction name="GetGreeting" access="Public" returnType="ModelGlue.Core.Event"
output="false" hint="I am an event handler.">
<cfargument name="event" type="ModelGlue.Core.Event" required="true">
<cfreturn arguments.event />
</cffunction>
messageのListen-2

今度は、<message-listener>タグを追加しましょう。<controller>ブロックは以下のように
なります。
<controller name="myController" type="newapp.Controller.Controller">
<message-listener message="OnRequestStart" function="OnRequestStart" />
<message-listener message="OnRequestEnd" function="OnRequestEnd" />
<message-listener message="DoHelloWorld" function="GetGreeting" />
</controller>



実行しようとすればできたのですが、何も起こりませんでした。
まだ・・・なぜ?そう、ControllerはmessageをListenしているのですが、GetGreeting()メ
ソッドが実際に何もしていないからです。
証拠をみたければ、GetGreeting()メソッドの中に、
<cflocation url="http://www.fullasagoog.com" addToken="no">


を追加して、http://[host]/newApp/?event=HelloWorld を訪れてみてください。
別な方法で、先に進んだでしょうか?
Controllerからデータバックする-1

さて、今度は何をしましょう。ControllerがmessageをListenする方法とmessageListen時
にCFCを起動する方法を学んできました。今度は、Controllerからデータバックする方法を見
てみましょう。

<message-listener>タグによってコールされるCFCファンクションは、最低ひとつの引数を
持つべきです。ModelGlue.Core.Eventタイプのその引数は、その内部にいくつかのメソッド
群を持ち、そのメソッド群によって、ユーザ入力値を取り出したり、Viewにデータをバックしたり
することができます。最初に変数の値をそのまま返す方法を見てみましょう。それは簡単です。
ControllerのGetGreeting()メソッドのreturn構文の直前に以下を追加してください:
<cfset arguments.event.SetValue("Greeting", "Hello, I am the Controller!") />

今度は、前述の挨拶文を表示するためにViewを変更します。dsp.helloworld.comを開いて、
若干の変更を加えます。

Viewには、2つの利用可能な変数(ViewCollection と ViewState)があること、
ViewCollectionがどのようにして他のViewを制御するかとViewStateは後ほど説明する旨
を述べたことを覚えていますか?(あなたが命じているかどうかわかりませんが、)今がそのと
きです。
Controllerからデータバックする-2

ViewStateは、とてもハンディなModelGlue.Util.GenericCollection CFCのインスタンスで
あるメソッド群を持っています。最も興味あるメソッドの一つは、GetValue()です。
GetValue()は、入手すべきValue名と、オプションでコレクション内にValue名がセットされて
いない場合のデフォルト値を返します。デフォルトが定義されていない場合、Value名が定義さ
れていない場合、empty文字が返されます。

ControllerからのmessageをViewで表示してみましょう。dsp.helloworld.cfmに以下の
コードを追加してみましょう。
<cfoutput>
<h1>#viewState.GetValue("Greeting", "Oops, No Greeting Defined!")#</h1>
</cfoutput>

そうする一方で、ちょっといらつくので、挨拶分を2回表示しないようにModelGlue.xmlを修正
します。

http://[host]/newApp/?event=HelloWorld でチェックしてみましょう。
ControllerやViewでユーザ入力値を得る-1

オーケー、これで、arguments.event.SetValue(name, value) がControllerから変数を
データバックすることができました。

さて、ユーザ入力値を入手するできるということは、何を物語っているのでしょうか?

Model-Glueは、Formスコープ変数とURLスコープ変数を全て自動的に収集し、それらを
ViewStateに格納します。それはこういうことです。どんなViewにおいても、
ViewState.GetValue(name)によって、Formスコープ変数とURLスコープ変数中の現在値
を入手することができるということです。(注意:Viewは、Form, URL, Session,
Application, CGI, もしくは他のどんなスコープ変数を参照できるということでは決してありま
せん。ともかく、ViewStateによってFormスコープ変数とURLスコープ変数は参照できるとい
うことです。他のスコープ変数を参照しようとしても、その値は不定です。)

さあ、Viewについての四方山話は置いといて、Controllerはどのようにしてユーザインプット
値を入手するのでしょうか?

そう、SetValue()のようなもの、その反対語であるGetValue()によってです。
GetValue(name, [optional] default)の構文によるViewStateのGetValue()のように使
用します。デフォルトが定義されていない場合、Value名が定義されていない場合、empty文
字が返されます。
ControllerやViewでユーザ入力値を得る-2

実際に見てみましょう。GetGreeting()メソッドを以下のように変更してください。
<cffunction name="GetGreeting" access="Public" returnType="ModelGlue.Core.Event"
output="false" hint="I am an event handler.">
<cfargument name="event" type="ModelGlue.Core.Event" required="true">
<cfset var name = arguments.event.GetValue("Username") />
<cfif len(name)>
<cfset arguments.event.SetValue("greeting", "Hello, #name#, I am the
Controller!") />
<cfelse>
<cfset arguments.event.SetValue("greeting", "Fine, be an unfriendly sod, I didn't
want to know your name anyways!") />
</cfif>
<cfreturn arguments.event />
</cffunction>




そして、http://[host]/newApp/?event=HelloWorld を訪れてみてください。まだ、フレンドリー
でない旨を告げるはずです。
名前をURL変数に入力して何が起こるかみて見ましょう。
http://[host]/newApp/?event=HelloWorld&username=Joe
やった!さらによくなりましたね。今度は、フォームページを追加したら、ガスでクッキングしよう。
Modelを作成する

実際にはそんなことしませんよね?単一のクイックスタートで、すべてをカバーし、情報満載の
OOモデルの問題集を作成しようとはしていないだろうか?

Well, yes, I am, and I'm going to do it very quickly, and if it works well
enough, you won't notice the PhD-candidancy-worthy amount of material I'm
going to gloss over, leave out, and otherwise ignore.

Simply put, your Model is your applications. It's just a bunch of CFCs that,
when used together, do something. That something is your application. You
Model doesn't know Model-Glue from a hill of beans (or Mach-II, or Fusebox).
You should be able to write a bunch of little .CFM scripts to test your entire
model without any UI (check out CFCUnit, already!).

ともかく、株価表示アプリケーションは非常にシンプルです。2つのCFCで構成され、一つ目は、
StockQuoteGetterで、 その名のとおり株価を取得するCFCです。 もう一方は、
StockQuoteResult でStockQuoteGetterとのインタフェース用CFCです。これら2つの
CFCは、とてもスモールで、大変わかりやすく、アプリケーションにとって大変重要です。
次に進む前に、開いて、一読しておいてください。

/ModelGlue/Samples/StockQuote/Model.
Modelを使用する-1

さて、Modelを使用するためにはイベントを必要とします。新しいイベントハンドラーを追加しましょう。
<event-handler name="StockQuote">
<broadcasts>
<message name="DoStockQuote" />
</broadcasts>
<views>
<include name="content" template="form.StockQuote.cfm" />
<include name="main" template="layout.main.cfm" />
</views>
</event-handler>

今度は、ControllerにDoStockQuoteのmessageをListenさせましょう。
<controller name="myController" type="newapp.Controller.Controller">
<message-listener message="OnRequestStart" function="OnRequestStart" />
<message-listener message="OnRequestEnd" function="OnRequestEnd" />
<message-listener message="DoHelloWorld" function="GetGreeting" />
<message-listener message="DoStockQuote" function="GetStockQuote" />
</controller>

次に、controller.cfcにGetStockQuote ファンクションを定義しましょう。これこそが、"Glue"(接着
する)の言われです。
Modelを使用する-2

さて、セオリーどおりに作ると、GetStockQuote()は以下のようになります。
<cffunction name="GetStockQuote" access="Public"
returnType="ModelGlue.Core.Event" output="false" hint="I am an event handler.">
<cfargument name="event" type="ModelGlue.Core.Event" required="true">
<cfset var symbol = arguments.event.GetValue("symbol") />
<cfset var QuoteGetter = createObject("component",
"ModelGlue.Samples.StockQuote.Model.StockQuoteService").init() />
<cfset var result = QuoteGetter.GetQuote(symbol) />
<cfset arguments.event.setValue("symbol", result.getSymbol()) />
<cfset arguments.event.setValue("price", result.getResult()) />
<cfreturn arguments.event />
</cffunction>
</cfcomponent>

オーケー、http://[host]/newapp/index.cfm?event=stockquote をキックして、株価表示
ページを見てみましょう。適切な株価コードを入力したなら、時事刻々の株価を見れるはずです。
messageの引数-1




私たちの友達<message>タグに戻りましょう。
<message>タグは、子供を持つことになります(おめでとう、<message>タグくん、あなた
のライフパートナー<message-listner>は本当にゾクゾクする存在だね!)。
messageをブロードキャストする際、messageはいくつかの引数を渡すことができます。この
ことによって、ロールベースのセキュリティをとても手軽に実装できるのです。
さて、株価コードのデフォルトシンボルを、そうですねーMACRにするよう、Model-Glue.xml
を修正してみましょう。
<event-handler name="StockQuote">
<broadcasts>
<message name="DoStockQuote">
<argument name="DefaultSymbol" value="MACR" />
</message>
</broadcasts>
<views>
<include name="content" template="form.StockQuote.cfm" />
<include name="main" template="layout.main.cfm" />
</views>
</event-handler>
messageの引数-2

次に、株価コードシンボルが空欄だったらデフォルトシンボルを使うように、controller.cfcをいじって
みましょう。
<cffunction name="GetStockQuote" access="Public"
returnType="ModelGlue.Core.Event" output="false" hint="I am an event handler.">
<cfargument name="event" type="ModelGlue.Core.Event" required="true">
<cfset var symbol = arguments.event.GetValue("symbol") />
<cfset var QuoteGetter = createObject("component",
"ModelGlue.Samples.StockQuote.Model.StockQuoteService").init() />
<cfset var result = "" />
<cfif not len(symbol)>
<cfset symbol = arguments.event.GetArgument("DefaultSymbol") />
</cfif>
<cfset result = QuoteGetter.GetQuote(symbol) />
<cfset arguments.event.setValue("symbol", result.getSymbol()) />
<cfset arguments.event.setValue("price", result.getResult()) />
<cfreturn arguments.event />
</cffunction>
追加イベントのリクエスト-1

Controller.cfcには、集約したほうがいい繰り返しのコードがあります。

HelloWorldとStockQuoteのイベントハンドラーには、各々の<view>ブロックに
layout.main.cfmファイルへの書き出し用Viewがあります。layout.main.cfmをそれ専用
のイベントハンドラー化して、HelloWorld と StockQuote イベントハンドラーがその仕事を
終了したときにそのイベントを起動するようフレームワークに通知したほうがいいと思いませ
ん?

そうです、とてもいいことですし、いとも簡単に実現できます。<result>ブロックが<eventhandler>ブロックの3つめに起こりうる3つ目のブロックになります。<results>タグ内に、
<result>タグを個々に記述してください。各々の<result>タグは、どのイベントハンドラーを
発動するかを記述した"DO"属性を持ちます。

これを適用するために、最初にLayout用のイベントハンドラーを外部に書き出します。
<event-handler name="Layout">
<views>
<include name="main" template="layout.main.cfm" />
</views>
</event-handler>
追加イベントのリクエスト-2

次に、StockQuote や HelloWorld 用イベントハンドラーに適当な<results>ブロックを追
加します。
<event-handler name="StockQuote">
<broadcasts>
<message name="DoStockQuote">
<argument name="DefaultSymbol" value="MACR" />
</message>
</broadcasts>
<views>
<include name="content" template="form.stockquote.cfm" />
</views>
<results>
<result do="Layout" />
</results>
</event-handler>
複数イベントの結果をマッピングする-1

Controller.cfc内部で処理を継続するということは、外部のイベントハンドラーを発動してもよ
いということです。Model-Glue風に言う"Result"でこのことが実現できます。Controller.cfc
で、resultmessageをイベントに追加することができます。Model-Glue.xmlファイル内で、
<result>タグ内に"name"属性を記述することによってイベントハンドラーの処理message
をマッピングできます。いくつものイベントを発動することで、一つ以上のresultmessageを
resultmessageにマッピングすることができますので、注意してください。




注意:Model-Glueの過去のバージョンでは、controller.cfcからイベントハンドラーを直接起
動するaddEventReauest()メソッドがありました。これは反対を唱えられ、Ver0.7以降削除さ
れました。
さて、前述の外部イベント発動結果のマッピングをStockQuoteアプリケーションでどのように
実装するか見てみましょう。"株価のシンボルコードが存在しない(株価戻り値:-1.0)ときに、
"Oops!"messageを表示するBadStockSymbol"イベントハンドラーを発動してみましょう。
最初に、BadStockSymbolイベント用のViewを作成します。dsp.badStockSymbol.cfm
としましょう。"this should be old hat by now!"のようなmessageを表示するように、コー
ディングしてください。
次に、BadStockSymbol用イベントハンドラーを作成します。
<event-handler name="BadStockSymbol">
<views>
<include name="content" template="dsp.badStocksymbol.cfm" />
</views>
</event-handler>
複数イベントの結果をマッピングする-2

オーケー、次に、イベント結果をresultにマッピングさせてみましょう。<event-handler>タグ
に記述可能な3つのタグ(broadcasts, views, 及びresults)の3つ目の<results>タグを
追加します。暗黙的な呼び出し結果(name属性なし)の前に、明示的な呼び出し結果(name
属性あり)を必ず発動してください。以下のようになります:
<event-handler name="StockQuote">
<broadcasts>
<message name="DoStockQuote">
<argument name="DefaultSymbol" value="MACR" />
</message>
</broadcasts>
<views>
<include name="content" template="form.stockquote.cfm" />
</views>
<results>
<result name="BadSymbol" do="BadStockSymbol" />
<result do="Layout" />
</results>
</event-handler>

この<result>タグは、以下のようなことを物語っています。
 controller.cfcがeventに'BadSymbol'を追加したなら、BadStockSymbolイベントハ
ンドラーを発動しなさい。
複数イベントの結果をマッピングする-3

そういうことで、controller.cfcにそのことを追記しましょう。
<cffunction name="GetStockQuote" access="Public" returnType="ModelGlue.Core.Event"
output="false" hint="I am an event handler.">
<cfargument name="event" type="ModelGlue.Core.Event" required="true">
<cfset var symbol = arguments.event.GetValue("symbol") />
<cfset var QuoteGetter = createObject("component",
"ModelGlue.Samples.StockQuote.Model.StockQuoteService").init() />
<cfset var result = "" />
<cfif not len(symbol)>
<cfset symbol = arguments.event.GetArgument("DefaultSymbol") />
</cfif>
<cfset result = QuoteGetter.GetQuote(symbol) />
<cfset arguments.event.setValue("symbol", result.getSymbol()) />
<Cfset arguments.event.setValue("price", result.getResult()) />
<cfif result.getResult() lt 0>
<cfset arguments.event.addResult("badSymbol") />
</cfif>
<cfreturn arguments.event />
</cffunction>

さーて、株価シンボルに"314159"を追加してみよう。そうすると、株価コードが間違っているこ
とを教えてくれるでしょう。
ConfigBean/IoC -1

IoC(Inversion of Control)は、制御の逆転です。Model-GlueのConfigBean機能
は、ChiliBeans componentを使って大変シンプルに実装されている一方で、アプリ
ケーションをランタイムで容易に制御するためにXMLファイルを使います。

株価サービスを使用するために株価サービス用WSDLを含むconfigbeanを使用できる
ようにアプリケーションを再編してみましょう。

最初に、/ModelGlue/Beans/CommonBeans内にある
ExampleSimpleConfigBean.xmlを/NewApp/Config/Beansフォルダにコピーして、
StockQuoteConfig.xmlとリネームしてください。

そして、それを開いて以下の内容をペーストしてください。
<bean class="ModelGlue.Bean.CommonBeans.Simpleconfig" singleton="true">
<property name="config">
<struct>
<entry key="wsdl"><value>http://services.xmethods.net/soap/urn:xmethodsdelayed-quotes.wsdl</value></entry>
</struct>
</property>
</bean>
ConfigBean/IoC -2

さて、これを使って、controller.cfcを変更してみましょう。利用できる株価情報サービスにはい
ろいろなやり方がありますが、株価サービス用のWDSLを使うために、Init()コンストラクラを
持っています。
<cfset var symbol = arguments.event.GetValue("symbol") />
<cfset var stockQuoteConfig = GetModelGlue().GetConfigBean("StockQuoteConfig.xml")
/>
<cfset var QuoteGetter = createObject("component",
"ModelGlue.Samples.StockQuote.Model.StockQuoteServiceConfigurable").init(stock
QuoteConfig.getConfigSetting("wsdl")) />
<cfset var result = "" />





はい、なんか色々書いていますね。
最初に、GetModelGlue()はフレームワーク自身への参照ポイントで、どんなサービスであれ
これを参照します(理論上、新しいコントローラを瞬時に追加することも可能!)。
次に、GetConfigBean()は、フレームワークにModel-Glue.xmlファイル中の
BeanMappingsディレクティブに定義されたフォルダ内からStockQuoteConfig.xmlを探し
なさいということを示しています。
それが見つかると、StockQuoteConfig.xml中の<bean>タグ内の“class”属性を読み取り、
そのインスタンスを返します。
最後に、StockQuoteServiceConfigurableを使うためにQuoteGetterオブジェクトを作成
して、それをconfiguration beanの値で初期化します。詳細をみたければ、
CommonBeans内のSimpleConfig.cfcとStockQuoteConfig.xmlを見てください。
実行!製品モード

デフォルトでは、Model-Glueはフレームワークをページがロードするたびにリロードします。
Controllerがアプリケーションスコープ上にあるので、開発時はこれで重宝します。しかしなが
ら、このことはパフォーマンスの低下をもたらします(キャッシュを使用するかどうかも選択でき
ますけど)。ModelGlue.xmlファイルを開いて、"reload"セッティングを"false"にしてください。
もし、リロードさせたれば、index.cfm?[reloadKey]=[reloadPassword] としてください。

クイックスタート:デバックトレースのオン/オフ

Model-Glueアプリケーションテンプレートを使ってアプリケーションを書いたことがあるなら、
ページの最下部にデバックログが見えていることと思います。ここで、ユーザにこれを見せない
ように、デバックトレースをオフにしてみましょう。

デバッギングは、"debug"セッティングで制御しています。Model-Glue.xmlでは、デバッギン
グがオンにセッティングされています。

<setting name="debug" value="true" />

"debug"属性を"false"に変更するか、一行まるまる削除すると、デバッグトレース機能をオフ
にできます。
例外処理

生の例外処理をユーザに見せることは、決していいことではありません。Model-Glueでは、簡
単にフレンドリーなmessageの表示をすることができ、defaultExceptionHandlerセッティン
グを使ってプログラム的にハンドリングすることができます。ModelGlue.xml内の<config>
ブロックセッティングは、ある例外が生じた時にイベントハンドラーを呼び出す定義例です。

<setting name="defaultExceptionHandler" value="Exception" />

この例では、"ユーザリクエストに何らかのエラーを生じたら、実施中の動作を中断し、
'Exception'というイベントハンドラーをリクエストしなさい"ということを示しています。

例外が投げられると、"exception"という変数がViewStateに追加されます。これによって、
#viewState.getValue("exception")#を記述することによって、Viewに表示できることを
意味します。broadcast, message,message-listener及びlistener内の関数、並びに
listner内の関数(arguments.event.getValue("exception")によって参照される)を追加
することで、プログラム的に大きな例外処理を実行できます。

注意:もし、あなたの定義した例外用のevent-handlerが、例外を投げたときは、生の例外
messageが表示されます。確実に例外処理を行ってくださいね。

株価表示アプリケーションにおける実際の動作を見たければ、次のURLを入力してみて、実際
に存在しないイベントにアクセスしようとしたときの動作をみてください。

http://[host]/newapp/index.cfm?event=IAmNotAValidEvent
Tartanについて
Tartanとは?
 Tartanは




ColdFusion用サービスフレームワーク
 大規模アプリケーション向け
 コマンドドリブンベース
 機能性の完全分離 と Modelのサブレイヤ化 を提供
2つのServiceコントローラを配置し、ビジネスロジック層配下を制御
 ローカルサービス:CFCs
 リモートサービス:Flash Remoting 、SOAP web services.
ビジネスロジック層配下を制御は、Commandを介して実行
 Command:アプリケーション内で 機能分離されたオペレーションを実装
 Command:アプリケーションのコアロジックを含む
Tartanの6つのコアクラス
 RemoteService, LocalService
 Command, DAO, ValueObject and ExceptionHandler.
 開発者
 Paul Kenney
http://tartanframework.org/
CFMX用フレームワークのポジショニング
PC
Implicit
Invocation
FrameWork
(Model-Glue)
Service
Framework
(Tartan)
Application Server
(ColdFision)
WAP
Flash7
Player
Presentation Layer
(View)
Bussiness Logic
(Controller)
Service Layer
(Service)
DataStore Layer
(DataSource)
WebService
ModelGlueとTartanの設計構造
Controller
DataSource
TT
Service.xml
Service
ConfigBeans
MG
ModelGlue.xml
View
Main.Layout.cfm
dsp.pod.cfms
dsp.pod.js
dsp.pod.css
Controllers
Remote
Services
LocalServices
Commands
VOs
GWs
DAO’S
DS
WS
View
Tartanの概観
Controller
Service
Tartan
(Model)
Config.xml
Service.xml
Configure
Tartan
Configure
Framework
Coldfusion
Framework
Core Files
Controllers
(.cfc)
Command
VO
DAO/Gateway
Tartanは、Coldfusion用サービスフ
レームワーク。既存MVCフレームワー
クと連携し、Model部分における機能
性の完全的分離とサービスレイヤ以
下のサブレイア化を実現する。
Tartan
Core Files
Call &
Return
Services
(.cfc)
Call &
Return
Commans
VOs
DAOs/Gateways
(.cfc)
View
ModelGlue+Tartanの概観
ModelGlue
Controller
Service
Tartan
ModelGlue.xml
Service.xml
Configure
ModelGlue
User
InterAction
Click
html
Command
VO/DAO/GW
Configure
Tartan
ModelGlue
Core Files
Tartanは、Coldfusion用サー
ビスフレームワーク。
ModelGlueと連携し、Model
部分における機能性の完全的
分離とサービスレイヤ以下のサ
ブレイア化を実現する。
Tartan
Core Files
Include
Views
(.cfm)
Controllers
(.cfc)
Call &
Return
Services
(.cfc)
Call &
Return
Commans
VOs
DAOs/Gateways
(.cfc)
Tartan ガイドライン
 Servicesに関するルール
 Commandsに関するルール
 DAOsに関するルール
 DAOを介したgateway読出
Servicesに関するルール
 Serviceメソッドは、 command実行後に’args’ structを介して
データを返す
 Service群は、service.xmlの中で定義され、以下のサブセクション
を持つ
 生成ファクトリー群
 DAOs, Commands, VOs, and Exceptions
 サービス全体にかかるオプショナルパラメータ群
 クラス名は、サービスローダのgetService(“serviceName”) メソッド
で呼び出されるサービス名
 クラスメソッドは、クラスファイルのローケーションポイントを示す必要が
ある
 Service群は、データを操作し、変更し、再フォーマットし、もしくは変
換できる
 Service群は、シェアードスコープ内に格納することができる
Commandsに関するルール
 Commandオブジェクトは、 ‘true’ か ‘false’ を返さねばならない
 Commandオブジェクトは、
 arguments.event.setArg(s)()を呼び出し、新規オブジェクト(例:
VO)か文字列群にインサートすることによって、Command処理結果を
Serviceに戻す
 Commandは、
 Service内でexecuteCommand(“name”,argsStruct)を実行する
ことによって呼び出される
 Command群は、
 データを処理しない、思考せず、実行するだけ
 Command群を機能的に結合してもよい、しかしながら・・・
 ログイベントはアプリケーションイベントと対にすること
 DBレコードを変更する前に、履歴レコードを生成すること
 関係するアクションは、連続して発生させること
DAOsに関するルール
 DAOは、次のことを満足する必要がある
 レコードの生成、更新、削除に対しては、’true’または’false’を返すこと
 レコード読出もしくはゲートウェイメソッドに対しては、データを返すこと
 個々のDAOは、データソースに対するDAO(CURD)かゲートウェイ
メソッドを含むことができる
 DAOは、Serviceのデータコレクションニーズに応じて配置される
DAOを介したgateway読出
1.
2.
3.
4.
5.
DAOのインスタンス化
オプションで、VOのインスタンス化
Cftryの試行
DAO gatewayメソッドの呼び出し(引数付)
処理結果の’args struct’への挿入:以下のいずれか
 クエリー結果を、event.setArg(“qryName”,qry) によって、EVENT
に渡す
 VOを生成し、クエリー結果をループオーバーしてVOに定住化
(populate)し、最後にevent.setArg(“qryVO”,VO) を実行し、
EVENTに渡す
6. 他コマンドの呼び出し、他DAOの呼び出し、もしくはロギングの実行
 ロギング:cflogによりログファイルを更新(データアクセスログの記述)
7. cfcatch結果に基づく’true’ か ‘false’ のリターン
Service.xmlのサンプル
<service-factory>
<service name="SampleService" class="com.example.services.LocalService">
<properties>
<property name="username" value="samiam"/>
<property name="password" value="p@$$w0rd"/>
</properties>
<dao-factory name="sample"">
<default-datasource>sample</default-datasource>
<dao name="contact" class="com.example.data.ContactDAO"/>
<dao name="address" class="com.example.data.AddressDAO" dsn="sample2"/>
</dao-factory>
<command-factory name="sample">
<command name="GetContact" class="com.example.commands.GetContact">
<parameters>
<parameter name="param1" value="value1">
<parameter name="param2" value="value2">
</parameters>
</command>
</command-factory>
<valueobject-factory name="sample">
<valueobject name="contact" class="com.example.vo.Contact" remote-class="ContactVO"/>
<valueobject name="address" class="com.example.vo.Address" remote-class="AddressVO"/>
</valueobject-factory>
<exception-handlers name="sample">
<default-handler>any</default-handler>
<exception-handler name="any" class="tartan.exception.EMFHandler">
<parameters>
<parameter name="configFile" value="/com/sample/config/ExceptionHandler.xml"/>
<parameter name="configFileType" value="mapping"/>
</parameters>
</exception-handler>
</exception-handlers>
</service> </service-factory>
<service-factory>