Showing posts with label Service. Show all posts
Showing posts with label Service. Show all posts

18 July 2009

Calling a service relative to the Silverlight XAP (improved)

In my previous post I showed a way to call a service defined with a full path, as created by the designer, relative to that of the location of your XAP, by means of an extension to a user control. I developed that during a training and while blogging it I alreadly realized that there was room for improvement. And here it is:
using System;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Windows;

namespace LocalJoost.Utilities.Silverlight
{
  public static class ServiceEndpointExtension
  {
    public static void MakeRelative(
      this ServiceEndpoint endpoint)
    {
      var clientbinLocation = 
        Application.Current.Host.Source.ToString().Substring(0,
        Application.Current.Host.Source.ToString().LastIndexOf("/"));
      var rootLocation = 
        clientbinLocation.Substring(0, 
        clientbinLocation.LastIndexOf("/"));
      endpoint.Address = new EndpointAddress(
        new Uri(string.Concat(rootLocation, 
          endpoint.Address.Uri.AbsolutePath)));
    }
  }
}
In stead of extending the user control I now extend the ServiceEndpoint. If you now want to call a service that is sitting in the root of your web application that is hosting you can call it like this:
var client = new CloudMapperDataServiceClient();
client.Endpoint.Address.MakeRelative();
Substitute "CloudMapperDataServiceClient" by your own client proxy class and you are go for launch (Sorry, currently listening to wechoosethemoon.org ;-)

15 July 2009

Calling a service relative to the Silverlight XAP

This post is deprecated and left here for link reference only. See this post or a better way to do this. A small tidbit that I developed when I was following a training by Dennis van der Stelt of Class-A. One of the assigments was to consume a service and display data. The lab contained a string with a hard coded path. I thought of the following, IMHO much cleaner solution. I also discovered that WCF services are defined in a ServiceReferences.ClientConfig that contains a full path to the service. That is not nice on deployment time. This may have been caused by the fact I hosted the Silverlight App in an Azure project, I am not sure yet. But anyhow, I made an extension method to the Silverlight System.Windows.Controls.Control like this
using System;
using System.ServiceModel;
using System.Windows;
using System.Windows.Controls;

namespace LocalJoost.Utilities.Silverlight
{
  public static class UserControlExtensions
  {
    public static EndpointAddress GetRelativeEndpointAdress(
      this UserControl control, string path)
    {
      var clientbinLocation = 
          Application.Current.Host.Source.ToString().Substring(0,
          Application.Current.Host.Source.ToString().LastIndexOf("/"));
      return new EndpointAddress(new Uri(string.Format("{0}/{1}",
        clientbinLocation.Substring(0,
        clientbinLocation.LastIndexOf("/")), path)));
    }
  }
}
If you now want to call a service that is sitting in the root of your web application that is hosting you can call it like this:
var client = new CloudMapperDataServiceClient();
client.Endpoint.Address = 
 this.GetRelativeEndpointAdress("CloudMapperDataService.svc");
Substitute "CloudMapperDataServiceClient" by your own client proxy class and "CloudMapperDataService.svc" by your own svc and you are ready to roll. Seeing this again while I blog it I think it might be even better to get the name of the svc from the original Endpoint.Address setting. Well, that's something for later

06 June 2009

Hosting a CGI program as a REST service on the Azure .NET Service Bus (allowing anonymous access)

The point of this posting is threefold: I want to show how you can make REST services on the .NET service bus, how you can allow anonymous access to these, and that anything that is accessible by an URL can be hosted on the .NET service bus. For my example I am going to host a WMS Service by UMN MapServer - which is in fact a CGI program - on the .NET Service bus, but you can host anything you want. I am going to assume that you have Visual Studio 2008, .NET 3.5 SP1, the Azure tools and toolkits CTP May 2009 installed, that you have access to the .NET Services portal and that you know how to create a solution for a .NET Service on that portal. Setting the stage Create a new solution with two projects: a console application and a WCF Service library. I have called my solution "CloudMapExample", the console app "CloudMapRunner" and the library "CloudMap.Contracts" Setup the console application Add references to "System.ServiceModel", "System.ServiceModel.Web" and the Service library you have just created. Add an app.config, and make sure it looks like this:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="WMSServices"
   type="System.Configuration.NameValueSectionHandler" />
  </configSections>
  <WMSServices>
    <add key="DemoSrv"
   value="http://localhost/MapServer/mapserv.exe?map=d:/MapServer/CONFIG/Demo.map"/>
  </WMSServices>
</configuration>
This of course requires you to have setup MapServer. Don't worry, you can also set a url to a HTML page or ASPX page running on your local machine mocking this behaviour. Setup the service library Right-click the CloudMap.Contracts project, select the "WCF Option" tab and unselect the checkbox before "Start WCF Service Host blah blah blah" to prevent the WCF host popping up every time you test your project. Then, delete App.config, IService1.cs and Service1.cs Finally, add references to "System.ServiceModel.Web" and "System.Configuration" Add the service contract Add to the service library an interface IWMSProxyService that looks like this:
using System.ServiceModel;
using System.ServiceModel.Web;
using System.IO;

namespace CloudMap.Contracts
{
  [ServiceContract]
  public interface IWMSProxyService
  {
    [OperationContract]
    [WebGet(UriTemplate = "WMS/{map}/*")]
    Stream WMSRequest(string map);
  }
}
Notice the WebGet attribute "UriTemplate". The asterisk is a wildcard that tells WCF that any url starting with this template is mapped to the WMSRequest method. A nifty trick, since the idea is that I can call the method like "http://host/baseurl/WMS/MyMap/?key1=value&key2=value. "MyMap" will automatically be populated into the "map" parameter of the method "WMSRequest". How the rest of the querystring will become available is shown in the service implementation. Add the service implementation class
using System;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.ServiceModel.Web;
using System.Configuration;
namespace CloudMap.Contracts
{
  public class WMSProxyService : IWMSProxyService
  {
    public Stream WMSRequest(string map)
    {
      Console.WriteLine("Proxy call!");
      var wms = GetWMSbyMap(map);
      return RelayUrl(string.Format("{0}{1}{2}",
                      wms,
                      (wms.Contains("?") ? "&" : "?"),
      WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters));     
    }

    private string GetWMSbyMap( string map )
    {
      var services = ConfigurationManager.GetSection("WMSServices")
               as NameValueCollection;
      return services != null ? services[map] : null;
    }

    private Stream RelayUrl(string urlToLoadFrom)
    {
      var webRequest = HttpWebRequest.Create(urlToLoadFrom) 
         as HttpWebRequest;
      // Important! Keeps the request from blocking after the first
      // time!
      webRequest.KeepAlive = false;
      webRequest.Credentials = CredentialCache.DefaultCredentials;
      using (var backendResponse = (HttpWebResponse)webRequest.GetResponse())
      {
        using (var receiveStream = backendResponse.GetResponseStream())
        {
          var ms = new MemoryStream();
          var response = WebOperationContext.Current.OutgoingResponse;
          // Copy headers      
          // Check if header contains a contenth-lenght since IE
          // goes bananas if this is missing
          bool contentLenghtFound = false;
          foreach (string header in backendResponse.Headers)
          {
            if (string.Compare(header, "CONTENT-LENGTH", true) == 0)
            {
              contentLenghtFound = true;
            }
            response.Headers.Add(header, backendResponse.Headers[header]);
          }
          // Copy contents      
          var buff = new byte[1024];
          var length = 0;
          int bytes;
          while ((bytes = receiveStream.Read(buff, 0, 1024)) > 0)
          {
            length += bytes;
            ms.Write(buff, 0, bytes);
          }
          // Add contentlength if it is missing
          if (!contentLenghtFound) response.ContentLength = length;
          // Set the stream to the start
          ms.Position = 0;
          return ms;
        }
      }
    }
  }
}
Now this may look a bit intimidating, but in fact it is just a little extension of my WCF proxy example described earlier in this blog. The 'extensions' are pretty simple: first of all the method "GetWMS" searches for a configuration section "WMSServices" in the App.config and then tries to retrieve the base url of the WMS defined by the value of "map". It then concatenates the rest of querystring, which is accessible by the not quite self-evident statement "WebOperationContext.Current.IncomingRequest.UriTemplateMatch.QueryParameters", to the url found in the config section and calls it, passing the parameters of the query string to it. Anyway, don't worry too much about it. This is the more or less generic proxy. The point is getting it to run and then hosting it on the .NET service bus. Add code code to host the service Open the "Program.cs" file in the Console application, and make it look like this:
using System;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Web;
using CloudMap.Contracts;

namespace CloudMapRunner
{
  class Program
  {
    static void Main(string[] args)
    {
      var serviceType = typeof(WMSProxyService);
      var _host = new WebServiceHost(serviceType);
      _host.Open();

      // Code to show what's running
      Console.WriteLine("Number of base addresses : {0}",
        _host.BaseAddresses.Count);
      foreach (var uri in _host.BaseAddresses)
      {
        Console.WriteLine("\t{0}", uri);
      }
      Console.WriteLine();
      Console.WriteLine("Number of dispatchers listening : {0}",
        _host.ChannelDispatchers.Count);
      foreach (ChannelDispatcher dispatcher in 
               _host.ChannelDispatchers)
      {
        Console.WriteLine("\t{0}, {1}",
          dispatcher.Listener.Uri,
          dispatcher.BindingName);
      }

      // Exit when user presser ENTER
      Console.ReadLine();
    }
  }
}
This basically just starts up the service. Everything between the comments "// Code to show what's running" and "// Exit when user presser ENTER" just displays some information about the service, using code I - er - borrowed from Dennis van der Stelt when I attend a WCF training by him at Class-A some two years ago. Notice I am using a WebServiceHost in stead of the normal ServiceHost. As long as you run your code locally, ServiceHost will do as well, but not when you want to harness your service in the cloud. But this is the only thing you need to take care of in code to make sure you can painlessly move from a locally hosted WCF service to the Azure .NET service bus - the rest is just configuration. Adding basic WCF configuration To get the service to work a REST service, you need to add the following configuration to the app.config of your console application:
<system.serviceModel>
  <services>
    <service name="CloudMap.Contracts.WMSProxyService" >
      <endpoint address="" binding="webHttpBinding"
        behaviorConfiguration="WebHttpBehavior"
        contract="CloudMap.Contracts.IWMSProxyService"
        bindingNamespace="http://dotnetbyexample.blogspot.com">
      </endpoint>
      <host>
        <baseAddresses>
          <add baseAddress="http://localhost:8002/CloudMapper" />
        </baseAddresses>
      </host>
    </service>
  </services>
  <behaviors>
    <endpointBehaviors>
      <behavior name="WebHttpBehavior">
        <webHttp />
      </behavior>
    </endpointBehaviors>
  </behaviors>
</system.serviceModel>
The usage of [WebGet] and the webHttp tag are covered in a previous post so I'm skipping the details on that. Run the console application, and you will see something like this in your command window: Number of base addresses : 1 http://localhost:8002/CloudMapper Number of dispatchers listening : 1 http://localhost:8002/CloudMapper, http://dotnetbyexample.blogspot.com:WebHttpBinding If you have, like me, configured the proxy to be used for a WMS service you can now enter something like "http://localhost:8002/CloudMapper/WMS/DemoSrv/?SRS=EPSG:4326&FORMAT=GIF&SERVICE=WMS&VERSION=1.1.1&REQUEST=GetMap&LAYERS=PERCEEL,GBKN_BIJGEBOUW,GBKN_VERHARDING,GBKN_HUISNR&TRANSPARENT=TRUE&BBOX=5.855712890625,51.8221981833694,5.86669921875,51.8289883636691&WIDTH=256&HEIGHT=256&REASPECT=FALSE" in your browser and then get a map image. Nice format, WMS, isn't it? :-) Next - to the clouds and beyond ;-) Make a new .NET Services solution Go to http://portal.ex.azure.microsoft.com and make a solution. I have called mine "LocalJoost" and let us suppose the password is "dotnetbyexample" Add reference to ServiceBus dll In your console application, add a reference to Microsoft.ServiceBus.dll. On my computer it resides in "D:\Program Files\Microsoft .NET Services SDK (March 2009 CTP)\Assemblies" Add .NET service bus configuration In your app.config, change "webRelayBinding" into "webHttpRelayBinding" Then, change the baseAdress from "http://localhost:8002/CloudMapper" to "http://localjoost.servicebus.windows.net/CloudMapper/". Notice: "localjoost", the first part of the URL is the solution name. Yours is likely to be different. Finally, in your app.config, add the following configuration to the behaviur "WebHttpBehavior", directly under the "<webHttp />" tag:
<transportClientEndpointBehavior credentialType="UserNamePassword">
  <clientCredentials>
     <userNamePassword userName="LocalJoost" 
                          password="dotnetbyexample" />
  </clientCredentials>
</transportClientEndpointBehavior>
If you now run your console application, you will see this: Number of base addresses : 1 http://localjoost.servicebus.windows.net/CloudMapper/ Number of dispatchers listening : 1 sb://localjoost.servicebus.windows.net/CloudMapper/, http://dotnetbyexample.blogspot.com:WebHttpRelayBinding. Now your REST service is callable via the Service bus. It is THAT easy. You only have to replace "http://localhost:8002" in your browser by "http://localjoost.servicebus.windows.net" and there you go. Well... almost. Instead of a map you get a site called "http://accesscontrol.windows.net" that asks you, once again, to enter the solution name and password. And THEN you get the map. Enable anonymous access For an encore, I am going to show you how to allow anonymous access to your proxy. Whether or not that is a wise thing to do is up to you. Remember that your service is now callable by anyone in the world, no matter how many firewalls are between you and the big bad world ;-) Add the following configuration data in the system.serviceModel section of your console application
<bindings>
  <webHttpRelayBinding>
 <binding name="allowAnonymousAccess">
   <security relayClientAuthenticationType="None"/>
 </binding>
  </webHttpRelayBinding>
</bindings>
and add bindingConfiguration="allowAnonymousAccess" to your endpoint. This is what your systems.serviceModel section of your app.config should look like when you're done:
<system.serviceModel>
  <bindings>
    <webHttpRelayBinding>
      <binding name="allowAnonymousAccess">
        <security relayClientAuthenticationType="None"/>
      </binding>
    </webHttpRelayBinding>
  </bindings>

  <services>
    <service name="CloudMap.Contracts.WMSProxyService" >
      <endpoint address="" binding="webHttpRelayBinding"
       bindingConfiguration="allowAnonymousAccess"
       behaviorConfiguration="WebHttpBehavior"
       contract="CloudMap.Contracts.IWMSProxyService"
       bindingNamespace="http://dotnetbyexample.blogspot.com">
      </endpoint>
      <host>
        <baseAddresses>
          <add baseAddress="http://localjoost.servicebus.windows.net/CloudMapper/" />
        </baseAddresses>
      </host>
    </service>
  </services>
  <behaviors>
    <endpointBehaviors>
      <behavior name="WebHttpBehavior">
        <webHttp />
        <transportClientEndpointBehavior credentialType="UserNamePassword">
          <clientCredentials>
            <userNamePassword userName="LocalJoost"
                                 password="dotnetbyexample" />
          </clientCredentials>
        </transportClientEndpointBehavior>
      </behavior>
    </endpointBehaviors>
  </behaviors>
</system.serviceModel>
And that's all there is to it. Moving a service to the cloud is mostly configuration, and hardly any programming. Are we spoiled by Microsoft or what? ;-) Complete sample downloadable here

16 March 2008

JSON services revisited: using a Dictionary as a generic parameter

Somebody at Microsoft deserves a Nobel Prize. Well, that is maybe a bit strong, but I ran into the problem that I wanted to create JSON scriptserver that would accept a 'parameter bag' of unknown size and type. I expected problems in the serialization, since Javascript typing is at best a bit lax, but it turns out that when you define a web service with a parameter of type Dictionary<string, object> dict, the JSON serializer automatically derives the object types from the javascript data! It works like this: suppose we define the following method:
[WebMethod]
public string DictMethod(Dictionary<string, object> dict)
{
  StringBuilder b = new StringBuilder("C# reports<BR/>");
  foreach( string s in dict.Keys )
  {
    b.AppendFormat(string.Format("{0}:{1}={2}<BR/>", s, dict[s].GetType(), 
                   dict[s]));
  }
  return b.ToString();
}
We can call this method from javascript like this
<script type="text/javascript">
  function LoadDict()
  {
    dict = new Object();
    dict["test1"] = "Joost";
    dict["test2"] = 21;
    dict["test3"] = true;
    dict["test4"] = 9999999999;
    dict["test5"] = new Date(2008,09,12,13,14,00);
    DictService.DictMethod( dict, ProcessResult );
  }

  function ProcessResult( WebServiceResult )
  {
    document.write( WebServiceResult );
  }
</script>
If you attach the LoadDict javascript function to some client side event (The OnClientClick of a button for example) and launch it, you will see the following output: C# reports test1:System.String=Joost test2:System.Int32=21 test3:System.Boolean=True test4:System.Int64=9999999999 test5:System.DateTime=12-10-2008 11:14:00 So you can see, the javascript string converted into a true .NET string, the value 21 into an Int32 - but the value 9999999999 is too long for an int and is converted into a Int64, a.k.a. long - I really like that one, for it shows true craftmanship of the programmer ;-). The "true" javascript value is converted into a Boolean finally, the javascript Date is converted into a neat DateTime. And presto, all your stuff is converted into real .NET objects. Of course, you should always used typed parameters when possible, but for some generic solutions this may come in very handy. Complete code downloadable here.

08 March 2008

Running and debugging a Windows Managed Service directly from Visual Studio.NET

Developing a Windows Managed Service (i.e. a service written in .NET ;-) ) is a lot easier than it used to be, still it can be a real PITA to debug, especially in the early stages, and even more so when the service is a kind of server and something goes wrong with the communication. You are continually running in circles:
  1. Stop running service
  2. Replace binaries
  3. Start services
  4. Attach Visual Studio debugger to process
  5. Launch your client process
  6. Find bug
  7. Fix bug
  8. Repeat until no more bug.

Life could be so much easier if you could launch the service from Visual Studio and see how it behaves. And the fun thing is, you can.

1. Create a new service project in your solution
If you already have a service project you can skip this step. I'll add a service project DemoService with a Service MyService in it with not too complicated functionality:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.ServiceProcess;
using System.Text;

namespace DemoService
{
  public partial class MyService : ServiceBase
  {
    public MyService()
    {
      InitializeComponent();
    }

    protected override void OnStart(string[] args)
    {
      Console.WriteLine("OnStart");
    }

    protected override void OnStop()
    {
      Console.WriteLine("OnEnd");
    }
  }
}

2. Change the application output type
Right-click the DemoService project, choose "Properties", click tab "Application". Default this says "Windows application" - change this to Console application

3. Add debug mode methods to your service class

#if DEBUG

  public void StartService()
  {
   OnStart(null);
   Console.WriteLine("MyService started, press >ENTER< to terminate");
   Console.ReadLine();
  }

  public void StopService()
  {
   OnStop();
  }
#endif
4. Add debug mode code to the Main method
Open the "Program.cs" file in the service project, and change it so it looks like this
using System;
using System.Collections.Generic;
using System.ServiceProcess;
using System.Text;

namespace DemoService
{
  static class Program
  {
    /// 
    /// The main entry point for the application.
    /// 
    static void Main()
    {
#if DEBUG
      MyService s = new MyService();
      s.StartService();
      s.StopService();

#else
      ServiceBase[] ServicesToRun;

      ServicesToRun = new ServiceBase[] { new MyService() };

      ServiceBase.Run(ServicesToRun);
#endif
    }
  }
}

And that's it. You can now run the 'service' as a program in debug mode - it will pop-up a console window in which the 'service' runs and says OnStart MyService started, press >ENTER< to terminate

If you hit enter, you will just be able to see "OnEnd" passing by before the window closes. Of course, normally you will launch a thread of some kind in the OnStart method, or you will start a WCF service, and then you can see the dynamic behaviour of the 'service' while it runs by adding more "Console.WriteLine" calls - or, since it is already a process inside VS.NET, see it hit breakpoints or the exception you have been hunting all week now. And by setting the solution to "Multiple Startup" you can start both 'service' and client in one go.

And if you make a release build, all this stuff will be left out and the service will be compiled as a proper service which can be installed and will run in the usual way.

CAVEAT: be aware that the 'service' is running with your login credentials when it runs in a console window, and not as 'System'. This method is therefore not applicable when debugging access releated problems.