AppFabric Appliactions: Episode VI - Implementing Custom External Service

Damir Dobric Posts

Next talks:

 

    

Follow me on Twitter: #ddobric



 

 

Archives

As already mentioned in previous posts AppFabric Applications  helps you to create an application by composing of services. Most technical people assume that service is a Web Service. Similarly, most managers assume that the service is an application which provides some functionality to consumers. In context of AppFabric Applications service can be almost any kind of application. The only constrain is that the application exposed as a service must provide some kind of endpoint. Technically this can be anything as long you are enough creative to invent required endpoint.

AppFabric applications by definition does not care about technical implementation of your service. This is reasonable, because the service is external to AppFabric Applications toolset. External service can even be another AppFabric Application which exposes some endpoint. That means AppFabric Applications is not responsible to deploy or to monitor external service. All AppFabric Applications needs to know is basically how to consume the external service. In theory consuming of an external “thing” is done via some proxy type. So, if you want to integrate your external service, you will have to provide the proxy type implementation, which is responsible to deal with external service.
For example, in this post I have an external service named CustomServiceSample. To make it visible to AppFabric Applications, I have “invented” the proxy type called CustomService. Once the composed application is running some component will invoke some method on CustomService (note this is the proxy class) and this method will take a care about interaction with the service. The test application which I have used in this post implements one Code-service which consumes my CustomService.

image

To implement the Custom External Service you need to implement two things:

a) Visual Studio Package VSIX
b) Custom Service Extensibility Artifacts

Before we start I recommend also to take a look at this great article posted by Alan.

The sample implementation described in this post can be downloaded here.

Create Visual Studio Package

Remarks: Visual Studio 2010 SP1 SDK required.

Visual Studio package is required to integrate you custom service in Visual Studio. As you will se later, there are few extensibility artifacts which enable Visual Studio to interact with your service at design time.Create new project and select Other Project types | Visual Studio Extensibility | Visual Studio Package.
Click ok, then next.

image       image
Then choose the language, peek the signing key and optionally enter some package description (recommended).
image     image

At the next dialog do not check any of menu, tool and custom editor. AppFabric Application does not need them. Now uncheck test staff. 

image     image

Click finish and following will appear. In this dialog click remove content.

image

Then Press button Add Content, select MEF Component and your project as shown at the next picture:

image

You can now build the solution. After all open output folder and double-click VSIX file, click on Install and finally on Close.

imageimageimage

To see if all worked fine restart all instances of VS and go to Tools | Extension Manager. Following should appear.

image

Because you know now, how to create the VS - MEF package, remove it and restart visual studio.

Create Custom Service Extensibility Artifacts

To implement the custom external service you need to implement four interfaces in the VS MEF package (VSPackage1):

1.Service Proxy
First of all you have to decide how to communicate with your service. Whatever your decision is, implement it in this class. The class in this post which implements the service proxy is called CustomService.
Following code is the full implementation of the CustomService class.

public class CustomService
{
       
private int Prop1 { get; set; }|


        private string Prop2 { get; set; }

        public CustomService(int prop1, string prop2)

        {
            Prop1 = prop1;
            Prop2 = prop2;
        }


        public int DoIt()
        {
            ++Prop1;
           
Debug.WriteLine(String.Format("{0} - {2}"
, Prop2, Prop1));
           
return Prop1;
        }
    }


When developer wants to consume the service it will do something like this.

CustomService svc = ServiceReferences.CreateImport1();

svc.DoIt();

As you see the implementation of the service doesn’t invoke any external service. AppFabric Applications has no glue about this. Because of that, the implementation of the service can support various implementations like:

- Consume External Service.
- Implement all logic internally like in my sample. Note that this pattern will not scale at all.
- Consume with external service which is itself composite application. In fact, this post also describe how to integrate existing AppFabric Application as the custom service.


2. ExternalServiceMetadata
It provides design time information which describe important properties of your service. These are Friendly Name of the service, its description, the icon which will represent the service in Visual Studio, the qualified name of service proxy class and the list of all other artifacts (i.e. assemblies) which are required to run the proxy.
The class is the abstract one and it looks like shown below:

public abstract class ExternalServiceMetadata

    {

        public static readonly Dictionary<string, object> DefaultDiagramProperties;

 

        protected ExternalServiceMetadata();

 

        public static DrawingBrush DefaultExternalServiceIcon { get; }

        public static DrawingBrush DefaultSelectedExternalServiceIcon { get; }

        public virtual string Description { get; }

        public Dictionary<string, object> DiagramProperties { get; }

        public abstract string FriendlyName { get; }

        public virtual DrawingBrush Icon { get; }

        public virtual List<string> ProxyArtifacts { get; }

        public virtual string ProxyTypeName { get; }

        public virtual List<string> ReferenceAssemblies { get; }

        public virtual DrawingBrush SelectedIcon { get; }

 

        public virtual ICustomTypeDescriptor GetExportManifestTypeDescriptor(ICustomTypeDescriptor reflectTypeDescriptor);

        protected internal virtual void InitializeExternalService(ExportManifest service);

    }


The mandatory method is FriendlyName. VS uses this value to show the name of the service in the list of installed services.
Description provides a more information about the service which also appear in the list of service when the service is selected.
Icon defines the icon which will represent the service. If you override this property you should also override SelectedIcon too. This one is shown when the user selects the icon.

ProxyTypeName is the type name of the proxy class (CustomService)

ReferencedAssemblies contains the list of all assemblies which your service proxy require. Note that the service will be deployed in the cloud and the machine might not have all required references.
My sample implementation looks like:

[ExternalServiceExport(typeof(CustomServiceExportDefinition))]
   
public class CustomServiceMetadata : ExternalServiceMetadata
    {
       
List<string> m_ProxyArtifacts = new List<string
>();

       
List<string> m_ReferencedAssemblies = new List<string
>();


       
public override string
FriendlyName
        {
           
get { return "CustomServiceSample"
; }
        }

       
public override string
Description
        {
           
get
            {
               
return "Demonstrate how to build the Custom Service"
;
            }
        }

       
public override System.Windows.Media.DrawingBrush
Icon
        {
           
get
            {
               
return
drawIcon();
            }
        }

       
public override string
ProxyTypeName
        {
           
get
            {
               
return typeof(CustomService
).AssemblyQualifiedName;
            }
        }

       
public override List<string
> ReferenceAssemblies
        {
           
get
            {
               
if
(m_ReferencedAssemblies.Count == 0)
                {
                    m_ReferencedAssemblies.Add(
typeof(CustomServiceExportDefinition
).Assembly.Location);
                }

               
return
m_ReferenceAssemblies;
            }
        }


       
public override List<string
> ProxyArtifacts
        {
           
get
            {
               
if
(m_ProxyArtifacts.Count == 0)
                {
                    m_ProxyArtifacts.Add(
typeof(CustomServiceExportDefinition
).Assembly.Location);
                }

               
return m_ProxyArtifacts;
            }
        }
}

3. ServiceExportDefinition
This is the abstract class which defines any property which can be set on your service within Visual Studio. The instance of this class will be used on runtime when the service endpoint have to be imported by calling ServiceReference.CreateImport(). Additionally this class provide a property which defines the type which will act as service proxy. (In this post CustomService) .
The class is also the abstract one and it looks like shown below:

public abstract class ServiceExportDefinition

    {

        protected ServiceExportDefinition();

 

        [Browsable(false)]

        public NamedEndPoint CanonicalAddress { get; internal set; }

        [Browsable(false)]

        public string Category { get; set; }

        [Browsable(false)]

        [OverridableProperty]

        public string CertificateName { get; set; }

        [Browsable(false)]

        public NamedEndPoint ClaimedListenAddress { get; internal set; }

        [Browsable(false)]

        public string ExportingServiceName { get; }

        protected abstract Type ExportType { get; }

        [Browsable(false)]

        public bool IsExternal { get; set; }

        [Browsable(false)]

        public bool IsLocked { get; }

        [Browsable(false)]

        [OverridableProperty]

        public NamedEndPoint ListenAddress { get; set; }

        [RefreshProperties(RefreshProperties.Repaint)]

        [Browsable(false)]

        public string Name { get; set; }

        [DataMember(EmitDefaultValue = false)]

        [Browsable(false)]

        public string[] OverridableProperties { get; internal set; }

        [Browsable(false)]

        [DataMember(EmitDefaultValue = false)]

        public string[] OverridablePropertiesOnlyDuringProvisioning { get; internal set; }

        [Browsable(false)]

        public KeyedCollection<string, Artifact> ProxyArtifacts { get; }

        [Browsable(false)]

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]

        public Type ProxyType { get; set; }

        [OverridableProperty]

        [Browsable(false)]

        public NamedEndPoint PublishedAddress { get; set; }

        [Browsable(false)]

        public string QualifiedName { get; }

        protected internal Collection<ResourceUsagePolicy> ResourceUsagePolicies { get; }

        [Browsable(false)]

        public Uri SelfLink { get; }

        [Browsable(false)]

        public TransportType TransportType { get; set; }

        [Browsable(false)]

        [OverridableProperty]

        public ServiceExportVisibility Visibility { get; set; }

 

        public override string ToString();

        public bool TryGetParentDefinition<TDefinition>(out TDefinition parentDefinition) where TDefinition : class;

        protected virtual void Validate(ValidationContext context);

    }

Most important property in this class is ExportType. This property should return the type which implements the custom service proxy.
Here is my implementation:

public class CustomServiceExportDefinition : ServiceExportDefinition
    {
      

        protected override Type ExportType

        {
           
get { return typeof(CustomServiceExport); }
        }

 

        private int m_Prop1 = 11;

 

        public int CustomServiceProp1
        {
           
get
            {


                return m_Prop1;
            }
           
set
            {
                m_Prop1 =
value;
            }
        }


        private string m_Prop2 = "Hello";

 

        public string CustomServiceProp2
        {

            get
            {
               
return m_Prop2;
            }


            set
            {
                m_Prop2 =
value;
            }
        }
    }

4. CustomServiceExport
Ween ServiceReference.CreateImport() is invoked on runtime within some service, the method CustomServiceExport.Resolve() is invoked implicitly to create the instance of the service proxy (CustomService). This abstract class delivers the instance of the proxy which will be used to communictae with the external service.

To explain how all this works, let’s create some CompApp and add the new service reference as shown at following sequence diagrams. First of all, when working with services in AppFabric Applications there are three major steps relevant for topic of this post:

I  – Add the service
II – Add the reference to service
III – Deploy the service
IV – Run the service

Each step listed above utilize different extensibility points. Here is how all approximately works:

Add Service

image

The picture shows a diagram of some composed application. If you want to add a new service just click on “Add Service”. As next, the dialog with all registered services will appear.

image

As you see the list contains a CustomService named “CustomServiceSample”, which is implemented by me. When developer click on “Add Service” the VS AppFabric Application package will instantiate the implementation of your ExternaServiceMetadata class. By using of that instance VS will first obtain the service description, then the name of the service and finally the icon. This all is needed to make you service appear in the list shown above. Following sequence diagram shows how this work  at the moment.

image

Add the reference to service
At some time the developer will add the reference to custom service.

image

When developer clicks on “Custom1” (see picture above) AppFabric Package will again use the instance of ExternalServiceMetadata to obtain the type of the proxy.

image

Note that sequences describe above are a bit simplified.I didn’t describe sequences which goes through build process etc. I also didn’t use real object names in the sequence life time. My intension is to describe only sequences relevant to deeply understand how one service in AppFabric Applications works.

Deploy the service

During this step all deployment relevant artifacts are collected.

Run the service

When the host decides to start the application the container will ask ServiceExportDefinition to provide the type of the service proxy by reading property ExportType. After that the instance the service (service which consumes custom external service) is started and depending on service code at some time the proxy instance will be created. Objects in green represents the code of you AppFabric application which contains the service consumer of custom service. You will invoke CreateImport on auto-generated object ServiceReferences. This class is generated when developer adds the service reference. Finally, the method Resolve of custom implementation of ServiceExport will be called. This is the place where the instance of custom service proxy is created.

image

 

Hope this helps.


Posted Sep 05 2011, 12:50 AM by Damir Dobric
Filed under:

Comments

Damir Dobric Posts wrote Introduction to AppFabric Applications
on 09-13-2011 23:25

Few weeks ago Microsoft has published the first public CTP related to Application Composition under the

developers.de is a .Net Community Blog powered by daenet GmbH.