Click here to Skip to main content
15,891,136 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.....WCF WorkflowInvoker 
//                                
//    Author..........Roman Kiss, rkiss@pathcom.com
//    Copyright � 2005 ATZ Consulting Inc. (see included license.rtf file)     
//                        
//    Date Created:    04/04/06
//
//    Date        Modified By     Description
//-----------------------------------------------------------------------------
//    04/04/06    Roman Kiss     Initial Revision
//    07/07/06    Roman Kiss     migrating to JuneCTP 
//*****************************************************************************
//
#region References
using System;
using System.Threading;
using System.Configuration;
using System.Collections.Specialized;
using System.Collections.ObjectModel;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Transactions;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.ServiceModel.Configuration;
using System.Reflection;
using System.Workflow.Runtime;
using System.Workflow.Activities;
using System.Workflow.Runtime.Hosting;
//
#endregion

namespace RKiss.WorkflowInvoker
{
    #region Workflow Local Service - Contract
    [ExternalDataExchange]
    public interface IInvokerLocalService
    {
        // to workflow
        event EventHandler<RequestEventArgs> Request;

        // from workflow
        void RaiseResponseEvent(Guid workflowInstanceId, object response);
    }
    #endregion

    #region ExternalDataEventArgs
    [Serializable]
    public class RequestEventArgs : ExternalDataEventArgs
    {
        private object _request;
        public RequestEventArgs(Guid InstanceId, object request)
            : base(InstanceId)
        {
            this._request = request;
        }
        public object Request
        {
            get { return _request; }
            set { _request = value; }
        }
    }
    [Serializable]
    public class ResponseEventArgs : ExternalDataEventArgs
    {
        private object _response;
        public ResponseEventArgs(Guid InstanceId, object response)
            : base(InstanceId)
        {
            this._response = response;
        }
        public object Response
        {
            get { return _response; }
            set { _response = value; }
        }
    }
    #endregion

    #region Workflow Local Service - Implementation
    public class InvokerLocalService : IInvokerLocalService
    {
        private WorkflowRuntime _workflowRuntime;
        private ManualWorkflowSchedulerService _scheduler;
        public WorkflowRuntime WorkflowRuntime { get { return _workflowRuntime; } }
        public ManualWorkflowSchedulerService Scheduler { get { return _scheduler; } }
        public InvokerLocalService(WorkflowRuntime workflowRuntime)
        {
            if (workflowRuntime == null)
                throw new ArgumentNullException("workflowRuntime");

            this._workflowRuntime = workflowRuntime;
            this._scheduler = workflowRuntime.GetService<ManualWorkflowSchedulerService>(); 
        }
  
        #region IInvokerLocalService Members - to workflow
        public event EventHandler<RequestEventArgs> Request;
        public void RaiseRequestEvent(Guid workflowInstanceId, object request)
        {
            // Create the EventArgs for this event
            RequestEventArgs e = new RequestEventArgs(workflowInstanceId, request);

            // Raise the event
            if (this.Request != null)
            {
                if (this.Scheduler == null)
                {
                    this.Request(null, e);
                }
                else
                {
                    this.Scheduler.RunWorkflow(workflowInstanceId);
                    this.Request(null, e);
                    this.Scheduler.RunWorkflow(workflowInstanceId);
                }
            }
        }
        #endregion

        #region IInvokerLocalService Members - from workflow
        public event EventHandler<ResponseEventArgs> Response;
        public void RaiseResponseEvent(Guid workflowInstanceId, object response)
        {
            // Create the EventArgs for this event
            ResponseEventArgs e = new ResponseEventArgs(workflowInstanceId, response);

            // Raise the event
            if (this.Response != null)
            {
                this.Response(null, e);
            }
        }
        #endregion
    }
    #endregion

    #region WorkflowInvoker
    public class WorkflowInvoker
    {
        #region private Constructor
        private WorkflowInvoker()
        {
        }
        #endregion

        #region Create
        public static Invoker Create(Type workflowType)
        {
            return Create(workflowType, null, Guid.NewGuid());
        }
        public static Invoker Create(Type workflowType, HybridDictionary context)
        {
            return Create(workflowType, context, Guid.NewGuid());
        }
        public static Invoker Create(Type workflowType, HybridDictionary context, Guid instanceId)
        {
            // from current operation to the workflow
            Dictionary<string, object> namedArgumentValues = new Dictionary<string, object>();
            if (context == null) 
                context = new HybridDictionary();
            context.Add("IncomingMessageHeaders", OperationContext.Current.IncomingMessageHeaders);
            context.Add("IncomingMessageProperties", OperationContext.Current.IncomingMessageProperties);
            context.Add("TransactionDependentClone", Transaction.Current == null ?
                null : Transaction.Current.DependentClone(DependentCloneOption.BlockCommitUntilComplete));
            namedArgumentValues.Add("_Context", context);

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

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

            // Get a reference to the ExternalDataExchangeService from the WorkflowRuntime
            ExternalDataExchangeService dataExchangeService = extension.ExternalDataExchangeService;

            // Get a reference to the WSTransferLocalService
            InvokerLocalService invokerLocalService = (InvokerLocalService)dataExchangeService.GetService(typeof(InvokerLocalService));

            // If the InvokerLocalService is not registered with the runtime, then create an instance now and add it.  
            if (invokerLocalService == null)
            {
                invokerLocalService = new InvokerLocalService(workflowRuntime);
                dataExchangeService.AddService(invokerLocalService);
            }

            // create instance of the workflow
            WorkflowInstance instance = workflowRuntime.CreateWorkflow(workflowType, namedArgumentValues, instanceId);
            instance.Start();

            // hook to the WCF
            Invoker invoker = new Invoker(invokerLocalService, instance);
            return invoker;
        }
        #endregion

        #region RequestResponse
        public static TResponse RequestResponse<TResponse>(Type workflowType, object request) where TResponse : class
        {
            return RequestResponse<TResponse>(workflowType, request, null, int.MaxValue/1000);
        }
        public static TResponse RequestResponse<TResponse>(Type workflowType, object request, HybridDictionary context) where TResponse : class
        {
            return RequestResponse<TResponse>(workflowType, request, context, int.MaxValue/1000);
        }
        public static TResponse RequestResponse<TResponse>(Type workflowType, object request, HybridDictionary context, int secondsTimeOut) where TResponse : class
        {
            // create workflow invoker
            Invoker invoker = Create(workflowType, context);

            // pass request object to the workflow
            invoker.RaiseRequestEvent(request);

            // wait for response from workflow
            invoker.WaitForResponse(secondsTimeOut);

            // response
            return invoker.GetResponse<TResponse>();
        }
        #endregion
    }
    #endregion

    #region Invoker
    public class Invoker
    {
        #region Private
        AutoResetEvent _waitForResponse = new AutoResetEvent(false);
        WorkflowTerminatedEventArgs _e;
        InvokerLocalService _localservice;
        ResponseEventArgs _response;
        WorkflowInstance _instance;
        #endregion

        public Invoker(InvokerLocalService localservice, WorkflowInstance instance)
        {
            _instance = instance;
            _localservice = localservice;

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

            // exception handler
            _localservice.WorkflowRuntime.WorkflowTerminated += new EventHandler<WorkflowTerminatedEventArgs>(OnWorkflowTerminated);
        }
        public WorkflowInstance WorkflowInstance
        {
            get { return _instance; }
        }
        public void OnWorkflowTerminated(object sender, WorkflowTerminatedEventArgs e)
        {
            if (_instance.InstanceId == 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 WorkflowTerminatedEventArgs WorkflowTerminatedEventArgs
        {
            get { return _e; }
        }
        public void localservice_FireResponse(object sender, ResponseEventArgs e)
        {
            if (_instance.InstanceId == e.InstanceId)
            {
                _response = e;
                _waitForResponse.Set();
                _localservice.Response -= new EventHandler<ResponseEventArgs>(localservice_FireResponse);
            }
        }
        public void RaiseRequestEvent(object request)
        {
            _localservice.RaiseRequestEvent(_instance.InstanceId, request);
        }
        public T GetResponse<T>() where T : class
        {
            return (T)_response.Response;
        }

    }
    #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