Resource Scripts in the Live Framework

Resource scripts are small programs that can run directly in the LOE, either locally or in the cloud, to simplify processing of live item resources. An immediate benefit is that resource scripts can reduce the number of POSTS required to perform a task. For example, something that might require an extended sequence of POSTS when invoked using the Live Framework API might involve only a single POST when done using Resource Scripts.

A resource script is a collection of statements where each statement is an instance of a class derived from the Statement class in the Microsoft.LiveFX.ResourceModel.Scripting namespace. The resource script is compiled into the script language expected by the server before being POSTed to the cloud or local LOE. Resource scripts are invoked on the cloud LOE by POSTing their compiled form to https://user-ctp.windows.net/V0.1/Script. If desired, resource scripts can be serialized as POX or JSON files and saved for future use.

The Live Framework API documentation for resource scripts is deeper than most of the documentation but has not yet been updated to take account of the significant changes in the April 2009 CTP release. This is unfortunate because Resource Scripts appears to be the functionality most affected by the new release. There is a very useful document About Live Framework Resource Scripts (pdf) from the LiveFX team but this also has not yet been updated. Oran Dennison has a number of posts introducing his Fluent MeshScripts library (download): MeshScript Queries, Live Items and Magic; and MeshScript Ideas for the Future. I did an earlier post on Data Binding in Live Framework Resource Scripts.

UPDATE 3/18/2009

I added a section at the end of the post describing the differences between the StatementParameter and StatementBinding parameters to the Statement factory methods.

I also added an update to the ConditionalStatement section confirming a ConditionalStatement bug in the April 2009 Live Framework CTP.

Statement Class

The Statement class is declared:

public abstract class Statement : IDisposable {
    // Constructors
    public Statement(String name);

    // Properties
    public String Description { get; set; }
    public String Name { get; }
    public CompoundStatement Parent { get; }

    // Methods
    public Statement FindStatement(String statementToFind);
    public void SaveAsJSon(Stream outputStream);
    public void SaveAsJSon(String filePath);
    public void SaveAsPlainOldXml(XmlWriter writer);
    public void SaveAsPlainOldXml(String filePath);
    public void SaveAsPlainOldXml(Stream stream);

    // About 50 methods forming the core of Resource Script creation

    // Implemented Interfaces and Overridden Members
    void IDisposable.Dispose();
}

This class mixes a large number of important static methods with a small number of instance methods which tend to get lost in the class declaration. The static methods are primarily factory methods to create instances of the classes derived from Statement. There are also a few static methods supporting dynamic binding for resource scripts.

A typical factory method is:

public static CreateResourceStatement<TResource> CreateResource<TResource>(String name,
     params StatementBinding[] bindings) where TResource : Resource;

which creates an instance of type CreateResourceStatement<TResource> named name using the dynamic bindings specified in bindings.

There are several different factory methods for each derived class.

An example binding method is:

public static StatementBinding Bind(String targetProperty, String sourceStatement, String sourceProperty);

which specifies runtime binding of the value at sourceProperty of the sourceStatement to the targetProperty. Note that the April 2009 CTP deleted most of the classes used in runtime binding.

There are two distinct inheritance trees, serving different purposes, derived from Statement . Live Item Resource statements are derived through WebOperationStatement. Control Flow statements are derived from Statement through the CompoundStatement class. The About Live Framework Resource Scripts document refers to Live Item Resource statements as AtomPub and FeedSync Primitives. As indicated by their names, Control Flow statements allow control over the statements in a resource script while Live Item Resource statements are the statements that actually do the work of modifying the contents of the user’s live mesh.

Live Item Resource Statements

The Live Item Resource statements are all derived from WebOperationStatement declared as:

public abstract class WebOperationStatement : Statement, IDisposable {
    // Fields
    protected|internal Object request;
    protected|internal Object response;

    // Properties
    public ErrorResource Error { get; }
    public String ETag { get; }
    public String IfMatch { get; }
    public DateTime IfModifiedSince { get; }
    public String IfNoneMatch { get; }
    public DateTime LastModified { get; }
    public String Location { get; }
    public Boolean MethodOverride { get; }
    public IDictionary<String,String> RequestHeaders { get; }
    public CookieCollection ResponseCookies { get; }
    public IDictionary<String,String> ResponseHeaders { get; }
    public HttpStatusCode StatusCode { get; }
    public String StatusDescription { get; }
    protected|internal Uri TargetUrl { get; }
    public Boolean ThrowOnError { get; }
    public Boolean WasOperationSuccessful { get; }
}

These properties match the request and response data POSTed as the resource script.

The Live Item Resource Statement classes are:

(Note that there is currently no documentation for the classes without a link in the above list. They are not "undocumented" classes they are just not documented.)

CreateResourceStatement<TResource>, ReadResourceStatement<TResource>, UpdateResourceStatement<TResource> and DeleteResourceStatement perform CRUD operations on Live Items stored in the Live Mesh while ReadResourceCollectionStatement<TResource> retrieves collections of Live Items. TResource is constrained to be one of the Live Item Resource classes derived from Resource such as MeshObjectResource or DataEntryResource.

CreateMediaResourceStatement, ReadMediaResourceStatement, UpdateMediaResourceStatement and DeleteMediaResourceStatement perform CRUD operations on a Live Item media resource.

SynchronizeResourceCollectionStatement performs a synchronization operation between the cloud LOE and the local device LOE.

For example, the CreateResourceStatement class is declared

public sealed class CreateResourceStatement<TResource> : WebOperationStatement  where TResource : Resource {
    // Properties
    public Uri CollectionUrl { get; }
    public TResource Request { get; }
    public TResource Response { get; }
}

There is no constructor, so an instance of this class can be created only by invoking an appropriate factory method in the Statement class. For example:

Statement.CreateResource<MeshObjectResource>("CreateMeshObject", meshObjectsUri, meshObjectResource, null),

invokes

public static CreateResourceStatement<TResource> CreateResource<TResource>(String name, Uri collectionUrl,
     TResource request, params StatementBinding[] bindings) where TResource : Resource;

to create a statement of type CreateResource<MeshObjectResource> named CreateMeshObject. When invoked on the LOE, the resource script will POST the MeshObjectResource defined in meshObjectResource to the mesh objects collection specified in meshObjectsUri. This particular method call does not use dynamic binding so the bindings parameter is null. A successful invocation of the script will create a new mesh object defined by meshObjectResource.

Dynamic binding allows the resource script to get or set any of the properties defined in the Live Item Editing statement or the base Statement class. For example, ignoring the properties of the Statement class for the sake of simplicity, the CollectionUrl and Request properties of the CreateResourceStatement<TResource> class could be set during binding while the CollectionUrl, Request and Resource properties could all be got during binding of a later statement.

For example:

StatementBinding dataEntryStatementBinding = Statement.Bind("CollectionUrl", "CreateDataFeed",
     "Response.DataEntriesLink");

Statement.CreateResource<DataEntryResource>("CreateDataEntry", null, dataEntryResource, dataEntryStatementBinding)

In this case, the CollectionUrl property of the CreateResourceStatement<DataEntryResource> is set at runtime to the Response.DataEntriesLink property of the CreateResource<DataFeed> statement named CreateDataFeed (not shown). This allows a resource script to create a Live Item in one statement which can then be used by a later statement.  Although I have explicitly declared a StatementBinding object in this example, the StatementBinding could have been done inline in the Statement.CreateResource call.

Note that although the Live Item Editing statements can be created independently there is no way to compile them separately from a Control Flow statement . There must be at least one Control Flow statement in each resource script.

Control Flow Statements

The Control Flow statements are all derived from CompoundStatement declared as:

public abstract class CompoundStatement : Statement {
    // Constructors
    public CompoundStatement(String name, Statement[] statements);
    public CompoundStatement(String name);
    public CompoundStatement();

    // Properties
    protected|internal ReadOnlyCollection<Statement> Statements { get; }
}

The Control Flow statement classes are:

The LoopStatement class is new in the April 2009 CTP and is not yet documented.

SequenceStatement

The simplest Control Flow statement is the SequenceStatement which executes a sequence of child statements in order. The InterleaveStatement executes a set of child statements (branches) in an "interleaved manner and waits for all the children to complete." The documentation on InterleaveStatements is not as clear as it should be and my understanding of the phrase in quotes is that different branches of an interleave statement could be invoked simultaneously. The ConditionalStatement class supports simple if/else capability allowing a runtime decision to be taken as to which statements to invoke in the resource script. The LoopStatement class allows statements to be looped over in a resource script so that a single statement can be invoked multiple times.

The SequenceStatement class is declared:

public class SequenceStatement : CompoundStatement {
    // Properties
    public IEnumerable<Statement> Children { get; }

    // Implemented Interfaces and Overridden Members
    public ResourceScript<SequenceStatement> Compile();
    public ReadOnlyCollection<StatementParameter> Parameters { get; }
}

An instance of a SequenceStatement is created using one of the following factory methods in the Statement class:

public static SequenceStatement Sequence(String name, Statement[] children, params StatementParameter[] parameters);
public static SequenceStatement Sequence(params Statement[] children);
public static SequenceStatement Sequence(String name, params Statement[] children);
public static SequenceStatement Sequence(Statement[] children, params StatementParameter[] parameters);

The following example uses the factory method taking only a params Statement[] parameter to create a SequenceStatement with five child Live Item Resource statements:

SequenceStatement sequence = Statement.Sequence(
    Statement.CreateResource<MeshObjectResource>("CreatMeshObject", meshObjectsUri, meshObjectResource, null),
    Statement.CreateResource<DataFeedResource>("CreateDataFeed", null, dataFeedResource, dataFeedStatementBinding),
    Statement.CreateResource<DataEntryResource>("CreateDataEntry", null, dataEntryResource, dataEntryStatementBinding),
    Statement.ReadResourceCollection<DataEntryResource>("ReadDataEntryResourceCollection",
         resourceCollectionStatementBinding),
    Statement.ReadResource<DataEntryResource>("ReadDataEntryResource", readDataEntryStatementBinding)
);

This sequence statement creates a MeshObject, a DataFeed attached to it and then adds a DataEntry to the DataFeed. Finally, the DataEntries are retrieved from the DataFeed and a DataEntryResource retrieved from the DataFeed. Dynamic binding is used to link the various Live Item Resource statements.

InterleaveStatement

An instance of an InterleaveStatement is created similarly to the SequenceStatement. A set of SequenceStatements can be interleaved by assigning each SequenceStatement to a branch of the InterleaveStatement.

ConditionalStatement

A ConditionalStatement allows a choice to be made between invoking one statement or another based on the result of an Expression<Func<Statement,Boolean>> condition. The alternate statements could be other Control Flow statements, such as SequenceStatement, or Live Item Resource statements such as CreateResourceStatement<TResource>.

There are two factory methods on the Statement class that create instances of a ConditionalStatement:

public static ConditionalStatement Conditional(Expression<Func<Statement,Boolean>> condition,
     Statement ifBranch, Statement elseBranch, params StatementParameter[] parameters);

public static ConditionalStatement Conditional(String name, Expression<Func<Statement,Boolean>> condition,
     Statement ifBranch, Statement elseBranch, params StatementParameter[] parameters);

The only difference between these is that one creates a named ConditionalStatement. Otherwise, they both take a condition of type Expression<Func<Statement,Boolean>> and invoke the Statement ifBranch when condition is true or the Statement elseBranch when condition is false. Some parameters can be passed into the ConditionalStatement through parameters.

The following is an example of a ConditionalStatement inside a SequenceStatement:

SequenceStatement sequence = Statement.Sequence(
                Statement.ReadResourceCollection<DataEntryResource>("ReadDataEntries", dataEntriesUri, null, false, null),
                Statement.Conditional(
                statement =>
                    (
                        from de in (((ReadResourceCollectionStatement<DataEntryResource>)statement.FindStatement
                             ("ReadDataEntries")).Response).Entries
                        where de.Title == "London Calling"
                        select de
                    ).Count() != 0,
                Statement.CreateResource<DataEntryResource>(dataEntriesUri, ifDataEntryResource),
                Statement.CreateResource<DataEntryResource>(dataEntriesUri, elseDataEntryResource)));

The intent of this example is that the ReadResourceCollectionStatement named ReadDataEntries retrieves the data entries at the URI identified by dataEntriesUri. Then a ConditionalStatement is invoked that searches for the Statement named ReadDataEntries and checks if that found a DataEntry named London Calling. If found, a DataEntry is created from ifDataEntryResource otherwise it is created from elseDataEntryResource.

The problem is that this SequenceStatement will not compile as written. It will compile, and behave as expected,  if the condition is replaced by something trivial like:

statement => true

When the full example is compiled, however, an unhandled InvalidOperationException is thrown with the error message:

Validation Errors were found:
error 0: ConditionalStatement. — Type ‘System.Linq.Enumerable’ is not supported in expression specified for property ‘Condition’.

There is, of course, a strong possibility that this exception is a result of user error. However, given that the example is similar to the example provided in the About Live Framework Resource Scripts document and an example on Mike Taulty’s blog there is the possibility of a bug. The April 2009 CTP changed the signature of Statement.ReadResourceCollection so the problem could lie in an incorrect use of that method.

UPDATE 3/18/2009

Ben Williams of Microsoft has confirmed that this is a bug introduced in the April 2009 Live Framework CTP. He filed a bug report on Microsoft Connect.

LoopStatement

A LoopStatement allows a single Statement to be looped over a number of times or until a condition has been satisfied. The Statement class has three factory methods creating instances of LoopStatement:

public static LoopStatement Loop(String name, Statement body, Int32 count,
     Expression<Func<Statement,Boolean>> terminateCondition, Object[] iterationData, StatementBinding[] bindings,
     params StatementParameter[] parameters);

public static LoopStatement Loop(String name, Statement body, Int32 count,
     Expression<Func<Statement,Boolean>> terminateCondition, Object[] iterationData,
     params StatementBinding[] bindings);

public static LoopStatement Loop(String name, Statement body, Int32 count,
     Expression<Func<Statement,Boolean>> terminateCondition, params StatementBinding[] bindings);

The LoopStatement is named name. The LoopStatement invokes the Statement body a total of count times or until the terminateCondition is satisfied. iterationData allows data to be inserted into individual passes through the loop while bindings, as usual, allows runtime binding and parameters provide another means of inserting data into the loop.  Note that the maximum value for count is 50.

An example of a LoopStatement in action is:

String[] songTitles = { "Silence", "Hunter", "Nylon Smile", "The Rip", "Plastic", "We carry on", "Deep Water", "Machine Gun",
     "Small", "Magic Doors", "Threads" };

DataEntryResource dataEntryResource = new DataEntryResource();
dataEntryResource.Type = "Song";

StatementBinding iterationStatementBinding = Statement.Bind("Request.Title", "loopStatement", "CurrentIterationData",
     typeof(String));

LoopStatement loopStatement = Statement.Loop(
    "loopStatement",
    Statement.CreateResource<DataEntryResource>("CreateDataEntry", dataEntriesUri, dataEntryResource,
          iterationStatementBinding),
    songTitles.Count(),
    null,
    songTitles,
    null);

This LoopStatement loops over a CreateResourceStatement and creates a DataEntry cloned from the DataEntryResource named dataEntryResource. It performs one loop for each song title in the array songTitles which is made available to the loop through the specification of songTitles as the iterationData parameter. The iterationStatementBinding then associates each song title in songTitles, as CurrentIterationData, with the DataEntryResource Title identified as Request.Title.

This example demonstrates the power of LoopStatements. The LoopStatement makes up a complete Resource Script, creating 12 separate data entries, which can be compiled and then submitted to the LOE in a single POST. It otherwise requires 12 POSTs to new up 12 data entries and add them to a DataFeed.

Compiling and Running Resource Scripts

Resource Scripts must be compiled before they can be POSTed to either the cloud or local LOE and executed. Each of the Control Flow Statements, (SequenceStatement, InterleaveStatement, ConditionalStatement and LoopStatement) has a Compile method defined, for example, as follows:

public ResourceScript<SequenceStatement> Compile();

The ResourceScript<TStatement> class is declared:

public sealed class ResourceScript<TStatement> : ResourceScript, IServiceProvider  where TStatement : Statement {
    // Properties
    public TStatement Result { get; }
    public TStatement Source { get; }

    // Methods
    public static ResourceScript<TStatement> LoadFromJSon(String filePath);
    public static ResourceScript<TStatement> LoadFromJSon(Stream jsonStream);
    public static ResourceScript<TStatement> LoadFromPlainOldXml(Stream stream);
    public static ResourceScript<TStatement> LoadFromPlainOldXml(String filePath);
    public static ResourceScript<TStatement> LoadFromPlainOldXml(XmlReader poxReader);
    public void Run(NetworkCredential credential);
    public void Run(CookieContainer cookies);
    public void Run(String authToken, AuthenticationTokenType tokenType);
    public void RunAsync(NetworkCredential credential, Action callback);
    public void RunAsync(String authToken, AuthenticationTokenType tokenType, Action callback);
    public void RunAsync(CookieContainer cookies, Action callback);
    public void RunAtServer(NetworkCredential credential);
    public void RunAtServer(CookieContainer cookies, Uri scriptUrl);
    public void RunAtServer(String authToken, AuthenticationTokenType tokenType, Uri scriptUrl);
    public void RunAtServerAsync(NetworkCredential credential, Action callback);
    public void RunAtServerAsync(String authToken, AuthenticationTokenType tokenType, Uri scriptUrl, Action callback);
    public void RunAtServerAsync(CookieContainer cookies, Uri scriptUrl, Action callback);
    public void SaveAsJSon(String filePath);
    public void SaveAsJSon(Stream outputStream);
    public void SaveAsPlainOldXml(String filePath);
    public void SaveAsPlainOldXml(Stream stream);
    public void SaveAsPlainOldXml(XmlWriter writer);
}

The ResourceScript<TStatement> class provides several methods allowing resource scripts to be invoked both synchronously and asynchronously either in the local LOE or the cloud LOE. It additionally provides serialization methods allowing compiled resource scripts to be saved and loaded in various formats.

The following demonstrates the compilation and invocation on the cloud LOE of a SequenceStatement:

String scriptsEndPoint = "https://user-ctp.windows.net/V0.1/Script";
NetworkCredential networkCredential = new NetworkCredential(USER_NAME, PASSWORD, scriptsEndPoint);

SequenceStatement sequenceStatement = Statement.Sequence(… );

ResourceScript<SequenceStatement> resourceScript = sequenceStatement.Compile();

resourceScript.RunAtServer(networkCredential);

Results of Resource Scripts

As each statement in a resource script is processed in the LOE it generates a result describing the outcome of the statement. These results can be accessed through the Result property of the ResourceScript<TStatement> class. Result has type TStatement.

The Live Framework API documentation has a pretty detailed example of retrieving results from a script operation. The following shows an example of a SequenceStatement being created, compiled and executed followed by the retrieval of the results from the final ReadResourceStatement<DataEntryResource> in the SequenceStatement. Finally, the StatusCode and StatusDescription of the statement are retrieved:

SequenceStatement sequence = Statement.Sequence(
     Statement.CreateResource<MeshObjectResource>("meshObjectResource", meshObjectsUri, meshObjectResource, null),
     Statement.CreateResource<DataFeedResource>("dataFeedResource", null, dataFeedResource, dataFeedStatementBinding),
     Statement.CreateResource<DataEntryResource>("dataEntryResource", null, dataEntryResource, dataEntryStatementBinding),
     Statement.ReadResourceCollection<DataEntryResource>("readDataEntryResourceCollection", resourceCollectionStatementBinding),
     Statement.ReadResource<DataEntryResource>("readDataEntryResource", readDataEntryStatementBinding));

ResourceScript<SequenceStatement> resourceScript = sequence.Compile();

resourceScript.RunAtServer(networkCredential);

List<Statement> statements = new List<Statement>(resourceScript.Result.Children);

ReadResourceStatement<DataEntryResource> createdDataEntryResource = (ReadResourceStatement<DataEntryResource>)resourceScript.Result.FindStatement("readDataEntryResource");

HttpStatusCode statusCode = createdDataEntryResource.StatusCode;
String statusDescription = createdDataEntryResource.StatusDescription;

Statement Parameters and Statement Bindings

The really careful reader might have noticed that some of the Statement factory methods have a  StatementBinding[] parameter while others have a StatementParameter[] parameter. In fact, one of the Statement.Loop factory methods takes both StatementBinding[] and a StatementParameter[] parameters.

There is a fairly simple distinction in the use of these two parameters. Live Item Resource statements take a StatementBinding[] parameter while Control Flow Statements take a StatementParameter[] parameter. The Statement.Loop factory method possibly supports both because a LoopStatement tightly relates a Control Flow Statement with an enclosed Live Item Resource statement and having both a StatementBinding[] and and StatementParameter[] allows somewhat concise code.

Every resource script must be contained in a Control Flow Statement, because there is no Compile() method on Live Item Resource statements. The StatementParameter[] parameter is used in Control Flow Statements to allow parameters to be passed into the resource script and accessed at resource script compile time. The StatementBinding[] parameter is used to allow binding of data to Live Item Resources at resource script compile time or runtime. The binding process has access to statement parameters. The content of the StatementParameter[] and StatementBinding[] parameters are contained in the representation of the resource script POSTed to the LOE.

The following is a complete example showing the use of StatementParameter and StatementBinding[] parameters:

public void ResourceScriptsExample()
{
     String cloudEndPoint = "https://user-ctp.windows.net/V0.1/";
     String scriptsEndPoint = cloudEndPoint + "Script";
     Uri meshObjectsUri = new Uri(cloudEndPoint + "Mesh/MeshObjects");

     // Create Live Item Resources
     MeshObjectResource meshObjectResource = new MeshObjectResource();
     meshObjectResource.Type = "Band";
     meshObjectResource.Title = "Gotan Project";

     DataFeedResource dataFeedResource = new DataFeedResource();
     dataFeedResource.Type = "SongsFeed";

     DataEntryResource dataEntryResource = new DataEntryResource();
     dataEntryResource.Type = "Song";
     dataEntryResource.Title = "Triptico";

     // Create and initialize StatementParameter[]
     StatementParameter[] parameters = new StatementParameter[1];
     parameters[0] = Statement.ObjectParameter("AlbumTitle", "La Revancha Del Tango");

     // Create and initialize Statement[]
     Statement[] statements = new Statement[3];

     statements[0] = Statement.CreateResource<MeshObjectResource>("meshObjectResource", meshObjectsUri,
          meshObjectResource, null);

     statements[1] = Statement.CreateResource<DataFeedResource>("dataFeedResource", null, dataFeedResource,
          Statement.Bind("CollectionUrl", "meshObjectResource", "Response.DataFeedsLink"),
          Statement.Bind("Request.Title", parameters[0]));

      statements[2] = Statement.CreateResource<DataEntryResource>("dataEntryResource", null, dataEntryResource,
           Statement.Bind("CollectionUrl", "dataFeedResource", "Response.DataEntriesLink"));

       // Create resource script Compound Statement
       SequenceStatement sequence = Statement.Sequence(statements, parameters);

       // Compile into resource script
       ResourceScript<SequenceStatement> resourceScript = sequence.Compile();

       // POST resource script to LOE
       NetworkCredential networkCredential = new NetworkCredential(Constants.UserName, Constants.Password,
            scriptsEndPoint);
       resourceScript.RunAtServer(networkCredential);

       // Look at results of resource script
       CreateResourceStatement<DataEntryResource> createdDataEntryResource =
            (CreateResourceStatement<DataEntryResource>)resourceScript.Result.FindStatement("dataEntryResource");
       HttpStatusCode statusCode = createdDataEntryResource.StatusCode;
       String statusDescription = createdDataEntryResource.StatusDescription;
  }

This example is structured somewhat differently from the earlier examples. The Statement[] and StatementParameter[] parameters to the Statement.Sequence factory method are both created before the factory method is invoked. Furthermore, data binding is performed inline during invocation of the Live Item Resource factory methods. This greatly simplifies the call to Statement.Sequence over that seen in earlier examples.

It looks as if the StatementParameter parameters[0] is consumed when statements[1] is created and that there should be no need to pass parameters into the Statement.Sequence method. However, failure to pass in this parameter leads to a runtime error when the resource script is compiled. Presumably, if parameters is not passed into the resource script then it is not available to the resource script compiler which can only access the information contained in the Control Flow statement being compiled.

Epilogue

Hopefully, this is useful information on Resource Scripts following on from my earlier post on Data Binding in Live Framework Resource Scripts. I will probably finish off this short series by looking at Triggers at some point in the future.

UPDATE – 3/30/2009

Ben Williams has done a post, Feeling a bit loopy, focussing on the Loop Statement.

About these ads

About Neil Mackenzie

Azure Architect at Satory Global.
This entry was posted in Uncategorized. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s