Archive for the ‘FluorineFx’ Category.

Silverlight and Flash/Flex RPC with the FluorineFx Silverlight library

The last post of the Silverlight series that should have been the first one…

Network Security Access Restrictions in Silverlight 2
If the case of RPC over HTTP the Silverlight 2 runtime will try to download a security policy file using the HTTP protocol. The runtime tries to download a Silverlight policy file with a name of “clientaccesspolicy.xml” at the root of the requested target domain using the HTTP protocol (it also accepts “crossdomain.xml” at the root of the requested target domain)
In our sample I’ve put the following clientaccesspolicy.xml file in the web site root:

<?xml version=”1.0″ encoding=”utf-8″?>
<access-policy>
    <cross-domain-access>
        <policy>
            <allow-from http-request-headers=”*”>
                <domain uri=”*”/>
            </allow-from>
            <grant-to>
                <resource path=”/” include-subpaths=”true”/>
            </grant-to>
        </policy>
    </cross-domain-access>
</access-policy>

The ASP.NET application

The web application is taken form the FluorineFx samples [FluorineFx InstallDir]\Samples\Flex\Remoting\DataAccess and exposes a service returning a DataTable as strongly typed CustomerVO objects:

[DataTableType(“Flex.CustomerVO”)]
[Description(“Filter customer by phone area code”)]
public DataTable GetCustomers(string areaCode)
{
using (OleDbConnection connection = new OleDbConnection(GetConnectionString()))
      {
      	OleDbCommand command = new OleDbCommand(“SELECT firstname, lastname, phone FROM [customer] WHERE phone LIKE ‘” + areaCode + “%’”, connection);
            connection.Open();
            OleDbDataAdapter adapter = new OleDbDataAdapter(command);
            DataTable result = new DataTable();
            adapter.Fill(result);
            return result;
       }
}

The code snippet above retrieves a DataTable and instructs the gateway to return the result as a collection (ArrayCollection) of “Flex.CustomerVO” objects (the properties of the object will be the column names).

Creating the Silverlight project

Start Visual Studio 2008, create a new Silverlight Application (I’ve named it SilverlightApplication) a new web application to host the Silverlight client (the web application can be the same as the one exposing the remoting services).

Put the following XAML markup in the Page.xaml file:

<UserControl xmlns:my=”clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data”  x:Class=”SilverlightApplication.Page”
    xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation” 
    xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml” 
    Width=”400″ Height=”300″>
    <Grid x:Name=”LayoutRoot”>
        <Grid.RowDefinitions>
            <RowDefinition Height=”40″ />
            <RowDefinition />
            <RowDefinition Height=”80″ />
        </Grid.RowDefinitions>
        <StackPanel Orientation=”Horizontal”
                HorizontalAlignment=”Left”
                Grid.Row=”0″>
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width=”300″/>
                    <ColumnDefinition Width=”100″/>
                </Grid.ColumnDefinitions>
                <TextBox x:Name=”txtSearch” Grid.Column=”0″ FontSize=”12″ Padding=”5″ Height=”26″ Margin=”0,0,5,0″/>
                <Button Content=”Get Customers”
                  Padding=”5″
                  Margin=”0,0,0,0″
                  Height=”26″
                  Grid.Column=”1″
                  x:Name=”btnPopulate”
                  Click=”Button_Click” />
            </Grid>
        </StackPanel>
        <my:DataGrid x:Name=”customersDataGrid” IsReadOnly=”True”
                 Grid.Row=”1″ RowHeight=”30″
                 AutoGenerateColumns=”True”>
        </my:DataGrid>
        <TextBox x:Name=”TxtLog” Width=”400″ VerticalScrollBarVisibility=”Visible” HorizontalScrollBarVisibility=”Visible”  Grid.Row=”2″ />
    </Grid>
</UserControl>

In the Solution Explorer add a reference to FluorineFx.dll (note: we are adding the FluorineFx Silverlight client library to the Silverlight application and not the remoting gateway!). The assembly should be located in the “Bin\net\Silverlight2.0” folder.

Add the following code to the Page.xaml.cs code file:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;   

using FluorineFx;
using FluorineFx.Net;
using FluorineFx.Messaging.Api;
using FluorineFx.Messaging.Api.Service;
using FluorineFx.AMF3;   

namespace SilverlightApplication
{
    public partial class Page : UserControl
    {
        NetConnection _netConnection;   

        public Page()
        {
            InitializeComponent();   

            // Create NetConnection client
            _netConnection = new NetConnection();
            _netConnection.ObjectEncoding = ObjectEncoding.AMF3;
            _netConnection.NetStatus += new NetStatusHandler(_netConnection_NetStatus);
            // Put your gateway url here
            _netConnection.Connect(“http://localhost:1781/SilverlightApplicationWeb/Gateway.aspx”);   

        }   

        void _netConnection_NetStatus(object sender, NetStatusEventArgs e)
        {
            string level = e.Info[“level”] as string;
            if (level == “error”)
            {
                //received an error
                Log(“Error: ” + e.Info[“code”] as string);
            }
            if (level == “status”)
            {
                Log(“Status: ” + e.Info[“code”] as string);
            }
        }   

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            _netConnection.Call(“ServiceLibrary.MyDataService.GetCustomers”, new GetCustomersHandler(this), new object[] { txtSearch.Text });
        }   

        public void Log(string msg)
        {
            Dispatcher.BeginInvoke(delegate()
            {
                TxtLog.Text += msg + “\r\n”;
            });
        }   

        public void Bind(IList list)
        {
            Dispatcher.BeginInvoke(delegate()
            {
                customersDataGrid.ItemsSource = list;
            });
        }   

    }   

    public class GetCustomersHandler : IPendingServiceCallback
    {
        Page _page;   

        public GetCustomersHandler(Page page)
        {
            _page = page;
        }   

        public void ResultReceived(IPendingServiceCall call)
        {
            object result = call.Result;   

            //DataAccess sample sends back an ArrayCollection (AMF3)
            ArrayCollection items = result as ArrayCollection;
            foreach (object item in items)
            {
                Flex.CustomerVO customer = item as Flex.CustomerVO;
                _page.Log(customer.ToString());
            }
            _page.Bind(items);
        }
    }
}

Then add a CustomerVO class to the Silverlight application (note the namespace is called Flex so class mapping will work automatically)

using System;   

namespace Flex
{
    public class CustomerVO
    {
        string _firstname;   

        public string firstname
        {
            get { return _firstname; }
            set { _firstname = value; }
        }   

        string _lastname;   

        public string lastname
        {
            get { return _lastname; }
            set { _lastname = value; }
        }   

        string _phone;   

        public string phone
        {
            get { return _phone; }
            set { _phone = value; }
        }
    }
}

Run the application.

Here is a screenshot of the running application

Update: the Silverlight library is available for download in the latest FluorineFx version

Silverlight and FluorineFx integration with the FluorineFx Silverlight library

In the previous entry I was showing how to connect a Silverlight client to the Flash Media Server. This guide shows how to connect Silverlight to a FluorineFx powered ASP.NET web site.

Network Security Access Restrictions in Silverlight 2
In order to have the client able to connect you must have a policy server running and to configure FluorineFx’s RTMP server to listen in the 4502-4534 port range.

You can use the FluorineFx Silverlight Policy Server provided OR to configure the gateway to start internally the policy server. This requires the following settings in the web.config file:

  <fluorinefx>
    <settings>
	
      <silverlight>
        <policyServer enable=”true” policyFile=”~clientaccesspolicy.xml”/>
      </silverlight>
	
    </settings>
  </fluorinefx>

The clientaccesspolicy.xml file must be located in the web site root.

Define a RTMP channel in the service configuration file with the endpoint url set as uri=”rtmp://{server.name}:4502″

The RTM application

Add the “apps” folder to the web application and create a new app.config file

<?xml version=”1.0″ encoding=”utf-8″?>
<configuration>
  <application-handler type=”ServiceLibrary.SOTestApplication”/>
</configuration>

In the service library (or in the web site project if medium trust is not enforced) define the application handler class:

using System;
using FluorineFx;
using FluorineFx.Messaging.Api;
using FluorineFx.Messaging.Adapter;
using FluorineFx.Messaging.Api.SO;
using FluorineFx.Messaging.Api.Service;
using FluorineFx.Messaging.Api.Stream; 

namespace ServiceLibrary
{
    public class SOTestApplication : ApplicationAdapter
    {
        int _usersID;
        string _history; 

        public SOTestApplication()
	  {
	  } 

        public override bool AppStart(IScope application)
        {
            _usersID = 1;
            _history = string.Empty;
            this.CreateSharedObject(this.Scope, “users”, false);
            return base.AppStart(application);
        } 

        public override bool AppConnect(IConnection connection, object[] parameters)
        {
            lock (typeof(SOTestApplication))
            {
                connection.SetAttribute(“uniqueUserID”, _usersID);
                //set the shared object
                ISharedObject usersSO = GetSharedObject(this.Scope, “users”);
                ASObject aso = new ASObject();
                aso.Add(“uniqueUserID”, _usersID);
                aso.Add(“connectStartTime”, DateTime.Now);
                usersSO.SetAttribute(“user” + _usersID, aso);
                _usersID++; 

                // Call the client function ’setHistory,’ and pass 
                // the initial history
                if( connection is IServiceCapableConnection )
                    (connection as IServiceCapableConnection).Invoke(“setHistory”, new object[]{_history}, null);
            }
            return base.AppConnect(connection, parameters);
        } 

        public override void AppDisconnect(IConnection connection)
        {
            int uniqueUserID = (int)connection.GetAttribute(“uniqueUserID”);
            ISharedObject usersSO = GetSharedObject(this.Scope, “users”);
            if( usersSO != null )
                usersSO.SetAttribute(“user” + uniqueUserID, null);
            base.AppDisconnect(connection);
        } 

        public void msgFromClient(IConnection connection, string msg)
        {
            lock (typeof(SOTestApplication))
            {
                int uniqueUserID = (int)connection.GetAttribute(“uniqueUserID”);
                msg = “user” + uniqueUserID + “: ” + msg + “\n”;
                _history += msg;
                ISharedObject usersSO = GetSharedObject(this.Scope, “users”);
                usersSO.SendMessage(“msgFromSrvr”, new object[]{msg});
            }
        }
    }
}

Create the Silverlight Application project and select the existing web site to host the client. Follow the steps described in the Creating Silverlight project section of the previous post.

Run the web application

Here is a screenshot of the running application

Get the latest FluorineFx code and binaries from the SVN repository

Update: the Silverlight library is available for download in the latest FluorineFx version

Silverlight and Flash Media Server integration with the FluorineFx Silverlight library

This sample provides an overview of creating a Silverlight project and connecting it with Flash Media Server using the FluorineFx Silverlight library (Visual Studio 2008 with Silverlight extensions is required).

The sample starts with the following FMS web chat application (sotest.asc):

application.onAppStart = function()
{
	this.usersID = 1;
	this.usersSO = SharedObject.get(“users”, false); 

	// Initialize the history of the text share
	application.history = “”;
} 

application.onConnect = function(clientObj)
{
	clientObj.uniqueUserID = this.usersID;
	clientObj.connectStartTime = new Date();
	//set the shared object
	this.usersSO.setProperty(“user”+ this.usersID, clientObj);
	this.usersID++;
	// Accept the connection.
	application.acceptConnection( clientObj ); 

	// Call the client function ’setHistory,’ and pass 
	// the initial history
 	clientObj.call(“setHistory”, null, application.history); 

	// The client will call this function to get the server
	// to accept the message, add the user’s name to it, and
	// send it back out to all connected clients.
	clientObj.msgFromClient = function(msg) {
		msg = “user”+ this.uniqueUserID + “: ” + msg + “\n”;
		application.history += msg;
		application.usersSO.send(“msgFromSrvr”, msg);
	} 	
} 

application.onDisconnect = function(clientObj)
{
	this.usersSO.setProperty(“user”+ clientObj.uniqueUserID, null);
}

When the application is started a shared object is created on the server side. Every user connecting to the application will get a unique identity. Clients will call the “msgFromClient” function to send messages to the other connected clients.

Copy the above code to the FMS\applications\sotest directory.

Network Security Access Restrictions
Before going forward to the Silverlight client one must be aware of the Network Security Access Restrictions in Silverlight 2:
1) When a socket request is made to the site (cross-domain or site of origin), the Silverlight 2 runtime opens a connection using TCP to a well-known port (port 943) on the target site. Then the runtime sends a special string <policy-file-request/> to the server to request a Silverlight policy file. The Silverlight 2 runtime then waits to receive a reply from the target site that contains a Silverlight policy file.
The policy file retrieved will have to grant permission to have a connection to the target host opened.

2) Port range that a Silverlight 2 network application is allowed to connect to must be within the range of 4502-4534

For (1) you can use the FluorineFx Silverlight Policy Server provided. The application comes with a clientaccesspolicy.xml file you can change accordingly:


<socket-resource port=”4502-4534″ protocol=”tcp”/>

Our sample uses only the port 4502 so it is recommended to change the range.

For (2) we must configure the Flash Media Server to listen on the port we can access from our Silverlight application. Open the fms.ini file and change the entry
ADAPTOR.HOSTPORT = :1935 (if previously used 1935)
to
ADAPTOR.HOSTPORT = :4502

Creating the Silverlight project

Start Visual Studio 2008, create a new Silverlight Application (I’ve named it SilverlightChatApplication) and the web application to host the Silverlight client (I’ve named it SilverlightApplicationWeb).

Put the following XAML markup in the Page.xaml file:

<UserControl x:Class=”SilverlightChatApplication.Page”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
Width=”800″ Height=”500″ xmlns:d=”http://schemas.microsoft.com/expression/blend/2008″
xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006″ mc:Ignorable=”d”>
<Grid x:Name=”LayoutRoot” Width=”800″ Height=”500″ Background=”Black”>
<ScrollViewer x:Name=”ScrollBar” Background=”White” HorizontalScrollBarVisibility=”Auto” Canvas.Top=”60″ Canvas.Left=”340″ Margin=”8,8,8,50″>
<TextBlock x:Name=”ChatConsole” HorizontalAlignment=”Stretch” Margin=”8,8,8,47.4630012512207″ VerticalAlignment=”Stretch” Text=”" TextWrapping=”Wrap”/>
</ScrollViewer>
<TextBox x:Name=”InputTextBox” HorizontalAlignment=”Stretch” Margin=”8,0,59.6339988708496,8″ VerticalAlignment=”Bottom” Text=”Sample Text”/>
<Button Height=”35.463″ HorizontalAlignment=”Right” Margin=”0,0,8,8″ VerticalAlignment=”Bottom” Width=”47.634″ Content=”Send” d:LayoutOverrides=”Width, Height” Click=”Button_Click”/>
</Grid>
</UserControl>

In the Solution Explorer add a reference to FluorineFx.dll (note: we are adding the FluorineFx Silverlight client library to the Silverlight application and not the remoting gateway!). The assembly should be located in the “Bin\net\Silverlight2.0” folder.

Add the following code to the Page.xaml.cs code file:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes; 

using FluorineFx;
using FluorineFx.Net;
using FluorineFx.Messaging.Api;
using FluorineFx.Messaging.Api.Service;
using FluorineFx.AMF3; 

namespace SilverlightChatApplication
{
    public partial class Page : UserControl
    {
        NetConnection _netConnection;
        RemoteSharedObject _sharedObject; 

        public Page()
        {
            InitializeComponent(); 

            // Create NetConnection client
            _netConnection = new NetConnection();
            _netConnection.ObjectEncoding = ObjectEncoding.AMF0;
            _netConnection.OnConnect += new ConnectHandler(_netConnection_OnConnect);
            _netConnection.OnDisconnect += new DisconnectHandler(_netConnection_OnDisconnect);
            _netConnection.NetStatus += new NetStatusHandler(_netConnection_NetStatus);
            _netConnection.Client = this;
            //FMS test
            _netConnection.Connect(“rtmp://localhost:4502/sotest”);
        } 

        void _netConnection_NetStatus(object sender, NetStatusEventArgs e)
        {
            string level = null;
            if (e.Info.ContainsKey(“level”))
                level = e.Info[“level”] as string;
            if (level == “error”)
            {
                //received an error
                Log(“Error: ” + e.Info[“code”] as string);
                Log(“Client not connected.”);
            }
            if (level == “status”)
            {
                Log(“Status: ” + e.Info[“code”] as string);
            } 

        } 

        void _netConnection_OnDisconnect(object sender, EventArgs e)
        {
            Log(“Connection disconnected.”);
        } 

        void _netConnection_OnConnect(object sender, EventArgs e)
        {
            Log(“Connected to server. Connecting to RSO…”);
            _sharedObject = RemoteSharedObject.GetRemote(typeof(UsersRSO), “users”, _netConnection.Uri.ToString(), false);
            (_sharedObject as UsersRSO).Page = this; 

            _sharedObject.OnConnect += new ConnectHandler(_sharedObject_OnConnect);
            _sharedObject.OnDisconnect += new DisconnectHandler(_sharedObject_OnDisconnect);
            _sharedObject.NetStatus += new NetStatusHandler(_sharedObject_NetStatus);
            _sharedObject.Sync += new SyncHandler(_sharedObject_Sync);
            _sharedObject.Connect(_netConnection);            
        } 

        void _sharedObject_Sync(object sender, SyncEventArgs e)
        {
            ASObject[] changeList = e.ChangeList;
            for (int i = 0; i < changeList.Length; i++)
            {
                ASObject info = changeList[i];
                if (info.ContainsKey(“name”) && info[“name”] != null)
                    Log(info[“name”].ToString() + ” - ” + info[“code”].ToString());
                else
                    Log(info[“code”].ToString());
            }
        } 

        void _sharedObject_NetStatus(object sender, NetStatusEventArgs e)
        {
            string level = e.Info[“level”] as string;
            if (level == “error”)
            {
                //received an error
                Log(“Error: ” + e.Info[“code”] as string);
            }
            if (level == “status”)
            {
                Log(“Status: ” + e.Info[“code”] as string);
            }
        } 

        void _sharedObject_OnDisconnect(object sender, EventArgs e)
        {
            Log(“Disconnected RSO.”);
        } 

        void _sharedObject_OnConnect(object sender, EventArgs e)
        {
            Log(“Connected to RSO.”);
        } 

        public void setHistory(string history)
        {
            Log(history);
        } 

        /// <summary>
        /// Submit text to the server
        /// </summary>
        /// <param name=“sender”></param>
        /// <param name=“e”></param>
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            if (_sharedObject != null && _sharedObject.Connected)
            {
                _netConnection.Call(“msgFromClient”, null, InputTextBox.Text);
            }
            else
            {
                Log(“Not connected to the server, reload page and try again.”);
            }
        } 

        public void Log(string msg)
        {
            Dispatcher.BeginInvoke(delegate()
            {
                ChatConsole.Text += msg + “\n”;
                ScrollBar.ScrollToVerticalOffset(ScrollBar.ScrollableHeight);
            });
        } 

        public void LogMessage(string msg)
        {
            Dispatcher.BeginInvoke(delegate()
            {
                ChatConsole.Text += msg;
                ScrollBar.ScrollToVerticalOffset(ScrollBar.ScrollableHeight);
            });
        }
    }
}

Additionally add a new UsersRSO class to the Silverlight application

using System;
using FluorineFx;
using FluorineFx.Net; 

namespace SilverlightChatApplication
{
    public class UsersRSO : RemoteSharedObject
    {
        Page _page; 

        //This class must have parameterless constructor!
        public UsersRSO()
        {
        } 

        public Page Page
        {
            get { return _page; }
            set { _page = value; }
        } 

        public void msgFromSrvr(string msg)
        {
            _page.LogMessage(msg);
        }
    }
}

This class is our custom RemoteSharedObject class and it is required as FMS will notify us by calling the “msgFromSrvr” method on RSO.

Run FMS
Run the FluorineFx Silverlight Policy Server
Run the Silverlight client application (by running the host web application)

Here is a screenshot of the running application

Get the latest FluorineFx code and binaries from the SVN repository

Update: the Silverlight library is available for download in the latest FluorineFx version

Video: Towards FluorineFx version 1.1

The video showcases some of the planned features for FluorineFx 1.1 (and Visual Studio integration)
Note: in the video, the top project in the solution browser is a Flex Builder project wrapper for Visual Studio.

Video

Quick Start Video: FluorineFx + Attach to Visual Studio’s Development Web Server

Video shows how to attach to Visual Studio’s Development Web Server (to a running web application) for debugging purposes.
That is when you don’t start your web application in debug mode and you need to attach the debugger.

If you are using IIS then attach to the aspnet_wp.exe executable.

Video

Quick Start Video: FluorineFx + Visual Studio 2005

Video showing how to start with FluorineFx and Microsoft Visual Studio 2005

Video