|
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#pragma warning disable 1634, 1691
namespace System.ServiceModel.ComIntegration
{
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Runtime;
using System.Runtime.InteropServices;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Diagnostics;
using System.ServiceModel.Dispatcher;
using System.Threading;
using System.Xml;
using System.Xml.Schema;
using ConfigNS = System.ServiceModel.Configuration;
using DiscoNS = System.Web.Services.Discovery;
using WsdlNS = System.Web.Services.Description;
class MexServiceChannelBuilder : IProxyCreator, IProvideChannelBuilderSettings
{
ContractDescription contractDescription = null;
ServiceChannelFactory serviceChannelFactory = null;
Dictionary<MonikerHelper.MonikerAttribute, string> propertyTable;
// Double-checked locking pattern requires volatile for read/write synchronization
volatile ServiceChannel serviceChannel = null;
ServiceEndpoint serviceEndpoint = null;
KeyedByTypeCollection<IEndpointBehavior> behaviors = new KeyedByTypeCollection<IEndpointBehavior>();
bool useXmlSerializer = false;
//Suppressing PreSharp warning that property get methods should not throw
#pragma warning disable 6503
ServiceChannelFactory IProvideChannelBuilderSettings.ServiceChannelFactoryReadWrite
{
get
{
if (serviceChannel != null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new COMException(SR.GetString(SR.TooLate), HR.RPC_E_TOO_LATE));
return serviceChannelFactory;
}
}
#pragma warning restore 6503
ServiceChannel IProvideChannelBuilderSettings.ServiceChannel
{
get
{
return CreateChannel();
}
}
ServiceChannelFactory IProvideChannelBuilderSettings.ServiceChannelFactoryReadOnly
{
get
{
return serviceChannelFactory;
}
}
//Suppressing PreSharp warning that property get methods should not throw
#pragma warning disable 6503
KeyedByTypeCollection<IEndpointBehavior> IProvideChannelBuilderSettings.Behaviors
{
get
{
if (serviceChannel != null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new COMException(SR.GetString(SR.TooLate), HR.RPC_E_TOO_LATE));
return behaviors;
}
}
#pragma warning restore 6503
void IDisposable.Dispose()
{
if (serviceChannel != null)
serviceChannel.Close();
}
internal MexServiceChannelBuilder(Dictionary<MonikerHelper.MonikerAttribute, string> propertyTable)
{
this.propertyTable = propertyTable;
DoMex();
}
ServiceChannel CreateChannel()
{
if (serviceChannel == null)
{
lock (this)
{
if (serviceChannel == null)
{
try
{
if (serviceChannelFactory == null)
{
FaultInserviceChannelFactory();
}
if (serviceChannelFactory == null)
{
throw Fx.AssertAndThrow("ServiceChannelFactory cannot be null at this point");
}
serviceChannelFactory.Open();
if (serviceEndpoint == null)
{
throw Fx.AssertAndThrow("ServiceEndpoint cannot be null");
}
ServiceChannel localChannel = serviceChannelFactory.CreateServiceChannel(new EndpointAddress(serviceEndpoint.Address.Uri, serviceEndpoint.Address.Identity, serviceEndpoint.Address.Headers), serviceEndpoint.Address.Uri);
serviceChannel = localChannel;
ComPlusChannelCreatedTrace.Trace(TraceEventType.Verbose, TraceCode.ComIntegrationChannelCreated,
SR.TraceCodeComIntegrationChannelCreated, serviceEndpoint.Address.Uri, contractDescription.ContractType);
if (serviceChannel == null)
{
throw Fx.AssertAndThrow("serviceProxy MUST derive from RealProxy");
}
}
finally
{
if ((serviceChannel == null) && (serviceChannelFactory != null))
{
serviceChannelFactory.Close();
}
}
}
}
}
return serviceChannel;
}
private ServiceChannelFactory CreateServiceChannelFactory()
{
serviceChannelFactory = ServiceChannelFactory.BuildChannelFactory(serviceEndpoint) as ServiceChannelFactory;
if (serviceChannelFactory == null)
{
throw Fx.AssertAndThrow("We should get a ServiceChannelFactory back");
}
FixupProxyBehavior();
return serviceChannelFactory;
}
void FaultInserviceChannelFactory()
{
if (propertyTable == null)
{
throw Fx.AssertAndThrow("PropertyTable should not be null");
}
foreach (IEndpointBehavior behavior in behaviors)
serviceEndpoint.Behaviors.Add(behavior);
serviceChannelFactory = CreateServiceChannelFactory();
}
void FixupProxyBehavior()
{
ClientOperation operation = null;
if (useXmlSerializer)
XmlSerializerOperationBehavior.AddBehaviors(contractDescription);
foreach (OperationDescription opDesc in contractDescription.Operations)
{
operation = serviceChannelFactory.ClientRuntime.Operations[opDesc.Name];
operation.SerializeRequest = true;
operation.DeserializeReply = true;
if (useXmlSerializer)
operation.Formatter = XmlSerializerOperationBehavior.CreateOperationFormatter(opDesc);
else
operation.Formatter = new DataContractSerializerOperationFormatter(opDesc, TypeLoader.DefaultDataContractFormatAttribute, null);
}
}
private void DoMex()
{
string mexAddress;
string mexBindingSectionName;
string mexBindingConfiguration;
string contract;
string contractNamespace;
string binding;
string bindingNamespace;
string address;
string spnIdentity = null;
string upnIdentity = null;
string dnsIdentity = null;
string mexSpnIdentity = null;
string mexUpnIdentity = null;
string mexDnsIdentity = null;
string serializer = null;
EndpointIdentity identity = null;
EndpointIdentity mexIdentity = null;
propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.Contract, out contract);
propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.ContractNamespace, out contractNamespace);
propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.BindingNamespace, out bindingNamespace);
propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.Binding, out binding);
propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.MexAddress, out mexAddress);
propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.MexBinding, out mexBindingSectionName);
propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.MexBindingConfiguration, out mexBindingConfiguration);
propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.Address, out address);
propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.SpnIdentity, out spnIdentity);
propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.UpnIdentity, out upnIdentity);
propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.DnsIdentity, out dnsIdentity);
propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.MexSpnIdentity, out mexSpnIdentity);
propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.MexUpnIdentity, out mexUpnIdentity);
propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.MexDnsIdentity, out mexDnsIdentity);
propertyTable.TryGetValue(MonikerHelper.MonikerAttribute.Serializer, out serializer);
if (string.IsNullOrEmpty(mexAddress))
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerMexAddressNotSpecified)));
if (!string.IsNullOrEmpty(mexSpnIdentity))
{
if ((!string.IsNullOrEmpty(mexUpnIdentity)) || (!string.IsNullOrEmpty(mexDnsIdentity)))
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerIncorrectServerIdentityForMex)));
mexIdentity = EndpointIdentity.CreateSpnIdentity(mexSpnIdentity);
}
else if (!string.IsNullOrEmpty(mexUpnIdentity))
{
if ((!string.IsNullOrEmpty(mexSpnIdentity)) || (!string.IsNullOrEmpty(mexDnsIdentity)))
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerIncorrectServerIdentityForMex)));
mexIdentity = EndpointIdentity.CreateUpnIdentity(mexUpnIdentity);
}
else if (!string.IsNullOrEmpty(mexDnsIdentity))
{
if ((!string.IsNullOrEmpty(mexSpnIdentity)) || (!string.IsNullOrEmpty(mexUpnIdentity)))
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerIncorrectServerIdentityForMex)));
mexIdentity = EndpointIdentity.CreateDnsIdentity(mexDnsIdentity);
}
else
mexIdentity = null;
if (string.IsNullOrEmpty(address))
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerAddressNotSpecified)));
if (string.IsNullOrEmpty(contract))
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerContractNotSpecified)));
if (string.IsNullOrEmpty(binding))
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerBindingNotSpecified)));
if (string.IsNullOrEmpty(bindingNamespace))
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerBindingNamespacetNotSpecified)));
if (!string.IsNullOrEmpty(spnIdentity))
{
if ((!string.IsNullOrEmpty(upnIdentity)) || (!string.IsNullOrEmpty(dnsIdentity)))
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerIncorrectServerIdentity)));
identity = EndpointIdentity.CreateSpnIdentity(spnIdentity);
}
else if (!string.IsNullOrEmpty(upnIdentity))
{
if ((!string.IsNullOrEmpty(spnIdentity)) || (!string.IsNullOrEmpty(dnsIdentity)))
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerIncorrectServerIdentity)));
identity = EndpointIdentity.CreateUpnIdentity(upnIdentity);
}
else if (!string.IsNullOrEmpty(dnsIdentity))
{
if ((!string.IsNullOrEmpty(spnIdentity)) || (!string.IsNullOrEmpty(upnIdentity)))
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerIncorrectServerIdentity)));
identity = EndpointIdentity.CreateDnsIdentity(dnsIdentity);
}
else
identity = null;
MetadataExchangeClient resolver = null;
EndpointAddress mexEndpointAddress = new EndpointAddress(new Uri(mexAddress), mexIdentity);
if (!string.IsNullOrEmpty(mexBindingSectionName))
{
Binding mexBinding = null;
try
{
mexBinding = ConfigLoader.LookupBinding(mexBindingSectionName, mexBindingConfiguration);
}
catch (System.Configuration.ConfigurationErrorsException)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MexBindingNotFoundInConfig, mexBindingSectionName)));
}
if (null == mexBinding)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MexBindingNotFoundInConfig, mexBindingSectionName)));
resolver = new MetadataExchangeClient(mexBinding);
}
else if (string.IsNullOrEmpty(mexBindingConfiguration))
resolver = new MetadataExchangeClient(mexEndpointAddress);
else
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerMexBindingSectionNameNotSpecified)));
if (null != mexIdentity)
{
// To disable AllowNtlm warning.
#pragma warning disable 618
resolver.SoapCredentials.Windows.AllowNtlm = false;
#pragma warning restore 618
}
bool removeXmlSerializerImporter = false;
if (!String.IsNullOrEmpty(serializer))
{
if ("xml" != serializer && "datacontract" != serializer)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerIncorectSerializer)));
if ("xml" == serializer)
useXmlSerializer = true;
else
removeXmlSerializerImporter = true; // specifying datacontract will explicitly remove the Xml importer
// if this parameter is not set we will simply use indigo defaults
}
ServiceEndpoint endpoint = null;
ServiceEndpointCollection serviceEndpointsRetrieved = null;
WsdlImporter importer;
try
{
MetadataSet metadataSet = resolver.GetMetadata(mexEndpointAddress);
if (useXmlSerializer)
importer = CreateXmlSerializerImporter(metadataSet);
else
{
if (removeXmlSerializerImporter)
importer = CreateDataContractSerializerImporter(metadataSet);
else
importer = new WsdlImporter(metadataSet);
}
serviceEndpointsRetrieved = this.ImportWsdlPortType(new XmlQualifiedName(contract, contractNamespace), importer);
ComPlusMexChannelBuilderMexCompleteTrace.Trace(TraceEventType.Verbose, TraceCode.ComIntegrationMexMonikerMetadataExchangeComplete, SR.TraceCodeComIntegrationMexMonikerMetadataExchangeComplete, serviceEndpointsRetrieved);
}
catch (Exception e)
{
if (Fx.IsFatal(e))
throw;
if (UriSchemeSupportsDisco(mexEndpointAddress.Uri))
{
try
{
DiscoNS.DiscoveryClientProtocol discoClient = new DiscoNS.DiscoveryClientProtocol();
discoClient.UseDefaultCredentials = true;
discoClient.AllowAutoRedirect = true;
discoClient.DiscoverAny(mexEndpointAddress.Uri.AbsoluteUri);
discoClient.ResolveAll();
MetadataSet metadataSet = new MetadataSet();
foreach (object document in discoClient.Documents.Values)
{
AddDocumentToSet(metadataSet, document);
}
if (useXmlSerializer)
importer = CreateXmlSerializerImporter(metadataSet);
else
{
if (removeXmlSerializerImporter)
importer = CreateDataContractSerializerImporter(metadataSet);
else
importer = new WsdlImporter(metadataSet);
}
serviceEndpointsRetrieved = this.ImportWsdlPortType(new XmlQualifiedName(contract, contractNamespace), importer);
ComPlusMexChannelBuilderMexCompleteTrace.Trace(TraceEventType.Verbose, TraceCode.ComIntegrationMexMonikerMetadataExchangeComplete, SR.TraceCodeComIntegrationMexMonikerMetadataExchangeComplete, serviceEndpointsRetrieved);
}
catch (Exception ex)
{
if (Fx.IsFatal(ex))
throw;
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerFailedToDoMexRetrieve, ex.Message)));
}
}
else
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerFailedToDoMexRetrieve, e.Message)));
}
if (serviceEndpointsRetrieved.Count == 0)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerContractNotFoundInRetreivedMex)));
foreach (ServiceEndpoint retrievedEndpoint in serviceEndpointsRetrieved)
{
Binding bindingSelected = retrievedEndpoint.Binding;
if ((bindingSelected.Name == binding) && (bindingSelected.Namespace == bindingNamespace))
{
endpoint = retrievedEndpoint;
break;
}
}
if (endpoint == null)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new MonikerSyntaxException(SR.GetString(SR.MonikerNoneOfTheBindingMatchedTheSpecifiedBinding)));
contractDescription = endpoint.Contract;
this.serviceEndpoint = new ServiceEndpoint(contractDescription, endpoint.Binding, new EndpointAddress(new Uri(address), identity, (AddressHeaderCollection)null));
ComPlusMexChannelBuilderTrace.Trace(TraceEventType.Verbose, TraceCode.ComIntegrationMexChannelBuilderLoaded,
SR.TraceCodeComIntegrationMexChannelBuilderLoaded, endpoint.Contract, endpoint.Binding, address);
}
static bool UriSchemeSupportsDisco(Uri serviceUri)
{
return (serviceUri.Scheme == Uri.UriSchemeHttp) || (serviceUri.Scheme == Uri.UriSchemeHttps);
}
void AddDocumentToSet(MetadataSet metadataSet, object document)
{
WsdlNS.ServiceDescription wsdl = document as WsdlNS.ServiceDescription;
XmlSchema schema = document as XmlSchema;
XmlElement xmlDoc = document as XmlElement;
if (wsdl != null)
{
metadataSet.MetadataSections.Add(MetadataSection.CreateFromServiceDescription(wsdl));
}
else if (schema != null)
{
metadataSet.MetadataSections.Add(MetadataSection.CreateFromSchema(schema));
}
else if (xmlDoc != null && MetadataSection.IsPolicyElement(xmlDoc))
{
metadataSet.MetadataSections.Add(MetadataSection.CreateFromPolicy(xmlDoc, null));
}
else
{
MetadataSection mexDoc = new MetadataSection();
mexDoc.Metadata = document;
metadataSet.MetadataSections.Add(mexDoc);
}
}
public WsdlImporter CreateDataContractSerializerImporter(MetadataSet metaData)
{
Collection<IWsdlImportExtension> wsdlImportExtensions = ConfigNS.ClientSection.GetSection().Metadata.LoadWsdlImportExtensions();
for (int i = 0; i < wsdlImportExtensions.Count; i++)
{
if (wsdlImportExtensions[i].GetType() == typeof(XmlSerializerMessageContractImporter))
wsdlImportExtensions.RemoveAt(i);
}
WsdlImporter importer = new WsdlImporter(metaData, null, wsdlImportExtensions);
return importer;
}
public WsdlImporter CreateXmlSerializerImporter(MetadataSet metaData)
{
Collection<IWsdlImportExtension> wsdlImportExtensions = ConfigNS.ClientSection.GetSection().Metadata.LoadWsdlImportExtensions();
for (int i = 0; i < wsdlImportExtensions.Count; i++)
{
if (wsdlImportExtensions[i].GetType() == typeof(DataContractSerializerMessageContractImporter))
wsdlImportExtensions.RemoveAt(i);
}
WsdlImporter importer = new WsdlImporter(metaData, null, wsdlImportExtensions);
return importer;
}
ServiceEndpointCollection ImportWsdlPortType(XmlQualifiedName portTypeQName, WsdlImporter importer)
{
foreach (WsdlNS.ServiceDescription wsdl in importer.WsdlDocuments)
{
if (wsdl.TargetNamespace == portTypeQName.Namespace)
{
WsdlNS.PortType wsdlPortType = wsdl.PortTypes[portTypeQName.Name];
if (wsdlPortType != null)
{
ServiceEndpointCollection endpoints = importer.ImportEndpoints(wsdlPortType);
return endpoints;
}
}
}
return new ServiceEndpointCollection();
}
ComProxy IProxyCreator.CreateProxy(IntPtr outer, ref Guid riid)
{
IntPtr inner = IntPtr.Zero;
if (riid != InterfaceID.idIDispatch)
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidCastException(SR.GetString(SR.NoInterface, riid)));
if (contractDescription == null)
{
throw Fx.AssertAndThrow("ContractDescription should not be null at this point");
}
return DispatchProxy.Create(outer, contractDescription, this);
}
bool IProxyCreator.SupportsErrorInfo(ref Guid riid)
{
if (riid != InterfaceID.idIDispatch)
return false;
else
return true;
}
bool IProxyCreator.SupportsDispatch()
{
return true;
}
bool IProxyCreator.SupportsIntrinsics()
{
return true;
}
}
}
|