Step by Step SharePoint Service Applications : Part 4 (Deploying the Solution)

This is the fourth post in a series of writing a SharePoint Service Application. As stated in part 3, if you’ve missed the first posts, read those or this won’t make a lick of sense. (It may not make a lick of sense regardless!) Once again, note that creating a service application is OVERKILL for most things you’ll create for SharePoint. And while this example only spits out a simple “Hello World” type of sample, you can use this as “starter code” for a much more complex application.

For those who attended SharePoint Saturday in Denver last week, here’s the promised code, and thanks for coming to my session!

Here are the first four posts in this series:

Now that you have the guts in place, it’s time to create the deployment solution. Whenever you write complex SharePoint solutions, it’s best to keep the SharePoint project that creates the WSP file scoped to the deployment. Think of it ONLY as the Installer project for your SharePoint solution.

Since it’s been a while since I wrote the first posts (I am sorry if you’ve been waiting…), I am going to take the lazy approach here, and post the code for you to download and follow along. Rather than trying to walk you through creating it from scratch, simply download the source and I’ll talk you through the components.

DOWNLOAD THE SERVICE APPLICATION SOURCE HERE

In the source code you’ll see 2 project: the first is the SuperCoolServiceLibrary. This is the code for the service application, as discussed in the previous posts. The second is the SharePoint “installer” project which we’ll focus on in this post.

The WSP needs to contain the following mapped folder structure:

  • ADMIN (mapped folder {SharePointRoot}\Template\ADMIN)
    • SuperCoolServiceApplication
      • Default.aspx
      • Manage.aspx
  • Layouts (mapped folder {SharePointRoot}\Template\Layouts)
    • SuperCoolServiceApplication
      • Debug.aspx (just a test page)
  • WebClients (mapped folder {SharePointRoot}\WebClients)
    • SuperCoolServiceApplication
      • Client.config
  • WebServices (mapped folder {SharePointRoot}\WebServices)
    • SuperCoolServiceApplication
      • Web.config
      • SuperCool.svc

 

Let’s start with the most obvious. WebServices contains the WCF web service which will be the backend WCF service. Note that it needs a Web.config file as well as the .svc file.

Next up is WebClients. WebClients contains the client.config file which the configuration based channel factory will use to communicate to the backend service.

Then… the Layouts folder contains just a debug page which will exercise the Hello World method in the service.

Most importantly, you’ll need to include an installer feature which will create the instances and make available your service in the Manage Application Services page. That code looks like this (from installer.eventreceiver.cs):

public override void FeatureActivated(SPFeatureReceiverProperties properties)
{
    SPFarm farm = SPFarm.Local;
    SPServer server = SPServer.Local;

    // check if the service has already been installed in the farm
    var service = farm.Services.GetValue<SuperCoolService>();
    if (service == null)
    {
        // create the service
        service = new SuperCoolService(farm);
        service.Status = SPObjectStatus.Online;
        service.Update();
    }

    // with the service added to the farm, see if there is a installed instance on the server... 
    //   if not, create it
    SuperCoolServiceInstance serverSvcInstance = new SuperCoolServiceInstance(server, service);
    serverSvcInstance.Update(true);

    // install the service proxy into the farm if isn't already installed
    var serviceProxy = farm.ServiceProxies.GetValue<SuperCoolServiceProxy>();
    if (serviceProxy == null)
    {
        serviceProxy = new SuperCoolServiceProxy(farm);
        serviceProxy.Update(true);
    }
}

        
public override void FeatureDeactivating(SPFeatureReceiverProperties properties)
{
    SPFarm farm = SPFarm.Local;
    SPServer server = SPServer.Local;

    //Remove all instances
    var instance = server.ServiceInstances.GetValue<SuperCoolServiceInstance>();
    while (instance != null)
    {
        server.ServiceInstances.Remove(instance.Id);
        instance = server.ServiceInstances.GetValue<SuperCoolServiceInstance>();
    }
//Uninstall the service proxy var serviceProxy = farm.ServiceProxies.GetValue<SuperCoolServiceProxy>(); if (serviceProxy != null) { farm.ServiceProxies.Remove(serviceProxy.Id); } //Remove service and jobs var service = farm.Services.GetValue<SuperCoolService>(); if (service != null) farm.Services.Remove(service.Id); }

In the Admin folder, you’ll need a default.aspx page which will be the UI to CREATE an instance of your service application. You can also include an admin page here as well.

The code behind from the default page in Admin contains the following code, which is used to create your service application:

var service = SPFarm.Local.Services.GetValue<SuperCoolService>();
if (service == null)
    throw new InvalidOperationException("The service is not installed.");
                    
//// Get the service proxy object
SuperCoolServiceProxy serviceProxy = SPFarm.Local.ServiceProxies.GetValue<SuperCoolServiceProxy>();
if (null == serviceProxy)
    throw new InvalidOperationException("The service proxy is not installed.");
                    
var appPoolInput = this.applicationPoolSection as IisWebServiceApplicationPoolSection;
using (SPLongOperation operation = new SPLongOperation(this))
{
    operation.Begin();

    SPIisWebServiceApplicationPool pool = null;
    pool = appPoolInput.GetOrCreateApplicationPool();
    if (pool == null)
        throw new ConfigurationErrorsException("Could not find an application pool to use!!!");

    var app = new SuperCool.ServiceLibrary.SuperCoolServiceApplication( 
            this.serviceNameInput.Text, service, pool);

    app.Update(true);

    app.Provision();

    var serviceAppProxy = new SuperCoolServiceApplicationProxy("Super Cool Service Proxy Thingamajig",
        serviceProxy, app.Uri);
    serviceAppProxy.Provision();
    SPServiceApplicationProxyGroup.Default.Add(serviceAppProxy);
    SPServiceApplicationProxyGroup.Default.Update();

    //Start the service instance
    var server = SPServer.Local;
    var instance = server.ServiceInstances.GetValue<SuperCoolServiceInstance>();
    if (instance == null) 
    {   //It should be created during feature activation, but if we can't find it  - create it here.
        instance = new SuperCoolServiceInstance(server, service);
        instance.Update(true);
    }
    if (instance != null && instance.Status != SPObjectStatus.Online)
        instance.Provision();
}
this.SendResponseForPopUI();

In case you missed the link, DOWNLOAD THE SERVICE APPLICATION SOURCE HERE. Then you can play along!!! I’ll post more notes on this in the coming weeks after the Thanksgiving celebrations die down.

To all of you, Happy Thanksgiving, and may you have a great season of the holy days.

Daniel Larson

Posted in Programming, SharePoint | Tagged , | 17 Comments

Berthoud Pass 3/5/2011

Saturday was another great day at Berthoud Pass. Our first run up was on the West Side, about an hour hike (it’s probably a whole lot less if you’re not out of shape like I am!) We hit the Meadows and then down to Current Creek, and then took another lap through the 90s off of the Aqueduct. The snow was great, a pretty stable snowpack with about 9 inches of powder.

DSC01388

This time I took a pair of showshoes and a new riding partner, Tony Anzelmo, another .NET developer, and a friend of my last riding partner who STILL HASN’T BOUGHT A BEACON (you know who you are!) Even with a consolidated snowpack, it’s important to have avalanche gear out there. (Thanks to Friends of Berthoud Pass for the avalanche education!)

After lunch, we hit up one last run on Hell’s Half Acre, starting above treeline and down through Hanging Meadow and hit just a little action in Sentinel Chute. I’m not much of a photographer when I ride, but here’s a few pics from some of the slower parts of the day.

DSC01387  IMAG0368

IMAG0367  IMAG0370

Posted in Berthoud Pass, snowboarding | Tagged , , , | 2 Comments

Step by Step SharePoint Service Applications : Part 3 (WCF, the hard way)

This is the third post in a series of writing a SharePoint Service Application. If you’ve missed the first posts, read those or this won’t make a lick of sense. (It may not make a lick of sense regardless!) Also note that creating a service application is OVERKILL for most things you’ll create for SharePoint… this post is NOT “how to write WCF code for SharePoint”, it is VERY SPECIFIC to creating a SharePoint 2010 Managed Service Application. And while this example only spits out a simple “Hello World” sample, you can use this as “starter code” for a much more complex application.

So far, we’ve created the service application library, but no WCF service. In this post, we’ll create the WCF service and a WCF client to call it using the SharePoint application proxy. The WCF client will run on the web front end and will call back to the application server’s instance of the application through your backend web service. In the next post, we’ll cover how to actually deploy the service application in the WSP project. But for now, we’ll just look at the code side of the WCF library. Because we really want to focus on CODE, we won’t cover the WCF configuration stuff quite yet. We’ll take care of that in the WSP project.

The Service Interface

As with any WCF service, start with the service interface. Ours will simply return a debug statement—which is actually ideal to include in your production WCF service for debugging support issues. Here is my service interface:

using System;
using System.ServiceModel;

namespace SuperCool.ServiceLibrary.Services
{
    /// <summary>
    /// Defines the WCF interface to the backend service application
    /// </summary>
    /// <remarks>Referenced in WebClients\[app name]\client.config</remarks>
    [ServiceContract]
    public interface ICoolService
    {
        [OperationContract]
        string GetServiceDebug();
    }
}

The Service Class

For our service implementation, I’ll return some debug info about the call, so you can see what URL it actually gets called on and what identity is calling it. You can also include the attribute [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.NotAllowed)] on this class, since we do NOT support this interface in an ASP.NET context.

using System;
using Microsoft.SharePoint.Administration.Claims;
using System.ServiceModel;
using System.Globalization;
using Microsoft.SharePoint.Administration;

namespace SuperCool.ServiceLibrary.Services
{
    /// <summary>
    /// The actual WCF-callable backend service. (The rest is plumbing)
    /// </summary>
    public class CoolService : ICoolService
    {
        // this method would be called as the user.
        public string GetServiceDebug()
        {
            var encAccountName = 
               SPClaimProviderManager.Local.GetUserIdentifierEncodedClaim(
               ServiceSecurityContext.Current.PrimaryIdentity);
            var claimsAccountName = 
               SPClaimProviderManager.Local.ConvertClaimToIdentifier(encAccountName);

            // you can also use System.Threading.Thread.CurrentPrincipal.Identity

            SPIisWebServiceApplication current = SPIisWebServiceApplication.Current;

            return string.Format(CultureInfo.InvariantCulture, 
                @"Hello, {0}. So glad you used ""{1}"" (a {2}, id {3}) at {4}.",
                claimsAccountName,
                current.DisplayName,
                current.GetType().Name,
                current.Id,
               OperationContext.Current.IncomingMessageProperties[
               "OriginalHttpRequestUri"]               
                );
        }
    }
}

 

This backend service will ALWAYS be authenticated by claims authentication—it is the delegation used by SharePoint, which we’ll configure in the client in the next code sample. The ServiceSecurityContext.Current.PrimaryIdentity will be a ClaimsIdentity, which you can get the current user from. To get the current User Profile from the backend service, you can use this claims identity to get the user profile from the User Profile application (it will use the User Profile Application that is in the same proxy group as your application).

private UserProfile GetDelegatedIdentityProfile()
{
   var encAccountName = 
      SPClaimProviderManager.Local.GetUserIdentifierEncodedClaim(
      ServiceSecurityContext.Current.PrimaryIdentity); 
   var claimsAccountName = 
      SPClaimProviderManager.Local.ConvertClaimToIdentifier(encAccountName);
   var um = new UserProfileManager(SPServiceContext.Current, false, false);
   return um.GetUserProfile(claimsAccountName, false);
} 

Getting the Service Application from your WCF Code

Within your service, there is a MAGIC property that you can use to get the instance of your application. SPIisWebServiceApplication.Current will return the current application that your service is created under. Note that you can create multiple instances of the service on the Central Administration “Manage Service Applications” page, each of which could have it’s own persisted data, database connections and more. This magic property is the only thing that ties your WCF class to your service application! In the smaple code above, you’ll see that we’re simply writing out the name and id of the application.

Calling the Backend through a Service Client

The last part of the plumbing is creating an actual service client. With “sane” WCF programming, you usually let Visual Studio do this type of thing for you. Not so in this case however, you’ve got to do a lot to the channel with credentials and dynamic, load-balanced endpoint URLs.

Most of this is “boilerplate” code that uses the SharePoint load balancer to call the application servers. To scale out your application, be sure the service instance is running on multiple app servers. You’ll need to start it on the “Services on Server” page (well, after you create the WCF project, which we’ll do in the next post!). The client will use the ConfigurationChannelFactory and the proxy’s configuration which we’ll deploy in the WSP project in order to configure itself. Specifically, the channel factory will call SuperCoolServiceApplicationProxy.Configuration to read the configuration for the client. We previously defined this as:

internal Configuration Configuration
{
    get
    {
        return OpenClientConfiguration(SPUtility.GetGenericSetupPath(
            @"WebClients\SuperCoolServiceApplication"));
    }
}

Following is a client class that contains the infrastructure to call the backend service. You could make this into a common base class, but I’ll keep it simple to get the concept across. The main methods to look at are “ExecuteOnChannel” and GetChannel. After that, just implement the interface of your service, and use ExecuteOnChannel to call the backend service.

using SuperCool.ServiceLibrary.Services;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System;

namespace SuperCool.ServiceLibrary.Client
{
   /// <summary>
   /// A WCF client for talking through the WCF channel to the backend. 
   /// </summary>
   public class SuperCoolClient : ICoolService
   {
      private SPServiceContext serviceContext;

      public SuperCoolClient() : this(null) { }
      public SuperCoolClient(SPServiceContext serviceContext)
      {
         if (serviceContext == null)
         {
            if (SPServiceContext.Current != null)
               serviceContext = SPServiceContext.Current;
            else
               this.serviceContext = SPServiceContext.GetContext(
                   SPServiceApplicationProxyGroup.Default,
                   new SPSiteSubscriptionIdentifier(Guid.Empty));
         }
         else
            this.serviceContext = serviceContext;

         if (this.serviceContext == null)
            throw new ArgumentNullException("serviceContext");
      }

      private delegate void CodeToExecuteOnChannel(ICoolService channel);

      private void ExecuteOnChannel(CodeToExecuteOnChannel codeBlock, 
         bool asProcess)
      {
         if (codeBlock == null)
            throw new ArgumentNullException("codeBlock");
         SuperCoolServiceApplicationProxy proxy = 
            (SuperCoolServiceApplicationProxy)
            this.serviceContext.GetDefaultProxy(
            typeof(SuperCoolServiceApplicationProxy));

         if (proxy == null)
            throw new InvalidOperationException("service proxy not found.");

         SPServiceLoadBalancer loadBalancer = proxy.LoadBalancer;
         if (loadBalancer == null)
            throw new InvalidOperationException("load balancer not found.");

         SPServiceLoadBalancerContext loadBalancerContext = 
            loadBalancer.BeginOperation();
         try
         {
            using (new SPServiceContextScope(this.serviceContext))
            {
               // Get a channel to the service application endpoint
               IChannel channel = (IChannel)GetChannel(proxy,
                   loadBalancerContext.EndpointAddress, asProcess);
               try
               {
                  // Execute the delegate
                  codeBlock((ICoolService)channel);

                  // Close the channel
                  channel.Close();
               }
               finally
               {
                  if (channel.State != CommunicationState.Closed)
                     channel.Abort();
               }
            }
         }
         catch (EndpointNotFoundException)
         {
            loadBalancerContext.Status = SPServiceLoadBalancerStatus.Failed;
            throw;
         }
         finally
         {
            loadBalancerContext.EndOperation();
         }
      }

      // Used to cache the client channel factory
      private static ChannelFactory<ICoolService> s_ChannelFactory;
      private static object s_ChannelFactoryLock = new object();

      private ICoolService GetChannel(SuperCoolServiceApplicationProxy proxy,
          Uri address, bool asProcess)
      {
         if (proxy == null)
            throw new ArgumentNullException("proxy");

         if (address == null)
            throw new ArgumentNullException("address");

         // Check for a cached channel factory
         if (s_ChannelFactory == null)
         {
            lock (s_ChannelFactoryLock)
            {
               if (s_ChannelFactory == null)
               {
                  s_ChannelFactory = 
                     new ConfigurationChannelFactory<ICoolService>(
                     "https", proxy.Configuration, null);

                  // Configure the channel factory for claims-based auth
                  s_ChannelFactory.ConfigureCredentials(
                     SPServiceAuthenticationMode.Claims);
               }
            }
         }
         if (asProcess)
            return s_ChannelFactory.CreateChannelAsProcess(
               new EndpointAddress(address));
         else
            return s_ChannelFactory.CreateChannelActingAsLoggedOnUser(
               new EndpointAddress(address));
      }

      public string GetServiceDebug()
      {
         string result = null;
         this.ExecuteOnChannel(
            channel => result = channel.GetServiceDebug(), asProcess: false);
         return result;
      }
   }
}

Also note that ExecuteOnChannel takes an “asProcess” parameter—we will either create the channel as the logged in user and configure the SharePoint claim to delegate, or we’ll delegate the current process identity.

The last piece is a ServiceHostFactory. There’s not too much to this, but you’ve got to use a custom host factory to configure claims authentication. We’ll reference this class in the .SVC file. Not sure why there isn’t one of these that SharePoint ships, or maybe there is one that I’m missing… but for some reason you have to do this to wire up the backend WCF endpoint. There’s nothing special about this class for our application.

using System;
using System.ServiceModel.Activation;
using System.ServiceModel;
using Microsoft.SharePoint;

namespace SuperCool.ServiceLibrary.Services
{
    internal sealed class SuperCoolHostFactory: ServiceHostFactory
    {
        public override ServiceHostBase CreateServiceHost(
           string constructorString, Uri[] baseAddresses)
        {
            ServiceHostBase serviceHost = base.CreateServiceHost(
               constructorString, baseAddresses);
            serviceHost.Configure(SPServiceAuthenticationMode.Claims);
            return serviceHost;
        }
    }
}

With the WCF interface, service and client in place, we’re now ready to deploy the service application in a WSP project. We’ll do that next… stay tuned! (And I promise, I’ll post the whole source code for this… )

Posted in Programming, SharePoint, WCF | Tagged , , , | 14 Comments

Step by Step SharePoint Service Applications : Part 2 (Plumbing)

Read the previous posts in this series: Intro, Part 1

In the previous post, we looked at a simple SPIisWebServiceApplication. The SPIisWebServiceApplication is the “instance” of your application that gets provisioned and is managed in Central Administration. It “has” things like the database, jobs and persisted storage associated with it. Soon we’ll add a WCF service which web front end code (and remote farms!) can talk to it with. But before we’ll be able to create the application, we’ll need to define the “Create” links to it in the SharePoint infrastructure. We’ll need a service, a service instance, and then we’ll need a proxy and application proxy in order to talk to it. In this post we’ll focus on these components—they are simple “infrastructure” components without anything super-fancy.

The following class is used to create a menu-item for creating a new Service Application on the Manage Service Applications page. Really the only thing you need to edit is the GetApplicationTypeDescription method and the GetCreateApplicationLink method. The rest is “plumbing” code which you really don’t need to change.

Also note that each of these classes has a Guid attribute. Put a Guid attribute on each “Infrastructure” class for the service application. These generally aren’t referenced, but you’ll need to reference the SPIisWebServiceApplication’s GUID in it’s proxy class. 

In the service library project, the following SPIIsWebService class creates the link to creating a new Super Cool Service application. As I mentioned, there’s nothing spectacular about this code. It’s just used to create the link.

using System;
using Microsoft.SharePoint.Administration;
using System.Runtime.InteropServices;

namespace SuperCool.ServiceLibrary
{
    /// <summary>
    /// The service plumbing-- the thing that's creatable from Central Admin.
    /// </summary>
    [Guid("6810AFD9-3137-4bc2-8AF1-00EB8461A861")]// arbitrary GUID, not referenced in code. 
    public sealed class SuperCoolService : SPIisWebService, IServiceAdministration
    {
        public SuperCoolService() { }
        public SuperCoolService(SPFarm farm): base(farm) { }

        Type[] IServiceAdministration.GetApplicationTypes()
        {
            return new Type[] { typeof(SuperCoolServiceApplication) };
        }

        SPPersistedTypeDescription IServiceAdministration.GetApplicationTypeDescription(
Type serviceApplicationType) { if (serviceApplicationType != typeof(SuperCoolServiceApplication)) { throw new NotSupportedException(); } return new SPPersistedTypeDescription( "Super Cool Service", "A super cool service application for managing super cool things."); } public override SPAdministrationLink GetCreateApplicationLink(Type serviceApplicationType) { return new SPAdministrationLink("/_admin/SuperCoolServiceApplication/default.aspx"); } public override SPCreateApplicationOptions GetCreateApplicationOptions(
Type serviceApplicationType) { return SPCreateApplicationOptions.None; } SPServiceApplication IServiceAdministration.CreateApplication( string name, Type serviceApplicationType, SPServiceProvisioningContext provisioningContext) { throw new NotSupportedException(); } SPServiceApplicationProxy IServiceAdministration.CreateProxy( string name, SPServiceApplication serviceApplication, SPServiceProvisioningContext provisioningContext) { throw new NotSupportedException(); } } }

The next class you’ll need is the SPIisWebServiceInstance class. This isn’t really used for anything but letting SharePoint define which service runs on each server. Nothing spectacular here, and the only thing you really need to specify is the name.

using System.Runtime.InteropServices;
using Microsoft.SharePoint.Administration;

namespace SuperCool.ServiceLibrary
{
    [Guid("0EBCD546-373C-4c1e-83BD-1D3B504F3E98")]
    public class SuperCoolServiceInstance : SPIisWebServiceInstance
    {
        public SuperCoolServiceInstance() { }
        public SuperCoolServiceInstance(SPServer server, SuperCoolService service)
            : base(server, service) { }
                
        /// <summary>
        /// Gets the display name for the administrative UI.
        /// </summary>
        public override string TypeName
        {
            get { return "Super-Duper Super-Cool Service"; }
        }
    }
}

That’s it for Service classes. Now we’ll need to add Proxy classes, which are used by the front end to talk to the backend. These are relatively simple classes too, nothing but plumbing.

The SPIisWebServiceProxy is used to connect to the service application. This isn’t the ACTUAL application proxy, just the “glue” to tie the application proxy to the service. It needs bi-directional references with the SPIisWebServiceApplicationProxy, so if you’re writing your own create your own subclasses of SPIisWebServiceProxy and SPIisWebServiceApplicationProxy at this time. My classes will be called SuperCoolServiceProxy and SuperCoolServiceApplicationProxy.

First, the SPIisWebServiceProxy is used to connect the proxies to the service. The important thing here though is that you need to reference the service application’s Guid that this supports. This is the magic that lets it work… without matching Guids in the SupportedServiceApplication attribute it’s not going to work. The rest is self-explanatory, just copy the following code and refactor it for your app.

using System;
using Microsoft.SharePoint.Administration;
using System.Runtime.InteropServices;

namespace SuperCool.ServiceLibrary.Client
{
    [Guid("4DE3E259-EC52-4841-9D5B-B9A3AA095B51")]
    [SupportedServiceApplication("12A1BE54-04E7-4457-B8FE-6E11647E7EA6", 
        "1.0.0.0", typeof(SuperCoolServiceApplicationProxy))]   
    public class SuperCoolServiceProxy : SPIisWebServiceProxy, IServiceProxyAdministration
    {
        public SuperCoolServiceProxy() { }
        public SuperCoolServiceProxy(SPFarm farm) : base(farm) { }

        /// <summary>
        /// Gets an array of the service application proxy types supported by the service proxy.
        /// </summary>
        Type[] IServiceProxyAdministration.GetProxyTypes()
        {
            return new Type[] { typeof(SuperCoolServiceApplicationProxy) };
        }

        /// <summary>
        /// Gets a description of the specified service application proxy type.
        /// </summary>
        /// <param name="serviceApplicationProxyType">The service application proxy type.</param>
        /// <returns>A description of the specified type suitable for display.</returns>
        SPPersistedTypeDescription IServiceProxyAdministration.GetProxyTypeDescription(
            Type serviceApplicationProxyType)
        {
            if (serviceApplicationProxyType != typeof(SuperCoolServiceApplicationProxy))
                throw new NotSupportedException();

            return new SPPersistedTypeDescription(
                "Super-Duper Super Cool Service Proxy",
                "Connects to a Super Cool Service Application for managing super cool things.");
        }

        /// <summary>
        /// Creates an instance of the specified service application proxy type.
        /// </summary>
        /// <param name="serviceApplicationProxyType">The type of proxy to create.</param>
        /// <param name="name">The name of the new proxy.</param>
        /// <param name="serviceApplicationUri">The address of the connected service application.</param>
        /// <param name="provisioningContext">Context for the provisioning operation.</param>
        /// <returns></returns>
        SPServiceApplicationProxy IServiceProxyAdministration.CreateProxy(
            Type serviceApplicationProxyType, string name, Uri serviceApplicationUri, 
SPServiceProvisioningContext provisioningContext) { if (serviceApplicationProxyType != typeof(SuperCoolServiceApplicationProxy)) throw new NotSupportedException(); return new SuperCoolServiceApplicationProxy(name, this, serviceApplicationUri); } } }

 

Next, the ACTUAL code that is the proxy is responsible for setting up the load balancer and reading the client configuration. You’re going to want to scale your service application across multiple app servers—we need redundancy for our Hello World service app!!! But again, the code is relatively simple. You should be able to refactor the following code without effort. Following is a simple implementation of the SPIisWebServiceApplicationProxy. Next to the SPIisWebServiceApplication class, this is the next most important piece of the puzzle. There isn’t TOO much in here though, we’ll actually code much of the proxy logic into the client.

using System;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint;
using System.Configuration;
using Microsoft.SharePoint.Utilities;
using SuperCool.ServiceLibrary.Services;

namespace SuperCool.ServiceLibrary.Client
{
    [System.Runtime.InteropServices.Guid("D49DF84A-77E3-47f9-85D3-965FA3D9E305")]
    [IisWebServiceApplicationProxyBackupBehavior]
    public class SuperCoolServiceApplicationProxy : SPIisWebServiceApplicationProxy
    {
        [Persisted]
        private SPServiceLoadBalancer loadBalancer;
        public SuperCoolServiceApplicationProxy(){}

        /// <summary>
        /// Constructs a new service application proxy.
        /// </summary>
        /// <param name="name">The name of the new service application proxy.</param>
        /// <param name="serviceProxy">The parent service proxy.</param>
        /// <param name="serviceApplicationAddress">
        /// The URI of the service application to which the new proxy will connect.</param>
        public SuperCoolServiceApplicationProxy(string name, SuperCoolServiceProxy serviceProxy, 
            Uri serviceApplicationAddress)
            : base(name, serviceProxy, serviceApplicationAddress)
        {
            // Create a new round-robin load balancer
            this.loadBalancer = new SPRoundRobinServiceLoadBalancer(serviceApplicationAddress);
        }

        /// <summary>
        /// Gets the display name that describes the object in the admin UI
        /// </summary>
        public override string TypeName
        {
            get { return "Super Cool Application Proxy"; }
        }

        public override void Provision()
        {
            this.loadBalancer.Provision();
            base.Provision();
        }

        public override void Unprovision(bool deleteData)
        {
            // Unprovision the load balancer
            this.loadBalancer.Unprovision();
            base.Unprovision(deleteData);
        }

        /// <summary>
        /// Gets the load balancer used by the service application proxy.
        /// </summary>
        internal SPServiceLoadBalancer LoadBalancer
        {
            get
            {
                return this.loadBalancer;
            }
        }

        /// <summary>
        /// Gets the configuration used by the service application proxy.
        /// </summary>
        internal Configuration Configuration
        {
            get
            {
                return OpenClientConfiguration(SPUtility.GetGenericSetupPath(
                    @"WebClients\SuperCoolServiceApplication"));
            }
        }
    }
}

With those classes, SPIisWebServiceApplication (covered in the last post), SPIisWebService, SPIisWebServiceInstance, SPIisWebServiceProxy and SPIisWebServiceApplicationProxy, we’ve got the SharePoint plumbing part of our service application library completed. Nothing too complex, but a lot of redundant code. In the next post, we’ll create our Hello World WCF service and we’ll cover the WCF plumbing, and then we’ll look at deploying it in the WSP project. After that, we’ll circle back and talk about databases and timer jobs within the service application framework.

About Daniel Larson: I am a SharePoint Architect at NewsGator, (the Social Platform for SharePoint 2010) and author of the best-selling Microsoft Press books Inside Microsoft Windows SharePoint Services 3.0 (with Ted Pattison) and Developing Service-Oriented AJAX Applications.

Posted in Programming, SharePoint | 3 Comments

Step by Step SharePoint Service Applications : Part 1

If you missed the intro, check it out for some background on WHY you’d want to create a backend service library for SharePoint 2010. (Read it here!) It really isn’t for everyone—but if you’re creating enterprise scale applications for SharePoint 2010, the service architecture/framework is the right way to scale out your application.

To get stated, create a Class Library called “SuperCool.ServiceLibrary”. This will be the backend service library, and will also contain the proxy classes and client proxy.

At the heart of the service library is the SPIisWebServiceApplication. This class is your application instance, but it is NOT the backend WCF service that you will call. (We’ll cover that later.) This object is persisted in the Config database, and holds references to the WCF endpoints it serves as well as any databases you create. It will also be responsible for provisioning any jobs you define for your application.

The first three methods are what points the application to your backend WCF service (that we haven’t written yet), so you can ignore those for now but you’ve got to include them. The rest is pretty much boilerplate code, it sets up the application plumbing.

using System;
using System.Collections.Generic;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Utilities;
using Microsoft.SharePoint;

namespace SuperCool.ServiceLibrary
{           
    /// <summary>
    /// The conceptual service application. 
    /// </summary>
    // This magic GUID is used to define the service application and match it with supported proxies
    [System.Runtime.InteropServices.Guid(“ACE7BB07-1891-46e0-A7DD-0B23CD7ED90D”)]
    [IisWebServiceApplicationBackupBehavior]
    public class SuperCoolServiceApplication : SPIisWebServiceApplication
    {       
        protected override string VirtualPath
        { get{ return “SuperCool.svc”; } }

        protected override string DefaultEndpointName
        { get { return “https”; } }

        protected override string InstallPath
        {
            get {return SPUtility.GetGenericSetupPath(@”WebServices\SuperCoolServiceApplication”); }
        }

        /// <summary>
        /// Target location admin is sent to from within CA when clicking on Service App or
        /// selecting it and picking Manage in the ribbon from CA > Manage Service Apps page.
        /// </summary>
        public override SPAdministrationLink ManageLink
        {
            get { return new SPAdministrationLink(
           “/_admin/SuperCoolServiceApplication/Manage.aspx?ApplicationId=” + this.Id.ToString(“N”)); }
        }
       
        public SuperCoolServiceApplication(){}

        public SuperCoolServiceApplication(string name, SPIisWebService service, SPIisWebServiceApplicationPool applicationPool)
            : base(name, service, applicationPool){}

        public override void Provision()
        {
            base.Provision();
            if (this.Id == Guid.Empty)
                this.Update();
           
            RegisterEndpoint(this, “https”, SPIisWebServiceBindingType.Https, “secure”);
             
            if (this.Service.Status != SPObjectStatus.Online)
            {
                //this.Service.Provision();
                this.Service.Status = SPObjectStatus.Online;
                this.Service.Update();
            }
            this.Status = SPObjectStatus.Online;
            this.Update();
        }

        public override void Unprovision(bool deleteData)
        {
            foreach (SPIisWebServiceEndpoint ep in this.Endpoints)
                ep.Delete();           
            base.Unprovision(deleteData);
        }

        private static void RegisterEndpoint(SPIisWebServiceApplication serviceApplication, string name, SPIisWebServiceBindingType binding, string relativeAddress)
        {           
            foreach (SPIisWebServiceEndpoint endpoint in serviceApplication.Endpoints)
                if (endpoint.Name == name)
                    return;
       
if (!string.IsNullOrEmpty(relativeAddress))
                serviceApplication.AddServiceEndpoint(name, binding, relativeAddress);           
            else
                serviceApplication.AddServiceEndpoint(name, binding);
         }
    }
}

The Provision method is the main method that sets things up. This method registers the endpoints, and is also where you’d provision any databases, jobs or additional resources that will be “owned” by the service application.

With that one class, we’ve got the guts of Service Application created. In the next few posts we’ll look at creating the service instance, the service proxy and client, and we’ll wire it up through the SharePoint solution.

(Where are we going with this? This will just be a sample app that spits out “hello, world”. But it’ll have a backend database and be centrally managed. I’ll leave it to you to come up with something interesting, something super-cool to add to this!!!)

Posted in SharePoint | 4 Comments

Step by Step SharePoint Service Applications: Intro

In the following blog posts, I’ll be focusing on writing custom service applications for SharePoint 2010. Service Applications are shared services that are managed in Central Administration that can be consumed by the local or remote farms. Service applications can include a WCF backend service, timer jobs, custom database(s), permissions and central administration management.

For trivial web part applications, a Service Application is overkill. But to move your application to a service-based architecture that can be consumed across the enterprise (by SharePoint farms, that is…) then you should consider a Service Application.

Reasons to write a service application:

  1. You need a custom database
  2. You need Central Administration of your application
  3. You want to publish your application services to multiple web applications
  4. You want to publish your application services to multiple SharePoint farms

Probable the best use case example of a managed Service Application is the SharePoint User Profile Application. It provides the “User Profile” framework with My Site and My NewsFeed integration across your SharePoint web applications, even across multiple SharePoint farms.

There are multiple sample applications and blogs about this (just use the google!) but I found them a bit tough to follow, and not very repeatable. After shipping 2 enterprise class Service Applications at NewsGator, I decided it’s time to break it down into it’s simplest repeatable pattern. So in the following posts, we’ll talk about:

  1. The “Core” of the Service Application: SPIisWebServiceApplication
  2. Service Application Plumbing: Service instances, service provisioning, proxies, and more.
  3. Service Application Plumbing: WCF, the Hard Way
  4. Deploying the Service Application in a WSP.

SharePoint Projects (WSP projects) are ONLY for Deployment

To start off, it’s important to get a good, clean code story. Whenever I write solutions for SharePoint, I follow the golden development rule: “WSPs are ONLY for Deployment”. Keep your code in a class library. Following that rule will make your code much cleaner, simpler to follow, and reusable. For example, at NewsGator we have a WSP deployment project, a data contract project, a service library project (for the Service Application), a Web Part project, and code libraries. I recommend splitting up your code into a similar structure, it makes references a whole lot easier and separates out boundaries. But at a minimum, you’ll need a WSP project for deployment, and a class library.

Credit

The first pass of my service applications came from Andrew Connell’s sample code. You may find some remnants of that in the code I post, however it’s mostly rewritten from scratch. Big thanks to AC for writing the first reliable sample service application.

Let’s Go!

To get started, create a SharePoint project named SuperCoolServiceApplication, and a class library named SuperCool.ServiceLibrary. Then follow along the next few posts and you’ll be up and running with your own service application in no time. First we’ll cover the basics. Then we’ll go back and add a database, jobs, and security.

In the class library, make sure you add references to Microsoft.SharePoint and System.ServiceModel. Then get ready, we’ll dive into writing code in the next post.

Continue on to part 1!

Posted in Uncategorized | 3 Comments

SharePoint Service Based Ajax: talk from SharePoint Fest Denver

Yesterday I spoke at SharePoint Fest Denver about building service-based AJAX applications on SharePoint Server 2010. Basically, use WCF REST Web Services for data access, and use a JavaScript client to render your Web Parts. We also talked about the SharePoint AJAX Library, a simple toolkit for client-side rendering within Web Parts. Not only is this an open source project, it’s at the heart of NewsGator product code, and has been tested thoroughly and deployed in the top SharePoint installations worldwide through the NewsGator channel. 

Here’s the slide deck plus sample code. The project site for the SharePoint Ajax project is at sharepointajax.codeplex.com.

  • Slide Deck
  • Sample code (Includes SharePoint Ajax Library 1.1 for SharePoint 2010, plus sample code)
  • More posts soon, I’ve been so busy with my children lately and haven’t had time to write! Check out my cute kids on slide 2. 🙂

    Posted in SharePoint | Leave a comment

    SharePoint Snowboarding with NewsGator

    Where else but Denver can you write SharePoint code all day, and ride fresh powder all afternoon until 8 at night? Joining the usual NewsGator snowboarding crew is Shannon Bray of Planet Technologies. Video shot at Loveland Pass on April 8, 2010. This might be the last NG run for this year, we’ll pick up the tradition in 2011 from Daylight Savings until April. Next up: we’re shipping betas of NewsGator Social Sites 2010, which will RTM by SP2010 RTM. SharePoint social is not complete without it.

    Posted in NewsGator | Leave a comment

    Chat with the SharePoint MVP Experts

    Monday, March 15th at 10MST I’ll be a part of the following MSDN chat on SharePoint 2010. This is a great opportunity to talk to some really smart SharePoint thought leaders about SharePoint 2010.

    Do you have tough technical questions regarding SharePoint for which you’re seeking answers? Do you want to tap into the deep knowledge of the talented Microsoft Most Valuable Professionals? The SharePoint MVPs are the same people you see in the technical community as authors, speakers, user group leaders and answerers in the MSDN forums. This is the first time we have brought these experts together as a collective group to answer your questions live. So please join us and bring on the questions! This chat will cover WSS, MOSS and the SharePoint 2010 beta. Topics include setup and administration, design, development and general questions.

    Andrew Connell, Becky Bertram, Brendon Schwartz, Dan Attis, Darrin Bishop, John Ross, Matthew McDermott, Mike Oryszak, Paul Galvin, Paul Schaeflein, Randy Drisgill, and Rob Foster and myself will be leading this chat. 

    When:  March 15, 2010
    Time:  9:00 am PDT
    Where:  MSDN Chat Room
    Add this to your calendar
    Join this Facebook Event
    Twitter Hash Tag #SPMVPChat

    Posted in SharePoint | Leave a comment

    ASP.NET at Arapahoe Community College

    We all have something we’d like to do “when we retire”. For me, it’s teaching. After a successful programming career, I’d like to retire and teach at a community college. What better way to give back to the local community by teaching others how to excel in their fields? But if there’s something that’s worth doing later in life, why isn’t it worth doing now?

    Well, I’m not about to retire YET, and my career at NewsGator is heating up after the acquisition of Tomoye this week. But still, why should I wait until I retire? Starting Monday, January 25th I’ll be teaching ASP.NET Application Development at Arapahoe Community College. I’ll cover the basics but we’re going to heat it up pretty quickly as we get into modern web application development. One thing for sure—”classic” ASP.NET is dead. So we’ll cover the underlying technology, course requirements, and then how to build a modern application framework using AJAX and web services. Oh, and a little bit of ASP.NET of course. 🙂

    If you’re in the Denver area, you can register at http://www.arapahoe.edu/ and sign up for class “CSC 253 MS ASP.NET WEB APP DEVELOPMENT”. Here’s the textbook: Beginning ASP.NET 3.5 In C#.

    UPDATE: STUDENTS, the class folder share is here: ACC_ASPNET. You can download the class slides and course syllabus.

    Posted in Uncategorized | Leave a comment