Click here to Skip to main content
15,896,111 members
Articles / Programming Languages / XML

WS-Transfer Service for Workflow

Rate me:
Please Sign up or sign in to vote.
4.93/5 (16 votes)
23 Nov 2006CPOL12 min read 96K   438   62  
This article describes a design and implementation of the WF workflow connectivity to the Windows Communication Foundation (WCF) Service for WS-Transfer operation contract.
//*****************************************************************************
//    Description.....WS-Transfer Service Adapter for Workflow
//                                
//    Author..........Roman Kiss, rkiss@pathcom.com
//    Copyright � 2005 ATZ Consulting Inc. (see included license.rtf file)       
//                        
//    Date Created:    05/05/06
//
//    Date        Modified By     Description
//-----------------------------------------------------------------------------
//    05/05/06    Roman Kiss     Initial Revision
//    11/11/06    Roman Kiss     RTM
//*****************************************************************************
//
#region References
using System;
using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Xml;
using System.Transactions;
//
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
//
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Runtime.Hosting;
//
using RKiss.WSTransfer.Adapters;
using RKiss.ServiceHostExtension;
#endregion

namespace RKiss.WSTransfer.Adapters
{
    /// <summary>
    /// Adapter for factory and operation of the resource in the workflow process
    /// </summary>
    public class WFAdapter : WSTransferAdapterBase<ResourceDescriptor>, IWSTransferAdapter 
    {
        #region Private members
        string resourceTopic = null;
        int workflowTimeoutInSec = Defaults.Values.WorkflowTimeOut;
        WorkflowRuntime workflowRuntime = null;
        WSTransferLocalService wstransferLocalService = null;
        #endregion

        #region Constructor
        public WFAdapter(HybridDictionary config)
        {
            // config file
            base.Properties = config;

            // resource topic
            resourceTopic = base.Properties[Defaults.Keys.Topic] as string;

            // response time from workflow
            if (int.TryParse(base.Properties[Defaults.Keys.WorkflowTimeOut] as string, out workflowTimeoutInSec) == false)
                workflowTimeoutInSec = Defaults.Values.WorkflowTimeOut;

            // follow to the workflow
            base.Properties["IncomingMessageHeaders"] = OperationContext.Current.IncomingMessageHeaders;
            base.Properties["IncomingMessageProperties"] = OperationContext.Current.IncomingMessageProperties;

            // Find the custom Indigio Extension on the Service Host by it's type
            WFServiceHostExtension extension = OperationContext.Current.Host.Extensions.Find<WFServiceHostExtension>();

            // Get a reference to the WorkflowRuntime
            this.workflowRuntime = extension.WorkflowRuntime;

            // Get a reference to the WSTransferLocalService
            this.wstransferLocalService = (WSTransferLocalService)extension.ExternalDataExchangeService.GetService(typeof(WSTransferLocalService));

            // If the WSTransferLocalService is not registered with the runtime, then create an instance now and add it.  
            if (this.wstransferLocalService == null)
            {
                this.wstransferLocalService = new WSTransferLocalService(this.workflowRuntime);
                extension.ExternalDataExchangeService.AddService(this.wstransferLocalService);
            }  
        }
        #endregion

        #region IWSTransferAdapter
        /// <summary>
        /// Create resource (factory)
        /// </summary>
        /// <param name="resource"></param>
        /// <returns></returns>
        public EndpointAddress Create(object resource)
        {
            // transaction context
            base.Properties["TransactionDependentClone"] = Transaction.Current == null ?
                null : Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);

            // create workflow
            WorkflowInstance wi = CreateWorkflow(Defaults.Keys.WorkflowTypeCreate);

            // register response event
            AsyncResponseFromLocalService ar = new AsyncResponseFromLocalService(this.wstransferLocalService, wi.InstanceId);

            // create resource descriptor
            ResourceDescriptor rd = new ResourceDescriptor();
            rd.Topic = resourceTopic;

            // workflow local service can handled only binary serialization
            string strResource = (resource as XmlElement).OuterXml;

            // fire request
            this.wstransferLocalService.RaiseRequestEvent(wi.InstanceId, rd, strResource);

            // processing
            ar.WaitForResponse(workflowTimeoutInSec);

            // result
            ResourceDescriptor result = ar.Response.ResourceDescriptor;

            return this.ResourceEndpointAddress(null, result);

            #region another way returning a resource identifier
            //List<AddressHeader> list = new List<AddressHeader>();
            //list.Add(AddressHeader.CreateAddressHeader(ResourceDescriptor.DataContractName, ResourceDescriptor.DataContractNamespace, rd));
            //return this.ResourceEndpointAddress(null, list);
            #endregion
        }

        /// <summary>
        /// Get resource (operation)
        /// </summary>
        /// <param name="resourceIdentifier"></param>
        /// <returns></returns>
        public object Get(MessageHeaders resourceIdentifier)
        {
            // transaction context
            base.Properties["TransactionDependentClone"] = Transaction.Current == null ?
                null : Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);

            // create workflow
            WorkflowInstance wi = CreateWorkflow(Defaults.Keys.WorkflowTypeGet);

            // register response event
            AsyncResponseFromLocalService ar = new AsyncResponseFromLocalService(this.wstransferLocalService, wi.InstanceId);

            // resource identifier
            ResourceDescriptor rd = GetResourceIdentifier(resourceIdentifier);

            #region another way getting a resource identifier
            //ResourceDescriptor rd = GetResourceIdentifier<ResourceDescriptor>(resourceIdentifier, ResourceDescriptor.XmlQualifiedName);
            #endregion

            // fire request
            this.wstransferLocalService.RaiseRequestEvent(wi.InstanceId, rd);
         
            // processing
            ar.WaitForResponse(workflowTimeoutInSec);

            // result
            object result = ar.Response.Resource;

            // convert xml text formatted resource back to the XmlElement
            XmlDocument doc = new XmlDocument();
            doc.LoadXml(result as string);
            result = doc.DocumentElement;

            return result;
        }

        /// <summary>
        /// Put resource (operation)
        /// </summary>
        /// <param name="resourceIdentifier"></param>
        /// <param name="resource"></param>
        /// <returns></returns>
        public object Put(MessageHeaders resourceIdentifier, object resource)
        {
            // transaction context
            base.Properties["TransactionDependentClone"] = Transaction.Current == null ? 
                null : Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);

            // create workflow
            WorkflowInstance wi = CreateWorkflow(Defaults.Keys.WorkflowTypePut);
      
            // register response event
            AsyncResponseFromLocalService ar = new AsyncResponseFromLocalService(this.wstransferLocalService, wi.InstanceId);

            // resource identifier
            ResourceDescriptor rd = GetResourceIdentifier(resourceIdentifier);

            // workflow local service can handle only binary serialization
            string strResource = (resource as XmlElement).OuterXml;
      
            // fire request
            this.wstransferLocalService.RaiseRequestEvent(wi.InstanceId, rd, strResource);
          
            // processing
            ar.WaitForResponse(workflowTimeoutInSec);

            // result
            object result = ar.Response.Resource;

            return result;
        }

        /// <summary>
        /// Delete resource (operation)
        /// </summary>
        /// <param name="resourceIdentifier"></param>
        public void Delete(MessageHeaders resourceIdentifier)
        {
            // transaction context
            base.Properties["TransactionDependentClone"] = Transaction.Current == null ? 
                null : Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete);

            // create workflow
            WorkflowInstance wi = CreateWorkflow(Defaults.Keys.WorkflowTypeDelete);
            
            // register response event
            AsyncResponseFromLocalService ar = new AsyncResponseFromLocalService(this.wstransferLocalService, wi.InstanceId);

            // resource identifier
            ResourceDescriptor rd = GetResourceIdentifier(resourceIdentifier);
   
            // fire request
            this.wstransferLocalService.RaiseRequestEvent(wi.InstanceId, rd);
     
            // processing
            ar.WaitForResponse(workflowTimeoutInSec);
        }
        #endregion

        #region helpers
        private WorkflowInstance CreateWorkflow(string workflow)
        {
            // Load and start the specified workflow
            string strWorkflowType = base.Properties[workflow] as string;
            if (strWorkflowType == null) 
            {
                strWorkflowType = base.Properties[Defaults.Keys.WorkflowType] as string;
            }
            if (strWorkflowType != null)
            {
                Dictionary<string, object> adapter = new Dictionary<string, object>();
                adapter.Add(Defaults.Keys.Config, base.Properties);
                Guid workflowInstanceId = Guid.NewGuid();
                Type workflowType = Type.GetType(strWorkflowType);
                if (workflowType != null)
                {
                    WorkflowInstance instance = this.workflowRuntime.CreateWorkflow(workflowType, adapter, workflowInstanceId);
                    instance.Start();
                    return instance;
                }
                throw new FaultException(string.Format("Can't get a workflow type '{0}'. Please, specified a workflow type in the config file.", strWorkflowType));
            }
            throw new FaultException(string.Format("The workflow '{0}' is not supported. Please, specified a workflow type in the config file.", workflow));
        }
        
        internal class AsyncResponseFromLocalService
        {
            AutoResetEvent _waitForResponse = new AutoResetEvent(false);
            WorkflowTerminatedEventArgs _e;
            WSTransferLocalService _localservice;
            ResponseEventArgs _response;
            Guid _istanceId;
            public AsyncResponseFromLocalService(WSTransferLocalService localservice, Guid instanceid)
            {
                _istanceId = instanceid;
                _localservice = localservice;

                // response handler
                _localservice.Response += new EventHandler<ResponseEventArgs>(localservice_FireResponse);

                // exception handler
                _localservice.WorkflowRuntime.WorkflowTerminated += new EventHandler<WorkflowTerminatedEventArgs>(OnWorkflowTerminated);
            }
            public void OnWorkflowTerminated(object sender, WorkflowTerminatedEventArgs e)
            {
                if (_istanceId == e.WorkflowInstance.InstanceId)
                {
                    this._e = e;
                    _waitForResponse.Set();
                    _localservice.WorkflowRuntime.WorkflowTerminated -= new EventHandler<WorkflowTerminatedEventArgs>(OnWorkflowTerminated);
                }
            }
            public void WaitForResponse(int secondsTimeOut)
            {
                bool retval = _waitForResponse.WaitOne(secondsTimeOut * 1000, false);
                if (_e != null)
                    throw this._e.Exception;
                if (retval == false)
                    throw new FaultException("The workflow timeout expired");
            }
            public ResponseEventArgs Response
            {
                get { return _response; }
            }
            public WorkflowTerminatedEventArgs WorkflowTerminatedEventArgs
            {
                get { return _e; }
            }
            public void localservice_FireResponse(object sender, ResponseEventArgs e)
            {
                if (_istanceId == e.InstanceId)
                {
                    _response = e;
                    _waitForResponse.Set();
                    _localservice.Response -= new EventHandler<ResponseEventArgs>(localservice_FireResponse);
                }
            }
        }
        #endregion
    } 
}


By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions