e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
382 lines
14 KiB
C#
382 lines
14 KiB
C#
//------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------
|
|
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.Collections;
|
|
using System.Collections.ObjectModel;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Specialized;
|
|
using System.Reflection;
|
|
using System.Workflow.ComponentModel;
|
|
using System.Workflow.Runtime;
|
|
using System.Workflow.Runtime.Configuration;
|
|
using System.Workflow.Runtime.Hosting;
|
|
using System.Runtime.Serialization;
|
|
using System.Globalization;
|
|
using System.Threading;
|
|
using System.Runtime.Serialization.Formatters.Binary;
|
|
using System.Configuration;
|
|
|
|
namespace System.Workflow.Activities
|
|
{
|
|
internal interface IDeliverMessage
|
|
{
|
|
object[] PrepareEventArgsArray(object sender, ExternalDataEventArgs eventArgs, out object workItem, out IPendingWork workHandler);
|
|
void DeliverMessage(ExternalDataEventArgs eventArgs, IComparable queueName, object message, object workItem, IPendingWork workHandler);
|
|
}
|
|
|
|
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
|
|
public class ExternalDataExchangeServiceSection : ConfigurationSection
|
|
{
|
|
private const string _services = "Services";
|
|
|
|
/// <summary> The providers to be instantiated by the service container. </summary>
|
|
[ConfigurationProperty(_services, DefaultValue = null)]
|
|
public WorkflowRuntimeServiceElementCollection Services
|
|
{
|
|
get
|
|
{
|
|
return (WorkflowRuntimeServiceElementCollection)base[_services];
|
|
}
|
|
}
|
|
}
|
|
|
|
[Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")]
|
|
public class ExternalDataExchangeService : WorkflowRuntimeService
|
|
{
|
|
Dictionary<int, WorkflowMessageEventHandler> eventHandlers = null;
|
|
object handlersLock = new object();
|
|
private const string configurationSectionAttributeName = "ConfigurationSection";
|
|
ExternalDataExchangeServiceSection settings = null;
|
|
IDeliverMessage enqueueMessageWrapper = null;
|
|
List<object> services;
|
|
object servicesLock = new object();
|
|
|
|
public ExternalDataExchangeService()
|
|
{
|
|
this.eventHandlers = new Dictionary<int, WorkflowMessageEventHandler>();
|
|
this.services = new List<object>();
|
|
this.enqueueMessageWrapper = new EnqueueMessageWrapper(this);
|
|
}
|
|
|
|
public ExternalDataExchangeService(string configSectionName)
|
|
: this()
|
|
{
|
|
if (configSectionName == null)
|
|
throw new ArgumentNullException("configSectionName");
|
|
|
|
settings = ConfigurationManager.GetSection(configSectionName) as ExternalDataExchangeServiceSection;
|
|
if (settings == null)
|
|
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
|
|
SR.GetString(SR.Error_ConfigurationSectionNotFound), configSectionName));
|
|
}
|
|
|
|
public ExternalDataExchangeService(NameValueCollection parameters)
|
|
: this()
|
|
{
|
|
if (parameters == null)
|
|
throw new ArgumentNullException("parameters");
|
|
string configurationSectionName = null;
|
|
|
|
foreach (string key in parameters.Keys)
|
|
{
|
|
if (key.Equals(configurationSectionAttributeName, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
configurationSectionName = parameters[key];
|
|
}
|
|
else
|
|
{
|
|
throw new ArgumentException(
|
|
String.Format(Thread.CurrentThread.CurrentCulture, SR.GetString(SR.Error_UnknownConfigurationParameter), key), "parameters");
|
|
}
|
|
}
|
|
if (configurationSectionName != null)
|
|
{
|
|
settings = ConfigurationManager.GetSection(configurationSectionName) as ExternalDataExchangeServiceSection;
|
|
if (settings == null)
|
|
throw new ArgumentException(String.Format(CultureInfo.CurrentCulture,
|
|
SR.GetString(SR.Error_ConfigurationSectionNotFound), configurationSectionName));
|
|
}
|
|
|
|
}
|
|
|
|
public ExternalDataExchangeService(ExternalDataExchangeServiceSection settings)
|
|
: this()
|
|
{
|
|
if (settings == null)
|
|
throw new ArgumentNullException("settings");
|
|
|
|
this.settings = settings;
|
|
}
|
|
|
|
internal ReadOnlyCollection<object> GetAllServices()
|
|
{
|
|
ReadOnlyCollection<object> collection;
|
|
lock (this.servicesLock)
|
|
{
|
|
collection = this.services.AsReadOnly();
|
|
}
|
|
|
|
return collection;
|
|
}
|
|
|
|
protected override void Start()
|
|
{
|
|
if (settings != null)
|
|
{
|
|
foreach (WorkflowRuntimeServiceElement service in settings.Services)
|
|
{
|
|
AddService(ServiceFromSettings(service));
|
|
}
|
|
}
|
|
|
|
if (this.Runtime != null)
|
|
{
|
|
base.Start();
|
|
}
|
|
}
|
|
|
|
internal void SetEnqueueMessageWrapper(IDeliverMessage wrapper)
|
|
{
|
|
this.enqueueMessageWrapper = wrapper;
|
|
foreach (WorkflowMessageEventHandler eventHandler in this.eventHandlers.Values)
|
|
{
|
|
eventHandler.EnqueueWrapper = wrapper;
|
|
}
|
|
}
|
|
|
|
// Todo: This is duplicate of code in WorkflowRuntime
|
|
internal object ServiceFromSettings(WorkflowRuntimeServiceElement serviceSettings)
|
|
{
|
|
object service = null;
|
|
|
|
Type t = Type.GetType(serviceSettings.Type, true);
|
|
|
|
ConstructorInfo serviceProviderAndSettingsConstructor = null;
|
|
ConstructorInfo serviceProviderConstructor = null;
|
|
ConstructorInfo settingsConstructor = null;
|
|
|
|
foreach (ConstructorInfo ci in t.GetConstructors())
|
|
{
|
|
ParameterInfo[] pi = ci.GetParameters();
|
|
if (pi.Length == 1)
|
|
{
|
|
if (typeof(IServiceProvider).IsAssignableFrom(pi[0].ParameterType))
|
|
{
|
|
serviceProviderConstructor = ci;
|
|
}
|
|
else if (typeof(NameValueCollection).IsAssignableFrom(pi[0].ParameterType))
|
|
{
|
|
settingsConstructor = ci;
|
|
}
|
|
}
|
|
else if (pi.Length == 2)
|
|
{
|
|
if (typeof(IServiceProvider).IsAssignableFrom(pi[0].ParameterType)
|
|
&& typeof(NameValueCollection).IsAssignableFrom(pi[1].ParameterType))
|
|
{
|
|
serviceProviderAndSettingsConstructor = ci;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (serviceProviderAndSettingsConstructor != null)
|
|
{
|
|
service = serviceProviderAndSettingsConstructor.Invoke(
|
|
new object[] { Runtime, serviceSettings.Parameters });
|
|
}
|
|
else if (serviceProviderConstructor != null)
|
|
{
|
|
service = serviceProviderConstructor.Invoke(new object[] { Runtime });
|
|
}
|
|
else if (settingsConstructor != null)
|
|
{
|
|
service = settingsConstructor.Invoke(new object[] { serviceSettings.Parameters });
|
|
}
|
|
else
|
|
{
|
|
service = Activator.CreateInstance(t);
|
|
}
|
|
return service;
|
|
}
|
|
|
|
public virtual void AddService(object service)
|
|
{
|
|
if (service == null)
|
|
throw new ArgumentNullException("service");
|
|
|
|
InterceptService(service, true);
|
|
if (this.Runtime != null)
|
|
{
|
|
this.Runtime.AddService(service);
|
|
}
|
|
else
|
|
{
|
|
lock (this.servicesLock)
|
|
{
|
|
this.services.Add(service);
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual void RemoveService(object service)
|
|
{
|
|
if (service == null)
|
|
throw new ArgumentNullException("service");
|
|
|
|
InterceptService(service, false);
|
|
if (this.Runtime != null)
|
|
{
|
|
this.Runtime.RemoveService(service);
|
|
}
|
|
else
|
|
{
|
|
lock (this.servicesLock)
|
|
{
|
|
this.services.Remove(service);
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual object GetService(Type serviceType)
|
|
{
|
|
if (serviceType == null)
|
|
throw new ArgumentNullException("serviceType");
|
|
|
|
if (this.Runtime != null)
|
|
{
|
|
return this.Runtime.GetService(serviceType);
|
|
}
|
|
else
|
|
{
|
|
lock (this.servicesLock)
|
|
{
|
|
foreach (object service in this.services)
|
|
{
|
|
if (serviceType.IsAssignableFrom(service.GetType()))
|
|
{
|
|
return service;
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void InterceptService(object service, bool add)
|
|
{
|
|
bool isDataExchangeService = false;
|
|
Type[] interfaceTypes = service.GetType().GetInterfaces();
|
|
foreach (Type type in interfaceTypes)
|
|
{
|
|
object[] attributes = type.GetCustomAttributes(typeof(ExternalDataExchangeAttribute), false);
|
|
if (attributes.Length == 0)
|
|
continue;
|
|
|
|
if (this.Runtime != null && this.Runtime.GetService(type) != null && add)
|
|
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_ExternalDataExchangeServiceExists), type));
|
|
|
|
isDataExchangeService = true;
|
|
EventInfo[] events = type.GetEvents();
|
|
if (events == null)
|
|
continue;
|
|
|
|
foreach (EventInfo e in events)
|
|
{
|
|
WorkflowMessageEventHandler handler = null;
|
|
int hash = type.GetHashCode() ^ e.Name.GetHashCode();
|
|
lock (handlersLock)
|
|
{
|
|
if (!this.eventHandlers.ContainsKey(hash))
|
|
{
|
|
handler = new WorkflowMessageEventHandler(type, e, this.enqueueMessageWrapper);
|
|
this.eventHandlers.Add(hash, handler);
|
|
}
|
|
else
|
|
{
|
|
handler = this.eventHandlers[hash];
|
|
}
|
|
}
|
|
AddRemove(service, handler.Delegate, add, e.Name);
|
|
}
|
|
}
|
|
if (!isDataExchangeService)
|
|
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.Error_ServiceMissingExternalDataExchangeInterface)));
|
|
}
|
|
|
|
private void AddRemove(object addedservice, Delegate delg, bool add, string eventName)
|
|
{
|
|
try
|
|
{
|
|
string eventAction;
|
|
if (add)
|
|
eventAction = "add_" + eventName;
|
|
else
|
|
eventAction = "remove_" + eventName;
|
|
|
|
Type serviceType = addedservice.GetType();
|
|
if (delg != null)
|
|
{
|
|
// add or remove interception handler
|
|
object[] del = { delg };
|
|
serviceType.InvokeMember(eventAction, BindingFlags.InvokeMethod, null, addedservice, del, null);
|
|
}
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (IsIrrecoverableException(e))
|
|
{
|
|
throw;
|
|
}
|
|
// cannot intercept this event
|
|
}
|
|
}
|
|
|
|
internal static bool IsIrrecoverableException(Exception e)
|
|
{
|
|
return ((e is OutOfMemoryException) ||
|
|
(e is StackOverflowException) ||
|
|
(e is ThreadInterruptedException) ||
|
|
(e is ThreadAbortException));
|
|
}
|
|
|
|
class EnqueueMessageWrapper : IDeliverMessage
|
|
{
|
|
ExternalDataExchangeService eds;
|
|
|
|
public EnqueueMessageWrapper(ExternalDataExchangeService eds)
|
|
{
|
|
this.eds = eds;
|
|
}
|
|
|
|
public object[] PrepareEventArgsArray(object sender, ExternalDataEventArgs eventArgs, out object workItem, out IPendingWork workHandler)
|
|
{
|
|
// remove the batch items from the event args, only the runtime needs this data
|
|
// and it is not necessarily serializable.
|
|
workItem = eventArgs.WorkItem;
|
|
eventArgs.WorkItem = null;
|
|
workHandler = eventArgs.WorkHandler;
|
|
eventArgs.WorkHandler = null;
|
|
|
|
return new object[] { sender, eventArgs };
|
|
}
|
|
public void DeliverMessage(ExternalDataEventArgs eventArgs, IComparable queueName, object message, object workItem, IPendingWork workHandler)
|
|
{
|
|
WorkflowInstance workflowInstance = this.eds.Runtime.GetWorkflow(eventArgs.InstanceId);
|
|
if (eventArgs.WaitForIdle)
|
|
{
|
|
workflowInstance.EnqueueItemOnIdle(queueName, message, workHandler, workItem);
|
|
}
|
|
else
|
|
{
|
|
workflowInstance.EnqueueItem(queueName, message, workHandler, workItem);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|