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