使ってみよう! Live Framework

第13回Resource Script(2)

はじめに

前回に続きResource Scriptについてです。今回は、リソースの生成・読み取り・更新・削除のリソース操作およびデータフローについて紹介します。また、最後にはシリアライズ化したResource Scriptを直接、Live Operating EnvironmentへHTTP POSTし、Scriptの処理動作を確かめてみたいと思います。

今回紹介しているコードはC#を使用しています。実際にコードを実行するには、適当なコンソールアプリケーション プロジェクトを作成し、Live Framework SDKの.NET Framework用のライブラリの参照を追加してください[1]⁠。

また、下記のように名前空間をインポートします。

using Microsoft.LiveFX.Client;
using Microsoft.LiveFX.ResourceModel;
using Microsoft.LiveFX.ResourceModel.Scripting;

リソースの作成

手始めにStatementを使用してリソースを作成してみましょう。リソースの作成には、Statement.CreateResource<TResource>メソッドを使用してCreateResourceStatement<TResource>オブジェクトを生成します。TResource部分にはリソースの型を指定します。Newsアイテムを作成する場合、次のようになります。Newsアイテムについては本連載第8回で紹介しています。

// News アイテムリソースの生成
var news = new NewsItemResource("LiveMesh.AppMessagePost");
news.Contexts.Add(new NewsItemContext("Target", "text/plain", "メッセージテスト", null, null));

// CreateResourceStatement の生成
var s1 = Statement.CreateResource<NewsItemResource>("s1", new Uri("https://user-ctp.windows.net/V0.1/Mesh/News"), news, null);

CreateResourceメソッドの戻り値がCreateResourceStatementオブジェクトで、上記コードで示した引数は最初から順に以下のプロパティに設定されます。

  1. CreateResourceStatementのNameプロパティ
  2. CreateResourceStatementのCollectionUrlプロパティ
  3. CreateResourceStatementのRequestプロパティ

CollectionUrlに指定したコレクション(ここではNewsアイテムコレクション)にRequestで指定したリソースを作成するという意味の構文になります。NameはCreateResourceStatement自身の名前です。Statementの名前は、複数のStatementを使用する場合に特定のStatementを指定するために使用します。

CreateResourceメソッドは、これ以外にもいくつかのオーバーロードが定義されています。リソースを作成するだけであればNameプロパティを指定しない記述も可能です。

var s1 = Statement.CreateResource<NewsItemResource>(new Uri("https://user-ctp.windows.net/V0.1/Mesh/News"), news);

CreateResourceStatementだけでは実行できないため、順次処理を表すSequenceStatementと組み合わせ、コンパイル後実行します。

// Sequence Statement の生成
var sequence = Statement.Sequence(s1);

// コンパイル
var script = sequence.Compile();

// 実行
var creds = new NetworkCredential("user@example.jp", "password", "https://user-ctp.windows.net/V0.1/Script");
script.RunAtServer(creds);

実行後、Live Framework ClientのNewsを確認すると図1のようにNewsアイテムが追加されたことがわかります。

図1 作成したNewsアイテム
図1 作成したNewsアイテム

上記のResource Scriptによるリソース作成は、Resource Scriptを使用しなかった場合、次のコードと同じ内容になります。

var creds = new NetworkCredential(User.UserId, User.Password);
var loe = new LiveOperatingEnvironment();
var news = new NewsItem("LiveMesh.AppMessagePost");
news.Resource.Contexts.Add(new NewsItemContext("Target", "text/plain", "メッセージテスト", null, null));
loe.Mesh.News.Add(ref news);

データフロー

リソースの作成はひとつのStatementで処理を完結することが可能でしたが、たいていのリソース操作では複数のStatementを組み合わせることが必要です。

例えば、Live Meshフォルダを作成する場合はMesh Objectを作成し、そのMesh ObjectにData Feedを作成・追加する必要があります。どちらもCreateResourceStatementを使用しますが、そのStatement同士の関連も記述する必要があります。このStatementの関係の記述をデータフローやデータの相互関係の記述と前回呼んでいました。

Live Meshフォルダを作成する例を示します。Mesh Objectの作成は先ほどのNewsアイテムの作成と同等です。

// Live Mesh フォルダとなる Mesh Object の作成
var folder = new MeshObjectResource("アルバム");
folder.Type = "LiveMeshFolder";
var s1 = Statement.CreateResource<MeshObjectResource>("s1", new Uri("https://user-ctp.windows.net/V0.1/Mesh/MeshObjects"), folder);

プロパティの連結

続いてData Feedを作成します。Data Feedを作成する先は、作成したMesh ObjectのData Feedコレクションです。そのコレクションを示すURLは、folder.DataFeedsLinkプロパティにあたりますが、folderはまだMeshへ追加されていないため値を参照してもnullです。

CreateResourceStatementクラスは、RequestResponseというリソース型のプロパティを持っています。それぞれStatement実行前のリソースと実行後のリソースの状態を表します。このプロパティを用いるとMesh Objectリソース作成後のData FeedコレクションURLは、s1.Response.DataFeedsLinkと表すことができます。

以上からData Feedリソースを作成するコードは次のようなイメージになります。ただし、これもまだ動作しません。

// 間違った Data Feed 作成のStatement 記述
var s2 = Statement.CreateResource<DataFeedResource>("s2", s1.Response.DataFeedsLink, df);

このコードが処理される時点では、まだMesh Object作成処理は実行されていないためs1のResponseの値もnullになっています。ここで使用するのが、Statement.BindメソッドとStatementBindingクラスです。Statement.Bindメソッドの戻り値がStatementBindingです。

Data Feedを作成する例では、CollectionUrlを指定する引数部分をnullとし、後半の引数にStatementBindingオブジェクトを次のように追加します。

var s2 = Statement.CreateResource<DataFeedResource>("s2", null, df, Statement.Bind("CollectionUrl", "s1", "Response.DataFeedsLink"));

Statement.Bindの引数には、s2の対象となるプロパティ、参照先のStatementの名前(s1⁠⁠、参照先のプロパティを指定します。これによって上記コードは、s2のCollectionUrlにs1.Response.DataFeedLinkを設定するという構文になります。StatementBindingを使用するとResource Scriptが実行される際に評価されるため、結果として正しく動作します。Statement.Bindメソッドはこのほかにも多数のオーバーロードが定義されています。

ふたつのStatementを順次処理するとLive Meshフォルダが作成されます。

var sequence = Statement.Sequence(s1, s2);
var script = sequence.Compile();
var creds = new NetworkCredential("user@example.jp", "password", "https://user-ctp.windows.net/V0.1/Script");
script.RunAtServer(creds);

式の連結

以上のStatementBindingはStatementのプロパティ同士を結び付ける例でした。これ以外にもプロパティへ値を代入する使用方法もあります。

リソース読み取りの例をみてみましょう。リソースの読み取りは、Statement.ReadResource<TResource>メソッドを使用してReadResourceStatement<TResource>クラスを生成します。次の例はプロパティの連結と同じタイプのStatementBindingです。

var s3 = Statement.ReadResource<MeshObjectResource>("s3", Statement.Bind("EntryUrl", "s1", "Response.SelfLink"));

Statement s1の結果を読み取るStatementです。ReadResourceStatementクラスはEntryUrlプロパティに読み取るリソースのURLを指定する必要があります。StatementBindingを使用して、s1のResponse.SelfLinkプロパティの値を指定しています。

次に、読み取ったリソースのタイトルを変更してみましょう。Statement.UpdateResource<TResource>メソッドとUpdateResourceStatement<TResource>クラスを用います。

var s4 = Statement.UpdateResource<MeshObjectResource>("s4",
    Statement.Bind("Request", "s3", "Response"),
    Statement.Bind("Request.Title", System.Linq.Expressions.Expression.Constant("アルバムその2")));

このコードではふたつのStatementBindingオブジェクトを指定しています(CreateResourceメソッドなどリソース操作のStatementのメソッドに指定できるStatementBindingは可変の引数として定義されています⁠⁠。

ひとつ目のStatementBindingは、s4のRequestにs3.Responseを設定し、更新対象のリソースを指定しています。ふたつ目のStatementBindingが、更新対象のリソースのTitleプロパティに定数を指定しています。これがStatementBindingを使用したプロパティへの値の代入です。

これまでに作成したStatement s1からs4を順次実行すると、結果的に「アルバムその2」というLive Meshフォルダが作成されます。

var sequence = Statement.Sequence(s1, s2, s3, s4);
var script = sequence.Compile();
script.RunAtServer(creds);

実行後、Live Desktopで確認してみましょう図2⁠。

図2 作成されたLive Meshフォルダ
図2 作成されたLive Meshフォルダ

最後に基本操作となるリソース削除についてもふれておきます。Statement.DeleteResourceメソッドとDeleteResourceStatementクラスを使用します。どちらもこれまでと違いジェネリック型ではありません。

var s5 = Statement.DeleteResource("s5", Statement.Bind("EntryUrl", "s1", "Response.SelfLink"));

DeleteResourceStatementクラスのEntryUrlに削除するリソースのURLを指定します。上の例では、Statement s1で作成したリソースを削除してしまっているのでs1からs5を実行すると結果的に何も起きないScriptになります。

シリアライズ

前回Resource Scriptのサーバーサイドでの実行について、アプリケーションは(ライブラリによって)Resource ScriptをXMLやJSON形式にシリアライズ化して、Live Operating EnvironmentにHTTP POSTメソッドにより渡していると紹介しました。

このシリアライズ化した内容は簡単に確認することができます。ResourceScriptオブジェクトのSaveAsJSonまたはSaveAsPlainOldXmlメソッドを使用して各形式の内容を取得します。先ほどのまでのコードの例を用いると次のようになります。

var sequence = Statement.Sequence(s1, s2);
var script = sequence.Compile();
script.SaveAsJSon("script.txt"); // JSON 形式
script.SaveAsPlainOldXml("script.xml"); // XML 形式

実行するとふたつのファイルが保存されます。作成されたファイルのうちのひとつJSON形式は以下のような内容です。コードは見やすいよう整形しています。

{
    __type: ResourceScriptOfSequence:http://user.windows.net (string)
    ,Source: {
        __type: Sequence:http://user.windows.net (string)
        ,Children: [
            {
                __type: CreateResourceOfMeshObject:http://user.windows.net (string)
                ,Bindings: []
                ,Name: s1 (string)
                ,Request: {
                    __type: MeshObject:http://user.windows.net (string)
                    ,Title: アルバム (string)
                    ,Triggers: {}
                    ,Type: LiveMeshFolder (string)
                }
                ,ThrowOnError: true (boolean)
                ,CollectionUrl: https://user-ctp.windows.net/V0.1/Mesh/MeshObjects (string)
            }
            ,{
                __type: CreateResourceOfDataFeed:http://user.windows.net (string)
                ,Bindings: [
                    {
                        __type: PropertyBinding:http://user.windows.net (string)
                        ,Target: s2 (string)
                        ,TargetProperty: CollectionUrl (string)
                        ,Source: s1 (string)
                        ,SourceProperty: Response.DataFeedsLink (string)
                    }
                ]
                ,Name: s2 (string)
                ,Request: {
                    __type: DataFeed:http://user.windows.net (string)
                    ,Title: DataFeedResource (string)
                    ,Triggers: {}
                    ,Type: LiveMeshFiles (string)
                }
                ,ThrowOnError: true (boolean)
            }
        ]
        ,Parameters: []
    }
}

今回のコードには出てきていないパラメータも記述されていることがわかります。

このテキストをLive Operating EnvironmentにHTTP POSTメソッドで渡すとRunAtServerメソッドと同様のことが可能です。実際にC#以外の言語を使用して試してみましょう。

PHPによるHTTP POSTメソッドによりファイル内容を送信するコードを示します。コードと同じディレクトリにC#で出力したJSON形式のテキストファイルscript.txtがあるものとします。

<?php
$auth = 'Resource Browser から Request Headers 内の Authorization の値をコピー';
$opts = array(
        'http' => array(
                'method'  => 'POST',
                'content' => @file_get_contents('script.txt'),
                'header'  => 
                        "acceptt: application/json\r\n" .
                        "content-type: application/json\r\n" .
                        "Authorization: " . $auth . "\r\n"));

@file_get_contents('https://user-ctp.windows.net/V0.1/Script', 
                                   false, @stream_context_create($opts));
?>

実行にあたりひとつ問題があります。クラウド上のLive Operating Environmentにアクセスする場合は、認証が必要です。HTTPのリクエストヘッダーにAuthorizationの値を設定しなくてはいけません。今回は簡易的に、この値はLive Framework SDK付属のツール、Live Framework Resource Browserを使い、実際のリクエストヘッダーから値をコピーすることにしましょう。

Live Framework SDKのToolsフォルダ以下(%programfiles%\Microsoft SDKs\Live Framework\v0.91\Tools\Live Framework Resource Browser)に、Resource Browserがあります。これを実行して、クラウドのLive Operating Environmentに接続します図3⁠。

図3 LOEへの接続
図3 LOEへの接続

図中のリンクをクリックするとWindows Live IDアカウントの入力が求められ、サインイン可能です。次にウィンドウ右上の「Show Headers」をクリックし、表示されたタブの「Request Headers」を確認します図4⁠。

図4 リクエストヘッダーの確認
図4 リクエストヘッダーの確認

ここで実際にLive Operating Environmentへ送信しているリクエストヘッダーを見ることができます。ここにある「Authorization:」以下の値をコピーして、PHPのコードの$auth変数の値に設定します。

以上でPHPのコードを実行すると、Live Meshフォルダが作成されます。実際に試してみましょう。

PHP等の言語でLive Framework SDKのライブラリを使用せずLiveサービスへのアクセスも可能ですが、単純なHTTPとのやりとりを記述する必要があり、たいへんな作業です。このようにResource Scriptを使用すれば、あらかじめC#を用いてリソース操作をシリアライズ化しておき、そのファイルを利用することで簡単にほかの言語からもLiveサービスへのアクセスが可能になります。


今回は以上です。いかがでしたでしょうか。次回はResource Scriptの特長のひとつでもあるトリガーによる処理についてです。

おすすめ記事

記事・ニュース一覧