|
//*****************************************************************************
// 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.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.