1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4 namespace System.ServiceModel.ComIntegration
5 {
6     using System;
7     using System.Collections.Generic;
8     using System.Diagnostics;
9     using System.EnterpriseServices;
10     using System.Reflection;
11     using System.Runtime;
12     using System.Runtime.InteropServices;
13     using System.Runtime.Serialization;
14     using System.ServiceModel;
15     using System.ServiceModel.Configuration;
16     using System.ServiceModel.Description;
17     using System.ServiceModel.Diagnostics;
18     using System.ServiceModel.Dispatcher;
19     using System.Xml;
20     using SR = System.ServiceModel.SR;
21 
22     class ComPlusTypeLoader : IContractResolver
23     {
24         ServiceInfo info;
25         bool transactionFlow;
26         ITypeCacheManager interfaceResolver;
27         Dictionary<Guid, ContractDescription> contracts;
28 
ComPlusTypeLoader(ServiceInfo info)29         public ComPlusTypeLoader(ServiceInfo info)
30         {
31             this.info = info;
32             this.transactionFlow = info.TransactionOption == TransactionOption.Required ||
33                                     info.TransactionOption == TransactionOption.Supported;
34             this.interfaceResolver = new TypeCacheManager();
35             this.contracts = new Dictionary<Guid, ContractDescription>();
36         }
37 
ValidateInterface(Guid iid)38         void ValidateInterface(Guid iid)
39         {
40             // Filter known invalid IIDs
41             if (!ComPlusTypeValidator.IsValidInterface(iid))
42             {
43                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.InvalidWebServiceInterface, iid)));
44             }
45 
46             // Filter out interfaces with no configured methods
47             bool configuredInterface = false;
48             foreach (ContractInfo contractInfo in this.info.Contracts)
49             {
50                 if (contractInfo.IID == iid)
51                 {
52                     if (contractInfo.Operations.Count == 0)
53                     {
54                         throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.RequireConfiguredMethods, iid)));
55                     }
56 
57                     configuredInterface = true;
58                     break;
59                 }
60             }
61 
62             // Filter out interfaces that aren't configured at all
63             if (!configuredInterface)
64             {
65                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.RequireConfiguredInterfaces, iid)));
66             }
67         }
68 
CreateContractDescriptionInternal(Guid iid, Type type)69         ContractDescription CreateContractDescriptionInternal(Guid iid, Type type)
70         {
71             ComContractElement contractConfigElement = ConfigLoader.LookupComContract(iid);
72 
73             if (contractConfigElement == null)
74                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.InterfaceNotFoundInConfig, iid)));
75             if (String.IsNullOrEmpty(contractConfigElement.Name) || String.IsNullOrEmpty(contractConfigElement.Namespace))
76                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.CannotHaveNullOrEmptyNameOrNamespaceForIID, iid)));
77 
78             ContractDescription contract = new ContractDescription(contractConfigElement.Name, contractConfigElement.Namespace);
79             contract.ContractType = type;
80             contract.SessionMode = contractConfigElement.RequiresSession ? SessionMode.Required : SessionMode.Allowed;
81 
82             bool methodFound = false;
83 
84             List<Guid> guidList = new List<Guid>();
85 
86             foreach (ComPersistableTypeElement typeElement in contractConfigElement.PersistableTypes)
87             {
88                 Guid typeGuid = Fx.CreateGuid(typeElement.ID);
89 
90                 guidList.Add(typeGuid);
91             }
92 
93             IDataContractSurrogate contractSurrogate = null;
94 
95             // We create a surrogate when the persistable types config section is there
96             // even if we have no types that we allow.
97             // That way we have control over the error when the client tries to make a call
98             // persistable type.
99             if (guidList.Count > 0 || contractConfigElement.PersistableTypes.EmitClear)
100             {
101                 contractSurrogate = new DataContractSurrogateForPersistWrapper(guidList.ToArray());
102             }
103 
104             foreach (ComMethodElement configMethod in contractConfigElement.ExposedMethods)
105             {
106                 methodFound = false;
107                 foreach (MethodInfo method in type.GetMethods())
108                 {
109                     if (method.Name == configMethod.ExposedMethod)
110                     {
111                         OperationDescription operation = CreateOperationDescription(contract, method, contractConfigElement, (null != contractSurrogate));
112                         ConfigureOperationDescriptionBehaviors(operation, contractSurrogate);
113                         contract.Operations.Add(operation);
114                         methodFound = true;
115                         break;
116                     }
117                 }
118                 if (!methodFound)
119                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.MethodGivenInConfigNotFoundOnInterface, configMethod.ExposedMethod, iid)));
120             }
121 
122             if (contract.Operations.Count == 0)
123                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.NoneOfTheMethodsForInterfaceFoundInConfig, iid)));
124 
125             ConfigureContractDescriptionBehaviors(contract);
126             return contract;
127         }
128 
ConfigureContractDescriptionBehaviors(ContractDescription contract)129         void ConfigureContractDescriptionBehaviors(ContractDescription contract)
130         {
131             // OperationSelectorBehavior
132             contract.Behaviors.Add(new OperationSelectorBehavior());
133 
134             // ComPlusContractBehavior
135             ComPlusContractBehavior comPlusContractBehavior = new ComPlusContractBehavior(this.info);
136             contract.Behaviors.Add(comPlusContractBehavior);
137         }
138 
ConfigureOperationDescriptionBehaviors(OperationDescription operation, IDataContractSurrogate contractSurrogate)139         void ConfigureOperationDescriptionBehaviors(OperationDescription operation, IDataContractSurrogate contractSurrogate)
140         {
141             // DataContractSerializerOperationBehavior
142             DataContractSerializerOperationBehavior contractSerializer = new DataContractSerializerOperationBehavior(operation, TypeLoader.DefaultDataContractFormatAttribute);
143 
144             if (null != contractSurrogate)
145             {
146                 contractSerializer.DataContractSurrogate = contractSurrogate;
147             }
148 
149             operation.Behaviors.Add(contractSerializer);
150 
151             // OperationInvokerBehavior
152             operation.Behaviors.Add(new OperationInvokerBehavior());
153 
154             if (info.TransactionOption == TransactionOption.Supported || info.TransactionOption == TransactionOption.Required)
155             {
156                 operation.Behaviors.Add(new TransactionFlowAttribute(TransactionFlowOption.Allowed));
157             }
158 
159             // OperationBehaviorAttribute
160             OperationBehaviorAttribute operationBehaviorAttribute = new OperationBehaviorAttribute();
161             operationBehaviorAttribute.TransactionAutoComplete = true;
162             operationBehaviorAttribute.TransactionScopeRequired = false;
163             operation.Behaviors.Add(operationBehaviorAttribute);
164         }
165 
166         //
167         // Note - the code below this line a paraphrase of the SM reflection code in TypeLoader.cs
168         // Ideally we would be re-using their code, but our assumptions are too disjoint
169         // for that to be realistic at the time of writing (12/2004).
170         //
171 
CreateOperationDescription(ContractDescription contract, MethodInfo methodInfo, ComContractElement config, bool allowReferences)172         OperationDescription CreateOperationDescription(ContractDescription contract, MethodInfo methodInfo, ComContractElement config, bool allowReferences)
173         {
174             XmlName operationName = new XmlName(ServiceReflector.GetLogicalName(methodInfo));
175             XmlName returnValueName = TypeLoader.GetReturnValueName(operationName);
176 
177             if (ServiceReflector.IsBegin(methodInfo) || ServiceReflector.IsTask(methodInfo))
178             {
179                 Fx.Assert("No async operations allowed");
180 
181                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.NoAsyncOperationsAllowed());
182             }
183             if (contract.Operations.FindAll(operationName.EncodedName).Count != 0)
184             {
185                 Fx.Assert("Duplicate operation name");
186 
187                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.DuplicateOperation());
188             }
189 
190             OperationDescription operationDescription = new OperationDescription(operationName.EncodedName, contract);
191             operationDescription.SyncMethod = methodInfo;
192             operationDescription.IsInitiating = true;
193             operationDescription.IsTerminating = false;
194 
195             operationDescription.KnownTypes.Add(typeof(Array));
196             operationDescription.KnownTypes.Add(typeof(DBNull));
197             operationDescription.KnownTypes.Add(typeof(CurrencyWrapper));
198             operationDescription.KnownTypes.Add(typeof(ErrorWrapper));
199 
200             if (allowReferences)
201                 operationDescription.KnownTypes.Add(typeof(PersistStreamTypeWrapper));
202 
203             foreach (ComUdtElement udt in config.UserDefinedTypes)
204             {
205                 Type knownType;
206 
207                 Guid typeLibID = Fx.CreateGuid(udt.TypeLibID);
208 
209                 TypeCacheManager.Provider.FindOrCreateType(typeLibID, udt.TypeLibVersion, Fx.CreateGuid(udt.TypeDefID), out knownType, false);
210 
211                 this.info.AddUdt(knownType, typeLibID);
212                 operationDescription.KnownTypes.Add(knownType);
213             }
214 
215 
216             string ns = contract.Namespace;
217             XmlQualifiedName contractQName = new XmlQualifiedName(contract.Name, ns);
218 
219             string requestAction = NamingHelper.GetMessageAction(contractQName,
220                                                                  operationName.DecodedName,
221                                                                  null,
222                                                                  false);
223 
224             string responseAction = NamingHelper.GetMessageAction(contractQName,
225                                                                   operationName.DecodedName,
226                                                                   null,
227                                                                   true);
228 
229             MessageDescription inMessage = CreateIncomingMessageDescription(contract,
230                                                                             methodInfo,
231                                                                             ns,
232                                                                             requestAction,
233                                                                             allowReferences);
234 
235             MessageDescription outMessage = CreateOutgoingMessageDescription(contract,
236                                                                              methodInfo,
237                                                                              returnValueName,
238                                                                              ns,
239                                                                              responseAction,
240                                                                              allowReferences);
241 
242             operationDescription.Messages.Add(inMessage);
243             operationDescription.Messages.Add(outMessage);
244 
245             return operationDescription;
246         }
247 
CreateIncomingMessageDescription(ContractDescription contract, MethodInfo methodInfo, string ns, string action, bool allowReferences)248         MessageDescription CreateIncomingMessageDescription(ContractDescription contract,
249                                                             MethodInfo methodInfo,
250                                                             string ns,
251                                                             string action,
252                                                             bool allowReferences)
253         {
254             ParameterInfo[] parameters = ServiceReflector.GetInputParameters(methodInfo, false);
255             return CreateParameterMessageDescription(contract,
256                                                      parameters,
257                                                      null,
258                                                      null,
259                                                      null,
260                                                      methodInfo.Name,
261                                                      ns,
262                                                      action,
263                                                      MessageDirection.Input,
264                                                      allowReferences);
265         }
266 
CreateOutgoingMessageDescription(ContractDescription contract, MethodInfo methodInfo, XmlName returnValueName, string ns, string action, bool allowReferences)267         MessageDescription CreateOutgoingMessageDescription(ContractDescription contract,
268                                                             MethodInfo methodInfo,
269                                                             XmlName returnValueName,
270                                                             string ns,
271                                                             string action,
272                                                             bool allowReferences)
273         {
274             ParameterInfo[] parameters = ServiceReflector.GetOutputParameters(methodInfo, false);
275             return CreateParameterMessageDescription(contract,
276                                                      parameters,
277                                                      methodInfo.ReturnType,
278                                                      methodInfo.ReturnTypeCustomAttributes,
279                                                      returnValueName,
280                                                      methodInfo.Name,
281                                                      ns,
282                                                      action,
283                                                      MessageDirection.Output,
284                                                      allowReferences);
285         }
286 
CreateParameterMessageDescription(ContractDescription contract, ParameterInfo[] parameters, Type returnType, ICustomAttributeProvider returnCustomAttributes, XmlName returnValueName, string methodName, string ns, string action, MessageDirection direction, bool allowReferences)287         MessageDescription CreateParameterMessageDescription(ContractDescription contract,
288                                                              ParameterInfo[] parameters,
289                                                              Type returnType,
290                                                              ICustomAttributeProvider returnCustomAttributes,
291                                                              XmlName returnValueName,
292                                                              string methodName,
293                                                              string ns,
294                                                              string action,
295                                                              MessageDirection direction,
296                                                              bool allowReferences)
297         {
298             MessageDescription messageDescription = new MessageDescription(action, direction);
299             messageDescription.Body.WrapperNamespace = ns;
300 
301             for (int index = 0; index < parameters.Length; index++)
302             {
303                 ParameterInfo parameter = parameters[index];
304                 Type parameterType = TypeLoader.GetParameterType(parameter);
305 
306                 if (!ComPlusTypeValidator.IsValidParameter(parameterType, parameter, allowReferences))
307                 {
308                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.InvalidWebServiceParameter,
309                                                                 parameter.Name,
310                                                                 parameterType.Name,
311                                                                 methodName,
312                                                                 contract.Name)));
313                 }
314 
315                 MessagePartDescription messagePart = CreateMessagePartDescription(parameterType,
316                                                                                   new XmlName(parameter.Name),
317                                                                                   ns,
318                                                                                   index);
319                 messageDescription.Body.Parts.Add(messagePart);
320             }
321 
322             XmlName xmlName = new XmlName(methodName);
323             if (returnType == null)
324             {
325                 messageDescription.Body.WrapperName = xmlName.EncodedName;
326             }
327             else
328             {
329                 messageDescription.Body.WrapperName = TypeLoader.GetBodyWrapperResponseName(xmlName).EncodedName;
330 
331                 if (!ComPlusTypeValidator.IsValidParameter(returnType, returnCustomAttributes, allowReferences))
332                 {
333                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.InvalidWebServiceReturnValue,
334                                                                 returnType.Name,
335                                                                 methodName,
336                                                                 contract.Name)));
337                 }
338 
339                 MessagePartDescription messagePart = CreateMessagePartDescription(returnType,
340                                                                                   returnValueName,
341                                                                                   ns,
342                                                                                   0);
343                 messageDescription.Body.ReturnValue = messagePart;
344             }
345 
346             return messageDescription;
347         }
348 
CreateMessagePartDescription(Type bodyType, XmlName name, string ns, int index)349         MessagePartDescription CreateMessagePartDescription(Type bodyType,
350                                                             XmlName name,
351                                                             string ns,
352                                                             int index)
353         {
354             MessagePartDescription partDescription = new MessagePartDescription(name.EncodedName, ns);
355             partDescription.SerializationPosition = index;
356             partDescription.MemberInfo = null;
357             partDescription.Type = bodyType;
358             partDescription.Index = index;
359             return partDescription;
360         }
361 
ResolveIMetadataExchangeToContract()362         ContractDescription ResolveIMetadataExchangeToContract()
363         {
364             // Use ServiceModel's TypeLoader to load the IMetadataExchange contract
365             TypeLoader typeLoader = new TypeLoader();
366             return typeLoader.LoadContractDescription(typeof(IMetadataExchange));
367         }
368 
ResolveContract(string contractTypeString)369         public ContractDescription ResolveContract(string contractTypeString)
370         {
371             Guid iid;
372             if (ServiceMetadataBehavior.MexContractName == contractTypeString)
373                 iid = typeof(IMetadataExchange).GUID;
374             else
375             {
376                 if (!DiagnosticUtility.Utility.TryCreateGuid(contractTypeString, out iid))
377                 {
378                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(SR.GetString(SR.ContractTypeNotAnIID, contractTypeString)));
379                 }
380 
381                 ValidateInterface(iid);
382             }
383 
384             // Check our cache to see if we already have one
385             ContractDescription contract;
386             if (this.contracts.TryGetValue(iid, out contract))
387             {
388                 return contract;
389             }
390 
391             // If this is not IMetadataExchange continue
392             if (iid != typeof(IMetadataExchange).GUID)
393             {
394                 Type type;
395                 // Generate a managed type corresponding to the interface in question
396                 try
397                 {
398                     this.interfaceResolver.FindOrCreateType(this.info.ServiceType, iid, out type, false, true);
399                 }
400                 catch (InvalidOperationException e)
401                 {
402                     throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Error.ListenerInitFailed(e.Message));
403                 }
404 
405                 contract = CreateContractDescriptionInternal(iid, type);
406             }
407             else
408                 contract = ResolveIMetadataExchangeToContract();
409             contracts.Add(iid, contract);
410 
411             ComPlusServiceHostTrace.Trace(TraceEventType.Verbose, TraceCode.ComIntegrationServiceHostCreatedServiceContract,
412                                 SR.TraceCodeComIntegrationServiceHostCreatedServiceContract, this.info, contract);
413 
414             return contract;
415         }
416     }
417 }
418