using System; using System.Reflection; using System.Drawing; using System.Drawing.Design; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; using System.ComponentModel.Design; using System.Workflow.ComponentModel; using System.Workflow.Runtime; using System.Workflow.ComponentModel.Design; using System.Workflow.ComponentModel.Compiler; using System.Runtime.Remoting.Messaging; using System.Diagnostics; using System.Workflow.Activities.Common; namespace System.Workflow.Activities { [SRDescription(SR.WebServiceResponseActivityDescription)] [SRCategory(SR.Standard)] [ToolboxBitmap(typeof(WebServiceOutputActivity), "Resources.WebServiceOut.png")] [Designer(typeof(WebServiceResponseDesigner), typeof(IDesigner))] [ActivityValidator(typeof(WebServiceResponseValidator))] [DefaultEvent("SendingOutput")] [Obsolete("The System.Workflow.* types are deprecated. Instead, please use the new types from System.Activities.*")] public sealed class WebServiceOutputActivity : Activity, IPropertyValueProvider, IDynamicPropertyTypeProvider { //metadata properties public static readonly DependencyProperty InputActivityNameProperty = DependencyProperty.Register("InputActivityName", typeof(string), typeof(WebServiceOutputActivity), new PropertyMetadata("", DependencyPropertyOptions.Metadata)); //instance properties public static readonly DependencyProperty ParameterBindingsProperty = DependencyProperty.Register("ParameterBindings", typeof(WorkflowParameterBindingCollection), typeof(WebServiceOutputActivity), new PropertyMetadata(DependencyPropertyOptions.Metadata | DependencyPropertyOptions.ReadOnly, new Attribute[] { new BrowsableAttribute(false), new DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Content) })); //event public static readonly DependencyProperty SendingOutputEvent = DependencyProperty.Register("SendingOutput", typeof(EventHandler), typeof(WebServiceOutputActivity)); #region Constructors public WebServiceOutputActivity() { // base.SetReadOnlyPropertyValue(ParameterBindingsProperty, new WorkflowParameterBindingCollection(this)); } public WebServiceOutputActivity(string name) : base(name) { // base.SetReadOnlyPropertyValue(ParameterBindingsProperty, new WorkflowParameterBindingCollection(this)); } #endregion [SRCategory(SR.Activity)] [SRDescription(SR.ReceiveActivityNameDescription)] [TypeConverter(typeof(PropertyValueProviderTypeConverter))] [RefreshProperties(RefreshProperties.All)] [MergablePropertyAttribute(false)] [DefaultValue("")] public string InputActivityName { get { return base.GetValue(InputActivityNameProperty) as string; } set { base.SetValue(InputActivityNameProperty, value); } } [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)] [Browsable(false)] public WorkflowParameterBindingCollection ParameterBindings { get { return base.GetValue(ParameterBindingsProperty) as WorkflowParameterBindingCollection; } } [SRDescription(SR.OnBeforeResponseDescr)] [SRCategory(SR.Handlers)] [MergableProperty(false)] public event EventHandler SendingOutput { add { base.AddHandler(SendingOutputEvent, value); } remove { base.RemoveHandler(SendingOutputEvent, value); } } ICollection IPropertyValueProvider.GetPropertyValues(ITypeDescriptorContext context) { StringCollection names = new StringCollection(); if (context.PropertyDescriptor.Name == "InputActivityName") { foreach (Activity activity in WebServiceActivityHelpers.GetPreceedingActivities(this)) { if (activity is WebServiceInputActivity) { names.Add(activity.QualifiedName); } } } return names; } protected override void Initialize(IServiceProvider provider) { if (this.Parent == null) throw new InvalidOperationException(SR.GetString(SR.Error_MustHaveParent)); base.Initialize(provider); } #region Execute protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext) { if (executionContext == null) throw new ArgumentNullException("executionContext"); WorkflowQueuingService queueService = executionContext.GetService(); // fire event this.RaiseEvent(WebServiceOutputActivity.SendingOutputEvent, this, EventArgs.Empty); WebServiceInputActivity webservicereceive = this.GetActivityByName(this.InputActivityName) as WebServiceInputActivity; if (webservicereceive == null) { Activity parent = this.Parent; while (parent != null) { //typically if defined inside a custom activity string qualifiedName = parent.QualifiedName + "." + this.InputActivityName; webservicereceive = this.GetActivityByName(qualifiedName) as WebServiceInputActivity; if (webservicereceive != null) break; parent = this.Parent; } } if (webservicereceive == null) throw new InvalidOperationException(SR.GetString(SR.Error_CannotResolveWebServiceInput, this.QualifiedName, this.InputActivityName)); IComparable queueId = new EventQueueName(webservicereceive.InterfaceType, webservicereceive.MethodName, webservicereceive.QualifiedName); MethodInfo mInfo = webservicereceive.InterfaceType.GetMethod(webservicereceive.MethodName); if (!queueService.Exists(queueId)) { // determine if no response is required, // compiler did not catch it, do the runtime check and return if (mInfo.ReturnType == typeof(void)) { return ActivityExecutionStatus.Closed; } bool isresponseRequired = false; foreach (ParameterInfo formalParameter in mInfo.GetParameters()) { if (formalParameter.ParameterType.IsByRef || (formalParameter.IsIn && formalParameter.IsOut)) { isresponseRequired = true; } } if (isresponseRequired) { return ActivityExecutionStatus.Closed; } } if (!queueService.Exists(queueId)) throw new InvalidOperationException(SR.GetString(SR.Error_WebServiceInputNotProcessed, webservicereceive.QualifiedName)); IMethodResponseMessage responseMessage = null; WorkflowQueue queue = queueService.GetWorkflowQueue(queueId); if (queue.Count != 0) responseMessage = queue.Dequeue() as IMethodResponseMessage; IMethodMessage message = responseMessage as IMethodMessage; WorkflowParameterBindingCollection parameterBindings = this.ParameterBindings; ArrayList outArgs = new ArrayList(); // populate result if (this.ParameterBindings.Contains("(ReturnValue)")) { WorkflowParameterBinding retBind = this.ParameterBindings["(ReturnValue)"]; if (retBind != null) { outArgs.Add(retBind.Value); } } foreach (ParameterInfo formalParameter in mInfo.GetParameters()) { // update out and byref values if (formalParameter.ParameterType.IsByRef || (formalParameter.IsIn && formalParameter.IsOut)) { WorkflowParameterBinding binding = parameterBindings[formalParameter.Name]; outArgs.Add(binding.Value); } } // reset the waiting thread responseMessage.SendResponse(outArgs); return ActivityExecutionStatus.Closed; } #endregion #region IDynamicPropertyTypeProvider Type IDynamicPropertyTypeProvider.GetPropertyType(IServiceProvider serviceProvider, string propertyName) { if (propertyName == null) throw new ArgumentNullException("propertyName"); Dictionary parameters = new Dictionary(); this.GetParameterPropertyDescriptors(parameters); if (parameters.ContainsKey(propertyName)) { ParameterInfoBasedPropertyDescriptor descriptor = parameters[propertyName] as ParameterInfoBasedPropertyDescriptor; if (descriptor != null) return descriptor.ParameterType; } return null; } AccessTypes IDynamicPropertyTypeProvider.GetAccessType(IServiceProvider serviceProvider, string propertyName) { if (propertyName == null) throw new ArgumentNullException("propertyName"); return AccessTypes.Read; } internal void GetParameterPropertyDescriptors(IDictionary properties) { if (((IComponent)this).Site == null) return; ITypeProvider typeProvider = (ITypeProvider)((IComponent)this).Site.GetService(typeof(ITypeProvider)); if (typeProvider == null) throw new InvalidOperationException(SR.GetString(SR.General_MissingService, typeof(ITypeProvider).FullName)); if (this.InputActivityName != null && !String.IsNullOrEmpty(this.InputActivityName.Trim())) { WebServiceInputActivity webServiceReceive = Helpers.ParseActivity(Helpers.GetRootActivity(this), this.InputActivityName) as WebServiceInputActivity; if (webServiceReceive != null) { Type type = null; if (webServiceReceive.InterfaceType != null) type = typeProvider.GetType(webServiceReceive.InterfaceType.AssemblyQualifiedName); if (type != null) { MethodInfo method = Helpers.GetInterfaceMethod(type, webServiceReceive.MethodName); if (method != null && WebServiceActivityHelpers.ValidateParameterTypes(method).Count == 0) { List inputParameters, outParameters; WebServiceActivityHelpers.GetParameterInfo(method, out inputParameters, out outParameters); foreach (ParameterInfo paramInfo in outParameters) { PropertyDescriptor prop = null; if (paramInfo.Position == -1) prop = new ParameterInfoBasedPropertyDescriptor(typeof(WebServiceOutputActivity), paramInfo, false, DesignOnlyAttribute.Yes); else prop = new ParameterInfoBasedPropertyDescriptor(typeof(WebServiceOutputActivity), paramInfo, true, DesignOnlyAttribute.Yes); if (prop != null) properties[prop.Name] = prop; } } } } } } #endregion } internal sealed class WebServiceResponseValidator : ActivityValidator { public override ValidationErrorCollection Validate(ValidationManager manager, object obj) { ValidationErrorCollection validationErrors = base.Validate(manager, obj); WebServiceOutputActivity webServiceResponse = obj as WebServiceOutputActivity; if (webServiceResponse == null) throw new ArgumentException(SR.GetString(SR.Error_UnexpectedArgumentType, typeof(WebServiceOutputActivity).FullName), "obj"); if (Helpers.IsActivityLocked(webServiceResponse)) { return validationErrors; } WebServiceInputActivity webServiceReceive = null; if (String.IsNullOrEmpty(webServiceResponse.InputActivityName)) validationErrors.Add(ValidationError.GetNotSetValidationError("InputActivityName")); else { ITypeProvider typeProvider = (ITypeProvider)manager.GetService(typeof(ITypeProvider)); if (typeProvider == null) throw new InvalidOperationException(SR.GetString(SR.General_MissingService, typeof(ITypeProvider).FullName)); bool foundMatchingReceive = false; foreach (Activity activity in WebServiceActivityHelpers.GetPreceedingActivities(webServiceResponse)) { if ((activity is WebServiceOutputActivity && String.Compare(((WebServiceOutputActivity)activity).InputActivityName, webServiceResponse.InputActivityName, StringComparison.Ordinal) == 0) || (activity is WebServiceFaultActivity && String.Compare(((WebServiceFaultActivity)activity).InputActivityName, webServiceResponse.InputActivityName, StringComparison.Ordinal) == 0)) { if (activity is WebServiceOutputActivity) validationErrors.Add(new ValidationError(SR.GetString(SR.Error_DuplicateWebServiceResponseFound, activity.QualifiedName, webServiceResponse.InputActivityName), ErrorNumbers.Error_DuplicateWebServiceResponseFound)); else validationErrors.Add(new ValidationError(SR.GetString(SR.Error_DuplicateWebServiceFaultFound, activity.QualifiedName, webServiceResponse.InputActivityName), ErrorNumbers.Error_DuplicateWebServiceFaultFound)); return validationErrors; } } foreach (Activity activity in WebServiceActivityHelpers.GetPreceedingActivities(webServiceResponse)) { if (String.Compare(activity.QualifiedName, webServiceResponse.InputActivityName, StringComparison.Ordinal) == 0) { if (activity is WebServiceInputActivity) { webServiceReceive = activity as WebServiceInputActivity; foundMatchingReceive = true; } else { foundMatchingReceive = false; validationErrors.Add(new ValidationError(SR.GetString(SR.Error_WebServiceReceiveNotValid, webServiceResponse.InputActivityName), ErrorNumbers.Error_WebServiceReceiveNotValid)); return validationErrors; } break; } } if (!foundMatchingReceive) { validationErrors.Add(new ValidationError(SR.GetString(SR.Error_WebServiceReceiveNotFound, webServiceResponse.InputActivityName), ErrorNumbers.Error_WebServiceReceiveNotFound)); return validationErrors; } else { Type interfaceType = null; if (webServiceReceive.InterfaceType != null) interfaceType = typeProvider.GetType(webServiceReceive.InterfaceType.AssemblyQualifiedName); if (interfaceType == null) { validationErrors.Add(new ValidationError(SR.GetString(SR.Error_WebServiceReceiveNotConfigured, webServiceReceive.Name), ErrorNumbers.Error_WebServiceReceiveNotConfigured)); } else { // Validate method if (String.IsNullOrEmpty(webServiceReceive.MethodName)) validationErrors.Add(new ValidationError(SR.GetString(SR.Error_WebServiceReceiveNotConfigured, webServiceReceive.Name), ErrorNumbers.Error_WebServiceReceiveNotConfigured)); else { MethodInfo methodInfo = Helpers.GetInterfaceMethod(interfaceType, webServiceReceive.MethodName); if (methodInfo == null) { validationErrors.Add(new ValidationError(SR.GetString(SR.Error_WebServiceReceiveNotConfigured, webServiceReceive.Name), ErrorNumbers.Error_WebServiceReceiveNotConfigured)); } else { ValidationErrorCollection parameterTypeErrors = WebServiceActivityHelpers.ValidateParameterTypes(methodInfo); if (parameterTypeErrors.Count > 0) { foreach (ValidationError parameterTypeError in parameterTypeErrors) { parameterTypeError.PropertyName = "InputActivityName"; } validationErrors.AddRange(parameterTypeErrors); } else { List inputParameters, outParameters; WebServiceActivityHelpers.GetParameterInfo(methodInfo, out inputParameters, out outParameters); if (outParameters.Count == 0) { validationErrors.Add(new ValidationError(SR.GetString(SR.Error_WebServiceResponseNotNeeded), ErrorNumbers.Error_WebServiceResponseNotNeeded)); } else { // Check to see if all output parameters have a valid bindings. foreach (ParameterInfo paramInfo in outParameters) { string paramName = paramInfo.Name; Type paramType = paramInfo.ParameterType.IsByRef ? paramInfo.ParameterType.GetElementType() : paramInfo.ParameterType; if (paramInfo.Position == -1) paramName = "(ReturnValue)"; object paramValue = null; if (webServiceResponse.ParameterBindings.Contains(paramName)) { if (webServiceResponse.ParameterBindings[paramName].IsBindingSet(WorkflowParameterBinding.ValueProperty)) paramValue = webServiceResponse.ParameterBindings[paramName].GetBinding(WorkflowParameterBinding.ValueProperty); else paramValue = webServiceResponse.ParameterBindings[paramName].GetValue(WorkflowParameterBinding.ValueProperty); } if (!paramType.IsPublic || !paramType.IsSerializable) { ValidationError validationError = new ValidationError(SR.GetString(SR.Error_TypeNotPublicSerializable, paramName, paramType.FullName), ErrorNumbers.Error_TypeNotPublicSerializable); validationError.PropertyName = (String.Compare(paramName, "(ReturnValue)", StringComparison.Ordinal) == 0) ? paramName : ParameterInfoBasedPropertyDescriptor.GetParameterPropertyName(webServiceReceive.GetType(), paramName); validationErrors.Add(validationError); } else if (!webServiceResponse.ParameterBindings.Contains(paramName) || paramValue == null) { ValidationError validationError = ValidationError.GetNotSetValidationError(paramName); validationError.PropertyName = (String.Compare(paramName, "(ReturnValue)", StringComparison.Ordinal) == 0) ? paramName : ParameterInfoBasedPropertyDescriptor.GetParameterPropertyName(webServiceReceive.GetType(), paramName); validationErrors.Add(validationError); } else { AccessTypes access = AccessTypes.Read; if (paramInfo.IsOut || paramInfo.IsRetval || paramInfo.Position == -1) access = AccessTypes.Write; ValidationErrorCollection variableErrors = ValidationHelpers.ValidateProperty(manager, webServiceResponse, paramValue, new PropertyValidationContext(webServiceResponse.ParameterBindings[paramName], null, paramName), new BindValidationContext(paramInfo.ParameterType.IsByRef ? paramInfo.ParameterType.GetElementType() : paramInfo.ParameterType, access)); foreach (ValidationError variableError in variableErrors) { if (String.Compare(paramName, "(ReturnValue)", StringComparison.Ordinal) != 0) variableError.PropertyName = ParameterInfoBasedPropertyDescriptor.GetParameterPropertyName(webServiceReceive.GetType(), paramName); } validationErrors.AddRange(variableErrors); } } if (webServiceResponse.ParameterBindings.Count > outParameters.Count) validationErrors.Add(new ValidationError(SR.GetString(SR.Warning_AdditionalBindingsFound), ErrorNumbers.Warning_AdditionalBindingsFound, true)); } } } } } } } return validationErrors; } } internal static class WebServiceActivityHelpers { private static IEnumerable GetContainedActivities(CompositeActivity activity) { if (!activity.Enabled) yield break; foreach (Activity containedActivity in activity.Activities) { if (containedActivity is CompositeActivity && !Helpers.IsCustomActivity((CompositeActivity)containedActivity)) { foreach (Activity nestedActivity in WebServiceActivityHelpers.GetContainedActivities((CompositeActivity)containedActivity)) { if (nestedActivity.Enabled) yield return nestedActivity; } } else { if (containedActivity.Enabled) yield return containedActivity; } } yield break; } internal static IEnumerable GetPreceedingActivities(Activity startActivity) { return GetPreceedingActivities(startActivity, false); } internal static IEnumerable GetPreceedingActivities(Activity startActivity, bool crossOverLoop) { Activity currentActivity = null; Stack activityStack = new Stack(); activityStack.Push(startActivity); while ((currentActivity = activityStack.Pop()) != null) { if (currentActivity is CompositeActivity && Helpers.IsCustomActivity((CompositeActivity)currentActivity)) break; if (currentActivity.Parent != null) { foreach (Activity siblingActivity in currentActivity.Parent.Activities) { // if (siblingActivity == currentActivity && ((currentActivity.Parent is ParallelActivity && !Helpers.IsFrameworkActivity(currentActivity)) || (currentActivity.Parent is StateActivity && !Helpers.IsFrameworkActivity(currentActivity)))) continue; // if (currentActivity.Parent is IfElseActivity && !Helpers.IsFrameworkActivity(currentActivity)) continue; //For Listen Case. if (currentActivity.Parent is ListenActivity && !Helpers.IsFrameworkActivity(currentActivity)) continue; // State Machine logic: // If startActivity was in the InitialState, then // there are no preceeding activities. // Otherwise, we just return the parent state as // the preceeding activity. StateActivity currentState = currentActivity.Parent as StateActivity; if (currentState != null) { StateActivity enclosingState = StateMachineHelpers.FindEnclosingState(startActivity); //If we are at Initial State there is no preceeding above us. if (StateMachineHelpers.IsInitialState(enclosingState)) yield break; else yield return currentState; } if (siblingActivity == currentActivity) break; if (siblingActivity.Enabled) { if (siblingActivity is CompositeActivity && !Helpers.IsCustomActivity((CompositeActivity)siblingActivity) && (crossOverLoop || !IsLoopActivity(siblingActivity))) { foreach (Activity containedActivity in WebServiceActivityHelpers.GetContainedActivities((CompositeActivity)siblingActivity)) yield return containedActivity; } else { yield return siblingActivity; } } } } if (!crossOverLoop && IsLoopActivity(currentActivity.Parent)) break; else activityStack.Push(currentActivity.Parent); } yield break; } internal static bool IsLoopActivity(Activity activity) { // if (activity is WhileActivity || activity is ReplicatorActivity || activity is ConditionedActivityGroup) return true; return false; } internal static bool IsInsideLoop(Activity webServiceActivity, Activity searchBoundary) { IEnumerable searchBoundaryPath = GetActivityPath(searchBoundary); IEnumerable currentActivityPath = GetActivityPath(webServiceActivity); String leastCommonParent = FindLeastCommonParent(searchBoundaryPath, currentActivityPath); Activity currentActivity = webServiceActivity; while (currentActivity.Parent != null && currentActivity.Parent.QualifiedName != leastCommonParent) { if (IsLoopActivity(currentActivity)) return true; currentActivity = currentActivity.Parent; } return false; } static IEnumerable GetActivityPath(Activity activity) { if (activity != null) { foreach (String path in GetActivityPath(activity.Parent)) yield return path; yield return activity.QualifiedName; } } static String FindLeastCommonParent(IEnumerable source, IEnumerable dest) { IEnumerator srcEnum = source.GetEnumerator(); IEnumerator destEnum = dest.GetEnumerator(); String leastCommonParent = null; while (srcEnum.MoveNext() && destEnum.MoveNext()) { if (srcEnum.Current.Equals(destEnum.Current)) leastCommonParent = (String)srcEnum.Current; else return leastCommonParent; } return leastCommonParent; } internal static IEnumerable GetSucceedingActivities(Activity startActivity) { Activity currentActivity = null; Stack activityStack = new Stack(); activityStack.Push(startActivity); while ((currentActivity = activityStack.Pop()) != null) { if (currentActivity is CompositeActivity && Helpers.IsCustomActivity((CompositeActivity)currentActivity)) break; if (currentActivity.Parent != null) { bool pastCurrentActivity = false; foreach (Activity siblingActivity in currentActivity.Parent.Activities) { if (siblingActivity == currentActivity) { pastCurrentActivity = true; continue; } if (!pastCurrentActivity) continue; if (siblingActivity.Enabled) { if (siblingActivity is CompositeActivity && !Helpers.IsCustomActivity((CompositeActivity)siblingActivity)) { foreach (Activity containedActivity in WebServiceActivityHelpers.GetContainedActivities((CompositeActivity)siblingActivity)) yield return containedActivity; } else { yield return siblingActivity; } } } } activityStack.Push(currentActivity.Parent); } yield break; } internal static void GetParameterInfo(MethodInfo methodInfo, out List inParameters, out List outParameters) { inParameters = new List(); outParameters = new List(); foreach (ParameterInfo paramInfo in methodInfo.GetParameters()) { if (paramInfo.IsOut || paramInfo.IsRetval || paramInfo.ParameterType.IsByRef) outParameters.Add(paramInfo); if (!paramInfo.IsOut && !paramInfo.IsRetval) inParameters.Add(paramInfo); } if (methodInfo.ReturnType != typeof(void)) outParameters.Add(methodInfo.ReturnParameter); return; } internal static ValidationErrorCollection ValidateParameterTypes(MethodInfo methodInfo) { ValidationErrorCollection validationErrors = new ValidationErrorCollection(); if (methodInfo == null) return validationErrors; foreach (ParameterInfo paramInfo in methodInfo.GetParameters()) { if (paramInfo.ParameterType == null) { validationErrors.Add(new ValidationError(SR.GetString(SR.Error_ParameterTypeNotFound, methodInfo.Name, paramInfo.Name), ErrorNumbers.Error_ParameterTypeNotFound)); } } if (methodInfo.ReturnType != typeof(void) && methodInfo.ReturnParameter.ParameterType == null) { validationErrors.Add(new ValidationError(SR.GetString(SR.Error_ReturnTypeNotFound, methodInfo.Name), ErrorNumbers.Error_ReturnTypeNotFound)); } return validationErrors; } } }