summaryrefslogtreecommitdiff
path: root/qpid/gentools
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/gentools')
-rw-r--r--qpid/gentools/LICENSE202
-rw-r--r--qpid/gentools/NOTICE2
-rw-r--r--qpid/gentools/README.txt61
-rwxr-xr-xqpid/gentools/build37
-rw-r--r--qpid/gentools/build.xml43
-rw-r--r--qpid/gentools/lib/LICENSE0
-rw-r--r--qpid/gentools/lib/NOTICE0
-rw-r--r--qpid/gentools/lib/README.txt0
-rw-r--r--qpid/gentools/lib/velocity-1.4.jarbin0 -> 361173 bytes
-rw-r--r--qpid/gentools/lib/velocity-dep-1.4.jarbin0 -> 517761 bytes
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpClass.java197
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpClassMap.java29
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpConstant.java191
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpConstantSet.java152
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpDomain.java89
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpDomainMap.java128
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpDomainVersionMap.java62
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpField.java269
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpFieldMap.java452
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpFlagMap.java77
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpMethod.java351
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpMethodMap.java36
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpModel.java132
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpOrdinalFieldMap.java96
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpOrdinalVersionMap.java76
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpOverloadedParameterMap.java29
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpParseException.java30
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpTemplateException.java30
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpTypeMappingException.java30
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpVersion.java72
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/AmqpVersionSet.java79
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/BitFieldGenerateMethod.java29
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/CommandGenerateMethod.java26
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/ConsolidatedField.java120
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/CppGenerator.java1716
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/DotnetGenerator.java382
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/GenerateMethod.java27
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/Generator.java857
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/JavaGenerator.java1826
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/LanguageConverter.java42
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/Main.java301
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/MangledGenerateMethod.java26
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/NodeAware.java47
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/Printable.java28
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/SingleVersionClass.java103
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/SingleVersionField.java68
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/SingleVersionMethod.java154
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/SingleVersionModel.java71
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/TargetDirectoryException.java30
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/Utils.java159
-rw-r--r--qpid/gentools/src/org/apache/qpid/gentools/VersionConsistencyCheck.java26
-rw-r--r--qpid/gentools/templ.cpp/method/MethodBodyClass.h.tmpl112
-rw-r--r--qpid/gentools/templ.cpp/model/AMQP_ClientOperations.h.tmpl82
-rw-r--r--qpid/gentools/templ.cpp/model/AMQP_ClientProxy.cpp.tmpl52
-rw-r--r--qpid/gentools/templ.cpp/model/AMQP_ClientProxy.h.tmpl75
-rw-r--r--qpid/gentools/templ.cpp/model/AMQP_Constants.h.tmpl34
-rw-r--r--qpid/gentools/templ.cpp/model/AMQP_HighestVersion.h.tmpl42
-rw-r--r--qpid/gentools/templ.cpp/model/AMQP_MethodVersionMap.cpp.tmpl62
-rw-r--r--qpid/gentools/templ.cpp/model/AMQP_MethodVersionMap.h.tmpl57
-rw-r--r--qpid/gentools/templ.cpp/model/AMQP_ServerOperations.h.tmpl83
-rw-r--r--qpid/gentools/templ.cpp/model/AMQP_ServerProxy.cpp.tmpl51
-rw-r--r--qpid/gentools/templ.cpp/model/AMQP_ServerProxy.h.tmpl74
-rw-r--r--qpid/gentools/templ.java/PropertyContentHeaderClass.tmpl208
-rw-r--r--qpid/gentools/templ.java/method/version/MethodBodyClass.vm190
-rw-r--r--qpid/gentools/templ.java/model/ProtocolVersionListClass.vm154
-rw-r--r--qpid/gentools/templ.java/model/version/AmqpConstantsClass.vm37
-rw-r--r--qpid/gentools/templ.java/model/version/MethodRegistryClass.vm145
-rw-r--r--qpid/gentools/xml-src/amqp-0.10.test.xml4241
-rw-r--r--qpid/gentools/xml-src/amqp-0.8.test.xml3959
-rw-r--r--qpid/gentools/xml-src/amqp-0.9.test.xml4282
-rw-r--r--qpid/gentools/xml-src/cluster-0.9.test.xml59
71 files changed, 22989 insertions, 0 deletions
diff --git a/qpid/gentools/LICENSE b/qpid/gentools/LICENSE
new file mode 100644
index 0000000000..43fa6abd19
--- /dev/null
+++ b/qpid/gentools/LICENSE
@@ -0,0 +1,202 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed 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.
+
diff --git a/qpid/gentools/NOTICE b/qpid/gentools/NOTICE
new file mode 100644
index 0000000000..09e9ae4902
--- /dev/null
+++ b/qpid/gentools/NOTICE
@@ -0,0 +1,2 @@
+This product includes software developed by The Apache Software Foundation (http://www.apache.org/).
+
diff --git a/qpid/gentools/README.txt b/qpid/gentools/README.txt
new file mode 100644
index 0000000000..94f705b064
--- /dev/null
+++ b/qpid/gentools/README.txt
@@ -0,0 +1,61 @@
+================================================================================
+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.
+================================================================================
+
+AMQP MULTI_VERSION CODE GENERATOR
+
+This directory contains the first part of the new multi-AMQP-version code
+generator. The Java generation is almost complete, C++ will follow.
+
+NOTE: The generator has NOT been integrated into the current build, and is
+included here to run stand-alone for the purposes of review and comment. As
+currently configured, this generator will not interact with any file or
+directory outside of this directory.
+
+To build (from this directory):
+rm org/apache/qpid/gentools/*.class
+javac org/apache/qpid/gentools/Main.java
+
+Make sure you are using Sun's JDK1.5.0; Eclipse and gcj do not work.
+
+To run (from this directory):
+java org/apache/qpid/gentools/Main -j [xml_spec_file, ...]
+
+XML test files are located in the xml-src directory. Pay attention to the
+Basic class and Basic.Consume method - these were the primary test vehicles
+for this generator. *** NOTE *** These files do not represent any current or
+future version of the AMQP specification - do not use in production!
+
+Folders:
+--------
+org/apache/qpid/gentools/: Source.
+xml-src/: Test AMQP specification files.
+templ.java/: Templates for java code generation.
+out.java/: Output folder for generated Java files (will be created with use
+ of -j flag on command-line).
+templ.cpp/: (Future:) Templates for C++ code generation.
+out.cpp/: Output folder for generated C++ files (will be created with use
+ of -c flag on command-line).
+
+For a more detaild description of the generator, see the Qpid Wiki
+(http://cwiki.apache.org/qpid/multiple-amqp-version-support.html).
+
+Please send comments and bugs to me (kim.vdriet [at] redhat.com) or via the
+Apache Qpid list (dev [at] qpid.apache.org).
+
+Kim van der Riet
diff --git a/qpid/gentools/build b/qpid/gentools/build
new file mode 100755
index 0000000000..a18a984dff
--- /dev/null
+++ b/qpid/gentools/build
@@ -0,0 +1,37 @@
+#!/bin/bash
+#
+#
+# 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.
+#
+#
+
+
+cd src
+echo "--------- Building gentools ----------"
+echo "Clearing out old build files..."
+for f in org/apache/qpid/gentools/*.class; do
+ if [ -e $f ]; then
+ rm $f
+ fi
+done
+echo "Compiling..."
+javac -source=1.5 -target=1.5 org/apache/qpid/gentools/*.java
+echo "Done. Try it out..."
+java org.apache.qpid.gentools.Main
+echo "--------- Building gentools completed ----------"
+cd ..
diff --git a/qpid/gentools/build.xml b/qpid/gentools/build.xml
new file mode 100644
index 0000000000..5d0976f56d
--- /dev/null
+++ b/qpid/gentools/build.xml
@@ -0,0 +1,43 @@
+<!--
+ -
+ - 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.
+ -
+ -->
+<project name="gentools" default="compile">
+ <property name="src" location="src" />
+
+ <property name="java.source" value="1.5"/>
+ <property name="java.target" value="1.5"/>
+
+ <target name="compile">
+ <javac srcdir="${src}" source="${java.source}" target="${java.target}" fork="true" debug="on">
+ <classpath>
+ <fileset dir="${src}/../lib">
+ <include name="**/*.jar"/>
+ </fileset>
+ </classpath>
+ </javac>
+ </target>
+
+ <target name="clean">
+ <delete>
+ <fileset dir="${src}/org/apache/qpid/gentools" includes="*.class" />
+ </delete>
+ </target>
+
+</project>
diff --git a/qpid/gentools/lib/LICENSE b/qpid/gentools/lib/LICENSE
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/qpid/gentools/lib/LICENSE
diff --git a/qpid/gentools/lib/NOTICE b/qpid/gentools/lib/NOTICE
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/qpid/gentools/lib/NOTICE
diff --git a/qpid/gentools/lib/README.txt b/qpid/gentools/lib/README.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/qpid/gentools/lib/README.txt
diff --git a/qpid/gentools/lib/velocity-1.4.jar b/qpid/gentools/lib/velocity-1.4.jar
new file mode 100644
index 0000000000..04ec9d2f85
--- /dev/null
+++ b/qpid/gentools/lib/velocity-1.4.jar
Binary files differ
diff --git a/qpid/gentools/lib/velocity-dep-1.4.jar b/qpid/gentools/lib/velocity-dep-1.4.jar
new file mode 100644
index 0000000000..375712b0e8
--- /dev/null
+++ b/qpid/gentools/lib/velocity-dep-1.4.jar
Binary files differ
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpClass.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpClass.java
new file mode 100644
index 0000000000..26195da2e3
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpClass.java
@@ -0,0 +1,197 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintStream;
+import java.util.Collection;
+
+public class AmqpClass implements Printable, NodeAware
+{
+
+ private final AmqpVersionSet _versionSet = new AmqpVersionSet();
+ private final AmqpFieldMap _fieldMap = new AmqpFieldMap();
+ private final AmqpMethodMap _methodMap = new AmqpMethodMap();
+ private final AmqpOrdinalVersionMap _indexMap = new AmqpOrdinalVersionMap();
+
+
+ private final String _name;
+ private final Generator _generator;
+
+ public AmqpClass(String name, Generator generator)
+ {
+ _name = name;
+ _generator = generator;
+ }
+
+ public boolean addFromNode(Node classNode, int ordinal, AmqpVersion version)
+ throws AmqpParseException, AmqpTypeMappingException
+ {
+ getVersionSet().add(version);
+ int index = Utils.getNamedIntegerAttribute(classNode, "index");
+ AmqpVersionSet indexVersionSet = getIndexMap().get(index);
+ if (indexVersionSet != null)
+ {
+ indexVersionSet.add(version);
+ }
+ else
+ {
+ indexVersionSet = new AmqpVersionSet();
+ indexVersionSet.add(version);
+ getIndexMap().put(index, indexVersionSet);
+ }
+ NodeList nList = classNode.getChildNodes();
+ int fieldCntr = getFieldMap().size();
+ for (int i = 0; i < nList.getLength(); i++)
+ {
+ Node child = nList.item(i);
+ if (child.getNodeName().compareTo(Utils.ELEMENT_FIELD) == 0)
+ {
+ String fieldName = getGenerator().prepareDomainName(Utils.getNamedAttribute(child,
+ Utils.ATTRIBUTE_NAME));
+ AmqpField thisField = getFieldMap().get(fieldName);
+ if (thisField == null)
+ {
+ thisField = new AmqpField(fieldName, getGenerator());
+ getFieldMap().add(fieldName, thisField);
+ }
+ if (!thisField.addFromNode(child, fieldCntr++, version))
+ {
+ String className = getGenerator().prepareClassName(Utils.getNamedAttribute(classNode,
+ Utils.ATTRIBUTE_NAME));
+ System.out.println("INFO: Generation supression tag found for field " +
+ className + "." + fieldName + " - removing.");
+ thisField.removeVersion(version);
+ getFieldMap().remove(fieldName);
+ }
+ }
+ else if (child.getNodeName().compareTo(Utils.ELEMENT_METHOD) == 0)
+ {
+ String methodName = getGenerator().prepareMethodName(Utils.getNamedAttribute(child,
+ Utils.ATTRIBUTE_NAME));
+ AmqpMethod thisMethod = getMethodMap().get(methodName);
+ if (thisMethod == null)
+ {
+ thisMethod = new AmqpMethod(methodName, getGenerator());
+ getMethodMap().put(methodName, thisMethod);
+ }
+ if (!thisMethod.addFromNode(child, 0, version))
+ {
+ String className = getGenerator().prepareClassName(Utils.getNamedAttribute(classNode,
+ Utils.ATTRIBUTE_NAME));
+ System.out.println("INFO: Generation supression tag found for method " +
+ className + "." + methodName + " - removing.");
+ thisMethod.removeVersion(version);
+ getMethodMap().remove(methodName);
+ }
+ }
+ else if (child.getNodeName().compareTo(Utils.ELEMENT_CODEGEN) == 0)
+ {
+ String value = Utils.getNamedAttribute(child, Utils.ATTRIBUTE_VALUE);
+ if (value.compareTo("no-gen") == 0)
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public void removeVersion(AmqpVersion version)
+ {
+ getIndexMap().removeVersion(version);
+ getFieldMap().removeVersion(version);
+ getMethodMap().removeVersion(version);
+ getVersionSet().remove(version);
+ }
+
+ public void print(PrintStream out, int marginSize, int tabSize)
+ {
+ String margin = Utils.createSpaces(marginSize);
+ String tab = Utils.createSpaces(tabSize);
+ out.println(margin + "[C] " + getName() + ": " + getVersionSet());
+
+ for (Integer thisIndex : getIndexMap().keySet())
+ {
+ AmqpVersionSet indexVersionSet = getIndexMap().get(thisIndex);
+ out.println(margin + tab + "[I] " + thisIndex + indexVersionSet);
+ }
+
+ for (String thisFieldName : getFieldMap().keySet())
+ {
+ AmqpField thisField = getFieldMap().get(thisFieldName);
+ thisField.print(out, marginSize + tabSize, tabSize);
+ }
+
+ for (String thisMethodName : getMethodMap().keySet())
+ {
+ AmqpMethod thisMethod = getMethodMap().get(thisMethodName);
+ thisMethod.print(out, marginSize + tabSize, tabSize);
+ }
+ }
+
+ public AmqpVersionSet getVersionSet()
+ {
+ return _versionSet;
+ }
+
+ public Generator getGenerator()
+ {
+ return _generator;
+ }
+
+
+ public AmqpFieldMap getFieldMap()
+ {
+ return _fieldMap;
+ }
+
+
+ public AmqpMethodMap getMethodMap()
+ {
+ return _methodMap;
+ }
+
+ public Collection<AmqpMethod> getMethods()
+ {
+ return getMethodMap().values();
+ }
+
+
+ public String getName()
+ {
+ return _name;
+ }
+
+
+ public AmqpOrdinalVersionMap getIndexMap()
+ {
+ return _indexMap;
+ }
+
+ public SingleVersionClass asSingleVersionClass(AmqpVersion version)
+ {
+ return new SingleVersionClass(this,version, _generator);
+ }
+
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpClassMap.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpClassMap.java
new file mode 100644
index 0000000000..a27a50d07e
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpClassMap.java
@@ -0,0 +1,29 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.util.TreeMap;
+
+@SuppressWarnings("serial")
+public class AmqpClassMap extends TreeMap<String, AmqpClass>
+{
+
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpConstant.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpConstant.java
new file mode 100644
index 0000000000..df5bc6c362
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpConstant.java
@@ -0,0 +1,191 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.io.PrintStream;
+import java.util.TreeMap;
+
+/**
+ * @author kpvdr
+ * Class to represent the &lt;constant&gt; declaration within the AMQP specification.
+ * Currently, only integer values exist within the specification, however looking forward
+ * to other possible types in the future, string and double types are also supported.
+ * <p/>
+ * The &lt;constant&gt; declaration in the specification contains only two attributes:
+ * name and value.
+ * <p/>
+ * The value of the constant is mapped against the version(s) for which the name is defined.
+ * This allows for a change in the value rather than the name only from one version to the next.
+ */
+@SuppressWarnings("serial")
+public class AmqpConstant extends TreeMap<String, AmqpVersionSet>
+ implements Printable, VersionConsistencyCheck, Comparable<AmqpConstant>
+{
+ /**
+ * Constant name as defined by the name attribute of the &lt;constant&gt; declaration.
+ */
+ private final String _name;
+
+ /**
+ * Set of versions for which this constant name is defined.
+ */
+ private final AmqpVersionSet _versionSet;
+
+ /**
+ * Constructor
+ *
+ * @param name Constant name as defined by the name attribute of the &lt;constant&gt; declaration.
+ * @param value Constant value as defined by the value attribute of the &lt;constant&gt; declaration.
+ * @param version AMQP version for which this constant is defined
+ */
+ public AmqpConstant(String name, String value, AmqpVersion version)
+ {
+ _name = name;
+ _versionSet = new AmqpVersionSet(version);
+ AmqpVersionSet valueVersionSet = new AmqpVersionSet(version);
+ put(value, valueVersionSet);
+ }
+
+
+ /**
+ * Get the name of this constant.
+ *
+ * @return Name of this constant, being the name attribute of the &lt;constant&gt; declaration
+ * represented by this class.
+ */
+ public String getName()
+ {
+ return _name;
+ }
+
+ /**
+ * Get the value of this constant as a String.
+ *
+ * @param version AMQP version for which this value is required.
+ * @return Value of this constant, being the value attribute of the &lt;constant&gt; declaration
+ * represented by this class.
+ * @throws AmqpTypeMappingException when a value is requested for a version for which it is not
+ * defined in the AMQP specifications.
+ */
+ public String getStringValue(AmqpVersion version)
+ throws AmqpTypeMappingException
+ {
+ for (String thisValue : keySet())
+ {
+ AmqpVersionSet versionSet = get(thisValue);
+ if (versionSet.contains(version))
+ {
+ return thisValue;
+ }
+ }
+ throw new AmqpTypeMappingException("Unable to find value for constant \"" + getName() +
+ "\" for version " + version.toString() + ".");
+ }
+
+ /**
+ * Get the value of this constant as an integer.
+ *
+ * @param version AMQP version for which this value is required.
+ * @return Value of this constant, being the value attribute of the &lt;constant&gt; declaration
+ * represented by this class.
+ * @throws AmqpTypeMappingException when a value is requested for a version for which it is not
+ * defined in the AMQP specifications.
+ */
+ public int getIntegerValue(AmqpVersion version)
+ throws AmqpTypeMappingException
+ {
+ return Integer.parseInt(getStringValue(version));
+ }
+
+ /**
+ * Get the value of this constant as a double.
+ *
+ * @param version AMQP version for which this value is required.
+ * @return Value of this constant, being the value attribute of the &lt;constant&gt; declaration
+ * represented by this class.
+ * @throws AmqpTypeMappingException when a value is requested for a version for which it is not
+ * defined in the AMQP specifications.
+ */
+ public double getDoubleValue(AmqpVersion version)
+ throws AmqpTypeMappingException
+ {
+ return Double.parseDouble(getStringValue(version));
+ }
+
+ /**
+ * Get the version set for this constant. It contains the all the versions for which this
+ * constant name exists.
+ *
+ * @return Set of versions for which this constant exists.
+ */
+ public AmqpVersionSet getVersionSet()
+ {
+ return _versionSet;
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+
+ public int compareTo(AmqpConstant other)
+ {
+ int res = getName().compareTo(other.getName());
+ if (res != 0)
+ {
+ return res;
+ }
+ return getVersionSet().compareTo(other.getVersionSet());
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.qpid.gentools.VersionConsistencyCheck#isVersionConsistent(org.apache.qpid.gentools.AmqpVersionSet)
+ */
+ public boolean isVersionConsistent(AmqpVersionSet globalVersionSet)
+ {
+ if (size() != 1)
+ {
+ return false;
+ }
+ return get(firstKey()).equals(globalVersionSet);
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.qpid.gentools.Printable#print(java.io.PrintStream, int, int)
+ */
+ public void print(PrintStream out, int marginSize, int tabSize)
+ {
+ String margin = Utils.createSpaces(marginSize);
+ String tab = Utils.createSpaces(tabSize);
+ if (size() == 1)
+ {
+ out.println(margin + tab + "[C] " + getName() + " = \"" + firstKey() + "\" " + getVersionSet());
+ }
+ else
+ {
+ out.println(margin + tab + "[C] " + getName() + ": " + getVersionSet());
+ for (String thisValue : keySet())
+ {
+ out.println(margin + tab + tab + "= \"" + thisValue + "\" " + get(thisValue));
+ }
+ }
+ }
+
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpConstantSet.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpConstantSet.java
new file mode 100644
index 0000000000..ab8b8be61e
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpConstantSet.java
@@ -0,0 +1,152 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintStream;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+/**
+ * @author kpvdr
+ * This class implements a set collection for {@link AmqpConstant AmqpConstant} objects, being the collection
+ * of constants accumulated from various AMQP specification files processed. Each name occurs once only in the set.
+ * The {@link AmqpConstant AmqpConstant} objects (derived from {@link java.util.TreeMap TreeMap}) keep track of
+ * the value and version(s) assigned to this name.
+ */
+@SuppressWarnings("serial")
+public class AmqpConstantSet implements Printable, NodeAware //, Comparable<AmqpConstantSet>
+{
+ private final LanguageConverter _converter;
+ private final TreeSet<AmqpConstant> _constants = new TreeSet<AmqpConstant>();
+ private final AmqpVersionSet _versionSet = new AmqpVersionSet();
+
+ public AmqpConstantSet(LanguageConverter converter)
+ {
+ _converter = converter;
+
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.qpid.gentools.NodeAware#addFromNode(org.w3c.dom.Node, int, org.apache.qpid.gentools.AmqpVersion)
+ */
+ public boolean addFromNode(Node node, int ordinal, AmqpVersion version)
+ throws AmqpParseException, AmqpTypeMappingException
+ {
+ _versionSet.add(version);
+ NodeList nodeList = node.getChildNodes();
+ for (int i = 0; i < nodeList.getLength(); i++)
+ {
+ Node childNode = nodeList.item(i);
+ if (childNode.getNodeName().compareTo(Utils.ELEMENT_CONSTANT) == 0)
+ {
+ String name = getConverter().prepareConstantName(Utils.getNamedAttribute(childNode, Utils.ATTRIBUTE_NAME));
+ String value = Utils.getNamedAttribute(childNode, Utils.ATTRIBUTE_VALUE);
+ // Find this name in the existing set of objects
+ boolean foundName = false;
+ Iterator<AmqpConstant> cItr = _constants.iterator();
+ while (cItr.hasNext() && !foundName)
+ {
+ AmqpConstant thisConstant = cItr.next();
+ if (name.compareTo(thisConstant.getName()) == 0)
+ {
+ foundName = true;
+ thisConstant.getVersionSet().add(version);
+ // Now, find the value in the map
+ boolean foundValue = false;
+ for (String thisValue : thisConstant.keySet())
+ {
+ if (value.compareTo(thisValue) == 0)
+ {
+ foundValue = true;
+ // Add this version to existing version set.
+ AmqpVersionSet versionSet = thisConstant.get(thisValue);
+ versionSet.add(version);
+ }
+ }
+ // Check that the value was found - if not, add it
+ if (!foundValue)
+ {
+ thisConstant.put(value, new AmqpVersionSet(version));
+ }
+ }
+ }
+ // Check that the name was found - if not, add it
+ if (!foundName)
+ {
+ _constants.add(new AmqpConstant(name, value, version));
+ }
+ }
+ }
+ return true;
+ }
+
+ /* (non-Javadoc)
+ * @see org.apache.qpid.gentools.Printable#print(java.io.PrintStream, int, int)
+ */
+ public void print(PrintStream out, int marginSize, int tabSize)
+ {
+ out.println(Utils.createSpaces(marginSize) + "Constants: ");
+ for (AmqpConstant thisAmqpConstant : _constants)
+ {
+ thisAmqpConstant.print(out, marginSize, tabSize);
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see java.lang.Comparable#compareTo(java.lang.Object)
+ */
+// public int compareTo(AmqpConstantSet other)
+// {
+// int res = size() - other.size();
+// if (res != 0)
+// return res;
+// Iterator<AmqpConstant> cItr = iterator();
+// Iterator<AmqpConstant> oItr = other.iterator();
+// while (cItr.hasNext() && oItr.hasNext())
+// {
+// AmqpConstant constant = cItr.next();
+// AmqpConstant oConstant = oItr.next();
+// res = constant.compareTo(oConstant);
+// if (res != 0)
+// return res;
+// }
+// return 0;
+// }
+
+ public Iterable<? extends AmqpConstant> getContstants()
+ {
+ return _constants;
+ }
+
+ public AmqpVersionSet getVersionSet()
+ {
+ return _versionSet;
+ }
+
+ public LanguageConverter getConverter()
+ {
+ return _converter;
+ }
+
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpDomain.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpDomain.java
new file mode 100644
index 0000000000..ba8552a6a6
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpDomain.java
@@ -0,0 +1,89 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.io.PrintStream;
+import java.util.TreeMap;
+
+@SuppressWarnings("serial")
+public class AmqpDomain extends TreeMap<String, AmqpVersionSet> implements Printable
+{
+ private final String _domainName;
+
+ public AmqpDomain(String domainName)
+ {
+ _domainName = domainName;
+ }
+
+ public void addDomain(String domainType, AmqpVersion version) throws AmqpParseException
+ {
+ AmqpVersionSet versionSet = get(domainType);
+ if (versionSet == null) // First time, create new entry
+ {
+ versionSet = new AmqpVersionSet();
+ put(domainType, versionSet);
+ }
+ versionSet.add(version);
+ }
+
+ public String getDomainType(AmqpVersion version)
+ throws AmqpTypeMappingException
+ {
+ for (String thisDomainType : keySet())
+ {
+ AmqpVersionSet versionSet = get(thisDomainType);
+ if (versionSet.contains(version))
+ {
+ return thisDomainType;
+ }
+ }
+ throw new AmqpTypeMappingException("Unable to find version " + version + ".");
+ }
+
+ public boolean hasVersion(String type, AmqpVersion v)
+ {
+ AmqpVersionSet vs = get(type);
+ if (vs == null)
+ {
+ return false;
+ }
+ return vs.contains(v);
+ }
+
+ public void print(PrintStream out, int marginSize, int tabSize)
+ {
+ String margin = Utils.createSpaces(marginSize);
+ String tab = Utils.createSpaces(tabSize);
+ out.println(margin + getDomainName() + ":");
+
+ for (String thisDomainType : keySet())
+ {
+ AmqpVersionSet vs = get(thisDomainType);
+ out.println(margin + tab + thisDomainType + " : " + vs.toString());
+ }
+ }
+
+ public String getDomainName()
+ {
+ return _domainName;
+ }
+
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpDomainMap.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpDomainMap.java
new file mode 100644
index 0000000000..0cd9d214bd
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpDomainMap.java
@@ -0,0 +1,128 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintStream;
+import java.util.TreeMap;
+
+@SuppressWarnings("serial")
+public class AmqpDomainMap extends TreeMap<String, AmqpDomain> implements Printable, NodeAware
+{
+ private final LanguageConverter _converter;
+
+ public AmqpDomainMap(LanguageConverter converter)
+ {
+ _converter = converter;
+
+ }
+
+ public boolean addFromNode(Node n, int o, AmqpVersion v)
+ throws AmqpParseException, AmqpTypeMappingException
+ {
+ NodeList nl = n.getChildNodes();
+ for (int i = 0; i < nl.getLength(); i++)
+ {
+ Node c = nl.item(i);
+ // All versions 0.9 and greater use <domain> for all domains
+ if (c.getNodeName().compareTo(Utils.ELEMENT_DOMAIN) == 0)
+ {
+ String domainName = getConverter().prepareDomainName(Utils.getNamedAttribute(c, Utils.ATTRIBUTE_NAME));
+ String type = Utils.getNamedAttribute(c, Utils.ATTRIBUTE_TYPE);
+ AmqpDomain thisDomain = get(domainName);
+ if (thisDomain == null)
+ {
+ thisDomain = new AmqpDomain(domainName);
+ put(domainName, thisDomain);
+ }
+ thisDomain.addDomain(type, v);
+ }
+ // Version(s) 0.8 and earlier use <domain> for all complex domains and use
+ // attribute <field type=""...> for simple types. Add these simple types to
+ // domain list - but beware of duplicates!
+ else if (c.getNodeName().compareTo(Utils.ELEMENT_FIELD) == 0)
+ {
+ try
+ {
+ String type = getConverter().prepareDomainName(Utils.getNamedAttribute(c, Utils.ATTRIBUTE_TYPE));
+ AmqpDomain thisDomain = get(type);
+ if (thisDomain == null)
+ {
+ thisDomain = new AmqpDomain(type);
+ put(type, thisDomain);
+ }
+ if (!thisDomain.hasVersion(type, v))
+ {
+ thisDomain.addDomain(type, v);
+ }
+ }
+ catch (AmqpParseException e)
+ {
+ } // Ignore fields without type attribute
+ }
+ else if (c.getNodeName().compareTo(Utils.ELEMENT_CLASS) == 0 ||
+ c.getNodeName().compareTo(Utils.ELEMENT_METHOD) == 0)
+ {
+ addFromNode(c, 0, v);
+ }
+ }
+ return true;
+ }
+
+ public String getDomainType(String domainName, AmqpVersion version)
+ {
+ AmqpDomain domainType = get(domainName);
+ // For AMQP 8.0, primitive types were not described as domains, so
+ // return itself as the type.
+ if (domainType == null)
+ {
+ return domainName;
+ }
+ try
+ {
+ return domainType.getDomainType(version);
+ }
+ catch (AmqpTypeMappingException e)
+ {
+ throw new AmqpTypeMappingException("Unable to find domain type for domain \"" + domainName +
+ "\" version " + version + ".");
+ }
+ }
+
+
+ public void print(PrintStream out, int marginSize, int tabSize)
+ {
+ out.println(Utils.createSpaces(marginSize) + "Domain Map:");
+ for (String thisDomainName : keySet())
+ {
+ AmqpDomain domain = get(thisDomainName);
+ domain.print(out, marginSize + tabSize, tabSize);
+ }
+ }
+
+ public LanguageConverter getConverter()
+ {
+ return _converter;
+ }
+
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpDomainVersionMap.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpDomainVersionMap.java
new file mode 100644
index 0000000000..e39550b96f
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpDomainVersionMap.java
@@ -0,0 +1,62 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.util.ArrayList;
+import java.util.TreeMap;
+
+@SuppressWarnings("serial")
+public class AmqpDomainVersionMap extends TreeMap<String, AmqpVersionSet> implements VersionConsistencyCheck
+{
+ public boolean isVersionConsistent(AmqpVersionSet globalVersionSet)
+ {
+ if (size() != 1)
+ {
+ return false;
+ }
+ return get(firstKey()).equals(globalVersionSet);
+ }
+
+ public boolean removeVersion(AmqpVersion version)
+ {
+ Boolean res = false;
+ ArrayList<String> removeList = new ArrayList<String>();
+ for (String domainName : keySet())
+ {
+ AmqpVersionSet versionSet = get(domainName);
+ if (versionSet.contains(version))
+ {
+ versionSet.remove(version);
+ if (versionSet.isEmpty())
+ {
+ removeList.add(domainName);
+ }
+ res = true;
+ }
+ }
+ // Get rid of domains no longer in use
+ for (String domainName : removeList)
+ {
+ remove(domainName);
+ }
+ return res;
+ }
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpField.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpField.java
new file mode 100644
index 0000000000..7c721cf913
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpField.java
@@ -0,0 +1,269 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+public class AmqpField implements Printable, NodeAware, VersionConsistencyCheck
+{
+
+ private final AmqpVersionSet _versionSet = new AmqpVersionSet();
+ private final AmqpDomainVersionMap _domainMap = new AmqpDomainVersionMap();
+ private final AmqpOrdinalVersionMap _ordinalMap = new AmqpOrdinalVersionMap();
+
+ private final String _name;
+ private final Generator _generator;
+
+ private final Map<AmqpVersion, String> _versionToDomainMap = new HashMap<AmqpVersion, String>();
+ private final Map<AmqpVersion, Integer> _versionToOrdinalMap = new HashMap<AmqpVersion, Integer>();
+
+
+ public AmqpField(String name, Generator generator)
+ {
+ _name = name;
+ _generator = generator;
+
+ }
+
+ public boolean addFromNode(Node fieldNode, int ordinal, AmqpVersion version)
+ throws AmqpParseException, AmqpTypeMappingException
+ {
+ _versionSet.add(version);
+ String domainType;
+ // Early versions of the spec (8.0) used the "type" attribute instead of "domain" for some fields.
+ try
+ {
+ domainType = _generator.prepareDomainName(Utils.getNamedAttribute(fieldNode, Utils.ATTRIBUTE_DOMAIN));
+ }
+ catch (AmqpParseException e)
+ {
+ domainType = _generator.prepareDomainName(Utils.getNamedAttribute(fieldNode, Utils.ATTRIBUTE_TYPE));
+ }
+ AmqpVersionSet thisVersionList = _domainMap.get(domainType);
+ if (thisVersionList == null) // First time, create new entry
+ {
+ thisVersionList = new AmqpVersionSet();
+ _domainMap.put(domainType, thisVersionList);
+ }
+
+ _versionToDomainMap.put(version, domainType);
+ _versionToOrdinalMap.put(version, ordinal);
+
+ thisVersionList.add(version);
+ thisVersionList = _ordinalMap.get(ordinal);
+ if (thisVersionList == null) // First time, create new entry
+ {
+ thisVersionList = new AmqpVersionSet();
+ _ordinalMap.put(ordinal, thisVersionList);
+ }
+ thisVersionList.add(version);
+ NodeList nList = fieldNode.getChildNodes();
+ for (int i = 0; i < nList.getLength(); i++)
+ {
+ Node child = nList.item(i);
+ if (child.getNodeName().compareTo(Utils.ELEMENT_CODEGEN) == 0)
+ {
+ String value = Utils.getNamedAttribute(child, Utils.ATTRIBUTE_VALUE);
+ if (value.compareTo("no-gen") == 0)
+ {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ public void removeVersion(AmqpVersion version)
+ {
+ _domainMap.removeVersion(version);
+ _ordinalMap.removeVersion(version);
+ _versionSet.remove(version);
+ }
+
+ public boolean isCodeTypeConsistent(LanguageConverter converter)
+ throws AmqpTypeMappingException
+ {
+ if (_domainMap.size() == 1)
+ {
+ return true; // By definition
+ }
+ ArrayList<String> codeTypeList = new ArrayList<String>();
+ for (String thisDomainName : _domainMap.keySet())
+ {
+ AmqpVersionSet versionSet = _domainMap.get(thisDomainName);
+ String codeType = converter.getGeneratedType(thisDomainName, versionSet.first());
+ if (!codeTypeList.contains(codeType))
+ {
+ codeTypeList.add(codeType);
+ }
+ }
+ return codeTypeList.size() == 1;
+ }
+
+ public boolean isConsistent(Generator generator)
+ throws AmqpTypeMappingException
+ {
+ if (!isCodeTypeConsistent(generator))
+ {
+ return false;
+ }
+ if (_ordinalMap.size() != 1)
+ {
+ return false;
+ }
+ // Since the various doamin names map to the same code type, add the version occurrences
+ // across all domains to see we have all possible versions covered
+ int vCntr = 0;
+ for (String thisDomainName : _domainMap.keySet())
+ {
+ vCntr += _domainMap.get(thisDomainName).size();
+ }
+ return vCntr == generator.getVersionSet().size();
+ }
+
+ public boolean isTypeAndNameConsistent(Generator generator)
+ throws AmqpTypeMappingException
+ {
+ if (!isCodeTypeConsistent(generator))
+ {
+ return false;
+ }
+ // Since the various doamin names map to the same code type, add the version occurrences
+ // across all domains to see we have all possible versions covered
+ int vCntr = 0;
+ for (String thisDomainName : _domainMap.keySet())
+ {
+ vCntr += _domainMap.get(thisDomainName).size();
+ }
+ return vCntr == getVersionSet().size();
+ }
+
+
+ public void print(PrintStream out, int marginSize, int tabSize)
+ {
+ String margin = Utils.createSpaces(marginSize);
+ out.println(margin + "[F] " + _name + ": " + _versionSet);
+
+ for (Integer thisOrdinal : _ordinalMap.keySet())
+ {
+ AmqpVersionSet versionList = _ordinalMap.get(thisOrdinal);
+ out.println(margin + " [O] " + thisOrdinal + " : " + versionList.toString());
+ }
+
+ for (String thisDomainName : _domainMap.keySet())
+ {
+ AmqpVersionSet versionList = _domainMap.get(thisDomainName);
+ out.println(margin + " [D] " + thisDomainName + " : " + versionList.toString());
+ }
+ }
+
+ public boolean isVersionConsistent(AmqpVersionSet globalVersionSet)
+ {
+ if (!_versionSet.equals(globalVersionSet))
+ {
+ return false;
+ }
+ if (!_domainMap.isVersionConsistent(globalVersionSet))
+ {
+ return false;
+ }
+ if (!_ordinalMap.isVersionConsistent(globalVersionSet))
+ {
+ return false;
+ }
+ return true;
+ }
+
+
+ public boolean isVersionInterfaceConsistent(AmqpVersionSet globalVersionSet)
+ {
+ if (!_versionSet.equals(globalVersionSet))
+ {
+ return false;
+ }
+ if (!_domainMap.isVersionConsistent(globalVersionSet))
+ {
+ return false;
+ }
+ if (!_ordinalMap.isVersionConsistent(globalVersionSet))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public String getDomain(AmqpVersion version)
+ {
+ return _versionToDomainMap.get(version);
+ }
+
+ public String getConsistentNativeType()
+ {
+ return _generator.getNativeType(_generator.getDomainType(getDomain(_versionSet.first()),_versionSet.first()));
+ }
+
+ public int getOrdinal(AmqpVersion version)
+ {
+ return _versionToOrdinalMap.get(version);
+ }
+
+ public AmqpVersionSet getVersionSet()
+ {
+ return _versionSet;
+ }
+
+ public AmqpDomainVersionMap getDomainMap()
+ {
+ return _domainMap;
+ }
+
+ public AmqpOrdinalVersionMap getOrdinalMap()
+ {
+ return _ordinalMap;
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public LanguageConverter getGenerator()
+ {
+ return _generator;
+ }
+
+ public Map<AmqpVersion, String> getVersionToDomainMap()
+ {
+ return _versionToDomainMap;
+ }
+
+ public Map<AmqpVersion, Integer> getVersionToOrdinalMap()
+ {
+ return _versionToOrdinalMap;
+ }
+
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpFieldMap.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpFieldMap.java
new file mode 100644
index 0000000000..0bb5e03a61
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpFieldMap.java
@@ -0,0 +1,452 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+
+@SuppressWarnings("serial")
+public class AmqpFieldMap implements VersionConsistencyCheck
+{
+
+ private final TreeMap<String, AmqpField> _map = new TreeMap<String, AmqpField>();
+
+ private final AmqpVersionSet _versionSet = new AmqpVersionSet();
+
+ public void removeVersion(AmqpVersion version)
+ {
+ String[] fieldNameArray = new String[size()];
+ _map.keySet().toArray(fieldNameArray);
+ Iterator<Entry<String, AmqpField>> iter = _map.entrySet().iterator();
+
+ while (iter.hasNext())
+ {
+ Entry<String, AmqpField> entry = iter.next();
+ entry.getValue().removeVersion(version);
+ iter.remove();
+ }
+ }
+
+ public int size()
+ {
+ return _map.size();
+
+ }
+
+ public AmqpFieldMap getFieldMapForOrdinal(int ordinal)
+ {
+ AmqpFieldMap newMap = new AmqpFieldMap();
+ for (AmqpField field : _map.values())
+ {
+
+ TreeMap<Integer, AmqpVersionSet> ordinalMap = field.getOrdinalMap();
+ AmqpVersionSet ordinalVersions = ordinalMap.get(ordinal);
+ if (ordinalVersions != null)
+ {
+ newMap.add(field.getName(), field);
+ }
+ }
+ return newMap;
+ }
+
+ public void add(String name, AmqpField field)
+ {
+ _versionSet.addAll(field.getVersionSet());
+ _map.put(name, field);
+ }
+
+ public AmqpOrdinalFieldMap getMapForVersion(AmqpVersion version, boolean codeTypeFlag,
+ LanguageConverter converter)
+ {
+ // TODO: REVIEW THIS! There may be a bug here that affects C++ generation (only with >1 version)...
+ // If version == null (a common scenario) then the version map is built up on the
+ // basis of first found item, and ignores other version variations.
+ // This should probably be disallowed by throwing an NPE, as AmqpOrdinalFieldMap cannot
+ // represent these possibilities.
+ // *OR*
+ // Change the structure of AmqpOrdianlFieldMap to allow for the various combinations that
+ // will result from version variation - but that is what AmqpFieldMap is... :-$
+ AmqpOrdinalFieldMap ordinalFieldMap = new AmqpOrdinalFieldMap();
+ for (AmqpField field : _map.values())
+ {
+
+ if (version == null || field.getVersionSet().contains(version))
+ {
+ // 1. Search for domain name in field domain map with version that matches
+ String domain = "";
+ boolean dFound = false;
+ for (String thisDomainName : field.getDomainMap().keySet())
+ {
+ domain = thisDomainName;
+ AmqpVersionSet versionSet = field.getDomainMap().get(domain);
+ if (version == null || versionSet.contains(version))
+ {
+ if (codeTypeFlag)
+ {
+ domain = converter.getGeneratedType(domain, version);
+ }
+ dFound = true;
+ }
+ }
+
+ // 2. Search for ordinal in field ordianl map with version that matches
+ int ordinal = -1;
+ boolean oFound = false;
+ for (Integer thisOrdinal : field.getOrdinalMap().keySet())
+ {
+ ordinal = thisOrdinal;
+ AmqpVersionSet versionSet = field.getOrdinalMap().get(ordinal);
+ if (version == null || versionSet.contains(version))
+ {
+ oFound = true;
+ }
+ }
+
+ if (dFound && oFound)
+ {
+ String[] fieldDomainPair = {field.getName(), domain};
+ ordinalFieldMap.put(ordinal, fieldDomainPair);
+ }
+ }
+ }
+ return ordinalFieldMap;
+ }
+
+ public boolean isDomainConsistent(Generator generator, AmqpVersionSet versionSet)
+ throws AmqpTypeMappingException
+ {
+ if (size() != 1) // Only one field for this ordinal
+ {
+ return false;
+ }
+ return _map.get(_map.firstKey()).isConsistent(generator);
+ }
+
+ public int getNumFields(AmqpVersion version)
+ {
+ int fCntr = 0;
+ for (AmqpField field : _map.values())
+ {
+
+ if (field.getVersionSet().contains(version))
+ {
+ fCntr++;
+ }
+ }
+ return fCntr;
+ }
+
+ public String parseFieldMap(CommandGenerateMethod commonGenerateMethod, MangledGenerateMethod mangledGenerateMethod,
+ int indentSize, int tabSize, LanguageConverter converter)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String cr = Utils.LINE_SEPARATOR;
+ StringBuffer sb = new StringBuffer();
+
+ if (commonGenerateMethod == null)
+ {
+ // Generate warnings in code if required methods are null.
+ sb.append(indent + "/*********************************************************" + cr);
+ sb.append(indent + " * WARNING: Generated code could be missing." + cr);
+ sb.append(indent + " * In call to parseFieldMap(), generation method was null." + cr);
+ sb.append(indent + " * Check for NoSuchMethodException on startup." + cr);
+ sb.append(indent + " *********************************************************/" + cr);
+ }
+
+ Iterator<Entry<String, AmqpField>> itr = _map.entrySet().iterator();
+ while (itr.hasNext())
+ {
+ Entry<String, AmqpField> entry = itr.next();
+ String fieldName = entry.getKey();
+ AmqpField field = entry.getValue();
+ if (field.isCodeTypeConsistent(converter))
+ {
+ // All versions identical - Common declaration
+ String domainName = field.getDomainMap().firstKey();
+ AmqpVersionSet versionSet = field.getDomainMap().get(domainName);
+ String codeType = converter.getGeneratedType(domainName, versionSet.first());
+ if (commonGenerateMethod != null)
+ {
+ sb.append(commonGenerateMethod.generate(codeType, field, versionSet,
+ indentSize, tabSize, itr.hasNext()));
+ }
+ }
+ else if (mangledGenerateMethod != null) // Version-mangled
+ {
+ sb.append(mangledGenerateMethod.generate(field, indentSize, tabSize,
+ itr.hasNext()));
+ }
+ }
+ return sb.toString();
+ }
+
+ public String parseFieldMapOrdinally(GenerateMethod generateMethod, BitFieldGenerateMethod bitGenerateMethod,
+ int indentSize, int tabSize, Generator codeGenerator)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String cr = Utils.LINE_SEPARATOR;
+ StringBuffer sb = new StringBuffer();
+
+ // Generate warnings in code if required methods are null.
+ if (generateMethod == null || bitGenerateMethod == null)
+ {
+ sb.append(indent + "/***********************************************" + cr);
+ sb.append(indent + " * WARNING: In call to parseFieldMapOrdinally():" + cr);
+ if (generateMethod == null)
+ {
+ sb.append(indent + " * => generateMethod is null." + cr);
+ }
+ if (bitGenerateMethod == null)
+ {
+ sb.append(indent + " * => bitGenerateMethod is null." + cr);
+ }
+ sb.append(indent + " * Generated code could be missing." + cr);
+ sb.append(indent + " * Check for NoSuchMethodException on startup." + cr);
+ sb.append(indent + " ***********************************************/" + cr);
+ }
+
+ /* We must process elements in ordinal order because adjacent booleans (bits)
+ * must be combined into a single byte (in groups of up to 8). Start with shared
+ * declarations until an ordinal divergence is found. (For most methods where
+ * there is no difference between versions, this will simplify the generated
+ * code. */
+
+ ArrayList<String> bitFieldList = new ArrayList<String>();
+ boolean ordinalDivergenceFlag = false;
+ int ordinal = 0;
+ while (ordinal < size() && !ordinalDivergenceFlag)
+ {
+ /* Since the getFieldMapOrdinal() function may map more than one Field to
+ * an ordinal, the number of ordinals may be less than the total number of
+ * fields in the fieldMap. Check for empty fieldmaps... */
+ AmqpFieldMap ordinalFieldMap = getFieldMapForOrdinal(ordinal);
+ if (ordinalFieldMap.size() > 0)
+ {
+ if (ordinalFieldMap.isDomainConsistent(codeGenerator, getVersionSet()))
+ {
+ String fieldName = ordinalFieldMap.getFirstFieldName();
+ String domain = ordinalFieldMap._map.get(fieldName).getDomainMap().firstKey();
+
+ String domainType = codeGenerator.getDomainType(domain,
+ codeGenerator.getVersionSet().first());
+
+ if (domainType.compareTo("bit") == 0)
+ {
+ bitFieldList.add(fieldName);
+ }
+ else if (bitFieldList.size() > 0)
+ {
+ // End of bit types - handle deferred bit type generation
+ if (bitGenerateMethod != null)
+ {
+ sb.append(bitGenerateMethod.generate(bitFieldList, ordinal,
+ indentSize, tabSize));
+ }
+ bitFieldList.clear();
+ }
+ if (!ordinalDivergenceFlag)
+ {
+ // Defer generation of bit types until all adjacent bits have been
+ // accounted for.
+ if (bitFieldList.size() == 0 && generateMethod != null)
+ {
+ sb.append(generateMethod.generate(domainType, fieldName, ordinal,
+ indentSize, tabSize));
+ }
+ }
+ ordinal++;
+ }
+ else
+ {
+ ordinalDivergenceFlag = true;
+ }
+ }
+ }
+
+ // Check if there is still more to do under a version-specific breakout
+ if (ordinalDivergenceFlag && ordinal < size())
+ {
+ // 1. Cycle through all versions in order, create outer if(version) structure
+ AmqpVersion[] versionArray = new AmqpVersion[getVersionSet().size()];
+ getVersionSet().toArray(versionArray);
+ for (int v = 0; v < versionArray.length; v++)
+ {
+ sb.append(indent);
+ if (v > 0)
+ {
+ sb.append("else ");
+ }
+ sb.append("if (major == " + versionArray[v].getMajor() + " && minor == " +
+ versionArray[v].getMinor() + ")" + cr);
+ sb.append(indent + "{" + cr);
+
+ // 2. Cycle though each ordinal from where we left off in the loop above.
+ ArrayList<String> bitFieldList2 = new ArrayList<String>(bitFieldList);
+ for (int o = ordinal; o < size(); o++)
+ {
+ AmqpFieldMap ordinalFieldMap = getFieldMapForOrdinal(o);
+ if (ordinalFieldMap.size() > 0)
+ {
+ // 3. Cycle through each of the fields that have this ordinal.
+ Iterator<Map.Entry<String, AmqpField>> i = ordinalFieldMap._map.entrySet().iterator();
+ while (i.hasNext())
+ {
+
+ Map.Entry<String, AmqpField> entry = i.next();
+ AmqpField field = entry.getValue();
+ String fieldName = entry.getKey();
+
+ // 4. Some fields may have more than one ordinal - match by both
+ // ordinal and version.
+ Iterator<Integer> j = field.getOrdinalMap().keySet().iterator();
+ while (j.hasNext())
+ {
+ int thisOrdinal = j.next();
+ AmqpVersionSet v1 = field.getOrdinalMap().get(thisOrdinal);
+ if (thisOrdinal == o && v1.contains(versionArray[v]))
+ {
+ // 5. Now get the domain for this version
+ int domainCntr = 0;
+ Iterator<String> k = field.getDomainMap().keySet().iterator();
+ while (k.hasNext())
+ {
+ // Mangle domain-divergent field names
+ String mangledFieldName = fieldName;
+ if (field.getDomainMap().size() > 1)
+ {
+ mangledFieldName += "_" + (domainCntr++);
+ }
+ String domainName = k.next();
+ AmqpVersionSet v2 = field.getDomainMap().get(domainName);
+ if (v2.contains(versionArray[v]))
+ {
+ // 6. (Finally!!) write the declaration
+ String domainType = codeGenerator.getDomainType(domainName,
+ versionArray[v]);
+ if (domainType.compareTo("bit") == 0)
+ {
+ bitFieldList2.add(mangledFieldName);
+ }
+ else if (bitFieldList2.size() > 0)
+ {
+ // End of bit types - handle deferred bit type generation
+ if (bitGenerateMethod != null)
+ {
+ sb.append(bitGenerateMethod.generate(
+ bitFieldList2, o, indentSize + tabSize,
+ tabSize));
+ }
+ bitFieldList2.clear();
+ }
+ // Defer generation of bit types until all adjacent bits have
+ // been accounted for.
+ if (bitFieldList2.size() == 0 && generateMethod != null)
+ {
+ sb.append(generateMethod.generate(domainType,
+ mangledFieldName, o, indentSize + tabSize, tabSize));
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ // Check for remaining deferred bits
+ if (bitFieldList2.size() > 0 && bitGenerateMethod != null)
+ {
+ sb.append(bitGenerateMethod.generate(bitFieldList2, size(),
+ indentSize + tabSize, tabSize));
+ }
+ sb.append(indent + "}" + cr);
+ }
+ }
+ // Check for remaining deferred bits
+ else if (bitFieldList.size() > 0 && bitGenerateMethod != null)
+ {
+ sb.append(bitGenerateMethod.generate(bitFieldList, size(),
+ indentSize, tabSize));
+ }
+ return sb.toString();
+ }
+
+ private String getFirstFieldName()
+ {
+ return _map.firstKey();
+ }
+
+ public boolean isVersionConsistent(AmqpVersionSet globalVersionSet)
+ {
+ for (String thisFieldName : _map.keySet())
+ {
+ AmqpField field = _map.get(thisFieldName);
+ if (!field.isVersionConsistent(globalVersionSet))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean isVersionInterfaceConsistent(AmqpVersionSet globalVersionSet)
+ {
+ for (String thisFieldName : _map.keySet())
+ {
+ AmqpField field = _map.get(thisFieldName);
+ if (!field.isVersionInterfaceConsistent(globalVersionSet))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ public AmqpVersionSet getVersionSet()
+ {
+ return _versionSet;
+ }
+
+ public Collection<AmqpField> values()
+ {
+ return _map.values();
+ }
+
+ public AmqpField get(String fieldName)
+ {
+ return _map.get(fieldName);
+ }
+
+ public void remove(String fieldName)
+ {
+ _map.remove(fieldName);
+ }
+
+ public Set<String> keySet()
+ {
+ return _map.keySet();
+ }
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpFlagMap.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpFlagMap.java
new file mode 100644
index 0000000000..5993a1b715
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpFlagMap.java
@@ -0,0 +1,77 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.util.ArrayList;
+import java.util.TreeMap;
+
+@SuppressWarnings("serial")
+public class AmqpFlagMap extends TreeMap<Boolean, AmqpVersionSet> implements VersionConsistencyCheck
+{
+ public boolean isSet()
+ {
+ return containsKey(true);
+ }
+
+ public String toString()
+ {
+ AmqpVersionSet versionSet = get(true);
+ if (versionSet != null)
+ {
+ return versionSet.toString();
+ }
+ return "";
+ }
+
+ public boolean isVersionConsistent(AmqpVersionSet globalVersionSet)
+ {
+ if (size() != 1)
+ {
+ return false;
+ }
+ return get(firstKey()).equals(globalVersionSet);
+ }
+
+ public boolean removeVersion(AmqpVersion version)
+ {
+ Boolean res = false;
+ ArrayList<Boolean> removeList = new ArrayList<Boolean>();
+ for (Boolean flag : keySet())
+ {
+ AmqpVersionSet versionSet = get(flag);
+ if (versionSet.contains(version))
+ {
+ versionSet.remove(version);
+ if (versionSet.isEmpty())
+ {
+ removeList.add(flag);
+ }
+ res = true;
+ }
+ }
+ // Get rid of flags no longer in use
+ for (Boolean flag : removeList)
+ {
+ remove(flag);
+ }
+ return res;
+ }
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpMethod.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpMethod.java
new file mode 100644
index 0000000000..4ec39b209e
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpMethod.java
@@ -0,0 +1,351 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Collection;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class AmqpMethod implements Printable, NodeAware, VersionConsistencyCheck
+{
+ private final AmqpVersionSet _versionSet = new AmqpVersionSet();
+ private final AmqpFieldMap _fieldMap = new AmqpFieldMap();
+
+ private final AmqpOrdinalVersionMap _indexMap = new AmqpOrdinalVersionMap();
+ private final AmqpFlagMap _clientMethodFlagMap = new AmqpFlagMap(); // Method called on client (<chassis name="server"> in XML)
+ private final AmqpFlagMap _serverMethodFlagMap = new AmqpFlagMap(); // Method called on server (<chassis name="client"> in XML)
+
+ private final Map<AmqpVersion, AmqpFieldMap> _versionToFieldsMap = new HashMap<AmqpVersion, AmqpFieldMap>();
+
+ private final Map<AmqpVersion, AtomicInteger> _versionToFieldCount = new HashMap<AmqpVersion, AtomicInteger>();
+
+ private final String _name;
+ private final Generator _generator;
+
+
+ public AmqpMethod(String name, Generator generator)
+ {
+ _name = name;
+ _generator = generator;
+ }
+
+ public boolean addFromNode(Node methodNode, int ordinal, AmqpVersion version)
+ throws AmqpParseException, AmqpTypeMappingException
+ {
+ _versionSet.add(version);
+ boolean serverChassisFlag = false;
+ boolean clientChassisFlag = false;
+ int index = Utils.getNamedIntegerAttribute(methodNode, "index");
+ AmqpVersionSet indexVersionSet = _indexMap.get(index);
+ if (indexVersionSet != null)
+ {
+ indexVersionSet.add(version);
+ }
+ else
+ {
+ indexVersionSet = new AmqpVersionSet();
+ indexVersionSet.add(version);
+ _indexMap.put(index, indexVersionSet);
+ }
+ NodeList nList = methodNode.getChildNodes();
+ AtomicInteger fieldCntr = _versionToFieldCount.get(version);
+ if(fieldCntr == null)
+ {
+ fieldCntr = new AtomicInteger(0);
+ _versionToFieldCount.put(version, fieldCntr);
+ }
+ for (int i = 0; i < nList.getLength(); i++)
+ {
+ Node child = nList.item(i);
+ if (child.getNodeName().compareTo(Utils.ELEMENT_FIELD) == 0)
+ {
+ String fieldName = _generator.prepareDomainName(Utils.getNamedAttribute(child,
+ Utils.ATTRIBUTE_NAME));
+ AmqpField thisField = _fieldMap.get(fieldName);
+ AmqpFieldMap versionSpecificFieldMap = _versionToFieldsMap.get(version);
+ if (versionSpecificFieldMap == null)
+ {
+ versionSpecificFieldMap = new AmqpFieldMap();
+ _versionToFieldsMap.put(version, versionSpecificFieldMap);
+ }
+
+
+ if (thisField == null)
+ {
+ thisField = new AmqpField(fieldName, _generator);
+ _fieldMap.add(fieldName, thisField);
+ }
+
+ AmqpField versionSpecificField = new AmqpField(fieldName, _generator);
+ versionSpecificFieldMap.add(fieldName, versionSpecificField);
+
+ versionSpecificField.addFromNode(child, fieldCntr.intValue(), version);
+
+ if (!thisField.addFromNode(child, fieldCntr.getAndIncrement(), version))
+ {
+ String className = _generator.prepareClassName(Utils.getNamedAttribute(methodNode.getParentNode(),
+ Utils.ATTRIBUTE_NAME));
+ String methodName = _generator.prepareMethodName(Utils.getNamedAttribute(methodNode,
+ Utils.ATTRIBUTE_NAME));
+ System.out.println("INFO: Generation supression tag found for field " +
+ className + "." + methodName + "." + fieldName + " - removing.");
+ thisField.removeVersion(version);
+ _fieldMap.remove(fieldName);
+ }
+ }
+ else if (child.getNodeName().compareTo(Utils.ELEMENT_CHASSIS) == 0)
+ {
+ String chassisName = Utils.getNamedAttribute(child, Utils.ATTRIBUTE_NAME);
+ if (chassisName.compareTo("server") == 0)
+ {
+ serverChassisFlag = true;
+ }
+ else if (chassisName.compareTo("client") == 0)
+ {
+ clientChassisFlag = true;
+ }
+ }
+ else if (child.getNodeName().compareTo(Utils.ELEMENT_CODEGEN) == 0)
+ {
+ String value = Utils.getNamedAttribute(child, Utils.ATTRIBUTE_VALUE);
+ if (value.compareTo("no-gen") == 0)
+ {
+ return false;
+ }
+ }
+ }
+ processChassisFlags(serverChassisFlag, clientChassisFlag, version);
+ return true;
+ }
+
+ public void removeVersion(AmqpVersion version)
+ {
+ _clientMethodFlagMap.removeVersion(version);
+ _serverMethodFlagMap.removeVersion(version);
+ _indexMap.removeVersion(version);
+ _fieldMap.removeVersion(version);
+ _versionSet.remove(version);
+ }
+
+ public void print(PrintStream out, int marginSize, int tabSize)
+ {
+ String margin = Utils.createSpaces(marginSize);
+ String tab = Utils.createSpaces(tabSize);
+ out.println(margin + "[M] " + _name + " {" + (_serverMethodFlagMap.isSet() ? "S " +
+ _serverMethodFlagMap + (
+ _clientMethodFlagMap.isSet() ? ", " : "") : "") +
+ (_clientMethodFlagMap.isSet()
+ ? "C " + _clientMethodFlagMap : "") + "}" + ": " +
+ _versionSet);
+
+ for (Integer thisIndex : _indexMap.keySet())
+ {
+ AmqpVersionSet indexVersionSet = _indexMap.get(thisIndex);
+ out.println(margin + tab + "[I] " + thisIndex + indexVersionSet);
+ }
+
+ for (String thisFieldName : _fieldMap.keySet())
+ {
+ AmqpField thisField = _fieldMap.get(thisFieldName);
+ thisField.print(out, marginSize + tabSize, tabSize);
+ }
+ }
+
+ protected void processChassisFlags(boolean serverFlag, boolean clientFlag, AmqpVersion version)
+ {
+ AmqpVersionSet versionSet = _serverMethodFlagMap.get(serverFlag);
+ if (versionSet != null)
+ {
+ versionSet.add(version);
+ }
+ else
+ {
+ versionSet = new AmqpVersionSet();
+ versionSet.add(version);
+ _serverMethodFlagMap.put(serverFlag, versionSet);
+ }
+
+ versionSet = _clientMethodFlagMap.get(clientFlag);
+ if (versionSet != null)
+ {
+ versionSet.add(version);
+ }
+ else
+ {
+ versionSet = new AmqpVersionSet();
+ versionSet.add(version);
+ _clientMethodFlagMap.put(clientFlag, versionSet);
+ }
+ }
+
+ public AmqpOverloadedParameterMap getOverloadedParameterLists(AmqpVersionSet globalVersionSet,
+ Generator generator)
+ throws AmqpTypeMappingException
+ {
+ AmqpOverloadedParameterMap parameterVersionMap = new AmqpOverloadedParameterMap();
+ for (AmqpVersion thisVersion : globalVersionSet)
+ {
+ AmqpOrdinalFieldMap ordinalFieldMap = _fieldMap.getMapForVersion(thisVersion, true, generator);
+ AmqpVersionSet methodVersionSet = parameterVersionMap.get(ordinalFieldMap);
+ if (methodVersionSet == null)
+ {
+ methodVersionSet = new AmqpVersionSet();
+ methodVersionSet.add(thisVersion);
+ parameterVersionMap.put(ordinalFieldMap, methodVersionSet);
+ }
+ else
+ {
+ methodVersionSet.add(thisVersion);
+ }
+ }
+ return parameterVersionMap;
+ }
+
+ public boolean isVersionInterfaceConsistent()
+ {
+ return isVersionInterfaceConsistent(_generator.getVersionSet());
+ }
+
+ public boolean isVersionInterfaceConsistent(AmqpVersionSet globalVersionSet)
+ {
+ if (!_versionSet.equals(globalVersionSet))
+ {
+ return false;
+ }
+ if (!_clientMethodFlagMap.isVersionConsistent(globalVersionSet))
+ {
+ return false;
+ }
+ if (!_serverMethodFlagMap.isVersionConsistent(globalVersionSet))
+ {
+ return false;
+ }
+ if (!_fieldMap.isVersionInterfaceConsistent(globalVersionSet))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public boolean isVersionConsistent()
+ {
+ return isVersionConsistent(_generator.getVersionSet());
+ }
+
+
+ public boolean isVersionConsistent(AmqpVersionSet globalVersionSet)
+ {
+ return isVersionInterfaceConsistent(globalVersionSet)
+ && _indexMap.isVersionConsistent(globalVersionSet)
+ && _fieldMap.isVersionConsistent(globalVersionSet);
+ }
+
+ public AmqpVersionSet getVersionSet()
+ {
+ return _versionSet;
+ }
+
+ public AmqpFieldMap getFieldMap()
+ {
+ return _fieldMap;
+ }
+
+ public AmqpOrdinalVersionMap getIndexMap()
+ {
+ return _indexMap;
+ }
+
+ public AmqpFlagMap getClientMethodFlagMap()
+ {
+ return _clientMethodFlagMap;
+ }
+
+ public AmqpFlagMap getServerMethodFlagMap()
+ {
+ return _serverMethodFlagMap;
+ }
+
+ public Map<AmqpVersion, AmqpFieldMap> getVersionToFieldsMap()
+ {
+ return _versionToFieldsMap;
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public LanguageConverter getGenerator()
+ {
+ return _generator;
+ }
+
+ public SingleVersionMethod asSingleVersionMethod(AmqpVersion version)
+ {
+ return new SingleVersionMethod(this, version, _generator);
+ }
+
+ public Collection<AmqpField> getFields()
+ {
+ return _fieldMap.values();
+ }
+
+ public boolean isCommon(AmqpField field)
+ {
+ return field.getVersionSet().equals(getVersionSet()) && field.isTypeAndNameConsistent(_generator);
+ }
+
+ public boolean isConsistentServerMethod()
+ {
+ AmqpVersionSet serverVersions = _serverMethodFlagMap.get(true);
+ return (serverVersions != null) && serverVersions.containsAll(_generator.getVersionSet());
+ }
+
+
+ public boolean isConsistentClientMethod()
+ {
+ AmqpVersionSet clientVersions = _clientMethodFlagMap.get(true);
+ return (clientVersions != null) && clientVersions.containsAll(_generator.getVersionSet());
+ }
+
+ public boolean isServerMethod(AmqpVersion version)
+ {
+ AmqpVersionSet serverVersions = _serverMethodFlagMap.get(true);
+ return (serverVersions != null) && serverVersions.contains(version);
+ }
+
+
+ public boolean isClientMethod(AmqpVersion version)
+ {
+ AmqpVersionSet clientVersions = _clientMethodFlagMap.get(true);
+ return (clientVersions != null) && clientVersions.contains(version);
+ }
+
+ public boolean inAllVersions()
+ {
+ return _versionSet.containsAll(_generator.getVersionSet());
+ }
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpMethodMap.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpMethodMap.java
new file mode 100644
index 0000000000..d98dab4a39
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpMethodMap.java
@@ -0,0 +1,36 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.util.TreeMap;
+
+@SuppressWarnings("serial")
+public class AmqpMethodMap extends TreeMap<String, AmqpMethod>
+{
+ public void removeVersion(AmqpVersion version)
+ {
+ for (String methodName : keySet())
+ {
+ get(methodName).removeVersion(version);
+ }
+ }
+
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpModel.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpModel.java
new file mode 100644
index 0000000000..45f0adb18d
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpModel.java
@@ -0,0 +1,132 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.io.PrintStream;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Collection;
+
+public class AmqpModel implements Printable, NodeAware
+{
+ private final Generator _generator;
+ private final AmqpClassMap classMap = new AmqpClassMap();
+ private final AmqpVersionSet _versionSet = new AmqpVersionSet();
+
+ private final Map<AmqpVersion, AmqpClassMap> _versionToClassMapMap = new HashMap<AmqpVersion, AmqpClassMap>();
+
+ public AmqpModel(Generator generator)
+ {
+ _generator = generator;
+ }
+
+ public AmqpClassMap getAmqpClassMap(AmqpVersion version)
+ {
+ return _versionToClassMapMap.get(version);
+ }
+
+
+ public AmqpVersionSet getVersionSet()
+ {
+ return _versionSet;
+ }
+
+ public boolean addFromNode(Node n, int o, AmqpVersion version)
+ throws AmqpParseException, AmqpTypeMappingException
+ {
+ _versionSet.add(version);
+ NodeList nList = n.getChildNodes();
+
+ AmqpClassMap versionSpecificClassMap = _versionToClassMapMap.get(version);
+
+ if (versionSpecificClassMap == null)
+ {
+ versionSpecificClassMap = new AmqpClassMap();
+ _versionToClassMapMap.put(version, versionSpecificClassMap);
+ }
+
+ int eCntr = 0;
+ for (int i = 0; i < nList.getLength(); i++)
+ {
+ Node c = nList.item(i);
+ if (c.getNodeName().compareTo(Utils.ELEMENT_CLASS) == 0)
+ {
+ String className = _generator.prepareClassName(Utils.getNamedAttribute(c, Utils.ATTRIBUTE_NAME));
+ AmqpClass thisClass = classMap.get(className);
+ if (thisClass == null)
+ {
+ thisClass = new AmqpClass(className, _generator);
+ classMap.put(className, thisClass);
+ }
+
+ AmqpClass versionSpecificClass = new AmqpClass(className, _generator);
+ versionSpecificClassMap.put(className, versionSpecificClass);
+
+ versionSpecificClass.addFromNode(c, eCntr, version);
+
+ if (!thisClass.addFromNode(c, eCntr++, version))
+ {
+ System.out.println("INFO: Generation supression tag found for class " + className + " - removing.");
+ thisClass.removeVersion(version);
+ classMap.remove(className);
+ }
+ }
+ }
+ return true;
+ }
+
+ public void print(PrintStream out, int marginSize, int tabSize)
+ {
+ out.println(Utils.createSpaces(marginSize) +
+ "[C]=class; [M]=method; [F]=field; [D]=domain; [I]=index; [O]=ordinal" + Utils.LINE_SEPARATOR);
+ out.println(Utils.createSpaces(marginSize) + "Model:");
+
+ for (String thisClassName : classMap.keySet())
+ {
+ AmqpClass thisClass = classMap.get(thisClassName);
+ thisClass.print(out, marginSize + tabSize, tabSize);
+ }
+ }
+
+ public LanguageConverter getGenerator()
+ {
+ return _generator;
+ }
+
+ public AmqpClassMap getClassMap()
+ {
+ return classMap;
+ }
+
+
+ public Collection<AmqpClass> getClasses()
+ {
+ return classMap.values();
+ }
+
+ public SingleVersionModel asSingleVersionModel()
+ {
+ return new SingleVersionModel(this, getVersionSet().first(), _generator);
+ }
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpOrdinalFieldMap.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpOrdinalFieldMap.java
new file mode 100644
index 0000000000..0633eff1e1
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpOrdinalFieldMap.java
@@ -0,0 +1,96 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.util.Iterator;
+import java.util.Set;
+import java.util.TreeMap;
+
+@SuppressWarnings("serial")
+public class AmqpOrdinalFieldMap extends TreeMap<Integer, String[]> implements Comparable
+{
+
+
+ public int compareTo(Object obj)
+ {
+ AmqpOrdinalFieldMap o = (AmqpOrdinalFieldMap) obj;
+ Set<Integer> thisKeySet = keySet();
+ Set<Integer> oKeySet = o.keySet();
+ if (!thisKeySet.equals(oKeySet)) // Not equal, but why?
+ {
+ // Size difference
+ int sizeDiff = thisKeySet.size() - oKeySet.size(); // -ve if this < other
+ if (sizeDiff != 0)
+ {
+ return sizeDiff;
+ }
+ // Conetent difference
+ Iterator<Integer> itr = thisKeySet.iterator();
+ Iterator<Integer> oItr = oKeySet.iterator();
+ while (itr.hasNext() && oItr.hasNext())
+ {
+ int diff = itr.next() - oItr.next(); // -ve if this < other
+ if (diff != 0)
+ {
+ return diff;
+ }
+ }
+ // We should never get here...
+ System.err.println("AmqpOrdinalFieldMap.compareTo(): " +
+ "WARNING - unable to find cause of keySet difference.");
+ }
+ // Keys are equal, now check the String[]s
+ Iterator<Integer> itr = thisKeySet.iterator();
+ Iterator<Integer> oItr = oKeySet.iterator();
+ while (itr.hasNext() && oItr.hasNext())
+ {
+ String[] thisPair = get(itr.next());
+ String[] oPair = o.get(oItr.next());
+ // Size difference
+ int sizeDiff = thisPair.length - oPair.length; // -ve if this < other
+ if (sizeDiff != 0)
+ {
+ return sizeDiff;
+ }
+ // Conetent difference
+ for (int i = 0; i < thisPair.length; i++)
+ {
+ int diff = thisPair[i].compareTo(oPair[i]);
+ if (diff != 0)
+ {
+ return diff;
+ }
+ }
+ }
+ return 0;
+ }
+
+ public String toString()
+ {
+ StringBuffer sb = new StringBuffer();
+ for (Integer thisOrdinal : keySet())
+ {
+ String[] pair = get(thisOrdinal);
+ sb.append("[" + thisOrdinal + "] " + pair[0] + " : " + pair[1] + Utils.LINE_SEPARATOR);
+ }
+ return sb.toString();
+ }
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpOrdinalVersionMap.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpOrdinalVersionMap.java
new file mode 100644
index 0000000000..fede88631a
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpOrdinalVersionMap.java
@@ -0,0 +1,76 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.util.ArrayList;
+import java.util.TreeMap;
+
+@SuppressWarnings("serial")
+public class AmqpOrdinalVersionMap extends TreeMap<Integer, AmqpVersionSet> implements VersionConsistencyCheck
+{
+ public boolean isVersionConsistent(AmqpVersionSet globalVersionSet)
+ {
+ if (size() != 1)
+ {
+ return false;
+ }
+ return get(firstKey()).equals(globalVersionSet);
+ }
+
+ public int getOrdinal(AmqpVersion version)
+ throws AmqpTypeMappingException
+ {
+ for (Integer thisOrdinal : keySet())
+ {
+ AmqpVersionSet versionSet = get(thisOrdinal);
+ if (versionSet.contains(version))
+ {
+ return thisOrdinal;
+ }
+ }
+ throw new AmqpTypeMappingException("Unable to locate version " + version + " in ordianl version map.");
+ }
+
+ public boolean removeVersion(AmqpVersion version)
+ {
+ Boolean res = false;
+ ArrayList<Integer> removeList = new ArrayList<Integer>();
+ for (Integer ordinal : keySet())
+ {
+ AmqpVersionSet versionSet = get(ordinal);
+ if (versionSet.contains(version))
+ {
+ versionSet.remove(version);
+ if (versionSet.isEmpty())
+ {
+ removeList.add(ordinal);
+ }
+ res = true;
+ }
+ }
+ // Get rid of ordinals no longer in use
+ for (Integer ordinal : removeList)
+ {
+ remove(ordinal);
+ }
+ return res;
+ }
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpOverloadedParameterMap.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpOverloadedParameterMap.java
new file mode 100644
index 0000000000..10978d0e4a
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpOverloadedParameterMap.java
@@ -0,0 +1,29 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.util.TreeMap;
+
+@SuppressWarnings("serial")
+public class AmqpOverloadedParameterMap extends TreeMap<AmqpOrdinalFieldMap, AmqpVersionSet>
+{
+
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpParseException.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpParseException.java
new file mode 100644
index 0000000000..3f3d4611fc
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpParseException.java
@@ -0,0 +1,30 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+@SuppressWarnings("serial")
+public class AmqpParseException extends RuntimeException
+{
+ public AmqpParseException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpTemplateException.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpTemplateException.java
new file mode 100644
index 0000000000..1ac09ea453
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpTemplateException.java
@@ -0,0 +1,30 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+@SuppressWarnings("serial")
+public class AmqpTemplateException extends RuntimeException
+{
+ public AmqpTemplateException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpTypeMappingException.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpTypeMappingException.java
new file mode 100644
index 0000000000..127a8835b0
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpTypeMappingException.java
@@ -0,0 +1,30 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+@SuppressWarnings("serial")
+public class AmqpTypeMappingException extends RuntimeException
+{
+ public AmqpTypeMappingException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpVersion.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpVersion.java
new file mode 100644
index 0000000000..dbeef1b895
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpVersion.java
@@ -0,0 +1,72 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+public class AmqpVersion implements Comparable<AmqpVersion>
+{
+ private final int _major;
+ private final int _minor;
+
+ public AmqpVersion(int major, int minor)
+ {
+ _major = major;
+ _minor = minor;
+ }
+
+ public AmqpVersion(AmqpVersion version)
+ {
+ _major = version.getMajor();
+ _minor = version.getMinor();
+ }
+
+ public int getMajor()
+ {
+ return _major;
+ }
+
+ public int getMinor()
+ {
+ return _minor;
+ }
+
+ public int compareTo(AmqpVersion v)
+ {
+ if (_major != v.getMajor())
+ {
+ return _major - v.getMajor();
+ }
+ if (_minor != v.getMinor())
+ {
+ return _minor - v.getMinor();
+ }
+ return 0;
+ }
+
+ public String namespace()
+ {
+ return "ver_" + _major + "_" + _minor;
+ }
+
+ public String toString()
+ {
+ return _major + "-" + _minor;
+ }
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/AmqpVersionSet.java b/qpid/gentools/src/org/apache/qpid/gentools/AmqpVersionSet.java
new file mode 100644
index 0000000000..6419e23a1e
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/AmqpVersionSet.java
@@ -0,0 +1,79 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.io.PrintStream;
+import java.util.Iterator;
+import java.util.TreeSet;
+
+@SuppressWarnings("serial")
+public class AmqpVersionSet extends TreeSet<AmqpVersion> implements Printable, Comparable<AmqpVersionSet>
+{
+ public AmqpVersionSet()
+ {
+ super();
+ }
+
+ public AmqpVersionSet(AmqpVersion version)
+ {
+ super();
+ add(version);
+ }
+
+ public AmqpVersion find(AmqpVersion version)
+ {
+ for (AmqpVersion v : this)
+ {
+ if (v.compareTo(version) == 0)
+ {
+ return v;
+ }
+ }
+ return null;
+ }
+
+ public void print(PrintStream out, int marginSize, int tabSize)
+ {
+ out.print(Utils.createSpaces(marginSize) + "Version Set: " + toString() + Utils.LINE_SEPARATOR);
+ }
+
+ public int compareTo(AmqpVersionSet other)
+ {
+ int res = size() - other.size();
+ if (res != 0)
+ {
+ return res;
+ }
+ Iterator<AmqpVersion> vItr = iterator();
+ Iterator<AmqpVersion> oItr = other.iterator();
+ while (vItr.hasNext() && oItr.hasNext())
+ {
+ AmqpVersion version = vItr.next();
+ AmqpVersion oVersion = oItr.next();
+ res = version.compareTo(oVersion);
+ if (res != 0)
+ {
+ return res;
+ }
+ }
+ return 0;
+ }
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/BitFieldGenerateMethod.java b/qpid/gentools/src/org/apache/qpid/gentools/BitFieldGenerateMethod.java
new file mode 100644
index 0000000000..d85510ee98
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/BitFieldGenerateMethod.java
@@ -0,0 +1,29 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+
+import java.util.List;
+
+public interface BitFieldGenerateMethod
+{
+ String generate(List<String> bitFieldList, int ordinal, int indentSize, int tabSize);
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/CommandGenerateMethod.java b/qpid/gentools/src/org/apache/qpid/gentools/CommandGenerateMethod.java
new file mode 100644
index 0000000000..641f50c3f8
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/CommandGenerateMethod.java
@@ -0,0 +1,26 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+public interface CommandGenerateMethod
+{
+ String generate(String codeType, AmqpField field, AmqpVersionSet versionSet, int indentSize, int tabSize, boolean notLast);
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/ConsolidatedField.java b/qpid/gentools/src/org/apache/qpid/gentools/ConsolidatedField.java
new file mode 100644
index 0000000000..9ab7eb178b
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/ConsolidatedField.java
@@ -0,0 +1,120 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: U146758
+ * Date: 06-Mar-2007
+ * Time: 09:22:21
+ * To change this template use File | Settings | File Templates.
+ */
+public class ConsolidatedField
+{
+ private final String _name;
+ private final String _type;
+ private final List<String> _underlyingFields = new ArrayList<String>();
+ private final Generator _generator;
+ private boolean _isConsolidated;
+
+ public ConsolidatedField(Generator generator, String name, String type)
+ {
+ this(generator,name,type,name,false);
+ }
+
+ public ConsolidatedField(Generator generator, String name, String type, String firstField)
+ {
+ this(generator,name,type,firstField,true);
+ }
+
+ public ConsolidatedField(Generator generator, String name, String type, String firstField, boolean consolidated)
+ {
+
+ _generator = generator;
+ _name = name;
+ _type = type;
+ _isConsolidated = consolidated;
+ _underlyingFields.add(firstField);
+
+ }
+
+
+ public void setConsolidated(boolean consolidated)
+ {
+ _isConsolidated = consolidated;
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public String getType()
+ {
+ return _type;
+ }
+
+ public String getNativeType()
+ {
+ return _generator.getNativeType(_type);
+ }
+
+ public String getEncodingType()
+ {
+ return _generator.getEncodingType(_type);
+ }
+
+ public void add(String name)
+ {
+ _underlyingFields.add(name);
+ }
+
+ public Collection<String> getUnderlyingFields()
+ {
+ return Collections.unmodifiableCollection(_underlyingFields);
+ }
+
+ public int getPosition(String fieldName)
+ {
+ return _underlyingFields.indexOf(fieldName);
+ }
+
+ public boolean isConsolidated()
+ {
+ return _isConsolidated;
+ }
+
+ public boolean isFixedSize()
+ {
+ return _generator.isFixedSizeType( getType() );
+ }
+
+ public int getSize()
+ {
+ return _generator.getTypeSize( getType() );
+ }
+
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/CppGenerator.java b/qpid/gentools/src/org/apache/qpid/gentools/CppGenerator.java
new file mode 100644
index 0000000000..4f58cba34e
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/CppGenerator.java
@@ -0,0 +1,1716 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.TreeMap;
+
+public class CppGenerator extends Generator
+{
+ protected static final String versionNamespaceStartToken = "${version_namespace_start}";
+ protected static final String versionNamespaceEndToken = "${version_namespace_end}";
+
+ // TODO: Move this to parent class
+ protected static final int FIELD_NAME = 0;
+ protected static final int FIELD_CODE_TYPE = 1;
+
+ /**
+ * A complete list of C++ reserved words. The names of varous XML elements within the AMQP
+ * specification file are used for C++ identifier names in the generated code. Each proposed
+ * name is checked against this list and is modified (by adding an '_' to the end of the
+ * name - see function parseForReservedWords()) if found to be present.
+ */
+ protected static final String[] cppReservedWords = {"and", "and_eq", "asm", "auto", "bitand",
+ "bitor", "bool", "break", "case", "catch", "char", "class", "compl", "const", "const_cast",
+ "continue", "default", "delete", "do", "DomainInfo", "double", "dynamic_cast", "else",
+ "enum", "explicit", "extern", "false", "float", "for", "friend", "goto", "if", "inline",
+ "int", "long", "mutable", "namespace", "new", "not", "not_eq", "operator", "or", "or_eq",
+ "private", "protected", "public", "register", "reinterpret_cast", "return", "short",
+ "signed", "sizeof", "static", "static_cast", "struct", "switch", "template", "this",
+ "throw", "true", "try", "typedef", "typeid", "typename", "union", "unsigned", "using",
+ "virtual", "void", "volatile", "wchar_t", "while", "xor", "xor_eq"};
+
+ /**
+ * Although not reserved words, the following list of variable names that may cause compile
+ * problems within a C++ environment because they clash with common #includes. The names of
+ * varous XML elements within the AMQP specification file are used for C++ identifier names
+ * in the generated code. Each proposed name is checked against this list and is modified
+ * (by adding an '_' to the end of the name - see function parseForReservedWords()) if found
+ * to be present. This list is best added to on an as-needed basis.
+ */
+ protected static final String[] cppCommonDefines = {"string"};
+
+ // TODO: Move this to the Generator superclass?
+ protected boolean quietFlag; // Supress warning messages to the console
+
+ private class DomainInfo
+ {
+ public String type;
+ public String size;
+ public String encodeExpression;
+ public String decodeExpression;
+
+ public DomainInfo(String domain, String size, String encodeExpression,
+ String decodeExpression)
+ {
+ this.type = domain;
+ this.size = size;
+ this.encodeExpression = encodeExpression;
+ this.decodeExpression = decodeExpression;
+ }
+ }
+
+ private static TreeMap<String, DomainInfo> typeMap = new TreeMap<String, DomainInfo>();
+
+ public CppGenerator()
+ {
+ super();
+ quietFlag = true;
+ // Load C++ type and size maps.
+ // Adjust or add to these lists as new types are added/defined.
+ // The char '#' will be replaced by the field variable name (any type).
+ // The char '~' will be replaced by the compacted bit array size (type bit only).
+ typeMap.put("bit", new DomainInfo(
+ "bool", // type
+ "~", // size
+ "", // encodeExpression
+ "")); // decodeExpression
+ typeMap.put("content", new DomainInfo(
+ "Content", // type
+ "#.size()", // size
+ "buffer.putContent(#)", // encodeExpression
+ "buffer.getContent(#)")); // decodeExpression
+ typeMap.put("long", new DomainInfo(
+ "u_int32_t", // type
+ "4", // size
+ "buffer.putLong(#)", // encodeExpression
+ "# = buffer.getLong()")); // decodeExpression
+ typeMap.put("longlong", new DomainInfo(
+ "u_int64_t", // type
+ "8", // size
+ "buffer.putLongLong(#)", // encodeExpression
+ "# = buffer.getLongLong()")); // decodeExpression
+ typeMap.put("longstr", new DomainInfo(
+ "string", // type
+ "4 + #.length()", // size
+ "buffer.putLongString(#)", // encodeExpression
+ "buffer.getLongString(#)")); // decodeExpression
+ typeMap.put("octet", new DomainInfo(
+ "u_int8_t", // type
+ "1", // size
+ "buffer.putOctet(#)", // encodeExpression
+ "# = buffer.getOctet()")); // decodeExpression
+ typeMap.put("short", new DomainInfo(
+ "u_int16_t", // type
+ "2", // size
+ "buffer.putShort(#)", // encodeExpression
+ "# = buffer.getShort()")); // decodeExpression
+ typeMap.put("shortstr", new DomainInfo(
+ "string", // type
+ "1 + #.length()", // size
+ "buffer.putShortString(#)", // encodeExpression
+ "buffer.getShortString(#)")); // decodeExpression
+ typeMap.put("table", new DomainInfo(
+ "FieldTable", // type
+ "#.size()", // size
+ "buffer.putFieldTable(#)", // encodeExpression
+ "buffer.getFieldTable(#)")); // decodeExpression
+ typeMap.put("timestamp", new DomainInfo(
+ "u_int64_t", // type
+ "8", // size
+ "buffer.putLongLong(#)", // encodeExpression
+ "buffer.getLongLong(#)")); // decodeExpression
+ }
+
+
+ public boolean isQuietFlag()
+ {
+ return quietFlag;
+ }
+
+ public void setQuietFlag(boolean quietFlag)
+ {
+ this.quietFlag = quietFlag;
+ }
+
+ // === Start of methods for Interface LanguageConverter ===
+
+ public String prepareClassName(String className)
+ {
+ return camelCaseName(className, true);
+ }
+
+ public String prepareMethodName(String methodName)
+ {
+ return camelCaseName(methodName, false);
+ }
+
+ public String prepareDomainName(String domainName)
+ {
+ return camelCaseName(domainName, false);
+ }
+
+
+ public String getGeneratedType(String domainName, AmqpVersion version)
+ throws AmqpTypeMappingException
+ {
+ String domainType = getDomainType(domainName, version);
+ if (domainType == null)
+ {
+ throw new AmqpTypeMappingException("Domain type \"" + domainName +
+ "\" not found in C++ typemap.");
+ }
+ DomainInfo info = typeMap.get(domainType);
+ if (info == null)
+ {
+ throw new AmqpTypeMappingException("Unknown domain: \"" + domainType + "\"");
+ }
+ return info.type;
+ }
+
+ // === Abstract methods from class Generator - C++-specific implementation ===
+
+ @Override
+ protected String prepareFilename(String filenameTemplate, AmqpClass thisClass, AmqpMethod method,
+ AmqpField field, AmqpVersion version)
+ {
+ StringBuffer sb = new StringBuffer(filenameTemplate);
+ if (thisClass != null)
+ {
+ replaceToken(sb, "${CLASS}", thisClass.getName());
+ }
+ if (method != null)
+ {
+ replaceToken(sb, "${METHOD}", method.getName());
+ }
+ if (field != null)
+ {
+ replaceToken(sb, "${FIELD}", field.getName());
+ }
+ return sb.toString();
+ }
+
+ @Override
+ protected void processModelTemplate(NamedTemplate template)
+ {
+ processTemplate(template, null, null, null, null);
+ }
+
+ @Override
+ protected void processClassTemplate(NamedTemplate template, AmqpClass thisClass)
+ {
+ processTemplate(template, thisClass, null, null, null);
+ }
+
+ @Override
+ protected void processMethodTemplate(NamedTemplate template, AmqpClass thisClass,
+ AmqpMethod method)
+ {
+ StringBuffer sb = new StringBuffer(template.getTemplate());
+ String filename = prepareFilename(getTemplateFileName(sb), thisClass, method, null, null);
+ boolean templateProcessedFlag = false;
+
+ // If method is not version consistent, create a namespace for each version
+ // i.e. copy the bit between the versionNamespaceStartToken and versionNamespaceEndToken
+ // once for each namespace.
+ if (method != null)
+ {
+ if (!method.isVersionConsistent(getVersionSet()))
+ {
+ int namespaceStartIndex = sb.indexOf(versionNamespaceStartToken);
+ int namespaceEndIndex = sb.indexOf(versionNamespaceEndToken) +
+ versionNamespaceEndToken.length();
+ if (namespaceStartIndex >= 0 && namespaceEndIndex >= 0 &&
+ namespaceStartIndex <= namespaceEndIndex)
+ {
+ String namespaceSpan = sb.substring(namespaceStartIndex, namespaceEndIndex) + CR;
+ sb.delete(namespaceStartIndex, namespaceEndIndex);
+ for (AmqpVersion v : method.getVersionSet())
+ {
+ StringBuffer nssb = new StringBuffer(namespaceSpan);
+ processTemplate(nssb, thisClass, method, null, template.getName(), v);
+ sb.insert(namespaceStartIndex, nssb);
+ }
+ // Process all tokens *not* within the namespace span prior to inserting namespaces
+ processTemplate(sb, thisClass, method, null, template.getName(), null);
+ }
+ templateProcessedFlag = true;
+ }
+ }
+ // Remove any remaining namespace tags
+ int nsTokenIndex = sb.indexOf(versionNamespaceStartToken);
+ while (nsTokenIndex > 0)
+ {
+ sb.delete(nsTokenIndex, nsTokenIndex + versionNamespaceStartToken.length());
+ nsTokenIndex = sb.indexOf(versionNamespaceStartToken);
+ }
+ nsTokenIndex = sb.indexOf(versionNamespaceEndToken);
+ while (nsTokenIndex > 0)
+ {
+ sb.delete(nsTokenIndex, nsTokenIndex + versionNamespaceEndToken.length());
+ nsTokenIndex = sb.indexOf(versionNamespaceEndToken);
+ }
+
+ if (!templateProcessedFlag)
+ {
+ processTemplate(sb, thisClass, method, null, template.getName(), null);
+ }
+ writeTargetFile(sb, new File(getOutputDirectory() + Utils.FILE_SEPARATOR + filename));
+ generatedFileCounter++;
+ }
+
+ @Override
+ protected void processTemplate(NamedTemplate template, AmqpClass thisClass, AmqpMethod method,
+ AmqpField field, AmqpVersion version)
+ {
+ StringBuffer sb = new StringBuffer(template.getTemplate());
+ String filename = prepareFilename(getTemplateFileName(sb), thisClass, method, field, version);
+ processTemplate(sb, thisClass, method, field, template.getName(), null);
+ writeTargetFile(sb, new File(getOutputDirectory() + Utils.FILE_SEPARATOR + filename));
+ generatedFileCounter++;
+ }
+
+ protected void processTemplate(StringBuffer sb, AmqpClass thisClass, AmqpMethod method,
+ AmqpField field, String templateFileName, AmqpVersion version)
+ {
+ try
+ {
+ processAllLists(sb, thisClass, method, version);
+ }
+ catch (AmqpTemplateException e)
+ {
+ System.out.println("ERROR: " + templateFileName + ": " + e.getMessage());
+ }
+ try
+ {
+ processAllTokens(sb, thisClass, method, field, version);
+ }
+ catch (AmqpTemplateException e)
+ {
+ System.out.println("ERROR: " + templateFileName + ": " + e.getMessage());
+ }
+ }
+
+ @Override
+ protected String processToken(String token, AmqpClass thisClass, AmqpMethod method, AmqpField field,
+ AmqpVersion version)
+ {
+ if (token.compareTo("${GENERATOR}") == 0)
+ {
+ return GENERATOR_INFO;
+ }
+ if (token.compareTo("${CLASS}") == 0 && thisClass != null)
+ {
+ return thisClass.getName();
+ }
+ if (token.compareTo("${CLASS_ID_INIT}") == 0 && thisClass != null)
+ {
+ if (version == null)
+ {
+ return String.valueOf(thisClass.getIndexMap().firstKey());
+ }
+ return getIndex(thisClass.getIndexMap(), version);
+ }
+ if (token.compareTo("${METHOD}") == 0 && method != null)
+ {
+ return method.getName();
+ }
+ if (token.compareTo("${METHOD_ID_INIT}") == 0 && method != null)
+ {
+ if (version == null)
+ {
+ return String.valueOf(method.getIndexMap().firstKey());
+ }
+ return getIndex(method.getIndexMap(), version);
+ }
+ if (token.compareTo("${FIELD}") == 0 && field != null)
+ {
+ return field.getName();
+ }
+ if (token.compareTo(versionNamespaceStartToken) == 0 && version != null)
+ {
+ return "namespace " + version.namespace() + CR + "{";
+ }
+ if (token.compareTo(versionNamespaceEndToken) == 0 && version != null)
+ {
+ return "} // namespace " + version.namespace();
+ }
+ if (token.compareTo("${mb_constructor_with_initializers}") == 0)
+ {
+ return generateConstructor(thisClass, method, version, 4, 4);
+ }
+ if (token.compareTo("${mb_server_operation_invoke}") == 0)
+ {
+ return generateServerOperationsInvoke(thisClass, method, version, 4, 4);
+ }
+ if (token.compareTo("${mb_buffer_param}") == 0)
+ {
+ return method.getFieldMap().size() > 0 ? " buffer" : "";
+ }
+ if (token.compareTo("${hv_latest_major}") == 0)
+ {
+ return String.valueOf(getVersionSet().last().getMajor());
+ }
+ if (token.compareTo("${hv_latest_minor}") == 0)
+ {
+ return String.valueOf(getVersionSet().last().getMinor());
+ }
+
+ throw new AmqpTemplateException("Template token " + token + " unknown.");
+ }
+
+ @Override
+ protected void processClassList(StringBuffer sb, int listMarkerStartIndex, int listMarkerEndIndex,
+ AmqpModel model, AmqpVersion version)
+ {
+ String codeSnippet;
+ int lend = sb.indexOf(CR, listMarkerStartIndex) + 1; // Include cr at end of line
+ String tline = sb.substring(listMarkerEndIndex, lend); // Line excluding line marker, including cr
+ int tokxStart = tline.indexOf('$');
+ String token = tline.substring(tokxStart).trim();
+ sb.delete(listMarkerStartIndex, lend);
+
+ // ClientOperations.h
+ if (token.compareTo("${coh_method_handler_get_method}") == 0)
+ {
+ codeSnippet = generateOpsMethodHandlerGetMethods(model, false, 4);
+ }
+ else if (token.compareTo("${coh_inner_class}") == 0)
+ {
+ codeSnippet = generateOpsInnerClasses(model, false, 4, 4);
+ }
+
+ // ServerOperations.h
+ else if (token.compareTo("${soh_method_handler_get_method}") == 0)
+ {
+ codeSnippet = generateOpsMethodHandlerGetMethods(model, true, 4);
+ }
+ else if (token.compareTo("${soh_inner_class}") == 0)
+ {
+ codeSnippet = generateOpsInnerClasses(model, true, 4, 4);
+ }
+
+ // ClientProxy.h/cpp
+ else if (token.compareTo("${cph_inner_class_instance}") == 0)
+ {
+ codeSnippet = generateProxyInnerClassInstances(model, false, 4);
+ }
+ else if (token.compareTo("${cph_inner_class_get_method}") == 0)
+ {
+ codeSnippet = generateProxyInnerClassGetMethodDecls(model, false, 4);
+ }
+ else if (token.compareTo("${cph_inner_class_defn}") == 0)
+ {
+ codeSnippet = generateProxyInnerClassDefinitions(model, false, 4, 4);
+ }
+ else if (token.compareTo("${cpc_constructor_initializer}") == 0)
+ {
+ codeSnippet = generateProxyConstructorInitializers(model, false, 4);
+ }
+ else if (token.compareTo("${cpc_inner_class_get_method}") == 0)
+ {
+ codeSnippet = generateProxyInnerClassGetMethodImpls(model, false, 0, 4);
+ }
+ else if (token.compareTo("${cpc_inner_class_impl}") == 0)
+ {
+ codeSnippet = generateProxyInnerClassImpl(model, false, 0, 4);
+ }
+ else if (token.compareTo("${cph_handler_pointer_defn}") == 0)
+ {
+ codeSnippet = generateHandlerPointerDefinitions(model, false, 4);
+ }
+ else if (token.compareTo("${cph_handler_pointer_get_method}") == 0)
+ {
+ codeSnippet = generateHandlerPointerGetMethods(model, false, 4);
+ }
+
+ // SerrverProxy.h/cpp
+ else if (token.compareTo("${sph_inner_class_instance}") == 0)
+ {
+ codeSnippet = generateProxyInnerClassInstances(model, true, 4);
+ }
+ else if (token.compareTo("${sph_inner_class_get_method}") == 0)
+ {
+ codeSnippet = generateProxyInnerClassGetMethodDecls(model, true, 4);
+ }
+ else if (token.compareTo("${sph_inner_class_defn}") == 0)
+ {
+ codeSnippet = generateProxyInnerClassDefinitions(model, true, 4, 4);
+ }
+ else if (token.compareTo("${spc_constructor_initializer}") == 0)
+ {
+ codeSnippet = generateProxyConstructorInitializers(model, true, 4);
+ }
+ else if (token.compareTo("${spc_inner_class_get_method}") == 0)
+ {
+ codeSnippet = generateProxyInnerClassGetMethodImpls(model, true, 0, 4);
+ }
+ else if (token.compareTo("${spc_inner_class_impl}") == 0)
+ {
+ codeSnippet = generateProxyInnerClassImpl(model, true, 0, 4);
+ }
+ else if (token.compareTo("${sph_handler_pointer_defn}") == 0)
+ {
+ codeSnippet = generateHandlerPointerDefinitions(model, true, 4);
+ }
+ else if (token.compareTo("${sph_handler_pointer_get_method}") == 0)
+ {
+ codeSnippet = generateHandlerPointerGetMethods(model, true, 4);
+ }
+
+ // amqp_methods.h/cpp
+ else if (token.compareTo("${mh_method_body_class_indlude}") == 0)
+ {
+ codeSnippet = generateMethodBodyIncludeList(model, 0);
+ }
+ else if (token.compareTo("${mh_method_body_class_instance}") == 0)
+ {
+ codeSnippet = generateMethodBodyInstances(model, 0);
+ }
+ else if (token.compareTo("${mc_create_method_body_map_entry}") == 0)
+ {
+ codeSnippet = generateMethodBodyMapEntry(model, 4);
+ }
+
+ else // Oops!
+ {
+ throw new AmqpTemplateException("Template token \"" + token + "\" unknown.");
+ }
+ sb.insert(listMarkerStartIndex, codeSnippet);
+ }
+
+ @Override
+ protected void processMethodList(StringBuffer sb, int listMarkerStartIndex, int listMarkerEndIndex,
+ AmqpClass thisClass)
+ {
+ String codeSnippet;
+ int lend = sb.indexOf(CR, listMarkerStartIndex) + 1; // Include cr at end of line
+ String tline = sb.substring(listMarkerEndIndex, lend); // Line excluding line marker, including cr
+ int tokxStart = tline.indexOf('$');
+ String token = tline.substring(tokxStart).trim();
+ sb.delete(listMarkerStartIndex, lend);
+
+ if (token.compareTo("${cpc_method_body_include}") == 0)
+ {
+ codeSnippet = generateMethodBodyIncludes(thisClass, 0);
+ }
+ else if (token.compareTo("${spc_method_body_include}") == 0)
+ {
+ codeSnippet = generateMethodBodyIncludes(thisClass, 0);
+ }
+ else if (token.compareTo("${mc_method_body_include}") == 0)
+ {
+ codeSnippet = generateMethodBodyIncludes(thisClass, 0);
+ }
+
+ else // Oops!
+ {
+ throw new AmqpTemplateException("Template token " + token + " unknown.");
+ }
+ sb.insert(listMarkerStartIndex, codeSnippet);
+ }
+
+ @Override
+ protected void processFieldList(StringBuffer sb, int listMarkerStartIndex, int listMarkerEndIndex,
+ AmqpFieldMap fieldMap, AmqpVersion version)
+ {
+ String codeSnippet;
+ int lend = sb.indexOf(CR, listMarkerStartIndex) + 1; // Include cr at end of line
+ String tline = sb.substring(listMarkerEndIndex, lend); // Line excluding line marker, including cr
+ int tokxStart = tline.indexOf('$');
+ String token = tline.substring(tokxStart).trim();
+ sb.delete(listMarkerStartIndex, lend);
+
+ if (token.compareTo("${mb_field_declaration}") == 0)
+ {
+ codeSnippet = generateFieldDeclarations(fieldMap, version, 4);
+ }
+ else if (token.compareTo("${mb_field_get_method}") == 0)
+ {
+ codeSnippet = generateFieldGetMethods(fieldMap, version, 4);
+ }
+ else if (token.compareTo("${mb_field_print}") == 0)
+ {
+ codeSnippet = generatePrintMethodContents(fieldMap, version, 8);
+ }
+ else if (token.compareTo("${mb_body_size}") == 0)
+ {
+ codeSnippet = generateBodySizeMethodContents(fieldMap, version, 8);
+ }
+ else if (token.compareTo("${mb_encode}") == 0)
+ {
+ codeSnippet = generateEncodeMethodContents(fieldMap, version, 8);
+ }
+ else if (token.compareTo("${mb_decode}") == 0)
+ {
+ codeSnippet = generateDecodeMethodContents(fieldMap, version, 8);
+ }
+
+ else // Oops!
+ {
+ throw new AmqpTemplateException("Template token " + token + " unknown.");
+ }
+ sb.insert(listMarkerStartIndex, codeSnippet);
+ }
+
+ @Override
+ protected void processConstantList(StringBuffer sb, int listMarkerStartIndex, int listMarkerEndIndex,
+ AmqpConstantSet constantSet)
+ {
+ String codeSnippet;
+ int lend = sb.indexOf(CR, listMarkerStartIndex) + 1; // Include cr at end of line
+ String tline = sb.substring(listMarkerEndIndex, lend); // Line excluding line marker, including cr
+ int tokxStart = tline.indexOf('$');
+ String token = tline.substring(tokxStart).trim();
+ sb.delete(listMarkerStartIndex, lend);
+
+ if (token.compareTo("${ch_get_value_method}") == 0)
+ {
+ codeSnippet = generateConstantGetMethods(constantSet, 4, 4);
+ }
+
+ else // Oops!
+ {
+ throw new AmqpTemplateException("Template token " + token + " unknown.");
+ }
+ sb.insert(listMarkerStartIndex, codeSnippet);
+ }
+
+ // === Protected and private helper functions unique to C++ implementation ===
+
+ // Methods for generation of code snippets for AMQP_Constants.h file
+
+ protected String generateConstantGetMethods(AmqpConstantSet constantSet,
+ int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ for (AmqpConstant thisConstant : constantSet.getContstants())
+ {
+ if (thisConstant.isVersionConsistent(getVersionSet()))
+ {
+ // return a constant
+ String value = thisConstant.firstKey();
+ sb.append(indent + "static const char* " + thisConstant.getName() + "() { return \"" +
+ thisConstant.firstKey() + "\"; }" + CR);
+ if (Utils.containsOnlyDigits(value))
+ {
+ sb.append(indent + "static int " + thisConstant.getName() + "AsInt() { return " +
+ thisConstant.firstKey() + "; }" + CR);
+ }
+ if (Utils.containsOnlyDigitsAndDecimal(value))
+ {
+ sb.append(indent + "static double " + thisConstant.getName() + "AsDouble() { return (double)" +
+ thisConstant.firstKey() + "; }" + CR);
+ }
+ sb.append(CR);
+ }
+ else
+ {
+ // Return version-specific constant
+ sb.append(generateVersionDependentGet(thisConstant, "const char*", "", "\"", "\"", indentSize, tabSize));
+ sb.append(generateVersionDependentGet(thisConstant, "int", "AsInt", "", "", indentSize, tabSize));
+ sb.append(generateVersionDependentGet(thisConstant, "double", "AsDouble", "(double)", "", indentSize, tabSize));
+ sb.append(CR);
+ }
+ }
+ return sb.toString();
+ }
+
+ protected String generateVersionDependentGet(AmqpConstant constant, String methodReturnType,
+ String methodNameSuffix, String returnPrefix, String returnPostfix, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+ sb.append(indent + methodReturnType + " " + constant.getName() + methodNameSuffix +
+ "() const" + CR);
+ sb.append(indent + "{" + CR);
+ boolean first = true;
+ for (String thisValue : constant.keySet())
+ {
+ AmqpVersionSet versionSet = constant.get(thisValue);
+ sb.append(indent + tab + (first ? "" : "else ") + "if (" + generateVersionCheck(versionSet) +
+ ")" + CR);
+ sb.append(indent + tab + "{" + CR);
+ if (methodReturnType.compareTo("int") == 0 && !Utils.containsOnlyDigits(thisValue))
+ {
+ sb.append(generateConstantDeclarationException(constant.getName(), methodReturnType,
+ indentSize + (2 * tabSize), tabSize));
+ }
+ else if (methodReturnType.compareTo("double") == 0 && !Utils.containsOnlyDigitsAndDecimal(thisValue))
+ {
+ sb.append(generateConstantDeclarationException(constant.getName(), methodReturnType,
+ indentSize + (2 * tabSize), tabSize));
+ }
+ else
+ {
+ sb.append(indent + tab + tab + "return " + returnPrefix + thisValue + returnPostfix + ";" + CR);
+ }
+ sb.append(indent + tab + "}" + CR);
+ first = false;
+ }
+ sb.append(indent + tab + "else" + CR);
+ sb.append(indent + tab + "{" + CR);
+ sb.append(indent + tab + tab + "std::stringstream ss;" + CR);
+ sb.append(indent + tab + tab + "ss << \"Constant \\\"" + constant.getName() +
+ "\\\" is undefined for AMQP version \" <<" + CR);
+ sb.append(indent + tab + tab + tab + "version.toString() << \".\";" + CR);
+ sb.append(indent + tab + tab + "throw ProtocolVersionException(ss.str());" + CR);
+ sb.append(indent + tab + "}" + CR);
+ sb.append(indent + "}" + CR);
+ return sb.toString();
+ }
+
+ protected String generateConstantDeclarationException(String name, String methodReturnType,
+ int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+ sb.append(indent + "std::stringstream ss;" + CR);
+ sb.append(indent + "ss << \"Constant \\\"" + name + "\\\" cannot be converted to type " +
+ methodReturnType + " for AMQP version \" <<" + CR);
+ sb.append(indent + tab + "version.toString() << \".\";" + CR);
+ sb.append(indent + "throw ProtocolVersionException(ss.str());" + CR);
+ return sb.toString();
+ }
+
+ // Methods used for generation of code snippets for Server/ClientOperations class generation
+
+ protected String generateOpsMethodHandlerGetMethods(AmqpModel model, boolean serverFlag, int indentSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ for (String thisClassName : model.getClassMap().keySet())
+ {
+ AmqpClass thisClass = model.getClassMap().get(thisClassName);
+ // Only generate for this class if there is at least one method of the
+ // required chassis (server/client flag).
+ boolean chassisFoundFlag = false;
+ for (String thisMethodName : thisClass.getMethodMap().keySet())
+ {
+ AmqpMethod method = thisClass.getMethodMap().get(thisMethodName);
+ boolean clientChassisFlag = method.getClientMethodFlagMap().isSet();
+ boolean serverChassisFlag = method.getServerMethodFlagMap().isSet();
+ if ((serverFlag && serverChassisFlag) || (!serverFlag && clientChassisFlag))
+ {
+ chassisFoundFlag = true;
+ }
+ }
+ if (chassisFoundFlag)
+ {
+ sb.append(indent + "virtual AMQP_" + (serverFlag ? "Server" : "Client") + "Operations::" +
+ thisClass.getName() + "Handler* get" + thisClass.getName() + "Handler() = 0;" + CR);
+ }
+ }
+ return sb.toString();
+ }
+
+ protected String generateOpsInnerClasses(AmqpModel model, boolean serverFlag, int indentSize, int tabSize)
+ {
+
+ String proxyClassName = "AMQP_" + (serverFlag ? "Server" : "Client") + "Proxy";
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+ boolean first = true;
+ for (String thisClassName : model.getClassMap().keySet())
+ {
+ AmqpClass thisClass = model.getClassMap().get(thisClassName);
+ String handlerClassName = thisClass.getName() + "Handler";
+ if (!first)
+ {
+ sb.append(CR);
+ }
+ sb.append(indent + "// ==================== class " + handlerClassName +
+ " ====================" + CR);
+ sb.append(indent + "class " + handlerClassName);
+ if (thisClass.getVersionSet().size() != getVersionSet().size())
+ {
+ sb.append(" // AMQP Version(s) " + thisClass.getVersionSet() + CR);
+ }
+ else
+ {
+ sb.append(CR);
+ }
+ sb.append(indent + "{" + CR);
+ sb.append(indent + "private:" + CR);
+ sb.append(indent + tab + proxyClassName + "* parent;" + CR);
+ sb.append(CR);
+ sb.append(indent + tab + "// Constructors and destructors" + CR);
+ sb.append(CR);
+ sb.append(indent + "protected:" + CR);
+ sb.append(indent + tab + handlerClassName + "() {}" + CR);
+ sb.append(indent + "public:" + CR);
+ sb.append(indent + tab + handlerClassName +
+ "(" + proxyClassName + "* _parent) {parent = _parent;}" + CR);
+ sb.append(indent + tab + "virtual ~" + handlerClassName + "() {}" + CR);
+ sb.append(CR);
+ sb.append(indent + tab + "// Protocol methods" + CR);
+ sb.append(CR);
+ sb.append(generateInnerClassMethods(thisClass, serverFlag, true, indentSize + tabSize, tabSize));
+ sb.append(indent + "}; // class " + handlerClassName + CR);
+ first = false;
+ }
+ return sb.toString();
+ }
+
+ protected String generateInnerClassMethods(AmqpClass thisClass, boolean serverFlag,
+ boolean abstractMethodFlag, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ String outerClassName = "AMQP_" + (serverFlag ? "Server" : "Client") + (abstractMethodFlag ? "Operations"
+ : "Proxy");
+ boolean first = true;
+ for (String thisMethodName : thisClass.getMethodMap().keySet())
+ {
+ AmqpMethod method = thisClass.getMethodMap().get(thisMethodName);
+ boolean clientChassisFlag = method.getClientMethodFlagMap().isSet();
+ boolean serverChassisFlag = method.getServerMethodFlagMap().isSet();
+ if ((serverFlag && serverChassisFlag) || (!serverFlag && clientChassisFlag))
+ {
+ String methodName = parseForReservedWords(method.getName(), outerClassName + "." + thisClass.getName());
+ AmqpOverloadedParameterMap overloadededParameterMap =
+ method.getOverloadedParameterLists(thisClass.getVersionSet(), this);
+ for (AmqpOrdinalFieldMap thisFieldMap : overloadededParameterMap.keySet())
+ {
+ AmqpVersionSet versionSet = overloadededParameterMap.get(thisFieldMap);
+ if (!first)
+ {
+ sb.append(CR);
+ }
+ sb.append(indent + "virtual void " + methodName + "( u_int16_t channel");
+ sb.append(generateMethodParameterList(thisFieldMap, indentSize + (5 * tabSize), true, true, true));
+ sb.append(" )");
+ if (abstractMethodFlag)
+ {
+ sb.append(" = 0");
+ }
+ sb.append(";");
+ if (versionSet.size() != getVersionSet().size())
+ {
+ sb.append(" // AMQP Version(s) " + versionSet);
+ }
+ sb.append(CR);
+ first = false;
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ // Methods used for generation of code snippets for Server/ClientProxy class generation
+
+ protected String generateHandlerPointerDefinitions(AmqpModel model, boolean serverFlag,
+ int indentSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ String outerClassName = "AMQP_" + (serverFlag ? "Server" : "Client") + "Operations";
+ for (String thisClassName : model.getClassMap().keySet())
+ {
+ AmqpClass thisClass = model.getClassMap().get(thisClassName);
+ sb.append(indent + outerClassName + "::" + thisClass.getName() + "Handler* " +
+ thisClass.getName() + "HandlerPtr;" + CR);
+ }
+ return sb.toString();
+ }
+
+ protected String generateHandlerPointerGetMethods(AmqpModel model, boolean serverFlag,
+ int indentSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ String outerClassName = "AMQP_" + (serverFlag ? "Server" : "Client") + "Operations";
+ for (String thisClassName : model.getClassMap().keySet())
+ {
+ AmqpClass thisClass = model.getClassMap().get(thisClassName);
+ sb.append(indent + "virtual inline " + outerClassName + "::" + thisClass.getName() + "Handler* get" +
+ thisClass.getName() + "Handler() { return &" + Utils.firstLower(thisClass.getName()) + ";}" + CR);
+ }
+ return sb.toString();
+ }
+
+ protected String generateProxyInnerClassInstances(AmqpModel model, boolean serverFlag,
+ int indentSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ String outerClassName = "AMQP_" + (serverFlag ? "Server" : "Client") + "Proxy";
+ for (String thisClassName : model.getClassMap().keySet())
+ {
+ AmqpClass thisClass = model.getClassMap().get(thisClassName);
+ String instanceName = parseForReservedWords(Utils.firstLower(thisClass.getName()), outerClassName);
+ String className = parseForReservedWords(thisClass.getName(), null);
+ sb.append(indent + className + " " + instanceName + ";");
+ if (thisClass.getVersionSet().size() != getVersionSet().size())
+ {
+ sb.append(" // AMQP Version(s) " + thisClass.getVersionSet() + CR);
+ }
+ else
+ {
+ sb.append(CR);
+ }
+ }
+ return sb.toString();
+ }
+
+ protected String generateProxyInnerClassGetMethodDecls(AmqpModel model, boolean serverFlag,
+ int indentSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ String outerClassName = "AMQP_" + (serverFlag ? "Server" : "Client") + "Proxy";
+ for (String thisClassName : model.getClassMap().keySet())
+ {
+ AmqpClass thisClass = model.getClassMap().get(thisClassName);
+ String className = parseForReservedWords(thisClass.getName(), outerClassName);
+ sb.append(indent + className + "& get" + className + "();");
+ if (thisClass.getVersionSet().size() != getVersionSet().size())
+ {
+ sb.append(" // AMQP Version(s) " + thisClass.getVersionSet() + CR);
+ }
+ else
+ {
+ sb.append(CR);
+ }
+ }
+ return sb.toString();
+ }
+
+ protected String generateProxyInnerClassDefinitions(AmqpModel model, boolean serverFlag,
+ int indentSize, int tabSize)
+ {
+ String proxyClassName = "AMQP_" + (serverFlag ? "Server" : "Client") + "Proxy";
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+ boolean first = true;
+ for (String thisClassName : model.getClassMap().keySet())
+ {
+ AmqpClass thisClass = model.getClassMap().get(thisClassName);
+ String className = thisClass.getName();
+ String superclassName = "AMQP_" + (serverFlag ? "Server" : "Client") + "Operations::" +
+ thisClass.getName() + "Handler";
+ if (!first)
+ {
+ sb.append(CR);
+ }
+ sb.append(indent + "// ==================== class " + className +
+ " ====================" + CR);
+ sb.append(indent + "class " + className + " : virtual public " + superclassName);
+ if (thisClass.getVersionSet().size() != getVersionSet().size())
+ {
+ sb.append(" // AMQP Version(s) " + thisClass.getVersionSet() + CR);
+ }
+ else
+ {
+ sb.append(CR);
+ }
+ sb.append(indent + "{" + CR);
+ sb.append(indent + "private:" + CR);
+ sb.append(indent + tab + "OutputHandler* out;" + CR);
+ sb.append(indent + tab + proxyClassName + "* parent;" + CR);
+ sb.append(CR);
+ sb.append(indent + "public:" + CR);
+ sb.append(indent + tab + "// Constructors and destructors" + CR);
+ sb.append(CR);
+ sb.append(indent + tab + className + "(OutputHandler* out, " + proxyClassName + "* _parent) : " + CR);
+ sb.append(indent + tab + tab + "out(out) {parent = _parent;}" + CR);
+ sb.append(indent + tab + "virtual ~" + className + "() {}" + CR);
+ sb.append(CR);
+ sb.append(indent + tab + "// Protocol methods" + CR);
+ sb.append(CR);
+ sb.append(generateInnerClassMethods(thisClass, serverFlag, false, indentSize + tabSize, tabSize));
+ sb.append(indent + "}; // class " + className + CR);
+ first = false;
+ }
+ return sb.toString();
+ }
+
+ protected String generateProxyConstructorInitializers(AmqpModel model, boolean serverFlag,
+ int indentSize)
+ {
+ String outerClassName = "AMQP_" + (serverFlag ? "Server" : "Client") + "Proxy";
+ String superclassName = "AMQP_" + (serverFlag ? "Server" : "Client") + "Operations";
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer(indent + superclassName + "(major, minor)," + CR);
+ sb.append(indent + "version(major, minor)," + CR);
+ sb.append(indent + "out(out)");
+ Iterator<String> cItr = model.getClassMap().keySet().iterator();
+ while (cItr.hasNext())
+ {
+ AmqpClass thisClass = model.getClassMap().get(cItr.next());
+ String instanceName = parseForReservedWords(Utils.firstLower(thisClass.getName()), outerClassName);
+ sb.append("," + CR);
+ sb.append(indent + instanceName + "(out, this)");
+ if (!cItr.hasNext())
+ {
+ sb.append(CR);
+ }
+ }
+ return sb.toString();
+ }
+
+ protected String generateProxyInnerClassGetMethodImpls(AmqpModel model, boolean serverFlag,
+ int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+ String outerClassName = "AMQP_" + (serverFlag ? "Server" : "Client") + "Proxy";
+ Iterator<String> cItr = model.getClassMap().keySet().iterator();
+ while (cItr.hasNext())
+ {
+ AmqpClass thisClass = model.getClassMap().get(cItr.next());
+ String className = thisClass.getName();
+ String instanceName = parseForReservedWords(Utils.firstLower(thisClass.getName()), outerClassName);
+ sb.append(indent + outerClassName + "::" + className + "& " +
+ outerClassName + "::get" + className + "()" + CR);
+ sb.append(indent + "{" + CR);
+ if (thisClass.getVersionSet().size() != getVersionSet().size())
+ {
+ sb.append(indent + tab + "if (!" + generateVersionCheck(thisClass.getVersionSet()) + ")" + CR);
+ sb.append(indent + tab + tab + "throw new ProtocolVersionException();" + CR);
+ }
+ sb.append(indent + tab + "return " + instanceName + ";" + CR);
+ sb.append(indent + "}" + CR);
+ if (cItr.hasNext())
+ {
+ sb.append(CR);
+ }
+ }
+ return sb.toString();
+ }
+
+ protected String generateProxyInnerClassImpl(AmqpModel model, boolean serverFlag,
+ int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ boolean firstClassFlag = true;
+ for (String thisClassName : model.getClassMap().keySet())
+ {
+ AmqpClass thisClass = model.getClassMap().get(thisClassName);
+ String className = thisClass.getName();
+ if (!firstClassFlag)
+ {
+ sb.append(CR);
+ }
+ sb.append(indent + "// ==================== class " + className +
+ " ====================" + CR);
+ sb.append(generateInnerClassMethodImpls(thisClass, serverFlag, indentSize, tabSize));
+ firstClassFlag = false;
+ }
+ return sb.toString();
+ }
+
+ protected String generateInnerClassMethodImpls(AmqpClass thisClass, boolean serverFlag,
+ int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ String outerclassName = "AMQP_" + (serverFlag ? "Server" : "Client") + "Proxy";
+ boolean first = true;
+ for (String thisMethodName : thisClass.getMethodMap().keySet())
+ {
+ AmqpMethod method = thisClass.getMethodMap().get(thisMethodName);
+ String methodBodyClassName = thisClass.getName() + Utils.firstUpper(method.getName()) + "Body";
+ boolean clientChassisFlag = method.getClientMethodFlagMap().isSet();
+ boolean serverChassisFlag = method.getServerMethodFlagMap().isSet();
+ boolean versionConsistentFlag = method.isVersionConsistent(getVersionSet());
+ if ((serverFlag && serverChassisFlag) || (!serverFlag && clientChassisFlag))
+ {
+ String methodName = parseForReservedWords(method.getName(), outerclassName + "." + thisClass.getName());
+ AmqpOverloadedParameterMap overloadededParameterMap =
+ method.getOverloadedParameterLists(thisClass.getVersionSet(), this);
+ for (AmqpOrdinalFieldMap thisFieldMap : overloadededParameterMap.keySet())
+ {
+ AmqpVersionSet versionSet = overloadededParameterMap.get(thisFieldMap);
+ if (!first)
+ {
+ sb.append(CR);
+ }
+ sb.append(indent + "void " + outerclassName + "::" + thisClass.getName() + "::" +
+ methodName + "( u_int16_t channel");
+ sb.append(generateMethodParameterList(thisFieldMap, indentSize + (5 * tabSize), true, true, true));
+ sb.append(" )");
+ if (versionSet.size() != getVersionSet().size())
+ {
+ sb.append(" // AMQP Version(s) " + versionSet);
+ }
+ sb.append(CR);
+ sb.append(indent + "{" + CR);
+ sb.append(generateMethodBodyCallContext(thisFieldMap, outerclassName, methodBodyClassName,
+ versionConsistentFlag, versionSet, indentSize + tabSize, tabSize));
+ sb.append(indent + "}" + CR);
+ sb.append(CR);
+ first = false;
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ protected String generateMethodBodyCallContext(AmqpOrdinalFieldMap fieldMap, String outerclassName,
+ String methodBodyClassName, boolean versionConsistentFlag, AmqpVersionSet versionSet,
+ int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+ if (versionConsistentFlag)
+ {
+ sb.append(generateMethodBodyCall(fieldMap, methodBodyClassName, null, indentSize, tabSize));
+ }
+ else
+ {
+ boolean firstOverloadedMethodFlag = true;
+ for (AmqpVersion thisVersion : versionSet)
+ {
+ sb.append(indent);
+ if (!firstOverloadedMethodFlag)
+ {
+ sb.append("else ");
+ }
+ sb.append("if (" + generateVersionCheck(thisVersion) + ")" + CR);
+ sb.append(indent + "{" + CR);
+ sb.append(generateMethodBodyCall(fieldMap, methodBodyClassName, thisVersion,
+ indentSize + tabSize, tabSize));
+ sb.append(indent + "}" + CR);
+ firstOverloadedMethodFlag = false;
+ }
+ sb.append(indent + "else" + CR);
+ sb.append(indent + "{" + CR);
+ sb.append(indent + tab + "std::stringstream ss;" + CR);
+ sb.append(indent + tab + "ss << \"Call to " + outerclassName + "::" + methodBodyClassName +
+ "(u_int16_t" + generateMethodParameterList(fieldMap, 0, true, true, false) + ")\"" + CR);
+ sb.append(indent + tab + tab + "<< \" is invalid for AMQP version \" << version.toString() << \".\";" + CR);
+ sb.append(indent + tab + "throw new ProtocolVersionException(ss.str());" + CR);
+ sb.append(indent + "}" + CR);
+ }
+ return sb.toString();
+ }
+
+ protected String generateMethodBodyCall(AmqpOrdinalFieldMap fieldMap, String methodBodyClassName,
+ AmqpVersion version, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ String namespace = version != null ? version.namespace() + "::" : "";
+ StringBuffer sb = new StringBuffer(indent + "out->send( new AMQFrame(parent->getProtocolVersion(), channel," + CR);
+ sb.append(indent + tab + "new " + namespace + methodBodyClassName + "( parent->getProtocolVersion()");
+ sb.append(generateMethodParameterList(fieldMap, indentSize + (5 * tabSize), true, false, true));
+ sb.append(" )));" + CR);
+ return sb.toString();
+ }
+
+ protected String generateMethodBodyIncludes(AmqpClass thisClass, int indentSize)
+ {
+ StringBuffer sb = new StringBuffer();
+ if (thisClass != null)
+ {
+ sb.append(generateClassMethodBodyInclude(thisClass, indentSize));
+ }
+ else
+ {
+ for (String thisClassName : getModel().getClassMap().keySet())
+ {
+ thisClass = getModel().getClassMap().get(thisClassName);
+ sb.append(generateClassMethodBodyInclude(thisClass, indentSize));
+ }
+ }
+ return sb.toString();
+ }
+
+ protected String generateClassMethodBodyInclude(AmqpClass thisClass, int indentSize)
+ {
+ StringBuffer sb = new StringBuffer();
+ String indent = Utils.createSpaces(indentSize);
+ for (String thisMethodName : thisClass.getMethodMap().keySet())
+ {
+ AmqpMethod method = thisClass.getMethodMap().get(thisMethodName);
+ sb.append(indent + "#include <" + thisClass.getName() +
+ Utils.firstUpper(method.getName()) + "Body.h>" + CR);
+ }
+ return sb.toString();
+ }
+
+ // Methods used for generation of code snippets for MethodBody class generation
+
+ protected String getIndex(AmqpOrdinalVersionMap indexMap, AmqpVersion version)
+ {
+ for (Integer thisIndex : indexMap.keySet())
+ {
+ AmqpVersionSet versionSet = indexMap.get(thisIndex);
+ if (versionSet.contains(version))
+ {
+ return String.valueOf(thisIndex);
+ }
+ }
+ throw new AmqpTemplateException("Unable to find index for version " + version);
+ }
+
+ protected String generateFieldDeclarations(AmqpFieldMap fieldMap, AmqpVersion version, int indentSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+
+ if (version == null)
+ {
+ version = getVersionSet().first();
+ }
+ AmqpOrdinalFieldMap ordinalFieldMap = fieldMap.getMapForVersion(version, true, this);
+ for (Integer thisOrdinal : ordinalFieldMap.keySet())
+ {
+ String[] fieldDomainPair = ordinalFieldMap.get(thisOrdinal);
+ sb.append(indent + fieldDomainPair[FIELD_CODE_TYPE] + " " + fieldDomainPair[FIELD_NAME] + ";" + CR);
+ }
+ return sb.toString();
+ }
+
+ protected String generateFieldGetMethods(AmqpFieldMap fieldMap, AmqpVersion version, int indentSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+
+ if (version == null)
+ {
+ version = getVersionSet().first();
+ }
+ AmqpOrdinalFieldMap ordinalFieldMap = fieldMap.getMapForVersion(version, true, this);
+ for (Integer thisOrdinal : ordinalFieldMap.keySet())
+ {
+ String[] fieldDomainPair = ordinalFieldMap.get(thisOrdinal);
+ sb.append(indent + "inline " + setRef(fieldDomainPair[FIELD_CODE_TYPE]) + " get" +
+ Utils.firstUpper(fieldDomainPair[FIELD_NAME]) + "() { return " +
+ fieldDomainPair[FIELD_NAME] + "; }" + CR);
+ }
+ return sb.toString();
+ }
+
+ protected String generatePrintMethodContents(AmqpFieldMap fieldMap, AmqpVersion version, int indentSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+
+ if (version == null)
+ {
+ version = getVersionSet().first();
+ }
+ AmqpOrdinalFieldMap ordinalFieldMap = fieldMap.getMapForVersion(version, true, this);
+ boolean firstFlag = true;
+ for (Integer thisOrdinal : ordinalFieldMap.keySet())
+ {
+ String[] fieldDomainPair = ordinalFieldMap.get(thisOrdinal);
+ String cast = fieldDomainPair[FIELD_CODE_TYPE].compareTo("u_int8_t") == 0 ? "(int)" : "";
+ sb.append(indent + "out << \"");
+ if (!firstFlag)
+ {
+ sb.append("; ");
+ }
+ sb.append(fieldDomainPair[FIELD_NAME] + "=\" << " + cast + fieldDomainPair[FIELD_NAME] + ";" + CR);
+ firstFlag = false;
+ }
+ return sb.toString();
+ }
+
+ protected String generateBodySizeMethodContents(AmqpFieldMap fieldMap, AmqpVersion version,
+ int indentSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ ArrayList<String> bitFieldList = new ArrayList<String>();
+ AmqpOrdinalFieldMap ordinalFieldMap = fieldMap.getMapForVersion(version, false, this);
+ Iterator<Integer> oItr = ordinalFieldMap.keySet().iterator();
+ int ordinal = 0;
+ while (oItr.hasNext())
+ {
+ ordinal = oItr.next();
+ String[] fieldDomainPair = ordinalFieldMap.get(ordinal);
+ AmqpVersion thisVersion = version == null ? getVersionSet().first() : version;
+ String domainType = getDomainType(fieldDomainPair[FIELD_CODE_TYPE], thisVersion);
+
+ // Defer bit types by adding them to an array. When the first subsequent non-bit
+ // type is encountered, then handle the bits. This allows consecutive bits to be
+ // placed into the same byte(s) - 8 bits to the byte.
+ if (domainType.compareTo("bit") == 0)
+ {
+ bitFieldList.add(fieldDomainPair[FIELD_NAME]);
+ }
+ else
+ {
+ if (bitFieldList.size() > 0) // Handle accumulated bit types (if any)
+ {
+ sb.append(generateBitArrayBodySizeMethodContents(bitFieldList, ordinal, indentSize));
+ }
+ sb.append(indent + "size += " +
+ typeMap.get(domainType).size.replaceAll("#", fieldDomainPair[FIELD_NAME]) +
+ "; /* " + fieldDomainPair[FIELD_NAME] + ": " +
+ domainType + " */" + CR);
+ }
+ }
+ if (bitFieldList.size() > 0) // Handle any remaining accumulated bit types
+ {
+ sb.append(generateBitArrayBodySizeMethodContents(bitFieldList, ordinal, indentSize));
+ }
+ return sb.toString();
+ }
+
+ protected String generateBitArrayBodySizeMethodContents(ArrayList<String> bitFieldList,
+ int ordinal, int indentSize)
+ {
+ int numBytes = ((bitFieldList.size() - 1) / 8) + 1;
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ String comment = bitFieldList.size() == 1 ?
+ bitFieldList.get(0) + ": bit" :
+ "Combinded bits: " + bitFieldList;
+ sb.append(indent + "size += " +
+ typeMap.get("bit").size.replaceAll("~", String.valueOf(numBytes)) +
+ "; /* " + comment + " */" + CR);
+ bitFieldList.clear();
+ return sb.toString();
+ }
+
+ protected String generateEncodeMethodContents(AmqpFieldMap fieldMap, AmqpVersion version,
+ int indentSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ ArrayList<String> bitFieldList = new ArrayList<String>();
+ AmqpOrdinalFieldMap ordinalFieldMap = fieldMap.getMapForVersion(version, false, this);
+ Iterator<Integer> oItr = ordinalFieldMap.keySet().iterator();
+ int ordinal = 0;
+ while (oItr.hasNext())
+ {
+ ordinal = oItr.next();
+ String[] fieldDomainPair = ordinalFieldMap.get(ordinal);
+ AmqpVersion thisVersion = version == null ? getVersionSet().first() : version;
+ String domainType = getDomainType(fieldDomainPair[FIELD_CODE_TYPE], thisVersion);
+
+ // Defer bit types by adding them to an array. When the first subsequent non-bit
+ // type is encountered, then handle the bits. This allows consecutive bits to be
+ // placed into the same byte(s) - 8 bits to the byte.
+ if (domainType.compareTo("bit") == 0)
+ {
+ bitFieldList.add(fieldDomainPair[FIELD_NAME]);
+ }
+ else
+ {
+ if (bitFieldList.size() > 0) // Handle accumulated bit types (if any)
+ {
+ sb.append(generateBitEncodeMethodContents(bitFieldList, ordinal, indentSize));
+ }
+ sb.append(indent +
+ typeMap.get(domainType).encodeExpression.replaceAll("#", fieldDomainPair[FIELD_NAME]) +
+ "; /* " + fieldDomainPair[FIELD_NAME] + ": " + domainType + " */" + CR);
+ }
+ }
+ if (bitFieldList.size() > 0) // Handle any remaining accumulated bit types
+ {
+ sb.append(generateBitEncodeMethodContents(bitFieldList, ordinal, indentSize));
+ }
+
+ return sb.toString();
+ }
+
+ protected String generateBitEncodeMethodContents(ArrayList<String> bitFieldList, int ordinal,
+ int indentSize)
+ {
+ int numBytes = ((bitFieldList.size() - 1) / 8) + 1;
+ String indent = Utils.createSpaces(indentSize);
+ String bitArrayName = "flags_" + ordinal;
+ StringBuffer sb = new StringBuffer(indent + "u_int8_t " + bitArrayName +
+ "[" + numBytes + "] = {0};" +
+ (numBytes != 1 ? " /* All array elements will be initialized to 0 */" : "") +
+ CR);
+ for (int i = 0; i < bitFieldList.size(); i++)
+ {
+ int bitIndex = i % 8;
+ int byteIndex = i / 8;
+ sb.append(indent + bitArrayName + "[" + byteIndex + "] |= " + bitFieldList.get(i) +
+ " << " + bitIndex + "; /* " + bitFieldList.get(i) + ": bit */" + CR);
+ }
+ for (int i = 0; i < numBytes; i++)
+ {
+ sb.append(indent + "buffer.putOctet(" + bitArrayName + "[" + i + "]);" + CR);
+ }
+ bitFieldList.clear();
+ return sb.toString();
+ }
+
+ protected String generateDecodeMethodContents(AmqpFieldMap fieldMap, AmqpVersion version,
+ int indentSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ ArrayList<String> bitFieldList = new ArrayList<String>();
+ AmqpOrdinalFieldMap ordinalFieldMap = fieldMap.getMapForVersion(version, false, this);
+ Iterator<Integer> oItr = ordinalFieldMap.keySet().iterator();
+ int ordinal = 0;
+ while (oItr.hasNext())
+ {
+ ordinal = oItr.next();
+ String[] fieldDomainPair = ordinalFieldMap.get(ordinal);
+ AmqpVersion thisVersion = version == null ? getVersionSet().first() : version;
+ String domainType = getDomainType(fieldDomainPair[FIELD_CODE_TYPE], thisVersion);
+
+ // Defer bit types by adding them to an array. When the first subsequent non-bit
+ // type is encountered, then handle the bits. This allows consecutive bits to be
+ // placed into the same byte(s) - 8 bits to the byte.
+ if (domainType.compareTo("bit") == 0)
+ {
+ bitFieldList.add(fieldDomainPair[FIELD_NAME]);
+ }
+ else
+ {
+ if (bitFieldList.size() > 0) // Handle accumulated bit types (if any)
+ {
+ sb.append(generateBitDecodeMethodContents(bitFieldList, ordinal, indentSize));
+ }
+ sb.append(indent +
+ typeMap.get(domainType).decodeExpression.replaceAll("#", fieldDomainPair[FIELD_NAME]) +
+ "; /* " + fieldDomainPair[FIELD_NAME] + ": " + domainType + " */" + CR);
+ }
+ }
+ if (bitFieldList.size() > 0) // Handle any remaining accumulated bit types
+ {
+ sb.append(generateBitDecodeMethodContents(bitFieldList, ordinal, indentSize));
+ }
+
+ return sb.toString();
+ }
+
+ protected String generateBitDecodeMethodContents(ArrayList<String> bitFieldList, int ordinal,
+ int indentSize)
+ {
+ int numBytes = ((bitFieldList.size() - 1) / 8) + 1;
+ String indent = Utils.createSpaces(indentSize);
+ String bitArrayName = "flags_" + ordinal;
+ StringBuffer sb = new StringBuffer(indent + "u_int8_t " + bitArrayName +
+ "[" + numBytes + "];" + CR);
+ for (int i = 0; i < numBytes; i++)
+ {
+ sb.append(indent + bitArrayName + "[" + i + "] = buffer.getOctet();" + CR);
+ }
+ for (int i = 0; i < bitFieldList.size(); i++)
+ {
+ int bitIndex = i % 8;
+ int byteIndex = i / 8;
+ sb.append(indent + bitFieldList.get(i) + " = (1 << " + bitIndex + ") & " +
+ bitArrayName + "[" + byteIndex + "]; /* " + bitFieldList.get(i) +
+ ": bit */" + CR);
+ }
+ bitFieldList.clear();
+ return sb.toString();
+ }
+
+ protected String generateFieldList(AmqpFieldMap fieldMap, AmqpVersion version, boolean defineFlag,
+ boolean initializerFlag, int indentSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ AmqpOrdinalFieldMap ordinalFieldMap = fieldMap.getMapForVersion(version, true, this);
+ Iterator<Integer> oItr = ordinalFieldMap.keySet().iterator();
+ while (oItr.hasNext())
+ {
+ int ordinal = oItr.next();
+ String[] fieldDomainPair = ordinalFieldMap.get(ordinal);
+ sb.append(indent + (defineFlag ? setRef(fieldDomainPair[FIELD_CODE_TYPE]) + " " : "") +
+ fieldDomainPair[FIELD_NAME] + (initializerFlag ? "(" + fieldDomainPair[FIELD_NAME] + ")" : "") +
+ (oItr.hasNext() ? "," : "") + CR);
+ }
+ return sb.toString();
+ }
+
+ protected String generateMethodParameterList(AmqpOrdinalFieldMap fieldMap, int indentSize,
+ boolean leadingCommaFlag, boolean fieldTypeFlag, boolean fieldNameFlag)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ boolean first = true;
+ Iterator<Integer> pItr = fieldMap.keySet().iterator();
+ while (pItr.hasNext())
+ {
+ String[] field = fieldMap.get(pItr.next());
+ if (first && leadingCommaFlag)
+ {
+ sb.append("," + (fieldNameFlag ? CR : " "));
+ }
+ if (!first || leadingCommaFlag)
+ {
+ sb.append(indent);
+ }
+ sb.append(
+ (fieldTypeFlag ? setRef(field[FIELD_CODE_TYPE]) : "") +
+ (fieldNameFlag ? " " + field[FIELD_NAME] : "") +
+ (pItr.hasNext() ? "," + (fieldNameFlag ? CR : " ") : ""));
+ first = false;
+ }
+ return sb.toString();
+ }
+
+ protected String generateConstructor(AmqpClass thisClass, AmqpMethod method,
+ AmqpVersion version, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+ if (method.getFieldMap().size() > 0)
+ {
+ sb.append(indent + thisClass.getName() + Utils.firstUpper(method.getName()) + "Body(ProtocolVersion& version," + CR);
+ sb.append(generateFieldList(method.getFieldMap(), version, true, false, 8));
+ sb.append(indent + tab + ") :" + CR);
+ sb.append(indent + tab + "AMQMethodBody(version)," + CR);
+ sb.append(generateFieldList(method.getFieldMap(), version, false, true, 8));
+ sb.append(indent + "{ }" + CR);
+ }
+ return sb.toString();
+ }
+
+ protected String generateServerOperationsInvoke(AmqpClass thisClass, AmqpMethod method,
+ AmqpVersion version, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+
+ if (method.getServerMethodFlagMap().size() > 0) // At least one AMQP version defines this method as a server method
+ {
+ Iterator<Boolean> bItr = method.getServerMethodFlagMap().keySet().iterator();
+ while (bItr.hasNext())
+ {
+ if (bItr.next()) // This is a server operation
+ {
+ boolean fieldMapNotEmptyFlag = method.getFieldMap().size() > 0;
+ sb.append(indent + "inline void invoke(AMQP_ServerOperations& target, u_int16_t channel)" + CR);
+ sb.append(indent + "{" + CR);
+ sb.append(indent + tab + "target.get" + thisClass.getName() + "Handler()->" +
+ parseForReservedWords(Utils.firstLower(method.getName()),
+ thisClass.getName() + Utils.firstUpper(method.getName()) + "Body.invoke()") + "(channel");
+ if (fieldMapNotEmptyFlag)
+ {
+ sb.append("," + CR);
+ sb.append(generateFieldList(method.getFieldMap(), version, false, false, indentSize + 4 * tabSize));
+ sb.append(indent + tab + tab + tab + tab);
+ }
+ sb.append(");" + CR);
+ sb.append(indent + "}" + CR);
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ // Methods for generation of code snippets for amqp_methods.h/cpp files
+
+ protected String generateMethodBodyIncludeList(AmqpModel model, int indentSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+
+ for (String thisClassName : model.getClassMap().keySet())
+ {
+ AmqpClass thisClass = model.getClassMap().get(thisClassName);
+ for (String thisMethodName : thisClass.getMethodMap().keySet())
+ {
+ AmqpMethod method = thisClass.getMethodMap().get(thisMethodName);
+ sb.append(indent + "#include \"" + thisClass.getName() + Utils.firstUpper(method.getName()) + "Body.h\"" + CR);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ protected String generateMethodBodyInstances(AmqpModel model, int indentSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+
+ for (String thisClassName : model.getClassMap().keySet())
+ {
+ AmqpClass thisClass = model.getClassMap().get(thisClassName);
+ for (String thisMethodName : thisClass.getMethodMap().keySet())
+ {
+ AmqpMethod method = thisClass.getMethodMap().get(thisMethodName);
+ sb.append(indent + "const " + thisClass.getName() + Utils.firstUpper(method.getName()) + "Body " +
+ Utils.firstLower(thisClass.getName()) + "_" + method.getName() + ";" + CR);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ protected String generateMethodBodyMapEntry(AmqpModel model, int indentSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+
+ for (AmqpVersion version : getVersionSet())
+ {
+ for (String thisClassName : model.getClassMap().keySet())
+ {
+ AmqpClass thisClass = model.getClassMap().get(thisClassName);
+ for (String thisMethodName : thisClass.getMethodMap().keySet())
+ {
+ AmqpMethod method = thisClass.getMethodMap().get(thisMethodName);
+ String namespace = method.isVersionConsistent(getVersionSet()) ? "" : version.namespace() + "::";
+ try
+ {
+ int classOrdinal = thisClass.getIndexMap().getOrdinal(version);
+ int methodOrdinal = method.getIndexMap().getOrdinal(version);
+ String methodModyClassName = namespace + thisClass.getName() + Utils.firstUpper(method.getName()) + "Body";
+ sb.append(indent + "insert(std::make_pair(createMapKey(" + classOrdinal + ", " +
+ methodOrdinal + ", " + version.getMajor() + ", " + version.getMinor() +
+ "), &createMethodBodyFn<" + methodModyClassName + ">));" + CR);
+ }
+ catch (AmqpTypeMappingException e)
+ {
+ } // ignore
+ }
+ }
+ }
+
+ return sb.toString();
+ }
+
+ // Helper functions
+
+ private String generateVersionCheck(AmqpVersion version)
+ {
+ return "version.equals(" + version.getMajor() + ", " + version.getMinor() + ")";
+ }
+
+ private String generateVersionCheck(AmqpVersionSet versionSet)
+ {
+ StringBuffer sb = new StringBuffer();
+ for (AmqpVersion v : versionSet)
+ {
+ if (!v.equals(versionSet.first()))
+ {
+ sb.append(" || ");
+ }
+ if (versionSet.size() > 1)
+ {
+ sb.append("(");
+ }
+ sb.append("version.equals(" + v.getMajor() + ", " + v.getMinor() + ")");
+ if (versionSet.size() > 1)
+ {
+ sb.append(")");
+ }
+ }
+ return sb.toString();
+ }
+
+ private String parseForReservedWords(String name, String context)
+ {
+ for (String cppReservedWord : cppReservedWords)
+ {
+ if (name.compareTo(cppReservedWord) == 0)
+ {
+ if (!quietFlag)
+ {
+ System.out.println("WARNING: " + (context == null ? "" : context + ": ") +
+ "Found XML method \"" + name + "\", which is a C++ reserved word. " +
+ "Changing generated name to \"" + name + "_\".");
+ }
+ return name + "_";
+ }
+ }
+
+ for (String cppCommonDefine : cppCommonDefines)
+ {
+ if (name.compareTo(cppCommonDefine) == 0)
+ {
+ if (!quietFlag)
+ {
+ System.out.println("WARNING: " + (context == null ? "" : context + ": ") +
+ "Found XML method \"" + name + "\", which may clash with commonly used defines within C++. " +
+ "Changing generated name to \"" + name + "_\".");
+ }
+ return name + "_";
+ }
+ }
+
+ return name;
+ }
+
+ private String setRef(String codeType)
+ {
+ if (codeType.compareTo("string") == 0 ||
+ codeType.compareTo("FieldTable") == 0)
+ {
+ return "const " + codeType + "&";
+ }
+ return codeType;
+ }
+
+ private String camelCaseName(String name, boolean upperFirstFlag)
+ {
+ StringBuffer ccn = new StringBuffer();
+ String[] toks = name.split("[-_.\\ ]");
+ for (int i = 0; i < toks.length; i++)
+ {
+ StringBuffer b = new StringBuffer(toks[i]);
+ if (upperFirstFlag || i > 0)
+ {
+ b.setCharAt(0, Character.toUpperCase(toks[i].charAt(0)));
+ }
+ ccn.append(b);
+ }
+ return ccn.toString();
+ }
+
+ public static Factory<CppGenerator> _factoryInstance = new Factory<CppGenerator>()
+ {
+
+ public CppGenerator newInstance()
+ {
+ return new CppGenerator();
+ }
+ };
+
+ public static Factory<CppGenerator> getFactory()
+ {
+ return _factoryInstance;
+ }
+
+ void processModelTemplate(NamedTemplate template, AmqpVersion version)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+ public String getNativeType(String type)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getEncodingType(String type)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/DotnetGenerator.java b/qpid/gentools/src/org/apache/qpid/gentools/DotnetGenerator.java
new file mode 100644
index 0000000000..9fc81dd428
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/DotnetGenerator.java
@@ -0,0 +1,382 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+package org.apache.qpid.gentools;
+
+import java.io.File;
+import java.util.TreeMap;
+
+public class DotnetGenerator extends Generator
+{
+ private class DomainInfo
+ {
+ public String type;
+ public String size;
+ public String encodeExpression;
+ public String decodeExpression;
+
+ public DomainInfo(String domain, String size, String encodeExpression, String decodeExpression)
+ {
+ this.type = domain;
+ this.size = size;
+ this.encodeExpression = encodeExpression;
+ this.decodeExpression = decodeExpression;
+ }
+ }
+
+ private static TreeMap<String, DomainInfo> typeMap = new TreeMap<String, DomainInfo>();
+
+ public String getNativeType(String type)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public String getEncodingType(String type)
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public DotnetGenerator()
+ {
+ super();
+ // Load .NET type and size maps.
+ // Adjust or add to these lists as new types are added/defined.
+ // The char '#' will be replaced by the field variable name (any type).
+ // The char '~' will be replaced by the compacted bit array size (type bit only).
+ // TODO: I have left a copy of the Java typeMap here - replace with appropriate .NET values.
+ typeMap.put("bit", new DomainInfo(
+ "boolean", // .NET code type
+ "~", // size
+ "EncodingUtils.writeBooleans(buffer, #)", // encode expression
+ "# = EncodingUtils.readBooleans(buffer)")); // decode expression
+ typeMap.put("content", new DomainInfo(
+ "Content", // .NET code type
+ "EncodingUtils.encodedContentLength(#)", // size
+ "EncodingUtils.writeContentBytes(buffer, #)", // encode expression
+ "# = EncodingUtils.readContent(buffer)")); // decode expression
+ typeMap.put("long", new DomainInfo(
+ "long", // .NET code type
+ "4", // size
+ "EncodingUtils.writeUnsignedInteger(buffer, #)", // encode expression
+ "# = buffer.getUnsignedInt()")); // decode expression
+ typeMap.put("longlong", new DomainInfo(
+ "long", // .NET code type
+ "8", // size
+ "buffer.putLong(#)", // encode expression
+ "# = buffer.getLong()")); // decode expression
+ typeMap.put("longstr", new DomainInfo(
+ "byte[]", // .NET code type
+ "EncodingUtils.encodedLongstrLength(#)", // size
+ "EncodingUtils.writeLongStringBytes(buffer, #)", // encode expression
+ "# = EncodingUtils.readLongstr(buffer)")); // decode expression
+ typeMap.put("octet", new DomainInfo(
+ "short", // .NET code type
+ "1", // size
+ "EncodingUtils.writeUnsignedByte(buffer, #)", // encode expression
+ "# = buffer.getUnsigned()")); // decode expression
+ typeMap.put("short", new DomainInfo(
+ "int", // .NET code type
+ "2", // size
+ "EncodingUtils.writeUnsignedShort(buffer, #)", // encode expression
+ "# = buffer.getUnsignedShort()")); // decode expression
+ typeMap.put("shortstr", new DomainInfo(
+ "AMQShortString", // .NET code type
+ "EncodingUtils.encodedShortStringLength(#)", // size
+ "EncodingUtils.writeShortStringBytes(buffer, #)", // encode expression
+ "# = EncodingUtils.readAMQShortString(buffer)")); // decode expression
+ typeMap.put("table", new DomainInfo(
+ "FieldTable", // .NET code type
+ "EncodingUtils.encodedFieldTableLength(#)", // size
+ "EncodingUtils.writeFieldTableBytes(buffer, #)", // encode expression
+ "# = EncodingUtils.readFieldTable(buffer)")); // decode expression
+ typeMap.put("timestamp", new DomainInfo(
+ "long", // .NET code type
+ "8", // size
+ "EncodingUtils.writeTimestamp(buffer, #)", // encode expression
+ "# = EncodingUtils.readTimestamp(buffer)")); // decode expression
+ }
+
+ void processModelTemplate(NamedTemplate template, AmqpVersion version)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ void processClassTemplate(NamedTemplate template, AmqpClass amqpClass, AmqpVersion version)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ void processMethodTemplate(NamedTemplate template, AmqpClass amqpClass, AmqpMethod amqpMethod, AmqpVersion version)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ void processFieldTemplate(NamedTemplate template, AmqpClass amqpClass, AmqpMethod amqpMethod, AmqpField amqpField, AmqpVersion version)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ protected String prepareFilename(String filenameTemplate,
+ AmqpClass thisClass, AmqpMethod method, AmqpField field, AmqpVersion version)
+ {
+ StringBuffer sb = new StringBuffer(filenameTemplate);
+ if (thisClass != null)
+ {
+ replaceToken(sb, "${CLASS}", thisClass.getName());
+ }
+ if (method != null)
+ {
+ replaceToken(sb, "${METHOD}", method.getName());
+ }
+ if (field != null)
+ {
+ replaceToken(sb, "${FIELD}", field.getName());
+ }
+ return sb.toString();
+ }
+
+ @Override
+ protected void processClassList(StringBuffer sb, int listMarkerStartIndex,
+ int listMarkerEndIndex, AmqpModel model, AmqpVersion version)
+ throws AmqpTemplateException, AmqpTypeMappingException
+ {
+ String codeSnippet;
+ int lend = sb.indexOf(CR, listMarkerStartIndex) + 1; // Include cr at end of line
+ String tline = sb.substring(listMarkerEndIndex, lend); // Line excluding line marker, including cr
+ int tokStart = tline.indexOf('$');
+ String token = tline.substring(tokStart).trim();
+ sb.delete(listMarkerStartIndex, lend);
+
+ // TODO: Add in tokens and calls to their corresponding generator methods here...
+ if (token.compareTo("${??????????}") == 0)
+ {
+ codeSnippet = token; // This is a stub to get the compile working - remove when gen method is present.
+// codeSnippet = generateRegistry(model, 8, 4);
+ }
+
+ else // Oops!
+ {
+ throw new AmqpTemplateException("Template token " + token + " unknown.");
+ }
+ sb.insert(listMarkerStartIndex, codeSnippet);
+ }
+
+ @Override
+ protected void processConstantList(StringBuffer sb,
+ int listMarkerStartIndex, int listMarkerEndIndex,
+ AmqpConstantSet constantSet) throws AmqpTemplateException,
+ AmqpTypeMappingException
+ {
+ String codeSnippet;
+ int lend = sb.indexOf(CR, listMarkerStartIndex) + 1; // Include cr at end of line
+ String tline = sb.substring(listMarkerEndIndex, lend); // Line excluding line marker, including cr
+ int tokStart = tline.indexOf('$');
+ String token = tline.substring(tokStart).trim();
+ sb.delete(listMarkerStartIndex, lend);
+
+ // TODO: Add in tokens and calls to their corresponding generator methods here...
+ if (token.compareTo("${??????????}") == 0)
+ {
+ codeSnippet = token; // This is a stub to get the compile working - remove when gen method is present.
+// codeSnippet = generateConstantGetMethods(constantSet, 4, 4);
+ }
+
+ else // Oops!
+ {
+ throw new AmqpTemplateException("Template token " + token + " unknown.");
+ }
+ sb.insert(listMarkerStartIndex, codeSnippet);
+ }
+
+ @Override
+ protected void processFieldList(StringBuffer sb, int listMarkerStartIndex,
+ int listMarkerEndIndex, AmqpFieldMap fieldMap, AmqpVersion version)
+ {
+ String codeSnippet;
+ int lend = sb.indexOf(CR, listMarkerStartIndex) + 1; // Include cr at end of line
+ String tline = sb.substring(listMarkerEndIndex, lend); // Line excluding line marker, including cr
+ int tokStart = tline.indexOf('$');
+ String token = tline.substring(tokStart).trim();
+ sb.delete(listMarkerStartIndex, lend);
+
+ // TODO: Add in tokens and calls to their corresponding generator methods here...
+ if (token.compareTo("${??????????}") == 0)
+ {
+ codeSnippet = token; // This is a stub to get the compile working - remove when gen method is present.
+// codeSnippet = fieldMap.parseFieldMap(declarationGenerateMethod,
+// mangledDeclarationGenerateMethod, 4, 4, this);
+ }
+
+ else // Oops!
+ {
+ throw new AmqpTemplateException("Template token " + token + " unknown.");
+ }
+ sb.insert(listMarkerStartIndex, codeSnippet);
+ }
+
+ @Override
+ protected void processMethodList(StringBuffer sb, int listMarkerStartIndex,
+ int listMarkerEndIndex, AmqpClass thisClass)
+ throws AmqpTemplateException, AmqpTypeMappingException
+ {
+ String codeSnippet;
+ int lend = sb.indexOf(CR, listMarkerStartIndex) + 1; // Include cr at end of line
+ String tline = sb.substring(listMarkerEndIndex, lend); // Line excluding line marker, including cr
+ int tokStart = tline.indexOf('$');
+ String token = tline.substring(tokStart).trim();
+ sb.delete(listMarkerStartIndex, lend);
+
+ // TODO: Add in tokens and calls to their corresponding generator methods here...
+ if (token.compareTo("${??????????}") == 0)
+ {
+ codeSnippet = token; // This is a stub to get the compile working - remove when gen method is present.
+ }
+
+ else // Oops!
+ {
+ throw new AmqpTemplateException("Template token " + token + " unknown.");
+ }
+ sb.insert(listMarkerStartIndex, codeSnippet);
+ }
+
+ @Override
+ protected void processModelTemplate(NamedTemplate template)
+ {
+ // I've put in the Java model here - this can be changed if a different pattern is required.
+ processTemplate(template, null, null, null, null);
+ }
+
+ @Override
+ protected void processClassTemplate(NamedTemplate template, AmqpClass thisClass)
+ {
+ // I've put in the Java model here - this can be changed if a different pattern is required.
+ processTemplate(template, thisClass, null, null, null);
+ }
+
+ @Override
+ protected void processMethodTemplate(NamedTemplate template, AmqpClass thisClass,
+ AmqpMethod method)
+ {
+ // I've put in the Java model here - this can be changed if a different pattern is required.
+ processTemplate(template, thisClass, method, null, null);
+ }
+
+ @Override
+ protected void processTemplate(NamedTemplate template, AmqpClass thisClass,
+ AmqpMethod method, AmqpField field, AmqpVersion version)
+ {
+ // I've put in the Java model here - this can be changed if a different pattern is required.
+ StringBuffer sb = new StringBuffer(template.getTemplate());
+ String filename = prepareFilename(getTemplateFileName(sb), thisClass, method, field, version);
+ try
+ {
+ processAllLists(sb, thisClass, method, null);
+ }
+ catch (AmqpTemplateException e)
+ {
+ System.out.println("WARNING: " + template.getName() + ": " + e.getMessage());
+ }
+ try
+ {
+ processAllTokens(sb, thisClass, method, field, null);
+ }
+ catch (AmqpTemplateException e)
+ {
+ System.out.println("WARNING: " + template.getName() + ": " + e.getMessage());
+ }
+ writeTargetFile(sb, new File(getOutputDirectory() + Utils.FILE_SEPARATOR + filename));
+ generatedFileCounter++;
+ }
+
+ @Override
+ protected String processToken(String token, AmqpClass thisClass,
+ AmqpMethod method, AmqpField field, AmqpVersion version)
+ throws AmqpTemplateException, AmqpTypeMappingException
+ {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+ public String getGeneratedType(String domainName, AmqpVersion version)
+ throws AmqpTypeMappingException
+ {
+ String domainType = getDomainType(domainName, version);
+ if (domainType == null)
+ {
+ throw new AmqpTypeMappingException("Domain type \"" + domainName +
+ "\" not found in Java typemap.");
+ }
+ DomainInfo info = typeMap.get(domainType);
+ if (info == null)
+ {
+ throw new AmqpTypeMappingException("Unknown domain: \"" + domainType + "\"");
+ }
+ return info.type;
+ }
+
+ public String prepareClassName(String className)
+ {
+ return camelCaseName(className, true);
+ }
+
+ public String prepareDomainName(String domainName)
+ {
+ return camelCaseName(domainName, false);
+ }
+
+ public String prepareMethodName(String methodName)
+ {
+ return camelCaseName(methodName, false);
+ }
+
+ private String camelCaseName(String name, boolean upperFirstFlag)
+ {
+ StringBuffer ccn = new StringBuffer();
+ String[] toks = name.split("[-_.\\ ]");
+ for (int i = 0; i < toks.length; i++)
+ {
+ StringBuffer b = new StringBuffer(toks[i]);
+ if (upperFirstFlag || i > 0)
+ {
+ b.setCharAt(0, Character.toUpperCase(toks[i].charAt(0)));
+ }
+ ccn.append(b);
+ }
+ return ccn.toString();
+ }
+
+
+ public static Factory<DotnetGenerator> _factoryInstance = new Factory<DotnetGenerator>()
+ {
+
+ public DotnetGenerator newInstance()
+ {
+ return new DotnetGenerator();
+ }
+ };
+
+ public static Factory<DotnetGenerator> getFactory()
+ {
+ return _factoryInstance;
+ }
+
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/GenerateMethod.java b/qpid/gentools/src/org/apache/qpid/gentools/GenerateMethod.java
new file mode 100644
index 0000000000..8b0bb99b41
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/GenerateMethod.java
@@ -0,0 +1,27 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+
+public interface GenerateMethod
+{
+ String generate(String domainType, String fieldName, int ordinal, int indentSize, int tabSize);
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/Generator.java b/qpid/gentools/src/org/apache/qpid/gentools/Generator.java
new file mode 100644
index 0000000000..5d6e7be527
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/Generator.java
@@ -0,0 +1,857 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import org.apache.velocity.Template;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.Velocity;
+import org.w3c.dom.Node;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumMap;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.Map;
+
+public abstract class Generator implements LanguageConverter
+{
+ protected static String CR = Utils.LINE_SEPARATOR;
+
+
+ private static final Map<String, Integer> FIXED_SIZE_TYPES = new HashMap<String, Integer>();
+
+ static
+ {
+ FIXED_SIZE_TYPES.put("bit", 1);
+ FIXED_SIZE_TYPES.put("bitfield", 1);
+ FIXED_SIZE_TYPES.put("long", 4);
+ FIXED_SIZE_TYPES.put("longlong", 8);
+ FIXED_SIZE_TYPES.put("octet", 1);
+ FIXED_SIZE_TYPES.put("short", 2);
+ FIXED_SIZE_TYPES.put("timestamp", 8);
+
+ }
+
+ private String _templateDirectory;
+ private String _outputDirectory;
+
+ public AmqpDomainMap getDomainMap()
+ {
+ return _domainMap;
+ }
+
+ public AmqpConstantSet getConstantSet()
+ {
+ return _constantSet;
+ }
+
+ public AmqpModel getModel()
+ {
+ return _model;
+ }
+
+ abstract public String getNativeType(String type);
+
+ abstract public String getEncodingType(String type);
+
+
+
+ protected static enum EnumConstOutputTypes
+ {
+ OUTPUT_STRING,
+ OUTPUT_INTEGER,
+ OUTPUT_DOUBLE;
+ }
+
+ ;
+
+ public static enum TemplateType
+ {
+ model("model"),
+ clazz("class"),
+ method("method"),
+ field("field");
+
+ private final String _name;
+
+ private TemplateType(String name)
+ {
+ _name = name;
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+ }
+
+ ;
+
+
+ public static interface Factory<X extends Generator>
+ {
+ public X newInstance();
+ }
+
+
+ protected static final class NamedTemplate
+ {
+ private final String _name;
+ private final String _template;
+ private final File _file;
+
+
+ public NamedTemplate(String relativePath, File templateFile)
+ {
+ _file = templateFile;
+ _name = relativePath + Utils.FILE_SEPARATOR + templateFile.getName();
+
+ _template = loadTemplate(templateFile);
+ }
+
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public String getTemplate()
+ {
+ return _template;
+ }
+
+
+ public File getFile()
+ {
+ return _file;
+ }
+
+ }
+
+
+ private static final String VELOCITY_TEMPLATE_SUFFIX = ".vm";
+ private static final String STANDARD_TEMPLATE_SUFFIX = ".tmpl";
+ private static FilenameFilter _tmplFileFilter = new FilenameFilter()
+ {
+
+ public boolean accept(File dir, String name)
+ {
+ return name.endsWith(STANDARD_TEMPLATE_SUFFIX) || name.endsWith(VELOCITY_TEMPLATE_SUFFIX);
+ }
+ };
+
+
+ // This string is reproduced in every generated file as a comment
+ // TODO: Tie the version info into the build system.
+ protected static final String GENERATOR_INFO = "Qpid Gentools v.0.1";
+
+
+ private final Map<TemplateType, Collection<NamedTemplate>> _templates =
+ new EnumMap<TemplateType, Collection<NamedTemplate>>(TemplateType.class);
+
+ private final Map<TemplateType, Collection<NamedTemplate>> _versionSpecificTemplates =
+ new EnumMap<TemplateType, Collection<NamedTemplate>>(TemplateType.class);
+
+
+ private final AmqpVersionSet _versionSet;
+
+ private final AmqpDomainMap _domainMap;
+ private final Map<AmqpVersion, AmqpDomainMap> _versionToDomainMapMap = new HashMap<AmqpVersion, AmqpDomainMap>();
+
+ private final AmqpConstantSet _constantSet;
+ private final Map<AmqpVersion, AmqpConstantSet> _versionToConstantSetMap = new HashMap<AmqpVersion, AmqpConstantSet>();
+
+
+ public AmqpVersionSet getVersionSet()
+ {
+ return _versionSet;
+ }
+
+ private final AmqpModel _model;
+ private final Map<AmqpVersion, AmqpModel> _versionToModelMap = new HashMap<AmqpVersion, AmqpModel>();
+
+ protected int generatedFileCounter;
+
+ public Generator()
+ {
+ _versionSet = new AmqpVersionSet();
+ _model = new AmqpModel(this);
+ _constantSet = new AmqpConstantSet(this);
+ _domainMap = new AmqpDomainMap(this);
+
+ generatedFileCounter = 0;
+ }
+
+// public final AmqpVersionSet getVersionSet()
+// {
+// return _versionSet;
+// }
+
+
+ public void addVersion(AmqpVersion version)
+ {
+ _versionSet.add(version);
+ if (!_versionToModelMap.containsKey(version))
+ {
+ _versionToModelMap.put(version, new AmqpModel(this));
+ }
+ if (!_versionToDomainMapMap.containsKey(version))
+ {
+ _versionToDomainMapMap.put(version, new AmqpDomainMap(this));
+ }
+ if (!_versionToConstantSetMap.containsKey(version))
+ {
+ _versionToConstantSetMap.put(version, new AmqpConstantSet(this));
+ }
+ }
+
+ public int getNumberGeneratedFiles()
+ {
+ return generatedFileCounter;
+ }
+
+// public AmqpDomainMap getDomainMap()
+// {
+// return _domainMap;
+// }
+//
+// public AmqpConstantSet getConstantSet()
+// {
+// return _constantSet;
+// }
+//
+//
+// public AmqpModel getModel()
+// {
+// return _model;
+// }
+
+ public void initializeTemplates() throws IOException
+ {
+
+ for (TemplateType type : EnumSet.allOf(TemplateType.class))
+ {
+ ArrayList<NamedTemplate> typeTemplates = new ArrayList<NamedTemplate>();
+ _templates.put(type, typeTemplates);
+ ArrayList<NamedTemplate> versionSpecificTypeTemplates = new ArrayList<NamedTemplate>();
+ _versionSpecificTemplates.put(type, versionSpecificTypeTemplates);
+
+ File templateDirectory = new File(getTemplateDirectory() + Utils.FILE_SEPARATOR + type.getName());
+ File versionTemplateDirectory = new File(getTemplateDirectory() + Utils.FILE_SEPARATOR + type.getName() + Utils.FILE_SEPARATOR + "version");
+
+ System.out.println("Looking for template files in directory: " + templateDirectory.getAbsoluteFile());
+
+ File[] templateFiles = templateDirectory.listFiles(_tmplFileFilter);
+
+ File[] versionTemplateFiles = new File[0];
+
+ System.out.println("Looking for version specific template files in directory: " + versionTemplateDirectory.getAbsoluteFile());
+
+ if (versionTemplateDirectory.exists())
+ {
+ versionTemplateFiles = versionTemplateDirectory.listFiles(_tmplFileFilter);
+ }
+
+ if(templateFiles != null)
+ {
+ for (File templateFile : templateFiles)
+ {
+ System.out.println(type.getName() + " template file(s):");
+ System.out.println(" " + templateFile.getCanonicalPath());
+ typeTemplates.add(new NamedTemplate(type.getName(), templateFile));
+ }
+ }
+
+ if(versionTemplateFiles != null)
+ {
+ for (File versionTemplateFile : versionTemplateFiles)
+ {
+ System.out.println(type.getName() + " template file(s):");
+ System.out.println(" " + versionTemplateFile.getCanonicalPath());
+ versionSpecificTypeTemplates.add(new NamedTemplate(type.getName() + Utils.FILE_SEPARATOR + "version", versionTemplateFile));
+ }
+ }
+
+ }
+ }
+
+ public String getTemplateDirectory()
+ {
+ return _templateDirectory;
+ }
+
+
+ public void setTemplateDirectory(String templateDirectory)
+ {
+ _templateDirectory = templateDirectory;
+ }
+
+
+ public void setOutputDirectory(String outputDirectory)
+ {
+ _outputDirectory = outputDirectory;
+ }
+
+ public void generate()
+ {
+ prepareTargetDirectory(new File(_outputDirectory), true);
+ System.out.println("Generation directory: " + _outputDirectory);
+
+
+ processModelTemplates(_templates);
+
+ for (AmqpClass amqpClass : _model.getClassMap().values())
+ {
+ processClassTemplates(_templates, amqpClass);
+
+ for (AmqpMethod amqpMethod : amqpClass.getMethodMap().values())
+ {
+ processMethodTemplates(_templates, amqpClass, amqpMethod);
+
+ for (AmqpField amqpField : amqpMethod.getFieldMap().values())
+ {
+ processFieldTemplates(_templates, amqpClass, amqpMethod, amqpField, null);
+ }
+ }
+ }
+
+
+ for (AmqpVersion version : _versionSet)
+ {
+ AmqpModel model = _versionToModelMap.get(version);
+ processModelTemplates(_versionSpecificTemplates, version);
+
+ for (AmqpClass amqpClass : model.getClassMap().values())
+ {
+ processClassTemplates(_versionSpecificTemplates, amqpClass, version);
+
+ for (AmqpMethod amqpMethod : amqpClass.getMethodMap().values())
+ {
+ processMethodTemplates(_versionSpecificTemplates, amqpClass, amqpMethod, version);
+
+ for (AmqpField amqpField : amqpMethod.getFieldMap().values())
+ {
+ processFieldTemplates(_versionSpecificTemplates, amqpClass, amqpMethod, amqpField, version);
+ }
+ }
+ }
+
+ }
+ }
+
+ private void processMethodTemplates(Map<TemplateType, Collection<NamedTemplate>> templates, AmqpClass amqpClass, AmqpMethod amqpMethod, AmqpVersion version)
+ {
+ for (NamedTemplate template : templates.get(TemplateType.method))
+ {
+ if(isVelocityTemplate(template))
+ {
+ processVelocityTemplate(template,version,amqpClass,amqpMethod,null);
+ }
+ else
+ {
+ processMethodTemplate(template, amqpClass, amqpMethod);
+ }
+ }
+
+ }
+
+ private void processClassTemplates(Map<TemplateType, Collection<NamedTemplate>> templates, AmqpClass amqpClass, AmqpVersion version)
+ {
+ for (NamedTemplate template : templates.get(TemplateType.clazz))
+ {
+ if(isVelocityTemplate(template))
+ {
+ processVelocityTemplate(template,version,amqpClass,null,null);
+ }
+ else
+ {
+ processClassTemplate(template, amqpClass);
+ }
+ }
+
+ }
+
+
+ private void processModelTemplates(Map<TemplateType, Collection<NamedTemplate>> templates, AmqpVersion version)
+ {
+ for (NamedTemplate template : templates.get(TemplateType.model))
+ {
+ if (isVelocityTemplate(template))
+ {
+ processModelVelocityTemplate(template, version);
+ }
+ else
+ {
+ processModelTemplate(template, version);
+ }
+ }
+ }
+
+ abstract void processModelTemplate(NamedTemplate template, AmqpVersion version);
+
+
+ protected void processModelTemplates(Map<TemplateType, Collection<NamedTemplate>> templates)
+ {
+ for (NamedTemplate template : templates.get(TemplateType.model))
+ {
+ if (isVelocityTemplate(template))
+ {
+ processModelVelocityTemplate(template, null);
+ }
+ else
+ {
+ processModelTemplate(template);
+ }
+ }
+ }
+
+ private boolean isVelocityTemplate(NamedTemplate template)
+ {
+ return template.getName().endsWith(VELOCITY_TEMPLATE_SUFFIX);
+ }
+
+ private void processModelVelocityTemplate(NamedTemplate template, AmqpVersion version)
+ {
+ processVelocityTemplate(template,version,null,null,null);
+ }
+
+ private void processVelocityTemplate(NamedTemplate template, AmqpVersion version,
+ AmqpClass amqpClass, AmqpMethod amqpMethod, AmqpField amqpField)
+ {
+
+ VelocityContext context = new VelocityContext();
+
+ AmqpModel model = _model;
+ if(version != null)
+ {
+ model = _versionToModelMap.get(version);
+ }
+ context.put("model", model);
+ context.put("generator", GENERATOR_INFO);
+
+ if (version != null)
+ {
+ context.put("version", version);
+ }
+ if(amqpClass != null)
+ {
+ context.put("amqpClass", amqpClass);
+ }
+
+ if(amqpClass != null)
+ {
+ context.put("amqpMethod", amqpMethod);
+ }
+
+
+ StringWriter sw = new StringWriter();
+
+
+ try
+ {
+ Template velocityTemplate = Velocity.getTemplate(template.getName());
+ velocityTemplate.merge(context, sw);
+ String filename = String.valueOf(context.get("filename"));
+
+ File outputFile = new File(getOutputDirectory() + Utils.FILE_SEPARATOR + filename);
+ outputFile.getParentFile().mkdirs();
+ FileWriter outputFileWriter = new FileWriter(outputFile);
+
+ outputFileWriter.append(sw.toString());
+ outputFileWriter.close();
+
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+
+
+ }
+
+
+ protected void processClassTemplates(Map<TemplateType, Collection<NamedTemplate>> templates, AmqpClass amqpClass)
+ {
+ for (NamedTemplate template : templates.get(TemplateType.clazz))
+ {
+ if(isVelocityTemplate(template))
+ {
+ processVelocityTemplate(template,null,amqpClass,null,null);
+ }
+ else
+ {
+ processClassTemplate(template, amqpClass);
+ }
+ }
+ }
+
+ protected void processMethodTemplates(Map<TemplateType, Collection<NamedTemplate>> templates, AmqpClass amqpClass, AmqpMethod amqpMethod)
+ {
+ for (NamedTemplate template : templates.get(TemplateType.method))
+ {
+ if(isVelocityTemplate(template))
+ {
+ processVelocityTemplate(template,null,amqpClass,amqpMethod,null);
+ }
+ else
+ {
+ processMethodTemplate(template, amqpClass, amqpMethod);
+ }
+ }
+ }
+
+
+ protected void processFieldTemplates(Map<TemplateType, Collection<NamedTemplate>> templates, AmqpClass amqpClass, AmqpMethod amqpMethod, AmqpField amqpField, AmqpVersion amqpVersion)
+ {
+ for (NamedTemplate template : templates.get(TemplateType.field))
+ {
+ if(isVelocityTemplate(template))
+ {
+ processVelocityTemplate(template,amqpVersion,amqpClass,amqpMethod,amqpField);
+ }
+ else
+ {
+ processTemplate(template, amqpClass, amqpMethod, amqpField, amqpVersion);
+ }
+ }
+ }
+
+
+ protected void processVersionList(StringBuffer sb, int tokStart, int tokEnd)
+ {
+ int lend = sb.indexOf(Utils.LINE_SEPARATOR, tokStart) + 1; // Include cr at end of line
+ String tline = sb.substring(tokEnd, lend); // Line excluding line marker, including cr
+ sb.delete(tokStart, lend);
+
+ for (AmqpVersion v : _versionSet)
+ {
+ // Insert copy of target line
+ StringBuffer isb = new StringBuffer(tline);
+ if (isb.indexOf("${protocol-version-list-entry}") >= 0)
+ {
+ String versionListEntry = " { ${major}, ${minor} }" +
+ (v.equals(_versionSet.last()) ? "" : ",");
+ replaceToken(isb, "${protocol-version-list-entry}", String.valueOf(versionListEntry));
+ }
+ if (isb.indexOf("${major}") >= 0)
+ {
+ replaceToken(isb, "${major}", String.valueOf(v.getMajor()));
+ }
+ if (isb.indexOf("${minor}") >= 0)
+ {
+ replaceToken(isb, "${minor}", String.valueOf(v.getMinor()));
+ }
+ sb.insert(tokStart, isb.toString());
+ tokStart += isb.length();
+ }
+ }
+
+ // Helper functions common to all generators
+
+ protected static void prepareTargetDirectory(File dir, boolean createFlag)
+ {
+ if (dir.exists())
+ {
+ if (!dir.isDirectory())
+ {
+ throw new TargetDirectoryException("\"" + dir.getAbsolutePath() +
+ "\" exists, but is not a directory.");
+ }
+ }
+ else if (createFlag) // Create dir
+ {
+ if (!dir.mkdirs())
+ {
+ throw new TargetDirectoryException("Unable to create directory \"" +
+ dir.getAbsolutePath() + "\".");
+ }
+ }
+ else
+ {
+ throw new TargetDirectoryException("Directory \"" + dir.getAbsolutePath() +
+ "\" not found.");
+ }
+
+ }
+
+ protected void processAllLists(StringBuffer sb, AmqpClass thisClass, AmqpMethod method, AmqpVersion version)
+ {
+ AmqpModel model = (version == null) ? _model : _versionToModelMap.get(version);
+
+
+ int lstart = sb.indexOf("%{");
+ while (lstart != -1)
+ {
+ int lend = sb.indexOf("}", lstart + 2);
+ if (lend > 0)
+ {
+ String listToken = sb.substring(lstart + 2, lend);
+ if (listToken.compareTo("VLIST") == 0)
+ {
+ processVersionList(sb, lstart, lend + 1);
+ }
+ else if (listToken.compareTo("CLIST") == 0)
+ {
+ processClassList(sb, lstart, lend + 1, model, version);
+ }
+ else if (listToken.compareTo("MLIST") == 0)
+ {
+ processMethodList(sb, lstart, lend + 1, thisClass);
+ }
+ else if (listToken.compareTo("FLIST") == 0)
+ {
+ // Pass the FieldMap from either a class or a method.
+ // If this is called from a class-level template, we assume that the
+ // class field list is required. In this case, method will be null.
+ processFieldList(sb, lstart, lend + 1,
+ (method == null ? thisClass.getFieldMap() : method.getFieldMap()),
+ version);
+ }
+ else if (listToken.compareTo("TLIST") == 0)
+ {
+ processConstantList(sb, lstart, lend + 1, _constantSet);
+ }
+ else
+ {
+ throw new AmqpTemplateException("Unknown list token \"%{" + listToken +
+ "}\" found in template at index " + lstart + ".");
+ }
+ }
+ lstart = sb.indexOf("%{", lstart + 1);
+ }
+ }
+
+ protected void processAllTokens(StringBuffer sb, AmqpClass thisClass, AmqpMethod method, AmqpField field,
+ AmqpVersion version)
+ {
+ int lstart = sb.indexOf("${");
+ while (lstart != -1)
+ {
+ int lend = sb.indexOf("}", lstart + 2);
+ if (lend > 0)
+ {
+ String token = sb.substring(lstart, lend + 1);
+ replaceToken(sb, lstart, token, processToken(token, thisClass, method, field, version));
+ }
+ lstart = sb.indexOf("${", lstart);
+ }
+ }
+
+ protected static void writeTargetFile(StringBuffer sb, File f)
+ {
+ try
+ {
+ f.getParentFile().mkdirs();
+ FileWriter fw = new FileWriter(f);
+ fw.write(sb.toString().toCharArray());
+ fw.flush();
+ fw.close();
+ }
+ catch (IOException e)
+ {
+ throw new AmqpTemplateException(e.getMessage());
+ }
+ }
+
+
+ protected static String getTemplateFileName(StringBuffer sb)
+ {
+ if (sb.charAt(0) != '&')
+ {
+ throw new AmqpTemplateException("No filename marker &{filename} found at start of template.");
+ }
+ int cr = sb.indexOf(Utils.LINE_SEPARATOR);
+ if (cr < 0)
+ {
+ throw new AmqpTemplateException("Bad template structure - unable to find first line.");
+ }
+ String fileName = sb.substring(2, cr - 1);
+ sb.delete(0, cr + 1);
+ return fileName;
+ }
+
+ protected static void replaceToken(StringBuffer sb, String token, String replacement)
+ {
+ replaceToken(sb, 0, token, replacement);
+ }
+
+ protected static void replaceToken(StringBuffer sb, int index, String token, String replacement)
+ {
+ if (replacement != null)
+ {
+ int start = sb.indexOf(token, index);
+ if (start != -1)
+ {
+ int len = token.length();
+ // Find first letter in token and determine if it is capitalized
+ char firstTokenLetter = getFirstLetter(token);
+ if (firstTokenLetter != 0 && Character.isUpperCase(firstTokenLetter))
+ {
+ sb.replace(start, start + len, Utils.firstUpper(replacement));
+ }
+ else
+ {
+ sb.replace(start, start + len, replacement);
+ }
+ }
+ }
+ }
+
+ private static char getFirstLetter(String str)
+ {
+ int len = str.length();
+ int index = 0;
+ char tokChar = str.charAt(index);
+ while (!Character.isLetter(tokChar) && index < len - 1)
+ {
+ tokChar = str.charAt(++index);
+ }
+ if (Character.isLetter(tokChar))
+ {
+ return tokChar;
+ }
+ return 0;
+ }
+
+ private static String loadTemplate(File f)
+ {
+ try
+ {
+ StringBuffer sb = new StringBuffer();
+ FileReader fr = new FileReader(f);
+ LineNumberReader lnr = new LineNumberReader(fr);
+ String line = lnr.readLine();
+ while (line != null)
+ {
+
+ sb.append(line);
+ sb.append(Utils.LINE_SEPARATOR);
+
+ line = lnr.readLine();
+ }
+ lnr.close();
+ fr.close();
+ return sb.toString();
+ }
+ catch (FileNotFoundException e)
+ {
+ throw new AmqpTemplateException("File not found: " + e.getMessage());
+ }
+ catch (IOException e)
+ {
+ throw new AmqpTemplateException("IOException: " + e.getMessage());
+ }
+ }
+
+ public String getDomainType(String domainName, AmqpVersion version)
+ {
+ if (version == null)
+ {
+ version = _versionSet.first();
+ }
+ return getDomainMap().getDomainType(domainName, version);
+ }
+
+
+ public void addFromNode(Node amqpNode, AmqpVersion version)
+ {
+ // 1c. Extract domains
+ getConstantSet().addFromNode(amqpNode, 0, version);
+ _versionToConstantSetMap.get(version).addFromNode(amqpNode, 0, version);
+
+ // 1d. Extract domains
+ getDomainMap().addFromNode(amqpNode, 0, version);
+ _versionToDomainMapMap.get(version).addFromNode(amqpNode, 0, version);
+
+ // 1e. Extract class/method/field heirarchy
+ getModel().addFromNode(amqpNode, 0, version);
+ _versionToModelMap.get(version).addFromNode(amqpNode, 0, version);
+ }
+
+
+ public String getOutputDirectory()
+ {
+ return _outputDirectory;
+ }
+
+ public String prepareConstantName(String constantName)
+ {
+ return prepareDomainName(constantName);
+ }
+
+
+ public boolean isFixedSizeType(String type)
+ {
+ return FIXED_SIZE_TYPES.containsKey(type);
+ }
+
+
+ public int getTypeSize(String type)
+ {
+ return FIXED_SIZE_TYPES.get(type);
+ }
+
+
+
+ // Model-level template processing
+ abstract protected void processModelTemplate(NamedTemplate template);
+
+ // Class-level template processing
+ abstract protected void processClassTemplate(NamedTemplate template, AmqpClass thisClass);
+
+ // Method-level template processing
+ abstract protected void processMethodTemplate(NamedTemplate template, AmqpClass thisClass,
+ AmqpMethod method);
+
+ // Field-level template processing
+ abstract protected void processTemplate(NamedTemplate template, AmqpClass thisClass,
+ AmqpMethod method, AmqpField field, AmqpVersion version);
+
+ abstract protected String prepareFilename(String filenameTemplate, AmqpClass thisClass, AmqpMethod method,
+ AmqpField field, AmqpVersion version);
+
+ abstract protected String processToken(String token, AmqpClass thisClass, AmqpMethod method,
+ AmqpField field, AmqpVersion version);
+
+ abstract protected void processClassList(StringBuffer sb, int listMarkerStartIndex, int listMarkerEndIndex,
+ AmqpModel model, AmqpVersion version);
+
+ abstract protected void processMethodList(StringBuffer sb, int listMarkerStartIndex, int listMarkerEndIndex,
+ AmqpClass thisClass);
+
+
+ abstract protected void processFieldList(StringBuffer sb, int listMarkerStartIndex, int listMarkerEndIndex,
+ AmqpFieldMap fieldMap, AmqpVersion version);
+
+ abstract protected void processConstantList(StringBuffer sb, int listMarkerStartIndex, int listMarkerEndIndex,
+ AmqpConstantSet constantSet);
+
+
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/JavaGenerator.java b/qpid/gentools/src/org/apache/qpid/gentools/JavaGenerator.java
new file mode 100644
index 0000000000..7730fca1bd
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/JavaGenerator.java
@@ -0,0 +1,1826 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeMap;
+
+public class JavaGenerator extends Generator
+{
+ // TODO: Move this to parent class
+ protected static final int FIELD_NAME = 0;
+ protected static final int FIELD_CODE_TYPE = 1;
+
+ private class DomainInfo
+ {
+ final public String type;
+ final public String size;
+ final public String encodingType;
+ final public String encodeExpression;
+ final public String decodeExpression;
+
+ public DomainInfo(String domain, String size, String encodingType, String encodeExpression, String decodeExpression)
+ {
+ this.type = domain;
+ this.size = size;
+ this.encodeExpression = encodeExpression;
+ this.decodeExpression = decodeExpression;
+ this.encodingType = encodingType;
+ }
+ }
+
+ private static TreeMap<String, DomainInfo> typeMap = new TreeMap<String, DomainInfo>();
+
+ // Methods used for generation of code snippets called from the field map parsers
+
+ // Common methods
+ private final CommandGenerateMethod declarationGenerateMethod = new CommandGenerateMethod()
+ {
+ public String generate(String codeType, AmqpField field, AmqpVersionSet versionSet, int indentSize, int tabSize, boolean notLast)
+ {
+ return generateFieldDeclaration(codeType, field, versionSet, indentSize, tabSize, notLast);
+ }
+ };
+
+ private MangledGenerateMethod mangledDeclarationGenerateMethod = new MangledGenerateMethod()
+ {
+ public String generate(AmqpField field, int indentSize, int tabSize, boolean notLast)
+ {
+ return generateMangledFieldDeclaration(field, indentSize, tabSize, notLast);
+ }
+ };
+
+ // Methods for MessageBody classes
+ private CommandGenerateMethod mbGetGenerateMethod = new CommandGenerateMethod()
+ {
+ public String generate(String codeType, AmqpField field, AmqpVersionSet versionSet, int indentSize, int tabSize, boolean notLast)
+ {
+ return generateMbGetMethod(codeType, field, versionSet, indentSize, tabSize, notLast); //To change body of implemented methods use File | Settings | File Templates.
+ }
+ };
+
+ private MangledGenerateMethod mbMangledGetGenerateMethod = new MangledGenerateMethod()
+ {
+ public String generate(AmqpField field, int indentSize, int tabSize, boolean notLast)
+ {
+ return generateMbMangledGetMethod(field, indentSize, tabSize, notLast);
+ }
+ };
+ private CommandGenerateMethod mbParamListGenerateMethod = new CommandGenerateMethod()
+ {
+ public String generate(String codeType, AmqpField field, AmqpVersionSet versionSet, int indentSize, int tabSize, boolean notLast)
+ {
+ return generateMbParamList(codeType, field, versionSet, indentSize, tabSize, notLast);
+ }
+ };
+ private CommandGenerateMethod mbPassedParamListGenerateMethod = new CommandGenerateMethod()
+ {
+ public String generate(String codeType, AmqpField field, AmqpVersionSet versionSet, int indentSize, int tabSize, boolean notLast)
+ {
+ return generateMbPassedParamList(codeType, field, versionSet, indentSize, tabSize, notLast);
+ }
+ };
+ private MangledGenerateMethod mbMangledParamListGenerateMethod = new MangledGenerateMethod()
+ {
+ public String generate(AmqpField field, int indentSize, int tabSize, boolean notLast)
+ {
+ return generateMbMangledParamList(field, indentSize, tabSize, notLast);
+ }
+ };
+ private MangledGenerateMethod mbMangledPassedParamListGenerateMethod = new MangledGenerateMethod()
+ {
+ public String generate(AmqpField field, int indentSize, int tabSize, boolean notLast)
+ {
+ return generateMbMangledPassedParamList(field, indentSize, tabSize, notLast);
+ }
+ };
+ private CommandGenerateMethod mbBodyInitGenerateMethod = new CommandGenerateMethod()
+ {
+ public String generate(String codeType, AmqpField field, AmqpVersionSet versionSet, int indentSize, int tabSize, boolean notLast)
+ {
+ return generateMbBodyInit(codeType, field, versionSet, indentSize, tabSize, notLast);
+ }
+ };
+ private MangledGenerateMethod mbMangledBodyInitGenerateMethod = new MangledGenerateMethod()
+ {
+ public String generate(AmqpField field, int indentSize, int tabSize, boolean notLast)
+ {
+ return generateMbMangledBodyInit(field, indentSize, tabSize, notLast);
+ }
+ };
+ private GenerateMethod mbSizeGenerateMethod = new GenerateMethod()
+ {
+ public String generate(String domainType, String fieldName, int ordinal, int indentSize, int tabSize)
+ {
+ return generateMbFieldSize(domainType, fieldName, ordinal, indentSize, tabSize);
+ }
+ };
+ private BitFieldGenerateMethod mbBitSizeGenerateMethod = new BitFieldGenerateMethod()
+ {
+ public String generate(List<String> bitFieldList, int ordinal, int indentSize, int tabSize)
+ {
+ return generateMbBitArrayFieldSize(bitFieldList, ordinal, indentSize, tabSize);
+ }
+ };
+ private GenerateMethod mbEncodeGenerateMethod = new GenerateMethod()
+ {
+ public String generate(String domainType, String fieldName, int ordinal, int indentSize, int tabSize)
+ {
+ return generateMbFieldEncode(domainType, fieldName, ordinal, indentSize, tabSize);
+ }
+ };
+ private BitFieldGenerateMethod mbBitEncodeGenerateMethod = new BitFieldGenerateMethod()
+ {
+ public String generate(List<String> bitFieldList, int ordinal, int indentSize, int tabSize)
+ {
+ return generateMbBitFieldEncode(bitFieldList, ordinal, indentSize, tabSize);
+ }
+ };
+ private GenerateMethod mbDecodeGenerateMethod = new GenerateMethod()
+ {
+ public String generate(String domainType, String fieldName, int ordinal, int indentSize, int tabSize)
+ {
+ return generateMbFieldDecode(domainType, fieldName, ordinal, indentSize, tabSize);
+ }
+ };
+ private BitFieldGenerateMethod mbBitDecodeGenerateMethod = new BitFieldGenerateMethod()
+ {
+ public String generate(List<String> bitFieldList, int ordinal, int indentSize, int tabSize)
+ {
+ return generateMbBitFieldDecode(bitFieldList, ordinal, indentSize, tabSize);
+ }
+ };
+ private GenerateMethod mbToStringGenerateMethod = new GenerateMethod()
+ {
+ public String generate(String domainType, String fieldName, int ordinal, int indentSize, int tabSize)
+ {
+ return generateMbFieldToString(domainType, fieldName, ordinal, indentSize, tabSize);
+ }
+ };
+ private BitFieldGenerateMethod mbBitToStringGenerateMethod = new BitFieldGenerateMethod()
+ {
+ public String generate(List<String> bitFieldList, int ordinal, int indentSize, int tabSize)
+ {
+ return generateMbBitFieldToString(bitFieldList, ordinal, indentSize, tabSize);
+ }
+ };
+
+ // Methods for PropertyContentHeader classes
+ private CommandGenerateMethod pchClearGenerateMethod = new CommandGenerateMethod()
+ {
+ public String generate(String codeType, AmqpField field, AmqpVersionSet versionSet, int indentSize, int tabSize, boolean notLast)
+ {
+ return generatePchClearMethod(codeType, field, versionSet, indentSize, tabSize, notLast);
+ }
+ };
+ private MangledGenerateMethod pchMangledClearGenerateMethod = new MangledGenerateMethod()
+ {
+ public String generate(AmqpField field, int indentSize, int tabSize, boolean notLast)
+ {
+ return generatePchMangledClearMethod(field, indentSize, tabSize, notLast);
+ }
+ };
+ private CommandGenerateMethod pchGetGenerateMethod = new CommandGenerateMethod()
+ {
+ public String generate(String codeType, AmqpField field, AmqpVersionSet versionSet, int indentSize, int tabSize, boolean notLast)
+ {
+ return generatePchGetMethod(codeType, field, versionSet, indentSize, tabSize, notLast);
+ }
+ };
+ private MangledGenerateMethod pchMangledGetGenerateMethod = new MangledGenerateMethod()
+ {
+ public String generate(AmqpField field, int indentSize, int tabSize, boolean notLast)
+ {
+ return generatePchMangledGetMethod(field, indentSize, tabSize, notLast);
+ }
+ };
+ private CommandGenerateMethod pchSetGenerateMethod = new CommandGenerateMethod()
+ {
+ public String generate(String codeType, AmqpField field, AmqpVersionSet versionSet, int indentSize, int tabSize, boolean notLast)
+ {
+ return generatePchSetMethod(codeType, field, versionSet, indentSize, tabSize, notLast);
+ }
+ };
+ private MangledGenerateMethod pchMangledSetGenerateMethod = new MangledGenerateMethod()
+ {
+ public String generate(AmqpField field, int indentSize, int tabSize, boolean notLast)
+ {
+ return generatePchMangledSetMethod(field, indentSize, tabSize, notLast);
+ }
+ };
+ private GenerateMethod pchSizeGenerateMethod = new GenerateMethod()
+ {
+ public String generate(String domainType, String fieldName, int ordinal, int indentSize, int tabSize)
+ {
+ return generatePchFieldSize(domainType, fieldName, ordinal, indentSize, tabSize);
+ }
+ };
+ private BitFieldGenerateMethod pchBitSizeGenerateMethod = new BitFieldGenerateMethod()
+ {
+ public String generate(List<String> bitFieldList, int ordinal, int indentSize, int tabSize)
+ {
+ return generatePchBitArrayFieldSize(bitFieldList, ordinal, indentSize, tabSize);
+ }
+ };
+ private GenerateMethod pchEncodeGenerateMethod = new GenerateMethod()
+ {
+ public String generate(String domainType, String fieldName, int ordinal, int indentSize, int tabSize)
+ {
+ return generatePchFieldEncode(domainType, fieldName, ordinal, indentSize, tabSize);
+ }
+ };
+ private BitFieldGenerateMethod pchBitEncodeGenerateMethod = new BitFieldGenerateMethod()
+ {
+ public String generate(List<String> bitFieldList, int ordinal, int indentSize, int tabSize)
+ {
+ return generatePchBitFieldEncode(bitFieldList, ordinal, indentSize, tabSize);
+ }
+ };
+ private GenerateMethod pchDecodeGenerateMethod = new GenerateMethod()
+ {
+ public String generate(String domainType, String fieldName, int ordinal, int indentSize, int tabSize)
+ {
+ return generatePchFieldDecode(domainType, fieldName, ordinal, indentSize, tabSize);
+ }
+ };
+ private BitFieldGenerateMethod pchBitDecodeGenerateMethod = new BitFieldGenerateMethod()
+ {
+ public String generate(List<String> bitFieldList, int ordinal, int indentSize, int tabSize)
+ {
+ return generatePchBitFieldDecode(bitFieldList, ordinal, indentSize, tabSize);
+ }
+ };
+ private GenerateMethod pchGetPropertyFlagsGenerateMethod = new GenerateMethod()
+ {
+ public String generate(String domainType, String fieldName, int ordinal, int indentSize, int tabSize)
+ {
+ return generatePchGetPropertyFlags(domainType, fieldName, ordinal, indentSize, tabSize);
+ }
+ };
+ private BitFieldGenerateMethod pchBitGetPropertyFlagsGenerateMethod = new BitFieldGenerateMethod()
+ {
+ public String generate(List<String> bitFieldList, int ordinal, int indentSize, int tabSize)
+ {
+ return generatePchBitGetPropertyFlags(bitFieldList, ordinal, indentSize, tabSize);
+ }
+ };
+ private GenerateMethod pchSetPropertyFlagsGenerateMethod = new GenerateMethod()
+ {
+ public String generate(String domainType, String fieldName, int ordinal, int indentSize, int tabSize)
+ {
+ return generatePchSetPropertyFlags(domainType, fieldName, ordinal, indentSize, tabSize);
+ }
+ };
+ private BitFieldGenerateMethod pchBitSetPropertyFlagsGenerateMethod = new BitFieldGenerateMethod()
+ {
+ public String generate(List<String> bitFieldList, int ordinal, int indentSize, int tabSize)
+ {
+ return generatePchBitSetPropertyFlags(bitFieldList, ordinal, indentSize, tabSize);
+ }
+ };
+
+
+ public String getNativeType(String type)
+ {
+ return typeMap.get(type).type;
+ }
+
+ public String getEncodingType(String type)
+ {
+ return typeMap.get(type).encodingType;
+ }
+
+
+ public JavaGenerator()
+ {
+ super();
+ // Load Java type and size maps.
+ // Adjust or add to these lists as new types are added/defined.
+ // The char '#' will be replaced by the field variable name (any type).
+ // The char '~' will be replaced by the compacted bit array size (type bit only).
+ typeMap.put("bit", new DomainInfo(
+ "boolean", // Java code type
+ "~", // size
+ "Boolean", // Java code type
+ "EncodingUtils.writeBooleans(buffer, #)", // encode expression
+ "# = EncodingUtils.readBooleans(buffer)")); // decode expression
+ typeMap.put("bitfield", new DomainInfo(
+ "byte", // Java code type
+ "~", // size
+ "Bitfield",
+ "EncodingUtils.writeBooleans(buffer, #)", // encode expression
+ "# = EncodingUtils.readBooleans(buffer)")); // decode expression
+
+ typeMap.put("content", new DomainInfo(
+ "Content", // Java code type
+ "EncodingUtils.encodedContentLength(#)", // size
+ "Content", // Java code type
+ "EncodingUtils.writeContentBytes(buffer, #)", // encode expression
+ "# = EncodingUtils.readContent(buffer)")); // decode expression
+ typeMap.put("long", new DomainInfo(
+ "long", // Java code type
+ "4", // size
+ "UnsignedInteger", // Java code type
+ "EncodingUtils.writeUnsignedInteger(buffer, #)", // encode expression
+ "# = buffer.getUnsignedInt()")); // decode expression
+ typeMap.put("longlong", new DomainInfo(
+ "long", // Java code type
+ "8", // size
+ "Long",
+ "buffer.putLong(#)", // encode expression
+ "# = buffer.getLong()")); // decode expression
+ typeMap.put("longstr", new DomainInfo(
+ "byte[]", // Java code type
+ "EncodingUtils.encodedLongstrLength(#)", // size
+ "Bytes",
+ "EncodingUtils.writeLongStringBytes(buffer, #)", // encode expression
+ "# = EncodingUtils.readLongstr(buffer)")); // decode expression
+ typeMap.put("octet", new DomainInfo(
+ "short", // Java code type
+ "1", // size
+ "UnsignedByte",
+ "EncodingUtils.writeUnsignedByte(buffer, #)", // encode expression
+ "# = buffer.getUnsigned()")); // decode expression
+ typeMap.put("short", new DomainInfo(
+ "int", // Java code type
+ "2", // size
+ "UnsignedShort",
+ "EncodingUtils.writeUnsignedShort(buffer, #)", // encode expression
+ "# = buffer.getUnsignedShort()")); // decode expression
+ typeMap.put("shortstr", new DomainInfo(
+ "AMQShortString", // Java code type
+ "EncodingUtils.encodedShortStringLength(#)", // size
+ "AMQShortString", // Java code type
+ "EncodingUtils.writeShortStringBytes(buffer, #)", // encode expression
+ "# = EncodingUtils.readAMQShortString(buffer)")); // decode expression
+ typeMap.put("table", new DomainInfo(
+ "FieldTable", // Java code type
+ "EncodingUtils.encodedFieldTableLength(#)", // size
+ "FieldTable", // Java code type
+ "EncodingUtils.writeFieldTableBytes(buffer, #)", // encode expression
+ "# = EncodingUtils.readFieldTable(buffer)")); // decode expression
+ typeMap.put("timestamp", new DomainInfo(
+ "long", // Java code type
+ "8", // size
+ "Timestamp",
+ "EncodingUtils.writeTimestamp(buffer, #)", // encode expression
+ "# = EncodingUtils.readTimestamp(buffer)")); // decode expression
+ }
+
+ // === Start of methods for Interface LanguageConverter ===
+
+ public String prepareClassName(String className)
+ {
+ return camelCaseName(className, true);
+ }
+
+ public String prepareMethodName(String methodName)
+ {
+ return camelCaseName(methodName, false);
+ }
+
+ public String prepareDomainName(String domainName)
+ {
+ return camelCaseName(domainName, false);
+ }
+
+
+ public String getGeneratedType(String domainName, AmqpVersion version)
+ {
+ String domainType = getDomainType(domainName, version);
+ if (domainType == null)
+ {
+ throw new AmqpTypeMappingException("Domain type \"" + domainName +
+ "\" not found in Java typemap.");
+ }
+ DomainInfo info = typeMap.get(domainType);
+ if (info == null)
+ {
+ throw new AmqpTypeMappingException("Unknown domain: \"" + domainType + "\"");
+ }
+ return info.type;
+ }
+
+ // === Abstract methods from class Generator - Java-specific implementations ===
+
+ @Override
+ protected String prepareFilename(String filenameTemplate, AmqpClass thisClass, AmqpMethod method,
+ AmqpField field, AmqpVersion version)
+ {
+ StringBuffer sb = new StringBuffer(filenameTemplate);
+ if (thisClass != null)
+ {
+ replaceToken(sb, "${CLASS}", thisClass.getName());
+ }
+ if (method != null)
+ {
+ replaceToken(sb, "${METHOD}", method.getName());
+ }
+ if (field != null)
+ {
+ replaceToken(sb, "${FIELD}", field.getName());
+ }
+ if (version != null)
+ {
+ replaceToken(sb, "${MAJOR}", String.valueOf(version.getMajor()));
+ replaceToken(sb, "${MINOR}", String.valueOf(version.getMinor()));
+ }
+ return sb.toString();
+ }
+
+ @Override
+ protected void processModelTemplate(NamedTemplate template)
+ {
+ processTemplate(template, null, null, null, null);
+ }
+
+ @Override
+ protected void processClassTemplate(NamedTemplate template, AmqpClass thisClass)
+ {
+ processTemplate(template, thisClass, null, null,
+ thisClass.getVersionSet().size() == 1 ? thisClass.getVersionSet().first() : null);
+ }
+
+ @Override
+ protected void processMethodTemplate(NamedTemplate template, AmqpClass thisClass,
+ AmqpMethod method)
+ {
+ processTemplate(template, thisClass, method, null,
+ thisClass.getVersionSet().size() == 1 ? thisClass.getVersionSet().first() : null);
+ }
+
+ protected void processFieldTemplate(NamedTemplate template, AmqpClass thisClass,
+ AmqpMethod method, AmqpField field)
+ {
+ processTemplate(template, thisClass, method, field,
+ thisClass.getVersionSet().size() == 1 ? thisClass.getVersionSet().first() : null);
+ }
+
+ @Override
+ protected void processTemplate(NamedTemplate template, AmqpClass thisClass,
+ AmqpMethod method, AmqpField field, AmqpVersion version)
+ {
+ StringBuffer sb = new StringBuffer(template.getTemplate());
+ String filename = prepareFilename(getTemplateFileName(sb), thisClass, method, field, version);
+ processTemplate(sb, thisClass, method, field, template.getName(), version);
+ writeTargetFile(sb, new File(getOutputDirectory() + Utils.FILE_SEPARATOR + filename));
+ generatedFileCounter++;
+ }
+
+ protected void processTemplate(StringBuffer sb, AmqpClass thisClass, AmqpMethod method,
+ AmqpField field, String templateFileName, AmqpVersion version)
+ {
+ try
+ {
+ processAllLists(sb, thisClass, method, version);
+ }
+ catch (AmqpTemplateException e)
+ {
+ System.out.println("WARNING: " + templateFileName + ": " + e.getMessage());
+ }
+ try
+ {
+ processAllTokens(sb, thisClass, method, field, version);
+ }
+ catch (AmqpTemplateException e)
+ {
+ System.out.println("WARNING: " + templateFileName + ": " + e.getMessage());
+ }
+ }
+
+ @Override
+ protected String processToken(String token, AmqpClass thisClass, AmqpMethod method, AmqpField field,
+ AmqpVersion version)
+ {
+ if (token.compareTo("${GENERATOR}") == 0)
+ {
+ return GENERATOR_INFO;
+ }
+ if (token.compareTo("${CLASS}") == 0 && thisClass != null)
+ {
+ return thisClass.getName();
+ }
+ if (token.compareTo("${CLASS_ID_INIT}") == 0 && thisClass != null)
+ {
+ return generateIndexInitializer("registerClassId", thisClass.getIndexMap(), 8);
+ }
+ if (token.compareTo("${METHOD}") == 0 && method != null)
+ {
+ return method.getName();
+ }
+ if (token.compareTo("${METHOD_ID_INIT}") == 0 && method != null)
+ {
+ return generateIndexInitializer("registerMethodId", method.getIndexMap(), 8);
+ }
+ if (token.compareTo("${FIELD}") == 0 && field != null)
+ {
+ return field.getName();
+ }
+
+ // This token is used only with class or method-level templates
+ if (token.compareTo("${pch_property_flags_declare}") == 0)
+ {
+ return generatePchPropertyFlagsDeclare();
+ }
+ else if (token.compareTo("${pch_property_flags_initializer}") == 0)
+ {
+ int mapSize = method == null ? thisClass.getFieldMap().size() : method.getFieldMap().size();
+ return generatePchPropertyFlagsInitializer(mapSize);
+ }
+ else if (token.compareTo("${pch_compact_property_flags_initializer}") == 0)
+ {
+ return generatePchCompactPropertyFlagsInitializer(thisClass, 8, 4);
+ }
+ else if (token.compareTo("${pch_compact_property_flags_check}") == 0)
+ {
+ return generatePchCompactPropertyFlagsCheck(thisClass, 8, 4);
+ }
+
+ // Oops!
+ throw new AmqpTemplateException("Template token " + token + " unknown.");
+ }
+
+ @Override
+ protected void processClassList(StringBuffer sb, int listMarkerStartIndex, int listMarkerEndIndex,
+ AmqpModel model, AmqpVersion version)
+ {
+ String codeSnippet;
+ int lend = sb.indexOf(CR, listMarkerStartIndex) + 1; // Include cr at end of line
+ String tline = sb.substring(listMarkerEndIndex, lend); // Line excluding line marker, including cr
+ int tokStart = tline.indexOf('$');
+ String token = tline.substring(tokStart).trim();
+ sb.delete(listMarkerStartIndex, lend);
+
+ if (token.compareTo("${reg_map_put_method}") == 0)
+ {
+ codeSnippet = generateRegistry(model, 8, 4);
+ }
+
+ else // Oops!
+ {
+ throw new AmqpTemplateException("Template token " + token + " unknown.");
+ }
+
+ sb.insert(listMarkerStartIndex, codeSnippet);
+ }
+
+ @Override
+ protected void processMethodList(StringBuffer sb, int listMarkerStartIndex, int listMarkerEndIndex,
+ AmqpClass thisClass)
+ {
+ String codeSnippet;
+ int lend = sb.indexOf(CR, listMarkerStartIndex) + 1; // Include cr at end of line
+ String tline = sb.substring(listMarkerEndIndex, lend); // Line excluding line marker, including cr
+ int tokStart = tline.indexOf('$');
+ String token = tline.substring(tokStart).trim();
+ sb.delete(listMarkerStartIndex, lend);
+
+ //TODO - we don't have any cases of this (yet).
+ if (token.compareTo("${???}") == 0)
+ {
+ codeSnippet = token;
+ }
+ else // Oops!
+ {
+ throw new AmqpTemplateException("Template token " + token + " unknown.");
+ }
+
+ sb.insert(listMarkerStartIndex, codeSnippet);
+ }
+
+ @Override
+ protected void processFieldList(StringBuffer sb, int listMarkerStartIndex, int listMarkerEndIndex,
+ AmqpFieldMap fieldMap, AmqpVersion version)
+ {
+ String codeSnippet;
+ int lend = sb.indexOf(CR, listMarkerStartIndex) + 1; // Include cr at end of line
+ String tline = sb.substring(listMarkerEndIndex, lend); // Line excluding line marker, including cr
+ int tokStart = tline.indexOf('$');
+ String token = tline.substring(tokStart).trim();
+ sb.delete(listMarkerStartIndex, lend);
+
+ // Field declarations - common to MethodBody and PropertyContentHeader classes
+ if (token.compareTo("${field_declaration}") == 0)
+ {
+ codeSnippet = fieldMap.parseFieldMap(declarationGenerateMethod,
+ mangledDeclarationGenerateMethod, 4, 4, this);
+ }
+
+ // MethodBody classes
+ else if (token.compareTo("${mb_field_get_method}") == 0)
+ {
+ codeSnippet = fieldMap.parseFieldMap(mbGetGenerateMethod,
+ mbMangledGetGenerateMethod, 4, 4, this);
+ }
+ else if (token.compareTo("${mb_field_parameter_list}") == 0)
+ {
+ // <cringe> The code generated by this is ugly... It puts a comma on a line by itself!
+ // TODO: Find a more elegant solution here sometime...
+ codeSnippet = fieldMap.size() > 0 ? Utils.createSpaces(42) + "," + CR : "";
+ // </cringe>
+ codeSnippet += fieldMap.parseFieldMap(mbParamListGenerateMethod,
+ mbMangledParamListGenerateMethod, 42, 4, this);
+ }
+
+ else if (token.compareTo("${mb_field_passed_parameter_list}") == 0)
+ {
+ // <cringe> The code generated by this is ugly... It puts a comma on a line by itself!
+ // TODO: Find a more elegant solution here sometime...
+ codeSnippet = fieldMap.size() > 0 ? Utils.createSpaces(42) + "," + CR : "";
+ // </cringe>
+ codeSnippet += fieldMap.parseFieldMap(mbPassedParamListGenerateMethod,
+ mbMangledPassedParamListGenerateMethod, 42, 4, this);
+ }
+ else if (token.compareTo("${mb_field_body_initialize}") == 0)
+ {
+ codeSnippet = fieldMap.parseFieldMap(mbBodyInitGenerateMethod,
+ mbMangledBodyInitGenerateMethod, 8, 4, this);
+ }
+ else if (token.compareTo("${mb_field_size}") == 0)
+ {
+ codeSnippet = fieldMap.parseFieldMapOrdinally(mbSizeGenerateMethod,
+ mbBitSizeGenerateMethod, 8, 4, this);
+ }
+ else if (token.compareTo("${mb_field_encode}") == 0)
+ {
+ codeSnippet = fieldMap.parseFieldMapOrdinally(mbEncodeGenerateMethod,
+ mbBitEncodeGenerateMethod, 8, 4, this);
+ }
+ else if (token.compareTo("${mb_field_decode}") == 0)
+ {
+ codeSnippet = fieldMap.parseFieldMapOrdinally(mbDecodeGenerateMethod,
+ mbBitDecodeGenerateMethod, 8, 4, this);
+ }
+ else if (token.compareTo("${mb_field_to_string}") == 0)
+ {
+ codeSnippet = fieldMap.parseFieldMapOrdinally(mbToStringGenerateMethod,
+ mbBitToStringGenerateMethod, 8, 4, this);
+ }
+
+ // PropertyContentHeader classes
+ else if (token.compareTo("${pch_field_list_size}") == 0)
+ {
+ codeSnippet = fieldMap.parseFieldMapOrdinally(pchSizeGenerateMethod,
+ pchBitSizeGenerateMethod, 12, 4, this);
+ }
+ else if (token.compareTo("${pch_field_list_payload}") == 0)
+ {
+ codeSnippet = fieldMap.parseFieldMapOrdinally(pchEncodeGenerateMethod,
+ pchBitEncodeGenerateMethod, 12, 4, this);
+ }
+ else if (token.compareTo("${pch_field_list_decode}") == 0)
+ {
+ codeSnippet = fieldMap.parseFieldMapOrdinally(pchDecodeGenerateMethod,
+ pchBitDecodeGenerateMethod, 12, 4, this);
+ }
+ else if (token.compareTo("${pch_get_compact_property_flags}") == 0)
+ {
+ codeSnippet = fieldMap.parseFieldMapOrdinally(pchGetPropertyFlagsGenerateMethod,
+ pchBitGetPropertyFlagsGenerateMethod, 8, 4, this);
+ }
+ else if (token.compareTo("${pch_set_compact_property_flags}") == 0)
+ {
+ codeSnippet = fieldMap.parseFieldMapOrdinally(pchSetPropertyFlagsGenerateMethod,
+ pchBitSetPropertyFlagsGenerateMethod, 8, 4, this);
+ }
+ else if (token.compareTo("${pch_field_clear_methods}") == 0)
+ {
+ codeSnippet = fieldMap.parseFieldMap(pchClearGenerateMethod,
+ pchMangledClearGenerateMethod, 4, 4, this);
+ }
+ else if (token.compareTo("${pch_field_get_methods}") == 0)
+ {
+ codeSnippet = fieldMap.parseFieldMap(pchGetGenerateMethod,
+ pchMangledGetGenerateMethod, 4, 4, this);
+ }
+ else if (token.compareTo("${pch_field_set_methods}") == 0)
+ {
+ codeSnippet = fieldMap.parseFieldMap(pchSetGenerateMethod,
+ pchMangledSetGenerateMethod, 4, 4, this);
+ }
+
+ else // Oops!
+ {
+ throw new AmqpTemplateException("Template token " + token + " unknown.");
+ }
+ sb.insert(listMarkerStartIndex, codeSnippet);
+ }
+
+ @Override
+ protected void processConstantList(StringBuffer sb, int listMarkerStartIndex, int listMarkerEndIndex,
+ AmqpConstantSet constantSet)
+ {
+ String codeSnippet;
+ int lend = sb.indexOf(CR, listMarkerStartIndex) + 1; // Include cr at end of line
+ String tline = sb.substring(listMarkerEndIndex, lend); // Line excluding line marker, including cr
+ int tokStart = tline.indexOf('$');
+ String token = tline.substring(tokStart).trim();
+ sb.delete(listMarkerStartIndex, lend);
+
+ if (token.compareTo("${const_get_method}") == 0)
+ {
+ codeSnippet = generateConstantGetMethods(constantSet, 4, 4);
+ }
+
+ else // Oops!
+ {
+ throw new AmqpTemplateException("Template token " + token + " unknown.");
+ }
+
+ sb.insert(listMarkerStartIndex, codeSnippet);
+ }
+
+ // === Protected and private helper functions unique to Java implementation ===
+
+ // Methods used for generation of code snippets called from the field map parsers
+
+ // Common methods
+
+ protected String generateFieldDeclaration(String codeType, AmqpField field,
+ AmqpVersionSet versionSet, int indentSize, int tabSize, boolean nextFlag)
+ {
+ return Utils.createSpaces(indentSize) + "public " + codeType + " " + field.getName() +
+ "; // AMQP version(s): " + versionSet + CR;
+ }
+
+ protected String generateMangledFieldDeclaration(AmqpField field, int indentSize,
+ int tabSize, boolean nextFlag)
+ {
+ StringBuffer sb = new StringBuffer();
+ Iterator<String> dItr = field.getDomainMap().keySet().iterator();
+ int domainCntr = 0;
+ while (dItr.hasNext())
+ {
+ String domainName = dItr.next();
+ AmqpVersionSet versionSet = field.getDomainMap().get(domainName);
+ String codeType = getGeneratedType(domainName, versionSet.first());
+ sb.append(Utils.createSpaces(indentSize) + "public " + codeType + " " +
+ field.getName() + "_" + (domainCntr++) + "; // AMQP Version(s): " + versionSet +
+ CR);
+ }
+ return sb.toString();
+ }
+
+ protected String generateIndexInitializer(String mapName, AmqpOrdinalVersionMap indexMap, int indentSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+
+ Iterator<Integer> iItr = indexMap.keySet().iterator();
+ while (iItr.hasNext())
+ {
+ int index = iItr.next();
+ AmqpVersionSet versionSet = indexMap.get(index);
+ Iterator<AmqpVersion> vItr = versionSet.iterator();
+ while (vItr.hasNext())
+ {
+ AmqpVersion version = vItr.next();
+ sb.append(indent + mapName + "( (byte) " + version.getMajor() + ", (byte) " + version.getMinor() + ", " + index + ");" + CR);
+ }
+ }
+ return sb.toString();
+ }
+
+ protected String generateRegistry(AmqpModel model, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+
+ for (String className : model.getClassMap().keySet())
+ {
+ AmqpClass thisClass = model.getClassMap().get(className);
+ for (String methodName : thisClass.getMethodMap().keySet())
+ {
+ AmqpMethod method = thisClass.getMethodMap().get(methodName);
+ for (AmqpVersion version : model.getVersionSet())
+ {
+ // Find class and method index for this version (if it exists)
+ try
+ {
+ int classIndex = findIndex(thisClass.getIndexMap(), version);
+ int methodIndex = findIndex(method.getIndexMap(), version);
+ sb.append(indent + "registerMethod(" + CR);
+ sb.append(indent + tab + "(short)" + classIndex +
+ ", (short)" + methodIndex + ", (byte)" + version.getMajor() +
+ ", (byte)" + version.getMinor() + ", " + CR);
+ sb.append(indent + tab + Utils.firstUpper(thisClass.getName()) +
+ Utils.firstUpper(method.getName()) + "Body.getFactory());" + CR);
+ }
+ catch (Exception e)
+ {
+ } // Ignore
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ protected int findIndex(TreeMap<Integer, AmqpVersionSet> map, AmqpVersion version)
+ {
+ Iterator<Integer> iItr = map.keySet().iterator();
+ while (iItr.hasNext())
+ {
+ int index = iItr.next();
+ AmqpVersionSet versionSet = map.get(index);
+ if (versionSet.contains(version))
+ {
+ return index;
+ }
+ }
+ throw new IllegalArgumentException("Index not found");
+ }
+
+ // Methods for AmqpConstants class
+
+
+ public String prepareConstantName(String constantName)
+ {
+ return upperCaseName(constantName);
+ }
+
+
+ protected String generateConstantGetMethods(AmqpConstantSet constantSet,
+ int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+
+ for (AmqpConstant constant : constantSet.getContstants())
+ {
+
+ if (constant.isVersionConsistent(constantSet.getVersionSet()))
+ {
+ // return a constant
+ String value = constant.firstKey();
+ if (Utils.containsOnlyDigits(value))
+ {
+ sb.append(indent + "public static final int " + constant.getName() + " = " +
+ constant.firstKey() + ";" + CR);
+ }
+ else if (Utils.containsOnlyDigitsAndDecimal(value))
+ {
+ sb.append(indent + "public static double " + constant.getName() + " = " +
+ constant.firstKey() + "; " + CR);
+ }
+ else
+ {
+ sb.append(indent + "public static String " + constant.getName() + " = " +
+ constant.firstKey() + "\"; " + CR);
+
+ }
+ sb.append(CR);
+ }
+ else
+ {
+ // Return version-specific constant
+ sb.append(generateVersionDependentGet(constant, "String", "", "\"", "\"", indentSize, tabSize));
+ sb.append(generateVersionDependentGet(constant, "int", "AsInt", "", "", indentSize, tabSize));
+ sb.append(generateVersionDependentGet(constant, "double", "AsDouble", "(double)", "", indentSize, tabSize));
+ sb.append(CR);
+ }
+ }
+ return sb.toString();
+ }
+
+ protected String generateVersionDependentGet(AmqpConstant constant,
+ String methodReturnType, String methodNameSuffix, String returnPrefix, String returnPostfix,
+ int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+ sb.append(indent + "public static " + methodReturnType + " " + constant.getName() +
+ methodNameSuffix + "(byte major, byte minor) throws AMQProtocolVersionException" + CR);
+ sb.append(indent + "{" + CR);
+ boolean first = true;
+ Iterator<String> sItr = constant.keySet().iterator();
+ while (sItr.hasNext())
+ {
+ String value = sItr.next();
+ AmqpVersionSet versionSet = constant.get(value);
+ sb.append(indent + tab + (first ? "" : "else ") + "if (" + generateVersionCheck(versionSet) +
+ ")" + CR);
+ sb.append(indent + tab + "{" + CR);
+ if (methodReturnType.compareTo("int") == 0 && !Utils.containsOnlyDigits(value))
+ {
+ sb.append(generateConstantDeclarationException(constant.getName(), methodReturnType,
+ indentSize + (2 * tabSize), tabSize));
+ }
+ else if (methodReturnType.compareTo("double") == 0 && !Utils.containsOnlyDigitsAndDecimal(value))
+ {
+ sb.append(generateConstantDeclarationException(constant.getName(), methodReturnType,
+ indentSize + (2 * tabSize), tabSize));
+ }
+ else
+ {
+ sb.append(indent + tab + tab + "return " + returnPrefix + value + returnPostfix + ";" + CR);
+ }
+ sb.append(indent + tab + "}" + CR);
+ first = false;
+ }
+ sb.append(indent + tab + "else" + CR);
+ sb.append(indent + tab + "{" + CR);
+ sb.append(indent + tab + tab + "throw new AMQProtocolVersionException(\"Constant \\\"" +
+ constant.getName() + "\\\" \" +" + CR);
+ sb.append(indent + tab + tab + tab +
+ "\"is undefined for AMQP version \" + major + \"-\" + minor + \".\");" + CR);
+ sb.append(indent + tab + "}" + CR);
+ sb.append(indent + "}" + CR);
+ return sb.toString();
+ }
+
+ protected String generateConstantDeclarationException(String name, String methodReturnType,
+ int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+ sb.append(indent + "throw new AMQProtocolVersionException(\"Constant \\\"" +
+ name + "\\\" \" +" + CR);
+ sb.append(indent + tab + "\"cannot be converted to type " + methodReturnType +
+ " for AMQP version \" + major + \"-\" + minor + \".\");" + CR);
+ return sb.toString();
+ }
+
+ // Methods for MessageBody classes
+ protected String generateMbGetMethod(String codeType, AmqpField field,
+ AmqpVersionSet versionSet, int indentSize, int tabSize, boolean nextFlag)
+ {
+ return Utils.createSpaces(indentSize) + "public " + codeType + " get" +
+ Utils.firstUpper(field.getName()) + "() { return " + field.getName() + "; }" +
+ CR;
+ }
+
+ protected String generateMbMangledGetMethod(AmqpField field, int indentSize,
+ int tabSize, boolean nextFlag)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer(CR);
+ sb.append(indent + "public <T> T get" + Utils.firstUpper(field.getName()) +
+ "(Class<T> classObj) throws AMQProtocolVersionException" + CR);
+ sb.append(indent + "{" + CR);
+ Iterator<String> dItr = field.getDomainMap().keySet().iterator();
+ int domainCntr = 0;
+ while (dItr.hasNext())
+ {
+ String domainName = dItr.next();
+ AmqpVersionSet versionSet = field.getDomainMap().get(domainName);
+ String codeType = getGeneratedType(domainName, versionSet.first());
+ sb.append(indent + tab + "if (classObj.equals(" + codeType +
+ ".class)) // AMQP Version(s): " + versionSet + CR);
+ sb.append(indent + tab + tab + "return (T)(Object)" + field.getName() + "_" +
+ (domainCntr++) + ";" + CR);
+ }
+ sb.append(indent + tab +
+ "throw new AMQProtocolVersionException(\"None of the AMQP versions defines \" +" +
+ CR + " \"field \\\"" + field.getName() +
+ "\\\" as domain \\\"\" + classObj.getName() + \"\\\".\");" + CR);
+ sb.append(indent + "}" + CR);
+ sb.append(CR);
+ return sb.toString();
+ }
+
+ protected String generateMbParamList(String codeType, AmqpField field,
+ AmqpVersionSet versionSet, int indentSize, int tabSize, boolean nextFlag)
+ {
+ return Utils.createSpaces(indentSize) + codeType + " " + field.getName() +
+ (nextFlag ? "," : "") + " // AMQP version(s): " + versionSet + CR;
+ }
+
+
+ protected String generateMbPassedParamList(String codeType, AmqpField field,
+ AmqpVersionSet versionSet, int indentSize, int tabSize, boolean nextFlag)
+ {
+ return Utils.createSpaces(indentSize) + field.getName() +
+ (nextFlag ? "," : "") + " // AMQP version(s): " + versionSet + CR;
+ }
+
+
+ protected String generateMbMangledParamList(AmqpField field, int indentSize,
+ int tabSize, boolean nextFlag)
+ {
+ StringBuffer sb = new StringBuffer();
+ Iterator<String> dItr = field.getDomainMap().keySet().iterator();
+ int domainCntr = 0;
+ while (dItr.hasNext())
+ {
+ String domainName = dItr.next();
+ AmqpVersionSet versionSet = field.getDomainMap().get(domainName);
+ String codeType = getGeneratedType(domainName, versionSet.first());
+ sb.append(Utils.createSpaces(indentSize) + codeType + " " + field.getName() + "_" +
+ (domainCntr++) + (nextFlag ? "," : "") + " // AMQP version(s): " +
+ versionSet + CR);
+ }
+ return sb.toString();
+ }
+
+ protected String generateMbMangledPassedParamList(AmqpField field, int indentSize,
+ int tabSize, boolean nextFlag)
+ {
+ StringBuffer sb = new StringBuffer();
+ Iterator<String> dItr = field.getDomainMap().keySet().iterator();
+ int domainCntr = 0;
+ while (dItr.hasNext())
+ {
+ String domainName = dItr.next();
+ AmqpVersionSet versionSet = field.getDomainMap().get(domainName);
+ sb.append(Utils.createSpaces(indentSize) + field.getName() + "_" +
+ (domainCntr++) + (nextFlag ? "," : "") + " // AMQP version(s): " +
+ versionSet + CR);
+ }
+ return sb.toString();
+ }
+
+
+ protected String generateMbBodyInit(String codeType, AmqpField field,
+ AmqpVersionSet versionSet, int indentSize, int tabSize, boolean nextFlag)
+ {
+ return Utils.createSpaces(indentSize) + "this." + field.getName() + " = " + field.getName() +
+ ";" + CR;
+ }
+
+ protected String generateMbMangledBodyInit(AmqpField field, int indentSize,
+ int tabSize, boolean nextFlag)
+ {
+ StringBuffer sb = new StringBuffer();
+ Iterator<String> dItr = field.getDomainMap().keySet().iterator();
+ int domainCntr = 0;
+ while (dItr.hasNext())
+ {
+ dItr.next();
+ sb.append(Utils.createSpaces(indentSize) + "this." + field.getName() + "_" + domainCntr +
+ " = " + field.getName() + "_" + (domainCntr++) + ";" + CR);
+ }
+ return sb.toString();
+ }
+
+ protected String generateMbFieldSize(String domainType, String fieldName,
+ int ordinal, int indentSize, int tabSize)
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append(Utils.createSpaces(indentSize) + "size += " +
+ typeMap.get(domainType).size.replaceAll("#", fieldName) +
+ "; // " + fieldName + ": " + domainType + CR);
+ return sb.toString();
+ }
+
+ protected String generateMbBitArrayFieldSize(List<String> bitFieldList,
+ int ordinal, int indentSize, int tabSize)
+ {
+ StringBuffer sb = new StringBuffer();
+ int numBytes = ((bitFieldList.size() - 1) / 8) + 1;
+ String comment = bitFieldList.size() == 1 ?
+ bitFieldList.get(0) + ": bit" :
+ "Combinded bits: " + bitFieldList;
+ sb.append(Utils.createSpaces(indentSize) + "size += " +
+ typeMap.get("bit").size.replaceAll("~", String.valueOf(numBytes)) +
+ "; // " + comment + CR);
+ return sb.toString();
+ }
+
+ protected String generateMbFieldEncode(String domain, String fieldName,
+ int ordinal, int indentSize, int tabSize)
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append(Utils.createSpaces(indentSize) +
+ typeMap.get(domain).encodeExpression.replaceAll("#", fieldName) +
+ "; // " + fieldName + ": " + domain + CR);
+ return sb.toString();
+ }
+
+ protected String generateMbBitFieldEncode(List<String> bitFieldList,
+ int ordinal, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+
+ StringBuilder sb = new StringBuilder();
+ int i = 0;
+ while (i < bitFieldList.size())
+ {
+
+ StringBuilder line = new StringBuilder();
+
+ for (int j = 0; i < bitFieldList.size() && j < 8; i++, j++)
+ {
+ if (j != 0)
+ {
+ line.append(", ");
+ }
+ line.append(bitFieldList.get(i));
+ }
+
+ sb.append(indent +
+ typeMap.get("bit").encodeExpression.replaceAll("#", line.toString()) + ";" + CR);
+ }
+ return sb.toString();
+ }
+
+ protected String generateMbFieldDecode(String domain, String fieldName,
+ int ordinal, int indentSize, int tabSize)
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append(Utils.createSpaces(indentSize) +
+ typeMap.get(domain).decodeExpression.replaceAll("#", fieldName) +
+ "; // " + fieldName + ": " + domain + CR);
+ return sb.toString();
+ }
+
+ protected String generateMbBitFieldDecode(List<String> bitFieldList,
+ int ordinal, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+
+ StringBuilder sb = new StringBuilder(indent);
+ sb.append("byte packedValue;");
+ sb.append(CR);
+
+ // RG HERE!
+
+ int i = 0;
+ while (i < bitFieldList.size())
+ {
+ sb.append(indent + "packedValue = EncodingUtils.readByte(buffer);" + CR);
+
+ for (int j = 0; i < bitFieldList.size() && j < 8; i++, j++)
+ {
+ sb.append(indent + bitFieldList.get(i) + " = ( packedValue & (byte) (1 << " + j + ") ) != 0;" + CR);
+ }
+ }
+ return sb.toString();
+ }
+
+ protected String generateMbFieldToString(String domain, String fieldName,
+ int ordinal, int indentSize, int tabSize)
+ {
+ StringBuffer sb = new StringBuffer();
+ sb.append(Utils.createSpaces(indentSize) +
+ "buf.append(\" " + fieldName + ": \" + " + fieldName + ");" + CR);
+ return sb.toString();
+ }
+
+ protected String generateMbBitFieldToString(List<String> bitFieldList,
+ int ordinal, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < bitFieldList.size(); i++)
+ {
+ String bitFieldName = bitFieldList.get(i);
+ sb.append(indent + "buf.append(\" " + bitFieldName + ": \" + " + bitFieldName +
+ ");" + CR);
+ }
+ return sb.toString();
+ }
+
+ // Methods for PropertyContentHeader classes
+
+ protected String generatePchClearMethod(String codeType, AmqpField field,
+ AmqpVersionSet versionSet, int indentSize, int tabSize, boolean nextFlag)
+ {
+ // This is one case where the ordinal info is the only significant factor,
+ // the domain info plays no part. Defer to the mangled version; the code would be
+ // identical anyway...
+ return generatePchMangledClearMethod(field, indentSize, tabSize, nextFlag);
+ }
+
+ protected String generatePchMangledClearMethod(AmqpField field, int indentSize,
+ int tabSize, boolean nextFlag)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+ sb.append(indent + "public void clear" + Utils.firstUpper(field.getName()) +
+ "()" + CR);
+ sb.append(indent + "{" + CR);
+
+ // If there is more than one ordinal for this field or the ordinal does not
+ // apply to all known versions, then we need to generate version checks so
+ // we know which fieldProperty to clear.
+ if (field.getOrdinalMap().size() == 1 &&
+ field.getOrdinalMap().get(field.getOrdinalMap().firstKey()).size() == field.getVersionSet().size())
+ {
+ int ordinal = field.getOrdinalMap().firstKey();
+ sb.append(indent + tab + "clearEncodedForm();" + CR);
+ sb.append(indent + tab + "propertyFlags[" + ordinal + "] = false;" + CR);
+ }
+ else
+ {
+ Iterator<Integer> oItr = field.getOrdinalMap().keySet().iterator();
+ while (oItr.hasNext())
+ {
+ int ordinal = oItr.next();
+ AmqpVersionSet versionSet = field.getOrdinalMap().get(ordinal);
+ sb.append(indent + tab);
+ if (ordinal != field.getOrdinalMap().firstKey())
+ {
+ sb.append("else ");
+ }
+ sb.append("if (");
+ sb.append(generateVersionCheck(versionSet));
+ sb.append(")" + CR);
+ sb.append(indent + tab + "{" + CR);
+ sb.append(indent + tab + tab + "clearEncodedForm();" + CR);
+ sb.append(indent + tab + tab + "propertyFlags[" + ordinal + "] = false;" + CR);
+ sb.append(indent + tab + "}" + CR);
+ }
+ }
+ sb.append(indent + "}" + CR);
+ sb.append(CR);
+ return sb.toString();
+ }
+
+ protected String generatePchGetMethod(String codeType, AmqpField field,
+ AmqpVersionSet versionSet, int indentSize, int tabSize, boolean nextFlag)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer(indent + "public " + codeType + " get" +
+ Utils.firstUpper(field.getName()) + "()" + CR);
+ sb.append(indent + "{" + CR);
+ sb.append(indent + tab + "decodeIfNecessary();" + CR);
+ sb.append(indent + tab + "return " + field.getName() + ";" + CR);
+ sb.append(indent + "}" + CR);
+ sb.append(CR);
+ return sb.toString();
+ }
+
+ protected String generatePchMangledGetMethod(AmqpField field, int indentSize,
+ int tabSize, boolean nextFlag)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer(indent + "public <T> T get" +
+ Utils.firstUpper(field.getName()) +
+ "(Class<T> classObj) throws AMQProtocolVersionException" + CR);
+ sb.append(indent + "{" + CR);
+ Iterator<String> dItr = field.getDomainMap().keySet().iterator();
+ int domainCntr = 0;
+ while (dItr.hasNext())
+ {
+ String domainName = dItr.next();
+ AmqpVersionSet versionSet = field.getDomainMap().get(domainName);
+ String codeType = getGeneratedType(domainName, versionSet.first());
+ sb.append(indent + tab + "if (classObj.equals(" + codeType +
+ ".class)) // AMQP Version(s): " + versionSet + CR);
+ sb.append(indent + tab + "{" + CR);
+ sb.append(indent + tab + tab + "decodeIfNecessary();" + CR);
+ sb.append(indent + tab + tab + "return (T)(Object)" + field.getName() + "_" +
+ (domainCntr++) + ";" + CR);
+ sb.append(indent + tab + "}" + CR);
+ }
+ sb.append(indent + tab +
+ "throw new AMQProtocolVersionException(\"None of the AMQP versions defines \" +" +
+ CR + " \"field \\\"" + field.getName() +
+ "\\\" as domain \\\"\" + classObj.getName() + \"\\\".\");" + CR);
+ sb.append(indent + "}" + CR);
+ sb.append(CR);
+ return sb.toString();
+ }
+
+ protected String generatePchSetMethod(String codeType, AmqpField field,
+ AmqpVersionSet versionSet, int indentSize, int tabSize, boolean nextFlag)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+ sb.append(indent + "public void set" + Utils.firstUpper(field.getName()) +
+ "(" + codeType + " " + field.getName() + ")" + CR);
+ sb.append(indent + "{" + CR);
+
+ // If there is more than one ordinal for this field or the ordinal does not
+ // apply to all known versions, then we need to generate version checks so
+ // we know which fieldProperty to clear.
+ if (field.getOrdinalMap().size() == 1 &&
+ field.getOrdinalMap().get(field.getOrdinalMap().firstKey()).size() == field.getVersionSet().size())
+ {
+ int ordinal = field.getOrdinalMap().firstKey();
+ sb.append(indent + tab + "clearEncodedForm();" + CR);
+ sb.append(indent + tab + "propertyFlags[" + ordinal + "] = true;" + CR);
+ sb.append(indent + tab + "this." + field.getName() + " = " + field.getName() + ";" + CR);
+ }
+ else
+ {
+ Iterator<Integer> oItr = field.getOrdinalMap().keySet().iterator();
+ while (oItr.hasNext())
+ {
+ int ordinal = oItr.next();
+ AmqpVersionSet oVersionSet = field.getOrdinalMap().get(ordinal);
+ sb.append(indent + tab);
+ if (ordinal != field.getOrdinalMap().firstKey())
+ {
+ sb.append("else ");
+ }
+ sb.append("if (");
+ sb.append(generateVersionCheck(oVersionSet));
+ sb.append(")" + CR);
+ sb.append(indent + tab + "{" + CR);
+ sb.append(indent + tab + tab + "clearEncodedForm();" + CR);
+ sb.append(indent + tab + tab + "propertyFlags[" + ordinal + "] = true;" + CR);
+ sb.append(indent + tab + tab + "this." + field.getName() + " = " + field.getName() + ";" + CR);
+ sb.append(indent + tab + "}" + CR);
+ }
+ }
+ sb.append(indent + "}" + CR);
+ sb.append(CR);
+ return sb.toString();
+ }
+
+ protected String generatePchMangledSetMethod(AmqpField field, int indentSize,
+ int tabSize, boolean nextFlag)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+
+ Iterator<String> dItr = field.getDomainMap().keySet().iterator();
+ int domainCntr = 0;
+ while (dItr.hasNext())
+ {
+ String domainName = dItr.next();
+ AmqpVersionSet versionSet = field.getDomainMap().get(domainName);
+ String codeType = getGeneratedType(domainName, versionSet.first());
+
+ // Find ordinal with matching version
+ AmqpVersionSet commonVersionSet = new AmqpVersionSet();
+ Iterator<Integer> oItr = field.getOrdinalMap().keySet().iterator();
+ while (oItr.hasNext())
+ {
+ int ordinal = oItr.next();
+ AmqpVersionSet oVersionSet = field.getOrdinalMap().get(ordinal);
+ Iterator<AmqpVersion> vItr = oVersionSet.iterator();
+ boolean first = true;
+ while (vItr.hasNext())
+ {
+ AmqpVersion thisVersion = vItr.next();
+ if (versionSet.contains(thisVersion))
+ {
+ commonVersionSet.add(thisVersion);
+ }
+ }
+ if (!commonVersionSet.isEmpty())
+ {
+ sb.append(indent + "public void set" + Utils.firstUpper(field.getName()) +
+ "(" + codeType + " " + field.getName() + ")" + CR);
+ sb.append(indent + "{" + CR);
+ sb.append(indent + tab);
+ if (!first)
+ {
+ sb.append("else ");
+ }
+ sb.append("if (");
+ sb.append(generateVersionCheck(commonVersionSet));
+ sb.append(")" + CR);
+ sb.append(indent + tab + "{" + CR);
+ sb.append(indent + tab + tab + "clearEncodedForm();" + CR);
+ sb.append(indent + tab + tab + "propertyFlags[" + ordinal + "] = true;" + CR);
+ sb.append(indent + tab + tab + "this." + field.getName() + "_" + (domainCntr++) +
+ " = " + field.getName() + ";" + CR);
+ sb.append(indent + tab + "}" + CR);
+ sb.append(indent + "}" + CR);
+ sb.append(CR);
+ first = false;
+ }
+ }
+ }
+ return sb.toString();
+ }
+
+ protected String generatePchFieldSize(String domainType, String fieldName,
+ int ordinal, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer(indent + "if (propertyFlags[" + ordinal + "]) // " +
+ fieldName + ": " + domainType + CR);
+ sb.append(indent + Utils.createSpaces(tabSize) + "size += " +
+ typeMap.get(domainType).size.replaceAll("#", fieldName) + ";" + CR);
+ sb.append(CR);
+ return sb.toString();
+ }
+
+ protected String generatePchBitArrayFieldSize(List<String> bitFieldList,
+ int ordinal, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ String comment = bitFieldList.size() == 1 ?
+ bitFieldList.get(0) + ": bit" :
+ "Combinded bits: " + bitFieldList;
+ StringBuffer sb = new StringBuffer();
+
+ if (bitFieldList.size() == 1) // single bit
+ {
+ sb.append(indent + "if (propertyFlags[" + (ordinal - 1) + "]) // " + comment + CR);
+ sb.append(indent + tab + "size += " +
+ typeMap.get("bit").size.replaceAll("~", "1") + ";" + CR);
+ }
+ else // multiple bits - up to 8 are combined into one byte
+ {
+ String bitCntrName = "bitCntr_" + ordinal;
+ int startOrdinal = ordinal - bitFieldList.size();
+ sb.append(indent + "// " + comment + CR);
+ sb.append(indent + "int " + bitCntrName + " = 0;" + CR);
+ sb.append(indent + "for (int i=" + startOrdinal + "; i<" + ordinal + "; i++)" + CR);
+ sb.append(indent + "{" + CR);
+ sb.append(indent + tab + "if (propertyFlags[i])" + CR);
+ sb.append(indent + tab + tab + bitCntrName + "++;" + CR);
+ sb.append(indent + "}" + CR);
+ sb.append(indent + "size += " +
+ typeMap.get("bit").size.replaceAll("~", bitCntrName +
+ " > 0 ? ((" + bitCntrName + " - 1) / 8) + 1 : 0") + ";" + CR);
+ }
+ sb.append(CR);
+ return sb.toString();
+ }
+
+ protected String generatePchFieldEncode(String domainType, String fieldName,
+ int ordinal, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ sb.append(indent + "if (propertyFlags[" + ordinal + "]) // " + fieldName + ": " +
+ domainType + CR);
+ sb.append(indent + Utils.createSpaces(tabSize) +
+ typeMap.get(domainType).encodeExpression.replaceAll("#", fieldName) + ";" + CR);
+ sb.append(CR);
+ return sb.toString();
+ }
+
+ protected String generatePchBitFieldEncode(List<String> bitFieldList,
+ int ordinal, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ String comment = bitFieldList.size() == 1 ?
+ bitFieldList.get(0) + ": bit" :
+ "Combinded bits: " + bitFieldList;
+ StringBuffer sb = new StringBuffer();
+
+ if (bitFieldList.size() == 1) // single bit
+ {
+ sb.append(indent + "if (propertyFlags[" + (ordinal - 1) + "]) // " +
+ bitFieldList.get(0) + ": bit" + CR);
+ sb.append(indent + tab + typeMap.get("bit").encodeExpression.replaceAll("#",
+ "new boolean[] {" + bitFieldList.get(0) + "}") + ";" + CR);
+ }
+ else // multiple bits - up to 8 are combined into one byte
+ {
+ int startOrdinal = ordinal - bitFieldList.size();
+ String bitCntrName = "bitCntr" + startOrdinal;
+ sb.append(indent + "// " + comment + CR);
+ sb.append(indent + "int " + bitCntrName + " = 0;" + CR);
+ sb.append(indent + "for (int i=" + startOrdinal + "; i<=" + (ordinal - 1) + "; i++)" + CR);
+ sb.append(indent + "{" + CR);
+ sb.append(indent + tab + "if (propertyFlags[i])" + CR);
+ sb.append(indent + tab + tab + bitCntrName + "++;" + CR);
+ sb.append(indent + "}" + CR);
+ sb.append(indent + "if (" + bitCntrName + " > 0) // Are any of the property bits set?" + CR);
+ sb.append(indent + "{" + CR);
+ sb.append(indent + tab + "boolean[] fullBitArray = new boolean[] { ");
+ for (int i = 0; i < bitFieldList.size(); i++)
+ {
+ if (i != 0)
+ {
+ sb.append(", ");
+ }
+ sb.append(bitFieldList.get(i));
+ }
+ sb.append(" };" + CR);
+ sb.append(indent + tab + "boolean[] flaggedBitArray = new boolean[" + bitCntrName +
+ "];" + CR);
+ sb.append(indent + tab + bitCntrName + " = 0;" + CR);
+ sb.append(indent + tab + "for (int i=" + startOrdinal + "; i<=" + (ordinal - 1) +
+ "; i++)" + CR);
+ sb.append(indent + tab + "{" + CR);
+ sb.append(indent + tab + tab + "if (propertyFlags[i])" + CR);
+ sb.append(indent + tab + tab + tab + "flaggedBitArray[" + bitCntrName +
+ "++] = fullBitArray[i];" + CR);
+ sb.append(indent + tab + "}" + CR);
+ sb.append(indent + tab + typeMap.get("bit").encodeExpression.replaceAll("#",
+ "flaggedBitArray") + ";" + CR);
+ sb.append(indent + "}" + CR);
+ }
+ sb.append(CR);
+ return sb.toString();
+ }
+
+ protected String generatePchFieldDecode(String domainType, String fieldName,
+ int ordinal, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ sb.append(indent + "if (propertyFlags[" + ordinal + "]) // " + fieldName + ": " +
+ domainType + CR);
+ sb.append(indent + Utils.createSpaces(tabSize) +
+ typeMap.get(domainType).decodeExpression.replaceAll("#", fieldName) + ";" + CR);
+ sb.append(CR);
+ return sb.toString();
+ }
+
+ protected String generatePchBitFieldDecode(List<String> bitFieldList,
+ int ordinal, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ String comment = bitFieldList.size() == 1 ?
+ bitFieldList.get(0) + ": bit" :
+ "Combinded bits: " + bitFieldList;
+ StringBuffer sb = new StringBuffer();
+
+ if (bitFieldList.size() == 1) // single bit
+ {
+ sb.append(indent + "if (propertyFlags[" + (ordinal - 1) + "]) // " +
+ bitFieldList.get(0) + ": bit" + CR);
+ sb.append(indent + "{" + CR);
+ sb.append(indent + tab + typeMap.get("bit").decodeExpression.replaceAll("#",
+ "boolean[] flaggedBitArray") + ";" + CR);
+ sb.append(indent + tab + bitFieldList.get(0) + " = flaggedBitArray[0];" + CR);
+ sb.append(indent + "}" + CR);
+ }
+ else // multiple bits - up to 8 are combined into one byte
+ {
+ int startOrdinal = ordinal - bitFieldList.size();
+ String bitCntr = "bitCntr" + startOrdinal;
+ sb.append(indent + "// " + comment + CR);
+ sb.append(indent + "int " + bitCntr + " = 0;" + CR);
+ sb.append(indent + "for (int i=" + startOrdinal + "; i<=" + (ordinal - 1) + "; i++)" + CR);
+ sb.append(indent + "{" + CR);
+ sb.append(indent + tab + "if (propertyFlags[i])" + CR);
+ sb.append(indent + tab + tab + bitCntr + "++;" + CR);
+ sb.append(indent + "}" + CR);
+ sb.append(indent + "if (" + bitCntr + " > 0) // Are any of the property bits set?" + CR);
+ sb.append(indent + "{" + CR);
+ sb.append(indent + tab + typeMap.get("bit").decodeExpression.replaceAll("#",
+ "boolean[] flaggedBitArray") + ";" + CR);
+ sb.append(indent + tab + bitCntr + " = 0;" + CR);
+ for (int i = 0; i < bitFieldList.size(); i++)
+ {
+ sb.append(indent + tab + "if (propertyFlags[" + (startOrdinal + i) + "])" + CR);
+ sb.append(indent + tab + tab + bitFieldList.get(i) + " = flaggedBitArray[" +
+ bitCntr + "++];" + CR);
+ }
+ sb.append(indent + "}" + CR);
+ }
+
+ sb.append(CR);
+ return sb.toString();
+ }
+
+ protected String generatePchGetPropertyFlags(String domainType, String fieldName,
+ int ordinal, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+ int word = ordinal / 15;
+ int bit = 15 - (ordinal % 15);
+ sb.append(indent + "if (propertyFlags[" + ordinal + "]) // " + fieldName + ": " +
+ domainType + CR);
+ sb.append(indent + tab + "compactPropertyFlags[" + word + "] |= (1 << " +
+ bit + ");" + CR);
+ sb.append(CR);
+ return sb.toString();
+ }
+
+ protected String generatePchBitGetPropertyFlags(List<String> bitFieldList,
+ int ordinal, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+ int startOrdinal = ordinal - bitFieldList.size();
+
+ for (int i = 0; i < bitFieldList.size(); i++)
+ {
+ int thisOrdinal = startOrdinal + i;
+ int word = thisOrdinal / 15;
+ int bit = 15 - (thisOrdinal % 15);
+ sb.append(indent + "if (propertyFlags[" + thisOrdinal + "])" + CR);
+ sb.append(indent + tab + "compactPropertyFlags[" + word +
+ "] |= (1 << " + bit + ");" + CR);
+ }
+
+ sb.append(CR);
+ return sb.toString();
+ }
+
+ protected String generatePchSetPropertyFlags(String domainType, String fieldName,
+ int ordinal, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ int word = ordinal / 15;
+ int bit = 15 - (ordinal % 15);
+ sb.append(indent + "propertyFlags[" + ordinal + "] = (compactPropertyFlags[" +
+ word + "] & (1 << " + bit + ")) > 0;" + CR);
+ return sb.toString();
+ }
+
+ protected String generatePchBitSetPropertyFlags(List<String> bitFieldList,
+ int ordinal, int indentSize, int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ StringBuffer sb = new StringBuffer();
+ int startOrdinal = ordinal - bitFieldList.size();
+
+ for (int i = 0; i < bitFieldList.size(); i++)
+ {
+ int thisOrdinal = startOrdinal + i;
+ int word = thisOrdinal / 15;
+ int bit = 15 - (thisOrdinal % 15);
+ sb.append(indent + "propertyFlags[" + thisOrdinal + "] = (compactPropertyFlags[" +
+ word + "] & (1 << " + bit + ")) > 0;" + CR);
+ }
+ return sb.toString();
+ }
+
+ private String generatePchPropertyFlagsDeclare()
+ {
+ return "private boolean[] propertyFlags;";
+ }
+
+ private String generatePchPropertyFlagsInitializer(int totNumFields)
+ {
+ return "propertyFlags = new boolean[" + totNumFields + "];";
+ }
+
+ private String generatePchCompactPropertyFlagsInitializer(AmqpClass thisClass, int indentSize,
+ int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+ Iterator<AmqpVersion> vItr = thisClass.getVersionSet().iterator();
+ while (vItr.hasNext())
+ {
+ AmqpVersion version = vItr.next();
+ int numBytes = ((thisClass.getFieldMap().getNumFields(version) - 1) / 15) + 1;
+
+ sb.append(indent);
+ if (!version.equals(thisClass.getVersionSet().first()))
+ {
+ sb.append("else ");
+ }
+ sb.append("if ( major == " + version.getMajor() + " && minor == " +
+ version.getMinor() + " )" + CR);
+ sb.append(indent + tab + "compactPropertyFlags = new int[] { ");
+ for (int i = 0; i < numBytes; i++)
+ {
+ if (i != 0)
+ {
+ sb.append(", ");
+ }
+ sb.append(i < numBytes - 1 ? "1" : "0"); // Set the "continue" flag where required
+ }
+ sb.append(" };" + CR);
+ }
+ return sb.toString();
+ }
+
+ private String generatePchCompactPropertyFlagsCheck(AmqpClass thisClass, int indentSize,
+ int tabSize)
+ {
+ String indent = Utils.createSpaces(indentSize);
+ String tab = Utils.createSpaces(tabSize);
+ StringBuffer sb = new StringBuffer();
+ Iterator<AmqpVersion> vItr = thisClass.getVersionSet().iterator();
+ while (vItr.hasNext())
+ {
+ AmqpVersion version = vItr.next();
+ int numFields = thisClass.getFieldMap().getNumFields(version);
+ int numBytes = ((numFields - 1) / 15) + 1;
+
+ sb.append(indent);
+ if (!version.equals(thisClass.getVersionSet().first()))
+ {
+ sb.append("else ");
+ }
+ sb.append("if ( major == " + version.getMajor() + " && minor == " +
+ version.getMinor() + " && compactPropertyFlags.length != " + numBytes + " )" + CR);
+ sb.append(indent + tab +
+ "throw new AMQProtocolVersionException(\"Property flag array size mismatch:\" +" + CR);
+ sb.append(indent + tab + tab + "\"(Size found: \" + compactPropertyFlags.length +" + CR);
+ sb.append(indent + tab + tab + "\") Version " + version + " has " + numFields +
+ " fields which requires an int array of size " + numBytes + ".\");" + CR);
+ }
+ return sb.toString();
+ }
+
+ private String generateVersionCheck(AmqpVersionSet v)
+ {
+ StringBuffer sb = new StringBuffer();
+ AmqpVersion[] versionArray = new AmqpVersion[v.size()];
+ v.toArray(versionArray);
+ for (int i = 0; i < versionArray.length; i++)
+ {
+ if (i != 0)
+ {
+ sb.append(" || ");
+ }
+ if (versionArray.length > 1)
+ {
+ sb.append("(");
+ }
+ sb.append("major == (byte)" + versionArray[i].getMajor() + " && minor == (byte)" +
+ versionArray[i].getMinor());
+ if (versionArray.length > 1)
+ {
+ sb.append(")");
+ }
+ }
+ return sb.toString();
+ }
+
+ private String camelCaseName(String name, boolean upperFirstFlag)
+ {
+ StringBuffer ccn = new StringBuffer();
+ String[] toks = name.split("[-_.\\ ]");
+ for (int i = 0; i < toks.length; i++)
+ {
+ StringBuffer b = new StringBuffer(toks[i]);
+ if (upperFirstFlag || i > 0)
+ {
+ b.setCharAt(0, Character.toUpperCase(toks[i].charAt(0)));
+ }
+ ccn.append(b);
+ }
+ return ccn.toString();
+ }
+
+
+ private String upperCaseName(String name)
+ {
+ StringBuffer ccn = new StringBuffer();
+ String[] toks = name.split("[-_.\\ ]");
+ for (int i = 0; i < toks.length; i++)
+ {
+ if (i != 0)
+ {
+ ccn.append('_');
+ }
+ ccn.append(toks[i].toUpperCase());
+
+
+ }
+ return ccn.toString();
+ }
+
+
+ public static Factory<JavaGenerator> _factoryInstance = new Factory<JavaGenerator>()
+ {
+
+ public JavaGenerator newInstance()
+ {
+ return new JavaGenerator();
+ }
+ };
+
+ public static Factory<JavaGenerator> getFactory()
+ {
+ return _factoryInstance;
+ }
+
+
+ void processModelTemplate(NamedTemplate template, AmqpVersion version)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ void processClassTemplate(NamedTemplate template, AmqpClass amqpClass, AmqpVersion version)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ void processMethodTemplate(NamedTemplate template, AmqpClass amqpClass, AmqpMethod amqpMethod, AmqpVersion version)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ void processFieldTemplate(NamedTemplate template, AmqpClass amqpClass, AmqpMethod amqpMethod, AmqpField amqpField, AmqpVersion version)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/LanguageConverter.java b/qpid/gentools/src/org/apache/qpid/gentools/LanguageConverter.java
new file mode 100644
index 0000000000..5e692d86e7
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/LanguageConverter.java
@@ -0,0 +1,42 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+public interface LanguageConverter
+{
+
+// public AmqpDomainMap getDomainMap();
+// public AmqpConstantSet getConstantSet();
+// public AmqpModel getModel();
+
+ //
+ public String prepareClassName(String className);
+
+ public String prepareMethodName(String methodName);
+
+ public String prepareDomainName(String domainName);
+
+ public String getDomainType(String domainName, AmqpVersion version);
+
+ public String getGeneratedType(String domainName, AmqpVersion version);
+
+ public String prepareConstantName(String constantName);
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/Main.java b/qpid/gentools/src/org/apache/qpid/gentools/Main.java
new file mode 100644
index 0000000000..c0584f7ca7
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/Main.java
@@ -0,0 +1,301 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import org.apache.velocity.app.Velocity;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Properties;
+
+public class Main
+{
+ private static final String DEFAULT_OUTPUT_DIR = ".." + Utils.FILE_SEPARATOR + "gen";
+ private static final String DEFAULT_TEMPLATE_DIR_BASE = ".." + Utils.FILE_SEPARATOR;
+
+ private enum GeneratedLanguage
+ {
+ CPP(".cpp", CppGenerator.getFactory()),
+ DOTNET(".net", DotnetGenerator.getFactory()),
+ JAVA(".java", JavaGenerator.getFactory());
+
+ private final String _suffix;
+ private final Generator.Factory _factory;
+
+
+ private final String _defaultTemplateDirectory;
+
+ GeneratedLanguage(String suffix, Generator.Factory factory)
+ {
+ _suffix = suffix;
+ _factory = factory;
+ _defaultTemplateDirectory = DEFAULT_TEMPLATE_DIR_BASE + "templ" + _suffix;
+ }
+
+ public String getSuffix()
+ {
+ return _suffix;
+ }
+
+ public Generator newGenerator()
+ {
+ return _factory.newInstance();
+ }
+
+ public String getDefaultTemplateDirectory()
+ {
+ return _defaultTemplateDirectory;
+ }
+ }
+
+ private Generator generator;
+
+ private String outDir;
+ private String tmplDir;
+ private GeneratedLanguage _generatorLang;
+ private ArrayList<String> xmlFiles;
+
+ public Main()
+ {
+ xmlFiles = new ArrayList<String>();
+ }
+
+ public void run(String[] args)
+ throws Exception,
+ SAXException,
+ AmqpParseException,
+ AmqpTypeMappingException,
+ AmqpTemplateException,
+ TargetDirectoryException,
+ IllegalAccessException,
+ InvocationTargetException, ParserConfigurationException
+ {
+
+ // 0. Initialize
+ outDir = DEFAULT_OUTPUT_DIR;
+ tmplDir = null;
+ _generatorLang = GeneratedLanguage.CPP; // Default generation language
+ xmlFiles.clear();
+ processArgs(args);
+
+ if (tmplDir == null)
+ {
+ tmplDir = _generatorLang.getDefaultTemplateDirectory();
+ }
+
+
+ generator = _generatorLang.newGenerator();
+ generator.setTemplateDirectory(tmplDir);
+ generator.setOutputDirectory(outDir);
+
+ // 1. Suck in all the XML spec files provided on the command line
+ analyzeXML();
+
+ Properties p = new Properties();
+ p.setProperty("file.resource.loader.path", tmplDir);
+
+ Velocity.init(p);
+
+ // 2. Load up all templates
+ generator.initializeTemplates();
+
+ // 3. Generate output
+ generator.generate();
+
+ System.out.println("Files generated: " + generator.getNumberGeneratedFiles());
+ System.out.println("Done.");
+ }
+
+ private void processArgs(String[] args)
+ {
+ // Crude but simple...
+ for (int i = 0; i < args.length; i++)
+ {
+ String arg = args[i];
+ if (arg.charAt(0) == '-')
+ {
+ switch (arg.charAt(1))
+ {
+ case'c':
+ case'C':
+ _generatorLang = GeneratedLanguage.CPP;
+ break;
+ case'j':
+ case'J':
+ _generatorLang = GeneratedLanguage.JAVA;
+ break;
+ case'n':
+ case'N':
+ _generatorLang = GeneratedLanguage.DOTNET;
+ break;
+ case'o':
+ case'O':
+ if (++i < args.length)
+ {
+ outDir = args[i];
+ }
+ break;
+ case't':
+ case'T':
+ if (++i < args.length)
+ {
+ tmplDir = args[i];
+ }
+ break;
+ }
+ }
+ else
+ {
+ xmlFiles.add(args[i]);
+ }
+ }
+ }
+
+ private void analyzeXML()
+ throws IOException, SAXException, AmqpParseException, AmqpTypeMappingException, ParserConfigurationException
+ {
+ DocumentBuilder docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+
+ System.out.println("XML files: " + xmlFiles);
+ for (String filename : xmlFiles)
+ {
+ File f = new File(filename);
+ if (f.exists())
+ {
+ // 1a. Initialize dom
+ System.out.print(" \"" + filename + "\":");
+ Document doc = docBuilder.parse(new File(filename));
+ Node amqpNode = Utils.findChild(doc, Utils.ELEMENT_AMQP);
+
+ // 1b. Extract version (major and minor) from the XML file
+ int major = Utils.getNamedIntegerAttribute(amqpNode, Utils.ATTRIBUTE_MAJOR);
+ int minor = Utils.getNamedIntegerAttribute(amqpNode, Utils.ATTRIBUTE_MINOR);
+ AmqpVersion version = new AmqpVersion(major, minor);
+ System.out.println(" Found version " + version.toString() + ".");
+ generator.addVersion(version);
+ generator.addFromNode(amqpNode, version);
+
+
+ }
+ else
+ {
+ System.err.println("ERROR: AMQP XML file \"" + filename + "\" not found.");
+ }
+ }
+// *** DEBUG INFO *** Uncomment bits from this block to see lots of stuff....
+// System.out.println();
+// System.out.println("*** Debug output ***");
+// System.out.println();
+// versionSet.print(System.out, 0, 2); // List of loaded versions
+// System.out.println();
+// constants.print(System.out, 0, 2); // List of constants
+// System.out.println();
+// domainMap.print(System.out, 0, 2); // List of domains
+// System.out.println();
+// model.print(System.out, 0, 2); // Internal version map model
+// System.out.println();
+// System.out.println("*** End debug output ***");
+// System.out.println();
+ }
+
+ public static void main(String[] args)
+ {
+ int exitCode = 1;
+ // TODO: This is a simple and klunky way of hangling command-line args, and could be improved upon.
+ if (args.length < 2)
+ {
+ usage();
+ }
+ else
+ {
+ try
+ {
+ new Main().run(args);
+ exitCode = 0;
+ }
+ catch (IOException e)
+ {
+ e.printStackTrace();
+ }
+ catch (ParserConfigurationException e)
+ {
+ e.printStackTrace();
+ }
+ catch (SAXException e)
+ {
+ e.printStackTrace();
+ }
+ catch (AmqpParseException e)
+ {
+ e.printStackTrace();
+ }
+ catch (AmqpTypeMappingException e)
+ {
+ e.printStackTrace();
+ }
+ catch (AmqpTemplateException e)
+ {
+ e.printStackTrace();
+ }
+ catch (TargetDirectoryException e)
+ {
+ e.printStackTrace();
+ }
+ catch (IllegalAccessException e)
+ {
+ e.printStackTrace();
+ }
+ catch (InvocationTargetException e)
+ {
+ e.printStackTrace();
+ }
+ catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+ System.exit(exitCode);
+ }
+
+ public static void usage()
+ {
+ System.out.println("AMQP XML generator v.0.0");
+ System.out.println("Usage: Main -c|-j [-o outDir] [-t tmplDir] XMLfile [XMLfile ...]");
+ System.out.println(" where -c: Generate C++.");
+ System.out.println(" -j: Generate Java.");
+ System.out.println(" -n: Generate .NET.");
+ System.out.println(" -o outDir: Use outDir as the output dir (default=\"" + DEFAULT_OUTPUT_DIR + "\").");
+ System.out.println(" -t tmplDir: Find templates in tmplDir.");
+ System.out.println(" Defaults: \"" + GeneratedLanguage.CPP.getDefaultTemplateDirectory() + "\" for C++;");
+ System.out.println(" \"" + GeneratedLanguage.JAVA.getDefaultTemplateDirectory() + "\" for java.;");
+ System.out.println(" \"" + GeneratedLanguage.DOTNET.getDefaultTemplateDirectory() + "\" for .NET.");
+ System.out.println(" XMLfile is a space-separated list of AMQP XML files to be parsed.");
+ }
+
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/MangledGenerateMethod.java b/qpid/gentools/src/org/apache/qpid/gentools/MangledGenerateMethod.java
new file mode 100644
index 0000000000..ffeefed900
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/MangledGenerateMethod.java
@@ -0,0 +1,26 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+public interface MangledGenerateMethod
+{
+ String generate(AmqpField field, int indentSize, int tabSize, boolean notLast);
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/NodeAware.java b/qpid/gentools/src/org/apache/qpid/gentools/NodeAware.java
new file mode 100644
index 0000000000..f832da75ad
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/NodeAware.java
@@ -0,0 +1,47 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import org.w3c.dom.Node;
+
+/**
+ * @author kpvdr
+ * Interface allowing the addition of elements from a node in the
+ * DOM of the AMQP specification. It is used by each of the model
+ * elements in a recursive fashion to build the model.
+ */
+public interface NodeAware
+{
+ /**
+ * Add a model element from the current DOM node. All model elements must implement
+ * this interface. If the node contains children that are also a part of the model,
+ * then this method is called on new instances of those model elements.
+ *
+ * @param n Node from which the current model element is to be added.
+ * @param o Ordinal value of the current model elemet.
+ * @param v Verion of the DOM from which the node comes.
+ * @throws AmqpParseException
+ * @throws AmqpTypeMappingException
+ * @returns true if a node was added, false if not
+ */
+ public boolean addFromNode(Node n, int o, AmqpVersion v)
+ throws AmqpParseException, AmqpTypeMappingException;
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/Printable.java b/qpid/gentools/src/org/apache/qpid/gentools/Printable.java
new file mode 100644
index 0000000000..aa13df7b68
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/Printable.java
@@ -0,0 +1,28 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.io.PrintStream;
+
+public interface Printable
+{
+ public void print(PrintStream out, int marginSize, int tabSize);
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/SingleVersionClass.java b/qpid/gentools/src/org/apache/qpid/gentools/SingleVersionClass.java
new file mode 100644
index 0000000000..8e1af1c551
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/SingleVersionClass.java
@@ -0,0 +1,103 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.util.Map;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Map.Entry;
+
+
+public class SingleVersionClass
+{
+ private final int _classId;
+
+
+ private final AmqpClass _amqpClass;
+ private final AmqpVersion _amqpVersion;
+ private final Generator _generator;
+ private final List<SingleVersionMethod> _methodList = new ArrayList<SingleVersionMethod>();
+
+ public SingleVersionClass(AmqpClass amqpClass, AmqpVersion amqpVersion, Generator generator)
+ {
+ _amqpClass = amqpClass;
+ _amqpVersion = amqpVersion;
+ _generator = generator;
+
+ AmqpOrdinalVersionMap indexMap = amqpClass.getIndexMap();
+ int classId = 0;
+ for(Entry<Integer, AmqpVersionSet> entry : indexMap.entrySet())
+ {
+ if(entry.getValue().contains(_amqpVersion))
+ {
+ classId = entry.getKey();
+ break;
+ }
+ }
+ _classId = classId;
+
+
+ Collection<AmqpMethod> methods = _amqpClass.getMethodMap().values();
+
+ for(AmqpMethod amqpMethod : methods)
+ {
+ _methodList.add(new SingleVersionMethod(amqpMethod, _amqpVersion, _generator));
+
+ }
+
+ Collections.sort(_methodList, new Comparator<SingleVersionMethod>(){
+ public int compare(SingleVersionMethod method1, SingleVersionMethod method2)
+ {
+ return method1.getMethodId() - method2.getMethodId();
+ }
+ });
+
+
+ }
+
+ public int getClassId()
+ {
+ return _classId;
+ }
+
+ public String getName()
+ {
+ return _amqpClass.getName();
+ }
+
+
+
+
+
+ public List<SingleVersionMethod> getMethodList()
+ {
+ return _methodList;
+ }
+
+
+ public int getMaximumMethodId()
+ {
+ return _methodList.get(_methodList.size()-1).getMethodId();
+ }
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/SingleVersionField.java b/qpid/gentools/src/org/apache/qpid/gentools/SingleVersionField.java
new file mode 100644
index 0000000000..b795663d15
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/SingleVersionField.java
@@ -0,0 +1,68 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+
+public class SingleVersionField
+{
+ private final AmqpField _field;
+ private final AmqpVersion _amqpVersion;
+ private final Generator _generator;
+
+ public SingleVersionField(AmqpField field, AmqpVersion amqpVersion, Generator generator)
+ {
+ _field = field;
+ _amqpVersion = amqpVersion;
+ _generator = generator;
+ }
+
+ public String getName()
+ {
+ return _field.getName();
+ }
+
+ public String getDomain()
+ {
+ return _field.getDomain(_amqpVersion);
+ }
+
+
+ public String getDomainType()
+ {
+ return _generator.getDomainType(_field.getDomain(_amqpVersion),_amqpVersion);
+ }
+
+ public String getNativeType()
+ {
+ return _generator.getNativeType(getDomainType());
+ }
+
+ public String getEncodingType()
+ {
+ return _generator.getEncodingType(getDomainType());
+ }
+
+
+ public int getPosition()
+ {
+ return _field.getOrdinal(_amqpVersion);
+ }
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/SingleVersionMethod.java b/qpid/gentools/src/org/apache/qpid/gentools/SingleVersionMethod.java
new file mode 100644
index 0000000000..59a6d9e28a
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/SingleVersionMethod.java
@@ -0,0 +1,154 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.util.Map.Entry;
+import java.util.Collection;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+
+public class SingleVersionMethod
+{
+ private final AmqpMethod _amqpMethod;
+ private final AmqpVersion _amqpVersion;
+ private final int _methodId;
+ private final List<SingleVersionField> _fieldList = new ArrayList<SingleVersionField>();
+ private final Generator _generator;
+ private final List<ConsolidatedField> _consolidatedFields = new ArrayList<ConsolidatedField>();
+ private final Map<String, ConsolidatedField> _fieldNameToConsolidatedFieldMap = new HashMap<String, ConsolidatedField>();
+
+
+ public SingleVersionMethod(AmqpMethod amqpMethod, AmqpVersion amqpVersion, Generator generator)
+ {
+ _amqpMethod = amqpMethod;
+ _amqpVersion = amqpVersion;
+ _generator = generator;
+
+ AmqpOrdinalVersionMap indexMap = amqpMethod.getIndexMap();
+ int methodId = 0;
+ for(Entry<Integer, AmqpVersionSet> entry : indexMap.entrySet())
+ {
+ if(entry.getValue().contains(_amqpVersion))
+ {
+ methodId = entry.getKey();
+ break;
+ }
+ }
+ _methodId = methodId;
+
+ Collection<AmqpField> fields = _amqpMethod.getFieldMap().values();
+
+ for(AmqpField field : fields)
+ {
+ _fieldList.add(new SingleVersionField(field, _amqpVersion, _generator));
+
+ }
+
+ Collections.sort(_fieldList, new Comparator<SingleVersionField>(){
+ public int compare(SingleVersionField field1, SingleVersionField field2)
+ {
+ return field1.getPosition() - field2.getPosition();
+ }
+ });
+
+
+
+ ConsolidatedField lastField = null;
+ int bitfieldNum = 0;
+ for(SingleVersionField field : _fieldList)
+ {
+ String domainType = field.getDomainType();
+ if(!domainType.equals("bit"))
+ {
+ lastField = new ConsolidatedField(_generator,
+ field.getName(),
+ field.getDomainType());
+ _consolidatedFields.add(lastField);
+ }
+ else if(lastField == null || !lastField.getType().equals("bitfield"))
+ {
+ lastField = new ConsolidatedField(_generator,
+ domainType.equals("bit") ? "bitfield"+bitfieldNum++ : field.getName(),
+ domainType.equals("bit") ? "bitfield" : field.getDomainType(),
+ field.getName());
+ _consolidatedFields.add(lastField);
+ }
+ else
+ {
+ lastField.add(field.getName());
+ }
+ _fieldNameToConsolidatedFieldMap.put(field.getName(), lastField);
+
+ }
+ }
+
+ public int getMethodId()
+ {
+ return _methodId;
+ }
+
+ public String getName()
+ {
+ return _amqpMethod.getName();
+ }
+
+ public Collection<SingleVersionField> getFieldList()
+ {
+ return Collections.unmodifiableCollection(_fieldList);
+ }
+
+ public List<ConsolidatedField> getConsolidatedFields()
+ {
+ return _consolidatedFields;
+ }
+
+ public String getConsolidatedFieldName(String fieldName)
+ {
+ return _fieldNameToConsolidatedFieldMap.get(fieldName).getName();
+ }
+
+ public boolean isConsolidated(String fieldName)
+ {
+ return _fieldNameToConsolidatedFieldMap.get(fieldName).isConsolidated();
+ }
+
+ public int getPositionInBitField(String fieldName)
+ {
+ return _fieldNameToConsolidatedFieldMap.get(fieldName).getPosition(fieldName);
+ }
+
+
+ public boolean isServerMethod()
+ {
+ return _amqpMethod.isServerMethod(_amqpVersion);
+ }
+
+
+ public boolean isClientMethod()
+ {
+ return _amqpMethod.isClientMethod(_amqpVersion);
+ }
+
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/SingleVersionModel.java b/qpid/gentools/src/org/apache/qpid/gentools/SingleVersionModel.java
new file mode 100644
index 0000000000..22b416e45a
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/SingleVersionModel.java
@@ -0,0 +1,71 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+
+public class SingleVersionModel
+{
+ private final AmqpModel _amqpModel;
+ private final AmqpVersion _amqpVersion;
+ private final Generator _generator;
+ private final List<SingleVersionClass> _classList = new ArrayList<SingleVersionClass>();
+
+ public SingleVersionModel(AmqpModel amqpModel, AmqpVersion amqpVersion, Generator generator)
+ {
+ _amqpModel = amqpModel;
+ _amqpVersion = amqpVersion;
+ _generator = generator;
+
+
+ Collection<AmqpClass> originalClasses = _amqpModel.getClassMap().values();
+
+ for(AmqpClass amqpClass : originalClasses)
+ {
+ _classList.add(new SingleVersionClass(amqpClass, _amqpVersion, _generator));
+
+ }
+
+ Collections.sort(_classList, new Comparator<SingleVersionClass>(){
+ public int compare(SingleVersionClass amqpClass1, SingleVersionClass amqpClass2)
+ {
+ return amqpClass1.getClassId() - amqpClass2.getClassId();
+ }
+ });
+
+
+ }
+
+ public Collection<SingleVersionClass> getClassList()
+ {
+ return Collections.unmodifiableCollection(_classList);
+ }
+
+ public int getMaximumClassId()
+ {
+ return _classList.get(_classList.size()-1).getClassId();
+ }
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/TargetDirectoryException.java b/qpid/gentools/src/org/apache/qpid/gentools/TargetDirectoryException.java
new file mode 100644
index 0000000000..39ce666288
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/TargetDirectoryException.java
@@ -0,0 +1,30 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+@SuppressWarnings("serial")
+public class TargetDirectoryException extends RuntimeException
+{
+ public TargetDirectoryException(String msg)
+ {
+ super(msg);
+ }
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/Utils.java b/qpid/gentools/src/org/apache/qpid/gentools/Utils.java
new file mode 100644
index 0000000000..1cedaeea12
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/Utils.java
@@ -0,0 +1,159 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+import org.w3c.dom.Attr;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+public class Utils
+{
+ public final static String FILE_SEPARATOR = System.getProperty("file.separator");
+ public final static String LINE_SEPARATOR = System.getProperty("line.separator");
+
+ public final static String ATTRIBUTE_NAME = "name";
+ public final static String ATTRIBUTE_MAJOR = "major";
+ public final static String ATTRIBUTE_MINOR = "minor";
+ public final static String ATTRIBUTE_INDEX = "index";
+ public final static String ATTRIBUTE_LABEL = "label";
+ public final static String ATTRIBUTE_SYNCHRONOUS = "synchronous";
+ public final static String ATTRIBUTE_CONTENT = "content";
+ public final static String ATTRIBUTE_HANDLER = "handler";
+ public final static String ATTRIBUTE_DOMAIN = "domain";
+ public final static String ATTRIBUTE_VALUE = "value";
+ public final static String ATTRIBUTE_TYPE = "type"; // For compatibility with AMQP 8.0
+
+ public final static String ELEMENT_AMQP = "amqp";
+ public final static String ELEMENT_CHASSIS = "chassis";
+ public final static String ELEMENT_CLASS = "class";
+ public final static String ELEMENT_CODEGEN = "codegen";
+ public final static String ELEMENT_CONSTANT = "constant";
+ public final static String ELEMENT_DOMAIN = "domain";
+ public final static String ELEMENT_METHOD = "method";
+ public final static String ELEMENT_FIELD = "field";
+ public final static String ELEMENT_VERSION = "version";
+
+ // Attribute functions
+
+ public static String getNamedAttribute(Node n, String attrName) throws AmqpParseException
+ {
+ NamedNodeMap nnm = n.getAttributes();
+ if (nnm == null)
+ {
+ throw new AmqpParseException("Node \"" + n.getNodeName() + "\" has no attributes.");
+ }
+ Attr a = (Attr) nnm.getNamedItem(attrName);
+ if (a == null)
+ {
+ throw new AmqpParseException("Node \"" + n.getNodeName() + "\" has no attribute \"" + attrName + "\".");
+ }
+ return a.getNodeValue();
+ }
+
+ public static int getNamedIntegerAttribute(Node n, String attrName) throws AmqpParseException
+ {
+ return Integer.parseInt(getNamedAttribute(n, attrName));
+ }
+
+ // Element functions
+
+ public static Node findChild(Node n, String eltName) throws AmqpParseException
+ {
+ NodeList nl = n.getChildNodes();
+ for (int i = 0; i < nl.getLength(); i++)
+ {
+ Node cn = nl.item(i);
+ if (cn.getNodeName().compareTo(eltName) == 0)
+ {
+ return cn;
+ }
+ }
+ throw new AmqpParseException("Node \"" + n.getNodeName() +
+ "\" does not contain child element \"" + eltName + "\".");
+ }
+
+ // String functions
+
+ public static String firstUpper(String str)
+ {
+ if (!Character.isLetter(str.charAt(0)) || !Character.isLowerCase(str.charAt(0)))
+ {
+ return str;
+ }
+ StringBuffer sb = new StringBuffer(str);
+ sb.setCharAt(0, Character.toUpperCase(str.charAt(0)));
+ return sb.toString();
+ }
+
+ public static String firstLower(String str)
+ {
+ if (!Character.isUpperCase(str.charAt(0)))
+ {
+ return str;
+ }
+ StringBuffer sb = new StringBuffer(str);
+ sb.setCharAt(0, Character.toLowerCase(str.charAt(0)));
+ return sb.toString();
+ }
+
+ public static String createSpaces(int cnt)
+ {
+ StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < cnt; i++)
+ {
+ sb.append(' ');
+ }
+ return sb.toString();
+ }
+
+ public static boolean containsOnlyDigits(String str)
+ {
+ boolean foundNonDigit = false;
+ for (int i = 0; i < str.length() && !foundNonDigit; i++)
+ {
+ if (!Character.isDigit(str.charAt(i)))
+ {
+ foundNonDigit = true;
+ }
+ }
+ return !foundNonDigit;
+ }
+
+ public static boolean containsOnlyDigitsAndDecimal(String str)
+ {
+ boolean foundNonDigit = false;
+ int decimalCntr = 0;
+ for (int i = 0; i < str.length() && !foundNonDigit && decimalCntr < 2; i++)
+ {
+ char ch = str.charAt(i);
+ if (!(Character.isDigit(ch) || ch == '.'))
+ {
+ foundNonDigit = true;
+ }
+ else if (ch == '.')
+ {
+ decimalCntr++;
+ }
+ }
+ return !foundNonDigit && decimalCntr < 2;
+ }
+}
diff --git a/qpid/gentools/src/org/apache/qpid/gentools/VersionConsistencyCheck.java b/qpid/gentools/src/org/apache/qpid/gentools/VersionConsistencyCheck.java
new file mode 100644
index 0000000000..a9cdd56e88
--- /dev/null
+++ b/qpid/gentools/src/org/apache/qpid/gentools/VersionConsistencyCheck.java
@@ -0,0 +1,26 @@
+/*
+ *
+ * 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.
+ *
+ */
+package org.apache.qpid.gentools;
+
+public interface VersionConsistencyCheck
+{
+ public boolean isVersionConsistent(AmqpVersionSet globalVersionSet);
+}
diff --git a/qpid/gentools/templ.cpp/method/MethodBodyClass.h.tmpl b/qpid/gentools/templ.cpp/method/MethodBodyClass.h.tmpl
new file mode 100644
index 0000000000..5819a9cf9c
--- /dev/null
+++ b/qpid/gentools/templ.cpp/method/MethodBodyClass.h.tmpl
@@ -0,0 +1,112 @@
+&{${CLASS}${METHOD}Body.h}
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This file is auto-generated by ${GENERATOR} - do not modify.
+ * Supported AMQP versions:
+%{VLIST} * ${major}-${minor}
+ */
+
+#ifndef qpid_framing_${CLASS}${METHOD}Body__
+#define qpid_framing_${CLASS}${METHOD}Body__
+
+#include <string>
+#include <sstream>
+
+#include <amqp_types.h>
+#include <AMQMethodBody.h>
+#include <Buffer.h>
+#include <FieldTable.h>
+
+namespace qpid
+{
+namespace framing
+{
+${version_namespace_start}
+
+class ${CLASS}${METHOD}Body : public AMQMethodBody
+{
+ // Method field declarations
+
+%{FLIST} ${mb_field_declaration}
+
+
+public:
+ typedef boost::shared_ptr<${CLASS}${METHOD}Body> shared_ptr;
+
+ // Constructors and destructors
+
+${mb_constructor_with_initializers}
+
+ inline ${CLASS}${METHOD}Body(u_int8_t major, u_int8_t minor): AMQMethodBody(major, minor) {}
+ inline ${CLASS}${METHOD}Body(ProtocolVersion& version): AMQMethodBody(version) {}
+ virtual ~${CLASS}${METHOD}Body() {}
+
+ // Attribute get methods
+
+%{FLIST} ${mb_field_get_method}
+
+ // Helper methods
+
+ inline void print(std::ostream& out) const
+ {
+ out << "${CLASS}${METHOD}: ";
+%{FLIST} ${mb_field_print}
+ }
+
+ inline u_int16_t amqpClassId() const
+ {
+ return ${CLASS_ID_INIT};
+ }
+
+ inline u_int16_t amqpMethodId() const
+ {
+ return ${METHOD_ID_INIT};
+ }
+
+ inline u_int32_t bodySize() const
+ {
+ u_int32_t size = 0;
+%{FLIST} ${mb_body_size}
+ return size;
+ }
+
+ inline void encodeContent(Buffer&${mb_buffer_param}) const
+ {
+%{FLIST} ${mb_encode}
+ }
+
+ inline void decodeContent(Buffer&${mb_buffer_param})
+ {
+%{FLIST} ${mb_decode}
+ }
+
+${mb_server_operation_invoke}
+
+}; // class ${CLASS}${METHOD}Body
+
+${version_namespace_end}
+} // namespace framing
+} // namespace qpid
+
+#endif
+
diff --git a/qpid/gentools/templ.cpp/model/AMQP_ClientOperations.h.tmpl b/qpid/gentools/templ.cpp/model/AMQP_ClientOperations.h.tmpl
new file mode 100644
index 0000000000..a9fb0e0f69
--- /dev/null
+++ b/qpid/gentools/templ.cpp/model/AMQP_ClientOperations.h.tmpl
@@ -0,0 +1,82 @@
+&{AMQP_ClientOperations.h}
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This file is auto-generated by ${GENERATOR} - do not modify.
+ * Supported AMQP versions:
+%{VLIST} * ${major}-${minor}
+ */
+
+#ifndef qpid_framing_AMQP_ClientOperations__
+#define qpid_framing_AMQP_ClientOperations__
+
+#include <sstream>
+
+#include <FieldTable.h>
+#include <ProtocolVersion.h>
+#include <ProtocolVersionException.h>
+
+namespace qpid {
+namespace framing {
+
+class AMQP_ClientProxy;
+
+class AMQP_ClientOperations
+{
+protected:
+ ProtocolVersion version;
+ AMQP_ClientOperations() {}
+
+public:
+ AMQP_ClientOperations(u_int8_t major, u_int8_t minor) : version(major, minor) {}
+ AMQP_ClientOperations(ProtocolVersion& version) : version(version) {}
+ virtual ~AMQP_ClientOperations() {}
+
+ inline u_int8_t getMajor() const { return version.getMajor(); }
+ inline u_int8_t getMinor() const { return version.getMinor(); }
+ inline const ProtocolVersion& getVersion() const { return version; }
+ inline bool isVersion(u_int8_t _major, u_int8_t _minor) const
+ {
+ return version.equals(_major, _minor);
+ }
+ inline bool isVersion(ProtocolVersion& _version) const
+ {
+ return version.equals(_version);
+ }
+
+ // Include framing constant declarations
+ #include <AMQP_Constants.h>
+
+ // Inner classes
+
+%{CLIST} ${coh_inner_class}
+
+ // Method handler get methods
+
+%{CLIST} ${coh_method_handler_get_method}
+
+}; /* class AMQP_ClientOperations */
+
+} /* namespace framing */
+} /* namespace qpid */
+
+#endif
diff --git a/qpid/gentools/templ.cpp/model/AMQP_ClientProxy.cpp.tmpl b/qpid/gentools/templ.cpp/model/AMQP_ClientProxy.cpp.tmpl
new file mode 100644
index 0000000000..8cca6e5cec
--- /dev/null
+++ b/qpid/gentools/templ.cpp/model/AMQP_ClientProxy.cpp.tmpl
@@ -0,0 +1,52 @@
+&{AMQP_ClientProxy.cpp}
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This file is auto-generated by ${GENERATOR} - do not modify.
+ * Supported AMQP versions:
+%{VLIST} * ${major}-${minor}
+ */
+
+#include <sstream>
+
+#include <AMQP_ClientProxy.h>
+#include <AMQFrame.h>
+%{MLIST} ${cpc_method_body_include}
+
+namespace qpid {
+namespace framing {
+
+AMQP_ClientProxy::AMQP_ClientProxy(OutputHandler* out, u_int8_t major, u_int8_t minor) :
+%{CLIST} ${cpc_constructor_initializer}
+
+{}
+
+ // Inner class instance get methods
+
+%{CLIST} ${cpc_inner_class_get_method}
+
+ // Inner class implementation
+
+%{CLIST} ${cpc_inner_class_impl}
+
+} /* namespace framing */
+} /* namespace qpid */
diff --git a/qpid/gentools/templ.cpp/model/AMQP_ClientProxy.h.tmpl b/qpid/gentools/templ.cpp/model/AMQP_ClientProxy.h.tmpl
new file mode 100644
index 0000000000..0653ed7186
--- /dev/null
+++ b/qpid/gentools/templ.cpp/model/AMQP_ClientProxy.h.tmpl
@@ -0,0 +1,75 @@
+&{AMQP_ClientProxy.h}
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This file is auto-generated by ${GENERATOR} - do not modify.
+ * Supported AMQP versions:
+%{VLIST} * ${major}-${minor}
+ */
+
+#ifndef qpid_framing_AMQP_ClientProxy__
+#define qpid_framing_AMQP_ClientProxy__
+
+#include <AMQP_ClientOperations.h>
+#include <FieldTable.h>
+#include <OutputHandler.h>
+
+namespace qpid {
+namespace framing {
+
+class AMQP_ClientProxy : public AMQP_ClientOperations
+{
+private:
+
+ ProtocolVersion version;
+ OutputHandler* out;
+%{CLIST} ${cph_handler_pointer_defn}
+
+public:
+ AMQP_ClientProxy(OutputHandler* out, u_int8_t major, u_int8_t minor);
+ ProtocolVersion& getProtocolVersion() {return version;}
+ virtual ~AMQP_ClientProxy() {}
+
+ // Get methods for handlers
+
+%{CLIST} ${cph_handler_pointer_get_method}
+
+ // Inner class definitions
+
+%{CLIST} ${cph_inner_class_defn}
+
+private:
+ // Inner class instances
+
+%{CLIST} ${cph_inner_class_instance}
+
+public:
+ // Inner class instance get methods
+
+%{CLIST} ${cph_inner_class_get_method}
+
+}; /* class AMQP_ClientProxy */
+
+} /* namespace framing */
+} /* namespace qpid */
+
+#endif
diff --git a/qpid/gentools/templ.cpp/model/AMQP_Constants.h.tmpl b/qpid/gentools/templ.cpp/model/AMQP_Constants.h.tmpl
new file mode 100644
index 0000000000..4631bc8de6
--- /dev/null
+++ b/qpid/gentools/templ.cpp/model/AMQP_Constants.h.tmpl
@@ -0,0 +1,34 @@
+&{AMQP_Constants.h}
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This file is auto-generated by ${GENERATOR} - do not modify.
+ * Supported AMQP versions:
+%{VLIST} * ${major}-${minor}
+ */
+ // NOTE: This file is intended to be included within the class structure of both
+ // the client and server operations classes. These need to have <sstream> included.
+
+ // Constant getValue methods
+
+%{TLIST} ${ch_get_value_method}
+ \ No newline at end of file
diff --git a/qpid/gentools/templ.cpp/model/AMQP_HighestVersion.h.tmpl b/qpid/gentools/templ.cpp/model/AMQP_HighestVersion.h.tmpl
new file mode 100644
index 0000000000..9753b454ba
--- /dev/null
+++ b/qpid/gentools/templ.cpp/model/AMQP_HighestVersion.h.tmpl
@@ -0,0 +1,42 @@
+&{AMQP_HighestVersion.h}
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This file is auto-generated by ${GENERATOR} - do not modify.
+ * Supported AMQP versions:
+%{VLIST} * ${major}-${minor}
+ */
+#ifndef qpid_framing_highestProtocolVersion__
+#define qpid_framing_highestProtocolVersion__
+
+#include <ProtocolVersion.h>
+
+
+namespace qpid {
+namespace framing {
+
+static ProtocolVersion highestProtocolVersion(${hv_latest_major}, ${hv_latest_minor});
+
+} /* namespace framing */
+} /* namespace qpid */
+
+#endif
diff --git a/qpid/gentools/templ.cpp/model/AMQP_MethodVersionMap.cpp.tmpl b/qpid/gentools/templ.cpp/model/AMQP_MethodVersionMap.cpp.tmpl
new file mode 100644
index 0000000000..dc2a890c88
--- /dev/null
+++ b/qpid/gentools/templ.cpp/model/AMQP_MethodVersionMap.cpp.tmpl
@@ -0,0 +1,62 @@
+&{AMQP_MethodVersionMap.cpp}
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This file is auto-generated by ${GENERATOR} - do not modify.
+ * Supported AMQP versions:
+%{VLIST} * ${major}-${minor}
+ */
+
+#include <sstream>
+
+#include <AMQP_MethodVersionMap.h>
+
+namespace qpid
+{
+namespace framing
+{
+
+AMQP_MethodVersionMap::AMQP_MethodVersionMap()
+{
+%{CLIST} ${mc_create_method_body_map_entry}
+}
+
+AMQMethodBody* AMQP_MethodVersionMap::createMethodBody(u_int16_t classId, u_int16_t methodId, u_int8_t major, u_int8_t minor)
+{
+ iterator itr = find(createMapKey(classId, methodId, major, minor));
+ if (itr == end())
+ {
+ std::stringstream ss;
+ ss << "Unable to find MethodBody class for classId = " << classId << ", methodId = " <<
+ methodId << ", AMQ protocol version = " << major << "-" << minor << ".";
+ throw ProtocolVersionException(ss.str());
+ }
+ return (itr->second)(major, minor);
+}
+
+u_int64_t AMQP_MethodVersionMap::createMapKey(u_int16_t classId, u_int16_t methodId, u_int8_t major, u_int8_t minor)
+{
+ return ((u_int64_t)classId<<48) + ((u_int64_t)methodId<<32) + ((u_int64_t)major<<16) + minor;
+}
+
+} /* namespace framing */
+} /* namespace qpid */
diff --git a/qpid/gentools/templ.cpp/model/AMQP_MethodVersionMap.h.tmpl b/qpid/gentools/templ.cpp/model/AMQP_MethodVersionMap.h.tmpl
new file mode 100644
index 0000000000..c197871d4b
--- /dev/null
+++ b/qpid/gentools/templ.cpp/model/AMQP_MethodVersionMap.h.tmpl
@@ -0,0 +1,57 @@
+&{AMQP_MethodVersionMap.h}
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This file is auto-generated by ${GENERATOR} - do not modify.
+ * Supported AMQP versions:
+%{VLIST} * ${major}-${minor}
+ */
+
+#ifndef qpid_framing_AMQP_MethodVersionMap__
+#define qpid_framing_AMQP_MethodVersionMap__
+
+#include <map>
+#include <AMQMethodBody.h>
+
+%{MLIST} ${mc_method_body_include}
+
+namespace qpid
+{
+namespace framing
+{
+
+template <class T> AMQMethodBody* createMethodBodyFn(u_int8_t major, u_int8_t minor) { return new T(major, minor); }
+typedef AMQMethodBody* (*fnPtr)(u_int8_t, u_int8_t);
+
+class AMQP_MethodVersionMap: public std::map<u_int64_t, fnPtr>
+{
+protected:
+ u_int64_t createMapKey(u_int16_t classId, u_int16_t methodId, u_int8_t major, u_int8_t minor);
+public:
+ AMQP_MethodVersionMap();
+ AMQMethodBody* createMethodBody(u_int16_t classId, u_int16_t methodId, u_int8_t major, u_int8_t minor);
+};
+
+} /* namespace framing */
+} /* namespace qpid */
+
+#endif
diff --git a/qpid/gentools/templ.cpp/model/AMQP_ServerOperations.h.tmpl b/qpid/gentools/templ.cpp/model/AMQP_ServerOperations.h.tmpl
new file mode 100644
index 0000000000..e87723667b
--- /dev/null
+++ b/qpid/gentools/templ.cpp/model/AMQP_ServerOperations.h.tmpl
@@ -0,0 +1,83 @@
+&{AMQP_ServerOperations.h}
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This file is auto-generated by ${GENERATOR} - do not modify.
+ * Supported AMQP versions:
+%{VLIST} * ${major}-${minor}
+ */
+
+#ifndef qpid_framing_AMQP_ServerOperations__
+#define qpid_framing_AMQP_ServerOperations__
+
+#include <sstream>
+
+#include <FieldTable.h>
+#include <ProtocolVersion.h>
+#include <ProtocolVersionException.h>
+
+namespace qpid {
+namespace framing {
+
+class AMQP_ServerProxy;
+class AMQP_ClientProxy;
+
+class AMQP_ServerOperations
+{
+protected:
+ ProtocolVersion version;
+ AMQP_ServerOperations() {}
+
+public:
+ AMQP_ServerOperations(u_int8_t major, u_int8_t minor) : version(major, minor) {}
+ AMQP_ServerOperations(ProtocolVersion& version) : version(version) {}
+ virtual ~AMQP_ServerOperations() {}
+
+ inline u_int8_t getMajor() const { return version.getMajor(); }
+ inline u_int8_t getMinor() const { return version.getMinor(); }
+ inline const ProtocolVersion& getVersion() const { return version; }
+ inline bool isVersion(u_int8_t _major, u_int8_t _minor) const
+ {
+ return version.equals(_major, _minor);
+ }
+ inline bool isVersion(ProtocolVersion& _version) const
+ {
+ return version.equals(_version);
+ }
+
+ // Include framing constant declarations
+ #include <AMQP_Constants.h>
+
+ // Inner classes
+
+%{CLIST} ${soh_inner_class}
+
+ // Method handler get methods
+
+%{CLIST} ${soh_method_handler_get_method}
+
+}; /* class AMQP_ServerOperations */
+
+} /* namespace framing */
+} /* namespace qpid */
+
+#endif
diff --git a/qpid/gentools/templ.cpp/model/AMQP_ServerProxy.cpp.tmpl b/qpid/gentools/templ.cpp/model/AMQP_ServerProxy.cpp.tmpl
new file mode 100644
index 0000000000..cce369f98b
--- /dev/null
+++ b/qpid/gentools/templ.cpp/model/AMQP_ServerProxy.cpp.tmpl
@@ -0,0 +1,51 @@
+&{AMQP_ServerProxy.cpp}
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This file is auto-generated by ${GENERATOR} - do not modify.
+ * Supported AMQP versions:
+%{VLIST} * ${major}-${minor}
+ */
+
+#include <sstream>
+
+#include <AMQP_ServerProxy.h>
+#include <AMQFrame.h>
+%{MLIST} ${spc_method_body_include}
+
+namespace qpid {
+namespace framing {
+
+AMQP_ServerProxy::AMQP_ServerProxy(OutputHandler* out, u_int8_t major, u_int8_t minor) :
+%{CLIST} ${spc_constructor_initializer}
+{}
+
+ // Inner class instance get methods
+
+%{CLIST} ${spc_inner_class_get_method}
+
+ // Inner class implementation
+
+%{CLIST} ${spc_inner_class_impl}
+
+} /* namespace framing */
+} /* namespace qpid */
diff --git a/qpid/gentools/templ.cpp/model/AMQP_ServerProxy.h.tmpl b/qpid/gentools/templ.cpp/model/AMQP_ServerProxy.h.tmpl
new file mode 100644
index 0000000000..fab29f2c60
--- /dev/null
+++ b/qpid/gentools/templ.cpp/model/AMQP_ServerProxy.h.tmpl
@@ -0,0 +1,74 @@
+&{AMQP_ServerProxy.h}
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This file is auto-generated by ${GENERATOR} - do not modify.
+ * Supported AMQP versions:
+%{VLIST} * ${major}-${minor}
+ */
+
+#ifndef qpid_framing_AMQP_ServerProxy__
+#define qpid_framing_AMQP_ServerProxy__
+
+#include <AMQP_ServerOperations.h>
+#include <FieldTable.h>
+#include <OutputHandler.h>
+
+namespace qpid {
+namespace framing {
+
+class AMQP_ServerProxy : public AMQP_ServerOperations
+{
+private:
+ ProtocolVersion version;
+ OutputHandler* out;
+%{CLIST} ${sph_handler_pointer_defn}
+
+public:
+ AMQP_ServerProxy(OutputHandler* out, u_int8_t major, u_int8_t minor);
+ ProtocolVersion& getProtocolVersion() {return version;}
+ virtual ~AMQP_ServerProxy() {}
+
+ // Get methods for handlers
+
+%{CLIST} ${sph_handler_pointer_get_method}
+
+ // Inner class definitions
+
+%{CLIST} ${sph_inner_class_defn}
+
+private:
+ // Inner class instances
+
+%{CLIST} ${sph_inner_class_instance}
+
+public:
+ // Inner class instance get methods
+
+%{CLIST} ${sph_inner_class_get_method}
+
+}; /* class AMQP_ServerProxy */
+
+} /* namespace framing */
+} /* namespace qpid */
+
+#endif
diff --git a/qpid/gentools/templ.java/PropertyContentHeaderClass.tmpl b/qpid/gentools/templ.java/PropertyContentHeaderClass.tmpl
new file mode 100644
index 0000000000..ab6406b1fe
--- /dev/null
+++ b/qpid/gentools/templ.java/PropertyContentHeaderClass.tmpl
@@ -0,0 +1,208 @@
+&{${CLASS}ContentHeaderProperties.java}
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This file is auto-generated by ${GENERATOR} - do not modify.
+ * Supported AMQP versions:
+%{VLIST} * ${major}-${minor}
+ */
+
+package org.apache.qpid.framing;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.apache.mina.common.ByteBuffer;
+
+public class ${CLASS}ContentHeaderProperties implements ContentHeaderProperties
+{
+ private static final Logger logger = LoggerFactory.getLogger(BasicContentHeaderProperties.class);
+
+ /**
+ * We store the encoded form when we decode the content header so that if we need to
+ * write it out without modifying it we can do so without incurring the expense of
+ * reencoding it.
+ */
+ private byte[] encodedBuffer;
+
+ /**
+ * Flag indicating whether the entire content header has been decoded yet.
+ */
+ private boolean decodedFlag = true;
+
+ /**
+ * We have some optimisations for partial decoding for maximum performance. The
+ * headers are used in the broker for routing in some cases so we can decode that
+ * separately.
+ */
+ private boolean decodedHeadersFlag = true;
+
+ /**
+ * We have some optimisations for partial decoding for maximum performance. The
+ * content type is used by all clients to determine the message type.
+ */
+ private boolean decodedContentTypeFlag = true;
+
+ /**
+ * AMQP major and minor version of this instance.
+ */
+ private byte major;
+ private byte minor;
+
+ /**
+ * Property flags.
+ */
+ ${pch_property_flags_declare}
+
+ // Header fields from specification
+%{FLIST} ${field_declaration}
+
+ /**
+ * Constructor
+ */
+ public ${CLASS}ContentHeaderProperties(byte major, byte minor)
+ {
+ this.major = major;
+ this.minor = minor;
+
+ // Although one flag is initialized per property, the flags are used
+ // in ordinal order of the AMQP version represented by this instance,
+ // thus the number of flags actually used may be less than the total
+ // number defined.
+ ${pch_property_flags_initializer}
+ }
+
+ public int getPropertyListSize()
+ {
+ if (encodedBuffer != null)
+ {
+ return encodedBuffer.length;
+ }
+ else
+ {
+ int size = 0;
+%{FLIST} ${pch_field_list_size}
+ return size;
+ }
+ }
+
+ private void clearEncodedForm()
+ {
+ if (!decodedFlag && encodedBuffer != null)
+ {
+ //decode();
+ }
+ encodedBuffer = null;
+ }
+
+ public void setPropertyFlags(int[] compactPropertyFlags)
+ throws AMQProtocolVersionException
+ {
+ clearEncodedForm();
+${pch_compact_property_flags_check}
+%{FLIST} ${pch_set_compact_property_flags}
+ }
+
+ public int[] getPropertyFlags()
+ {
+ int[] compactPropertyFlags = new int[] { 0 };
+${pch_compact_property_flags_initializer}
+%{FLIST} ${pch_get_compact_property_flags}
+ return compactPropertyFlags;
+ }
+
+ public void writePropertyListPayload(ByteBuffer buffer)
+ {
+ if (encodedBuffer != null)
+ {
+ buffer.put(encodedBuffer);
+ }
+ else
+ {
+%{FLIST} ${pch_field_list_payload}
+ }
+ }
+
+ public void populatePropertiesFromBuffer(ByteBuffer buffer, int[] propertyFlags, int size)
+ throws AMQFrameDecodingException, AMQProtocolVersionException
+ {
+ setPropertyFlags(propertyFlags);
+
+ if (logger.isDebugEnabled())
+ {
+ logger.debug("Property flags: " + propertyFlags);
+ }
+ decode(buffer);
+ /*encodedBuffer = new byte[size];
+ buffer.get(encodedBuffer, 0, size);
+ decodedFlag = false;
+ decodedHeadersFlag = false;
+ decodedContentTypeFlag = false;*/
+ }
+
+ private void decode(ByteBuffer buffer)
+ {
+ //ByteBuffer buffer = ByteBuffer.wrap(encodedBuffer);
+ int pos = buffer.position();
+ try
+ {
+%{FLIST} ${pch_field_list_decode}
+ // This line does nothing, but prevents a compiler error (Exception not thrown)
+ // if this block is empty.
+ if (false) throw new AMQFrameDecodingException("");
+ }
+ catch (AMQFrameDecodingException e)
+ {
+ throw new RuntimeException("Error in content header data: " + e);
+ }
+
+ final int endPos = buffer.position();
+ buffer.position(pos);
+ final int len = endPos - pos;
+ encodedBuffer = new byte[len];
+ final int limit = buffer.limit();
+ buffer.limit(endPos);
+ buffer.get(encodedBuffer, 0, len);
+ buffer.limit(limit);
+ buffer.position(endPos);
+ decodedFlag = true;
+ }
+
+ private void decodeIfNecessary()
+ {
+ if (!decodedFlag)
+ {
+ //decode();
+ }
+ }
+
+ // Field clear methods
+
+%{FLIST} ${pch_field_clear_methods}
+
+ // Field get methods
+
+%{FLIST} ${pch_field_get_methods}
+
+ // Field set methods
+
+%{FLIST} ${pch_field_set_methods}
+}
diff --git a/qpid/gentools/templ.java/method/version/MethodBodyClass.vm b/qpid/gentools/templ.java/method/version/MethodBodyClass.vm
new file mode 100644
index 0000000000..bb62438a65
--- /dev/null
+++ b/qpid/gentools/templ.java/method/version/MethodBodyClass.vm
@@ -0,0 +1,190 @@
+#macro( UpperCamel $name )
+#set( $name = "${name.substring(0,1).toUpperCase()}${name.substring(1)}" )
+#end
+#macro( toUpperCamel $name )${name.substring(0,1).toUpperCase()}${name.substring(1)}#end
+
+
+
+#set( $amqp_ClassName = $amqpClass.Name)
+#UpperCamel( $amqp_ClassName )
+#set( $amqp_MethodName = $amqpMethod.Name )
+#UpperCamel( $amqp_MethodName )
+#set( $javaClassName = "${amqp_ClassName}${amqp_MethodName}BodyImpl" )
+#set( $interfaceName = "${amqp_ClassName}${amqp_MethodName}Body" )
+#set( $amqpPackageName = "amqp_$version.getMajor()_$version.getMinor()" )
+
+#set( $filename = "${amqpPackageName}/${javaClassName}.java")
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This file is auto-generated by ${generator} - do not modify.
+ * Supported AMQP version:
+ * $version.getMajor()-$version.getMinor()
+ */
+
+#set( $clazz = $amqpClass.asSingleVersionClass( $version ) )
+#set( $method = $amqpMethod.asSingleVersionMethod( $version ) )
+
+package org.apache.qpid.framing.amqp_$version.getMajor()_$version.getMinor();
+
+import java.util.HashMap;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.framing.*;
+
+public class ${javaClassName} extends AMQMethodBody_$version.getMajor()_$version.getMinor() implements $interfaceName
+{
+ private static final AMQMethodBodyInstanceFactory FACTORY_INSTANCE = new AMQMethodBodyInstanceFactory()
+ {
+ public AMQMethodBody newInstance(ByteBuffer in, long size) throws AMQFrameDecodingException
+ {
+ return new ${javaClassName}(in);
+ }
+
+
+ };
+
+
+ public static AMQMethodBodyInstanceFactory getFactory()
+ {
+ return FACTORY_INSTANCE;
+ }
+
+ public static int CLASS_ID = $clazz.ClassId;
+
+ public static int METHOD_ID = $method.MethodId;
+
+
+
+ // Fields declared in specification
+#foreach( $field in $method.ConsolidatedFields )
+ private final $field.NativeType _$field.getName(); // $field.UnderlyingFields
+#end
+
+
+ // Constructor
+
+ public ${javaClassName}(ByteBuffer buffer) throws AMQFrameDecodingException
+ {
+#foreach( $field in $method.ConsolidatedFields )
+ _$field.Name = read$field.getEncodingType()( buffer );
+#end
+ }
+
+ public ${javaClassName}(
+#foreach( $field in $method.FieldList )
+#if( $velocityCount == $method.getFieldList().size() )
+ $field.NativeType $field.Name
+#else
+ $field.NativeType $field.Name,
+#end
+#end)
+ {
+#set( $consolidatedFieldName = "" )
+#foreach( $field in $method.FieldList )
+#if( $method.isConsolidated( $field.Name ) )
+#if( !$method.getConsolidatedFieldName( $field.Name ).equals( $consolidatedFieldName ) )
+#if( !$consolidatedFieldName.equals("") )
+ _$consolidatedFieldName = $consolidatedFieldName; // 1
+#end
+#set( $consolidatedFieldName = $method.getConsolidatedFieldName( $field.Name ) )
+ byte $consolidatedFieldName = (byte)0;
+#end
+ if( $field.Name )
+ {
+ $consolidatedFieldName = (byte) (((int) $consolidatedFieldName) | (1 << $method.getPositionInBitField( $field.Name )));
+ }
+#if( $velocityCount == $method.getFieldList().size())
+ _$consolidatedFieldName = $consolidatedFieldName;
+#else
+
+#end
+#else
+#if( !$consolidatedFieldName.equals("") )
+ _$consolidatedFieldName = $consolidatedFieldName;
+#end
+#set( $consolidatedFieldName = "" )
+ _$field.Name = $field.Name;
+#end
+#end
+ }
+
+ public int getClazz()
+ {
+ return CLASS_ID;
+ }
+
+ public int getMethod()
+ {
+ return METHOD_ID;
+ }
+
+
+#foreach( $field in $method.FieldList )
+ public final $field.NativeType get#toUpperCamel( ${field.Name} )()
+ {
+#if( $method.isConsolidated( $field.Name ) )
+ return (((int)(_$method.getConsolidatedFieldName( $field.Name ))) & ( 1 << $method.getPositionInBitField( $field.Name ))) != 0;
+#else
+ return _$field.Name;
+#end
+ }
+#end
+
+ protected int getBodySize()
+ {
+ int size = 0;
+#foreach( $field in $method.ConsolidatedFields )
+#if( $field.isFixedSize() )
+ size += $field.Size;
+#else
+ size += getSizeOf( _$field.Name );
+#end
+#end
+ return size;
+ }
+
+ public void writeMethodPayload(ByteBuffer buffer)
+ {
+#foreach( $field in $method.ConsolidatedFields )
+
+ write$field.getEncodingType()( buffer, _$field.Name );
+#end
+ }
+
+
+ public String toString()
+ {
+ StringBuffer buf = new StringBuffer("[$javaClassName: ");
+#foreach( $field in $method.FieldList )
+ buf.append( "$field.Name=" );
+ buf.append( get#toUpperCamel( $field.Name )() );
+#if( $velocityCount != $method.FieldList.size() )
+ buf.append( ", " );
+#end
+#end
+ buf.append("]");
+ return buf.toString();
+ }
+
+
+}
diff --git a/qpid/gentools/templ.java/model/ProtocolVersionListClass.vm b/qpid/gentools/templ.java/model/ProtocolVersionListClass.vm
new file mode 100644
index 0000000000..bcf7db345b
--- /dev/null
+++ b/qpid/gentools/templ.java/model/ProtocolVersionListClass.vm
@@ -0,0 +1,154 @@
+#set( $filename = "ProtocolVersion.java" )
+/*
+*
+* 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.
+*
+*/
+
+/*
+* This file is auto-generated by $generator - do not modify.
+* Supported AMQP versions:
+#foreach( $version in $model.getVersionSet() )
+* $version.getMajor()-$version.getMinor()
+#end
+*/
+
+package org.apache.qpid.framing;
+
+import java.util.SortedSet;
+import java.util.Collections;
+import java.util.TreeSet;
+
+
+public class ProtocolVersion implements Comparable
+{
+ private final byte _majorVersion;
+ private final byte _minorVersion;
+ private final String _stringFormat;
+
+
+ public ProtocolVersion(byte majorVersion, byte minorVersion)
+ {
+ _majorVersion = majorVersion;
+ _minorVersion = minorVersion;
+ _stringFormat = _majorVersion+"-"+_minorVersion;
+ }
+
+ public byte getMajorVersion()
+ {
+ return _majorVersion;
+ }
+
+ public byte getMinorVersion()
+ {
+ return _minorVersion;
+ }
+
+ public String toString()
+ {
+ return _stringFormat;
+ }
+
+ public int compareTo(Object o)
+ {
+ ProtocolVersion pv = (ProtocolVersion) o;
+
+ /*
+ * 0-8 has it's major and minor numbers the wrong way round (it's actually 8-0)...
+ * so we need to deal with that case specially
+ */
+
+ if((_majorVersion == (byte) 8) && (_minorVersion == (byte) 0))
+ {
+ ProtocolVersion fixedThis = new ProtocolVersion(_minorVersion, _majorVersion);
+ return fixedThis.compareTo(pv);
+ }
+
+ if((pv.getMajorVersion() == (byte) 8) && (pv.getMinorVersion() == (byte) 0))
+ {
+ ProtocolVersion fixedOther = new ProtocolVersion(pv.getMinorVersion(), pv.getMajorVersion());
+ return this.compareTo(fixedOther);
+ }
+
+ if(_majorVersion > pv.getMajorVersion())
+ {
+ return 1;
+ }
+ else if(_majorVersion < pv.getMajorVersion())
+ {
+ return -1;
+ }
+ else if(_minorVersion > pv.getMinorVersion())
+ {
+ return 1;
+ }
+ else if(getMinorVersion() < pv.getMinorVersion())
+ {
+ return -1;
+ }
+ else
+ {
+ return 0;
+ }
+
+ }
+
+ public boolean equals(Object o)
+ {
+ return o != null && (o == this || (compareTo(o) == 0));
+ }
+
+ public int hashCode()
+ {
+ return (0xFF & (int)_minorVersion) | ((0xFF & (int)_majorVersion) << 8);
+ }
+
+
+ public boolean isSupported()
+ {
+ return _supportedVersions.contains(this);
+ }
+
+ public static ProtocolVersion getLatestSupportedVersion()
+ {
+ return _supportedVersions.last();
+ }
+
+ private static final SortedSet<ProtocolVersion> _supportedVersions;
+
+ static
+ {
+ SortedSet<ProtocolVersion> versions = new TreeSet<ProtocolVersion>();
+
+#foreach( $version in $model.getVersionSet() )
+ versions.add(new ProtocolVersion((byte)$version.getMajor(),(byte)$version.getMinor()));
+#end
+ _supportedVersions = Collections.unmodifiableSortedSet(versions);
+ }
+
+
+ public static SortedSet<ProtocolVersion> getSupportedProtocolVersions()
+ {
+ return _supportedVersions;
+ }
+
+
+
+
+
+}
diff --git a/qpid/gentools/templ.java/model/version/AmqpConstantsClass.vm b/qpid/gentools/templ.java/model/version/AmqpConstantsClass.vm
new file mode 100644
index 0000000000..8d459f2977
--- /dev/null
+++ b/qpid/gentools/templ.java/model/version/AmqpConstantsClass.vm
@@ -0,0 +1,37 @@
+&{AmqpConstants.java}
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This file is auto-generated by ${GENERATOR} - do not modify.
+ * Supported AMQP versions:
+%{VLIST} * ${major}-${minor}
+ */
+
+package org.apache.qpid.framing;
+
+class AmqpConstants
+{
+ // Constant getValue methods
+
+%{TLIST} ${const_get_method}
+
+}
diff --git a/qpid/gentools/templ.java/model/version/MethodRegistryClass.vm b/qpid/gentools/templ.java/model/version/MethodRegistryClass.vm
new file mode 100644
index 0000000000..82287e7f8f
--- /dev/null
+++ b/qpid/gentools/templ.java/model/version/MethodRegistryClass.vm
@@ -0,0 +1,145 @@
+#set( $filename = "amqp_$version.getMajor()_$version.getMinor()/MethodRegistry_${version.getMajor()}_${version.getMinor()}.java")
+/*
+ *
+ * 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.
+ *
+ */
+
+/*
+ * This file is auto-generated by $generator - do not modify.
+ * Supported AMQP version:
+ * $version.getMajor()-$version.getMinor()
+ */
+
+package org.apache.qpid.framing.amqp_${version.getMajor()}_${version.getMinor()};
+
+import org.apache.qpid.framing.AMQMethodBodyInstanceFactory;
+import org.apache.qpid.framing.AMQFrameDecodingException;
+import org.apache.qpid.framing.AMQMethodBody;
+import org.apache.qpid.framing.MethodRegistry;
+import org.apache.qpid.framing.ProtocolVersion;
+
+
+import org.apache.log4j.Logger;
+import org.apache.mina.common.ByteBuffer;
+
+public class MethodRegistry_$version.getMajor()_$version.getMinor() extends MethodRegistry
+{
+
+ private static final Logger _log = Logger.getLogger(MethodRegistry.class);
+
+#set( $specificModel = $model.asSingleVersionModel() )
+
+
+
+ private final AMQMethodBodyInstanceFactory[][] _factories = new AMQMethodBodyInstanceFactory[$specificModel.getMaximumClassId()+1][];
+
+ public MethodRegistry_$version.getMajor()_$version.getMinor()()
+ {
+ this(new ProtocolVersion((byte)$version.getMajor(),(byte)$version.getMinor()));
+ }
+
+ public MethodRegistry_$version.getMajor()_$version.getMinor()(ProtocolVersion pv)
+ {
+ super(pv);
+#foreach( $amqpClass in $specificModel.getClassList() )
+#set( $amqpClassNameFirstChar = $amqpClass.getName().substring(0,1) )
+#set( $amqpClassNameFirstCharU = $amqpClassNameFirstChar.toUpperCase() )
+#set( $amqpClassNameUpperCamel = "$amqpClassNameFirstCharU$amqpClass.getName().substring(1)" )
+
+
+
+ // Register method body instance factories for the $amqpClassNameUpperCamel class.
+
+ _factories[$amqpClass.getClassId()] = new AMQMethodBodyInstanceFactory[$amqpClass.getMaximumMethodId()+1];
+
+#foreach( $amqpMethod in $amqpClass.getMethodList() )
+#set( $amqpMethodNameFirstChar = $amqpMethod.getName().substring(0,1) )
+#set( $amqpMethodNameFirstCharU = $amqpMethodNameFirstChar.toUpperCase() )
+#set( $amqpMethodNameUpperCamel = "$amqpMethodNameFirstCharU$amqpMethod.getName().substring(1)" )
+ _factories[$amqpClass.getClassId()][$amqpMethod.getMethodId()] = ${amqpClassNameUpperCamel}${amqpMethodNameUpperCamel}BodyImpl.getFactory();
+#end
+
+#end
+
+
+ }
+
+
+ public AMQMethodBody convertToBody(ByteBuffer in, long size)
+ throws AMQFrameDecodingException
+ {
+ int classId = in.getUnsignedShort();
+ int methodId = in.getUnsignedShort();
+
+ AMQMethodBodyInstanceFactory bodyFactory;
+ try
+ {
+ bodyFactory = _factories[classId][methodId];
+ }
+ catch(NullPointerException e)
+ {
+ throw new AMQFrameDecodingException(_log,
+ "Class " + classId + " unknown in AMQP version $version.getMajor()-$version.getMinor()"
+ + " (while trying to decode class " + classId + " method " + methodId + ".");
+ }
+ catch(IndexOutOfBoundsException e)
+ {
+ if(classId >= _factories.length)
+ {
+ throw new AMQFrameDecodingException(_log,
+ "Class " + classId + " unknown in AMQP version $version.getMajor()-$version.getMinor()"
+ + " (while trying to decode class " + classId + " method " + methodId + ".");
+
+ }
+ else
+ {
+ throw new AMQFrameDecodingException(_log,
+ "Method " + methodId + " unknown in AMQP version $version.getMajor()-$version.getMinor()"
+ + " (while trying to decode class " + classId + " method " + methodId + ".");
+
+ }
+ }
+
+
+ if (bodyFactory == null)
+ {
+ throw new AMQFrameDecodingException(_log,
+ "Method " + methodId + " unknown in AMQP version $version.getMajor()-$version.getMinor()"
+ + " (while trying to decode class " + classId + " method " + methodId + ".");
+ }
+
+
+ return bodyFactory.newInstance(in, size);
+
+
+ }
+
+
+ public int getMaxClassId()
+ {
+ return $specificModel.getMaximumClassId();
+ }
+
+ public int getMaxMethodId(int classId)
+ {
+ return _factories[classId].length - 1;
+ }
+
+
+}
diff --git a/qpid/gentools/xml-src/amqp-0.10.test.xml b/qpid/gentools/xml-src/amqp-0.10.test.xml
new file mode 100644
index 0000000000..5d3d80648b
--- /dev/null
+++ b/qpid/gentools/xml-src/amqp-0.10.test.xml
@@ -0,0 +1,4241 @@
+<?xml version = "1.0"?>
+
+<!--
+ EDITORS: (PH) Pieter Hintjens <ph@imatix.com>
+ (KvdR) Kim van der Riet <kim.vdriet@redhat.com>
+
+ These editors have been assigned by the AMQP working group.
+ Please do not edit/commit this file without consulting with
+ one of the above editors.
+ ========================================================
+
+ TODOs
+ - see TODO comments in the text
+-->
+
+<!--
+ Copyright Notice
+ ================
+ (c) Copyright JPMorgan Chase Bank & Co., Cisco Systems, Inc., Envoy Technologies Inc.,
+ iMatix Corporation, IONA\ufffd Technologies, Red Hat, Inc.,
+ TWIST Process Innovations, and 29West Inc. 2006. All rights reserved.
+
+ License
+ =======
+ JPMorgan Chase Bank & Co., Cisco Systems, Inc., Envoy Technologies Inc., iMatix
+ Corporation, IONA\ufffd Technologies, Red Hat, Inc., TWIST Process Innovations, and
+ 29West Inc. (collectively, the "Authors") each hereby grants to you a worldwide,
+ perpetual, royalty-free, nontransferable, nonexclusive license to
+ (i) copy, display, and implement the Advanced Messaging Queue Protocol
+ ("AMQP") Specification and (ii) the Licensed Claims that are held by
+ the Authors, all for the purpose of implementing the Advanced Messaging
+ Queue Protocol Specification. Your license and any rights under this
+ Agreement will terminate immediately without notice from
+ any Author if you bring any claim, suit, demand, or action related to
+ the Advanced Messaging Queue Protocol Specification against any Author.
+ Upon termination, you shall destroy all copies of the Advanced Messaging
+ Queue Protocol Specification in your possession or control.
+
+ As used hereunder, "Licensed Claims" means those claims of a patent or
+ patent application, throughout the world, excluding design patents and
+ design registrations, owned or controlled, or that can be sublicensed
+ without fee and in compliance with the requirements of this
+ Agreement, by an Author or its affiliates now or at any
+ future time and which would necessarily be infringed by implementation
+ of the Advanced Messaging Queue Protocol Specification. A claim is
+ necessarily infringed hereunder only when it is not possible to avoid
+ infringing it because there is no plausible non-infringing alternative
+ for implementing the required portions of the Advanced Messaging Queue
+ Protocol Specification. Notwithstanding the foregoing, Licensed Claims
+ shall not include any claims other than as set forth above even if
+ contained in the same patent as Licensed Claims; or that read solely
+ on any implementations of any portion of the Advanced Messaging Queue
+ Protocol Specification that are not required by the Advanced Messaging
+ Queue Protocol Specification, or that, if licensed, would require a
+ payment of royalties by the licensor to unaffiliated third parties.
+ Moreover, Licensed Claims shall not include (i) any enabling technologies
+ that may be necessary to make or use any Licensed Product but are not
+ themselves expressly set forth in the Advanced Messaging Queue Protocol
+ Specification (e.g., semiconductor manufacturing technology, compiler
+ technology, object oriented technology, networking technology, operating
+ system technology, and the like); or (ii) the implementation of other
+ published standards developed elsewhere and merely referred to in the
+ body of the Advanced Messaging Queue Protocol Specification, or
+ (iii) any Licensed Product and any combinations thereof the purpose or
+ function of which is not required for compliance with the Advanced
+ Messaging Queue Protocol Specification. For purposes of this definition,
+ the Advanced Messaging Queue Protocol Specification shall be deemed to
+ include both architectural and interconnection requirements essential
+ for interoperability and may also include supporting source code artifacts
+ where such architectural, interconnection requirements and source code
+ artifacts are expressly identified as being required or documentation to
+ achieve compliance with the Advanced Messaging Queue Protocol Specification.
+
+ As used hereunder, "Licensed Products" means only those specific portions
+ of products (hardware, software or combinations thereof) that implement
+ and are compliant with all relevant portions of the Advanced Messaging
+ Queue Protocol Specification.
+
+ The following disclaimers, which you hereby also acknowledge as to any
+ use you may make of the Advanced Messaging Queue Protocol Specification:
+
+ THE ADVANCED MESSAGING QUEUE PROTOCOL SPECIFICATION IS PROVIDED "AS IS,"
+ AND THE AUTHORS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+ IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, OR TITLE; THAT THE
+ CONTENTS OF THE ADVANCED MESSAGING QUEUE PROTOCOL SPECIFICATION ARE
+ SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF THE ADVANCED
+ MESSAGING QUEUE PROTOCOL SPECIFICATION WILL NOT INFRINGE ANY THIRD PARTY
+ PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
+
+ THE AUTHORS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL,
+ INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO ANY
+ USE, IMPLEMENTATION OR DISTRIBUTION OF THE ADVANCED MESSAGING QUEUE
+ PROTOCOL SPECIFICATION.
+
+ The name and trademarks of the Authors may NOT be used in any manner,
+ including advertising or publicity pertaining to the Advanced Messaging
+ Queue Protocol Specification or its contents without specific, written
+ prior permission. Title to copyright in the Advanced Messaging Queue
+ Protocol Specification will at all times remain with the Authors.
+
+ No other rights are granted by implication, estoppel or otherwise.
+
+ Upon termination of your license or rights under this Agreement, you
+ shall destroy all copies of the Advanced Messaging Queue Protocol
+ Specification in your possession or control.
+
+ Trademarks
+ ==========
+ "JPMorgan", "JPMorgan Chase", "Chase", the JPMorgan Chase logo and the
+ Octagon Symbol are trademarks of JPMorgan Chase & Co.
+
+ IMATIX and the iMatix logo are trademarks of iMatix Corporation sprl.
+
+ IONA, IONA Technologies, and the IONA logos are trademarks of IONA
+ Technologies PLC and/or its subsidiaries.
+
+ LINUX is a trademark of Linus Torvalds. RED HAT and JBOSS are registered
+ trademarks of Red Hat, Inc. in the US and other countries.
+
+ Java, all Java-based trademarks and OpenOffice.org are trademarks of
+ Sun Microsystems, Inc. in the United States, other countries, or both.
+
+ Other company, product, or service names may be trademarks or service
+ marks of others.
+
+ Links to full AMQP specification:
+ =================================
+ http://www.envoytech.org/spec/amq/
+ http://www.iona.com/opensource/amqp/
+ http://www.redhat.com/solutions/specifications/amqp/
+ http://www.twiststandards.org/tiki-index.php?page=AMQ
+ http://www.imatix.com/amqp
+-->
+
+<!--
+ <!DOCTYPE amqp SYSTEM "amqp.dtd">
+-->
+
+<!-- XML Notes
+
+ We use entities to indicate repetition; attributes to indicate properties.
+
+ We use the 'name' attribute as an identifier, usually within the context
+ of the surrounding entities.
+
+ We use spaces to seperate words in names, so that we can print names in
+ their natural form depending on the context - underlines for source code,
+ hyphens for written text, etc.
+
+ We do not enforce any particular validation mechanism but we support all
+ mechanisms. The protocol definition conforms to a formal grammar that is
+ published seperately in several technologies.
+
+ -->
+
+<amqp major = "0" minor = "10" port = "5672" comment = "AMQ Protocol">
+ <!--
+ ======================================================
+ == CONSTANTS
+ ======================================================
+ -->
+ <!-- Frame types -->
+ <constant name = "frame-method" value = "1" />
+ <constant name = "frame-header" value = "2" />
+ <constant name = "frame-body" value = "3" />
+ <constant name = "frame-oob-method" value = "4" />
+ <constant name = "frame-oob-header" value = "5" />
+ <constant name = "frame-oob-body" value = "6" />
+ <constant name = "frame-trace" value = "7" />
+ <constant name = "frame-heartbeat" value = "8" />
+
+ <!-- Protocol constants -->
+ <constant name = "frame-min-size" value = "4096" />
+ <constant name = "frame-end" value = "206" />
+
+ <!-- Reply codes -->
+ <constant name = "reply-success" value = "200">
+ <doc>
+ Indicates that the method completed successfully. This reply code is
+ reserved for future use - the current protocol design does not use positive
+ confirmation and reply codes are sent only in case of an error.
+ </doc>
+ </constant>
+
+ <constant name = "not-delivered" value = "310" class = "soft-error">
+ <doc>
+ The client asked for a specific message that is no longer available.
+ The message was delivered to another client, or was purged from the queue
+ for some other reason.
+ </doc>
+ </constant>
+
+ <constant name = "content-too-large" value = "311" class = "soft-error">
+ <doc>
+ The client attempted to transfer content larger than the server could accept
+ at the present time. The client may retry at a later time.
+ </doc>
+ </constant>
+
+ <constant name = "connection-forced" value = "320" class = "hard-error">
+ <doc>
+ An operator intervened to close the connection for some reason. The client
+ may retry at some later date.
+ </doc>
+ </constant>
+
+ <constant name = "invalid-path" value = "402" class = "hard-error">
+ <doc>
+ The client tried to work with an unknown virtual host.
+ </doc>
+ </constant>
+
+ <constant name = "access-refused" value = "403" class = "soft-error">
+ <doc>
+ The client attempted to work with a server entity to which it has no
+ access due to security settings.
+ </doc>
+ </constant>
+
+ <constant name = "not-found" value = "404" class = "soft-error">
+ <doc>The client attempted to work with a server entity that does not exist.</doc>
+ </constant>
+
+ <constant name = "resource-locked" value = "405" class = "soft-error">
+ <doc>
+ The client attempted to work with a server entity to which it has no
+ access because another client is working with it.
+ </doc>
+ </constant>
+
+ <constant name = "precondition-failed" value = "406" class = "soft-error">
+ <doc>
+ The client requested a method that was not allowed because some precondition
+ failed.
+ </doc>
+ </constant>
+
+ <constant name = "frame-error" value = "501" class = "hard-error">
+ <doc>
+ The client sent a malformed frame that the server could not decode. This
+ strongly implies a programming error in the client.
+ </doc>
+ </constant>
+
+ <constant name = "syntax-error" value = "502" class = "hard-error">
+ <doc>
+ The client sent a frame that contained illegal values for one or more
+ fields. This strongly implies a programming error in the client.
+ </doc>
+ </constant>
+
+ <constant name = "command-invalid" value = "503" class = "hard-error">
+ <doc>
+ The client sent an invalid sequence of frames, attempting to perform an
+ operation that was considered invalid by the server. This usually implies
+ a programming error in the client.
+ </doc>
+ </constant>
+
+ <constant name = "channel-error" value = "504" class = "hard-error">
+ <doc>
+ The client attempted to work with a channel that had not been correctly
+ opened. This most likely indicates a fault in the client layer.
+ </doc>
+ </constant>
+
+ <constant name = "resource-error" value = "506" class = "hard-error">
+ <doc>
+ The server could not complete the method because it lacked sufficient
+ resources. This may be due to the client creating too many of some type
+ of entity.
+ </doc>
+ </constant>
+
+ <constant name = "not-allowed" value = "530" class = "hard-error">
+ <doc>
+ The client tried to work with some entity in a manner that is prohibited
+ by the server, due to security settings or by some other criteria.
+ </doc>
+ </constant>
+
+ <constant name = "not-implemented" value = "540" class = "hard-error">
+ <doc>
+ The client tried to use functionality that is not implemented in the
+ server.
+ </doc>
+ </constant>
+
+ <constant name = "internal-error" value = "545" class = "hard-error">
+ <doc>
+ The server could not complete the method because of an internal error.
+ The server may require intervention by an operator in order to resume
+ normal operations.
+ </doc>
+ </constant>
+
+ <constant name = "test-double" value = "3.141592654"/>
+ <constant name = "test-str1" value = "hello, world!"/>
+ <constant name = "test-str2" value = "1.2.3.4"/>
+
+ <!--
+ ======================================================
+ == DOMAIN TYPES
+ ======================================================
+ -->
+
+ <domain name = "access-ticket" type = "short" label = "access ticket granted by server">
+ <doc>
+ An access ticket granted by the server for a certain set of access rights
+ within a specific realm. Access tickets are valid within the channel where
+ they were created, and expire when the channel closes.
+ </doc>
+ <assert check = "ne" value = "0" />
+ </domain>
+
+ <domain name = "class-id" type = "short" />
+
+ <domain name = "consumer-tag" type = "shortstr" label = "consumer tag">
+ <doc>
+ Identifier for the consumer, valid within the current connection.
+ </doc>
+ </domain>
+
+ <domain name = "delivery-tag" type = "longlong" label = "server-assigned delivery tag">
+ <doc>
+ The server-assigned and channel-specific delivery tag
+ </doc>
+ <rule name = "channel-local">
+ <doc>
+ The delivery tag is valid only within the channel from which the message was
+ received. I.e. a client MUST NOT receive a message on one channel and then
+ acknowledge it on another.
+ </doc>
+ </rule>
+ <rule name = "non-zero">
+ <doc>
+ The server MUST NOT use a zero value for delivery tags. Zero is reserved
+ for client use, meaning "all messages so far received".
+ </doc>
+ </rule>
+ </domain>
+
+ <domain name = "exchange-name" type = "shortstr" label = "exchange name">
+ <doc>
+ The exchange name is a client-selected string that identifies the exchange for publish
+ methods. Exchange names may consist of any mixture of digits, letters, and underscores.
+ Exchange names are scoped by the virtual host.
+ </doc>
+ <assert check = "length" value = "127" />
+ </domain>
+
+ <domain name = "known-hosts" type = "shortstr" label = "list of known hosts">
+ <doc>
+ Specifies the list of equivalent or alternative hosts that the server knows about,
+ which will normally include the current server itself. Clients can cache this
+ information and use it when reconnecting to a server after a failure. This field
+ may be empty.
+ </doc>
+ </domain>
+
+ <domain name = "method-id" type = "long" />
+
+ <domain name = "no-ack" type = "bit" label = "no acknowledgement needed">
+ <doc>
+ If this field is set the server does not expect acknowledgments for
+ messages. That is, when a message is delivered to the client the server
+ automatically and silently acknowledges it on behalf of the client. This
+ functionality increases performance but at the cost of reliability.
+ Messages can get lost if a client dies before it can deliver them to the
+ application.
+ </doc>
+ </domain>
+
+ <domain name = "no-local" type = "bit" label = "do not deliver own messages">
+ <doc>
+ If the no-local field is set the server will not send messages to the client that
+ published them.
+ </doc>
+ </domain>
+
+ <domain name = "path" type = "shortstr">
+ <doc>
+ Must start with a slash "/" and continue with path names separated by slashes. A path
+ name consists of any combination of at least one of [A-Za-z0-9] plus zero or more of
+ [.-_+!=:].
+ </doc>
+
+ <assert check = "notnull" />
+ <assert check = "syntax" rule = "path" />
+ <assert check = "length" value = "127" />
+ </domain>
+
+ <domain name = "peer-properties" type = "table">
+ <doc>
+ This string provides a set of peer properties, used for identification, debugging, and
+ general information.
+ </doc>
+ </domain>
+
+ <domain name = "queue-name" type = "shortstr" label = "queue name">
+ <doc>
+ The queue name identifies the queue within the vhost. Queue names may consist of any
+ mixture of digits, letters, and underscores.
+ </doc>
+ <assert check = "length" value = "127" />
+ </domain>
+
+ <domain name = "redelivered" type = "bit" label = "message is being redelivered">
+ <doc>
+ This indicates that the message has been previously delivered to this or
+ another client.
+ </doc>
+ <rule name = "implementation">
+ <doc>
+ The server SHOULD try to signal redelivered messages when it can. When
+ redelivering a message that was not successfully acknowledged, the server
+ SHOULD deliver it to the original client if possible.
+ </doc>
+ <doc type = "scenario">
+ Create a shared queue and publish a message to the queue. Consume the
+ message using explicit acknowledgements, but do not acknowledge the
+ message. Close the connection, reconnect, and consume from the queue
+ again. The message should arrive with the redelivered flag set.
+ </doc>
+ </rule>
+ <rule name = "hinting">
+ <doc>
+ The client MUST NOT rely on the redelivered field but should take it as a
+ hint that the message may already have been processed. A fully robust
+ client must be able to track duplicate received messages on non-transacted,
+ and locally-transacted channels.
+ </doc>
+ </rule>
+ </domain>
+
+ <domain name = "reply-code" type = "short" label = "reply code from server">
+ <doc>
+ The reply code. The AMQ reply codes are defined as constants at the start
+ of this formal specification.
+ </doc>
+ <assert check = "notnull" />
+ </domain>
+
+ <domain name = "reply-text" type = "shortstr" label = "localised reply text">
+ <doc>
+ The localised reply text. This text can be logged as an aid to resolving
+ issues.
+ </doc>
+ <assert check = "notnull" />
+ </domain>
+
+ <!-- Elementary domains -->
+ <domain name = "bit" type = "bit" label = "single bit" />
+ <domain name = "octet" type = "octet" label = "single octet" />
+ <domain name = "short" type = "short" label = "16-bit integer" />
+ <domain name = "long" type = "long" label = "32-bit integer" />
+ <domain name = "longlong" type = "longlong" label = "64-bit integer" />
+ <domain name = "shortstr" type = "shortstr" label = "short string" />
+ <domain name = "longstr" type = "longstr" label = "long string" />
+ <domain name = "timestamp" type = "timestamp" label = "64-bit timestamp" />
+ <domain name = "table" type = "table" label = "field table" />
+
+ <!-- == CONNECTION ======================================================= -->
+
+ <!-- TODO 0.81 - the 'handler' attribute of methods needs to be reviewed, and if
+ no current implementations use it, removed. /PH 2006/07/20
+ -->
+
+ <class name = "connection" handler = "connection" index = "10" label = "work with socket connections">
+ <doc>
+ The connection class provides methods for a client to establish a network connection to
+ a server, and for both peers to operate the connection thereafter.
+ </doc>
+
+ <doc type = "grammar">
+ connection = open-connection *use-connection close-connection
+ open-connection = C:protocol-header
+ S:START C:START-OK
+ *challenge
+ S:TUNE C:TUNE-OK
+ C:OPEN S:OPEN-OK | S:REDIRECT
+ challenge = S:SECURE C:SECURE-OK
+ use-connection = *channel
+ close-connection = C:CLOSE S:CLOSE-OK
+ / S:CLOSE C:CLOSE-OK
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "start" synchronous = "1" index = "10" label = "start connection negotiation">
+ <doc>
+ This method starts the connection negotiation process by telling the client the
+ protocol version that the server proposes, along with a list of security mechanisms
+ which the client can use for authentication.
+ </doc>
+
+ <rule name = "protocol-name">
+ <doc>
+ If the server cannot support the protocol specified in the protocol header,
+ it MUST close the socket connection without sending any response method.
+ </doc>
+ <doc type = "scenario">
+ The client sends a protocol header containing an invalid protocol name.
+ The server must respond by closing the connection.
+ </doc>
+ </rule>
+ <rule name = "server-support">
+ <doc>
+ The server MUST provide a protocol version that is lower than or equal to
+ that requested by the client in the protocol header.
+ </doc>
+ <doc type = "scenario">
+ The client requests a protocol version that is higher than any valid
+ implementation, e.g. 9.0. The server must respond with a current
+ protocol version, e.g. 1.0.
+ </doc>
+ </rule>
+ <rule name = "client-support">
+ <doc>
+ If the client cannot handle the protocol version suggested by the server
+ it MUST close the socket connection.
+ </doc>
+ <doc type = "scenario">
+ The server sends a protocol version that is lower than any valid
+ implementation, e.g. 0.1. The client must respond by closing the
+ connection.
+ </doc>
+ </rule>
+
+ <chassis name = "client" implement = "MUST" />
+ <response name = "start-ok" />
+
+ <field name = "version-major" domain = "octet" label = "protocol major version">
+ <doc>
+ The version of the protocol, expressed in protocol units of 0.1 public
+ versions and properly printed as two digits with a leading zero. I.e. a
+ protocol version of "09" represents a public version "0.9". The decimal
+ shift allows the correct expression of pre-1.0 protocol releases.
+ </doc>
+ <doc type = "todo">
+ This field should be renamed to "protocol version".
+ </doc>
+ </field>
+
+ <field name = "version-minor" domain = "octet" label = "protocol major version">
+ <doc>
+ The protocol revision, expressed as an integer from 0 to 9. The use of more
+ than ten revisions is discouraged. The public version string is constructed
+ from the protocol version and revision as follows: we print the protocol
+ version with one decimal position, and we append the protocol revision. A
+ version=10 and revision=2 are printed as "1.02".
+ </doc>
+ <doc type = "todo">
+ This field should be renamed to "protocol revision".
+ </doc>
+ </field>
+
+ <field name = "server-properties" domain = "peer-properties" label = "server properties">
+ <rule name = "required-fields">
+ <doc>
+ The properties SHOULD contain at least these fields: "host", specifying the
+ server host name or address, "product", giving the name of the server product,
+ "version", giving the name of the server version, "platform", giving the name
+ of the operating system, "copyright", if appropriate, and "information", giving
+ other general information.
+ </doc>
+ <doc type = "scenario">
+ Client connects to server and inspects the server properties. It checks for
+ the presence of the required fields.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "mechanisms" domain = "longstr" label = "available security mechanisms">
+ <doc>
+ A list of the security mechanisms that the server supports, delimited by spaces.
+ Currently ASL supports these mechanisms: PLAIN.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+
+ <field name = "locales" domain = "longstr" label = "available message locales">
+ <doc>
+ A list of the message locales that the server supports, delimited by spaces. The
+ locale defines the language in which the server will send reply texts.
+ </doc>
+ <rule name = "required-support">
+ <doc>
+ The server MUST support at least the en_US locale.
+ </doc>
+ <doc type = "scenario">
+ Client connects to server and inspects the locales field. It checks for
+ the presence of the required locale(s).
+ </doc>
+ </rule>
+ <assert check = "notnull" />
+ </field>
+ </method>
+
+ <method name = "start-ok" synchronous = "1" index = "11"
+ label = "select security mechanism and locale">
+ <doc>
+ This method selects a SASL security mechanism. ASL uses SASL (RFC2222) to
+ negotiate authentication and encryption.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "client-properties" domain = "peer-properties" label = "client properties">
+ <rule name = "required-fields">
+ <!-- This rule is not testable from the client side -->
+ <doc>
+ The properties SHOULD contain at least these fields: "product", giving the name
+ of the client product, "version", giving the name of the client version, "platform",
+ giving the name of the operating system, "copyright", if appropriate, and
+ "information", giving other general information.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "mechanism" domain = "shortstr" label = "selected security mechanism">
+ <doc>
+ A single security mechanisms selected by the client, which must be one of those
+ specified by the server.
+ </doc>
+ <rule name = "security">
+ <doc>
+ The client SHOULD authenticate using the highest-level security profile it
+ can handle from the list provided by the server.
+ </doc>
+ </rule>
+ <rule name = "validity">
+ <doc>
+ If the mechanism field does not contain one of the security mechanisms
+ proposed by the server in the Start method, the server MUST close the
+ connection without sending any further data.
+ </doc>
+ <doc type = "scenario">
+ Client connects to server and sends an invalid security mechanism. The
+ server must respond by closing the connection (a socket close, with no
+ connection close negotiation).
+ </doc>
+ </rule>
+ <assert check = "notnull" />
+ </field>
+
+ <field name = "response" domain = "longstr" label = "security response data">
+ <doc>
+ A block of opaque data passed to the security mechanism. The contents of this
+ data are defined by the SASL security mechanism.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+
+ <field name = "locale" domain = "shortstr" label = "selected message locale">
+ <doc>
+ A single message local selected by the client, which must be one of those
+ specified by the server.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "secure" synchronous = "1" index = "20" label = "security mechanism challenge">
+ <doc>
+ The SASL protocol works by exchanging challenges and responses until both peers have
+ received sufficient information to authenticate each other. This method challenges
+ the client to provide more information.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+ <response name = "secure-ok" />
+
+ <field name = "challenge" domain = "longstr" label = "security challenge data">
+ <doc>
+ Challenge information, a block of opaque binary data passed to the security
+ mechanism.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "secure-ok" synchronous = "1" index = "21" label = "security mechanism response">
+ <doc>
+ This method attempts to authenticate, passing a block of SASL data for the security
+ mechanism at the server side.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "response" domain = "longstr" label = "security response data">
+ <doc>
+ A block of opaque data passed to the security mechanism. The contents of this
+ data are defined by the SASL security mechanism.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "tune" synchronous = "1" index = "30"
+ label = "propose connection tuning parameters">
+ <doc>
+ This method proposes a set of connection configuration values to the client. The
+ client can accept and/or adjust these.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <response name = "tune-ok" />
+
+ <field name = "channel-max" domain = "short" label = "proposed maximum channels">
+ <doc>
+ The maximum total number of channels that the server allows per connection. Zero
+ means that the server does not impose a fixed limit, but the number of allowed
+ channels may be limited by available server resources.
+ </doc>
+ </field>
+
+ <field name = "frame-max" domain = "long" label = "proposed maximum frame size">
+ <doc>
+ The largest frame size that the server proposes for the connection. The client
+ can negotiate a lower value. Zero means that the server does not impose any
+ specific limit but may reject very large frames if it cannot allocate resources
+ for them.
+ </doc>
+ <rule name = "minimum">
+ <doc>
+ Until the frame-max has been negotiated, both peers MUST accept frames of up
+ to frame-min-size octets large, and the minimum negotiated value for frame-max
+ is also frame-min-size.
+ </doc>
+ <doc type = "scenario">
+ Client connects to server and sends a large properties field, creating a frame
+ of frame-min-size octets. The server must accept this frame.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "heartbeat" domain = "short" label = "desired heartbeat delay">
+ <!-- TODO 0.82 - the heartbeat negotiation mechanism was changed during
+ implementation because the model documented here does not actually
+ work properly. The best model we found is that the server proposes
+ a heartbeat value to the client; the client can reply with zero, meaning
+ 'do not use heartbeats (as documented here), or can propose its own
+ heartbeat value, which the server should then accept. This is different
+ from the model here which is disconnected - e.g. each side requests a
+ heartbeat independently. Basically a connection is heartbeated in
+ both ways, or not at all, depending on whether both peers support
+ heartbeating or not, and the heartbeat value should itself be chosen
+ by the client so that remote links can get a higher value. Also, the
+ actual heartbeat mechanism needs documentation, and is as follows: so
+ long as there is activity on a connection - in or out - both peers
+ assume the connection is active. When there is no activity, each peer
+ must send heartbeat frames. When no heartbeat frame is received after
+ N cycles (where N is at least 2), the connection can be considered to
+ have died. /PH 2006/07/19
+ -->
+ <doc>
+ The delay, in seconds, of the connection heartbeat that the server wants.
+ Zero means the server does not want a heartbeat.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "tune-ok" synchronous = "1" index = "31"
+ label = "negotiate connection tuning parameters">
+ <doc>
+ This method sends the client's connection tuning parameters to the server.
+ Certain fields are negotiated, others provide capability information.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "channel-max" domain = "short" label = "negotiated maximum channels">
+ <doc>
+ The maximum total number of channels that the client will use per connection.
+ </doc>
+ <rule name = "upper-limit">
+ <doc>
+ If the client specifies a channel max that is higher than the value provided
+ by the server, the server MUST close the connection without attempting a
+ negotiated close. The server may report the error in some fashion to assist
+ implementors.
+ </doc>
+ </rule>
+ <assert check = "notnull" />
+ <assert check = "le" method = "tune" field = "channel-max" />
+ </field>
+
+ <field name = "frame-max" domain = "long" label = "negotiated maximum frame size">
+ <doc>
+ The largest frame size that the client and server will use for the connection.
+ Zero means that the client does not impose any specific limit but may reject
+ very large frames if it cannot allocate resources for them. Note that the
+ frame-max limit applies principally to content frames, where large contents can
+ be broken into frames of arbitrary size.
+ </doc>
+ <rule name = "minimum">
+ <doc>
+ Until the frame-max has been negotiated, both peers MUST accept frames of up
+ to frame-min-size octets large, and the minimum negotiated value for frame-max
+ is also frame-min-size.
+ </doc>
+ </rule>
+ <rule name = "upper-limit">
+ <doc>
+ If the client specifies a frame max that is higher than the value provided
+ by the server, the server MUST close the connection without attempting a
+ negotiated close. The server may report the error in some fashion to assist
+ implementors.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "heartbeat" domain = "short" label = "desired heartbeat delay">
+ <doc>
+ The delay, in seconds, of the connection heartbeat that the client wants. Zero
+ means the client does not want a heartbeat.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "open" synchronous = "1" index = "40" label = "open connection to virtual host">
+ <doc>
+ This method opens a connection to a virtual host, which is a collection of
+ resources, and acts to separate multiple application domains within a server.
+ The server may apply arbitrary limits per virtual host, such as the number
+ of each type of entity that may be used, per connection and/or in total.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "open-ok" />
+ <response name = "redirect" />
+
+ <field name = "virtual-host" domain = "path" label = "virtual host name">
+ <!-- TODO 0.82 - the entire vhost model needs review. This concept was
+ prompted by the HTTP vhost concept but does not fit very well into
+ AMQP. Currently we use the vhost as a "cluster identifier" which is
+ inaccurate usage. /PH 2006/07/19
+ -->
+ <assert check = "regexp" value = "^[a-zA-Z0-9/-_]+$" />
+ <doc>
+ The name of the virtual host to work with.
+ </doc>
+ <rule name = "separation">
+ <doc>
+ If the server supports multiple virtual hosts, it MUST enforce a full
+ separation of exchanges, queues, and all associated entities per virtual
+ host. An application, connected to a specific virtual host, MUST NOT be able
+ to access resources of another virtual host.
+ </doc>
+ </rule>
+ <rule name = "security">
+ <doc>
+ The server SHOULD verify that the client has permission to access the
+ specified virtual host.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "capabilities" domain = "shortstr" label = "required capabilities">
+ <doc>
+ The client can specify zero or more capability names, delimited by spaces.
+ The server can use this string to how to process the client's connection
+ request.
+ </doc>
+ </field>
+
+ <field name = "insist" domain = "bit" label = "insist on connecting to server">
+ <doc>
+ In a configuration with multiple collaborating servers, the server may respond
+ to a Connection.Open method with a Connection.Redirect. The insist option tells
+ the server that the client is insisting on a connection to the specified server.
+ </doc>
+ <rule name = "behaviour">
+ <doc>
+ When the client uses the insist option, the server MUST NOT respond with a
+ Connection.Redirect method. If it cannot accept the client's connection
+ request it should respond by closing the connection with a suitable reply
+ code.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "open-ok" synchronous = "1" index = "41" label = "signal that connection is ready">
+ <doc>
+ This method signals to the client that the connection is ready for use.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "known-hosts" domain = "known-hosts" />
+ </method>
+
+ <method name = "redirect" synchronous = "1" index = "42" label = "redirects client to other server">
+ <doc>
+ This method redirects the client to another server, based on the requested virtual
+ host and/or capabilities.
+ </doc>
+ <rule name = "usage">
+ <doc>
+ When getting the Connection.Redirect method, the client SHOULD reconnect to
+ the host specified, and if that host is not present, to any of the hosts
+ specified in the known-hosts list.
+ </doc>
+ </rule>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "host" domain = "shortstr" label = "server to connect to">
+ <doc>
+ Specifies the server to connect to. This is an IP address or a DNS name,
+ optionally followed by a colon and a port number. If no port number is
+ specified, the client should use the default port number for the protocol.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+ <field name = "known-hosts" domain = "known-hosts" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "close" synchronous = "1" index = "50" label = "request a connection close">
+ <doc>
+ This method indicates that the sender wants to close the connection. This may be
+ due to internal conditions (e.g. a forced shut-down) or due to an error handling
+ a specific method, i.e. an exception. When a close is due to an exception, the
+ sender provides the class and method id of the method which caused the exception.
+ </doc>
+ <!-- TODO: the connection close mechanism needs to be reviewed from the ODF
+ documentation and better expressed as rules here. /PH 2006/07/20
+ -->
+ <rule name = "stability">
+ <doc>
+ After sending this method any received method except the Close-OK method MUST
+ be discarded.
+ </doc>
+ </rule>
+
+ <chassis name = "client" implement = "MUST" />
+ <chassis name = "server" implement = "MUST" />
+ <response name = "close-ok" />
+
+ <field name = "reply-code" domain = "reply-code" />
+ <field name = "reply-text" domain = "reply-text" />
+
+ <field name = "class-id" domain = "class-id" label = "failing method class">
+ <doc>
+ When the close is provoked by a method exception, this is the class of the
+ method.
+ </doc>
+ </field>
+
+ <field name = "method-id" domain = "method-id" label = "failing method ID">
+ <doc>
+ When the close is provoked by a method exception, this is the ID of the method.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "close-ok" synchronous = "1" index = "51" label = "confirm a connection close">
+ <doc>
+ This method confirms a Connection.Close method and tells the recipient that it is
+ safe to release resources for the connection and close the socket.
+ </doc>
+ <rule name = "reporting">
+ <doc>
+ A peer that detects a socket closure without having received a Close-Ok
+ handshake method SHOULD log the error.
+ </doc>
+ </rule>
+ <chassis name = "client" implement = "MUST" />
+ <chassis name = "server" implement = "MUST" />
+ </method>
+ </class>
+
+ <!-- == CHANNEL ========================================================== -->
+
+ <class name = "channel" handler = "channel" index = "20" label = "work with channels">
+ <doc>
+ The channel class provides methods for a client to establish a channel to a
+ server and for both peers to operate the channel thereafter.
+ </doc>
+
+ <doc type = "grammar">
+ channel = open-channel *use-channel close-channel
+ open-channel = C:OPEN S:OPEN-OK
+ use-channel = C:FLOW S:FLOW-OK
+ / S:FLOW C:FLOW-OK
+ / S:ALERT
+ / functional-class
+ close-channel = C:CLOSE S:CLOSE-OK
+ / S:CLOSE C:CLOSE-OK
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "open" synchronous = "1" index = "10" label = "open a channel for use">
+ <doc>
+ This method opens a channel to the server.
+ </doc>
+ <rule name = "state" on-failure = "channel-error">
+ <doc>
+ The client MUST NOT use this method on an alread-opened channel.
+ </doc>
+ <doc type = "scenario">
+ Client opens a channel and then reopens the same channel.
+ </doc>
+ </rule>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "open-ok" />
+ <field name = "out of band" domain = "shortstr" label = "out-of-band settings">
+ <doc>
+ Configures out-of-band transfers on this channel. The syntax and meaning of this
+ field will be formally defined at a later date.
+ </doc>
+ <assert check = "null" />
+ </field>
+ </method>
+
+ <method name = "open-ok" synchronous = "1" index = "11" label = "signal that the channel is ready">
+ <doc>
+ This method signals to the client that the channel is ready for use.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "flow" synchronous = "1" index = "20" label = "enable/disable flow from peer">
+ <doc>
+ This method asks the peer to pause or restart the flow of content data. This is a
+ simple flow-control mechanism that a peer can use to avoid oveflowing its queues or
+ otherwise finding itself receiving more messages than it can process. Note that this
+ method is not intended for window control. The peer that receives a disable flow
+ method should finish sending the current content frame, if any, then pause.
+ </doc>
+
+ <rule name = "initial-state">
+ <doc>
+ When a new channel is opened, it is active (flow is active). Some applications
+ assume that channels are inactive until started. To emulate this behaviour a
+ client MAY open the channel, then pause it.
+ </doc>
+ </rule>
+
+ <rule name = "bidirectional">
+ <doc>
+ When sending content frames, a peer SHOULD monitor the channel for incoming
+ methods and respond to a Channel.Flow as rapidly as possible.
+ </doc>
+ </rule>
+
+ <rule name = "throttling">
+ <doc>
+ A peer MAY use the Channel.Flow method to throttle incoming content data for
+ internal reasons, for example, when exchanging data over a slower connection.
+ </doc>
+ </rule>
+
+ <rule name = "expected-behaviour">
+ <doc>
+ The peer that requests a Channel.Flow method MAY disconnect and/or ban a peer
+ that does not respect the request. This is to prevent badly-behaved clients
+ from overwhelming a broker.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <response name = "flow-ok" />
+
+ <field name = "active" domain = "bit" label = "start/stop content frames">
+ <doc>
+ If 1, the peer starts sending content frames. If 0, the peer stops sending
+ content frames.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "flow-ok" index = "21" label = "confirm a flow method">
+ <doc>
+ Confirms to the peer that a flow command was received and processed.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+ <field name = "active" domain = "bit" label = "current flow setting">
+ <doc>
+ Confirms the setting of the processed flow method: 1 means the peer will start
+ sending or continue to send content frames; 0 means it will not.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- TODO 0.82 - remove this method entirely
+ /PH 2006/07/20
+ -->
+ <method name = "alert" index = "30" label = "send a non-fatal warning message">
+ <doc>
+ This method allows the server to send a non-fatal warning to the client. This is
+ used for methods that are normally asynchronous and thus do not have confirmations,
+ and for which the server may detect errors that need to be reported. Fatal errors
+ are handled as channel or connection exceptions; non-fatal errors are sent through
+ this method.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "reply-code" domain = "reply-code" />
+ <field name = "reply-text" domain = "reply-text" />
+ <field name = "details" domain = "table" label = "detailed information for warning">
+ <doc>
+ A set of fields that provide more information about the problem. The meaning of
+ these fields are defined on a per-reply-code basis (TO BE DEFINED).
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "close" synchronous = "1" index = "40" label = "request a channel close">
+ <doc>
+ This method indicates that the sender wants to close the channel. This may be due to
+ internal conditions (e.g. a forced shut-down) or due to an error handling a specific
+ method, i.e. an exception. When a close is due to an exception, the sender provides
+ the class and method id of the method which caused the exception.
+ </doc>
+
+ <!-- TODO: the channel close behaviour needs to be reviewed from the ODF
+ documentation and better expressed as rules here. /PH 2006/07/20
+ -->
+ <rule name = "stability">
+ <doc>
+ After sending this method any received method except the Close-OK method MUST
+ be discarded.
+ </doc>
+ </rule>
+
+ <chassis name = "client" implement = "MUST" />
+ <chassis name = "server" implement = "MUST" />
+ <response name = "close-ok" />
+
+ <field name = "reply-code" domain = "reply-code" />
+ <field name = "reply-text" domain = "reply-text" />
+
+ <field name = "class-id" domain = "class-id" label = "failing method class">
+ <doc>
+ When the close is provoked by a method exception, this is the class of the
+ method.
+ </doc>
+ </field>
+
+ <field name = "method-id" domain = "method-id" label = "failing method ID">
+ <doc>
+ When the close is provoked by a method exception, this is the ID of the method.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "close-ok" synchronous = "1" index = "41" label = "confirm a channel close">
+ <doc>
+ This method confirms a Channel.Close method and tells the recipient that it is safe
+ to release resources for the channel and close the socket.
+ </doc>
+ <rule name = "reporting">
+ <doc>
+ A peer that detects a socket closure without having received a Channel.Close-Ok
+ handshake method SHOULD log the error.
+ </doc>
+ </rule>
+ <chassis name = "client" implement = "MUST" />
+ <chassis name = "server" implement = "MUST" />
+ </method>
+ </class>
+
+ <!-- == ACCESS =========================================================== -->
+
+ <!-- TODO 0.82 - this class must be implemented by two teams before we can
+ consider it matured.
+ -->
+
+ <class name = "access" handler = "connection" index = "30" label = "work with access tickets">
+ <doc>
+ The protocol control access to server resources using access tickets. A
+ client must explicitly request access tickets before doing work. An access
+ ticket grants a client the right to use a specific set of resources -
+ called a "realm" - in specific ways.
+ </doc>
+
+ <doc type = "grammar">
+ access = C:REQUEST S:REQUEST-OK
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "request" synchronous = "1" index = "10" label = "request an access ticket">
+ <doc>
+ This method requests an access ticket for an access realm. The server
+ responds by granting the access ticket. If the client does not have
+ access rights to the requested realm this causes a connection exception.
+ Access tickets are a per-channel resource.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "request-ok" />
+
+ <field name = "realm" domain = "shortstr" label = "name of requested realm">
+ <doc>
+ Specifies the name of the realm to which the client is requesting access.
+ The realm is a configured server-side object that collects a set of
+ resources (exchanges, queues, etc.). If the channel has already requested
+ an access ticket onto this realm, the previous ticket is destroyed and a
+ new ticket is created with the requested access rights, if allowed.
+ </doc>
+ <rule name = "validity" on-failure = "access-refused">
+ <doc>
+ The client MUST specify a realm that is known to the server. The server
+ makes an identical response for undefined realms as it does for realms
+ that are defined but inaccessible to this client.
+ </doc>
+ <doc type = "scenario">
+ Client specifies an undefined realm.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exclusive" domain = "bit" label = "request exclusive access">
+ <doc>
+ Request exclusive access to the realm, meaning that this will be the only
+ channel that uses the realm's resources.
+ </doc>
+ <rule name = "validity" on-failure = "access-refused">
+ <doc>
+ The client MAY NOT request exclusive access to a realm that has active
+ access tickets, unless the same channel already had the only access
+ ticket onto that realm.
+ </doc>
+ <doc type = "scenario">
+ Client opens two channels and requests exclusive access to the same realm.
+ </doc>
+ </rule>
+ </field>
+ <field name = "passive" domain = "bit" label = "request passive access">
+ <doc>
+ Request message passive access to the specified access realm. Passive
+ access lets a client get information about resources in the realm but
+ not to make any changes to them.
+ </doc>
+ </field>
+ <field name = "active" domain = "bit" label = "request active access">
+ <doc>
+ Request message active access to the specified access realm. Active access lets
+ a client get create and delete resources in the realm.
+ </doc>
+ </field>
+ <field name = "write" domain = "bit" label = "request write access">
+ <doc>
+ Request write access to the specified access realm. Write access lets a client
+ publish messages to all exchanges in the realm.
+ </doc>
+ </field>
+ <field name = "read" domain = "bit" label = "request read access">
+ <doc>
+ Request read access to the specified access realm. Read access lets a client
+ consume messages from queues in the realm.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "request-ok" synchronous = "1" index = "11" label = "grant access to server resources">
+ <doc>
+ This method provides the client with an access ticket. The access ticket is valid
+ within the current channel and for the lifespan of the channel.
+ </doc>
+ <rule name = "per-channel" on-failure = "not-allowed">
+ <doc>
+ The client MUST NOT use access tickets except within the same channel as
+ originally granted.
+ </doc>
+ <doc type = "scenario">
+ Client opens two channels, requests a ticket on one channel, and then
+ tries to use that ticket in a seconc channel.
+ </doc>
+ </rule>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "ticket" domain = "access-ticket" />
+ </method>
+ </class>
+
+ <!-- == EXCHANGE ========================================================= -->
+
+ <class name = "exchange" handler = "channel" index = "40" label = "work with exchanges">
+ <doc>
+ Exchanges match and distribute messages across queues. Exchanges can be configured in
+ the server or created at runtime.
+ </doc>
+
+ <doc type = "grammar">
+ exchange = C:DECLARE S:DECLARE-OK
+ / C:DELETE S:DELETE-OK
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <rule name = "required-types">
+ <doc>
+ The server MUST implement these standard exchange types: fanout, direct.
+ </doc>
+ <doc type = "scenario">
+ Client attempts to declare an exchange with each of these standard types.
+ </doc>
+ </rule>
+ <rule name = "recommended-types">
+ <doc>
+ The server SHOULD implement these standard exchange types: topic, headers.
+ </doc>
+ <doc type = "scenario">
+ Client attempts to declare an exchange with each of these standard types.
+ </doc>
+ </rule>
+ <rule name = "required-instances">
+ <doc>
+ The server MUST, in each virtual host, pre-declare an exchange instance
+ for each standard exchange type that it implements, where the name of the
+ exchange instance is "amq." followed by the exchange type name.
+ </doc>
+ <doc type = "scenario">
+ Client creates a temporary queue and attempts to bind to each required
+ exchange instance (amq.fanout, amq.direct, and amq.topic, amq.headers if
+ those types are defined).
+ </doc>
+ </rule>
+ <rule name = "default-exchange">
+ <doc>
+ The server MUST predeclare a direct exchange to act as the default exchange
+ for content Publish methods and for default queue bindings.
+ </doc>
+ <doc type = "scenario">
+ Client checks that the default exchange is active by specifying a queue
+ binding with no exchange name, and publishing a message with a suitable
+ routing key but without specifying the exchange name, then ensuring that
+ the message arrives in the queue correctly.
+ </doc>
+ </rule>
+ <rule name = "default-access">
+ <doc>
+ The server MUST NOT allow clients to access the default exchange except
+ by specifying an empty exchange name in the Queue.Bind and content Publish
+ methods.
+ </doc>
+ </rule>
+ <rule name = "extensions">
+ <doc>
+ The server MAY implement other exchange types as wanted.
+ </doc>
+ </rule>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "declare" synchronous = "1" index = "10" label = "declare exchange, create if needed">
+ <doc>
+ This method creates an exchange if it does not already exist, and if the exchange
+ exists, verifies that it is of the correct and expected class.
+ </doc>
+ <rule name = "minimum">
+ <doc>
+ The server SHOULD support a minimum of 16 exchanges per virtual host and
+ ideally, impose no limit except as defined by available resources.
+ </doc>
+ <doc type = "scenario">
+ The client creates as many exchanges as it can until the server reports
+ an error; the number of exchanges successfuly created must be at least
+ sixteen.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "declare-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <doc>
+ When a client defines a new exchange, this belongs to the access realm of the
+ ticket used. All further work done with that exchange must be done with an
+ access ticket for the same realm.
+ </doc>
+ <rule name = "validity" on-failure = "access-refused">
+ <doc>
+ The client MUST provide a valid access ticket giving "active" access to
+ the realm in which the exchange exists or will be created, or "passive"
+ access if the if-exists flag is set.
+ </doc>
+ <doc type = "scenario">
+ Client creates access ticket with wrong access rights and attempts to use
+ in this method.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name">
+ <rule name = "reserved" on-failure = "access-refused">
+ <doc>
+ Exchange names starting with "amq." are reserved for predeclared and
+ standardised exchanges. The client MUST NOT attempt to create an exchange
+ starting with "amq.".
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ <assert check = "regexp" value = "^[a-zA-Z0-9-_.:]+$" />
+ </field>
+
+ <field name = "type" domain = "shortstr" label = "exchange type">
+ <doc>
+ Each exchange belongs to one of a set of exchange types implemented by the
+ server. The exchange types define the functionality of the exchange - i.e. how
+ messages are routed through it. It is not valid or meaningful to attempt to
+ change the type of an existing exchange.
+ </doc>
+ <rule name = "typed" on-failure = "not-allowed">
+ <doc>
+ Exchanges cannot be redeclared with different types. The client MUST not
+ attempt to redeclare an existing exchange with a different type than used
+ in the original Exchange.Declare method.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ <rule name = "support" on-failure = "command-invalid">
+ <doc>
+ The client MUST NOT attempt to create an exchange with a type that the
+ server does not support.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ <assert check = "regexp" value = "^[a-zA-Z0-9-_.:]+$" />
+ </field>
+
+ <field name = "passive" domain = "bit" label = "do not create exchange">
+ <doc>
+ If set, the server will not create the exchange. The client can use this to
+ check whether an exchange exists without modifying the server state.
+ </doc>
+ <rule name = "not-found">
+ <doc>
+ If set, and the exchange does not already exist, the server MUST raise a
+ channel exception with reply code 404 (not found).
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "durable" domain = "bit" label = "request a durable exchange">
+ <doc>
+ If set when creating a new exchange, the exchange will be marked as durable.
+ Durable exchanges remain active when a server restarts. Non-durable exchanges
+ (transient exchanges) are purged if/when a server restarts.
+ </doc>
+ <rule name = "support">
+ <doc>
+ The server MUST support both durable and transient exchanges.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ <rule name = "sticky">
+ <doc>
+ The server MUST ignore the durable field if the exchange already exists.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <!-- TODO 0.82 - clarify how this works; there is no way to cancel a binding
+ except by deleting a queue.
+ -->
+ <field name = "auto-delete" domain = "bit" label = "auto-delete when unused">
+ <doc>
+ If set, the exchange is deleted when all queues have finished using it.
+ </doc>
+ <rule name = "sticky">
+ <doc>
+ The server MUST ignore the auto-delete field if the exchange already
+ exists.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "internal" domain = "bit" label = "create internal exchange">
+ <doc>
+ If set, the exchange may not be used directly by publishers, but only when bound
+ to other exchanges. Internal exchanges are used to construct wiring that is not
+ visible to applications.
+ </doc>
+ </field>
+
+ <field name = "arguments" domain = "table" label = "arguments for declaration">
+ <doc>
+ A set of arguments for the declaration. The syntax and semantics of these
+ arguments depends on the server implementation. This field is ignored if passive
+ is 1.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "declare-ok" synchronous = "1" index = "11" label = "confirm exchange declaration">
+ <doc>
+ This method confirms a Declare method and confirms the name of the exchange,
+ essential for automatically-named exchanges.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "delete" synchronous = "1" index = "20" label = "delete an exchange">
+ <doc>
+ This method deletes an exchange. When an exchange is deleted all queue bindings on
+ the exchange are cancelled.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "delete-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "validity" on-failure = "access-refused">
+ <doc>
+ The client MUST provide a valid access ticket giving "active" access
+ rights to the exchange's access realm.
+ </doc>
+ <doc type = "scenario">
+ Client creates access ticket with wrong access rights and attempts to use
+ in this method.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name">
+ <rule name = "exists" on-failure = "not-found">
+ <doc>
+ The client MUST NOT attempt to delete an exchange that does not exist.
+ </doc>
+ </rule>
+ <assert check = "notnull" />
+ </field>
+
+ <!-- TODO 0.82 - discuss whether this option is useful or not. I don't have
+ any real use case for it. /PH 2006-07-23.
+ -->
+ <field name = "if-unused" domain = "bit" label = "delete only if unused">
+ <doc>
+ If set, the server will only delete the exchange if it has no queue bindings. If
+ the exchange has queue bindings the server does not delete it but raises a
+ channel exception instead.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "delete-ok" synchronous = "1" index = "21"
+ label = "confirm deletion of an exchange">
+ <doc>This method confirms the deletion of an exchange.</doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+ </class>
+
+ <!-- == QUEUE ============================================================ -->
+
+ <class name = "queue" handler = "channel" index = "50" label = "work with queues">
+ <doc>
+ Queues store and forward messages. Queues can be configured in the server or created at
+ runtime. Queues must be attached to at least one exchange in order to receive messages
+ from publishers.
+ </doc>
+
+ <doc type = "grammar">
+ queue = C:DECLARE S:DECLARE-OK
+ / C:BIND S:BIND-OK
+ / C:PURGE S:PURGE-OK
+ / C:DELETE S:DELETE-OK
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <rule name = "any-content">
+ <doc>
+ A server MUST allow any content class to be sent to any queue, in any mix, and
+ queue and deliver these content classes independently. Note that all methods
+ that fetch content off queues are specific to a given content class.
+ </doc>
+ <doc type = "scenario">
+ Client creates an exchange of each standard type and several queues that
+ it binds to each exchange. It must then sucessfully send each of the standard
+ content types to each of the available queues.
+ </doc>
+ </rule>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "declare" synchronous = "1" index = "10" label = "declare queue, create if needed">
+ <doc>
+ This method creates or checks a queue. When creating a new queue the client can
+ specify various properties that control the durability of the queue and its
+ contents, and the level of sharing for the queue.
+ </doc>
+
+ <rule name = "default-binding">
+ <doc>
+ The server MUST create a default binding for a newly-created queue to the
+ default exchange, which is an exchange of type 'direct'.
+ </doc>
+ <doc type = "scenario">
+ Client creates a new queue, and then without explicitly binding it to an
+ exchange, attempts to send a message through the default exchange binding,
+ i.e. publish a message to the empty exchange, with the queue name as routing
+ key.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_35" -->
+ <rule name = "minimum-queues">
+ <doc>
+ The server SHOULD support a minimum of 256 queues per virtual host and ideally,
+ impose no limit except as defined by available resources.
+ </doc>
+ <doc type = "scenario">
+ Client attempts to create as many queues as it can until the server reports
+ an error. The resulting count must at least be 256.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "declare-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <doc>
+ When a client defines a new queue, this belongs to the access realm of the
+ ticket used. All further work done with that queue must be done with an access
+ ticket for the same realm.
+ </doc>
+ <rule name = "validity" on-failure = "access-refused">
+ <doc>
+ The client MUST provide a valid access ticket giving "active" access to
+ the realm in which the queue exists or will be created.
+ </doc>
+ <doc type = "scenario">
+ Client creates access ticket with wrong access rights and attempts to use
+ in this method.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <rule name = "default-name">
+ <doc>
+ The queue name MAY be empty, in which case the server MUST create a new
+ queue with a unique generated name and return this to the client in the
+ Declare-Ok method.
+ </doc>
+ <doc type = "scenario">
+ Client attempts to create several queues with an empty name. The client then
+ verifies that the server-assigned names are unique and different.
+ </doc>
+ </rule>
+ <rule name = "reserved-prefix" on-failure = "not-allowed">
+ <doc>
+ Queue names starting with "amq." are reserved for predeclared and
+ standardised server queues. A client MAY NOT attempt to declare a queue with a
+ name that starts with "amq." and the passive option set to zero.
+ </doc>
+ <doc type = "scenario">
+ A client attempts to create a queue with a name starting with "amq." and with
+ the passive option set to zero.
+ </doc>
+ </rule>
+ <assert check = "regexp" value = "^[a-zA-Z0-9-_.:]*$" />
+ </field>
+
+ <field name = "passive" domain = "bit" label = "do not create queue">
+ <doc>
+ If set, the server will not create the queue. This field allows the client
+ to assert the presence of a queue without modifying the server state.
+ </doc>
+ <rule name = "passive" on-failure = "not-found">
+ <doc>
+ The client MAY ask the server to assert that a queue exists without
+ creating the queue if not. If the queue does not exist, the server
+ treats this as a failure.
+ </doc>
+ <doc type = "scenario">
+ Client declares an existing queue with the passive option and expects
+ the server to respond with a declare-ok. Client then attempts to declare
+ a non-existent queue with the passive option, and the server must close
+ the channel with the correct reply-code.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "durable" domain = "bit" label = "request a durable queue">
+ <doc>
+ If set when creating a new queue, the queue will be marked as durable. Durable
+ queues remain active when a server restarts. Non-durable queues (transient
+ queues) are purged if/when a server restarts. Note that durable queues do not
+ necessarily hold persistent messages, although it does not make sense to send
+ persistent messages to a transient queue.
+ </doc>
+ <!-- Rule test name: was "amq_queue_03" -->
+ <rule name = "persistence">
+ <doc>The server MUST recreate the durable queue after a restart.</doc>
+
+ <!-- TODO: use 'client does something' rather than 'a client does something'. -->
+ <doc type = "scenario">
+ A client creates a durable queue. The server is then restarted. The client
+ then attempts to send a message to the queue. The message should be successfully
+ delivered.
+ </doc>
+ </rule>
+ <!-- Rule test name: was "amq_queue_36" -->
+ <rule name = "types">
+ <doc>The server MUST support both durable and transient queues.</doc>
+ <doc type = "scenario">
+ A client creates two named queues, one durable and one transient.
+ </doc>
+ </rule>
+ <!-- Rule test name: was "amq_queue_37" -->
+ <rule name = "pre-existence">
+ <doc>The server MUST ignore the durable field if the queue already exists.</doc>
+ <doc type = "scenario">
+ A client creates two named queues, one durable and one transient. The client
+ then attempts to declare the two queues using the same names again, but reversing
+ the value of the durable flag in each case. Verify that the queues still exist
+ with the original durable flag values.
+ <!-- TODO: but how? -->
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exclusive" domain = "bit" label = "request an exclusive queue">
+ <doc>
+ Exclusive queues may only be consumed from by the current connection. Setting
+ the 'exclusive' flag always implies 'auto-delete'.
+ </doc>
+
+ <!-- Rule test name: was "amq_queue_38" -->
+ <rule name = "types">
+ <doc>
+ The server MUST support both exclusive (private) and non-exclusive (shared)
+ queues.
+ </doc>
+ <doc type = "scenario">
+ A client creates two named queues, one exclusive and one non-exclusive.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_04" -->
+ <rule name = "02" on-failure = "channel-error">
+ <doc>
+ The client MAY NOT attempt to declare any existing and exclusive queue
+ on multiple connections.
+ </doc>
+ <doc type = "scenario">
+ A client declares an exclusive named queue. A second client on a different
+ connection attempts to declare a queue of the same name.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "auto-delete" domain = "bit" label = "auto-delete queue when unused">
+ <doc>
+ If set, the queue is deleted when all consumers have finished using it. Last
+ consumer can be cancelled either explicitly or because its channel is closed. If
+ there was no consumer ever on the queue, it won't be deleted.
+ </doc>
+
+ <!-- Rule test name: was "amq_queue_31" -->
+ <rule name = "pre-existence">
+ <doc>
+ The server MUST ignore the auto-delete field if the queue already exists.
+ </doc>
+ <doc type = "scenario">
+ A client creates two named queues, one as auto-delete and one explicit-delete.
+ The client then attempts to declare the two queues using the same names again,
+ but reversing the value of the auto-delete field in each case. Verify that the
+ queues still exist with the original auto-delete flag values.
+ <!-- TODO: but how? -->
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "arguments" domain = "table" label = "arguments for declaration">
+ <doc>
+ A set of arguments for the declaration. The syntax and semantics of these
+ arguments depends on the server implementation. This field is ignored if passive
+ is 1.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "declare-ok" synchronous = "1" index = "11" label = "confirms a queue definition">
+ <doc>
+ This method confirms a Declare method and confirms the name of the queue, essential
+ for automatically-named queues.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Reports the name of the queue. If the server generated a queue name, this field
+ contains that name.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+
+ <field name = "message-count" domain = "long" label = "number of messages in queue">
+ <doc>
+ Reports the number of messages in the queue, which will be zero for
+ newly-created queues.
+ </doc>
+ </field>
+
+ <field name = "consumer-count" domain = "long" label = "number of consumers">
+ <doc>
+ Reports the number of active consumers for the queue. Note that consumers can
+ suspend activity (Channel.Flow) in which case they do not appear in this count.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "bind" synchronous = "1" index = "20" label = "bind queue to an exchange">
+ <doc>
+ This method binds a queue to an exchange. Until a queue is bound it will not receive
+ any messages. In a classic messaging model, store-and-forward queues are bound to a
+ dest exchange and subscription queues are bound to a dest_wild exchange.
+ </doc>
+
+ <!-- Rule test name: was "amq_queue_25" -->
+ <rule name = "duplicates">
+ <doc>
+ A server MUST allow ignore duplicate bindings - that is, two or more bind
+ methods for a specific queue, with identical arguments - without treating these
+ as an error.
+ </doc>
+ <doc type = "scenario">
+ A client binds a named queue to an exchange. The client then repeats the bind
+ (with identical arguments).
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_39" -->
+ <rule name = "failure" on-failure = "??????">
+ <!--
+ TODO: Find correct code. The on-failure code returned should depend on why the bind
+ failed. Assuming that failures owing to bad parameters are covered in the rules relating
+ to those parameters, the only remaining reason for a failure would be the lack of
+ server resorces or some internal error - such as too many queues open. Would these
+ cases qualify as "resource error" 506 or "internal error" 541?
+ -->
+ <doc>If a bind fails, the server MUST raise a connection exception.</doc>
+ <doc type = "scenario">
+ TODO
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_12" -->
+ <rule name = "transient-exchange" on-failure = "not-allowed">
+ <doc>
+ The server MUST NOT allow a durable queue to bind to a transient exchange.
+ </doc>
+ <doc type = "scenario">
+ A client creates a transient exchange. The client then declares a named durable
+ queue and then attempts to bind the transient exchange to the durable queue.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_13" -->
+ <rule name = "durable-exchange">
+ <doc>
+ Bindings for durable queues are automatically durable and the server SHOULD
+ restore such bindings after a server restart.
+ </doc>
+ <doc type = "scenario">
+ A server creates a named durable queue and binds it to a durable exchange. The
+ server is restarted. The client then attempts to use the queue/exchange combination.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_17" -->
+ <rule name = "internal-exchange">
+ <doc>
+ If the client attempts to bind to an exchange that was declared as internal, the server
+ MUST raise a connection exception with reply code 530 (not allowed).
+ </doc>
+ <doc type = "scenario">
+ A client attempts to bind a named queue to an internal exchange.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_40" -->
+ <rule name = "binding-count">
+ <doc>
+ The server SHOULD support at least 4 bindings per queue, and ideally, impose no
+ limit except as defined by available resources.
+ </doc>
+ <doc type = "scenario">
+ A client creates a named queue and attempts to bind it to 4 different non-internal
+ exchanges.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "bind-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <doc>
+ The client provides a valid access ticket giving "active" access rights to the
+ queue's access realm.
+ </doc>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to bind. If the queue name is empty, refers to
+ the current queue for the channel, which is the last declared queue.
+ </doc>
+
+ <rule name = "empty-queue" on-failure = "not-allowed">
+ <doc>
+ A client MUST NOT be allowed to bind a non-existent and unnamed queue (i.e.
+ empty queue name) to an exchange.
+ </doc>
+ <doc type = "scenario">
+ A client attempts to bind with an unnamed (empty) queue name to an exchange.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_26" -->
+ <rule name = "queue-existence" on-failure = "not-found">
+ <doc>
+ A client MUST NOT be allowed to bind a non-existent queue (i.e. not previously
+ declared) to an exchange.
+ </doc>
+ <doc type = "scenario">
+ A client attempts to bind an undeclared queue name to an exchange.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name" label = "name of the exchange to bind to">
+ <!-- Rule test name: was "amq_queue_14" -->
+ <rule name = "exchange-existence" on-failure = "not-found">
+ <doc>
+ A client MUST NOT be allowed to bind a queue to a non-existent exchange.
+ </doc>
+ <doc type = "scenario">
+ A client attempts to bind an named queue to a undeclared exchange.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "message routing key">
+ <doc>
+ Specifies the routing key for the binding. The routing key is used for routing
+ messages depending on the exchange configuration. Not all exchanges use a
+ routing key - refer to the specific exchange documentation. If the queue name
+ is empty, the server uses the last queue declared on the channel. If the
+ routing key is also empty, the server uses this queue name for the routing
+ key as well. If the queue name is provided but the routing key is empty, the
+ server does the binding with that empty routing key. The meaning of empty
+ routing keys depends on the exchange implementation.
+ </doc>
+ </field>
+
+ <field name = "arguments" domain = "table" label = "arguments for binding">
+ <doc>
+ A set of arguments for the binding. The syntax and semantics of these arguments
+ depends on the exchange class.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "bind-ok" synchronous = "1" index = "21" label = "confirm bind successful">
+ <doc>This method confirms that the bind was successful.</doc>
+
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "purge" synchronous = "1" index = "30" label = "purge a queue">
+ <doc>
+ This method removes all messages from a queue. It does not cancel consumers. Purged
+ messages are deleted without any formal "undo" mechanism.
+ </doc>
+
+ <!-- Rule test name: was "amq_queue_15" -->
+ <rule name = "01">
+ <doc>A call to purge MUST result in an empty queue.</doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_41" -->
+ <rule name = "02">
+ <doc>
+ On transacted channels the server MUST not purge messages that have already been
+ sent to a client but not yet acknowledged.
+ </doc>
+ </rule>
+
+ <!-- TODO: Rule split? -->
+
+ <!-- Rule test name: was "amq_queue_42" -->
+ <rule name = "03">
+ <doc>
+ The server MAY implement a purge queue or log that allows system administrators
+ to recover accidentally-purged messages. The server SHOULD NOT keep purged
+ messages in the same storage spaces as the live messages since the volumes of
+ purged messages may get very large.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "purge-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <doc>The access ticket must be for the access realm that holds the queue.</doc>
+
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "read" access rights to
+ the queue's access realm. Note that purging a queue is equivalent to reading
+ all messages and discarding them.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to purge. If the queue name is empty, refers to
+ the current queue for the channel, which is the last declared queue.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ If the client did not previously declare a queue, and the queue name in this
+ method is empty, the server MUST raise a connection exception with reply
+ code 530 (not allowed).
+ </doc>
+ </rule>
+
+ <!-- TODO Rule split? -->
+
+ <!-- Rule test name: was "amq_queue_16" -->
+ <rule name = "02">
+ <doc>
+ The queue MUST exist. Attempting to purge a non-existing queue MUST cause a
+ channel exception.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "purge-ok" synchronous = "1" index = "31" label = "confirms a queue purge">
+ <doc>This method confirms the purge of a queue.</doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "message-count" domain = "long" label = "number of messages purged">
+ <doc>Reports the number of messages purged.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "delete" synchronous = "1" index = "40" label = "delete a queue">
+ <doc>
+ This method deletes a queue. When a queue is deleted any pending messages are sent
+ to a dead-letter queue if this is defined in the server configuration, and all
+ consumers on the queue are cancelled.
+ </doc>
+
+ <!-- TODO: Rule split? -->
+
+ <!-- Rule test name: was "amq_queue_43" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD use a dead-letter queue to hold messages that were pending on
+ a deleted queue, and MAY provide facilities for a system administrator to move
+ these messages back to an active queue.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "delete-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <doc>
+ The client provides a valid access ticket giving "active" access rights to the
+ queue's access realm.
+ </doc>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to delete. If the queue name is empty, refers to
+ the current queue for the channel, which is the last declared queue.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ If the client did not previously declare a queue, and the queue name in this
+ method is empty, the server MUST raise a connection exception with reply
+ code 530 (not allowed).
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_21" -->
+ <rule name = "02">
+ <doc>
+ The queue must exist. If the client attempts to delete a non-existing queue
+ the server MUST raise a channel exception with reply code 404 (not found).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "if-unused" domain = "bit" label = "delete only if unused">
+ <doc>
+ If set, the server will only delete the queue if it has no consumers. If the
+ queue has consumers the server does does not delete it but raises a channel
+ exception instead.
+ </doc>
+
+ <!-- Rule test name: was "amq_queue_29" and "amq_queue_30" -->
+ <rule name = "01">
+ <doc>The server MUST respect the if-unused flag when deleting a queue.</doc>
+ </rule>
+ </field>
+
+ <field name = "if-empty" domain = "bit" label = "delete only if empty">
+ <doc>
+ If set, the server will only delete the queue if it has no messages.
+ </doc>
+ <rule name = "01">
+ <doc>
+ If the queue is not empty the server MUST raise a channel exception with
+ reply code 406 (precondition failed).
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "delete-ok" synchronous = "1" index = "41" label = "confirm deletion of a queue">
+ <doc>This method confirms the deletion of a queue.</doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "message-count" domain = "long" label = "number of messages purged">
+ <doc>Reports the number of messages purged.</doc>
+ </field>
+ </method>
+ </class>
+
+ <!-- == BASIC ============================================================ -->
+
+ <class name = "basic" handler = "channel" index = "60" label = "work with basic content">
+ <doc>
+ The Basic class provides methods that support an industry-standard messaging model.
+ </doc>
+
+ <doc type = "grammar">
+ basic = C:QOS S:QOS-OK
+ / C:CONSUME S:CONSUME-OK
+ / C:CANCEL S:CANCEL-OK
+ / C:PUBLISH content
+ / S:RETURN content
+ / S:DELIVER content
+ / C:GET ( S:GET-OK content / S:GET-EMPTY )
+ / C:ACK
+ / C:REJECT
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MAY" />
+
+ <!-- Rule test name: was "amq_basic_08" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD respect the persistent property of basic messages and
+ SHOULD make a best-effort to hold persistent basic messages on a reliable
+ storage mechanism.
+ </doc>
+ <doc type = "scenario">
+ Send a persistent message to queue, stop server, restart server and then
+ verify whether message is still present. Assumes that queues are durable.
+ Persistence without durable queues makes no sense.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_09" -->
+ <rule name = "02">
+ <doc>
+ The server MUST NOT discard a persistent basic message in case of a queue
+ overflow.
+ </doc>
+ <doc type = "scenario">
+ Create a queue overflow situation with persistent messages and verify that
+ messages do not get lost (presumably the server will write them to disk).
+ </doc>
+ </rule>
+
+ <rule name = "03">
+ <doc>
+ The server MAY use the Channel.Flow method to slow or stop a basic message
+ publisher when necessary.
+ </doc>
+ <doc type = "scenario">
+ Create a queue overflow situation with non-persistent messages and verify
+ whether the server responds with Channel.Flow or not. Repeat with persistent
+ messages.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_10" -->
+ <rule name = "04">
+ <doc>
+ The server MAY overflow non-persistent basic messages to persistent
+ storage.
+ </doc>
+ <!-- Test scenario: untestable -->
+ </rule>
+
+ <rule name = "05">
+ <doc>
+ The server MAY discard or dead-letter non-persistent basic messages on a
+ priority basis if the queue size exceeds some configured limit.
+ </doc>
+ <!-- Test scenario: untestable -->
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_11" -->
+ <rule name = "06">
+ <doc>
+ The server MUST implement at least 2 priority levels for basic messages,
+ where priorities 0-4 and 5-9 are treated as two distinct levels.
+ </doc>
+ <doc type = "scenario">
+ Send a number of priority 0 messages to a queue. Send one priority 9
+ message. Consume messages from the queue and verify that the first message
+ received was priority 9.
+ </doc>
+ </rule>
+
+ <rule name = "07">
+ <doc>
+ The server MAY implement up to 10 priority levels.
+ </doc>
+ <doc type = "scenario">
+ Send a number of messages with mixed priorities to a queue, so that all
+ priority values from 0 to 9 are exercised. A good scenario would be ten
+ messages in low-to-high priority. Consume from queue and verify how many
+ priority levels emerge.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_12" -->
+ <rule name = "08">
+ <doc>
+ The server MUST deliver messages of the same priority in order irrespective of
+ their individual persistence.
+ </doc>
+ <doc type = "scenario">
+ Send a set of messages with the same priority but different persistence
+ settings to a queue. Consume and verify that messages arrive in same order
+ as originally published.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_13" -->
+ <rule name = "09">
+ <doc>
+ The server MUST support automatic acknowledgements on Basic content, i.e.
+ consumers with the no-ack field set to FALSE.
+ </doc>
+ <doc type = "scenario">
+ Create a queue and a consumer using automatic acknowledgements. Publish
+ a set of messages to the queue. Consume the messages and verify that all
+ messages are received.
+ </doc>
+ </rule>
+
+ <rule name = "10">
+ <doc>
+ The server MUST support explicit acknowledgements on Basic content, i.e.
+ consumers with the no-ack field set to TRUE.
+ </doc>
+ <doc type = "scenario">
+ Create a queue and a consumer using explicit acknowledgements. Publish a
+ set of messages to the queue. Consume the messages but acknowledge only
+ half of them. Disconnect and reconnect, and consume from the queue.
+ Verify that the remaining messages are received.
+ </doc>
+ </rule>
+
+ <!-- These are the properties for a Basic content -->
+
+ <field name = "content-type" domain = "shortstr" label = "MIME content type" />
+ <field name = "content-encoding" domain = "shortstr" label = "MIME content encoding" />
+ <field name = "headers" domain = "table" label = "message header field table" />
+ <field name = "delivery-mode" domain = "octet" label = "non-persistent (1) or persistent (2)" />
+ <field name = "priority" domain = "short" label = "message priority, 0 to 9" />
+ <field name = "correlation-id" domain = "shortstr" label = "application correlation identifier" />
+ <field name = "reply-to" domain = "shortstr" label = "destination to reply to" />
+ <field name = "expiration" domain = "shortstr" label = "message expiration specification" />
+ <field name = "timestamp" domain = "timestamp" label = "message timestamp" />
+ <field name = "message-id" domain = "shortstr" label = "application message identifier" />
+ <field name = "type" domain = "shortstr" label = "message type name" />
+ <field name = "user-id" domain = "shortstr" label = "creating user id" />
+ <field name = "app-id" domain = "shortstr" label = "creating application id" />
+ <!-- This field is deprecated pending review -->
+ <field name = "cluster-id" domain = "shortstr" label = "intra-cluster routing identifier" />
+
+ <!-- Type diversity test -->
+ <field name = "property-bit" domain = "bit" label = "Extra property for testing only" />
+ <field name = "property-octet" domain = "octet" label = "Extra property for testing only" />
+ <field name = "property-short" domain = "short" label = "Extra property for testing only" />
+ <field name = "property-long" domain = "long" label = "Extra property for testing only" />
+ <field name = "property-longlong" domain = "longlong" label = "Extra property for testing only" />
+ <field name = "property-shortstr" domain = "shortstr" label = "Extra property for testing only" />
+ <field name = "property-longstr" domain = "longstr" label = "Extra property for testing only" />
+ <field name = "property-timestamp" domain = "timestamp" label = "Extra property for testing only" />
+ <field name = "property-table" domain = "table" label = "Extra property for testing only" />
+ <field name = "property-access-ticket" domain = "access-ticket" label = "Extra property for testing only" />
+ <field name = "property-class-id" domain = "class-id" label = "Extra property for testing only" />
+ <field name = "property-consumer-tag" domain = "consumer-tag" label = "Extra property for testing only" />
+ <field name = "property-delivery-tag" domain = "delivery-tag" label = "Extra property for testing only" />
+ <field name = "property-exchange-name" domain = "exchange-name" label = "Extra property for testing only" />
+ <field name = "property-known-hosts" domain = "known-hosts" label = "Extra property for testing only" />
+ <field name = "property-method-id" domain = "method-id" label = "Extra property for testing only" />
+ <field name = "property-no-ack" domain = "no-ack" label = "Extra property for testing only" />
+ <field name = "property-no-local" domain = "no-local" label = "Extra property for testing only" />
+ <field name = "property-path" domain = "path" label = "Extra property for testing only" />
+ <field name = "property-peer-properties" domain = "peer-properties" label = "Extra property for testing only" />
+ <field name = "property-queue-name" domain = "queue-name" label = "Extra property for testing only" />
+ <field name = "property-redelivered" domain = "redelivered" label = "Extra property for testing only" />
+ <field name = "property-reply-code" domain = "reply-code" label = "Extra property for testing only" />
+ <field name = "property-reply-text" domain = "reply-text" label = "Extra property for testing only" />
+
+ <!-- Bit field test -->
+ <field name = "property-long-A" domain = "long" label = "Extra property for testing only" />
+ <field name = "property-bit-B" domain = "bit" label = "Extra property for testing only" />
+ <field name = "property-bit-C" domain = "bit" label = "Extra property for testing only" />
+ <field name = "property-bit-D" domain = "bit" label = "Extra property for testing only" />
+ <field name = "property-bit-E" domain = "bit" label = "Extra property for testing only" />
+ <field name = "property-bit-F" domain = "bit" label = "Extra property for testing only" />
+ <field name = "property-shortstr-G" domain = "shortstr" label = "Extra property for testing only" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "qos" synchronous = "1" index = "10" label = "specify quality of service">
+ <doc>
+ This method requests a specific quality of service. The QoS can be specified for the
+ current channel or for all channels on the connection. The particular properties and
+ semantics of a qos method always depend on the content class semantics. Though the
+ qos method could in principle apply to both peers, it is currently meaningful only
+ for the server.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "qos-ok" />
+
+ <field name = "prefetch-size" domain = "long" label = "prefetch window in octets">
+ <doc>
+ The client can request that messages be sent in advance so that when the client
+ finishes processing a message, the following message is already held locally,
+ rather than needing to be sent down the channel. Prefetching gives a performance
+ improvement. This field specifies the prefetch window size in octets. The server
+ will send a message in advance if it is equal to or smaller in size than the
+ available prefetch size (and also falls into other prefetch limits). May be set
+ to zero, meaning "no specific limit", although other prefetch limits may still
+ apply. The prefetch-size is ignored if the no-ack option is set.
+ </doc>
+ <!-- Rule test name: was "amq_basic_17" -->
+ <rule name = "01">
+ <doc>
+ The server MUST ignore this setting when the client is not processing any
+ messages - i.e. the prefetch size does not limit the transfer of single
+ messages to a client, only the sending in advance of more messages while
+ the client still has one or more unacknowledged messages.
+ </doc>
+ <doc type = "scenario">
+ Define a QoS prefetch-size limit and send a single message that exceeds
+ that limit. Verify that the message arrives correctly.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "prefetch-count" domain = "short" label = "prefetch window in messages">
+ <doc>
+ Specifies a prefetch window in terms of whole messages. This field may be used
+ in combination with the prefetch-size field; a message will only be sent in
+ advance if both prefetch windows (and those at the channel and connection level)
+ allow it. The prefetch-count is ignored if the no-ack option is set.
+ </doc>
+ <!-- Rule test name: was "amq_basic_18" -->
+ <rule name = "01">
+ <doc>
+ The server may send less data in advance than allowed by the client's
+ specified prefetch windows but it MUST NOT send more.
+ </doc>
+ <doc type = "scenario">
+ Define a QoS prefetch-size limit and a prefetch-count limit greater than
+ one. Send multiple messages that exceed the prefetch size. Verify that
+ no more than one message arrives at once.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "global" domain = "bit" label = "apply to entire connection">
+ <doc>
+ By default the QoS settings apply to the current channel only. If this field is
+ set, they are applied to the entire connection.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "qos-ok" synchronous = "1" index = "11" label = "confirm the requested qos">
+ <doc>
+ This method tells the client that the requested QoS levels could be handled by the
+ server. The requested QoS applies to all active consumers until a new QoS is
+ defined.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "consume" synchronous = "1" index = "20" label = "start a queue consumer">
+ <doc>
+ This method asks the server to start a "consumer", which is a transient request for
+ messages from a specific queue. Consumers last as long as the channel they were
+ created on, or until the client cancels them.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_01" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD support at least 16 consumers per queue, and ideally, impose
+ no limit except as defined by available resources.
+ </doc>
+ <doc type = "scenario">
+ Create a queue and create consumers on that queue until the server closes the
+ connection. Verify that the number of consumers created was at least sixteen
+ and report the total number.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "consume-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01" on-failure = "access-refused">
+ <doc>
+ The client MUST provide a valid access ticket giving "read" access rights to
+ the realm for the queue.
+ </doc>
+ <doc type = "scenario">
+ Attempt to create a consumer with an invalid (non-zero) access ticket.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to consume from. If the queue name is null,
+ refers to the current queue for the channel, which is the last declared queue.
+ </doc>
+ <rule name = "01" on-failure = "not-allowed">
+ <doc>
+ If the queue name is empty the client MUST have previously declared a
+ queue using this channel.
+ </doc>
+ <doc type = "scenario">
+ Attempt to create a consumer with an empty queue name and no previously
+ declared queue on the channel.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>
+ Specifies the identifier for the consumer. The consumer tag is local to a
+ connection, so two clients can use the same consumer tags. If this field is
+ empty the server will generate a unique tag.
+ </doc>
+ <rule name = "01" on-failure = "not-allowed">
+ <doc>
+ The client MUST NOT specify a tag that refers to an existing consumer.
+ </doc>
+ <doc type = "scenario">
+ Attempt to create two consumers with the same non-empty tag.
+ </doc>
+ </rule>
+ <rule name = "02" on-failure = "not-allowed">
+ <doc>
+ The consumer tag is valid only within the channel from which the
+ consumer was created. I.e. a client MUST NOT create a consumer in one
+ channel and then use it in another.
+ </doc>
+ <doc type = "scenario">
+ Attempt to create a consumer in one channel, then use in another channel,
+ in which consumers have also been created (to test that the server uses
+ unique consumer tags).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "no-local" domain = "no-local" />
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise
+ a channel or connection exception.
+ </doc>
+ </field>
+
+ <field name = "bit-test-1" domain = "bit" />
+ <field name = "bit-test-2" domain = "bit" />
+ <field name = "bit-test-3" domain = "bit" />
+ <field name = "bit-test-4" domain = "bit" />
+ <field name = "bit-test-5" domain = "bit" />
+ <field name = "bit-test-6" domain = "bit" />
+ <field name = "bit-test-7" domain = "bit" />
+ <field name = "bit-test-8" domain = "bit" />
+ <field name = "bit-test-9" domain = "bit" />
+
+ <field name = "no-ack" domain = "short" />
+
+ <field name = "exclusive" domain = "bit" label = "request exclusive access">
+ <doc>
+ Request exclusive consumer access, meaning only this consumer can access the
+ queue.
+ </doc>
+ <!-- Rule test name: was "amq_basic_02" -->
+ <rule name = "01" on-failure = "access-refused">
+ <doc>
+ The client MAY NOT gain exclusive access to a queue that already has
+ active consumers.
+ </doc>
+ <doc type = "scenario">
+ Open two connections to a server, and in one connection create a shared
+ (non-exclusive) queue and then consume from the queue. In the second
+ connection attempt to consume from the same queue using the exclusive
+ option.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "priority" domain = "short" label = "consume priority"/>
+ </method>
+
+ <method name = "consume-ok" synchronous = "1" index = "21" label = "confirm a new consumer">
+ <doc>
+ The server provides the client with a consumer tag, which is used by the client
+ for methods called on the consumer at a later stage.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>
+ Holds the consumer tag specified by the client or provided by the server.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "cancel" synchronous = "1" index = "30" label = "end a queue consumer">
+ <doc>
+ This method cancels a consumer. This does not affect already delivered
+ messages, but it does mean the server will not send any more messages for
+ that consumer. The client may receive an abitrary number of messages in
+ between sending the cancel method and receiving the cancel-ok reply.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ If the queue does not exist the server MUST ignore the cancel method, so
+ long as the consumer tag is valid for that channel.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "cancel-ok" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+ </method>
+
+ <method name = "cancel-ok" synchronous = "1" index = "31" label = "confirm a cancelled consumer">
+ <doc>
+ This method confirms that the cancellation was completed.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "consumer-tag" domain = "consumer-tag" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "publish" content = "1" index = "40" label = "publish a message">
+ <doc>
+ This method publishes a message to a specific exchange. The message will be routed
+ to queues as defined by the exchange configuration and distributed to any active
+ consumers when the transaction, if any, is committed.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "write" access rights
+ to the access realm for the exchange.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange to publish to. The exchange name can be
+ empty, meaning the default exchange. If the exchange name is specified, and that
+ exchange does not exist, the server will raise a channel exception.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_06" -->
+ <rule name = "01">
+ <doc>
+ The server MUST accept a blank exchange name to mean the default exchange.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_14" -->
+ <rule name = "02">
+ <doc>
+ If the exchange was declared as an internal exchange, the server MUST raise
+ a channel exception with a reply code 403 (access refused).
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_15" -->
+ <rule name = "03">
+ <doc>
+ The exchange MAY refuse basic content in which case it MUST raise a channel
+ exception with reply code 540 (not implemented).
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>
+ Specifies the routing key for the message. The routing key is used for routing
+ messages depending on the exchange configuration.
+ </doc>
+ </field>
+
+ <field name = "mandatory" domain = "bit" label = "indicate mandatory routing">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue. If this flag is set, the server will return an unroutable message with a
+ Return method. If this flag is zero, the server silently drops the message.
+ </doc>
+ <!-- Rule test name: was "amq_basic_07" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD implement the mandatory flag.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "immediate" domain = "bit" label = "request immediate delivery">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue consumer immediately. If this flag is set, the server will return an
+ undeliverable message with a Return method. If this flag is zero, the server
+ will queue the message, but with no guarantee that it will ever be consumed.
+ </doc>
+ <!-- Rule test name: was "amq_basic_16" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD implement the immediate flag.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "return" content = "1" index = "50" label = "return a failed message">
+ <doc>
+ This method returns an undeliverable message that was published with the "immediate"
+ flag set, or an unroutable message published with the "mandatory" flag set. The
+ reply code and text provide information about the reason that the message was
+ undeliverable.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "reply-code" domain = "reply-code" />
+
+ <field name = "reply-text" domain = "reply-text" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>
+ Specifies the routing key name specified when the message was published.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "deliver" content = "1" index = "60"
+ label = "notify the client of a consumer message">
+ <doc>
+ This method delivers a message to the client, via a consumer. In the asynchronous
+ message delivery model, the client starts a consumer using the Consume method, then
+ the server responds with Deliver methods as and when messages arrive for that
+ consumer.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_19" -->
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server SHOULD track the number of times a message has been delivered to
+ clients and when a message is redelivered a certain number of times - e.g. 5
+ times - without being acknowledged, the server SHOULD consider the message to be
+ unprocessable (possibly causing client applications to abort), and move the
+ message to a dead letter queue.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "redelivered" domain = "redelivered" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>Specifies the routing key name specified when the message was published.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "get" synchronous = "1" index = "70" label = "direct access to a queue">
+ <doc>
+ This method provides a direct access to the messages in a queue using a synchronous
+ dialogue that is designed for specific types of application where synchronous
+ functionality is more important than performance.
+ </doc>
+
+ <response name = "get-ok" />
+ <response name = "get-empty" />
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "read" access rights to
+ the realm for the queue.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to consume from. If the queue name is null,
+ refers to the current queue for the channel, which is the last declared queue.
+ </doc>
+ <rule name = "01">
+ <doc>
+ If the client did not previously declare a queue, and the queue name in this
+ method is empty, the server MUST raise a connection exception with reply
+ code 530 (not allowed).
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "no-ack" domain = "no-ack" />
+ </method>
+
+ <method name = "get-ok" synchronous = "1" content = "1" index = "71"
+ label = "provide client with a message">
+ <doc>
+ This method delivers a message to the client following a get method. A message
+ delivered by 'get-ok' must be acknowledged unless the no-ack option was set in the
+ get method.
+ </doc>
+
+ <chassis name = "client" implement = "MAY" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "redelivered" domain = "redelivered" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ If empty, the message was published to the default exchange.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>Specifies the routing key name specified when the message was published.</doc>
+ </field>
+
+ <field name = "message-count" domain = "long" label = "number of messages pending">
+ <doc>
+ This field reports the number of messages pending on the queue, excluding the
+ message being delivered. Note that this figure is indicative, not reliable, and
+ can change arbitrarily as messages are added to the queue and removed by other
+ clients.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "get-empty" synchronous = "1" index = "72"
+ label = "indicate no messages available">
+ <doc>
+ This method tells the client that the queue has no messages available for the
+ client.
+ </doc>
+
+ <chassis name = "client" implement = "MAY" />
+
+ <!-- This field is deprecated pending review -->
+ <field name = "cluster-id" domain = "shortstr" label = "Cluster id">
+ <doc>
+ For use by cluster applications, should not be used by client applications.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "ack" index = "80" label = "acknowledge one or more messages">
+ <doc>
+ This method acknowledges one or more messages delivered via the Deliver or Get-Ok
+ methods. The client can ask to confirm a single message or a set of messages up to
+ and including a specific message.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "multiple" domain = "bit" label = "acknowledge multiple messages">
+ <doc>
+ If set to 1, the delivery tag is treated as "up to and including", so that the
+ client can acknowledge multiple messages with a single method. If set to zero,
+ the delivery tag refers to a single message. If the multiple field is 1, and the
+ delivery tag is zero, tells the server to acknowledge all outstanding mesages.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_20" -->
+ <rule name = "01">
+ <doc>
+ The server MUST validate that a non-zero delivery-tag refers to an delivered
+ message, and raise a channel exception if this is not the case.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "reject" index = "90" label = "reject an incoming message">
+ <doc>
+ This method allows a client to reject a message. It can be used to interrupt and
+ cancel large incoming messages, or return untreatable messages to their original
+ queue.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_21" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD be capable of accepting and process the Reject method while
+ sending message content with a Deliver or Get-Ok method. I.e. the server should
+ read and process incoming methods while sending output frames. To cancel a
+ partially-send content, the server sends a content body frame of size 1 (i.e.
+ with no data except the frame-end octet).
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_22" -->
+ <rule name = "02">
+ <doc>
+ The server SHOULD interpret this method as meaning that the client is unable to
+ process the message at this time.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <rule name = "03">
+ <!-- TODO: Rule split? -->
+ <doc>
+ A client MUST NOT use this method as a means of selecting messages to process. A
+ rejected message MAY be discarded or dead-lettered, not necessarily passed to
+ another client.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "requeue" domain = "bit" label = "requeue the message">
+ <doc>
+ If this field is zero, the message will be discarded. If this bit is 1, the
+ server will attempt to requeue the message.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_23" -->
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server MUST NOT deliver the message to the same client within the
+ context of the current channel. The recommended strategy is to attempt to
+ deliver the message to an alternative consumer, and if that is not possible,
+ to move the message to a dead-letter queue. The server MAY use more
+ sophisticated tracking to hold the message on the queue and redeliver it to
+ the same client at a later stage.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "recover" index = "100" label = "redeliver unacknowledged messages">
+ <doc>
+ This method asks the broker to redeliver all unacknowledged messages on a specified
+ channel. Zero or more messages may be redelivered. This method is only allowed on
+ non-transacted channels.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server MUST set the redelivered flag on all messages that are resent.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <doc>
+ The server MUST raise a channel exception if this is called on a transacted
+ channel.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "requeue" domain = "bit" label = "requeue the message">
+ <doc>
+ If this field is zero, the message will be redelivered to the original
+ recipient. If this bit is 1, the server will attempt to requeue the message,
+ potentially then delivering it to an alternative subscriber.
+ </doc>
+ </field>
+ </method>
+ </class>
+
+ <!-- == FILE ============================================================= -->
+
+ <class name = "file" handler = "channel" index = "70" label = "work with file content">
+ <doc>
+ The file class provides methods that support reliable file transfer. File
+ messages have a specific set of properties that are required for interoperability
+ with file transfer applications. File messages and acknowledgements are subject to
+ channel transactions. Note that the file class does not provide message browsing
+ methods; these are not compatible with the staging model. Applications that need
+ browsable file transfer should use Basic content and the Basic class.
+ </doc>
+
+ <doc type = "grammar">
+ file = C:QOS S:QOS-OK
+ / C:CONSUME S:CONSUME-OK
+ / C:CANCEL S:CANCEL-OK
+ / C:OPEN S:OPEN-OK C:STAGE content
+ / S:OPEN C:OPEN-OK S:STAGE content
+ / C:PUBLISH
+ / S:DELIVER
+ / S:RETURN
+ / C:ACK
+ / C:REJECT
+ </doc>
+
+ <chassis name = "server" implement = "MAY" />
+ <chassis name = "client" implement = "MAY" />
+
+ <rule name = "01">
+ <doc>
+ The server MUST make a best-effort to hold file messages on a reliable storage
+ mechanism.
+ </doc>
+ </rule>
+
+ <!-- TODO Rule implement attr inverse? -->
+
+ <!-- TODO: Rule split? -->
+
+ <rule name = "02">
+ <doc>
+ The server MUST NOT discard a file message in case of a queue overflow. The server
+ MUST use the Channel.Flow method to slow or stop a file message publisher when
+ necessary.
+ </doc>
+ </rule>
+
+ <!-- TODO: Rule split? -->
+
+ <rule name = "03">
+ <doc>
+ The server MUST implement at least 2 priority levels for file messages, where
+ priorities 0-4 and 5-9 are treated as two distinct levels. The server MAY implement
+ up to 10 priority levels.
+ </doc>
+ </rule>
+
+ <rule name = "04">
+ <doc>
+ The server MUST support both automatic and explicit acknowledgements on file
+ content.
+ </doc>
+ </rule>
+
+ <!-- These are the properties for a File content -->
+
+ <field name = "content-type" domain = "shortstr" label = "MIME content type" />
+ <field name = "content-encoding" domain = "shortstr" label = "MIME content encoding" />
+ <field name = "headers" domain = "table" label = "message header field table" />
+ <field name = "priority" domain = "octet" label = "message priority, 0 to 9" />
+ <field name = "reply-to" domain = "shortstr" label = "destination to reply to" />
+ <field name = "message-id" domain = "shortstr" label = "application message identifier" />
+ <field name = "filename" domain = "shortstr" label = "message filename" />
+ <field name = "timestamp" domain = "timestamp" label = "message timestamp" />
+ <!-- This field is deprecated pending review -->
+ <field name = "cluster-id" domain = "shortstr" label = "intra-cluster routing identifier" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "qos" synchronous = "1" index = "10" label = "specify quality of service">
+ <doc>
+ This method requests a specific quality of service. The QoS can be specified for the
+ current channel or for all channels on the connection. The particular properties and
+ semantics of a qos method always depend on the content class semantics. Though the
+ qos method could in principle apply to both peers, it is currently meaningful only
+ for the server.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "qos-ok" />
+
+ <field name = "prefetch-size" domain = "long" label = "prefetch window in octets">
+ <doc>
+ The client can request that messages be sent in advance so that when the client
+ finishes processing a message, the following message is already held locally,
+ rather than needing to be sent down the channel. Prefetching gives a performance
+ improvement. This field specifies the prefetch window size in octets. May be set
+ to zero, meaning "no specific limit". Note that other prefetch limits may still
+ apply. The prefetch-size is ignored if the no-ack option is set.
+ </doc>
+ </field>
+
+ <field name = "prefetch-count" domain = "short" label = "prefetch window in messages">
+ <doc>
+ Specifies a prefetch window in terms of whole messages. This is compatible with
+ some file API implementations. This field may be used in combination with the
+ prefetch-size field; a message will only be sent in advance if both prefetch
+ windows (and those at the channel and connection level) allow it. The
+ prefetch-count is ignored if the no-ack option is set.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server MAY send less data in advance than allowed by the client's
+ specified prefetch windows but it MUST NOT send more.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "global" domain = "bit" label = "apply to entire connection">
+ <doc>
+ By default the QoS settings apply to the current channel only. If this field is
+ set, they are applied to the entire connection.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "qos-ok" synchronous = "1" index = "11" label = "confirm the requested qos">
+ <doc>
+ This method tells the client that the requested QoS levels could be handled by the
+ server. The requested QoS applies to all active consumers until a new QoS is
+ defined.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "consume" synchronous = "1" index = "20" label = "start a queue consumer">
+ <doc>
+ This method asks the server to start a "consumer", which is a transient request for
+ messages from a specific queue. Consumers last as long as the channel they were
+ created on, or until the client cancels them.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server SHOULD support at least 16 consumers per queue, unless the queue was
+ declared as private, and ideally, impose no limit except as defined by available
+ resources.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "consume-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "read" access rights to
+ the realm for the queue.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to consume from. If the queue name is null,
+ refers to the current queue for the channel, which is the last declared queue.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ If the client did not previously declare a queue, and the queue name in this
+ method is empty, the server MUST raise a connection exception with reply
+ code 530 (not allowed).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>
+ Specifies the identifier for the consumer. The consumer tag is local to a
+ connection, so two clients can use the same consumer tags. If this field is
+ empty the server will generate a unique tag.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The tag MUST NOT refer to an existing consumer. If the client attempts to
+ create two consumers with the same non-empty tag the server MUST raise a
+ connection exception with reply code 530 (not allowed).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "no-local" domain = "no-local" />
+
+ <field name = "no-ack" domain = "no-ack" />
+
+ <field name = "exclusive" domain = "bit" label = "request exclusive access">
+ <doc>
+ Request exclusive consumer access, meaning only this consumer can access the
+ queue.
+ </doc>
+
+ <!-- Rule test name: was "amq_file_00" -->
+ <rule name = "01">
+ <doc>
+ If the server cannot grant exclusive access to the queue when asked, -
+ because there are other consumers active - it MUST raise a channel exception
+ with return code 405 (resource locked).
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "consume-ok" synchronous = "1" index = "21" label = "confirm a new consumer">
+ <doc>
+ This method provides the client with a consumer tag which it MUST use in methods
+ that work with the consumer.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>Holds the consumer tag specified by the client or provided by the server.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "cancel" synchronous = "1" index = "30" label = "end a queue consumer">
+ <doc>
+ This method cancels a consumer. This does not affect already delivered messages, but
+ it does mean the server will not send any more messages for that consumer.
+ </doc>
+
+ <response name = "cancel-ok" />
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+ </method>
+
+ <method name = "cancel-ok" synchronous = "1" index = "31" label = "confirm a cancelled consumer">
+ <doc>This method confirms that the cancellation was completed.</doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "open" synchronous = "1" index = "40" label = "request to start staging">
+ <doc>
+ This method requests permission to start staging a message. Staging means sending
+ the message into a temporary area at the recipient end and then delivering the
+ message by referring to this temporary area. Staging is how the protocol handles
+ partial file transfers - if a message is partially staged and the connection breaks,
+ the next time the sender starts to stage it, it can restart from where it left off.
+ </doc>
+
+ <response name = "open-ok" />
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "identifier" domain = "shortstr" label = "staging identifier">
+ <doc>
+ This is the staging identifier. This is an arbitrary string chosen by the
+ sender. For staging to work correctly the sender must use the same staging
+ identifier when staging the same message a second time after recovery from a
+ failure. A good choice for the staging identifier would be the SHA1 hash of the
+ message properties data (including the original filename, revised time, etc.).
+ </doc>
+ </field>
+
+ <field name = "content-size" domain = "longlong" label = "message content size">
+ <doc>
+ The size of the content in octets. The recipient may use this information to
+ allocate or check available space in advance, to avoid "disk full" errors during
+ staging of very large messages.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The sender MUST accurately fill the content-size field. Zero-length content
+ is permitted.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "open-ok" synchronous = "1" index = "41" label = "confirm staging ready">
+ <doc>
+ This method confirms that the recipient is ready to accept staged data. If the
+ message was already partially-staged at a previous time the recipient will report
+ the number of octets already staged.
+ </doc>
+
+ <response name = "stage" />
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "staged-size" domain = "longlong" label = "already staged amount">
+ <doc>
+ The amount of previously-staged content in octets. For a new message this will
+ be zero.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The sender MUST start sending data from this octet offset in the message,
+ counting from zero.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The recipient MAY decide how long to hold partially-staged content and MAY
+ implement staging by always discarding partially-staged content. However if
+ it uses the file content type it MUST support the staging methods.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "stage" content = "1" index = "50" label = "stage message content">
+ <doc>
+ This method stages the message, sending the message content to the recipient from
+ the octet offset specified in the Open-Ok method.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "publish" index = "60" label = "publish a message">
+ <doc>
+ This method publishes a staged file message to a specific exchange. The file message
+ will be routed to queues as defined by the exchange configuration and distributed to
+ any active consumers when the transaction, if any, is committed.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "write" access rights
+ to the access realm for the exchange.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange to publish to. The exchange name can be
+ empty, meaning the default exchange. If the exchange name is specified, and that
+ exchange does not exist, the server will raise a channel exception.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server MUST accept a blank exchange name to mean the default exchange.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <doc>
+ If the exchange was declared as an internal exchange, the server MUST
+ respond with a reply code 403 (access refused) and raise a channel
+ exception.
+ </doc>
+ </rule>
+
+ <!-- TODO: Rule split? -->
+
+ <rule name = "03">
+ <doc>
+ The exchange MAY refuse file content in which case it MUST respond with a
+ reply code 540 (not implemented) and raise a channel exception.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>
+ Specifies the routing key for the message. The routing key is used for routing
+ messages depending on the exchange configuration.
+ </doc>
+ </field>
+
+ <field name = "mandatory" domain = "bit" label = "indicate mandatory routing">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue. If this flag is set, the server will return an unroutable message with a
+ Return method. If this flag is zero, the server silently drops the message.
+ </doc>
+
+ <!-- Rule test name: was "amq_file_00" -->
+ <rule name = "01">
+ <doc>The server SHOULD implement the mandatory flag.</doc>
+ </rule>
+ </field>
+
+ <field name = "immediate" domain = "bit" label = "request immediate delivery">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue consumer immediately. If this flag is set, the server will return an
+ undeliverable message with a Return method. If this flag is zero, the server
+ will queue the message, but with no guarantee that it will ever be consumed.
+ </doc>
+
+ <!-- Rule test name: was "amq_file_00" -->
+ <rule name = "01">
+ <doc>The server SHOULD implement the immediate flag.</doc>
+ </rule>
+ </field>
+
+ <field name = "identifier" domain = "shortstr" label = "staging identifier">
+ <doc>
+ This is the staging identifier of the message to publish. The message must have
+ been staged. Note that a client can send the Publish method asynchronously
+ without waiting for staging to finish.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "return" content = "1" index = "70" label = "return a failed message">
+ <doc>
+ This method returns an undeliverable message that was published with the "immediate"
+ flag set, or an unroutable message published with the "mandatory" flag set. The
+ reply code and text provide information about the reason that the message was
+ undeliverable.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "reply-code" domain = "reply-code" />
+
+ <field name = "reply-text" domain = "reply-text" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>Specifies the routing key name specified when the message was published.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "deliver" index = "80" label = "notify the client of a consumer message">
+ <doc>
+ This method delivers a staged file message to the client, via a consumer. In the
+ asynchronous message delivery model, the client starts a consumer using the Consume
+ method, then the server responds with Deliver methods as and when messages arrive
+ for that consumer.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server SHOULD track the number of times a message has been delivered to
+ clients and when a message is redelivered a certain number of times - e.g. 5
+ times - without being acknowledged, the server SHOULD consider the message to be
+ unprocessable (possibly causing client applications to abort), and move the
+ message to a dead letter queue.
+ </doc>
+ </rule>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "redelivered" domain = "redelivered" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>Specifies the routing key name specified when the message was published.</doc>
+ </field>
+
+ <field name = "identifier" domain = "shortstr" label = "staging identifier">
+ <doc>
+ This is the staging identifier of the message to deliver. The message must have
+ been staged. Note that a server can send the Deliver method asynchronously
+ without waiting for staging to finish.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "ack" index = "90" label = "acknowledge one or more messages">
+ <doc>
+ This method acknowledges one or more messages delivered via the Deliver method. The
+ client can ask to confirm a single message or a set of messages up to and including
+ a specific message.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "multiple" domain = "bit" label = "acknowledge multiple messages">
+ <doc>
+ If set to 1, the delivery tag is treated as "up to and including", so that the
+ client can acknowledge multiple messages with a single method. If set to zero,
+ the delivery tag refers to a single message. If the multiple field is 1, and the
+ delivery tag is zero, tells the server to acknowledge all outstanding mesages.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server MUST validate that a non-zero delivery-tag refers to an delivered
+ message, and raise a channel exception if this is not the case.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "reject" index = "100" label = "reject an incoming message">
+ <doc>
+ This method allows a client to reject a message. It can be used to return
+ untreatable messages to their original queue. Note that file content is staged
+ before delivery, so the client will not use this method to interrupt delivery of a
+ large message.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server SHOULD interpret this method as meaning that the client is unable to
+ process the message at this time.
+ </doc>
+ </rule>
+
+ <!-- TODO: Rule split? -->
+
+ <rule name = "02">
+ <doc>
+ A client MUST NOT use this method as a means of selecting messages to process. A
+ rejected message MAY be discarded or dead-lettered, not necessarily passed to
+ another client.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "requeue" domain = "bit" label = "requeue the message">
+ <doc>
+ If this field is zero, the message will be discarded. If this bit is 1, the
+ server will attempt to requeue the message.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server MUST NOT deliver the message to the same client within the
+ context of the current channel. The recommended strategy is to attempt to
+ deliver the message to an alternative consumer, and if that is not possible,
+ to move the message to a dead-letter queue. The server MAY use more
+ sophisticated tracking to hold the message on the queue and redeliver it to
+ the same client at a later stage.
+ </doc>
+ </rule>
+ </field>
+ </method>
+ </class>
+
+ <!-- == STREAM =========================================================== -->
+
+ <class name = "stream" handler = "channel" index = "80" label = "work with streaming content">
+ <doc>
+ The stream class provides methods that support multimedia streaming. The stream class
+ uses the following semantics: one message is one packet of data; delivery is
+ unacknowleged and unreliable; the consumer can specify quality of service parameters
+ that the server can try to adhere to; lower-priority messages may be discarded in favour
+ of high priority messages.
+ </doc>
+
+ <doc type = "grammar">
+ stream = C:QOS S:QOS-OK
+ / C:CONSUME S:CONSUME-OK
+ / C:CANCEL S:CANCEL-OK
+ / C:PUBLISH content
+ / S:RETURN
+ / S:DELIVER content
+ </doc>
+
+ <chassis name = "server" implement = "MAY" />
+ <chassis name = "client" implement = "MAY" />
+
+ <rule name = "01">
+ <doc>
+ The server SHOULD discard stream messages on a priority basis if the queue size
+ exceeds some configured limit.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server MUST implement at least 2 priority levels for stream messages, where
+ priorities 0-4 and 5-9 are treated as two distinct levels. The server MAY implement
+ up to 10 priority levels.
+ </doc>
+ </rule>
+
+ <rule name = "03">
+ <doc>
+ The server MUST implement automatic acknowledgements on stream content. That is, as
+ soon as a message is delivered to a client via a Deliver method, the server must
+ remove it from the queue.
+ </doc>
+ </rule>
+
+ <!-- These are the properties for a Stream content -->
+
+ <field name = "content-type" domain = "shortstr" label = "MIME content type" />
+ <field name = "content-encoding" domain = "shortstr" label = "MIME content encoding" />
+ <field name = "headers" domain = "table" label = "message header field table" />
+ <field name = "priority" domain = "octet" label = "message priority, 0 to 9" />
+ <field name = "timestamp" domain = "timestamp" label = "message timestamp" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "qos" synchronous = "1" index = "10" label = "specify quality of service">
+ <doc>
+ This method requests a specific quality of service. The QoS can be specified for the
+ current channel or for all channels on the connection. The particular properties and
+ semantics of a qos method always depend on the content class semantics. Though the
+ qos method could in principle apply to both peers, it is currently meaningful only
+ for the server.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "qos-ok" />
+
+ <field name = "prefetch-size" domain = "long" label = "prefetch window in octets">
+ <doc>
+ The client can request that messages be sent in advance so that when the client
+ finishes processing a message, the following message is already held locally,
+ rather than needing to be sent down the channel. Prefetching gives a performance
+ improvement. This field specifies the prefetch window size in octets. May be set
+ to zero, meaning "no specific limit". Note that other prefetch limits may still
+ apply.
+ </doc>
+ </field>
+
+ <field name = "prefetch-count" domain = "short" label = "prefetch window in messages">
+ <doc>
+ Specifies a prefetch window in terms of whole messages. This field may be used
+ in combination with the prefetch-size field; a message will only be sent in
+ advance if both prefetch windows (and those at the channel and connection level)
+ allow it.
+ </doc>
+ </field>
+
+ <field name = "consume-rate" domain = "long" label = "transfer rate in octets/second">
+ <doc>
+ Specifies a desired transfer rate in octets per second. This is usually
+ determined by the application that uses the streaming data. A value of zero
+ means "no limit", i.e. as rapidly as possible.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server MAY ignore the prefetch values and consume rates, depending on
+ the type of stream and the ability of the server to queue and/or reply it.
+ The server MAY drop low-priority messages in favour of high-priority
+ messages.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "global" domain = "bit" label = "apply to entire connection">
+ <doc>
+ By default the QoS settings apply to the current channel only. If this field is
+ set, they are applied to the entire connection.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "qos-ok" synchronous = "1" index = "11" label = "confirm the requested qos">
+ <doc>
+ This method tells the client that the requested QoS levels could be handled by the
+ server. The requested QoS applies to all active consumers until a new QoS is
+ defined.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "consume" synchronous = "1" index = "20" label = "start a queue consumer">
+ <doc>
+ This method asks the server to start a "consumer", which is a transient request for
+ messages from a specific queue. Consumers last as long as the channel they were
+ created on, or until the client cancels them.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server SHOULD support at least 16 consumers per queue, unless the queue was
+ declared as private, and ideally, impose no limit except as defined by available
+ resources.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <doc>
+ Streaming applications SHOULD use different channels to select different
+ streaming resolutions. AMQP makes no provision for filtering and/or transforming
+ streams except on the basis of priority-based selective delivery of individual
+ messages.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "consume-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "read" access rights to
+ the realm for the queue.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to consume from. If the queue name is null,
+ refers to the current queue for the channel, which is the last declared queue.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ If the client did not previously declare a queue, and the queue name in this
+ method is empty, the server MUST raise a connection exception with reply
+ code 530 (not allowed).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>
+ Specifies the identifier for the consumer. The consumer tag is local to a
+ connection, so two clients can use the same consumer tags. If this field is
+ empty the server will generate a unique tag.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The tag MUST NOT refer to an existing consumer. If the client attempts to
+ create two consumers with the same non-empty tag the server MUST raise a
+ connection exception with reply code 530 (not allowed).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "no-local" domain = "no-local" />
+
+ <field name = "exclusive" domain = "bit" label = "request exclusive access">
+ <doc>
+ Request exclusive consumer access, meaning only this consumer can access the
+ queue.
+ </doc>
+
+
+ <!-- Rule test name: was "amq_file_00" -->
+ <rule name = "01">
+ <doc>
+ If the server cannot grant exclusive access to the queue when asked, -
+ because there are other consumers active - it MUST raise a channel exception
+ with return code 405 (resource locked).
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "consume-ok" synchronous = "1" index = "21" label = "confirm a new consumer">
+ <doc>
+ This method provides the client with a consumer tag which it may use in methods that
+ work with the consumer.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>Holds the consumer tag specified by the client or provided by the server.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "cancel" synchronous = "1" index = "30" label = "end a queue consumer">
+ <doc>
+ This method cancels a consumer. Since message delivery is asynchronous the client
+ may continue to receive messages for a short while after canceling a consumer. It
+ may process or discard these as appropriate.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "cancel-ok" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+ </method>
+
+ <method name = "cancel-ok" synchronous = "1" index = "31" label = "confirm a cancelled consumer">
+ <doc>This method confirms that the cancellation was completed.</doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "publish" content = "1" index = "40" label = "publish a message">
+ <doc>
+ This method publishes a message to a specific exchange. The message will be routed
+ to queues as defined by the exchange configuration and distributed to any active
+ consumers as appropriate.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "write" access rights
+ to the access realm for the exchange.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange to publish to. The exchange name can be
+ empty, meaning the default exchange. If the exchange name is specified, and that
+ exchange does not exist, the server will raise a channel exception.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server MUST accept a blank exchange name to mean the default exchange.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <doc>
+ If the exchange was declared as an internal exchange, the server MUST
+ respond with a reply code 403 (access refused) and raise a channel
+ exception.
+ </doc>
+ </rule>
+
+ <rule name = "03">
+ <doc>
+ The exchange MAY refuse stream content in which case it MUST respond with a
+ reply code 540 (not implemented) and raise a channel exception.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>
+ Specifies the routing key for the message. The routing key is used for routing
+ messages depending on the exchange configuration.
+ </doc>
+ </field>
+
+ <field name = "mandatory" domain = "bit" label = "indicate mandatory routing">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue. If this flag is set, the server will return an unroutable message with a
+ Return method. If this flag is zero, the server silently drops the message.
+ </doc>
+
+ <!-- Rule test name: was "amq_stream_00" -->
+ <rule name = "01">
+ <doc>The server SHOULD implement the mandatory flag.</doc>
+ </rule>
+ </field>
+
+ <field name = "immediate" domain = "bit" label = "request immediate delivery">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue consumer immediately. If this flag is set, the server will return an
+ undeliverable message with a Return method. If this flag is zero, the server
+ will queue the message, but with no guarantee that it will ever be consumed.
+ </doc>
+
+ <!-- Rule test name: was "amq_stream_00" -->
+ <rule name = "01">
+ <doc>The server SHOULD implement the immediate flag.</doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "return" content = "1" index = "50" label = "return a failed message">
+ <doc>
+ This method returns an undeliverable message that was published with the "immediate"
+ flag set, or an unroutable message published with the "mandatory" flag set. The
+ reply code and text provide information about the reason that the message was
+ undeliverable.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "reply-code" domain = "reply-code" />
+
+ <field name = "reply-text" domain = "reply-text" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>Specifies the routing key name specified when the message was published.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "deliver" content = "1" index = "60"
+ label = "notify the client of a consumer message">
+ <doc>
+ This method delivers a message to the client, via a consumer. In the asynchronous
+ message delivery model, the client starts a consumer using the Consume method, then
+ the server responds with Deliver methods as and when messages arrive for that
+ consumer.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue that the message came from. Note that a single
+ channel can start many consumers on different queues.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+ </method>
+ </class>
+
+ <!-- == TX =============================================================== -->
+
+ <class name = "tx" handler = "channel" index = "90" label = "work with standard transactions">
+ <doc>
+ Standard transactions provide so-called "1.5 phase commit". We can ensure that work is
+ never lost, but there is a chance of confirmations being lost, so that messages may be
+ resent. Applications that use standard transactions must be able to detect and ignore
+ duplicate messages.
+ </doc>
+
+ <!-- TODO: Rule split? -->
+
+ <rule name = "01">
+ <doc>
+ An client using standard transactions SHOULD be able to track all messages received
+ within a reasonable period, and thus detect and reject duplicates of the same
+ message. It SHOULD NOT pass these to the application layer.
+ </doc>
+ </rule>
+
+ <doc type = "grammar">
+ tx = C:SELECT S:SELECT-OK
+ / C:COMMIT S:COMMIT-OK
+ / C:ROLLBACK S:ROLLBACK-OK
+ </doc>
+
+ <chassis name = "server" implement = "SHOULD" />
+ <chassis name = "client" implement = "MAY" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "select" synchronous = "1" index = "10" label = "select standard transaction mode">
+ <doc>
+ This method sets the channel to use standard transactions. The client must use this
+ method at least once on a channel before using the Commit or Rollback methods.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "select-ok" />
+ </method>
+
+ <method name = "select-ok" synchronous = "1" index = "11" label = "confirm transaction mode">
+ <doc>
+ This method confirms to the client that the channel was successfully set to use
+ standard transactions.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "commit" synchronous = "1" index = "20" label = "commit the current transaction">
+ <doc>
+ This method commits all messages published and acknowledged in the current
+ transaction. A new transaction starts immediately after a commit.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "commit-ok" />
+ </method>
+
+ <method name = "commit-ok" synchronous = "1" index = "21" label = "confirm a successful commit">
+ <doc>
+ This method confirms to the client that the commit succeeded. Note that if a commit
+ fails, the server raises a channel exception.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "rollback" synchronous = "1" index = "30"
+ label = "abandon the current transaction">
+ <doc>
+ This method abandons all messages published and acknowledged in the current
+ transaction. A new transaction starts immediately after a rollback.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "rollback-ok" />
+ </method>
+
+ <method name = "rollback-ok" synchronous = "1" index = "31" label = "confirm successful rollback">
+ <doc>
+ This method confirms to the client that the rollback succeeded. Note that if an
+ rollback fails, the server raises a channel exception.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+ </class>
+
+ <!-- == DTX ============================================================== -->
+
+ <class name = "dtx" handler = "channel" index = "100" label = "work with distributed transactions">
+ <doc>
+ Distributed transactions provide so-called "2-phase commit". The AMQP distributed
+ transaction model supports the X-Open XA architecture and other distributed transaction
+ implementations. The Dtx class assumes that the server has a private communications
+ channel (not AMQP) to a distributed transaction coordinator.
+ </doc>
+
+ <doc type = "grammar">
+ dtx = C:SELECT S:SELECT-OK
+ C:START S:START-OK
+ </doc>
+
+ <chassis name = "server" implement = "MAY" />
+ <chassis name = "client" implement = "MAY" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "select" synchronous = "1" index = "10" label = "select standard transaction mode">
+ <doc>
+ This method sets the channel to use distributed transactions. The client must use
+ this method at least once on a channel before using the Start method.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "select-ok" />
+ </method>
+
+ <method name = "select-ok" synchronous = "1" index = "11" label = "confirm transaction mode">
+ <doc>
+ This method confirms to the client that the channel was successfully set to use
+ distributed transactions.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "start" synchronous = "1" index = "20"
+ label = "start a new distributed transaction">
+ <doc>
+ This method starts a new distributed transaction. This must be the first method on a
+ new channel that uses the distributed transaction mode, before any methods that
+ publish or consume messages.
+ </doc>
+ <chassis name = "server" implement = "MAY" />
+ <response name = "start-ok" />
+ <field name = "dtx-identifier" domain = "shortstr" label = "transaction identifier">
+ <doc>
+ The distributed transaction key. This identifies the transaction so that the
+ AMQP server can coordinate with the distributed transaction coordinator.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+ </method>
+
+ <method name = "start-ok" synchronous = "1" index = "21"
+ label = "confirm the start of a new distributed transaction">
+ <doc>
+ This method confirms to the client that the transaction started. Note that if a
+ start fails, the server raises a channel exception.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+ </class>
+
+ <!-- == TUNNEL =========================================================== -->
+
+ <class name = "tunnel" handler = "tunnel" index = "110" label = "methods for protocol tunneling">
+ <doc>
+ The tunnel methods are used to send blocks of binary data - which can be serialised AMQP
+ methods or other protocol frames - between AMQP peers.
+ </doc>
+
+ <doc type = "grammar">
+ tunnel = C:REQUEST
+ / S:REQUEST
+ </doc>
+
+ <chassis name = "server" implement = "MAY" />
+ <chassis name = "client" implement = "MAY" />
+
+ <field name = "headers" domain = "table" label = "message header field table" />
+ <field name = "proxy-name" domain = "shortstr" label = "identity of tunnelling proxy" />
+ <field name = "data-name" domain = "shortstr" label = "name or type of message being tunnelled" />
+ <field name = "durable" domain = "octet" label = "message durability indicator" />
+ <field name = "broadcast" domain = "octet" label = "message broadcast mode" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "request" content = "1" index = "10" label = "sends a tunnelled method">
+ <doc>
+ This method tunnels a block of binary data, which can be an encoded
+ AMQP method or other data. The binary data is sent as the content for
+ the Tunnel.Request method.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <field name = "meta-data" domain = "table" label = "meta data for the tunnelled block">
+ <doc>
+ This field table holds arbitrary meta-data that the sender needs to
+ pass to the recipient.
+ </doc>
+ </field>
+ </method>
+ </class>
+</amqp>
diff --git a/qpid/gentools/xml-src/amqp-0.8.test.xml b/qpid/gentools/xml-src/amqp-0.8.test.xml
new file mode 100644
index 0000000000..b0adf31828
--- /dev/null
+++ b/qpid/gentools/xml-src/amqp-0.8.test.xml
@@ -0,0 +1,3959 @@
+<?xml version="1.0"?>
+
+<!--
+ Copyright Notice
+ ================
+ (c) Copyright JPMorgan Chase Bank & Co., Cisco Systems, Inc., Envoy Technologies Inc.,
+ iMatix Corporation, IONA\ufffd Technologies, Red Hat, Inc.,
+ TWIST Process Innovations, and 29West Inc. 2006. All rights reserved.
+
+ License
+ =======
+ JPMorgan Chase Bank & Co., Cisco Systems, Inc., Envoy Technologies Inc., iMatix
+ Corporation, IONA\ufffd Technologies, Red Hat, Inc., TWIST Process Innovations, and
+ 29West Inc. (collectively, the "Authors") each hereby grants to you a worldwide,
+ perpetual, royalty-free, nontransferable, nonexclusive license to
+ (i) copy, display, and implement the Advanced Messaging Queue Protocol
+ ("AMQP") Specification and (ii) the Licensed Claims that are held by
+ the Authors, all for the purpose of implementing the Advanced Messaging
+ Queue Protocol Specification. Your license and any rights under this
+ Agreement will terminate immediately without notice from
+ any Author if you bring any claim, suit, demand, or action related to
+ the Advanced Messaging Queue Protocol Specification against any Author.
+ Upon termination, you shall destroy all copies of the Advanced Messaging
+ Queue Protocol Specification in your possession or control.
+
+ As used hereunder, "Licensed Claims" means those claims of a patent or
+ patent application, throughout the world, excluding design patents and
+ design registrations, owned or controlled, or that can be sublicensed
+ without fee and in compliance with the requirements of this
+ Agreement, by an Author or its affiliates now or at any
+ future time and which would necessarily be infringed by implementation
+ of the Advanced Messaging Queue Protocol Specification. A claim is
+ necessarily infringed hereunder only when it is not possible to avoid
+ infringing it because there is no plausible non-infringing alternative
+ for implementing the required portions of the Advanced Messaging Queue
+ Protocol Specification. Notwithstanding the foregoing, Licensed Claims
+ shall not include any claims other than as set forth above even if
+ contained in the same patent as Licensed Claims; or that read solely
+ on any implementations of any portion of the Advanced Messaging Queue
+ Protocol Specification that are not required by the Advanced Messaging
+ Queue Protocol Specification, or that, if licensed, would require a
+ payment of royalties by the licensor to unaffiliated third parties.
+ Moreover, Licensed Claims shall not include (i) any enabling technologies
+ that may be necessary to make or use any Licensed Product but are not
+ themselves expressly set forth in the Advanced Messaging Queue Protocol
+ Specification (e.g., semiconductor manufacturing technology, compiler
+ technology, object oriented technology, networking technology, operating
+ system technology, and the like); or (ii) the implementation of other
+ published standards developed elsewhere and merely referred to in the
+ body of the Advanced Messaging Queue Protocol Specification, or
+ (iii) any Licensed Product and any combinations thereof the purpose or
+ function of which is not required for compliance with the Advanced
+ Messaging Queue Protocol Specification. For purposes of this definition,
+ the Advanced Messaging Queue Protocol Specification shall be deemed to
+ include both architectural and interconnection requirements essential
+ for interoperability and may also include supporting source code artifacts
+ where such architectural, interconnection requirements and source code
+ artifacts are expressly identified as being required or documentation to
+ achieve compliance with the Advanced Messaging Queue Protocol Specification.
+
+ As used hereunder, "Licensed Products" means only those specific portions
+ of products (hardware, software or combinations thereof) that implement
+ and are compliant with all relevant portions of the Advanced Messaging
+ Queue Protocol Specification.
+
+ The following disclaimers, which you hereby also acknowledge as to any
+ use you may make of the Advanced Messaging Queue Protocol Specification:
+
+ THE ADVANCED MESSAGING QUEUE PROTOCOL SPECIFICATION IS PROVIDED "AS IS,"
+ AND THE AUTHORS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+ IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, OR TITLE; THAT THE
+ CONTENTS OF THE ADVANCED MESSAGING QUEUE PROTOCOL SPECIFICATION ARE
+ SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF THE ADVANCED
+ MESSAGING QUEUE PROTOCOL SPECIFICATION WILL NOT INFRINGE ANY THIRD PARTY
+ PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
+
+ THE AUTHORS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL,
+ INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO ANY
+ USE, IMPLEMENTATION OR DISTRIBUTION OF THE ADVANCED MESSAGING QUEUE
+ PROTOCOL SPECIFICATION.
+
+ The name and trademarks of the Authors may NOT be used in any manner,
+ including advertising or publicity pertaining to the Advanced Messaging
+ Queue Protocol Specification or its contents without specific, written
+ prior permission. Title to copyright in the Advanced Messaging Queue
+ Protocol Specification will at all times remain with the Authors.
+
+ No other rights are granted by implication, estoppel or otherwise.
+
+ Upon termination of your license or rights under this Agreement, you
+ shall destroy all copies of the Advanced Messaging Queue Protocol
+ Specification in your possession or control.
+
+ Trademarks
+ ==========
+ "JPMorgan", "JPMorgan Chase", "Chase", the JPMorgan Chase logo and the
+ Octagon Symbol are trademarks of JPMorgan Chase & Co.
+
+ IMATIX and the iMatix logo are trademarks of iMatix Corporation sprl.
+
+ IONA, IONA Technologies, and the IONA logos are trademarks of IONA
+ Technologies PLC and/or its subsidiaries.
+
+ LINUX is a trademark of Linus Torvalds. RED HAT and JBOSS are registered
+ trademarks of Red Hat, Inc. in the US and other countries.
+
+ Java, all Java-based trademarks and OpenOffice.org are trademarks of
+ Sun Microsystems, Inc. in the United States, other countries, or both.
+
+ Other company, product, or service names may be trademarks or service
+ marks of others.
+
+ Links to full AMQP specification:
+ =================================
+ http://www.envoytech.org/spec/amq/
+ http://www.iona.com/opensource/amqp/
+ http://www.redhat.com/solutions/specifications/amqp/
+ http://www.twiststandards.org/tiki-index.php?page=AMQ
+ http://www.imatix.com/amqp
+
+-->
+
+<!--
+========================================================
+EDITORS: (PH) Pieter Hintjens <ph@imatix.com>
+ (KvdR) Kim van der Riet <kim.vdriet@redhat.com>
+
+NOTE: These editors have been assigned by the AMQP working group. Please do not
+edit/commit this file without consulting with one of the above editors.
+========================================================
+
+Revision history:
+ 2006-06-07 (PH) - version number changed to 0.8 to conform to public
+ release documentation.
+
+ 2006-05-15 (PH) - fixed comments on queue name in basic.get to clarify
+ use of current queue in this method.
+
+ 2006-05-15 (PH) - fixed comments on routing key in queue.bind to clarify
+ how routing key is filled when empty (to allow asynch queue.declare).
+
+ 2006-05-11 (PH) - reset version to 0.70 so that putatitive standards
+ group can release 2-3 major new versions before hitting 1.0 (again).
+
+ 2006-05-11 (PH) - TODO in documentation: cycle field in frame header
+ has been removed.
+
+ 2006-05-11 (PH) - added nowait option to exchange.declare, delete,
+ queue.declare, delete, bind, purge, basic.consume, cancel,
+ file.consume, cancel, stream.consume and cancel methods.
+
+ 2006-05-11 (PH) - removed notnull rule and added explanations on queue
+ name in queue.bind, purge, delete, basic.consume, cancel, file.consume,
+ cancel, stream.consume and cancel methods.
+
+ 2006-05-11 (PH) - added basic.qos, file.qos, and stream.qos methods that
+ regroup all prefetch options from the consume methods. Also removed the
+ prefetch option from channel.open.
+
+ 2006-05-11 (PH) - renumbered method indexes to show request-response
+ nature of methods; requests are 10, 20, 30 while responses are 11, 21,
+ etc.
+
+ 2006-05-11 (PH) - removed OpenAMQ extension methods from this definition
+ since these are maintained seperately.
+
+ 2006-05-26 (RG) - added Basic.Recover method to allow replay of
+ unacknowledged messages on a channel.
+
+ 2006-07-03 (PH) - cosmetic clean-up of Basic.Recover comments.
+-->
+
+<amqp major="8" minor="0" port="5672" comment="AMQ protocol 0.80">
+ AMQ Protocol 0.80
+
+<!--
+======================================================
+== CONSTANTS
+======================================================
+-->
+ <constant name="frame method" value="1"/>
+ <constant name="frame header" value="2"/>
+ <constant name="frame body" value="3"/>
+ <constant name="frame oob method" value="4"/>
+ <constant name="frame oob header" value="5"/>
+ <constant name="frame oob body" value="6"/>
+ <constant name="frame trace" value="7"/>
+ <constant name="frame heartbeat" value="8"/>
+ <constant name="frame min size" value="4096"/>
+ <constant name="frame end" value="206"/>
+ <constant name="reply success" value="200">
+ Indicates that the method completed successfully. This reply code is
+ reserved for future use - the current protocol design does not use
+ positive confirmation and reply codes are sent only in case of an
+ error.
+</constant>
+ <constant name="not delivered" value="310" class="soft error">
+ The client asked for a specific message that is no longer available.
+ The message was delivered to another client, or was purged from the
+ queue for some other reason.
+</constant>
+ <constant name="content too large" value="311" class="soft error">
+ The client attempted to transfer content larger than the server
+ could accept at the present time. The client may retry at a later
+ time.
+</constant>
+ <constant name="connection forced" value="320" class="hard error">
+ An operator intervened to close the connection for some reason.
+ The client may retry at some later date.
+</constant>
+ <constant name="invalid path" value="402" class="hard error">
+ The client tried to work with an unknown virtual host or cluster.
+</constant>
+ <constant name="access refused" value="403" class="soft error">
+ The client attempted to work with a server entity to which it has
+ no due to security settings.
+</constant>
+ <constant name="not found" value="404" class="soft error">
+ The client attempted to work with a server entity that does not exist.
+</constant>
+ <constant name="resource locked" value="405" class="soft error">
+ The client attempted to work with a server entity to which it has
+ no access because another client is working with it.
+</constant>
+ <constant name="frame error" value="501" class="hard error">
+ The client sent a malformed frame that the server could not decode.
+ This strongly implies a programming error in the client.
+</constant>
+ <constant name="syntax error" value="502" class="hard error">
+ The client sent a frame that contained illegal values for one or more
+ fields. This strongly implies a programming error in the client.
+</constant>
+ <constant name="command invalid" value="503" class="hard error">
+ The client sent an invalid sequence of frames, attempting to perform
+ an operation that was considered invalid by the server. This usually
+ implies a programming error in the client.
+</constant>
+ <constant name="channel error" value="504" class="hard error">
+ The client attempted to work with a channel that had not been
+ correctly opened. This most likely indicates a fault in the client
+ layer.
+</constant>
+ <constant name="resource error" value="506" class="hard error">
+ The server could not complete the method because it lacked sufficient
+ resources. This may be due to the client creating too many of some
+ type of entity.
+</constant>
+ <constant name="not allowed" value="530" class="hard error">
+ The client tried to work with some entity in a manner that is
+ prohibited by the server, due to security settings or by some other
+ criteria.
+</constant>
+ <constant name="not implemented" value="540" class="hard error">
+ The client tried to use functionality that is not implemented in the
+ server.
+</constant>
+ <constant name="internal error" value="541" class="hard error">
+ The server could not complete the method because of an internal error.
+ The server may require intervention by an operator in order to resume
+ normal operations.
+</constant>
+ <!--
+======================================================
+== DOMAIN TYPES
+======================================================
+-->
+ <domain name="access ticket" type="short">
+ access ticket granted by server
+ <doc>
+ An access ticket granted by the server for a certain set of access
+ rights within a specific realm. Access tickets are valid within the
+ channel where they were created, and expire when the channel closes.
+ </doc>
+ <assert check="ne" value="0"/>
+ </domain>
+ <domain name="class id" type="short"/>
+ <domain name="consumer tag" type="shortstr">
+ consumer tag
+ <doc>
+ Identifier for the consumer, valid within the current connection.
+ </doc>
+ <rule implement="MUST">
+ The consumer tag is valid only within the channel from which the
+ consumer was created. I.e. a client MUST NOT create a consumer in
+ one channel and then use it in another.
+ </rule>
+ </domain>
+ <domain name="delivery tag" type="longlong">
+ server-assigned delivery tag
+ <doc>
+ The server-assigned and channel-specific delivery tag
+ </doc>
+ <rule implement="MUST">
+ The delivery tag is valid only within the channel from which the
+ message was received. I.e. a client MUST NOT receive a message on
+ one channel and then acknowledge it on another.
+ </rule>
+ <rule implement="MUST">
+ The server MUST NOT use a zero value for delivery tags. Zero is
+ reserved for client use, meaning "all messages so far received".
+ </rule>
+ </domain>
+ <domain name="exchange name" type="shortstr">
+ exchange name
+ <doc>
+ The exchange name is a client-selected string that identifies
+ the exchange for publish methods. Exchange names may consist
+ of any mixture of digits, letters, and underscores. Exchange
+ names are scoped by the virtual host.
+ </doc>
+ <assert check="length" value="127"/>
+ </domain>
+ <domain name="known hosts" type="shortstr">
+list of known hosts
+<doc>
+Specifies the list of equivalent or alternative hosts that the server
+knows about, which will normally include the current server itself.
+Clients can cache this information and use it when reconnecting to a
+server after a failure.
+</doc>
+ <rule implement="MAY">
+The server MAY leave this field empty if it knows of no other
+hosts than itself.
+</rule>
+ </domain>
+ <domain name="method id" type="short"/>
+ <domain name="no ack" type="bit">
+ no acknowledgement needed
+ <doc>
+ If this field is set the server does not expect acknowledgments
+ for messages. That is, when a message is delivered to the client
+ the server automatically and silently acknowledges it on behalf
+ of the client. This functionality increases performance but at
+ the cost of reliability. Messages can get lost if a client dies
+ before it can deliver them to the application.
+ </doc>
+ </domain>
+ <domain name="no local" type="bit">
+ do not deliver own messages
+ <doc>
+ If the no-local field is set the server will not send messages to
+ the client that published them.
+ </doc>
+ </domain>
+ <domain name="path" type="shortstr">
+ <doc>
+ Must start with a slash "/" and continue with path names
+ separated by slashes. A path name consists of any combination
+ of at least one of [A-Za-z0-9] plus zero or more of [.-_+!=:].
+</doc>
+ <assert check="notnull"/>
+ <assert check="syntax" rule="path"/>
+ <assert check="length" value="127"/>
+ </domain>
+ <domain name="peer properties" type="table">
+ <doc>
+This string provides a set of peer properties, used for
+identification, debugging, and general information.
+</doc>
+ <rule implement="SHOULD">
+The properties SHOULD contain these fields:
+"product", giving the name of the peer product, "version", giving
+the name of the peer version, "platform", giving the name of the
+operating system, "copyright", if appropriate, and "information",
+giving other general information.
+</rule>
+ </domain>
+ <domain name="queue name" type="shortstr">
+ queue name
+ <doc>
+ The queue name identifies the queue within the vhost. Queue
+ names may consist of any mixture of digits, letters, and
+ underscores.
+ </doc>
+ <assert check="length" value="127"/>
+ </domain>
+ <domain name="redelivered" type="bit">
+ message is being redelivered
+ <doc>
+ This indicates that the message has been previously delivered to
+ this or another client.
+ </doc>
+ <rule implement="SHOULD">
+ The server SHOULD try to signal redelivered messages when it can.
+ When redelivering a message that was not successfully acknowledged,
+ the server SHOULD deliver it to the original client if possible.
+ </rule>
+ <rule implement="MUST">
+ The client MUST NOT rely on the redelivered field but MUST take it
+ as a hint that the message may already have been processed. A
+ fully robust client must be able to track duplicate received messages
+ on non-transacted, and locally-transacted channels.
+ </rule>
+ </domain>
+ <domain name="reply code" type="short">
+reply code from server
+<doc>
+ The reply code. The AMQ reply codes are defined in AMQ RFC 011.
+</doc>
+ <assert check="notnull"/>
+ </domain>
+ <domain name="reply text" type="shortstr">
+localised reply text
+<doc>
+ The localised reply text. This text can be logged as an aid to
+ resolving issues.
+</doc>
+ <assert check="notnull"/>
+ </domain>
+ <class name="connection" handler="connection" index="10">
+ <!--
+======================================================
+== CONNECTION
+======================================================
+-->
+ work with socket connections
+<doc>
+ The connection class provides methods for a client to establish a
+ network connection to a server, and for both peers to operate the
+ connection thereafter.
+</doc>
+ <doc name="grammar">
+ connection = open-connection *use-connection close-connection
+ open-connection = C:protocol-header
+ S:START C:START-OK
+ *challenge
+ S:TUNE C:TUNE-OK
+ C:OPEN S:OPEN-OK | S:REDIRECT
+ challenge = S:SECURE C:SECURE-OK
+ use-connection = *channel
+ close-connection = C:CLOSE S:CLOSE-OK
+ / S:CLOSE C:CLOSE-OK
+</doc>
+ <chassis name="server" implement="MUST"/>
+ <chassis name="client" implement="MUST"/>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="start" synchronous="1" index="10">
+ start connection negotiation
+ <doc>
+ This method starts the connection negotiation process by telling
+ the client the protocol version that the server proposes, along
+ with a list of security mechanisms which the client can use for
+ authentication.
+ </doc>
+ <rule implement="MUST">
+ If the client cannot handle the protocol version suggested by the
+ server it MUST close the socket connection.
+ </rule>
+ <rule implement="MUST">
+ The server MUST provide a protocol version that is lower than or
+ equal to that requested by the client in the protocol header. If
+ the server cannot support the specified protocol it MUST NOT send
+ this method, but MUST close the socket connection.
+ </rule>
+ <chassis name="client" implement="MUST"/>
+ <response name="start-ok"/>
+ <field name="version major" type="octet">
+ protocol major version
+ <doc>
+ The protocol major version that the server agrees to use, which
+ cannot be higher than the client's major version.
+ </doc>
+ </field>
+ <field name="version minor" type="octet">
+ protocol major version
+ <doc>
+ The protocol minor version that the server agrees to use, which
+ cannot be higher than the client's minor version.
+ </doc>
+ </field>
+ <field name="server properties" domain="peer properties">
+ server properties
+ </field>
+ <field name="mechanisms" type="longstr">
+ available security mechanisms
+ <doc>
+ A list of the security mechanisms that the server supports, delimited
+ by spaces. Currently ASL supports these mechanisms: PLAIN.
+ </doc>
+ <see name="security mechanisms"/>
+ <assert check="notnull"/>
+ </field>
+ <field name="locales" type="longstr">
+ available message locales
+ <doc>
+ A list of the message locales that the server supports, delimited
+ by spaces. The locale defines the language in which the server
+ will send reply texts.
+ </doc>
+ <rule implement="MUST">
+ All servers MUST support at least the en_US locale.
+ </rule>
+ <assert check="notnull"/>
+ </field>
+ </method>
+ <method name="start-ok" synchronous="1" index="11">
+ select security mechanism and locale
+ <doc>
+ This method selects a SASL security mechanism. ASL uses SASL
+ (RFC2222) to negotiate authentication and encryption.
+ </doc>
+ <chassis name="server" implement="MUST"/>
+ <field name="client properties" domain="peer properties">
+ client properties
+ </field>
+ <field name="mechanism" type="shortstr">
+ selected security mechanism
+ <doc>
+ A single security mechanisms selected by the client, which must be
+ one of those specified by the server.
+ </doc>
+ <rule implement="SHOULD">
+ The client SHOULD authenticate using the highest-level security
+ profile it can handle from the list provided by the server.
+ </rule>
+ <rule implement="MUST">
+ The mechanism field MUST contain one of the security mechanisms
+ proposed by the server in the Start method. If it doesn't, the
+ server MUST close the socket.
+ </rule>
+ <assert check="notnull"/>
+ </field>
+ <field name="response" type="longstr">
+ security response data
+ <doc>
+ A block of opaque data passed to the security mechanism. The contents
+ of this data are defined by the SASL security mechanism. For the
+ PLAIN security mechanism this is defined as a field table holding
+ two fields, LOGIN and PASSWORD.
+ </doc>
+ <assert check="notnull"/>
+ </field>
+ <field name="locale" type="shortstr">
+ selected message locale
+ <doc>
+ A single message local selected by the client, which must be one
+ of those specified by the server.
+ </doc>
+ <assert check="notnull"/>
+ </field>
+ </method>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="secure" synchronous="1" index="20">
+ security mechanism challenge
+ <doc>
+ The SASL protocol works by exchanging challenges and responses until
+ both peers have received sufficient information to authenticate each
+ other. This method challenges the client to provide more information.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ <response name="secure-ok"/>
+ <field name="challenge" type="longstr">
+ security challenge data
+ <doc>
+ Challenge information, a block of opaque binary data passed to
+ the security mechanism.
+ </doc>
+ <see name="security mechanisms"/>
+ </field>
+ </method>
+ <method name="secure-ok" synchronous="1" index="21">
+ security mechanism response
+ <doc>
+ This method attempts to authenticate, passing a block of SASL data
+ for the security mechanism at the server side.
+ </doc>
+ <chassis name="server" implement="MUST"/>
+ <field name="response" type="longstr">
+ security response data
+ <doc>
+ A block of opaque data passed to the security mechanism. The contents
+ of this data are defined by the SASL security mechanism.
+ </doc>
+ <assert check="notnull"/>
+ </field>
+ </method>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="tune" synchronous="1" index="30">
+ propose connection tuning parameters
+ <doc>
+ This method proposes a set of connection configuration values
+ to the client. The client can accept and/or adjust these.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ <response name="tune-ok"/>
+ <field name="channel max" type="short">
+ proposed maximum channels
+ <doc>
+ The maximum total number of channels that the server allows
+ per connection. Zero means that the server does not impose a
+ fixed limit, but the number of allowed channels may be limited
+ by available server resources.
+ </doc>
+ </field>
+ <field name="frame max" type="long">
+ proposed maximum frame size
+ <doc>
+ The largest frame size that the server proposes for the
+ connection. The client can negotiate a lower value. Zero means
+ that the server does not impose any specific limit but may reject
+ very large frames if it cannot allocate resources for them.
+ </doc>
+ <rule implement="MUST">
+ Until the frame-max has been negotiated, both peers MUST accept
+ frames of up to 4096 octets large. The minimum non-zero value for
+ the frame-max field is 4096.
+ </rule>
+ </field>
+ <field name="heartbeat" type="short">
+ desired heartbeat delay
+ <doc>
+ The delay, in seconds, of the connection heartbeat that the server
+ wants. Zero means the server does not want a heartbeat.
+ </doc>
+ </field>
+ </method>
+ <method name="tune-ok" synchronous="1" index="31">
+ negotiate connection tuning parameters
+ <doc>
+ This method sends the client's connection tuning parameters to the
+ server. Certain fields are negotiated, others provide capability
+ information.
+ </doc>
+ <chassis name="server" implement="MUST"/>
+ <field name="channel max" type="short">
+ negotiated maximum channels
+ <doc>
+ The maximum total number of channels that the client will use
+ per connection. May not be higher than the value specified by
+ the server.
+ </doc>
+ <rule implement="MAY">
+ The server MAY ignore the channel-max value or MAY use it for
+ tuning its resource allocation.
+ </rule>
+ <assert check="notnull"/>
+ <assert check="le" method="tune" field="channel max"/>
+ </field>
+ <field name="frame max" type="long">
+ negotiated maximum frame size
+ <doc>
+ The largest frame size that the client and server will use for
+ the connection. Zero means that the client does not impose any
+ specific limit but may reject very large frames if it cannot
+ allocate resources for them. Note that the frame-max limit
+ applies principally to content frames, where large contents
+ can be broken into frames of arbitrary size.
+ </doc>
+ <rule implement="MUST">
+ Until the frame-max has been negotiated, both peers must accept
+ frames of up to 4096 octets large. The minimum non-zero value for
+ the frame-max field is 4096.
+ </rule>
+ </field>
+ <field name="heartbeat" type="short">
+ desired heartbeat delay
+ <doc>
+ The delay, in seconds, of the connection heartbeat that the client
+ wants. Zero means the client does not want a heartbeat.
+ </doc>
+ </field>
+ </method>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="open" synchronous="1" index="40">
+ open connection to virtual host
+ <doc>
+ This method opens a connection to a virtual host, which is a
+ collection of resources, and acts to separate multiple application
+ domains within a server.
+ </doc>
+ <rule implement="MUST">
+ The client MUST open the context before doing any work on the
+ connection.
+ </rule>
+ <chassis name="server" implement="MUST"/>
+ <response name="open-ok"/>
+ <response name="redirect"/>
+ <field name="virtual host" domain="path">
+ virtual host name
+ <assert check="regexp" value="^[a-zA-Z0-9/-_]+$"/>
+ <doc>
+ The name of the virtual host to work with.
+ </doc>
+ <rule implement="MUST">
+ If the server supports multiple virtual hosts, it MUST enforce a
+ full separation of exchanges, queues, and all associated entities
+ per virtual host. An application, connected to a specific virtual
+ host, MUST NOT be able to access resources of another virtual host.
+ </rule>
+ <rule implement="SHOULD">
+ The server SHOULD verify that the client has permission to access
+ the specified virtual host.
+ </rule>
+ <rule implement="MAY">
+ The server MAY configure arbitrary limits per virtual host, such
+ as the number of each type of entity that may be used, per
+ connection and/or in total.
+ </rule>
+ </field>
+ <field name="capabilities" type="shortstr">
+ required capabilities
+ <doc>
+ The client may specify a number of capability names, delimited by
+ spaces. The server can use this string to how to process the
+ client's connection request.
+ </doc>
+ </field>
+ <field name="insist" type="bit">
+ insist on connecting to server
+ <doc>
+ In a configuration with multiple load-sharing servers, the server
+ may respond to a Connection.Open method with a Connection.Redirect.
+ The insist option tells the server that the client is insisting on
+ a connection to the specified server.
+ </doc>
+ <rule implement="SHOULD">
+ When the client uses the insist option, the server SHOULD accept
+ the client connection unless it is technically unable to do so.
+ </rule>
+ </field>
+ </method>
+ <method name="open-ok" synchronous="1" index="41">
+ signal that the connection is ready
+ <doc>
+ This method signals to the client that the connection is ready for
+ use.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ <field name="known hosts" domain="known hosts"/>
+ </method>
+ <method name="redirect" synchronous="1" index="50">
+ asks the client to use a different server
+ <doc>
+ This method redirects the client to another server, based on the
+ requested virtual host and/or capabilities.
+ </doc>
+ <rule implement="SHOULD">
+ When getting the Connection.Redirect method, the client SHOULD
+ reconnect to the host specified, and if that host is not present,
+ to any of the hosts specified in the known-hosts list.
+ </rule>
+ <chassis name="client" implement="MAY"/>
+ <field name="host" type="shortstr">
+ server to connect to
+ <doc>
+ Specifies the server to connect to. This is an IP address or a
+ DNS name, optionally followed by a colon and a port number. If
+ no port number is specified, the client should use the default
+ port number for the protocol.
+ </doc>
+ <assert check="notnull"/>
+ </field>
+ <field name="known hosts" domain="known hosts"/>
+ </method>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="close" synchronous="1" index="60">
+ request a connection close
+ <doc>
+ This method indicates that the sender wants to close the connection.
+ This may be due to internal conditions (e.g. a forced shut-down) or
+ due to an error handling a specific method, i.e. an exception. When
+ a close is due to an exception, the sender provides the class and
+ method id of the method which caused the exception.
+ </doc>
+ <rule implement="MUST">
+ After sending this method any received method except the Close-OK
+ method MUST be discarded.
+ </rule>
+ <rule implement="MAY">
+ The peer sending this method MAY use a counter or timeout to
+ detect failure of the other peer to respond correctly with
+ the Close-OK method.
+ </rule>
+ <rule implement="MUST">
+ When a server receives the Close method from a client it MUST
+ delete all server-side resources associated with the client's
+ context. A client CANNOT reconnect to a context after sending
+ or receiving a Close method.
+ </rule>
+ <chassis name="client" implement="MUST"/>
+ <chassis name="server" implement="MUST"/>
+ <response name="close-ok"/>
+ <field name="reply code" domain="reply code"/>
+ <field name="reply text" domain="reply text"/>
+ <field name="class id" domain="class id">
+ failing method class
+ <doc>
+ When the close is provoked by a method exception, this is the
+ class of the method.
+ </doc>
+ </field>
+ <field name="method id" domain="class id">
+ failing method ID
+ <doc>
+ When the close is provoked by a method exception, this is the
+ ID of the method.
+ </doc>
+ </field>
+ </method>
+ <method name="close-ok" synchronous="1" index="61">
+ confirm a connection close
+ <doc>
+ This method confirms a Connection.Close method and tells the
+ recipient that it is safe to release resources for the connection
+ and close the socket.
+ </doc>
+ <rule implement="SHOULD">
+ A peer that detects a socket closure without having received a
+ Close-Ok handshake method SHOULD log the error.
+ </rule>
+ <chassis name="client" implement="MUST"/>
+ <chassis name="server" implement="MUST"/>
+ </method>
+ </class>
+ <class name="channel" handler="channel" index="20">
+ <!--
+======================================================
+== CHANNEL
+======================================================
+-->
+ work with channels
+<doc>
+ The channel class provides methods for a client to establish a virtual
+ connection - a channel - to a server and for both peers to operate the
+ virtual connection thereafter.
+</doc>
+ <doc name="grammar">
+ channel = open-channel *use-channel close-channel
+ open-channel = C:OPEN S:OPEN-OK
+ use-channel = C:FLOW S:FLOW-OK
+ / S:FLOW C:FLOW-OK
+ / S:ALERT
+ / functional-class
+ close-channel = C:CLOSE S:CLOSE-OK
+ / S:CLOSE C:CLOSE-OK
+</doc>
+ <chassis name="server" implement="MUST"/>
+ <chassis name="client" implement="MUST"/>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="open" synchronous="1" index="10">
+ open a channel for use
+ <doc>
+ This method opens a virtual connection (a channel).
+ </doc>
+ <rule implement="MUST">
+ This method MUST NOT be called when the channel is already open.
+ </rule>
+ <chassis name="server" implement="MUST"/>
+ <response name="open-ok"/>
+ <field name="out of band" type="shortstr">
+ out-of-band settings
+ <doc>
+ Configures out-of-band transfers on this channel. The syntax and
+ meaning of this field will be formally defined at a later date.
+ </doc>
+ <assert check="null"/>
+ </field>
+ </method>
+ <method name="open-ok" synchronous="1" index="11">
+ signal that the channel is ready
+ <doc>
+ This method signals to the client that the channel is ready for use.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ </method>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="flow" synchronous="1" index="20">
+ enable/disable flow from peer
+ <doc>
+ This method asks the peer to pause or restart the flow of content
+ data. This is a simple flow-control mechanism that a peer can use
+ to avoid oveflowing its queues or otherwise finding itself receiving
+ more messages than it can process. Note that this method is not
+ intended for window control. The peer that receives a request to
+ stop sending content should finish sending the current content, if
+ any, and then wait until it receives a Flow restart method.
+ </doc>
+ <rule implement="MAY">
+ When a new channel is opened, it is active. Some applications
+ assume that channels are inactive until started. To emulate this
+ behaviour a client MAY open the channel, then pause it.
+ </rule>
+ <rule implement="SHOULD">
+ When sending content data in multiple frames, a peer SHOULD monitor
+ the channel for incoming methods and respond to a Channel.Flow as
+ rapidly as possible.
+ </rule>
+ <rule implement="MAY">
+ A peer MAY use the Channel.Flow method to throttle incoming content
+ data for internal reasons, for example, when exchangeing data over a
+ slower connection.
+ </rule>
+ <rule implement="MAY">
+ The peer that requests a Channel.Flow method MAY disconnect and/or
+ ban a peer that does not respect the request.
+ </rule>
+ <chassis name="server" implement="MUST"/>
+ <chassis name="client" implement="MUST"/>
+ <response name="flow-ok"/>
+ <field name="active" type="bit">
+ start/stop content frames
+ <doc>
+ If 1, the peer starts sending content frames. If 0, the peer
+ stops sending content frames.
+ </doc>
+ </field>
+ </method>
+ <method name="flow-ok" index="21">
+ confirm a flow method
+ <doc>
+ Confirms to the peer that a flow command was received and processed.
+ </doc>
+ <chassis name="server" implement="MUST"/>
+ <chassis name="client" implement="MUST"/>
+ <field name="active" type="bit">
+ current flow setting
+ <doc>
+ Confirms the setting of the processed flow method: 1 means the
+ peer will start sending or continue to send content frames; 0
+ means it will not.
+ </doc>
+ </field>
+ </method>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="alert" index="30">
+ send a non-fatal warning message
+ <doc>
+ This method allows the server to send a non-fatal warning to the
+ client. This is used for methods that are normally asynchronous
+ and thus do not have confirmations, and for which the server may
+ detect errors that need to be reported. Fatal errors are handled
+ as channel or connection exceptions; non-fatal errors are sent
+ through this method.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ <field name="reply code" domain="reply code"/>
+ <field name="reply text" domain="reply text"/>
+ <field name="details" type="table">
+ detailed information for warning
+ <doc>
+ A set of fields that provide more information about the
+ problem. The meaning of these fields are defined on a
+ per-reply-code basis (TO BE DEFINED).
+ </doc>
+ </field>
+ </method>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="close" synchronous="1" index="40">
+ request a channel close
+ <doc>
+ This method indicates that the sender wants to close the channel.
+ This may be due to internal conditions (e.g. a forced shut-down) or
+ due to an error handling a specific method, i.e. an exception. When
+ a close is due to an exception, the sender provides the class and
+ method id of the method which caused the exception.
+ </doc>
+ <rule implement="MUST">
+ After sending this method any received method except
+ Channel.Close-OK MUST be discarded.
+ </rule>
+ <rule implement="MAY">
+ The peer sending this method MAY use a counter or timeout to detect
+ failure of the other peer to respond correctly with Channel.Close-OK..
+ </rule>
+ <chassis name="client" implement="MUST"/>
+ <chassis name="server" implement="MUST"/>
+ <response name="close-ok"/>
+ <field name="reply code" domain="reply code"/>
+ <field name="reply text" domain="reply text"/>
+ <field name="class id" domain="class id">
+ failing method class
+ <doc>
+ When the close is provoked by a method exception, this is the
+ class of the method.
+ </doc>
+ </field>
+ <field name="method id" domain="method id">
+ failing method ID
+ <doc>
+ When the close is provoked by a method exception, this is the
+ ID of the method.
+ </doc>
+ </field>
+ </method>
+ <method name="close-ok" synchronous="1" index="41">
+ confirm a channel close
+ <doc>
+ This method confirms a Channel.Close method and tells the recipient
+ that it is safe to release resources for the channel and close the
+ socket.
+ </doc>
+ <rule implement="SHOULD">
+ A peer that detects a socket closure without having received a
+ Channel.Close-Ok handshake method SHOULD log the error.
+ </rule>
+ <chassis name="client" implement="MUST"/>
+ <chassis name="server" implement="MUST"/>
+ </method>
+ </class>
+ <class name="access" handler="connection" index="30">
+ <!--
+======================================================
+== ACCESS CONTROL
+======================================================
+-->
+ work with access tickets
+<doc>
+ The protocol control access to server resources using access tickets.
+ A client must explicitly request access tickets before doing work.
+ An access ticket grants a client the right to use a specific set of
+ resources - called a "realm" - in specific ways.
+</doc>
+ <doc name="grammar">
+ access = C:REQUEST S:REQUEST-OK
+</doc>
+ <chassis name="server" implement="MUST"/>
+ <chassis name="client" implement="MUST"/>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="request" synchronous="1" index="10">
+ request an access ticket
+ <doc>
+ This method requests an access ticket for an access realm.
+ The server responds by granting the access ticket. If the
+ client does not have access rights to the requested realm
+ this causes a connection exception. Access tickets are a
+ per-channel resource.
+ </doc>
+ <rule implement="MUST">
+ The realm name MUST start with either "/data" (for application
+ resources) or "/admin" (for server administration resources).
+ If the realm starts with any other path, the server MUST raise
+ a connection exception with reply code 403 (access refused).
+ </rule>
+ <rule implement="MUST">
+ The server MUST implement the /data realm and MAY implement the
+ /admin realm. The mapping of resources to realms is not
+ defined in the protocol - this is a server-side configuration
+ issue.
+ </rule>
+ <chassis name="server" implement="MUST"/>
+ <response name="request-ok"/>
+ <field name="realm" domain="path">
+ name of requested realm
+ <rule implement="MUST">
+ If the specified realm is not known to the server, the server
+ must raise a channel exception with reply code 402 (invalid
+ path).
+ </rule>
+ </field>
+ <field name="exclusive" type="bit">
+ request exclusive access
+ <doc>
+ Request exclusive access to the realm. If the server cannot grant
+ this - because there are other active tickets for the realm - it
+ raises a channel exception.
+ </doc>
+ </field>
+ <field name="passive" type="bit">
+ request passive access
+ <doc>
+ Request message passive access to the specified access realm.
+ Passive access lets a client get information about resources in
+ the realm but not to make any changes to them.
+ </doc>
+ </field>
+ <field name="active" type="bit">
+ request active access
+ <doc>
+ Request message active access to the specified access realm.
+ Acvtive access lets a client get create and delete resources in
+ the realm.
+ </doc>
+ </field>
+ <field name="write" type="bit">
+ request write access
+ <doc>
+ Request write access to the specified access realm. Write access
+ lets a client publish messages to all exchanges in the realm.
+ </doc>
+ </field>
+ <field name="read" type="bit">
+ request read access
+ <doc>
+ Request read access to the specified access realm. Read access
+ lets a client consume messages from queues in the realm.
+ </doc>
+ </field>
+ </method>
+ <method name="request-ok" synchronous="1" index="11">
+ grant access to server resources
+ <doc>
+ This method provides the client with an access ticket. The access
+ ticket is valid within the current channel and for the lifespan of
+ the channel.
+ </doc>
+ <rule implement="MUST">
+ The client MUST NOT use access tickets except within the same
+ channel as originally granted.
+ </rule>
+ <rule implement="MUST">
+ The server MUST isolate access tickets per channel and treat an
+ attempt by a client to mix these as a connection exception.
+ </rule>
+ <chassis name="client" implement="MUST"/>
+ <field name="ticket" domain="access ticket"/>
+ </method>
+ </class>
+ <class name="exchange" handler="channel" index="40">
+ <!--
+======================================================
+== EXCHANGES (or "routers", if you prefer)
+== (Or matchers, plugins, extensions, agents,... Routing is just one of
+== the many fun things an exchange can do.)
+======================================================
+-->
+ work with exchanges
+<doc>
+ Exchanges match and distribute messages across queues. Exchanges can be
+ configured in the server or created at runtime.
+</doc>
+ <doc name="grammar">
+ exchange = C:DECLARE S:DECLARE-OK
+ / C:DELETE S:DELETE-OK
+</doc>
+ <chassis name="server" implement="MUST"/>
+ <chassis name="client" implement="MUST"/>
+ <rule implement="MUST">
+ <test>amq_exchange_19</test>
+ The server MUST implement the direct and fanout exchange types, and
+ predeclare the corresponding exchanges named amq.direct and amq.fanout
+ in each virtual host. The server MUST also predeclare a direct
+ exchange to act as the default exchange for content Publish methods
+ and for default queue bindings.
+</rule>
+ <rule implement="SHOULD">
+ <test>amq_exchange_20</test>
+ The server SHOULD implement the topic exchange type, and predeclare
+ the corresponding exchange named amq.topic in each virtual host.
+</rule>
+ <rule implement="MAY">
+ <test>amq_exchange_21</test>
+ The server MAY implement the system exchange type, and predeclare the
+ corresponding exchanges named amq.system in each virtual host. If the
+ client attempts to bind a queue to the system exchange, the server
+ MUST raise a connection exception with reply code 507 (not allowed).
+</rule>
+ <rule implement="MUST">
+ <test>amq_exchange_22</test>
+ The default exchange MUST be defined as internal, and be inaccessible
+ to the client except by specifying an empty exchange name in a content
+ Publish method. That is, the server MUST NOT let clients make explicit
+ bindings to this exchange.
+</rule>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="declare" synchronous="1" index="10">
+ declare exchange, create if needed
+ <doc>
+ This method creates an exchange if it does not already exist, and if the
+ exchange exists, verifies that it is of the correct and expected class.
+ </doc>
+ <rule implement="SHOULD">
+ <test>amq_exchange_23</test>
+ The server SHOULD support a minimum of 16 exchanges per virtual host
+ and ideally, impose no limit except as defined by available resources.
+ </rule>
+ <chassis name="server" implement="MUST"/>
+ <response name="declare-ok"/>
+ <field name="ticket" domain="access ticket">
+ <doc>
+ When a client defines a new exchange, this belongs to the access realm
+ of the ticket used. All further work done with that exchange must be
+ done with an access ticket for the same realm.
+ </doc>
+ <rule implement="MUST">
+ The client MUST provide a valid access ticket giving "active" access
+ to the realm in which the exchange exists or will be created, or
+ "passive" access if the if-exists flag is set.
+ </rule>
+ </field>
+ <field name="exchange" domain="exchange name">
+ <rule implement="MUST">
+ <test>amq_exchange_15</test>
+ Exchange names starting with "amq." are reserved for predeclared
+ and standardised exchanges. If the client attempts to create an
+ exchange starting with "amq.", the server MUST raise a channel
+ exception with reply code 403 (access refused).
+ </rule>
+ <assert check="regexp" value="^[a-zA-Z0-9-_.:]+$"/>
+ </field>
+ <field name="type" type="shortstr">
+ exchange type
+ <doc>
+ Each exchange belongs to one of a set of exchange types implemented
+ by the server. The exchange types define the functionality of the
+ exchange - i.e. how messages are routed through it. It is not valid
+ or meaningful to attempt to change the type of an existing exchange.
+ </doc>
+ <rule implement="MUST">
+ <test>amq_exchange_16</test>
+ If the exchange already exists with a different type, the server
+ MUST raise a connection exception with a reply code 507 (not allowed).
+ </rule>
+ <rule implement="MUST">
+ <test>amq_exchange_18</test>
+ If the server does not support the requested exchange type it MUST
+ raise a connection exception with a reply code 503 (command invalid).
+ </rule>
+ <assert check="regexp" value="^[a-zA-Z0-9-_.:]+$"/>
+ </field>
+ <field name="passive" type="bit">
+ do not create exchange
+ <doc>
+ If set, the server will not create the exchange. The client can use
+ this to check whether an exchange exists without modifying the server
+ state.
+ </doc>
+ <rule implement="MUST">
+ <test>amq_exchange_05</test>
+ If set, and the exchange does not already exist, the server MUST
+ raise a channel exception with reply code 404 (not found).
+ </rule>
+ </field>
+ <field name="durable" type="bit">
+ request a durable exchange
+ <doc>
+ If set when creating a new exchange, the exchange will be marked as
+ durable. Durable exchanges remain active when a server restarts.
+ Non-durable exchanges (transient exchanges) are purged if/when a
+ server restarts.
+ </doc>
+ <rule implement="MUST">
+ <test>amq_exchange_24</test>
+ The server MUST support both durable and transient exchanges.
+ </rule>
+ <rule implement="MUST">
+ The server MUST ignore the durable field if the exchange already
+ exists.
+ </rule>
+ </field>
+ <field name="auto delete" type="bit">
+ auto-delete when unused
+ <doc>
+ If set, the exchange is deleted when all queues have finished
+ using it.
+ </doc>
+ <rule implement="SHOULD">
+ <test>amq_exchange_02</test>
+ The server SHOULD allow for a reasonable delay between the point
+ when it determines that an exchange is not being used (or no longer
+ used), and the point when it deletes the exchange. At the least it
+ must allow a client to create an exchange and then bind a queue to
+ it, with a small but non-zero delay between these two actions.
+ </rule>
+ <rule implement="MUST">
+ <test>amq_exchange_25</test>
+ The server MUST ignore the auto-delete field if the exchange already
+ exists.
+ </rule>
+ </field>
+ <field name="internal" type="bit">
+ create internal exchange
+ <doc>
+ If set, the exchange may not be used directly by publishers, but
+ only when bound to other exchanges. Internal exchanges are used to
+ construct wiring that is not visible to applications.
+ </doc>
+ </field>
+
+ <field name = "nowait" type = "bit">
+ do not send a reply method
+ <doc>
+ If set, the server will not respond to the method. The client should
+ not wait for a reply method. If the server could not complete the
+ method it will raise a channel or connection exception.
+ </doc>
+ </field>
+
+ <field name="arguments" type="table">
+ arguments for declaration
+ <doc>
+ A set of arguments for the declaration. The syntax and semantics
+ of these arguments depends on the server implementation. This
+ field is ignored if passive is 1.
+ </doc>
+ </field>
+ </method>
+ <method name="declare-ok" synchronous="1" index="11">
+ confirms an exchange declaration
+ <doc>
+ This method confirms a Declare method and confirms the name of the
+ exchange, essential for automatically-named exchanges.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ </method>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="delete" synchronous="1" index="20">
+ delete an exchange
+ <doc>
+ This method deletes an exchange. When an exchange is deleted all queue
+ bindings on the exchange are cancelled.
+ </doc>
+ <chassis name="server" implement="MUST"/>
+ <response name="delete-ok"/>
+ <field name="ticket" domain="access ticket">
+ <rule implement="MUST">
+ The client MUST provide a valid access ticket giving "active"
+ access rights to the exchange's access realm.
+ </rule>
+ </field>
+ <field name="exchange" domain="exchange name">
+ <rule implement="MUST">
+ <test>amq_exchange_11</test>
+ The exchange MUST exist. Attempting to delete a non-existing exchange
+ causes a channel exception.
+ </rule>
+ <assert check="notnull"/>
+ </field>
+ <field name="if unused" type="bit">
+ delete only if unused
+ <doc>
+ If set, the server will only delete the exchange if it has no queue
+ bindings. If the exchange has queue bindings the server does not
+ delete it but raises a channel exception instead.
+ </doc>
+ <rule implement="SHOULD">
+ <test>amq_exchange_12</test>
+ If set, the server SHOULD delete the exchange but only if it has
+ no queue bindings.
+ </rule>
+ <rule implement="SHOULD">
+ <test>amq_exchange_13</test>
+ If set, the server SHOULD raise a channel exception if the exchange is in
+ use.
+ </rule>
+ </field>
+
+ <field name = "nowait" type = "bit">
+ do not send a reply method
+ <doc>
+ If set, the server will not respond to the method. The client should
+ not wait for a reply method. If the server could not complete the
+ method it will raise a channel or connection exception.
+ </doc>
+ </field>
+
+ </method>
+ <method name="delete-ok" synchronous="1" index="21">
+ confirm deletion of an exchange
+ <doc>
+ This method confirms the deletion of an exchange.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ </method>
+ </class>
+ <class name="queue" handler="channel" index="50">
+ <!--
+======================================================
+== QUEUES
+======================================================
+-->
+ work with queues
+
+<doc>
+ Queues store and forward messages. Queues can be configured in the server
+ or created at runtime. Queues must be attached to at least one exchange
+ in order to receive messages from publishers.
+</doc>
+ <doc name="grammar">
+ queue = C:DECLARE S:DECLARE-OK
+ / C:BIND S:BIND-OK
+ / C:PURGE S:PURGE-OK
+ / C:DELETE S:DELETE-OK
+</doc>
+ <chassis name="server" implement="MUST"/>
+ <chassis name="client" implement="MUST"/>
+ <rule implement="MUST">
+ <test>amq_queue_33</test>
+ A server MUST allow any content class to be sent to any queue, in any
+ mix, and queue and delivery these content classes independently. Note
+ that all methods that fetch content off queues are specific to a given
+ content class.
+</rule>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="declare" synchronous="1" index="10">
+ declare queue, create if needed
+ <doc>
+ This method creates or checks a queue. When creating a new queue
+ the client can specify various properties that control the durability
+ of the queue and its contents, and the level of sharing for the queue.
+ </doc>
+ <rule implement="MUST">
+ <test>amq_queue_34</test>
+ The server MUST create a default binding for a newly-created queue
+ to the default exchange, which is an exchange of type 'direct'.
+ </rule>
+ <rule implement="SHOULD">
+ <test>amq_queue_35</test>
+ The server SHOULD support a minimum of 256 queues per virtual host
+ and ideally, impose no limit except as defined by available resources.
+ </rule>
+ <chassis name="server" implement="MUST"/>
+ <response name="declare-ok"/>
+ <field name="ticket" domain="access ticket">
+ <doc>
+ When a client defines a new queue, this belongs to the access realm
+ of the ticket used. All further work done with that queue must be
+ done with an access ticket for the same realm.
+ </doc>
+ <doc>
+ The client provides a valid access ticket giving "active" access
+ to the realm in which the queue exists or will be created, or
+ "passive" access if the if-exists flag is set.
+ </doc>
+ </field>
+ <field name="queue" domain="queue name">
+ <rule implement="MAY">
+ <test>amq_queue_10</test>
+ The queue name MAY be empty, in which case the server MUST create
+ a new queue with a unique generated name and return this to the
+ client in the Declare-Ok method.
+ </rule>
+ <rule implement="MUST">
+ <test>amq_queue_32</test>
+ Queue names starting with "amq." are reserved for predeclared and
+ standardised server queues. If the queue name starts with "amq."
+ and the passive option is zero, the server MUST raise a connection
+ exception with reply code 403 (access refused).
+ </rule>
+ <assert check="regexp" value="^[a-zA-Z0-9-_.:]*$"/>
+ </field>
+ <field name="passive" type="bit">
+ do not create queue
+ <doc>
+ If set, the server will not create the queue. The client can use
+ this to check whether a queue exists without modifying the server
+ state.
+ </doc>
+ <rule implement="MUST">
+ <test>amq_queue_05</test>
+ If set, and the queue does not already exist, the server MUST
+ respond with a reply code 404 (not found) and raise a channel
+ exception.
+ </rule>
+ </field>
+ <field name="durable" type="bit">
+ request a durable queue
+ <doc>
+ If set when creating a new queue, the queue will be marked as
+ durable. Durable queues remain active when a server restarts.
+ Non-durable queues (transient queues) are purged if/when a
+ server restarts. Note that durable queues do not necessarily
+ hold persistent messages, although it does not make sense to
+ send persistent messages to a transient queue.
+ </doc>
+ <rule implement="MUST">
+ <test>amq_queue_03</test>
+ The server MUST recreate the durable queue after a restart.
+ </rule>
+ <rule implement="MUST">
+ <test>amq_queue_36</test>
+ The server MUST support both durable and transient queues.
+ </rule>
+ <rule implement="MUST">
+ <test>amq_queue_37</test>
+ The server MUST ignore the durable field if the queue already
+ exists.
+ </rule>
+ </field>
+ <field name="exclusive" type="bit">
+ request an exclusive queue
+ <doc>
+ Exclusive queues may only be consumed from by the current connection.
+ Setting the 'exclusive' flag always implies 'auto-delete'.
+ </doc>
+ <rule implement="MUST">
+ <test>amq_queue_38</test>
+ The server MUST support both exclusive (private) and non-exclusive
+ (shared) queues.
+ </rule>
+ <rule implement="MUST">
+ <test>amq_queue_04</test>
+ The server MUST raise a channel exception if 'exclusive' is specified
+ and the queue already exists and is owned by a different connection.
+ </rule>
+ </field>
+ <field name="auto delete" type="bit">
+ auto-delete queue when unused
+ <doc>
+ If set, the queue is deleted when all consumers have finished
+ using it. Last consumer can be cancelled either explicitly or because
+ its channel is closed. If there was no consumer ever on the queue, it
+ won't be deleted.
+ </doc>
+ <rule implement="SHOULD">
+ <test>amq_queue_02</test>
+ The server SHOULD allow for a reasonable delay between the point
+ when it determines that a queue is not being used (or no longer
+ used), and the point when it deletes the queue. At the least it
+ must allow a client to create a queue and then create a consumer
+ to read from it, with a small but non-zero delay between these
+ two actions. The server should equally allow for clients that may
+ be disconnected prematurely, and wish to re-consume from the same
+ queue without losing messages. We would recommend a configurable
+ timeout, with a suitable default value being one minute.
+ </rule>
+ <rule implement="MUST">
+ <test>amq_queue_31</test>
+ The server MUST ignore the auto-delete field if the queue already
+ exists.
+ </rule>
+ </field>
+ <field name = "nowait" type = "bit">
+ do not send a reply method
+ <doc>
+ If set, the server will not respond to the method. The client should
+ not wait for a reply method. If the server could not complete the
+ method it will raise a channel or connection exception.
+ </doc>
+ </field>
+
+ <field name="arguments" type="table">
+ arguments for declaration
+ <doc>
+ A set of arguments for the declaration. The syntax and semantics
+ of these arguments depends on the server implementation. This
+ field is ignored if passive is 1.
+ </doc>
+ </field>
+ </method>
+ <method name="declare-ok" synchronous="1" index="11">
+ confirms a queue definition
+ <doc>
+ This method confirms a Declare method and confirms the name of the
+ queue, essential for automatically-named queues.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ <field name="queue" domain="queue name">
+ <doc>
+ Reports the name of the queue. If the server generated a queue
+ name, this field contains that name.
+ </doc>
+ <assert check="notnull"/>
+ </field>
+ <field name="message count" type="long">
+ number of messages in queue
+ <doc>
+ Reports the number of messages in the queue, which will be zero
+ for newly-created queues.
+ </doc>
+ </field>
+ <field name="consumer count" type="long">
+ number of consumers
+ <doc>
+ Reports the number of active consumers for the queue. Note that
+ consumers can suspend activity (Channel.Flow) in which case they
+ do not appear in this count.
+ </doc>
+ </field>
+ </method>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="bind" synchronous="1" index="20">
+ bind queue to an exchange
+ <doc>
+ This method binds a queue to an exchange. Until a queue is
+ bound it will not receive any messages. In a classic messaging
+ model, store-and-forward queues are bound to a dest exchange
+ and subscription queues are bound to a dest_wild exchange.
+ </doc>
+ <rule implement="MUST">
+ <test>amq_queue_25</test>
+ A server MUST allow ignore duplicate bindings - that is, two or
+ more bind methods for a specific queue, with identical arguments
+ - without treating these as an error.
+ </rule>
+ <rule implement="MUST">
+ <test>amq_queue_39</test>
+ If a bind fails, the server MUST raise a connection exception.
+ </rule>
+ <rule implement="MUST">
+ <test>amq_queue_12</test>
+ The server MUST NOT allow a durable queue to bind to a transient
+ exchange. If the client attempts this the server MUST raise a
+ channel exception.
+ </rule>
+ <rule implement="SHOULD">
+ <test>amq_queue_13</test>
+ Bindings for durable queues are automatically durable and the
+ server SHOULD restore such bindings after a server restart.
+ </rule>
+ <rule implement="MUST">
+ <test>amq_queue_17</test>
+ If the client attempts to an exchange that was declared as internal,
+ the server MUST raise a connection exception with reply code 530
+ (not allowed).
+ </rule>
+ <rule implement="SHOULD">
+ <test>amq_queue_40</test>
+ The server SHOULD support at least 4 bindings per queue, and
+ ideally, impose no limit except as defined by available resources.
+ </rule>
+ <chassis name="server" implement="MUST"/>
+ <response name="bind-ok"/>
+ <field name="ticket" domain="access ticket">
+ <doc>
+ The client provides a valid access ticket giving "active"
+ access rights to the queue's access realm.
+ </doc>
+ </field>
+
+ <field name = "queue" domain = "queue name">
+ <doc>
+ Specifies the name of the queue to bind. If the queue name is
+ empty, refers to the current queue for the channel, which is
+ the last declared queue.
+ </doc>
+ <doc name = "rule">
+ If the client did not previously declare a queue, and the queue
+ name in this method is empty, the server MUST raise a connection
+ exception with reply code 530 (not allowed).
+ </doc>
+ <doc name = "rule" test = "amq_queue_26">
+ If the queue does not exist the server MUST raise a channel exception
+ with reply code 404 (not found).
+ </doc>
+ </field>
+
+ <field name="exchange" domain="exchange name">
+ The name of the exchange to bind to.
+ <rule implement="MUST">
+ <test>amq_queue_14</test>
+ If the exchange does not exist the server MUST raise a channel
+ exception with reply code 404 (not found).
+ </rule>
+ </field>
+ <field name="routing key" type="shortstr">
+ message routing key
+ <doc>
+ Specifies the routing key for the binding. The routing key is
+ used for routing messages depending on the exchange configuration.
+ Not all exchanges use a routing key - refer to the specific
+ exchange documentation. If the routing key is empty and the queue
+ name is empty, the routing key will be the current queue for the
+ channel, which is the last declared queue.
+ </doc>
+ </field>
+
+ <field name = "nowait" type = "bit">
+ do not send a reply method
+ <doc>
+ If set, the server will not respond to the method. The client should
+ not wait for a reply method. If the server could not complete the
+ method it will raise a channel or connection exception.
+ </doc>
+ </field>
+
+ <field name="arguments" type="table">
+ arguments for binding
+ <doc>
+ A set of arguments for the binding. The syntax and semantics of
+ these arguments depends on the exchange class.
+ </doc>
+ </field>
+ </method>
+ <method name="bind-ok" synchronous="1" index="21">
+ confirm bind successful
+ <doc>
+ This method confirms that the bind was successful.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ </method>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="purge" synchronous="1" index="30">
+ purge a queue
+ <doc>
+ This method removes all messages from a queue. It does not cancel
+ consumers. Purged messages are deleted without any formal "undo"
+ mechanism.
+ </doc>
+ <rule implement="MUST">
+ <test>amq_queue_15</test>
+ A call to purge MUST result in an empty queue.
+ </rule>
+ <rule implement="MUST">
+ <test>amq_queue_41</test>
+ On transacted channels the server MUST not purge messages that have
+ already been sent to a client but not yet acknowledged.
+ </rule>
+ <rule implement="MAY">
+ <test>amq_queue_42</test>
+ The server MAY implement a purge queue or log that allows system
+ administrators to recover accidentally-purged messages. The server
+ SHOULD NOT keep purged messages in the same storage spaces as the
+ live messages since the volumes of purged messages may get very
+ large.
+ </rule>
+ <chassis name="server" implement="MUST"/>
+ <response name="purge-ok"/>
+ <field name="ticket" domain="access ticket">
+ <doc>
+ The access ticket must be for the access realm that holds the
+ queue.
+ </doc>
+ <rule implement="MUST">
+ The client MUST provide a valid access ticket giving "read" access
+ rights to the queue's access realm. Note that purging a queue is
+ equivalent to reading all messages and discarding them.
+ </rule>
+ </field>
+ <field name = "queue" domain = "queue name">
+ <doc>
+ Specifies the name of the queue to purge. If the queue name is
+ empty, refers to the current queue for the channel, which is
+ the last declared queue.
+ </doc>
+ <doc name = "rule">
+ If the client did not previously declare a queue, and the queue
+ name in this method is empty, the server MUST raise a connection
+ exception with reply code 530 (not allowed).
+ </doc>
+ <doc name = "rule" test = "amq_queue_16">
+ The queue must exist. Attempting to purge a non-existing queue
+ causes a channel exception.
+ </doc>
+ </field>
+
+ <field name = "nowait" type = "bit">
+ do not send a reply method
+ <doc>
+ If set, the server will not respond to the method. The client should
+ not wait for a reply method. If the server could not complete the
+ method it will raise a channel or connection exception.
+ </doc>
+ </field>
+ </method>
+ <method name="purge-ok" synchronous="1" index="31">
+ confirms a queue purge
+ <doc>
+ This method confirms the purge of a queue.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ <field name="message count" type="long">
+ number of messages purged
+ <doc>
+ Reports the number of messages purged.
+ </doc>
+ </field>
+ </method>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="delete" synchronous="1" index="40">
+ delete a queue
+ <doc>
+ This method deletes a queue. When a queue is deleted any pending
+ messages are sent to a dead-letter queue if this is defined in the
+ server configuration, and all consumers on the queue are cancelled.
+ </doc>
+ <rule implement="SHOULD">
+ <test>amq_queue_43</test>
+ The server SHOULD use a dead-letter queue to hold messages that
+ were pending on a deleted queue, and MAY provide facilities for
+ a system administrator to move these messages back to an active
+ queue.
+ </rule>
+ <chassis name="server" implement="MUST"/>
+ <response name="delete-ok"/>
+ <field name="ticket" domain="access ticket">
+ <doc>
+ The client provides a valid access ticket giving "active"
+ access rights to the queue's access realm.
+ </doc>
+ </field>
+
+ <field name = "queue" domain = "queue name">
+ <doc>
+ Specifies the name of the queue to delete. If the queue name is
+ empty, refers to the current queue for the channel, which is the
+ last declared queue.
+ </doc>
+ <doc name = "rule">
+ If the client did not previously declare a queue, and the queue
+ name in this method is empty, the server MUST raise a connection
+ exception with reply code 530 (not allowed).
+ </doc>
+ <doc name = "rule" test = "amq_queue_21">
+ The queue must exist. Attempting to delete a non-existing queue
+ causes a channel exception.
+ </doc>
+ </field>
+
+ <field name="if unused" type="bit">
+ delete only if unused
+ <doc>
+ If set, the server will only delete the queue if it has no
+ consumers. If the queue has consumers the server does does not
+ delete it but raises a channel exception instead.
+ </doc>
+ <rule implement="MUST">
+ <test>amq_queue_29</test>
+ <test>amq_queue_30</test>
+ The server MUST respect the if-unused flag when deleting a queue.
+ </rule>
+ </field>
+ <field name="if empty" type="bit">
+ delete only if empty
+ <test>amq_queue_27</test>
+ <doc>
+ If set, the server will only delete the queue if it has no
+ messages. If the queue is not empty the server raises a channel
+ exception.
+ </doc>
+ </field>
+ <field name = "nowait" type = "bit">
+ do not send a reply method
+ <doc>
+ If set, the server will not respond to the method. The client should
+ not wait for a reply method. If the server could not complete the
+ method it will raise a channel or connection exception.
+ </doc>
+ </field>
+ </method>
+
+ <method name="delete-ok" synchronous="1" index="41">
+ confirm deletion of a queue
+ <doc>
+ This method confirms the deletion of a queue.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ <field name="message count" type="long">
+ number of messages purged
+ <doc>
+ Reports the number of messages purged.
+ </doc>
+ </field>
+ </method>
+ </class>
+ <class name="basic" handler="channel" index="60">
+ <!--
+======================================================
+== BASIC MIDDLEWARE
+======================================================
+-->
+ work with basic content
+<doc>
+ The Basic class provides methods that support an industry-standard
+ messaging model.
+</doc>
+
+<doc name = "grammar">
+ basic = C:QOS S:QOS-OK
+ / C:CONSUME S:CONSUME-OK
+ / C:CANCEL S:CANCEL-OK
+ / C:PUBLISH content
+ / S:RETURN content
+ / S:DELIVER content
+ / C:GET ( S:GET-OK content / S:GET-EMPTY )
+ / C:ACK
+ / C:REJECT
+</doc>
+
+<chassis name = "server" implement = "MUST" />
+<chassis name = "client" implement = "MAY" />
+
+<doc name = "rule" test = "amq_basic_08">
+ The server SHOULD respect the persistent property of basic messages
+ and SHOULD make a best-effort to hold persistent basic messages on a
+ reliable storage mechanism.
+</doc>
+<doc name = "rule" test = "amq_basic_09">
+ The server MUST NOT discard a persistent basic message in case of a
+ queue overflow. The server MAY use the Channel.Flow method to slow
+ or stop a basic message publisher when necessary.
+</doc>
+<doc name = "rule" test = "amq_basic_10">
+ The server MAY overflow non-persistent basic messages to persistent
+ storage and MAY discard or dead-letter non-persistent basic messages
+ on a priority basis if the queue size exceeds some configured limit.
+</doc>
+<doc name = "rule" test = "amq_basic_11">
+ The server MUST implement at least 2 priority levels for basic
+ messages, where priorities 0-4 and 5-9 are treated as two distinct
+ levels. The server MAY implement up to 10 priority levels.
+</doc>
+<doc name = "rule" test = "amq_basic_12">
+ The server MUST deliver messages of the same priority in order
+ irrespective of their individual persistence.
+</doc>
+<doc name = "rule" test = "amq_basic_13">
+ The server MUST support both automatic and explicit acknowledgements
+ on Basic content.
+</doc>
+
+<!-- These are the properties for a Basic content -->
+
+<field name = "content type" type = "shortstr">
+ MIME content type
+</field>
+<field name = "content encoding" type = "shortstr">
+ MIME content encoding
+</field>
+<field name = "headers" type = "table">
+ Message header field table
+</field>
+<field name = "delivery mode" type = "octet">
+ Non-persistent (1) or persistent (2)
+</field>
+<field name = "priority" type = "octet">
+ The message priority, 0 to 9
+</field>
+<field name = "correlation id" type = "shortstr">
+ The application correlation identifier
+</field>
+<field name = "reply to" type = "shortstr">
+ The destination to reply to
+</field>
+<field name = "expiration" type = "shortstr">
+ Message expiration specification
+</field>
+<field name = "message id" type = "shortstr">
+ The application message identifier
+</field>
+<field name = "timestamp" type = "timestamp">
+ The message timestamp
+</field>
+<field name = "type" type = "shortstr">
+ The message type name
+</field>
+<field name = "user id" type = "shortstr">
+ The creating user id
+</field>
+<field name = "app id" type = "shortstr">
+ The creating application id
+</field>
+<field name = "cluster id" type = "shortstr">
+ Intra-cluster routing identifier
+</field>
+
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "qos" synchronous = "1" index = "10">
+ specify quality of service
+ <doc>
+ This method requests a specific quality of service. The QoS can
+ be specified for the current channel or for all channels on the
+ connection. The particular properties and semantics of a qos method
+ always depend on the content class semantics. Though the qos method
+ could in principle apply to both peers, it is currently meaningful
+ only for the server.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "qos-ok" />
+
+ <field name = "prefetch size" type = "long">
+ prefetch window in octets
+ <doc>
+ The client can request that messages be sent in advance so that
+ when the client finishes processing a message, the following
+ message is already held locally, rather than needing to be sent
+ down the channel. Prefetching gives a performance improvement.
+ This field specifies the prefetch window size in octets. The
+ server will send a message in advance if it is equal to or
+ smaller in size than the available prefetch size (and also falls
+ into other prefetch limits). May be set to zero, meaning "no
+ specific limit", although other prefetch limits may still apply.
+ The prefetch-size is ignored if the no-ack option is set.
+ </doc>
+ <doc name = "rule" test = "amq_basic_17">
+ The server MUST ignore this setting when the client is not
+ processing any messages - i.e. the prefetch size does not limit
+ the transfer of single messages to a client, only the sending in
+ advance of more messages while the client still has one or more
+ unacknowledged messages.
+ </doc>
+ </field>
+
+ <field name = "prefetch count" type = "short">
+ prefetch window in messages
+ <doc>
+ Specifies a prefetch window in terms of whole messages. This
+ field may be used in combination with the prefetch-size field;
+ a message will only be sent in advance if both prefetch windows
+ (and those at the channel and connection level) allow it.
+ The prefetch-count is ignored if the no-ack option is set.
+ </doc>
+ <doc name = "rule" test = "amq_basic_18">
+ The server MAY send less data in advance than allowed by the
+ client's specified prefetch windows but it MUST NOT send more.
+ </doc>
+ </field>
+
+ <field name = "global" type = "bit">
+ apply to entire connection
+ <doc>
+ By default the QoS settings apply to the current channel only. If
+ this field is set, they are applied to the entire connection.
+ </doc>
+ </field>
+</method>
+
+<method name = "qos-ok" synchronous = "1" index = "11">
+ confirm the requested qos
+ <doc>
+ This method tells the client that the requested QoS levels could
+ be handled by the server. The requested QoS applies to all active
+ consumers until a new QoS is defined.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+</method>
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "consume" synchronous = "1" index = "20">
+ start a queue consumer
+ <doc>
+ This method asks the server to start a "consumer", which is a
+ transient request for messages from a specific queue. Consumers
+ last as long as the channel they were created on, or until the
+ client cancels them.
+ </doc>
+ <doc name = "rule" test = "amq_basic_01">
+ The server SHOULD support at least 16 consumers per queue, unless
+ the queue was declared as private, and ideally, impose no limit
+ except as defined by available resources.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "consume-ok" />
+
+ <field name = "ticket" domain = "access ticket">
+ <doc name = "rule">
+ The client MUST provide a valid access ticket giving "read" access
+ rights to the realm for the queue.
+ </doc>
+ </field>
+
+ <field name = "queue" domain = "queue name">
+ <doc>
+ Specifies the name of the queue to consume from. If the queue name
+ is null, refers to the current queue for the channel, which is the
+ last declared queue.
+ </doc>
+ <doc name = "rule">
+ If the client did not previously declare a queue, and the queue name
+ in this method is empty, the server MUST raise a connection exception
+ with reply code 530 (not allowed).
+ </doc>
+ </field>
+
+ <field name = "consumer tag" domain = "consumer tag">
+ <doc>
+ Specifies the identifier for the consumer. The consumer tag is
+ local to a connection, so two clients can use the same consumer
+ tags. If this field is empty the server will generate a unique
+ tag.
+ </doc>
+ <doc name = "rule" test = "todo">
+ The tag MUST NOT refer to an existing consumer. If the client
+ attempts to create two consumers with the same non-empty tag
+ the server MUST raise a connection exception with reply code
+ 530 (not allowed).
+ </doc>
+ </field>
+
+ <field name = "no local" domain = "no local" />
+
+ <field name = "no ack" domain = "no ack" />
+
+ <field name = "exclusive" type = "bit">
+ request exclusive access
+ <doc>
+ Request exclusive consumer access, meaning only this consumer can
+ access the queue.
+ </doc>
+ <doc name = "rule" test = "amq_basic_02">
+ If the server cannot grant exclusive access to the queue when asked,
+ - because there are other consumers active - it MUST raise a channel
+ exception with return code 403 (access refused).
+ </doc>
+ </field>
+
+ <field name = "nowait" type = "bit">
+ do not send a reply method
+ <doc>
+ If set, the server will not respond to the method. The client should
+ not wait for a reply method. If the server could not complete the
+ method it will raise a channel or connection exception.
+ </doc>
+ </field>
+</method>
+
+<method name = "consume-ok" synchronous = "1" index = "21">
+ confirm a new consumer
+ <doc>
+ The server provides the client with a consumer tag, which is used
+ by the client for methods called on the consumer at a later stage.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer tag" domain = "consumer tag">
+ <doc>
+ Holds the consumer tag specified by the client or provided by
+ the server.
+ </doc>
+ </field>
+</method>
+
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "cancel" synchronous = "1" index = "30">
+ end a queue consumer
+ <doc test = "amq_basic_04">
+ This method cancels a consumer. This does not affect already
+ delivered messages, but it does mean the server will not send any
+ more messages for that consumer. The client may receive an
+ abitrary number of messages in between sending the cancel method
+ and receiving the cancel-ok reply.
+ </doc>
+ <doc name = "rule" test = "todo">
+ If the queue no longer exists when the client sends a cancel command,
+ or the consumer has been cancelled for other reasons, this command
+ has no effect.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "cancel-ok" />
+
+ <field name = "consumer tag" domain = "consumer tag" />
+
+ <field name = "nowait" type = "bit">
+ do not send a reply method
+ <doc>
+ If set, the server will not respond to the method. The client should
+ not wait for a reply method. If the server could not complete the
+ method it will raise a channel or connection exception.
+ </doc>
+ </field>
+</method>
+
+<method name = "cancel-ok" synchronous = "1" index = "31">
+ confirm a cancelled consumer
+ <doc>
+ This method confirms that the cancellation was completed.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer tag" domain = "consumer tag" />
+</method>
+
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "publish" content = "1" index = "40">
+ publish a message
+ <doc>
+ This method publishes a message to a specific exchange. The message
+ will be routed to queues as defined by the exchange configuration
+ and distributed to any active consumers when the transaction, if any,
+ is committed.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "ticket" domain = "access ticket">
+ <doc name = "rule">
+ The client MUST provide a valid access ticket giving "write"
+ access rights to the access realm for the exchange.
+ </doc>
+ </field>
+
+ <field name = "exchange" domain = "exchange name">
+ <doc>
+ Specifies the name of the exchange to publish to. The exchange
+ name can be empty, meaning the default exchange. If the exchange
+ name is specified, and that exchange does not exist, the server
+ will raise a channel exception.
+ </doc>
+ <doc name = "rule" test = "amq_basic_06">
+ The server MUST accept a blank exchange name to mean the default
+ exchange.
+ </doc>
+ <doc name = "rule" test = "amq_basic_14">
+ If the exchange was declared as an internal exchange, the server
+ MUST raise a channel exception with a reply code 403 (access
+ refused).
+ </doc>
+ <doc name = "rule" test = "amq_basic_15">
+ The exchange MAY refuse basic content in which case it MUST raise
+ a channel exception with reply code 540 (not implemented).
+ </doc>
+ </field>
+
+ <field name = "routing key" type = "shortstr">
+ Message routing key
+ <doc>
+ Specifies the routing key for the message. The routing key is
+ used for routing messages depending on the exchange configuration.
+ </doc>
+ </field>
+
+ <field name = "mandatory" type = "bit">
+ indicate mandatory routing
+ <doc>
+ This flag tells the server how to react if the message cannot be
+ routed to a queue. If this flag is set, the server will return an
+ unroutable message with a Return method. If this flag is zero, the
+ server silently drops the message.
+ </doc>
+ <doc name = "rule" test = "amq_basic_07">
+ The server SHOULD implement the mandatory flag.
+ </doc>
+ </field>
+
+ <field name = "immediate" type = "bit">
+ request immediate delivery
+ <doc>
+ This flag tells the server how to react if the message cannot be
+ routed to a queue consumer immediately. If this flag is set, the
+ server will return an undeliverable message with a Return method.
+ If this flag is zero, the server will queue the message, but with
+ no guarantee that it will ever be consumed.
+ </doc>
+ <doc name = "rule" test = "amq_basic_16">
+ The server SHOULD implement the immediate flag.
+ </doc>
+ </field>
+</method>
+
+<method name = "return" content = "1" index = "50">
+ return a failed message
+ <doc>
+ This method returns an undeliverable message that was published
+ with the "immediate" flag set, or an unroutable message published
+ with the "mandatory" flag set. The reply code and text provide
+ information about the reason that the message was undeliverable.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "reply code" domain = "reply code" />
+ <field name = "reply text" domain = "reply text" />
+
+ <field name = "exchange" domain = "exchange name">
+ <doc>
+ Specifies the name of the exchange that the message was
+ originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing key" type = "shortstr">
+ Message routing key
+ <doc>
+ Specifies the routing key name specified when the message was
+ published.
+ </doc>
+ </field>
+</method>
+
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "deliver" content = "1" index = "60">
+ notify the client of a consumer message
+ <doc>
+ This method delivers a message to the client, via a consumer. In
+ the asynchronous message delivery model, the client starts a
+ consumer using the Consume method, then the server responds with
+ Deliver methods as and when messages arrive for that consumer.
+ </doc>
+ <doc name = "rule" test = "amq_basic_19">
+ The server SHOULD track the number of times a message has been
+ delivered to clients and when a message is redelivered a certain
+ number of times - e.g. 5 times - without being acknowledged, the
+ server SHOULD consider the message to be unprocessable (possibly
+ causing client applications to abort), and move the message to a
+ dead letter queue.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer tag" domain = "consumer tag" />
+
+ <field name = "delivery tag" domain = "delivery tag" />
+
+ <field name = "redelivered" domain = "redelivered" />
+
+ <field name = "exchange" domain = "exchange name">
+ <doc>
+ Specifies the name of the exchange that the message was
+ originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing key" type = "shortstr">
+ Message routing key
+ <doc>
+ Specifies the routing key name specified when the message was
+ published.
+ </doc>
+ </field>
+</method>
+
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "get" synchronous = "1" index = "70">
+ direct access to a queue
+ <doc>
+ This method provides a direct access to the messages in a queue
+ using a synchronous dialogue that is designed for specific types of
+ application where synchronous functionality is more important than
+ performance.
+ </doc>
+ <response name = "get-ok" />
+ <response name = "get-empty" />
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "ticket" domain = "access ticket">
+ <doc name = "rule">
+ The client MUST provide a valid access ticket giving "read"
+ access rights to the realm for the queue.
+ </doc>
+ </field>
+
+ <field name = "queue" domain = "queue name">
+ <doc>
+ Specifies the name of the queue to consume from. If the queue name
+ is null, refers to the current queue for the channel, which is the
+ last declared queue.
+ </doc>
+ <doc name = "rule">
+ If the client did not previously declare a queue, and the queue name
+ in this method is empty, the server MUST raise a connection exception
+ with reply code 530 (not allowed).
+ </doc>
+ </field>
+
+ <field name = "no ack" domain = "no ack" />
+</method>
+
+<method name = "get-ok" synchronous = "1" content = "1" index = "71">
+ provide client with a message
+ <doc>
+ This method delivers a message to the client following a get
+ method. A message delivered by 'get-ok' must be acknowledged
+ unless the no-ack option was set in the get method.
+ </doc>
+ <chassis name = "client" implement = "MAY" />
+
+ <field name = "delivery tag" domain = "delivery tag" />
+
+ <field name = "redelivered" domain = "redelivered" />
+
+ <field name = "exchange" domain = "exchange name">
+ <doc>
+ Specifies the name of the exchange that the message was originally
+ published to. If empty, the message was published to the default
+ exchange.
+ </doc>
+ </field>
+
+ <field name = "routing key" type = "shortstr">
+ Message routing key
+ <doc>
+ Specifies the routing key name specified when the message was
+ published.
+ </doc>
+ </field>
+
+ <field name = "message count" type = "long" >
+ number of messages pending
+ <doc>
+ This field reports the number of messages pending on the queue,
+ excluding the message being delivered. Note that this figure is
+ indicative, not reliable, and can change arbitrarily as messages
+ are added to the queue and removed by other clients.
+ </doc>
+ </field>
+</method>
+
+
+<method name = "get-empty" synchronous = "1" index = "72">
+ indicate no messages available
+ <doc>
+ This method tells the client that the queue has no messages
+ available for the client.
+ </doc>
+ <chassis name = "client" implement = "MAY" />
+
+ <field name = "cluster id" type = "shortstr">
+ Cluster id
+ <doc>
+ For use by cluster applications, should not be used by
+ client applications.
+ </doc>
+ </field>
+</method>
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "ack" index = "80">
+ acknowledge one or more messages
+ <doc>
+ This method acknowledges one or more messages delivered via the
+ Deliver or Get-Ok methods. The client can ask to confirm a
+ single message or a set of messages up to and including a specific
+ message.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <field name = "delivery tag" domain = "delivery tag" />
+
+ <field name = "multiple" type = "bit">
+ acknowledge multiple messages
+ <doc>
+ If set to 1, the delivery tag is treated as "up to and including",
+ so that the client can acknowledge multiple messages with a single
+ method. If set to zero, the delivery tag refers to a single
+ message. If the multiple field is 1, and the delivery tag is zero,
+ tells the server to acknowledge all outstanding mesages.
+ </doc>
+ <doc name = "rule" test = "amq_basic_20">
+ The server MUST validate that a non-zero delivery-tag refers to an
+ delivered message, and raise a channel exception if this is not the
+ case.
+ </doc>
+ </field>
+</method>
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "reject" index = "90">
+ reject an incoming message
+ <doc>
+ This method allows a client to reject a message. It can be used to
+ interrupt and cancel large incoming messages, or return untreatable
+ messages to their original queue.
+ </doc>
+ <doc name = "rule" test = "amq_basic_21">
+ The server SHOULD be capable of accepting and process the Reject
+ method while sending message content with a Deliver or Get-Ok
+ method. I.e. the server should read and process incoming methods
+ while sending output frames. To cancel a partially-send content,
+ the server sends a content body frame of size 1 (i.e. with no data
+ except the frame-end octet).
+ </doc>
+ <doc name = "rule" test = "amq_basic_22">
+ The server SHOULD interpret this method as meaning that the client
+ is unable to process the message at this time.
+ </doc>
+ <doc name = "rule">
+ A client MUST NOT use this method as a means of selecting messages
+ to process. A rejected message MAY be discarded or dead-lettered,
+ not necessarily passed to another client.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "delivery tag" domain = "delivery tag" />
+
+ <field name = "requeue" type = "bit">
+ requeue the message
+ <doc>
+ If this field is zero, the message will be discarded. If this bit
+ is 1, the server will attempt to requeue the message.
+ </doc>
+ <doc name = "rule" test = "amq_basic_23">
+ The server MUST NOT deliver the message to the same client within
+ the context of the current channel. The recommended strategy is
+ to attempt to deliver the message to an alternative consumer, and
+ if that is not possible, to move the message to a dead-letter
+ queue. The server MAY use more sophisticated tracking to hold
+ the message on the queue and redeliver it to the same client at
+ a later stage.
+ </doc>
+ </field>
+</method>
+
+<method name = "recover" index = "100">
+ redeliver unacknowledged messages
+ <doc>
+ This method asks the broker to redeliver all unacknowledged messages on a
+ specified channel. Zero or more messages may be redelivered. This method
+ is only allowed on non-transacted channels.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "requeue" type = "bit">
+ requeue the message
+ <doc>
+ If this field is zero, the message will be redelivered to the original
+ recipient. If this bit is 1, the server will attempt to requeue the
+ message, potentially then delivering it to an alternative subscriber.
+ </doc>
+ </field>
+ <doc name="rule">
+ The server MUST set the redelivered flag on all messages that are resent.
+ </doc>
+ <doc name="rule">
+ The server MUST raise a channel exception if this is called on a
+ transacted channel.
+ </doc>
+</method>
+
+</class>
+
+
+ <class name="file" handler="channel" index="70">
+ <!--
+======================================================
+== FILE TRANSFER
+======================================================
+-->
+ work with file content
+<doc>
+ The file class provides methods that support reliable file transfer.
+ File messages have a specific set of properties that are required for
+ interoperability with file transfer applications. File messages and
+ acknowledgements are subject to channel transactions. Note that the
+ file class does not provide message browsing methods; these are not
+ compatible with the staging model. Applications that need browsable
+ file transfer should use Basic content and the Basic class.
+</doc>
+
+<doc name = "grammar">
+ file = C:QOS S:QOS-OK
+ / C:CONSUME S:CONSUME-OK
+ / C:CANCEL S:CANCEL-OK
+ / C:OPEN S:OPEN-OK C:STAGE content
+ / S:OPEN C:OPEN-OK S:STAGE content
+ / C:PUBLISH
+ / S:DELIVER
+ / S:RETURN
+ / C:ACK
+ / C:REJECT
+</doc>
+
+<chassis name = "server" implement = "MAY" />
+<chassis name = "client" implement = "MAY" />
+
+<doc name = "rule">
+ The server MUST make a best-effort to hold file messages on a
+ reliable storage mechanism.
+</doc>
+<doc name = "rule">
+ The server MUST NOT discard a file message in case of a queue
+ overflow. The server MUST use the Channel.Flow method to slow or stop
+ a file message publisher when necessary.
+</doc>
+<doc name = "rule">
+ The server MUST implement at least 2 priority levels for file
+ messages, where priorities 0-4 and 5-9 are treated as two distinct
+ levels. The server MAY implement up to 10 priority levels.
+</doc>
+<doc name = "rule">
+ The server MUST support both automatic and explicit acknowledgements
+ on file content.
+</doc>
+
+<!-- These are the properties for a File content -->
+
+<field name = "content type" type = "shortstr">
+ MIME content type
+</field>
+<field name = "content encoding" type = "shortstr">
+ MIME content encoding
+</field>
+<field name = "headers" type = "table">
+ Message header field table
+</field>
+<field name = "priority" type = "octet">
+ The message priority, 0 to 9
+</field>
+<field name = "reply to" type = "shortstr">
+ The destination to reply to
+</field>
+<field name = "message id" type = "shortstr">
+ The application message identifier
+</field>
+<field name = "filename" type = "shortstr">
+ The message filename
+</field>
+<field name = "timestamp" type = "timestamp">
+ The message timestamp
+</field>
+<field name = "cluster id" type = "shortstr">
+ Intra-cluster routing identifier
+</field>
+
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "qos" synchronous = "1" index = "10">
+ specify quality of service
+ <doc>
+ This method requests a specific quality of service. The QoS can
+ be specified for the current channel or for all channels on the
+ connection. The particular properties and semantics of a qos method
+ always depend on the content class semantics. Though the qos method
+ could in principle apply to both peers, it is currently meaningful
+ only for the server.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "qos-ok" />
+
+ <field name = "prefetch size" type = "long">
+ prefetch window in octets
+ <doc>
+ The client can request that messages be sent in advance so that
+ when the client finishes processing a message, the following
+ message is already held locally, rather than needing to be sent
+ down the channel. Prefetching gives a performance improvement.
+ This field specifies the prefetch window size in octets. May be
+ set to zero, meaning "no specific limit". Note that other
+ prefetch limits may still apply. The prefetch-size is ignored
+ if the no-ack option is set.
+ </doc>
+ </field>
+
+ <field name = "prefetch count" type = "short">
+ prefetch window in messages
+ <doc>
+ Specifies a prefetch window in terms of whole messages. This
+ is compatible with some file API implementations. This field
+ may be used in combination with the prefetch-size field; a
+ message will only be sent in advance if both prefetch windows
+ (and those at the channel and connection level) allow it.
+ The prefetch-count is ignored if the no-ack option is set.
+ </doc>
+ <doc name = "rule">
+ The server MAY send less data in advance than allowed by the
+ client's specified prefetch windows but it MUST NOT send more.
+ </doc>
+ </field>
+
+ <field name = "global" type = "bit">
+ apply to entire connection
+ <doc>
+ By default the QoS settings apply to the current channel only. If
+ this field is set, they are applied to the entire connection.
+ </doc>
+ </field>
+</method>
+
+<method name = "qos-ok" synchronous = "1" index = "11">
+ confirm the requested qos
+ <doc>
+ This method tells the client that the requested QoS levels could
+ be handled by the server. The requested QoS applies to all active
+ consumers until a new QoS is defined.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+</method>
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "consume" synchronous = "1" index = "20">
+ start a queue consumer
+ <doc>
+ This method asks the server to start a "consumer", which is a
+ transient request for messages from a specific queue. Consumers
+ last as long as the channel they were created on, or until the
+ client cancels them.
+ </doc>
+ <doc name = "rule">
+ The server SHOULD support at least 16 consumers per queue, unless
+ the queue was declared as private, and ideally, impose no limit
+ except as defined by available resources.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "consume-ok" />
+
+ <field name = "ticket" domain = "access ticket">
+ <doc name = "rule">
+ The client MUST provide a valid access ticket giving "read" access
+ rights to the realm for the queue.
+ </doc>
+ </field>
+
+ <field name = "queue" domain = "queue name">
+ <doc>
+ Specifies the name of the queue to consume from. If the queue name
+ is null, refers to the current queue for the channel, which is the
+ last declared queue.
+ </doc>
+ <doc name = "rule">
+ If the client did not previously declare a queue, and the queue name
+ in this method is empty, the server MUST raise a connection exception
+ with reply code 530 (not allowed).
+ </doc>
+ </field>
+
+ <field name = "consumer tag" domain = "consumer tag">
+ <doc>
+ Specifies the identifier for the consumer. The consumer tag is
+ local to a connection, so two clients can use the same consumer
+ tags. If this field is empty the server will generate a unique
+ tag.
+ </doc>
+ <doc name = "rule" test = "todo">
+ The tag MUST NOT refer to an existing consumer. If the client
+ attempts to create two consumers with the same non-empty tag
+ the server MUST raise a connection exception with reply code
+ 530 (not allowed).
+ </doc>
+ </field>
+
+ <field name = "no local" domain = "no local" />
+
+ <field name = "no ack" domain = "no ack" />
+
+ <field name = "exclusive" type = "bit">
+ request exclusive access
+ <doc>
+ Request exclusive consumer access, meaning only this consumer can
+ access the queue.
+ </doc>
+ <doc name = "rule" test = "amq_file_00">
+ If the server cannot grant exclusive access to the queue when asked,
+ - because there are other consumers active - it MUST raise a channel
+ exception with return code 405 (resource locked).
+ </doc>
+ </field>
+
+ <field name = "nowait" type = "bit">
+ do not send a reply method
+ <doc>
+ If set, the server will not respond to the method. The client should
+ not wait for a reply method. If the server could not complete the
+ method it will raise a channel or connection exception.
+ </doc>
+ </field>
+</method>
+
+<method name = "consume-ok" synchronous = "1" index = "21">
+ confirm a new consumer
+ <doc>
+ This method provides the client with a consumer tag which it MUST
+ use in methods that work with the consumer.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer tag" domain = "consumer tag">
+ <doc>
+ Holds the consumer tag specified by the client or provided by
+ the server.
+ </doc>
+ </field>
+</method>
+
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "cancel" synchronous = "1" index = "30">
+ end a queue consumer
+ <doc>
+ This method cancels a consumer. This does not affect already
+ delivered messages, but it does mean the server will not send any
+ more messages for that consumer.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "cancel-ok" />
+
+ <field name = "consumer tag" domain = "consumer tag" />
+
+ <field name = "nowait" type = "bit">
+ do not send a reply method
+ <doc>
+ If set, the server will not respond to the method. The client should
+ not wait for a reply method. If the server could not complete the
+ method it will raise a channel or connection exception.
+ </doc>
+ </field>
+</method>
+
+<method name = "cancel-ok" synchronous = "1" index = "31">
+ confirm a cancelled consumer
+ <doc>
+ This method confirms that the cancellation was completed.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer tag" domain = "consumer tag" />
+</method>
+
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "open" synchronous = "1" index = "40">
+ request to start staging
+ <doc>
+ This method requests permission to start staging a message. Staging
+ means sending the message into a temporary area at the recipient end
+ and then delivering the message by referring to this temporary area.
+ Staging is how the protocol handles partial file transfers - if a
+ message is partially staged and the connection breaks, the next time
+ the sender starts to stage it, it can restart from where it left off.
+ </doc>
+ <response name = "open-ok" />
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "identifier" type = "shortstr">
+ staging identifier
+ <doc>
+ This is the staging identifier. This is an arbitrary string chosen
+ by the sender. For staging to work correctly the sender must use
+ the same staging identifier when staging the same message a second
+ time after recovery from a failure. A good choice for the staging
+ identifier would be the SHA1 hash of the message properties data
+ (including the original filename, revised time, etc.).
+ </doc>
+ </field>
+
+ <field name = "content size" type = "longlong">
+ message content size
+ <doc>
+ The size of the content in octets. The recipient may use this
+ information to allocate or check available space in advance, to
+ avoid "disk full" errors during staging of very large messages.
+ </doc>
+ <doc name = "rule">
+ The sender MUST accurately fill the content-size field.
+ Zero-length content is permitted.
+ </doc>
+ </field>
+</method>
+
+<method name = "open-ok" synchronous = "1" index = "41">
+ confirm staging ready
+ <doc>
+ This method confirms that the recipient is ready to accept staged
+ data. If the message was already partially-staged at a previous
+ time the recipient will report the number of octets already staged.
+ </doc>
+ <response name = "stage" />
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "staged size" type = "longlong">
+ already staged amount
+ <doc>
+ The amount of previously-staged content in octets. For a new
+ message this will be zero.
+ </doc>
+ <doc name = "rule">
+ The sender MUST start sending data from this octet offset in the
+ message, counting from zero.
+ </doc>
+ <doc name = "rule">
+ The recipient MAY decide how long to hold partially-staged content
+ and MAY implement staging by always discarding partially-staged
+ content. However if it uses the file content type it MUST support
+ the staging methods.
+ </doc>
+ </field>
+</method>
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "stage" content = "1" index = "50">
+ stage message content
+ <doc>
+ This method stages the message, sending the message content to the
+ recipient from the octet offset specified in the Open-Ok method.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+</method>
+
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "publish" index = "60">
+ publish a message
+ <doc>
+ This method publishes a staged file message to a specific exchange.
+ The file message will be routed to queues as defined by the exchange
+ configuration and distributed to any active consumers when the
+ transaction, if any, is committed.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "ticket" domain = "access ticket">
+ <doc name = "rule">
+ The client MUST provide a valid access ticket giving "write"
+ access rights to the access realm for the exchange.
+ </doc>
+ </field>
+
+ <field name = "exchange" domain = "exchange name">
+ <doc>
+ Specifies the name of the exchange to publish to. The exchange
+ name can be empty, meaning the default exchange. If the exchange
+ name is specified, and that exchange does not exist, the server
+ will raise a channel exception.
+ </doc>
+ <doc name = "rule">
+ The server MUST accept a blank exchange name to mean the default
+ exchange.
+ </doc>
+ <doc name = "rule">
+ If the exchange was declared as an internal exchange, the server
+ MUST respond with a reply code 403 (access refused) and raise a
+ channel exception.
+ </doc>
+ <doc name = "rule">
+ The exchange MAY refuse file content in which case it MUST respond
+ with a reply code 540 (not implemented) and raise a channel
+ exception.
+ </doc>
+ </field>
+
+ <field name = "routing key" type = "shortstr">
+ Message routing key
+ <doc>
+ Specifies the routing key for the message. The routing key is
+ used for routing messages depending on the exchange configuration.
+ </doc>
+ </field>
+
+ <field name = "mandatory" type = "bit">
+ indicate mandatory routing
+ <doc>
+ This flag tells the server how to react if the message cannot be
+ routed to a queue. If this flag is set, the server will return an
+ unroutable message with a Return method. If this flag is zero, the
+ server silently drops the message.
+ </doc>
+ <doc name = "rule" test = "amq_file_00">
+ The server SHOULD implement the mandatory flag.
+ </doc>
+ </field>
+
+ <field name = "immediate" type = "bit">
+ request immediate delivery
+ <doc>
+ This flag tells the server how to react if the message cannot be
+ routed to a queue consumer immediately. If this flag is set, the
+ server will return an undeliverable message with a Return method.
+ If this flag is zero, the server will queue the message, but with
+ no guarantee that it will ever be consumed.
+ </doc>
+ <doc name = "rule" test = "amq_file_00">
+ The server SHOULD implement the immediate flag.
+ </doc>
+ </field>
+
+ <field name = "identifier" type = "shortstr">
+ staging identifier
+ <doc>
+ This is the staging identifier of the message to publish. The
+ message must have been staged. Note that a client can send the
+ Publish method asynchronously without waiting for staging to
+ finish.
+ </doc>
+ </field>
+</method>
+
+<method name = "return" content = "1" index = "70">
+ return a failed message
+ <doc>
+ This method returns an undeliverable message that was published
+ with the "immediate" flag set, or an unroutable message published
+ with the "mandatory" flag set. The reply code and text provide
+ information about the reason that the message was undeliverable.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "reply code" domain = "reply code" />
+ <field name = "reply text" domain = "reply text" />
+
+ <field name = "exchange" domain = "exchange name">
+ <doc>
+ Specifies the name of the exchange that the message was
+ originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing key" type = "shortstr">
+ Message routing key
+ <doc>
+ Specifies the routing key name specified when the message was
+ published.
+ </doc>
+ </field>
+</method>
+
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "deliver" index = "80">
+ notify the client of a consumer message
+ <doc>
+ This method delivers a staged file message to the client, via a
+ consumer. In the asynchronous message delivery model, the client
+ starts a consumer using the Consume method, then the server
+ responds with Deliver methods as and when messages arrive for
+ that consumer.
+ </doc>
+ <doc name = "rule">
+ The server SHOULD track the number of times a message has been
+ delivered to clients and when a message is redelivered a certain
+ number of times - e.g. 5 times - without being acknowledged, the
+ server SHOULD consider the message to be unprocessable (possibly
+ causing client applications to abort), and move the message to a
+ dead letter queue.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer tag" domain = "consumer tag" />
+
+ <field name = "delivery tag" domain = "delivery tag" />
+
+ <field name = "redelivered" domain = "redelivered" />
+
+ <field name = "exchange" domain = "exchange name">
+ <doc>
+ Specifies the name of the exchange that the message was originally
+ published to.
+ </doc>
+ </field>
+
+ <field name = "routing key" type = "shortstr">
+ Message routing key
+ <doc>
+ Specifies the routing key name specified when the message was
+ published.
+ </doc>
+ </field>
+
+ <field name = "identifier" type = "shortstr">
+ staging identifier
+ <doc>
+ This is the staging identifier of the message to deliver. The
+ message must have been staged. Note that a server can send the
+ Deliver method asynchronously without waiting for staging to
+ finish.
+ </doc>
+ </field>
+</method>
+
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "ack" index = "90">
+ acknowledge one or more messages
+ <doc>
+ This method acknowledges one or more messages delivered via the
+ Deliver method. The client can ask to confirm a single message or
+ a set of messages up to and including a specific message.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <field name = "delivery tag" domain = "delivery tag" />
+
+ <field name = "multiple" type = "bit">
+ acknowledge multiple messages
+ <doc>
+ If set to 1, the delivery tag is treated as "up to and including",
+ so that the client can acknowledge multiple messages with a single
+ method. If set to zero, the delivery tag refers to a single
+ message. If the multiple field is 1, and the delivery tag is zero,
+ tells the server to acknowledge all outstanding mesages.
+ </doc>
+ <doc name = "rule">
+ The server MUST validate that a non-zero delivery-tag refers to an
+ delivered message, and raise a channel exception if this is not the
+ case.
+ </doc>
+ </field>
+</method>
+
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "reject" index = "100">
+ reject an incoming message
+ <doc>
+ This method allows a client to reject a message. It can be used to
+ return untreatable messages to their original queue. Note that file
+ content is staged before delivery, so the client will not use this
+ method to interrupt delivery of a large message.
+ </doc>
+ <doc name = "rule">
+ The server SHOULD interpret this method as meaning that the client
+ is unable to process the message at this time.
+ </doc>
+ <doc name = "rule">
+ A client MUST NOT use this method as a means of selecting messages
+ to process. A rejected message MAY be discarded or dead-lettered,
+ not necessarily passed to another client.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "delivery tag" domain = "delivery tag" />
+
+ <field name = "requeue" type = "bit">
+ requeue the message
+ <doc>
+ If this field is zero, the message will be discarded. If this bit
+ is 1, the server will attempt to requeue the message.
+ </doc>
+ <doc name = "rule">
+ The server MUST NOT deliver the message to the same client within
+ the context of the current channel. The recommended strategy is
+ to attempt to deliver the message to an alternative consumer, and
+ if that is not possible, to move the message to a dead-letter
+ queue. The server MAY use more sophisticated tracking to hold
+ the message on the queue and redeliver it to the same client at
+ a later stage.
+ </doc>
+ </field>
+</method>
+
+</class>
+
+ <class name="stream" handler="channel" index="80">
+ <!--
+======================================================
+== STREAMING
+======================================================
+-->
+ work with streaming content
+
+<doc>
+ The stream class provides methods that support multimedia streaming.
+ The stream class uses the following semantics: one message is one
+ packet of data; delivery is unacknowleged and unreliable; the consumer
+ can specify quality of service parameters that the server can try to
+ adhere to; lower-priority messages may be discarded in favour of high
+ priority messages.
+</doc>
+
+<doc name = "grammar">
+ stream = C:QOS S:QOS-OK
+ / C:CONSUME S:CONSUME-OK
+ / C:CANCEL S:CANCEL-OK
+ / C:PUBLISH content
+ / S:RETURN
+ / S:DELIVER content
+</doc>
+
+<chassis name = "server" implement = "MAY" />
+<chassis name = "client" implement = "MAY" />
+
+<doc name = "rule">
+ The server SHOULD discard stream messages on a priority basis if
+ the queue size exceeds some configured limit.
+</doc>
+<doc name = "rule">
+ The server MUST implement at least 2 priority levels for stream
+ messages, where priorities 0-4 and 5-9 are treated as two distinct
+ levels. The server MAY implement up to 10 priority levels.
+</doc>
+<doc name = "rule">
+ The server MUST implement automatic acknowledgements on stream
+ content. That is, as soon as a message is delivered to a client
+ via a Deliver method, the server must remove it from the queue.
+</doc>
+
+
+<!-- These are the properties for a Stream content -->
+
+<field name = "content type" type = "shortstr">
+ MIME content type
+</field>
+<field name = "content encoding" type = "shortstr">
+ MIME content encoding
+</field>
+<field name = "headers" type = "table">
+ Message header field table
+</field>
+<field name = "priority" type = "octet">
+ The message priority, 0 to 9
+</field>
+<field name = "timestamp" type = "timestamp">
+ The message timestamp
+</field>
+
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "qos" synchronous = "1" index = "10">
+ specify quality of service
+ <doc>
+ This method requests a specific quality of service. The QoS can
+ be specified for the current channel or for all channels on the
+ connection. The particular properties and semantics of a qos method
+ always depend on the content class semantics. Though the qos method
+ could in principle apply to both peers, it is currently meaningful
+ only for the server.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "qos-ok" />
+
+ <field name = "prefetch size" type = "long">
+ prefetch window in octets
+ <doc>
+ The client can request that messages be sent in advance so that
+ when the client finishes processing a message, the following
+ message is already held locally, rather than needing to be sent
+ down the channel. Prefetching gives a performance improvement.
+ This field specifies the prefetch window size in octets. May be
+ set to zero, meaning "no specific limit". Note that other
+ prefetch limits may still apply.
+ </doc>
+ </field>
+
+ <field name = "prefetch count" type = "short">
+ prefetch window in messages
+ <doc>
+ Specifies a prefetch window in terms of whole messages. This
+ field may be used in combination with the prefetch-size field;
+ a message will only be sent in advance if both prefetch windows
+ (and those at the channel and connection level) allow it.
+ </doc>
+ </field>
+
+ <field name = "consume rate" type = "long">
+ transfer rate in octets/second
+ <doc>
+ Specifies a desired transfer rate in octets per second. This is
+ usually determined by the application that uses the streaming
+ data. A value of zero means "no limit", i.e. as rapidly as
+ possible.
+ </doc>
+ <doc name = "rule">
+ The server MAY ignore the prefetch values and consume rates,
+ depending on the type of stream and the ability of the server
+ to queue and/or reply it. The server MAY drop low-priority
+ messages in favour of high-priority messages.
+ </doc>
+ </field>
+
+ <field name = "global" type = "bit">
+ apply to entire connection
+ <doc>
+ By default the QoS settings apply to the current channel only. If
+ this field is set, they are applied to the entire connection.
+ </doc>
+ </field>
+</method>
+
+<method name = "qos-ok" synchronous = "1" index = "11">
+ confirm the requested qos
+ <doc>
+ This method tells the client that the requested QoS levels could
+ be handled by the server. The requested QoS applies to all active
+ consumers until a new QoS is defined.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+</method>
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "consume" synchronous = "1" index = "20">
+ start a queue consumer
+ <doc>
+ This method asks the server to start a "consumer", which is a
+ transient request for messages from a specific queue. Consumers
+ last as long as the channel they were created on, or until the
+ client cancels them.
+ </doc>
+ <doc name = "rule">
+ The server SHOULD support at least 16 consumers per queue, unless
+ the queue was declared as private, and ideally, impose no limit
+ except as defined by available resources.
+ </doc>
+ <doc name = "rule">
+ Streaming applications SHOULD use different channels to select
+ different streaming resolutions. AMQP makes no provision for
+ filtering and/or transforming streams except on the basis of
+ priority-based selective delivery of individual messages.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "consume-ok" />
+
+ <field name = "ticket" domain = "access ticket">
+ <doc name = "rule">
+ The client MUST provide a valid access ticket giving "read" access
+ rights to the realm for the queue.
+ </doc>
+ </field>
+
+ <field name = "queue" domain = "queue name">
+ <doc>
+ Specifies the name of the queue to consume from. If the queue name
+ is null, refers to the current queue for the channel, which is the
+ last declared queue.
+ </doc>
+ <doc name = "rule">
+ If the client did not previously declare a queue, and the queue name
+ in this method is empty, the server MUST raise a connection exception
+ with reply code 530 (not allowed).
+ </doc>
+ </field>
+
+ <field name = "consumer tag" domain = "consumer tag">
+ <doc>
+ Specifies the identifier for the consumer. The consumer tag is
+ local to a connection, so two clients can use the same consumer
+ tags. If this field is empty the server will generate a unique
+ tag.
+ </doc>
+ <doc name = "rule" test = "todo">
+ The tag MUST NOT refer to an existing consumer. If the client
+ attempts to create two consumers with the same non-empty tag
+ the server MUST raise a connection exception with reply code
+ 530 (not allowed).
+ </doc>
+ </field>
+
+ <field name = "no local" domain = "no local" />
+
+ <field name = "exclusive" type = "bit">
+ request exclusive access
+ <doc>
+ Request exclusive consumer access, meaning only this consumer can
+ access the queue.
+ </doc>
+ <doc name = "rule" test = "amq_file_00">
+ If the server cannot grant exclusive access to the queue when asked,
+ - because there are other consumers active - it MUST raise a channel
+ exception with return code 405 (resource locked).
+ </doc>
+ </field>
+
+ <field name = "nowait" type = "bit">
+ do not send a reply method
+ <doc>
+ If set, the server will not respond to the method. The client should
+ not wait for a reply method. If the server could not complete the
+ method it will raise a channel or connection exception.
+ </doc>
+ </field>
+</method>
+
+
+<method name = "consume-ok" synchronous = "1" index = "21">
+ confirm a new consumer
+ <doc>
+ This method provides the client with a consumer tag which it may
+ use in methods that work with the consumer.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer tag" domain = "consumer tag">
+ <doc>
+ Holds the consumer tag specified by the client or provided by
+ the server.
+ </doc>
+ </field>
+</method>
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "cancel" synchronous = "1" index = "30">
+ end a queue consumer
+ <doc>
+ This method cancels a consumer. Since message delivery is
+ asynchronous the client may continue to receive messages for
+ a short while after canceling a consumer. It may process or
+ discard these as appropriate.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "cancel-ok" />
+
+ <field name = "consumer tag" domain = "consumer tag" />
+
+ <field name = "nowait" type = "bit">
+ do not send a reply method
+ <doc>
+ If set, the server will not respond to the method. The client should
+ not wait for a reply method. If the server could not complete the
+ method it will raise a channel or connection exception.
+ </doc>
+ </field>
+</method>
+
+<method name = "cancel-ok" synchronous = "1" index = "31">
+ confirm a cancelled consumer
+ <doc>
+ This method confirms that the cancellation was completed.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer tag" domain = "consumer tag" />
+</method>
+
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "publish" content = "1" index = "40">
+ publish a message
+ <doc>
+ This method publishes a message to a specific exchange. The message
+ will be routed to queues as defined by the exchange configuration
+ and distributed to any active consumers as appropriate.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "ticket" domain = "access ticket">
+ <doc name = "rule">
+ The client MUST provide a valid access ticket giving "write"
+ access rights to the access realm for the exchange.
+ </doc>
+ </field>
+
+ <field name = "exchange" domain = "exchange name">
+ <doc>
+ Specifies the name of the exchange to publish to. The exchange
+ name can be empty, meaning the default exchange. If the exchange
+ name is specified, and that exchange does not exist, the server
+ will raise a channel exception.
+ </doc>
+ <doc name = "rule">
+ The server MUST accept a blank exchange name to mean the default
+ exchange.
+ </doc>
+ <doc name = "rule">
+ If the exchange was declared as an internal exchange, the server
+ MUST respond with a reply code 403 (access refused) and raise a
+ channel exception.
+ </doc>
+ <doc name = "rule">
+ The exchange MAY refuse stream content in which case it MUST
+ respond with a reply code 540 (not implemented) and raise a
+ channel exception.
+ </doc>
+ </field>
+
+ <field name = "routing key" type = "shortstr">
+ Message routing key
+ <doc>
+ Specifies the routing key for the message. The routing key is
+ used for routing messages depending on the exchange configuration.
+ </doc>
+ </field>
+
+ <field name = "mandatory" type = "bit">
+ indicate mandatory routing
+ <doc>
+ This flag tells the server how to react if the message cannot be
+ routed to a queue. If this flag is set, the server will return an
+ unroutable message with a Return method. If this flag is zero, the
+ server silently drops the message.
+ </doc>
+ <doc name = "rule" test = "amq_stream_00">
+ The server SHOULD implement the mandatory flag.
+ </doc>
+ </field>
+
+ <field name = "immediate" type = "bit">
+ request immediate delivery
+ <doc>
+ This flag tells the server how to react if the message cannot be
+ routed to a queue consumer immediately. If this flag is set, the
+ server will return an undeliverable message with a Return method.
+ If this flag is zero, the server will queue the message, but with
+ no guarantee that it will ever be consumed.
+ </doc>
+ <doc name = "rule" test = "amq_stream_00">
+ The server SHOULD implement the immediate flag.
+ </doc>
+ </field>
+</method>
+
+<method name = "return" content = "1" index = "50">
+ return a failed message
+ <doc>
+ This method returns an undeliverable message that was published
+ with the "immediate" flag set, or an unroutable message published
+ with the "mandatory" flag set. The reply code and text provide
+ information about the reason that the message was undeliverable.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "reply code" domain = "reply code" />
+ <field name = "reply text" domain = "reply text" />
+
+ <field name = "exchange" domain = "exchange name">
+ <doc>
+ Specifies the name of the exchange that the message was
+ originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing key" type = "shortstr">
+ Message routing key
+ <doc>
+ Specifies the routing key name specified when the message was
+ published.
+ </doc>
+ </field>
+</method>
+
+
+<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+<method name = "deliver" content = "1" index = "60">
+ notify the client of a consumer message
+ <doc>
+ This method delivers a message to the client, via a consumer. In
+ the asynchronous message delivery model, the client starts a
+ consumer using the Consume method, then the server responds with
+ Deliver methods as and when messages arrive for that consumer.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer tag" domain = "consumer tag" />
+
+ <field name = "delivery tag" domain = "delivery tag" />
+
+ <field name = "exchange" domain = "exchange name">
+ <doc>
+ Specifies the name of the exchange that the message was originally
+ published to.
+ </doc>
+ </field>
+
+ <field name = "queue" domain = "queue name">
+ <doc>
+ Specifies the name of the queue that the message came from. Note
+ that a single channel can start many consumers on different
+ queues.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+</method>
+ </class>
+
+ <class name="tx" handler="channel" index="90">
+ <!--
+======================================================
+== TRANSACTIONS
+======================================================
+-->
+ work with standard transactions
+
+<doc>
+ Standard transactions provide so-called "1.5 phase commit". We can
+ ensure that work is never lost, but there is a chance of confirmations
+ being lost, so that messages may be resent. Applications that use
+ standard transactions must be able to detect and ignore duplicate
+ messages.
+</doc>
+ <rule implement="SHOULD">
+ An client using standard transactions SHOULD be able to track all
+ messages received within a reasonable period, and thus detect and
+ reject duplicates of the same message. It SHOULD NOT pass these to
+ the application layer.
+</rule>
+ <doc name="grammar">
+ tx = C:SELECT S:SELECT-OK
+ / C:COMMIT S:COMMIT-OK
+ / C:ROLLBACK S:ROLLBACK-OK
+</doc>
+ <chassis name="server" implement="SHOULD"/>
+ <chassis name="client" implement="MAY"/>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="select" synchronous="1" index="10">
+select standard transaction mode
+ <doc>
+ This method sets the channel to use standard transactions. The
+ client must use this method at least once on a channel before
+ using the Commit or Rollback methods.
+ </doc>
+ <chassis name="server" implement="MUST"/>
+ <response name="select-ok"/>
+ </method>
+ <method name="select-ok" synchronous="1" index="11">
+confirm transaction mode
+ <doc>
+ This method confirms to the client that the channel was successfully
+ set to use standard transactions.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ </method>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="commit" synchronous="1" index="20">
+commit the current transaction
+ <doc>
+ This method commits all messages published and acknowledged in
+ the current transaction. A new transaction starts immediately
+ after a commit.
+ </doc>
+ <chassis name="server" implement="MUST"/>
+ <response name="commit-ok"/>
+ </method>
+ <method name="commit-ok" synchronous="1" index="21">
+confirm a successful commit
+ <doc>
+ This method confirms to the client that the commit succeeded.
+ Note that if a commit fails, the server raises a channel exception.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ </method>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="rollback" synchronous="1" index="30">
+abandon the current transaction
+ <doc>
+ This method abandons all messages published and acknowledged in
+ the current transaction. A new transaction starts immediately
+ after a rollback.
+ </doc>
+ <chassis name="server" implement="MUST"/>
+ <response name="rollback-ok"/>
+ </method>
+ <method name="rollback-ok" synchronous="1" index="31">
+confirm a successful rollback
+ <doc>
+ This method confirms to the client that the rollback succeeded.
+ Note that if an rollback fails, the server raises a channel exception.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ </method>
+ </class>
+ <class name="dtx" handler="channel" index="100">
+ <!--
+======================================================
+== DISTRIBUTED TRANSACTIONS
+======================================================
+-->
+ work with distributed transactions
+
+<doc>
+ Distributed transactions provide so-called "2-phase commit". The
+ AMQP distributed transaction model supports the X-Open XA
+ architecture and other distributed transaction implementations.
+ The Dtx class assumes that the server has a private communications
+ channel (not AMQP) to a distributed transaction coordinator.
+</doc>
+ <doc name="grammar">
+ dtx = C:SELECT S:SELECT-OK
+ C:START S:START-OK
+</doc>
+ <chassis name="server" implement="MAY"/>
+ <chassis name="client" implement="MAY"/>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="select" synchronous="1" index="10">
+select standard transaction mode
+ <doc>
+ This method sets the channel to use distributed transactions. The
+ client must use this method at least once on a channel before
+ using the Start method.
+ </doc>
+ <chassis name="server" implement="MUST"/>
+ <response name="select-ok"/>
+ </method>
+ <method name="select-ok" synchronous="1" index="11">
+confirm transaction mode
+ <doc>
+ This method confirms to the client that the channel was successfully
+ set to use distributed transactions.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ </method>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="start" synchronous="1" index="20">
+ start a new distributed transaction
+ <doc>
+ This method starts a new distributed transaction. This must be
+ the first method on a new channel that uses the distributed
+ transaction mode, before any methods that publish or consume
+ messages.
+ </doc>
+ <chassis name="server" implement="MAY"/>
+ <response name="start-ok"/>
+ <field name="dtx identifier" type="shortstr">
+ transaction identifier
+ <doc>
+ The distributed transaction key. This identifies the transaction
+ so that the AMQP server can coordinate with the distributed
+ transaction coordinator.
+ </doc>
+ <assert check="notnull"/>
+ </field>
+ </method>
+ <method name="start-ok" synchronous="1" index="21">
+ confirm the start of a new distributed transaction
+ <doc>
+ This method confirms to the client that the transaction started.
+ Note that if a start fails, the server raises a channel exception.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ </method>
+ </class>
+ <class name="tunnel" handler="tunnel" index="110">
+ <!--
+======================================================
+== TUNNEL
+======================================================
+-->
+ methods for protocol tunneling.
+
+<doc>
+ The tunnel methods are used to send blocks of binary data - which
+ can be serialised AMQP methods or other protocol frames - between
+ AMQP peers.
+</doc>
+ <doc name="grammar">
+ tunnel = C:REQUEST
+ / S:REQUEST
+</doc>
+ <chassis name="server" implement="MAY"/>
+ <chassis name="client" implement="MAY"/>
+ <field name="headers" type="table">
+ Message header field table
+</field>
+ <field name="proxy name" type="shortstr">
+ The identity of the tunnelling proxy
+</field>
+ <field name="data name" type="shortstr">
+ The name or type of the message being tunnelled
+</field>
+ <field name="durable" type="octet">
+ The message durability indicator
+</field>
+ <field name="broadcast" type="octet">
+ The message broadcast mode
+</field>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="request" content="1" index="10">
+ sends a tunnelled method
+ <doc>
+ This method tunnels a block of binary data, which can be an
+ encoded AMQP method or other data. The binary data is sent
+ as the content for the Tunnel.Request method.
+ </doc>
+ <chassis name="server" implement="MUST"/>
+ <field name="meta data" type="table">
+ meta data for the tunnelled block
+ <doc>
+ This field table holds arbitrary meta-data that the sender needs
+ to pass to the recipient.
+ </doc>
+ </field>
+ </method>
+ </class>
+ <class name="test" handler="channel" index="120">
+ <!--
+======================================================
+== TEST - CHECK FUNCTIONAL CAPABILITIES OF AN IMPLEMENTATION
+======================================================
+-->
+ test functional primitives of the implementation
+
+<doc>
+ The test class provides methods for a peer to test the basic
+ operational correctness of another peer. The test methods are
+ intended to ensure that all peers respect at least the basic
+ elements of the protocol, such as frame and content organisation
+ and field types. We assume that a specially-designed peer, a
+ "monitor client" would perform such tests.
+</doc>
+ <doc name="grammar">
+ test = C:INTEGER S:INTEGER-OK
+ / S:INTEGER C:INTEGER-OK
+ / C:STRING S:STRING-OK
+ / S:STRING C:STRING-OK
+ / C:TABLE S:TABLE-OK
+ / S:TABLE C:TABLE-OK
+ / C:CONTENT S:CONTENT-OK
+ / S:CONTENT C:CONTENT-OK
+</doc>
+ <chassis name="server" implement="MUST"/>
+ <chassis name="client" implement="SHOULD"/>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="integer" synchronous="1" index="10">
+ test integer handling
+ <doc>
+ This method tests the peer's capability to correctly marshal integer
+ data.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ <chassis name="server" implement="MUST"/>
+ <response name="integer-ok"/>
+ <field name="integer 1" type="octet">
+ octet test value
+ <doc>
+ An octet integer test value.
+ </doc>
+ </field>
+ <field name="integer 2" type="short">
+ short test value
+ <doc>
+ A short integer test value.
+ </doc>
+ </field>
+ <field name="integer 3" type="long">
+ long test value
+ <doc>
+ A long integer test value.
+ </doc>
+ </field>
+ <field name="integer 4" type="longlong">
+ long-long test value
+ <doc>
+ A long long integer test value.
+ </doc>
+ </field>
+ <field name="operation" type="octet">
+ operation to test
+ <doc>
+ The client must execute this operation on the provided integer
+ test fields and return the result.
+ </doc>
+ <assert check="enum">
+ <value name="add">return sum of test values</value>
+ <value name="min">return lowest of test values</value>
+ <value name="max">return highest of test values</value>
+ </assert>
+ </field>
+ </method>
+ <method name="integer-ok" synchronous="1" index="11">
+ report integer test result
+ <doc>
+ This method reports the result of an Integer method.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ <chassis name="server" implement="MUST"/>
+ <field name="result" type="longlong">
+ result value
+ <doc>
+ The result of the tested operation.
+ </doc>
+ </field>
+ </method>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="string" synchronous="1" index="20">
+ test string handling
+ <doc>
+ This method tests the peer's capability to correctly marshal string
+ data.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ <chassis name="server" implement="MUST"/>
+ <response name="string-ok"/>
+ <field name="string 1" type="shortstr">
+ short string test value
+ <doc>
+ An short string test value.
+ </doc>
+ </field>
+ <field name="string 2" type="longstr">
+ long string test value
+ <doc>
+ A long string test value.
+ </doc>
+ </field>
+ <field name="operation" type="octet">
+ operation to test
+ <doc>
+ The client must execute this operation on the provided string
+ test fields and return the result.
+ </doc>
+ <assert check="enum">
+ <value name="add">return concatentation of test strings</value>
+ <value name="min">return shortest of test strings</value>
+ <value name="max">return longest of test strings</value>
+ </assert>
+ </field>
+ </method>
+ <method name="string-ok" synchronous="1" index="21">
+ report string test result
+ <doc>
+ This method reports the result of a String method.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ <chassis name="server" implement="MUST"/>
+ <field name="result" type="longstr">
+ result value
+ <doc>
+ The result of the tested operation.
+ </doc>
+ </field>
+ </method>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="table" synchronous="1" index="30">
+ test field table handling
+ <doc>
+ This method tests the peer's capability to correctly marshal field
+ table data.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ <chassis name="server" implement="MUST"/>
+ <response name="table-ok"/>
+ <field name="table" type="table">
+ field table of test values
+ <doc>
+ A field table of test values.
+ </doc>
+ </field>
+ <field name="integer op" type="octet">
+ operation to test on integers
+ <doc>
+ The client must execute this operation on the provided field
+ table integer values and return the result.
+ </doc>
+ <assert check="enum">
+ <value name="add">return sum of numeric field values</value>
+ <value name="min">return min of numeric field values</value>
+ <value name="max">return max of numeric field values</value>
+ </assert>
+ </field>
+ <field name="string op" type="octet">
+ operation to test on strings
+ <doc>
+ The client must execute this operation on the provided field
+ table string values and return the result.
+ </doc>
+ <assert check="enum">
+ <value name="add">return concatenation of string field values</value>
+ <value name="min">return shortest of string field values</value>
+ <value name="max">return longest of string field values</value>
+ </assert>
+ </field>
+ </method>
+ <method name="table-ok" synchronous="1" index="31">
+ report table test result
+ <doc>
+ This method reports the result of a Table method.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ <chassis name="server" implement="MUST"/>
+ <field name="integer result" type="longlong">
+ integer result value
+ <doc>
+ The result of the tested integer operation.
+ </doc>
+ </field>
+ <field name="string result" type="longstr">
+ string result value
+ <doc>
+ The result of the tested string operation.
+ </doc>
+ </field>
+ </method>
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <method name="content" synchronous="1" content="1" index="40">
+ test content handling
+ <doc>
+ This method tests the peer's capability to correctly marshal content.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ <chassis name="server" implement="MUST"/>
+ <response name="content-ok"/>
+ </method>
+ <method name="content-ok" synchronous="1" content="1" index="41">
+ report content test result
+ <doc>
+ This method reports the result of a Content method. It contains the
+ content checksum and echoes the original content as provided.
+ </doc>
+ <chassis name="client" implement="MUST"/>
+ <chassis name="server" implement="MUST"/>
+ <field name="content checksum" type="long">
+ content hash
+ <doc>
+ The 32-bit checksum of the content, calculated by adding the
+ content into a 32-bit accumulator.
+ </doc>
+ </field>
+ </method>
+ </class>
+</amqp>
diff --git a/qpid/gentools/xml-src/amqp-0.9.test.xml b/qpid/gentools/xml-src/amqp-0.9.test.xml
new file mode 100644
index 0000000000..e12e9c787a
--- /dev/null
+++ b/qpid/gentools/xml-src/amqp-0.9.test.xml
@@ -0,0 +1,4282 @@
+<?xml version = "1.0"?>
+
+<!--
+ EDITORS: (PH) Pieter Hintjens <ph@imatix.com>
+ (KvdR) Kim van der Riet <kim.vdriet@redhat.com>
+
+ These editors have been assigned by the AMQP working group.
+ Please do not edit/commit this file without consulting with
+ one of the above editors.
+ ========================================================
+
+ TODOs
+ - see TODO comments in the text
+-->
+
+<!--
+ Copyright Notice
+ ================
+ (c) Copyright JPMorgan Chase Bank & Co., Cisco Systems, Inc., Envoy Technologies Inc.,
+ iMatix Corporation, IONA\ufffd Technologies, Red Hat, Inc.,
+ TWIST Process Innovations, and 29West Inc. 2006. All rights reserved.
+
+ License
+ =======
+ JPMorgan Chase Bank & Co., Cisco Systems, Inc., Envoy Technologies Inc., iMatix
+ Corporation, IONA\ufffd Technologies, Red Hat, Inc., TWIST Process Innovations, and
+ 29West Inc. (collectively, the "Authors") each hereby grants to you a worldwide,
+ perpetual, royalty-free, nontransferable, nonexclusive license to
+ (i) copy, display, and implement the Advanced Messaging Queue Protocol
+ ("AMQP") Specification and (ii) the Licensed Claims that are held by
+ the Authors, all for the purpose of implementing the Advanced Messaging
+ Queue Protocol Specification. Your license and any rights under this
+ Agreement will terminate immediately without notice from
+ any Author if you bring any claim, suit, demand, or action related to
+ the Advanced Messaging Queue Protocol Specification against any Author.
+ Upon termination, you shall destroy all copies of the Advanced Messaging
+ Queue Protocol Specification in your possession or control.
+
+ As used hereunder, "Licensed Claims" means those claims of a patent or
+ patent application, throughout the world, excluding design patents and
+ design registrations, owned or controlled, or that can be sublicensed
+ without fee and in compliance with the requirements of this
+ Agreement, by an Author or its affiliates now or at any
+ future time and which would necessarily be infringed by implementation
+ of the Advanced Messaging Queue Protocol Specification. A claim is
+ necessarily infringed hereunder only when it is not possible to avoid
+ infringing it because there is no plausible non-infringing alternative
+ for implementing the required portions of the Advanced Messaging Queue
+ Protocol Specification. Notwithstanding the foregoing, Licensed Claims
+ shall not include any claims other than as set forth above even if
+ contained in the same patent as Licensed Claims; or that read solely
+ on any implementations of any portion of the Advanced Messaging Queue
+ Protocol Specification that are not required by the Advanced Messaging
+ Queue Protocol Specification, or that, if licensed, would require a
+ payment of royalties by the licensor to unaffiliated third parties.
+ Moreover, Licensed Claims shall not include (i) any enabling technologies
+ that may be necessary to make or use any Licensed Product but are not
+ themselves expressly set forth in the Advanced Messaging Queue Protocol
+ Specification (e.g., semiconductor manufacturing technology, compiler
+ technology, object oriented technology, networking technology, operating
+ system technology, and the like); or (ii) the implementation of other
+ published standards developed elsewhere and merely referred to in the
+ body of the Advanced Messaging Queue Protocol Specification, or
+ (iii) any Licensed Product and any combinations thereof the purpose or
+ function of which is not required for compliance with the Advanced
+ Messaging Queue Protocol Specification. For purposes of this definition,
+ the Advanced Messaging Queue Protocol Specification shall be deemed to
+ include both architectural and interconnection requirements essential
+ for interoperability and may also include supporting source code artifacts
+ where such architectural, interconnection requirements and source code
+ artifacts are expressly identified as being required or documentation to
+ achieve compliance with the Advanced Messaging Queue Protocol Specification.
+
+ As used hereunder, "Licensed Products" means only those specific portions
+ of products (hardware, software or combinations thereof) that implement
+ and are compliant with all relevant portions of the Advanced Messaging
+ Queue Protocol Specification.
+
+ The following disclaimers, which you hereby also acknowledge as to any
+ use you may make of the Advanced Messaging Queue Protocol Specification:
+
+ THE ADVANCED MESSAGING QUEUE PROTOCOL SPECIFICATION IS PROVIDED "AS IS,"
+ AND THE AUTHORS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+ IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT, OR TITLE; THAT THE
+ CONTENTS OF THE ADVANCED MESSAGING QUEUE PROTOCOL SPECIFICATION ARE
+ SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF THE ADVANCED
+ MESSAGING QUEUE PROTOCOL SPECIFICATION WILL NOT INFRINGE ANY THIRD PARTY
+ PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
+
+ THE AUTHORS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL,
+ INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF OR RELATING TO ANY
+ USE, IMPLEMENTATION OR DISTRIBUTION OF THE ADVANCED MESSAGING QUEUE
+ PROTOCOL SPECIFICATION.
+
+ The name and trademarks of the Authors may NOT be used in any manner,
+ including advertising or publicity pertaining to the Advanced Messaging
+ Queue Protocol Specification or its contents without specific, written
+ prior permission. Title to copyright in the Advanced Messaging Queue
+ Protocol Specification will at all times remain with the Authors.
+
+ No other rights are granted by implication, estoppel or otherwise.
+
+ Upon termination of your license or rights under this Agreement, you
+ shall destroy all copies of the Advanced Messaging Queue Protocol
+ Specification in your possession or control.
+
+ Trademarks
+ ==========
+ "JPMorgan", "JPMorgan Chase", "Chase", the JPMorgan Chase logo and the
+ Octagon Symbol are trademarks of JPMorgan Chase & Co.
+
+ IMATIX and the iMatix logo are trademarks of iMatix Corporation sprl.
+
+ IONA, IONA Technologies, and the IONA logos are trademarks of IONA
+ Technologies PLC and/or its subsidiaries.
+
+ LINUX is a trademark of Linus Torvalds. RED HAT and JBOSS are registered
+ trademarks of Red Hat, Inc. in the US and other countries.
+
+ Java, all Java-based trademarks and OpenOffice.org are trademarks of
+ Sun Microsystems, Inc. in the United States, other countries, or both.
+
+ Other company, product, or service names may be trademarks or service
+ marks of others.
+
+ Links to full AMQP specification:
+ =================================
+ http://www.envoytech.org/spec/amq/
+ http://www.iona.com/opensource/amqp/
+ http://www.redhat.com/solutions/specifications/amqp/
+ http://www.twiststandards.org/tiki-index.php?page=AMQ
+ http://www.imatix.com/amqp
+-->
+
+<!--
+ <!DOCTYPE amqp SYSTEM "amqp.dtd">
+-->
+
+<!-- XML Notes
+
+ We use entities to indicate repetition; attributes to indicate properties.
+
+ We use the 'name' attribute as an identifier, usually within the context
+ of the surrounding entities.
+
+ We use spaces to seperate words in names, so that we can print names in
+ their natural form depending on the context - underlines for source code,
+ hyphens for written text, etc.
+
+ We do not enforce any particular validation mechanism but we support all
+ mechanisms. The protocol definition conforms to a formal grammar that is
+ published seperately in several technologies.
+
+ -->
+
+<amqp major = "0" minor = "9" port = "5672" comment = "AMQ Protocol">
+ <!--
+ ======================================================
+ == CONSTANTS
+ ======================================================
+ -->
+ <!-- Frame types -->
+ <constant name = "frame-method" value = "1" />
+ <constant name = "frame-header" value = "2" />
+ <constant name = "frame-body" value = "3" />
+ <constant name = "frame-oob-method" value = "4" />
+ <constant name = "frame-oob-header" value = "5" />
+ <constant name = "frame-oob-body" value = "6" />
+ <constant name = "frame-trace" value = "7" />
+ <constant name = "frame-heartbeat" value = "8" />
+
+ <!-- Protocol constants -->
+ <constant name = "frame-min-size" value = "4096" />
+ <constant name = "frame-end" value = "206" />
+
+ <!-- Reply codes -->
+ <constant name = "reply-success" value = "200">
+ <doc>
+ Indicates that the method completed successfully. This reply code is
+ reserved for future use - the current protocol design does not use positive
+ confirmation and reply codes are sent only in case of an error.
+ </doc>
+ </constant>
+
+ <constant name = "not-delivered" value = "310" class = "soft-error">
+ <doc>
+ The client asked for a specific message that is no longer available.
+ The message was delivered to another client, or was purged from the queue
+ for some other reason.
+ </doc>
+ </constant>
+
+ <constant name = "content-too-large" value = "311" class = "soft-error">
+ <doc>
+ The client attempted to transfer content larger than the server could accept
+ at the present time. The client may retry at a later time.
+ </doc>
+ </constant>
+
+ <constant name = "connection-forced" value = "320" class = "hard-error">
+ <doc>
+ An operator intervened to close the connection for some reason. The client
+ may retry at some later date.
+ </doc>
+ </constant>
+
+ <constant name = "invalid-path" value = "402" class = "hard-error">
+ <doc>
+ The client tried to work with an unknown virtual host.
+ </doc>
+ </constant>
+
+ <constant name = "access-refused" value = "403" class = "soft-error">
+ <doc>
+ The client attempted to work with a server entity to which it has no
+ access due to security settings.
+ </doc>
+ </constant>
+
+ <constant name = "not-found" value = "404" class = "soft-error">
+ <doc>The client attempted to work with a server entity that does not exist.</doc>
+ </constant>
+
+ <constant name = "resource-locked" value = "405" class = "soft-error">
+ <doc>
+ The client attempted to work with a server entity to which it has no
+ access because another client is working with it.
+ </doc>
+ </constant>
+
+ <constant name = "precondition-failed" value = "406" class = "soft-error">
+ <doc>
+ The client requested a method that was not allowed because some precondition
+ failed.
+ </doc>
+ </constant>
+
+ <constant name = "frame-error" value = "501" class = "hard-error">
+ <doc>
+ The client sent a malformed frame that the server could not decode. This
+ strongly implies a programming error in the client.
+ </doc>
+ </constant>
+
+ <constant name = "syntax-error" value = "502" class = "hard-error">
+ <doc>
+ The client sent a frame that contained illegal values for one or more
+ fields. This strongly implies a programming error in the client.
+ </doc>
+ </constant>
+
+ <constant name = "command-invalid" value = "503" class = "hard-error">
+ <doc>
+ The client sent an invalid sequence of frames, attempting to perform an
+ operation that was considered invalid by the server. This usually implies
+ a programming error in the client.
+ </doc>
+ </constant>
+
+ <constant name = "channel-error" value = "504" class = "hard-error">
+ <doc>
+ The client attempted to work with a channel that had not been correctly
+ opened. This most likely indicates a fault in the client layer.
+ </doc>
+ </constant>
+
+ <constant name = "resource-error" value = "506" class = "hard-error">
+ <doc>
+ The server could not complete the method because it lacked sufficient
+ resources. This may be due to the client creating too many of some type
+ of entity.
+ </doc>
+ </constant>
+
+ <constant name = "not-allowed" value = "530" class = "hard-error">
+ <doc>
+ The client tried to work with some entity in a manner that is prohibited
+ by the server, due to security settings or by some other criteria.
+ </doc>
+ </constant>
+
+ <constant name = "not-implemented" value = "540" class = "hard-error">
+ <doc>
+ The client tried to use functionality that is not implemented in the
+ server.
+ </doc>
+ </constant>
+
+ <constant name = "internal-error" value = "541" class = "hard-error">
+ <doc>
+ The server could not complete the method because of an internal error.
+ The server may require intervention by an operator in order to resume
+ normal operations.
+ </doc>
+ </constant>
+
+ <constant name = "test-str2" value = "1.2.3.3"/>
+
+ <!--
+ ======================================================
+ == DOMAIN TYPES
+ ======================================================
+ -->
+
+ <domain name = "access-ticket" type = "short" label = "access ticket granted by server">
+ <doc>
+ An access ticket granted by the server for a certain set of access rights
+ within a specific realm. Access tickets are valid within the channel where
+ they were created, and expire when the channel closes.
+ </doc>
+ <assert check = "ne" value = "0" />
+ </domain>
+
+ <domain name = "class-id" type = "short" />
+
+ <domain name = "consumer-tag" type = "shortstr" label = "consumer tag">
+ <doc>
+ Identifier for the consumer, valid within the current connection.
+ </doc>
+ </domain>
+
+ <domain name = "delivery-tag" type = "longlong" label = "server-assigned delivery tag">
+ <doc>
+ The server-assigned and channel-specific delivery tag
+ </doc>
+ <rule name = "channel-local">
+ <doc>
+ The delivery tag is valid only within the channel from which the message was
+ received. I.e. a client MUST NOT receive a message on one channel and then
+ acknowledge it on another.
+ </doc>
+ </rule>
+ <rule name = "non-zero">
+ <doc>
+ The server MUST NOT use a zero value for delivery tags. Zero is reserved
+ for client use, meaning "all messages so far received".
+ </doc>
+ </rule>
+ </domain>
+
+ <domain name = "exchange-name" type = "shortstr" label = "exchange name">
+ <doc>
+ The exchange name is a client-selected string that identifies the exchange for publish
+ methods. Exchange names may consist of any mixture of digits, letters, and underscores.
+ Exchange names are scoped by the virtual host.
+ </doc>
+ <assert check = "length" value = "127" />
+ </domain>
+
+ <domain name = "known-hosts" type = "shortstr" label = "list of known hosts">
+ <doc>
+ Specifies the list of equivalent or alternative hosts that the server knows about,
+ which will normally include the current server itself. Clients can cache this
+ information and use it when reconnecting to a server after a failure. This field
+ may be empty.
+ </doc>
+ </domain>
+
+ <domain name = "method-id" type = "short" />
+
+ <domain name = "no-ack" type = "bit" label = "no acknowledgement needed">
+ <doc>
+ If this field is set the server does not expect acknowledgments for
+ messages. That is, when a message is delivered to the client the server
+ automatically and silently acknowledges it on behalf of the client. This
+ functionality increases performance but at the cost of reliability.
+ Messages can get lost if a client dies before it can deliver them to the
+ application.
+ </doc>
+ </domain>
+
+ <domain name = "no-local" type = "bit" label = "do not deliver own messages">
+ <doc>
+ If the no-local field is set the server will not send messages to the client that
+ published them.
+ </doc>
+ </domain>
+
+ <domain name = "path" type = "shortstr">
+ <doc>
+ Must start with a slash "/" and continue with path names separated by slashes. A path
+ name consists of any combination of at least one of [A-Za-z0-9] plus zero or more of
+ [.-_+!=:].
+ </doc>
+
+ <assert check = "notnull" />
+ <assert check = "syntax" rule = "path" />
+ <assert check = "length" value = "127" />
+ </domain>
+
+ <domain name = "peer-properties" type = "table">
+ <doc>
+ This string provides a set of peer properties, used for identification, debugging, and
+ general information.
+ </doc>
+ </domain>
+
+ <domain name = "queue-name" type = "shortstr" label = "queue name">
+ <doc>
+ The queue name identifies the queue within the vhost. Queue names may consist of any
+ mixture of digits, letters, and underscores.
+ </doc>
+ <assert check = "length" value = "127" />
+ </domain>
+
+ <domain name = "redelivered" type = "bit" label = "message is being redelivered">
+ <doc>
+ This indicates that the message has been previously delivered to this or
+ another client.
+ </doc>
+ <rule name = "implementation">
+ <doc>
+ The server SHOULD try to signal redelivered messages when it can. When
+ redelivering a message that was not successfully acknowledged, the server
+ SHOULD deliver it to the original client if possible.
+ </doc>
+ <doc type = "scenario">
+ Create a shared queue and publish a message to the queue. Consume the
+ message using explicit acknowledgements, but do not acknowledge the
+ message. Close the connection, reconnect, and consume from the queue
+ again. The message should arrive with the redelivered flag set.
+ </doc>
+ </rule>
+ <rule name = "hinting">
+ <doc>
+ The client MUST NOT rely on the redelivered field but should take it as a
+ hint that the message may already have been processed. A fully robust
+ client must be able to track duplicate received messages on non-transacted,
+ and locally-transacted channels.
+ </doc>
+ </rule>
+ </domain>
+
+ <domain name = "reply-code" type = "short" label = "reply code from server">
+ <doc>
+ The reply code. The AMQ reply codes are defined as constants at the start
+ of this formal specification.
+ </doc>
+ <assert check = "notnull" />
+ </domain>
+
+ <domain name = "reply-text" type = "shortstr" label = "localised reply text">
+ <doc>
+ The localised reply text. This text can be logged as an aid to resolving
+ issues.
+ </doc>
+ <assert check = "notnull" />
+ </domain>
+
+ <!-- Elementary domains -->
+ <domain name = "bit" type = "bit" label = "single bit" />
+ <domain name = "octet" type = "octet" label = "single octet" />
+ <domain name = "short" type = "short" label = "16-bit integer" />
+ <domain name = "long" type = "long" label = "32-bit integer" />
+ <domain name = "longlong" type = "longlong" label = "64-bit integer" />
+ <domain name = "shortstr" type = "shortstr" label = "short string" />
+ <domain name = "longstr" type = "longstr" label = "long string" />
+ <domain name = "timestamp" type = "timestamp" label = "64-bit timestamp" />
+ <domain name = "table" type = "table" label = "field table" />
+
+ <!-- == CONNECTION ======================================================= -->
+
+ <!-- TODO 0.81 - the 'handler' attribute of methods needs to be reviewed, and if
+ no current implementations use it, removed. /PH 2006/07/20
+ -->
+
+ <class name = "connection" handler = "connection" index = "10" label = "work with socket connections">
+ <doc>
+ The connection class provides methods for a client to establish a network connection to
+ a server, and for both peers to operate the connection thereafter.
+ </doc>
+
+ <doc type = "grammar">
+ connection = open-connection *use-connection close-connection
+ open-connection = C:protocol-header
+ S:START C:START-OK
+ *challenge
+ S:TUNE C:TUNE-OK
+ C:OPEN S:OPEN-OK | S:REDIRECT
+ challenge = S:SECURE C:SECURE-OK
+ use-connection = *channel
+ close-connection = C:CLOSE S:CLOSE-OK
+ / S:CLOSE C:CLOSE-OK
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "start" synchronous = "1" index = "10" label = "start connection negotiation">
+ <doc>
+ This method starts the connection negotiation process by telling the client the
+ protocol version that the server proposes, along with a list of security mechanisms
+ which the client can use for authentication.
+ </doc>
+
+ <rule name = "protocol-name">
+ <doc>
+ If the server cannot support the protocol specified in the protocol header,
+ it MUST close the socket connection without sending any response method.
+ </doc>
+ <doc type = "scenario">
+ The client sends a protocol header containing an invalid protocol name.
+ The server must respond by closing the connection.
+ </doc>
+ </rule>
+ <rule name = "server-support">
+ <doc>
+ The server MUST provide a protocol version that is lower than or equal to
+ that requested by the client in the protocol header.
+ </doc>
+ <doc type = "scenario">
+ The client requests a protocol version that is higher than any valid
+ implementation, e.g. 9.0. The server must respond with a current
+ protocol version, e.g. 1.0.
+ </doc>
+ </rule>
+ <rule name = "client-support">
+ <doc>
+ If the client cannot handle the protocol version suggested by the server
+ it MUST close the socket connection.
+ </doc>
+ <doc type = "scenario">
+ The server sends a protocol version that is lower than any valid
+ implementation, e.g. 0.1. The client must respond by closing the
+ connection.
+ </doc>
+ </rule>
+
+ <chassis name = "client" implement = "MUST" />
+ <response name = "start-ok" />
+
+ <field name = "version-major" domain = "octet" label = "protocol major version">
+ <doc>
+ The version of the protocol, expressed in protocol units of 0.1 public
+ versions and properly printed as two digits with a leading zero. I.e. a
+ protocol version of "09" represents a public version "0.9". The decimal
+ shift allows the correct expression of pre-1.0 protocol releases.
+ </doc>
+ <doc type = "todo">
+ This field should be renamed to "protocol version".
+ </doc>
+ </field>
+
+ <field name = "version-minor" domain = "octet" label = "protocol major version">
+ <doc>
+ The protocol revision, expressed as an integer from 0 to 9. The use of more
+ than ten revisions is discouraged. The public version string is constructed
+ from the protocol version and revision as follows: we print the protocol
+ version with one decimal position, and we append the protocol revision. A
+ version=10 and revision=2 are printed as "1.02".
+ </doc>
+ <doc type = "todo">
+ This field should be renamed to "protocol revision".
+ </doc>
+ </field>
+
+ <field name = "server-properties" domain = "peer-properties" label = "server properties">
+ <rule name = "required-fields">
+ <doc>
+ The properties SHOULD contain at least these fields: "host", specifying the
+ server host name or address, "product", giving the name of the server product,
+ "version", giving the name of the server version, "platform", giving the name
+ of the operating system, "copyright", if appropriate, and "information", giving
+ other general information.
+ </doc>
+ <doc type = "scenario">
+ Client connects to server and inspects the server properties. It checks for
+ the presence of the required fields.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "mechanisms" domain = "longstr" label = "available security mechanisms">
+ <doc>
+ A list of the security mechanisms that the server supports, delimited by spaces.
+ Currently ASL supports these mechanisms: PLAIN.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+
+ <field name = "locales" domain = "longstr" label = "available message locales">
+ <doc>
+ A list of the message locales that the server supports, delimited by spaces. The
+ locale defines the language in which the server will send reply texts.
+ </doc>
+ <rule name = "required-support">
+ <doc>
+ The server MUST support at least the en_US locale.
+ </doc>
+ <doc type = "scenario">
+ Client connects to server and inspects the locales field. It checks for
+ the presence of the required locale(s).
+ </doc>
+ </rule>
+ <assert check = "notnull" />
+ </field>
+ </method>
+
+ <method name = "start-ok" synchronous = "1" index = "11"
+ label = "select security mechanism and locale">
+ <doc>
+ This method selects a SASL security mechanism. ASL uses SASL (RFC2222) to
+ negotiate authentication and encryption.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "client-properties" domain = "peer-properties" label = "client properties">
+ <rule name = "required-fields">
+ <!-- This rule is not testable from the client side -->
+ <doc>
+ The properties SHOULD contain at least these fields: "product", giving the name
+ of the client product, "version", giving the name of the client version, "platform",
+ giving the name of the operating system, "copyright", if appropriate, and
+ "information", giving other general information.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "mechanism" domain = "shortstr" label = "selected security mechanism">
+ <doc>
+ A single security mechanisms selected by the client, which must be one of those
+ specified by the server.
+ </doc>
+ <rule name = "security">
+ <doc>
+ The client SHOULD authenticate using the highest-level security profile it
+ can handle from the list provided by the server.
+ </doc>
+ </rule>
+ <rule name = "validity">
+ <doc>
+ If the mechanism field does not contain one of the security mechanisms
+ proposed by the server in the Start method, the server MUST close the
+ connection without sending any further data.
+ </doc>
+ <doc type = "scenario">
+ Client connects to server and sends an invalid security mechanism. The
+ server must respond by closing the connection (a socket close, with no
+ connection close negotiation).
+ </doc>
+ </rule>
+ <assert check = "notnull" />
+ </field>
+
+ <field name = "response" domain = "longstr" label = "security response data">
+ <doc>
+ A block of opaque data passed to the security mechanism. The contents of this
+ data are defined by the SASL security mechanism.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+
+ <field name = "locale" domain = "shortstr" label = "selected message locale">
+ <doc>
+ A single message local selected by the client, which must be one of those
+ specified by the server.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "secure" synchronous = "1" index = "20" label = "security mechanism challenge">
+ <doc>
+ The SASL protocol works by exchanging challenges and responses until both peers have
+ received sufficient information to authenticate each other. This method challenges
+ the client to provide more information.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+ <response name = "secure-ok" />
+
+ <field name = "challenge" domain = "longstr" label = "security challenge data">
+ <doc>
+ Challenge information, a block of opaque binary data passed to the security
+ mechanism.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "secure-ok" synchronous = "1" index = "21" label = "security mechanism response">
+ <doc>
+ This method attempts to authenticate, passing a block of SASL data for the security
+ mechanism at the server side.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "response" domain = "longstr" label = "security response data">
+ <doc>
+ A block of opaque data passed to the security mechanism. The contents of this
+ data are defined by the SASL security mechanism.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "tune" synchronous = "1" index = "30"
+ label = "propose connection tuning parameters">
+ <doc>
+ This method proposes a set of connection configuration values to the client. The
+ client can accept and/or adjust these.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <response name = "tune-ok" />
+
+ <field name = "channel-max" domain = "short" label = "proposed maximum channels">
+ <doc>
+ The maximum total number of channels that the server allows per connection. Zero
+ means that the server does not impose a fixed limit, but the number of allowed
+ channels may be limited by available server resources.
+ </doc>
+ </field>
+
+ <field name = "frame-max" domain = "long" label = "proposed maximum frame size">
+ <doc>
+ The largest frame size that the server proposes for the connection. The client
+ can negotiate a lower value. Zero means that the server does not impose any
+ specific limit but may reject very large frames if it cannot allocate resources
+ for them.
+ </doc>
+ <rule name = "minimum">
+ <doc>
+ Until the frame-max has been negotiated, both peers MUST accept frames of up
+ to frame-min-size octets large, and the minimum negotiated value for frame-max
+ is also frame-min-size.
+ </doc>
+ <doc type = "scenario">
+ Client connects to server and sends a large properties field, creating a frame
+ of frame-min-size octets. The server must accept this frame.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "heartbeat" domain = "short" label = "desired heartbeat delay">
+ <!-- TODO 0.82 - the heartbeat negotiation mechanism was changed during
+ implementation because the model documented here does not actually
+ work properly. The best model we found is that the server proposes
+ a heartbeat value to the client; the client can reply with zero, meaning
+ 'do not use heartbeats (as documented here), or can propose its own
+ heartbeat value, which the server should then accept. This is different
+ from the model here which is disconnected - e.g. each side requests a
+ heartbeat independently. Basically a connection is heartbeated in
+ both ways, or not at all, depending on whether both peers support
+ heartbeating or not, and the heartbeat value should itself be chosen
+ by the client so that remote links can get a higher value. Also, the
+ actual heartbeat mechanism needs documentation, and is as follows: so
+ long as there is activity on a connection - in or out - both peers
+ assume the connection is active. When there is no activity, each peer
+ must send heartbeat frames. When no heartbeat frame is received after
+ N cycles (where N is at least 2), the connection can be considered to
+ have died. /PH 2006/07/19
+ -->
+ <doc>
+ The delay, in seconds, of the connection heartbeat that the server wants.
+ Zero means the server does not want a heartbeat.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "tune-ok" synchronous = "1" index = "31"
+ label = "negotiate connection tuning parameters">
+ <doc>
+ This method sends the client's connection tuning parameters to the server.
+ Certain fields are negotiated, others provide capability information.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "channel-max" domain = "short" label = "negotiated maximum channels">
+ <doc>
+ The maximum total number of channels that the client will use per connection.
+ </doc>
+ <rule name = "upper-limit">
+ <doc>
+ If the client specifies a channel max that is higher than the value provided
+ by the server, the server MUST close the connection without attempting a
+ negotiated close. The server may report the error in some fashion to assist
+ implementors.
+ </doc>
+ </rule>
+ <assert check = "notnull" />
+ <assert check = "le" method = "tune" field = "channel-max" />
+ </field>
+
+ <field name = "frame-max" domain = "long" label = "negotiated maximum frame size">
+ <doc>
+ The largest frame size that the client and server will use for the connection.
+ Zero means that the client does not impose any specific limit but may reject
+ very large frames if it cannot allocate resources for them. Note that the
+ frame-max limit applies principally to content frames, where large contents can
+ be broken into frames of arbitrary size.
+ </doc>
+ <rule name = "minimum">
+ <doc>
+ Until the frame-max has been negotiated, both peers MUST accept frames of up
+ to frame-min-size octets large, and the minimum negotiated value for frame-max
+ is also frame-min-size.
+ </doc>
+ </rule>
+ <rule name = "upper-limit">
+ <doc>
+ If the client specifies a frame max that is higher than the value provided
+ by the server, the server MUST close the connection without attempting a
+ negotiated close. The server may report the error in some fashion to assist
+ implementors.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "heartbeat" domain = "short" label = "desired heartbeat delay">
+ <doc>
+ The delay, in seconds, of the connection heartbeat that the client wants. Zero
+ means the client does not want a heartbeat.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "open" synchronous = "1" index = "40" label = "open connection to virtual host">
+ <doc>
+ This method opens a connection to a virtual host, which is a collection of
+ resources, and acts to separate multiple application domains within a server.
+ The server may apply arbitrary limits per virtual host, such as the number
+ of each type of entity that may be used, per connection and/or in total.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "open-ok" />
+ <response name = "redirect" />
+
+ <field name = "virtual-host" domain = "path" label = "virtual host name">
+ <!-- TODO 0.82 - the entire vhost model needs review. This concept was
+ prompted by the HTTP vhost concept but does not fit very well into
+ AMQP. Currently we use the vhost as a "cluster identifier" which is
+ inaccurate usage. /PH 2006/07/19
+ -->
+ <assert check = "regexp" value = "^[a-zA-Z0-9/-_]+$" />
+ <doc>
+ The name of the virtual host to work with.
+ </doc>
+ <rule name = "separation">
+ <doc>
+ If the server supports multiple virtual hosts, it MUST enforce a full
+ separation of exchanges, queues, and all associated entities per virtual
+ host. An application, connected to a specific virtual host, MUST NOT be able
+ to access resources of another virtual host.
+ </doc>
+ </rule>
+ <rule name = "security">
+ <doc>
+ The server SHOULD verify that the client has permission to access the
+ specified virtual host.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "capabilities" domain = "shortstr" label = "required capabilities">
+ <doc>
+ The client can specify zero or more capability names, delimited by spaces.
+ The server can use this string to how to process the client's connection
+ request.
+ </doc>
+ </field>
+
+ <field name = "insist" domain = "bit" label = "insist on connecting to server">
+ <doc>
+ In a configuration with multiple collaborating servers, the server may respond
+ to a Connection.Open method with a Connection.Redirect. The insist option tells
+ the server that the client is insisting on a connection to the specified server.
+ </doc>
+ <rule name = "behaviour">
+ <doc>
+ When the client uses the insist option, the server MUST NOT respond with a
+ Connection.Redirect method. If it cannot accept the client's connection
+ request it should respond by closing the connection with a suitable reply
+ code.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "open-ok" synchronous = "1" index = "41" label = "signal that connection is ready">
+ <doc>
+ This method signals to the client that the connection is ready for use.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "known-hosts" domain = "known-hosts" />
+ </method>
+
+ <method name = "redirect" synchronous = "1" index = "42" label = "redirects client to other server">
+ <doc>
+ This method redirects the client to another server, based on the requested virtual
+ host and/or capabilities.
+ </doc>
+ <rule name = "usage">
+ <doc>
+ When getting the Connection.Redirect method, the client SHOULD reconnect to
+ the host specified, and if that host is not present, to any of the hosts
+ specified in the known-hosts list.
+ </doc>
+ </rule>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "host" domain = "shortstr" label = "server to connect to">
+ <doc>
+ Specifies the server to connect to. This is an IP address or a DNS name,
+ optionally followed by a colon and a port number. If no port number is
+ specified, the client should use the default port number for the protocol.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+ <field name = "known-hosts" domain = "known-hosts" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "close" synchronous = "1" index = "50" label = "request a connection close">
+ <doc>
+ This method indicates that the sender wants to close the connection. This may be
+ due to internal conditions (e.g. a forced shut-down) or due to an error handling
+ a specific method, i.e. an exception. When a close is due to an exception, the
+ sender provides the class and method id of the method which caused the exception.
+ </doc>
+ <!-- TODO: the connection close mechanism needs to be reviewed from the ODF
+ documentation and better expressed as rules here. /PH 2006/07/20
+ -->
+ <rule name = "stability">
+ <doc>
+ After sending this method any received method except the Close-OK method MUST
+ be discarded.
+ </doc>
+ </rule>
+
+ <chassis name = "client" implement = "MUST" />
+ <chassis name = "server" implement = "MUST" />
+ <response name = "close-ok" />
+
+ <field name = "reply-code" domain = "reply-code" />
+ <field name = "reply-text" domain = "reply-text" />
+
+ <field name = "class-id" domain = "class-id" label = "failing method class">
+ <doc>
+ When the close is provoked by a method exception, this is the class of the
+ method.
+ </doc>
+ </field>
+
+ <field name = "method-id" domain = "method-id" label = "failing method ID">
+ <doc>
+ When the close is provoked by a method exception, this is the ID of the method.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "close-ok" synchronous = "1" index = "51" label = "confirm a connection close">
+ <doc>
+ This method confirms a Connection.Close method and tells the recipient that it is
+ safe to release resources for the connection and close the socket.
+ </doc>
+ <rule name = "reporting">
+ <doc>
+ A peer that detects a socket closure without having received a Close-Ok
+ handshake method SHOULD log the error.
+ </doc>
+ </rule>
+ <chassis name = "client" implement = "MUST" />
+ <chassis name = "server" implement = "MUST" />
+ </method>
+ </class>
+
+ <!-- == CHANNEL ========================================================== -->
+
+ <class name = "channel" handler = "channel" index = "20" label = "work with channels">
+ <doc>
+ The channel class provides methods for a client to establish a channel to a
+ server and for both peers to operate the channel thereafter.
+ </doc>
+
+ <doc type = "grammar">
+ channel = open-channel *use-channel close-channel
+ open-channel = C:OPEN S:OPEN-OK
+ use-channel = C:FLOW S:FLOW-OK
+ / S:FLOW C:FLOW-OK
+ / S:ALERT
+ / functional-class
+ close-channel = C:CLOSE S:CLOSE-OK
+ / S:CLOSE C:CLOSE-OK
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "open" synchronous = "1" index = "10" label = "open a channel for use">
+ <doc>
+ This method opens a channel to the server.
+ </doc>
+ <rule name = "state" on-failure = "channel-error">
+ <doc>
+ The client MUST NOT use this method on an alread-opened channel.
+ </doc>
+ <doc type = "scenario">
+ Client opens a channel and then reopens the same channel.
+ </doc>
+ </rule>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "open-ok" />
+ <field name = "out of band" domain = "shortstr" label = "out-of-band settings">
+ <doc>
+ Configures out-of-band transfers on this channel. The syntax and meaning of this
+ field will be formally defined at a later date.
+ </doc>
+ <assert check = "null" />
+ </field>
+ </method>
+
+ <method name = "open-ok" synchronous = "1" index = "11" label = "signal that the channel is ready">
+ <doc>
+ This method signals to the client that the channel is ready for use.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "flow" synchronous = "1" index = "20" label = "enable/disable flow from peer">
+ <doc>
+ This method asks the peer to pause or restart the flow of content data. This is a
+ simple flow-control mechanism that a peer can use to avoid oveflowing its queues or
+ otherwise finding itself receiving more messages than it can process. Note that this
+ method is not intended for window control. The peer that receives a disable flow
+ method should finish sending the current content frame, if any, then pause.
+ </doc>
+
+ <rule name = "initial-state">
+ <doc>
+ When a new channel is opened, it is active (flow is active). Some applications
+ assume that channels are inactive until started. To emulate this behaviour a
+ client MAY open the channel, then pause it.
+ </doc>
+ </rule>
+
+ <rule name = "bidirectional">
+ <doc>
+ When sending content frames, a peer SHOULD monitor the channel for incoming
+ methods and respond to a Channel.Flow as rapidly as possible.
+ </doc>
+ </rule>
+
+ <rule name = "throttling">
+ <doc>
+ A peer MAY use the Channel.Flow method to throttle incoming content data for
+ internal reasons, for example, when exchanging data over a slower connection.
+ </doc>
+ </rule>
+
+ <rule name = "expected-behaviour">
+ <doc>
+ The peer that requests a Channel.Flow method MAY disconnect and/or ban a peer
+ that does not respect the request. This is to prevent badly-behaved clients
+ from overwhelming a broker.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <response name = "flow-ok" />
+
+ <field name = "active" domain = "bit" label = "start/stop content frames">
+ <doc>
+ If 1, the peer starts sending content frames. If 0, the peer stops sending
+ content frames.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "flow-ok" index = "21" label = "confirm a flow method">
+ <doc>
+ Confirms to the peer that a flow command was received and processed.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+ <field name = "active" domain = "bit" label = "current flow setting">
+ <doc>
+ Confirms the setting of the processed flow method: 1 means the peer will start
+ sending or continue to send content frames; 0 means it will not.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+ <!-- TODO 0.82 - remove this method entirely
+ /PH 2006/07/20
+ -->
+ <method name = "alert" index = "30" label = "send a non-fatal warning message">
+ <doc>
+ This method allows the server to send a non-fatal warning to the client. This is
+ used for methods that are normally asynchronous and thus do not have confirmations,
+ and for which the server may detect errors that need to be reported. Fatal errors
+ are handled as channel or connection exceptions; non-fatal errors are sent through
+ this method.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "reply-code" domain = "reply-code" />
+ <field name = "reply-text" domain = "reply-text" />
+ <field name = "details" domain = "table" label = "detailed information for warning">
+ <doc>
+ A set of fields that provide more information about the problem. The meaning of
+ these fields are defined on a per-reply-code basis (TO BE DEFINED).
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "close" synchronous = "1" index = "40" label = "request a channel close">
+ <doc>
+ This method indicates that the sender wants to close the channel. This may be due to
+ internal conditions (e.g. a forced shut-down) or due to an error handling a specific
+ method, i.e. an exception. When a close is due to an exception, the sender provides
+ the class and method id of the method which caused the exception.
+ </doc>
+
+ <!-- TODO: the channel close behaviour needs to be reviewed from the ODF
+ documentation and better expressed as rules here. /PH 2006/07/20
+ -->
+ <rule name = "stability">
+ <doc>
+ After sending this method any received method except the Close-OK method MUST
+ be discarded.
+ </doc>
+ </rule>
+
+ <chassis name = "client" implement = "MUST" />
+ <chassis name = "server" implement = "MUST" />
+ <response name = "close-ok" />
+
+ <field name = "reply-code" domain = "reply-code" />
+ <field name = "reply-text" domain = "reply-text" />
+
+ <field name = "class-id" domain = "class-id" label = "failing method class">
+ <doc>
+ When the close is provoked by a method exception, this is the class of the
+ method.
+ </doc>
+ </field>
+
+ <field name = "method-id" domain = "method-id" label = "failing method ID">
+ <doc>
+ When the close is provoked by a method exception, this is the ID of the method.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "close-ok" synchronous = "1" index = "41" label = "confirm a channel close">
+ <doc>
+ This method confirms a Channel.Close method and tells the recipient that it is safe
+ to release resources for the channel and close the socket.
+ </doc>
+ <rule name = "reporting">
+ <doc>
+ A peer that detects a socket closure without having received a Channel.Close-Ok
+ handshake method SHOULD log the error.
+ </doc>
+ </rule>
+ <chassis name = "client" implement = "MUST" />
+ <chassis name = "server" implement = "MUST" />
+ </method>
+ </class>
+
+ <!-- == ACCESS =========================================================== -->
+
+ <!-- TODO 0.82 - this class must be implemented by two teams before we can
+ consider it matured.
+ -->
+
+ <class name = "access" handler = "connection" index = "30" label = "work with access tickets">
+ <doc>
+ The protocol control access to server resources using access tickets. A
+ client must explicitly request access tickets before doing work. An access
+ ticket grants a client the right to use a specific set of resources -
+ called a "realm" - in specific ways.
+ </doc>
+
+ <doc type = "grammar">
+ access = C:REQUEST S:REQUEST-OK
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "request" synchronous = "1" index = "10" label = "request an access ticket">
+ <doc>
+ This method requests an access ticket for an access realm. The server
+ responds by granting the access ticket. If the client does not have
+ access rights to the requested realm this causes a connection exception.
+ Access tickets are a per-channel resource.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "request-ok" />
+
+ <field name = "realm" domain = "shortstr" label = "name of requested realm">
+ <doc>
+ Specifies the name of the realm to which the client is requesting access.
+ The realm is a configured server-side object that collects a set of
+ resources (exchanges, queues, etc.). If the channel has already requested
+ an access ticket onto this realm, the previous ticket is destroyed and a
+ new ticket is created with the requested access rights, if allowed.
+ </doc>
+ <rule name = "validity" on-failure = "access-refused">
+ <doc>
+ The client MUST specify a realm that is known to the server. The server
+ makes an identical response for undefined realms as it does for realms
+ that are defined but inaccessible to this client.
+ </doc>
+ <doc type = "scenario">
+ Client specifies an undefined realm.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exclusive" domain = "bit" label = "request exclusive access">
+ <doc>
+ Request exclusive access to the realm, meaning that this will be the only
+ channel that uses the realm's resources.
+ </doc>
+ <rule name = "validity" on-failure = "access-refused">
+ <doc>
+ The client MAY NOT request exclusive access to a realm that has active
+ access tickets, unless the same channel already had the only access
+ ticket onto that realm.
+ </doc>
+ <doc type = "scenario">
+ Client opens two channels and requests exclusive access to the same realm.
+ </doc>
+ </rule>
+ </field>
+ <field name = "passive" domain = "bit" label = "request passive access">
+ <doc>
+ Request message passive access to the specified access realm. Passive
+ access lets a client get information about resources in the realm but
+ not to make any changes to them.
+ </doc>
+ </field>
+ <field name = "active" domain = "bit" label = "request active access">
+ <doc>
+ Request message active access to the specified access realm. Active access lets
+ a client get create and delete resources in the realm.
+ </doc>
+ </field>
+ <field name = "write" domain = "bit" label = "request write access">
+ <doc>
+ Request write access to the specified access realm. Write access lets a client
+ publish messages to all exchanges in the realm.
+ </doc>
+ </field>
+ <field name = "read" domain = "bit" label = "request read access">
+ <doc>
+ Request read access to the specified access realm. Read access lets a client
+ consume messages from queues in the realm.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "request-ok" synchronous = "1" index = "11" label = "grant access to server resources">
+ <doc>
+ This method provides the client with an access ticket. The access ticket is valid
+ within the current channel and for the lifespan of the channel.
+ </doc>
+ <rule name = "per-channel" on-failure = "not-allowed">
+ <doc>
+ The client MUST NOT use access tickets except within the same channel as
+ originally granted.
+ </doc>
+ <doc type = "scenario">
+ Client opens two channels, requests a ticket on one channel, and then
+ tries to use that ticket in a seconc channel.
+ </doc>
+ </rule>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "ticket" domain = "access-ticket" />
+ </method>
+ </class>
+
+ <!-- == EXCHANGE ========================================================= -->
+
+ <class name = "exchange" handler = "channel" index = "40" label = "work with exchanges">
+ <doc>
+ Exchanges match and distribute messages across queues. Exchanges can be configured in
+ the server or created at runtime.
+ </doc>
+
+ <doc type = "grammar">
+ exchange = C:DECLARE S:DECLARE-OK
+ / C:DELETE S:DELETE-OK
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <rule name = "required-types">
+ <doc>
+ The server MUST implement these standard exchange types: fanout, direct.
+ </doc>
+ <doc type = "scenario">
+ Client attempts to declare an exchange with each of these standard types.
+ </doc>
+ </rule>
+ <rule name = "recommended-types">
+ <doc>
+ The server SHOULD implement these standard exchange types: topic, headers.
+ </doc>
+ <doc type = "scenario">
+ Client attempts to declare an exchange with each of these standard types.
+ </doc>
+ </rule>
+ <rule name = "required-instances">
+ <doc>
+ The server MUST, in each virtual host, pre-declare an exchange instance
+ for each standard exchange type that it implements, where the name of the
+ exchange instance is "amq." followed by the exchange type name.
+ </doc>
+ <doc type = "scenario">
+ Client creates a temporary queue and attempts to bind to each required
+ exchange instance (amq.fanout, amq.direct, and amq.topic, amq.headers if
+ those types are defined).
+ </doc>
+ </rule>
+ <rule name = "default-exchange">
+ <doc>
+ The server MUST predeclare a direct exchange to act as the default exchange
+ for content Publish methods and for default queue bindings.
+ </doc>
+ <doc type = "scenario">
+ Client checks that the default exchange is active by specifying a queue
+ binding with no exchange name, and publishing a message with a suitable
+ routing key but without specifying the exchange name, then ensuring that
+ the message arrives in the queue correctly.
+ </doc>
+ </rule>
+ <rule name = "default-access">
+ <doc>
+ The server MUST NOT allow clients to access the default exchange except
+ by specifying an empty exchange name in the Queue.Bind and content Publish
+ methods.
+ </doc>
+ </rule>
+ <rule name = "extensions">
+ <doc>
+ The server MAY implement other exchange types as wanted.
+ </doc>
+ </rule>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "declare" synchronous = "1" index = "10" label = "declare exchange, create if needed">
+ <doc>
+ This method creates an exchange if it does not already exist, and if the exchange
+ exists, verifies that it is of the correct and expected class.
+ </doc>
+ <rule name = "minimum">
+ <doc>
+ The server SHOULD support a minimum of 16 exchanges per virtual host and
+ ideally, impose no limit except as defined by available resources.
+ </doc>
+ <doc type = "scenario">
+ The client creates as many exchanges as it can until the server reports
+ an error; the number of exchanges successfuly created must be at least
+ sixteen.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "declare-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <doc>
+ When a client defines a new exchange, this belongs to the access realm of the
+ ticket used. All further work done with that exchange must be done with an
+ access ticket for the same realm.
+ </doc>
+ <rule name = "validity" on-failure = "access-refused">
+ <doc>
+ The client MUST provide a valid access ticket giving "active" access to
+ the realm in which the exchange exists or will be created, or "passive"
+ access if the if-exists flag is set.
+ </doc>
+ <doc type = "scenario">
+ Client creates access ticket with wrong access rights and attempts to use
+ in this method.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name">
+ <rule name = "reserved" on-failure = "access-refused">
+ <doc>
+ Exchange names starting with "amq." are reserved for predeclared and
+ standardised exchanges. The client MUST NOT attempt to create an exchange
+ starting with "amq.".
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ <assert check = "regexp" value = "^[a-zA-Z0-9-_.:]+$" />
+ </field>
+
+ <field name = "type" domain = "shortstr" label = "exchange type">
+ <doc>
+ Each exchange belongs to one of a set of exchange types implemented by the
+ server. The exchange types define the functionality of the exchange - i.e. how
+ messages are routed through it. It is not valid or meaningful to attempt to
+ change the type of an existing exchange.
+ </doc>
+ <rule name = "typed" on-failure = "not-allowed">
+ <doc>
+ Exchanges cannot be redeclared with different types. The client MUST not
+ attempt to redeclare an existing exchange with a different type than used
+ in the original Exchange.Declare method.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ <rule name = "support" on-failure = "command-invalid">
+ <doc>
+ The client MUST NOT attempt to create an exchange with a type that the
+ server does not support.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ <assert check = "regexp" value = "^[a-zA-Z0-9-_.:]+$" />
+ </field>
+
+ <field name = "passive" domain = "bit" label = "do not create exchange">
+ <doc>
+ If set, the server will not create the exchange. The client can use this to
+ check whether an exchange exists without modifying the server state.
+ </doc>
+ <rule name = "not-found">
+ <doc>
+ If set, and the exchange does not already exist, the server MUST raise a
+ channel exception with reply code 404 (not found).
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "durable" domain = "bit" label = "request a durable exchange">
+ <doc>
+ If set when creating a new exchange, the exchange will be marked as durable.
+ Durable exchanges remain active when a server restarts. Non-durable exchanges
+ (transient exchanges) are purged if/when a server restarts.
+ </doc>
+ <rule name = "support">
+ <doc>
+ The server MUST support both durable and transient exchanges.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ <rule name = "sticky">
+ <doc>
+ The server MUST ignore the durable field if the exchange already exists.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <!-- TODO 0.82 - clarify how this works; there is no way to cancel a binding
+ except by deleting a queue.
+ -->
+ <field name = "auto-delete" domain = "bit" label = "auto-delete when unused">
+ <doc>
+ If set, the exchange is deleted when all queues have finished using it.
+ </doc>
+ <rule name = "sticky">
+ <doc>
+ The server MUST ignore the auto-delete field if the exchange already
+ exists.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "internal" domain = "bit" label = "create internal exchange">
+ <doc>
+ If set, the exchange may not be used directly by publishers, but only when bound
+ to other exchanges. Internal exchanges are used to construct wiring that is not
+ visible to applications.
+ </doc>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+
+ <field name = "arguments" domain = "table" label = "arguments for declaration">
+ <doc>
+ A set of arguments for the declaration. The syntax and semantics of these
+ arguments depends on the server implementation. This field is ignored if passive
+ is 1.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "declare-ok" synchronous = "1" index = "11" label = "confirm exchange declaration">
+ <doc>
+ This method confirms a Declare method and confirms the name of the exchange,
+ essential for automatically-named exchanges.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "delete" synchronous = "1" index = "20" label = "delete an exchange">
+ <doc>
+ This method deletes an exchange. When an exchange is deleted all queue bindings on
+ the exchange are cancelled.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "delete-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "validity" on-failure = "access-refused">
+ <doc>
+ The client MUST provide a valid access ticket giving "active" access
+ rights to the exchange's access realm.
+ </doc>
+ <doc type = "scenario">
+ Client creates access ticket with wrong access rights and attempts to use
+ in this method.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name">
+ <rule name = "exists" on-failure = "not-found">
+ <doc>
+ The client MUST NOT attempt to delete an exchange that does not exist.
+ </doc>
+ </rule>
+ <assert check = "notnull" />
+ </field>
+
+ <!-- TODO 0.82 - discuss whether this option is useful or not. I don't have
+ any real use case for it. /PH 2006-07-23.
+ -->
+ <field name = "if-unused" domain = "bit" label = "delete only if unused">
+ <doc>
+ If set, the server will only delete the exchange if it has no queue bindings. If
+ the exchange has queue bindings the server does not delete it but raises a
+ channel exception instead.
+ </doc>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "delete-ok" synchronous = "1" index = "21"
+ label = "confirm deletion of an exchange">
+ <doc>This method confirms the deletion of an exchange.</doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+ </class>
+
+ <!-- == QUEUE ============================================================ -->
+
+ <class name = "queue" handler = "channel" index = "50" label = "work with queues">
+ <doc>
+ Queues store and forward messages. Queues can be configured in the server or created at
+ runtime. Queues must be attached to at least one exchange in order to receive messages
+ from publishers.
+ </doc>
+
+ <doc type = "grammar">
+ queue = C:DECLARE S:DECLARE-OK
+ / C:BIND S:BIND-OK
+ / C:PURGE S:PURGE-OK
+ / C:DELETE S:DELETE-OK
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <rule name = "any-content">
+ <doc>
+ A server MUST allow any content class to be sent to any queue, in any mix, and
+ queue and deliver these content classes independently. Note that all methods
+ that fetch content off queues are specific to a given content class.
+ </doc>
+ <doc type = "scenario">
+ Client creates an exchange of each standard type and several queues that
+ it binds to each exchange. It must then sucessfully send each of the standard
+ content types to each of the available queues.
+ </doc>
+ </rule>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "declare" synchronous = "1" index = "10" label = "declare queue, create if needed">
+ <doc>
+ This method creates or checks a queue. When creating a new queue the client can
+ specify various properties that control the durability of the queue and its
+ contents, and the level of sharing for the queue.
+ </doc>
+
+ <rule name = "default-binding">
+ <doc>
+ The server MUST create a default binding for a newly-created queue to the
+ default exchange, which is an exchange of type 'direct'.
+ </doc>
+ <doc type = "scenario">
+ Client creates a new queue, and then without explicitly binding it to an
+ exchange, attempts to send a message through the default exchange binding,
+ i.e. publish a message to the empty exchange, with the queue name as routing
+ key.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_35" -->
+ <rule name = "minimum-queues">
+ <doc>
+ The server SHOULD support a minimum of 256 queues per virtual host and ideally,
+ impose no limit except as defined by available resources.
+ </doc>
+ <doc type = "scenario">
+ Client attempts to create as many queues as it can until the server reports
+ an error. The resulting count must at least be 256.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "declare-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <doc>
+ When a client defines a new queue, this belongs to the access realm of the
+ ticket used. All further work done with that queue must be done with an access
+ ticket for the same realm.
+ </doc>
+ <rule name = "validity" on-failure = "access-refused">
+ <doc>
+ The client MUST provide a valid access ticket giving "active" access to
+ the realm in which the queue exists or will be created.
+ </doc>
+ <doc type = "scenario">
+ Client creates access ticket with wrong access rights and attempts to use
+ in this method.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <rule name = "default-name">
+ <doc>
+ The queue name MAY be empty, in which case the server MUST create a new
+ queue with a unique generated name and return this to the client in the
+ Declare-Ok method.
+ </doc>
+ <doc type = "scenario">
+ Client attempts to create several queues with an empty name. The client then
+ verifies that the server-assigned names are unique and different.
+ </doc>
+ </rule>
+ <rule name = "reserved-prefix" on-failure = "not-allowed">
+ <doc>
+ Queue names starting with "amq." are reserved for predeclared and
+ standardised server queues. A client MAY NOT attempt to declare a queue with a
+ name that starts with "amq." and the passive option set to zero.
+ </doc>
+ <doc type = "scenario">
+ A client attempts to create a queue with a name starting with "amq." and with
+ the passive option set to zero.
+ </doc>
+ </rule>
+ <assert check = "regexp" value = "^[a-zA-Z0-9-_.:]*$" />
+ </field>
+
+ <field name = "passive" domain = "bit" label = "do not create queue">
+ <doc>
+ If set, the server will not create the queue. This field allows the client
+ to assert the presence of a queue without modifying the server state.
+ </doc>
+ <rule name = "passive" on-failure = "not-found">
+ <doc>
+ The client MAY ask the server to assert that a queue exists without
+ creating the queue if not. If the queue does not exist, the server
+ treats this as a failure.
+ </doc>
+ <doc type = "scenario">
+ Client declares an existing queue with the passive option and expects
+ the server to respond with a declare-ok. Client then attempts to declare
+ a non-existent queue with the passive option, and the server must close
+ the channel with the correct reply-code.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "durable" domain = "bit" label = "request a durable queue">
+ <doc>
+ If set when creating a new queue, the queue will be marked as durable. Durable
+ queues remain active when a server restarts. Non-durable queues (transient
+ queues) are purged if/when a server restarts. Note that durable queues do not
+ necessarily hold persistent messages, although it does not make sense to send
+ persistent messages to a transient queue.
+ </doc>
+ <!-- Rule test name: was "amq_queue_03" -->
+ <rule name = "persistence">
+ <doc>The server MUST recreate the durable queue after a restart.</doc>
+
+ <!-- TODO: use 'client does something' rather than 'a client does something'. -->
+ <doc type = "scenario">
+ A client creates a durable queue. The server is then restarted. The client
+ then attempts to send a message to the queue. The message should be successfully
+ delivered.
+ </doc>
+ </rule>
+ <!-- Rule test name: was "amq_queue_36" -->
+ <rule name = "types">
+ <doc>The server MUST support both durable and transient queues.</doc>
+ <doc type = "scenario">
+ A client creates two named queues, one durable and one transient.
+ </doc>
+ </rule>
+ <!-- Rule test name: was "amq_queue_37" -->
+ <rule name = "pre-existence">
+ <doc>The server MUST ignore the durable field if the queue already exists.</doc>
+ <doc type = "scenario">
+ A client creates two named queues, one durable and one transient. The client
+ then attempts to declare the two queues using the same names again, but reversing
+ the value of the durable flag in each case. Verify that the queues still exist
+ with the original durable flag values.
+ <!-- TODO: but how? -->
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exclusive" domain = "bit" label = "request an exclusive queue">
+ <doc>
+ Exclusive queues may only be consumed from by the current connection. Setting
+ the 'exclusive' flag always implies 'auto-delete'.
+ </doc>
+
+ <!-- Rule test name: was "amq_queue_38" -->
+ <rule name = "types">
+ <doc>
+ The server MUST support both exclusive (private) and non-exclusive (shared)
+ queues.
+ </doc>
+ <doc type = "scenario">
+ A client creates two named queues, one exclusive and one non-exclusive.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_04" -->
+ <rule name = "02" on-failure = "channel-error">
+ <doc>
+ The client MAY NOT attempt to declare any existing and exclusive queue
+ on multiple connections.
+ </doc>
+ <doc type = "scenario">
+ A client declares an exclusive named queue. A second client on a different
+ connection attempts to declare a queue of the same name.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "auto-delete" domain = "bit" label = "auto-delete queue when unused">
+ <doc>
+ If set, the queue is deleted when all consumers have finished using it. Last
+ consumer can be cancelled either explicitly or because its channel is closed. If
+ there was no consumer ever on the queue, it won't be deleted.
+ </doc>
+
+ <!-- Rule test name: was "amq_queue_31" -->
+ <rule name = "pre-existence">
+ <doc>
+ The server MUST ignore the auto-delete field if the queue already exists.
+ </doc>
+ <doc type = "scenario">
+ A client creates two named queues, one as auto-delete and one explicit-delete.
+ The client then attempts to declare the two queues using the same names again,
+ but reversing the value of the auto-delete field in each case. Verify that the
+ queues still exist with the original auto-delete flag values.
+ <!-- TODO: but how? -->
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+
+ <field name = "arguments" domain = "table" label = "arguments for declaration">
+ <doc>
+ A set of arguments for the declaration. The syntax and semantics of these
+ arguments depends on the server implementation. This field is ignored if passive
+ is 1.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "declare-ok" synchronous = "1" index = "11" label = "confirms a queue definition">
+ <doc>
+ This method confirms a Declare method and confirms the name of the queue, essential
+ for automatically-named queues.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Reports the name of the queue. If the server generated a queue name, this field
+ contains that name.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+
+ <field name = "message-count" domain = "long" label = "number of messages in queue">
+ <doc>
+ Reports the number of messages in the queue, which will be zero for
+ newly-created queues.
+ </doc>
+ </field>
+
+ <field name = "consumer-count" domain = "long" label = "number of consumers">
+ <doc>
+ Reports the number of active consumers for the queue. Note that consumers can
+ suspend activity (Channel.Flow) in which case they do not appear in this count.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "bind" synchronous = "1" index = "20" label = "bind queue to an exchange">
+ <doc>
+ This method binds a queue to an exchange. Until a queue is bound it will not receive
+ any messages. In a classic messaging model, store-and-forward queues are bound to a
+ dest exchange and subscription queues are bound to a dest_wild exchange.
+ </doc>
+
+ <!-- Rule test name: was "amq_queue_25" -->
+ <rule name = "duplicates">
+ <doc>
+ A server MUST allow ignore duplicate bindings - that is, two or more bind
+ methods for a specific queue, with identical arguments - without treating these
+ as an error.
+ </doc>
+ <doc type = "scenario">
+ A client binds a named queue to an exchange. The client then repeats the bind
+ (with identical arguments).
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_39" -->
+ <rule name = "failure" on-failure = "??????">
+ <!--
+ TODO: Find correct code. The on-failure code returned should depend on why the bind
+ failed. Assuming that failures owing to bad parameters are covered in the rules relating
+ to those parameters, the only remaining reason for a failure would be the lack of
+ server resorces or some internal error - such as too many queues open. Would these
+ cases qualify as "resource error" 506 or "internal error" 541?
+ -->
+ <doc>If a bind fails, the server MUST raise a connection exception.</doc>
+ <doc type = "scenario">
+ TODO
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_12" -->
+ <rule name = "transient-exchange" on-failure = "not-allowed">
+ <doc>
+ The server MUST NOT allow a durable queue to bind to a transient exchange.
+ </doc>
+ <doc type = "scenario">
+ A client creates a transient exchange. The client then declares a named durable
+ queue and then attempts to bind the transient exchange to the durable queue.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_13" -->
+ <rule name = "durable-exchange">
+ <doc>
+ Bindings for durable queues are automatically durable and the server SHOULD
+ restore such bindings after a server restart.
+ </doc>
+ <doc type = "scenario">
+ A server creates a named durable queue and binds it to a durable exchange. The
+ server is restarted. The client then attempts to use the queue/exchange combination.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_17" -->
+ <rule name = "internal-exchange">
+ <doc>
+ If the client attempts to bind to an exchange that was declared as internal, the server
+ MUST raise a connection exception with reply code 530 (not allowed).
+ </doc>
+ <doc type = "scenario">
+ A client attempts to bind a named queue to an internal exchange.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_40" -->
+ <rule name = "binding-count">
+ <doc>
+ The server SHOULD support at least 4 bindings per queue, and ideally, impose no
+ limit except as defined by available resources.
+ </doc>
+ <doc type = "scenario">
+ A client creates a named queue and attempts to bind it to 4 different non-internal
+ exchanges.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "bind-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <doc>
+ The client provides a valid access ticket giving "active" access rights to the
+ queue's access realm.
+ </doc>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to bind. If the queue name is empty, refers to
+ the current queue for the channel, which is the last declared queue.
+ </doc>
+
+ <rule name = "empty-queue" on-failure = "not-allowed">
+ <doc>
+ A client MUST NOT be allowed to bind a non-existent and unnamed queue (i.e.
+ empty queue name) to an exchange.
+ </doc>
+ <doc type = "scenario">
+ A client attempts to bind with an unnamed (empty) queue name to an exchange.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_26" -->
+ <rule name = "queue-existence" on-failure = "not-found">
+ <doc>
+ A client MUST NOT be allowed to bind a non-existent queue (i.e. not previously
+ declared) to an exchange.
+ </doc>
+ <doc type = "scenario">
+ A client attempts to bind an undeclared queue name to an exchange.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name" label = "name of the exchange to bind to">
+ <!-- Rule test name: was "amq_queue_14" -->
+ <rule name = "exchange-existence" on-failure = "not-found">
+ <doc>
+ A client MUST NOT be allowed to bind a queue to a non-existent exchange.
+ </doc>
+ <doc type = "scenario">
+ A client attempts to bind an named queue to a undeclared exchange.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "message routing key">
+ <doc>
+ Specifies the routing key for the binding. The routing key is used for routing
+ messages depending on the exchange configuration. Not all exchanges use a
+ routing key - refer to the specific exchange documentation. If the queue name
+ is empty, the server uses the last queue declared on the channel. If the
+ routing key is also empty, the server uses this queue name for the routing
+ key as well. If the queue name is provided but the routing key is empty, the
+ server does the binding with that empty routing key. The meaning of empty
+ routing keys depends on the exchange implementation.
+ </doc>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+
+ <field name = "arguments" domain = "table" label = "arguments for binding">
+ <doc>
+ A set of arguments for the binding. The syntax and semantics of these arguments
+ depends on the exchange class.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "bind-ok" synchronous = "1" index = "21" label = "confirm bind successful">
+ <doc>This method confirms that the bind was successful.</doc>
+
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "purge" synchronous = "1" index = "30" label = "purge a queue">
+ <doc>
+ This method removes all messages from a queue. It does not cancel consumers. Purged
+ messages are deleted without any formal "undo" mechanism.
+ </doc>
+
+ <!-- Rule test name: was "amq_queue_15" -->
+ <rule name = "01">
+ <doc>A call to purge MUST result in an empty queue.</doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_41" -->
+ <rule name = "02">
+ <doc>
+ On transacted channels the server MUST not purge messages that have already been
+ sent to a client but not yet acknowledged.
+ </doc>
+ </rule>
+
+ <!-- TODO: Rule split? -->
+
+ <!-- Rule test name: was "amq_queue_42" -->
+ <rule name = "03">
+ <doc>
+ The server MAY implement a purge queue or log that allows system administrators
+ to recover accidentally-purged messages. The server SHOULD NOT keep purged
+ messages in the same storage spaces as the live messages since the volumes of
+ purged messages may get very large.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "purge-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <doc>The access ticket must be for the access realm that holds the queue.</doc>
+
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "read" access rights to
+ the queue's access realm. Note that purging a queue is equivalent to reading
+ all messages and discarding them.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to purge. If the queue name is empty, refers to
+ the current queue for the channel, which is the last declared queue.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ If the client did not previously declare a queue, and the queue name in this
+ method is empty, the server MUST raise a connection exception with reply
+ code 530 (not allowed).
+ </doc>
+ </rule>
+
+ <!-- TODO Rule split? -->
+
+ <!-- Rule test name: was "amq_queue_16" -->
+ <rule name = "02">
+ <doc>
+ The queue MUST exist. Attempting to purge a non-existing queue MUST cause a
+ channel exception.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "purge-ok" synchronous = "1" index = "31" label = "confirms a queue purge">
+ <doc>This method confirms the purge of a queue.</doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "message-count" domain = "long" label = "number of messages purged">
+ <doc>Reports the number of messages purged.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "delete" synchronous = "1" index = "40" label = "delete a queue">
+ <doc>
+ This method deletes a queue. When a queue is deleted any pending messages are sent
+ to a dead-letter queue if this is defined in the server configuration, and all
+ consumers on the queue are cancelled.
+ </doc>
+
+ <!-- TODO: Rule split? -->
+
+ <!-- Rule test name: was "amq_queue_43" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD use a dead-letter queue to hold messages that were pending on
+ a deleted queue, and MAY provide facilities for a system administrator to move
+ these messages back to an active queue.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "delete-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <doc>
+ The client provides a valid access ticket giving "active" access rights to the
+ queue's access realm.
+ </doc>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to delete. If the queue name is empty, refers to
+ the current queue for the channel, which is the last declared queue.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ If the client did not previously declare a queue, and the queue name in this
+ method is empty, the server MUST raise a connection exception with reply
+ code 530 (not allowed).
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_queue_21" -->
+ <rule name = "02">
+ <doc>
+ The queue must exist. If the client attempts to delete a non-existing queue
+ the server MUST raise a channel exception with reply code 404 (not found).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "if-unused" domain = "bit" label = "delete only if unused">
+ <doc>
+ If set, the server will only delete the queue if it has no consumers. If the
+ queue has consumers the server does does not delete it but raises a channel
+ exception instead.
+ </doc>
+
+ <!-- Rule test name: was "amq_queue_29" and "amq_queue_30" -->
+ <rule name = "01">
+ <doc>The server MUST respect the if-unused flag when deleting a queue.</doc>
+ </rule>
+ </field>
+
+ <field name = "if-empty" domain = "bit" label = "delete only if empty">
+ <doc>
+ If set, the server will only delete the queue if it has no messages.
+ </doc>
+ <rule name = "01">
+ <doc>
+ If the queue is not empty the server MUST raise a channel exception with
+ reply code 406 (precondition failed).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "delete-ok" synchronous = "1" index = "41" label = "confirm deletion of a queue">
+ <doc>This method confirms the deletion of a queue.</doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "message-count" domain = "long" label = "number of messages purged">
+ <doc>Reports the number of messages purged.</doc>
+ </field>
+ </method>
+ </class>
+
+ <!-- == BASIC ============================================================ -->
+
+ <class name = "basic" handler = "channel" index = "60" label = "work with basic content">
+ <doc>
+ The Basic class provides methods that support an industry-standard messaging model.
+ </doc>
+
+ <doc type = "grammar">
+ basic = C:QOS S:QOS-OK
+ / C:CONSUME S:CONSUME-OK
+ / C:CANCEL S:CANCEL-OK
+ / C:PUBLISH content
+ / S:RETURN content
+ / S:DELIVER content
+ / C:GET ( S:GET-OK content / S:GET-EMPTY )
+ / C:ACK
+ / C:REJECT
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MAY" />
+
+ <!-- Rule test name: was "amq_basic_08" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD respect the persistent property of basic messages and
+ SHOULD make a best-effort to hold persistent basic messages on a reliable
+ storage mechanism.
+ </doc>
+ <doc type = "scenario">
+ Send a persistent message to queue, stop server, restart server and then
+ verify whether message is still present. Assumes that queues are durable.
+ Persistence without durable queues makes no sense.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_09" -->
+ <rule name = "02">
+ <doc>
+ The server MUST NOT discard a persistent basic message in case of a queue
+ overflow.
+ </doc>
+ <doc type = "scenario">
+ Create a queue overflow situation with persistent messages and verify that
+ messages do not get lost (presumably the server will write them to disk).
+ </doc>
+ </rule>
+
+ <rule name = "03">
+ <doc>
+ The server MAY use the Channel.Flow method to slow or stop a basic message
+ publisher when necessary.
+ </doc>
+ <doc type = "scenario">
+ Create a queue overflow situation with non-persistent messages and verify
+ whether the server responds with Channel.Flow or not. Repeat with persistent
+ messages.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_10" -->
+ <rule name = "04">
+ <doc>
+ The server MAY overflow non-persistent basic messages to persistent
+ storage.
+ </doc>
+ <!-- Test scenario: untestable -->
+ </rule>
+
+ <rule name = "05">
+ <doc>
+ The server MAY discard or dead-letter non-persistent basic messages on a
+ priority basis if the queue size exceeds some configured limit.
+ </doc>
+ <!-- Test scenario: untestable -->
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_11" -->
+ <rule name = "06">
+ <doc>
+ The server MUST implement at least 2 priority levels for basic messages,
+ where priorities 0-4 and 5-9 are treated as two distinct levels.
+ </doc>
+ <doc type = "scenario">
+ Send a number of priority 0 messages to a queue. Send one priority 9
+ message. Consume messages from the queue and verify that the first message
+ received was priority 9.
+ </doc>
+ </rule>
+
+ <rule name = "07">
+ <doc>
+ The server MAY implement up to 10 priority levels.
+ </doc>
+ <doc type = "scenario">
+ Send a number of messages with mixed priorities to a queue, so that all
+ priority values from 0 to 9 are exercised. A good scenario would be ten
+ messages in low-to-high priority. Consume from queue and verify how many
+ priority levels emerge.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_12" -->
+ <rule name = "08">
+ <doc>
+ The server MUST deliver messages of the same priority in order irrespective of
+ their individual persistence.
+ </doc>
+ <doc type = "scenario">
+ Send a set of messages with the same priority but different persistence
+ settings to a queue. Consume and verify that messages arrive in same order
+ as originally published.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_13" -->
+ <rule name = "09">
+ <doc>
+ The server MUST support automatic acknowledgements on Basic content, i.e.
+ consumers with the no-ack field set to FALSE.
+ </doc>
+ <doc type = "scenario">
+ Create a queue and a consumer using automatic acknowledgements. Publish
+ a set of messages to the queue. Consume the messages and verify that all
+ messages are received.
+ </doc>
+ </rule>
+
+ <rule name = "10">
+ <doc>
+ The server MUST support explicit acknowledgements on Basic content, i.e.
+ consumers with the no-ack field set to TRUE.
+ </doc>
+ <doc type = "scenario">
+ Create a queue and a consumer using explicit acknowledgements. Publish a
+ set of messages to the queue. Consume the messages but acknowledge only
+ half of them. Disconnect and reconnect, and consume from the queue.
+ Verify that the remaining messages are received.
+ </doc>
+ </rule>
+
+ <!-- These are the properties for a Basic content -->
+
+ <field name = "content-type" domain = "shortstr" label = "MIME content type" />
+ <field name = "content-encoding" domain = "shortstr" label = "MIME content encoding" />
+ <field name = "headers" domain = "table" label = "message header field table" />
+ <field name = "delivery-mode" domain = "octet" label = "non-persistent (1) or persistent (2)" />
+ <field name = "priority" domain = "octet" label = "message priority, 0 to 9" />
+ <field name = "correlation-id" domain = "shortstr" label = "application correlation identifier" />
+ <field name = "reply-to" domain = "shortstr" label = "destination to reply to" />
+ <field name = "expiration" domain = "shortstr" label = "message expiration specification" />
+ <field name = "message-id" domain = "shortstr" label = "application message identifier" />
+ <field name = "timestamp" domain = "timestamp" label = "message timestamp" />
+ <field name = "type" domain = "shortstr" label = "message type name" />
+ <field name = "user-id" domain = "shortstr" label = "creating user id" />
+ <field name = "app-id" domain = "shortstr" label = "creating application id" />
+ <!-- This field is deprecated pending review -->
+ <field name = "cluster-id" domain = "shortstr" label = "intra-cluster routing identifier" />
+
+ <field name = "property-one" domain = "shortstr" label = "Extra property for testing only" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "qos" synchronous = "1" index = "10" label = "specify quality of service">
+ <doc>
+ This method requests a specific quality of service. The QoS can be specified for the
+ current channel or for all channels on the connection. The particular properties and
+ semantics of a qos method always depend on the content class semantics. Though the
+ qos method could in principle apply to both peers, it is currently meaningful only
+ for the server.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "qos-ok" />
+
+ <field name = "prefetch-size" domain = "long" label = "prefetch window in octets">
+ <doc>
+ The client can request that messages be sent in advance so that when the client
+ finishes processing a message, the following message is already held locally,
+ rather than needing to be sent down the channel. Prefetching gives a performance
+ improvement. This field specifies the prefetch window size in octets. The server
+ will send a message in advance if it is equal to or smaller in size than the
+ available prefetch size (and also falls into other prefetch limits). May be set
+ to zero, meaning "no specific limit", although other prefetch limits may still
+ apply. The prefetch-size is ignored if the no-ack option is set.
+ </doc>
+ <!-- Rule test name: was "amq_basic_17" -->
+ <rule name = "01">
+ <doc>
+ The server MUST ignore this setting when the client is not processing any
+ messages - i.e. the prefetch size does not limit the transfer of single
+ messages to a client, only the sending in advance of more messages while
+ the client still has one or more unacknowledged messages.
+ </doc>
+ <doc type = "scenario">
+ Define a QoS prefetch-size limit and send a single message that exceeds
+ that limit. Verify that the message arrives correctly.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "prefetch-count" domain = "short" label = "prefetch window in messages">
+ <doc>
+ Specifies a prefetch window in terms of whole messages. This field may be used
+ in combination with the prefetch-size field; a message will only be sent in
+ advance if both prefetch windows (and those at the channel and connection level)
+ allow it. The prefetch-count is ignored if the no-ack option is set.
+ </doc>
+ <!-- Rule test name: was "amq_basic_18" -->
+ <rule name = "01">
+ <doc>
+ The server may send less data in advance than allowed by the client's
+ specified prefetch windows but it MUST NOT send more.
+ </doc>
+ <doc type = "scenario">
+ Define a QoS prefetch-size limit and a prefetch-count limit greater than
+ one. Send multiple messages that exceed the prefetch size. Verify that
+ no more than one message arrives at once.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "global" domain = "bit" label = "apply to entire connection">
+ <doc>
+ By default the QoS settings apply to the current channel only. If this field is
+ set, they are applied to the entire connection.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "qos-ok" synchronous = "1" index = "11" label = "confirm the requested qos">
+ <doc>
+ This method tells the client that the requested QoS levels could be handled by the
+ server. The requested QoS applies to all active consumers until a new QoS is
+ defined.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "consume" synchronous = "1" index = "30" label = "start a queue consumer">
+ <doc>
+ This method asks the server to start a "consumer", which is a transient request for
+ messages from a specific queue. Consumers last as long as the channel they were
+ created on, or until the client cancels them.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_01" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD support at least 16 consumers per queue, and ideally, impose
+ no limit except as defined by available resources.
+ </doc>
+ <doc type = "scenario">
+ Create a queue and create consumers on that queue until the server closes the
+ connection. Verify that the number of consumers created was at least sixteen
+ and report the total number.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "consume-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01" on-failure = "access-refused">
+ <doc>
+ The client MUST provide a valid access ticket giving "read" access rights to
+ the realm for the queue.
+ </doc>
+ <doc type = "scenario">
+ Attempt to create a consumer with an invalid (non-zero) access ticket.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to consume from. If the queue name is null,
+ refers to the current queue for the channel, which is the last declared queue.
+ </doc>
+ <rule name = "01" on-failure = "not-allowed">
+ <doc>
+ If the queue name is empty the client MUST have previously declared a
+ queue using this channel.
+ </doc>
+ <doc type = "scenario">
+ Attempt to create a consumer with an empty queue name and no previously
+ declared queue on the channel.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>
+ Specifies the identifier for the consumer. The consumer tag is local to a
+ connection, so two clients can use the same consumer tags. If this field is
+ empty the server will generate a unique tag.
+ </doc>
+ <rule name = "01" on-failure = "not-allowed">
+ <doc>
+ The client MUST NOT specify a tag that refers to an existing consumer.
+ </doc>
+ <doc type = "scenario">
+ Attempt to create two consumers with the same non-empty tag.
+ </doc>
+ </rule>
+ <rule name = "02" on-failure = "not-allowed">
+ <doc>
+ The consumer tag is valid only within the channel from which the
+ consumer was created. I.e. a client MUST NOT create a consumer in one
+ channel and then use it in another.
+ </doc>
+ <doc type = "scenario">
+ Attempt to create a consumer in one channel, then use in another channel,
+ in which consumers have also been created (to test that the server uses
+ unique consumer tags).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "no-local" domain = "no-local" />
+
+ <field name = "no-ack" domain = "no-ack" />
+
+ <field name = "exclusive" domain = "bit" label = "request exclusive access">
+ <doc>
+ Request exclusive consumer access, meaning only this consumer can access the
+ queue.
+ </doc>
+ <!-- Rule test name: was "amq_basic_02" -->
+ <rule name = "01" on-failure = "access-refused">
+ <doc>
+ The client MAY NOT gain exclusive access to a queue that already has
+ active consumers.
+ </doc>
+ <doc type = "scenario">
+ Open two connections to a server, and in one connection create a shared
+ (non-exclusive) queue and then consume from the queue. In the second
+ connection attempt to consume from the same queue using the exclusive
+ option.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise
+ a channel or connection exception.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "consume-ok" synchronous = "1" index = "31" label = "confirm a new consumer">
+ <doc>
+ The server provides the client with a consumer tag, which is used by the client
+ for methods called on the consumer at a later stage.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>
+ Holds the consumer tag specified by the client or provided by the server.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "cancel" synchronous = "1" index = "20" label = "end a queue consumer">
+ <doc>
+ This method cancels a consumer. This does not affect already delivered
+ messages, but it does mean the server will not send any more messages for
+ that consumer. The client may receive an abitrary number of messages in
+ between sending the cancel method and receiving the cancel-ok reply.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ If the queue does not exist the server MUST ignore the cancel method, so
+ long as the consumer tag is valid for that channel.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "cancel-ok" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "cancel-ok" synchronous = "1" index = "21" label = "confirm a cancelled consumer">
+ <doc>
+ This method confirms that the cancellation was completed.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ <field name = "consumer-tag" domain = "consumer-tag" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "publish" content = "1" index = "40" label = "publish a message">
+ <doc>
+ This method publishes a message to a specific exchange. The message will be routed
+ to queues as defined by the exchange configuration and distributed to any active
+ consumers when the transaction, if any, is committed.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "write" access rights
+ to the access realm for the exchange.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange to publish to. The exchange name can be
+ empty, meaning the default exchange. If the exchange name is specified, and that
+ exchange does not exist, the server will raise a channel exception.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_06" -->
+ <rule name = "01">
+ <doc>
+ The server MUST accept a blank exchange name to mean the default exchange.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_14" -->
+ <rule name = "02">
+ <doc>
+ If the exchange was declared as an internal exchange, the server MUST raise
+ a channel exception with a reply code 403 (access refused).
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_15" -->
+ <rule name = "03">
+ <doc>
+ The exchange MAY refuse basic content in which case it MUST raise a channel
+ exception with reply code 540 (not implemented).
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>
+ Specifies the routing key for the message. The routing key is used for routing
+ messages depending on the exchange configuration.
+ </doc>
+ </field>
+
+ <field name = "mandatory" domain = "bit" label = "indicate mandatory routing">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue. If this flag is set, the server will return an unroutable message with a
+ Return method. If this flag is zero, the server silently drops the message.
+ </doc>
+ <!-- Rule test name: was "amq_basic_07" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD implement the mandatory flag.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "immediate" domain = "bit" label = "request immediate delivery">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue consumer immediately. If this flag is set, the server will return an
+ undeliverable message with a Return method. If this flag is zero, the server
+ will queue the message, but with no guarantee that it will ever be consumed.
+ </doc>
+ <!-- Rule test name: was "amq_basic_16" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD implement the immediate flag.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "return" content = "1" index = "50" label = "return a failed message">
+ <doc>
+ This method returns an undeliverable message that was published with the "immediate"
+ flag set, or an unroutable message published with the "mandatory" flag set. The
+ reply code and text provide information about the reason that the message was
+ undeliverable.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "reply-code" domain = "reply-code" />
+
+ <field name = "reply-text" domain = "reply-text" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>
+ Specifies the routing key name specified when the message was published.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "deliver" content = "1" index = "60"
+ label = "notify the client of a consumer message">
+ <doc>
+ This method delivers a message to the client, via a consumer. In the asynchronous
+ message delivery model, the client starts a consumer using the Consume method, then
+ the server responds with Deliver methods as and when messages arrive for that
+ consumer.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_19" -->
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server SHOULD track the number of times a message has been delivered to
+ clients and when a message is redelivered a certain number of times - e.g. 5
+ times - without being acknowledged, the server SHOULD consider the message to be
+ unprocessable (possibly causing client applications to abort), and move the
+ message to a dead letter queue.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "redelivered" domain = "redelivered" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>Specifies the routing key name specified when the message was published.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "get" synchronous = "1" index = "70" label = "direct access to a queue">
+ <doc>
+ This method provides a direct access to the messages in a queue using a synchronous
+ dialogue that is designed for specific types of application where synchronous
+ functionality is more important than performance.
+ </doc>
+
+ <response name = "get-ok" />
+ <response name = "get-empty" />
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "read" access rights to
+ the realm for the queue.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to consume from. If the queue name is null,
+ refers to the current queue for the channel, which is the last declared queue.
+ </doc>
+ <rule name = "01">
+ <doc>
+ If the client did not previously declare a queue, and the queue name in this
+ method is empty, the server MUST raise a connection exception with reply
+ code 530 (not allowed).
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "no-ack" domain = "no-ack" />
+ </method>
+
+ <method name = "get-ok" synchronous = "1" content = "1" index = "71"
+ label = "provide client with a message">
+ <doc>
+ This method delivers a message to the client following a get method. A message
+ delivered by 'get-ok' must be acknowledged unless the no-ack option was set in the
+ get method.
+ </doc>
+
+ <chassis name = "client" implement = "MAY" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "redelivered" domain = "redelivered" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ If empty, the message was published to the default exchange.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>Specifies the routing key name specified when the message was published.</doc>
+ </field>
+
+ <field name = "message-count" domain = "long" label = "number of messages pending">
+ <doc>
+ This field reports the number of messages pending on the queue, excluding the
+ message being delivered. Note that this figure is indicative, not reliable, and
+ can change arbitrarily as messages are added to the queue and removed by other
+ clients.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "get-empty" synchronous = "1" index = "72"
+ label = "indicate no messages available">
+ <doc>
+ This method tells the client that the queue has no messages available for the
+ client.
+ </doc>
+
+ <chassis name = "client" implement = "MAY" />
+
+ <!-- This field is deprecated pending review -->
+ <field name = "cluster-id" domain = "shortstr" label = "Cluster id">
+ <doc>
+ For use by cluster applications, should not be used by client applications.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "ack" index = "80" label = "acknowledge one or more messages">
+ <doc>
+ This method acknowledges one or more messages delivered via the Deliver or Get-Ok
+ methods. The client can ask to confirm a single message or a set of messages up to
+ and including a specific message.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "multiple" domain = "bit" label = "acknowledge multiple messages">
+ <doc>
+ If set to 1, the delivery tag is treated as "up to and including", so that the
+ client can acknowledge multiple messages with a single method. If set to zero,
+ the delivery tag refers to a single message. If the multiple field is 1, and the
+ delivery tag is zero, tells the server to acknowledge all outstanding mesages.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_20" -->
+ <rule name = "01">
+ <doc>
+ The server MUST validate that a non-zero delivery-tag refers to an delivered
+ message, and raise a channel exception if this is not the case.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "reject" index = "90" label = "reject an incoming message">
+ <doc>
+ This method allows a client to reject a message. It can be used to interrupt and
+ cancel large incoming messages, or return untreatable messages to their original
+ queue.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_21" -->
+ <rule name = "01">
+ <doc>
+ The server SHOULD be capable of accepting and process the Reject method while
+ sending message content with a Deliver or Get-Ok method. I.e. the server should
+ read and process incoming methods while sending output frames. To cancel a
+ partially-send content, the server sends a content body frame of size 1 (i.e.
+ with no data except the frame-end octet).
+ </doc>
+ </rule>
+
+ <!-- Rule test name: was "amq_basic_22" -->
+ <rule name = "02">
+ <doc>
+ The server SHOULD interpret this method as meaning that the client is unable to
+ process the message at this time.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <rule name = "03">
+ <!-- TODO: Rule split? -->
+ <doc>
+ A client MUST NOT use this method as a means of selecting messages to process. A
+ rejected message MAY be discarded or dead-lettered, not necessarily passed to
+ another client.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "requeue" domain = "bit" label = "requeue the message">
+ <doc>
+ If this field is zero, the message will be discarded. If this bit is 1, the
+ server will attempt to requeue the message.
+ </doc>
+
+ <!-- Rule test name: was "amq_basic_23" -->
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server MUST NOT deliver the message to the same client within the
+ context of the current channel. The recommended strategy is to attempt to
+ deliver the message to an alternative consumer, and if that is not possible,
+ to move the message to a dead-letter queue. The server MAY use more
+ sophisticated tracking to hold the message on the queue and redeliver it to
+ the same client at a later stage.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "recover" index = "100" label = "redeliver unacknowledged messages">
+ <doc>
+ This method asks the broker to redeliver all unacknowledged messages on a specified
+ channel. Zero or more messages may be redelivered. This method is only allowed on
+ non-transacted channels.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server MUST set the redelivered flag on all messages that are resent.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <doc>
+ The server MUST raise a channel exception if this is called on a transacted
+ channel.
+ </doc>
+ <doc type = "scenario">
+ TODO.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "requeue" domain = "bit" label = "requeue the message">
+ <doc>
+ If this field is zero, the message will be redelivered to the original
+ recipient. If this bit is 1, the server will attempt to requeue the message,
+ potentially then delivering it to an alternative subscriber.
+ </doc>
+ </field>
+ </method>
+ </class>
+
+ <!-- == FILE ============================================================= -->
+
+ <class name = "file" handler = "channel" index = "70" label = "work with file content">
+ <doc>
+ The file class provides methods that support reliable file transfer. File
+ messages have a specific set of properties that are required for interoperability
+ with file transfer applications. File messages and acknowledgements are subject to
+ channel transactions. Note that the file class does not provide message browsing
+ methods; these are not compatible with the staging model. Applications that need
+ browsable file transfer should use Basic content and the Basic class.
+ </doc>
+
+ <doc type = "grammar">
+ file = C:QOS S:QOS-OK
+ / C:CONSUME S:CONSUME-OK
+ / C:CANCEL S:CANCEL-OK
+ / C:OPEN S:OPEN-OK C:STAGE content
+ / S:OPEN C:OPEN-OK S:STAGE content
+ / C:PUBLISH
+ / S:DELIVER
+ / S:RETURN
+ / C:ACK
+ / C:REJECT
+ </doc>
+
+ <chassis name = "server" implement = "MAY" />
+ <chassis name = "client" implement = "MAY" />
+
+ <rule name = "01">
+ <doc>
+ The server MUST make a best-effort to hold file messages on a reliable storage
+ mechanism.
+ </doc>
+ </rule>
+
+ <!-- TODO Rule implement attr inverse? -->
+
+ <!-- TODO: Rule split? -->
+
+ <rule name = "02">
+ <doc>
+ The server MUST NOT discard a file message in case of a queue overflow. The server
+ MUST use the Channel.Flow method to slow or stop a file message publisher when
+ necessary.
+ </doc>
+ </rule>
+
+ <!-- TODO: Rule split? -->
+
+ <rule name = "03">
+ <doc>
+ The server MUST implement at least 2 priority levels for file messages, where
+ priorities 0-4 and 5-9 are treated as two distinct levels. The server MAY implement
+ up to 10 priority levels.
+ </doc>
+ </rule>
+
+ <rule name = "04">
+ <doc>
+ The server MUST support both automatic and explicit acknowledgements on file
+ content.
+ </doc>
+ </rule>
+
+ <!-- These are the properties for a File content -->
+
+ <field name = "content-type" domain = "shortstr" label = "MIME content type" />
+ <field name = "content-encoding" domain = "shortstr" label = "MIME content encoding" />
+ <field name = "headers" domain = "table" label = "message header field table" />
+ <field name = "priority" domain = "octet" label = "message priority, 0 to 9" />
+ <field name = "reply-to" domain = "shortstr" label = "destination to reply to" />
+ <field name = "message-id" domain = "shortstr" label = "application message identifier" />
+ <field name = "filename" domain = "shortstr" label = "message filename" />
+ <field name = "timestamp" domain = "timestamp" label = "message timestamp" />
+ <!-- This field is deprecated pending review -->
+ <field name = "cluster-id" domain = "shortstr" label = "intra-cluster routing identifier" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "qos" synchronous = "1" index = "10" label = "specify quality of service">
+ <doc>
+ This method requests a specific quality of service. The QoS can be specified for the
+ current channel or for all channels on the connection. The particular properties and
+ semantics of a qos method always depend on the content class semantics. Though the
+ qos method could in principle apply to both peers, it is currently meaningful only
+ for the server.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "qos-ok" />
+
+ <field name = "prefetch-size" domain = "long" label = "prefetch window in octets">
+ <doc>
+ The client can request that messages be sent in advance so that when the client
+ finishes processing a message, the following message is already held locally,
+ rather than needing to be sent down the channel. Prefetching gives a performance
+ improvement. This field specifies the prefetch window size in octets. May be set
+ to zero, meaning "no specific limit". Note that other prefetch limits may still
+ apply. The prefetch-size is ignored if the no-ack option is set.
+ </doc>
+ </field>
+
+ <field name = "prefetch-count" domain = "short" label = "prefetch window in messages">
+ <doc>
+ Specifies a prefetch window in terms of whole messages. This is compatible with
+ some file API implementations. This field may be used in combination with the
+ prefetch-size field; a message will only be sent in advance if both prefetch
+ windows (and those at the channel and connection level) allow it. The
+ prefetch-count is ignored if the no-ack option is set.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server MAY send less data in advance than allowed by the client's
+ specified prefetch windows but it MUST NOT send more.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "global" domain = "bit" label = "apply to entire connection">
+ <doc>
+ By default the QoS settings apply to the current channel only. If this field is
+ set, they are applied to the entire connection.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "qos-ok" synchronous = "1" index = "11" label = "confirm the requested qos">
+ <doc>
+ This method tells the client that the requested QoS levels could be handled by the
+ server. The requested QoS applies to all active consumers until a new QoS is
+ defined.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "consume" synchronous = "1" index = "20" label = "start a queue consumer">
+ <doc>
+ This method asks the server to start a "consumer", which is a transient request for
+ messages from a specific queue. Consumers last as long as the channel they were
+ created on, or until the client cancels them.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server SHOULD support at least 16 consumers per queue, unless the queue was
+ declared as private, and ideally, impose no limit except as defined by available
+ resources.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "consume-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "read" access rights to
+ the realm for the queue.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to consume from. If the queue name is null,
+ refers to the current queue for the channel, which is the last declared queue.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ If the client did not previously declare a queue, and the queue name in this
+ method is empty, the server MUST raise a connection exception with reply
+ code 530 (not allowed).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>
+ Specifies the identifier for the consumer. The consumer tag is local to a
+ connection, so two clients can use the same consumer tags. If this field is
+ empty the server will generate a unique tag.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The tag MUST NOT refer to an existing consumer. If the client attempts to
+ create two consumers with the same non-empty tag the server MUST raise a
+ connection exception with reply code 530 (not allowed).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "no-local" domain = "no-local" />
+
+ <field name = "no-ack" domain = "no-ack" />
+
+ <field name = "exclusive" domain = "bit" label = "request exclusive access">
+ <doc>
+ Request exclusive consumer access, meaning only this consumer can access the
+ queue.
+ </doc>
+
+ <!-- Rule test name: was "amq_file_00" -->
+ <rule name = "01">
+ <doc>
+ If the server cannot grant exclusive access to the queue when asked, -
+ because there are other consumers active - it MUST raise a channel exception
+ with return code 405 (resource locked).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "consume-ok" synchronous = "1" index = "21" label = "confirm a new consumer">
+ <doc>
+ This method provides the client with a consumer tag which it MUST use in methods
+ that work with the consumer.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>Holds the consumer tag specified by the client or provided by the server.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "cancel" synchronous = "1" index = "30" label = "end a queue consumer">
+ <doc>
+ This method cancels a consumer. This does not affect already delivered messages, but
+ it does mean the server will not send any more messages for that consumer.
+ </doc>
+
+ <response name = "cancel-ok" />
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "cancel-ok" synchronous = "1" index = "31" label = "confirm a cancelled consumer">
+ <doc>This method confirms that the cancellation was completed.</doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "open" synchronous = "1" index = "40" label = "request to start staging">
+ <doc>
+ This method requests permission to start staging a message. Staging means sending
+ the message into a temporary area at the recipient end and then delivering the
+ message by referring to this temporary area. Staging is how the protocol handles
+ partial file transfers - if a message is partially staged and the connection breaks,
+ the next time the sender starts to stage it, it can restart from where it left off.
+ </doc>
+
+ <response name = "open-ok" />
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "identifier" domain = "shortstr" label = "staging identifier">
+ <doc>
+ This is the staging identifier. This is an arbitrary string chosen by the
+ sender. For staging to work correctly the sender must use the same staging
+ identifier when staging the same message a second time after recovery from a
+ failure. A good choice for the staging identifier would be the SHA1 hash of the
+ message properties data (including the original filename, revised time, etc.).
+ </doc>
+ </field>
+
+ <field name = "content-size" domain = "longlong" label = "message content size">
+ <doc>
+ The size of the content in octets. The recipient may use this information to
+ allocate or check available space in advance, to avoid "disk full" errors during
+ staging of very large messages.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The sender MUST accurately fill the content-size field. Zero-length content
+ is permitted.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "open-ok" synchronous = "1" index = "41" label = "confirm staging ready">
+ <doc>
+ This method confirms that the recipient is ready to accept staged data. If the
+ message was already partially-staged at a previous time the recipient will report
+ the number of octets already staged.
+ </doc>
+
+ <response name = "stage" />
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "staged-size" domain = "longlong" label = "already staged amount">
+ <doc>
+ The amount of previously-staged content in octets. For a new message this will
+ be zero.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The sender MUST start sending data from this octet offset in the message,
+ counting from zero.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The recipient MAY decide how long to hold partially-staged content and MAY
+ implement staging by always discarding partially-staged content. However if
+ it uses the file content type it MUST support the staging methods.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "stage" content = "1" index = "50" label = "stage message content">
+ <doc>
+ This method stages the message, sending the message content to the recipient from
+ the octet offset specified in the Open-Ok method.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "publish" index = "60" label = "publish a message">
+ <doc>
+ This method publishes a staged file message to a specific exchange. The file message
+ will be routed to queues as defined by the exchange configuration and distributed to
+ any active consumers when the transaction, if any, is committed.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "write" access rights
+ to the access realm for the exchange.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange to publish to. The exchange name can be
+ empty, meaning the default exchange. If the exchange name is specified, and that
+ exchange does not exist, the server will raise a channel exception.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server MUST accept a blank exchange name to mean the default exchange.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <doc>
+ If the exchange was declared as an internal exchange, the server MUST
+ respond with a reply code 403 (access refused) and raise a channel
+ exception.
+ </doc>
+ </rule>
+
+ <!-- TODO: Rule split? -->
+
+ <rule name = "03">
+ <doc>
+ The exchange MAY refuse file content in which case it MUST respond with a
+ reply code 540 (not implemented) and raise a channel exception.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>
+ Specifies the routing key for the message. The routing key is used for routing
+ messages depending on the exchange configuration.
+ </doc>
+ </field>
+
+ <field name = "mandatory" domain = "bit" label = "indicate mandatory routing">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue. If this flag is set, the server will return an unroutable message with a
+ Return method. If this flag is zero, the server silently drops the message.
+ </doc>
+
+ <!-- Rule test name: was "amq_file_00" -->
+ <rule name = "01">
+ <doc>The server SHOULD implement the mandatory flag.</doc>
+ </rule>
+ </field>
+
+ <field name = "immediate" domain = "bit" label = "request immediate delivery">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue consumer immediately. If this flag is set, the server will return an
+ undeliverable message with a Return method. If this flag is zero, the server
+ will queue the message, but with no guarantee that it will ever be consumed.
+ </doc>
+
+ <!-- Rule test name: was "amq_file_00" -->
+ <rule name = "01">
+ <doc>The server SHOULD implement the immediate flag.</doc>
+ </rule>
+ </field>
+
+ <field name = "identifier" domain = "shortstr" label = "staging identifier">
+ <doc>
+ This is the staging identifier of the message to publish. The message must have
+ been staged. Note that a client can send the Publish method asynchronously
+ without waiting for staging to finish.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "return" content = "1" index = "70" label = "return a failed message">
+ <doc>
+ This method returns an undeliverable message that was published with the "immediate"
+ flag set, or an unroutable message published with the "mandatory" flag set. The
+ reply code and text provide information about the reason that the message was
+ undeliverable.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "reply-code" domain = "reply-code" />
+
+ <field name = "reply-text" domain = "reply-text" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>Specifies the routing key name specified when the message was published.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "deliver" index = "80" label = "notify the client of a consumer message">
+ <doc>
+ This method delivers a staged file message to the client, via a consumer. In the
+ asynchronous message delivery model, the client starts a consumer using the Consume
+ method, then the server responds with Deliver methods as and when messages arrive
+ for that consumer.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server SHOULD track the number of times a message has been delivered to
+ clients and when a message is redelivered a certain number of times - e.g. 5
+ times - without being acknowledged, the server SHOULD consider the message to be
+ unprocessable (possibly causing client applications to abort), and move the
+ message to a dead letter queue.
+ </doc>
+ </rule>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "redelivered" domain = "redelivered" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>Specifies the routing key name specified when the message was published.</doc>
+ </field>
+
+ <field name = "identifier" domain = "shortstr" label = "staging identifier">
+ <doc>
+ This is the staging identifier of the message to deliver. The message must have
+ been staged. Note that a server can send the Deliver method asynchronously
+ without waiting for staging to finish.
+ </doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "ack" index = "90" label = "acknowledge one or more messages">
+ <doc>
+ This method acknowledges one or more messages delivered via the Deliver method. The
+ client can ask to confirm a single message or a set of messages up to and including
+ a specific message.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "multiple" domain = "bit" label = "acknowledge multiple messages">
+ <doc>
+ If set to 1, the delivery tag is treated as "up to and including", so that the
+ client can acknowledge multiple messages with a single method. If set to zero,
+ the delivery tag refers to a single message. If the multiple field is 1, and the
+ delivery tag is zero, tells the server to acknowledge all outstanding mesages.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server MUST validate that a non-zero delivery-tag refers to an delivered
+ message, and raise a channel exception if this is not the case.
+ </doc>
+ </rule>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "reject" index = "100" label = "reject an incoming message">
+ <doc>
+ This method allows a client to reject a message. It can be used to return
+ untreatable messages to their original queue. Note that file content is staged
+ before delivery, so the client will not use this method to interrupt delivery of a
+ large message.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server SHOULD interpret this method as meaning that the client is unable to
+ process the message at this time.
+ </doc>
+ </rule>
+
+ <!-- TODO: Rule split? -->
+
+ <rule name = "02">
+ <doc>
+ A client MUST NOT use this method as a means of selecting messages to process. A
+ rejected message MAY be discarded or dead-lettered, not necessarily passed to
+ another client.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "requeue" domain = "bit" label = "requeue the message">
+ <doc>
+ If this field is zero, the message will be discarded. If this bit is 1, the
+ server will attempt to requeue the message.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server MUST NOT deliver the message to the same client within the
+ context of the current channel. The recommended strategy is to attempt to
+ deliver the message to an alternative consumer, and if that is not possible,
+ to move the message to a dead-letter queue. The server MAY use more
+ sophisticated tracking to hold the message on the queue and redeliver it to
+ the same client at a later stage.
+ </doc>
+ </rule>
+ </field>
+ </method>
+ </class>
+
+ <!-- == STREAM =========================================================== -->
+
+ <class name = "stream" handler = "channel" index = "80" label = "work with streaming content">
+ <doc>
+ The stream class provides methods that support multimedia streaming. The stream class
+ uses the following semantics: one message is one packet of data; delivery is
+ unacknowleged and unreliable; the consumer can specify quality of service parameters
+ that the server can try to adhere to; lower-priority messages may be discarded in favour
+ of high priority messages.
+ </doc>
+
+ <doc type = "grammar">
+ stream = C:QOS S:QOS-OK
+ / C:CONSUME S:CONSUME-OK
+ / C:CANCEL S:CANCEL-OK
+ / C:PUBLISH content
+ / S:RETURN
+ / S:DELIVER content
+ </doc>
+
+ <chassis name = "server" implement = "MAY" />
+ <chassis name = "client" implement = "MAY" />
+
+ <rule name = "01">
+ <doc>
+ The server SHOULD discard stream messages on a priority basis if the queue size
+ exceeds some configured limit.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server MUST implement at least 2 priority levels for stream messages, where
+ priorities 0-4 and 5-9 are treated as two distinct levels. The server MAY implement
+ up to 10 priority levels.
+ </doc>
+ </rule>
+
+ <rule name = "03">
+ <doc>
+ The server MUST implement automatic acknowledgements on stream content. That is, as
+ soon as a message is delivered to a client via a Deliver method, the server must
+ remove it from the queue.
+ </doc>
+ </rule>
+
+ <!-- These are the properties for a Stream content -->
+
+ <field name = "content-type" domain = "shortstr" label = "MIME content type" />
+ <field name = "content-encoding" domain = "shortstr" label = "MIME content encoding" />
+ <field name = "headers" domain = "table" label = "message header field table" />
+ <field name = "priority" domain = "octet" label = "message priority, 0 to 9" />
+ <field name = "timestamp" domain = "timestamp" label = "message timestamp" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "qos" synchronous = "1" index = "10" label = "specify quality of service">
+ <doc>
+ This method requests a specific quality of service. The QoS can be specified for the
+ current channel or for all channels on the connection. The particular properties and
+ semantics of a qos method always depend on the content class semantics. Though the
+ qos method could in principle apply to both peers, it is currently meaningful only
+ for the server.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "qos-ok" />
+
+ <field name = "prefetch-size" domain = "long" label = "prefetch window in octets">
+ <doc>
+ The client can request that messages be sent in advance so that when the client
+ finishes processing a message, the following message is already held locally,
+ rather than needing to be sent down the channel. Prefetching gives a performance
+ improvement. This field specifies the prefetch window size in octets. May be set
+ to zero, meaning "no specific limit". Note that other prefetch limits may still
+ apply.
+ </doc>
+ </field>
+
+ <field name = "prefetch-count" domain = "short" label = "prefetch window in messages">
+ <doc>
+ Specifies a prefetch window in terms of whole messages. This field may be used
+ in combination with the prefetch-size field; a message will only be sent in
+ advance if both prefetch windows (and those at the channel and connection level)
+ allow it.
+ </doc>
+ </field>
+
+ <field name = "consume-rate" domain = "long" label = "transfer rate in octets/second">
+ <doc>
+ Specifies a desired transfer rate in octets per second. This is usually
+ determined by the application that uses the streaming data. A value of zero
+ means "no limit", i.e. as rapidly as possible.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The server MAY ignore the prefetch values and consume rates, depending on
+ the type of stream and the ability of the server to queue and/or reply it.
+ The server MAY drop low-priority messages in favour of high-priority
+ messages.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "global" domain = "bit" label = "apply to entire connection">
+ <doc>
+ By default the QoS settings apply to the current channel only. If this field is
+ set, they are applied to the entire connection.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "qos-ok" synchronous = "1" index = "11" label = "confirm the requested qos">
+ <doc>
+ This method tells the client that the requested QoS levels could be handled by the
+ server. The requested QoS applies to all active consumers until a new QoS is
+ defined.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "consume" synchronous = "1" index = "20" label = "start a queue consumer">
+ <doc>
+ This method asks the server to start a "consumer", which is a transient request for
+ messages from a specific queue. Consumers last as long as the channel they were
+ created on, or until the client cancels them.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server SHOULD support at least 16 consumers per queue, unless the queue was
+ declared as private, and ideally, impose no limit except as defined by available
+ resources.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <doc>
+ Streaming applications SHOULD use different channels to select different
+ streaming resolutions. AMQP makes no provision for filtering and/or transforming
+ streams except on the basis of priority-based selective delivery of individual
+ messages.
+ </doc>
+ </rule>
+
+ <chassis name = "server" implement = "MUST" />
+ <response name = "consume-ok" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "read" access rights to
+ the realm for the queue.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue to consume from. If the queue name is null,
+ refers to the current queue for the channel, which is the last declared queue.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ If the client did not previously declare a queue, and the queue name in this
+ method is empty, the server MUST raise a connection exception with reply
+ code 530 (not allowed).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>
+ Specifies the identifier for the consumer. The consumer tag is local to a
+ connection, so two clients can use the same consumer tags. If this field is
+ empty the server will generate a unique tag.
+ </doc>
+
+ <rule name = "01">
+ <!-- TODO: Rule split? -->
+ <doc>
+ The tag MUST NOT refer to an existing consumer. If the client attempts to
+ create two consumers with the same non-empty tag the server MUST raise a
+ connection exception with reply code 530 (not allowed).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "no-local" domain = "no-local" />
+
+ <field name = "exclusive" domain = "bit" label = "request exclusive access">
+ <doc>
+ Request exclusive consumer access, meaning only this consumer can access the
+ queue.
+ </doc>
+
+
+ <!-- Rule test name: was "amq_file_00" -->
+ <rule name = "01">
+ <doc>
+ If the server cannot grant exclusive access to the queue when asked, -
+ because there are other consumers active - it MUST raise a channel exception
+ with return code 405 (resource locked).
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "consume-ok" synchronous = "1" index = "21" label = "confirm a new consumer">
+ <doc>
+ This method provides the client with a consumer tag which it may use in methods that
+ work with the consumer.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag">
+ <doc>Holds the consumer tag specified by the client or provided by the server.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "cancel" synchronous = "1" index = "30" label = "end a queue consumer">
+ <doc>
+ This method cancels a consumer. Since message delivery is asynchronous the client
+ may continue to receive messages for a short while after canceling a consumer. It
+ may process or discard these as appropriate.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <response name = "cancel-ok" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+
+ <field name = "nowait" domain = "bit" label = "do not send a reply method">
+ <doc>
+ If set, the server will not respond to the method. The client should not wait
+ for a reply method. If the server could not complete the method it will raise a
+ channel or connection exception.
+ </doc>
+ </field>
+ </method>
+
+ <method name = "cancel-ok" synchronous = "1" index = "31" label = "confirm a cancelled consumer">
+ <doc>This method confirms that the cancellation was completed.</doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "publish" content = "1" index = "40" label = "publish a message">
+ <doc>
+ This method publishes a message to a specific exchange. The message will be routed
+ to queues as defined by the exchange configuration and distributed to any active
+ consumers as appropriate.
+ </doc>
+
+ <chassis name = "server" implement = "MUST" />
+
+ <field name = "ticket" domain = "access-ticket">
+ <rule name = "01">
+ <doc>
+ The client MUST provide a valid access ticket giving "write" access rights
+ to the access realm for the exchange.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange to publish to. The exchange name can be
+ empty, meaning the default exchange. If the exchange name is specified, and that
+ exchange does not exist, the server will raise a channel exception.
+ </doc>
+
+ <rule name = "01">
+ <doc>
+ The server MUST accept a blank exchange name to mean the default exchange.
+ </doc>
+ </rule>
+
+ <rule name = "02">
+ <doc>
+ If the exchange was declared as an internal exchange, the server MUST
+ respond with a reply code 403 (access refused) and raise a channel
+ exception.
+ </doc>
+ </rule>
+
+ <rule name = "03">
+ <doc>
+ The exchange MAY refuse stream content in which case it MUST respond with a
+ reply code 540 (not implemented) and raise a channel exception.
+ </doc>
+ </rule>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>
+ Specifies the routing key for the message. The routing key is used for routing
+ messages depending on the exchange configuration.
+ </doc>
+ </field>
+
+ <field name = "mandatory" domain = "bit" label = "indicate mandatory routing">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue. If this flag is set, the server will return an unroutable message with a
+ Return method. If this flag is zero, the server silently drops the message.
+ </doc>
+
+ <!-- Rule test name: was "amq_stream_00" -->
+ <rule name = "01">
+ <doc>The server SHOULD implement the mandatory flag.</doc>
+ </rule>
+ </field>
+
+ <field name = "immediate" domain = "bit" label = "request immediate delivery">
+ <doc>
+ This flag tells the server how to react if the message cannot be routed to a
+ queue consumer immediately. If this flag is set, the server will return an
+ undeliverable message with a Return method. If this flag is zero, the server
+ will queue the message, but with no guarantee that it will ever be consumed.
+ </doc>
+
+ <!-- Rule test name: was "amq_stream_00" -->
+ <rule name = "01">
+ <doc>The server SHOULD implement the immediate flag.</doc>
+ </rule>
+ </field>
+ </method>
+
+ <method name = "return" content = "1" index = "50" label = "return a failed message">
+ <doc>
+ This method returns an undeliverable message that was published with the "immediate"
+ flag set, or an unroutable message published with the "mandatory" flag set. The
+ reply code and text provide information about the reason that the message was
+ undeliverable.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "reply-code" domain = "reply-code" />
+
+ <field name = "reply-text" domain = "reply-text" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "routing-key" domain = "shortstr" label = "Message routing key">
+ <doc>Specifies the routing key name specified when the message was published.</doc>
+ </field>
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "deliver" content = "1" index = "60"
+ label = "notify the client of a consumer message">
+ <doc>
+ This method delivers a message to the client, via a consumer. In the asynchronous
+ message delivery model, the client starts a consumer using the Consume method, then
+ the server responds with Deliver methods as and when messages arrive for that
+ consumer.
+ </doc>
+
+ <chassis name = "client" implement = "MUST" />
+
+ <field name = "consumer-tag" domain = "consumer-tag" />
+
+ <field name = "delivery-tag" domain = "delivery-tag" />
+
+ <field name = "exchange" domain = "exchange-name">
+ <doc>
+ Specifies the name of the exchange that the message was originally published to.
+ </doc>
+ </field>
+
+ <field name = "queue" domain = "queue-name">
+ <doc>
+ Specifies the name of the queue that the message came from. Note that a single
+ channel can start many consumers on different queues.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+ </method>
+ </class>
+
+ <!-- == TX =============================================================== -->
+
+ <class name = "tx" handler = "channel" index = "90" label = "work with standard transactions">
+ <doc>
+ Standard transactions provide so-called "1.5 phase commit". We can ensure that work is
+ never lost, but there is a chance of confirmations being lost, so that messages may be
+ resent. Applications that use standard transactions must be able to detect and ignore
+ duplicate messages.
+ </doc>
+
+ <!-- TODO: Rule split? -->
+
+ <rule name = "01">
+ <doc>
+ An client using standard transactions SHOULD be able to track all messages received
+ within a reasonable period, and thus detect and reject duplicates of the same
+ message. It SHOULD NOT pass these to the application layer.
+ </doc>
+ </rule>
+
+ <doc type = "grammar">
+ tx = C:SELECT S:SELECT-OK
+ / C:COMMIT S:COMMIT-OK
+ / C:ROLLBACK S:ROLLBACK-OK
+ </doc>
+
+ <chassis name = "server" implement = "SHOULD" />
+ <chassis name = "client" implement = "MAY" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "select" synchronous = "1" index = "10" label = "select standard transaction mode">
+ <doc>
+ This method sets the channel to use standard transactions. The client must use this
+ method at least once on a channel before using the Commit or Rollback methods.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "select-ok" />
+ </method>
+
+ <method name = "select-ok" synchronous = "1" index = "11" label = "confirm transaction mode">
+ <doc>
+ This method confirms to the client that the channel was successfully set to use
+ standard transactions.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "commit" synchronous = "1" index = "20" label = "commit the current transaction">
+ <doc>
+ This method commits all messages published and acknowledged in the current
+ transaction. A new transaction starts immediately after a commit.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "commit-ok" />
+ </method>
+
+ <method name = "commit-ok" synchronous = "1" index = "21" label = "confirm a successful commit">
+ <doc>
+ This method confirms to the client that the commit succeeded. Note that if a commit
+ fails, the server raises a channel exception.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "rollback" synchronous = "1" index = "30"
+ label = "abandon the current transaction">
+ <doc>
+ This method abandons all messages published and acknowledged in the current
+ transaction. A new transaction starts immediately after a rollback.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "rollback-ok" />
+ </method>
+
+ <method name = "rollback-ok" synchronous = "1" index = "31" label = "confirm successful rollback">
+ <doc>
+ This method confirms to the client that the rollback succeeded. Note that if an
+ rollback fails, the server raises a channel exception.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+ </class>
+
+ <!-- == DTX ============================================================== -->
+
+ <class name = "dtx" handler = "channel" index = "100" label = "work with distributed transactions">
+ <doc>
+ Distributed transactions provide so-called "2-phase commit". The AMQP distributed
+ transaction model supports the X-Open XA architecture and other distributed transaction
+ implementations. The Dtx class assumes that the server has a private communications
+ channel (not AMQP) to a distributed transaction coordinator.
+ </doc>
+
+ <doc type = "grammar">
+ dtx = C:SELECT S:SELECT-OK
+ C:START S:START-OK
+ </doc>
+
+ <chassis name = "server" implement = "MAY" />
+ <chassis name = "client" implement = "MAY" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "select" synchronous = "1" index = "10" label = "select standard transaction mode">
+ <doc>
+ This method sets the channel to use distributed transactions. The client must use
+ this method at least once on a channel before using the Start method.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <response name = "select-ok" />
+ </method>
+
+ <method name = "select-ok" synchronous = "1" index = "11" label = "confirm transaction mode">
+ <doc>
+ This method confirms to the client that the channel was successfully set to use
+ distributed transactions.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "start" synchronous = "1" index = "20"
+ label = "start a new distributed transaction">
+ <doc>
+ This method starts a new distributed transaction. This must be the first method on a
+ new channel that uses the distributed transaction mode, before any methods that
+ publish or consume messages.
+ </doc>
+ <chassis name = "server" implement = "MAY" />
+ <response name = "start-ok" />
+ <field name = "dtx-identifier" domain = "shortstr" label = "transaction identifier">
+ <doc>
+ The distributed transaction key. This identifies the transaction so that the
+ AMQP server can coordinate with the distributed transaction coordinator.
+ </doc>
+ <assert check = "notnull" />
+ </field>
+ </method>
+
+ <method name = "start-ok" synchronous = "1" index = "21"
+ label = "confirm the start of a new distributed transaction">
+ <doc>
+ This method confirms to the client that the transaction started. Note that if a
+ start fails, the server raises a channel exception.
+ </doc>
+ <chassis name = "client" implement = "MUST" />
+ </method>
+ </class>
+
+ <!-- == TUNNEL =========================================================== -->
+
+ <class name = "tunnel" handler = "tunnel" index = "110" label = "methods for protocol tunneling">
+ <doc>
+ The tunnel methods are used to send blocks of binary data - which can be serialised AMQP
+ methods or other protocol frames - between AMQP peers.
+ </doc>
+
+ <doc type = "grammar">
+ tunnel = C:REQUEST
+ / S:REQUEST
+ </doc>
+
+ <chassis name = "server" implement = "MAY" />
+ <chassis name = "client" implement = "MAY" />
+
+ <field name = "headers" domain = "table" label = "message header field table" />
+ <field name = "proxy-name" domain = "shortstr" label = "identity of tunnelling proxy" />
+ <field name = "data-name" domain = "shortstr" label = "name or type of message being tunnelled" />
+ <field name = "durable" domain = "octet" label = "message durability indicator" />
+ <field name = "broadcast" domain = "octet" label = "message broadcast mode" />
+
+ <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
+
+ <method name = "request" content = "1" index = "10" label = "sends a tunnelled method">
+ <doc>
+ This method tunnels a block of binary data, which can be an encoded
+ AMQP method or other data. The binary data is sent as the content for
+ the Tunnel.Request method.
+ </doc>
+ <chassis name = "server" implement = "MUST" />
+ <field name = "meta-data" domain = "table" label = "meta data for the tunnelled block">
+ <doc>
+ This field table holds arbitrary meta-data that the sender needs to
+ pass to the recipient.
+ </doc>
+ </field>
+ </method>
+ </class>
+</amqp>
diff --git a/qpid/gentools/xml-src/cluster-0.9.test.xml b/qpid/gentools/xml-src/cluster-0.9.test.xml
new file mode 100644
index 0000000000..142e6c9380
--- /dev/null
+++ b/qpid/gentools/xml-src/cluster-0.9.test.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0"?>
+<!--
+ -
+ - 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.
+ -
+ -->
+
+<amqp major="0" minor="9" port="5672" comment="AMQ protocol 0.80">
+
+<class name = "cluster" index = "101">
+
+<doc>
+ An extension that allows brokers to communicate in order to
+ provide a clustered service to clients.
+</doc>
+
+<method name = "join" index="10">
+ <field name = "broker" type = "shortstr" />
+</method>
+
+<method name = "membership" index="20">
+ <field name = "members" type = "longstr" />
+</method>
+
+<method name = "synch" index="30">
+</method>
+
+<method name = "leave" index="40">
+ <field name = "broker" type = "shortstr" />
+</method>
+
+<method name = "suspect" index="50">
+ <field name = "broker" type = "shortstr" />
+</method>
+
+<method name = "ping" index="60">
+ <field name = "broker" type = "shortstr" />
+ <field name = "load" type = "long" />
+ <field name = "response required" type = "bit" />
+</method>
+
+</class>
+
+</amqp>