1 //----------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //----------------------------------------------------------------
4 
5 namespace System.ServiceModel.Activities
6 {
7     using System;
8     using System.Activities;
9     using System.Activities.Expressions;
10     using System.Collections.Generic;
11     using System.Collections.ObjectModel;
12     using System.Runtime;
13     using System.Runtime.DurableInstancing;
14     using System.ServiceModel;
15     using System.ServiceModel.Activities.Dispatcher;
16     using System.ServiceModel.Channels;
17     using System.Xaml;
18     using Microsoft.VisualBasic.Activities;
19     using SR2 = System.ServiceModel.Activities.SR;
20 
21     static class MessagingActivityHelper
22     {
23         static Type faultExceptionType = typeof(FaultException);
24         static Type faultExceptionGenericType = typeof(FaultException<>);
25 
26         public const string ActivityInstanceId = "ActivityInstanceId";
27         public const string ActivityName = "ActivityName";
28         public const string ActivityType = "ActivityType";
29         public const string ActivityTypeExecuteUserCode = "ExecuteUserCode";
30         public const string MessagingActivityTypeActivityExecution = "MessagingActivityExecution";
31         public const string E2EActivityId = "E2EActivityId";
32         public const string MessageId = "MessageId";
33         public const string ActivityNameWorkflowOperationInvoke = "WorkflowOperationInvoke";
34         public const string MessageCorrelationReceiveRecord = "MessageCorrelationReceiveRecord";
35         public const string MessageCorrelationSendRecord = "MessageCorrelationSendRecord";
36 
FixMessageArgument(Argument messageArgument, ArgumentDirection direction, ActivityMetadata metadata)37         public static void FixMessageArgument(Argument messageArgument, ArgumentDirection direction, ActivityMetadata metadata)
38         {
39             Type messageType = (messageArgument == null) ? TypeHelper.ObjectType : messageArgument.ArgumentType;
40             AddRuntimeArgument(messageArgument, "Message", messageType, direction, metadata);
41         }
42 
AddRuntimeArgument(Argument messageArgument, string runtimeArgumentName, Type runtimeArgumentType, ArgumentDirection runtimeArgumentDirection, ActivityMetadata metadata)43         public static void AddRuntimeArgument(Argument messageArgument, string runtimeArgumentName, Type runtimeArgumentType,
44                 ArgumentDirection runtimeArgumentDirection, ActivityMetadata metadata)
45         {
46             RuntimeArgument argument = new RuntimeArgument(runtimeArgumentName, runtimeArgumentType, runtimeArgumentDirection);
47             metadata.Bind(messageArgument, argument);
48             metadata.AddArgument(argument);
49         }
50 
51         //
52         public static IList<T> GetCallbacks<T>(ExecutionProperties executionProperties)
53             where T : class
54         {
55             List<T> list = null;
56 
57             if (!executionProperties.IsEmpty)
58             {
59                 T temp;
60                 foreach (KeyValuePair<string, object> item in executionProperties)
61                 {
62                     temp = item.Value as T;
63 
64                     if (temp != null)
65                     {
66                         if (list == null)
67                         {
68                             list = new List<T>();
69                         }
70                         list.Add(temp);
71                     }
72                 }
73             }
74 
75             return list;
76         }
77 
InitializeCorrelationHandles(NativeActivityContext context, CorrelationHandle selectHandle, CorrelationHandle ambientHandle, Collection<CorrelationInitializer> additionalCorrelations, CorrelationKeyCalculator keyCalculator, Message message)78         public static Message InitializeCorrelationHandles(NativeActivityContext context,
79             CorrelationHandle selectHandle, CorrelationHandle ambientHandle, Collection<CorrelationInitializer> additionalCorrelations,
80             CorrelationKeyCalculator keyCalculator, Message message)
81         {
82             InstanceKey instanceKey;
83             ICollection<InstanceKey> additionalKeys;
84 
85             //
86             MessageBuffer buffer = message.CreateBufferedCopy(int.MaxValue);
87             if (keyCalculator.CalculateKeys(buffer, message, out instanceKey, out additionalKeys))
88             {
89                 InitializeCorrelationHandles(context, selectHandle, ambientHandle, additionalCorrelations, instanceKey, additionalKeys);
90             }
91             return buffer.CreateMessage();
92         }
93 
InitializeCorrelationHandles(NativeActivityContext context, CorrelationHandle selectHandle, CorrelationHandle ambientHandle, Collection<CorrelationInitializer> additionalCorrelations, MessageProperties messageProperties)94         public static void InitializeCorrelationHandles(NativeActivityContext context,
95             CorrelationHandle selectHandle, CorrelationHandle ambientHandle, Collection<CorrelationInitializer> additionalCorrelations,
96             MessageProperties messageProperties)
97         {
98             CorrelationMessageProperty correlationMessageProperty;
99             if (CorrelationMessageProperty.TryGet(messageProperties, out correlationMessageProperty))
100             {
101                 InitializeCorrelationHandles(context, selectHandle, ambientHandle, additionalCorrelations,
102                     correlationMessageProperty.CorrelationKey, correlationMessageProperty.AdditionalKeys);
103             }
104         }
105 
106 
107         // both receive and send initialize correlations using this method
108         // if selectHandle is not null, we first try to initalize instanceKey with it , else we try to initalize the ambient handle
109         // if ambient handle is not used for initializing instance key , we might use it for initalizing  queryCorrelationsInitalizer.
110 
111         // SelectHandle usage:
112         // Receive: selectHandle is the correlatesWith handle
113         // SendReply: in case of context based correlation, this is the context handle
114         // Send: in case of context based correlation, this will be the callback handle
115         // ReceiveReply: selectHandle will be always null
116         // Note that only Receive can initialize a content based correlation with a selectHandle (parallel convoy)
InitializeCorrelationHandles(NativeActivityContext context, CorrelationHandle selectHandle, CorrelationHandle ambientHandle, Collection<CorrelationInitializer> additionalCorrelations, InstanceKey instanceKey, ICollection<InstanceKey> additionalKeys)117         internal static void InitializeCorrelationHandles(NativeActivityContext context,
118              CorrelationHandle selectHandle, CorrelationHandle ambientHandle, Collection<CorrelationInitializer> additionalCorrelations,
119              InstanceKey instanceKey, ICollection<InstanceKey> additionalKeys)
120         {
121             bool isAmbientHandleUsed = false;
122             if (instanceKey != null && instanceKey.IsValid)
123             {
124                 if (selectHandle != null)
125                 {
126                     selectHandle.InitializeBookmarkScope(context, instanceKey);
127                 }
128                 else if (ambientHandle != null)
129                 {
130                     ambientHandle.InitializeBookmarkScope(context, instanceKey);
131                     isAmbientHandleUsed = true;
132                 }
133                 else if (context.DefaultBookmarkScope.IsInitialized)
134                 {
135                     if (context.DefaultBookmarkScope.Id != instanceKey.Value)
136                     {
137                         throw FxTrace.Exception.AsError(
138                             new InvalidOperationException(SR2.CorrelationHandleInUse(context.DefaultBookmarkScope.Id, instanceKey.Value)));
139                     }
140                 }
141                 else
142                 {
143                     context.DefaultBookmarkScope.Initialize(context, instanceKey.Value);
144                 }
145             }
146 
147             if (additionalKeys != null && additionalCorrelations != null)
148             {
149                 // The ordering of items in SelectAdditional and additional correlations are the same
150                 // Therefore, we assign keys iteratively
151 
152                 IEnumerator<CorrelationInitializer> enumerator = additionalCorrelations.GetEnumerator();
153 
154                 foreach (InstanceKey key in additionalKeys)
155                 {
156                     Fx.Assert(key != null && key.IsValid, "only valid keys should be passed into InitializeCorrelationHandles");
157 
158                     while (enumerator.MoveNext())
159                     {
160                         QueryCorrelationInitializer queryCorrelation = enumerator.Current as QueryCorrelationInitializer;
161                         if (queryCorrelation != null)
162                         {
163                             CorrelationHandle handle = (queryCorrelation.CorrelationHandle != null ? queryCorrelation.CorrelationHandle.Get(context) : null);
164                             if (handle == null)
165                             {
166                                 if (ambientHandle != null && !isAmbientHandleUsed)
167                                 {
168                                     handle = ambientHandle;
169                                     isAmbientHandleUsed = true;
170                                 }
171                                 else
172                                 {
173                                     throw FxTrace.Exception.AsError(
174                                         new InvalidOperationException(SR2.QueryCorrelationInitializerCannotBeInitialized));
175                                 }
176                             }
177                             handle.InitializeBookmarkScope(context, key);
178                             break;
179                         }
180                     }
181                 }
182             }
183         }
184 
CreateCorrelationCallbackContext(MessageProperties messageProperties)185         public static CorrelationCallbackContext CreateCorrelationCallbackContext(MessageProperties messageProperties)
186         {
187             CallbackContextMessageProperty callbackMessageContextProperty;
188             if (CallbackContextMessageProperty.TryGet(messageProperties, out callbackMessageContextProperty))
189             {
190                 EndpointAddress listenAddress;
191                 IDictionary<string, string> context;
192                 callbackMessageContextProperty.GetListenAddressAndContext(out listenAddress, out context);
193 
194                 return new CorrelationCallbackContext
195                 {
196                     ListenAddress = EndpointAddress10.FromEndpointAddress(listenAddress),
197                     Context = context
198                 };
199             }
200             return null;
201         }
202 
CreateCorrelationContext(MessageProperties messageProperties)203         public static CorrelationContext CreateCorrelationContext(MessageProperties messageProperties)
204         {
205             ContextMessageProperty contextMessageProperty;
206             if (ContextMessageProperty.TryGet(messageProperties, out contextMessageProperty))
207             {
208                 IDictionary<string, string> context;
209                 context = contextMessageProperty.Context;
210                 return new CorrelationContext
211                 {
212                     Context = context
213                 };
214             }
215             return null;
216         }
217 
CompareContextEquality(IDictionary<string, string> context1, IDictionary<string, string> context2)218         public static bool CompareContextEquality(IDictionary<string, string> context1, IDictionary<string, string> context2)
219         {
220             if (context1 != context2)
221             {
222                 if (context1 == null ||
223                     context2 == null ||
224                     context1.Count != context2.Count)
225                 {
226                     return false;
227                 }
228                 foreach (KeyValuePair<string, string> pair in context1)
229                 {
230                     if (!context2.Contains(pair))
231                     {
232                         return false;
233                     }
234                 }
235             }
236             return true;
237         }
238 
CreateReplyCorrelatesWith(InArgument<CorrelationHandle> requestCorrelatesWith)239         public static InArgument<CorrelationHandle> CreateReplyCorrelatesWith(InArgument<CorrelationHandle> requestCorrelatesWith)
240         {
241             Fx.Assert(requestCorrelatesWith != null, "Argument cannot be null!");
242 
243             VariableValue<CorrelationHandle> variableValue = requestCorrelatesWith.Expression as VariableValue<CorrelationHandle>;
244             if (variableValue != null)
245             {
246                 return new InArgument<CorrelationHandle>(variableValue.Variable);
247             }
248 
249             VisualBasicValue<CorrelationHandle> vbvalue = requestCorrelatesWith.Expression as VisualBasicValue<CorrelationHandle>;
250             if (vbvalue != null)
251             {
252                 return new InArgument<CorrelationHandle>(new VisualBasicValue<CorrelationHandle>(vbvalue.ExpressionText));
253             }
254 
255             // We use XAML roundtrip to clone expression
256             string xamlStr = XamlServices.Save(requestCorrelatesWith.Expression);
257             object obj = XamlServices.Parse(xamlStr);
258 
259             Activity<CorrelationHandle> expression = obj as Activity<CorrelationHandle>;
260             Fx.Assert(expression != null, "Failed to clone CorrelationHandle using XAML roundtrip!");
261 
262             return new InArgument<CorrelationHandle>(expression);
263 
264         }
265 
ValidateCorrelationInitializer(ActivityMetadata metadata, Collection<CorrelationInitializer> correlationInitializers, bool isReply, string displayName, string operationName)266         public static void ValidateCorrelationInitializer(ActivityMetadata metadata, Collection<CorrelationInitializer> correlationInitializers, bool isReply, string displayName, string operationName)
267         {
268             Fx.Assert(metadata != null, "cannot be null");
269 
270             if (correlationInitializers != null && correlationInitializers.Count > 0)
271             {
272                 bool queryInitializerWithEmptyHandle = false;
273                 foreach (CorrelationInitializer correlation in correlationInitializers)
274                 {
275                     if (correlation is RequestReplyCorrelationInitializer && isReply)
276                     {
277                         // This is a reply, so additional correlations should not have a request reply handle
278                         metadata.AddValidationError(SR.ReplyShouldNotIncludeRequestReplyHandle(displayName, operationName));
279                     }
280 
281                     QueryCorrelationInitializer queryCorrelation = correlation as QueryCorrelationInitializer;
282                     if (queryCorrelation != null)
283                     {
284                         if (queryCorrelation.MessageQuerySet.Count == 0)
285                         {
286                             metadata.AddValidationError(SR.QueryCorrelationInitializerWithEmptyMessageQuerySet(displayName, operationName));
287                         }
288                     }
289 
290                     if (correlation.CorrelationHandle == null)
291                     {
292                         if (correlation is QueryCorrelationInitializer)
293                         {
294                             if (!queryInitializerWithEmptyHandle)
295                             {
296                                 queryInitializerWithEmptyHandle = true;
297                             }
298                             else
299                             {
300                                 // more than one queryInitializer present, in this case we don't permit null handle
301                                 metadata.AddValidationError(SR.NullCorrelationHandleInMultipleQueryCorrelation);
302                             }
303                         }
304                         else
305                         {
306                             metadata.AddValidationError(SR.NullCorrelationHandleInInitializeCorrelation(correlation.GetType().Name));
307                         }
308                     }
309                 }
310             }
311         }
312     }
313 }
314