/*
* 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
}
}