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>
© Copyright 2024 ExpyDoc