Tuesday 24 November 2009

Create Custom Service Application in SharePoint 2010 – Part 2

Create Service Application - Introduction

Next we will create a number of classes for the Hello service application. Each of these classes represents a key component for a service application, such as:

  • Service
  • Service Proxy
  • Service Application
  • Service Application Proxy
  • Service Instance
Service Instance

Create a new class in the root folder of the project and call it “HelloServiceInstance.cs”. The following shows the code for the HelloServiceInstance class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Administration;

namespace SharePointEgg
{
    [System.Runtime.InteropServices.Guid("AC6D9D69-7132-429A-BB1B-FD60B124254B")]
    public class HelloServiceInstance : SPIisWebServiceInstance
    {
        private const string HelloServiceInstanceName = "HelloServiceInstance";

        // Methods
        public HelloServiceInstance()
        {
        }

        internal HelloServiceInstance(SPServer server, HelloService service)
            : base(server, service)
        {
        }

        internal HelloServiceInstance(string name, SPServer server, HelloService service)
            : base(server, service)
        {
        }

        // Properties
        public override string DisplayName
        {
            get
            {
                return HelloServiceInstanceName;
            }
        }

        internal bool IsLocal
        {
            get
            {
                SPServer local = SPServer.Local;
                SPServer server = base.Server;
                return ((local != null) && (local.Id == server.Id));
            }
        }

        internal static bool IsLocalInstanceOnline
        {
            get
            {
                bool flag2;
                try
                {
                    bool isOnline = false;
                    SPServer local = SPServer.Local;
                    if (local != null)
                    {
                        HelloServiceInstance instance = local.ServiceInstances.GetValue<HelloServiceInstance>();
                        if (instance != null)
                        {
                            isOnline = instance.IsOnline;
                        }
                    }
                    flag2 = isOnline;
                }
                catch
                {
                    //ULS.SendTraceTag(0x38337a6c, UlsInformation.CalculationServer, ULSTraceLevel.Unexpected, "ExcelServerWebServiceInstance.IsLocalInstanceOnline: caught an unexpected exception. Exception: {0}", new object[] { exception });
                    throw;
                }
                return flag2;
            }
        }

        internal bool IsOnline
        {
            get
            {
                return (base.Status == SPObjectStatus.Online);
            }
        }

        internal static HelloServiceInstance Local
        {
            get
            {
                SPServer local = SPServer.Local;
                if (local == null)
                {
                    //ULS.SendTraceTag(0x39786c6c, UlsInformation.Management, ULSTraceLevel.Verbose, "ExcelServerWebServiceInstance.Local: Could not determine local server context.");
                    throw new InvalidOperationException();
                }
                return local.ServiceInstances.GetValue<HelloServiceInstance>();
            }
        }

        public override string TypeName
        {
            get
            {
                return HelloServiceInstanceName;
            }
        }
    }
}
Service Application

Next create a new class called HelloServiceApplication.cs. This class will implements IHelloService and contains the actual implementation of our Hello method.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Utilities;
using SharePointEgg.WebServices.HelloServiceApplication;

namespace SharePointEgg
{
    [System.Runtime.InteropServices.Guid("C18B3BB5-C0AE-4873-95A2-39AED8657A6C")]
    public sealed class HelloServiceApplication : SPIisWebServiceApplication, IHelloService
    {
        private HelloServiceInstance m_serviceInstance;

        // Methods
        public HelloServiceApplication()
        {
        }

        internal HelloServiceApplication(string name, HelloService service, SPIisWebServiceApplicationPool applicationPool)
            : base(name, service, applicationPool)
        {
        }

        internal static HelloServiceApplication GetApplicationById(Guid applicationId)
        {
            return HelloService.Local.GetWebApplicationById(applicationId);
        }

        internal static HelloServiceApplication GetApplicationByName(string applicationName)
        {
            return HelloService.Local.GetWebApplicationByName(applicationName);
        }


        protected override string VirtualPath
        {
            get
            {
                return "Service.svc";
            }
        }

        protected override string InstallPath
        {
            get
            {
                return Path.GetFullPath(SPUtility.GetGenericSetupPath(@"WebServices\HelloServiceApplication"));
            }
        }

        public override SPAdministrationLink ManageLink
        {
            get
            {
                //return new SPAdministrationLink(ExcelServerAdminPage.UrlWithApplicationId("ExcelServicesAdmin.aspx", base.Id, true));
                return new SPAdministrationLink(string.Format("/_admin/HelloServiceApplication/HelloServiceAdmin.aspx?id={0}", Id.ToString()));
            }
        }

        public Uri DefaultServicePath
        {
            get { return this.DefaultEndpoint.GetEndpointUri(this.OnlineInstance); }
        }

        public HelloServiceInstance OnlineInstance
        {
            get
            {
                if (null == m_serviceInstance)
                {
                    foreach (SPServiceInstance i in this.ServiceInstances)
                    {
                        HelloServiceInstance instance = i as HelloServiceInstance;
                        if ((instance != null) && instance.IsOnline)
                        {
                            m_serviceInstance = instance;
                            break;
                        }
                    }
                }

                return m_serviceInstance;
            }
        }


        public string Hello(string name)
        {
            return string.Format("Hello!!! {0}, this is from the Hello Service Application", name);
        }
    }
}
Service Application Proxy

Next create a new class called HelloServiceApplicationProxy.cs:

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

namespace SharePointEgg
{
    [System.Runtime.InteropServices.Guid("A15526E7-97F8-499D-A5A7-22A7A8920162")]
    public sealed class HelloServiceApplicationProxy : SPIisWebServiceApplicationProxy
    {
        // Fields
        [Persisted]
        private HelloServiceApplication m_application;

        // Methods
        public HelloServiceApplicationProxy()
        {
        }

        internal HelloServiceApplicationProxy(string name, SPIisWebServiceProxy serviceProxy, HelloServiceApplication serviceApplication)
            : base(name, serviceProxy, serviceApplication.Uri)
        {
            this.m_application = serviceApplication;
        }

        internal HelloServiceApplicationProxy(string name, SPIisWebServiceProxy serviceProxy, Uri serviceApplicationAddress)
            : base(name, serviceProxy, serviceApplicationAddress)
        {
        }

        internal void AddToDefaultGroup(bool setDefault)
        {
            if (setDefault)
            {
                //ULS.SendTraceTag(0x39766662, UlsInformation.Management, ULSTraceLevel.Medium, "ExcelServerWebServiceApplicationProxy.AddToDefaultGroup: Adding proxy {0} to the default proxy group.", new object[] { base.Name });
                SPServiceApplicationProxyGroup group = SPServiceApplicationProxyGroup.Default;
                group.Add(this);
                group.Update();
            }
        }

        // Properties
        public HelloServiceApplication Application
        {
            get
            {
                if (this.m_application == null)
                {
                    //ULS.SendTraceTag(0x636e6139, UlsInformation.Management, ULSTraceLevel.High, "ExcelServerWebServiceApplicationProxy.Application: Application reference was null. This indicates a stale proxy.");
                    return null;
                }
                return HelloService.Local.GetWebApplicationByName(this.m_application.Name);
            }
        }

        internal static string ClientConfigurationPath
        {
            get
            {
                return SPUtility.GetGenericSetupPath(@"WebClients\HelloService");
            }
        }

        internal static HelloServiceApplicationProxy Local
        {
            get
            {
                SPServiceContext current = SPServiceContext.Current;
                if (current == null)
                {
                    //ULS.SendTraceTag(0x39657a34, UlsInformation.Management, ULSTraceLevel.High, "ExcelServerWebServiceApplicationProxy.Local: Could not retrieve the application for the current context because the context could not be retrieved.");
                    throw new InvalidOperationException("Could not retrieve the application for the current context because the context could not be retrieved.");
                }
                return (HelloServiceApplicationProxy)current.GetDefaultProxy(typeof(HelloServiceApplicationProxy));
            }
        }

        public override string TypeName
        {
            get
            {
                return "Hello Service Application Proxy";
            }
        }

        public static HelloServiceApplicationProxy GetProxy(SPServiceContext serviceContext)
        {
            if (serviceContext == null)
            {
                throw new ArgumentNullException("serviceContext");
            }
            return (serviceContext.GetDefaultProxy(typeof(HelloServiceApplicationProxy)) as HelloServiceApplicationProxy);
        }

    }
}
Service

Next create a new class called HelloService.cs and have the following code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Administration;

namespace SharePointEgg
{
    [System.Runtime.InteropServices.Guid("2DD80E70-B651-44E7-A5B9-6F820B15865C")]
    public class HelloService : SPIisWebService, IServiceAdministration
    {
        private const string ServiceName = "HelloService";
        private static HelloService m_local = null;

        // Methods
        public HelloService()
        {
        }

        internal HelloService(SPFarm farm)
            : base(farm)
        {
            this.Name = ServiceName;
        }

        //internal HelloService(string name, SPFarm farm)
        //    : base(farm)
        //{
        //}

        internal HelloServiceApplication CreateApplication(string name, SPIisWebServiceApplicationPool applicationPool)
        {
            //ULS.SendTraceTag(0x64333071, UlsInformation.CalculationServer, ULSTraceLevel.VerboseEx, "ExcelServerWebService.CreateApplication: About to create web service application with name = {0}.", new object[] { name });
            HelloServiceApplication application = new HelloServiceApplication(name, this, applicationPool);
            //ULS.SendTraceTag(0x64333072, UlsInformation.CalculationServer, ULSTraceLevel.VerboseEx, "ExcelServerWebService.CreateApplication: Web service application created.");
            application.Update();
            //ULS.SendTraceTag(0x64333073, UlsInformation.CalculationServer, ULSTraceLevel.VerboseEx, "ExcelServerWebService.CreateApplication: Web service application committed to database.");
            application.AddServiceEndpoint("", SPIisWebServiceBindingType.Http);
            application.AddServiceEndpoint("secure", SPIisWebServiceBindingType.Https, "secure");
            //ULS.SendTraceTag(0x64333074, UlsInformation.CalculationServer, ULSTraceLevel.VerboseEx, "ExcelServerWebService.CreateApplication: Service end-points added.");
            return application;
        }

        public SPServiceApplication CreateApplication(string name, Type serviceApplicationType, SPServiceProvisioningContext provisioningContext)
        {
            if (!ValidateType(serviceApplicationType))
            {
                return null;
            }
            HelloServiceApplication webApplicationByName = this.GetWebApplicationByName(name);
            if (((webApplicationByName == null) && (provisioningContext != null)) && (provisioningContext.IisWebServiceApplicationPool != null))
            {
                webApplicationByName = this.CreateApplication(name, provisioningContext.IisWebServiceApplicationPool);
            }
            return webApplicationByName;
        }

        internal HelloServiceApplicationProxy CreateProxy(string name, SPServiceApplication serviceApplication)
        {
            return (HelloServiceApplicationProxy)this.CreateProxy(name, serviceApplication, null);
        }

        public SPServiceApplicationProxy CreateProxy(string name, SPServiceApplication serviceApplication, SPServiceProvisioningContext provisioningContext)
        {
            if (null == serviceApplication)
            {
                throw new ArgumentNullException("serviceApplication");
            }
            if (serviceApplication.GetType() != typeof(HelloServiceApplication))
            {
                throw new NotSupportedException();
            }
            HelloServiceProxy serviceProxy = (HelloServiceProxy)base.Farm.GetObject(string.Empty, base.Farm.Id, typeof(HelloServiceProxy));
            if (serviceProxy == null)
            {
                serviceProxy = new HelloServiceProxy(base.Farm);
                serviceProxy.Update();
            }
            HelloServiceApplicationProxy applicationProxy = HelloServiceProxy.Local.GetApplicationProxy(name);
            if (applicationProxy != null)
            {
                return applicationProxy;
            }
            return new HelloServiceApplicationProxy(name, serviceProxy, (HelloServiceApplication)serviceApplication);
        }

        private static HelloServiceApplication EnsureSettingsAttributesInitialization(HelloServiceApplication application)
        {
            if (application != null)
            {
                //application.InitAttributes();
            }
            return application;
        }

        public SPPersistedTypeDescription GetApplicationTypeDescription(Type serviceApplicationType)
        {
            if (!ValidateType(serviceApplicationType))
            {
                return null;
            }
            return new SPPersistedTypeDescription("Hello Service Application", "Hello Service Application");
        }

        public Type[] GetApplicationTypes()
        {
            return new Type[] { typeof(HelloServiceApplication) };
        }

        public override SPAdministrationLink GetCreateApplicationLink(Type serviceApplicationType)
        {
            if (!ValidateType(serviceApplicationType))
            {
                return null;
            }
            //return new SPAdministrationLink("/_admin/ExcelServerCreateApplication.aspx?scenarioid=ExcelServicesCreateApplication");
            return new SPAdministrationLink("/_admin/HelloServiceApplication/CreateApplication.aspx");
        }

        internal static HelloService GetServiceByFarm(SPFarm farm)
        {
            if (null == farm)
            {
                throw new InvalidOperationException("The SharePoint farm has not been provisioned properly.");
            }
            return farm.Services.GetValue<HelloService>();
        }

        internal HelloServiceApplication GetWebApplicationById(Guid applicationId)
        {
            return EnsureSettingsAttributesInitialization(base.Applications.GetValue<HelloServiceApplication>(applicationId));
        }

        internal HelloServiceApplication GetWebApplicationByName(string applicationName)
        {
            return EnsureSettingsAttributesInitialization(base.Applications.GetValue<HelloServiceApplication>(applicationName));
        }

        private static bool ValidateType(Type type)
        {
            if (type != typeof(HelloServiceApplication))
            {
                //ULS.SendTraceTag(0x39766661, UlsInformation.Management, ULSTraceLevel.Medium, "ExcelServerWebService.ValidateType: Only applications of type ExcelServerWebServiceApplication are supported by the Excel Service.");
                return false;
            }
            return true;
        }

        // Properties
        public override string DisplayName
        {
            get
            {
                return ServiceName;
            }
        }

        public static HelloService Local
        {
            get
            {
                if (HelloService.m_local == null)
                {
                    HelloService.m_local =
                      SPFarm.Local.Services.GetValue<HelloService>("HelloService");
                }

                return HelloService.m_local;

                //HelloService serviceByFarm = GetServiceByFarm(LocalFarm);
                //if (serviceByFarm == null)
                //{
                //    throw new InvalidOperationException("The HelloService has not been properly registered with the config DB.");
                //}
                //return serviceByFarm;
            }
        }

        private static SPFarm LocalFarm
        {
            get
            {
                SPFarm local = SPFarm.Local;
                if (local == null)
                {
                    throw new InvalidOperationException("The SharePoint farm has not been provisioned properly.");
                }
                return local;
            }
        }

        public override string TypeName
        {
            get
            {
                return ServiceName;
            }
        }
    }
}
Service Proxy

Lastly create a new class called "HelloServiceProxy.cs”. Note that it has a SupportedServiceApplication decorative and it is referencing the HelloServiceApplicationProxy by its Guid.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Administration;

namespace SharePointEgg
{
    [System.Runtime.InteropServices.Guid("1CB0F971-42A3-4BCE-ACC1-1D7CDF22BBBE")]
    [SupportedServiceApplication("A15526E7-97F8-499D-A5A7-22A7A8920162", "1.0.0.0", typeof(HelloServiceApplicationProxy))]
    internal sealed class HelloServiceProxy : SPIisWebServiceProxy, IServiceProxyAdministration
    {
        // Methods
        public HelloServiceProxy()
        {
        }

        internal HelloServiceProxy(SPFarm farm)
            : base(farm)
        {
        }

        internal HelloServiceProxy(string name, SPFarm farm)
            : base(farm)
        {
        }

        public SPServiceApplicationProxy CreateProxy(Type serviceApplicationProxyType, string name, Uri serviceApplicationUri, SPServiceProvisioningContext provisioningContext)
        {
            if (serviceApplicationProxyType != typeof(HelloServiceApplicationProxy))
            {
                throw new NotSupportedException();
            }
            return new HelloServiceApplicationProxy(name, this, serviceApplicationUri);
        }

        internal HelloServiceApplicationProxy GetApplicationProxy(string name)
        {
            return this.ApplicationProxies.GetValue<HelloServiceApplicationProxy>(name);
        }

        public SPPersistedTypeDescription GetProxyTypeDescription(Type serviceApplicationProxyType)
        {
            return new SPPersistedTypeDescription("Hello Service Proxy", "Hello Service Proxy");
        }

        public Type[] GetProxyTypes()
        {
            return new Type[] { typeof(HelloServiceApplicationProxy) };
        }

        internal static HelloServiceProxy GetServiceProxyByFarm(SPFarm farm)
        {
            SPServiceProxyCollection serviceProxies = farm.ServiceProxies;
            if (serviceProxies == null)
            {
                throw new InvalidOperationException("The SharePoint farm has not been provisioned properly.");
            }
            HelloServiceProxy proxy = serviceProxies.GetValue<HelloServiceProxy>();
            if (proxy == null)
            {
                throw new InvalidOperationException("The HelloService has not been properly registered with the config DB.");
            }
            return proxy;
        }

        // Properties
        internal static HelloServiceProxy Local
        {
            get
            {
                return GetServiceProxyByFarm(LocalFarm);
            }
        }

        private static SPFarm LocalFarm
        {
            get
            {
                SPFarm local = SPFarm.Local;
                if (local == null)
                {
                    throw new InvalidOperationException("The SharePoint farm has not been provisioned properly.");
                }
                return local;
            }
        }
    }
}

And we have completed our custom Hello service application implementation. But we are not there yet. We will need to create administration pages for service creation and management. And we also need to register the service application to the farm so that we can create an instance of it. More to come next.

1 comment:

Unknown said...

Wilson,
I am eager to try your sample. Would you have a source solution to speed this up?
Cheers
Leonid