トリガーとしてのResource Script
Live FrameworkのMesh Objectなどのリソースは、そのリソースの生成・更新・削除を要因として、それらの処理前または後に特定の動作をさせるトリガー処理ができます。このトリガーにResource Scriptを使用します。今回は、このトリガー処理についてみてみましょう。また、Scriptの実行結果の処理方法についても紹介します。
トリガーに使用できるリソースの操作は、リソースの生成・更新・削除の前後の6種類です。Live Framework SDKのライブラリを使用した場合、以下のプロパティをリソースクラスが持っています。これらのプロパティにResource Scriptを指定することでトリガー処理が可能です。
PreCreateTrigger
PostCreateTrigger
PreUpdateTrigger
PostUpdateTrigger
PreDeleteTrigger
PostDeleteTrigger
PreCreateTriggerとPostCreateTriggerはリソースの生成の前後、実際のLive Operating Environmentとのやり取りではHTTP POSTメソッドが行われる前後にResource Scriptを実行します。同様に、PreUpdateTriggerとPostUpdateTriggerはリソースの更新(HTTP PUTメソッド)の前後、PreDeleteTriggerとPostDeleteTriggerはリソースの削除(HTTP DELETEメソッド)の前後にScriptを実行します。
Create・Update
Createトリガー
さっそくトリガーを実際に使ってみましょう。以下にPostCreateTriggerを使用したコードを示します。
var serviceUrl = "https://user-ctp.windows.net" ;
var token = new NetworkCredential ( User . UserId , User . Password , serviceUrl ). GetWindowsLiveAuthenticationToken ();
var loe = new LiveOperatingEnvironment ();
loe . Connect ( token , AuthenticationTokenType . UserToken , new Uri ( serverUrl ), new LiveItemAccessOptions ( true ));
var mo = new MeshObject ( "Mesh Object 1" );
mo . Resource . Triggers . PostCreateTrigger =
Statement . Sequence (
Statement . CreateResource < MeshObjectResource >(
"s1" ,
new Uri ( "https://user-ctp.windows.net/V0.1/Mesh/MeshObjects" ),
new MeshObjectResource ( "Mesh Object 2" ))). Compile ();
loe . Mesh . MeshObjects . Add ( ref mo );
コードの処理内容は特に意味のないものですが、Mesh Object 1という名前のMesh ObjectをMeshに追加しています。このMesh Object 1が作成された後に、MeshにMesh Object 2という名前のMesh Objectをさらにトリガー処理により追加しています。
実行した結果は、Live Framework Resource Browserで確認するか、リソース読み取りのコードを記述して確認してみましょう。Mesh内に上記ふたつのMesh Objectが生成されていれば正しく動作していることがわかります。
Updateトリガー
続いて、Updateトリガーの例を示します。作成済みのMesh Objectを取得し、名前の変更を行います。その際にトリガーでNewsアイテムを追加します。
var mos = from m in loe . Mesh . MeshObjects . Entries
orderby m . Resource . Title
where m . Resource . Title == "Mesh Object 1"
select m ;
foreach ( var m in mos )
{
var news = new NewsItemResource ( "LiveMesh.AppMessagePost" );
news . Contexts . Add ( new NewsItemContext ( "Scope" , "LiveFX/MeshObject" , null , null , "LiveFX/MeshObject" ));
news . Contexts . Add ( new NewsItemContext ( "Target" , "text/plain" , null , null , null ));
var param = Statement . ResourceParameter ( typeof ( MeshObjectResource ));
m . Resource . Triggers . PostUpdateTrigger =
Statement . Sequence (
new Statement [] {
Statement . CreateResource < NewsItemResource >( "s1" , null , news ,
Statement . Bind ( "CollectionUrl" , param , "NewsFeedLink" ),
Statement . Bind ( "Request.Contexts[0].Text" , param , "Title" ),
Statement . Bind ( "Request.Contexts[1].Text" , param , "Title" ))},
param ). Compile ();
m . Resource . Title = "Mesh Object 1 - Renamed" ;
m . Update ();
}
Newsアイテムの生成とResource Scriptの内容が少し複雑になっていますが、Mesh Objectのトリガーを設定して、リソース操作(ここでは更新)を行っているところはCreateトリガーの場合と特に変わりありません。
Resource Scriptの記述中にこれまでに登場していないResourceParameterという記述があります。Newsアイテムを生成するとき、そのNewsアイテムの追加先はMesh ObjectのNewsFeedLinkです。また、Newsアイテムのメッセージ内容には変更されたMesh Objectの名前を設定しています。これらを指定するためにResourceParameterを使用しています。コード中のようにStatement.Bindメソッドの引数とStatement.CreateResourceメソッドの引数に指定することで、foreach内の変数m(MeshObject型)のプロパティ値をStatement内で使用できます。
このコードを実行すると、Mesh Object 1というMesh Objectが、「 Mesh Object 1 – Renamed」という名前に変更され、トリガーによりMesh Object内のNewsに新しい項目が追加されているはずです。実行の確認はResource Browser等を用いる必要がありますが、名前の変更対象にLive MeshフォルダーのMesh Objectを選択するようにコードを変更すると、Live DesktopのMesh barのNewsで実行動作の確認が可能です。
トリガーの制限
Live Framework CTPの未実装による制限か仕様かは明確になっていませんが、Create・Updateトリガーは、リソースの生成と更新を行う前に毎回設定する必要があります。つまり、トリガーを設定したリソースの生成・更新を行うと、そのリソースに設定されていたトリガーはトリガー処理後に解除され、リソースはトリガーが設定されていない状態になります。特にUpdateの際も毎回設定する必要があり、現在は活用しづらいものになっています。
Delete
Deleteトリガーについても確認してみましょう。Deleteトリガーの場合、Create・UpdateトリガーのようにDelete前にトリガーの設定をする必要はありません。Mesh Objectの生成時にDeleteトリガーをしておくと、そのMesh Objectが削除されるときに何かしらの処理を行うことができます。以下にコードを示します。
var mo = ( from m in loe . Mesh . MeshObjects . Entries
where m . Resource . Title == "Mesh Object 2"
select m ). First < MeshObject >();
var news = new NewsItemResource ( "LiveMesh.AppMessagePost" );
news . Contexts . Add ( new NewsItemContext ( "Scope" , "LiveFX/MeshObject" , null , null , "LiveFX/MeshObject" ));
news . Contexts . Add ( new NewsItemContext ( "Target" , "text/plain" , null , null , null ));
var param = Statement . ResourceParameter ( typeof ( MeshObjectResource ));
mo . Resource . Triggers . PreDeleteTrigger =
Statement . Sequence (
new Statement [] {
Statement . CreateResource < NewsItemResource >( "s1" , new Uri ( "https://user-ctp.windows.net/V0.1/Mesh/News" ), news ,
Statement . Bind ( "Request.Contexts[0].Text" , param , "Title" ),
Statement . Bind ( "Request.Contexts[1].Text" , param , "Title" ))},
param ). Compile ();
mo . Update ();
Mesh内からMesh Object 2という名前のMesh Objectを取得し、Deleteトリガーを設定しています。Mesh Objectが削除されたとき、そのMesh Objectの名前をメッセージとしたNewsアイテムをMeshのMesh全体のNewsコレクションに追加します。
実行し、Resource Browserで確認すると、対象のMesh ObjectのPreDeleteTriggerにトリガーが設定されたことがわかります(図1 ) 。
図1 PreDeleteTrigger
以下のコードのように対象のMesh Objectを削除すると、Newsに新しいNewsアイテムが追加されます。
var mo = ( from m in loe . Mesh . MeshObjects . Entries
where m . Resource . Title == "Mesh Object 2"
select m ). First < MeshObject >();
loe . Mesh . MeshObjects . Remove ( mo );
Scriptの実行結果
これまでResource Scriptを実行し、その処理の結果については確認していませんでした。トリガーに指定したResource Scriptは、Scriptの実行が成功したかどうかは判断する手段が用意されていませんが、通常のScriptの実行では、Scriptの実行結果を処理することができます。最後にこの方法について確認しておきましょう。
たとえば以下のScriptを実行します。Mesh Objectを連続で3個生成します。
var url = new Uri ( "https://user-ctp.windows.net/V0.1/Mesh/MeshObjects" );
var script = Statement . Sequence
(
Statement . CreateResource < MeshObjectResource >( "s1" , url , new MeshObjectResource ( "Mesh Object 1" )),
Statement . CreateResource < MeshObjectResource >( "s2" , url , new MeshObjectResource ( "Mesh Object 2" )),
Statement . CreateResource < MeshObjectResource >( "s3" , url , new MeshObjectResource ( "Mesh Object 3" ))
). Compile ();
var creds = new NetworkCredential ( User . UserId , User . Password );
script . RunAtServer ( creds );
Scriptの実行結果は、ResourceScriptクラスのResultプロパティから取得できます。CreateResourceStatement<TResource>クラスなどリソース操作を行うStatementクラスは、WebOperationStatementというクラスを継承しています。このクラスはScript実行結果の内容を表す汎用的なプロパティを持っていて、Statementごとに実行結果の判断が可能です。
上記コードの場合、次のようにして、実行されたCreateResourceStatementオブジェクトを取得できます。
var createStatements = from c in script . Result . Children
. Where ( n => n . GetType (). Name . Contains ( "CreateResource" ))
select c ;
単純に各Statementの実行が成功したか否かは次のように処理します。WasOperationSuccessfulはWebOperationStatementクラスのプロパティです。
foreach ( CreateResourceStatement < MeshObjectResource > c in createStatements )
{
if ( c . WasOperationSuccessful )
{
Console . WriteLine ( c . Name + " is successful" );
}
}
実行したStatementの型にキャストしているため、Statementオブジェクトの持つプロパティを参照して次のように詳しい実行結果を得ることも可能です。今回のコードはすべてCreateResourceStatement<MeshObjectResource>型のため一律にキャストできますが、複数のStatementの型を使用したScriptの場合は、それぞれの型に合わせてキャストする必要があります。
foreach ( CreateResourceStatement < MeshObjectResource > c in createStatements )
{
if ( c . WasOperationSuccessful )
{
Console . WriteLine ( c . Response . Title + "'s link is " + c . Response . SelfLink . ToString ());
}
}
Responseプロパティは、CreateResourceStatement<TResource>クラスのプロパティです。
Resource Scriptの実行に関して、Statementの文法の間違いは、ある程度C#のコンパイルまたは実行時エラーにて判断することができます。実行時に注意する点として、Resource Scriptの制限のため時間のかかる処理を実行することはできません。
12回 から3回に渡ってResource Scriptについて紹介しました。紹介できたものは、ほんの一部でしたが、Live FrameworkとResource Scriptのおもしろさを少しでも伝えられたら幸いです。Resource Scriptはドキュメントが少なく、より詳しく知るには試行錯誤が必要だと思いますが是非試してみてください。