Tuesday 24 November 2009

Create Custom Service Application in SharePoint 2010 – Part 3

Creating Service Administration Pages

We will need to create two administration pages for our Hello service application. One will be used for service application instance creation, and the other one for managing existing application instance. As a reference we will use the pages for Excel Service, which can be located in the ADMIN folder (C:\Program Files\Common Files\Microsoft Shared\Web Server Extensions\14\TEMPLATE\ADMIN).

First we will create a mapped folder to the ADMIN folder and this is the folder where the administration pages will deploy. To do this, right click the HelloServiceApplication > Add > SharePoint Mapped Folder..., then expand the “TEMPLATE” node and select the “ADMIN” folder. A new ADMIN folder “HelloServiceApplication” will be added to the project.

We will first create the service creation page. Right click the newly created “HelloServiceApplication” folder under “ADMIN” and add a new item. Select the “Application Page” template and give the page a name “CreateApplication.aspx”

I am using Visual Studio 2010 Beta 2 and notice a funny thing. VS2010 will create the CreateApplication.aspx in “Layouts/HelloServiceAplication”. If you have this problem, just drag the aspx file to ADMIN\HelloServiceApplication, as this is the location where we referencing in out Service Application implementation (HelloService.cs).

Put the following code in the aspx file:

<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
<%@ Register TagPrefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="wssawc" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="CreateApplication.aspx.cs" Inherits="SharePointEgg.Layouts.HelloServiceApplication.CreateApplication" MasterPageFile="~/_layouts/dialog.master" %>

<%@ Assembly Name="Microsoft.Office.Excel.Server.MossHost, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>
<%@ Assembly Name="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>
<%@ Register TagPrefix="wssuc" TagName="LinksTable" src="/_controltemplates/LinksTable.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="InputFormSection" src="/_controltemplates/InputFormSection.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="InputFormControl" src="/_controltemplates/InputFormControl.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="LinkSection" src="/_controltemplates/LinkSection.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="ButtonSection" src="/_controltemplates/ButtonSection.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="ActionBar" src="/_controltemplates/ActionBar.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="ToolBar" src="/_controltemplates/ToolBar.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="ToolBarButton" src="/_controltemplates/ToolBarButton.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="Welcome" src="/_controltemplates/Welcome.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="IisWebServiceApplicationPoolSection" src="~/_admin/IisWebServiceApplicationPoolSection.ascx" %>

<asp:Content ID="Content1" contentplaceholderid="PlaceHolderDialogHeaderPageTitle" runat="server">
    <asp:Literal ID="CreateASAppTitle" Text="Create New Hello Service Application" runat="server"/>
</asp:Content>

<asp:Content ID="Content2" contentplaceholderid="PlaceHolderDialogDescription" runat="server">
    <asp:Literal ID="CreateASAppDesc" Text="Specify the name, application pool, and default for this Application." runat="server"/>
</asp:Content>

<asp:Content ID="Content3" ContentPlaceHolderId="PlaceHolderDialogBodyMainSection" runat="server">    
    <TABLE border="0" cellspacing="0" cellpadding="0" width="100%" class="ms-authoringcontrols">
        <wssuc:InputFormSection
            Title="<%$Resources:xlsrv, ManagementUI_ServiceAppNameLabel%>"
            runat="server">
            <Template_InputFormControls>
                <wssuc:InputFormControl LabelText="" LabelAssociatedControlID="TextBoxAppName" runat="server">
                    <Template_control>
                        <wssawc:InputFormTextBox title="<%$Resources:xlsrv, ManagementUI_ServiceAppNameLabel%>" class="ms-input" ID="TextBoxAppName" Columns="35" Runat="server" MaxLength=256 />
                        <wssawc:InputFormRequiredFieldValidator ID="AppNameValidator"
                            ControlToValidate="TextBoxAppName"
                            ErrorMessage="<%$Resources:xlsrv, ManagementUI_RequiredFieldErrorMessage%>"
                            width='300px'
                            Runat="server"/>
                        <wssawc:InputFormCustomValidator ID="UniqueNameValidator"
                            ControlToValidate="TextBoxAppName"
                            ErrorMessage="<%$Resources:xlsrv, ManagementUI_DuplicateNameErrorMessage%>"
                            OnServerValidate="ValidateUniqueName"
                            runat="server" />
                    </Template_control>
                </wssuc:InputFormControl>
            </Template_InputFormControls>
        </wssuc:InputFormSection>

        <wssuc:IisWebServiceApplicationPoolSection id="AppPoolSection" runat="server" />

        <wssuc:InputFormSection
            Title="<%$Resources:xlsrv, ManagementUI_DefaultLabel%>"
            Description="<%$Resources:xlsrv, ManagementUI_DefaultDescription%>"
            runat="server">
            <Template_InputFormControls>
                <wssuc:InputFormControl LabelText="" LabelAssociatedControlID="CheckBoxDefault" runat="server">
                    <Template_control>
                        <asp:CheckBox Checked="True" ID="CheckBoxDefault" Text="<%$Resources:xlsrv, ManagementUI_DefaultCheckboxDescription%>" Runat="server" />
                    </Template_control>
                </wssuc:InputFormControl>
            </Template_InputFormControls>
        </wssuc:InputFormSection>

        <SharePoint:FormDigest ID="FormDigest1" runat=server/>        
</asp:Content>

Put enter the following for the code behind:

using System;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.SharePoint.Administration;
using Microsoft.Office.Server.Internal.UI;

namespace SharePointEgg.Layouts.HelloServiceApplication
{
    public partial class CreateApplication : GlobalAdminPageBase
    {
        // Fields
        protected RequiredFieldValidator AppNameValidator;
        protected IisWebServiceApplicationPoolSection AppPoolSection;
        protected Button ButtonOk;
        protected CheckBox CheckBoxDefault;
        protected TextBox TextBoxAppName;
        protected CustomValidator UniqueNameValidator;

        // Methods
        protected void OkButton_Click(object sender, EventArgs e)
        {
            //ULS.SendTraceTag(0x39766663, UlsInformation.Management, ULSTraceLevel.Verbose, "ExcelServerCreateApplication.ButtonNext_Click: Entering ButtonNext_Click...");
            this.Page.Validate();
            if (this.Page.IsValid)
            {
                string applicationName = this.TextBoxAppName.Text.Trim();
                SPLongOperation.Begin(delegate(SPLongOperation longOperation)
                {
                    try
                    {
                        SharePointEgg.HelloServiceApplication serviceApplication = null;
                        serviceApplication = HelloService.Local.CreateApplication(applicationName, this.AppPoolSection.GetOrCreateApplicationPool());
                        serviceApplication.Update();
                        IAsyncResult asyncResult = serviceApplication.BeginProvision(null, null);
                        serviceApplication.EndProvision(asyncResult);
                        HelloServiceApplicationProxy proxy = HelloService.Local.CreateProxy(applicationName, serviceApplication);
                        proxy.Update();
                        proxy.AddToDefaultGroup(this.CheckBoxDefault.Checked);
                    }
                    catch (Exception exception)
                    {
                        //ULS.SendTraceTag(0x39766664, UlsInformation.Management, ULSTraceLevel.High, "ExcelServerCreateApplication.ButtonNext_Click: {0}", new object[] { exception.Message });
                        throw;
                    }
                    longOperation.EndScript("window.frameElement.commonModalDialogClose(1, null);");
                });
            }
            //ULS.SendTraceTag(0x39766665, UlsInformation.Management, ULSTraceLevel.Verbose, "ExcelServerCreateApplication.ButtonNext_Click: Exiting ButtonNext_Click.");
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            ((DialogMaster)this.Page.Master).OkButton.Click += new EventHandler(this.OkButton_Click);
        }

        protected void ValidateUniqueName(object sender, ServerValidateEventArgs eventArgs)
        {
            if (eventArgs == null)
            {
                throw new ArgumentNullException("eventArgs");
            }
            SharePointEgg.HelloServiceApplication applicationByName = SharePointEgg.HelloServiceApplication.GetApplicationByName(this.TextBoxAppName.Text.Trim());
            eventArgs.IsValid = applicationByName == null;
        }
    }
}

We also need to remove some of the control’s declaration in teh designer.cs because we declared these controls in the code behind. CreateApplication.aspx.designer.cs looks like this:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace SharePointEgg.Layouts.HelloServiceApplication {


    public partial class CreateApplication
    {

        /// <summary>
        /// CreateASAppTitle control.
        /// </summary>
        /// <remarks>
        /// Auto-generated field.
        /// To modify move field declaration from designer file to code-behind file.
        /// </remarks>
        protected global::System.Web.UI.WebControls.Literal CreateASAppTitle;

        /// <summary>
        /// CreateASAppDesc control.
        /// </summary>
        /// <remarks>
        /// Auto-generated field.
        /// To modify move field declaration from designer file to code-behind file.
        /// </remarks>
        protected global::System.Web.UI.WebControls.Literal CreateASAppDesc;

        /// <summary>
        /// FormDigest1 control.
        /// </summary>
        /// <remarks>
        /// Auto-generated field.
        /// To modify move field declaration from designer file to code-behind file.
        /// </remarks>
        protected global::Microsoft.SharePoint.WebControls.FormDigest FormDigest1;
    }
}

Next we will create a management page for our service application. For demonstration purpose, this page is just a dummy page that doesn’t do anything. Right click the newly created “HelloServiceApplication” folder under “ADMIN” and add a new item. Select the “Application Page” template and this time we call it “HelloServiceAdmin.aspx”. Due to the bug that I mentioned above, you may need to drag the page from “Layouts/HelloServiceApplication” back to “ADMIN\HelloServiceApplication”.

Put the following code in the aspx file:

<%@ Assembly Name="$SharePoint.Project.AssemblyFullName$" %>
<%@ Import Namespace="Microsoft.SharePoint.ApplicationPages" %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="asp" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
<%@ Import Namespace="Microsoft.SharePoint" %>
<%@ Assembly Name="Microsoft.Web.CommandUI, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="HelloServiceAdmin.aspx.cs" Inherits="SharePointEgg.Layouts.HelloServiceApplication.HelloServiceAdmin" MasterPageFile="~/_admin/admin.master" %>

<%@ Assembly Name="Microsoft.SharePoint.ApplicationPages.Administration, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c"%>

<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register Tagprefix="wssawc" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=14.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<%@ Register TagPrefix="wssuc" TagName="LinksTable" src="/_controltemplates/LinksTable.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="InputFormSection" src="/_controltemplates/InputFormSection.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="InputFormControl" src="/_controltemplates/InputFormControl.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="LinkSection" src="/_controltemplates/LinkSection.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="ButtonSection" src="/_controltemplates/ButtonSection.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="ActionBar" src="/_controltemplates/ActionBar.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="ToolBar" src="/_controltemplates/ToolBar.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="ToolBarButton" src="/_controltemplates/ToolBarButton.ascx" %>
<%@ Register TagPrefix="wssuc" TagName="Welcome" src="/_controltemplates/Welcome.ascx" %>

<asp:Content ID="Content1" contentplaceholderid="PlaceHolderPageTitle" runat="server">
    Test Service Admin Page
</asp:content>

<asp:Content ID="Content2" contentplaceholderid="PlaceHolderPageTitleInTitleArea" runat="server">
    Test Service Admin Page
</asp:Content>

<asp:content ID="Content3" contentplaceholderid="PlaceHolderPageDescription" runat="server">
    <asp:Literal ID="m_pathPageDescription" runat="server" />
</asp:content>

<asp:content ID="Content4" contentplaceholderid="PlaceHolderMain" runat="server">
    <table width="100%" class="propertysheet" cellspacing="0" cellpadding="0" border="0"> <tr> <td class="ms-descriptionText"> <asp:Label ID="LabelMessage" Runat="server" EnableViewState="False" class="ms-descriptionText"/> </td> </tr> <tr> <td class="ms-error"><asp:Label ID="LabelErrorMessage" Runat="server" EnableViewState="False" /></td> </tr> <tr> <td class="ms-descriptionText"> <asp:ValidationSummary ID="ValSummary" HeaderText="<%$SPHtmlEncodedResources:spadmin, ValidationSummaryHeaderText%>" DisplayMode="BulletList" ShowSummary="True" runat="server"> </asp:ValidationSummary> </td> </tr> </table>

    <P>
        <span style="font-size:140%"><asp:HyperLink ID="m_linkToSettingsPage" runat="server"/></span><br />
        Test Service Admin Page Description
    </P>
    <P>
        <span style="font-size:140%"><asp:HyperLink ID="m_linkToTrustedLocations" runat="server"/></span><br />
        Trusted Location
    </P>
    <P>
        <span style="font-size:140%"><asp:HyperLink ID="m_linkToTrustedDataProviders" runat="server"/></span><br />
        Trusted Data Provider
    </P>
    <P>
        <span style="font-size:140%"><asp:HyperLink ID="m_linkToTrustedDcls" runat="server"/></span><br />
        Trusted Data Connection Library
    </P>
    <P>
        <span style="font-size:140%"><asp:HyperLink ID="m_linkToUdfs" runat="server"/></span><br />
        User Defined Library
    </P>

</asp:content>

The code behind looks like this:

using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using Microsoft.Office.Server.Internal.UI;

namespace SharePointEgg.Layouts.HelloServiceApplication
{
    public partial class HelloServiceAdmin : GlobalAdminPageBase
    {
        protected void Page_Load(object sender, EventArgs e)
        {
        }
    }
}

And we have completed the administration pages for our service application. However we are not there yet. We need to add our service application to the server farm so that we can create an instance of it. We will create a feature receiver to do this.

Feature Receiver: Create Service Application in Server Farm

Right click the “Features” folder in Visual Studio and select “Add Feature”. Then right click the newly created Feature1.feature node and select “Add Event Receiver”. This will create the event receiver class and xml file.

Open “Feature1.EventReceiver.cs” and uncomment the “FeatureActivated” method. Put the following code in the method:

        public override void FeatureActivated(SPFeatureReceiverProperties properties)
        {
            try
            {
                HelloService service = HelloService.Local;
                if (service == null)
                {
                    service = new HelloService(SPFarm.Local);
                    service.Update();
                }
            }
            catch
            {
            }
        }

And there you go our service application is ready to be deployed!!!

Creating Client Page

Ops, just before we deploy, we will quickly create a page under the “Layouts\HelloServiceApplication” folder to call our service application. Right click the “Layouts” folder in Visual Studio and add a new item. Select “Application Page” and we will call this page “Test.aspx”. We will have a textbox, a button, and a literal control in our page. When the user clicks the button, the page will grab whatever value in the textbox and sends it to our custom service application. The literal control will be used to display whatever returns from the service application. We will insert the controls into “PlaceHolderMain” like this:

<asp:Content ID="Main" ContentPlaceHolderID="PlaceHolderMain" runat="server">
    <asp:UpdatePanel runat="server">
        <ContentTemplate>
            <SharePoint:InputFormTextBox ID="InputFormTextBoxName" runat="server" />
            <asp:Button runat="server" ID="OKButton" Text="Submit" />
            <asp:Literal runat="server" ID="ResultLiteral"></asp:Literal>
        </ContentTemplate>
    </asp:UpdatePanel>
</asp:Content>

The code behind is here:

using System;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;

namespace SharePointEgg.Layouts.HelloServiceApplication
{
    public partial class Test : LayoutsPageBase
    {
        protected void Page_Load(object sender, EventArgs e)
        {
        }

        protected override void OnInit(EventArgs e)
        {
            base.OnInit(e);
            OKButton.Click += new EventHandler(OKButton_Click);
        }

        void OKButton_Click(object sender, EventArgs e)
        {
            try
            {
                HelloServiceApplicationProxy proxy = HelloServiceApplicationProxy.GetProxy(SPServiceContext.Current);
                SharePointEgg.HelloServiceApplication app = proxy.Application;
                string endPoint = app.DefaultEndpoint.ToString();

                ResultLiteral.Text = app.Hello(InputFormTextBoxName.Text);

            }
            catch (Exception ex)
            {
                ResultLiteral.Text = ex.Message;
            }
        }
    }
}
Deploying Service Application

Right click the “HelloServiceApplication” and select “Deploy”. Hopefully you will get a “Deploy succeeded” message.

Create an Hello Service Application Instance

First browse to the SharePoint Central Administration site. Under “Application Management”, click “Manage service applications”. In the “Manage Service Applications” page, click the “New” button in the ribbon and you should see “Hello Service Application” in there":

Select “Hello Service Application” and a dialog should show up. This is actually the CreateApplication.aspx that we created earlier.

Give it a name “Hello Service Application”. Create a new application pool called “HelloServiceAppPool”. Check the Default checkbox as well. Click “OK” to proceed.

After a while, a new service application called “Hello Service Application” is created, like shown as below.

Select the new service application and click “Manage” in the Ribbon. You will be redirect to the management page, however this page doesn’t really do anything now.

Now let see if everything is working by accessing the client page that we created. You can get to the client page via the URL http://yoursite/_layouts/HelloServiceApplication/Test.aspx

Enter a name say “Wilson” in the text box and click OK. If anything is working, you will get and message back like the following screen:

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.