/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.IO; using System.ServiceModel; using System.ServiceModel.Channels; using System.ServiceModel.Description; using System.Web.Services.Description; using System.Xml; namespace Apache.NMS.WCF { /// /// Key class to specify the custom transport class and its schema. /// /// /// /// Its key role in the WCF is to be the 'factory of factories'. It determines what shape /// the channel will be. In this case by returning channel factory (for the client) that returns an /// or , and a channel listener /// (for the server) that returns an or , /// this class determines that this implementation is a datagram 'shape'. /// /// /// The request/reply channel shape is not supported by WCF. /// /// public class NmsTransportBindingElement : TransportBindingElement, IWsdlExportExtension, IPolicyExportExtension { #region Constructors /// /// Initializes a new instance of the class. /// public NmsTransportBindingElement() { } /// /// Initializes a new instance of the class. /// /// The element. protected NmsTransportBindingElement(NmsTransportBindingElement element) { ManualAddressing = element.ManualAddressing; Destination = element.Destination; DestinationType = element.DestinationType; } #endregion #region Public properties /// /// Gets or sets the name of the message destination. /// /// The name of the message destination. public string Destination { get { return _destination; } set { _destination = value; } } /// /// Gets or sets the type of the message destination. /// /// The type of the message destination (may be either queue or topic, and temporary /// or permanent versions of either). public DestinationType DestinationType { get { return _destinationType; } set { _destinationType = value; } } #endregion #region Public methods /// /// Determines whether this instance can build a channel factory in the specified context. /// Only implementations of and are supported. /// /// The type of the channel. /// The context. /// /// true if the requested channel factory can be built; otherwise, false. /// public override bool CanBuildChannelFactory(BindingContext context) { return (typeof(TChannel) == typeof(IOutputChannel) || typeof(TChannel) == typeof(IOutputSessionChannel)); } /// /// Determines whether this instance can build a channel listener in the specified context. /// Only implementations of and are supported. /// /// The type of the channel. /// The context. /// /// true if the requested channel listener can be built; otherwise, false. /// /// the requested channel does not implement . public override bool CanBuildChannelListener(BindingContext context) { return (typeof(TChannel) == typeof(IInputChannel) || typeof(TChannel) == typeof(IInputSessionChannel)); } /// /// Builds the channel factory. /// /// The type of the channel. /// The context. /// /// the requested channel does not implement . public override IChannelFactory BuildChannelFactory(BindingContext context) { if(context == null) { throw new ArgumentNullException("context"); } if(!CanBuildChannelFactory(context)) { throw new ArgumentException(String.Format("Unsupported channel type: {0}.", typeof(TChannel).Name)); } return (IChannelFactory) new NmsChannelFactory(this, context); } /// /// Builds the channel listener. /// /// The type of the channel. /// The context. /// public override IChannelListener BuildChannelListener(BindingContext context) { if(context == null) { throw new ArgumentNullException("context"); } if(!CanBuildChannelListener(context)) { throw new ArgumentException(String.Format("Unsupported channel type: {0}.", typeof(TChannel).Name)); } if (typeof(TChannel) == typeof(IInputSessionChannel)) { return (IChannelListener)new NmsInputSessionChannelListener(this, context); } return (IChannelListener) new NmsInputChannelListener(this, context); } /// /// Gets the URI scheme for the transport. /// /// /// Returns the URI scheme for the transport, which varies depending on what derived class implements this method. public override string Scheme { get { return NmsConstants.TransportScheme; } } /// /// When overridden in a derived class, returns a copy of the binding element object. /// /// /// A object that is a deep clone of the original. /// public override BindingElement Clone() { return new NmsTransportBindingElement(this); } /// /// Gets the property. /// /// /// The context. /// public override T GetProperty(BindingContext context) { if(context == null) { throw new ArgumentNullException("context"); } return context.GetInnerProperty(); } #endregion #region Implementation of IWsdlExportExtension /// /// Writes custom Web Services Description Language (WSDL) elements into the generated WSDL for a contract. /// /// The that exports the contract information. /// Provides mappings from exported WSDL elements to the contract description. public void ExportContract(WsdlExporter exporter, WsdlContractConversionContext context) { } /// /// Writes custom Web Services Description Language (WSDL) elements into the generated WSDL for an endpoint. /// /// The that exports the endpoint information. /// Provides mappings from exported WSDL elements to the endpoint description. public void ExportEndpoint(WsdlExporter exporter, WsdlEndpointConversionContext context) { BindingElementCollection bindingElements = context.Endpoint.Binding.CreateBindingElements(); MessageEncodingBindingElement encodingBindingElement = bindingElements.Find() ?? new TextMessageEncodingBindingElement(); if(context.WsdlPort != null) { AddAddressToWsdlPort(context.WsdlPort, context.Endpoint.Address, encodingBindingElement.MessageVersion.Addressing); } } #endregion #region Implementation of IPolicyExportExtension /// /// Implement to include for exporting a custom policy assertion about bindings. /// /// The that you can use to modify the exporting process. /// The that you can use to insert your custom policy assertion. public void ExportPolicy(MetadataExporter exporter, PolicyConversionContext context) { if(exporter == null) { throw new ArgumentNullException("exporter"); } if(context == null) { throw new ArgumentNullException("context"); } bool createdNew = false; MessageEncodingBindingElement encodingBindingElement = context.BindingElements.Find(); if(encodingBindingElement == null) { createdNew = true; encodingBindingElement = new TextMessageEncodingBindingElement(); } if(createdNew && encodingBindingElement is IPolicyExportExtension) { ((IPolicyExportExtension) encodingBindingElement).ExportPolicy(exporter, context); } AddWSAddressingAssertion(context, encodingBindingElement.MessageVersion.Addressing); } /// /// Adds the address to WSDL port. /// /// The WSDL port. /// The endpoint address. /// The addressing. private static void AddAddressToWsdlPort(Port wsdlPort, EndpointAddress endpointAddress, AddressingVersion addressing) { if(addressing == AddressingVersion.None) { return; } MemoryStream memoryStream = new MemoryStream(); XmlWriter xmlWriter = XmlWriter.Create(memoryStream); xmlWriter.WriteStartElement("temp"); if(addressing == AddressingVersion.WSAddressing10) { xmlWriter.WriteAttributeString("xmlns", "wsa10", null, AddressingVersions.WSAddressing10NameSpace); } else if(addressing == AddressingVersion.WSAddressingAugust2004) { xmlWriter.WriteAttributeString("xmlns", "wsa", null, AddressingVersions.WSAddressingAugust2004NameSpace); } else { throw new InvalidOperationException("This addressing version is not supported:\n" + addressing); } endpointAddress.WriteTo(addressing, xmlWriter); xmlWriter.WriteEndElement(); xmlWriter.Flush(); memoryStream.Seek(0, SeekOrigin.Begin); XmlReader xmlReader = XmlReader.Create(memoryStream); xmlReader.MoveToContent(); XmlElement endpointReference = (XmlElement) XmlDoc.ReadNode(xmlReader).ChildNodes[0]; wsdlPort.Extensions.Add(endpointReference); } /// /// Adds the WS addressing assertion. /// /// The context. /// The addressing. static void AddWSAddressingAssertion(PolicyConversionContext context, AddressingVersion addressing) { XmlElement addressingAssertion; if(addressing == AddressingVersion.WSAddressing10) { addressingAssertion = XmlDoc.CreateElement("wsaw", "UsingAddressing", "http://www.w3.org/2006/05/addressing/wsdl"); } else if(addressing == AddressingVersion.WSAddressingAugust2004) { addressingAssertion = XmlDoc.CreateElement("wsap", "UsingAddressing", AddressingVersions.WSAddressingAugust2004NameSpace + "/policy"); } else if(addressing == AddressingVersion.None) { // do nothing addressingAssertion = null; } else { throw new InvalidOperationException("This addressing version is not supported:\n" + addressing); } if(addressingAssertion != null) { context.GetBindingAssertions().Add(addressingAssertion); } } /// /// Gets the XML doc. /// static XmlDocument XmlDoc { get { if(_xmlDocument == null) { NameTable nameTable = new NameTable(); nameTable.Add("Policy"); nameTable.Add("All"); nameTable.Add("ExactlyOne"); nameTable.Add("PolicyURIs"); nameTable.Add("Id"); nameTable.Add("UsingAddressing"); nameTable.Add("UsingAddressing"); _xmlDocument = new XmlDocument(nameTable); } return _xmlDocument; } } private static XmlDocument _xmlDocument; #endregion #region Private members private string _destination; private DestinationType _destinationType; #endregion } }