Commit 975e4e97 authored by Mark van Renswoude's avatar Mark van Renswoude

Implemented argument templates in XML

parent 2f9526d9
<?xml version="1.0" encoding="UTF-8"?>
<Topology xmlns="http://schema.x2software.net/RabbitMetaQueue">
<Templates>
<Template name="default-deadletter" type="Arguments">
<Arguments template="loop">
<Argument name="x-dead-letter-exchange">argh</Argument>
</Arguments>
</Template>
<Template name="loop" type="Arguments">
<Arguments template="default-deadletter" />
</Template>
</Templates>
<Queues>
<Queue name="queue1">
<Arguments template="loop" />
</Queue>
<Queue name="queue2">
<Arguments template="default-deadletter">
<Argument name="x-extend-template">test</Argument>
</Arguments>
</Queue>
</Queues>
</Topology>
<?xml version="1.0" encoding="UTF-8"?>
<Topology xmlns="http://schema.x2software.net/RabbitMetaQueue">
<Templates>
<Template name="default-deadletter" type="Arguments">
<Arguments>
<Argument name="x-dead-letter-exchange">argh</Argument>
</Arguments>
</Template>
</Templates>
<Queues>
<Queue name="queue1">
<Arguments template="invalid" />
</Queue>
<Queue name="queue2">
<Arguments template="default-deadletter">
<Argument name="x-extend-template">test</Argument>
</Arguments>
</Queue>
</Queues>
</Topology>
<?xml version="1.0" encoding="UTF-8"?>
<Topology xmlns="http://schema.x2software.net/RabbitMetaQueue">
<Templates>
<Template name="default-deadletter" type="Arguments">
<Arguments>
<Argument name="x-dead-letter-exchange">argh</Argument>
</Arguments>
</Template>
</Templates>
<Queues>
<Queue name="queue1">
<Arguments template="default-deadletter" />
</Queue>
<Queue name="queue2">
<Arguments template="default-deadletter">
<Argument name="x-extend-template">test</Argument>
</Arguments>
</Queue>
</Queues>
</Topology>
<?xml version="1.0" encoding="UTF-8"?>
<Topology xmlns="http://schema.x2software.net/RabbitMetaQueue" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schema.x2software.net/RabbitMetaQueue ..\Schema\Topology.xsd">
<Topology xmlns="http://schema.x2software.net/RabbitMetaQueue">
<Exchanges>
<Exchange name="RMetaQ.test1" type="Topic" />
<Exchange name="RMetaQ.test2" type="Topic" />
......
......@@ -27,6 +27,42 @@ namespace RabbitMetaQueue.Tests.Infrastructure
}
[Test]
public void ArgumentTemplate()
{
var topology = Parse("ArgumentTemplate.xml");
Assert.AreEqual(2, topology.Queues.Count, "Queue count");
TestQueue(topology.Queues[0], "queue1");
TestQueue(topology.Queues[1], "queue2");
var arguments = topology.Queues[0].Arguments;
Assert.AreEqual(1, arguments.Count, "Queue1 Argument Count");
Assert.IsTrue(arguments.ContainsKey("x-dead-letter-exchange"), "Dead Letter Exchange argument");
Assert.AreEqual("argh", arguments["x-dead-letter-exchange"], "Dead Letter Exchange value");
arguments = topology.Queues[1].Arguments;
Assert.AreEqual(2, arguments.Count, "Queue2 Argument Count");
Assert.IsTrue(arguments.ContainsKey("x-dead-letter-exchange"), "Dead Letter Exchange argument");
Assert.AreEqual("argh", arguments["x-dead-letter-exchange"], "Dead Letter Exchange value");
Assert.IsTrue(arguments.ContainsKey("x-extend-template"), "Extra argument");
Assert.AreEqual("test", arguments["x-extend-template"], "Extra value");
}
[Test]
public void ArgumentTemplateInvalidName()
{
Assert.Catch(typeof(XmlTopologyReader.TemplateException), () => Parse("ArgumentTemplate-InvalidName.xml"));
}
[Test]
public void ArgumentTemplateCircularReference()
{
Assert.Catch(typeof(XmlTopologyReader.TemplateException), () => Parse("ArgumentTemplate-CircularReference.xml"));
}
private static void TestExchange(Exchange exchange, string expectedName, ExchangeType expectedType, bool expectedDurable = true)
{
Assert.AreEqual(expectedName, exchange.Name, "Exchange Name");
......
......@@ -59,10 +59,18 @@
<Name>RabbitMetaQueue</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<ItemGroup>
<EmbeddedResource Include="Data\ArgumentTemplate.xml" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Data\BasicDefinition.xml" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Data\ArgumentTemplate-InvalidName.xml" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Data\ArgumentTemplate-CircularReference.xml" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition="Exists('$(SolutionDir)\.nuget\NuGet.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
......
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Xml.Serialization;
using RabbitMetaQueue.Resources;
using RabbitMetaQueue.Schema;
namespace RabbitMetaQueue.Infrastructure
{
public class XmlTopologyReader
{
public class TemplateException : Exception
{
public TemplateException(string message) : base(message) { }
}
private Schema.Topology definition = null;
public Model.Topology Parse(string filename)
{
using (var stream = new FileStream(filename, FileMode.Open, FileAccess.Read))
......@@ -18,7 +31,8 @@ namespace RabbitMetaQueue.Infrastructure
public Model.Topology Parse(Stream stream)
{
var serializer = new XmlSerializer(typeof(Schema.Topology));
var definition = (Schema.Topology)serializer.Deserialize(stream);
definition = (Schema.Topology)serializer.Deserialize(stream);
var model = new Model.Topology();
if (definition.Exchanges != null)
......@@ -31,7 +45,7 @@ namespace RabbitMetaQueue.Infrastructure
}
private void MapExchanges(IEnumerable<Schema.Exchange> definition, List<Model.Exchange> model)
private void MapExchanges(IEnumerable<Schema.Exchange> exchanges, List<Model.Exchange> model)
{
var exchangeTypeMap = new Dictionary<Schema.ExchangeType, Model.ExchangeType>
{
......@@ -42,7 +56,7 @@ namespace RabbitMetaQueue.Infrastructure
};
foreach (var sourceExchange in definition)
foreach (var sourceExchange in exchanges)
{
var destExchange = new Model.Exchange
{
......@@ -59,9 +73,9 @@ namespace RabbitMetaQueue.Infrastructure
}
private void MapQueues(IEnumerable<Schema.Queue> definition, List<Model.Queue> model)
private void MapQueues(IEnumerable<Schema.Queue> queues, List<Model.Queue> model)
{
foreach (var sourceQueue in definition)
foreach (var sourceQueue in queues)
{
var destQueue = new Model.Queue
{
......@@ -80,9 +94,9 @@ namespace RabbitMetaQueue.Infrastructure
}
private void MapBindings(IEnumerable<Schema.Binding> definition, List<Model.Binding> model)
private void MapBindings(IEnumerable<Schema.Binding> bindings, List<Model.Binding> model)
{
foreach (var sourceBinding in definition)
foreach (var sourceBinding in bindings)
{
var destBinding = new Model.Binding
{
......@@ -98,10 +112,46 @@ namespace RabbitMetaQueue.Infrastructure
}
private void MapArguments(IEnumerable<Schema.Argument> definition, Model.Arguments model)
private void MapArguments(Arguments arguments, Model.Arguments model)
{
foreach (var argument in definition)
model.Add(argument.name, argument.Value);
MapArguments(arguments, model, new HashSet<string>());
}
private void MapArguments(Arguments arguments, Model.Arguments model, HashSet<string> stackTrace)
{
if (!String.IsNullOrEmpty(arguments.template))
{
if (stackTrace.Contains(arguments.template, StringComparer.InvariantCulture))
throw new TemplateException(String.Format(Strings.XmlTemplateCircularReference, arguments.template));
var template = GetTemplate(arguments.template, TemplateType.Arguments);
stackTrace.Add(arguments.template);
MapArguments(template.Item, model, stackTrace);
}
if (arguments.Argument != null)
{
foreach (var argument in arguments.Argument)
model.Add(argument.name, argument.Value);
}
}
private Template GetTemplate(string name, TemplateType type)
{
if (definition.Templates == null)
throw new TemplateException(Strings.XmlNoTemplatesElement);
var template = definition.Templates.FirstOrDefault(t => t.name.Equals(name, StringComparison.InvariantCulture));
if (template == null)
throw new TemplateException(String.Format(Strings.XmlTemplateNotFound, name));
if (template.type != type)
throw new TemplateException(String.Format(Strings.XmlTemplateUnexpectedType, name));
return template;
}
}
}
......@@ -635,5 +635,41 @@ namespace RabbitMetaQueue.Resources {
return ResourceManager.GetString("StatusWaitForKey", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to No &lt;Templates&gt; element found.
/// </summary>
internal static string XmlNoTemplatesElement {
get {
return ResourceManager.GetString("XmlNoTemplatesElement", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Circular reference detected for template {0}.
/// </summary>
internal static string XmlTemplateCircularReference {
get {
return ResourceManager.GetString("XmlTemplateCircularReference", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Template {0} not found.
/// </summary>
internal static string XmlTemplateNotFound {
get {
return ResourceManager.GetString("XmlTemplateNotFound", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Template {0} is not of the expected type.
/// </summary>
internal static string XmlTemplateUnexpectedType {
get {
return ResourceManager.GetString("XmlTemplateUnexpectedType", resourceCulture);
}
}
}
}
......@@ -327,4 +327,19 @@
<value>Queue {0} has not been changed, checking for updated bindings</value>
<comment>0 = Name</comment>
</data>
<data name="XmlNoTemplatesElement" xml:space="preserve">
<value>No &lt;Templates&gt; element found</value>
</data>
<data name="XmlTemplateNotFound" xml:space="preserve">
<value>Template {0} not found</value>
<comment>0 = Name</comment>
</data>
<data name="XmlTemplateUnexpectedType" xml:space="preserve">
<value>Template {0} is not of the expected type</value>
<comment>0 = Name</comment>
</data>
<data name="XmlTemplateCircularReference" xml:space="preserve">
<value>Circular reference detected for template {0}</value>
<comment>0 = Name</comment>
</data>
</root>
\ No newline at end of file
......@@ -24,10 +24,23 @@ namespace RabbitMetaQueue.Schema {
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://schema.x2software.net/RabbitMetaQueue", IsNullable=false)]
public partial class Topology {
private Template[] templatesField;
private Exchange[] exchangesField;
private Queue[] queuesField;
/// <opmerkingen/>
[System.Xml.Serialization.XmlArrayItemAttribute(IsNullable=false)]
public Template[] Templates {
get {
return this.templatesField;
}
set {
this.templatesField = value;
}
}
/// <opmerkingen/>
[System.Xml.Serialization.XmlArrayItemAttribute(IsNullable=false)]
public Exchange[] Exchanges {
......@@ -57,28 +70,22 @@ namespace RabbitMetaQueue.Schema {
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://schema.x2software.net/RabbitMetaQueue")]
public partial class Exchange {
public partial class Template {
private Argument[] argumentsField;
private Arguments itemField;
private string nameField;
private ExchangeType typeField;
private bool durableField;
public Exchange() {
this.durableField = true;
}
private TemplateType typeField;
/// <opmerkingen/>
[System.Xml.Serialization.XmlArrayItemAttribute(IsNullable=false)]
public Argument[] Arguments {
[System.Xml.Serialization.XmlElementAttribute("Arguments")]
public Arguments Item {
get {
return this.argumentsField;
return this.itemField;
}
set {
this.argumentsField = value;
this.itemField = value;
}
}
......@@ -95,7 +102,7 @@ namespace RabbitMetaQueue.Schema {
/// <opmerkingen/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public ExchangeType type {
public TemplateType type {
get {
return this.typeField;
}
......@@ -103,16 +110,39 @@ namespace RabbitMetaQueue.Schema {
this.typeField = value;
}
}
}
/// <opmerkingen/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.42")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://schema.x2software.net/RabbitMetaQueue")]
public partial class Arguments {
private Argument[] argumentField;
private string templateField;
/// <opmerkingen/>
[System.Xml.Serialization.XmlElementAttribute("Argument")]
public Argument[] Argument {
get {
return this.argumentField;
}
set {
this.argumentField = value;
}
}
/// <opmerkingen/>
[System.Xml.Serialization.XmlAttributeAttribute()]
[System.ComponentModel.DefaultValueAttribute(true)]
public bool durable {
public string template {
get {
return this.durableField;
return this.templateField;
}
set {
this.durableField = value;
this.templateField = value;
}
}
}
......@@ -160,15 +190,14 @@ namespace RabbitMetaQueue.Schema {
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://schema.x2software.net/RabbitMetaQueue")]
public partial class Binding {
private Argument[] argumentsField;
private Arguments argumentsField;
private string exchangeField;
private string routingKeyField;
/// <opmerkingen/>
[System.Xml.Serialization.XmlArrayItemAttribute(IsNullable=false)]
public Argument[] Arguments {
public Arguments Arguments {
get {
return this.argumentsField;
}
......@@ -208,7 +237,7 @@ namespace RabbitMetaQueue.Schema {
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://schema.x2software.net/RabbitMetaQueue")]
public partial class Queue {
private Argument[] argumentsField;
private Arguments argumentsField;
private Binding[] bindingsField;
......@@ -221,8 +250,7 @@ namespace RabbitMetaQueue.Schema {
}
/// <opmerkingen/>
[System.Xml.Serialization.XmlArrayItemAttribute(IsNullable=false)]
public Argument[] Arguments {
public Arguments Arguments {
get {
return this.argumentsField;
}
......@@ -266,6 +294,71 @@ namespace RabbitMetaQueue.Schema {
}
}
/// <opmerkingen/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.42")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://schema.x2software.net/RabbitMetaQueue")]
public partial class Exchange {
private Arguments argumentsField;
private string nameField;
private ExchangeType typeField;
private bool durableField;
public Exchange() {
this.durableField = true;
}
/// <opmerkingen/>
public Arguments Arguments {
get {
return this.argumentsField;
}
set {
this.argumentsField = value;
}
}
/// <opmerkingen/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string name {
get {
return this.nameField;
}
set {
this.nameField = value;
}
}
/// <opmerkingen/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public ExchangeType type {
get {
return this.typeField;
}
set {
this.typeField = value;
}
}
/// <opmerkingen/>
[System.Xml.Serialization.XmlAttributeAttribute()]
[System.ComponentModel.DefaultValueAttribute(true)]
public bool durable {
get {
return this.durableField;
}
set {
this.durableField = value;
}
}
}
/// <opmerkingen/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.42")]
[System.SerializableAttribute()]
......@@ -284,4 +377,14 @@ namespace RabbitMetaQueue.Schema {
/// <opmerkingen/>
Headers,
}
/// <opmerkingen/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "2.0.50727.42")]
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="http://schema.x2software.net/RabbitMetaQueue")]
public enum TemplateType {
/// <opmerkingen/>
Arguments,
}
}
......@@ -3,6 +3,7 @@
<xs:element name="Topology">
<xs:complexType>
<xs:all>
<xs:element name="Templates" type="rmq:Templates" minOccurs="0"/>
<xs:element name="Exchanges" type="rmq:Exchanges" minOccurs="0"/>
<xs:element name="Queues" type="rmq:Queues" minOccurs="0"/>
</xs:all>
......@@ -50,6 +51,7 @@
<xs:sequence>
<xs:element name="Argument" type="rmq:Argument" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute name="template" type="xs:string"/>
</xs:complexType>
<xs:complexType name="Argument">
<xs:simpleContent>
......@@ -58,6 +60,18 @@
</xs:extension>
</xs:simpleContent>
</xs:complexType>
<xs:complexType name="Templates">
<xs:sequence>
<xs:element name="Template" type="rmq:Template" minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="Template">
<xs:choice>
<xs:element name="Arguments" type="rmq:Arguments"/>
</xs:choice>
<xs:attribute name="name" type="xs:string" use="required"/>
<xs:attribute name="type" type="rmq:TemplateType" use="required"/>
</xs:complexType>
<xs:simpleType name="ExchangeType">
<xs:restriction base="xs:string">
<xs:enumeration value="Fanout"/>
......@@ -66,4 +80,9 @@
<xs:enumeration value="Headers"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="TemplateType">
<xs:restriction base="xs:string">
<xs:enumeration value="Arguments"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
Disclaimer: this is a list of ideas, not a roadmap.
* Templates in the definition for arguments, for example
X Templates in the definition for arguments, for example
a "deadletter" template which can be referenced in each
of the queues.
Done!
* Specify prefix to determine scope of the exchanges / queues
Relevant when using the delete options to mirror the definition,
if multiple applications / scopes are on the same server.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment