diff options
author | Aidan Skinner <aidan@apache.org> | 2008-04-14 10:44:28 +0000 |
---|---|---|
committer | Aidan Skinner <aidan@apache.org> | 2008-04-14 10:44:28 +0000 |
commit | 5c1648acbc1742c0e19603dca5574e6b3c71f0d7 (patch) | |
tree | db32badcb18d406d137970c3fecaa68390f0db61 | |
parent | 2d34e47e824a0871b328acc8ee32dfd95ae35053 (diff) | |
download | qpid-python-5c1648acbc1742c0e19603dca5574e6b3c71f0d7.tar.gz |
QPID-832 sync cpp with trunk
git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/branches/thegreatmerge@647727 13f79535-47bb-0310-9956-ffa450edef68
626 files changed, 79050 insertions, 0 deletions
diff --git a/qpid/cpp/DESIGN b/qpid/cpp/DESIGN new file mode 100644 index 0000000000..7e9ba6755c --- /dev/null +++ b/qpid/cpp/DESIGN @@ -0,0 +1,79 @@ +Qpid C++ AMQP implementation +============================= + += Project layout = + +For Build system design see comment at start of Makefile. + +Project contains: + * Client library (lib/libqpid_client): src/qpid/client + * Broker library (lib/libqpid_broker): src/qpid/broker + * Common classes + * src/qpid/concurrent: concurrecy + * src/qpid/framing: wire encoding/decoding + * src/qpid/io: reading/writing + * src/qpid/Exception.cpp, QpidError.cpp: Exception classes. + * Qpid Daemon (bin/qpidd): src/qpidd.cpp + +Unit tests in test/unit: each *Test.cpp builds a CppUnit plugin. + +Client tests in test/client: each *.cpp builds a test executable. + +Test utilities: test/include + += Client Design = + +The client module is primarily concerned with presenting the +functionality offered by AMQP to users through a simple API that +nevertheless allows all the protocol functionality to be exploited. +[Note: it is currently nothing like complete in this regard!] + +The code in the client module is concerned with the logic of the AMQP +protocol and interacts with the lower level transport issues through +the InputHandler and OutputHandler abstractions defined in +common/framing. It uses these in conjunction with the Connector +interface, defined in common/io, for establishing a connection to the +broker and interacting with it through the sending and receiving of +messages represented by AMQFrame (defined in common/framing). + +The Connector implementation is responsible for connection set up, +threading strategy and getting data on and off the wire. It delegates +to the framing module for encode/decode operations. The interface +between the io and the framing modules is primarily through the Buffer +and AMQFrame classes. + +A Buffer allows 'raw' data to be read or written in terms of the AMQP +defined 'types' (octet, short, long, long long, short string, long +string, field table etc.). AMQP is defined in terms frames with +specific bodies and the frame (as well as these different bodies) are +defined in terms of these 'types'. The AMQFrame class allows a frame +to be decoded by reading from the supplied buffer, or it allows a +particular frame to be constructed and then encoded by writing to the +supplied buffer. The io layer can then access the raw data that +'backs' the buffer to either out it on the wire or to populate it from +the wire. + +One minor exception to this is the protocol initiation. AMQP defines +a protocol 'header', that is not a frame, and is sent by a client to +intiate a connection. The Connector allows (indeed requires) such a +frame to be passed in to initialise the connection (the Acceptor, when +defined, will allow an InitiationHandler to be set allowing the broker +to hook into the connection initiation). In order to remove +duplication, the ProtocolInitiation class and the AMQFrame class both +implement a AMQDataBlock class that defines the encode and decode +methods. This allows both types to be treated generically for the +purposes of encoding. In decoding, the context determines which type +is expected and should be used for decoding (this is only relevant to +the broker). + + + + + --------api-------- + Client Impl ...............uses..... +input handler --> --------- --------- <-- output handler . + A | . + | | framing utils + | V . + ------------------- <-- connector . + IO Layer ................uses.... diff --git a/qpid/cpp/LICENSE b/qpid/cpp/LICENSE new file mode 100644 index 0000000000..6b0b1270ff --- /dev/null +++ b/qpid/cpp/LICENSE @@ -0,0 +1,203 @@ + + 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/cpp/Makefile.am b/qpid/cpp/Makefile.am new file mode 100644 index 0000000000..42fc47ba5f --- /dev/null +++ b/qpid/cpp/Makefile.am @@ -0,0 +1,43 @@ +AUTOMAKE_OPTIONS = 1.9.2 foreign +ACLOCAL_AMFLAGS = -I m4 + +SPEC=$(PACKAGE).spec + +EXTRA_DIST = \ + LICENSE NOTICE README RELEASE_NOTES\ + etc/qpidd etc/qpidd.conf \ + $(SPEC) $(SPEC).in \ + rpm/README.qpidd-devel \ + xml/cluster.xml + +sysconf_DATA = etc/qpidd.conf + +SUBDIRS = src docs/api docs/man examples + +# Update libtool, if needed. +libtool: $(LIBTOOL_DEPS) + $(SHELL) ./config.status --recheck + +# +# Build RPMs from the distribution tarball. +# +RPMDIRS=rpm/BUILD rpm/RPMS rpm/SPECS rpm/SRPMS +RPMMACROS=--define "_topdir @abs_builddir@/rpm" --define "_sourcedir @abs_builddir@" +# Override this variable e.g. with -bs to produce srpm only +RPMOPTS=-ba + +clean-local: + -rm -rf $(RPMDIRS) + +.PHONY: rpmbuild + +rpmbuild: $(SPEC) dist-gzip + mkdir -p $(RPMDIRS) + rpmbuild $(RPMMACROS) $(RPMOPTS) $(SPEC) +if HAS_RPMLINT + rpmlint `find rpm -name '*.rpm'` +else + @echo "WARNING: rpmlint not found, could not validate RPMs." +endif + + diff --git a/qpid/cpp/NOTICE b/qpid/cpp/NOTICE new file mode 100644 index 0000000000..cae69a873a --- /dev/null +++ b/qpid/cpp/NOTICE @@ -0,0 +1,25 @@ +========================================================================= +== NOTICE file corresponding to the section 4 d of == +== the Apache License, Version 2.0, == +== in this case for the Apache Qpid distribution. == +========================================================================= + +This product includes software developed by the Apache Software Foundation +(http://www.apache.org/). + +Please read the LICENSE file present in the root directory of this +distribution. + + +Aside from contributions to the Apache Qpid project, this software also +includes (binary only): + - None at this time + +Project requires, not packaged: + * apr version 1.2.7 or later under the Apache Software License, Version 2.0, + and can be downloded from http://apr.apache.org + + * boost version 1.33.1 or later under the Boost Software License, and + can be downloaded from http://www.boost.org + - Included in most OS platfroms by defualt. + diff --git a/qpid/cpp/README b/qpid/cpp/README new file mode 100644 index 0000000000..7de7fd3f98 --- /dev/null +++ b/qpid/cpp/README @@ -0,0 +1,291 @@ += Qpid C++ = + +Qpid C++ is a C++ implementation of the AMQP protcol described at + http://amqp.org/ + +The Qpid project also provides Java, Ruby and Python implementations. + +NOTE: This release of Qpid C++ implements the AMQP 0-9 WIP. +It will not inter-operate with AMQP 0-8 implementations. +We will be moving to 0-10 as soon as it is available. + +For additional software or information on the Qpid project go to: + http://cwiki.apache.org/qpid/ + +Available documentation: + qpidd(1) man page - how to run the broker daemon. + html/index.html - C++ client API. + NEWS - release notes. +Note the daemon and client API can be installed separately. + +This README describes how to build the Qpid C++ broker and client, either +from a checkout of the source or from a source distribution. + +== Prerequisites == + +We prefer to avoid spending time accommodating older versions of these +packages, so please make sure that you have the latest stable versions. +Known version numbers for a succesfull build are given in brackets, take +these as a recommended minimum version. Older unix versions, for example, +Redhat Linux 3, will almost certainly require some packages to be upgraded. + +The following libraries and header files must be installed to build +a source distribution: + * boost <http://www.boost.org> (1.33.1) + * e2fsprogs <http://e2fsprogs.sourceforge.net/> (1.39) + * pkgconfig <http://pkgconfig.freedesktop.org/wiki/> (0.21) + +Optional cluster functionality requires: + * openais <http://openais.org/> (0.80.3) + +Running qpid test suite requires: + * cppunit <http://cppunit.sourceforge.net> (1.11.4) + +Qpid has been built using the GNU C++ compiler: + * gcc <http://gcc.gnu.org/> (3.2.3) + +If you want to build directly from the SVN repository you will need +all of the above plus: + + * GNU make <http://www.gnu.org/software/make/> (3.8.0) + * autoconf <http://www.gnu.org/software/autoconf/> (2.61) + * automake <http://www.gnu.org/software/automake/> (1.9.6) + * help2man <http://www.gnu.org/software/help2man/> (1.36.4) + * libtool <http://www.gnu.org/software/libtool/> (1.5.22) + * doxygen <ftp://ftp.stack.nl/pub/users/dimitri/> (1.5.1) + * graphviz <http://www.graphviz.org/> (2.12) + * ruby 1.8 <http://www.ruby-lang.org> (1.8.4) + +=== Installing as root === + +On linux most packages can be installed using your distribution's package +management tool. For example on Fedora: + # yum install pkgconfig e2fsprogs boost-devel cppunit-devel openais-devel ruby + # yum install make gcc-c++ autoconf automake libtool doxygen help2man graphviz + # yum install e2fsprogs-devel + +Follow the manual installation instruction below for any packages not +available through yum. + +=== Building and installing packages manually or as non-root user === + +Required dependencies can be installed and built from source distributions. +It is recommended that you create a directory to install them to, for example, +~/qpid-tools. To build and install the dependency pakcages: + + 1. Unzip and untar them and cd to the untared directory. + 2. do: + # ./configure --prefix=~/qpid-tools + # make install + +The exceptions are openais, boost, JDK 5.0. + +==== To build and install openais from source ==== + +Unpack the source distribution and do: + # make + # sudo make install DESTDIR= + # sudo ldconfig + +This will install in the standard places (/usr/lib, /usr/include etc.) + +Edit /etc/ais/openais.conf and modify the "bindnetaddr" setting +to your hosts IP address. Do not use 127.0.0.1. + +Make sure the UDP port set for mcastport in openais.conf (5405 by +default) is not blocked by your firewall. Disable the firewall or +configure it to allow this port for UDP. + +Finally start the ais daemon (must be done as root): + # sudo /sbin/aisexec + +Note that to run the AIS tests your primary group must be "ais". You +can change your primary group with the usermod command or set it +temporarily with the newgrp command. + +Troubleshooting tips: + +If aisexec goes into a loop printing "entering GATHER state", verify your firewall is allowing UDP traffic on the mcastport set in openais.conf. + +If aisexec reports "got nodejoin message 127.0.0.1" verify the +bindnetaddr in openais.conf is an active local IP address. ifconfig +will list local addresses. + +When aisexec is working correctly, the start-up log messages will end +with "entering OPERATIONAL state." and "got nodejoin message <ip +address>" where <ip address> is the local IP address specified for +bindnetaddr in openais.conf. + +For further info on openais http://openais.org/ + +==== To build the boost library ==== + + 1. Unpack boost-jam. + 2. Add bjam in the unpacked directory to your path. + 3. Unpack boost and cd to the boost untarred directory. + 4. do: + + # bjam -sTOOLS=gcc --prefix=~/qpid-tools + +==== To install JDK 5.0 ==== +Download and run its install script, or whatever +alternative instructions may be on the sun website. + +Ensure that all the build tools are available on your path, when they are +manually installed to non-standard locations. For example: + + # export PATH=~/qpid-tools/bin:$PATH + +Ensure that pkg-config is set up correctly. For example: + + # export PKG_CONFIG_PATH=~/qpid-tools/lib/pkgconfig:/usr/local/pkgconfig + # export PKG_CONFIG=~/qpid-tools/bin/pkg-config + +Ensure that the boost libraries are made available on the gcc library path. +For example: + + # export CXXFLAGS=-I~/qpid-tools/include/boost-1_33_1 + +Ensure that JDK 5.0 has its home location set up correctly and is added to +the path. For example: + + # export PATH=~/jdk1.5.0_11/bin:$PATH + +== Building from a source distribution. == + +In the distribution directory + +Build and install with: + + # ./configure --prefix=<install_location> + # make all + # make install + +To build and test everything: + + # make + # make check + +This builds in the source tree. You can have multiple builds in the +same working copy with different configuration. For example you can do +the following to build twice, once for debug, the other with +optimization: + + # make distclean + # mkdir .build-dbg .build-opt + # (cd .build-opt ../configure --prefix=/tmp/x && make && make check) + # (cd .build-dbg ../configure CXXFLAGS=-g --prefix=/tmp/x \ + && make && make check) + + +== For Qpid developers: building a repository working copy == + +=== Installing the latest autotools === + +If you don't have sufficiently up-to-date autotools you can get the +latest by running run the script qpid-autotools-install. + +1. Decide where you would like to install the tools. It should be in a + local directory so that you do not need root privileges. (Suggest + $HOME/qpid-tools.) Create an empty directory. +2. Modify your environment variable PATH to ensure that the bin directory + within this directory comes first in the PATH string: + PATH=$HOME/qpid-tools/bin:$PATH +3. Set PKG_CONFIG_PATH=$HOME/qpid-tools/lib/pkgconfig:/usr/lib/pkgconfig + (or if it already exists, make sure that the above path to your + qpid-tools directory is first). +4. Run the install utility from the cpp directory: + ./qpid-autotools-install --prefix=$HOME/qpid-tools --skip-check + (Note that --prefix will only accept an absolute path, so don't use + ~/qpid-tools.) The utility will download, compile and install the + required tools into the qpid-tools directory (this may take a little + time). Watch for any notices about paths at the end of the install - + this means that your environment is not correct - see steps 2 and 3 + above. + NOTE: If you omit the --skip-check option, the check of the build + can add up to an hour to what is normally a few minutes of install + time. +5. Perform a check: from the command-line run "which automake" and + ensure that it finds the automake in your qpid-tools directory. If not, + check that the build completed normally and your environment. +6. (Optional) If having the build artifacts lying around bothers you, delete + the (hidden) build directory cpp/.build-auto-tools. + +To see help, run ./qpid-autotools-install --help. + +=== Building a checkout === +To get the source code from the subversion repository (trunk) do: + + # svn checkout https://svn.apache.org/repos/asf/incubator/qpid/trunk/ . + +To build a fresh checkout: + +Cd to qpid/cpp subdirectory. Before running make on a fresh checkout do: + + # ./bootstrap + +This generates config, makefiles and the like - check the script for +details. You only need to do this once, "make" will keep everything up +to date thereafter (including re-generating configuration & Makefiles +if the automake templates change etc.) + +If you are developing code yourself, or if you want to help +us keep the code as tight and robust as possible, consider enabling +the use of valgrind. If you configure like this: + + # ./configure --enable-valgrind + +That will arrange (assuming you have valgrind installed) for "make check" +to run tests via valgrind. That makes the tests run more slowly, but +helps detect certain types of bugs, as well as memory leaks. If you run +"make check" and valgrind detects a leak that is not listed as being +"ignorable-for-now", the test script in question will fail. However, +recording whether a leak is ignorable is not easy, when the stack +signature, libraries, compiler, O/S, architecture, etc., may all vary, +so if you see a new leak, try to figure out if it's one you can fix +before adding it to the list. + +Now follow instruction for building from a source distribution. + +=== Portability === + +All system calls are abstracted by classes under lib/common/sys. This +provides an object-oriented C++ API and contains platform-specific +code. + +These wrappers are mainly inline by-value classes so they impose no +run-time penalty compared do direct system calls. + +Initially we will have a full linux implementation and a portable +implementation sufficient for the client using the APR portability +library. The implementations may change in future but the interface +for qpid code outside the qpid/sys namespace should remain stable. + +=== Tests === + +See src/tests/README for details. + +== Doxygen == + +Doxygen generates documentation in several formats from source code +using special comments. You can use javadoc style comments if you know +javadoc, if you don't or want to know the fully story on doxygen +markup see http://www.stack.nl/~dimitri/doxygen/ + +Even even if the code is completely uncommented, doxygen generates +UML-esque dependency diagrams that are ''extremely'' useful in navigating +around the code, especially for newcomers. + +To try it out "make doxygen" then open doxygen/html/index.html +This README describes how to build the Qpid C++ broker and client, either +from a checkout of the source or from a source distribution. + +=== Troubleshooting === + +When building, get the following on configure + configure: error: Package requirements (apr-1 >= 1.2.2) were not met: + No package 'apr-1' foun + +The following has not been set + export PKG_CONFIG_PATH=$HOME/qpid-tools/lib/pkgconfig:/usr/lib/pkgconfig + diff --git a/qpid/cpp/RELEASE_NOTES b/qpid/cpp/RELEASE_NOTES new file mode 100644 index 0000000000..819539b1ec --- /dev/null +++ b/qpid/cpp/RELEASE_NOTES @@ -0,0 +1,41 @@ +Apache Incubator Qpid C++ M2 Release Notes +------------------------------------------- + +The Qpid M2 release contains support the for AMQP 0-8 specification. +You can access the 0-8 specification using the following link. +http://www.amqp.org/tikiwiki/tiki-index.php?page=Download + +For full details of Qpid capabilities, as they currently stand, see our +detailed project documentation at: + +http://cwiki.apache.org/confluence/pages/viewpage.action?pageId=28284 + +Please take time to go through the README file provided with the distro to get a good understanding about build system etc. + + +Known Issues +------------ + +You can view the outstanding task list for Qpid by visiting our JIRA: +http://issues.apache.org/jira/browse/QPID + +Bug QPID-437 c++ broker doesn't obey the mandatory flag + + +M2 Tasks Completed +------------------- + +Test QPID-412 Implement initial C++ interop tests +Task QPID-124 Connect AMQP version from ProtocolInitiation object to all version-aware objects + +New Feature QPID-154 Logging/tracing for C++. +New Feature QPID-98 implement durable exchanges +New Feature QPID-41 Persistent storage for messages & durable queues + +Improvement QPID-450 C++ demos +Improvement QPID-64 C++ cluster design. +Improvement QPID-62 C++ event queue design. + +Bug QPID-481 c++ broker dosen't implement channel.flow +Bug QPID-467 Complete Interop Testing +Bug QPID-123 Sporadic failure on Python tests diff --git a/qpid/cpp/bootstrap b/qpid/cpp/bootstrap new file mode 100755 index 0000000000..c1fb753201 --- /dev/null +++ b/qpid/cpp/bootstrap @@ -0,0 +1,32 @@ +#!/bin/sh +set -e +aclocal -I m4 +autoheader +libtoolize --automake + +# Generate (for automake) lots of repetitive parts of tests/Makefile.am. +(cd src/tests && rm -f gen.mk + perl -ne '/^(include |if |else|endif)/ or print' Makefile.am \ + | make -f - abs_srcdir=`dirname $(pwd)` gen.mk > /dev/null ) + +# Create initial Makefile fragments that will force make to generate +# the real ones. +cat > src/rubygen.mk <<EOF +\$(srcdir)/rubygen.mk: force + \$(rgen_cmd) +EOF +cat > src/managementgen.mk <<EOF +\$(srcdir)/managementgen.mk: force + \$(mgen_cmd) +EOF + + +automake +autoconf + +if [ "$1" = "-build" -o "$1" = "--build" ] ; then + shift + ./configure "$@" + make + make check +fi diff --git a/qpid/cpp/build-aux/compile b/qpid/cpp/build-aux/compile new file mode 100755 index 0000000000..1b1d232169 --- /dev/null +++ b/qpid/cpp/build-aux/compile @@ -0,0 +1,142 @@ +#! /bin/sh +# Wrapper for compilers which do not understand `-c -o'. + +scriptversion=2005-05-14.22 + +# Copyright (C) 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. +# Written by Tom Tromey <tromey@cygnus.com>. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to <bug-automake@gnu.org> or send patches to +# <automake-patches@gnu.org>. + +case $1 in + '') + echo "$0: No command. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand `-c -o'. +Remove `-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file `INSTALL'. + +Report bugs to <bug-automake@gnu.org>. +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; +esac + +ofile= +cfile= +eat= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as `compile cc -o foo foo.c'. + # So we strip `-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no `-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # `.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed -e 's|^.*/||' -e 's/\.c$/.o/'` + +# Create the lock directory. +# Note: use `[/.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/qpid/cpp/build-aux/config.guess b/qpid/cpp/build-aux/config.guess new file mode 100755 index 0000000000..c93201a4d2 --- /dev/null +++ b/qpid/cpp/build-aux/config.guess @@ -0,0 +1,1501 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, +# Inc. + +timestamp='2006-11-08' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Originally written by Per Bothner <per@bothner.com>. +# Please send patches to <config-patches@gnu.org>. Submit a context +# diff and a properly formatted ChangeLog entry. +# +# This script attempts to guess a canonical system name similar to +# config.sub. If it succeeds, it prints the system name on stdout, and +# exits with 0. Otherwise, it exits with 1. +# +# The plan is that this can be called by configure scripts if you +# don't specify an explicit build system type. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to <config-patches@gnu.org>." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tupples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(/sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep __ELF__ >/dev/null + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE}|sed -e 's/[-_].*/\./'` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}" + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE="alpha" ;; + "EV4.5 (21064)") + UNAME_MACHINE="alpha" ;; + "LCA4 (21066/21068)") + UNAME_MACHINE="alpha" ;; + "EV5 (21164)") + UNAME_MACHINE="alphaev5" ;; + "EV5.6 (21164A)") + UNAME_MACHINE="alphaev56" ;; + "EV5.6 (21164PC)") + UNAME_MACHINE="alphapca56" ;; + "EV5.7 (21164PC)") + UNAME_MACHINE="alphapca57" ;; + "EV6 (21264)") + UNAME_MACHINE="alphaev6" ;; + "EV6.7 (21264A)") + UNAME_MACHINE="alphaev67" ;; + "EV6.8CB (21264C)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8AL (21264B)") + UNAME_MACHINE="alphaev68" ;; + "EV6.8CX (21264D)") + UNAME_MACHINE="alphaev68" ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE="alphaev69" ;; + "EV7 (21364)") + UNAME_MACHINE="alphaev7" ;; + "EV7.9 (21364A)") + UNAME_MACHINE="alphaev79" ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + exit ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm:riscos:*:*|arm:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:SunOS:5.*:*) + echo i386-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = "x" && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include <stdio.h> /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <sys/systemcfg.h> + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[45]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH="hppa1.0" ;; # CPU_PA_RISC1_0 + 528) HP_ARCH="hppa1.1" ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH="hppa2.0n" ;; + 64) HP_ARCH="hppa2.0w" ;; + '') HP_ARCH="hppa2.0" ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include <stdlib.h> + #include <unistd.h> + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = "hppa2.0w" ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep __LP64__ >/dev/null + then + HP_ARCH="hppa2.0w" + else + HP_ARCH="hppa64" + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <unistd.h> + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'` + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz' | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + case ${UNAME_MACHINE} in + pc98) + echo i386-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_MACHINE}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + i*:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + x86:Interix*:[3456]*) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + EM64T:Interix*:[3456]* | authenticamd:Interix*:[3456]*) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-gnu`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-gnu + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + arm*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + cris:Linux:*:*) + echo cris-axis-linux-gnu + exit ;; + crisv32:Linux:*:*) + echo crisv32-axis-linux-gnu + exit ;; + frv:Linux:*:*) + echo frv-unknown-linux-gnu + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + mips:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips + #undef mipsel + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mipsel + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips + #else + CPU= + #endif + #endif +EOF + eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' + /^CPU/{ + s: ::g + p + }'`" + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; + mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef mips64 + #undef mips64el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=mips64el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=mips64 + #else + CPU= + #endif + #endif +EOF + eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' + /^CPU/{ + s: ::g + p + }'`" + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-gnu"; exit; } + ;; + or32:Linux:*:*) + echo or32-unknown-linux-gnu + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-gnu + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-gnu + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep ld.so.1 >/dev/null + if test "$?" = 0 ; then LIBC="libc1" ; else LIBC="" ; fi + echo ${UNAME_MACHINE}-unknown-linux-gnu${LIBC} + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-gnu ;; + PA8*) echo hppa2.0-unknown-linux-gnu ;; + *) echo hppa-unknown-linux-gnu ;; + esac + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-gnu + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-gnu + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-gnu + exit ;; + x86_64:Linux:*:*) + echo x86_64-unknown-linux-gnu + exit ;; + i*86:Linux:*:*) + # The BFD linker knows what the default object file format is, so + # first see if it will tell us. cd to the root directory to prevent + # problems with other programs or directories called `ld' in the path. + # Set LC_ALL=C to ensure ld outputs messages in English. + ld_supported_targets=`cd /; LC_ALL=C ld --help 2>&1 \ + | sed -ne '/supported targets:/!d + s/[ ][ ]*/ /g + s/.*supported targets: *// + s/ .*// + p'` + case "$ld_supported_targets" in + elf32-i386) + TENTATIVE="${UNAME_MACHINE}-pc-linux-gnu" + ;; + a.out-i386-linux) + echo "${UNAME_MACHINE}-pc-linux-gnuaout" + exit ;; + coff-i386) + echo "${UNAME_MACHINE}-pc-linux-gnucoff" + exit ;; + "") + # Either a pre-BFD a.out linker (linux-gnuoldld) or + # one that does not give us useful --help. + echo "${UNAME_MACHINE}-pc-linux-gnuoldld" + exit ;; + esac + # Determine whether the default compiler is a.out or elf + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <features.h> + #ifdef __ELF__ + # ifdef __GLIBC__ + # if __GLIBC__ >= 2 + LIBC=gnu + # else + LIBC=gnulibc1 + # endif + # else + LIBC=gnulibc1 + # endif + #else + #if defined(__INTEL_COMPILER) || defined(__PGI) || defined(__SUNPRO_C) || defined(__SUNPRO_CC) + LIBC=gnu + #else + LIBC=gnuaout + #endif + #endif + #ifdef __dietlibc__ + LIBC=dietlibc + #endif +EOF + eval "`$CC_FOR_BUILD -E $dummy.c 2>/dev/null | sed -n ' + /^LIBC/{ + s: ::g + p + }'`" + test x"${LIBC}" != x && { + echo "${UNAME_MACHINE}-pc-linux-${LIBC}" + exit + } + test x"${TENTATIVE}" != x && { echo "${TENTATIVE}"; exit; } + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.0*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name` + echo ${UNAME_MACHINE}-pc-isc$UNAME_REL + elif /bin/uname -X 2>/dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i386. + echo i386-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.0*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says <Richard.M.Bartel@ccMail.Census.GOV> + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes <hewes@openmarket.com>. + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + case $UNAME_PROCESSOR in + unknown) UNAME_PROCESSOR=powerpc ;; + esac + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = "x86"; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NSE-?:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = "386"; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; +esac + +#echo '(No uname command or uname output not recognized.)' 1>&2 +#echo "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" 1>&2 + +eval $set_cc_for_build +cat >$dummy.c <<EOF +#ifdef _SEQUENT_ +# include <sys/types.h> +# include <sys/utsname.h> +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include <sys/param.h> + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (__arm) && defined (__acorn) && defined (__unix) + printf ("arm-acorn-riscix\n"); exit (0); +#endif + +#if defined (hp300) && !defined (hpux) + printf ("m68k-hp-bsd\n"); exit (0); +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); + +#endif + +#if defined (vax) +# if !defined (ultrix) +# include <sys/param.h> +# if defined (BSD) +# if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +# else +# if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# endif +# else + printf ("vax-dec-bsd\n"); exit (0); +# endif +# else + printf ("vax-dec-ultrix\n"); exit (0); +# endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. + +test -d /usr/apollo && { echo ${ISP}-apollo-${SYSTYPE}; exit; } + +# Convex versions that predate uname can use getsysinfo(1) + +if [ -x /usr/convex/getsysinfo ] +then + case `getsysinfo -f cpu_type` in + c1*) + echo c1-convex-bsd + exit ;; + c2*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + c34*) + echo c34-convex-bsd + exit ;; + c38*) + echo c38-convex-bsd + exit ;; + c4*) + echo c4-convex-bsd + exit ;; + esac +fi + +cat >&2 <<EOF +$0: unable to guess system type + +This script, last modified $timestamp, has failed to recognize +the operating system you are using. It is advised that you +download the most up to date version of the config scripts from + + http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.guess +and + http://savannah.gnu.org/cgi-bin/viewcvs/*checkout*/config/config/config.sub + +If the version you run ($0) is already up to date, please +send the following data and any information you think might be +pertinent to <config-patches@gnu.org> in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/qpid/cpp/build-aux/config.rpath b/qpid/cpp/build-aux/config.rpath new file mode 100755 index 0000000000..c492a93b66 --- /dev/null +++ b/qpid/cpp/build-aux/config.rpath @@ -0,0 +1,614 @@ +#! /bin/sh +# Output a system dependent set of variables, describing how to set the +# run time search path of shared libraries in an executable. +# +# Copyright 1996-2006 Free Software Foundation, Inc. +# Taken from GNU libtool, 2001 +# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. +# +# The first argument passed to this file is the canonical host specification, +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld +# should be set by the caller. +# +# The set of defined variables is at the end of this script. + +# Known limitations: +# - On IRIX 6.5 with CC="cc", the run time search patch must not be longer +# than 256 bytes, otherwise the compiler driver will dump core. The only +# known workaround is to choose shorter directory names for the build +# directory and/or the installation directory. + +# All known linkers require a `.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a +shrext=.so + +host="$1" +host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'` +host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'` +host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'` + +# Code taken from libtool.m4's _LT_CC_BASENAME. + +for cc_temp in $CC""; do + case $cc_temp in + compile | *[\\/]compile | ccache | *[\\/]ccache ) ;; + distcc | *[\\/]distcc | purify | *[\\/]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`echo "$cc_temp" | sed -e 's%^.*/%%'` + +# Code taken from libtool.m4's AC_LIBTOOL_PROG_COMPILER_PIC. + +wl= +if test "$GCC" = yes; then + wl='-Wl,' +else + case "$host_os" in + aix*) + wl='-Wl,' + ;; + darwin*) + case $cc_basename in + xlc*) + wl='-Wl,' + ;; + esac + ;; + mingw* | pw32* | os2*) + ;; + hpux9* | hpux10* | hpux11*) + wl='-Wl,' + ;; + irix5* | irix6* | nonstopux*) + wl='-Wl,' + ;; + newsos6) + ;; + linux*) + case $cc_basename in + icc* | ecc*) + wl='-Wl,' + ;; + pgcc | pgf77 | pgf90) + wl='-Wl,' + ;; + ccc*) + wl='-Wl,' + ;; + como) + wl='-lopt=' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + wl='-Wl,' + ;; + esac + ;; + esac + ;; + osf3* | osf4* | osf5*) + wl='-Wl,' + ;; + sco3.2v5*) + ;; + solaris*) + wl='-Wl,' + ;; + sunos4*) + wl='-Qoption ld ' + ;; + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + wl='-Wl,' + ;; + sysv4*MP*) + ;; + unicos*) + wl='-Wl,' + ;; + uts4*) + ;; + esac +fi + +# Code taken from libtool.m4's AC_LIBTOOL_PROG_LD_SHLIBS. + +hardcode_libdir_flag_spec= +hardcode_libdir_separator= +hardcode_direct=no +hardcode_minus_L=no + +case "$host_os" in + cygwin* | mingw* | pw32*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; +esac + +ld_shlibs=yes +if test "$with_gnu_ld" = yes; then + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + # Unlike libtool, we use -rpath here, not --rpath, since the documented + # option of GNU ld is called -rpath, not --rpath. + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + case "$host_os" in + aix3* | aix4* | aix5*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + ld_shlibs=no + fi + ;; + amigaos*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + # Samuel A. Falvo II <kc5tja@dolphin.openprojects.net> reports + # that the semantics of dynamic libraries on AmigaOS, at least up + # to version 4, is to share data among multiple programs linked + # with the same dynamic library. Since this doesn't match the + # behavior of shared libraries on other platforms, we cannot use + # them. + ld_shlibs=no + ;; + beos*) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + cygwin* | mingw* | pw32*) + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec='-L$libdir' + if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + interix3*) + hardcode_direct=no + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + linux*) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + netbsd*) + ;; + solaris*) + if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then + ld_shlibs=no + elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*) + ld_shlibs=no + ;; + *) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`' + else + ld_shlibs=no + fi + ;; + esac + ;; + sunos4*) + hardcode_direct=yes + ;; + *) + if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then + : + else + ld_shlibs=no + fi + ;; + esac + if test "$ld_shlibs" = no; then + hardcode_libdir_flag_spec= + fi +else + case "$host_os" in + aix3*) + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + hardcode_minus_L=yes + if test "$GCC" = yes; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + hardcode_direct=unsupported + fi + ;; + aix4* | aix5*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + else + aix_use_runtimelinking=no + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[23]|aix4.[23].*|aix5*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac + fi + hardcode_direct=yes + hardcode_libdir_separator=':' + if test "$GCC" = yes; then + case $host_os in aix4.[012]|aix4.[012].*) + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && \ + strings "$collect2name" | grep resolve_lib_name >/dev/null + then + # We have reworked collect2 + hardcode_direct=yes + else + # We have old collect2 + hardcode_direct=unsupported + hardcode_minus_L=yes + hardcode_libdir_flag_spec='-L$libdir' + hardcode_libdir_separator= + fi + ;; + esac + fi + # Begin _LT_AC_SYS_LIBPATH_AIX. + echo 'int main () { return 0; }' > conftest.c + ${CC} ${LDFLAGS} conftest.c -o conftest + aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` + if test -z "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0 *\(.*\)$/\1/; p; } +}'` + fi + if test -z "$aix_libpath"; then + aix_libpath="/usr/lib:/lib" + fi + rm -f conftest.c conftest + # End _LT_AC_SYS_LIBPATH_AIX. + if test "$aix_use_runtimelinking" = yes; then + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + else + if test "$host_cpu" = ia64; then + hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib' + else + hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath" + fi + fi + ;; + amigaos*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + # see comment about different semantics on the GNU ld section + ld_shlibs=no + ;; + bsdi[45]*) + ;; + cygwin* | mingw* | pw32*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + hardcode_libdir_flag_spec=' ' + libext=lib + ;; + darwin* | rhapsody*) + hardcode_direct=no + if test "$GCC" = yes ; then + : + else + case $cc_basename in + xlc*) + ;; + *) + ld_shlibs=no + ;; + esac + fi + ;; + dgux*) + hardcode_libdir_flag_spec='-L$libdir' + ;; + freebsd1*) + ld_shlibs=no + ;; + freebsd2.2*) + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + ;; + freebsd2*) + hardcode_direct=yes + hardcode_minus_L=yes + ;; + freebsd* | kfreebsd*-gnu | dragonfly*) + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + ;; + hpux9*) + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + hpux10*) + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + hardcode_direct=yes + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + fi + ;; + hpux11*) + if test "$with_gnu_ld" = no; then + hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir' + hardcode_libdir_separator=: + case $host_cpu in + hppa*64*|ia64*) + hardcode_direct=no + ;; + *) + hardcode_direct=yes + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + hardcode_minus_L=yes + ;; + esac + fi + ;; + irix5* | irix6* | nonstopux*) + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + netbsd*) + hardcode_libdir_flag_spec='-R$libdir' + hardcode_direct=yes + ;; + newsos6) + hardcode_direct=yes + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + openbsd*) + hardcode_direct=yes + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + else + case "$host_os" in + openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*) + hardcode_libdir_flag_spec='-R$libdir' + ;; + *) + hardcode_libdir_flag_spec='${wl}-rpath,$libdir' + ;; + esac + fi + ;; + os2*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_minus_L=yes + ;; + osf3*) + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + hardcode_libdir_separator=: + ;; + osf4* | osf5*) + if test "$GCC" = yes; then + hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir' + else + # Both cc and cxx compiler support -rpath directly + hardcode_libdir_flag_spec='-rpath $libdir' + fi + hardcode_libdir_separator=: + ;; + solaris*) + hardcode_libdir_flag_spec='-R$libdir' + ;; + sunos4*) + hardcode_libdir_flag_spec='-L$libdir' + hardcode_direct=yes + hardcode_minus_L=yes + ;; + sysv4) + case $host_vendor in + sni) + hardcode_direct=yes # is this really true??? + ;; + siemens) + hardcode_direct=no + ;; + motorola) + hardcode_direct=no #Motorola manual says yes, but my tests say they lie + ;; + esac + ;; + sysv4.3*) + ;; + sysv4*MP*) + if test -d /usr/nec; then + ld_shlibs=yes + fi + ;; + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7*) + ;; + sysv5* | sco3.2v5* | sco5v6*) + hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`' + hardcode_libdir_separator=':' + ;; + uts4*) + hardcode_libdir_flag_spec='-L$libdir' + ;; + *) + ld_shlibs=no + ;; + esac +fi + +# Check dynamic linker characteristics +# Code taken from libtool.m4's AC_LIBTOOL_SYS_DYNAMIC_LINKER. +libname_spec='lib$name' +case "$host_os" in + aix3*) + ;; + aix4* | aix5*) + ;; + amigaos*) + ;; + beos*) + ;; + bsdi[45]*) + ;; + cygwin* | mingw* | pw32*) + shrext=.dll + ;; + darwin* | rhapsody*) + shrext=.dylib + ;; + dgux*) + ;; + freebsd1*) + ;; + kfreebsd*-gnu) + ;; + freebsd* | dragonfly*) + ;; + gnu*) + ;; + hpux9* | hpux10* | hpux11*) + case $host_cpu in + ia64*) + shrext=.so + ;; + hppa*64*) + shrext=.sl + ;; + *) + shrext=.sl + ;; + esac + ;; + interix3*) + ;; + irix5* | irix6* | nonstopux*) + case "$host_os" in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 ;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;; + *) libsuff= shlibsuff= ;; + esac + ;; + esac + ;; + linux*oldld* | linux*aout* | linux*coff*) + ;; + linux*) + ;; + knetbsd*-gnu) + ;; + netbsd*) + ;; + newsos6) + ;; + nto-qnx*) + ;; + openbsd*) + ;; + os2*) + libname_spec='$name' + shrext=.dll + ;; + osf3* | osf4* | osf5*) + ;; + solaris*) + ;; + sunos4*) + ;; + sysv4 | sysv4.3*) + ;; + sysv4*MP*) + ;; + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + ;; + uts4*) + ;; +esac + +sed_quote_subst='s/\(["`$\\]\)/\\\1/g' +escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"` +shlibext=`echo "$shrext" | sed -e 's,^\.,,'` +escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"` + +LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' <<EOF + +# How to pass a linker flag through the compiler. +wl="$escaped_wl" + +# Static library suffix (normally "a"). +libext="$libext" + +# Shared library suffix (normally "so"). +shlibext="$shlibext" + +# Flag to hardcode \$libdir into a binary during linking. +# This must work even if \$libdir does not exist. +hardcode_libdir_flag_spec="$escaped_hardcode_libdir_flag_spec" + +# Whether we need a single -rpath flag with a separated argument. +hardcode_libdir_separator="$hardcode_libdir_separator" + +# Set to yes if using DIR/libNAME.so during linking hardcodes DIR into the +# resulting binary. +hardcode_direct="$hardcode_direct" + +# Set to yes if using the -LDIR flag during linking hardcodes DIR into the +# resulting binary. +hardcode_minus_L="$hardcode_minus_L" + +EOF diff --git a/qpid/cpp/build-aux/config.sub b/qpid/cpp/build-aux/config.sub new file mode 100755 index 0000000000..7ccee73057 --- /dev/null +++ b/qpid/cpp/build-aux/config.sub @@ -0,0 +1,1619 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, +# 2000, 2001, 2002, 2003, 2004, 2005, 2006 Free Software Foundation, +# Inc. + +timestamp='2006-11-07' + +# This file is (in principle) common to ALL GNU software. +# The presence of a machine in this file suggests that SOME GNU software +# can handle that machine. It does not imply ALL GNU software can. +# +# This file is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA +# 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + + +# Please send patches to <config-patches@gnu.org>. Submit a context +# diff and a properly formatted ChangeLog entry. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS + $0 [OPTION] ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to <config-patches@gnu.org>." + +version="\ +GNU config.sub ($timestamp) + +Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005 +Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | linux-uclibc* | \ + uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray) + os= + basic_machine=$1 + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arm | arm[bl]e | arme[lb] | armv[2345] | armv[345][lb] | avr | avr32 \ + | bfin \ + | c4x | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | fr30 | frv \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | mcore \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64vr | mips64vrel \ + | mips64orion | mips64orionel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | mt \ + | msp430 \ + | nios | nios2 \ + | ns16k | ns32k \ + | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle | ppcbe \ + | pyramid \ + | score \ + | sh | sh[1234] | sh[24]a | sh[23]e | sh[34]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu | strongarm \ + | tahoe | thumb | tic4x | tic80 | tron \ + | v850 | v850e \ + | we32k \ + | x86 | xc16x | xscale | xscalee[bl] | xstormy16 | xtensa \ + | z8k) + basic_machine=$basic_machine-unknown + ;; + m6811 | m68hc11 | m6812 | m68hc12) + # Motorola 68HC11/12. + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* | c54x-* | c55x-* | c6x-* \ + | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | elxsi-* \ + | f30[01]-* | f700-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nios-* | nios2-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* | ppcbe-* \ + | pyramid-* \ + | romp-* | rs6000-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | strongarm-* | sv1-* | sx?-* \ + | tahoe-* | thumb-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tron-* \ + | v850-* | v850e-* | vax-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* | xscale-* | xscalee[bl]-* \ + | xstormy16-* | xtensa-* \ + | ymp-* \ + | z8k-*) + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16c) + basic_machine=cr16c-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; +# I'm not sure what "Sysv32" means. Should this be sysv3.2? + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + mingw32) + basic_machine=i386-pc + os=-mingw32 + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc) basic_machine=powerpc-unknown + ;; + ppc-*) basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tic54x | c54x*) + basic_machine=tic54x-unknown + os=-coff + ;; + tic55x | c55x*) + basic_machine=tic55x-unknown + os=-coff + ;; + tic6x | c6x*) + basic_machine=tic6x-unknown + os=-coff + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -solaris* | -sym* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -openbsd* | -solidbsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* \ + | -cygwin* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -linux-gnu* | -linux-newlib* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -kaos*) + os=-kaos + ;; + -zvmoe) + os=-zvmoe + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + # This also exists in the configure program, but was not the + # default. + # os=-sunos4 + ;; + m68*-cisco) + os=-aout + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/qpid/cpp/build-aux/depcomp b/qpid/cpp/build-aux/depcomp new file mode 100755 index 0000000000..ca5ea4e1ef --- /dev/null +++ b/qpid/cpp/build-aux/depcomp @@ -0,0 +1,584 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2006-10-15.18 + +# Copyright (C) 1999, 2000, 2003, 2004, 2005, 2006 Free Software +# Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva <oliva@dcc.unicamp.br>. + +case $1 in + '') + echo "$0: No command. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by `PROGRAMS ARGS'. + object Object file output by `PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputing dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to <bug-automake@gnu.org>. +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. +## Unfortunately, FreeBSD c89 acceptance of flags depends upon +## the command line argument order; so add the flags where they +## appear in depend2.am. Note that the slowdown incurred here +## affects only configure: in makefiles, %FASTDEP% shortcuts this. + for arg + do + case $arg in + -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; + *) set fnord "$@" "$arg" ;; + esac + shift # fnord + shift # $arg + done + "$@" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + alpha=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz +## The second -e expression handles DOS-style file names with drive letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the `deleted header file' problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. + tr ' ' ' +' < "$tmpdepfile" | +## Some versions of gcc put a space before the `:'. On the theory +## that the space means something, we add a space to the output as +## well. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like `#:fec' to the end of the + # dependency line. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' | \ + tr ' +' ' ' >> $depfile + echo >> $depfile + + # The second pass generates a dummy entry for each header file. + tr ' ' ' +' < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> $depfile + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts `$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + stripped=`echo "$object" | sed 's/\(.*\)\..*$/\1/'` + tmpdepfile="$stripped.u" + if test "$libtool" = yes; then + "$@" -Wc,-M + else + "$@" -M + fi + stat=$? + + if test -f "$tmpdepfile"; then : + else + stripped=`echo "$stripped" | sed 's,^.*/,,'` + tmpdepfile="$stripped.u" + fi + + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + + if test -f "$tmpdepfile"; then + outname="$stripped.o" + # Each line is of the form `foo.o: dependent.h'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed -e "s,^$outname:,$object :," < "$tmpdepfile" > "$depfile" + sed -e "s,^$outname: \(.*\)$,\1:," < "$tmpdepfile" >> "$depfile" + else + # The sourcefile does not contain any dependencies, so just + # store a dummy comment line, to avoid errors with the Makefile + # "include basename.Plo" scheme. + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +icc) + # Intel's C compiler understands `-MD -MF file'. However on + # icc -MD -MF foo.d -c -o sub/foo.o sub/foo.c + # ICC 7.0 will fill foo.d with something like + # foo.o: sub/foo.c + # foo.o: sub/foo.h + # which is wrong. We want: + # sub/foo.o: sub/foo.c + # sub/foo.o: sub/foo.h + # sub/foo.c: + # sub/foo.h: + # ICC 7.1 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using \ : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" | + sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp2) + # The "hp" stanza above does not work with aCC (C++) and HP's ia64 + # compilers, which have integrated preprocessors. The correct option + # to use with these is +Maked; it writes dependencies to a file named + # 'foo.d', which lands next to the object file, wherever that + # happens to be. + # Much of this is similar to the tru64 case; see comments there. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir.libs/$base.d + "$@" -Wc,+Maked + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + "$@" +Maked + fi + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," "$tmpdepfile" > "$depfile" + # Add `dependent.h:' lines. + sed -ne '2,${; s/^ *//; s/ \\*$//; s/$/:/; p;}' "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" "$tmpdepfile2" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. `cc -MD -o foo.o ...' puts the dependencies into `foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in `foo.d' instead, so we check for that too. + # Subdirectories are respected. + dir=`echo "$object" | sed -e 's|/[^/]*$|/|'` + test "x$dir" = "x$object" && dir= + base=`echo "$object" | sed -e 's|^.*/||' -e 's/\.o$//' -e 's/\.lo$//'` + + if test "$libtool" = yes; then + # With Tru64 cc, shared objects can also be used to make a + # static library. This mechanism is used in libtool 1.4 series to + # handle both shared and static libraries in a single compilation. + # With libtool 1.4, dependencies were output in $dir.libs/$base.lo.d. + # + # With libtool 1.5 this exception was removed, and libtool now + # generates 2 separate objects for the 2 libraries. These two + # compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir.libs/$base.lo.d # libtool 1.4 + tmpdepfile2=$dir$base.o.d # libtool 1.5 + tmpdepfile3=$dir.libs/$base.o.d # libtool 1.5 + tmpdepfile4=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.o.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + tmpdepfile4=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -eq 0; then : + else + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" "$tmpdepfile4" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[a-z]*:,$object:," < "$tmpdepfile" > "$depfile" + # That's a tab and a space in the []. + sed -e 's,^.*\.[a-z]*:[ ]*,,' -e 's,$,:,' < "$tmpdepfile" >> "$depfile" + else + echo "#dummy" > "$depfile" + fi + rm -f "$tmpdepfile" + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for `:' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as `c:/foo/bar' could be seen as target `c' otherwise. + "$@" $dashmflag | + sed 's:^[ ]*[^: ][^:][^:]*\:[ ]*:'"$object"'\: :' > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + tr ' ' ' +' < "$tmpdepfile" | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no + for arg in "$@"; do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix="`echo $object | sed 's/^.*\././'`" + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + sed '1,2d' "$tmpdepfile" | tr ' ' ' +' | \ +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test $1 != '--mode=compile'; do + shift + done + shift + fi + + # Remove `-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E | + sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' | + sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o, + # because we must use -o when running libtool. + "$@" || exit $? + IFS=" " + for arg + do + case "$arg" in + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::echo "`cygpath -u \\"\1\\"`":p' | sort | uniq > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s:: \1 \\:p' >> "$depfile" + echo " " >> "$depfile" + . "$tmpdepfile" | sed 's% %\\ %g' | sed -n '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/qpid/cpp/build-aux/install-sh b/qpid/cpp/build-aux/install-sh new file mode 100755 index 0000000000..4fbbae7b7f --- /dev/null +++ b/qpid/cpp/build-aux/install-sh @@ -0,0 +1,507 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2006-10-14.15 + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +nl=' +' +IFS=" "" $nl" + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" +if test -z "$doit"; then + doit_exec=exec +else + doit_exec=$doit +fi + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +posix_glob= +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chmodcmd=$chmodprog +chowncmd= +chgrpcmd= +stripcmd= +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src= +dst= +dir_arg= +dstarg= +no_target_directory= + +usage="Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: +-c (ignored) +-d create directories instead of installing files. +-g GROUP $chgrpprog installed files to GROUP. +-m MODE $chmodprog installed files to MODE. +-o USER $chownprog installed files to USER. +-s $stripprog installed files. +-t DIRECTORY install into DIRECTORY. +-T report an error if DSTFILE is a directory. +--help display this help and exit. +--version display version info and exit. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + shift + shift + case $mode in + *' '* | *' '* | *' +'* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t) dstarg=$2 + shift + shift + continue;; + + -T) no_target_directory=true + shift + continue;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac +done + +if test $# -ne 0 && test -z "$dir_arg$dstarg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dstarg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dstarg" + shift # fnord + fi + shift # arg + dstarg=$arg + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call `install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + trap '(exit $?); exit' 1 2 13 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names starting with `-'. + case $src in + -*) src=./$src ;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dstarg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + + dst=$dstarg + # Protect names starting with `-'. + case $dst in + -*) dst=./$dst ;; + esac + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test -n "$no_target_directory"; then + echo "$0: $dstarg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + # Prefer dirname, but fall back on a substitute if dirname fails. + dstdir=` + (dirname "$dst") 2>/dev/null || + expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$dst" : 'X\(//\)[^/]' \| \ + X"$dst" : 'X\(//\)$' \| \ + X"$dst" : 'X\(/\)' \| . 2>/dev/null || + echo X"$dst" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q' + ` + + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writeable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix=/ ;; + -*) prefix=./ ;; + *) prefix= ;; + esac + + case $posix_glob in + '') + if (set -f) 2>/dev/null; then + posix_glob=true + else + posix_glob=false + fi ;; + esac + + oIFS=$IFS + IFS=/ + $posix_glob && set -f + set fnord $dstdir + shift + $posix_glob && set +f + IFS=$oIFS + + prefixes= + + for d + do + test -z "$d" && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } \ + && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } \ + && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } \ + && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # Now rename the file to the real destination. + { $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null \ + || { + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + if test -f "$dst"; then + $doit $rmcmd -f "$dst" 2>/dev/null \ + || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null \ + && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; }; }\ + || { + echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + else + : + fi + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + } || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/qpid/cpp/build-aux/ltmain.sh b/qpid/cpp/build-aux/ltmain.sh new file mode 100755 index 0000000000..c715b59412 --- /dev/null +++ b/qpid/cpp/build-aux/ltmain.sh @@ -0,0 +1,6871 @@ +# ltmain.sh - Provide generalized library-building support services. +# NOTE: Changing this file will not affect anything until you rerun configure. +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005 +# Free Software Foundation, Inc. +# Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +basename="s,^.*/,,g" + +# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh +# is ksh but when the shell is invoked as "sh" and the current value of +# the _XPG environment variable is not equal to 1 (one), the special +# positional parameter $0, within a function call, is the name of the +# function. +progpath="$0" + +# The name of this program: +progname=`echo "$progpath" | $SED $basename` +modename="$progname" + +# Global variables: +EXIT_SUCCESS=0 +EXIT_FAILURE=1 + +PROGRAM=ltmain.sh +PACKAGE=libtool +VERSION="1.5.22 Debian 1.5.22-4" +TIMESTAMP=" (1.1220.2.365 2005/12/18 22:14:06)" + +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes. +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + +# Check that we have a working $echo. +if test "X$1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift +elif test "X$1" = X--fallback-echo; then + # Avoid inline document here, it may be left over + : +elif test "X`($echo '\t') 2>/dev/null`" = 'X\t'; then + # Yippee, $echo works! + : +else + # Restart under the correct shell, and then maybe $echo will work. + exec $SHELL "$progpath" --no-reexec ${1+"$@"} +fi + +if test "X$1" = X--fallback-echo; then + # used as fallback echo + shift + cat <<EOF +$* +EOF + exit $EXIT_SUCCESS +fi + +default_mode= +help="Try \`$progname --help' for more information." +magic="%%%MAGIC variable%%%" +mkdir="mkdir" +mv="mv -f" +rm="rm -f" + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed="${SED}"' -e 1s/^X//' +sed_quote_subst='s/\([\\`\\"$\\\\]\)/\\\1/g' +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + SP2NL='tr \040 \012' + NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + SP2NL='tr \100 \n' + NL2SP='tr \r\n \100\100' + ;; +esac + +# NLS nuisances. +# Only set LANG and LC_ALL to C if already set. +# These must not be set unconditionally because not all systems understand +# e.g. LANG=C (notably SCO). +# We save the old values to restore during execute mode. +if test "${LC_ALL+set}" = set; then + save_LC_ALL="$LC_ALL"; LC_ALL=C; export LC_ALL +fi +if test "${LANG+set}" = set; then + save_LANG="$LANG"; LANG=C; export LANG +fi + +# Make sure IFS has a sensible default +lt_nl=' +' +IFS=" $lt_nl" + +if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then + $echo "$modename: not configured to build any kind of library" 1>&2 + $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit $EXIT_FAILURE +fi + +# Global variables. +mode=$default_mode +nonopt= +prev= +prevopt= +run= +show="$echo" +show_help= +execute_dlfiles= +duplicate_deps=no +preserve_args= +lo2o="s/\\.lo\$/.${objext}/" +o2lo="s/\\.${objext}\$/.lo/" + +##################################### +# Shell function definitions: +# This seems to be the best place for them + +# func_mktempdir [string] +# Make a temporary directory that won't clash with other running +# libtool processes, and avoids race conditions if possible. If +# given, STRING is the basename for that directory. +func_mktempdir () +{ + my_template="${TMPDIR-/tmp}/${1-$progname}" + + if test "$run" = ":"; then + # Return a directory name, but don't create it in dry-run mode + my_tmpdir="${my_template}-$$" + else + + # If mktemp works, use that first and foremost + my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` + + if test ! -d "$my_tmpdir"; then + # Failing that, at least try and use $RANDOM to avoid a race + my_tmpdir="${my_template}-${RANDOM-0}$$" + + save_mktempdir_umask=`umask` + umask 0077 + $mkdir "$my_tmpdir" + umask $save_mktempdir_umask + fi + + # If we're not in dry-run mode, bomb out on failure + test -d "$my_tmpdir" || { + $echo "cannot create temporary directory \`$my_tmpdir'" 1>&2 + exit $EXIT_FAILURE + } + fi + + $echo "X$my_tmpdir" | $Xsed +} + + +# func_win32_libid arg +# return the library type of file 'arg' +# +# Need a lot of goo to handle *both* DLLs and import libs +# Has to be a shell function in order to 'eat' the argument +# that is supplied when $file_magic_command is called. +func_win32_libid () +{ + win32_libid_type="unknown" + win32_fileres=`file -L $1 2>/dev/null` + case $win32_fileres in + *ar\ archive\ import\ library*) # definitely import + win32_libid_type="x86 archive import" + ;; + *ar\ archive*) # could be an import, or static + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | \ + $EGREP -e 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then + win32_nmres=`eval $NM -f posix -A $1 | \ + $SED -n -e '1,100{/ I /{s,.*,import,;p;q;};}'` + case $win32_nmres in + import*) win32_libid_type="x86 archive import";; + *) win32_libid_type="x86 archive static";; + esac + fi + ;; + *DLL*) + win32_libid_type="x86 DLL" + ;; + *executable*) # but shell scripts are "executable" too... + case $win32_fileres in + *MS\ Windows\ PE\ Intel*) + win32_libid_type="x86 DLL" + ;; + esac + ;; + esac + $echo $win32_libid_type +} + + +# func_infer_tag arg +# Infer tagged configuration to use if any are available and +# if one wasn't chosen via the "--tag" command line option. +# Only attempt this if the compiler in the base compile +# command doesn't match the default compiler. +# arg is usually of the form 'gcc ...' +func_infer_tag () +{ + if test -n "$available_tags" && test -z "$tagname"; then + CC_quoted= + for arg in $CC; do + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + CC_quoted="$CC_quoted $arg" + done + case $@ in + # Blanks in the command may have been stripped by the calling shell, + # but not from the CC environment variable when configure was run. + " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*) ;; + # Blanks at the start of $base_compile will cause this to fail + # if we don't check for them as well. + *) + for z in $available_tags; do + if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" + CC_quoted= + for arg in $CC; do + # Double-quote args containing other shell metacharacters. + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + CC_quoted="$CC_quoted $arg" + done + case "$@ " in + " $CC "* | "$CC "* | " `$echo $CC` "* | "`$echo $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$echo $CC_quoted` "* | "`$echo $CC_quoted` "*) + # The compiler in the base compile command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break + ;; + esac + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + $echo "$modename: unable to infer tagged configuration" + $echo "$modename: specify a tag with \`--tag'" 1>&2 + exit $EXIT_FAILURE +# else +# $echo "$modename: using $tagname tagged configuration" + fi + ;; + esac + fi +} + + +# func_extract_an_archive dir oldlib +func_extract_an_archive () +{ + f_ex_an_ar_dir="$1"; shift + f_ex_an_ar_oldlib="$1" + + $show "(cd $f_ex_an_ar_dir && $AR x $f_ex_an_ar_oldlib)" + $run eval "(cd \$f_ex_an_ar_dir && $AR x \$f_ex_an_ar_oldlib)" || exit $? + if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then + : + else + $echo "$modename: ERROR: object name conflicts: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" 1>&2 + exit $EXIT_FAILURE + fi +} + +# func_extract_archives gentop oldlib ... +func_extract_archives () +{ + my_gentop="$1"; shift + my_oldlibs=${1+"$@"} + my_oldobjs="" + my_xlib="" + my_xabs="" + my_xdir="" + my_status="" + + $show "${rm}r $my_gentop" + $run ${rm}r "$my_gentop" + $show "$mkdir $my_gentop" + $run $mkdir "$my_gentop" + my_status=$? + if test "$my_status" -ne 0 && test ! -d "$my_gentop"; then + exit $my_status + fi + + for my_xlib in $my_oldlibs; do + # Extract the objects. + case $my_xlib in + [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; + *) my_xabs=`pwd`"/$my_xlib" ;; + esac + my_xlib=`$echo "X$my_xlib" | $Xsed -e 's%^.*/%%'` + my_xdir="$my_gentop/$my_xlib" + + $show "${rm}r $my_xdir" + $run ${rm}r "$my_xdir" + $show "$mkdir $my_xdir" + $run $mkdir "$my_xdir" + exit_status=$? + if test "$exit_status" -ne 0 && test ! -d "$my_xdir"; then + exit $exit_status + fi + case $host in + *-darwin*) + $show "Extracting $my_xabs" + # Do not bother doing anything if just a dry run + if test -z "$run"; then + darwin_orig_dir=`pwd` + cd $my_xdir || exit $? + darwin_archive=$my_xabs + darwin_curdir=`pwd` + darwin_base_archive=`$echo "X$darwin_archive" | $Xsed -e 's%^.*/%%'` + darwin_arches=`lipo -info "$darwin_archive" 2>/dev/null | $EGREP Architectures 2>/dev/null` + if test -n "$darwin_arches"; then + darwin_arches=`echo "$darwin_arches" | $SED -e 's/.*are://'` + darwin_arch= + $show "$darwin_base_archive has multiple architectures $darwin_arches" + for darwin_arch in $darwin_arches ; do + mkdir -p "unfat-$$/${darwin_base_archive}-${darwin_arch}" + lipo -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" + cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" + func_extract_an_archive "`pwd`" "${darwin_base_archive}" + cd "$darwin_curdir" + $rm "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" + done # $darwin_arches + ## Okay now we have a bunch of thin objects, gotta fatten them up :) + darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print| xargs basename | sort -u | $NL2SP` + darwin_file= + darwin_files= + for darwin_file in $darwin_filelist; do + darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP` + lipo -create -output "$darwin_file" $darwin_files + done # $darwin_filelist + ${rm}r unfat-$$ + cd "$darwin_orig_dir" + else + cd "$darwin_orig_dir" + func_extract_an_archive "$my_xdir" "$my_xabs" + fi # $darwin_arches + fi # $run + ;; + *) + func_extract_an_archive "$my_xdir" "$my_xabs" + ;; + esac + my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` + done + func_extract_archives_result="$my_oldobjs" +} +# End of Shell function definitions +##################################### + +# Darwin sucks +eval std_shrext=\"$shrext_cmds\" + +disable_libs=no + +# Parse our command line options once, thoroughly. +while test "$#" -gt 0 +do + arg="$1" + shift + + case $arg in + -*=*) optarg=`$echo "X$arg" | $Xsed -e 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + execute_dlfiles) + execute_dlfiles="$execute_dlfiles $arg" + ;; + tag) + tagname="$arg" + preserve_args="${preserve_args}=$arg" + + # Check whether tagname contains only valid characters + case $tagname in + *[!-_A-Za-z0-9,/]*) + $echo "$progname: invalid tag name: $tagname" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + case $tagname in + CC) + # Don't test for the "default" C tag, as we know, it's there, but + # not specially marked. + ;; + *) + if grep "^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$" < "$progpath" > /dev/null; then + taglist="$taglist $tagname" + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$tagname'$/,/^# ### END LIBTOOL TAG CONFIG: '$tagname'$/p' < $progpath`" + else + $echo "$progname: ignoring unknown tag $tagname" 1>&2 + fi + ;; + esac + ;; + *) + eval "$prev=\$arg" + ;; + esac + + prev= + prevopt= + continue + fi + + # Have we seen a non-optional argument yet? + case $arg in + --help) + show_help=yes + ;; + + --version) + $echo "$PROGRAM (GNU $PACKAGE) $VERSION$TIMESTAMP" + $echo + $echo "Copyright (C) 2005 Free Software Foundation, Inc." + $echo "This is free software; see the source for copying conditions. There is NO" + $echo "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + exit $? + ;; + + --config) + ${SED} -e '1,/^# ### BEGIN LIBTOOL CONFIG/d' -e '/^# ### END LIBTOOL CONFIG/,$d' $progpath + # Now print the configurations for the tags. + for tagname in $taglist; do + ${SED} -n -e "/^# ### BEGIN LIBTOOL TAG CONFIG: $tagname$/,/^# ### END LIBTOOL TAG CONFIG: $tagname$/p" < "$progpath" + done + exit $? + ;; + + --debug) + $echo "$progname: enabling shell trace mode" + set -x + preserve_args="$preserve_args $arg" + ;; + + --dry-run | -n) + run=: + ;; + + --features) + $echo "host: $host" + if test "$build_libtool_libs" = yes; then + $echo "enable shared libraries" + else + $echo "disable shared libraries" + fi + if test "$build_old_libs" = yes; then + $echo "enable static libraries" + else + $echo "disable static libraries" + fi + exit $? + ;; + + --finish) mode="finish" ;; + + --mode) prevopt="--mode" prev=mode ;; + --mode=*) mode="$optarg" ;; + + --preserve-dup-deps) duplicate_deps="yes" ;; + + --quiet | --silent) + show=: + preserve_args="$preserve_args $arg" + ;; + + --tag) + prevopt="--tag" + prev=tag + preserve_args="$preserve_args --tag" + ;; + --tag=*) + set tag "$optarg" ${1+"$@"} + shift + prev=tag + preserve_args="$preserve_args --tag" + ;; + + -dlopen) + prevopt="-dlopen" + prev=execute_dlfiles + ;; + + -*) + $echo "$modename: unrecognized option \`$arg'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + ;; + + *) + nonopt="$arg" + break + ;; + esac +done + +if test -n "$prevopt"; then + $echo "$modename: option \`$prevopt' requires an argument" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE +fi + +case $disable_libs in +no) + ;; +shared) + build_libtool_libs=no + build_old_libs=yes + ;; +static) + build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` + ;; +esac + +# If this variable is set in any of the actions, the command in it +# will be execed at the end. This prevents here-documents from being +# left over by shells. +exec_cmd= + +if test -z "$show_help"; then + + # Infer the operation mode. + if test -z "$mode"; then + $echo "*** Warning: inferring the mode of operation is deprecated." 1>&2 + $echo "*** Future versions of Libtool will require --mode=MODE be specified." 1>&2 + case $nonopt in + *cc | cc* | *++ | gcc* | *-gcc* | g++* | xlc*) + mode=link + for arg + do + case $arg in + -c) + mode=compile + break + ;; + esac + done + ;; + *db | *dbx | *strace | *truss) + mode=execute + ;; + *install*|cp|mv) + mode=install + ;; + *rm) + mode=uninstall + ;; + *) + # If we have no mode, but dlfiles were specified, then do execute mode. + test -n "$execute_dlfiles" && mode=execute + + # Just use the default operation mode. + if test -z "$mode"; then + if test -n "$nonopt"; then + $echo "$modename: warning: cannot infer operation mode from \`$nonopt'" 1>&2 + else + $echo "$modename: warning: cannot infer operation mode without MODE-ARGS" 1>&2 + fi + fi + ;; + esac + fi + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$execute_dlfiles" && test "$mode" != execute; then + $echo "$modename: unrecognized option \`-dlopen'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Change the help message to a mode-specific one. + generic_help="$help" + help="Try \`$modename --help --mode=$mode' for more information." + + # These modes are in order of execution frequency so that they run quickly. + case $mode in + # libtool compile mode + compile) + modename="$modename: compile" + # Get the compilation command and the source file. + base_compile= + srcfile="$nonopt" # always keep a non-empty value in "srcfile" + suppress_opt=yes + suppress_output= + arg_mode=normal + libobj= + later= + + for arg + do + case $arg_mode in + arg ) + # do not "continue". Instead, add this to base_compile + lastarg="$arg" + arg_mode=normal + ;; + + target ) + libobj="$arg" + arg_mode=normal + continue + ;; + + normal ) + # Accept any command-line options. + case $arg in + -o) + if test -n "$libobj" ; then + $echo "$modename: you cannot specify \`-o' more than once" 1>&2 + exit $EXIT_FAILURE + fi + arg_mode=target + continue + ;; + + -static | -prefer-pic | -prefer-non-pic) + later="$later $arg" + continue + ;; + + -no-suppress) + suppress_opt=no + continue + ;; + + -Xcompiler) + arg_mode=arg # the next one goes into the "base_compile" arg list + continue # The current "srcfile" will either be retained or + ;; # replaced later. I would guess that would be a bug. + + -Wc,*) + args=`$echo "X$arg" | $Xsed -e "s/^-Wc,//"` + lastarg= + save_ifs="$IFS"; IFS=',' + for arg in $args; do + IFS="$save_ifs" + + # Double-quote args containing other shell metacharacters. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + lastarg="$lastarg $arg" + done + IFS="$save_ifs" + lastarg=`$echo "X$lastarg" | $Xsed -e "s/^ //"` + + # Add the arguments to base_compile. + base_compile="$base_compile $lastarg" + continue + ;; + + * ) + # Accept the current argument as the source file. + # The previous "srcfile" becomes the current argument. + # + lastarg="$srcfile" + srcfile="$arg" + ;; + esac # case $arg + ;; + esac # case $arg_mode + + # Aesthetically quote the previous argument. + lastarg=`$echo "X$lastarg" | $Xsed -e "$sed_quote_subst"` + + case $lastarg in + # Double-quote args containing other shell metacharacters. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, and some SunOS ksh mistreat backslash-escaping + # in scan sets (worked around with variable expansion), + # and furthermore cannot handle '|' '&' '(' ')' in scan sets + # at all, so we specify them separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + lastarg="\"$lastarg\"" + ;; + esac + + base_compile="$base_compile $lastarg" + done # for arg + + case $arg_mode in + arg) + $echo "$modename: you must specify an argument for -Xcompile" + exit $EXIT_FAILURE + ;; + target) + $echo "$modename: you must specify a target with \`-o'" 1>&2 + exit $EXIT_FAILURE + ;; + *) + # Get the name of the library object. + [ -z "$libobj" ] && libobj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%'` + ;; + esac + + # Recognize several different file suffixes. + # If the user specifies -o file.o, it is replaced with file.lo + xform='[cCFSifmso]' + case $libobj in + *.ada) xform=ada ;; + *.adb) xform=adb ;; + *.ads) xform=ads ;; + *.asm) xform=asm ;; + *.c++) xform=c++ ;; + *.cc) xform=cc ;; + *.ii) xform=ii ;; + *.class) xform=class ;; + *.cpp) xform=cpp ;; + *.cxx) xform=cxx ;; + *.f90) xform=f90 ;; + *.for) xform=for ;; + *.java) xform=java ;; + esac + + libobj=`$echo "X$libobj" | $Xsed -e "s/\.$xform$/.lo/"` + + case $libobj in + *.lo) obj=`$echo "X$libobj" | $Xsed -e "$lo2o"` ;; + *) + $echo "$modename: cannot determine name of library object from \`$libobj'" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + func_infer_tag $base_compile + + for arg in $later; do + case $arg in + -static) + build_old_libs=yes + continue + ;; + + -prefer-pic) + pic_mode=yes + continue + ;; + + -prefer-non-pic) + pic_mode=no + continue + ;; + esac + done + + qlibobj=`$echo "X$libobj" | $Xsed -e "$sed_quote_subst"` + case $qlibobj in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + qlibobj="\"$qlibobj\"" ;; + esac + test "X$libobj" != "X$qlibobj" \ + && $echo "X$libobj" | grep '[]~#^*{};<>?"'"'"' &()|`$[]' \ + && $echo "$modename: libobj name \`$libobj' may not contain shell special characters." + objname=`$echo "X$obj" | $Xsed -e 's%^.*/%%'` + xdir=`$echo "X$obj" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$obj"; then + xdir= + else + xdir=$xdir/ + fi + lobj=${xdir}$objdir/$objname + + if test -z "$base_compile"; then + $echo "$modename: you must specify a compilation command" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Delete any leftover library objects. + if test "$build_old_libs" = yes; then + removelist="$obj $lobj $libobj ${libobj}T" + else + removelist="$lobj $libobj ${libobj}T" + fi + + $run $rm $removelist + trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15 + + # On Cygwin there's no "real" PIC flag so we must build both object types + case $host_os in + cygwin* | mingw* | pw32* | os2*) + pic_mode=default + ;; + esac + if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then + # non-PIC code in shared libraries is not supported + pic_mode=default + fi + + # Calculate the filename of the output object if compiler does + # not support -o with -c + if test "$compiler_c_o" = no; then + output_obj=`$echo "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext} + lockfile="$output_obj.lock" + removelist="$removelist $output_obj $lockfile" + trap "$run $rm $removelist; exit $EXIT_FAILURE" 1 2 15 + else + output_obj= + need_locks=no + lockfile= + fi + + # Lock this critical section if it is needed + # We use this script file to make the link, it avoids creating a new file + if test "$need_locks" = yes; then + until $run ln "$progpath" "$lockfile" 2>/dev/null; do + $show "Waiting for $lockfile to be removed" + sleep 2 + done + elif test "$need_locks" = warn; then + if test -f "$lockfile"; then + $echo "\ +*** ERROR, $lockfile exists and contains: +`cat $lockfile 2>/dev/null` + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit $EXIT_FAILURE + fi + $echo "$srcfile" > "$lockfile" + fi + + if test -n "$fix_srcfile_path"; then + eval srcfile=\"$fix_srcfile_path\" + fi + qsrcfile=`$echo "X$srcfile" | $Xsed -e "$sed_quote_subst"` + case $qsrcfile in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + qsrcfile="\"$qsrcfile\"" ;; + esac + + $run $rm "$libobj" "${libobj}T" + + # Create a libtool object file (analogous to a ".la" file), + # but don't create it if we're doing a dry run. + test -z "$run" && cat > ${libobj}T <<EOF +# $libobj - a libtool object file +# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# Name of the PIC object. +EOF + + # Only build a PIC object if we are building libtool libraries. + if test "$build_libtool_libs" = yes; then + # Without this assignment, base_compile gets emptied. + fbsd_hideous_sh_bug=$base_compile + + if test "$pic_mode" != no; then + command="$base_compile $qsrcfile $pic_flag" + else + # Don't build PIC code + command="$base_compile $qsrcfile" + fi + + if test ! -d "${xdir}$objdir"; then + $show "$mkdir ${xdir}$objdir" + $run $mkdir ${xdir}$objdir + exit_status=$? + if test "$exit_status" -ne 0 && test ! -d "${xdir}$objdir"; then + exit $exit_status + fi + fi + + if test -z "$output_obj"; then + # Place PIC objects in $objdir + command="$command -o $lobj" + fi + + $run $rm "$lobj" "$output_obj" + + $show "$command" + if $run eval "$command"; then : + else + test -n "$output_obj" && $run $rm $removelist + exit $EXIT_FAILURE + fi + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $echo "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed, then go on to compile the next one + if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then + $show "$mv $output_obj $lobj" + if $run $mv $output_obj $lobj; then : + else + error=$? + $run $rm $removelist + exit $error + fi + fi + + # Append the name of the PIC object to the libtool object file. + test -z "$run" && cat >> ${libobj}T <<EOF +pic_object='$objdir/$objname' + +EOF + + # Allow error messages only from the first compilation. + if test "$suppress_opt" = yes; then + suppress_output=' >/dev/null 2>&1' + fi + else + # No PIC object so indicate it doesn't exist in the libtool + # object file. + test -z "$run" && cat >> ${libobj}T <<EOF +pic_object=none + +EOF + fi + + # Only build a position-dependent object if we build old libraries. + if test "$build_old_libs" = yes; then + if test "$pic_mode" != yes; then + # Don't build PIC code + command="$base_compile $qsrcfile" + else + command="$base_compile $qsrcfile $pic_flag" + fi + if test "$compiler_c_o" = yes; then + command="$command -o $obj" + fi + + # Suppress compiler output if we already did a PIC compilation. + command="$command$suppress_output" + $run $rm "$obj" "$output_obj" + $show "$command" + if $run eval "$command"; then : + else + $run $rm $removelist + exit $EXIT_FAILURE + fi + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $echo "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $run $rm $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed + if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then + $show "$mv $output_obj $obj" + if $run $mv $output_obj $obj; then : + else + error=$? + $run $rm $removelist + exit $error + fi + fi + + # Append the name of the non-PIC object the libtool object file. + # Only append if the libtool object file exists. + test -z "$run" && cat >> ${libobj}T <<EOF +# Name of the non-PIC object. +non_pic_object='$objname' + +EOF + else + # Append the name of the non-PIC object the libtool object file. + # Only append if the libtool object file exists. + test -z "$run" && cat >> ${libobj}T <<EOF +# Name of the non-PIC object. +non_pic_object=none + +EOF + fi + + $run $mv "${libobj}T" "${libobj}" + + # Unlock the critical section if it was locked + if test "$need_locks" != no; then + $run $rm "$lockfile" + fi + + exit $EXIT_SUCCESS + ;; + + # libtool link mode + link | relink) + modename="$modename: link" + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + # It is impossible to link a dll without this setting, and + # we shouldn't force the makefile maintainer to figure out + # which system we are compiling for in order to pass an extra + # flag for every libtool invocation. + # allow_undefined=no + + # FIXME: Unfortunately, there are problems with the above when trying + # to make a dll which has undefined symbols, in which case not + # even a static library is built. For now, we need to specify + # -no-undefined on the libtool link line when we can be certain + # that all symbols are satisfied, otherwise we get a static library. + allow_undefined=yes + ;; + *) + allow_undefined=yes + ;; + esac + libtool_args="$nonopt" + base_compile="$nonopt $@" + compile_command="$nonopt" + finalize_command="$nonopt" + + compile_rpath= + finalize_rpath= + compile_shlibpath= + finalize_shlibpath= + convenience= + old_convenience= + deplibs= + old_deplibs= + compiler_flags= + linker_flags= + dllsearchpath= + lib_search_path=`pwd` + inst_prefix_dir= + + avoid_version=no + dlfiles= + dlprefiles= + dlself=no + export_dynamic=no + export_symbols= + export_symbols_regex= + generated= + libobjs= + ltlibs= + module=no + no_install=no + objs= + non_pic_objects= + notinst_path= # paths that contain not-installed libtool libraries + precious_files_regex= + prefer_static_libs=no + preload=no + prev= + prevarg= + release= + rpath= + xrpath= + perm_rpath= + temp_rpath= + thread_safe=no + vinfo= + vinfo_number=no + + func_infer_tag $base_compile + + # We need to know -static, to get the right output filenames. + for arg + do + case $arg in + -all-static | -static) + if test "X$arg" = "X-all-static"; then + if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then + $echo "$modename: warning: complete static linking is impossible in this configuration" 1>&2 + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + else + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=built + fi + build_libtool_libs=no + build_old_libs=yes + break + ;; + esac + done + + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes + + # Go through the arguments, transforming them on the way. + while test "$#" -gt 0; do + arg="$1" + shift + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + qarg=\"`$echo "X$arg" | $Xsed -e "$sed_quote_subst"`\" ### testsuite: skip nested quoting test + ;; + *) qarg=$arg ;; + esac + libtool_args="$libtool_args $qarg" + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + compile_command="$compile_command @OUTPUT@" + finalize_command="$finalize_command @OUTPUT@" + ;; + esac + + case $prev in + dlfiles|dlprefiles) + if test "$preload" = no; then + # Add the symbol object into the linking commands. + compile_command="$compile_command @SYMFILE@" + finalize_command="$finalize_command @SYMFILE@" + preload=yes + fi + case $arg in + *.la | *.lo) ;; # We handle these cases below. + force) + if test "$dlself" = no; then + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + self) + if test "$prev" = dlprefiles; then + dlself=yes + elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then + dlself=yes + else + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + *) + if test "$prev" = dlfiles; then + dlfiles="$dlfiles $arg" + else + dlprefiles="$dlprefiles $arg" + fi + prev= + continue + ;; + esac + ;; + expsyms) + export_symbols="$arg" + if test ! -f "$arg"; then + $echo "$modename: symbol file \`$arg' does not exist" + exit $EXIT_FAILURE + fi + prev= + continue + ;; + expsyms_regex) + export_symbols_regex="$arg" + prev= + continue + ;; + inst_prefix) + inst_prefix_dir="$arg" + prev= + continue + ;; + precious_regex) + precious_files_regex="$arg" + prev= + continue + ;; + release) + release="-$arg" + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat $save_arg` + do +# moreargs="$moreargs $fil" + arg=$fil + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + pic_object= + non_pic_object= + + # Read the .lo file + # If there is no directory component, then add one. + case $arg in + */* | *\\*) . $arg ;; + *) . ./$arg ;; + esac + + if test -z "$pic_object" || \ + test -z "$non_pic_object" || + test "$pic_object" = none && \ + test "$non_pic_object" = none; then + $echo "$modename: cannot find name of object for \`$arg'" 1>&2 + exit $EXIT_FAILURE + fi + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi + + # A PIC object. + libobjs="$libobjs $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + non_pic_objects="$non_pic_objects $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if test -z "$run"; then + $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 + exit $EXIT_FAILURE + else + # Dry-run case. + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` + non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` + libobjs="$libobjs $pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + fi + done + else + $echo "$modename: link input file \`$save_arg' does not exist" + exit $EXIT_FAILURE + fi + arg=$save_arg + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + $echo "$modename: only absolute run-paths are allowed" 1>&2 + exit $EXIT_FAILURE + ;; + esac + if test "$prev" = rpath; then + case "$rpath " in + *" $arg "*) ;; + *) rpath="$rpath $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) xrpath="$xrpath $arg" ;; + esac + fi + prev= + continue + ;; + xcompiler) + compiler_flags="$compiler_flags $qarg" + prev= + compile_command="$compile_command $qarg" + finalize_command="$finalize_command $qarg" + continue + ;; + xlinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $wl$qarg" + prev= + compile_command="$compile_command $wl$qarg" + finalize_command="$finalize_command $wl$qarg" + continue + ;; + xcclinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $qarg" + prev= + compile_command="$compile_command $qarg" + finalize_command="$finalize_command $qarg" + continue + ;; + shrext) + shrext_cmds="$arg" + prev= + continue + ;; + darwin_framework|darwin_framework_skip) + test "$prev" = "darwin_framework" && compiler_flags="$compiler_flags $arg" + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + prev= + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; + esac + fi # test -n "$prev" + + prevarg="$arg" + + case $arg in + -all-static) + if test -n "$link_static_flag"; then + compile_command="$compile_command $link_static_flag" + finalize_command="$finalize_command $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + $echo "$modename: \`-allow-undefined' is deprecated because it is the default" 1>&2 + continue + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -dlopen) + prev=dlfiles + continue + ;; + + -dlpreopen) + prev=dlprefiles + continue + ;; + + -export-dynamic) + export_dynamic=yes + continue + ;; + + -export-symbols | -export-symbols-regex) + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + $echo "$modename: more than one -exported-symbols argument is not allowed" + exit $EXIT_FAILURE + fi + if test "X$arg" = "X-export-symbols"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -framework|-arch|-isysroot) + case " $CC " in + *" ${arg} ${1} "* | *" ${arg} ${1} "*) + prev=darwin_framework_skip ;; + *) compiler_flags="$compiler_flags $arg" + prev=darwin_framework ;; + esac + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + continue + ;; + + -inst-prefix-dir) + prev=inst_prefix + continue + ;; + + # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* + # so, if we see these flags be careful not to treat them like -L + -L[A-Z][A-Z]*:*) + case $with_gcc/$host in + no/*-*-irix* | /*-*-irix*) + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + ;; + esac + continue + ;; + + -L*) + dir=`$echo "X$arg" | $Xsed -e 's/^-L//'` + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + $echo "$modename: cannot determine absolute directory name of \`$dir'" 1>&2 + absdir="$dir" + notinst_path="$notinst_path $dir" + fi + dir="$absdir" + ;; + esac + case "$deplibs " in + *" -L$dir "*) ;; + *) + deplibs="$deplibs -L$dir" + lib_search_path="$lib_search_path $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + testbindir=`$echo "X$dir" | $Xsed -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$dir:"*) ;; + *) dllsearchpath="$dllsearchpath:$dir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + *) dllsearchpath="$dllsearchpath:$testbindir";; + esac + ;; + esac + continue + ;; + + -l*) + if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos*) + # These systems don't actually have a C or math library (as such) + continue + ;; + *-*-os2*) + # These systems don't actually have a C library (as such) + test "X$arg" = "X-lc" && continue + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + test "X$arg" = "X-lc" && continue + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C and math libraries are in the System framework + deplibs="$deplibs -framework System" + continue + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + test "X$arg" = "X-lc" && continue + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + test "X$arg" = "X-lc" && continue + ;; + esac + elif test "X$arg" = "X-lc_r"; then + case $host in + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc_r directly, use -pthread flag. + continue + ;; + esac + fi + deplibs="$deplibs $arg" + continue + ;; + + # Tru64 UNIX uses -model [arg] to determine the layout of C++ + # classes, name mangling, and exception handling. + -model) + compile_command="$compile_command $arg" + compiler_flags="$compiler_flags $arg" + finalize_command="$finalize_command $arg" + prev=xcompiler + continue + ;; + + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) + compiler_flags="$compiler_flags $arg" + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + continue + ;; + + -module) + module=yes + continue + ;; + + # -64, -mips[0-9] enable 64-bit mode on the SGI compiler + # -r[0-9][0-9]* specifies the processor on the SGI compiler + # -xarch=*, -xtarget=* enable 64-bit mode on the Sun compiler + # +DA*, +DD* enable 64-bit mode on the HP compiler + # -q* pass through compiler args for the IBM compiler + # -m* pass through architecture-specific compiler args for GCC + # -m*, -t[45]*, -txscale* pass through architecture-specific + # compiler args for GCC + # -pg pass through profiling flag for GCC + # @file GCC response files + -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*|-pg| \ + -t[45]*|-txscale*|@*) + + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + compiler_flags="$compiler_flags $arg" + continue + ;; + + -shrext) + prev=shrext + continue + ;; + + -no-fast-install) + fast_install=no + continue + ;; + + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + # The PATH hackery in wrapper scripts is required on Windows + # in order for the loader to find any dlls it needs. + $echo "$modename: warning: \`-no-install' is ignored for $host" 1>&2 + $echo "$modename: warning: assuming \`-no-fast-install' instead" 1>&2 + fast_install=no + ;; + *) no_install=yes ;; + esac + continue + ;; + + -no-undefined) + allow_undefined=no + continue + ;; + + -objectlist) + prev=objectlist + continue + ;; + + -o) prev=output ;; + + -precious-files-regex) + prev=precious_regex + continue + ;; + + -release) + prev=release + continue + ;; + + -rpath) + prev=rpath + continue + ;; + + -R) + prev=xrpath + continue + ;; + + -R*) + dir=`$echo "X$arg" | $Xsed -e 's/^-R//'` + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + $echo "$modename: only absolute run-paths are allowed" 1>&2 + exit $EXIT_FAILURE + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + continue + ;; + + -static) + # The effects of -static are defined in a previous loop. + # We used to do the same as -all-static on platforms that + # didn't have a PIC flag, but the assumption that the effects + # would be equivalent was wrong. It would break on at least + # Digital Unix and AIX. + continue + ;; + + -thread-safe) + thread_safe=yes + continue + ;; + + -version-info) + prev=vinfo + continue + ;; + -version-number) + prev=vinfo + vinfo_number=yes + continue + ;; + + -Wc,*) + args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wc,//'` + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + case $flag in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + flag="\"$flag\"" + ;; + esac + arg="$arg $wl$flag" + compiler_flags="$compiler_flags $flag" + done + IFS="$save_ifs" + arg=`$echo "X$arg" | $Xsed -e "s/^ //"` + ;; + + -Wl,*) + args=`$echo "X$arg" | $Xsed -e "$sed_quote_subst" -e 's/^-Wl,//'` + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + case $flag in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + flag="\"$flag\"" + ;; + esac + arg="$arg $wl$flag" + compiler_flags="$compiler_flags $wl$flag" + linker_flags="$linker_flags $flag" + done + IFS="$save_ifs" + arg=`$echo "X$arg" | $Xsed -e "s/^ //"` + ;; + + -Xcompiler) + prev=xcompiler + continue + ;; + + -Xlinker) + prev=xlinker + continue + ;; + + -XCClinker) + prev=xcclinker + continue + ;; + + # Some other compiler flag. + -* | +*) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + ;; + + *.$objext) + # A standard object. + objs="$objs $arg" + ;; + + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if (${SED} -e '2q' $arg | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + pic_object= + non_pic_object= + + # Read the .lo file + # If there is no directory component, then add one. + case $arg in + */* | *\\*) . $arg ;; + *) . ./$arg ;; + esac + + if test -z "$pic_object" || \ + test -z "$non_pic_object" || + test "$pic_object" = none && \ + test "$non_pic_object" = none; then + $echo "$modename: cannot find name of object for \`$arg'" 1>&2 + exit $EXIT_FAILURE + fi + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi + + # A PIC object. + libobjs="$libobjs $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + non_pic_objects="$non_pic_objects $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if test -z "$run"; then + $echo "$modename: \`$arg' is not a valid libtool object" 1>&2 + exit $EXIT_FAILURE + else + # Dry-run case. + + # Extract subdirectory from the argument. + xdir=`$echo "X$arg" | $Xsed -e 's%/[^/]*$%%'` + if test "X$xdir" = "X$arg"; then + xdir= + else + xdir="$xdir/" + fi + + pic_object=`$echo "X${xdir}${objdir}/${arg}" | $Xsed -e "$lo2o"` + non_pic_object=`$echo "X${xdir}${arg}" | $Xsed -e "$lo2o"` + libobjs="$libobjs $pic_object" + non_pic_objects="$non_pic_objects $non_pic_object" + fi + fi + ;; + + *.$libext) + # An archive. + deplibs="$deplibs $arg" + old_deplibs="$old_deplibs $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + if test "$prev" = dlfiles; then + # This library was specified with -dlopen. + dlfiles="$dlfiles $arg" + prev= + elif test "$prev" = dlprefiles; then + # The library was specified with -dlpreopen. + dlprefiles="$dlprefiles $arg" + prev= + else + deplibs="$deplibs $arg" + fi + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + fi + done # argument parsing loop + + if test -n "$prev"; then + $echo "$modename: the \`$prevarg' option requires an argument" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + compile_command="$compile_command $arg" + finalize_command="$finalize_command $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + outputname=`$echo "X$output" | $Xsed -e 's%^.*/%%'` + libobjs_save="$libobjs" + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$echo \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\` + else + shlib_search_path= + fi + eval sys_lib_search_path=\"$sys_lib_search_path_spec\" + eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" + + output_objdir=`$echo "X$output" | $Xsed -e 's%/[^/]*$%%'` + if test "X$output_objdir" = "X$output"; then + output_objdir="$objdir" + else + output_objdir="$output_objdir/$objdir" + fi + # Create the object directory. + if test ! -d "$output_objdir"; then + $show "$mkdir $output_objdir" + $run $mkdir $output_objdir + exit_status=$? + if test "$exit_status" -ne 0 && test ! -d "$output_objdir"; then + exit $exit_status + fi + fi + + # Determine the type of output + case $output in + "") + $echo "$modename: you must specify an output file" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + esac + + case $host in + *cygwin* | *mingw* | *pw32*) + # don't eliminate duplications in $postdeps and $predeps + duplicate_compiler_generated_deps=yes + ;; + *) + duplicate_compiler_generated_deps=$duplicate_deps + ;; + esac + specialdeplibs= + + libs= + # Find all interdependent deplibs by searching for libraries + # that are linked more than once (e.g. -la -lb -la) + for deplib in $deplibs; do + if test "X$duplicate_deps" = "Xyes" ; then + case "$libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + libs="$libs $deplib" + done + + if test "$linkmode" = lib; then + libs="$predeps $libs $compiler_lib_search_path $postdeps" + + # Compute libraries that are listed more than once in $predeps + # $postdeps and mark them as special (i.e., whose duplicates are + # not to be eliminated). + pre_post_deps= + if test "X$duplicate_compiler_generated_deps" = "Xyes" ; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;; + esac + pre_post_deps="$pre_post_deps $pre_post_dep" + done + fi + pre_post_deps= + fi + + deplibs= + newdependency_libs= + newlib_search_path= + need_relink=no # whether we're linking any uninstalled libtool libraries + notinst_deplibs= # not-installed libtool libraries + case $linkmode in + lib) + passes="conv link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + $echo "$modename: libraries can \`-dlopen' only libtool libraries: $file" 1>&2 + exit $EXIT_FAILURE + ;; + esac + done + ;; + prog) + compile_deplibs= + finalize_deplibs= + alldeplibs=no + newdlfiles= + newdlprefiles= + passes="conv scan dlopen dlpreopen link" + ;; + *) passes="conv" + ;; + esac + for pass in $passes; do + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan"; then + libs="$deplibs" + deplibs= + fi + if test "$linkmode" = prog; then + case $pass in + dlopen) libs="$dlfiles" ;; + dlpreopen) libs="$dlprefiles" ;; + link) + libs="$deplibs %DEPLIBS%" + test "X$link_all_deplibs" != Xno && libs="$libs $dependency_libs" + ;; + esac + fi + if test "$pass" = dlopen; then + # Collect dlpreopened libraries + save_deplibs="$deplibs" + deplibs= + fi + for deplib in $libs; do + lib= + found=no + case $deplib in + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + compiler_flags="$compiler_flags $deplib" + fi + continue + ;; + -l*) + if test "$linkmode" != lib && test "$linkmode" != prog; then + $echo "$modename: warning: \`-l' is ignored for archives/objects" 1>&2 + continue + fi + name=`$echo "X$deplib" | $Xsed -e 's/^-l//'` + for searchdir in $newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path; do + for search_ext in .la $std_shrext .so .a; do + # Search the libtool library + lib="$searchdir/lib${name}${search_ext}" + if test -f "$lib"; then + if test "$search_ext" = ".la"; then + found=yes + else + found=no + fi + break 2 + fi + done + done + if test "$found" != yes; then + # deplib doesn't seem to be a libtool library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + else # deplib is a libtool library + # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, + # We need to do some special things here, and not later. + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $deplib "*) + if (${SED} -e '2q' $lib | + grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + library_names= + old_library= + case $lib in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + for l in $old_library $library_names; do + ll="$l" + done + if test "X$ll" = "X$old_library" ; then # only static version available + found=no + ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` + test "X$ladir" = "X$lib" && ladir="." + lib=$ladir/$old_library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + fi + ;; + *) ;; + esac + fi + fi + ;; # -l + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test "$pass" = conv && continue + newdependency_libs="$deplib $newdependency_libs" + newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` + ;; + prog) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + if test "$pass" = scan; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'` + ;; + *) + $echo "$modename: warning: \`-L' is ignored for archives/objects" 1>&2 + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test "$pass" = link; then + dir=`$echo "X$deplib" | $Xsed -e 's/^-R//'` + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) lib="$deplib" ;; + *.$libext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + valid_a_lib=no + case $deplibs_check_method in + match_pattern*) + set dummy $deplibs_check_method + match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` + if eval $echo \"$deplib\" 2>/dev/null \ + | $SED 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + valid_a_lib=yes + fi + ;; + pass_all) + valid_a_lib=yes + ;; + esac + if test "$valid_a_lib" != yes; then + $echo + $echo "*** Warning: Trying to link with static lib archive $deplib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have" + $echo "*** because the file extensions .$libext of this argument makes me believe" + $echo "*** that it is just a static archive that I should not used here." + else + $echo + $echo "*** Warning: Linking the shared library $output against the" + $echo "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + fi + continue + ;; + prog) + if test "$pass" != link; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + continue + ;; + esac # linkmode + ;; # *.$libext + *.lo | *.$objext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + elif test "$linkmode" = prog; then + if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then + # If there is no dlopen support or we're linking statically, + # we need to preload. + newdlprefiles="$newdlprefiles $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + newdlfiles="$newdlfiles $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=yes + continue + ;; + esac # case $deplib + if test "$found" = yes || test -f "$lib"; then : + else + $echo "$modename: cannot find the library \`$lib' or unhandled argument \`$deplib'" 1>&2 + exit $EXIT_FAILURE + fi + + # Check to see that this really is a libtool archive. + if (${SED} -e '2q' $lib | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + exit $EXIT_FAILURE + fi + + ladir=`$echo "X$lib" | $Xsed -e 's%/[^/]*$%%'` + test "X$ladir" = "X$lib" && ladir="." + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + avoidtemprpath= + + + # Read the .la file + case $lib in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan" || + { test "$linkmode" != prog && test "$linkmode" != lib; }; then + test -n "$dlopen" && dlfiles="$dlfiles $dlopen" + test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen" + fi + + if test "$pass" = conv; then + # Only check for convenience libraries + deplibs="$lib $deplibs" + if test -z "$libdir"; then + if test -z "$old_library"; then + $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 + exit $EXIT_FAILURE + fi + # It is a libtool convenience library, so add in its objects. + convenience="$convenience $ladir/$objdir/$old_library" + old_convenience="$old_convenience $ladir/$objdir/$old_library" + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if test "X$duplicate_deps" = "Xyes" ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done + elif test "$linkmode" != prog && test "$linkmode" != lib; then + $echo "$modename: \`$lib' is not a convenience library" 1>&2 + exit $EXIT_FAILURE + fi + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + for l in $old_library $library_names; do + linklib="$l" + done + if test -z "$linklib"; then + $echo "$modename: cannot find name of link library for \`$lib'" 1>&2 + exit $EXIT_FAILURE + fi + + # This library was specified with -dlopen. + if test "$pass" = dlopen; then + if test -z "$libdir"; then + $echo "$modename: cannot -dlopen a convenience library: \`$lib'" 1>&2 + exit $EXIT_FAILURE + fi + if test -z "$dlname" || + test "$dlopen_support" != yes || + test "$build_libtool_libs" = no; then + # If there is no dlname, no dlopen support or we're linking + # statically, we need to preload. We also need to preload any + # dependent libraries so libltdl's deplib preloader doesn't + # bomb out in the load deplibs phase. + dlprefiles="$dlprefiles $lib $dependency_libs" + else + newdlfiles="$newdlfiles $lib" + fi + continue + fi # $pass = dlopen + + # We need an absolute path. + case $ladir in + [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; + *) + abs_ladir=`cd "$ladir" && pwd` + if test -z "$abs_ladir"; then + $echo "$modename: warning: cannot determine absolute directory name of \`$ladir'" 1>&2 + $echo "$modename: passing it literally to the linker, although it might fail" 1>&2 + abs_ladir="$ladir" + fi + ;; + esac + laname=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` + + # Find the relevant object directory and library name. + if test "X$installed" = Xyes; then + if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + $echo "$modename: warning: library \`$lib' was moved." 1>&2 + dir="$ladir" + absdir="$abs_ladir" + libdir="$abs_ladir" + else + dir="$libdir" + absdir="$libdir" + fi + test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes + else + if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then + dir="$ladir" + absdir="$abs_ladir" + # Remove this search path later + notinst_path="$notinst_path $abs_ladir" + else + dir="$ladir/$objdir" + absdir="$abs_ladir/$objdir" + # Remove this search path later + notinst_path="$notinst_path $abs_ladir" + fi + fi # $installed = yes + name=`$echo "X$laname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` + + # This library was specified with -dlpreopen. + if test "$pass" = dlpreopen; then + if test -z "$libdir"; then + $echo "$modename: cannot -dlpreopen a convenience library: \`$lib'" 1>&2 + exit $EXIT_FAILURE + fi + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + newdlprefiles="$newdlprefiles $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + newdlprefiles="$newdlprefiles $dir/$dlname" + else + newdlprefiles="$newdlprefiles $dir/$linklib" + fi + fi # $pass = dlpreopen + + if test -z "$libdir"; then + # Link the convenience library + if test "$linkmode" = lib; then + deplibs="$dir/$old_library $deplibs" + elif test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$dir/$old_library $compile_deplibs" + finalize_deplibs="$dir/$old_library $finalize_deplibs" + else + deplibs="$lib $deplibs" # used for prog,scan pass + fi + continue + fi + + + if test "$linkmode" = prog && test "$pass" != link; then + newlib_search_path="$newlib_search_path $ladir" + deplibs="$lib $deplibs" + + linkalldeplibs=no + if test "$link_all_deplibs" != no || test -z "$library_names" || + test "$build_libtool_libs" = no; then + linkalldeplibs=yes + fi + + tmp_libs= + for deplib in $dependency_libs; do + case $deplib in + -L*) newlib_search_path="$newlib_search_path "`$echo "X$deplib" | $Xsed -e 's/^-L//'`;; ### testsuite: skip nested quoting test + esac + # Need to link against all dependency_libs? + if test "$linkalldeplibs" = yes; then + deplibs="$deplib $deplibs" + else + # Need to hardcode shared library paths + # or/and link against static libraries + newdependency_libs="$deplib $newdependency_libs" + fi + if test "X$duplicate_deps" = "Xyes" ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done # for deplib + continue + fi # $linkmode = prog... + + if test "$linkmode,$pass" = "prog,link"; then + if test -n "$library_names" && + { test "$prefer_static_libs" = no || test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then + # Make sure the rpath contains only unique directories. + case "$temp_rpath " in + *" $dir "*) ;; + *" $absdir "*) ;; + *) temp_rpath="$temp_rpath $absdir" ;; + esac + fi + + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi # $linkmode,$pass = prog,link... + + if test "$alldeplibs" = yes && + { test "$deplibs_check_method" = pass_all || + { test "$build_libtool_libs" = yes && + test -n "$library_names"; }; }; then + # We only need to search for static libraries + continue + fi + fi + + link_static=no # Whether the deplib will be linked statically + use_static_libs=$prefer_static_libs + if test "$use_static_libs" = built && test "$installed" = yes ; then + use_static_libs=no + fi + if test -n "$library_names" && + { test "$use_static_libs" = no || test -z "$old_library"; }; then + if test "$installed" = no; then + notinst_deplibs="$notinst_deplibs $lib" + need_relink=yes + fi + # This is a shared library + + # Warn about portability, can't link against -module's on + # some systems (darwin) + if test "$shouldnotlink" = yes && test "$pass" = link ; then + $echo + if test "$linkmode" = prog; then + $echo "*** Warning: Linking the executable $output against the loadable module" + else + $echo "*** Warning: Linking the shared library $output against the loadable module" + fi + $echo "*** $linklib is not portable!" + fi + if test "$linkmode" = lib && + test "$hardcode_into_libs" = yes; then + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + realname="$2" + shift; shift + libname=`eval \\$echo \"$libname_spec\"` + # use dlname if we got it. it's perfectly good, no? + if test -n "$dlname"; then + soname="$dlname" + elif test -n "$soname_spec"; then + # bleh windows + case $host in + *cygwin* | mingw*) + major=`expr $current - $age` + versuffix="-$major" + ;; + esac + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot="$soname" + soname=`$echo $soroot | ${SED} -e 's/^.*\///'` + newlib="libimp-`$echo $soname | ${SED} 's/^lib//;s/\.dll$//'`.a" + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + $show "extracting exported symbol list from \`$soname'" + save_ifs="$IFS"; IFS='~' + cmds=$extract_expsyms_cmds + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + $show "generating import library for \`$soname'" + save_ifs="$IFS"; IFS='~' + cmds=$old_archive_from_expsyms_cmds + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + # make sure the library variables are pointing to the new library + dir=$output_objdir + linklib=$newlib + fi # test -n "$old_archive_from_expsyms_cmds" + + if test "$linkmode" = prog || test "$mode" != relink; then + add_shlibpath= + add_dir= + add= + lib_linked=yes + case $hardcode_action in + immediate | unsupported) + if test "$hardcode_direct" = no; then + add="$dir/$linklib" + case $host in + *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; + *-*-sysv4*uw2*) add_dir="-L$dir" ;; + *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ + *-*-unixware7*) add_dir="-L$dir" ;; + *-*-darwin* ) + # if the lib is a module then we can not link against + # it, someone is ignoring the new warnings I added + if /usr/bin/file -L $add 2> /dev/null | + $EGREP ": [^:]* bundle" >/dev/null ; then + $echo "** Warning, lib $linklib is a module, not a shared library" + if test -z "$old_library" ; then + $echo + $echo "** And there doesn't seem to be a static archive available" + $echo "** The link will probably fail, sorry" + else + add="$dir/$old_library" + fi + fi + esac + elif test "$hardcode_minus_L" = no; then + case $host in + *-*-sunos*) add_shlibpath="$dir" ;; + esac + add_dir="-L$dir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = no; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + relink) + if test "$hardcode_direct" = yes; then + add="$dir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$dir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + *) lib_linked=no ;; + esac + + if test "$lib_linked" != yes; then + $echo "$modename: configuration error: unsupported hardcode properties" + exit $EXIT_FAILURE + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_shlibpath:"*) ;; + *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;; + esac + fi + if test "$linkmode" = prog; then + test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" + test -n "$add" && compile_deplibs="$add $compile_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + if test "$hardcode_direct" != yes && \ + test "$hardcode_minus_L" != yes && \ + test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + fi + fi + fi + + if test "$linkmode" = prog || test "$mode" = relink; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test "$hardcode_direct" = yes; then + add="$libdir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$libdir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + add="-l$name" + elif test "$hardcode_automatic" = yes; then + if test -n "$inst_prefix_dir" && + test -f "$inst_prefix_dir$libdir/$linklib" ; then + add="$inst_prefix_dir$libdir/$linklib" + else + add="$libdir/$linklib" + fi + else + # We cannot seem to hardcode it, guess we'll fake it. + add_dir="-L$libdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + fi + + if test "$linkmode" = prog; then + test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" + test -n "$add" && finalize_deplibs="$add $finalize_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + fi + fi + elif test "$linkmode" = prog; then + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test "$hardcode_direct" != unsupported; then + test -n "$old_library" && linklib="$old_library" + compile_deplibs="$dir/$linklib $compile_deplibs" + finalize_deplibs="$dir/$linklib $finalize_deplibs" + else + compile_deplibs="-l$name -L$dir $compile_deplibs" + finalize_deplibs="-l$name -L$dir $finalize_deplibs" + fi + elif test "$build_libtool_libs" = yes; then + # Not a shared library + if test "$deplibs_check_method" != pass_all; then + # We're trying link a shared library against a static one + # but the system doesn't support it. + + # Just print a warning and add the library to dependency_libs so + # that the program can be linked against the static library. + $echo + $echo "*** Warning: This system can not link to static lib archive $lib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have." + if test "$module" = yes; then + $echo "*** But as you try to build a module library, libtool will still create " + $echo "*** a static module, that should work as long as the dlopening application" + $echo "*** is linked with the -dlopen flag to resolve symbols at runtime." + if test -z "$global_symbol_pipe"; then + $echo + $echo "*** However, this would only work if libtool was able to extract symbol" + $echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + $echo "*** not find such a program. So, this module is probably useless." + $echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + else + deplibs="$dir/$old_library $deplibs" + link_static=yes + fi + fi # link shared/static library? + + if test "$linkmode" = lib; then + if test -n "$dependency_libs" && + { test "$hardcode_into_libs" != yes || + test "$build_old_libs" = yes || + test "$link_static" = yes; }; then + # Extract -R from dependency_libs + temp_deplibs= + for libdir in $dependency_libs; do + case $libdir in + -R*) temp_xrpath=`$echo "X$libdir" | $Xsed -e 's/^-R//'` + case " $xrpath " in + *" $temp_xrpath "*) ;; + *) xrpath="$xrpath $temp_xrpath";; + esac;; + *) temp_deplibs="$temp_deplibs $libdir";; + esac + done + dependency_libs="$temp_deplibs" + fi + + newlib_search_path="$newlib_search_path $absdir" + # Link against this library + test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" + # ... and its dependency_libs + tmp_libs= + for deplib in $dependency_libs; do + newdependency_libs="$deplib $newdependency_libs" + if test "X$duplicate_deps" = "Xyes" ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done + + if test "$link_all_deplibs" != no; then + # Add the search paths of all dependency libraries + for deplib in $dependency_libs; do + case $deplib in + -L*) path="$deplib" ;; + *.la) + dir=`$echo "X$deplib" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$deplib" && dir="." + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + $echo "$modename: warning: cannot determine absolute directory name of \`$dir'" 1>&2 + absdir="$dir" + fi + ;; + esac + if grep "^installed=no" $deplib > /dev/null; then + path="$absdir/$objdir" + else + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + if test -z "$libdir"; then + $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 + exit $EXIT_FAILURE + fi + if test "$absdir" != "$libdir"; then + $echo "$modename: warning: \`$deplib' seems to be moved" 1>&2 + fi + path="$absdir" + fi + depdepl= + case $host in + *-*-darwin*) + # we do not want to link against static libs, + # but need to link against shared + eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` + if test -n "$deplibrary_names" ; then + for tmp in $deplibrary_names ; do + depdepl=$tmp + done + if test -f "$path/$depdepl" ; then + depdepl="$path/$depdepl" + fi + # do not add paths which are already there + case " $newlib_search_path " in + *" $path "*) ;; + *) newlib_search_path="$newlib_search_path $path";; + esac + fi + path="" + ;; + *) + path="-L$path" + ;; + esac + ;; + -l*) + case $host in + *-*-darwin*) + # Again, we only want to link against shared libraries + eval tmp_libs=`$echo "X$deplib" | $Xsed -e "s,^\-l,,"` + for tmp in $newlib_search_path ; do + if test -f "$tmp/lib$tmp_libs.dylib" ; then + eval depdepl="$tmp/lib$tmp_libs.dylib" + break + fi + done + path="" + ;; + *) continue ;; + esac + ;; + *) continue ;; + esac + case " $deplibs " in + *" $path "*) ;; + *) deplibs="$path $deplibs" ;; + esac + case " $deplibs " in + *" $depdepl "*) ;; + *) deplibs="$depdepl $deplibs" ;; + esac + done + fi # link_all_deplibs != no + fi # linkmode = lib + done # for deplib in $libs + dependency_libs="$newdependency_libs" + if test "$pass" = dlpreopen; then + # Link the dlpreopened libraries before other libraries + for deplib in $save_deplibs; do + deplibs="$deplib $deplibs" + done + fi + if test "$pass" != dlopen; then + if test "$pass" != conv; then + # Make sure lib_search_path contains only unique directories. + lib_search_path= + for dir in $newlib_search_path; do + case "$lib_search_path " in + *" $dir "*) ;; + *) lib_search_path="$lib_search_path $dir" ;; + esac + done + newlib_search_path= + fi + + if test "$linkmode,$pass" != "prog,link"; then + vars="deplibs" + else + vars="compile_deplibs finalize_deplibs" + fi + for var in $vars dependency_libs; do + # Add libraries to $var in reverse order + eval tmp_libs=\"\$$var\" + new_libs= + for deplib in $tmp_libs; do + # FIXME: Pedantically, this is the right thing to do, so + # that some nasty dependency loop isn't accidentally + # broken: + #new_libs="$deplib $new_libs" + # Pragmatically, this seems to cause very few problems in + # practice: + case $deplib in + -L*) new_libs="$deplib $new_libs" ;; + -R*) ;; + *) + # And here is the reason: when a library appears more + # than once as an explicit dependence of a library, or + # is implicitly linked in more than once by the + # compiler, it is considered special, and multiple + # occurrences thereof are not removed. Compare this + # with having the same library being listed as a + # dependency of multiple other libraries: in this case, + # we know (pedantically, we assume) the library does not + # need to be listed more than once, so we keep only the + # last copy. This is not always right, but it is rare + # enough that we require users that really mean to play + # such unportable linking tricks to link the library + # using -Wl,-lname, so that libtool does not consider it + # for duplicate removal. + case " $specialdeplibs " in + *" $deplib "*) new_libs="$deplib $new_libs" ;; + *) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$deplib $new_libs" ;; + esac + ;; + esac + ;; + esac + done + tmp_libs= + for deplib in $new_libs; do + case $deplib in + -L*) + case " $tmp_libs " in + *" $deplib "*) ;; + *) tmp_libs="$tmp_libs $deplib" ;; + esac + ;; + *) tmp_libs="$tmp_libs $deplib" ;; + esac + done + eval $var=\"$tmp_libs\" + done # for var + fi + # Last step: remove runtime libs from dependency_libs + # (they stay in deplibs) + tmp_libs= + for i in $dependency_libs ; do + case " $predeps $postdeps $compiler_lib_search_path " in + *" $i "*) + i="" + ;; + esac + if test -n "$i" ; then + tmp_libs="$tmp_libs $i" + fi + done + dependency_libs=$tmp_libs + done # for pass + if test "$linkmode" = prog; then + dlfiles="$newdlfiles" + dlprefiles="$newdlprefiles" + fi + + case $linkmode in + oldlib) + if test -n "$deplibs"; then + $echo "$modename: warning: \`-l' and \`-L' are ignored for archives" 1>&2 + fi + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen' is ignored for archives" 1>&2 + fi + + if test -n "$rpath"; then + $echo "$modename: warning: \`-rpath' is ignored for archives" 1>&2 + fi + + if test -n "$xrpath"; then + $echo "$modename: warning: \`-R' is ignored for archives" 1>&2 + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info/-version-number' is ignored for archives" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for archives" 1>&2 + fi + + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + $echo "$modename: warning: \`-export-symbols' is ignored for archives" 1>&2 + fi + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs="$output" + objs="$objs$old_deplibs" + ;; + + lib) + # Make sure we only generate libraries of the form `libNAME.la'. + case $outputname in + lib*) + name=`$echo "X$outputname" | $Xsed -e 's/\.la$//' -e 's/^lib//'` + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + ;; + *) + if test "$module" = no; then + $echo "$modename: libtool library \`$output' must begin with \`lib'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + if test "$need_lib_prefix" != no; then + # Add the "lib" prefix for modules if required + name=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + else + libname=`$echo "X$outputname" | $Xsed -e 's/\.la$//'` + fi + ;; + esac + + if test -n "$objs"; then + if test "$deplibs_check_method" != pass_all; then + $echo "$modename: cannot build libtool library \`$output' from non-libtool objects on this host:$objs" 2>&1 + exit $EXIT_FAILURE + else + $echo + $echo "*** Warning: Linking the shared library $output against the non-libtool" + $echo "*** objects $objs is not portable!" + libobjs="$libobjs $objs" + fi + fi + + if test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen self' is ignored for libtool libraries" 1>&2 + fi + + set dummy $rpath + if test "$#" -gt 2; then + $echo "$modename: warning: ignoring multiple \`-rpath's for a libtool library" 1>&2 + fi + install_libdir="$2" + + oldlibs= + if test -z "$rpath"; then + if test "$build_libtool_libs" = yes; then + # Building a libtool convenience library. + # Some compilers have problems with a `.al' extension so + # convenience libraries should have the same extension an + # archive normally would. + oldlibs="$output_objdir/$libname.$libext $oldlibs" + build_libtool_libs=convenience + build_old_libs=yes + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info/-version-number' is ignored for convenience libraries" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for convenience libraries" 1>&2 + fi + else + + # Parse the version information argument. + save_ifs="$IFS"; IFS=':' + set dummy $vinfo 0 0 0 + IFS="$save_ifs" + + if test -n "$8"; then + $echo "$modename: too many parameters to \`-version-info'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # convert absolute version numbers to libtool ages + # this retains compatibility with .la files and attempts + # to make the code below a bit more comprehensible + + case $vinfo_number in + yes) + number_major="$2" + number_minor="$3" + number_revision="$4" + # + # There are really only two kinds -- those that + # use the current revision as the major version + # and those that subtract age and use age as + # a minor version. But, then there is irix + # which has an extra 1 added just for fun + # + case $version_type in + darwin|linux|osf|windows) + current=`expr $number_major + $number_minor` + age="$number_minor" + revision="$number_revision" + ;; + freebsd-aout|freebsd-elf|sunos) + current="$number_major" + revision="$number_minor" + age="0" + ;; + irix|nonstopux) + current=`expr $number_major + $number_minor - 1` + age="$number_minor" + revision="$number_minor" + ;; + *) + $echo "$modename: unknown library version type \`$version_type'" 1>&2 + $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit $EXIT_FAILURE + ;; + esac + ;; + no) + current="$2" + revision="$3" + age="$4" + ;; + esac + + # Check that each of the things are valid numbers. + case $current in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + $echo "$modename: CURRENT \`$current' must be a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + case $revision in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + $echo "$modename: REVISION \`$revision' must be a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + case $age in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + $echo "$modename: AGE \`$age' must be a nonnegative integer" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + if test "$age" -gt "$current"; then + $echo "$modename: AGE \`$age' is greater than the current interface number \`$current'" 1>&2 + $echo "$modename: \`$vinfo' is not valid version information" 1>&2 + exit $EXIT_FAILURE + fi + + # Calculate the version variables. + major= + versuffix= + verstring= + case $version_type in + none) ;; + + darwin) + # Like Linux, but with the current version available in + # verstring for coding it into the library header + major=.`expr $current - $age` + versuffix="$major.$age.$revision" + # Darwin ld doesn't like 0 for these options... + minor_current=`expr $current + 1` + verstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" + ;; + + freebsd-aout) + major=".$current" + versuffix=".$current.$revision"; + ;; + + freebsd-elf) + major=".$current" + versuffix=".$current"; + ;; + + irix | nonstopux) + major=`expr $current - $age + 1` + + case $version_type in + nonstopux) verstring_prefix=nonstopux ;; + *) verstring_prefix=sgi ;; + esac + verstring="$verstring_prefix$major.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$revision + while test "$loop" -ne 0; do + iface=`expr $revision - $loop` + loop=`expr $loop - 1` + verstring="$verstring_prefix$major.$iface:$verstring" + done + + # Before this point, $major must not contain `.'. + major=.$major + versuffix="$major.$revision" + ;; + + linux) + major=.`expr $current - $age` + versuffix="$major.$age.$revision" + ;; + + osf) + major=.`expr $current - $age` + versuffix=".$current.$age.$revision" + verstring="$current.$age.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$age + while test "$loop" -ne 0; do + iface=`expr $current - $loop` + loop=`expr $loop - 1` + verstring="$verstring:${iface}.0" + done + + # Make executables depend on our current version. + verstring="$verstring:${current}.0" + ;; + + sunos) + major=".$current" + versuffix=".$current.$revision" + ;; + + windows) + # Use '-' rather than '.', since we only want one + # extension on DOS 8.3 filesystems. + major=`expr $current - $age` + versuffix="-$major" + ;; + + *) + $echo "$modename: unknown library version type \`$version_type'" 1>&2 + $echo "Fatal configuration error. See the $PACKAGE docs for more information." 1>&2 + exit $EXIT_FAILURE + ;; + esac + + # Clear the version info if we defaulted, and they specified a release. + if test -z "$vinfo" && test -n "$release"; then + major= + case $version_type in + darwin) + # we can't check for "0.0" in archive_cmds due to quoting + # problems, so we reset it completely + verstring= + ;; + *) + verstring="0.0" + ;; + esac + if test "$need_version" = no; then + versuffix= + else + versuffix=".0.0" + fi + fi + + # Remove version info from name if versioning should be avoided + if test "$avoid_version" = yes && test "$need_version" = no; then + major= + versuffix= + verstring="" + fi + + # Check to see if the archive will have undefined symbols. + if test "$allow_undefined" = yes; then + if test "$allow_undefined_flag" = unsupported; then + $echo "$modename: warning: undefined symbols not allowed in $host shared libraries" 1>&2 + build_libtool_libs=no + build_old_libs=yes + fi + else + # Don't allow undefined symbols. + allow_undefined_flag="$no_undefined_flag" + fi + fi + + if test "$mode" != relink; then + # Remove our outputs, but don't remove object files since they + # may have been created when compiling PIC objects. + removelist= + tempremovelist=`$echo "$output_objdir/*"` + for p in $tempremovelist; do + case $p in + *.$objext) + ;; + $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) + if test "X$precious_files_regex" != "X"; then + if echo $p | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 + then + continue + fi + fi + removelist="$removelist $p" + ;; + *) ;; + esac + done + if test -n "$removelist"; then + $show "${rm}r $removelist" + $run ${rm}r $removelist + fi + fi + + # Now set the variables for building old libraries. + if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then + oldlibs="$oldlibs $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP` + fi + + # Eliminate all temporary directories. + for path in $notinst_path; do + lib_search_path=`$echo "$lib_search_path " | ${SED} -e "s% $path % %g"` + deplibs=`$echo "$deplibs " | ${SED} -e "s% -L$path % %g"` + dependency_libs=`$echo "$dependency_libs " | ${SED} -e "s% -L$path % %g"` + done + + if test -n "$xrpath"; then + # If the user specified any rpath flags, then add them. + temp_xrpath= + for libdir in $xrpath; do + temp_xrpath="$temp_xrpath -R$libdir" + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" ;; + esac + done + if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then + dependency_libs="$temp_xrpath $dependency_libs" + fi + fi + + # Make sure dlfiles contains only unique files that won't be dlpreopened + old_dlfiles="$dlfiles" + dlfiles= + for lib in $old_dlfiles; do + case " $dlprefiles $dlfiles " in + *" $lib "*) ;; + *) dlfiles="$dlfiles $lib" ;; + esac + done + + # Make sure dlprefiles contains only unique files + old_dlprefiles="$dlprefiles" + dlprefiles= + for lib in $old_dlprefiles; do + case "$dlprefiles " in + *" $lib "*) ;; + *) dlprefiles="$dlprefiles $lib" ;; + esac + done + + if test "$build_libtool_libs" = yes; then + if test -n "$rpath"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos*) + # these systems don't actually have a c library (as such)! + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C library is in the System framework + deplibs="$deplibs -framework System" + ;; + *-*-netbsd*) + # Don't link with libc until the a.out ld.so is fixed. + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + ;; + *) + # Add libc to deplibs on all other systems if necessary. + if test "$build_libtool_need_lc" = "yes"; then + deplibs="$deplibs -lc" + fi + ;; + esac + fi + + # Transform deplibs into only deplibs that can be linked in shared. + name_save=$name + libname_save=$libname + release_save=$release + versuffix_save=$versuffix + major_save=$major + # I'm not sure if I'm treating the release correctly. I think + # release should show up in the -l (ie -lgmp5) so we don't want to + # add it in twice. Is that correct? + release="" + versuffix="" + major="" + newdeplibs= + droppeddeps=no + case $deplibs_check_method in + pass_all) + # Don't check for shared/static. Everything works. + # This might be a little naive. We might want to check + # whether the library exists or not. But this is on + # osf3 & osf4 and I'm not really sure... Just + # implementing what was already the behavior. + newdeplibs=$deplibs + ;; + test_compile) + # This code stresses the "libraries are programs" paradigm to its + # limits. Maybe even breaks it. We compile a program, linking it + # against the deplibs as a proxy for the library. Then we can check + # whether they linked in statically or dynamically with ldd. + $rm conftest.c + cat > conftest.c <<EOF + int main() { return 0; } +EOF + $rm conftest + $LTCC $LTCFLAGS -o conftest conftest.c $deplibs + if test "$?" -eq 0 ; then + ldd_output=`ldd conftest` + for i in $deplibs; do + name=`expr $i : '-l\(.*\)'` + # If $name is empty we are operating on a -L argument. + if test "$name" != "" && test "$name" -ne "0"; then + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $i "*) + newdeplibs="$newdeplibs $i" + i="" + ;; + esac + fi + if test -n "$i" ; then + libname=`eval \\$echo \"$libname_spec\"` + deplib_matches=`eval \\$echo \"$library_names_spec\"` + set dummy $deplib_matches + deplib_match=$2 + if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then + newdeplibs="$newdeplibs $i" + else + droppeddeps=yes + $echo + $echo "*** Warning: dynamic linker does not accept needed library $i." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which I believe you do not have" + $echo "*** because a test_compile did reveal that the linker did not use it for" + $echo "*** its dynamic dependency list that programs get resolved with at runtime." + fi + fi + else + newdeplibs="$newdeplibs $i" + fi + done + else + # Error occurred in the first compile. Let's try to salvage + # the situation: Compile a separate program for each library. + for i in $deplibs; do + name=`expr $i : '-l\(.*\)'` + # If $name is empty we are operating on a -L argument. + if test "$name" != "" && test "$name" != "0"; then + $rm conftest + $LTCC $LTCFLAGS -o conftest conftest.c $i + # Did it work? + if test "$?" -eq 0 ; then + ldd_output=`ldd conftest` + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $i "*) + newdeplibs="$newdeplibs $i" + i="" + ;; + esac + fi + if test -n "$i" ; then + libname=`eval \\$echo \"$libname_spec\"` + deplib_matches=`eval \\$echo \"$library_names_spec\"` + set dummy $deplib_matches + deplib_match=$2 + if test `expr "$ldd_output" : ".*$deplib_match"` -ne 0 ; then + newdeplibs="$newdeplibs $i" + else + droppeddeps=yes + $echo + $echo "*** Warning: dynamic linker does not accept needed library $i." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have" + $echo "*** because a test_compile did reveal that the linker did not use this one" + $echo "*** as a dynamic dependency that programs can get resolved with at runtime." + fi + fi + else + droppeddeps=yes + $echo + $echo "*** Warning! Library $i is needed by this library but I was not able to" + $echo "*** make it link in! You will probably need to install it or some" + $echo "*** library that it depends on before this library will be fully" + $echo "*** functional. Installing it before continuing would be even better." + fi + else + newdeplibs="$newdeplibs $i" + fi + done + fi + ;; + file_magic*) + set dummy $deplibs_check_method + file_magic_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` + for a_deplib in $deplibs; do + name=`expr $a_deplib : '-l\(.*\)'` + # If $name is empty we are operating on a -L argument. + if test "$name" != "" && test "$name" != "0"; then + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $a_deplib "*) + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + ;; + esac + fi + if test -n "$a_deplib" ; then + libname=`eval \\$echo \"$libname_spec\"` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + potential_libs=`ls $i/$libname[.-]* 2>/dev/null` + for potent_lib in $potential_libs; do + # Follow soft links. + if ls -lLd "$potent_lib" 2>/dev/null \ + | grep " -> " >/dev/null; then + continue + fi + # The statement above tries to avoid entering an + # endless loop below, in case of cyclic links. + # We might still enter an endless loop, since a link + # loop can be closed while we follow links, + # but so what? + potlib="$potent_lib" + while test -h "$potlib" 2>/dev/null; do + potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` + case $potliblink in + [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; + *) potlib=`$echo "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null \ + | ${SED} 10q \ + | $EGREP "$file_magic_regex" > /dev/null; then + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + $echo + $echo "*** Warning: linker path does not have real file for library $a_deplib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have" + $echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $echo "*** with $libname but no candidates were found. (...for file magic test)" + else + $echo "*** with $libname and none of the candidates passed a file format test" + $echo "*** using a file magic. Last file checked: $potlib" + fi + fi + else + # Add a -L argument. + newdeplibs="$newdeplibs $a_deplib" + fi + done # Gone through all deplibs. + ;; + match_pattern*) + set dummy $deplibs_check_method + match_pattern_regex=`expr "$deplibs_check_method" : "$2 \(.*\)"` + for a_deplib in $deplibs; do + name=`expr $a_deplib : '-l\(.*\)'` + # If $name is empty we are operating on a -L argument. + if test -n "$name" && test "$name" != "0"; then + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $a_deplib "*) + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + ;; + esac + fi + if test -n "$a_deplib" ; then + libname=`eval \\$echo \"$libname_spec\"` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + potential_libs=`ls $i/$libname[.-]* 2>/dev/null` + for potent_lib in $potential_libs; do + potlib="$potent_lib" # see symlink-check above in file_magic test + if eval $echo \"$potent_lib\" 2>/dev/null \ + | ${SED} 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + $echo + $echo "*** Warning: linker path does not have real file for library $a_deplib." + $echo "*** I have the capability to make that library automatically link in when" + $echo "*** you link to this library. But I can only do this if you have a" + $echo "*** shared version of the library, which you do not appear to have" + $echo "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $echo "*** with $libname but no candidates were found. (...for regex pattern test)" + else + $echo "*** with $libname and none of the candidates passed a file format test" + $echo "*** using a regex pattern. Last file checked: $potlib" + fi + fi + else + # Add a -L argument. + newdeplibs="$newdeplibs $a_deplib" + fi + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs="" + tmp_deplibs=`$echo "X $deplibs" | $Xsed -e 's/ -lc$//' \ + -e 's/ -[LR][^ ]*//g'` + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + for i in $predeps $postdeps ; do + # can't use Xsed below, because $i might contain '/' + tmp_deplibs=`$echo "X $tmp_deplibs" | ${SED} -e "1s,^X,," -e "s,$i,,"` + done + fi + if $echo "X $tmp_deplibs" | $Xsed -e 's/[ ]//g' \ + | grep . >/dev/null; then + $echo + if test "X$deplibs_check_method" = "Xnone"; then + $echo "*** Warning: inter-library dependencies are not supported in this platform." + else + $echo "*** Warning: inter-library dependencies are not known to be supported." + fi + $echo "*** All declared inter-library dependencies are being dropped." + droppeddeps=yes + fi + ;; + esac + versuffix=$versuffix_save + major=$major_save + release=$release_save + libname=$libname_save + name=$name_save + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + newdeplibs=`$echo "X $newdeplibs" | $Xsed -e 's/ -lc / -framework System /'` + ;; + esac + + if test "$droppeddeps" = yes; then + if test "$module" = yes; then + $echo + $echo "*** Warning: libtool could not satisfy all declared inter-library" + $echo "*** dependencies of module $libname. Therefore, libtool will create" + $echo "*** a static module, that should work as long as the dlopening" + $echo "*** application is linked with the -dlopen flag." + if test -z "$global_symbol_pipe"; then + $echo + $echo "*** However, this would only work if libtool was able to extract symbol" + $echo "*** lists from a program, using \`nm' or equivalent, but libtool could" + $echo "*** not find such a program. So, this module is probably useless." + $echo "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + else + $echo "*** The inter-library dependencies that have been dropped here will be" + $echo "*** automatically added whenever a program is linked with this library" + $echo "*** or is declared to -dlopen it." + + if test "$allow_undefined" = no; then + $echo + $echo "*** Since this library must not contain undefined symbols," + $echo "*** because either the platform does not support them or" + $echo "*** it was explicitly requested with -no-undefined," + $echo "*** libtool will only create a static version of it." + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + fi + fi + # Done checking deplibs! + deplibs=$newdeplibs + fi + + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $deplibs " in + *" -L$path/$objdir "*) + new_libs="$new_libs -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$new_libs $deplib" ;; + esac + ;; + *) new_libs="$new_libs $deplib" ;; + esac + done + deplibs="$new_libs" + + + # All the library-specific variables (install_libdir is set above). + library_names= + old_library= + dlname= + + # Test again, we may have decided not to build it any more + if test "$build_libtool_libs" = yes; then + if test "$hardcode_into_libs" = yes; then + # Hardcode the library paths + hardcode_libdirs= + dep_rpath= + rpath="$finalize_rpath" + test "$mode" != relink && rpath="$compile_rpath$rpath" + for libdir in $rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + dep_rpath="$dep_rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + if test -n "$hardcode_libdir_flag_spec_ld"; then + eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\" + else + eval dep_rpath=\"$hardcode_libdir_flag_spec\" + fi + fi + if test -n "$runpath_var" && test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" + fi + test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" + fi + + shlibpath="$finalize_shlibpath" + test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath" + if test -n "$shlibpath"; then + eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" + fi + + # Get the real and link names of the library. + eval shared_ext=\"$shrext_cmds\" + eval library_names=\"$library_names_spec\" + set dummy $library_names + realname="$2" + shift; shift + + if test -n "$soname_spec"; then + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + if test -z "$dlname"; then + dlname=$soname + fi + + lib="$output_objdir/$realname" + linknames= + for link + do + linknames="$linknames $link" + done + + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$echo "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then + $show "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $run $rm $export_symbols + cmds=$export_symbols_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + if len=`expr "X$cmd" : ".*"` && + test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then + $show "$cmd" + $run eval "$cmd" || exit $? + skipped_export=false + else + # The command line is too long to execute in one step. + $show "using reloadable object file for export list..." + skipped_export=: + # Break out early, otherwise skipped_export may be + # set to false by a later but shorter cmd. + break + fi + done + IFS="$save_ifs" + if test -n "$export_symbols_regex"; then + $show "$EGREP -e \"$export_symbols_regex\" \"$export_symbols\" > \"${export_symbols}T\"" + $run eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + $show "$mv \"${export_symbols}T\" \"$export_symbols\"" + $run eval '$mv "${export_symbols}T" "$export_symbols"' + fi + fi + fi + + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $run eval '$echo "X$include_expsyms" | $SP2NL >> "$export_symbols"' + fi + + tmp_deplibs= + for test_deplib in $deplibs; do + case " $convenience " in + *" $test_deplib "*) ;; + *) + tmp_deplibs="$tmp_deplibs $test_deplib" + ;; + esac + done + deplibs="$tmp_deplibs" + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + else + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + + func_extract_archives $gentop $convenience + libobjs="$libobjs $func_extract_archives_result" + fi + fi + + if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + linker_flags="$linker_flags $flag" + fi + + # Make a backup of the uninstalled library when relinking + if test "$mode" = relink; then + $run eval '(cd $output_objdir && $rm ${realname}U && $mv $realname ${realname}U)' || exit $? + fi + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + eval test_cmds=\"$module_expsym_cmds\" + cmds=$module_expsym_cmds + else + eval test_cmds=\"$module_cmds\" + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + eval test_cmds=\"$archive_expsym_cmds\" + cmds=$archive_expsym_cmds + else + eval test_cmds=\"$archive_cmds\" + cmds=$archive_cmds + fi + fi + + if test "X$skipped_export" != "X:" && + len=`expr "X$test_cmds" : ".*" 2>/dev/null` && + test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # The command line is too long to link in one step, link piecewise. + $echo "creating reloadable object files..." + + # Save the value of $output and $libobjs because we want to + # use them later. If we have whole_archive_flag_spec, we + # want to use save_libobjs as it was before + # whole_archive_flag_spec was expanded, because we can't + # assume the linker understands whole_archive_flag_spec. + # This may have to be revisited, in case too many + # convenience libraries get linked in and end up exceeding + # the spec. + if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + fi + save_output=$output + output_la=`$echo "X$output" | $Xsed -e "$basename"` + + # Clear the reloadable object creation command queue and + # initialize k to one. + test_cmds= + concat_cmds= + objlist= + delfiles= + last_robj= + k=1 + output=$output_objdir/$output_la-${k}.$objext + # Loop over the list of objects to be linked. + for obj in $save_libobjs + do + eval test_cmds=\"$reload_cmds $objlist $last_robj\" + if test "X$objlist" = X || + { len=`expr "X$test_cmds" : ".*" 2>/dev/null` && + test "$len" -le "$max_cmd_len"; }; then + objlist="$objlist $obj" + else + # The command $test_cmds is almost too long, add a + # command to the queue. + if test "$k" -eq 1 ; then + # The first file doesn't have a previous command to add. + eval concat_cmds=\"$reload_cmds $objlist $last_robj\" + else + # All subsequent reloadable object files will link in + # the last one created. + eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj\" + fi + last_robj=$output_objdir/$output_la-${k}.$objext + k=`expr $k + 1` + output=$output_objdir/$output_la-${k}.$objext + objlist=$obj + len=1 + fi + done + # Handle the remaining objects by creating one last + # reloadable object file. All subsequent reloadable object + # files will link in the last one created. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\" + + if ${skipped_export-false}; then + $show "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $run $rm $export_symbols + libobjs=$output + # Append the command to create the export file. + eval concat_cmds=\"\$concat_cmds~$export_symbols_cmds\" + fi + + # Set up a command to remove the reloadable object files + # after they are used. + i=0 + while test "$i" -lt "$k" + do + i=`expr $i + 1` + delfiles="$delfiles $output_objdir/$output_la-${i}.$objext" + done + + $echo "creating a temporary reloadable object file: $output" + + # Loop through the commands generated above and execute them. + save_ifs="$IFS"; IFS='~' + for cmd in $concat_cmds; do + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + + libobjs=$output + # Restore the value of output. + output=$save_output + + if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + fi + # Expand the library linking commands again to reset the + # value of $libobjs for piecewise linking. + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + cmds=$module_expsym_cmds + else + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + cmds=$archive_expsym_cmds + else + cmds=$archive_cmds + fi + fi + + # Append the command to remove the reloadable object files + # to the just-reset $cmds. + eval cmds=\"\$cmds~\$rm $delfiles\" + fi + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$mode" = relink; then + $run eval '(cd $output_objdir && $rm ${realname}T && $mv ${realname}U $realname)' + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + # Restore the uninstalled library and exit + if test "$mode" = relink; then + $run eval '(cd $output_objdir && $rm ${realname}T && $mv $realname ${realname}T && $mv "$realname"U $realname)' || exit $? + + if test -n "$convenience"; then + if test -z "$whole_archive_flag_spec"; then + $show "${rm}r $gentop" + $run ${rm}r "$gentop" + fi + fi + + exit $EXIT_SUCCESS + fi + + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + $show "(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)" + $run eval '(cd $output_objdir && $rm $linkname && $LN_S $realname $linkname)' || exit $? + fi + done + + # If -module or -export-dynamic was specified, set the dlname. + if test "$module" = yes || test "$export_dynamic" = yes; then + # On all known operating systems, these are identical. + dlname="$soname" + fi + fi + ;; + + obj) + if test -n "$deplibs"; then + $echo "$modename: warning: \`-l' and \`-L' are ignored for objects" 1>&2 + fi + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + $echo "$modename: warning: \`-dlopen' is ignored for objects" 1>&2 + fi + + if test -n "$rpath"; then + $echo "$modename: warning: \`-rpath' is ignored for objects" 1>&2 + fi + + if test -n "$xrpath"; then + $echo "$modename: warning: \`-R' is ignored for objects" 1>&2 + fi + + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info' is ignored for objects" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for objects" 1>&2 + fi + + case $output in + *.lo) + if test -n "$objs$old_deplibs"; then + $echo "$modename: cannot build library object \`$output' from non-libtool objects" 1>&2 + exit $EXIT_FAILURE + fi + libobj="$output" + obj=`$echo "X$output" | $Xsed -e "$lo2o"` + ;; + *) + libobj= + obj="$output" + ;; + esac + + # Delete the old objects. + $run $rm $obj $libobj + + # Objects from convenience libraries. This assumes + # single-version convenience libraries. Whenever we create + # different ones for PIC/non-PIC, this we'll have to duplicate + # the extraction. + reload_conv_objs= + gentop= + # reload_cmds runs $LD directly, so let us get rid of + # -Wl from whole_archive_flag_spec + wl= + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval reload_conv_objs=\"\$reload_objs $whole_archive_flag_spec\" + else + gentop="$output_objdir/${obj}x" + generated="$generated $gentop" + + func_extract_archives $gentop $convenience + reload_conv_objs="$reload_objs $func_extract_archives_result" + fi + fi + + # Create the old-style object. + reload_objs="$objs$old_deplibs "`$echo "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test + + output="$obj" + cmds=$reload_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + exit $EXIT_SUCCESS + fi + + if test "$build_libtool_libs" != yes; then + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + # Create an invalid libtool object if no PIC, so that we don't + # accidentally link it into a program. + # $show "echo timestamp > $libobj" + # $run eval "echo timestamp > $libobj" || exit $? + exit $EXIT_SUCCESS + fi + + if test -n "$pic_flag" || test "$pic_mode" != default; then + # Only do commands if we really have different PIC objects. + reload_objs="$libobjs $reload_conv_objs" + output="$libobj" + cmds=$reload_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + fi + + if test -n "$gentop"; then + $show "${rm}r $gentop" + $run ${rm}r $gentop + fi + + exit $EXIT_SUCCESS + ;; + + prog) + case $host in + *cygwin*) output=`$echo $output | ${SED} -e 's,.exe$,,;s,$,.exe,'` ;; + esac + if test -n "$vinfo"; then + $echo "$modename: warning: \`-version-info' is ignored for programs" 1>&2 + fi + + if test -n "$release"; then + $echo "$modename: warning: \`-release' is ignored for programs" 1>&2 + fi + + if test "$preload" = yes; then + if test "$dlopen_support" = unknown && test "$dlopen_self" = unknown && + test "$dlopen_self_static" = unknown; then + $echo "$modename: warning: \`AC_LIBTOOL_DLOPEN' not used. Assuming no dlopen support." + fi + fi + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + compile_deplibs=`$echo "X $compile_deplibs" | $Xsed -e 's/ -lc / -framework System /'` + finalize_deplibs=`$echo "X $finalize_deplibs" | $Xsed -e 's/ -lc / -framework System /'` + ;; + esac + + case $host in + *darwin*) + # Don't allow lazy linking, it breaks C++ global constructors + if test "$tagname" = CXX ; then + compile_command="$compile_command ${wl}-bind_at_load" + finalize_command="$finalize_command ${wl}-bind_at_load" + fi + ;; + esac + + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $compile_deplibs " in + *" -L$path/$objdir "*) + new_libs="$new_libs -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $compile_deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$new_libs $deplib" ;; + esac + ;; + *) new_libs="$new_libs $deplib" ;; + esac + done + compile_deplibs="$new_libs" + + + compile_command="$compile_command $compile_deplibs" + finalize_command="$finalize_command $finalize_deplibs" + + if test -n "$rpath$xrpath"; then + # If the user specified any rpath flags, then add them. + for libdir in $rpath $xrpath; do + # This is the magic to use -rpath. + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" ;; + esac + done + fi + + # Now hardcode the library paths + rpath= + hardcode_libdirs= + for libdir in $compile_rpath $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2*) + testbindir=`$echo "X$libdir" | $Xsed -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$libdir:"*) ;; + *) dllsearchpath="$dllsearchpath:$libdir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + *) dllsearchpath="$dllsearchpath:$testbindir";; + esac + ;; + esac + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + compile_rpath="$rpath" + + rpath= + hardcode_libdirs= + for libdir in $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + finalize_rpath="$rpath" + + if test -n "$libobjs" && test "$build_old_libs" = yes; then + # Transform all the library objects into standard objects. + compile_command=`$echo "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + finalize_command=`$echo "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + fi + + dlsyms= + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + dlsyms="${outputname}S.c" + else + $echo "$modename: not configured to extract global symbols from dlpreopened files" 1>&2 + fi + fi + + if test -n "$dlsyms"; then + case $dlsyms in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist="$output_objdir/${outputname}.nm" + + $show "$rm $nlist ${nlist}S ${nlist}T" + $run $rm "$nlist" "${nlist}S" "${nlist}T" + + # Parse the name list into a source file. + $show "creating $output_objdir/$dlsyms" + + test -z "$run" && $echo > "$output_objdir/$dlsyms" "\ +/* $dlsyms - symbol resolution table for \`$outputname' dlsym emulation. */ +/* Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +/* Prevent the only kind of declaration conflicts we can make. */ +#define lt_preloaded_symbols some_other_symbol + +/* External symbol declarations for the compiler. */\ +" + + if test "$dlself" = yes; then + $show "generating symbol list for \`$output'" + + test -z "$run" && $echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$echo "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + for arg in $progfiles; do + $show "extracting global C symbols from \`$arg'" + $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $run eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + $run eval '$mv "$nlist"T "$nlist"' + fi + + if test -n "$export_symbols_regex"; then + $run eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' + $run eval '$mv "$nlist"T "$nlist"' + fi + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols="$output_objdir/$outputname.exp" + $run $rm $export_symbols + $run eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + case $host in + *cygwin* | *mingw* ) + $run eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + $run eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' + ;; + esac + else + $run eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' + $run eval 'grep -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' + $run eval 'mv "$nlist"T "$nlist"' + case $host in + *cygwin* | *mingw* ) + $run eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + $run eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' + ;; + esac + fi + fi + + for arg in $dlprefiles; do + $show "extracting global C symbols from \`$arg'" + name=`$echo "$arg" | ${SED} -e 's%^.*/%%'` + $run eval '$echo ": $name " >> "$nlist"' + $run eval "$NM $arg | $global_symbol_pipe >> '$nlist'" + done + + if test -z "$run"; then + # Make sure we have at least an empty file. + test -f "$nlist" || : > "$nlist" + + if test -n "$exclude_expsyms"; then + $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T + $mv "$nlist"T "$nlist" + fi + + # Try sorting and uniquifying the output. + if grep -v "^: " < "$nlist" | + if sort -k 3 </dev/null >/dev/null 2>&1; then + sort -k 3 + else + sort +2 + fi | + uniq > "$nlist"S; then + : + else + grep -v "^: " < "$nlist" > "$nlist"S + fi + + if test -f "$nlist"S; then + eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$dlsyms"' + else + $echo '/* NONE */' >> "$output_objdir/$dlsyms" + fi + + $echo >> "$output_objdir/$dlsyms" "\ + +#undef lt_preloaded_symbols + +#if defined (__STDC__) && __STDC__ +# define lt_ptr void * +#else +# define lt_ptr char * +# define const +#endif + +/* The mapping between symbol names and symbols. */ +" + + case $host in + *cygwin* | *mingw* ) + $echo >> "$output_objdir/$dlsyms" "\ +/* DATA imports from DLLs on WIN32 can't be const, because + runtime relocations are performed -- see ld's documentation + on pseudo-relocs */ +struct { +" + ;; + * ) + $echo >> "$output_objdir/$dlsyms" "\ +const struct { +" + ;; + esac + + + $echo >> "$output_objdir/$dlsyms" "\ + const char *name; + lt_ptr address; +} +lt_preloaded_symbols[] = +{\ +" + + eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$dlsyms" + + $echo >> "$output_objdir/$dlsyms" "\ + {0, (lt_ptr) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif\ +" + fi + + pic_flag_for_symtable= + case $host in + # compiling the symbol table file with pic_flag works around + # a FreeBSD bug that causes programs to crash when -lm is + # linked before any other PIC object. But we must not use + # pic_flag when linking with -static. The problem exists in + # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. + *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) + case "$compile_command " in + *" -static "*) ;; + *) pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND";; + esac;; + *-*-hpux*) + case "$compile_command " in + *" -static "*) ;; + *) pic_flag_for_symtable=" $pic_flag";; + esac + esac + + # Now compile the dynamic symbol file. + $show "(cd $output_objdir && $LTCC $LTCFLAGS -c$no_builtin_flag$pic_flag_for_symtable \"$dlsyms\")" + $run eval '(cd $output_objdir && $LTCC $LTCFLAGS -c$no_builtin_flag$pic_flag_for_symtable "$dlsyms")' || exit $? + + # Clean up the generated files. + $show "$rm $output_objdir/$dlsyms $nlist ${nlist}S ${nlist}T" + $run $rm "$output_objdir/$dlsyms" "$nlist" "${nlist}S" "${nlist}T" + + # Transform the symbol file into the correct name. + case $host in + *cygwin* | *mingw* ) + if test -f "$output_objdir/${outputname}.def" ; then + compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}.def $output_objdir/${outputname}S.${objext}%"` + finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}.def $output_objdir/${outputname}S.${objext}%"` + else + compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` + finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` + fi + ;; + * ) + compile_command=`$echo "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` + finalize_command=`$echo "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/${outputname}S.${objext}%"` + ;; + esac + ;; + *) + $echo "$modename: unknown suffix for \`$dlsyms'" 1>&2 + exit $EXIT_FAILURE + ;; + esac + else + # We keep going just in case the user didn't refer to + # lt_preloaded_symbols. The linker will fail if global_symbol_pipe + # really was required. + + # Nullify the symbol file. + compile_command=`$echo "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"` + finalize_command=`$echo "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"` + fi + + if test "$need_relink" = no || test "$build_libtool_libs" != yes; then + # Replace the output file specification. + compile_command=`$echo "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + link_command="$compile_command$compile_rpath" + + # We have no uninstalled library dependencies, so finalize right now. + $show "$link_command" + $run eval "$link_command" + exit_status=$? + + # Delete the generated files. + if test -n "$dlsyms"; then + $show "$rm $output_objdir/${outputname}S.${objext}" + $run $rm "$output_objdir/${outputname}S.${objext}" + fi + + exit $exit_status + fi + + if test -n "$shlibpath_var"; then + # We should set the shlibpath_var + rpath= + for dir in $temp_rpath; do + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) + # Absolute path. + rpath="$rpath$dir:" + ;; + *) + # Relative path: add a thisdir entry. + rpath="$rpath\$thisdir/$dir:" + ;; + esac + done + temp_rpath="$rpath" + fi + + if test -n "$compile_shlibpath$finalize_shlibpath"; then + compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" + fi + if test -n "$finalize_shlibpath"; then + finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" + fi + + compile_var= + finalize_var= + if test -n "$runpath_var"; then + if test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + compile_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + if test -n "$finalize_perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $finalize_perm_rpath; do + rpath="$rpath$dir:" + done + finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + fi + + if test "$no_install" = yes; then + # We don't need to create a wrapper script. + link_command="$compile_var$compile_command$compile_rpath" + # Replace the output file specification. + link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + # Delete the old output file. + $run $rm $output + # Link the executable and exit + $show "$link_command" + $run eval "$link_command" || exit $? + exit $EXIT_SUCCESS + fi + + if test "$hardcode_action" = relink; then + # Fast installation is not supported + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + + $echo "$modename: warning: this platform does not like uninstalled shared libraries" 1>&2 + $echo "$modename: \`$output' will be relinked during installation" 1>&2 + else + if test "$fast_install" != no; then + link_command="$finalize_var$compile_command$finalize_rpath" + if test "$fast_install" = yes; then + relink_command=`$echo "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'` + else + # fast_install is set to needless + relink_command= + fi + else + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + fi + fi + + # Replace the output file specification. + link_command=`$echo "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + + # Delete the old output files. + $run $rm $output $output_objdir/$outputname $output_objdir/lt-$outputname + + $show "$link_command" + $run eval "$link_command" || exit $? + + # Now create the wrapper script. + $show "creating $output" + + # Quote the relink command for shipping. + if test -n "$relink_command"; then + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` + relink_command="$var=\"$var_value\"; export $var; $relink_command" + fi + done + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"` + fi + + # Quote $echo for shipping. + if test "X$echo" = "X$SHELL $progpath --fallback-echo"; then + case $progpath in + [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $progpath --fallback-echo";; + *) qecho="$SHELL `pwd`/$progpath --fallback-echo";; + esac + qecho=`$echo "X$qecho" | $Xsed -e "$sed_quote_subst"` + else + qecho=`$echo "X$echo" | $Xsed -e "$sed_quote_subst"` + fi + + # Only actually do things if our run command is non-null. + if test -z "$run"; then + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) output=`$echo $output|${SED} 's,.exe$,,'` ;; + esac + # test for cygwin because mv fails w/o .exe extensions + case $host in + *cygwin*) + exeext=.exe + outputname=`$echo $outputname|${SED} 's,.exe$,,'` ;; + *) exeext= ;; + esac + case $host in + *cygwin* | *mingw* ) + output_name=`basename $output` + output_path=`dirname $output` + cwrappersource="$output_path/$objdir/lt-$output_name.c" + cwrapper="$output_path/$output_name.exe" + $rm $cwrappersource $cwrapper + trap "$rm $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 + + cat > $cwrappersource <<EOF + +/* $cwrappersource - temporary wrapper executable for $objdir/$outputname + Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP + + The $output program cannot be directly executed until all the libtool + libraries that it depends on are installed. + + This wrapper executable should never be moved out of the build directory. + If it is, it will not operate correctly. + + Currently, it simply execs the wrapper *script* "/bin/sh $output", + but could eventually absorb all of the scripts functionality and + exec $objdir/$outputname directly. +*/ +EOF + cat >> $cwrappersource<<"EOF" +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <malloc.h> +#include <stdarg.h> +#include <assert.h> +#include <string.h> +#include <ctype.h> +#include <sys/stat.h> + +#if defined(PATH_MAX) +# define LT_PATHMAX PATH_MAX +#elif defined(MAXPATHLEN) +# define LT_PATHMAX MAXPATHLEN +#else +# define LT_PATHMAX 1024 +#endif + +#ifndef DIR_SEPARATOR +# define DIR_SEPARATOR '/' +# define PATH_SEPARATOR ':' +#endif + +#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ + defined (__OS2__) +# define HAVE_DOS_BASED_FILE_SYSTEM +# ifndef DIR_SEPARATOR_2 +# define DIR_SEPARATOR_2 '\\' +# endif +# ifndef PATH_SEPARATOR_2 +# define PATH_SEPARATOR_2 ';' +# endif +#endif + +#ifndef DIR_SEPARATOR_2 +# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) +#else /* DIR_SEPARATOR_2 */ +# define IS_DIR_SEPARATOR(ch) \ + (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) +#endif /* DIR_SEPARATOR_2 */ + +#ifndef PATH_SEPARATOR_2 +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) +#else /* PATH_SEPARATOR_2 */ +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) +#endif /* PATH_SEPARATOR_2 */ + +#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) +#define XFREE(stale) do { \ + if (stale) { free ((void *) stale); stale = 0; } \ +} while (0) + +/* -DDEBUG is fairly common in CFLAGS. */ +#undef DEBUG +#if defined DEBUGWRAPPER +# define DEBUG(format, ...) fprintf(stderr, format, __VA_ARGS__) +#else +# define DEBUG(format, ...) +#endif + +const char *program_name = NULL; + +void * xmalloc (size_t num); +char * xstrdup (const char *string); +const char * base_name (const char *name); +char * find_executable(const char *wrapper); +int check_executable(const char *path); +char * strendzap(char *str, const char *pat); +void lt_fatal (const char *message, ...); + +int +main (int argc, char *argv[]) +{ + char **newargz; + int i; + + program_name = (char *) xstrdup (base_name (argv[0])); + DEBUG("(main) argv[0] : %s\n",argv[0]); + DEBUG("(main) program_name : %s\n",program_name); + newargz = XMALLOC(char *, argc+2); +EOF + + cat >> $cwrappersource <<EOF + newargz[0] = (char *) xstrdup("$SHELL"); +EOF + + cat >> $cwrappersource <<"EOF" + newargz[1] = find_executable(argv[0]); + if (newargz[1] == NULL) + lt_fatal("Couldn't find %s", argv[0]); + DEBUG("(main) found exe at : %s\n",newargz[1]); + /* we know the script has the same name, without the .exe */ + /* so make sure newargz[1] doesn't end in .exe */ + strendzap(newargz[1],".exe"); + for (i = 1; i < argc; i++) + newargz[i+1] = xstrdup(argv[i]); + newargz[argc+1] = NULL; + + for (i=0; i<argc+1; i++) + { + DEBUG("(main) newargz[%d] : %s\n",i,newargz[i]); + ; + } + +EOF + + case $host_os in + mingw*) + cat >> $cwrappersource <<EOF + execv("$SHELL",(char const **)newargz); +EOF + ;; + *) + cat >> $cwrappersource <<EOF + execv("$SHELL",newargz); +EOF + ;; + esac + + cat >> $cwrappersource <<"EOF" + return 127; +} + +void * +xmalloc (size_t num) +{ + void * p = (void *) malloc (num); + if (!p) + lt_fatal ("Memory exhausted"); + + return p; +} + +char * +xstrdup (const char *string) +{ + return string ? strcpy ((char *) xmalloc (strlen (string) + 1), string) : NULL +; +} + +const char * +base_name (const char *name) +{ + const char *base; + +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + /* Skip over the disk name in MSDOS pathnames. */ + if (isalpha ((unsigned char)name[0]) && name[1] == ':') + name += 2; +#endif + + for (base = name; *name; name++) + if (IS_DIR_SEPARATOR (*name)) + base = name + 1; + return base; +} + +int +check_executable(const char * path) +{ + struct stat st; + + DEBUG("(check_executable) : %s\n", path ? (*path ? path : "EMPTY!") : "NULL!"); + if ((!path) || (!*path)) + return 0; + + if ((stat (path, &st) >= 0) && + ( + /* MinGW & native WIN32 do not support S_IXOTH or S_IXGRP */ +#if defined (S_IXOTH) + ((st.st_mode & S_IXOTH) == S_IXOTH) || +#endif +#if defined (S_IXGRP) + ((st.st_mode & S_IXGRP) == S_IXGRP) || +#endif + ((st.st_mode & S_IXUSR) == S_IXUSR)) + ) + return 1; + else + return 0; +} + +/* Searches for the full path of the wrapper. Returns + newly allocated full path name if found, NULL otherwise */ +char * +find_executable (const char* wrapper) +{ + int has_slash = 0; + const char* p; + const char* p_next; + /* static buffer for getcwd */ + char tmp[LT_PATHMAX + 1]; + int tmp_len; + char* concat_name; + + DEBUG("(find_executable) : %s\n", wrapper ? (*wrapper ? wrapper : "EMPTY!") : "NULL!"); + + if ((wrapper == NULL) || (*wrapper == '\0')) + return NULL; + + /* Absolute path? */ +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + if (isalpha ((unsigned char)wrapper[0]) && wrapper[1] == ':') + { + concat_name = xstrdup (wrapper); + if (check_executable(concat_name)) + return concat_name; + XFREE(concat_name); + } + else + { +#endif + if (IS_DIR_SEPARATOR (wrapper[0])) + { + concat_name = xstrdup (wrapper); + if (check_executable(concat_name)) + return concat_name; + XFREE(concat_name); + } +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + } +#endif + + for (p = wrapper; *p; p++) + if (*p == '/') + { + has_slash = 1; + break; + } + if (!has_slash) + { + /* no slashes; search PATH */ + const char* path = getenv ("PATH"); + if (path != NULL) + { + for (p = path; *p; p = p_next) + { + const char* q; + size_t p_len; + for (q = p; *q; q++) + if (IS_PATH_SEPARATOR(*q)) + break; + p_len = q - p; + p_next = (*q == '\0' ? q : q + 1); + if (p_len == 0) + { + /* empty path: current directory */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal ("getcwd failed"); + tmp_len = strlen(tmp); + concat_name = XMALLOC(char, tmp_len + 1 + strlen(wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + } + else + { + concat_name = XMALLOC(char, p_len + 1 + strlen(wrapper) + 1); + memcpy (concat_name, p, p_len); + concat_name[p_len] = '/'; + strcpy (concat_name + p_len + 1, wrapper); + } + if (check_executable(concat_name)) + return concat_name; + XFREE(concat_name); + } + } + /* not found in PATH; assume curdir */ + } + /* Relative path | not found in path: prepend cwd */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal ("getcwd failed"); + tmp_len = strlen(tmp); + concat_name = XMALLOC(char, tmp_len + 1 + strlen(wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + + if (check_executable(concat_name)) + return concat_name; + XFREE(concat_name); + return NULL; +} + +char * +strendzap(char *str, const char *pat) +{ + size_t len, patlen; + + assert(str != NULL); + assert(pat != NULL); + + len = strlen(str); + patlen = strlen(pat); + + if (patlen <= len) + { + str += len - patlen; + if (strcmp(str, pat) == 0) + *str = '\0'; + } + return str; +} + +static void +lt_error_core (int exit_status, const char * mode, + const char * message, va_list ap) +{ + fprintf (stderr, "%s: %s: ", program_name, mode); + vfprintf (stderr, message, ap); + fprintf (stderr, ".\n"); + + if (exit_status >= 0) + exit (exit_status); +} + +void +lt_fatal (const char *message, ...) +{ + va_list ap; + va_start (ap, message); + lt_error_core (EXIT_FAILURE, "FATAL", message, ap); + va_end (ap); +} +EOF + # we should really use a build-platform specific compiler + # here, but OTOH, the wrappers (shell script and this C one) + # are only useful if you want to execute the "real" binary. + # Since the "real" binary is built for $host, then this + # wrapper might as well be built for $host, too. + $run $LTCC $LTCFLAGS -s -o $cwrapper $cwrappersource + ;; + esac + $rm $output + trap "$rm $output; exit $EXIT_FAILURE" 1 2 15 + + $echo > $output "\ +#! $SHELL + +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP +# +# The $output program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed='${SED} -e 1s/^X//' +sed_quote_subst='$sed_quote_subst' + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command=\"$relink_command\" + +# This environment variable determines our operation mode. +if test \"\$libtool_install_magic\" = \"$magic\"; then + # install mode needs the following variable: + notinst_deplibs='$notinst_deplibs' +else + # When we are sourced in execute mode, \$file and \$echo are already set. + if test \"\$libtool_execute_magic\" != \"$magic\"; then + echo=\"$qecho\" + file=\"\$0\" + # Make sure echo works. + if test \"X\$1\" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift + elif test \"X\`(\$echo '\t') 2>/dev/null\`\" = 'X\t'; then + # Yippee, \$echo works! + : + else + # Restart under the correct shell, and then maybe \$echo will work. + exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"} + fi + fi\ +" + $echo >> $output "\ + + # Find the directory that this script lives in. + thisdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\` + test \"x\$thisdir\" = \"x\$file\" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\` + while test -n \"\$file\"; do + destdir=\`\$echo \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\` + + # If there was a directory component, then change thisdir. + if test \"x\$destdir\" != \"x\$file\"; then + case \"\$destdir\" in + [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; + *) thisdir=\"\$thisdir/\$destdir\" ;; + esac + fi + + file=\`\$echo \"X\$file\" | \$Xsed -e 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\` + done + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" + + if test "$fast_install" = yes; then + $echo >> $output "\ + program=lt-'$outputname'$exeext + progdir=\"\$thisdir/$objdir\" + + if test ! -f \"\$progdir/\$program\" || \\ + { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ + test \"X\$file\" != \"X\$progdir/\$program\"; }; then + + file=\"\$\$-\$program\" + + if test ! -d \"\$progdir\"; then + $mkdir \"\$progdir\" + else + $rm \"\$progdir/\$file\" + fi" + + $echo >> $output "\ + + # relink executable if necessary + if test -n \"\$relink_command\"; then + if relink_command_output=\`eval \$relink_command 2>&1\`; then : + else + $echo \"\$relink_command_output\" >&2 + $rm \"\$progdir/\$file\" + exit $EXIT_FAILURE + fi + fi + + $mv \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $rm \"\$progdir/\$program\"; + $mv \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $rm \"\$progdir/\$file\" + fi" + else + $echo >> $output "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi + + $echo >> $output "\ + + if test -f \"\$progdir/\$program\"; then" + + # Export our shlibpath_var if we have one. + if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $echo >> $output "\ + # Add our own library path to $shlibpath_var + $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" + + # Some systems cannot cope with colon-terminated $shlibpath_var + # The second colon is a workaround for a bug in BeOS R4 sed + $shlibpath_var=\`\$echo \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\` + + export $shlibpath_var +" + fi + + # fixup the dll searchpath if we need to. + if test -n "$dllsearchpath"; then + $echo >> $output "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + $echo >> $output "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. +" + case $host in + # Backslashes separate directories on plain windows + *-*-mingw | *-*-os2*) + $echo >> $output "\ + exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} +" + ;; + + *) + $echo >> $output "\ + exec \"\$progdir/\$program\" \${1+\"\$@\"} +" + ;; + esac + $echo >> $output "\ + \$echo \"\$0: cannot exec \$program \${1+\"\$@\"}\" + exit $EXIT_FAILURE + fi + else + # The program doesn't exist. + \$echo \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 + \$echo \"This script is just a wrapper for \$program.\" 1>&2 + $echo \"See the $PACKAGE documentation for more information.\" 1>&2 + exit $EXIT_FAILURE + fi +fi\ +" + chmod +x $output + fi + exit $EXIT_SUCCESS + ;; + esac + + # See if we need to build an old-fashioned archive. + for oldlib in $oldlibs; do + + if test "$build_libtool_libs" = convenience; then + oldobjs="$libobjs_save" + addlibs="$convenience" + build_libtool_libs=no + else + if test "$build_libtool_libs" = module; then + oldobjs="$libobjs_save" + build_libtool_libs=no + else + oldobjs="$old_deplibs $non_pic_objects" + fi + addlibs="$old_convenience" + fi + + if test -n "$addlibs"; then + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + + func_extract_archives $gentop $addlibs + oldobjs="$oldobjs $func_extract_archives_result" + fi + + # Do each command in the archive commands. + if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then + cmds=$old_archive_from_new_cmds + else + # POSIX demands no paths to be encoded in archives. We have + # to avoid creating archives with duplicate basenames if we + # might have to extract them afterwards, e.g., when creating a + # static archive out of a convenience library, or when linking + # the entirety of a libtool archive into another (currently + # not supported by libtool). + if (for obj in $oldobjs + do + $echo "X$obj" | $Xsed -e 's%^.*/%%' + done | sort | sort -uc >/dev/null 2>&1); then + : + else + $echo "copying selected object files to avoid basename conflicts..." + + if test -z "$gentop"; then + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + + $show "${rm}r $gentop" + $run ${rm}r "$gentop" + $show "$mkdir $gentop" + $run $mkdir "$gentop" + exit_status=$? + if test "$exit_status" -ne 0 && test ! -d "$gentop"; then + exit $exit_status + fi + fi + + save_oldobjs=$oldobjs + oldobjs= + counter=1 + for obj in $save_oldobjs + do + objbase=`$echo "X$obj" | $Xsed -e 's%^.*/%%'` + case " $oldobjs " in + " ") oldobjs=$obj ;; + *[\ /]"$objbase "*) + while :; do + # Make sure we don't pick an alternate name that also + # overlaps. + newobj=lt$counter-$objbase + counter=`expr $counter + 1` + case " $oldobjs " in + *[\ /]"$newobj "*) ;; + *) if test ! -f "$gentop/$newobj"; then break; fi ;; + esac + done + $show "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" + $run ln "$obj" "$gentop/$newobj" || + $run cp "$obj" "$gentop/$newobj" + oldobjs="$oldobjs $gentop/$newobj" + ;; + *) oldobjs="$oldobjs $obj" ;; + esac + done + fi + + eval cmds=\"$old_archive_cmds\" + + if len=`expr "X$cmds" : ".*"` && + test "$len" -le "$max_cmd_len" || test "$max_cmd_len" -le -1; then + cmds=$old_archive_cmds + else + # the command line is too long to link in one step, link in parts + $echo "using piecewise archive linking..." + save_RANLIB=$RANLIB + RANLIB=: + objlist= + concat_cmds= + save_oldobjs=$oldobjs + + # Is there a better way of finding the last object in the list? + for obj in $save_oldobjs + do + last_oldobj=$obj + done + for obj in $save_oldobjs + do + oldobjs="$objlist $obj" + objlist="$objlist $obj" + eval test_cmds=\"$old_archive_cmds\" + if len=`expr "X$test_cmds" : ".*" 2>/dev/null` && + test "$len" -le "$max_cmd_len"; then + : + else + # the above command should be used before it gets too long + oldobjs=$objlist + if test "$obj" = "$last_oldobj" ; then + RANLIB=$save_RANLIB + fi + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" + objlist= + fi + done + RANLIB=$save_RANLIB + oldobjs=$objlist + if test "X$oldobjs" = "X" ; then + eval cmds=\"\$concat_cmds\" + else + eval cmds=\"\$concat_cmds~\$old_archive_cmds\" + fi + fi + fi + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + eval cmd=\"$cmd\" + IFS="$save_ifs" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + done + + if test -n "$generated"; then + $show "${rm}r$generated" + $run ${rm}r$generated + fi + + # Now create the libtool archive. + case $output in + *.la) + old_library= + test "$build_old_libs" = yes && old_library="$libname.$libext" + $show "creating $output" + + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + var_value=`$echo "X$var_value" | $Xsed -e "$sed_quote_subst"` + relink_command="$var=\"$var_value\"; export $var; $relink_command" + fi + done + # Quote the link command for shipping. + relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" + relink_command=`$echo "X$relink_command" | $Xsed -e "$sed_quote_subst"` + if test "$hardcode_automatic" = yes ; then + relink_command= + fi + + + # Only create the output if not a dry run. + if test -z "$run"; then + for installed in no yes; do + if test "$installed" = yes; then + if test -z "$install_libdir"; then + break + fi + output="$output_objdir/$outputname"i + # Replace all uninstalled libtool libraries with the installed ones + newdependency_libs= + for deplib in $dependency_libs; do + case $deplib in + *.la) + name=`$echo "X$deplib" | $Xsed -e 's%^.*/%%'` + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + if test -z "$libdir"; then + $echo "$modename: \`$deplib' is not a valid libtool archive" 1>&2 + exit $EXIT_FAILURE + fi + newdependency_libs="$newdependency_libs $libdir/$name" + ;; + *) newdependency_libs="$newdependency_libs $deplib" ;; + esac + done + dependency_libs="$newdependency_libs" + newdlfiles= + for lib in $dlfiles; do + name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + if test -z "$libdir"; then + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + exit $EXIT_FAILURE + fi + newdlfiles="$newdlfiles $libdir/$name" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + name=`$echo "X$lib" | $Xsed -e 's%^.*/%%'` + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + if test -z "$libdir"; then + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + exit $EXIT_FAILURE + fi + newdlprefiles="$newdlprefiles $libdir/$name" + done + dlprefiles="$newdlprefiles" + else + newdlfiles= + for lib in $dlfiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + newdlfiles="$newdlfiles $abs" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + newdlprefiles="$newdlprefiles $abs" + done + dlprefiles="$newdlprefiles" + fi + $rm $output + # place dlname in correct position for cygwin + tdlname=$dlname + case $host,$output,$installed,$module,$dlname in + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;; + esac + $echo > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM - GNU $PACKAGE $VERSION$TIMESTAMP +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='$tdlname' + +# Names of this library. +library_names='$library_names' + +# The name of the static archive. +old_library='$old_library' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Version information for $libname. +current=$current +age=$age +revision=$revision + +# Is this an already installed library? +installed=$installed + +# Should we warn about portability when linking against -modules? +shouldnotlink=$module + +# Files to dlopen/dlpreopen +dlopen='$dlfiles' +dlpreopen='$dlprefiles' + +# Directory that this library needs to be installed in: +libdir='$install_libdir'" + if test "$installed" = no && test "$need_relink" = yes; then + $echo >> $output "\ +relink_command=\"$relink_command\"" + fi + done + fi + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + $show "(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)" + $run eval '(cd $output_objdir && $rm $outputname && $LN_S ../$outputname $outputname)' || exit $? + ;; + esac + exit $EXIT_SUCCESS + ;; + + # libtool install mode + install) + modename="$modename: install" + + # There may be an optional sh(1) argument at the beginning of + # install_prog (especially on Windows NT). + if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || + # Allow the use of GNU shtool's install command. + $echo "X$nonopt" | grep shtool > /dev/null; then + # Aesthetically quote it. + arg=`$echo "X$nonopt" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + install_prog="$arg " + arg="$1" + shift + else + install_prog= + arg=$nonopt + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + install_prog="$install_prog$arg" + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=no + stripme= + for arg + do + if test -n "$dest"; then + files="$files $dest" + dest=$arg + continue + fi + + case $arg in + -d) isdir=yes ;; + -f) + case " $install_prog " in + *[\\\ /]cp\ *) ;; + *) prev=$arg ;; + esac + ;; + -g | -m | -o) prev=$arg ;; + -s) + stripme=" -s" + continue + ;; + -*) + ;; + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + prev= + else + dest=$arg + continue + fi + ;; + esac + + # Aesthetically quote the argument. + arg=`$echo "X$arg" | $Xsed -e "$sed_quote_subst"` + case $arg in + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + arg="\"$arg\"" + ;; + esac + install_prog="$install_prog $arg" + done + + if test -z "$install_prog"; then + $echo "$modename: you must specify an install program" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + if test -n "$prev"; then + $echo "$modename: the \`$prev' option requires an argument" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + if test -z "$files"; then + if test -z "$dest"; then + $echo "$modename: no file or destination specified" 1>&2 + else + $echo "$modename: you must specify a destination" 1>&2 + fi + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Strip any trailing slash from the destination. + dest=`$echo "X$dest" | $Xsed -e 's%/$%%'` + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=yes + if test "$isdir" = yes; then + destdir="$dest" + destname= + else + destdir=`$echo "X$dest" | $Xsed -e 's%/[^/]*$%%'` + test "X$destdir" = "X$dest" && destdir=. + destname=`$echo "X$dest" | $Xsed -e 's%^.*/%%'` + + # Not a directory, so check to see that there is only one file specified. + set dummy $files + if test "$#" -gt 2; then + $echo "$modename: \`$dest' is not a directory" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + fi + case $destdir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + for file in $files; do + case $file in + *.lo) ;; + *) + $echo "$modename: \`$destdir' must be an absolute directory name" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + ;; + esac + done + ;; + esac + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + staticlibs= + future_libdirs= + current_libdirs= + for file in $files; do + + # Do each installation. + case $file in + *.$libext) + # Do the static libraries later. + staticlibs="$staticlibs $file" + ;; + + *.la) + # Check to see that this really is a libtool archive. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$file' is not a valid libtool archive" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + library_names= + old_library= + relink_command= + # If there is no directory component, then add one. + case $file in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) current_libdirs="$current_libdirs $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) future_libdirs="$future_libdirs $libdir" ;; + esac + fi + + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'`/ + test "X$dir" = "X$file/" && dir= + dir="$dir$objdir" + + if test -n "$relink_command"; then + # Determine the prefix the user has applied to our future dir. + inst_prefix_dir=`$echo "$destdir" | $SED "s%$libdir\$%%"` + + # Don't allow the user to place us outside of our expected + # location b/c this prevents finding dependent libraries that + # are installed to the same prefix. + # At present, this check doesn't affect windows .dll's that + # are installed into $libdir/../bin (currently, that works fine) + # but it's something to keep an eye on. + if test "$inst_prefix_dir" = "$destdir"; then + $echo "$modename: error: cannot install \`$file' to a directory not ending in $libdir" 1>&2 + exit $EXIT_FAILURE + fi + + if test -n "$inst_prefix_dir"; then + # Stick the inst_prefix_dir data into the link command. + relink_command=`$echo "$relink_command" | $SED "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` + else + relink_command=`$echo "$relink_command" | $SED "s%@inst_prefix_dir@%%"` + fi + + $echo "$modename: warning: relinking \`$file'" 1>&2 + $show "$relink_command" + if $run eval "$relink_command"; then : + else + $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 + exit $EXIT_FAILURE + fi + fi + + # See the names of the shared library. + set dummy $library_names + if test -n "$2"; then + realname="$2" + shift + shift + + srcname="$realname" + test -n "$relink_command" && srcname="$realname"T + + # Install the shared library and build the symlinks. + $show "$install_prog $dir/$srcname $destdir/$realname" + $run eval "$install_prog $dir/$srcname $destdir/$realname" || exit $? + if test -n "$stripme" && test -n "$striplib"; then + $show "$striplib $destdir/$realname" + $run eval "$striplib $destdir/$realname" || exit $? + fi + + if test "$#" -gt 0; then + # Delete the old symlinks, and create new ones. + # Try `ln -sf' first, because the `ln' binary might depend on + # the symlink we replace! Solaris /bin/ln does not understand -f, + # so we also need to try rm && ln -s. + for linkname + do + if test "$linkname" != "$realname"; then + $show "(cd $destdir && { $LN_S -f $realname $linkname || { $rm $linkname && $LN_S $realname $linkname; }; })" + $run eval "(cd $destdir && { $LN_S -f $realname $linkname || { $rm $linkname && $LN_S $realname $linkname; }; })" + fi + done + fi + + # Do each command in the postinstall commands. + lib="$destdir/$realname" + cmds=$postinstall_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$mode" = relink; then + $run eval '(cd $output_objdir && $rm ${realname}T && $mv ${realname}U $realname)' + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + fi + + # Install the pseudo-library for information purposes. + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + instname="$dir/$name"i + $show "$install_prog $instname $destdir/$name" + $run eval "$install_prog $instname $destdir/$name" || exit $? + + # Maybe install the static library, too. + test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library" + ;; + + *.lo) + # Install (i.e. copy) a libtool object. + + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + destfile="$destdir/$destfile" + fi + + # Deduce the name of the destination old-style object file. + case $destfile in + *.lo) + staticdest=`$echo "X$destfile" | $Xsed -e "$lo2o"` + ;; + *.$objext) + staticdest="$destfile" + destfile= + ;; + *) + $echo "$modename: cannot copy a libtool object to \`$destfile'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + # Install the libtool object if requested. + if test -n "$destfile"; then + $show "$install_prog $file $destfile" + $run eval "$install_prog $file $destfile" || exit $? + fi + + # Install the old object if enabled. + if test "$build_old_libs" = yes; then + # Deduce the name of the old-style object file. + staticobj=`$echo "X$file" | $Xsed -e "$lo2o"` + + $show "$install_prog $staticobj $staticdest" + $run eval "$install_prog \$staticobj \$staticdest" || exit $? + fi + exit $EXIT_SUCCESS + ;; + + *) + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + destfile=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + destfile="$destdir/$destfile" + fi + + # If the file is missing, and there is a .exe on the end, strip it + # because it is most likely a libtool script we actually want to + # install + stripped_ext="" + case $file in + *.exe) + if test ! -f "$file"; then + file=`$echo $file|${SED} 's,.exe$,,'` + stripped_ext=".exe" + fi + ;; + esac + + # Do a test to see if this is really a libtool program. + case $host in + *cygwin*|*mingw*) + wrapper=`$echo $file | ${SED} -e 's,.exe$,,'` + ;; + *) + wrapper=$file + ;; + esac + if (${SED} -e '4q' $wrapper | grep "^# Generated by .*$PACKAGE")>/dev/null 2>&1; then + notinst_deplibs= + relink_command= + + # Note that it is not necessary on cygwin/mingw to append a dot to + # foo even if both foo and FILE.exe exist: automatic-append-.exe + # behavior happens only for exec(3), not for open(2)! Also, sourcing + # `FILE.' does not work on cygwin managed mounts. + # + # If there is no directory component, then add one. + case $wrapper in + */* | *\\*) . ${wrapper} ;; + *) . ./${wrapper} ;; + esac + + # Check the variables that should have been set. + if test -z "$notinst_deplibs"; then + $echo "$modename: invalid libtool wrapper script \`$wrapper'" 1>&2 + exit $EXIT_FAILURE + fi + + finalize=yes + for lib in $notinst_deplibs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + # If there is no directory component, then add one. + case $lib in + */* | *\\*) . $lib ;; + *) . ./$lib ;; + esac + fi + libfile="$libdir/"`$echo "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test + if test -n "$libdir" && test ! -f "$libfile"; then + $echo "$modename: warning: \`$lib' has not been installed in \`$libdir'" 1>&2 + finalize=no + fi + done + + relink_command= + # Note that it is not necessary on cygwin/mingw to append a dot to + # foo even if both foo and FILE.exe exist: automatic-append-.exe + # behavior happens only for exec(3), not for open(2)! Also, sourcing + # `FILE.' does not work on cygwin managed mounts. + # + # If there is no directory component, then add one. + case $wrapper in + */* | *\\*) . ${wrapper} ;; + *) . ./${wrapper} ;; + esac + + outputname= + if test "$fast_install" = no && test -n "$relink_command"; then + if test "$finalize" = yes && test -z "$run"; then + tmpdir=`func_mktempdir` + file=`$echo "X$file$stripped_ext" | $Xsed -e 's%^.*/%%'` + outputname="$tmpdir/$file" + # Replace the output file specification. + relink_command=`$echo "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'` + + $show "$relink_command" + if $run eval "$relink_command"; then : + else + $echo "$modename: error: relink \`$file' with the above command before installing it" 1>&2 + ${rm}r "$tmpdir" + continue + fi + file="$outputname" + else + $echo "$modename: warning: cannot relink \`$file'" 1>&2 + fi + else + # Install the binary that we compiled earlier. + file=`$echo "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + # remove .exe since cygwin /usr/bin/install will append another + # one anyway + case $install_prog,$host in + */usr/bin/install*,*cygwin*) + case $file:$destfile in + *.exe:*.exe) + # this is ok + ;; + *.exe:*) + destfile=$destfile.exe + ;; + *:*.exe) + destfile=`$echo $destfile | ${SED} -e 's,.exe$,,'` + ;; + esac + ;; + esac + $show "$install_prog$stripme $file $destfile" + $run eval "$install_prog\$stripme \$file \$destfile" || exit $? + test -n "$outputname" && ${rm}r "$tmpdir" + ;; + esac + done + + for file in $staticlibs; do + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + + # Set up the ranlib parameters. + oldlib="$destdir/$name" + + $show "$install_prog $file $oldlib" + $run eval "$install_prog \$file \$oldlib" || exit $? + + if test -n "$stripme" && test -n "$old_striplib"; then + $show "$old_striplib $oldlib" + $run eval "$old_striplib $oldlib" || exit $? + fi + + # Do each command in the postinstall commands. + cmds=$old_postinstall_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || exit $? + done + IFS="$save_ifs" + done + + if test -n "$future_libdirs"; then + $echo "$modename: warning: remember to run \`$progname --finish$future_libdirs'" 1>&2 + fi + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + test -n "$run" && current_libdirs=" -n$current_libdirs" + exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' + else + exit $EXIT_SUCCESS + fi + ;; + + # libtool finish mode + finish) + modename="$modename: finish" + libdirs="$nonopt" + admincmds= + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for dir + do + libdirs="$libdirs $dir" + done + + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + cmds=$finish_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" || admincmds="$admincmds + $cmd" + done + IFS="$save_ifs" + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $run eval "$cmds" || admincmds="$admincmds + $cmds" + fi + done + fi + + # Exit here if they wanted silent mode. + test "$show" = : && exit $EXIT_SUCCESS + + $echo "X----------------------------------------------------------------------" | $Xsed + $echo "Libraries have been installed in:" + for libdir in $libdirs; do + $echo " $libdir" + done + $echo + $echo "If you ever happen to want to link against installed libraries" + $echo "in a given directory, LIBDIR, you must either use libtool, and" + $echo "specify the full pathname of the library, or use the \`-LLIBDIR'" + $echo "flag during linking and do at least one of the following:" + if test -n "$shlibpath_var"; then + $echo " - add LIBDIR to the \`$shlibpath_var' environment variable" + $echo " during execution" + fi + if test -n "$runpath_var"; then + $echo " - add LIBDIR to the \`$runpath_var' environment variable" + $echo " during linking" + fi + if test -n "$hardcode_libdir_flag_spec"; then + libdir=LIBDIR + eval flag=\"$hardcode_libdir_flag_spec\" + + $echo " - use the \`$flag' linker flag" + fi + if test -n "$admincmds"; then + $echo " - have your system administrator run these commands:$admincmds" + fi + if test -f /etc/ld.so.conf; then + $echo " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" + fi + $echo + $echo "See any operating system documentation about shared libraries for" + $echo "more information, such as the ld(1) and ld.so(8) manual pages." + $echo "X----------------------------------------------------------------------" | $Xsed + exit $EXIT_SUCCESS + ;; + + # libtool execute mode + execute) + modename="$modename: execute" + + # The first argument is the command name. + cmd="$nonopt" + if test -z "$cmd"; then + $echo "$modename: you must specify a COMMAND" 1>&2 + $echo "$help" + exit $EXIT_FAILURE + fi + + # Handle -dlopen flags immediately. + for file in $execute_dlfiles; do + if test ! -f "$file"; then + $echo "$modename: \`$file' is not a file" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + dir= + case $file in + *.la) + # Check to see that this really is a libtool archive. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then : + else + $echo "$modename: \`$lib' is not a valid libtool archive" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Read the libtool library. + dlname= + library_names= + + # If there is no directory component, then add one. + case $file in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && $echo "$modename: warning: \`$file' was not linked with \`-export-dynamic'" + continue + fi + + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$file" && dir=. + + if test -f "$dir/$objdir/$dlname"; then + dir="$dir/$objdir" + else + $echo "$modename: cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" 1>&2 + exit $EXIT_FAILURE + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + test "X$dir" = "X$file" && dir=. + ;; + + *) + $echo "$modename: warning \`-dlopen' is ignored for non-libtool libraries and objects" 1>&2 + continue + ;; + esac + + # Get the absolute pathname. + absdir=`cd "$dir" && pwd` + test -n "$absdir" && dir="$absdir" + + # Now add the directory to shlibpath_var. + if eval "test -z \"\$$shlibpath_var\""; then + eval "$shlibpath_var=\"\$dir\"" + else + eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" + fi + done + + # This variable tells wrapper scripts just to set shlibpath_var + # rather than running their programs. + libtool_execute_magic="$magic" + + # Check if any of the arguments is a wrapper script. + args= + for file + do + case $file in + -*) ;; + *) + # Do a test to see if this is really a libtool program. + if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + # If there is no directory component, then add one. + case $file in + */* | *\\*) . $file ;; + *) . ./$file ;; + esac + + # Transform arg to wrapped name. + file="$progdir/$program" + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + file=`$echo "X$file" | $Xsed -e "$sed_quote_subst"` + args="$args \"$file\"" + done + + if test -z "$run"; then + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi + + # Restore saved environment variables + if test "${save_LC_ALL+set}" = set; then + LC_ALL="$save_LC_ALL"; export LC_ALL + fi + if test "${save_LANG+set}" = set; then + LANG="$save_LANG"; export LANG + fi + + # Now prepare to actually exec the command. + exec_cmd="\$cmd$args" + else + # Display what would be done. + if test -n "$shlibpath_var"; then + eval "\$echo \"\$shlibpath_var=\$$shlibpath_var\"" + $echo "export $shlibpath_var" + fi + $echo "$cmd$args" + exit $EXIT_SUCCESS + fi + ;; + + # libtool clean and uninstall mode + clean | uninstall) + modename="$modename: $mode" + rm="$nonopt" + files= + rmforce= + exit_status=0 + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + for arg + do + case $arg in + -f) rm="$rm $arg"; rmforce=yes ;; + -*) rm="$rm $arg" ;; + *) files="$files $arg" ;; + esac + done + + if test -z "$rm"; then + $echo "$modename: you must specify an RM program" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + fi + + rmdirs= + + origobjdir="$objdir" + for file in $files; do + dir=`$echo "X$file" | $Xsed -e 's%/[^/]*$%%'` + if test "X$dir" = "X$file"; then + dir=. + objdir="$origobjdir" + else + objdir="$dir/$origobjdir" + fi + name=`$echo "X$file" | $Xsed -e 's%^.*/%%'` + test "$mode" = uninstall && objdir="$dir" + + # Remember objdir for removal later, being careful to avoid duplicates + if test "$mode" = clean; then + case " $rmdirs " in + *" $objdir "*) ;; + *) rmdirs="$rmdirs $objdir" ;; + esac + fi + + # Don't error if the file doesn't exist and rm -f was used. + if (test -L "$file") >/dev/null 2>&1 \ + || (test -h "$file") >/dev/null 2>&1 \ + || test -f "$file"; then + : + elif test -d "$file"; then + exit_status=1 + continue + elif test "$rmforce" = yes; then + continue + fi + + rmfiles="$file" + + case $name in + *.la) + # Possibly a libtool archive, so verify it. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + . $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + rmfiles="$rmfiles $objdir/$n" + done + test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library" + + case "$mode" in + clean) + case " $library_names " in + # " " in the beginning catches empty $dlname + *" $dlname "*) ;; + *) rmfiles="$rmfiles $objdir/$dlname" ;; + esac + test -n "$libdir" && rmfiles="$rmfiles $objdir/$name $objdir/${name}i" + ;; + uninstall) + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + cmds=$postuninstall_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" + if test "$?" -ne 0 && test "$rmforce" != yes; then + exit_status=1 + fi + done + IFS="$save_ifs" + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + cmds=$old_postuninstall_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $show "$cmd" + $run eval "$cmd" + if test "$?" -ne 0 && test "$rmforce" != yes; then + exit_status=1 + fi + done + IFS="$save_ifs" + fi + # FIXME: should reinstall the best remaining shared library. + ;; + esac + fi + ;; + + *.lo) + # Possibly a libtool object, so verify it. + if (${SED} -e '2q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + + # Read the .lo file + . $dir/$name + + # Add PIC object to the list of files to remove. + if test -n "$pic_object" \ + && test "$pic_object" != none; then + rmfiles="$rmfiles $dir/$pic_object" + fi + + # Add non-PIC object to the list of files to remove. + if test -n "$non_pic_object" \ + && test "$non_pic_object" != none; then + rmfiles="$rmfiles $dir/$non_pic_object" + fi + fi + ;; + + *) + if test "$mode" = clean ; then + noexename=$name + case $file in + *.exe) + file=`$echo $file|${SED} 's,.exe$,,'` + noexename=`$echo $name|${SED} 's,.exe$,,'` + # $file with .exe has already been added to rmfiles, + # add $file without .exe + rmfiles="$rmfiles $file" + ;; + esac + # Do a test to see if this is a libtool program. + if (${SED} -e '4q' $file | grep "^# Generated by .*$PACKAGE") >/dev/null 2>&1; then + relink_command= + . $dir/$noexename + + # note $name still contains .exe if it was in $file originally + # as does the version of $file that was added into $rmfiles + rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}" + if test "$fast_install" = yes && test -n "$relink_command"; then + rmfiles="$rmfiles $objdir/lt-$name" + fi + if test "X$noexename" != "X$name" ; then + rmfiles="$rmfiles $objdir/lt-${noexename}.c" + fi + fi + fi + ;; + esac + $show "$rm $rmfiles" + $run $rm $rmfiles || exit_status=1 + done + objdir="$origobjdir" + + # Try to remove the ${objdir}s in the directories where we deleted files + for dir in $rmdirs; do + if test -d "$dir"; then + $show "rmdir $dir" + $run rmdir $dir >/dev/null 2>&1 + fi + done + + exit $exit_status + ;; + + "") + $echo "$modename: you must specify a MODE" 1>&2 + $echo "$generic_help" 1>&2 + exit $EXIT_FAILURE + ;; + esac + + if test -z "$exec_cmd"; then + $echo "$modename: invalid operation mode \`$mode'" 1>&2 + $echo "$generic_help" 1>&2 + exit $EXIT_FAILURE + fi +fi # test -z "$show_help" + +if test -n "$exec_cmd"; then + eval exec $exec_cmd + exit $EXIT_FAILURE +fi + +# We need to display help for each of the modes. +case $mode in +"") $echo \ +"Usage: $modename [OPTION]... [MODE-ARG]... + +Provide generalized library-building support services. + + --config show all configuration variables + --debug enable verbose shell tracing +-n, --dry-run display commands without modifying any files + --features display basic configuration information and exit + --finish same as \`--mode=finish' + --help display this help message and exit + --mode=MODE use operation mode MODE [default=inferred from MODE-ARGS] + --quiet same as \`--silent' + --silent don't print informational messages + --tag=TAG use configuration variables from tag TAG + --version print version information + +MODE must be one of the following: + + clean remove files from the build directory + compile compile a source file into a libtool object + execute automatically set library path, then run a program + finish complete the installation of libtool libraries + install install libraries or executables + link create a library or an executable + uninstall remove libraries from an installed directory + +MODE-ARGS vary depending on the MODE. Try \`$modename --help --mode=MODE' for +a more detailed description of MODE. + +Report bugs to <bug-libtool@gnu.org>." + exit $EXIT_SUCCESS + ;; + +clean) + $echo \ +"Usage: $modename [OPTION]... --mode=clean RM [RM-OPTION]... FILE... + +Remove files from the build directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, object or program, all the files associated +with it are deleted. Otherwise, only FILE itself is deleted using RM." + ;; + +compile) + $echo \ +"Usage: $modename [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE + +Compile a source file into a libtool library object. + +This mode accepts the following additional options: + + -o OUTPUT-FILE set the output file name to OUTPUT-FILE + -prefer-pic try to building PIC objects only + -prefer-non-pic try to building non-PIC objects only + -static always build a \`.o' file suitable for static linking + +COMPILE-COMMAND is a command to be used in creating a \`standard' object file +from the given SOURCEFILE. + +The output file name is determined by removing the directory component from +SOURCEFILE, then substituting the C source code suffix \`.c' with the +library object suffix, \`.lo'." + ;; + +execute) + $echo \ +"Usage: $modename [OPTION]... --mode=execute COMMAND [ARGS]... + +Automatically set library path, then run a program. + +This mode accepts the following additional options: + + -dlopen FILE add the directory containing FILE to the library path + +This mode sets the library path environment variable according to \`-dlopen' +flags. + +If any of the ARGS are libtool executable wrappers, then they are translated +into their corresponding uninstalled binary, and any of their required library +directories are added to the library path. + +Then, COMMAND is executed, with ARGS as arguments." + ;; + +finish) + $echo \ +"Usage: $modename [OPTION]... --mode=finish [LIBDIR]... + +Complete the installation of libtool libraries. + +Each LIBDIR is a directory that contains libtool libraries. + +The commands that this mode executes may require superuser privileges. Use +the \`--dry-run' option if you just want to see what would be executed." + ;; + +install) + $echo \ +"Usage: $modename [OPTION]... --mode=install INSTALL-COMMAND... + +Install executables or libraries. + +INSTALL-COMMAND is the installation command. The first component should be +either the \`install' or \`cp' program. + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + +link) + $echo \ +"Usage: $modename [OPTION]... --mode=link LINK-COMMAND... + +Link object files or libraries together to form another library, or to +create an executable program. + +LINK-COMMAND is a command using the C compiler that you would use to create +a program from several object files. + +The following components of LINK-COMMAND are treated specially: + + -all-static do not do any dynamic linking at all + -avoid-version do not add a version suffix if possible + -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime + -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols + -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) + -export-symbols SYMFILE + try to export only the symbols listed in SYMFILE + -export-symbols-regex REGEX + try to export only the symbols matching REGEX + -LLIBDIR search LIBDIR for required installed libraries + -lNAME OUTPUT-FILE requires the installed library libNAME + -module build a library that can dlopened + -no-fast-install disable the fast-install mode + -no-install link a not-installable executable + -no-undefined declare that a library does not refer to external symbols + -o OUTPUT-FILE create OUTPUT-FILE from the specified objects + -objectlist FILE Use a list of object files found in FILE to specify objects + -precious-files-regex REGEX + don't remove output files matching REGEX + -release RELEASE specify package release information + -rpath LIBDIR the created library will eventually be installed in LIBDIR + -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries + -static do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + +All other options (arguments beginning with \`-') are ignored. + +Every other argument is treated as a filename. Files ending in \`.la' are +treated as uninstalled libtool libraries, other files are standard or library +object files. + +If the OUTPUT-FILE ends in \`.la', then a libtool library is created, +only library objects (\`.lo' files) may be specified, and \`-rpath' is +required, except when creating a convenience library. + +If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created +using \`ar' and \`ranlib', or on Windows using \`lib'. + +If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file +is created, otherwise an executable program is created." + ;; + +uninstall) + $echo \ +"Usage: $modename [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... + +Remove libraries from an installation directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, all the files associated with it are deleted. +Otherwise, only FILE itself is deleted using RM." + ;; + +*) + $echo "$modename: invalid operation mode \`$mode'" 1>&2 + $echo "$help" 1>&2 + exit $EXIT_FAILURE + ;; +esac + +$echo +$echo "Try \`$modename --help' for more information about other modes." + +exit $? + +# The TAGs below are defined such that we never get into a situation +# in which we disable both kinds of libraries. Given conflicting +# choices, we go for a static library, that is the most portable, +# since we can't tell whether shared libraries were disabled because +# the user asked for that or because the platform doesn't support +# them. This is particularly important on AIX, because we don't +# support having both static and shared libraries enabled at the same +# time on that platform, so we default to a shared-only configuration. +# If a disable-shared tag is given, we'll fallback to a static-only +# configuration. But we'll never go from static-only to shared-only. + +# ### BEGIN LIBTOOL TAG CONFIG: disable-shared +disable_libs=shared +# ### END LIBTOOL TAG CONFIG: disable-shared + +# ### BEGIN LIBTOOL TAG CONFIG: disable-static +disable_libs=static +# ### END LIBTOOL TAG CONFIG: disable-static + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: diff --git a/qpid/cpp/build-aux/mdate-sh b/qpid/cpp/build-aux/mdate-sh new file mode 100755 index 0000000000..cd916c0a34 --- /dev/null +++ b/qpid/cpp/build-aux/mdate-sh @@ -0,0 +1,201 @@ +#!/bin/sh +# Get modification time of a file or directory and pretty-print it. + +scriptversion=2005-06-29.22 + +# Copyright (C) 1995, 1996, 1997, 2003, 2004, 2005 Free Software +# Foundation, Inc. +# written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, June 1995 +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to <bug-automake@gnu.org> or send patches to +# <automake-patches@gnu.org>. + +case $1 in + '') + echo "$0: No file. Try \`$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: mdate-sh [--help] [--version] FILE + +Pretty-print the modification time of FILE. + +Report bugs to <bug-automake@gnu.org>. +EOF + exit $? + ;; + -v | --v*) + echo "mdate-sh $scriptversion" + exit $? + ;; +esac + +# Prevent date giving response in another language. +LANG=C +export LANG +LC_ALL=C +export LC_ALL +LC_TIME=C +export LC_TIME + +# GNU ls changes its time format in response to the TIME_STYLE +# variable. Since we cannot assume `unset' works, revert this +# variable to its documented default. +if test "${TIME_STYLE+set}" = set; then + TIME_STYLE=posix-long-iso + export TIME_STYLE +fi + +save_arg1=$1 + +# Find out how to get the extended ls output of a file or directory. +if ls -L /dev/null 1>/dev/null 2>&1; then + ls_command='ls -L -l -d' +else + ls_command='ls -l -d' +fi + +# A `ls -l' line looks as follows on OS/2. +# drwxrwx--- 0 Aug 11 2001 foo +# This differs from Unix, which adds ownership information. +# drwxrwx--- 2 root root 4096 Aug 11 2001 foo +# +# To find the date, we split the line on spaces and iterate on words +# until we find a month. This cannot work with files whose owner is a +# user named `Jan', or `Feb', etc. However, it's unlikely that `/' +# will be owned by a user whose name is a month. So we first look at +# the extended ls output of the root directory to decide how many +# words should be skipped to get the date. + +# On HPUX /bin/sh, "set" interprets "-rw-r--r--" as options, so the "x" below. +set x`ls -l -d /` + +# Find which argument is the month. +month= +command= +until test $month +do + shift + # Add another shift to the command. + command="$command shift;" + case $1 in + Jan) month=January; nummonth=1;; + Feb) month=February; nummonth=2;; + Mar) month=March; nummonth=3;; + Apr) month=April; nummonth=4;; + May) month=May; nummonth=5;; + Jun) month=June; nummonth=6;; + Jul) month=July; nummonth=7;; + Aug) month=August; nummonth=8;; + Sep) month=September; nummonth=9;; + Oct) month=October; nummonth=10;; + Nov) month=November; nummonth=11;; + Dec) month=December; nummonth=12;; + esac +done + +# Get the extended ls output of the file or directory. +set dummy x`eval "$ls_command \"\$save_arg1\""` + +# Remove all preceding arguments +eval $command + +# Because of the dummy argument above, month is in $2. +# +# On a POSIX system, we should have +# +# $# = 5 +# $1 = file size +# $2 = month +# $3 = day +# $4 = year or time +# $5 = filename +# +# On Darwin 7.7.0 and 7.6.0, we have +# +# $# = 4 +# $1 = day +# $2 = month +# $3 = year or time +# $4 = filename + +# Get the month. +case $2 in + Jan) month=January; nummonth=1;; + Feb) month=February; nummonth=2;; + Mar) month=March; nummonth=3;; + Apr) month=April; nummonth=4;; + May) month=May; nummonth=5;; + Jun) month=June; nummonth=6;; + Jul) month=July; nummonth=7;; + Aug) month=August; nummonth=8;; + Sep) month=September; nummonth=9;; + Oct) month=October; nummonth=10;; + Nov) month=November; nummonth=11;; + Dec) month=December; nummonth=12;; +esac + +case $3 in + ???*) day=$1;; + *) day=$3; shift;; +esac + +# Here we have to deal with the problem that the ls output gives either +# the time of day or the year. +case $3 in + *:*) set `date`; eval year=\$$# + case $2 in + Jan) nummonthtod=1;; + Feb) nummonthtod=2;; + Mar) nummonthtod=3;; + Apr) nummonthtod=4;; + May) nummonthtod=5;; + Jun) nummonthtod=6;; + Jul) nummonthtod=7;; + Aug) nummonthtod=8;; + Sep) nummonthtod=9;; + Oct) nummonthtod=10;; + Nov) nummonthtod=11;; + Dec) nummonthtod=12;; + esac + # For the first six month of the year the time notation can also + # be used for files modified in the last year. + if (expr $nummonth \> $nummonthtod) > /dev/null; + then + year=`expr $year - 1` + fi;; + *) year=$3;; +esac + +# The result. +echo $day $month $year + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/qpid/cpp/build-aux/missing b/qpid/cpp/build-aux/missing new file mode 100755 index 0000000000..1c8ff7049d --- /dev/null +++ b/qpid/cpp/build-aux/missing @@ -0,0 +1,367 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. + +scriptversion=2006-05-10.23 + +# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003, 2004, 2005, 2006 +# Free Software Foundation, Inc. +# Originally by Fran,cois Pinard <pinard@iro.umontreal.ca>, 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +run=: +sed_output='s/.* --output[ =]\([^ ]*\).*/\1/p' +sed_minuso='s/.* -o \([^ ]*\).*/\1/p' + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +msg="missing on your system" + +case $1 in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + # Exit code 63 means version mismatch. This often happens + # when the user try to use an ancient version of a tool on + # a file that requires a minimum version. In this case we + # we should proceed has if the program had been absent, or + # if --run hadn't been passed. + if test $? = 63; then + run=: + msg="probably too old" + fi + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + autom4te touch the output file, or create a stub one + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + tar try tar, gnutar, gtar, then tar without non-portable flags + yacc create \`y.tab.[ch]', if possible, from existing .[ch] + +Send bug reports to <bug-automake@gnu.org>." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + +esac + +# Now exit if we have it, but it failed. Also exit now if we +# don't have it and --version was passed (most likely to detect +# the program). +case $1 in + lex|yacc) + # Not GNU programs, they don't have --version. + ;; + + tar) + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + exit 1 + fi + ;; + + *) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + elif test "x$2" = "x--version" || test "x$2" = "x--help"; then + # Could not run --version or --help. This is probably someone + # running `$TOOL --version' or `$TOOL --help' to check whether + # $TOOL exists and not knowing $TOOL uses missing. + exit 1 + fi + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case $1 in + aclocal*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case $f in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake*) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + autom4te) + echo 1>&2 "\ +WARNING: \`$1' is needed, but is $msg. + You might have modified some files without having the + proper tools for further handling them. + You can get \`$1' as part of \`Autoconf' from any GNU + archive site." + + file=`echo "$*" | sed -n "$sed_output"` + test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo "#! /bin/sh" + echo "# Created by GNU Automake missing as a replacement of" + echo "# $ $@" + echo "exit 0" + chmod +x $file + exit 1 + fi + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' $msg. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if test $# -ne 1; then + eval LASTARG="\${$#}" + case $LASTARG in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if test -f "$SRCFILE"; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if test -f "$SRCFILE"; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if test ! -f y.tab.h; then + echo >y.tab.h + fi + if test ! -f y.tab.c; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if test $# -ne 1; then + eval LASTARG="\${$#}" + case $LASTARG in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if test -f "$SRCFILE"; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if test ! -f lex.yy.c; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n "$sed_output"` + test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit 1 + fi + ;; + + makeinfo) + echo 1>&2 "\ +WARNING: \`$1' is $msg. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + # The file to touch is that specified with -o ... + file=`echo "$*" | sed -n "$sed_output"` + test -z "$file" && file=`echo "$*" | sed -n "$sed_minuso"` + if test -z "$file"; then + # ... or it is the one specified with @setfilename ... + infile=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n ' + /^@setfilename/{ + s/.* \([^ ]*\) *$/\1/ + p + q + }' $infile` + # ... or it is derived from the source name (dir/f.texi becomes f.info) + test -z "$file" && file=`echo "$infile" | sed 's,.*/,,;s,.[^.]*$,,'`.info + fi + # If the file does not exist, the user really needs makeinfo; + # let's fail without touching anything. + test -f $file || exit 1 + touch $file + ;; + + tar) + shift + + # We have already tried tar in the generic part. + # Look for gnutar/gtar before invocation to avoid ugly error + # messages. + if (gnutar --version > /dev/null 2>&1); then + gnutar "$@" && exit 0 + fi + if (gtar --version > /dev/null 2>&1); then + gtar "$@" && exit 0 + fi + firstarg="$1" + if shift; then + case $firstarg in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + case $firstarg in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + fi + + echo 1>&2 "\ +WARNING: I can't seem to be able to run \`tar' with the given arguments. + You may want to install GNU tar or Free paxutils, or check the + command line arguments." + exit 1 + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and is $msg. + You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequisites for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-end: "$" +# End: diff --git a/qpid/cpp/configure.ac b/qpid/cpp/configure.ac new file mode 100644 index 0000000000..c062fc3f8b --- /dev/null +++ b/qpid/cpp/configure.ac @@ -0,0 +1,212 @@ +dnl Process this file with autoconf to produce a configure script. +dnl +dnl This file is free software; as a special exception the author gives +dnl unlimited permission to copy and/or distribute it, with or without +dnl modifications, as long as this notice is preserved. +dnl +dnl This program is distributed in the hope that it will be useful, but +dnl WITHOUT ANY WARRANTY, to the extent permitted by law; without even the +dnl implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +AC_INIT([qpidc], [0.2], [qpid-dev@incubator.apache.org]) +AC_CONFIG_AUX_DIR([build-aux]) +AM_INIT_AUTOMAKE([dist-bzip2 subdir-objects]) + +# Minimum Autoconf version required. +AC_PREREQ(2.59) + +AC_CONFIG_HEADERS([src/config.h]) +AC_CONFIG_SRCDIR([qpidc.spec.in]) + +AC_PROG_CC_STDC +AM_PROG_CC_C_O +AC_PROG_CXX +AC_USE_SYSTEM_EXTENSIONS +AC_LANG([C++]) + +# AM_MISSING_PROG([HELP2MAN], [help2man]) +AC_CHECK_PROG([HELP2MAN], [help2man], [help2man]) +test -z "$HELP2MAN" && AC_MSG_ERROR([Missing help2man installation (try "yum install help2man").]) + +AC_ARG_ENABLE(warnings, +[ --enable-warnings turn on lots of compiler warnings (recommended)], +[case "${enableval}" in + yes|no) ;; + *) AC_MSG_ERROR([bad value ${enableval} for warnings option]) ;; + esac], + [enableval=yes]) + +# Warnings: Enable as many as possible, keep the code clean. Please +# do not disable warnings or remove -Werror without discussing on +# qpid-dev list. +# +# The following warnings are deliberately omitted, they warn on valid code. +# -Wunreachable-code -Wpadded -Winline +# -Wshadow - warns about boost headers. + +if test "${enableval}" = yes; then + gl_COMPILER_FLAGS(-Werror) + gl_COMPILER_FLAGS(-pedantic) + gl_COMPILER_FLAGS(-Wall) + gl_COMPILER_FLAGS(-Wextra) + gl_COMPILER_FLAGS(-Wno-shadow) + gl_COMPILER_FLAGS(-Wpointer-arith) + gl_COMPILER_FLAGS(-Wcast-qual) + gl_COMPILER_FLAGS(-Wcast-align) + gl_COMPILER_FLAGS(-Wno-long-long) + gl_COMPILER_FLAGS(-Wvolatile-register-var) + gl_COMPILER_FLAGS(-Winvalid-pch) + gl_COMPILER_FLAGS(-Wno-system-headers) + gl_COMPILER_FLAGS(-Woverloaded-virtual) + AC_SUBST([WARNING_CFLAGS], [$COMPILER_FLAGS]) + AC_DEFINE([lint], 1, [Define to 1 if the compiler is checking for lint.]) + COMPILER_FLAGS= +fi + +AC_DISABLE_STATIC +AC_PROG_LIBTOOL +AC_SUBST([LIBTOOL_DEPS]) + +# For libraries (libcommon) that use dlopen, dlerror, etc., +# test whether we need to link with -ldl. +gl_saved_libs=$LIBS + AC_SEARCH_LIBS(dlopen, [dl], + [test "$ac_cv_search_dlopen" = "none required" || + LIB_DLOPEN=$ac_cv_search_dlopen]) + AC_SUBST([LIB_DLOPEN]) +LIBS=$gl_saved_libs + +# Set the argument to be used in "libtool -version-info ARG". +QPID_CURRENT=1 +QPID_REVISION=0 +QPID_AGE=1 +LIBTOOL_VERSION_INFO_ARG=$QPID_CURRENT:$QPID_REVISION:$QPID_AGE +AC_SUBST(LIBTOOL_VERSION_INFO_ARG) + +gl_CLOCK_TIME + +# Check for cppunit support. +CPPUNIT_MINIMUM_VERSION=1.10.2 +AM_PATH_CPPUNIT([$CPPUNIT_MINIMUM_VERSION], , [CPPUNIT_LIBS=-lcppunit]) +CPPUNIT_CXXFLAGS=$CPPUNIT_CFLAGS +AC_SUBST(CPPUNIT_LIBS) +AC_SUBST(CPPUNIT_CXXFLAGS) + +# Enable Valgrind +AC_ARG_ENABLE([valgrind], + [AS_HELP_STRING([--enable-valgrind], + [run valgrind memory checker on tests, if available (default yes)])], + [case $enableval in + yes|no) enable_VALGRIND=$enableval;; + *) AC_MSG_ERROR([Invalid value for --enable-valgrind: $enableval]);; + esac], + [enable_VALGRIND=yes] +) + +# We use valgrind for the tests. See if it's available. +# Check for it unconditionally, so we don't have to duplicate its +# use of AC_SUBST([VALGRIND]). +AC_CHECK_PROG([VALGRIND], [valgrind], [valgrind]) +test "$enable_VALGRIND" = no && VALGRIND= + +# If rpmlint is availalbe we'll run it when building RPMs. +AC_CHECK_PROG([RPMLINT], [rpmlint], [rpmlint]) +AM_CONDITIONAL([HAS_RPMLINT], [test -n "$RPMLINT"]) + +# Code generation: generated code is included in the distribution +# so code generation is only required in an svn checkout. +# It requires several external tools and files, which we check for here. + +AC_CHECK_PROG([RUBY], [ruby], [ruby]) +test -n "$RUBY" && generate=yes +test -z "$RUBY" && AC_MSG_ERROR([Missing ruby installation (try "yum install ruby").]) + +specdir=`pwd`/$srcdir/../specs +AMQP_PREVIEW_XML=$specdir/amqp.0-10-preview.xml +AMQP_FINAL_XML=$specdir/amqp.0-10.xml +AC_SUBST(AMQP_PREVIEW_XML) +AC_SUBST(AMQP_FINAL_XML) +AM_CONDITIONAL([GENERATE], [ls $AMQP_FINAL_XML >/dev/null]) + +# URL and download URL for the package. +URL=http://rhm.et.redhat.com/qpidc +AC_SUBST(URL) +DOWNLOAD_URL=http://rhm.et.redhat.com/download +AC_SUBST(DOWNLOAD_URL) + +# Check for headers from required devel kits. +AC_CHECK_HEADERS([boost/shared_ptr.hpp uuid/uuid.h],, + AC_MSG_ERROR([Missing required header files.])) + +# Check for optional CPG requirement. +LDFLAGS="$LDFLAGS -L/usr/lib/openais -L/usr/lib64/openais" + +AC_ARG_WITH([cpg], + [AS_HELP_STRING([--with-cpg], [Build with CPG support])], + [case "${withval}" in + yes) # yes - enable + with_CPG=yes + AC_CHECK_LIB([cpg],[cpg_initialize],,[AC_MSG_ERROR([libcpg not found, install openais])]) + AC_CHECK_HEADERS([openais/cpg.h],,[AC_MSG_ERROR([openais/cpg.h not found, install openais-devel])]) + ;; + no) # no -disable + with_CPG=no + ;; + *) AC_MSG_ERROR([Bad value ${withval} for --with-cpg option]) ;; + esac], + [ # not specified - enable if libs/headers available. + with_CPG=yes + AC_CHECK_HEADERS([openais/cpg.h],,[with_CPG=no]) + AC_CHECK_LIB([cpg],[cpg_initialize],,[with_CPG=no]) + ] +) +AM_CONDITIONAL([CPG], [test x$with_CPG = xyes]) +if test x$with_CPG = xyes; then + CPPFLAGS+=" -DCPG" +fi + + +# Setup --with-sasl/--without-sasl as arguments to configure +AC_ARG_WITH([sasl], + [AS_HELP_STRING([--with-sasl], [Build with SASL authentication support])], + [WANT_SASL="$withval"], + [WANT_SASL=check]) + +# Make sure --with-sasl/--without-sasl were only give yes|no|check +AS_IF([test "x$WANT_SASL" != xyes -a \ + "x$WANT_SASL" != xno -a \ + "x$WANT_SASL" != xcheck], + [AC_MSG_ERROR([Bad value for --with-sasl: $withval])]) + +# If we weren't explicitly asked /not/ to test, i.e. not given --without-sasl +AS_IF([test "x$WANT_SASL" != xno], + # Perform tests for headers and libraries. Remember, AC_CHECK_LIB + # will give you some useful default behavior, e.g. setup LDFLAGS, if + # you do not give it a second argument, so try not to + [AC_CHECK_HEADER([sasl/sasl.h], , [HAVE_SASL_H=no]) + AC_CHECK_LIB([sasl2], [sasl_checkpass], , [HAVE_SASL_LIB=no]) + # If any of the tests failed + AS_IF([test "x$HAVE_SASL_H" = xno -o \ + "x$HAVE_SASL_LIB" = xno], + # And we were given --with, then fail + [AS_IF([test "x$WANT_SASL" = xyes], + [AC_MSG_ERROR([sasl requested but not available])])], + # Otherwise, no tests failed, setup AC_SUBST/AC_DEFINE/AM_CONDITIONALs + [AC_DEFINE([BROKER_SASL_NAME], ["qpidd"], + [The SASL app name for the qpid Broker]) + AC_DEFINE([HAVE_SASL], [1], [Enable if libsasl is present])])]) + +# Files to generate +AC_CONFIG_FILES([ + qpidc.spec + Makefile + examples/Makefile + src/Makefile + src/tests/Makefile + docs/man/Makefile + docs/api/Makefile + docs/api/user.doxygen + docs/api/developer.doxygen + ]) +AC_OUTPUT + diff --git a/qpid/cpp/docs/api/Makefile.am b/qpid/cpp/docs/api/Makefile.am new file mode 100644 index 0000000000..bde8b5fa3d --- /dev/null +++ b/qpid/cpp/docs/api/Makefile.am @@ -0,0 +1,18 @@ +# +# Run doxygen to generate HTML doc. +# Generate dependency files so its rebuilt only when needed. +# + +# TODO aconway 2007-04-12: html should have a +# dependency on source/header files. + +EXTRA_DIST = html user.doxygen developer.doxygen html.timestamp + +html: html.timestamp + +html.timestamp: + doxygen user.doxygen + touch $@ + +clean-local: + rm -rf html html-dev html.timestamp diff --git a/qpid/cpp/docs/api/developer.doxygen.in b/qpid/cpp/docs/api/developer.doxygen.in new file mode 100644 index 0000000000..b267b12b90 --- /dev/null +++ b/qpid/cpp/docs/api/developer.doxygen.in @@ -0,0 +1,1241 @@ + # ---------------------------------------------------------------- +# Doxygen settings for Qpid developer documentation. +# +# ---------------------------------------------------------------- + +# Doxyfile 1.4.6 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = Qpid + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 0 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = . + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# This tag can be used to specify the encoding used in the generated output. +# The encoding is not always determined by the language that is chosen, +# but also whether or not the output is meant for Windows or non-Windows users. +# In case there is a difference, setting the USE_WINDOWS_ENCODING tag to YES +# forces the Windows encoding (this is the default for the Windows binary), +# whereas setting the tag to NO uses a Unix-style encoding (the default for +# all platforms other than Windows). + +USE_WINDOWS_ENCODING = NO + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = YES + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to +# include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = YES + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = YES + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = YES + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = doxygen.log + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @abs_top_srcdir@/src + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: + + +FILE_PATTERNS = *.h *.cpp + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = test + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html-dev + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = NO + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_WIDTH tag can be used to set the maximum allowed width +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_WIDTH = 1024 + +# The MAX_DOT_GRAPH_HEIGHT tag can be used to set the maximum allows height +# (in pixels) of the graphs generated by dot. If a graph becomes larger than +# this value, doxygen will try to truncate the graph, so that it fits within +# the specified constraint. Beware that most browsers cannot cope with very +# large images. + +MAX_DOT_GRAPH_HEIGHT = 1024 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = YES diff --git a/qpid/cpp/docs/api/user.doxygen.in b/qpid/cpp/docs/api/user.doxygen.in new file mode 100644 index 0000000000..4b65b73347 --- /dev/null +++ b/qpid/cpp/docs/api/user.doxygen.in @@ -0,0 +1,1218 @@ +# ---------------------------------------------------------------- +# Doxygen settings for Qpid user documentation. +# +# Note: Only public members of classes that are part of the public API +# should be documented here. For complete developer documentation use +# the developer.doxygen configuration. +# ---------------------------------------------------------------- + +# Doxyfile 1.4.6 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = Qpid + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = 0 + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = . + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Brazilian, Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, +# Dutch, Finnish, French, German, Greek, Hungarian, Italian, Japanese, +# Japanese-en (Japanese with English messages), Korean, Korean-en, Norwegian, +# Polish, Portuguese, Romanian, Russian, Serbian, Slovak, Slovene, Spanish, +# Swedish, and Ukrainian. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = YES + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = YES + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like the Qt-style comments (thus requiring an +# explicit @brief command for a brief description. + +JAVADOC_AUTOBRIEF = YES + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the DETAILS_AT_TOP tag is set to YES then Doxygen +# will output the detailed description near the top, like JavaDoc. +# If set to NO, the detailed description appears after the member +# documentation. + +DETAILS_AT_TOP = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for Java. +# For instance, namespaces will be presented as packages, qualified scopes +# will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want to +# include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = NO + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = YES + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = YES + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = YES + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = NO + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from the +# version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = doxygen.log + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @abs_top_srcdir@/src @abs_top_builddir@/src/gen + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: + + +FILE_PATTERNS = *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = test + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES (the default) +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES (the default) +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = NO + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compressed HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# If the GENERATE_TREEVIEW tag is set to YES, a side panel will be +# generated containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (for instance Mozilla 1.0+, +# Netscape 6.0+, Internet explorer 5.0+, or Konqueror). Windows users are +# probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4wide + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. This is useful +# if you want to understand what is going on. On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = YES + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT tags are set to YES then doxygen will +# generate a call dependency graph for every global function or class method. +# Note that enabling this option will significantly increase the time of a run. +# So in most cases it will be better to enable call graphs for selected +# functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that a graph may be further truncated if the graph's +# image dimensions are not sufficient to fit the graph (see MAX_DOT_GRAPH_WIDTH +# and MAX_DOT_GRAPH_HEIGHT). If 0 is used for the depth value (the default), +# the graph is not depth-constrained. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, which results in a white background. +# Warning: Depending on the platform used, enabling this option may lead to +# badly anti-aliased labels on the edges of a graph (i.e. they become hard to +# read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- + +# The SEARCHENGINE tag specifies whether or not a search engine should be +# used. If set to NO the values of all tags below this one will be ignored. + +SEARCHENGINE = YES diff --git a/qpid/cpp/docs/man/Makefile.am b/qpid/cpp/docs/man/Makefile.am new file mode 100644 index 0000000000..c8df4eab6a --- /dev/null +++ b/qpid/cpp/docs/man/Makefile.am @@ -0,0 +1,20 @@ +dist_man_MANS = qpidd.1 + +man_aux = $(dist_man_MANS:.1=.x) +EXTRA_DIST = $(man_aux) +DISTCLEANFILES = $(dist_man_MANS) + +dist-hook: $(man_aux) + +qpidd.1: $(srcdir)/qpidd.x $(top_builddir)/src/qpidd + +# Depend on configure.ac to get version number changes. +$(dist_man_MANS): $(top_srcdir)/configure.ac + +SUFFIXES = .x .1 +.x.1: + @rm -f $@ + @echo "Updating man page $@" + $(HELP2MAN) --no-info --include=$(srcdir)/$*.x --output=$@-t ../../src/$* + @chmod a-w $@-t + @mv $@-t $@ diff --git a/qpid/cpp/docs/man/qpidd.x b/qpid/cpp/docs/man/qpidd.x new file mode 100644 index 0000000000..edbe7b3bff --- /dev/null +++ b/qpid/cpp/docs/man/qpidd.x @@ -0,0 +1,46 @@ +[NAME] + +qpidd \- the Qpid AMQP Broker Daemon + +[SYNOPSIS] + +qpidd [options] + +[DESCRIPTION] + +An AMQP broker daemon that stores, routes and forwards messages using +the Advanced Message Queueing Protocol (AMQP). + +[OPTIONS] + +Options may be specified via command line, environment variable or configuration file. See FILES and ENVIRONMENT below for details. + +[FILES] +.I /etc/qpidd.conf +.RS +Default configuration file. +.RE + +Configuration file settings are over-ridden by command line or environment variable settings. '--config <file>' or 'export QPID_CONFIG=<file>' specifies an alternate file. + +Each line is a name=value pair. Blank lines and lines beginning with # are ignored. For example: + + # My qpidd configuration file. + port=6000 + max-connections=10 + log-output=stdout + log-output=/tmp/qpidd.log + +[ENVIRONMENT] +.I QPID_<option> +.RS +There is an environment variable for each option. +.RE + +The environment variable is the option name in uppercase, prefixed with QPID_ and '.' or '-' are replaced with '_'. Environment settings are over-ridden by command line settings. For example: + + export QPID_PORT=6000 + export QPID_MAX_CONNECTIONS=10 + export QPID_LOG_OUTPUT=/tmp/qpidd.log + + diff --git a/qpid/cpp/etc/qpidd b/qpid/cpp/etc/qpidd new file mode 100755 index 0000000000..d9076191d6 --- /dev/null +++ b/qpid/cpp/etc/qpidd @@ -0,0 +1,85 @@ +#!/bin/bash +# +# qpidd Startup script for the Qpid messaging daemon. +# + +### BEGIN INIT INFO +# Provides: qpidd +# Required-Start: $local_fs +# Required-Stop: $local_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: start or stop qpidd +# Description: Qpidd is an AMQP broker. It receives, stores, routes and forwards messages using the AMQP protcol. +### END INIT INFO + +# chkconfig: - 85 15 +# description: Qpidd is an AMQP broker. It receives, stores, routes and forwards messages using the AMQP protcol. +# processname: qpidd + +prog=qpidd +lockfile=/var/lock/subsys/$prog +pidfile=/var/run/qpidd.pid + +# Source function library. +. /etc/rc.d/init.d/functions + +if [ -f /etc/sysconfig/$prog ] ; then + . /etc/sysconfig/$prog +fi + +RETVAL=0 + +start() { + echo -n $"Starting Qpid AMQP daemon: " + daemon --pidfile $pidfile --check $prog --user qpidd $prog --daemon $QPIDD_OPTIONS + RETVAL=$? + echo + [ $RETVAL = 0 ] && touch $lockfile + if [ $RETVAL = 0 ]; then + touch $pidfile + chown qpidd.qpidd $pidfile + runuser -s /bin/sh qpidd -c "qpidd -c > $pidfile" + fi + return $RETVAL +} + +stop() { + echo -n $"Stopping Qpid AMQP daemon: " + killproc -p ${pidfile} $prog + RETVAL=$? + echo + [ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile} +} + +reload() { + echo 1>&2 $"$0: reload not supported" + exit 3 +} + +restart() { + stop + start +} + +# See how we were called. +case "$1" in + start|stop|restart|reload) + $1 + ;; + status) + status $prog + RETVAL=$? + ;; + force-reload) + restart + ;; + try-restart) + [ -e $lockfile ] && restart || : + ;; + *) + echo 1>&2 $"Usage: $0 {start|stop|restart|condrestart|status}" + exit 1 +esac + +exit $RETVAL diff --git a/qpid/cpp/etc/qpidd.conf b/qpid/cpp/etc/qpidd.conf new file mode 100644 index 0000000000..8064767e78 --- /dev/null +++ b/qpid/cpp/etc/qpidd.conf @@ -0,0 +1,2 @@ +# Configuration file for qpidd. +# Using default settings, qpidd --help to see defaults. diff --git a/qpid/cpp/examples/Makefile.am b/qpid/cpp/examples/Makefile.am new file mode 100644 index 0000000000..540e092ea2 --- /dev/null +++ b/qpid/cpp/examples/Makefile.am @@ -0,0 +1,79 @@ + +# List all example files here +nobase_pkgdata_DATA= \ + examples/Makefile \ + examples/request-response/client.cpp \ + examples/request-response/server.cpp \ + examples/request-response/Makefile \ + examples/fanout/Makefile \ + examples/fanout/listener.cpp \ + examples/fanout/fanout_producer.cpp \ + examples/pub-sub/Makefile \ + examples/pub-sub/topic_publisher.cpp \ + examples/pub-sub/topic_listener.cpp \ + examples/direct/Makefile \ + examples/direct/direct_producer.cpp \ + examples/direct/listener.cpp \ + examples/direct/declare_queues.cpp + +VERIFY_FILES= verify verify_all \ + examples/request-response/verify \ + examples/request-response/verify.in \ + examples/request-response/verify_cpp_python \ + examples/request-response/verify_cpp_python.in \ + examples/request-response/verify_python_cpp \ + examples/request-response/verify_python_cpp.in \ + examples/fanout/verify \ + examples/fanout/verify.in \ + examples/fanout/verify_cpp_python \ + examples/fanout/verify_cpp_python.in \ + examples/fanout/verify_python_cpp \ + examples/fanout/verify_python_cpp.in \ + examples/pub-sub/verify \ + examples/pub-sub/verify.in \ + examples/pub-sub/verify_cpp_python \ + examples/pub-sub/verify_cpp_python.in \ + examples/pub-sub/verify_python_cpp \ + examples/pub-sub/verify_python_cpp.in \ + examples/direct/verify \ + examples/direct/verify.in \ + examples/direct/verify_cpp_python \ + examples/direct/verify_cpp_python.in \ + examples/direct/verify_python_cpp \ + examples/direct/verify_python_cpp.in + +EXTRA_DIST=$(nobase_pkgdata_DATA) $(VERIFY_FILES) + +# Note: we don't use normal automake SUBDIRS because the example +# makefiles don't understand all the recursive automake targets. + +clean-local: + cd examples; $(MAKE) clean + rm -f examples/*/*.out examples/*/*.wait + +abs_top_builddir=@abs_top_builddir@ +abs_top_srcdir=@abs_top_srcdir@ + +# Build the examples - copy sources to the build tree in VPATH build. +all-local: + test -d examples || cp -R $(srcdir)/examples . + cd examples && $(MAKE) CXX="$(CXX)" CXXFLAGS="$(CXXFLAGS) -I../../$(top_srcdir)/src -I../../$(top_srcdir)/src/gen -I../../$(top_builddir)/src/gen -L../../$(top_builddir)/src/.libs -Wl,-rpath,$(abs_top_builddir)/src/.libs" all + +# FIXME aconway 2008-03-25: Re-enable when python client has been fixed +# to find .spec via PYTHONPATH. +# +# Verify the examples in the buid tree. +# check-local: all-local verify +# $(srcdir)/verify_all $(abs_top_srcdir)/.. + +# TODO: +# create a tarball for testing installed examples. +# installcheck-local to use the tarball on installed example and clean up after. +# Build and verify installed C++ examples, clean up to avoid rpmbuild warnings. +# EXAMPLE_FLAGS=-I$(DESTDIR)$(includedir) -L$(DESTDIR)$(libdir) -Wl,-rpath,$(DESTDIR)$(libdir) +# EXAMPLE_DIR=$(DESTDIR)$(pkgdatadir)/examples/cpp +# installcheck-local: +# cd $(EXAMPLE_DIR) && $(MAKE) CXX="$(CXX)" CXXFLAGS="$(EXAMPLE_FLAGS)" all +# cd $(EXAMPLE_DIR) && QPIDD=$(sbindir)/qpidd $(srcdir)/verify * +# cd $(EXAMPLE_DIR) && $(MAKE) clean + diff --git a/qpid/cpp/examples/examples/Makefile b/qpid/cpp/examples/examples/Makefile new file mode 100644 index 0000000000..8591bd3361 --- /dev/null +++ b/qpid/cpp/examples/examples/Makefile @@ -0,0 +1,6 @@ +SUBDIRS=direct fanout pub-sub request-response +all: + for d in $(SUBDIRS); do ( cd $$d; $(MAKE) $@; ) ; done +clean: + for d in $(SUBDIRS); do ( cd $$d; $(MAKE) $@; ) ; done + diff --git a/qpid/cpp/examples/examples/direct/Makefile b/qpid/cpp/examples/examples/direct/Makefile new file mode 100644 index 0000000000..380b5cfcb4 --- /dev/null +++ b/qpid/cpp/examples/examples/direct/Makefile @@ -0,0 +1,10 @@ +CXX=g++ +CXXFLAGS= +LDFLAGS=-lqpidclient + +PROGRAMS=declare_queues direct_producer listener + +all: $(PROGRAMS) + +clean: + rm -f $(PROGRAMS) diff --git a/qpid/cpp/examples/examples/direct/declare_queues.cpp b/qpid/cpp/examples/examples/direct/declare_queues.cpp new file mode 100644 index 0000000000..de7eff0490 --- /dev/null +++ b/qpid/cpp/examples/examples/direct/declare_queues.cpp @@ -0,0 +1,86 @@ +/* + * + * 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. + * + */ + +/** + * declare_queues.cpp + * + * This program is one of three programs designed to be used + * together. These programs use the "amq.direct" exchange. + * + * direct_config_queues.cpp (this program): + * + * Creates a queue on a broker, binding a routing key to route + * messages to that queue. + * + * direct_producer.cpp: + * + * Publishes to a broker, specifying a routing key. + * + * listener.cpp + * + * Reads from a queue on the broker using a message listener. + * + */ + +#include <qpid/client/Connection.h> +#include <qpid/client/Session.h> + +#include <unistd.h> +#include <cstdlib> +#include <iostream> + +using namespace qpid::client; +using namespace qpid::framing; + +using std::string; + + +int main(int argc, char** argv) { + const char* host = argc>1 ? argv[1] : "127.0.0.1"; + int port = argc>2 ? atoi(argv[2]) : 5672; + Connection connection; + Message msg; + try { + connection.open(host, port); + Session session = connection.newSession(ASYNC); + + + //--------- Main body of program -------------------------------------------- + + // Create a queue named "message_queue", and route all messages whose + // routing key is "routing_key to this newly created queue. + + session.queueDeclare(arg::queue="message_queue"); + session.queueBind(arg::exchange="amq.direct", arg::queue="message_queue", arg::routingKey="routing_key"); + + //----------------------------------------------------------------------------- + + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; + +} + + + diff --git a/qpid/cpp/examples/examples/direct/direct_producer.cpp b/qpid/cpp/examples/examples/direct/direct_producer.cpp new file mode 100644 index 0000000000..9b40f733c2 --- /dev/null +++ b/qpid/cpp/examples/examples/direct/direct_producer.cpp @@ -0,0 +1,106 @@ +/* + * + * 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. + * + */ + + +/** + * direct_producer.cpp: + * + * This program is one of three programs designed to be used + * together. These programs do not specify the exchange type - the + * default exchange type is the direct exchange. + * + * create_queues.cpp: + * + * Creates a queue on a broker, binding a routing key to route + * messages to that queue. + * + * direct_producer.cpp (this program): + * + * Publishes to a broker, specifying a routing key. + * + * listener.cpp + * + * Reads from a queue on the broker using a message listener. + * + */ + + +#include <qpid/client/Connection.h> +#include <qpid/client/Session.h> +#include <qpid/client/Message.h> + + +#include <unistd.h> +#include <cstdlib> +#include <iostream> + +#include <sstream> + +using namespace qpid::client; +using namespace qpid::framing; + +using std::stringstream; +using std::string; + +int main(int argc, char** argv) { + const char* host = argc>1 ? argv[1] : "127.0.0.1"; + int port = argc>2 ? atoi(argv[2]) : 5672; + Connection connection; + Message message; + try { + connection.open(host, port); + Session session = connection.newSession(ASYNC); + + //--------- Main body of program -------------------------------------------- + + // The routing key is a message property. We will use the same + // routing key for each message, so we'll set this property + // just once. (In most simple cases, there is no need to set + // other message properties.) + + message.getDeliveryProperties().setRoutingKey("routing_key"); + + // Now send some messages ... + + for (int i=0; i<10; i++) { + stringstream message_data; + message_data << "Message " << i; + + message.setData(message_data.str()); + session.messageTransfer(arg::content=message, arg::destination="amq.direct"); + } + + // And send a final message to indicate termination. + + message.setData("That's all, folks!"); + session.messageTransfer(arg::content=message, arg::destination="amq.direct"); + + //----------------------------------------------------------------------------- + + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/examples/examples/direct/listener.cpp b/qpid/cpp/examples/examples/direct/listener.cpp new file mode 100644 index 0000000000..ae6a7699f1 --- /dev/null +++ b/qpid/cpp/examples/examples/direct/listener.cpp @@ -0,0 +1,90 @@ +/* + * + * 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. + * + */ + +/** + * listener.cpp: This program reads messages fro a queue on + * the broker using a message listener. + */ + +#include <qpid/client/Dispatcher.h> +#include <qpid/client/Connection.h> +#include <qpid/client/Session.h> +#include <qpid/client/Message.h> +#include <qpid/client/SubscriptionManager.h> + +#include <unistd.h> +#include <cstdlib> +#include <iostream> + +using namespace qpid::client; +using namespace qpid::framing; + + +class Listener : public MessageListener{ + private: + SubscriptionManager& subscriptions; + public: + Listener(SubscriptionManager& subscriptions); + virtual void received(Message& message); +}; + +Listener::Listener(SubscriptionManager& subs) : subscriptions(subs) +{} + +void Listener::received(Message& message) { + std::cout << "Message: " << message.getData() << std::endl; + if (message.getData() == "That's all, folks!") { + std::cout << "Shutting down listener for " << message.getDestination() + << std::endl; + subscriptions.cancel(message.getDestination()); + } +} + +int main(int argc, char** argv) { + const char* host = argc>1 ? argv[1] : "127.0.0.1"; + int port = argc>2 ? atoi(argv[2]) : 5672; + Connection connection; + Message msg; + try { + connection.open(host, port); + Session session = connection.newSession(ASYNC); + + //--------- Main body of program -------------------------------------------- + + SubscriptionManager subscriptions(session); + // Create a listener and subscribe it to the queue named "message_queue" + Listener listener(subscriptions); + subscriptions.subscribe(listener, "message_queue"); + // Deliver messages until the subscription is cancelled + // by Listener::received() + subscriptions.run(); + + //--------------------------------------------------------------------------- + + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/examples/examples/direct/verify b/qpid/cpp/examples/examples/direct/verify new file mode 100644 index 0000000000..ac0464ef80 --- /dev/null +++ b/qpid/cpp/examples/examples/direct/verify @@ -0,0 +1,3 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +clients ./declare_queues ./direct_producer ./listener +outputs ./declare_queues.out ./direct_producer.out ./listener.out diff --git a/qpid/cpp/examples/examples/direct/verify.in b/qpid/cpp/examples/examples/direct/verify.in new file mode 100644 index 0000000000..d1e95f1151 --- /dev/null +++ b/qpid/cpp/examples/examples/direct/verify.in @@ -0,0 +1,15 @@ +==== declare_queues.out +==== direct_producer.out +==== listener.out +Message: Message 0 +Message: Message 1 +Message: Message 2 +Message: Message 3 +Message: Message 4 +Message: Message 5 +Message: Message 6 +Message: Message 7 +Message: Message 8 +Message: Message 9 +Message: That's all, folks! +Shutting down listener for message_queue diff --git a/qpid/cpp/examples/examples/direct/verify_cpp_python b/qpid/cpp/examples/examples/direct/verify_cpp_python new file mode 100644 index 0000000000..4dc445ba27 --- /dev/null +++ b/qpid/cpp/examples/examples/direct/verify_cpp_python @@ -0,0 +1,4 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +py=$PYTHON_EXAMPLES/direct +clients ./declare_queues ./direct_producer $py/direct_consumer.py +outputs ./declare_queues.out ./direct_producer.out $py/direct_consumer.py.out diff --git a/qpid/cpp/examples/examples/direct/verify_cpp_python.in b/qpid/cpp/examples/examples/direct/verify_cpp_python.in new file mode 100644 index 0000000000..1a329be59a --- /dev/null +++ b/qpid/cpp/examples/examples/direct/verify_cpp_python.in @@ -0,0 +1,14 @@ +==== declare_queues.out +==== direct_producer.out +==== direct_consumer.py.out +Message 0 +Message 1 +Message 2 +Message 3 +Message 4 +Message 5 +Message 6 +Message 7 +Message 8 +Message 9 +That's all, folks! diff --git a/qpid/cpp/examples/examples/direct/verify_python_cpp b/qpid/cpp/examples/examples/direct/verify_python_cpp new file mode 100644 index 0000000000..fe4893e120 --- /dev/null +++ b/qpid/cpp/examples/examples/direct/verify_python_cpp @@ -0,0 +1,5 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +py=$PYTHON_EXAMPLES/direct +clients $py/declare_queues.py $py/direct_producer.py ./listener +outputs $py/declare_queues.py.out $py/direct_producer.py.out ./listener.out + diff --git a/qpid/cpp/examples/examples/direct/verify_python_cpp.in b/qpid/cpp/examples/examples/direct/verify_python_cpp.in new file mode 100644 index 0000000000..6f35255b18 --- /dev/null +++ b/qpid/cpp/examples/examples/direct/verify_python_cpp.in @@ -0,0 +1,15 @@ +==== declare_queues.py.out +==== direct_producer.py.out +==== listener.out +Message: message 0 +Message: message 1 +Message: message 2 +Message: message 3 +Message: message 4 +Message: message 5 +Message: message 6 +Message: message 7 +Message: message 8 +Message: message 9 +Message: That's all, folks! +Shutting down listener for message_queue diff --git a/qpid/cpp/examples/examples/fanout/Makefile b/qpid/cpp/examples/examples/fanout/Makefile new file mode 100644 index 0000000000..7963af7ddf --- /dev/null +++ b/qpid/cpp/examples/examples/fanout/Makefile @@ -0,0 +1,10 @@ +CXX=g++ +CXXFLAGS= +LDFLAGS=-lqpidclient + +PROGRAMS=fanout_producer listener + +all: $(PROGRAMS) + +clean: + rm -f $(PROGRAMS) diff --git a/qpid/cpp/examples/examples/fanout/fanout_producer.cpp b/qpid/cpp/examples/examples/fanout/fanout_producer.cpp new file mode 100644 index 0000000000..8ae6bbc242 --- /dev/null +++ b/qpid/cpp/examples/examples/fanout/fanout_producer.cpp @@ -0,0 +1,104 @@ +/* + * + * 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. + * + */ + + +/** + * fanout_producer.cpp: + * + * This program is one of three programs designed to be used + * together. These programs do not specify the exchange type - the + * default exchange type is the direct exchange. + * + * declare_queues.cpp: + * + * Creates a queue on a broker, binding a routing key to route + * messages to that queue. + * + * fanout_producer.cpp (this program): + * + * Publishes to a broker, specifying a routing key. + * + * listener.cpp + * + * Reads from a queue on the broker using a message listener. + * + */ + + +#include <qpid/client/Connection.h> +#include <qpid/client/Session.h> +#include <qpid/client/Message.h> + + +#include <unistd.h> +#include <cstdlib> +#include <iostream> + +#include <sstream> + +using namespace qpid::client; +using namespace qpid::framing; + +using std::stringstream; +using std::string; + +int main(int argc, char** argv) { + const char* host = argc>1 ? argv[1] : "127.0.0.1"; + int port = argc>2 ? atoi(argv[2]) : 5672; + Connection connection; + Message message; + try { + connection.open(host, port); + Session session = connection.newSession(ASYNC); + + //--------- Main body of program -------------------------------------------- + + // Unlike topic exchanges and direct exchanges, a fanout + // exchange need not set a routing key. + + Message message; + + // Now send some messages ... + + for (int i=0; i<10; i++) { + stringstream message_data; + message_data << "Message " << i; + + message.setData(message_data.str()); + session.messageTransfer(arg::content=message, arg::destination="amq.fanout"); + } + + // And send a final message to indicate termination. + + message.setData("That's all, folks!"); + session.messageTransfer(arg::content=message, arg::destination="amq.fanout"); + + //----------------------------------------------------------------------------- + + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/examples/examples/fanout/listener.cpp b/qpid/cpp/examples/examples/fanout/listener.cpp new file mode 100644 index 0000000000..2860528b1f --- /dev/null +++ b/qpid/cpp/examples/examples/fanout/listener.cpp @@ -0,0 +1,105 @@ +/* + * + * 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. + * + */ + +/** + * listener.cpp: This program reads messages fro a queue on + * the broker using a message listener. + */ + +#include <qpid/client/Dispatcher.h> +#include <qpid/client/Connection.h> +#include <qpid/client/Session.h> +#include <qpid/client/Message.h> +#include <qpid/client/SubscriptionManager.h> + +#include <unistd.h> +#include <cstdlib> +#include <iostream> + +using namespace qpid::client; +using namespace qpid::framing; + + +class Listener : public MessageListener{ + private: + SubscriptionManager& subscriptions; + public: + Listener(SubscriptionManager& subscriptions); + virtual void received(Message& message); +}; + +Listener::Listener(SubscriptionManager& subs) : subscriptions(subs) +{} + +void Listener::received(Message& message) { + std::cout << "Message: " << message.getData() << std::endl; + if (message.getData() == "That's all, folks!") { + std::cout << "Shutting down listener for " << message.getDestination() + << std::endl; + subscriptions.cancel(message.getDestination()); + } +} + +int main(int argc, char** argv) { + const char* host = argc>1 ? argv[1] : "127.0.0.1"; + int port = argc>2 ? atoi(argv[2]) : 5672; + Connection connection; + Message msg; + try { + connection.open(host, port); + Session session = connection.newSession(ASYNC); + + //--------- Main body of program -------------------------------------------- + + // Unique name for private queue: + std::string myQueue=session.getId().str(); + // Declear my queue. + session.queueDeclare(arg::queue=myQueue, arg::exclusive=true, + arg::autoDelete=true); + // Bind my queue to the fanout exchange. + // Note no routingKey required, the fanout exchange delivers + // all messages to all bound queues unconditionally. + session.queueBind(arg::exchange="amq.fanout", arg::queue=myQueue); + + // Create a listener and subscribe it to my queue. + SubscriptionManager subscriptions(session); + Listener listener(subscriptions); + subscriptions.subscribe(listener, myQueue); + + // Wait for the broker to indicate that our queues have been created. + session.sync(); + + // Deliver messages until the subscription is cancelled + // by Listener::received() + std::cout << "Listening" << std::endl; + subscriptions.run(); + + //--------------------------------------------------------------------------- + + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/examples/examples/fanout/verify b/qpid/cpp/examples/examples/fanout/verify new file mode 100644 index 0000000000..ace4a6dfee --- /dev/null +++ b/qpid/cpp/examples/examples/fanout/verify @@ -0,0 +1,6 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +background "Listening" ./listener +background "Listening" ./listener +background "Listening" ./listener +clients ./fanout_producer +outputs ./fanout_producer.out "./listener.out | remove_uuid" "./listenerX.out | remove_uuid" "./listenerXX.out | remove_uuid" diff --git a/qpid/cpp/examples/examples/fanout/verify.in b/qpid/cpp/examples/examples/fanout/verify.in new file mode 100644 index 0000000000..8f8612ce67 --- /dev/null +++ b/qpid/cpp/examples/examples/fanout/verify.in @@ -0,0 +1,43 @@ +==== fanout_producer.out +==== listener.out | remove_uuid +Listening +Message: Message 0 +Message: Message 1 +Message: Message 2 +Message: Message 3 +Message: Message 4 +Message: Message 5 +Message: Message 6 +Message: Message 7 +Message: Message 8 +Message: Message 9 +Message: That's all, folks! +Shutting down listener for +==== listenerX.out | remove_uuid +Listening +Message: Message 0 +Message: Message 1 +Message: Message 2 +Message: Message 3 +Message: Message 4 +Message: Message 5 +Message: Message 6 +Message: Message 7 +Message: Message 8 +Message: Message 9 +Message: That's all, folks! +Shutting down listener for +==== listenerXX.out | remove_uuid +Listening +Message: Message 0 +Message: Message 1 +Message: Message 2 +Message: Message 3 +Message: Message 4 +Message: Message 5 +Message: Message 6 +Message: Message 7 +Message: Message 8 +Message: Message 9 +Message: That's all, folks! +Shutting down listener for diff --git a/qpid/cpp/examples/examples/fanout/verify_cpp_python b/qpid/cpp/examples/examples/fanout/verify_cpp_python new file mode 100644 index 0000000000..e840e68f91 --- /dev/null +++ b/qpid/cpp/examples/examples/fanout/verify_cpp_python @@ -0,0 +1,7 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +py=$PYTHON_EXAMPLES/fanout +background "Subscribed" $py/fanout_consumer.py +background "Subscribed" $py/fanout_consumer.py +clients ./fanout_producer +outputs ./fanout_producer.out "$py/fanout_consumer.py.out | remove_uuid64" "$py/fanout_consumer.pyX.out | remove_uuid64" + diff --git a/qpid/cpp/examples/examples/fanout/verify_cpp_python.in b/qpid/cpp/examples/examples/fanout/verify_cpp_python.in new file mode 100644 index 0000000000..fac2b365d3 --- /dev/null +++ b/qpid/cpp/examples/examples/fanout/verify_cpp_python.in @@ -0,0 +1,31 @@ +==== fanout_producer.out +==== fanout_consumer.py.out | remove_uuid64 +Messages queue: +Subscribed to queue +Response: Message 0 +Response: Message 1 +Response: Message 2 +Response: Message 3 +Response: Message 4 +Response: Message 5 +Response: Message 6 +Response: Message 7 +Response: Message 8 +Response: Message 9 +Response: That's all, folks! +No more messages! +==== fanout_consumer.pyX.out | remove_uuid64 +Messages queue: +Subscribed to queue +Response: Message 0 +Response: Message 1 +Response: Message 2 +Response: Message 3 +Response: Message 4 +Response: Message 5 +Response: Message 6 +Response: Message 7 +Response: Message 8 +Response: Message 9 +Response: That's all, folks! +No more messages! diff --git a/qpid/cpp/examples/examples/fanout/verify_python_cpp b/qpid/cpp/examples/examples/fanout/verify_python_cpp new file mode 100644 index 0000000000..d9b3361523 --- /dev/null +++ b/qpid/cpp/examples/examples/fanout/verify_python_cpp @@ -0,0 +1,7 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +py=$PYTHON_EXAMPLES/fanout +background "Listening" ./listener +background "Listening" ./listener +clients $py/fanout_producer.py +outputs $py/fanout_producer.py.out "./listener.out | remove_uuid" "./listenerX.out | remove_uuid" + diff --git a/qpid/cpp/examples/examples/fanout/verify_python_cpp.in b/qpid/cpp/examples/examples/fanout/verify_python_cpp.in new file mode 100644 index 0000000000..8f9e959053 --- /dev/null +++ b/qpid/cpp/examples/examples/fanout/verify_python_cpp.in @@ -0,0 +1,29 @@ +==== fanout_producer.py.out +==== listener.out | remove_uuid +Listening +Message: message 0 +Message: message 1 +Message: message 2 +Message: message 3 +Message: message 4 +Message: message 5 +Message: message 6 +Message: message 7 +Message: message 8 +Message: message 9 +Message: That's all, folks! +Shutting down listener for +==== listenerX.out | remove_uuid +Listening +Message: message 0 +Message: message 1 +Message: message 2 +Message: message 3 +Message: message 4 +Message: message 5 +Message: message 6 +Message: message 7 +Message: message 8 +Message: message 9 +Message: That's all, folks! +Shutting down listener for diff --git a/qpid/cpp/examples/examples/pub-sub/Makefile b/qpid/cpp/examples/examples/pub-sub/Makefile new file mode 100644 index 0000000000..4b2dd52efd --- /dev/null +++ b/qpid/cpp/examples/examples/pub-sub/Makefile @@ -0,0 +1,10 @@ +CXX=g++ +CXXFLAGS= +LDFLAGS=-lqpidclient + +PROGRAMS=topic_listener topic_publisher + +all: $(PROGRAMS) + +clean: + rm -f $(PROGRAMS) diff --git a/qpid/cpp/examples/examples/pub-sub/topic_listener.cpp b/qpid/cpp/examples/examples/pub-sub/topic_listener.cpp new file mode 100644 index 0000000000..e5292db703 --- /dev/null +++ b/qpid/cpp/examples/examples/pub-sub/topic_listener.cpp @@ -0,0 +1,175 @@ +/* + * + * 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. + * + */ + +/** + * topic_listener.cpp: + * + * This program is one of three programs designed to be used + * together. These programs use the topic exchange. + * + * topic_config_queues.cpp: + * + * Creates a queue on a broker, binding a routing key to route + * messages to that queue. + * + * topic_publisher.cpp: + * + * Publishes to a broker, specifying a routing key. + * + * topic_listener.cpp (this program): + * + * Reads from a queue on the broker using a message listener. + * + */ + +#include <qpid/client/Connection.h> +#include <qpid/client/Session.h> +#include <qpid/client/Message.h> +#include <qpid/client/MessageListener.h> +#include <qpid/client/Queue.h> +#include <qpid/client/SubscriptionManager.h> + +#include <unistd.h> +#include <cstdlib> +#include <iostream> +#include <set> + +using namespace qpid::client; +using namespace qpid::framing; + + +class Listener : public MessageListener { + private: + Session& session; + SubscriptionManager subscriptions; + public: + Listener(Session& session); + virtual void prepareQueue(std::string queue, std::string routing_key); + virtual void received(Message& message); + virtual void listen(); + ~Listener() { }; +}; + + +/* + * Listener::Listener + * + * Subscribe to the queue, route it to a client destination for the + * listener. (The destination name merely identifies the destination + * in the listener, you can use any name as long as you use the same + * name for the listener). + */ + +Listener::Listener(Session& session) : + session(session), + subscriptions(session) +{ +} + + +void Listener::prepareQueue(std::string queue, std::string routing_key) { + + /* Create a unique queue name for this consumer by concatenating + * the queue name parameter with the Session ID. + */ + + queue += session.getId().str(); + std::cout << "Declaring queue: " << queue << std::endl; + + /* Declare an exclusive queue on the broker + */ + + session.queueDeclare(arg::queue=queue, arg::exclusive=true); + + /* Route messages to the new queue if they match the routing key. + * + * Also route any messages to with the "control" routing key to + * this queue so we know when it's time to stop. A publisher sends + * a message with the content "That's all, Folks!", using the + * "control" routing key, when it is finished. + */ + + session.queueBind(arg::exchange="amq.topic", arg::queue=queue, arg::routingKey=routing_key); + session.queueBind(arg::exchange="amq.topic", arg::queue=queue, arg::routingKey="control"); + + /* + * subscribe to the queue using the subscription manager. + */ + + std::cout << "Subscribing to queue " << queue << std::endl; + subscriptions.subscribe(*this, queue); +} + +void Listener::received(Message& message) { + std::cout << "Message: " << message.getData() << " from " << message.getDestination() << std::endl; + + if (message.getData() == "That's all, folks!") { + std::cout << "Shutting down listener for " << message.getDestination() << std::endl; + subscriptions.cancel(message.getDestination()); + } +} + +void Listener::listen() { + // Receive messages + subscriptions.run(); +} + +int main(int argc, char** argv) { + const char* host = argc>1 ? argv[1] : "127.0.0.1"; + int port = argc>2 ? atoi(argv[2]) : 5672; + Connection connection; + try { + connection.open(host, port); + Session session = connection.newSession(ASYNC); + + //--------- Main body of program -------------------------------------------- + + // Create a listener for the session + + Listener listener(session); + + // Subscribe to messages on the queues we are interested in + + listener.prepareQueue("usa", "usa.#"); + listener.prepareQueue("europe", "europe.#"); + listener.prepareQueue("news", "#.news"); + listener.prepareQueue("weather", "#.weather"); + + // Wait for the broker to indicate that our queues have been created. + session.sync(); + + std::cout << "Listening for messages ..." << std::endl; + + // Give up control and receive messages + listener.listen(); + + + //----------------------------------------------------------------------------- + + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/examples/examples/pub-sub/topic_publisher.cpp b/qpid/cpp/examples/examples/pub-sub/topic_publisher.cpp new file mode 100644 index 0000000000..94cd3a0f56 --- /dev/null +++ b/qpid/cpp/examples/examples/pub-sub/topic_publisher.cpp @@ -0,0 +1,125 @@ +/* + * + * 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. + * + */ + + +/** + * topic_publisher.cpp: + * + * This program is one of three programs designed to be used + * together. These programs use the topic exchange. + * + * topic_config_queues.cpp: + * + * Creates a queue on a broker, binding a routing key to route + * messages to that queue. + * + * topic_publisher.cpp (this program): + * + * Publishes to a broker, specifying a routing key. + * + * topic_listener.cpp + * + * Reads from a queue on the broker using a message listener. + * + */ + + +#include <qpid/client/Connection.h> +#include <qpid/client/Session.h> +#include <qpid/client/Message.h> + + +#include <unistd.h> +#include <cstdlib> +#include <iostream> + +#include <sstream> + +using namespace qpid::client; +using namespace qpid::framing; + +using std::stringstream; +using std::string; + +void publish_messages(Session& session, string routing_key) +{ + Message message; + + // Set the routing key once, we'll use the same routing key for all + // messages. + + message.getDeliveryProperties().setRoutingKey(routing_key); + for (int i=0; i<5; i++) { + stringstream message_data; + message_data << "Message " << i; + + message.setData(message_data.str()); + session.messageTransfer(arg::content=message, arg::destination="amq.topic"); + } + +} + +/* + * no_more_messages() + * + * Send a message to indicate that no more messages are coming. + * Use the 'control' routing key (see comments in topic_config_queues.cpp). + * + */ + +void no_more_messages(Session& session) +{ + Message message; + + message.getDeliveryProperties().setRoutingKey("control"); + message.setData("That's all, folks!"); + session.messageTransfer(arg::content=message, arg::destination="amq.topic"); +} + +int main(int argc, char** argv) { + const char* host = argc>1 ? argv[1] : "127.0.0.1"; + int port = argc>2 ? atoi(argv[2]) : 5672; + Connection connection; + Message message; + try { + connection.open(host, port); + Session session = connection.newSession(ASYNC); + + //--------- Main body of program -------------------------------------------- + + publish_messages(session, "usa.news"); + publish_messages(session, "usa.weather"); + publish_messages(session, "europe.news"); + publish_messages(session, "europe.weather"); + + no_more_messages(session); + + //----------------------------------------------------------------------------- + + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/examples/examples/pub-sub/verify b/qpid/cpp/examples/examples/pub-sub/verify new file mode 100644 index 0000000000..3589a4c9da --- /dev/null +++ b/qpid/cpp/examples/examples/pub-sub/verify @@ -0,0 +1,4 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +background "Listening" ./topic_listener +clients ./topic_publisher +outputs ./topic_publisher.out "topic_listener.out | remove_uuid | sort" diff --git a/qpid/cpp/examples/examples/pub-sub/verify.in b/qpid/cpp/examples/examples/pub-sub/verify.in new file mode 100644 index 0000000000..6413c5c788 --- /dev/null +++ b/qpid/cpp/examples/examples/pub-sub/verify.in @@ -0,0 +1,59 @@ +==== topic_publisher.out +==== topic_listener.out | remove_uuid | sort +Declaring queue: europe +Declaring queue: news +Declaring queue: usa +Declaring queue: weather +Listening for messages ... +Message: Message 0 from europe +Message: Message 0 from europe +Message: Message 0 from news +Message: Message 0 from news +Message: Message 0 from usa +Message: Message 0 from usa +Message: Message 0 from weather +Message: Message 0 from weather +Message: Message 1 from europe +Message: Message 1 from europe +Message: Message 1 from news +Message: Message 1 from news +Message: Message 1 from usa +Message: Message 1 from usa +Message: Message 1 from weather +Message: Message 1 from weather +Message: Message 2 from europe +Message: Message 2 from europe +Message: Message 2 from news +Message: Message 2 from news +Message: Message 2 from usa +Message: Message 2 from usa +Message: Message 2 from weather +Message: Message 2 from weather +Message: Message 3 from europe +Message: Message 3 from europe +Message: Message 3 from news +Message: Message 3 from news +Message: Message 3 from usa +Message: Message 3 from usa +Message: Message 3 from weather +Message: Message 3 from weather +Message: Message 4 from europe +Message: Message 4 from europe +Message: Message 4 from news +Message: Message 4 from news +Message: Message 4 from usa +Message: Message 4 from usa +Message: Message 4 from weather +Message: Message 4 from weather +Message: That's all, folks! from europe +Message: That's all, folks! from news +Message: That's all, folks! from usa +Message: That's all, folks! from weather +Shutting down listener for europe +Shutting down listener for news +Shutting down listener for usa +Shutting down listener for weather +Subscribing to queue europe +Subscribing to queue news +Subscribing to queue usa +Subscribing to queue weather diff --git a/qpid/cpp/examples/examples/pub-sub/verify_cpp_python b/qpid/cpp/examples/examples/pub-sub/verify_cpp_python new file mode 100644 index 0000000000..ecc573eed3 --- /dev/null +++ b/qpid/cpp/examples/examples/pub-sub/verify_cpp_python @@ -0,0 +1,6 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +py=$PYTHON_EXAMPLES/pubsub +background "Queues created" $py/topic_subscriber.py +clients ./topic_publisher +outputs ./topic_publisher.out "$py/topic_subscriber.py.out | remove_uuid64 | sort" + diff --git a/qpid/cpp/examples/examples/pub-sub/verify_cpp_python.in b/qpid/cpp/examples/examples/pub-sub/verify_cpp_python.in new file mode 100644 index 0000000000..b3c9e750f5 --- /dev/null +++ b/qpid/cpp/examples/examples/pub-sub/verify_cpp_python.in @@ -0,0 +1,51 @@ +==== topic_publisher.out +==== topic_subscriber.py.out | remove_uuid64 | sort +Message 0 +Message 0 +Message 0 +Message 0 +Message 0 +Message 0 +Message 0 +Message 0 +Message 1 +Message 1 +Message 1 +Message 1 +Message 1 +Message 1 +Message 1 +Message 1 +Message 2 +Message 2 +Message 2 +Message 2 +Message 2 +Message 2 +Message 2 +Message 2 +Message 3 +Message 3 +Message 3 +Message 3 +Message 3 +Message 3 +Message 3 +Message 3 +Message 4 +Message 4 +Message 4 +Message 4 +Message 4 +Message 4 +Message 4 +Message 4 +Messages queue: europe +Messages queue: news +Messages queue: usa +Messages queue: weather +Queues created - please start the topic producer +That's all, folks! +That's all, folks! +That's all, folks! +That's all, folks! diff --git a/qpid/cpp/examples/examples/pub-sub/verify_python_cpp b/qpid/cpp/examples/examples/pub-sub/verify_python_cpp new file mode 100644 index 0000000000..2ddaad58c2 --- /dev/null +++ b/qpid/cpp/examples/examples/pub-sub/verify_python_cpp @@ -0,0 +1,6 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +py=$PYTHON_EXAMPLES/pubsub +background "Listening" ./topic_listener +clients $py/topic_publisher.py +outputs $py/topic_publisher.py.out "topic_listener.out | remove_uuid | sort" + diff --git a/qpid/cpp/examples/examples/pub-sub/verify_python_cpp.in b/qpid/cpp/examples/examples/pub-sub/verify_python_cpp.in new file mode 100644 index 0000000000..97fccf0a32 --- /dev/null +++ b/qpid/cpp/examples/examples/pub-sub/verify_python_cpp.in @@ -0,0 +1,59 @@ +==== topic_publisher.py.out +==== topic_listener.out | remove_uuid | sort +Declaring queue: europe +Declaring queue: news +Declaring queue: usa +Declaring queue: weather +Listening for messages ... +Message: message 0 from europe +Message: message 0 from europe +Message: message 0 from news +Message: message 0 from news +Message: message 0 from usa +Message: message 0 from usa +Message: message 0 from weather +Message: message 0 from weather +Message: message 1 from europe +Message: message 1 from europe +Message: message 1 from news +Message: message 1 from news +Message: message 1 from usa +Message: message 1 from usa +Message: message 1 from weather +Message: message 1 from weather +Message: message 2 from europe +Message: message 2 from europe +Message: message 2 from news +Message: message 2 from news +Message: message 2 from usa +Message: message 2 from usa +Message: message 2 from weather +Message: message 2 from weather +Message: message 3 from europe +Message: message 3 from europe +Message: message 3 from news +Message: message 3 from news +Message: message 3 from usa +Message: message 3 from usa +Message: message 3 from weather +Message: message 3 from weather +Message: message 4 from europe +Message: message 4 from europe +Message: message 4 from news +Message: message 4 from news +Message: message 4 from usa +Message: message 4 from usa +Message: message 4 from weather +Message: message 4 from weather +Message: That's all, folks! from europe +Message: That's all, folks! from news +Message: That's all, folks! from usa +Message: That's all, folks! from weather +Shutting down listener for europe +Shutting down listener for news +Shutting down listener for usa +Shutting down listener for weather +Subscribing to queue europe +Subscribing to queue news +Subscribing to queue usa +Subscribing to queue weather diff --git a/qpid/cpp/examples/examples/request-response/Makefile b/qpid/cpp/examples/examples/request-response/Makefile new file mode 100644 index 0000000000..e7ef2590d3 --- /dev/null +++ b/qpid/cpp/examples/examples/request-response/Makefile @@ -0,0 +1,10 @@ +CXX=g++ +CXXFLAGS= +LDFLAGS=-lqpidclient + +PROGRAMS=client server + +all: $(PROGRAMS) + +clean: + rm -f $(PROGRAMS) diff --git a/qpid/cpp/examples/examples/request-response/client.cpp b/qpid/cpp/examples/examples/request-response/client.cpp new file mode 100644 index 0000000000..9f82bd9d9e --- /dev/null +++ b/qpid/cpp/examples/examples/request-response/client.cpp @@ -0,0 +1,179 @@ +/* + * + * 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. + * + */ + + +/** + * client.cpp + * + * This program is one of two programs that illustrate the + * request/response pattern. + * + * client.cpp (this program) + * + * Make requests of a service, print the response. + * + * service.cpp + * + * Accept requests, reverse the letters in each message, and + * return it as a response. + * + */ + + +#include <qpid/client/Connection.h> +#include <qpid/client/Dispatcher.h> +#include <qpid/client/Session.h> +#include <qpid/client/Message.h> +#include <qpid/client/MessageListener.h> + +#include <unistd.h> +#include <cstdlib> +#include <iostream> + +#include <sstream> + +using namespace qpid::client; +using namespace qpid::framing; + +class Listener : public MessageListener{ +private: + Session session; + std::string destination_name; + Dispatcher dispatcher; + int counter; +public: + Listener(Session& session, string destination_name): + destination_name(destination_name), + dispatcher(session), + session(session), + counter(0) + {}; + + virtual void listen(); + virtual void wait(); + virtual void received(Message& message); + ~Listener() { }; +}; + + +void Listener::listen() { + std::cout << "Activating response queue listener for: " <<destination_name << std::endl; + + session.messageSubscribe(arg::queue=destination_name, arg::destination=destination_name); + + session.messageFlow(arg::destination=destination_name, arg::unit=0, arg::value=1);//messages ### Define a constant? + session.messageFlow(arg::destination=destination_name, arg::unit=1, arg::value=0xFFFFFFFF);//bytes ###### Define a constant? + + + dispatcher.listen(destination_name, this); +} + + +void Listener::wait() { + std::cout << "Waiting for all responses to arrive ..." << std::endl; + dispatcher.run(); +} + + +void Listener::received(Message& message) { + std::cout << "Response: " << message.getData() << std::endl; + + ++ counter; + if (counter > 3) { + std::cout << "Shutting down listener for " << destination_name << std::endl; + dispatcher.stop(); + } +} + + +using std::stringstream; +using std::string; + +int main(int argc, char** argv) { + const char* host = argc>1 ? argv[1] : "127.0.0.1"; + int port = argc>2 ? atoi(argv[2]) : 5672; + Connection connection; + Message request; + try { + connection.open(host, port); + Session session = connection.newSession(ASYNC); + + //--------- Main body of program -------------------------------------------- + + // Create a response queue so the server can send us responses + // to our requests. Use the client's session ID as the name + // of the response queue. + + stringstream response_queue; + response_queue << "client" << session.getId(); + + // Use the name of the response queue as the routing key + + session.queueDeclare(arg::queue=response_queue.str()); + session.queueBind(arg::exchange="amq.direct", arg::queue=response_queue.str(), arg::routingKey=response_queue.str()); + + // Create a listener for the response queue and start listening. + + Listener listener(session, response_queue.str()); + listener.listen(); + + + // The routing key for the request queue is simply + // "request", and all clients use the same routing key. + // + // Each client sends the name of their own response queue so + // the service knows where to route messages. + + request.getDeliveryProperties().setRoutingKey("request"); + request.getMessageProperties().setReplyTo(ReplyTo("amq.direct", response_queue.str())); + + // Now send some requests ... + + string s[] = { + "Twas brillig, and the slithy toves", + "Did gire and gymble in the wabe.", + "All mimsy were the borogroves,", + "And the mome raths outgrabe." + }; + + + for (int i=0; i<4; i++) { + request.setData(s[i]); + session.messageTransfer(arg::content=request, arg::destination="amq.direct"); + std::cout << "Request: " << s[i] << std::endl; + } + + // And wait for any outstanding responses to arrive + + listener.wait(); + + + //----------------------------------------------------------------------------- + + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/examples/examples/request-response/server.cpp b/qpid/cpp/examples/examples/request-response/server.cpp new file mode 100644 index 0000000000..0de2ce5234 --- /dev/null +++ b/qpid/cpp/examples/examples/request-response/server.cpp @@ -0,0 +1,165 @@ +/* + * + * 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. + * + */ + + +/** + * server.cpp + * + * This program is one of two programs that illustrate the + * request/response pattern. + * + * client.cpp + * + * Make requests of a service, print the response. + * + * server.cpp (this program) + * + * Accept requests, reverse the letters in each message, and + * return it as a response. + * + */ + + +#include <qpid/client/Connection.h> +#include <qpid/client/Dispatcher.h> +#include <qpid/client/Session.h> +#include <qpid/client/Message.h> +#include <qpid/client/MessageListener.h> + + +#include <unistd.h> +#include <cstdlib> +#include <iostream> +#include <algorithm> + +#include <sstream> +#include <string> + +using namespace qpid::client; +using namespace qpid::framing; +using std::stringstream; +using std::string; + +class Listener : public MessageListener{ +private: + std::string destination_name; + Dispatcher dispatcher; + Session session; +public: + Listener(Session& session, string destination_name): + destination_name(destination_name), + dispatcher(session), + session(session) + {}; + + virtual void listen(); + virtual void received(Message& message); + virtual void wait(); + ~Listener() { }; +}; + + +void Listener::listen() { + std::cout << "Activating request queue listener for: " <<destination_name << std::endl; + + session.messageSubscribe(arg::queue=destination_name, arg::destination=destination_name); + + // ##### Should not be needed. Sigh. + session.messageFlow(arg::destination=destination_name, arg::unit=0, arg::value=1);//messages ### Define a constant? + session.messageFlow(arg::destination=destination_name, arg::unit=1, arg::value=0xFFFFFFFF);//bytes ###### Define a constant? + + dispatcher.listen(destination_name, this); +} + + +void Listener::wait() { + std::cout << "Waiting for requests" << std::endl; + dispatcher.run(); +} + + +void Listener::received(Message& request) { + + Message response; + + // Get routing key for response from the request's replyTo property + + string routingKey; + + if (request.getMessageProperties().hasReplyTo()) { + routingKey = request.getMessageProperties().getReplyTo().getRoutingKey(); + } else { + std::cout << "Error: " << "No routing key for request (" << request.getData() << ")" << std::endl; + return; + } + + std::cout << "Request: " << request.getData() << " (" <<routingKey << ")" << std::endl; + + // Transform message content to upper case + std::string s = request.getData(); + std::transform (s.begin(), s.end(), s.begin(), toupper); + response.setData(s); + + // Send it back to the user + response.getDeliveryProperties().setRoutingKey(routingKey); + session.messageTransfer(arg::content=response, arg::destination="amq.direct"); +} + + +int main(int argc, char** argv) { + const char* host = argc>1 ? argv[1] : "127.0.0.1"; + int port = argc>2 ? atoi(argv[2]) : 5672; + Connection connection; + Message message; + try { + connection.open(host, port); + Session session = connection.newSession(ASYNC); + + //--------- Main body of program -------------------------------------------- + + // Create a request queue for clients to use when making + // requests. + + string request_queue = "request"; + + // Use the name of the request queue as the routing key + + session.queueDeclare(arg::queue=request_queue); + session.queueBind(arg::exchange="amq.direct", arg::queue=request_queue, arg::routingKey=request_queue); + + // Create a listener for the request queue and start listening. + + Listener listener(session, request_queue); + listener.listen(); + listener.wait(); + + + //----------------------------------------------------------------------------- + + connection.close(); + return 0; + } catch(const std::exception& error) { + std::cout << error.what() << std::endl; + } + return 1; +} + + diff --git a/qpid/cpp/examples/examples/request-response/verify b/qpid/cpp/examples/examples/request-response/verify new file mode 100644 index 0000000000..76007ff8d2 --- /dev/null +++ b/qpid/cpp/examples/examples/request-response/verify @@ -0,0 +1,5 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +background "Waiting" ./server +clients ./client +kill %% # Must kill the server. +outputs "./client.out | remove_uuid" "server.out | remove_uuid" diff --git a/qpid/cpp/examples/examples/request-response/verify.in b/qpid/cpp/examples/examples/request-response/verify.in new file mode 100644 index 0000000000..7925dc5671 --- /dev/null +++ b/qpid/cpp/examples/examples/request-response/verify.in @@ -0,0 +1,19 @@ +==== client.out | remove_uuid +Activating response queue listener for: client +Request: Twas brillig, and the slithy toves +Request: Did gire and gymble in the wabe. +Request: All mimsy were the borogroves, +Request: And the mome raths outgrabe. +Waiting for all responses to arrive ... +Response: TWAS BRILLIG, AND THE SLITHY TOVES +Response: DID GIRE AND GYMBLE IN THE WABE. +Response: ALL MIMSY WERE THE BOROGROVES, +Response: AND THE MOME RATHS OUTGRABE. +Shutting down listener for client +==== server.out | remove_uuid +Activating request queue listener for: request +Waiting for requests +Request: Twas brillig, and the slithy toves (client) +Request: Did gire and gymble in the wabe. (client) +Request: All mimsy were the borogroves, (client) +Request: And the mome raths outgrabe. (client) diff --git a/qpid/cpp/examples/examples/request-response/verify_cpp_python b/qpid/cpp/examples/examples/request-response/verify_cpp_python new file mode 100644 index 0000000000..9d71d51c37 --- /dev/null +++ b/qpid/cpp/examples/examples/request-response/verify_cpp_python @@ -0,0 +1,5 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +background "Request server running" $PYTHON_EXAMPLES/request-response/server.py +clients ./client +kill %% # Must kill the server. +outputs "./client.out | remove_uuid" "$PYTHON_EXAMPLES/request-response/server.py.out | remove_uuid64" diff --git a/qpid/cpp/examples/examples/request-response/verify_cpp_python.in b/qpid/cpp/examples/examples/request-response/verify_cpp_python.in new file mode 100644 index 0000000000..280484bd2a --- /dev/null +++ b/qpid/cpp/examples/examples/request-response/verify_cpp_python.in @@ -0,0 +1,15 @@ +==== client.out | remove_uuid +Activating response queue listener for: client +Request: Twas brillig, and the slithy toves +Request: Did gire and gymble in the wabe. +Request: All mimsy were the borogroves, +Request: And the mome raths outgrabe. +Waiting for all responses to arrive ... +Response: TWAS BRILLIG, AND THE SLITHY TOVES +Response: DID GIRE AND GYMBLE IN THE WABE. +Response: ALL MIMSY WERE THE BOROGROVES, +Response: AND THE MOME RATHS OUTGRABE. +Shutting down listener for client +==== server.py.out | remove_uuid64 +Request server running - run your client now. +(Times out after 100 seconds ...) diff --git a/qpid/cpp/examples/examples/request-response/verify_python_cpp b/qpid/cpp/examples/examples/request-response/verify_python_cpp new file mode 100644 index 0000000000..9f3f1caaf4 --- /dev/null +++ b/qpid/cpp/examples/examples/request-response/verify_python_cpp @@ -0,0 +1,5 @@ +# See https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid/bin/verify +background "Waiting" ./server +clients $PYTHON_EXAMPLES/request-response/client.py +kill %% # Must kill the server. +outputs "$PYTHON_EXAMPLES/request-response/client.py.out | remove_uuid64" "server.out | remove_uuid64" diff --git a/qpid/cpp/examples/examples/request-response/verify_python_cpp.in b/qpid/cpp/examples/examples/request-response/verify_python_cpp.in new file mode 100644 index 0000000000..7718d54973 --- /dev/null +++ b/qpid/cpp/examples/examples/request-response/verify_python_cpp.in @@ -0,0 +1,18 @@ +==== client.py.out | remove_uuid64 +Request: Twas brilling, and the slithy toves +Request: Did gyre and gimble in the wabe. +Request: All mimsy were the borogroves, +Request: And the mome raths outgrabe. +Messages queue: ReplyTo: +Response: TWAS BRILLING, AND THE SLITHY TOVES +Response: DID GYRE AND GIMBLE IN THE WABE. +Response: ALL MIMSY WERE THE BOROGROVES, +Response: AND THE MOME RATHS OUTGRABE. +No more messages! +==== server.out | remove_uuid64 +Activating request queue listener for: request +Waiting for requests +Request: Twas brilling, and the slithy toves (ReplyTo:) +Request: Did gyre and gimble in the wabe. (ReplyTo:) +Request: All mimsy were the borogroves, (ReplyTo:) +Request: And the mome raths outgrabe. (ReplyTo:) diff --git a/qpid/cpp/examples/verify b/qpid/cpp/examples/verify new file mode 100755 index 0000000000..251097930e --- /dev/null +++ b/qpid/cpp/examples/verify @@ -0,0 +1,85 @@ +#!/bin/sh +# Driver script to verify installed examples (also used for build tests.) +# +# Usage: verify example_dir [ example_dir ...] +# Where each example_dir must contain a verify sub-script to include. +# +# If $QPIDD is set, run a private QPIDD and use it. +# If $QPID_HOST or $QPID_PORT are set, use them to connect. +# + +export QPID_DATA_DIR= + +cleanup() { + test -n "$QPIDD" && $QPIDD -q # Private broker + kill %% > /dev/null 2>&1 # Leftover background jobs +} + +trap cleanup EXIT + +ARGS="${QPID_HOST:-localhost} $QPID_PORT" + +outfile() { + file=$1 + while [ -f $file.out ]; do file="${file}X"; done + echo $file.out + } + +fail() { test -n "$*" && echo $* 1>&2 ; FAIL=1; return 1; } + +client() { "$@" $ARGS > `outfile $*` || fail; } + +clients() { for cmd in "$@"; do client $cmd; done; } + +waitfor() { until grep -a -l "$2" $1 >/dev/null 2>&1 ; do sleep 1 ; done ; } + +background() { + pattern=$1; shift + out=`outfile $*` + eval "$* $ARGS > $out &" || { fail; return 1; } + waitfor $out "$pattern" +} + +name() { + for x in $*; do name="$name `basename $x`"; done + echo $name; +} + +outputs() { + wait 2> /dev/null # Wait for all backgroud processes to complete + rm -f $script.out + for f in "$@"; do + { echo "==== `name $f`"; eval "cat $f"; } >> $script.out || fail + done +} + +verify() { + FAIL= + if [ -d $1 ]; then dir=$1; script=verify; + else dir=`dirname $1`; script=`basename $1`; fi + cd $dir || return 1 + rm -f *.out + { source ./$script && diff -ac $script.out $script.in ; } || fail + test -z "$FAIL" && rm -f *.out + return $FAIL +} + +HEX="[a-fA-F0-9]" +remove_uuid() { + sed "s/$HEX\{8\}-$HEX\{4\}-$HEX\{4\}-$HEX\{4\}-$HEX\{12\}//g" $* +} +remove_uuid64() { + sed 's/[-A-Za-z0-9_]\{22\}==//g' $* +} + +# Start private broker if QPIDD is set. +if [ -n "$QPIDD" ] ; then + export QPID_PORT=`$QPIDD -dp0` || { echo "Cannot start $QPIDD" ; exit 1; } + trap "$QPIDD -q" EXIT +fi + +for example in "$@"; do + echo "== $example " + if ( verify $example; ) then echo "PASS"; else echo "FAIL"; RET=1; fi + done +exit $RET diff --git a/qpid/cpp/examples/verify_all b/qpid/cpp/examples/verify_all new file mode 100755 index 0000000000..5501239021 --- /dev/null +++ b/qpid/cpp/examples/verify_all @@ -0,0 +1,23 @@ +#!/bin/sh +# Verify all C++/python example combinations. +# + +srcdir=$1 ; +verify=`dirname $0`/verify +qpidd=../src/qpidd +python=$srcdir/python + +trap "$qpidd -q" exit +export QPID_PORT=`$qpidd -dp0 --data-dir ""` +export PYTHON_EXAMPLES=$python/examples +export PYTHONPATH=$python:$PYTHONPATH +export AMQP_SPEC=$srcdir/specs/amqp.0-10-preview.xml + +test -d $PYTHON_EXAMPLES || echo "Warning: not verifying python examples, $PYTHON_EXAMPLES not found" +find="find examples" +test -d $PYTHON_EXAMPLES && find="$find $PYTHON_EXAMPLES" +find="$find -name verify" +test -d $PYTHON_EXAMPLES && \ + find="$find -o -name verify_cpp_python -o -name verify_python_cpp" +$verify `$find` + diff --git a/qpid/cpp/m4/clock_time.m4 b/qpid/cpp/m4/clock_time.m4 new file mode 100644 index 0000000000..227a5978e5 --- /dev/null +++ b/qpid/cpp/m4/clock_time.m4 @@ -0,0 +1,30 @@ +# clock_time.m4 serial 8 +dnl Copyright (C) 2002-2006 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +# Check for clock_gettime and clock_settime, and set LIB_CLOCK_GETTIME. +# For a program named, say foo, you should add a line like the following +# in the corresponding Makefile.am file: +# foo_LDADD = $(LDADD) $(LIB_CLOCK_GETTIME) + +AC_DEFUN([gl_CLOCK_TIME], +[ + dnl Persuade glibc and Solaris <time.h> to declare these functions. + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + + # Solaris 2.5.1 needs -lposix4 to get the clock_gettime function. + # Solaris 7 prefers the library name -lrt to the obsolescent name -lposix4. + + # Save and restore LIBS so e.g., -lrt, isn't added to it. Otherwise, *all* + # programs in the package would end up linked with that potentially-shared + # library, inducing unnecessary run-time overhead. + gl_saved_libs=$LIBS + AC_SEARCH_LIBS(clock_gettime, [rt posix4], + [test "$ac_cv_search_clock_gettime" = "none required" || + LIB_CLOCK_GETTIME=$ac_cv_search_clock_gettime]) + AC_SUBST([LIB_CLOCK_GETTIME]) + AC_CHECK_FUNCS(clock_gettime clock_settime) + LIBS=$gl_saved_libs +]) diff --git a/qpid/cpp/m4/compiler-flags.m4 b/qpid/cpp/m4/compiler-flags.m4 new file mode 100644 index 0000000000..01cb728f02 --- /dev/null +++ b/qpid/cpp/m4/compiler-flags.m4 @@ -0,0 +1,23 @@ +# serial 3 +# Find valid warning flags for the C Compiler. -*-Autoconf-*- +dnl Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. +dnl Written by Jesse Thilo. + +AC_DEFUN([gl_COMPILER_FLAGS], + [AC_MSG_CHECKING(whether compiler accepts $1) + AC_SUBST(COMPILER_FLAGS) + ac_save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $1" + ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $1" + AC_TRY_COMPILE(, + [int x;], + COMPILER_FLAGS="$COMPILER_FLAGS $1" + AC_MSG_RESULT(yes), + AC_MSG_RESULT(no)) + CFLAGS="$ac_save_CFLAGS" + CXXFLAGS="$ac_save_CXXFLAGS" + ]) diff --git a/qpid/cpp/m4/cppunit.m4 b/qpid/cpp/m4/cppunit.m4 new file mode 100644 index 0000000000..f009086f9d --- /dev/null +++ b/qpid/cpp/m4/cppunit.m4 @@ -0,0 +1,89 @@ +dnl +dnl AM_PATH_CPPUNIT(MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]) +dnl +AC_DEFUN([AM_PATH_CPPUNIT], +[ + +AC_ARG_WITH(cppunit-prefix,[ --with-cppunit-prefix=PFX Prefix where CppUnit is installed (optional)], + cppunit_config_prefix="$withval", cppunit_config_prefix="") +AC_ARG_WITH(cppunit-exec-prefix,[ --with-cppunit-exec-prefix=PFX Exec prefix where CppUnit is installed (optional)], + cppunit_config_exec_prefix="$withval", cppunit_config_exec_prefix="") + + if test x$cppunit_config_exec_prefix != x ; then + cppunit_config_args="$cppunit_config_args --exec-prefix=$cppunit_config_exec_prefix" + if test x${CPPUNIT_CONFIG+set} != xset ; then + CPPUNIT_CONFIG=$cppunit_config_exec_prefix/bin/cppunit-config + fi + fi + if test x$cppunit_config_prefix != x ; then + cppunit_config_args="$cppunit_config_args --prefix=$cppunit_config_prefix" + if test x${CPPUNIT_CONFIG+set} != xset ; then + CPPUNIT_CONFIG=$cppunit_config_prefix/bin/cppunit-config + fi + fi + + AC_PATH_PROG(CPPUNIT_CONFIG, cppunit-config, no) + cppunit_version_min=$1 + + AC_MSG_CHECKING(for Cppunit - version >= $cppunit_version_min) + no_cppunit="" + if test "$CPPUNIT_CONFIG" = "no" ; then + AC_MSG_RESULT(no) + no_cppunit=yes + else + CPPUNIT_CFLAGS=`$CPPUNIT_CONFIG --cflags` + CPPUNIT_LIBS=`$CPPUNIT_CONFIG --libs` + cppunit_version=`$CPPUNIT_CONFIG --version` + + cppunit_major_version=`echo $cppunit_version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + cppunit_minor_version=`echo $cppunit_version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + cppunit_micro_version=`echo $cppunit_version | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + + cppunit_major_min=`echo $cppunit_version_min | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\1/'` + if test "x${cppunit_major_min}" = "x" ; then + cppunit_major_min=0 + fi + + cppunit_minor_min=`echo $cppunit_version_min | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\2/'` + if test "x${cppunit_minor_min}" = "x" ; then + cppunit_minor_min=0 + fi + + cppunit_micro_min=`echo $cppunit_version_min | \ + sed 's/\([[0-9]]*\).\([[0-9]]*\).\([[0-9]]*\)/\3/'` + if test "x${cppunit_micro_min}" = "x" ; then + cppunit_micro_min=0 + fi + + cppunit_version_proper=`expr \ + $cppunit_major_version \> $cppunit_major_min \| \ + $cppunit_major_version \= $cppunit_major_min \& \ + $cppunit_minor_version \> $cppunit_minor_min \| \ + $cppunit_major_version \= $cppunit_major_min \& \ + $cppunit_minor_version \= $cppunit_minor_min \& \ + $cppunit_micro_version \>= $cppunit_micro_min ` + + if test "$cppunit_version_proper" = "1" ; then + AC_MSG_RESULT([$cppunit_major_version.$cppunit_minor_version.$cppunit_micro_version]) + else + AC_MSG_RESULT(no) + no_cppunit=yes + fi + fi + + if test "x$no_cppunit" = x ; then + ifelse([$2], , :, [$2]) + else + CPPUNIT_CFLAGS="" + CPPUNIT_LIBS="" + ifelse([$3], , :, [$3]) + fi + + AC_SUBST(CPPUNIT_CFLAGS) + AC_SUBST(CPPUNIT_LIBS) +]) diff --git a/qpid/cpp/m4/extensions.m4 b/qpid/cpp/m4/extensions.m4 new file mode 100644 index 0000000000..143a9e5403 --- /dev/null +++ b/qpid/cpp/m4/extensions.m4 @@ -0,0 +1,58 @@ +# serial 4 -*- Autoconf -*- +# Enable extensions on systems that normally disable them. + +# Copyright (C) 2003, 2006 Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This definition of AC_USE_SYSTEM_EXTENSIONS is stolen from CVS +# Autoconf. Perhaps we can remove this once we can assume Autoconf +# 2.61 or later everywhere, but since CVS Autoconf mutates rapidly +# enough in this area it's likely we'll need to redefine +# AC_USE_SYSTEM_EXTENSIONS for quite some time. + +# AC_USE_SYSTEM_EXTENSIONS +# ------------------------ +# Enable extensions on systems that normally disable them, +# typically due to standards-conformance issues. +AC_DEFUN([AC_USE_SYSTEM_EXTENSIONS], +[ + AC_BEFORE([$0], [AC_COMPILE_IFELSE]) + AC_BEFORE([$0], [AC_RUN_IFELSE]) + + AC_REQUIRE([AC_GNU_SOURCE]) + AC_REQUIRE([AC_AIX]) + AC_REQUIRE([AC_MINIX]) + + AH_VERBATIM([__EXTENSIONS__], +[/* Enable extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif]) + AC_CACHE_CHECK([whether it is safe to define __EXTENSIONS__], + [ac_cv_safe_to_define___extensions__], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM([ +# define __EXTENSIONS__ 1 + AC_INCLUDES_DEFAULT])], + [ac_cv_safe_to_define___extensions__=yes], + [ac_cv_safe_to_define___extensions__=no])]) + test $ac_cv_safe_to_define___extensions__ = yes && + AC_DEFINE([__EXTENSIONS__]) + AC_DEFINE([_POSIX_PTHREAD_SEMANTICS]) + AC_DEFINE([_TANDEM_SOURCE]) +]) + +# gl_USE_SYSTEM_EXTENSIONS +# ------------------------ +# Enable extensions on systems that normally disable them, +# typically due to standards-conformance issues. +AC_DEFUN([gl_USE_SYSTEM_EXTENSIONS], + [AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS])]) diff --git a/qpid/cpp/make-dist b/qpid/cpp/make-dist new file mode 100755 index 0000000000..c23cad63a1 --- /dev/null +++ b/qpid/cpp/make-dist @@ -0,0 +1,83 @@ +#!/bin/bash +# +# Temporary hack for producing a binary dev distribution. +# Includes regular stuff from 'make install' + examples and headers. +# +# TODO: Also include debug libraries. +# + +Usage() { + echo "usage: $0 [release-version] + release-version e.g. 1.0M1 (defaults to the svn revision)" >&2 + exit 2 +} + +if [[ $# -eq 1 ]]; then + [[ $1 == "-?" ]] && Usage + version=$1 +elif [[ $# -ne 0 ]]; then + Usage +else + # Default the version to the svn revision + if which svn >/dev/null 2>&1; then + svnRevision=$(svn info | grep ^Revision: | awk '{print $2}') + version=r${svnRevision} + else + echo "You need to have svn in your PATH or specify a release-version" + exit 2 + fi +fi + +releaseName=qpid-cpp-dev-${version}-$(uname -s)-$(uname -p) +releaseDir=release/$releaseName + +if [[ -d $releaseDir ]]; then + echo "$releaseDir already exists" + exit 2 +fi + +# Copy bin. +mkdir -p $releaseDir/bin +cp -r src/.libs/* ${releaseDir}/bin + +# Copy libs. +mkdir -p $releaseDir/lib +cp lib/broker/.libs/lib* lib/common/.libs/lib* lib/client/.libs/lib* \ + $releaseDir/lib + +# Copy gen include files. +find gen -name \*.h | while read file; do + destFile=${releaseDir}/include/$file + baseDir=$(dirname $destFile) + mkdir -p $baseDir + cp $file $destFile +done + +# Copy in lib include files. +( + cd lib; find . -name \*.h | while read file; do + destFile=../${releaseDir}/include/$file + baseDir=$(dirname $destFile) + mkdir -p $baseDir + cp $file $destFile + done +) + +# Copy non-cppunit tests as examples. +mkdir -p $releaseDir/examples +for file in tests/*.cpp; do + if grep CppUnit $file >/dev/null; then + echo Skipping cppunit file $file + else + cp $file $releaseDir/examples + fi +done + +# Copy Makefile and README for examples. +cp tests/examples.Makefile $releaseDir/examples/Makefile +cp tests/examples.README $releaseDir/examples/README + +cd release +tar=$releaseName.tar +tar cvf $tar $releaseName +bzip2 $tar diff --git a/qpid/cpp/managementgen/generate.py b/qpid/cpp/managementgen/generate.py new file mode 100755 index 0000000000..e1c01de9b0 --- /dev/null +++ b/qpid/cpp/managementgen/generate.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python + +# +# 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. +# + +from xml.dom.minidom import parse, parseString, Node +from cStringIO import StringIO +from stat import * +from errno import * +import os +import os.path +import filecmp + +class Template: + """ + Expandable File Template - This class is instantiated each time a + template is to be expanded. It is instantiated with the "filename" + which is the full path to the template file and the "handler" which + is an object that is responsible for storing variables (setVariable) + and expanding tags (substHandler). + """ + def __init__ (self, filename, handler): + self.filename = filename + self.handler = handler + self.handler.initExpansion () + + def expandLine (self, line, stream, object): + cursor = 0 + while 1: + sub = line.find ("/*MGEN:", cursor) + if sub == -1: + stream.write (line[cursor:len (line)]) + return + + subend = line.find("*/", sub) + stream.write (line[cursor:sub]) + cursor = subend + 2 + + tag = line[sub:subend] + equalPos = tag.find ("=") + if equalPos == -1: + dotPos = tag.find (".") + if dotPos == -1: + raise ValueError ("Invalid tag: %s" % tag) + tagObject = tag[7:dotPos] + tagName = tag[dotPos + 1:len (tag)] + self.handler.substHandler (object, stream, tagObject, tagName) + else: + tagKey = tag[7:equalPos] + tagVal = tag[equalPos + 1:len (tag)] + self.handler.setVariable (tagKey, tagVal) + + def expand (self, object): + fd = open (self.filename) + stream = StringIO () + + for line in fd: + self.expandLine (line, stream, object) + fd.close () + + return stream + + +class Makefile: + """ Object representing a makefile fragment """ + def __init__ (self, filelists, templateFiles): + self.filelists = filelists + self.templateFiles = templateFiles + + def genGenSources (self, stream, variables): + mdir = variables["mgenDir"] + sdir = variables["specDir"] + stream.write (mdir + "/main.py \\\n") + stream.write (" " + mdir + "/generate.py \\\n") + stream.write (" " + mdir + "/schema.py \\\n") + stream.write (" " + sdir + "/management-types.xml \\\n") + stream.write (" " + sdir + "/management-schema.xml \\\n") + first = True + for template in self.templateFiles: + if first: + first = False + stream.write (" ") + else: + stream.write (" \\\n ") + stream.write (mdir + "/templates/" + template) + + def genGenCppFiles (self, stream, variables): + first = True + for file in self.filelists["cpp"]: + if first: + first = False + else: + stream.write (" \\\n ") + stream.write (file) + + def genGenHFiles (self, stream, variables): + first = True + for file in self.filelists["h"]: + if first: + first = False + else: + stream.write (" \\\n ") + stream.write (file) + + +class Generator: + """ + This class manages code generation using template files. It is instantiated + once for an entire code generation session. + """ + def createPath (self, path): + exists = True + try: + mode = os.stat (path)[ST_MODE] + except OSError, (err,text): + if err == ENOENT: + exists = False + else: + raise + if exists and not S_ISDIR (mode): + raise ValueError ("path is not directory: %s" % path) + if not exists: + pair = os.path.split (path) + self.createPath (pair[0]) + os.mkdir (path) + + def normalize (self, path): + newpath = os.path.normcase (os.path.normpath (path)) + self.createPath (newpath) + return newpath + "/" + + def __init__ (self, destDir, templateDir): + self.dest = self.normalize (destDir) + self.input = self.normalize (templateDir) + self.filelists = {} + self.filelists["h"] = [] + self.filelists["cpp"] = [] + self.filelists["mk"] = [] + self.templateFiles = [] + self.variables = {} + + def genDisclaimer (self, stream, variables): + prefix = variables["commentPrefix"] + stream.write (prefix + " This source file was created by a code generator.\n") + stream.write (prefix + " Please do not edit.") + + def fileExt (self, path): + dot = path.rfind (".") + if dot == -1: + return "" + return path[dot + 1:] + + def writeIfChanged (self, stream, target, force=False): + ext = self.fileExt (target) + self.filelists[ext].append (target) + tempFile = self.dest + "gen.tmp" + fd = open (tempFile, "w") + fd.write (stream.getvalue ()) + fd.close () + + try: + if not force and filecmp.cmp (target, tempFile): + os.remove (tempFile) + return + except: + pass + + try: + os.remove (target) + except: + pass + + os.rename (tempFile, target) + print "Generated:", target + + def targetPackageFile (self, schema, templateFile): + dot = templateFile.find(".") + if dot == -1: + raise ValueError ("Invalid template file name %s" % templateFile) + extension = templateFile[dot:len (templateFile)] + path = self.dest + "Package" + schema.getPackageName().capitalize() + extension + return path + + def targetClassFile (self, _class, templateFile): + dot = templateFile.find(".") + if dot == -1: + raise ValueError ("Invalid template file name %s" % templateFile) + extension = templateFile[dot:len (templateFile)] + path = self.dest + _class.getName ().capitalize () + extension + return path + + def targetMethodFile (self, method, templateFile): + """ Return the file name for a method file """ + dot = templateFile.rfind(".") + if dot == -1: + raise ValueError ("Invalid template file name %s" % templateFile) + extension = templateFile[dot:] + path = self.dest + "Args" + method.getFullName () + extension + return path + + def initExpansion (self): + self.variables = {} + + def substHandler (self, object, stream, tagObject, tag): + if tagObject == "Root": + obj = "self" + else: + obj = "object" # MUST be the same as the 2nd formal parameter + + call = obj + ".gen" + tag + "(stream, self.variables)" + eval (call) + + def setVariable (self, key, value): + self.variables[key] = value + + def makeClassFiles (self, templateFile, schema, force=False): + """ Generate an expanded template per schema class """ + classes = schema.getClasses () + template = Template (self.input + templateFile, self) + self.templateFiles.append (templateFile) + for _class in classes: + target = self.targetClassFile (_class, templateFile) + stream = template.expand (_class) + self.writeIfChanged (stream, target, force) + + def makeMethodFiles (self, templateFile, schema, force=False): + """ Generate an expanded template per method-with-arguments """ + classes = schema.getClasses () + template = Template (self.input + templateFile, self) + self.templateFiles.append (templateFile) + for _class in classes: + methods = _class.getMethods () + for method in methods: + if method.getArgCount () > 0: + target = self.targetMethodFile (method, templateFile) + stream = template.expand (method) + self.writeIfChanged (stream, target, force) + + def makePackageFile (self, templateFile, schema, force=False): + """ Generate a package-specific file """ + template = Template (self.input + templateFile, self) + self.templateFiles.append (templateFile) + target = self.targetPackageFile (schema, templateFile) + stream = template.expand (schema) + self.writeIfChanged (stream, target, force) + + def makeSingleFile (self, templateFile, target, force=False): + """ Generate a single expanded template """ + makefile = Makefile (self.filelists, self.templateFiles) + template = Template (self.input + templateFile, self) + self.templateFiles.append (templateFile) + stream = template.expand (makefile) + self.writeIfChanged (stream, target, force) diff --git a/qpid/cpp/managementgen/main.py b/qpid/cpp/managementgen/main.py new file mode 100755 index 0000000000..87ef3d5298 --- /dev/null +++ b/qpid/cpp/managementgen/main.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python + +# +# 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. +# + +from schema import PackageSchema, SchemaClass +from generate import Generator +from optparse import OptionParser + +# Set command line options +usage = "usage: %prog [options] schema-document type-document template-directory out-directory" +parser = OptionParser (usage=usage) +parser.add_option ("-m", "--makefile", dest="makefile", metavar="FILE", + help="Makefile fragment") +parser.add_option ("-i", "--include-prefix", dest="include_prefix", metavar="PATH", + default="qpid/management/", + help="Prefix for #include of generated headers in generated source, default: qpid/management/") + +(opts, args) = parser.parse_args () + +if len (args) < 4: + parser.error ("Too few arguments") + +schemafile = args[0] +typefile = args[1] +templatedir = args[2] +outdir = args[3] + +if opts.include_prefix == ".": + opts.include_prefix = None + +gen = Generator (outdir, templatedir) +schema = PackageSchema (typefile, schemafile, opts) + +gen.makeClassFiles ("Class.h", schema) +gen.makeClassFiles ("Class.cpp", schema) +gen.makeMethodFiles ("Args.h", schema) +gen.makePackageFile ("Package.h", schema) +gen.makePackageFile ("Package.cpp", schema) + +if opts.makefile != None: + gen.makeSingleFile ("Makefile.mk", opts.makefile, force=True) diff --git a/qpid/cpp/managementgen/schema.py b/qpid/cpp/managementgen/schema.py new file mode 100755 index 0000000000..7e4a91814f --- /dev/null +++ b/qpid/cpp/managementgen/schema.py @@ -0,0 +1,911 @@ +#!/usr/bin/env python + +# +# 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. +# + +from xml.dom.minidom import parse, parseString, Node +from cStringIO import StringIO +import md5 + +#===================================================================================== +# +#===================================================================================== +class SchemaType: + def __init__ (self, node): + self.name = None + self.base = None + self.cpp = None + self.encode = None + self.decode = None + self.style = "normal" + self.accessor = None + self.init = "0" + + attrs = node.attributes + for idx in range (attrs.length): + key = attrs.item(idx).nodeName + val = attrs.item(idx).nodeValue + if key == 'name': + self.name = val + + elif key == 'base': + self.base = val + + elif key == 'cpp': + self.cpp = val + + elif key == 'encode': + self.encode = val + + elif key == 'decode': + self.decode = val + + elif key == 'style': + self.style = val + + elif key == 'accessor': + self.accessor = val + + elif key == 'init': + self.init = val + + else: + raise ValueError ("Unknown attribute in type '%s'" % key) + + if self.name == None or self.base == None or self.cpp == None or \ + self.encode == None or self.decode == None: + raise ValueError ("Missing required attribute(s) in type") + + def getName (self): + return self.name + + def genAccessor (self, stream, varName, changeFlag = None): + if self.accessor == "direct": + stream.write (" inline void set_" + varName + " (" + self.cpp + " val){\n"); + stream.write (" sys::RWlock::ScopedWlock writeLock (accessLock);\n") + if self.style != "mma": + stream.write (" " + varName + " = val;\n"); + if self.style == "wm": + stream.write (" if (" + varName + "Low > val)\n") + stream.write (" " + varName + "Low = val;\n") + stream.write (" if (" + varName + "High < val)\n") + stream.write (" " + varName + "High = val;\n") + if self.style == "mma": + stream.write (" " + varName + "Count++;\n") + stream.write (" " + varName + "Total += val;\n") + stream.write (" if (" + varName + "Min > val)\n") + stream.write (" " + varName + "Min = val;\n") + stream.write (" if (" + varName + "Max < val)\n") + stream.write (" " + varName + "Max = val;\n") + if changeFlag != None: + stream.write (" " + changeFlag + " = true;\n") + stream.write (" }\n"); + elif self.accessor == "counter": + stream.write (" inline void inc_" + varName + " (" + self.cpp + " by = 1){\n"); + stream.write (" sys::RWlock::ScopedWlock writeLock (accessLock);\n") + stream.write (" " + varName + " += by;\n") + if self.style == "wm": + stream.write (" if (" + varName + "High < " + varName + ")\n") + stream.write (" " + varName + "High = " + varName + ";\n") + if changeFlag != None: + stream.write (" " + changeFlag + " = true;\n") + stream.write (" }\n"); + stream.write (" inline void dec_" + varName + " (" + self.cpp + " by = 1){\n"); + stream.write (" sys::RWlock::ScopedWlock writeLock (accessLock);\n") + stream.write (" " + varName + " -= by;\n") + if self.style == "wm": + stream.write (" if (" + varName + "Low > " + varName + ")\n") + stream.write (" " + varName + "Low = " + varName + ";\n") + if changeFlag != None: + stream.write (" " + changeFlag + " = true;\n") + stream.write (" }\n"); + stream.write (" inline void set_" + varName + " (" + self.cpp + " val){\n"); + stream.write (" sys::RWlock::ScopedWlock writeLock (accessLock);\n") + stream.write (" " + varName + " = val;\n"); + if self.style == "wm": + stream.write (" if (" + varName + "Low > val)\n") + stream.write (" " + varName + "Low = val;\n") + stream.write (" if (" + varName + "High < val)\n") + stream.write (" " + varName + "High = val;\n") + if changeFlag != None: + stream.write (" " + changeFlag + " = true;\n") + stream.write (" }\n"); + + def genHiLoStatResets (self, stream, varName): + if self.style == "wm": + stream.write (" " + varName + "High = " + varName + ";\n") + stream.write (" " + varName + "Low = " + varName + ";\n") + if self.style == "mma": + stream.write (" " + varName + "Count = 0;\n") + stream.write (" " + varName + "Total = 0;\n") + stream.write (" " + varName + "Min = -1;\n") + stream.write (" " + varName + "Max = 0;\n") + + def genWrite (self, stream, varName): + if self.style != "mma": + stream.write (" " + self.encode.replace ("@", "buf").replace ("#", varName) + ";\n") + if self.style == "wm": + stream.write (" " + self.encode.replace ("@", "buf") \ + .replace ("#", varName + "High") + ";\n") + stream.write (" " + self.encode.replace ("@", "buf") \ + .replace ("#", varName + "Low") + ";\n") + if self.style == "mma": + stream.write (" " + self.encode.replace ("@", "buf") \ + .replace ("#", varName + "Count") + ";\n") + stream.write (" " + self.encode.replace ("@", "buf") \ + .replace ("#", varName + "Count ? " + varName + "Min : 0") + ";\n") + stream.write (" " + self.encode.replace ("@", "buf") \ + .replace ("#", varName + "Max") + ";\n") + stream.write (" " + self.encode.replace ("@", "buf") \ + .replace ("#", varName + "Count ? " + varName + "Total / " + + varName + "Count : 0") + ";\n") + + + def getReadCode (self, varName, bufName): + result = self.decode.replace ("@", bufName).replace ("#", varName) + return result + + def getWriteCode (self, varName, bufName): + result = self.encode.replace ("@", bufName).replace ("#", varName) + return result + +#===================================================================================== +# +#===================================================================================== +class TypeSpec: + def __init__ (self, file): + self.types = {} + dom = parse (file) + document = dom.documentElement + if document.tagName != 'schema-types': + raise ValueError ("Expected 'schema-types' in type file") + + for child in document.childNodes: + if child.nodeType == Node.ELEMENT_NODE: + if child.nodeName == 'type': + stype = SchemaType (child) + self.types[stype.getName ()] = stype + else: + raise ValueError ("Unknown type tag '%s'" % child.nodeName) + + def getType (self, name): + return self.types[name] + + +#===================================================================================== +# +#===================================================================================== +class Type: + def __init__ (self, name, typespec): + self.type = typespec.getType (name) + +#===================================================================================== +# +#===================================================================================== +class SchemaConfig: + def __init__ (self, node, typespec): + self.name = None + self.type = None + self.access = "RO" + self.isIndex = 0 + self.isParentRef = 0 + self.unit = None + self.min = None + self.max = None + self.maxLen = None + self.desc = None + + attrs = node.attributes + for idx in range (attrs.length): + key = attrs.item(idx).nodeName + val = attrs.item(idx).nodeValue + if key == 'name': + self.name = val + + elif key == 'type': + self.type = Type (val, typespec) + + elif key == 'access': + self.access = val + + elif key == 'index': + if val != 'y': + raise ValueError ("Expected 'y' in index attribute") + self.isIndex = 1 + + elif key == 'parentRef': + if val != 'y': + raise ValueError ("Expected 'y' in parentRef attribute") + self.isParentRef = 1 + + elif key == 'unit': + self.unit = val + + elif key == 'min': + self.min = val + + elif key == 'max': + self.max = val + + elif key == 'maxlen': + self.maxLen = val + + elif key == 'desc': + self.desc = val + + else: + raise ValueError ("Unknown attribute in configElement '%s'" % key) + + if self.name == None: + raise ValueError ("Missing 'name' attribute in configElement") + if self.type == None: + raise ValueError ("Missing 'type' attribute in configElement") + + def getName (self): + return self.name + + def isConstructorArg (self): + if self.access == "RC" and self.isParentRef == 0: + return 1 + return 0 + + def genDeclaration (self, stream): + stream.write (" " + self.type.type.cpp + " " + self.name + ";\n") + + def genFormalParam (self, stream): + stream.write (self.type.type.cpp + " _" + self.name) + + def genAccessor (self, stream): + self.type.type.genAccessor (stream, self.name, "configChanged") + + def genSchema (self, stream): + stream.write (" ft = FieldTable ();\n") + stream.write (" ft.setString (NAME, \"" + self.name + "\");\n") + stream.write (" ft.setInt (TYPE, TYPE_" + self.type.type.base +");\n") + stream.write (" ft.setInt (ACCESS, ACCESS_" + self.access + ");\n") + stream.write (" ft.setInt (INDEX, " + str (self.isIndex) + ");\n") + if self.unit != None: + stream.write (" ft.setString (UNIT, \"" + self.unit + "\");\n") + if self.min != None: + stream.write (" ft.setInt (MIN, " + self.min + ");\n") + if self.max != None: + stream.write (" ft.setInt (MAX, " + self.max + ");\n") + if self.maxLen != None: + stream.write (" ft.setInt (MAXLEN, " + self.maxLen + ");\n") + if self.desc != None: + stream.write (" ft.setString (DESC, \"" + self.desc + "\");\n") + stream.write (" buf.put (ft);\n\n") + + def genWrite (self, stream): + self.type.type.genWrite (stream, self.name) + + +#===================================================================================== +# +#===================================================================================== +class SchemaInst: + def __init__ (self, node, typespec): + self.name = None + self.type = None + self.unit = None + self.desc = None + + attrs = node.attributes + for idx in range (attrs.length): + key = attrs.item(idx).nodeName + val = attrs.item(idx).nodeValue + if key == 'name': + self.name = val + + elif key == 'type': + self.type = Type (val, typespec) + + elif key == 'unit': + self.unit = val + + elif key == 'desc': + self.desc = val + + else: + raise ValueError ("Unknown attribute in instElement '%s'" % key) + + if self.name == None: + raise ValueError ("Missing 'name' attribute in instElement") + if self.type == None: + raise ValueError ("Missing 'type' attribute in instElement") + + def getName (self): + return self.name + + def genDeclaration (self, stream): + if self.type.type.style != "mma": + stream.write (" " + self.type.type.cpp + " " + self.name + ";\n") + if self.type.type.style == 'wm': + stream.write (" " + self.type.type.cpp + " " + self.name + "High;\n") + stream.write (" " + self.type.type.cpp + " " + self.name + "Low;\n") + if self.type.type.style == "mma": + stream.write (" " + self.type.type.cpp + " " + self.name + "Count;\n") + stream.write (" uint64_t " + self.name + "Total;\n") + stream.write (" " + self.type.type.cpp + " " + self.name + "Min;\n") + stream.write (" " + self.type.type.cpp + " " + self.name + "Max;\n") + + def genAccessor (self, stream): + self.type.type.genAccessor (stream, self.name, "instChanged") + + def genHiLoStatResets (self, stream): + self.type.type.genHiLoStatResets (stream, self.name) + + def genSchemaText (self, stream, name, desc): + stream.write (" ft = FieldTable ();\n") + stream.write (" ft.setString (NAME, \"" + name + "\");\n") + stream.write (" ft.setInt (TYPE, TYPE_" + self.type.type.base +");\n") + if self.unit != None: + stream.write (" ft.setString (UNIT, \"" + self.unit + "\");\n") + if desc != None: + stream.write (" ft.setString (DESC, \"" + desc + "\");\n") + stream.write (" buf.put (ft);\n\n") + + def genSchema (self, stream): + if self.type.type.style != "mma": + self.genSchemaText (stream, self.name, self.desc) + if self.type.type.style == "wm": + descHigh = self.desc + descLow = self.desc + if self.desc != None: + descHigh = descHigh + " (High)" + descLow = descLow + " (Low)" + self.genSchemaText (stream, self.name + "High", descHigh) + self.genSchemaText (stream, self.name + "Low", descLow) + if self.type.type.style == "mma": + descCount = self.desc + descMin = self.desc + descMax = self.desc + descAverage = self.desc + if self.desc != None: + descCount = descCount + " (Samples)" + descMin = descMin + " (Min)" + descMax = descMax + " (Max)" + descAverage = descAverage + " (Average)" + self.genSchemaText (stream, self.name + "Samples", descCount) + self.genSchemaText (stream, self.name + "Min", descMin) + self.genSchemaText (stream, self.name + "Max", descMax) + self.genSchemaText (stream, self.name + "Average", descAverage) + + def genWrite (self, stream): + self.type.type.genWrite (stream, self.name) + + def genInitialize (self, stream): + val = self.type.type.init + if self.type.type.style != "mma": + stream.write (" " + self.name + " = " + val + ";\n") + if self.type.type.style == "wm": + stream.write (" " + self.name + "High = " + val + ";\n") + stream.write (" " + self.name + "Low = " + val + ";\n") + if self.type.type.style == "mma": + stream.write (" " + self.name + "Count = 0;\n") + stream.write (" " + self.name + "Min = -1;\n") + stream.write (" " + self.name + "Max = 0;\n") + stream.write (" " + self.name + "Total = 0;\n") + + +#===================================================================================== +# +#===================================================================================== +class SchemaArg: + def __init__ (self, node, typespec): + self.name = None + self.type = None + self.unit = None + self.dir = "I" + self.min = None + self.max = None + self.maxLen = None + self.desc = None + self.default = None + + attrs = node.attributes + for idx in range (attrs.length): + key = attrs.item(idx).nodeName + val = attrs.item(idx).nodeValue + if key == 'name': + self.name = val + + elif key == 'type': + self.type = Type (val, typespec) + + elif key == 'unit': + self.unit = val + + elif key == 'dir': + self.dir = val.upper () + + elif key == 'min': + self.min = val + + elif key == 'max': + self.max = val + + elif key == 'maxlen': + self.maxLen = val + + elif key == 'desc': + self.desc = val + + elif key == 'default': + self.default = val + + else: + raise ValueError ("Unknown attribute in arg '%s'" % key) + + if self.name == None: + raise ValueError ("Missing 'name' attribute in arg") + if self.type == None: + raise ValueError ("Missing 'type' attribute in arg") + + def getName (self): + return self.name + + def getDir (self): + return self.dir + + def genSchema (self, stream): + stream.write (" ft = FieldTable ();\n") + stream.write (" ft.setString (NAME, \"" + self.name + "\");\n") + stream.write (" ft.setInt (TYPE, TYPE_" + self.type.type.base +");\n") + stream.write (" ft.setString (DIR, \"" + self.dir + "\");\n") + if self.unit != None: + stream.write (" ft.setString (UNIT, \"" + self.unit + "\");\n") + if self.min != None: + stream.write (" ft.setInt (MIN, " + self.min + ");\n") + if self.max != None: + stream.write (" ft.setInt (MAX, " + self.max + ");\n") + if self.maxLen != None: + stream.write (" ft.setInt (MAXLEN, " + self.maxLen + ");\n") + if self.desc != None: + stream.write (" ft.setString (DESC, \"" + self.desc + "\");\n") + if self.default != None: + stream.write (" ft.setString (DEFAULT, \"" + self.default + "\");\n") + stream.write (" buf.put (ft);\n\n") + +#===================================================================================== +# +#===================================================================================== +class SchemaMethod: + def __init__ (self, parent, node, typespec): + self.parent = parent + self.name = None + self.desc = None + self.args = [] + + attrs = node.attributes + for idx in range (attrs.length): + key = attrs.item(idx).nodeName + val = attrs.item(idx).nodeValue + if key == 'name': + self.name = val + + elif key == 'desc': + self.desc = val + + else: + raise ValueError ("Unknown attribute in method '%s'" % key) + + for child in node.childNodes: + if child.nodeType == Node.ELEMENT_NODE: + if child.nodeName == 'arg': + arg = SchemaArg (child, typespec) + self.args.append (arg) + else: + raise ValueError ("Unknown method tag '%s'" % child.nodeName) + + def getName (self): + return self.name + + def getFullName (self): + return self.parent.getName().capitalize() + self.name[0:1].upper() +\ + self.name[1:] + + def getArgCount (self): + return len (self.args) + + #=================================================================================== + # Code Generation Functions. The names of these functions (minus the leading "gen") + # match the substitution keywords in the template files. + #=================================================================================== + def genNameUpper (self, stream, variables): + stream.write (self.getFullName ().upper ()) + + def genNameCamel (self, stream, variables): + stream.write (self.getFullName ()) + + def genArguments (self, stream, variables): + for arg in self.args: + ctype = arg.type.type.cpp + dirTag = arg.dir.lower() + "_" + stream.write (" " + ctype + " " + dirTag + arg.getName () + ";\n") + + def genSchema (self, stream, variables): + stream.write (" ft = FieldTable ();\n") + stream.write (" ft.setString (NAME, \"" + self.name + "\");\n") + stream.write (" ft.setInt (ARGCOUNT, " + str (len (self.args)) + ");\n") + if self.desc != None: + stream.write (" ft.setString (DESC, \"" + self.desc + "\");\n") + stream.write (" buf.put (ft);\n\n") + for arg in self.args: + arg.genSchema (stream) + +#===================================================================================== +# +#===================================================================================== +class SchemaEvent: + def __init__ (self, parent, node, typespec): + self.parent = parent + self.name = None + self.desc = None + self.args = [] + + attrs = node.attributes + for idx in range (attrs.length): + key = attrs.item(idx).nodeName + val = attrs.item(idx).nodeValue + if key == 'name': + self.name = val + + elif key == 'desc': + self.desc = val + + else: + raise ValueError ("Unknown attribute in event '%s'" % key) + + for child in node.childNodes: + if child.nodeType == Node.ELEMENT_NODE: + if child.nodeName == 'arg': + arg = SchemaArg (child, typespec) + self.args.append (arg) + else: + raise ValueError ("Unknown event tag '%s'" % child.nodeName) + + def getName (self): + return self.name + + def getFullName (self): + return self.parent.getName ().capitalize() + self.name.capitalize () + + def getArgCount (self): + return len (self.args) + + +class SchemaClass: + def __init__ (self, package, node, typespec, fragments, options): + self.packageName = package + self.configElements = [] + self.instElements = [] + self.methods = [] + self.events = [] + self.options = options + self.md5Sum = md5.new () + + self.hash (node) + + attrs = node.attributes + self.name = attrs['name'].nodeValue + + children = node.childNodes + for child in children: + if child.nodeType == Node.ELEMENT_NODE: + if child.nodeName == 'configElement': + sub = SchemaConfig (child, typespec) + self.configElements.append (sub) + + elif child.nodeName == 'instElement': + sub = SchemaInst (child, typespec) + self.instElements.append (sub) + + elif child.nodeName == 'method': + sub = SchemaMethod (self, child, typespec) + self.methods.append (sub) + + elif child.nodeName == 'event': + sub = SchemaEvent (self, child, typespec) + self.events.append (sub) + + elif child.nodeName == 'group': + self.expandFragment (child, fragments) + + else: + raise ValueError ("Unknown class tag '%s'" % child.nodeName) + + def hash (self, node): + attrs = node.attributes + self.md5Sum.update (node.nodeName) + + for idx in range (attrs.length): + self.md5Sum.update (attrs.item(idx).nodeName) + self.md5Sum.update (attrs.item(idx).nodeValue) + + for child in node.childNodes: + if child.nodeType == Node.ELEMENT_NODE: + self.hash (child) + + def expandFragment (self, node, fragments): + attrs = node.attributes + name = attrs['name'].nodeValue + for fragment in fragments: + if fragment.name == name: + for config in fragment.configElements: + self.configElements.append (config) + for inst in fragment.instElements: + self.instElements.append (inst) + for method in fragment.methods: + self.methods.append (method) + for event in fragment.events: + self.events.append (event) + return + raise ValueError ("Undefined group '%s'" % name) + + def getName (self): + return self.name + + def getMethods (self): + return self.methods + + def getEvents (self): + return self.events + + #=================================================================================== + # Code Generation Functions. The names of these functions (minus the leading "gen") + # match the substitution keywords in the template files. + #=================================================================================== + def genAccessorMethods (self, stream, variables): + for config in self.configElements: + if config.access != "RC": + config.genAccessor (stream) + for inst in self.instElements: + inst.genAccessor (stream) + + def genConfigCount (self, stream, variables): + stream.write ("%d" % len (self.configElements)) + + def genConfigDeclarations (self, stream, variables): + for element in self.configElements: + element.genDeclaration (stream) + + def genConfigElementSchema (self, stream, variables): + for config in self.configElements: + config.genSchema (stream) + + def genConstructorArgs (self, stream, variables): + # Constructor args are config elements with read-create access + result = "" + for element in self.configElements: + if element.isConstructorArg (): + stream.write (", ") + element.genFormalParam (stream) + + def genConstructorInits (self, stream, variables): + for element in self.configElements: + if element.isConstructorArg (): + stream.write ("," + element.getName () + "(_" + element.getName () + ")") + + def genDoMethodArgs (self, stream, variables): + methodCount = 0 + inArgCount = 0 + for method in self.methods: + methodCount = methodCount + 1 + for arg in method.args: + if arg.getDir () == "I" or arg.getDir () == "IO": + inArgCount = inArgCount + 1 + + if methodCount == 0: + stream.write ("string, Buffer&, Buffer& outBuf") + else: + if inArgCount == 0: + stream.write ("string methodName, Buffer&, Buffer& outBuf") + else: + stream.write ("string methodName, Buffer& inBuf, Buffer& outBuf") + + def genEventCount (self, stream, variables): + stream.write ("%d" % len (self.events)) + + def genEventSchema (self, stream, variables): + pass ########################################################################### + + def genHiLoStatResets (self, stream, variables): + for inst in self.instElements: + inst.genHiLoStatResets (stream) + + def genInitializeElements (self, stream, variables): + for inst in self.instElements: + inst.genInitialize (stream) + + def genInstChangedStub (self, stream, variables): + if len (self.instElements) == 0: + stream.write (" // Stub for getInstChanged. There are no inst elements\n") + stream.write (" bool getInstChanged (void) { return false; }\n") + + def genInstCount (self, stream, variables): + count = 0 + for inst in self.instElements: + count = count + 1 + if inst.type.type.style == "wm": + count = count + 2 + if inst.type.type.style == "mma": + count = count + 3 + stream.write ("%d" % count) + + def genInstDeclarations (self, stream, variables): + for element in self.instElements: + element.genDeclaration (stream) + + def genInstElementSchema (self, stream, variables): + for inst in self.instElements: + inst.genSchema (stream) + + def genMethodArgIncludes (self, stream, variables): + for method in self.methods: + if method.getArgCount () > 0: + stream.write ("#include \"" + (self.options.include_prefix or "") +\ + "Args" + method.getFullName () + ".h\"\n") + + def genMethodCount (self, stream, variables): + stream.write ("%d" % len (self.methods)) + + def genMethodHandlers (self, stream, variables): + for method in self.methods: + stream.write ("\n if (methodName == \"" + method.getName () + "\")\n {\n") + if method.getArgCount () == 0: + stream.write (" ArgsNone ioArgs;\n") + else: + stream.write (" Args" + method.getFullName () + " ioArgs;\n") + for arg in method.args: + if arg.getDir () == "I" or arg.getDir () == "IO": + stream.write (" " +\ + arg.type.type.getReadCode ("ioArgs." +\ + arg.dir.lower () + "_" +\ + arg.name, "inBuf") + ";\n") + + stream.write (" status = coreObject->ManagementMethod (METHOD_" +\ + method.getName().upper() + ", ioArgs);\n") + stream.write (" outBuf.putLong (status);\n") + stream.write (" outBuf.putShortString (Manageable::StatusText (status));\n") + for arg in method.args: + if arg.getDir () == "O" or arg.getDir () == "IO": + stream.write (" " +\ + arg.type.type.getWriteCode ("ioArgs." +\ + arg.dir.lower () + "_" +\ + arg.name, "outBuf") + ";\n") + stream.write (" return;\n }\n") + + + def genMethodIdDeclarations (self, stream, variables): + number = 1 + for method in self.methods: + stream.write (" static const uint32_t METHOD_" + method.getName().upper() +\ + " = %d;\n" % number) + number = number + 1 + + def genMethodSchema (self, stream, variables): + for method in self.methods: + method.genSchema (stream, variables) + + def genNameCap (self, stream, variables): + stream.write (self.name.capitalize ()) + + def genNameLower (self, stream, variables): + stream.write (self.name.lower ()) + + def genNamePackageCap (self, stream, variables): + stream.write (self.packageName.capitalize ()) + + def genNamePackageLower (self, stream, variables): + stream.write (self.packageName.lower ()) + + def genNameUpper (self, stream, variables): + stream.write (self.name.upper ()) + + def genParentArg (self, stream, variables): + for config in self.configElements: + if config.isParentRef == 1: + stream.write (", Manageable* _parent") + return + + def genParentRefAssignment (self, stream, variables): + for config in self.configElements: + if config.isParentRef == 1: + stream.write (config.getName () + \ + " = _parent->GetManagementObject ()->getObjectId ();") + return + + def genSchemaMD5 (self, stream, variables): + sum = self.md5Sum.digest () + for idx in range (len (sum)): + if idx != 0: + stream.write (",") + stream.write (hex (ord (sum[idx]))) + + def genWriteConfig (self, stream, variables): + for config in self.configElements: + config.genWrite (stream); + + def genWriteInst (self, stream, variables): + for inst in self.instElements: + inst.genWrite (stream); + + + +class PackageSchema: + def __init__ (self, typefile, schemafile, options): + + self.classes = [] + self.fragments = [] + self.typespec = TypeSpec (typefile) + + dom = parse (schemafile) + document = dom.documentElement + if document.tagName != 'schema': + raise ValueError ("Expected 'schema' node") + attrs = document.attributes + self.packageName = attrs['package'].nodeValue + + children = document.childNodes + for child in children: + if child.nodeType == Node.ELEMENT_NODE: + if child.nodeName == 'class': + cls = SchemaClass (self.packageName, child, self.typespec, + self.fragments, options) + self.classes.append (cls) + + elif child.nodeName == 'group': + cls = SchemaClass (self.packageName, child, self.typespec, + self.fragments, options) + self.fragments.append (cls) + + else: + raise ValueError ("Unknown schema tag '%s'" % child.nodeName) + + def getPackageName (self): + return self.packageName + + def getClasses (self): + return self.classes + + def genPackageNameUpper (self, stream, variables): + stream.write (self.packageName.upper ()) + + def genPackageNameCap (self, stream, variables): + stream.write (self.packageName.capitalize ()) + + def genClassIncludes (self, stream, variables): + for _class in self.classes: + stream.write ("#include \"qpid/management/") + _class.genNameCap (stream, variables) + stream.write (".h\"\n") + + def genClassRegisters (self, stream, variables): + for _class in self.classes: + stream.write ("agent->RegisterClass (") + _class.genNameCap (stream, variables) + stream.write ("::packageName, ") + _class.genNameCap (stream, variables) + stream.write ("::className, ") + _class.genNameCap (stream, variables) + stream.write ("::md5Sum, ") + _class.genNameCap (stream, variables) + stream.write ("::writeSchema);\n") + diff --git a/qpid/cpp/managementgen/templates/Args.h b/qpid/cpp/managementgen/templates/Args.h new file mode 100644 index 0000000000..576d891a3f --- /dev/null +++ b/qpid/cpp/managementgen/templates/Args.h @@ -0,0 +1,40 @@ +/*MGEN:commentPrefix=//*/ +#ifndef _ARGS_/*MGEN:Method.NameUpper*/_ +#define _ARGS_/*MGEN:Method.NameUpper*/_ + +// +// 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. +// + +/*MGEN:Root.Disclaimer*/ + +#include "qpid/management/Args.h" +#include <string> + +namespace qpid { +namespace management { + +class Args/*MGEN:Method.NameCamel*/ : public Args +{ + public: +/*MGEN:Method.Arguments*/ +}; + +}} + +#endif /*!_ARGS_/*MGEN:Method.NameUpper*/_*/ diff --git a/qpid/cpp/managementgen/templates/Class.cpp b/qpid/cpp/managementgen/templates/Class.cpp new file mode 100644 index 0000000000..5862685670 --- /dev/null +++ b/qpid/cpp/managementgen/templates/Class.cpp @@ -0,0 +1,117 @@ +/*MGEN:commentPrefix=//*/ +// +// 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. +// + +/*MGEN:Root.Disclaimer*/ + +#include "qpid/log/Statement.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/management/Manageable.h" +#include "/*MGEN:Class.NameCap*/.h" +/*MGEN:Class.MethodArgIncludes*/ + +using namespace qpid::management; +using namespace qpid::sys; +using namespace qpid::framing; +using std::string; + +string /*MGEN:Class.NameCap*/::packageName = string ("/*MGEN:Class.NamePackageLower*/"); +string /*MGEN:Class.NameCap*/::className = string ("/*MGEN:Class.NameLower*/"); +uint8_t /*MGEN:Class.NameCap*/::md5Sum[16] = + {/*MGEN:Class.SchemaMD5*/}; + +/*MGEN:Class.NameCap*/::/*MGEN:Class.NameCap*/ (Manageable* _core/*MGEN:Class.ParentArg*//*MGEN:Class.ConstructorArgs*/) : + ManagementObject(_core) + /*MGEN:Class.ConstructorInits*/ +{ + /*MGEN:Class.ParentRefAssignment*/ +/*MGEN:Class.InitializeElements*/ +} + +/*MGEN:Class.NameCap*/::~/*MGEN:Class.NameCap*/ () {} + +namespace { + const string NAME("name"); + const string TYPE("type"); + const string ACCESS("access"); + const string INDEX("index"); + const string UNIT("unit"); + const string MIN("min"); + const string MAX("max"); + const string MAXLEN("maxlen"); + const string DESC("desc"); + const string ARGCOUNT("argCount"); + const string ARGS("args"); + const string DIR("dir"); + const string DEFAULT("default"); +} + +void /*MGEN:Class.NameCap*/::writeSchema (Buffer& buf) +{ + FieldTable ft; + + // Schema class header: + buf.putShortString (packageName); // Package Name + buf.putShortString (className); // Class Name + buf.putBin128 (md5Sum); // Schema Hash + buf.putShort (/*MGEN:Class.ConfigCount*/); // Config Element Count + buf.putShort (/*MGEN:Class.InstCount*/); // Inst Element Count + buf.putShort (/*MGEN:Class.MethodCount*/); // Method Count + buf.putShort (/*MGEN:Class.EventCount*/); // Event Count + + // Config Elements +/*MGEN:Class.ConfigElementSchema*/ + // Inst Elements +/*MGEN:Class.InstElementSchema*/ + // Methods +/*MGEN:Class.MethodSchema*/ + // Events +/*MGEN:Class.EventSchema*/ +} + +void /*MGEN:Class.NameCap*/::writeConfig (Buffer& buf) +{ + sys::RWlock::ScopedRlock readLock (accessLock); + configChanged = false; + + writeTimestamps (buf); +/*MGEN:Class.WriteConfig*/ +} + +void /*MGEN:Class.NameCap*/::writeInstrumentation (Buffer& buf, bool skipHeaders) +{ + sys::RWlock::ScopedWlock writeLock (accessLock); + instChanged = false; + + if (!skipHeaders) + writeTimestamps (buf); +/*MGEN:Class.WriteInst*/ + + // Maintenance of hi-lo statistics +/*MGEN:Class.HiLoStatResets*/ +} + +void /*MGEN:Class.NameCap*/::doMethod (/*MGEN:Class.DoMethodArgs*/) +{ + Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; +/*MGEN:Class.MethodHandlers*/ + outBuf.putLong (status); + outBuf.putShortString (Manageable::StatusText (status)); +} + diff --git a/qpid/cpp/managementgen/templates/Class.h b/qpid/cpp/managementgen/templates/Class.h new file mode 100644 index 0000000000..d95a06479e --- /dev/null +++ b/qpid/cpp/managementgen/templates/Class.h @@ -0,0 +1,76 @@ +/*MGEN:commentPrefix=//*/ +#ifndef _MANAGEMENT_/*MGEN:Class.NameUpper*/_ +#define _MANAGEMENT_/*MGEN:Class.NameUpper*/_ + +// +// 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. +// + +/*MGEN:Root.Disclaimer*/ + +#include "qpid/management/ManagementObject.h" +#include "qpid/framing/Uuid.h" + +namespace qpid { +namespace management { + +class /*MGEN:Class.NameCap*/ : public ManagementObject +{ + private: + + static std::string packageName; + static std::string className; + static uint8_t md5Sum[16]; + + // Configuration Elements +/*MGEN:Class.ConfigDeclarations*/ + // Instrumentation Elements +/*MGEN:Class.InstDeclarations*/ + // Private Methods + static void writeSchema (qpid::framing::Buffer& buf); + void writeConfig (qpid::framing::Buffer& buf); + void writeInstrumentation (qpid::framing::Buffer& buf, + bool skipHeaders = false); + void doMethod (std::string methodName, + qpid::framing::Buffer& inBuf, + qpid::framing::Buffer& outBuf); + writeSchemaCall_t getWriteSchemaCall (void) { return writeSchema; } + +/*MGEN:Class.InstChangedStub*/ + public: + + friend class Package/*MGEN:Class.NamePackageCap*/; + typedef boost::shared_ptr</*MGEN:Class.NameCap*/> shared_ptr; + + /*MGEN:Class.NameCap*/ (Manageable* coreObject/*MGEN:Class.ParentArg*//*MGEN:Class.ConstructorArgs*/); + ~/*MGEN:Class.NameCap*/ (void); + + std::string getPackageName (void) { return packageName; } + std::string getClassName (void) { return className; } + uint8_t* getMd5Sum (void) { return md5Sum; } + + // Method IDs +/*MGEN:Class.MethodIdDeclarations*/ + // Accessor Methods +/*MGEN:Class.AccessorMethods*/ +}; + +}} + + +#endif /*!_MANAGEMENT_/*MGEN:Class.NameUpper*/_*/ diff --git a/qpid/cpp/managementgen/templates/Makefile.mk b/qpid/cpp/managementgen/templates/Makefile.mk new file mode 100644 index 0000000000..0e6454c13a --- /dev/null +++ b/qpid/cpp/managementgen/templates/Makefile.mk @@ -0,0 +1,37 @@ +# +# 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. +# +/*MGEN:commentPrefix=#*/ +/*MGEN:mgenDir=$(mgen_dir)*/ +/*MGEN:specDir=$(top_srcdir)/../specs*/ +/*MGEN:Root.Disclaimer*/ + +mgen_generator=/*MGEN:Makefile.GenSources*/ + +mgen_broker_cpp=/*MGEN:Makefile.GenCppFiles*/ + +# Header file install rules. +qpid_managementdir = $(includedir)/qpid/management +dist_qpid_management_HEADERS = /*MGEN:Makefile.GenHFiles*/ + +if GENERATE +$(srcdir)/managementgen.mk: $(mgen_generator) + $(mgen_cmd) + +$(mgen_generator): +endif diff --git a/qpid/cpp/managementgen/templates/Package.cpp b/qpid/cpp/managementgen/templates/Package.cpp new file mode 100644 index 0000000000..0c5af8d71d --- /dev/null +++ b/qpid/cpp/managementgen/templates/Package.cpp @@ -0,0 +1,32 @@ +/*MGEN:commentPrefix=//*/ +// +// 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. +// + +/*MGEN:Root.Disclaimer*/ + +#include "qpid/management/Package/*MGEN:Schema.PackageNameCap*/.h" +/*MGEN:Schema.ClassIncludes*/ + +using namespace qpid::management; + +Package/*MGEN:Schema.PackageNameCap*/::Package/*MGEN:Schema.PackageNameCap*/ (ManagementAgent::shared_ptr agent) +{ +/*MGEN:Schema.ClassRegisters*/ +} + diff --git a/qpid/cpp/managementgen/templates/Package.h b/qpid/cpp/managementgen/templates/Package.h new file mode 100644 index 0000000000..214f811a1f --- /dev/null +++ b/qpid/cpp/managementgen/templates/Package.h @@ -0,0 +1,41 @@ +/*MGEN:commentPrefix=//*/ +#ifndef _MANAGEMENT_PACKAGE_/*MGEN:Schema.PackageNameUpper*/_ +#define _MANAGEMENT_PACKAGE_/*MGEN:Schema.PackageNameUpper*/_ + +// +// 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. +// + +/*MGEN:Root.Disclaimer*/ + +#include "qpid/management/ManagementAgent.h" + +namespace qpid { +namespace management { + +class Package/*MGEN:Schema.PackageNameCap*/ +{ + public: + Package/*MGEN:Schema.PackageNameCap*/ (ManagementAgent::shared_ptr agent); + ~Package/*MGEN:Schema.PackageNameCap*/ () {} +}; + +}} + + +#endif /*!_MANAGEMENT_PACKAGE_/*MGEN:Schema.PackageNameUpper*/_*/ diff --git a/qpid/cpp/qpid-autotools-install b/qpid/cpp/qpid-autotools-install new file mode 100755 index 0000000000..7fab3d0e9d --- /dev/null +++ b/qpid/cpp/qpid-autotools-install @@ -0,0 +1,205 @@ +#!/bin/sh +# Written by Jim Meyering + +VERSION='2007-07-10 09:09' # UTC + +prog_name=`basename $0` +die () { echo "$prog_name: $*" >&2; exit 1; } + +tarballs=' + http://pkgconfig.freedesktop.org/releases/pkg-config-0.21.tar.gz + ftp://ftp.gnu.org/gnu/m4/m4-1.4.10.tar.gz + ftp://ftp.gnu.org/gnu/automake/automake-1.10.tar.gz + ftp://ftp.gnu.org/gnu/autoconf/autoconf-2.61.tar.gz + ftp://ftp.gnu.org/gnu/libtool/libtool-1.5.24.tar.gz +' + +usage() { + echo >&2 "\ +Usage: $0 [OPTION]... +Download, build, and install some tools. + +Options: + --prefix=PREFIX install tools under specified directory + --skip-check do not run "make check" (this can save 50+ min) + --help display this help and exit + +For example, to install programs into \$HOME/qpid-tools/bin, run this command: + + $prog_name --prefix=\$HOME/qpid-tools + +If you've already verified that your system/environment can build working +versions of these tools, you can make this script complete in just a +minute or two (rather than about an hour if you let all "make check" +tests run) by invoking it like this: + + $prog_name --prefix=\$HOME/qpid-tools --skip-check + +" +} + +# Get the listed tarballs into the current directory. +get_sources() +{ + case `wget --help` in + *'--no-cache'*) + WGET_COMMAND='wget -nv --no-cache';; + *'--cache=on/off'*) + WGET_COMMAND='wget -nv --cache=off';; + *'--non-verbose'*) + WGET_COMMAND='wget -nv';; + *) + die 'no wget program found; please install it and try again';; + esac + + # Download the each tar-ball along with its signature, if there is one. + pkgs= + for t in $(echo $tarballs); do + base=$(basename $t) + pkgs="$pkgs $base" + test -f $base || $WGET_COMMAND $t + + # pkg-config has no .sig file. + case $base in pkg-config*) continue;; esac + + test -f $base.sig || $WGET_COMMAND $t.sig + # Verify each signature. + gpg --quiet --verify --trust-model=always \ + --trusted-key=32419B785D0CDCFC \ + --trusted-key=3859C03B2E236E47 \ + --trusted-key=B93F60C6B5C4CE13 \ + --trusted-key=F382AE19F4850180 \ + $base.sig > /dev/null 2>&1 \ + || echo "info: not verifying GPG signature for $base" 1>&2 + done + + printf 'verifying package SHA1 checksums...' 1>&2 + sha1sum -c --warn --status <<EOF || die "checksum mismatch" +69f37c509a4757d747b6f4c091d209ab3984d62f autoconf-2.61.tar.gz +69dc02b083b9a609b28fc4db129fef6a83ed2339 automake-1.10.tar.gz +b4c994f1bf4a76d2b0c1d0a6f54d16598c15f3db libtool-1.5.24.tar.gz +26d47c893722d683308f5d9fc172a11d5b2ad8a9 m4-1.4.10.tar.gz +b2508ba8404cad46ec42f6f58cbca43ae59d715f pkg-config-0.21.tar.gz +EOF + printf 'ok\n' 1>&2 + echo $pkgs +} + +################################################################# +set -e + +# Parse options. + +make_check=yes +prefix= + +for option +do + case $option in + --help) usage; exit;; + --skip-check) make_check=no;; + --prefix=*) prefix=`expr "$option" : '--prefix=\(.*\)'`;; + *) die "$option: unknown option";; + esac +done + +test -n "$prefix" \ + || die "you must specify a --prefix" + +case $prefix in + /*) ;; + *) die 'invalid prefix: '"$prefix"': it must be an absolute name';; +esac + +# Don't run as root. +# Make sure id -u succeeds. +my_uid=`id -u` +test $? = 0 || { + echo "$0: cannot run \`id -u'" 1>&2 + (exit 1); exit 1 +} +test $my_uid = 0 && die "please don't run this program as root" + +# Ensure that prefix is not /usr/bin or /bin, /sbin, etc. +case $prefix in + /bin|/sbin|/usr/bin|/usr/sbin) + die "don't set PREFIX to a system directory";; + *) ;; +esac + +# Create a build directory, then cd into it for the rest.... +tmpdir=.build-auto-tools +mkdir -p $tmpdir +cd $tmpdir + +pkgs=$(get_sources) + +for pkg in $pkgs; do + echo building/installing $pkg... + dir=$(basename $pkg .tar.gz) + rm -rf dir + gzip -dc $pkg|tar xf - + cd $dir + ./configure CFLAGS=-O2 LDFLAGS=-s --prefix=$prefix > makerr-config 2>&1 + make -j1 > makerr-build 2>&1 + if test "$make_check" = yes; then + case $pkg in + automake*) expected_duration_minutes=40;; + autoconf*) expected_duration_minutes=15;; + # libtool*) expected_duration_minutes=3;; + *);; + esac + test -n "$expected_duration_minutes" \ + && echo "running 'make check' for $pkg; NB: this can take over" \ + "$expected_duration_minutes minutes" + case $pkg in + # In this package, the check-requires-private test fails. + # Change the Makefile so it skips that test. + pkg-config-0.21.tar.gz) + perl -pi.bak -e 's/check-requires-private //' check/Makefile;; + + esac + make -j1 check > makerr-check 2>&1 + fi + make -j1 install > makerr-install 2>&1 + echo done at $(date +%Y-%m-%d.%T) + cd .. +done + +# Without checks (and with existing tarballs), it takes just one minute. +# Including all checks, it takes nearly an hour on an AMD64/3400+ + +case $PKG_CONFIG_PATH in + $prefix/lib/pkgconfig:/usr/lib/pkgconfig) + echo 'Good! your PKG_CONFIG_PATH envvar is already set';; + *) cat <<EOF;; +************************************************************************** +Be sure that PKG_CONFIG_PATH is set in your environment, e.g., +PKG_CONFIG_PATH=$prefix/lib/pkgconfig:/usr/lib/pkgconfig +************************************************************************** +EOF +esac + +case $PATH in + "$prefix/bin:"*) echo 'Good! your PATH is fine';; + *) cat <<EOF;; +************************************************************************** +Be sure that "$prefix/bin" is earlier in your PATH than /bin, /usr/bin, etc. +************************************************************************** +EOF +esac + +cat <<EOF +************************************************************************** +You may want to remove the tool build directory: +rm -rf $tmpdir +************************************************************************** +EOF + +## Local Variables: +## eval: (add-hook 'write-file-hooks 'time-stamp) +## time-stamp-start: "VERSION='" +## time-stamp-format: "%:y-%02m-%02d %02H:%02M" +## time-stamp-time-zone: "UTC" +## time-stamp-end: "' # UTC" +## End: diff --git a/qpid/cpp/qpid-config.in b/qpid/cpp/qpid-config.in new file mode 100755 index 0000000000..5a65767a8c --- /dev/null +++ b/qpid/cpp/qpid-config.in @@ -0,0 +1,98 @@ +#!/bin/sh + +prefix=@prefix@ +exec_prefix=@exec_prefix@ +exec_prefix_set=no +includedir=@includedir@ + +usage() +{ + cat <<EOF +Usage: qpid-config [OPTION] ... + +Generic options + --version output Qpid version information. + --help display this help and exit. + +Compilation support options + --cflags print pre-processor and compiler flags + --libs print library linking information + +Install directories Qpid was configured to + --prefix[=DIR] + --exec-prefix[=DIR] + +EOF + exit $1 +} + +if test $# -eq 0; then + usage 1 1>&2 +fi + +while test $# -gt 0; do + case "$1" in + -*=*) optarg=`echo "$1" | sed 's/[-_a-zA-Z0-9]*=//'` ;; + *) optarg= ;; + esac + + case $1 in + --prefix=*) + prefix=$optarg + if test $exec_prefix_set = no ; then + exec_prefix=$optarg + fi + ;; + --prefix) + echo_prefix=yes + ;; + --exec-prefix=*) + exec_prefix=$optarg + exec_prefix_set=yes + ;; + --exec-prefix) + echo_exec_prefix=yes + ;; + --version) + echo @QPID_VERSION@ + ;; + --help) + usage 0 + ;; + --cflags) + echo_cflags=yes + ;; + --libs) + echo_libs=yes + ;; + *) + usage 1 1>&2 + ;; + esac + shift +done + +if test "$echo_prefix" = "yes"; then + echo $prefix +fi + +if test "$echo_exec_prefix" = "yes"; then + echo $exec_prefix +fi + +if test "$echo_cflags" = "yes"; then + if test "$includedir" != "/usr/include" ; then + echo -I$includedir + fi +fi + +if test "$echo_libs" = "yes"; then + if test @libdir@ != /usr/lib ; then + my_linker_flags="-L@libdir@" + fi + echo ${my_linker_flags} -lqpidcommon +fi + + + + diff --git a/qpid/cpp/qpidc.spec.in b/qpid/cpp/qpidc.spec.in new file mode 100644 index 0000000000..2819f02c45 --- /dev/null +++ b/qpid/cpp/qpidc.spec.in @@ -0,0 +1,253 @@ +# +# Spec file for Qpid C++ packages: qpidc qpidc-devel, qpidd, qpidd-devel +# +%define qpidd qpidd + +Name: @PACKAGE@ +Version: @VERSION@ +Release: 25%{?dist} +Summary: Libraries for Qpid C++ client applications +Group: System Environment/Libraries +License: Apache Software License +URL: @URL@ +Source0: @DOWNLOAD_URL@/%{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) + +ExclusiveArch: i386 x86_64 + +BuildRequires: boost-devel +BuildRequires: cppunit-devel +BuildRequires: doxygen +BuildRequires: e2fsprogs-devel +BuildRequires: graphviz +BuildRequires: help2man +BuildRequires: libtool +BuildRequires: pkgconfig +BuildRequires: ruby + +Requires: boost + +Requires(post):/sbin/chkconfig +Requires(preun):/sbin/chkconfig +Requires(preun):/sbin/service +Requires(postun):/sbin/service + +%description +Run-time libraries for AMQP client applications developed using Qpid +C++. Clients exchange messages with an AMQP message broker using +the AMQP protocol. + +%package devel +Summary: Header files and documentation for developing Qpid C++ clients +Group: Development/System +Requires: %name = %version-%release +Requires: boost-devel +Requires: e2fsprogs-devel + +%description devel +Libraries, header files and documentation for developing AMQP clients +in C++ using Qpid. Qpid implements the AMQP messaging specification. + +%package -n %{qpidd} +Summary: An AMQP message broker daemon +Group: System Environment/Daemons +Requires: %name = %version-%release +Requires: openais + +%description -n %{qpidd} +A message broker daemon that receives stores and routes messages using +the open AMQP messaging protocol. + +%package -n %{qpidd}-devel +Summary: Libraries and header files for developing Qpid broker extensions +Group: Development/System +Requires: %name-devel = %version-%release +Requires: %{qpidd} = %version-%release +Requires: openais-devel + +%description -n %{qpidd}-devel +Libraries and header files for developing extensions to the +Qpid broker daemon. + +%pre +getent group qpidd >/dev/null || groupadd -r qpidd +getent passwd qpidd >/dev/null || \ + useradd -r -m -g qpidd -d %{_localstatedir}/lib/qpidd -s /sbin/nologin \ + -c "Owner of Qpidd Daemons" qpidd +exit 0 + +%prep +%setup -q + +%build +%configure --disable-static --without-cpg CXXFLAGS="-g -O3 -DNDEBUG" +make %{?_smp_mflags} +# Remove this generated perl file, we don't need it and it upsets rpmlint. +rm docs/api/html/installdox + +%install +rm -rf %{buildroot} +make install-strip DESTDIR=%{buildroot} +install -Dp -m0755 etc/qpidd %{buildroot}%{_initrddir}/qpidd +install -d -m0755 %{buildroot}%{_localstatedir}/lib/qpidd +rm -f %{buildroot}%_libdir/*.a +rm -f %{buildroot}%_libdir/*.la + +%clean +rm -rf %{buildroot} + +%check +make check + +%files +%defattr(-,root,root,-) +%doc LICENSE NOTICE README +%_libdir/libqpidcommon.so.0 +%_libdir/libqpidcommon.so.0.1.0 +%_libdir/libqpidclient.so.0 +%_libdir/libqpidclient.so.0.1.0 +%config(noreplace) %_sysconfdir/qpidd.conf + +%files devel +%defattr(-,root,root,-) +%_includedir/qpid/*.h +%_includedir/qpid/amqp_0_10 +%_includedir/qpid/client +%_includedir/qpid/framing +%_includedir/qpid/sys +%_includedir/qpid/log +%_includedir/qpid/management +%_libdir/libqpidcommon.so +%_libdir/libqpidclient.so +%doc %_datadir/%{name}/examples +%doc docs/api/html + +%files -n %{qpidd} +%defattr(-,root,root,-) +%_libdir/libqpidbroker.so.0 +%_libdir/libqpidbroker.so.0.1.0 +%_libdir/libqpidcluster.so.0 +%_libdir/libqpidcluster.so.0.1.0 +%_sbindir/%{qpidd} +%{_initrddir}/%{qpidd} +%_localstatedir/lib/qpidd +%doc %_mandir/man1/%{qpidd}.* + +%files -n %{qpidd}-devel +%defattr(-,root,root,-) +%doc rpm/README.qpidd-devel +%defattr(-,root,root,-) +%_libdir/libqpidbroker.so +%_libdir/libqpidcluster.so +%_includedir/qpid/broker + +%post -p /sbin/ldconfig + +%postun -p /sbin/ldconfig + +%post -n %{qpidd} +# This adds the proper /etc/rc*.d links for the script +/sbin/chkconfig --add qpidd +/sbin/ldconfig + +%preun -n %{qpidd} +# Check that this is actual deinstallation, not just removing for upgrade. +if [ $1 = 0 ]; then + /sbin/service qpidd stop >/dev/null 2>&1 || : + /sbin/chkconfig --del qpidd +fi + +%postun -n %{qpidd} +if [ "$1" -ge "1" ]; then + /sbin/service qpidd condrestart >/dev/null 2>&1 || : +fi +/sbin/ldconfig + +%changelog +* Mon Mar 31 2008 Nuno Santos <nsantos@redhat.com> - 0.2-25 +- Create user qpidd, start qpidd service as qpidd + +* Mon Feb 18 2008 Rafael Schloming <rafaels@redhat.com> - 0.2-24 +- Bug fix for TCK issue in Beta 3 + +* Thu Feb 14 2008 Rafael Schloming <rafaels@redhat.com> - 0.2-23 +- Bumped to pull in fixes for Beta 3 + +* Tue Feb 12 2008 Alan Conway <aconway@redhat.com> - 0.2-22 +- Added -g to compile flags for debug symbols. + +* Tue Feb 12 2008 Alan Conway <aconway@redhat.com> - 0.2-21 +- Create /var/lib/qpidd correctly. + +* Mon Feb 11 2008 Rafael Schloming <rafaels@redhat.com> - 0.2-20 +- bumped for Beta 3 + +* Mon Jan 21 2008 Gordon Sim <gsim@redhat.com> - 0.2-18 +- bump up rev for recent changes to plugin modules & mgmt + +* Thu Jan 03 2008 Nuno Santos <nsantos@redhat.com> - 0.2-17 +- add missing header file SessionManager.h + +* Thu Jan 03 2008 Nuno Santos <nsantos@redhat.com> - 0.2-16 +- limit builds to i386 and x86_64 archs + +* Thu Jan 03 2008 Nuno Santos <nsantos@redhat.com> - 0.2-15 +- add ruby as a build dependency + +* Tue Dec 18 2007 Nuno Santos <nsantos@redhat.com> - 0.2-14 +- include fixes from Gordon Sim (fragmentation, lazy-loading, staging) + and Alan Conway (exception handling in the client). + +* Thu Dec 6 2007 Alan Conway <aconway@redhat.com> - 0.2-13 +- installcheck target to build examples in installation. + +* Thu Nov 8 2007 Alan Conway <aconway@redhat.com> - 0.2-10 +- added examples to RPM package. + +* Thu Oct 9 2007 Alan Conway <aconway@redhat.com> - 0.2-9 +- added config(noreplace) for qpidd.conf + +* Thu Oct 4 2007 Alan Conway <aconway@redhat.com> - 0.2-8 +- Added qpidd.conf configuration file. +- Updated man page to detail configuration options. + +* Thu Sep 20 2007 Alan Conway <aconway@redhat.com> - 0.2-7 +- Removed apr dependency. + +* Wed Aug 1 2007 Alan Conway <aconway@redhat.com> - 0.2-6 +- added --disable-cluster flag + +* Tue Apr 17 2007 Alan Conway <aconway@redhat.com> - 0.2-5 +- Add missing Requires: e2fsprogs-devel for qpidc-devel. + +* Tue Apr 17 2007 Alan Conway <aconway@redhat.com> - 0.2-4 +- longer broker_start timeout to avoid failures in plague builds. + +* Tue Apr 17 2007 Alan Conway <aconway@redhat.com> - 0.2-3 +- Add missing Requires: apr in qpidc. + +* Mon Apr 16 2007 Alan Conway <aconway@redhat.com> - 0.2-2 +- Bugfix for memory errors on x86_64. + +* Thu Apr 12 2007 Alan Conway <aconway@redhat.com> - 0.2-1 +- Bumped version number for rhm dependencies. + +* Wed Apr 11 2007 Alan Conway <aconway@redhat.com> - 0.1-5 +- Add qpidd-devel sub-package. + +* Mon Feb 19 2007 Jim Meyering <meyering@redhat.com> - 0.1-4 +- Address http://bugzilla.redhat.com/220630: +- Remove redundant "cppunit" build-requires. +- Add --disable-static. + +* Thu Jan 25 2007 Alan Conway <aconway@redhat.com> - 0.1-3 +- Applied Jim Meyerings fixes from http://mail-archives.apache.org/mod_mbox/incubator-qpid-dev/200701.mbox/<87hcugzmyp.fsf@rho.meyering.net> + +* Mon Dec 22 2006 Alan Conway <aconway@redhat.com> - 0.1-1 +- Fixed all rpmlint complaints (with help from David Lutterkort) +- Added qpidd --daemon behaviour, fix init.rc scripts + +* Fri Dec 8 2006 David Lutterkort <dlutter@redhat.com> - 0.1-1 +- Initial version based on Jim Meyering's sketch and discussions with Alan + Conway + diff --git a/qpid/cpp/rpm/README.qpidd-devel b/qpid/cpp/rpm/README.qpidd-devel new file mode 100644 index 0000000000..f64a9de3b3 --- /dev/null +++ b/qpid/cpp/rpm/README.qpidd-devel @@ -0,0 +1,7 @@ + +This package provides header files needed to implement extensions to +the Qpid broker daemon. This feature is experimental and undocumented, +it is not require for normal use of qpid. + +See http://cwiki.apache.org/qpid/ for more information about the qpid project. + diff --git a/qpid/cpp/rubygen/0-10/allsegmenttypes.rb b/qpid/cpp/rubygen/0-10/allsegmenttypes.rb new file mode 100755 index 0000000000..c4c4095e02 --- /dev/null +++ b/qpid/cpp/rubygen/0-10/allsegmenttypes.rb @@ -0,0 +1,34 @@ +#!/usr/bin/env ruby +$: << ".." # Include .. in load path +require 'cppgen' + +class GenAllSegmentTypes < CppGen + def initialize(outdir, amqp) + super(outdir, amqp) + end + + def generate + h_file("tests/allSegmentTypes.h") { + include "qpid/amqp_0_10/specification.h" + include "qpid/amqp_0_10/Header.h" + include "qpid/amqp_0_10/Body.h" + genl + genl "using namespace qpid::amqp_0_10;" + genl + scope("template <class Op> size_t allSegmentTypes(Op& op) {"){ + genl "op(Header());" + genl "op(Body());" + n = 2; + @amqp.classes.each { |c| + c.commands.each { |s| genl "op(CommandHolder(#{c.nsname}::#{s.classname}()));" } + c.controls.each { |s| genl "op(ControlHolder(#{c.nsname}::#{s.classname}()));" } + n += 2 + } + genl "return #{n};" + } + } + end +end + +GenAllSegmentTypes.new($outdir, $amqp).generate(); + diff --git a/qpid/cpp/rubygen/0-10/exceptions.rb b/qpid/cpp/rubygen/0-10/exceptions.rb new file mode 100755 index 0000000000..2f62b2ccdf --- /dev/null +++ b/qpid/cpp/rubygen/0-10/exceptions.rb @@ -0,0 +1,55 @@ +#!/usr/bin/env ruby +$: << ".." # Include .. in load path +require 'cppgen' + +class GenExceptions < CppGen + + def initialize(outdir, amqp) + super(outdir, amqp) + @ns="qpid::amqp_#{@amqp.version.bars}" + @dir="qpid/amqp_#{@amqp.version.bars}" + end + + def exceptions_for_enum(enum, base, ns, suffix="") + enum.choices.each { |c| + name=c.name.typename+suffix+"Exception" + genl + doxygen_comment { genl c.doc } + struct(name, "public #{base}") { + genl "#{name}(const std::string& msg=std::string())" + genl " : #{base}(#{ns}::#{c.name.shout}, msg) {}" + protected + genl "std::string getPrefix() const { return \"#{name}\"; }" + } + } + end + + def gen_exceptions() + h_file("#{@dir}/exceptions") { + include "qpid/amqp_0_10/Exception" + namespace("#{@ns}") { + error_code = @amqp.class_("execution").domain("error-code").enum + exceptions_for_enum(error_code, "SessionAbortedException", "execution") + genl + + detach_code = @amqp.class_("session").domain("detach-code").enum + exceptions_for_enum(detach_code, "SessionDetachedException", "session", "Detached") + + genl + exceptions_for_enum(detach_code, "SessionExpiredException", "session", "Expired") + genl + + close_code = @amqp.class_("connection").domain("close-code").enum + exceptions_for_enum(close_code, "ConnectionException", "connection") + } + } + end + + def generate() + gen_exceptions + end +end + +GenExceptions.new($outdir, $amqp).generate(); + + diff --git a/qpid/cpp/rubygen/0-10/handlers.rb b/qpid/cpp/rubygen/0-10/handlers.rb new file mode 100755 index 0000000000..c23eb5faf4 --- /dev/null +++ b/qpid/cpp/rubygen/0-10/handlers.rb @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +$: << ".." # Include .. in load path +require 'cppgen' + +class GenHandlers < CppGen + def initialize(outdir, amqp) + super(outdir, amqp) + @ns="qpid::amqp_#{@amqp.version.bars}" + @dir="qpid/amqp_#{@amqp.version.bars}" + end + + def action_handler(type, actions) + genl + bases=actions.map { |a| "public #{a.fqclassname}::Handler" } + struct("#{type}Handler", *bases) { } + end + + def generate() + h_file("#{@dir}/handlers.h") { + include "specification" + namespace("#{@ns}") { + action_handler "Command", @amqp.collect_all(AmqpCommand) + action_handler "Control", @amqp.collect_all(AmqpControl) + } + } + end +end + +GenHandlers.new($outdir, $amqp).generate() diff --git a/qpid/cpp/rubygen/0-10/specification.rb b/qpid/cpp/rubygen/0-10/specification.rb new file mode 100755 index 0000000000..7e73d1c91e --- /dev/null +++ b/qpid/cpp/rubygen/0-10/specification.rb @@ -0,0 +1,334 @@ +#!/usr/bin/env ruby +$: << ".." # Include .. in load path +require 'cppgen' + +class Specification < CppGen + def initialize(outdir, amqp) + super(outdir, amqp) + @ns="qpid::amqp_#{@amqp.version.bars}" + @dir="qpid/amqp_#{@amqp.version.bars}" + end + + # domains + + def domain_h(d) + genl + typename=d.name.typename + if d.enum + scope("enum #{typename} {", "};") { + genl d.enum.choices.map { |c| + "#{c.name.constname} = #{c.value}" }.join(",\n") + } + scope("inline SerializeAs<#{typename}, uint8_t> serializable(#{typename}& e) {") { + genl "return SerializeAs<#{typename}, uint8_t>(e);" + } + else + genl "typedef #{d.amqp2cpp} #{typename};" + end + end + + # class constants + + def class_h(c) + genl "const uint8_t CODE=#{c.code};" + genl "extern const char* NAME;" + end + + def class_cpp(c) + genl "const char* NAME=\"#{c.fqname}\";" + end + + def visitable?(x) x.code and x.size=="4" end + + # Used by structs, commands and controls. + def action_struct_h(x, base, consts, &block) + genl + base = visitable?(x) ? ["public #{base}"] : [] + struct(x.classname, *base) { + x.fields.each { |f| genl "#{f.amqp2cpp} #{f.cppname};" } + genl + genl "static const char* NAME;" + consts.each { + |c| genl "static const uint8_t #{c.upcase}=#{(x.send c) or 0};" + } + genl "static const uint8_t CLASS_CODE=#{x.containing_class.nsname}::CODE;" + genl "static const char* CLASS_NAME;" + ctor_decl("explicit #{x.classname}", x.parameters(true)) + + if visitable? x + genl "void accept(Visitor&);" + genl "void accept(ConstVisitor&) const;" + end + + if (x.fields.empty?) + genl "template <class S> void serialize(S&) {}" + else + scope("template <class S> void serialize(S& s) {") { + gen "s"; x.fields.each { |f| gen "(#{f.cppname})"}; genl ";" + } + end + genl + yield if block + } + genl "inline Packer<#{x.classname}> serializable(#{x.classname}& x) { return Packer<#{x.classname}>(x); }" unless x.respond_to? :pack and x.pack == "0" + genl "std::ostream& operator << (std::ostream&, const #{x.classname}&);" + genl "bool operator==(const #{x.classname}&, const #{x.classname}&);" + end + + def action_struct_cpp(x, &block) + genl + genl "const char* #{x.classname}::NAME=\"#{x.fqname}\";" + genl "const char* #{x.classname}::CLASS_NAME=#{x.containing_class.nsname}::NAME;" + genl + ctor=x.classname+"::"+x.classname + ctor_defn(ctor, x.parameters, x.initializers) {} + + if visitable? x + genl "void #{x.classname}::accept(Visitor& v) { v.visit(*this); }" + genl "void #{x.classname}::accept(ConstVisitor& v) const { v.visit(*this); }" + end + genl + scope("std::ostream& operator << (std::ostream& o, const #{x.classname}&#{"x" unless x.fields.empty?}) {") { + genl "o << \"[#{x.fqname}\";"; + x.fields.each{ |f| genl "o << \" #{f.name}=\" << x.#{f.cppname};" } + genl "o << \"]\";" + genl "return o;" + } + yield if block + end + + # structs + + def struct_h(s) action_struct_h(s, "Struct", ["size","pack","code"]); end + def struct_cpp(s) action_struct_cpp(s) end + + # command and control + + def action_h(a) + action_struct_h(a, a.base, ["code"]) { + struct("Handler") { + scope("void #{a.funcname}(", ");") { + genl a.parameters.join(",\n") + } + } + function_defn("template <class T> void invoke", ["T& target"], "const") { + genl "target.#{a.funcname}(#{a.values.join(', ')} );" + } + } + end + + def action_cpp(a) + action_struct_cpp(a) { + scope("void #{a.classname}::Handler::#{a.funcname}(", ")") { + genl a.unused_parameters.join(",\n") + } + scope { + genl "assert(0);" + genl "throw NotImplementedException(QPID_MSG(\"#{a.fqname} not implemented.\"));" + } + } + end + + # Types that must be generated early because they are used by other types. + def pregenerate?(x) not @amqp.used_by[x.fqname].empty?; end + + def pregenerate_class?(c) + c.children.select{ |t| (t.is_a? AmqpStruct or t.is_a? AmqpDomain) and pregenerate? t} + end + + # Typedefs, enums and forward declarations for classes. + def gen_specification_fwd() + h_file("#{@dir}/specification_fwd") { + include "#{@dir}/built_in_types" + namespace(@ns) { + # Top level + @amqp.domains.each { |d| + # segment-type and track are are built in + domain_h d unless ["track","segment-type"].include?(d.name) + } + # Domains/structs that must be generated early because they are used by + # other definitions: + @amqp.classes.select{ |c| pregenerate_class?(c) }.each { |c| + namespace(c.nsname) { + c.collect_all(AmqpDomain).each { |d| domain_h d if pregenerate? d } + c.collect_all(AmqpStruct).each { |s| genl "class #{s.classname};" if pregenerate? s } + } + } + # Now dependent domains/structs and actions + each_class_ns { |c| + class_h c + c.collect_all(AmqpDomain).each { |d| domain_h d unless pregenerate? d} + c.collect_all(AmqpStruct).each { |s| genl "class #{s.classname};" unless pregenerate? s } + c.collect_all(AmqpAction).each { |a| genl "class #{a.classname};" unless pregenerate? a } + } + } + } + end + + # Generate the specification files + def gen_specification() + h_file("#{@dir}/specification") { + include "#{@dir}/specification_fwd" + include "#{@dir}/all_built_in_types" + include "#{@dir}/Packer.h" + include "<iosfwd>" + namespace(@ns) { + # Structs that must be generated early because + # they are used by other definitions: + each_class_ns { |c| + c.collect_all(AmqpStruct).each { |s| struct_h s if pregenerate? s } + } + # Now dependent domains/structs and actions + each_class_ns { |c| + c.collect_all(AmqpStruct).each { |s| struct_h s unless pregenerate? s} + c.collect_all(AmqpAction).each { |a| action_h a } + } + } + } + + cpp_file("#{@dir}/specification") { + include "#{@dir}/specification" + include "#{@dir}/exceptions" + include "<iostream>" + ["Command","Control", "Struct"].each { |x| include "#{@dir}/Apply#{x}" } + namespace(@ns) { + each_class_ns { |c| + class_cpp c + c.actions.each { |a| action_cpp a} + c.collect_all(AmqpStruct).each { |s| struct_cpp(s) } + } + } + } + end + + def gen_proxy() + h_file("#{@dir}/ProxyTemplate.h") { + include "#{@dir}/specification" + namespace(@ns) { + genl "template <class F, class R=typename F::result_type>" + cpp_class("ProxyTemplate") { + public + genl "ProxyTemplate(F f=F()) : functor(f) {}" + @amqp.classes.each { |c| + c.actions.each { |a| + genl + function_defn("R #{a.funcname}", a.parameters) { + var=a.name.funcname + args = a.arguments.empty? ? "" : "("+a.arguments.join(", ")+")" + genl("#{a.fqclassname} #{var}#{args};") + genl "return functor(#{var});" + } + } + } + private + genl "F functor;" + } + } + } + end + + def visitor_interface_h(base, subs, is_const) + name="#{is_const ? 'Const' : ''}#{base}Visitor" + const=is_const ? "const " : "" + struct(name) { + genl "virtual ~#{name}() {}" + genl "typedef #{const}#{base} BaseType;" + subs.each{ |s| + genl "virtual void visit(#{const}#{s.fqclassname}&) = 0;" + }} + end + + def visitor_impl(base, subs, is_const) + name="#{is_const ? 'Const' : ''}#{base}Visitor" + const=is_const ? "const " : "" + genl "template <class F>" + struct("ApplyVisitor<#{name}, F>", "public ApplyVisitorBase<#{name}, F>") { + subs.each{ |s| + genl "virtual void visit(#{const}#{s.fqclassname}& x) { this->invoke(x); }" + }} + end + + def gen_visitor(base, subs) + h_file("#{@dir}/#{base}Visitor.h") { + include "#{@dir}/specification" + namespace("#{@ns}") { + visitor_interface_h(base, subs, false) + visitor_interface_h(base, subs, true) + }} + + h_file("#{@dir}/Apply#{base}.h") { + include "#{@dir}/#{base}Visitor.h" + include "#{@dir}/apply.h" + namespace("#{@ns}") { + visitor_impl(base, subs, false) + visitor_impl(base, subs, true) + } + } + end + + def gen_holder(base, subs) + name=base.caps+"Holder" + h_file("#{@dir}/#{name}") { + include "#{@dir}/Apply#{base}" + include "#{@dir}/Holder" + namespace(@ns){ + namespace("#{base.downcase}_max") { + gen "template <class M, class X> " + struct("Max") { + genl "static const size_t max=(M::max > sizeof(X)) ? M::max : sizeof(X);" + } + genl "struct Max000 { static const size_t max=0; };" + last="Max000" + subs.each { |s| + genl "typedef Max<#{last}, #{s.fqclassname}> #{last.succ!};" + } + genl "static const int MAX=#{last}::max;" + } + holder_base="amqp_0_10::Holder<#{base}Holder, #{base}, #{base.downcase}_max::MAX>" + struct("#{name}", "public #{holder_base}") { + genl "#{name}() {}" + genl "template <class T> explicit #{name}(const T& t) : #{holder_base}(t) {}" + genl "using #{holder_base}::operator=;" + genl "void set(uint8_t classCode, uint8_t code);" + } + genl + genl "std::ostream& operator<<(std::ostream& o, const #{name}& h);" + } + } + + cpp_file("#{@dir}/#{name}") { + include "#{name}" + include "exceptions.h" + namespace(@ns) { + genl "using framing::in_place;" + genl + scope("void #{name}::set(uint8_t classCode, uint8_t code) {") { + genl "uint16_t key=(classCode<<8)+code;" + scope ("switch(key) {") { + subs.each { |s| + genl "case 0x#{s.full_code.to_s(16)}: *this=in_place<#{s.fqclassname}>(); break;" + } + genl "default: throw CommandInvalidException(QPID_MSG(\"Invalid class-#{base.downcase} key \" << std::hex << key));" + }} + genl + genl "std::ostream& operator<<(std::ostream& o, const #{name}& h) { return h.get() ? (o << *h.get()) : (o << \"<empty #{name}>\"); }" + } + } + end + + def gen_visitable(base, subs) + gen_holder(base, subs) + gen_visitor(base, subs) + end + + def generate + gen_specification_fwd + gen_specification + gen_proxy + gen_visitable("Command", @amqp.collect_all(AmqpCommand)) + gen_visitable("Control", @amqp.collect_all(AmqpControl)) + gen_visitable("Struct", @amqp.collect_all(AmqpStruct).select { |s| s.code}) + end +end + +Specification.new($outdir, $amqp).generate(); diff --git a/qpid/cpp/rubygen/0-10/typecode.rb b/qpid/cpp/rubygen/0-10/typecode.rb new file mode 100755 index 0000000000..e36b92c07c --- /dev/null +++ b/qpid/cpp/rubygen/0-10/typecode.rb @@ -0,0 +1,99 @@ +#!/usr/bin/env ruby +$: << ".." # Include .. in load path +require 'cppgen' + +class TypeCode < CppGen + def initialize(outdir, amqp) + super(outdir, amqp) + @ns="qpid::amqp_#{@amqp.version.bars}" + @dir="qpid/amqp_#{@amqp.version.bars}" + @types = @amqp.collect_all(AmqpType).select { |t| t.code } + + end + + def type_for_code_h() + h_file("#{@dir}/TypeForCode") { + include "#{@dir}/built_in_types.h" + include "#{@dir}/UnknownType.h" + namespace(@ns) { + genl + genl "template <uint8_t Code> struct TypeForCode;" + genl + @types.each { |t| + genl "template <> struct TypeForCode<#{t.code}> { typedef #{t.typename} type; };" + } + genl + genl "template <class V> typename V::result_type" + scope("apply_visitor(V& visitor, uint8_t code) {") { + scope("switch (code) {", "}") { + @types.each { |t| + genl "case #{t.code}: return visitor((#{t.typename}*)0);" + } + genl "default: return visitor((UnknownType*)0);" + } + } + genl + genl "std::string typeName(uint8_t code);" + } + } + end + + def type_for_code_cpp() + cpp_file("#{@dir}/TypeForCode") { + include "<string>" + include "<sstream>" + namespace(@ns) { + namespace("") { + struct("Names") { + scope("Names() {") { + scope("for (int i =0; i < 256; ++i) {") { + genl "std::ostringstream os;" + genl "os << \"UnknownType<\" << i << \">\";" + genl "names[i] = os.str();" + } + @types.each { |t| genl "names[#{t.code}] = \"#{t.name}\";" } + } + genl "std::string names[256];" + } + genl "Names names;" + } + genl "std::string typeName(uint8_t code) { return names.names[code]; }" + }} + end + + def code_for_type_h() + name="#{@dir}/CodeForType" + h_file(name) { + include "#{@dir}/built_in_types.h" + + namespace(@ns) { + genl + genl "template <class T> struct CodeForType;" + genl + @types.each { |t| + genl "template <> struct CodeForType<#{t.typename}> { static const uint8_t value; };" + } + genl + genl "template <class T> uint8_t codeFor(const T&) { return CodeForType<T>::value; }" + } + } + + cpp_file(name) { + include name + namespace(@ns) { + @types.each { |t| + genl "const uint8_t CodeForType<#{t.typename}>::value=#{t.code};" + } + } + } + end + + def generate + type_for_code_h + type_for_code_cpp + code_for_type_h + end +end + +TypeCode.new($outdir, $amqp).generate(); + diff --git a/qpid/cpp/rubygen/99-0/MethodBodyConstVisitor.rb b/qpid/cpp/rubygen/99-0/MethodBodyConstVisitor.rb new file mode 100755 index 0000000000..f9ef95f5a0 --- /dev/null +++ b/qpid/cpp/rubygen/99-0/MethodBodyConstVisitor.rb @@ -0,0 +1,27 @@ +#!/usr/bin/env ruby +$: << ".." # Include .. in load path +require 'cppgen' + +class MethodBodyConstVisitorGen < CppGen + + def initialize(outdir, amqp) + super(outdir, amqp) + @namespace="qpid::framing" + @classname="MethodBodyConstVisitor" + @filename="qpid/framing/MethodBodyConstVisitor" + end + + def generate() + h_file("#{@filename}") { + namespace(@namespace) { + @amqp.methods_.each { |m| genl "class #{m.body_name};" } + cpp_class("MethodBodyConstVisitor") { + genl "public:" + genl "virtual ~MethodBodyConstVisitor() {}" + @amqp.methods_.each { |m| genl "virtual void visit(const #{m.body_name}&) = 0;" } + }}} + end +end + +MethodBodyConstVisitorGen.new($outdir, $amqp).generate(); + diff --git a/qpid/cpp/rubygen/99-0/MethodBodyDefaultVisitor.rb b/qpid/cpp/rubygen/99-0/MethodBodyDefaultVisitor.rb new file mode 100755 index 0000000000..a74b0c06d6 --- /dev/null +++ b/qpid/cpp/rubygen/99-0/MethodBodyDefaultVisitor.rb @@ -0,0 +1,35 @@ +#!/usr/bin/env ruby +$: << ".." # Include .. in load path +require 'cppgen' + +class MethodBodyDefaultVisitorGen < CppGen + + def initialize(outdir, amqp) + super(outdir, amqp) + @namespace, @classname, @filename = parse_classname("qpid::framing::MethodBodyDefaultVisitor") + end + + def generate() + h_file(@filename) { + include "qpid/framing/MethodBodyConstVisitor" + namespace(@namespace) { + genl "class AMQMethodBody;" + cpp_class(@classname, "public MethodBodyConstVisitor") { + genl "public:" + genl "virtual void defaultVisit(const AMQMethodBody&) = 0;" + @amqp.methods_.each { |m| + genl "virtual void visit(const #{m.body_name}&);" } + }}} + + cpp_file(@filename) { + include(@filename) + include("all_method_bodies.h") + namespace(@namespace) { + @amqp.methods_.each { |m| + genl "void #{@classname}::visit(const #{m.body_name}& b) { defaultVisit(b); }" + }}} + end +end + +MethodBodyDefaultVisitorGen.new($outdir, $amqp).generate(); + diff --git a/qpid/cpp/rubygen/99-0/MethodHolder.rb b/qpid/cpp/rubygen/99-0/MethodHolder.rb new file mode 100755 index 0000000000..a708db6676 --- /dev/null +++ b/qpid/cpp/rubygen/99-0/MethodHolder.rb @@ -0,0 +1,100 @@ +#!/usr/bin/env ruby +$: << ".." # Include .. in load path +require 'cppgen' + +class MethodHolderGen < CppGen + + def initialize(outdir, amqp) + super(outdir, amqp) + @namespace="qpid::framing" + @classname="BodyHolder" + @filename="qpid/framing/BodyHolder" + end + + def gen_max_size() + # Generate program to generate MaxSize.h + cpp_file("generate_MaxMethodBodySize_h") { + include "qpid/framing/AMQHeaderBody" + include "qpid/framing/AMQContentBody" + include "qpid/framing/AMQHeartbeatBody" + @amqp.methods_.each { |m| include "qpid/framing/#{m.body_name}" } + genl + include "<algorithm>" + include "<fstream>" + genl + genl "using namespace std;" + genl "using namespace qpid::framing;" + genl + scope("int main(int, char** argv) {") { + genl "size_t maxSize=0;" + genl "maxSize=max(maxSize, sizeof(AMQHeaderBody));" + genl "maxSize=max(maxSize, sizeof(AMQContentBody));" + genl "maxSize=max(maxSize, sizeof(AMQHeartbeatBody));" + @amqp.methods_.each { |m| + genl "maxSize=max(maxSize, sizeof(#{m.body_name}));" } + gen <<EOS +ofstream out("qpid/framing/MaxMethodBodySize.h"); +out << "// GENERATED CODE: generated by " << argv[0] << endl; +out << "namespace qpid{ namespace framing { " << endl; +out << "const size_t MAX_METHOD_BODY_SIZE=" << maxSize << ";" << endl; +out << "}}" << endl; +EOS + } + } + end + + def gen_construct + cpp_file(@filename+"_gen") { + include @filename + include "qpid/framing/AMQHeaderBody" + include "qpid/framing/AMQContentBody" + include "qpid/framing/AMQHeartbeatBody" + @amqp.methods_.each { |m| include "qpid/framing/#{m.body_name}" } + include "qpid/framing/FrameDefaultVisitor.h" + include "qpid/Exception.h" + genl + namespace(@namespace) { + scope("void #{@classname}::setMethod(ClassId c, MethodId m) {") { + scope("switch (c) {") { + @amqp.classes.each { |c| + scope("case #{c.index}: switch(m) {") { + c.methods_.each { |m| + genl "case #{m.index}: blob = in_place<#{m.body_name}>(); break;" + } + genl "default: throw Exception(QPID_MSG(\"Invalid method id \" << int(m) << \" for class #{c.name} \"));" + } + genl "break;" + } + genl "default: throw Exception(QPID_MSG(\"Invalid class id \" << int(c)));" + } + } + + struct("CopyVisitor", "public FrameDefaultVisitor") { + genl "using FrameDefaultVisitor::visit;" + genl "using FrameDefaultVisitor::defaultVisit;" + genl "BodyHolder& holder;" + genl "CopyVisitor(BodyHolder& h) : holder(h) {}" + ["Header", "Content", "Heartbeat"].each { |type| + genl "void visit(const AMQ#{type}Body& x) { holder=x; }" + } + @amqp.methods_.each { |m| + genl "void visit(const #{m.body_name}& x) { holder=x; }" + } + genl "void defaultVisit(const AMQBody&) { assert(0); }" + } + genl + + scope("void BodyHolder::setBody(const AMQBody& b) {") { + genl "CopyVisitor cv(*this); b.accept(cv);" + } + }} + end + + def generate + gen_max_size + gen_construct + end +end + +MethodHolderGen.new($outdir, $amqp).generate(); + diff --git a/qpid/cpp/rubygen/99-0/Operations.rb b/qpid/cpp/rubygen/99-0/Operations.rb new file mode 100755 index 0000000000..c985bb6105 --- /dev/null +++ b/qpid/cpp/rubygen/99-0/Operations.rb @@ -0,0 +1,96 @@ +#!/usr/bin/env ruby +# Usage: output_directory xml_spec_file [xml_spec_file...] +# +$: << '..' +require 'cppgen' +require 'fileutils' +require 'etc' +require 'pathname' + +class OperationsGen < CppGen + + def initialize(chassis, outdir, amqp) + super(outdir, amqp) + @chassis=chassis + @classname="AMQP_#{@chassis.caps}Operations" + end + + def handler_method (m) + return_type = m.result ? m.result.cpptype.ret : "void" + gen "\nvirtual #{return_type} #{m.cppname}(" + gen m.signature.join(",\n") + gen ") = 0;\n" + end + + def handler_classname(c) c.name.caps+"Handler"; end + + def handler_class(c) + if (!c.methods_on(@chassis).empty?) + handlerclass=handler_classname c + gen <<EOS +// ==================== class #{handlerclass} ==================== +class #{handlerclass} { + // Constructors and destructors + public: + class Invoker; // Declared in #{@chassis.caps}Invoker + + #{handlerclass}(){}; + virtual ~#{handlerclass}() {} + // Protocol methods +EOS + c.methods_on(@chassis).each { |m| handler_method(m) if !m.content() } + gen <<EOS +}; // class #{handlerclass} + + +EOS + end + end + + def handler_get(c) + if (!c.methods_on(@chassis).empty?) + handlerclass=handler_classname c + gen "virtual #{handlerclass}* get#{handlerclass}() = 0;\n" + end + end + + def generate() + h_file("qpid/framing/#{@classname}.h") { + gen <<EOS +#include <sstream> +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/framing/amqp_structs.h" + +namespace qpid { +namespace framing { + +class AMQMethodBody; + +class #{@classname} { + public: + class Invoker; // Declared in #{@chassis.caps}Invoker + + virtual ~#{@classname}() {} + + virtual ProtocolVersion getVersion() const = 0; + + // Inner classes +EOS + indent { @amqp.classes.each { |c| handler_class(c) } } + gen <<EOS + + // Method handler get methods + +EOS + indent { @amqp.classes.each { |c| handler_get(c) } } + gen <<EOS +}; /* class #{@classname} */ +}} +EOS +} + end +end + +OperationsGen.new("client",ARGV[0], $amqp).generate() +OperationsGen.new("server",ARGV[0], $amqp).generate() + diff --git a/qpid/cpp/rubygen/99-0/OperationsInvoker.rb b/qpid/cpp/rubygen/99-0/OperationsInvoker.rb new file mode 100755 index 0000000000..642f98ce8e --- /dev/null +++ b/qpid/cpp/rubygen/99-0/OperationsInvoker.rb @@ -0,0 +1,92 @@ +#!/usr/bin/env ruby +# Usage: output_directory xml_spec_file [xml_spec_file...] +# +$: << '..' +require 'cppgen' + +class OperationsInvokerGen < CppGen + def initialize(chassis, outdir, amqp) + super(outdir, amqp) + @chassis=chassis + @ops="AMQP_#{@chassis.caps}Operations" + @classname="#{@ops}::Invoker" + @filename="qpid/framing/#{@chassis.caps}Invoker" + end + + def handler(c) "#{@ops}::#{c.cppname}Handler"; end + def getter(c) "get#{c.cppname}Handler"; end + def invoker(c) "#{handler(c)}::Invoker"; end + def visit_methods(c) c.methods_on(@chassis).select { |m| !m.content } end + + def handler_visits_cpp(c) + visit_methods(c).each { |m| + scope("void #{invoker(c)}::visit(const #{m.body_name}& body) {") { + if (m.result) + genl "this->encode(body.invoke(target), result.result);" + else + genl "body.invoke(target);" + end + genl "result.handled=true;" + } + } + end + + def ops_visits_cpp() + @amqp.classes.each { |c| + visit_methods(c).each { |m| + scope("void #{@classname}::visit(const #{m.body_name}& body) {") { + genl "#{handler(c)}::Invoker invoker(*target.#{getter(c)}());" + genl "body.accept(invoker);" + genl "result=invoker.getResult();" + } + } + } + end + + def invoker_h(invoker, target, methods) + return if methods.empty? + genl + cpp_class(invoker, "public qpid::framing::Invoker") { + genl "#{target}& target;" + public + genl("Invoker(#{target}& target_) : target(target_) {}") + genl "using MethodBodyDefaultVisitor::visit;" + methods.each { |m| genl "void visit(const #{m.body_name}& body);" } + } + end + + def generate() + h_file(@filename) { + include "qpid/framing/#{@ops}" + include "qpid/framing/Invoker.h" + namespace("qpid::framing") { + # AMQP_*Operations invoker. + methods=@amqp.classes.map { |c| visit_methods(c).to_a }.flatten + invoker_h(@classname, @ops, methods) + + # AMQP_*Operations::*Handler invokers. + @amqp.classes.each { |c| + invoker_h(invoker(c), handler(c), visit_methods(c)) + } + } + } + + cpp_file(@filename) { + include @filename + @amqp.classes.each { |c| + visit_methods(c).each { |m| + include "qpid/framing/#{m.body_name}" + }} + namespace("qpid::framing") { + ops_visits_cpp + @amqp.classes.each { |c| + next if visit_methods(c).empty? + handler_visits_cpp(c) + } + } + } + end +end + +OperationsInvokerGen.new("client",ARGV[0], $amqp).generate() +OperationsInvokerGen.new("server",ARGV[0], $amqp).generate() diff --git a/qpid/cpp/rubygen/99-0/Proxy.rb b/qpid/cpp/rubygen/99-0/Proxy.rb new file mode 100755 index 0000000000..87d809d4ad --- /dev/null +++ b/qpid/cpp/rubygen/99-0/Proxy.rb @@ -0,0 +1,84 @@ +#!/usr/bin/env ruby +$: << ".." # Include .. in load path +require 'cppgen' + +class ProxyGen < CppGen + + def initialize(chassis, outdir, amqp) + super(outdir, amqp) + @chassis=chassis + @classname="AMQP_#{@chassis.caps}Proxy" + @filename="qpid/framing/#{@classname}" + end + + def proxy_member(c) c.name.lcaps+"Proxy"; end + + def inner_class_decl(c) + cname=c.name.caps + cpp_class(cname, "Proxy") { + gen <<EOS +public: +#{cname}(FrameHandler& f) : Proxy(f) {} +static #{cname}& get(#{@classname}& proxy) { return proxy.get#{cname}(); } +EOS + c.methods_on(@chassis).each { |m| + genl "virtual void #{m.cppname}(#{m.signature.join(",\n ")});" + genl + }} + end + + def inner_class_defn(c) + cname=c.cppname + c.methods_on(@chassis).each { |m| + genl "void #{@classname}::#{cname}::#{m.cppname}(#{m.signature.join(", ")})" + scope { + params=(["getVersion()"]+m.param_names).join(", ") + genl "send(#{m.body_name}(#{params}));" + }} + end + + def generate + # .h file + h_file(@filename) { + include "qpid/framing/Proxy.h" + include "qpid/framing/Array.h" + include "qpid/framing/amqp_types.h" + include "qpid/framing/amqp_structs.h" + namespace("qpid::framing") { + cpp_class(@classname, "public Proxy") { + public + genl "#{@classname}(FrameHandler& out);" + genl + @amqp.classes.each { |c| + inner_class_decl(c) + genl + genl "#{c.cppname}& get#{c.cppname}() { return #{proxy_member(c)}; }" + genl + } + private + @amqp.classes.each{ |c| gen c.cppname+" "+proxy_member(c)+";\n" } + }}} + + # .cpp file + cpp_file(@filename) { + include "<sstream>" + include "#{@classname}.h" + include "qpid/framing/amqp_types_full.h" + @amqp.methods_on(@chassis).each { + |m| include "qpid/framing/"+m.body_name + } + genl + namespace("qpid::framing") { + genl "#{@classname}::#{@classname}(FrameHandler& f) :" + gen " Proxy(f)" + @amqp.classes.each { |c| gen ",\n "+proxy_member(c)+"(f)" } + genl "{}\n" + @amqp.classes.each { |c| inner_class_defn(c) } + }} + end +end + + +ProxyGen.new("client", $outdir, $amqp).generate; +ProxyGen.new("server", $outdir, $amqp).generate; + diff --git a/qpid/cpp/rubygen/99-0/Session.rb b/qpid/cpp/rubygen/99-0/Session.rb new file mode 100644 index 0000000000..1ec78f6167 --- /dev/null +++ b/qpid/cpp/rubygen/99-0/Session.rb @@ -0,0 +1,195 @@ +#!/usr/bin/env ruby +# Usage: output_directory xml_spec_file [xml_spec_file...] +# +$: << '..' +require 'cppgen' + +class CppGen + def session_methods + excludes = ["channel", "connection", "session", "execution"] + gen_methods=@amqp.methods_on(@chassis).reject { |m| + excludes.include? m.parent.name or m.body_name.include?("010") + } + end + + def doxygen(m) + doxygen_comment { + genl m.doc + genl + m.fields_c.each { |f| + genl "@param #{f.cppname}" + genl f.doc if f.doc + genl + } + } + end +end + +class ContentField # For extra content parameters + def cppname() "content" end + def signature() "const MethodContent& content" end + def sig_default() signature+"="+"DefaultContent(std::string())" end + def unpack() "p[arg::content|DefaultContent(std::string())]"; end + def doc() "Message content"; end +end + +class AmqpField + def unpack() "p[arg::#{cppname}|#{cpptype.default_value}]"; end + def sig_default() signature+"="+cpptype.default_value; end +end + +class AmqpMethod + def fields_c() content ? fields+[ContentField.new] : fields end + def param_names_c() fields_c.map { |f| f.cppname} end + def signature_c() fields_c.map { |f| f.signature }; end + def sig_c_default() fields_c.map { |f| f.sig_default }; end + def argpack_name() "#{parent.cppname}#{name.caps}Parameters"; end + def argpack_type() + "boost::parameter::parameters<" + + fields_c.map { |f| "arg::keyword_tags::"+f.cppname }.join(',') + + ">" + end + def return_type() + return "TypedResult<qpid::framing::#{result.cpptype.ret}>" if (result) + return "Response" if (not responses().empty?) + return "Completion" + end + def session_function() "#{parent.name.lcaps}#{name.caps}"; end +end + +class SessionNoKeywordGen < CppGen + + def initialize(outdir, amqp) + super(outdir, amqp) + @chassis="server" + @namespace,@classname,@file= + parse_classname "qpid::client::no_keyword::Session_#{@amqp.version.bars}" + end + + def generate() + h_file(@file) { + include "qpid/client/SessionBase.h" + + namespace("qpid::client") { + genl "using std::string;" + genl "using framing::Content;" + genl "using framing::FieldTable;" + genl "using framing::MethodContent;" + genl "using framing::SequenceNumberSet;" + genl "using framing::Uuid;" + genl + namespace("no_keyword") { + doxygen_comment { + genl "AMQP #{@amqp.version} session API." + genl @amqp.class_("session").doc + } + cpp_class(@classname, "public SessionBase") { + public + genl "Session_#{@amqp.version.bars}() {}" + genl "Session_#{@amqp.version.bars}(shared_ptr<SessionCore> core) : SessionBase(core) {}" + session_methods.each { |m| + genl + doxygen(m) + args=m.sig_c_default.join(", ") + genl "#{m.return_type} #{m.session_function}(#{args});" + } + }}}} + + cpp_file(@file) { + include @classname + include "qpid/framing/all_method_bodies.h" + namespace(@namespace) { + genl "using namespace framing;" + session_methods.each { |m| + genl + sig=m.signature_c.join(", ") + func="#{@classname}::#{m.session_function}" + scope("#{m.return_type} #{func}(#{sig}) {") { + args=(["ProtocolVersion()"]+m.param_names).join(", ") + body="#{m.body_name}(#{args})" + sendargs=body + sendargs << ", content" if m.content + genl "return #{m.return_type}(impl->send(#{sendargs}), impl);" + }}}} + end +end + +class SessionGen < CppGen + + def initialize(outdir, amqp) + super(outdir, amqp) + @chassis="server" + session="Session_#{@amqp.version.bars}" + @base="no_keyword::#{session}" + @fqclass=FqClass.new "qpid::client::#{session}" + @classname=@fqclass.name + @fqbase=FqClass.new("qpid::client::#{@base}") + end + + def gen_keyword_decl(m, prefix) + return if m.fields_c.empty? # Inherited function will do. + scope("BOOST_PARAMETER_MEMFUN(#{m.return_type}, #{m.session_function}, 0, #{m.fields_c.size}, #{m.argpack_name}) {") { + scope("return #{prefix}#{m.session_function}(",");") { + gen m.fields_c.map { |f| f.unpack() }.join(",\n") + } + } + genl + end + + def generate() + keyword_methods=session_methods.reject { |m| m.fields_c.empty? } + max_arity = keyword_methods.map{ |m| m.fields_c.size }.max + + h_file(@fqclass.file) { + include @fqbase.file + genl + genl "#define BOOST_PARAMETER_MAX_ARITY #{max_arity}" + include "<boost/parameter.hpp>" + genl + namespace("qpid::client") { + # Generate keyword tag declarations. + namespace("arg") { + keyword_methods.map{ |m| m.param_names_c }.flatten.uniq.each { |k| + genl "BOOST_PARAMETER_KEYWORD(keyword_tags, #{k})" + }} + genl + # Doxygen comment. + doxygen_comment { + genl "AMQP #{@amqp.version} session API with keyword arguments." + genl <<EOS +This class provides the same set of functions as #{@base}, but also +allows parameters be passed using keywords. The keyword is the +parameter name in the namespace "arg". + +For example given the normal function "foo(int x=0, int y=0, int z=0)" +you could call it in either of the following ways: + +@code +session.foo(1,2,3); // Normal no keywords +session.foo(arg::z=3, arg::x=1); // Keywords and a default +@endcode + +The keyword functions are easy to use but their declarations are hard +to read. You may find it easier to read the documentation for #{@base} +which provides the same set of functions using normal non-keyword +declarations. + +\\ingroup clientapi +EOS + } + # Session class. + cpp_class(@classname,"public #{@base}") { + private + genl "#{@classname}(shared_ptr<SessionCore> core) : #{ @base}(core) {}" + keyword_methods.each { |m| typedef m.argpack_type, m.argpack_name } + genl "friend class Connection;" + public + genl "#{@classname}() {}" + keyword_methods.each { |m| gen_keyword_decl(m,@base+"::") } + }}} + end +end + +SessionNoKeywordGen.new(ARGV[0], $amqp).generate() +SessionGen.new(ARGV[0], $amqp).generate() + diff --git a/qpid/cpp/rubygen/99-0/all_method_bodies.rb b/qpid/cpp/rubygen/99-0/all_method_bodies.rb new file mode 100755 index 0000000000..5971d49189 --- /dev/null +++ b/qpid/cpp/rubygen/99-0/all_method_bodies.rb @@ -0,0 +1,21 @@ +#!/usr/bin/env ruby +$: << ".." # Include .. in load path +require 'cppgen' + +class AllMethodBodiesGen < CppGen + + def initialize(outdir, amqp) + super(outdir, amqp) + @namespace="qpid::framing" + @filename="qpid/framing/all_method_bodies" + end + + def generate() + h_file(@filename) { + @amqp.methods_.each { |m| include "qpid/framing/"+m.body_name } + } + end +end + +AllMethodBodiesGen.new($outdir, $amqp).generate(); + diff --git a/qpid/cpp/rubygen/99-0/constants.rb b/qpid/cpp/rubygen/99-0/constants.rb new file mode 100755 index 0000000000..b5f559d504 --- /dev/null +++ b/qpid/cpp/rubygen/99-0/constants.rb @@ -0,0 +1,82 @@ +#!/usr/bin/env ruby +$: << ".." # Include .. in load path +require 'cppgen' + +class ConstantsGen < CppGen + + def initialize(outdir, amqp) + super(outdir, amqp) + @namespace="qpid::framing" + @dir="qpid/framing" + end + + def constants_h() + h_file("#{@dir}/constants") { + namespace(@namespace) { + scope("enum AmqpConstant {","};") { + l=[] + l.concat @amqp.constants.map { |c| "#{c.name.shout}=#{c.value}" } + @amqp.classes.each { |c| + l << "#{c.name.shout}_CLASS_ID=#{c.index}" + l.concat c.methods_.map { |m| + "#{c.name.shout}_#{m.name.shout}_METHOD_ID=#{m.index}" } + } + genl l.join(",\n") + }}} + end + + def exbase(c) + case c.class_ + when "soft-error" then "ChannelException" + when "hard-error" then "ConnectionException" + end + end + + def reply_exceptions_h() + h_file("#{@dir}/reply_exceptions") { + include "qpid/Exception" + namespace(@namespace) { + @amqp.constants.each { |c| + base = exbase c + if base + genl + struct(c.name.caps+"Exception", base) { + genl "#{c.name.caps}Exception(const std::string& msg=std::string()) : #{base}(#{c.value}, \"#{c.name}: \"+msg) {}" + } + end + } + genl + genl "void throwReplyException(int code, const std::string& text);" + } + } + end + + def reply_exceptions_cpp() + cpp_file("#{@dir}/reply_exceptions") { + include "#{@dir}/reply_exceptions" + include "<sstream>" + namespace("qpid::framing") { + scope("void throwReplyException(int code, const std::string& text) {"){ + scope("switch (code) {") { + genl "case 200: break; // No exception" + @amqp.constants.each { |c| + if exbase c + genl "case #{c.value}: throw #{c.name.caps}Exception(text);" + end + } + scope("default:","") { + genl "std::ostringstream msg;" + genl 'msg << "Invalid reply code " << code << ": " << text;' + genl 'throw InvalidArgumentException(msg.str());' + }}}}} + end + + def generate() + constants_h + reply_exceptions_h + reply_exceptions_cpp + end +end + +ConstantsGen.new($outdir, $amqp).generate(); + diff --git a/qpid/cpp/rubygen/99-0/frame_body_lists.rb b/qpid/cpp/rubygen/99-0/frame_body_lists.rb new file mode 100644 index 0000000000..b20e4550f3 --- /dev/null +++ b/qpid/cpp/rubygen/99-0/frame_body_lists.rb @@ -0,0 +1,31 @@ +$: << ".." # Include .. in load path +require 'cppgen' + +class FrameBodyListsGen < CppGen + + def initialize(outdir, amqp) + super(outdir, amqp); + end + + def generate + h_file("qpid/framing/frame_body_lists.h") { + gen <<EOS +/**@file + * Macro lists of frame body classes, used to generate Visitors + */ +EOS + gen "#define METHOD_BODIES() " + @amqp.methods_.each { |m| gen "\\\n (#{m.body_name}) " } + gen <<EOS + + +#define OTHER_BODIES() (AMQContentBody)(AMQHeaderBody)(AMQHeartbeatBody)) + +EOS + } + end +end + +FrameBodyListsGen.new(ARGV[0], $amqp).generate; + + diff --git a/qpid/cpp/rubygen/99-0/structs.rb b/qpid/cpp/rubygen/99-0/structs.rb new file mode 100644 index 0000000000..58e175af0f --- /dev/null +++ b/qpid/cpp/rubygen/99-0/structs.rb @@ -0,0 +1,590 @@ +#!/usr/bin/env ruby +# Usage: output_directory xml_spec_file [xml_spec_file...] +# +$: << '..' +require 'cppgen' + +class StructGen < CppGen + + def initialize(outdir, amqp) + super(outdir, amqp) + end + + EncodingMap={ + "octet"=>"Octet", + "short"=>"Short", + "long"=>"Long", + "longlong"=>"LongLong", + "longstr"=>"LongString", + "shortstr"=>"ShortString", + "mediumstr"=>"MediumString", + "timestamp"=>"LongLong", + "table"=>"FieldTable", + "content"=>"Content", + "long-struct"=>"LongString" + } + SizeMap={ + "octet"=>1, + "short"=>2, + "long"=>4, + "longlong"=>8, + "timestamp"=>8 + } + + ValueTypes=["octet", "short", "long", "longlong", "timestamp"] + + def is_packed(s) + #return true + s.kind_of?(AmqpStruct) or s.body_name.include?("010") + end + + def execution_header?(s) + #false and s.kind_of? AmqpMethod and s.parent.l4? + s.kind_of? AmqpMethod and s.parent.name.include?("010") and not s.parent.control? + end + + def has_bitfields_only(s) + s.fields.select {|f| f.domain.type_ != "bit"}.empty? + end + + def default_initialisation(s) + params = s.fields.select {|f| ValueTypes.include?(f.domain.type_) || (!is_packed(s) && f.domain.type_ == "bit")} + strings = params.collect {|f| "#{f.cppname}(0)"} + strings << "flags(0)" if (is_packed(s)) + if strings.empty? + return "" + else + return " : " + strings.join(", ") + end + end + + def printable_form(f) + if (f.cpptype.name == "uint8_t") + return "(int) " + f.cppname + elsif (f.domain.type_ == "bit") + return "get#{f.name.caps}()" + else + return f.cppname + end + end + + def flag_mask(s, i) + pos = SizeMap[s.pack]*8 - 8 - (i/8)*8 + (i % 8) + return "(1 << #{pos})" + end + + def encode_packed_struct(s) + genl s.cpp_pack_type.encode('flags', 'buffer') + process_packed_fields(s) { |f, i| encode_packed_field(s, f, i) unless f.domain.type_ == "bit" } + end + + def decode_packed_struct(s) + genl "#{s.cpp_pack_type.decode('flags', 'buffer')}" + process_packed_fields(s) { |f, i| decode_packed_field(s, f, i) unless f.domain.type_ == "bit" } + end + + def size_packed_struct(s) + genl "total += #{SizeMap[s.pack]};" + process_packed_fields(s) { |f, i| size_packed_field(s, f, i) unless f.domain.type_ == "bit" } + end + + def print_packed_struct(s) + process_packed_fields(s) { |f, i| print_packed_field(s, f, i) } + end + + def encode_packed_field(s, f, i) + genl "if (flags & #{flag_mask(s, i)})" + indent { genl f.domain.cpptype.encode(f.cppname,"buffer") } + end + + def decode_packed_field(s, f, i) + genl "if (flags & #{flag_mask(s, i)})" + indent { genl f.domain.cpptype.decode(f.cppname,"buffer") } + end + + def size_packed_field(s, f, i) + genl "if (flags & #{flag_mask(s, i)})" + indent { generate_size(f, []) } + end + + def print_packed_field(s, f, i) + genl "if (flags & #{flag_mask(s, i)})" + indent { + genl "out << \"#{f.name}=\" << #{printable_form(f)} << \"; \";" + } + end + + def generate_encode(f, combined) + if (f.domain.type_ == "bit") + genl "uint8_t #{f.cppname}_bits = #{f.cppname};" + count = 0 + combined.each { |c| genl "#{f.cppname}_bits |= #{c.cppname} << #{count += 1};" } + genl "buffer.putOctet(#{f.cppname}_bits);" + else + genl f.domain.cpptype.encode(f.cppname,"buffer") + end + end + + def generate_decode(f, combined) + if (f.domain.type_ == "bit") + genl "uint8_t #{f.cppname}_bits = buffer.getOctet();" + genl "#{f.cppname} = 1 & #{f.cppname}_bits;" + count = 0 + combined.each { |c| genl "#{c.cppname} = (1 << #{count += 1}) & #{f.cppname}_bits;" } + else + genl f.domain.cpptype.decode(f.cppname,"buffer") + end + end + + def generate_size(f, combined) + if (f.domain.type_ == "bit") + names = ([f] + combined).collect {|g| g.cppname} + genl "total += 1;//#{names.join(", ")}" + else + size = SizeMap[f.domain.type_] + if (size) + genl "total += #{size};//#{f.cppname}" + elsif (f.cpptype.name == "SequenceNumberSet") + genl "total += #{f.cppname}.encodedSize();" + else + encoded = EncodingMap[f.domain.type_] + gen "total += (" + gen "4 + " if encoded == "LongString" + gen "2 + " if encoded == "MediumString" + gen "1 + " if encoded == "ShortString" + genl "#{f.cppname}.size());" + end + end + end + + def process_packed_fields(s) + s.fields.each { |f| yield f, s.fields.index(f) } + end + + def process_fields(s) + last = nil + count = 0 + bits = [] + s.fields.each { + |f| if (last and last.bit? and f.bit? and count < 7) + count += 1 + bits << f + else + if (last and last.bit?) + yield last, bits + count = 0 + bits = [] + end + if (not f.bit?) + yield f + end + last = f + end + } + if (last and last.bit?) + yield last, bits + end + end + + def all_fields_via_accessors(s) + s.fields.collect { |f| "get#{f.name.caps}()" }.join(", ") + end + + def methodbody_extra_defs(s) + if (s.parent.control?) + genl "virtual uint8_t type() const { return 0;/*control segment*/ }" + end + + + gen <<EOS + typedef #{s.result ? s.result.struct.cpptype.name : 'void'} ResultType; + + template <class T> ResultType invoke(T& invocable) const { + return invocable.#{s.cppname}(#{all_fields_via_accessors(s)}); + } + + using AMQMethodBody::accept; + void accept(MethodBodyConstVisitor& v) const { v.visit(*this); } + + ClassId amqpClassId() const { return CLASS_ID; } + MethodId amqpMethodId() const { return METHOD_ID; } + bool isContentBearing() const { return #{s.content ? "true" : "false" }; } + bool resultExpected() const { return #{s.result ? "true" : "false"}; } + bool responseExpected() const { return #{s.responses().empty? ? "false" : "true"}; } +EOS + end + + def define_constructor(name, s) + if (s.fields.size > 0) + genl "#{name}(" + if (s.kind_of? AmqpMethod) + indent {gen "ProtocolVersion, "} + end + indent { gen s.fields.collect { |f| "#{f.cpptype.param} _#{f.cppname}" }.join(",\n") } + genl ") : " + if (is_packed(s)) + initialisers = s.fields.select { |f| f.domain.type_ != "bit"}.collect { |f| "#{f.cppname}(_#{f.cppname})"} + + initialisers << "flags(0)" + indent { gen initialisers.join(",\n") } + genl "{" + indent { + process_packed_fields(s) { |f, i| genl "set#{f.name.caps}(_#{f.cppname});" if f.domain.type_ == "bit"} + process_packed_fields(s) { |f, i| genl "flags |= #{flag_mask(s, i)};" unless f.domain.type_ == "bit"} + } + genl "}" + else + indent { gen s.fields.collect { |f| " #{f.cppname}(_#{f.cppname})" }.join(",\n") } + genl "{}" + end + end + #default constructors: + if (s.kind_of? AmqpMethod) + genl "#{name}(ProtocolVersion=ProtocolVersion()) #{default_initialisation(s)} {}" + end + if (s.kind_of? AmqpStruct) + genl "#{name}() #{default_initialisation(s)} {}" + end + end + + def define_packed_field_accessors(s, f, i) + if (s.kind_of? AmqpMethod) + define_packed_field_accessors_for_method(s, f, i) + else + define_packed_field_accessors_for_struct(s, f, i) + end + end + + def define_packed_field_accessors_for_struct(s, f, i) + if (f.domain.type_ == "bit") + genl "void #{s.cppname}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {" + indent { + genl "if (_#{f.cppname}) flags |= #{flag_mask(s, i)};" + genl "else flags &= ~#{flag_mask(s, i)};" + } + genl "}" + genl "#{f.cpptype.ret} #{s.cppname}::get#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }" + else + genl "void #{s.cppname}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {" + indent { + genl "#{f.cppname} = _#{f.cppname};" + genl "flags |= #{flag_mask(s, i)};" + } + genl "}" + genl "#{f.cpptype.ret} #{s.cppname}::get#{f.name.caps}() const { return #{f.cppname}; }" + if (f.cpptype.name == "FieldTable") + genl "#{f.cpptype.name}& #{s.cppname}::get#{f.name.caps}() {" + indent { + genl "flags |= #{flag_mask(s, i)};"#treat the field table as having been 'set' + genl "return #{f.cppname};" + } + genl "}" + end + genl "bool #{s.cppname}::has#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }" + genl "void #{s.cppname}::clear#{f.name.caps}Flag() { flags &= ~#{flag_mask(s, i)}; }" + end + genl "" + end + + def define_packed_field_accessors_for_method(s, f, i) + if (f.domain.type_ == "bit") + genl "void #{s.body_name}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {" + indent { + genl "if (_#{f.cppname}) flags |= #{flag_mask(s, i)};" + genl "else flags &= ~#{flag_mask(s, i)};" + } + genl "}" + genl "#{f.cpptype.ret} #{s.body_name}::get#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }" + else + genl "void #{s.body_name}::set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) {" + indent { + genl "#{f.cppname} = _#{f.cppname};" + genl "flags |= #{flag_mask(s, i)};" + } + genl "}" + genl "#{f.cpptype.ret} #{s.body_name}::get#{f.name.caps}() const { return #{f.cppname}; }" + if (f.cpptype.name == "FieldTable") + genl "#{f.cpptype.name}& #{s.body_name}::get#{f.name.caps}() {" + indent { + genl "flags |= #{flag_mask(s, i)};"#treat the field table as having been 'set' + genl "return #{f.cppname};" + } + genl "}" + end + genl "bool #{s.body_name}::has#{f.name.caps}() const { return flags & #{flag_mask(s, i)}; }" + genl "void #{s.body_name}::clear#{f.name.caps}Flag() { flags &= ~#{flag_mask(s, i)}; }" + end + genl "" + end + + def define_packed_accessors(s) + process_packed_fields(s) { |f, i| define_packed_field_accessors(s, f, i) } + end + + def declare_packed_accessors(f) + genl "void set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname});"; + genl "#{f.cpptype.ret} get#{f.name.caps}() const;" + if (f.cpptype.name == "FieldTable") + genl "#{f.cpptype.name}& get#{f.name.caps}();" + end + if (f.domain.type_ != "bit") + #extra 'accessors' for packed fields: + genl "bool has#{f.name.caps}() const;" + genl "void clear#{f.name.caps}Flag();" + end + end + + def define_accessors(f) + genl "void set#{f.name.caps}(#{f.cpptype.param} _#{f.cppname}) { #{f.cppname} = _#{f.cppname}; }" + genl "#{f.cpptype.ret} get#{f.name.caps}() const { return #{f.cppname}; }" + if (f.cpptype.name == "FieldTable") + genl "#{f.cpptype.name}& get#{f.name.caps}() { return #{f.cppname}; }" + end + end + + def define_struct(s) + classname = s.cppname + inheritance = "" + if (s.kind_of? AmqpMethod) + classname = s.body_name + if (execution_header?(s)) + inheritance = ": public ModelMethod" + else + inheritance = ": public AMQMethodBody" + end + end + + h_file("qpid/framing/#{classname}.h") { + if (s.kind_of? AmqpMethod) + gen <<EOS +#include "qpid/framing/AMQMethodBody.h" +#include "qpid/framing/AMQP_ServerOperations.h" +#include "qpid/framing/MethodBodyConstVisitor.h" +EOS + end + include "qpid/framing/ModelMethod.h" if (execution_header?(s)) + + #need to include any nested struct definitions + s.fields.each { |f| include f.cpptype.name if f.domain.struct } + + gen <<EOS + +#include <ostream> +#include "qpid/framing/amqp_types_full.h" + +namespace qpid { +namespace framing { + +class #{classname} #{inheritance} { +EOS + if (is_packed(s)) + indent { s.fields.each { |f| genl "#{f.cpptype.name} #{f.cppname};" unless f.domain.type_ == "bit"} } + indent { + genl "#{s.cpp_pack_type.name} flags;" + } + else + indent { s.fields.each { |f| genl "#{f.cpptype.name} #{f.cppname};" } } + end + genl "public:" + if (s.kind_of? AmqpMethod) + indent { genl "static const ClassId CLASS_ID = #{s.parent.index};" } + indent { genl "static const MethodId METHOD_ID = #{s.index};" } + end + + if (s.kind_of? AmqpStruct) + if (s.type_) + if (s.result?) + #as result structs have types that are only unique to the + #class, they have a class dependent qualifier added to them + #(this is inline with current python code but a formal + #solution is expected from the WG) + indent { genl "static const uint16_t TYPE = #{s.type_} + #{s.parent.parent.parent.index} * 256;" } + else + indent { genl "static const uint16_t TYPE = #{s.type_};" } + end + end + end + + indent { + define_constructor(classname, s) + genl "" + if (is_packed(s)) + s.fields.each { |f| declare_packed_accessors(f) } + else + s.fields.each { |f| define_accessors(f) } + end + } + if (s.kind_of? AmqpMethod) + methodbody_extra_defs(s) + end + if (s.kind_of? AmqpStruct) + indent {genl "friend std::ostream& operator<<(std::ostream&, const #{classname}&);" } + end + + gen <<EOS + void encode(Buffer&) const; + void decode(Buffer&, uint32_t=0); + void encodeStructBody(Buffer&) const; + void decodeStructBody(Buffer&, uint32_t=0); + uint32_t size() const; + uint32_t bodySize() const; + void print(std::ostream& out) const; +}; /* class #{classname} */ + +}} +EOS + } + cpp_file("qpid/framing/#{classname}.cpp") { + if (is_packed(s) || s.fields.size > 0 || execution_header?(s)) + buffer = "buffer" + else + buffer = "/*buffer*/" + end + gen <<EOS +#include "#{classname}.h" + +using namespace qpid::framing; + +EOS + + if (is_packed(s)) + define_packed_accessors(s) + end + gen <<EOS +void #{classname}::encodeStructBody(Buffer& #{buffer}) const +{ +EOS + if (execution_header?(s)) + genl "encodeHeader(buffer);" + end + + if (is_packed(s)) + indent {encode_packed_struct(s)} + else + indent { process_fields(s) { |f, combined| generate_encode(f, combined) } } + end + gen <<EOS +} + +void #{classname}::encode(Buffer& buffer) const +{ +EOS + indent { + if (s.kind_of? AmqpStruct) + if (s.type_) + genl "buffer.put#{s.size.caps}(bodySize() + 2/*typecode*/);" if s.size + genl "buffer.putShort(TYPE);" + else + genl "buffer.put#{s.size.caps}(bodySize());" if s.size + end + end + genl "encodeStructBody(buffer);" + } + gen <<EOS +} + +void #{classname}::decodeStructBody(Buffer& #{buffer}, uint32_t /*size*/) +{ +EOS + if (execution_header?(s)) + genl "decodeHeader(buffer);" + end + + if (is_packed(s)) + indent {decode_packed_struct(s)} + else + indent { process_fields(s) { |f, combined| generate_decode(f, combined) } } + end + gen <<EOS +} + +void #{classname}::decode(Buffer& buffer, uint32_t /*size*/) +{ +EOS + indent { + if (s.kind_of? AmqpStruct) + genl "buffer.get#{s.size.caps}();" if s.size + genl "if (TYPE != buffer.getShort()) throw InternalErrorException(\"Bad type code for struct\");" if s.type_ + end + genl "decodeStructBody(buffer);" + } + gen <<EOS +} + +uint32_t #{classname}::bodySize() const +{ + uint32_t total = 0; +EOS + if (execution_header?(s)) + genl "total += headerSize();" + end + + if (is_packed(s)) + indent {size_packed_struct(s)} + else + indent { process_fields(s) { |f, combined| generate_size(f, combined) } } + end + gen <<EOS + return total; +} + +uint32_t #{classname}::size() const +{ + uint32_t total = bodySize(); +EOS + if (s.kind_of? AmqpStruct) + genl "total += #{SizeMap[s.size]}/*size field*/;" if s.size + genl "total += 2/*typecode*/;" if s.type_ + end + gen <<EOS + return total; +} + +void #{classname}::print(std::ostream& out) const +{ + out << "{#{classname}: "; +EOS + if (is_packed(s)) + indent {print_packed_struct(s)} + else + copy = Array.new(s.fields) + f = copy.shift + + indent { + genl "out << \"#{f.name}=\" << #{printable_form(f)};" if f + copy.each { |f| genl "out << \"; #{f.name}=\" << #{printable_form(f)};" } + } + end + gen <<EOS + out << "}"; +} +EOS + + if (s.kind_of? AmqpStruct) + gen <<EOS +namespace qpid{ +namespace framing{ + + std::ostream& operator<<(std::ostream& out, const #{classname}& s) + { + s.print(out); + return out; + } + +} +} +EOS + end +} + end + + def generate() + @amqp.structs.each { |s| define_struct(s) } + @amqp.methods_.each { |m| define_struct(m) } + #generate a single include file containing the list of structs for convenience + h_file("qpid/framing/amqp_structs.h") { @amqp.structs.each { |s| genl "#include \"#{s.cppname}.h\"" } } + end +end + +StructGen.new(ARGV[0], $amqp).generate() + diff --git a/qpid/cpp/rubygen/MethodBodyDefaultVisitor.rb b/qpid/cpp/rubygen/MethodBodyDefaultVisitor.rb new file mode 100755 index 0000000000..1fff1d51db --- /dev/null +++ b/qpid/cpp/rubygen/MethodBodyDefaultVisitor.rb @@ -0,0 +1,34 @@ +#!/usr/bin/env ruby +$: << ".." # Include .. in load path +require 'cppgen' + +class MethodBodyDefaultVisitorGen < CppGen + + def initialize(outdir, amqp) + super(outdir, amqp) + set_classname("qpid::framing::MethodBodyDefaultVisitor") + end + + def generate() + h_file(@filename) { + include "qpid/framing/MethodBodyConstVisitor" + namespace(@namespace) { + genl + cpp_class(@classname, "public MethodBodyConstVisitor") { + genl "public:" + genl "virtual void defaultVisit() = 0;" + @amqp.methods_.each { |m| + genl "virtual void visit(const #{m.body_name}&);" } + }}} + + cpp_file(@filename) { + include(@filename) + namespace(@namespace) { + @amqp.methods_.each { |m| + genl "void #{@classname}::visit(const #{m.body_name}&) { defaultVisit(); }" + }}} + end +end + +MethodBodyDefaultVisitorGen.new($outdir, $amqp).generate(); + diff --git a/qpid/cpp/rubygen/README b/qpid/cpp/rubygen/README new file mode 100644 index 0000000000..a1fd6cfec8 --- /dev/null +++ b/qpid/cpp/rubygen/README @@ -0,0 +1,17 @@ +RUBY CODE GENERATOR + +Run ./generate for usage. +Examples in samples/ + +For example: + ./generate . ../../specs/amqp.0-9.xml samples/Proxy.rb +will generate + + + + + + + + + diff --git a/qpid/cpp/rubygen/amqpgen.rb b/qpid/cpp/rubygen/amqpgen.rb new file mode 100755 index 0000000000..bf473506d4 --- /dev/null +++ b/qpid/cpp/rubygen/amqpgen.rb @@ -0,0 +1,501 @@ +# +# Generic AMQP code generation library. +# + +# TODO aconway 2008-02-21: +# +# The amqp_attr_reader and amqp_child_reader for each Amqp* class +# should correspond exactly to ampq.dtd. Currently they are more +# permissive so we can parse 0-10 preview and 0-10 final XML. +# +# Code marked with "# preview" should be removed/modified when final 0-10 +# is complete and we are ready to remove preview-related code. +# + +require 'delegate' +require 'rexml/document' +require 'pathname' +include REXML + +# Handy String functions for converting names. +class String + # Convert to CapitalizedForm. + def caps() gsub( /(^|\W)(\w)/ ) { |m| $2.upcase } end + + # Convert to underbar_separated_form. + def bars() tr('- .','_'); end + + # Convert to ALL_UPPERCASE_FORM + def shout() bars.upcase!; end + + # Convert to lowerCaseCapitalizedForm + def lcaps() gsub( /\W(\w)/ ) { |m| $1.upcase } end + + def plural() self + (/[xs]$/ === self ? 'es' : 's'); end +end + +# Sort an array by name. +module Enumerable + def sort_by_name() sort { |a,b| a.name <=> b.name }; end +end + +# Add functions similar to attr_reader for AMQP attributes/children. +# Symbols that are ruby Object function names (e.g. class) get +# an "_" suffix. +class Module + # Add trailing _ to avoid conflict with Object methods. + def mangle(sym) + (Object.method_defined? sym) ? (sym.to_s+"_").intern : sym + end + + # Add attribute reader for XML attribute. + def amqp_attr_reader(*attrs) + attrs.each { |a| + case a + when Symbol + define_method(mangle(a)) { + @amqp_attr_reader||={ } + @amqp_attr_reader[a] ||= xml.attributes[a.to_s] + } + when Hash + a.each { |attr, default| + define_method(mangle(attr)) { + @amqp_attr_reader||={ } + value = xml.attributes[attr.to_s] + if value + @amqp_attr_reader[attr] ||= value + else + @amqp_attr_reader[attr] ||= default + end + } + } + end + } + end + + # Add 2 child readers: + # elname(name) == child('elname',name) + # elnames() == children('elname') + def amqp_child_reader(*element_names) + element_names.each { |e| + define_method(mangle(e)) { |name| child(e.to_s, name) } + define_method(mangle(e.to_s.plural)) { children(e.to_s) } } + end + + # When there can only be one child instance + def amqp_single_child_reader(*element_names) + element_names.each { |e| + define_method(mangle(e)) { children(e.to_s)[0] } } + end +end + + +# An AmqpElement contains an XML element and provides a convenient +# API to access AMQP data. +# +# NB: AmqpElements cache values from XML, they assume that +# the XML model does not change after the AmqpElement has +# been created. +class AmqpElement + + def wrap(xml) + return nil if ["assert","rule"].include? xml.name + eval("Amqp"+xml.name.caps).new(xml, self) or raise "nil wrapper" + end + + public + + def initialize(xml, parent) + @xml, @parent=xml, parent + @children=xml.elements.map { |e| wrap e }.compact + @cache_child={} + @cache_children={} + @cache_children[nil]=@children + end + + attr_reader :parent, :xml, :children, :doc + amqp_attr_reader :name, :label + + # List of children of type elname, or all children if elname + # not specified. + def children(elname=nil) + if elname + @cache_children[elname] ||= @children.select { |c| elname==c.xml.name } + else + @children + end + end + + def each_descendant(&block) + yield self + @children.each { |c| c.each_descendant(&block) } + end + + def collect_all(amqp_type) + collect=[] + each_descendant { |d| collect << d if d.is_a? amqp_type } + collect + end + + # Look up child of type elname with attribute name. + def child(elname, name) + @cache_child[[elname,name]] ||= children(elname).find { |c| c.name==name } + end + + # The root <amqp> element. + def root() @root ||=parent ? parent.root : self; end + + # Are we in preview or final 0-10 + # preview - used to make some classes behave differently for preview vs. final + def final?() root().version == "0-10"; end + + def to_s() "#<#{self.class}(#{name})>"; end + def inspect() to_s; end + + # Text of doc child if there is one. + def doc() d=xml.elements["doc"]; d and d.text; end + + def fqname() + throw "fqname: #{self} #{parent.fqname} has no name" unless name + p=parent && parent.fqname + p ? p+"."+name : name; + end + + def containing_class() + return self if is_a? AmqpClass + return parent && parent.containing_class + end + +end + +class AmqpResponse < AmqpElement + def initialize(xml, parent) super; end + def fqname() (parent ? parent.dotted_name+"." : "") + "response"; end +end + +class AmqpDoc < AmqpElement + def initialize(xml,parent) super; end + def text() @xml.text end +end + +class AmqpChoice < AmqpElement + def initialize(xml,parent) super; end + amqp_attr_reader :name, :value +end + +class AmqpEnum < AmqpElement + def initialize(xml,parent) super; end + amqp_child_reader :choice +end + +# 0-10 array domains are missing element type information, add it here. +ArrayTypes={ + "str16-array" => "str-16", + "amqp-host-array" => "connection.amqp-host-url", + "command-fragments" => "session.command-fragment", + "in-doubt" => "dtx.xid" +} + +class AmqpDomain < AmqpElement + def initialize(xml, parent) + super + root.used_by[uses].push(fqname) if uses and uses.index('.') + end + + amqp_attr_reader :type + amqp_single_child_reader :struct # preview + amqp_single_child_reader :enum + + def uses() type_=="array" ? ArrayTypes[name] : type_; end + + def unalias() + d=self + while (d.type_ != d.name and root.domain(d.type_)) + d=root.domain(d.type_) + end + return d + end +end + +class AmqpException < AmqpElement + def initialize(xml, amqp) super; end; + amqp_attr_reader :error_code +end + +class AmqpField < AmqpElement + def initialize(xml, amqp) + super; + root.used_by[type_].push(parent.fqname) if type_ and type_.index('.') + end + + def domain() root.domain(xml.attributes["domain"]); end + amqp_single_child_reader :struct # preview + amqp_child_reader :exception + amqp_attr_reader :type, :default, :code, :required +end + +class AmqpChassis < AmqpElement # preview + def initialize(xml, parent) super; end + amqp_attr_reader :implement +end + +class AmqpConstant < AmqpElement + def initialize(xml, parent) super; end + amqp_attr_reader :value, :class +end + +class AmqpResult < AmqpElement + def initialize(xml, parent) super; end + amqp_single_child_reader :struct # preview + amqp_attr_reader :type + def name() "result"; end +end + +class AmqpEntry < AmqpElement + def initialize(xml,parent) super; end + amqp_attr_reader :type +end + +class AmqpHeader < AmqpElement + def initialize(xml,parent) super; end + amqp_child_reader :entry + amqp_attr_reader :required +end + +class AmqpBody < AmqpElement + def initialize(xml,parent) super; end + amqp_attr_reader :required +end + +class AmqpSegments < AmqpElement + def initialize(xml,parent) super; end + amqp_child_reader :header, :body +end + +class AmqpStruct < AmqpElement + def initialize(xml, parent) super; end + amqp_attr_reader :type # preview + amqp_attr_reader :size, :code, :pack + amqp_child_reader :field + + # preview - preview code needs default "short" for pack. + alias :raw_pack :pack + def pack() raw_pack or (not parent.final? and "short"); end + def result?() parent.xml.name == "result"; end + def domain?() parent.xml.name == "domain"; end +end + +class AmqpMethod < AmqpElement + def initialize(xml, parent) super; end + + amqp_attr_reader :content, :index, :synchronous + amqp_child_reader :field, :chassis,:response + amqp_single_child_reader :result + + def on_chassis?(chassis) child("chassis", chassis); end + def on_client?() on_chassis? "client"; end + def on_server?() on_chassis? "server"; end +end + +class AmqpImplement < AmqpElement + def initialize(xml,amqp) super; end + amqp_attr_reader :handle, :send +end + +class AmqpRole < AmqpElement + def initialize(xml,amqp) super; end + amqp_attr_reader :implement +end + +# Base class for command and control. +class AmqpAction < AmqpElement + def initialize(xml,amqp) super; end + amqp_child_reader :implement, :field, :response + amqp_attr_reader :code + def size() "4" end # Encoded as a size 4 Struct +end + +class AmqpControl < AmqpAction + def initialize(xml,amqp) super; end +end + +class AmqpCommand < AmqpAction + def initialize(xml,amqp) super; end + amqp_child_reader :exception + amqp_single_child_reader :result, :segments +end + +class AmqpClass < AmqpElement + def initialize(xml,amqp) super; end + + amqp_attr_reader :index # preview + amqp_child_reader :method # preview + + amqp_child_reader :struct, :domain, :control, :command, :role + amqp_attr_reader :code + + # chassis should be "client" or "server" + def methods_on(chassis) # preview + @methods_on ||= { } + @methods_on[chassis] ||= methods_.select { |m| m.on_chassis? chassis } + end + + def l4?() # preview + !["connection", "session", "execution"].include?(name) + end + + def control?() + ["connection010", "session010"].include?(name) + end + + def actions() controls+commands; end +end + +class AmqpType < AmqpElement + def initialize(xml,amqp) super; end + amqp_attr_reader :code, :fixed_width, :variable_width +end + +class AmqpXref < AmqpElement + def initialize(xml,amqp) super; end +end + +# AMQP root element. +class AmqpRoot < AmqpElement + amqp_attr_reader :major, :minor, :port, :comment + amqp_child_reader :doc, :type, :struct, :domain, :constant, :class + + def get_root(x) + case x + when Element then x + when Document then x.root + else Document.new(x).root + end + end + + # Initialize with output directory and spec files from ARGV. + def initialize(*specs) + raise "No XML spec files." if specs.empty? + xml=get_root(specs.shift) + specs.each { |s| xml_merge(xml, get_root(s)) } + @used_by=Hash.new{ |h,k| h[k]=[] } + super(xml, nil) + end + + attr_reader :used_by + + def merge(root) xml_merge(xml, root.xml); end + + def version() major + "-" + minor; end + + # preview - only struct child reader remains for new mapping + def domain_structs() domains.map{ |d| d.struct }.compact; end + def result_structs() + methods_.map { |m| m.result and m.result.struct }.compact + end + def structs() result_structs+domain_structs; end + + def methods_() classes.map { |c| c.methods_ }.flatten; end + + #preview + # Return all methods on chassis for all classes. + def methods_on(chassis) + @methods_on ||= { } + @methods_on[chassis] ||= classes.map { |c| c.methods_on(chassis) }.flatten + end + + def fqname() nil; end + + private + + # Merge contents of elements. + def xml_merge(to,from) + from.elements.each { |from_child| + tag,name = from_child.name, from_child.attributes["name"] + to_child=to.elements["./#{tag}[@name='#{name}']"] + to_child ? xml_merge(to_child, from_child) : to.add(from_child.deep_clone) } + end +end + +# Collect information about generated files. +class GenFiles + @@files =[] + def GenFiles.add(f) @@files << f; end + def GenFiles.get() @@files; end +end + +# Base class for code generators. +# Supports setting a per-line prefix, useful for e.g. indenting code. +# +class Generator + # Takes directory for output or "-", meaning print file names that + # would be generated. + def initialize (outdir, amqp) + @amqp=amqp + @outdir=outdir + @prefix=[''] # For indentation or comments. + @indentstr=' ' # One indent level. + @outdent=2 + Pathname.new(@outdir).mkpath unless @outdir=="-" + end + + # Create a new file, set @out. + def file(file, &block) + GenFiles.add file + if (@outdir != "-") + @path=Pathname.new "#{@outdir}/#{file}" + @path.parent.mkpath + @out=String.new # Generate in memory first + if block then yield; endfile; end + end + end + + def endfile() + if @outdir != "-" + if @path.exist? and @path.read == @out + puts "Skipped #{@path} - unchanged" # Dont generate if unchanged + else + @path.open('w') { |f| f << @out } + puts "Generated #{@path}" + end + end + end + + # Append multi-line string to generated code, prefixing each line. + def gen(str) + str.each_line { |line| + @out << @prefix.last unless @midline + @out << line + @midline = nil + } + # Note if we stopped mid-line + @midline = /[^\n]\z/ === str + end + + # Append str + '\n' to generated code. + def genl(str="") gen str+"\n"; end + + # Generate code with added prefix. + def prefix(add, &block) + @prefix.push @prefix.last+add + if block then yield; endprefix; end + end + + def endprefix() + @prefix.pop + end + + # Generate indented code + def indent(n=1,&block) prefix(@indentstr * n,&block); end + alias :endindent :endprefix + + # Generate outdented code + def outdent(&block) + @prefix.push @prefix.last[0...-2] + if block then yield; endprefix; end + end + alias :endoutdent :endprefix + + attr_accessor :out +end + diff --git a/qpid/cpp/rubygen/cppgen.rb b/qpid/cpp/rubygen/cppgen.rb new file mode 100755 index 0000000000..757894163d --- /dev/null +++ b/qpid/cpp/rubygen/cppgen.rb @@ -0,0 +1,390 @@ +#!/usr/bin/ruby +# +# General purpose C++ code generation. +# +require 'amqpgen' +require 'set' + +Copyright=<<EOS +/* + * + * 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 was automatically generated from the AMQP specification. +/// Do not edit. +/// + + +EOS + +CppKeywords = Set.new(["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"]) +# Names that need a trailing "_" to avoid clashes. +CppMangle = CppKeywords+Set.new(["string"]) + +class String + def cppsafe() CppMangle.include?(self) ? self+"_" : self; end + + def amqp2cpp() + path=split(".") + name=path.pop + return name.typename if path.empty? + path.map! { |n| n.nsname } + return (path << name.caps.cppsafe).join("::") + end + + def typename() caps.cppsafe; end + def nsname() bars.cppsafe; end + def constname() shout.cppsafe; end + def funcname() lcaps.cppsafe; end + def varname() lcaps.cppsafe; end +end + +# Hold information about a C++ type. +# +# preview - new mapping does not use CppType, +# Each amqp type corresponds exactly by dotted name +# to a type, domain or struct, which in turns +# corresponds by name to a C++ type or typedef. +# (see String.amqp2cpp) +# +class CppType + def initialize(name) @name=@param=@ret=name; end + attr_reader :name, :param, :ret, :code + + def retref() @ret="#{name}&"; self; end + def retcref() @ret="const #{name}&"; self; end + def passcref() @param="const #{name}&"; self; end + def code(str) @code=str; self; end + def defval(str) @defval=str; self; end + + def encode(value, buffer) + @code ? "#{buffer}.put#{@code}(#{value});" : "#{value}.encode(#{buffer});" + end + + def decode(value,buffer) + if @code + if /&$/===param then + "#{buffer}.get#{@code}(#{value});" + else + "#{value} = #{buffer}.get#{@code}();" + end + else + "#{value}.decode(#{buffer});" + end + end + + def default_value() + return @defval ||= "#{name}()" + end + + def to_s() name; end; +end + +class AmqpElement + # convert my amqp type_ attribute to a C++ type. + def amqp2cpp() + return "ArrayDomain<#{ArrayTypes[name].amqp2cpp}> " if type_=="array" + return type_.amqp2cpp + end +end + +class AmqpField + def cppname() name.lcaps.cppsafe; end + def cpptype() domain.cpptype; end + def bit?() domain.type_ == "bit"; end + def signature() cpptype.param+" "+cppname; end + def fqtypename() + unless type_.index(".") + c=containing_class + return c.domain(type_).fqtypename if c.domain(type_) + return c.struct(type_).fqclassname if c.struct(type_) + end + return amqp2cpp + end + def paramtype() + /^(int|uint|char|boolean|bit)/ === type_ ? fqtypename : "const #{fqtypename}&" + end + def param_default() "=#{fqtypename}()" end +end + +class AmqpMethod + def cppname() name.lcaps.cppsafe; end + def param_names() fields.map { |f| f.cppname }; end + def signature() fields.map { |f| f.signature }; end + def body_name() parent.name.caps+name.caps+"Body"; end + + def cpp_pack_type() # preview + CppType.new("uint16_t").code("Short").defval("0"); + end + def pack() # preview + "short" + end +end + +module AmqpHasFields + def parameters(with_default=nil) + fields.map { |f| + "#{f.paramtype} #{f.cppname}_#{f.param_default if with_default}" + } + end + def unused_parameters() fields.map { |f| "#{f.paramtype} /*#{f.cppname}_*/"} end + def arguments() fields.map { |f| "#{f.cppname}_"} end + def values() fields.map { |f| "#{f.cppname}"} end + def initializers() fields.map { |f| "#{f.cppname}(#{f.cppname}_)"} end +end + +class AmqpAction + def classname() name.typename; end + def funcname() parent.name.funcname + name.caps; end + def fqclassname() parent.name+"::"+classname; end + def full_code() (containing_class.code.hex << 8)+code.hex; end + include AmqpHasFields +end + +class AmqpType + def typename() name.typename; end + def fixed?() fixed_width; end +end + +class AmqpCommand < AmqpAction + def base() "Command"; end +end + +class AmqpControl < AmqpAction + def base() "Control"; end +end + +class AmqpClass + def cppname() name.caps; end # preview + def nsname() name.nsname; end +end + +class AmqpDomain + @@typemap = { + "bit"=> CppType.new("bool").code("Octet").defval("false"), + "octet"=>CppType.new("uint8_t").code("Octet").defval("0"), + "short"=>CppType.new("uint16_t").code("Short").defval("0"), + "long"=>CppType.new("uint32_t").code("Long").defval("0"), + "longlong"=>CppType.new("uint64_t").code("LongLong").defval("0"), + "timestamp"=>CppType.new("uint64_t").code("LongLong").defval("0"), + "longstr"=>CppType.new("string").passcref.retcref.code("LongString"), + "shortstr"=>CppType.new("string").passcref.retcref.code("ShortString"), + "mediumstr"=>CppType.new("string").passcref.retcref.code("MediumString"), + "table"=>CppType.new("FieldTable").passcref.retcref, + "array"=>CppType.new("Array").passcref.retcref, + "content"=>CppType.new("Content").passcref.retcref, + "rfc1982-long-set"=>CppType.new("SequenceNumberSet").passcref.retcref, + "sequence-set"=>CppType.new("SequenceSet").passcref.retcref, + "long-struct"=>CppType.new("string").passcref.retcref.code("LongString"), + "uuid"=>CppType.new("Uuid").passcref.retcref + } + + def cppname() name.caps; end + + def fqtypename() + return containing_class.nsname+"::"+name.typename if containing_class + name.typename + end + + def cpptype() + d=unalias + @cpptype ||= @@typemap[d.type_] or + CppType.new(d.cppname).passcref.retcref or + raise "Invalid type #{self}" + end + + def AmqpDomain.lookup_type(t) + @@typemap[t] + end +end + +class AmqpResult + def cpptype() + @cpptype=CppType.new(parent.parent.name.caps+parent.name.caps+"Result").passcref + end +end + +class AmqpStruct + include AmqpHasFields + + def cpp_pack_type() # preview + AmqpDomain.lookup_type(pack()) or CppType.new("uint16_t"); + end + def cpptype() parent.cpptype; end # preview + def cppname() cpptype.name; end # preview + def fqclassname() containing_class.nsname+"::"+name.typename; end + def classname() name.typename; end + def full_code() (containing_class.code.hex << 8)+code.hex; end +end + +class CppGen < Generator + def initialize(outdir, *specs) + super(outdir,*specs) + # need to sort classes for dependencies + @actions=[] # Stack of end-scope actions + end + + # Write a header file. + def h_file(path, &block) + path = (/\.h$/ === path ? path : path+".h") + guard=path.upcase.tr('./-','_') + file(path) { + gen "#ifndef #{guard}\n" + gen "#define #{guard}\n" + gen Copyright + yield + gen "#endif /*!#{guard}*/\n" + } + end + + # Write a .cpp file. + def cpp_file(path, &block) + path = (/\.cpp$/ === path ? path : path+".cpp") + file(path) do + gen Copyright + yield + end + end + + def include(header) + header+=".h" unless /(\.h|[">])$/===header + header="\"#{header}\"" unless /(^<.*>$)|(^".*"$)/===header + genl "#include #{header}" + end + + def scope(open="{",close="}", &block) + genl open + indent &block + genl close + end + + def namespace(name, &block) + genl + names = name.split("::") + names.each { |n| genl "namespace #{n} {" } + genl "namespace {" if (names.empty?) + genl + yield + genl + genl('}'*([names.size, 1].max)+" // namespace "+name) + genl + end + + def struct_class(type, name, bases, &block) + gen "#{type} #{name}" + if (!bases.empty?) + genl ":" + indent { gen "#{bases.join(",\n")}" } + end + genl + scope("{","};", &block) + end + + def struct(name, *bases, &block) + struct_class("struct", name, bases, &block); + end + def cpp_class(name, *bases, &block) + struct_class("class", name, bases, &block); + end + + def typedef(type, name) genl "typedef #{type} #{name};\n"; end + + def variant(types) "boost::variant<#{types.join(", ")}>"; end + def variantl(types) "boost::variant<#{types.join(", \n")}>"; end + def blank_variant(types) variant(["boost::blank"]+types); end + def tuple(types) "boost::tuple<#{types.join(', ')}>"; end + + def public() outdent { genl "public:" } end + def private() outdent { genl "private:" } end + def protected() outdent { genl "protected:" } end + + # Returns [namespace, classname, filename] + def parse_classname(full_cname) + names=full_cname.split("::") + return names[0..-2].join('::'), names[-1], names.join("/") + end + + def doxygen_comment(&block) + genl "/**" + prefix(" * ",&block) + genl " */" + end + + # Generate code in namespace for each class + def each_class_ns() + @amqp.classes.each { |c| namespace(c.nsname) { yield c } } + end + + def signature(ret_name, params, trailer="") + if params.size <= 1 + genl ret_name+"(#{params})"+trailer + else + scope(ret_name+"(",")"+trailer) { genl params.join(",\n") } + end + end + + def function_decl(ret_name, params=[], trailer="") + signature(ret_name, params, trailer+";") + end + + def function_defn(ret_name, params=[], trailer="") + genl + signature(ret_name, params, trailer) + scope() { yield } + end + + def ctor_decl(name, params=[]) function_decl(name, params); end + + def ctor_defn(name, params=[], inits=[]) + signature(name, params, inits.empty? ? "" : " :") + indent { gen inits.join(",\n") } if not inits.empty? + scope() { yield } + end + + def function_call(name, params=[], trailer="") + gen name + list "(",params, ")" + gen trailer + end +end + +# Fully-qualified class name +class FqClass < Struct.new(:fqname,:namespace,:name,:file) + def initialize(fqclass) + names=fqclass.split "::" + super(fqclass, names[0..-2].join('::'), names[-1], names.join("/")) + end +end + diff --git a/qpid/cpp/rubygen/generate b/qpid/cpp/rubygen/generate new file mode 100755 index 0000000000..9f0ddf0f1c --- /dev/null +++ b/qpid/cpp/rubygen/generate @@ -0,0 +1,103 @@ +#!/usr/bin/env ruby +require 'pathname' +require 'amqpgen' + +# +# Run a set of code generation templates. +# +if ARGV.size < 3 + puts <<EOS +Usage: #{ARGV[0]} OUTDIR SPEC.xml [ ... ] TEMPLATE.rb [ ... ] +or: #{ARGV[0]} OUTDIR SPEC.xml [ ... ] all [ makefragment.mk ] + +Parse all SPEC.xml files to create an AMQP model, run each TEMPLATE +putting the resulting files under OUTDIR. Prints a list of files +generated to standard output. + +If OUTDIR is '-' then just prints file list without generating files. +EOS + exit 1 +end + +# Create array of specs by version +def parse_specs(files) + lists=Hash.new { |h,k| h[k]=[] } + files.each { |f| + spec=AmqpRoot.new(File.new(f)) + lists[spec.version] << spec + } + specs={} + lists.each_pair { |k,l| + specs[k] = l.size==1 ? l.first : AmqpRoot.new(*l.map { |s| s.xml}) + } + return specs +end + +# Run selected templates +if ARGV.any? { |arg| arg=="all" } + templates=Dir["#{File.dirname __FILE__}/*/*.rb"] +else + templates=ARGV.grep(/\.rb$/) +end + +$outdir=ARGV[0] +$models=parse_specs(ARGV.grep(/\.xml$/)) +templates.each { |t| + ver=Pathname.new(t).dirname.basename.to_s + $amqp=$models[ver] + if $amqp + load t + else + puts "Warning: skipping #{t}, no spec file for version #{ver}." + end +} + +def make_continue(lines) lines.join(" \\\n "); end + +# Generate makefile +makefile=ARGV.grep(/.mk$/)[0] +if makefile + dir=Dir.getwd + Dir.chdir File.dirname(__FILE__) + generator_files=Dir["**/*.rb"] << File.basename(__FILE__) + Dir.chdir dir + rgen_generator=generator_files.map{ |f| "$(rgen_dir)/#{f}" } + rgen_srcs=GenFiles.get.map{ |f| "#{$outdir}/#{f}" } + + File.open(makefile, 'w') { |out| + out << <<EOS +# Generated makefile fragment. +# Including makefile defines $(rgen_dir) $(rgen_cmd) and $(specs). + +rgen_generator=#{make_continue rgen_generator} + +rgen_client_cpp=#{make_continue(rgen_srcs.grep(%r|/qpid/client/.+\.cpp$|))} + +rgen_common_cpp=#{make_continue(rgen_srcs.grep(%r{qpid/(framing|amqp_.+)/.+\.cpp$}))} + +rgen_srcs=#{make_continue rgen_srcs} + +# Header file install rules. +EOS + ["amqp_0_10", "framing", "client/no_keyword","client", "broker"].each { |ns| + dir="qpid/#{ns}" + dir_ = dir.tr("/", "_") + regex=%r|#{dir}/[^/]+\.h$| + out << <<EOS +#{dir_}dir = $(includedir)/#{dir} +dist_#{dir_}_HEADERS = #{make_continue rgen_srcs.grep(regex)} + +EOS + } + out << <<EOS +if GENERATE +$(srcdir)/#{File.basename makefile}: $(rgen_generator) $(specs) + $(rgen_cmd) + +# Empty rule in case a generator file is renamed/removed. +$(rgen_generator): +endif +EOS + } +end + diff --git a/qpid/cpp/src/Makefile.am b/qpid/cpp/src/Makefile.am new file mode 100644 index 0000000000..a31dbdfa9d --- /dev/null +++ b/qpid/cpp/src/Makefile.am @@ -0,0 +1,502 @@ +SUBDIRS = . tests + +EXTRA_DIST= $(platform_dist) $(rgen_srcs) + +## Generated code + +# Note: generated soure and makefiles included in distribution so a +# distribution can be built without code generation tools and XML +# sources. + +# This phony target is needed by generated makefile fragments: +force: + +if GENERATE + +# AMQP_PREVIEW_XML and AMQP_FINAL_XML are defined in ../configure.ac +amqp_99_0_xml=@AMQP_PREVIEW_XML@ $(top_srcdir)/xml/extra.xml $(top_srcdir)/xml/cluster.xml +amqp_0_10_xml=@AMQP_FINAL_XML@ +specs=$(amqp_99_0_xml) $(amqp_0_10_xml) + +# Ruby generator. +rgen_dir=$(top_srcdir)/rubygen +rgen_cmd=ruby -I $(rgen_dir) $(rgen_dir)/generate $(srcdir)/gen $(specs) all $(srcdir)/rubygen.mk + +# Management generator. +mgen_dir=$(top_srcdir)/managementgen +mgen_cmd=$(mgen_dir)/main.py -m $(srcdir)/managementgen.mk \ + $(top_srcdir)/../specs/management-schema.xml \ + $(top_srcdir)/../specs/management-types.xml \ + $(mgen_dir)/templates $(srcdir)/gen/qpid/management + +endif # GENERATE + +include $(srcdir)/rubygen.mk +include $(srcdir)/managementgen.mk + +DISTCLEANFILES=rubygen.mk managementgen.mk + +# Code generated by C++ +noinst_PROGRAMS=generate_MaxMethodBodySize_h +generate_MaxMethodBodySize_h_SOURCES=gen/generate_MaxMethodBodySize_h.cpp +qpid/framing/MaxMethodBodySize.h: generate_MaxMethodBodySize_h + ./generate_MaxMethodBodySize_h +BUILT_SOURCES=qpid/framing/MaxMethodBodySize.h +DISTCLEANFILES+=qpid/framing/MaxMethodBodySize.h + + +## Compiler flags + +AM_CXXFLAGS = $(WARNING_CFLAGS) +AM_LDFLAGS = -version-info $(LIBTOOL_VERSION_INFO_ARG) +INCLUDES = -Igen -I$(srcdir)/gen + +## Automake macros to build libraries and executables. + +qpidd_LDADD = \ + libqpidbroker.la \ + libqpidcommon.la + +sbin_PROGRAMS = qpidd +qpidd_SOURCES = qpidd.cpp + +posix_plat_src = \ + qpid/sys/epoll/EpollPoller.cpp \ + qpid/sys/DeletionManager.h \ + qpid/sys/posix/Socket.cpp \ + qpid/sys/posix/AsynchIO.cpp \ + qpid/sys/posix/Time.cpp \ + qpid/sys/posix/Thread.cpp \ + qpid/sys/posix/Shlib.cpp + +posix_plat_hdr = \ + qpid/sys/posix/check.h \ + qpid/sys/posix/Condition.h \ + qpid/sys/posix/PrivatePosix.h \ + qpid/sys/posix/Mutex.h \ + qpid/sys/posix/Thread.h + +platform_src = $(posix_plat_src) +platform_hdr = $(posix_plat_hdr) + +lib_LTLIBRARIES = libqpidcommon.la libqpidbroker.la libqpidclient.la + +include cluster.mk + +# The logger library uses boost::date_time to format time. +# We have to disable the unused parameters warning to get around +# unused parameters in boost::date_time headers. So we build it +# in a convenience library to link into libqpid_common. +# +noinst_LTLIBRARIES=libLogger.la +libLogger_la_SOURCES=qpid/log/Logger.cpp qpid/log/Logger.h +libLogger_la_CXXFLAGS=$(AM_CXXFLAGS) -Wno-unused-parameter + +libqpidcommon_la_LIBADD = \ + -lboost_program_options \ + -lboost_filesystem \ + -luuid \ + libLogger.la \ + $(LIB_DLOPEN) \ + $(LIB_CLOCK_GETTIME) + +libqpidcommon_la_SOURCES = \ + $(rgen_common_cpp) \ + $(platform_src) \ + qpid/amqp_0_10/apply.h \ + qpid/amqp_0_10/all_built_in_types.h \ + qpid/amqp_0_10/built_in_types.h \ + qpid/amqp_0_10/complex_types.h \ + qpid/amqp_0_10/complex_types.cpp \ + qpid/amqp_0_10/Array.h \ + qpid/amqp_0_10/Array.cpp \ + qpid/amqp_0_10/Body.h \ + qpid/amqp_0_10/Header.h \ + qpid/amqp_0_10/FrameHeader.h \ + qpid/amqp_0_10/FrameHeader.cpp \ + qpid/amqp_0_10/Holder.h \ + qpid/amqp_0_10/Codec.h \ + qpid/amqp_0_10/Packer.h \ + qpid/amqp_0_10/Decimal.h \ + qpid/amqp_0_10/SerializableString.h \ + qpid/amqp_0_10/Map.h \ + qpid/amqp_0_10/Map.cpp \ + qpid/amqp_0_10/Unit.h \ + qpid/amqp_0_10/Unit.cpp \ + qpid/amqp_0_10/UnknownType.h \ + qpid/amqp_0_10/UnknownType.cpp \ + qpid/Serializer.h \ + qpid/framing/AccumulatedAck.cpp \ + qpid/framing/AMQBody.cpp \ + qpid/framing/AMQMethodBody.cpp \ + qpid/framing/AMQContentBody.cpp \ + qpid/framing/AMQFrame.cpp \ + qpid/framing/AMQHeaderBody.cpp \ + qpid/framing/AMQHeartbeatBody.cpp \ + qpid/framing/Array.cpp \ + qpid/framing/BodyHolder.cpp \ + qpid/framing/BodyHandler.cpp \ + qpid/framing/Buffer.cpp \ + qpid/framing/FieldTable.cpp \ + qpid/framing/FieldValue.cpp \ + qpid/framing/FramingContent.cpp \ + qpid/framing/FrameSet.cpp \ + qpid/framing/ProtocolInitiation.cpp \ + qpid/framing/ProtocolVersion.cpp \ + qpid/framing/SessionState.cpp \ + qpid/framing/SendContent.cpp \ + qpid/framing/SequenceNumber.cpp \ + qpid/framing/SequenceNumberSet.cpp \ + qpid/framing/SequenceSet.cpp \ + qpid/framing/Proxy.cpp \ + qpid/framing/Uuid.cpp \ + qpid/framing/AMQP_HighestVersion.h \ + qpid/framing/Blob.cpp \ + qpid/framing/MaxMethodBodySize.h \ + qpid/framing/TransferContent.cpp \ + qpid/assert.cpp qpid/assert.h \ + qpid/Exception.cpp \ + qpid/Plugin.cpp \ + qpid/Url.cpp \ + qpid/sys/AggregateOutput.cpp \ + qpid/sys/AsynchIOAcceptor.cpp \ + qpid/sys/Dispatcher.cpp \ + qpid/sys/Runnable.cpp \ + qpid/sys/SystemInfo.cpp \ + qpid/sys/Serializer.cpp \ + qpid/sys/Shlib.cpp \ + qpid/DataDir.cpp \ + qpid/Options.cpp \ + qpid/log/Options.cpp \ + qpid/log/Selector.cpp \ + qpid/log/Statement.cpp \ + qpid/IList.h \ + qpid/ISList.h \ + qpid/pointer_to_other.h + +libqpidbroker_la_LIBADD = libqpidcommon.la -lboost_iostreams +libqpidbroker_la_SOURCES = \ + $(mgen_broker_cpp) \ + qpid/amqp_0_10/Connection.h \ + qpid/amqp_0_10/Connection.cpp \ + qpid/broker/Broker.cpp \ + qpid/broker/BrokerAdapter.cpp \ + qpid/broker/SessionAdapter.cpp \ + qpid/broker/BrokerSingleton.cpp \ + qpid/broker/Exchange.cpp \ + qpid/broker/Queue.cpp \ + qpid/broker/PersistableMessage.cpp \ + qpid/broker/Bridge.cpp \ + qpid/broker/PreviewConnection.cpp \ + qpid/broker/PreviewConnectionCodec.cpp \ + qpid/broker/PreviewConnectionHandler.cpp \ + qpid/broker/PreviewSessionHandler.cpp \ + qpid/broker/PreviewSessionManager.cpp \ + qpid/broker/PreviewSessionState.cpp \ + qpid/broker/Connection.cpp \ + qpid/broker/ConnectionHandler.cpp \ + qpid/broker/ConnectionFactory.cpp \ + qpid/broker/Daemon.cpp \ + qpid/broker/DeliverableMessage.cpp \ + qpid/broker/DeliveryRecord.cpp \ + qpid/broker/DirectExchange.cpp \ + qpid/broker/DtxAck.cpp \ + qpid/broker/DtxBuffer.cpp \ + qpid/broker/DtxHandlerImpl.cpp \ + qpid/broker/DtxManager.cpp \ + qpid/broker/DtxTimeout.cpp \ + qpid/broker/DtxWorkRecord.cpp \ + qpid/broker/ExchangeRegistry.cpp \ + qpid/broker/FanOutExchange.cpp \ + qpid/broker/HeadersExchange.cpp \ + qpid/broker/IncomingExecutionContext.cpp \ + qpid/broker/IncompleteMessageList.cpp \ + qpid/broker/Message.cpp \ + qpid/broker/MessageAdapter.cpp \ + qpid/broker/MessageBuilder.cpp \ + qpid/broker/MessageDelivery.cpp \ + qpid/broker/MessageHandlerImpl.cpp \ + qpid/broker/MessageStoreModule.cpp \ + qpid/broker/NameGenerator.cpp \ + qpid/broker/NullMessageStore.cpp \ + qpid/broker/QueueBindings.cpp \ + qpid/broker/QueuePolicy.cpp \ + qpid/broker/QueueRegistry.cpp \ + qpid/broker/RecoveryManagerImpl.cpp \ + qpid/broker/RecoveredEnqueue.cpp \ + qpid/broker/RecoveredDequeue.cpp \ + qpid/broker/SemanticState.h \ + qpid/broker/SemanticState.cpp \ + qpid/broker/SessionState.h \ + qpid/broker/SessionState.cpp \ + qpid/broker/SessionManager.h \ + qpid/broker/SessionManager.cpp \ + qpid/broker/SessionHandler.h \ + qpid/broker/SessionContext.h \ + qpid/broker/SessionHandler.cpp \ + qpid/broker/SemanticHandler.cpp \ + qpid/broker/System.cpp \ + qpid/broker/Timer.cpp \ + qpid/broker/TopicExchange.cpp \ + qpid/broker/TxAccept.cpp \ + qpid/broker/TxAck.cpp \ + qpid/broker/TxBuffer.cpp \ + qpid/broker/TxPublish.cpp \ + qpid/broker/Vhost.cpp \ + qpid/management/Manageable.cpp \ + qpid/management/ManagementAgent.cpp \ + qpid/management/ManagementExchange.cpp \ + qpid/management/ManagementObject.cpp + +libqpidclient_la_LIBADD = libqpidcommon.la +libqpidclient_la_SOURCES = \ + $(rgen_client_cpp) \ + qpid/client/SessionBase.cpp \ + qpid/client/Connection.cpp \ + qpid/client/Channel.cpp \ + qpid/client/Exchange.cpp \ + qpid/broker/PersistableMessage.cpp \ + qpid/client/Queue.cpp \ + qpid/client/ConnectionImpl.cpp \ + qpid/client/Connector.cpp \ + qpid/client/Demux.cpp \ + qpid/client/Dispatcher.cpp \ + qpid/client/LocalQueue.cpp \ + qpid/client/MessageListener.cpp \ + qpid/client/Correlator.cpp \ + qpid/client/CompletionTracker.cpp \ + qpid/client/ConnectionHandler.cpp \ + qpid/client/ExecutionHandler.cpp \ + qpid/client/FutureCompletion.cpp \ + qpid/client/FutureResponse.cpp \ + qpid/client/FutureResult.cpp \ + qpid/client/SessionCore.cpp \ + qpid/client/StateManager.cpp \ + qpid/client/SubscriptionManager.cpp + +nobase_include_HEADERS = \ + $(platform_hdr) \ + qpid/amqp_0_10/apply.h \ + qpid/assert.h \ + qpid/DataDir.h \ + qpid/Exception.h \ + qpid/amqp_0_10/Exception.h \ + qpid/Msg.h \ + qpid/Options.h \ + qpid/Plugin.h \ + qpid/ptr_map.h \ + qpid/RefCounted.h \ + qpid/SharedObject.h \ + qpid/Url.h \ + qpid/InlineVector.h \ + qpid/InlineAllocator.h \ + qpid/memory.h \ + qpid/shared_ptr.h \ + qpid/broker/Broker.h \ + qpid/broker/BrokerAdapter.h \ + qpid/broker/SessionAdapter.h \ + qpid/broker/Exchange.h \ + qpid/broker/Queue.h \ + qpid/broker/BrokerSingleton.h \ + qpid/broker/Bridge.h \ + qpid/broker/PreviewConnection.h \ + qpid/broker/PreviewConnectionCodec.h \ + qpid/broker/PreviewConnectionHandler.h \ + qpid/broker/PreviewSessionHandler.h \ + qpid/broker/PreviewSessionManager.h \ + qpid/broker/PreviewSessionState.h \ + qpid/broker/Connection.h \ + qpid/broker/ConnectionState.h \ + qpid/broker/ConnectionFactory.h \ + qpid/broker/ConnectionHandler.h \ + qpid/broker/ConnectionToken.h \ + qpid/broker/OwnershipToken.h \ + qpid/broker/Consumer.h \ + qpid/broker/Daemon.h \ + qpid/broker/Deliverable.h \ + qpid/broker/DeliverableMessage.h \ + qpid/broker/DeliveryAdapter.h \ + qpid/broker/DeliveryId.h \ + qpid/broker/DeliveryRecord.h \ + qpid/broker/DeliveryToken.h \ + qpid/broker/DirectExchange.h \ + qpid/broker/DtxAck.h \ + qpid/broker/DtxBuffer.h \ + qpid/broker/DtxHandlerImpl.h \ + qpid/broker/DtxManager.h \ + qpid/broker/DtxTimeout.h \ + qpid/broker/DtxWorkRecord.h \ + qpid/broker/ExchangeRegistry.h \ + qpid/broker/FanOutExchange.h \ + qpid/broker/HandlerImpl.h \ + qpid/broker/HeadersExchange.h \ + qpid/broker/IncomingExecutionContext.h \ + qpid/broker/IncompleteMessageList.h \ + qpid/broker/Message.h \ + qpid/broker/MessageAdapter.h \ + qpid/broker/MessageBuilder.h \ + qpid/broker/MessageDelivery.h \ + qpid/broker/MessageHandlerImpl.h \ + qpid/broker/MessageStore.h \ + qpid/broker/MessageStoreModule.h \ + qpid/broker/NameGenerator.h \ + qpid/broker/NullMessageStore.h \ + qpid/broker/Persistable.h \ + qpid/broker/PersistableExchange.h \ + qpid/broker/PersistableMessage.h \ + qpid/broker/PersistableQueue.h \ + qpid/broker/Prefetch.h \ + qpid/broker/QueueBindings.h \ + qpid/broker/QueuePolicy.h \ + qpid/broker/QueueRegistry.h \ + qpid/broker/RecoverableExchange.h \ + qpid/broker/RecoverableMessage.h \ + qpid/broker/RecoverableQueue.h \ + qpid/broker/RecoverableTransaction.h \ + qpid/broker/RecoveredDequeue.h \ + qpid/broker/RecoveredEnqueue.h \ + qpid/broker/RecoveryManager.h \ + qpid/broker/RecoveryManagerImpl.h \ + qpid/broker/SemanticHandler.h \ + qpid/broker/SessionManager.h \ + qpid/broker/System.h \ + qpid/broker/Timer.h \ + qpid/broker/TopicExchange.h \ + qpid/broker/TransactionalStore.h \ + qpid/broker/TxAccept.h \ + qpid/broker/TxAck.h \ + qpid/broker/TxBuffer.h \ + qpid/broker/TxOp.h \ + qpid/broker/TxPublish.h \ + qpid/broker/Vhost.h \ + qpid/client/AckMode.h \ + qpid/client/ChainableFrameHandler.h \ + qpid/client/Channel.h \ + qpid/client/Exchange.h \ + qpid/client/Message.h \ + qpid/client/Queue.h \ + qpid/client/AckPolicy.h \ + qpid/client/Completion.h \ + qpid/client/CompletionTracker.h \ + qpid/client/Connection.h \ + qpid/client/ConnectionHandler.h \ + qpid/client/ConnectionImpl.h \ + qpid/client/Connector.h \ + qpid/client/Correlator.h \ + qpid/client/Demux.h \ + qpid/client/Dispatcher.h \ + qpid/client/LocalQueue.h \ + qpid/client/Execution.h \ + qpid/client/ExecutionHandler.h \ + qpid/client/Future.h \ + qpid/client/FutureCompletion.h \ + qpid/client/FutureResponse.h \ + qpid/client/FutureResult.h \ + qpid/client/MessageListener.h \ + qpid/client/MessageQueue.h \ + qpid/client/Response.h \ + qpid/client/SessionBase.h \ + qpid/client/Session.h \ + qpid/client/SessionCore.h \ + qpid/client/StateManager.h \ + qpid/client/SubscriptionManager.h \ + qpid/client/TypedResult.h \ + qpid/framing/AMQBody.h \ + qpid/framing/AMQCommandControlBody.h \ + qpid/framing/AMQContentBody.h \ + qpid/framing/AMQDataBlock.h \ + qpid/framing/AMQFrame.h \ + qpid/framing/AMQHeaderBody.h \ + qpid/framing/AMQHeartbeatBody.h \ + qpid/framing/AMQMethodBody.h \ + qpid/framing/AMQP_HighestVersion.h \ + qpid/framing/AccumulatedAck.h \ + qpid/framing/Array.h \ + qpid/framing/Blob.h \ + qpid/framing/BodyHandler.h \ + qpid/framing/Buffer.h \ + qpid/framing/ChannelHandler.h \ + qpid/framing/FieldTable.h \ + qpid/framing/FieldValue.h \ + qpid/framing/FrameDefaultVisitor.h \ + qpid/framing/FrameHandler.h \ + qpid/framing/FrameHandler.h \ + qpid/framing/FrameSet.h \ + qpid/framing/FramingContent.h \ + qpid/framing/Handler.h \ + qpid/framing/HeaderProperties.h \ + qpid/framing/Invoker.h \ + qpid/framing/InputHandler.h \ + qpid/framing/InitiationHandler.h \ + qpid/framing/MethodContent.h \ + qpid/framing/BodyHolder.h \ + qpid/framing/MaxMethodBodySize.h \ + qpid/framing/ModelMethod.h \ + qpid/framing/OutputHandler.h \ + qpid/framing/ProtocolInitiation.h \ + qpid/framing/ProtocolVersion.h \ + qpid/framing/Proxy.h \ + qpid/framing/SessionState.h \ + qpid/framing/SendContent.h \ + qpid/framing/SequenceNumber.h \ + qpid/framing/SequenceSet.h \ + qpid/framing/SequenceNumberSet.h \ + qpid/framing/SerializeHandler.h \ + qpid/framing/StructHelper.h \ + qpid/framing/TransferContent.h \ + qpid/framing/TypeFilter.h \ + qpid/framing/Uuid.h \ + qpid/framing/Visitor.h \ + qpid/framing/amqp_framing.h \ + qpid/framing/amqp_types.h \ + qpid/framing/amqp_types_full.h \ + qpid/framing/frame_functors.h \ + qpid/framing/variant.h \ + qpid/log/Helpers.h \ + qpid/log/Options.h \ + qpid/log/Selector.h \ + qpid/log/Statement.h \ + qpid/management/Args.h \ + qpid/management/Manageable.h \ + qpid/management/ManagementAgent.h \ + qpid/management/ManagementExchange.h \ + qpid/management/ManagementObject.h \ + qpid/sys/Acceptor.h \ + qpid/sys/AggregateOutput.h \ + qpid/sys/AsynchIO.h \ + qpid/sys/AtomicCount.h \ + qpid/sys/BlockingQueue.h \ + qpid/sys/Condition.h \ + qpid/sys/ConnectionInputHandler.h \ + qpid/sys/ConnectionInputHandlerFactory.h \ + qpid/sys/ConnectionOutputHandler.h \ + qpid/sys/Dispatcher.h \ + qpid/sys/Module.h \ + qpid/sys/Monitor.h \ + qpid/sys/Mutex.h \ + qpid/sys/OutputControl.h \ + qpid/sys/ConnectionCodec.h \ + qpid/sys/OutputTask.h \ + qpid/sys/Poller.h \ + qpid/sys/Runnable.h \ + qpid/sys/ScopedIncrement.h \ + qpid/sys/Semaphore.h \ + qpid/sys/Serializer.h \ + qpid/sys/SystemInfo.h \ + qpid/sys/Shlib.h \ + qpid/sys/ShutdownHandler.h \ + qpid/sys/Socket.h \ + qpid/sys/StateMonitor.h \ + qpid/sys/Waitable.h \ + qpid/sys/Thread.h \ + qpid/sys/Time.h \ + qpid/sys/TimeoutHandler.h + +# Force build of qpidd during dist phase so help2man will work. +dist-hook: $(BUILT_SOURCES) + $(MAKE) qpidd + +# Create the default data directory +install-data-local: + $(mkinstalldirs) $(DESTDIR)/$(localstatedir)/lib/qpidd + diff --git a/qpid/cpp/src/cluster.mk b/qpid/cpp/src/cluster.mk new file mode 100644 index 0000000000..9503845b92 --- /dev/null +++ b/qpid/cpp/src/cluster.mk @@ -0,0 +1,24 @@ +# +# Cluster library makefile fragment, to be included in Makefile.am +# +lib_LTLIBRARIES += libqpidcluster.la + +if CPG + +libqpidcluster_la_SOURCES = \ + qpid/cluster/Cluster.cpp \ + qpid/cluster/Cluster.h \ + qpid/cluster/Cpg.cpp \ + qpid/cluster/Cpg.h \ + qpid/cluster/Dispatchable.h \ + qpid/cluster/ClusterPlugin.cpp \ + qpid/cluster/ClassifierHandler.h \ + qpid/cluster/ClassifierHandler.cpp + +libqpidcluster_la_LIBADD= -lcpg libqpidbroker.la + +else +# Empty stub library to satisfy rpm spec file. +libqpidcluster_la_SOURCES = + +endif diff --git a/qpid/cpp/src/generate.sh b/qpid/cpp/src/generate.sh new file mode 100755 index 0000000000..3582893f24 --- /dev/null +++ b/qpid/cpp/src/generate.sh @@ -0,0 +1,48 @@ +# !/bin/sh +# Generate code from AMQP specification. +# specs and gentools_dir are set by Makefile +# +set -e + +test -z "$JAVA" && JAVA=java ; +test -z "$JAVAC" && JAVAC=javac ; + +srcdir=`dirname $0` +checkspecs() { + for s in $specs; do test -f $s || return 1; done + return 0 +} + +# Can we generate code? +if { test -d $gentools_dir && checkspecs && + which $JAVA && which $JAVAC; } > /dev/null; +then + echo "Generating code." + mkdir -p gen/qpid/framing + ( cd $gentools_dir/src && $JAVAC `find -name '*.java' -print` ; ) + $JAVA -cp $gentools_dir/src org.apache.qpid.gentools.Main \ + -c -o gen/qpid/framing -t $gentools_dir/templ.cpp $specs + GENERATED=yes +fi + +# Print a Makefile variable assignment. +make_assign() { + echo -n "$1 = "; shift + prefix=$1; shift + for f in $*; do echo "\\" ; echo -n " $prefix$f "; done + echo +} + +# Generate a Makefile fragment +( + make_assign "generated_cpp" "" `find gen -name '*.cpp' -print` + make_assign "generated_h" "" `find gen -name '*.h' -print` + if test x$GENERATED = xyes; then + make_assign "generator" "" $specs \ + `find ../gentools \( -name '*.java' -o -name '*.tmpl' \) -print` + fi +) > generate.mk-t +mv generate.mk-t $srcdir/generate.mk + + + diff --git a/qpid/cpp/src/prof b/qpid/cpp/src/prof new file mode 100755 index 0000000000..bd889a1446 --- /dev/null +++ b/qpid/cpp/src/prof @@ -0,0 +1,18 @@ +#!/bin/bash + +rm /var/lib/oprofile/oprofiled.log + +opcontrol --reset +opcontrol --setup --no-vmlinux --separate=library +opcontrol --start +# -- Do stuff here -- +./qpidd +# -- End of stuff -- +opcontrol --stop +opcontrol --dump +opcontrol --shutdown +opreport -l ./.libs/lt-qpidd > stats.txt +opannotate --source --output-dir=qpidd-prof ./.libs/lt-qpidd + +# clear the relusts +#opcontrol --reset diff --git a/qpid/cpp/src/qpid/DataDir.cpp b/qpid/cpp/src/qpid/DataDir.cpp new file mode 100644 index 0000000000..abf9b061e4 --- /dev/null +++ b/qpid/cpp/src/qpid/DataDir.cpp @@ -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. + * + */ + +#include "Exception.h" +#include "DataDir.h" +#include "qpid/log/Statement.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <cerrno> + +namespace qpid { + +DataDir::DataDir (std::string path) : + enabled (!path.empty ()), + dirPath (path) +{ + if (!enabled) + { + QPID_LOG (info, "No data directory - Disabling persistent configuration"); + return; + } + + const char *cpath = dirPath.c_str (); + struct stat s; + + if (::stat (cpath, &s)) + throw Exception ("Data directory not found: " + path); + + std::string lockFile (path); + lockFile = lockFile + "/lock"; + int fd = ::open (lockFile.c_str (), O_CREAT | O_EXCL, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd == -1) + { + if (errno == EEXIST) + throw Exception ("Data directory is locked by another process"); + if (errno == EACCES) + throw Exception ("Insufficient privileges for data directory"); + + std::ostringstream oss; + oss << "Error locking data directory: errno=" << errno; + throw Exception (oss.str ()); + } + + QPID_LOG (info, "Locked data directory: " << dirPath); +} + +DataDir::~DataDir () +{ + if (dirPath.empty ()) + return; + + std::string lockFile (dirPath); + lockFile = lockFile + "/lock"; + + ::unlink (lockFile.c_str ()); + QPID_LOG (info, "Unlocked data directory: " << dirPath); +} + +} // namespace qpid + diff --git a/qpid/cpp/src/qpid/DataDir.h b/qpid/cpp/src/qpid/DataDir.h new file mode 100644 index 0000000000..56aa4f26d7 --- /dev/null +++ b/qpid/cpp/src/qpid/DataDir.h @@ -0,0 +1,47 @@ +#ifndef QPID_DATADIR_H +#define QPID_DATADIR_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. + * + */ + +#include <string> + +namespace qpid { + +/** + * DataDir class. + */ +class DataDir +{ + const bool enabled; + const std::string dirPath; + + public: + + DataDir (std::string path); + ~DataDir (); + + bool isEnabled () { return enabled; } + std::string getPath () { return dirPath; } +}; + +} // namespace qpid + +#endif /*!QPID_DATADIR_H*/ diff --git a/qpid/cpp/src/qpid/Exception.cpp b/qpid/cpp/src/qpid/Exception.cpp new file mode 100644 index 0000000000..a69955c9dc --- /dev/null +++ b/qpid/cpp/src/qpid/Exception.cpp @@ -0,0 +1,55 @@ +/* + * + * 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. + * + */ + +#include "qpid/log/Statement.h" +#include "Exception.h" +#include <typeinfo> +#include <errno.h> +#include <assert.h> +#include <string.h> + +namespace qpid { + +std::string strError(int err) { + char buf[512]; + return std::string(strerror_r(err, buf, sizeof(buf))); +} + +Exception::Exception(const std::string& msg) throw() : message(msg) { + QPID_LOG(debug, "Exception thrown: " << message); +} + +Exception::~Exception() throw() {} + +std::string Exception::getPrefix() const { return "Exception"; } + +const char* Exception::what() const throw() { + if (whatStr.empty()) + whatStr = getPrefix() + ": " + message; + return whatStr.c_str(); +} + +ClosedException::ClosedException(const std::string& msg) + : Exception(msg) {} + +std::string ClosedException::getPrefix() const { return "Closed"; } + +} // namespace qpid diff --git a/qpid/cpp/src/qpid/Exception.h b/qpid/cpp/src/qpid/Exception.h new file mode 100644 index 0000000000..2f934166a7 --- /dev/null +++ b/qpid/cpp/src/qpid/Exception.h @@ -0,0 +1,86 @@ +#ifndef _Exception_ +#define _Exception_ + +/* + * + * 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. + * + */ + +#include "qpid/framing/amqp_types.h" +#include "qpid/Msg.h" + +#include <memory> +#include <string> + +namespace qpid +{ + +/** Get the error message for a system number err, e.g. errno. */ +std::string strError(int err); + +/** + * Base class for Qpid runtime exceptions. + */ +class Exception : public std::exception +{ + public: + explicit Exception(const std::string& message=std::string()) throw(); + virtual ~Exception() throw(); + virtual const char* what() const throw(); + + protected: + std::string getPrefix() const; + private: + std::string message; + mutable std::string whatStr; +}; + +/** + * I have made SessionException a common base for Channel- and + * Connection- Exceptions. This is not strictly correct but allows all + * model layer exceptions to be handled as SessionExceptions which is + * how they are classified in the final 0-10 specification. I.e. this + * is a temporary hack to allow the preview and final codepaths to + * co-exist with minimal changes. + */ + +struct SessionException : public Exception { + const framing::ReplyCode code; + SessionException(framing::ReplyCode code_, const std::string& message) + : Exception(message), code(code_) {} +}; + +struct ChannelException : public SessionException { + ChannelException(framing::ReplyCode code, const std::string& message) + : SessionException(code, message) {} +}; + +struct ConnectionException : public SessionException { + ConnectionException(framing::ReplyCode code, const std::string& message) + : SessionException(code, message) {} +}; + +struct ClosedException : public Exception { + ClosedException(const std::string& msg=std::string()); + std::string getPrefix() const; +}; + +} // namespace qpid + +#endif /*!_Exception_*/ diff --git a/qpid/cpp/src/qpid/IList.h b/qpid/cpp/src/qpid/IList.h new file mode 100644 index 0000000000..f5c78ced68 --- /dev/null +++ b/qpid/cpp/src/qpid/IList.h @@ -0,0 +1,196 @@ +#ifndef QPID_ILIST_H +#define QPID_ILIST_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. + * + */ + +#include "ISList.h" + +namespace qpid { + +template <class> class IList; + +/** Base class for values (nodes) in an IList. + *@param raw or smart-pointer type to use for the "next" pointer. + * Using a smart pointer like shared_ptr or intrusive_ptr + * will automate memory management. + */ +template <class Pointer> class IListNode { + public: + typedef Pointer pointer; + typedef typename Pointee<Pointer>::type NodeType; + typedef typename pointer_to_other<Pointer, const NodeType>::type const_pointer; + + protected: + IListNode() : prev() {} + IListNode(const IListNode&) {} // Don't copy next/prev pointers + + pointer getNext() { return next; } + const_pointer getNext() const { return next; } + pointer getPrev() { return prev; } + const_pointer getPrev() const { return prev; } + + private: + pointer prev, next; + friend class IList<NodeType>; +}; + + +/** + * Intrusive doubly-linked list. + * + * Provides bidirectional iterator and constant time insertion + * at front and back. + * + * The list itself performs no memory management. Use a smart pointer + * as the pointer type (e.g. intrusive_ptr, shared_ptr) for automated + * memory management. + * + * Unlike standard containers insert(), push_front() and push_back() + * take const pointer& rather than const value_type&. + * + * Iterators can be converted to the pointer type. + * + * Noncopyable - intrusively linked nodes cannot be shared between + * lists. Does provide swap() + * + * @param Node value type for the list, must derive from ISListNode<>. + */ +template<class Node> class IList { + template <class> class Iterator; + public: + typedef Node value_type; + typedef typename Node::pointer pointer; + typedef typename Node::const_pointer const_pointer; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef Iterator<value_type> iterator; + typedef Iterator<const value_type> const_iterator; + + IList() : begin_(), last_() {} + + iterator begin() { return begin_; } + const_iterator begin() const { return begin_; } + iterator end() { return 0; } + const_iterator end() const { return 0; } + + bool empty() const { return begin() == end(); } + + size_type size() const { + int s = 0; + for (const_iterator i=begin(); i != end(); ++i) + ++s; + return s; + } + + void swap(IList &x) { swap(begin_, x.begin_); swap(last_, x.last_); } + + iterator insert(iterator i, const pointer& p) { + if (empty()) { + begin_ = last_ = p; + insert(0, p, 0); + } + else if (i) { + insert(i->prev, p, i); + if (i == begin_) --begin_; + } + else { + insert(last_, p, 0) ; + last_ = p; + } + return p; + } + + void erase(iterator i) { + if (begin_ == last_) + begin_ = last_ = 0; + else { + if (i == begin_) ++begin_; + else i->prev->next = i->next; + if (i == last_) --last_; + else i->next->prev = i->prev; + } + i->prev = i->next = pointer(0); + } + + void erase(iterator i, iterator j) { while(i != j) erase(i); } + void clear() { while (!empty()) { erase(begin()); } } + + reference front() { return *begin(); } + const_reference front() const { return *begin(); } + void push_front(const pointer& p) { insert(begin(), p); } + void pop_front() { erase(begin()); } + + /** Iterator to the last element in the list. */ + iterator last() { return last_; } + const_iterator last() const { return last_; } + + reference back() { return *last(); } + const_reference back() const { return *last(); } + void push_back(const pointer& p) { insert(end(), p); } + void pop_back() { erase(last()); } + + private: + void insert(pointer a, pointer b, pointer c) { + b->prev = a; + if (a) a->next = b; + b->next = c; + if (c) c->prev = b; + } + + template <class T> + class Iterator : public boost::iterator_facade< + Iterator<T>, T, boost::bidirectional_traversal_tag> + { + public: + Iterator() : ptr() {}; + + template <class U> Iterator( + const Iterator<U>& i, + typename boost::enable_if_convertible<U*, T*>::type* = 0 + ) : ptr(i.ptr) {} + + operator pointer() { return ptr; } + operator const_pointer() const { return ptr; } + + private: + friend class boost::iterator_core_access; + + Iterator(const_pointer p) : ptr(const_cast<pointer>(p)) {}; + + T& dereference() const { return *ptr; } + void increment() { ptr = ptr->next; } + void decrement() { ptr = ptr->prev; } + bool equal(const Iterator& x) const { return ptr == x.ptr; } + + pointer ptr; + + friend class IList<Node>; + }; + + iterator begin_, last_; +}; + +} // namespace qpid + +#endif /*!QPID_ILIST_H*/ diff --git a/qpid/cpp/src/qpid/ISList.h b/qpid/cpp/src/qpid/ISList.h new file mode 100644 index 0000000000..96ba3ec726 --- /dev/null +++ b/qpid/cpp/src/qpid/ISList.h @@ -0,0 +1,176 @@ +#ifndef QPID_ISLIST_H +#define QPID_ISLIST_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. + * + */ + +#include <boost/iterator/iterator_adaptor.hpp> +#include <boost/noncopyable.hpp> +#include "pointer_to_other.h" + +namespace qpid { + +template <class Pointer> struct Pointee { + typedef typename Pointer::element_type type; +}; + +template <class T> struct Pointee<T*> { + typedef T type; +}; + +template <class> class ISList; +template <class> class IList; + +/** Base class for values (nodes) in an ISList. + *@param raw or smart-pointer type to use for the "next" pointer. + * Using a smart pointer like shared_ptr or intrusive_ptr + * will automate memory management. + */ +template <class Pointer> class ISListNode { + public: + typedef Pointer pointer; + typedef typename Pointee<Pointer>::type NodeType; + typedef typename pointer_to_other<Pointer, const NodeType>::type const_pointer; + + protected: + ISListNode() : next() {} + ISListNode(const ISListNode&) {} // Don't copy the next pointer. + + pointer getNext() { return next; } + const_pointer getNext() const { return next; } + + private: + pointer next; + friend class ISList<NodeType>; +}; + + +/** + * Intrusive singly-linked list. + * + * Provides forward iterator, constant time insertion and constant + * time pop_front (but not pop_back) so makes a good queue + * implementation. + * + * Unlike standard containers insert(), push_front() and push_back() + * take const pointer& rather than const value_type&. + * + * Iterators can be converted to pointers. + * + * Noncopyable - intrusively linked nodes cannot be shared. + * + * @param Node value type for the list, must derive from ISListNode<T>. + */ +template <class Node> class ISList : private boost::noncopyable { + template <class> class Iterator; + public: + typedef Node value_type; + typedef typename Node::pointer pointer; + typedef typename Node::const_pointer const_pointer; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef Iterator<value_type> iterator; + typedef Iterator<const value_type> const_iterator; + + ISList() : first(pointer()), end_(&first) {} + + iterator begin() { return iterator(&first); } + const_iterator begin() const { return const_iterator(&first); } + iterator end() { return end_; } + const_iterator end() const { return end_; } + + bool empty() const { return begin() == end(); } + + size_type size() const { + int s = 0; + for (const_iterator i=begin(); i != end(); ++i) + ++s; + return s; + } + + void swap(ISList &x) { swap(first, x.first); swap(end_, x.end_); } + + /** Unlike standard containers, insert takes a const pointer&, not a + * const value_type&. The value is not copied, only linked into the list. + */ + iterator insert(iterator i, const pointer& p) { + p->next = *(i.pptr); + *(i.pptr) = p; + if (i==end_) ++end_; + return i; + } + + void erase(iterator i) { + if (&i->next == end_.pptr) + end_ = i; + *(i.pptr) = (**(i.pptr)).next; + } + + void erase(iterator i, iterator j) { while(i != j) erase(i); } + void clear() { while (!empty()) { erase(begin()); } } + + reference front() { return *begin(); } + const_reference front() const { return *begin(); } + void pop_front() { erase(begin()); } + void push_front(pointer x) { insert(begin(), x); } + + void push_back(pointer x) { insert(end(), x); } + + private: + template <class T> + class Iterator : public boost::iterator_facade < + Iterator<T>, T, boost::forward_traversal_tag> + { + public: + Iterator() {}; + + template <class U> Iterator( + const Iterator<U>& i, + typename boost::enable_if_convertible<U*, T*>::type* = 0 + ) : pptr(i.pptr) {} + + operator pointer() { return *pptr; } + operator const_pointer() const { return *pptr; } + + private: + friend class boost::iterator_core_access; + + Iterator(const pointer* pp) : pptr(const_cast<pointer*>(pp)) {}; + + T& dereference() const { return **pptr; } + void increment() { pptr = &(**pptr).next; } + bool equal(const Iterator& x) const { return pptr == x.pptr; } + + pointer* pptr; + + friend class ISList<Node>; + }; + + private: + pointer first; + iterator end_; +}; + +} // namespace qpid + +#endif /*!QPID_ISLIST_H*/ diff --git a/qpid/cpp/src/qpid/InlineAllocator.h b/qpid/cpp/src/qpid/InlineAllocator.h new file mode 100644 index 0000000000..0bb30fa1a4 --- /dev/null +++ b/qpid/cpp/src/qpid/InlineAllocator.h @@ -0,0 +1,69 @@ +#ifndef QPID_INLINEALLOCATOR_H +#define QPID_INLINEALLOCATOR_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. + * + */ + +#include <memory> + +namespace qpid { + +/** + * An allocator that has inline storage for up to Max objects + * of type BaseAllocator::value_type. + */ +template <class BaseAllocator, size_t Max> +class InlineAllocator : public BaseAllocator { + public: + typedef typename BaseAllocator::pointer pointer; + typedef typename BaseAllocator::size_type size_type; + typedef typename BaseAllocator::value_type value_type; + + InlineAllocator() : allocated(false) {} + + pointer allocate(size_type n) { + if (n <= Max && !allocated) { + allocated=true; + return store; + } + else + return BaseAllocator::allocate(n, 0); + } + + void deallocate(pointer p, size_type n) { + if (p == store) allocated=false; + else BaseAllocator::deallocate(p, n); + } + + template<typename T1> + struct rebind { + typedef typename BaseAllocator::template rebind<T1>::other BaseOther; + typedef InlineAllocator<BaseOther, Max> other; + }; + + private: + value_type store[Max]; + bool allocated; +}; + +} // namespace qpid + +#endif /*!QPID_INLINEALLOCATOR_H*/ diff --git a/qpid/cpp/src/qpid/InlineVector.h b/qpid/cpp/src/qpid/InlineVector.h new file mode 100644 index 0000000000..551b9912e7 --- /dev/null +++ b/qpid/cpp/src/qpid/InlineVector.h @@ -0,0 +1,68 @@ +#ifndef QPID_INLINEVECTOR_H +#define QPID_INLINEVECTOR_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. + * + */ + +#include "InlineAllocator.h" +#include <vector> + +namespace qpid { + +/** + * A vector that stores up to Max elements in inline storage, + * otherwise uses normal vector allocation. + * + * NOTE: depends on some non-standard but highly probably assumptions + * about how std::vector uses its allocator, they are true for g++. + * - default constructor does not allocate. + * - reserve(N) does not allocate more than N elements. + * - vector never re-allocates when size() < capacity() + */ +template <class T, size_t Max, class Alloc=std::allocator<T> > +class InlineVector : public std::vector<T, InlineAllocator<Alloc, Max> > +{ + typedef std::vector<T, InlineAllocator<Alloc, Max> > Base; + public: + typedef typename Base::allocator_type allocator_type; + typedef typename Base::value_type value_type; + typedef typename Base::size_type size_type; + + explicit InlineVector(const allocator_type& a=allocator_type()) : Base(a) { + this->reserve(Max); + } + + explicit InlineVector(size_type n, const value_type& x = value_type(), + const allocator_type& a=allocator_type()) : Base(a) + { + this->reserve(std::max(n, Max)); + this->insert(this->end(), n, x); + } + + InlineVector(const InlineVector& x) : Base() { + this->reserve(std::max(x.size(), Max)); + *this = x; + } +}; + +} // namespace qpid + +#endif /*!QPID_INLINEVECTOR_H*/ diff --git a/qpid/cpp/src/qpid/Msg.h b/qpid/cpp/src/qpid/Msg.h new file mode 100644 index 0000000000..7214db611f --- /dev/null +++ b/qpid/cpp/src/qpid/Msg.h @@ -0,0 +1,61 @@ +#ifndef QPID_MSG_H +#define QPID_MSG_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. + * + */ + +#include <sstream> +#include <iostream> + +namespace qpid { + +/** A simple wrapper for std::ostringstream that allows + * in place construction of a message and automatic conversion + * to string. + * E.g. + *@code + * void foo(const std::string&); + * foo(Msg() << "hello " << 32); + *@endcode + * Will construct the string "hello 32" and pass it to foo() + */ +struct Msg { + std::ostringstream os; + Msg() {} + Msg(const Msg& m) : os(m.str()) {} + std::string str() const { return os.str(); } + operator std::string() const { return str(); } +}; + +template <class T> const Msg& operator<<(const Msg& m, const T& t) { + const_cast<std::ostringstream&>(m.os)<<t; return m; +} + +inline std::ostream& operator<<(std::ostream& o, const Msg& m) { + return o<<m.str(); +} + +/** Construct a message using operator << and append (file:line) */ +#define QPID_MSG(message) ::qpid::Msg() << message << " (" << __FILE__ << ":" << __LINE__ << ")" + +} // namespace qpid + +#endif /*!QPID_MSG_H*/ diff --git a/qpid/cpp/src/qpid/Options.cpp b/qpid/cpp/src/qpid/Options.cpp new file mode 100644 index 0000000000..a5d3b54dd6 --- /dev/null +++ b/qpid/cpp/src/qpid/Options.cpp @@ -0,0 +1,160 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "Options.h" +#include "qpid/Exception.h" + +#include <boost/bind.hpp> + +#include <fstream> +#include <algorithm> +#include <iostream> + +namespace qpid { + +using namespace std; + +namespace { + +struct EnvOptMapper { + static bool matchChar(char env, char opt) { + return (env==toupper(opt)) || (strchr("-.", opt) && env=='_'); + } + + static bool matchStr(const string& env, boost::shared_ptr<po::option_description> desc) { + return std::equal(env.begin(), env.end(), desc->long_name().begin(), &matchChar); + } + + static bool matchCase(const string& env, boost::shared_ptr<po::option_description> desc) { + return env == desc->long_name(); + } + + EnvOptMapper(const Options& o) : opts(o) {} + + string operator()(const string& envVar) { + static const std::string prefix("QPID_"); + if (envVar.substr(0, prefix.size()) == prefix) { + string env = envVar.substr(prefix.size()); + typedef const std::vector< boost::shared_ptr<po::option_description> > OptDescs; + OptDescs::const_iterator i = + find_if(opts.options().begin(), opts.options().end(), boost::bind(matchStr, env, _1)); + if (i != opts.options().end()) + return (*i)->long_name(); + } + return string(); + } + + string configFileLine (string& line) { + size_t pos = line.find ('='); + if (pos == string::npos) + return string(); + string key = line.substr (0, pos); + typedef const std::vector< boost::shared_ptr<po::option_description> > OptDescs; + OptDescs::const_iterator i = + find_if(opts.options().begin(), opts.options().end(), boost::bind(matchCase, key, _1)); + if (i != opts.options().end()) + return string (line) + "\n"; + return string (); + } + + const Options& opts; +}; + +} +std::string prettyArg(const std::string& name, const std::string& value) { + return value.empty() ? name+" " : name+" ("+value+") "; +} + +Options::Options(const string& name) : po::options_description(name) {} + +void Options::parse(int argc, char** argv, const std::string& configFile, bool allowUnknown) +{ + string defaultConfigFile = configFile; // May be changed by env/cmdline + string parsing; + try { + po::variables_map vm; + parsing="command line options"; + if (argc > 0 && argv != 0) { + if (allowUnknown) { + // This hideous workaround is required because boost 1.33 has a bug + // that causes 'allow_unregistered' to not work. + po::command_line_parser clp = po::command_line_parser(argc, const_cast<char**>(argv)). + options(*this).allow_unregistered(); + po::parsed_options opts = clp.run(); + po::parsed_options filtopts = clp.run(); + filtopts.options.clear (); + for (std::vector< po::basic_option<char> >::iterator i = opts.options.begin(); + i != opts.options.end(); i++) + if (!i->unregistered) + filtopts.options.push_back (*i); + po::store(filtopts, vm); + } + else + po::store(po::parse_command_line(argc, const_cast<char**>(argv), *this), vm); + } + parsing="environment variables"; + po::store(po::parse_environment(*this, EnvOptMapper(*this)), vm); + po::notify(vm); // configFile may be updated from arg/env options. + if (!configFile.empty()) { + parsing="configuration file "+configFile; + ifstream conf(configFile.c_str()); + if (conf.good()) { + // Remove this hack when we get a stable version of boost that + // can allow unregistered options in config files. + EnvOptMapper mapper(*this); + stringstream filtered; + + while (!conf.eof()) { + string line; + getline (conf, line); + filtered << mapper.configFileLine (line); + } + + po::store(po::parse_config_file(filtered, *this), vm); + // End of hack + } + else { + // No error if default configfile is missing/unreadable + // but complain for non-default config file. + if (configFile != defaultConfigFile) + throw Exception("cannot read configuration file " + +configFile); + } + } + po::notify(vm); + } + catch (const std::exception& e) { + ostringstream msg; + msg << "Error in " << parsing << ": " << e.what() << endl; + if (find_nothrow("help", false)) + msg << "Use --help to see valid options" << endl; + throw Exception(msg.str()); + } +} + +CommonOptions::CommonOptions(const string& name, const string& configfile) + : Options(name), config(configfile) +{ + addOptions() + ("help,h", optValue(help), "Displays the help message") + ("version,v", optValue(version), "Displays version information") + ("config", optValue(config, "FILE"), "Reads configuration from FILE"); +} + +} // namespace qpid + diff --git a/qpid/cpp/src/qpid/Options.h b/qpid/cpp/src/qpid/Options.h new file mode 100644 index 0000000000..475d8e91d5 --- /dev/null +++ b/qpid/cpp/src/qpid/Options.h @@ -0,0 +1,150 @@ +#ifndef QPID_COMMONOPTIONS_H +#define QPID_COMMONOPTIONS_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. + * + */ + +#include "qpid/Exception.h" +#include <boost/program_options.hpp> +#include <boost/format.hpp> +#include <sstream> +#include <iterator> +#include <algorithm> + +namespace qpid { +namespace po=boost::program_options; + +///@internal +std::string prettyArg(const std::string&, const std::string&); + +/** @internal Normally only constructed by optValue() */ +template <class T> +class OptionValue : public po::typed_value<T> { + public: + OptionValue(T& value, const std::string& arg) + : po::typed_value<T>(&value), argName(arg) {} + std::string name() const { return argName; } + + private: + std::string argName; +}; + + +/** Create an option value. + * name, value appear after the option name in help like this: + * <name> (=<value>) + * T must support operator <<. + *@see Options for example of use. + */ +template<class T> +po::value_semantic* optValue(T& value, const char* name) { + std::string valstr(boost::lexical_cast<std::string>(value)); + return new OptionValue<T>(value, prettyArg(name, valstr)); +} + +/** Create a vector value. Multiple occurences of the option are + * accumulated into the vector + */ +template <class T> +po::value_semantic* optValue(std::vector<T>& value, const char* name) { + using namespace std; + ostringstream os; + copy(value.begin(), value.end(), ostream_iterator<T>(os, " ")); + string val=os.str(); + if (!val.empty()) + val.erase(val.end()-1); // Remove trailing " " + return (new OptionValue<vector<T> >(value, prettyArg(name, val))); +} + +/** Create a boolean switch value. Presence of the option sets the value. */ +inline po::value_semantic* optValue(bool& value) { return po::bool_switch(&value); } + +/** + * Base class for options. + * Example of use: + @code + struct MySubOptions : public Options { + int x; + string y; + MySubOptions() : Options("Sub options") { + addOptions() + ("x", optValue(x,"XUNIT"), "Option X") + ("y", optValue(y, "YUNIT"), "Option Y"); + } + }; + + struct MyOptions : public Options { + bool z; + vector<string> foo; + MySubOptions subOptions; + MyOptions() : Options("My Options") { + addOptions() + ("z", boolSwitch(z), "Option Z") + ("foo", optValue(foo), "Multiple option foo"); + add(subOptions); + } + + main(int argc, char** argv) { + Options opts; + opts.parse(argc, char** argv); + // Use values + dosomething(opts.subOptions.x); + if (error) + cout << opts << end; // Help message. + } + + @endcode + */ +struct Options : public po::options_description { + struct Exception : public qpid::Exception { + Exception(const std::string& msg) : qpid::Exception(msg) {} + }; + + Options(const std::string& name=std::string()); + + boost::program_options::options_description_easy_init addOptions() { + return add_options(); + } + + /** + * Parses options from argc/argv, environment variables and config file. + * Note the filename argument can reference an options variable that + * is updated by argc/argv or environment variable parsing. + */ + void parse(int argc, char** argv, + const std::string& configfile=std::string(), + bool allowUnknown = false); +}; + +/** + * Standard options for configuration + */ +struct CommonOptions : public Options { + CommonOptions(const std::string& name=std::string(), + const std::string& configfile=std::string()); + bool help; + bool version; + std::string config; +}; + +} // namespace qpid + +#endif /*!QPID_COMMONOPTIONS_H*/ diff --git a/qpid/cpp/src/qpid/Plugin.cpp b/qpid/cpp/src/qpid/Plugin.cpp new file mode 100644 index 0000000000..d38b53a56e --- /dev/null +++ b/qpid/cpp/src/qpid/Plugin.cpp @@ -0,0 +1,40 @@ +/* + * 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. + * + */ + +#include "Plugin.h" + +namespace qpid { + +Plugin::Plugins Plugin::plugins; + +Plugin::Plugin() { + // Register myself. + plugins.push_back(this); +} + +Plugin::~Plugin() {} + +Options* Plugin::getOptions() { return 0; } + +const Plugin::Plugins& Plugin::getPlugins() { + return plugins; +} + +} // namespace qpid diff --git a/qpid/cpp/src/qpid/Plugin.h b/qpid/cpp/src/qpid/Plugin.h new file mode 100644 index 0000000000..e040662866 --- /dev/null +++ b/qpid/cpp/src/qpid/Plugin.h @@ -0,0 +1,98 @@ +#ifndef QPID_PLUGIN_H +#define QPID_PLUGIN_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. + * + */ + +#include "qpid/shared_ptr.h" +#include <boost/noncopyable.hpp> +#include <vector> +#include <boost/function.hpp> + + +/**@file Generic plug-in framework. */ + +namespace qpid { +class Options; + +/** + * Plug-in base class. + */ +class Plugin : boost::noncopyable +{ + public: + /** + * Base interface for targets that receive plug-ins. + * + * The Broker is a plug-in target, there might be others + * in future. + */ + struct Target { virtual ~Target() {} }; + + typedef std::vector<Plugin*> Plugins; + + /** + * Construct registers the plug-in to appear in getPlugins(). + * + * A concrete Plugin is instantiated as a global or static + * member variable in a library so it is registered during static + * initialization when the library is loaded. + */ + Plugin(); + + virtual ~Plugin(); + + /** + * Configuration options for the plugin. + * Then will be updated during option parsing by the host program. + * + * @return An options group or 0 for no options. Default returns 0. + * Plugin retains ownership of return value. + */ + virtual Options* getOptions(); + + /** + * Initialize Plugin functionality on a Target. + * Plugins should ignore targets they don't recognize. + * + * Called before the target itself is initialized. + */ + virtual void earlyInitialize(Target&) = 0; + + /** + * Initialize Plugin functionality on a Target. + * Plugins should ignore targets they don't recognize. + * + * Called after the target is fully initialized. + */ + virtual void initialize(Target&) = 0; + + /** List of registered Plugin objects. + * Caller must not delete plugin pointers. + */ + static const Plugins& getPlugins(); + + private: + static Plugins plugins; +}; + +} // namespace qpid + +#endif /*!QPID_PLUGIN_H*/ diff --git a/qpid/cpp/src/qpid/RefCounted.h b/qpid/cpp/src/qpid/RefCounted.h new file mode 100644 index 0000000000..d67f6c31db --- /dev/null +++ b/qpid/cpp/src/qpid/RefCounted.h @@ -0,0 +1,78 @@ +#ifndef QPID_REFCOUNTED_H +#define QPID_REFCOUNTED_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. + * + */ + +#include <boost/utility.hpp> +#include <boost/detail/atomic_count.hpp> + +namespace qpid { + +/** + * Reference-counted base class. + * Note: this class isn't copyable - you must copy the intrusive_ptr that points + * to the class that has mixed this in not the class itself (as that would sidestep + * the reference counting) + */ +class RefCounted : boost::noncopyable { + mutable boost::detail::atomic_count count; + +public: + RefCounted() : count(0) {} + void addRef() const { ++count; } + void release() const { if (--count==0) delete this; } + long refCount() { return count; } + +protected: + virtual ~RefCounted() {}; +}; + +/** + * Reference-counted member of a reference-counted parent class. + * Delegates reference counts to the parent so that the parent is + * deleted only when there are no references to the parent or any of + * its children. + * TODO: Delete this class if it's unused as I don't think this class makes much sense: + */ +struct RefCountedChild { + RefCounted& parent; + +protected: + RefCountedChild(RefCounted& parent_) : parent(parent_) {} + +public: + void addRef() const { parent.addRef(); } + void release() const { parent.release(); } +}; + +} // namespace qpid + +// intrusive_ptr support. +namespace boost { +inline void intrusive_ptr_add_ref(const qpid::RefCounted* p) { p->addRef(); } +inline void intrusive_ptr_release(const qpid::RefCounted* p) { p->release(); } +inline void intrusive_ptr_add_ref(const qpid::RefCountedChild* p) { p->addRef(); } +inline void intrusive_ptr_release(const qpid::RefCountedChild* p) { p->release(); } +} + + +#endif /*!QPID_REFCOUNTED_H*/ diff --git a/qpid/cpp/src/qpid/Serializer.h b/qpid/cpp/src/qpid/Serializer.h new file mode 100644 index 0000000000..fc53097207 --- /dev/null +++ b/qpid/cpp/src/qpid/Serializer.h @@ -0,0 +1,197 @@ +#ifndef QPID_SERIALIZER_H +#define QPID_SERIALIZER_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. + * + */ + +#include <limits> +#include <algorithm> +#include "qpid/Exception.h" // FIXME aconway 2008-04-03: proper exception class. + +namespace qpid { + +/** + * Overload for types that do not provide a serialize() member. + * It should retrun a wrapper holding a reference to t that implements + * serialize() + */ +template <class T> T& serializable(T& t) { return t; } + +/** Serialize std::pair */ +template <class T, class U> struct SerializablePair { + std::pair<T,U>& value; + SerializablePair(std::pair<T,U>& x) : value(x) {} + template <class S> void serialize(S& s) { s(value.first)(value.second); } +}; + +template <class T, class U> +SerializablePair<T,U> serializable(std::pair<T,U>& p) { + return SerializablePair<T,U>(p); +} + +/** + * Base class for all serializers. + * Derived serializers inherit from either Encoder or Decoder. + * Serializers can be used as functors or static_visitors. + */ +template <class Derived> class Serializer { + public: + /** Temporarily set a lower relative limit on the serializer */ + class ScopedLimit { + public: + ScopedLimit(Serializer& s, size_t l) + : serializer(s), save(serializer.setLimit(l)) {} + + ~ScopedLimit() { serializer.setAbsLimit(save); } + + private: + Serializer& serializer; + size_t save; + }; + + static size_t maxLimit() { return std::numeric_limits<size_t>::max(); } + + Serializer() : bytes(0), limit(maxLimit()) {} + + typedef Derived& result_type; // unary functor requirement. + + /** Wrapper functor to pass serializer functors by reference. */ + template <class S> struct Ref { + typedef typename S::result_type result_type; + S& s; + Ref(S& ss) : s(ss) {} + template <class T> result_type operator()(T& x) { return s(x); } + template <class T> result_type operator()(const T& x) { return s(x); } + }; + + /** Reference wrapper to pass serializers by reference, + * e.g. to std:: functions that take functors. + */ + template <class S> static Ref<S> ref(S& s) { return Ref<S>(s); } + + /** Generic rule to serialize an iterator range */ + template <class Iter> Derived& operator()(Iter begin, Iter end) { + std::for_each(begin, end, ref(this->self())); + return self(); + } + + /** Set limit relative to current position. + * @return old absolute limit. + */ + size_t setLimit(size_t n) { + size_t l=limit; + limit = bytes+n; + return l; + } + + /** Get the max number of bytes that can be processed under the + * current limit. + */ + size_t getLimit() const { + return limit - bytes; + } + /** Set absolute limit. */ + void setAbsLimit(size_t n) { + limit = n; + if (bytes > limit) + throw Exception("Framing error: data overrun"); // FIXME aconway 2008-04-03: proper exception. + } + + protected: + Derived& self() { return *static_cast<Derived*>(this); } + void addBytes(size_t n) { + size_t newBytes=bytes+n; + if (newBytes > limit) + throw Exception("Framing error: data overrun"); // FIXME aconway 2008-04-03: proper exception. + bytes = newBytes; + } + + private: + void checkLimit() { + } + + size_t bytes; // how many bytes serialized. + size_t limit; // bytes may not exceed this limit. +}; + +/** + * Base class for encoders, provides generic encode functions. + * + * A derived encoder must provide operator(const T&) to encode all + * primitive types T. + */ +template <class Derived> class EncoderBase : public Serializer<Derived> { + public: + using Serializer<Derived>::operator(); + using Serializer<Derived>::self; + + /** Default op() for non-primitive types. */ + template <class T> Derived& operator()(const T& t) { + serializable(const_cast<T&>(t)).serialize(self()); return self(); + } + + /** Split serialize() into encode()/decode() */ + template <class T> Derived& split(const T& t) { + t.encode(self()); return self(); + } +}; + +/** + * Base class for decoders, provides generic decode functions. + * + * A derived encoder must provide operator(T&) to encode all + * primitive types T. + */ +template <class Derived> class DecoderBase : public Serializer<Derived> { + public: + using Serializer<Derived>::operator(); + using Serializer<Derived>::self; + + /** Default op() for non-primitive types. */ + template <class T> Derived& operator()(T& t) { + + serializable(t).serialize(self()); return self(); + } + + /** Split serialize() into encode()/decode() */ + template <class T> Derived& split(T& t) { + t.decode(self()); return self(); + } +}; + +/** Serialize a type by converting it to/from another type. + * To serialize type Foo by converting to/from type Bar create + * a serializable() overload like this: + * + * SerializeAs<Foo,Bar> serializable(Foo& t) { return SerializeAs<Foo,Bar>(t); } + */ +template <class Type, class AsType> +struct SerializeAs { + Type& value; + SerializeAs(Type & t) : value(t) {} + template <class S> void serialize(S& s) { s.split(*this); } + template <class S> void encode(S& s) const { s(AsType(value)); } + template <class S> void decode(S& s) { AsType x; s(x); value=Type(x); } +}; + +} // namespace qpid + +#endif /*!QPID_SERIALIZER_H*/ diff --git a/qpid/cpp/src/qpid/SharedObject.h b/qpid/cpp/src/qpid/SharedObject.h new file mode 100644 index 0000000000..852a036ab9 --- /dev/null +++ b/qpid/cpp/src/qpid/SharedObject.h @@ -0,0 +1,55 @@ +#ifndef _SharedObject_ +#define _SharedObject_ + +/* + * + * 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. + * + */ + +#include <boost/shared_ptr.hpp> +#include <boost/noncopyable.hpp> + +namespace qpid { + /** + * Template to enforce shared object conventions. + * Shared object classes should inherit : public qpid::SharedObject + * That ensures Foo: + * - has typedef boost::shared_ptr<T> shared_ptr + * - has virtual destructor + * - is boost::noncopyable (no default copy or assign) + * - has a protected default constructor. + * + * Shared objects should not have public constructors. + * Make constructors protected and provide public statc create() + * functions that return a shared_ptr. + */ + template <class T> + class SharedObject : private boost::noncopyable + { + public: + typedef boost::shared_ptr<T> shared_ptr; + + virtual ~SharedObject() {}; + + protected: + SharedObject() {} + }; +} + +#endif /*!_SharedObject_*/ diff --git a/qpid/cpp/src/qpid/Url.cpp b/qpid/cpp/src/qpid/Url.cpp new file mode 100644 index 0000000000..090cbb712a --- /dev/null +++ b/qpid/cpp/src/qpid/Url.cpp @@ -0,0 +1,176 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "qpid/Url.h" +#include "qpid/Exception.h" +#include "qpid/Msg.h" + +#include <limits.h> // NB: must be before boost/spirit headers. +#include <boost/spirit.hpp> +#include <boost/spirit/actor.hpp> + +#include <sstream> + +#include <sys/ioctl.h> +#include <net/if.h> +#include <unistd.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <errno.h> + +using namespace boost::spirit; +using namespace std; + +namespace qpid { + +std::ostream& operator<<(std::ostream& os, const TcpAddress& a) { + return os << "tcp:" << a.host << ":" << a.port; +} + +std::istream& operator>>(std::istream&, const TcpAddress&); + +Url Url::getHostNameUrl(uint16_t port) { + char name[HOST_NAME_MAX]; + if (::gethostname(name, sizeof(name)) != 0) + throw InvalidUrl(QPID_MSG("Cannot get host name: " << strError(errno))); + return Url(TcpAddress(name, port)); +} + +static const string LOCALHOST("127.0.0.1"); + +Url Url::getIpAddressesUrl(uint16_t port) { + Url url; + int s = socket (PF_INET, SOCK_STREAM, 0); + for (int i=1;;i++) { + struct ifreq ifr; + ifr.ifr_ifindex = i; + if (::ioctl (s, SIOCGIFNAME, &ifr) < 0) + break; + /* now ifr.ifr_name is set */ + if (::ioctl (s, SIOCGIFADDR, &ifr) < 0) + continue; + struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr; + string addr(inet_ntoa(sin->sin_addr)); + if (addr != LOCALHOST) + url.push_back(TcpAddress(addr, port)); + } + close (s); + return url; +} + +string Url::str() const { + if (cache.empty() && !this->empty()) { + ostringstream os; + os << *this; + cache = os.str(); + } + return cache; +} + +ostream& operator<<(ostream& os, const Url& url) { + Url::const_iterator i = url.begin(); + os << "amqp:"; + if (i!=url.end()) { + os << *i++; + while (i != url.end()) + os << "," << *i++; + } + return os; +} + +// Addition to boost::spirit parsers: accept any character from a +// string. Vastly more compile-time-efficient than long rules of the +// form: ch_p('x') | ch_p('y') |... +// +struct ch_in : public char_parser<ch_in> { + ch_in(const string& chars_) : chars(chars_) {} + bool test(char ch_) const { + return chars.find(ch_) != string::npos; + } + string chars; +}; + +inline ch_in ch_in_p(const string& chars) { + return ch_in(chars); +} + +/** Grammar for AMQP URLs. */ +struct UrlGrammar : public grammar<UrlGrammar> +{ + Url& addr; + + UrlGrammar(Url& addr_) : addr(addr_) {} + + template <class ScannerT> + struct definition { + TcpAddress tcp; + + definition(const UrlGrammar& self) + { + first = eps_p[clear_a(self.addr)] >> amqp_url; + amqp_url = str_p("amqp:") >> prot_addr_list >> + !(str_p("/") >> !parameters); + prot_addr_list = prot_addr % ','; + prot_addr = tcp_prot_addr; // Extend for TLS etc. + + // TCP addresses + tcp_prot_addr = tcp_id >> tcp_addr[push_back_a(self.addr, tcp)]; + tcp_id = !str_p("tcp:"); + tcp_addr = !(host[assign_a(tcp.host)] >> !(':' >> port)); + + // See http://www.apps.ietf.org/rfc/rfc3986.html#sec-A + // for real host grammar. Shortcut: + port = uint_parser<uint16_t>()[assign_a(tcp.port)]; + host = *( unreserved | pct_encoded ); + unreserved = alnum_p | ch_in_p("-._~"); + pct_encoded = "%" >> xdigit_p >> xdigit_p; + parameters = *anychar_p >> end_p; // Ignore, not used yet. + } + + const rule<ScannerT>& start() const { return first; } + + rule<ScannerT> first, amqp_url, prot_addr_list, prot_addr, + tcp_prot_addr, tcp_id, tcp_addr, host, port, + unreserved, pct_encoded, parameters; + }; +}; + +void Url::parse(const char* url) { + cache.clear(); + if (!boost::spirit::parse(url, UrlGrammar(*this)).full) + throw InvalidUrl(string("Invalid AMQP url: ")+url); +} + +void Url::parseNoThrow(const char* url) { + cache.clear(); + if (!boost::spirit::parse(url, UrlGrammar(*this)).full) + clear(); +} + +void Url::throwIfEmpty() const { + throw InvalidUrl("URL contains no addresses"); +} + +std::istream& operator>>(std::istream& is, Url& url) { + std::string s; + is >> s; + url.parse(s); + return is; +} + +} // namespace qpid diff --git a/qpid/cpp/src/qpid/Url.h b/qpid/cpp/src/qpid/Url.h new file mode 100644 index 0000000000..20f42db0ad --- /dev/null +++ b/qpid/cpp/src/qpid/Url.h @@ -0,0 +1,109 @@ +#ifndef QPID_URL_H +#define QPID_URL_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "qpid/Exception.h" +#include <boost/variant.hpp> +#include <string> +#include <vector> +#include <new> +#include <ostream> + +namespace qpid { + +/** TCP address of a broker - host:port */ +struct TcpAddress { + static const uint16_t DEFAULT_PORT=5672; + explicit TcpAddress(const std::string& host_=std::string(), + uint16_t port_=DEFAULT_PORT) + : host(host_), port(port_) {} + std::string host; + uint16_t port; +}; + +inline bool operator==(const TcpAddress& x, const TcpAddress& y) { + return y.host==x.host && y.port == x.port; +} + +std::ostream& operator<<(std::ostream& os, const TcpAddress& a); + +/** Address is a variant of all address types, more coming in future. */ +typedef boost::variant<TcpAddress> Address; + +/** An AMQP URL contains a list of addresses */ +struct Url : public std::vector<Address> { + + /** Url with the hostname as returned by gethostname(2) */ + static Url getHostNameUrl(uint16_t port); + + /** Url with local IP address(es), may be more than one address + * on a multi-homed host. */ + static Url getIpAddressesUrl(uint16_t port); + + struct InvalidUrl : public Exception { + InvalidUrl(const std::string& s) : Exception(s) {} + }; + + /** Convert to string form. */ + std::string str() const; + + /** Empty URL. */ + Url() {} + + /** URL containing a single address */ + explicit Url(const Address& addr) { push_back(addr); } + + /** Parse url, throw InvalidUrl if invalid. */ + explicit Url(const std::string& url) { parse(url.c_str()); } + + /** Parse url, throw InvalidUrl if invalid. */ + explicit Url(const char* url) { parse(url); } + + template<class T> Url& operator=(T s) { parse(s); return *this; } + + /** Throw InvalidUrl if the URL does not contain any addresses. */ + void throwIfEmpty() const; + + /** Replace contents with parsed URL as defined in + * https://wiki.108.redhat.com/jira/browse/AMQP-95 + *@exception InvalidUrl if the url is invalid. + */ + void parse(const char* url); + void parse(const std::string& url) { parse(url.c_str()); } + + /** Replace contesnts with parsed URL as defined in + * https://wiki.108.redhat.com/jira/browse/AMQP-95 + * url.empty() will be true if url is invalid. + */ + void parseNoThrow(const char* url); + + private: + mutable std::string cache; // cache string form for efficiency. +}; + +inline bool operator==(const Url& a, const Url& b) { return a.str()==b.str(); } +inline bool operator!=(const Url& a, const Url& b) { return a.str()!=b.str(); } + +std::ostream& operator<<(std::ostream& os, const Url& url); +std::istream& operator>>(std::istream& is, Url& url); + +} // namespace qpid + +#endif /*!QPID_URL_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/Array.cpp b/qpid/cpp/src/qpid/amqp_0_10/Array.cpp new file mode 100644 index 0000000000..380e0f1f36 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Array.cpp @@ -0,0 +1,34 @@ +/* + * + * 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. + * + */ +#include "Array.h" + +namespace qpid { +namespace amqp_0_10 { + +std::ostream& operator<<(std::ostream& o, const Array& a) { + std::ostream_iterator<UnknownType> i(o, " "); + o << "Array<" << typeName(a.getType()) << "["; + std::copy(a.begin(), a.end(), i); + o << "]"; + return o; +} + +}} // namespace qpid::amqp_0_10 diff --git a/qpid/cpp/src/qpid/amqp_0_10/Array.h b/qpid/cpp/src/qpid/amqp_0_10/Array.h new file mode 100644 index 0000000000..8061a99b43 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Array.h @@ -0,0 +1,120 @@ +#ifndef QPID_AMQP_0_10_ARRAY_H +#define QPID_AMQP_0_10_ARRAY_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. + * + */ + +#include "qpid/amqp_0_10/TypeForCode.h" +#include "qpid/amqp_0_10/CodeForType.h" +#include "qpid/amqp_0_10/UnknownType.h" +#include "qpid/amqp_0_10/exceptions.h" +#include "qpid/amqp_0_10/Codec.h" +#include <vector> +#include <ostream> + +namespace qpid { +namespace amqp_0_10 { + +template <class T> class ArrayDomain : public std::vector<T> { + public: + template <class S> void serialize(S& s) { s.split(*this); s(this->begin(), this->end()); } + + template <class S> void encode(S& s) const { + s(contentSize())(CodeForType<T>::value)(uint32_t(this->size())); + } + + void encode(Codec::Size& s) const { s.raw(0, contentSize() + 4/*size*/); } + + template <class S> void decode(S& s) { + uint32_t size; uint8_t type; uint32_t count; + s(size); + s.setLimit(size); + s(type); + if (type != CodeForType<T>::value) + throw InvalidArgumentException(QPID_MSG("Array domain expected type " << CodeForType<T>::value << " but found " << type)); + s(count); + this->resize(count); + } + + private: + uint32_t contentSize() const { + return Codec::size(this->begin(), this->end()) + sizeof(uint32_t) /*count*/ + sizeof(uint8_t) /*type*/; + } +}; + +template <class T> +std::ostream& operator<<(std::ostream& o, const ArrayDomain<T>& ad) { + std::ostream_iterator<T> i(o, " "); + o << "Array<" << typeName(CodeForType<T>::value) << ">["; + std::copy(ad.begin(), ad.end(), i); + o << "]"; + return o; +} + +/** A non-domain array is represented as and array of UnknownType. + * Special case templat. + */ +template<> class ArrayDomain<UnknownType> : public std::vector<UnknownType> { + public: + ArrayDomain(uint8_t type_=0) : type(type_) {} + + template <class S> void serialize(S& s) { s.split(*this); s(this->begin(), this->end()); } + + template <class S> void encode(S& s) const { + s(contentSize())(type)(uint32_t(this->size())); + } + + void encode(Codec::Size& s) const { s.raw(0, contentSize() + 4/*size*/); } + + template <class S> void decode(S& s) { + uint32_t size; uint32_t count; + s(size); + s.setLimit(size); + s(type)(count); + this->clear(); + this->resize(count, UnknownType(type)); + } + + uint8_t getType() const { return type; } + + private: + uint32_t contentSize() const { + return Codec::size(this->begin(), this->end()) + sizeof(uint32_t) /*count*/ + sizeof(uint8_t) /*type*/; + } + uint8_t type; +}; + +std::ostream& operator<<(std::ostream& o, const Array& a); + +// FIXME aconway 2008-04-08: hack to supress encoding of +// command-fragments and in-doubt as there is a problem with the spec +// (command-fragments does not have a one byte type code.) +namespace session { class CommandFragment; } +namespace dtx { class Xid; } + +template <> struct ArrayDomain<session::CommandFragment> : public Void {}; +template <> struct ArrayDomain<dtx::Xid> : public Void {}; +inline std::ostream& operator<<(std::ostream& o, const ArrayDomain<session::CommandFragment>&) { return o; } +inline std::ostream& operator<<(std::ostream& o, const ArrayDomain<dtx::Xid>&) { return o; } + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_ARRAY_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/Body.h b/qpid/cpp/src/qpid/amqp_0_10/Body.h new file mode 100644 index 0000000000..c96931551c --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Body.h @@ -0,0 +1,55 @@ +#ifndef QPID_AMQP_0_10_BODY_H +#define QPID_AMQP_0_10_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. + * + */ +#include <string> +#include <ostream> + +namespace qpid { +namespace amqp_0_10 { + +/** Holds data from a body frame. */ +class Body { + public: + Body() {} + Body(size_t size_) : str(size_, '\0') {} + Body(const char* data_, size_t size_) : str(data_, size_) {} + + size_t size() const { return str.size(); }; + const char* data() const { return str.data(); } + char* data() { return const_cast<char*>(str.data()); } + + template <class S> void serialize(S& s) { s.raw(data(), size()); } + + private: + std::string str; + + friend std::ostream& operator<<(std::ostream&, const Body&); +}; + +inline std::ostream& operator<<(std::ostream& o, const Body& b) { + return o << b.str.substr(0, 16) << "... (" << b.size() << ")"; +} + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_BODY_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/Codec.h b/qpid/cpp/src/qpid/amqp_0_10/Codec.h new file mode 100644 index 0000000000..5cad5cf4ed --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Codec.h @@ -0,0 +1,213 @@ +#ifndef QPID_AMQP_0_10_CODEC_H +#define QPID_AMQP_0_10_CODEC_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. + * + */ + +#include "built_in_types.h" +#include "qpid/Serializer.h" +#include <boost/type_traits/is_integral.hpp> +#include <boost/type_traits/is_float.hpp> +#include <boost/type_traits/is_arithmetic.hpp> +#include <boost/detail/endian.hpp> +#include <boost/static_assert.hpp> +#include <iterator> + +namespace qpid { +namespace amqp_0_10 { + +template <class T> void reverse(T& t) { + char*p =reinterpret_cast<char*>(&t); + std::reverse(p, p+sizeof(T)); +} + +#ifdef BOOST_LITTLE_ENDIAN +template <class T> void bigEndian(T& t) { reverse(t); } +template <class T> void littleEndian(T&) {} +#else +template <class T> void littleEndian(T& t) { reverse(t); } +template <class T> void bigEndian(T&) {} +#endif + +/** + * AMQP 0-10 encoding and decoding. + */ +struct Codec { + /** Encode to an output byte iterator */ + template <class OutIter> + class Encoder : public EncoderBase<Encoder<OutIter> > + { + public: + typedef EncoderBase<Encoder<OutIter> > Base; + typedef OutIter Iterator; + + Encoder(OutIter o, size_t limit=Base::maxLimit()) : out(o) { + this->setLimit(limit); + } + + using EncoderBase<Encoder<OutIter> >::operator(); + + Encoder& operator()(bool x) { raw(x); return *this;} + Encoder& operator()(char x) { raw(x); return *this; } + Encoder& operator()(int8_t x) { raw(x); return *this; } + Encoder& operator()(uint8_t x) { raw(x); return *this; } + + Encoder& operator()(int16_t x) { return networkByteOrder(x); } + Encoder& operator()(int32_t x) { return networkByteOrder(x); } + Encoder& operator()(int64_t x) { return networkByteOrder(x); } + + Encoder& operator()(uint16_t x) { return networkByteOrder(x); } + Encoder& operator()(uint32_t x) { return networkByteOrder(x); } + Encoder& operator()(uint64_t x) { return networkByteOrder(x); } + + Encoder& operator()(float x) { return networkByteOrder(x); } + Encoder& operator()(double x) { return networkByteOrder(x); } + + void raw(const void* p, size_t n) { + this->addBytes(n); + out = std::copy((const char*)p, (const char*)p+n, out); + } + + void raw(char b) { this->addBytes(1); *out++=b; } + + template <class T> Encoder& littleEnd(T x) { + littleEndian(x); raw(&x, sizeof(x)); return *this; + } + + OutIter pos() const { return out; } + + private: + + template <class T> Encoder& networkByteOrder(T x) { + bigEndian(x); raw(&x, sizeof(x)); return *this; + } + + OutIter out; + }; + + template <class InIter> + class Decoder : public DecoderBase<Decoder<InIter> > { + public: + typedef DecoderBase<Decoder<InIter> > Base; + typedef InIter Iterator; + + Decoder(InIter i, size_t limit=Base::maxLimit()) : in(i) { + this->setLimit(limit); + } + + using DecoderBase<Decoder<InIter> >::operator(); + + // FIXME aconway 2008-03-10: wrong encoding, need packing support + Decoder& operator()(bool& x) { raw((char&)x); return *this; } + + Decoder& operator()(char& x) { raw((char&)x); return *this; } + Decoder& operator()(int8_t& x) { raw((char&)x); return *this; } + Decoder& operator()(uint8_t& x) { raw((char&)x); return *this; } + + Decoder& operator()(int16_t& x) { return networkByteOrder(x); } + Decoder& operator()(int32_t& x) { return networkByteOrder(x); } + Decoder& operator()(int64_t& x) { return networkByteOrder(x); } + + Decoder& operator()(uint16_t& x) { return networkByteOrder(x); } + Decoder& operator()(uint32_t& x) { return networkByteOrder(x); } + Decoder& operator()(uint64_t& x) { return networkByteOrder(x); } + + Decoder& operator()(float& x) { return networkByteOrder(x); } + Decoder& operator()(double& x) { return networkByteOrder(x); } + + void raw(void *p, size_t n) { + this->addBytes(n); + std::copy(in, in+n, (char*)p); + std::advance(in, n); + } + + void raw(char &b) { this->addBytes(1); b=*in++; } + + template <class T> Decoder& littleEnd(T& x) { + raw(&x, sizeof(x)); littleEndian(x); return *this; + } + + InIter pos() const { return in; } + + private: + + template <class T> Decoder& networkByteOrder(T& x) { + raw(&x, sizeof(x)); bigEndian(x); return *this; + } + + InIter in; + }; + + + class Size : public EncoderBase<Size> { + public: + Size() : size(0) {} + + operator size_t() const { return size; } + + using EncoderBase<Size>::operator(); + + // FIXME aconway 2008-03-10: wrong encoding, need packing support + Size& operator()(bool x) { size += sizeof(x); return *this; } + + Size& operator()(char x) { size += sizeof(x); return *this; } + Size& operator()(int8_t x) { size += sizeof(x); return *this; } + Size& operator()(uint8_t x) { size += sizeof(x); return *this; } + + Size& operator()(int16_t x) { size += sizeof(x); return *this; } + Size& operator()(int32_t x) { size += sizeof(x); return *this; } + Size& operator()(int64_t x) { size += sizeof(x); return *this; } + + Size& operator()(uint16_t x) { size += sizeof(x); return *this; } + Size& operator()(uint32_t x) { size += sizeof(x); return *this; } + Size& operator()(uint64_t x) { size += sizeof(x); return *this; } + + Size& operator()(float x) { size += sizeof(x); return *this; } + Size& operator()(double x) { size += sizeof(x); return *this; } + + // FIXME aconway 2008-04-03: optimize op()(Iter,Iter) + // for Iter with fixed-size value_type: + // distance(begin,end)*sizeof(value_type) + + void raw(const void*, size_t n){ size += n; } + + template <class T> Size& littleEnd(T) { size+= sizeof(T); return *this; } + + private: + size_t size; + }; + + // FIXME aconway 2008-03-11: rename to encoder(), decoder() + template <class InIter> static Decoder<InIter> decode(const InIter &i) { + return Decoder<InIter>(i); + } + + template <class OutIter> static Encoder<OutIter> encode(OutIter i) { + return Encoder<OutIter>(i); + } + + template <class T> static size_t size(const T& x) { return Size()(x); } + template <class Iter> static size_t size(const Iter& a, const Iter& z) { return Size()(a,z); } +}; + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_CODEC_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/Connection.cpp b/qpid/cpp/src/qpid/amqp_0_10/Connection.cpp new file mode 100644 index 0000000000..c5315ccf4c --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Connection.cpp @@ -0,0 +1,99 @@ +/* + * + * 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. + * + */ +#include "Connection.h" +#include "qpid/log/Statement.h" +#include "qpid/amqp_0_10/exceptions.h" + +namespace qpid { +namespace amqp_0_10 { + +using sys::Mutex; + +Connection::Connection(sys::OutputControl& o, broker::Broker& broker, const std::string& id) + : frameQueueClosed(false), output(o), connection(this, broker, id), + identifier(id), initialized(false) {} + +size_t Connection::decode(const char* buffer, size_t size) { + framing::Buffer in(const_cast<char*>(buffer), size); + framing::AMQFrame frame; + while(frame.decode(in)) { + QPID_LOG(trace, "RECV [" << identifier << "]: " << frame); + connection.received(frame); + } + return in.getPosition(); +} + +bool Connection::canEncode() { + if (!frameQueueClosed) connection.doOutput(); + Mutex::ScopedLock l(frameQueueLock); + return !initialized || !frameQueue.empty(); +} + +bool Connection::isClosed() const { + Mutex::ScopedLock l(frameQueueLock); + return frameQueueClosed; +} + +size_t Connection::encode(const char* buffer, size_t size) { + Mutex::ScopedLock l(frameQueueLock); + framing::Buffer out(const_cast<char*>(buffer), size); + if (!initialized) { + framing::ProtocolInitiation pi(getVersion()); + pi.encode(out); + initialized = true; + } + while (!frameQueue.empty() && (frameQueue.front().size() <= out.available())) { + frameQueue.front().encode(out); + QPID_LOG(trace, "SENT [" << identifier << "]: " << frameQueue.front()); + frameQueue.pop(); + } + assert(frameQueue.empty() || frameQueue.front().size() <= size); + if (!frameQueue.empty() && frameQueue.front().size() > size) + throw InternalErrorException(QPID_MSG("Could not write frame, too large for buffer.")); + return out.getPosition(); +} + +void Connection::activateOutput() { output.activateOutput(); } + +void Connection::close() { + // Close the output queue. + Mutex::ScopedLock l(frameQueueLock); + frameQueueClosed = true; +} + +void Connection::closed() { + connection.closed(); +} + +void Connection::send(framing::AMQFrame& f) { + { + Mutex::ScopedLock l(frameQueueLock); + if (!frameQueueClosed) + frameQueue.push(f); + } + activateOutput(); +} + +framing::ProtocolVersion Connection::getVersion() const { + return framing::ProtocolVersion(0,10); +} + +}} // namespace qpid::amqp_0_10 diff --git a/qpid/cpp/src/qpid/amqp_0_10/Connection.h b/qpid/cpp/src/qpid/amqp_0_10/Connection.h new file mode 100644 index 0000000000..e4672be722 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Connection.h @@ -0,0 +1,62 @@ +#ifndef QPID_BROKER_CONNECTION_H +#define QPID_BROKER_CONNECTION_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. + * + */ +#include "qpid/sys/ConnectionCodec.h" +#include "qpid/sys/ConnectionOutputHandler.h" +#include "qpid/sys/Mutex.h" +#include "Connection.h" +#include "qpid/broker/Connection.h" +#include <queue> + +namespace qpid { +namespace broker { class Broker; } +namespace amqp_0_10 { + +// FIXME aconway 2008-03-18: Update to 0-10. +class Connection : public sys::ConnectionCodec, + public sys::ConnectionOutputHandler +{ + std::queue<framing::AMQFrame> frameQueue; + bool frameQueueClosed; + mutable sys::Mutex frameQueueLock; + sys::OutputControl& output; + broker::Connection connection; // FIXME aconway 2008-03-18: + std::string identifier; + bool initialized; + + public: + Connection(sys::OutputControl&, broker::Broker&, const std::string& id); + size_t decode(const char* buffer, size_t size); + size_t encode(const char* buffer, size_t size); + bool isClosed() const; + bool canEncode(); + void activateOutput(); + void closed(); // connection closed by peer. + void close(); // closing from this end. + void send(framing::AMQFrame&); + framing::ProtocolVersion getVersion() const; +}; + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_BROKER_CONNECTION_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/Decimal.h b/qpid/cpp/src/qpid/amqp_0_10/Decimal.h new file mode 100644 index 0000000000..50fc457c76 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Decimal.h @@ -0,0 +1,51 @@ +#ifndef TESTS_DECIMAL_H +#define TESTS_DECIMAL_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. + * + */ +#include <ostream> + +namespace qpid { +namespace amqp_0_10 { + +template <class E, class M> struct Decimal { + E exponent; + M mantissa; + + Decimal(E exp=0, M man=0) : exponent(exp), mantissa(man) {} + + bool operator==(const Decimal& d) const { + return exponent == d.exponent && mantissa == d.mantissa; + } + + // TODO aconway 2008-02-20: We could provide arithmetic operators + // if anybody really cares about this type. + + template <class S> void serialize(S& s) { s(exponent)(mantissa); } +}; + +template<class E, class M> +inline std::ostream& operator<<(std::ostream& o, const Decimal<E,M>& d) { + return o << "Decimal{" << d.mantissa << "/10^" << (int)d.exponent << "}"; +} +}} + +#endif /*!TESTS_DECIMAL_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/Exception.h b/qpid/cpp/src/qpid/amqp_0_10/Exception.h new file mode 100644 index 0000000000..4841d91215 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Exception.h @@ -0,0 +1,186 @@ +#ifndef QPID_AMQP_0_10_EXCEPTION_H +#define QPID_AMQP_0_10_EXCEPTION_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. + * + */ + +#include "qpid/Exception.h" +#include "qpid/amqp_0_10/specification_fwd.h" + +namespace qpid { +namespace amqp_0_10 { + +/** + * Raised when the connection is unexpectedly closed. Sessions with + * non-0 timeout may be available for re-attachment on another connection. + */ +struct ConnectionException : public qpid::ConnectionException { + // FIXME aconway 2008-04-04: Merge qpid::ConnectionException + // into this when the old code is removed. + typedef connection::CloseCode Code; + ConnectionException(Code c, const std::string m) + : qpid::ConnectionException(c,m), code(c) {} + Code code; +}; + +/** + * Raised when a session is unexpectedly detached for any reason, or + * if an attempt is made to use a session that is not attached. + */ +struct SessionException : public qpid::SessionException { + // FIXME aconway 2008-04-04: should not have a code at this level. + // Leave in place till old preview code is gone. + SessionException(int code, const std::string& msg) : qpid::SessionException(code, msg) {} +}; + +/** Raised when the state of a session has been destroyed */ +struct SessionDestroyedException : public SessionException { + // FIXME aconway 2008-04-04: should not have a code at this level. + // Leave in place till old preview code is gone. + SessionDestroyedException(int code, const std::string& msg) : SessionException(code, msg){} +}; + +/** Raised when a session is destroyed due to an execution.exception */ +struct SessionAbortedException : public SessionDestroyedException { + typedef execution::ErrorCode Code; + SessionAbortedException(Code c, const std::string m) + : SessionDestroyedException(c, m), code(c) {} + Code code; +}; + +/** + * Raised when a session with 0 timeout is unexpectedly detached + * and therefore expires and is destroyed. + */ +struct SessionExpiredException : public SessionDestroyedException { + typedef session::DetachCode Code; + SessionExpiredException(Code c, const std::string m) + : SessionDestroyedException(c, m), code(c) {} + Code code; +}; + +/** + * Raised when a session with non-0 timeout is unexpectedly detached + * or if an attempt is made to use a session that is not attached. + * + * The session is not necessarily destroyed, it may be possible to + * re-attach. + */ +struct SessionDetachedException : public SessionException { + typedef session::DetachCode Code; + SessionDetachedException(Code c, const std::string m) + : SessionException(c, m), code(c) {} + Code code; +}; + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_EXCEPTION_H*/ +#ifndef QPID_AMQP_0_10_EXCEPTION_H +#define QPID_AMQP_0_10_EXCEPTION_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. + * + */ + +#include "qpid/Exception.h" +#include "qpid/amqp_0_10/specification_fwd.h" + +namespace qpid { +namespace amqp_0_10 { + +/** + * Raised when the connection is unexpectedly closed. Sessions with + * non-0 timeout may be available for re-attachment on another connection. + */ +struct ConnectionException : public Exception { + typedef connection::CloseCode Code; + ConnectionException(Code c, const std::string m) + : Exception(m), code(c) {} + Code code; +}; + +/** + * Raised when a session is unexpectedly detached for any reason, or + * if an attempt is made to use a session that is not attached. + */ +struct SessionException : public Exception { + SessionException(const std::string& msg) : Exception(msg) {} +}; + +/** Raised when the state of a session has been destroyed */ +struct SessionDestroyedException : public SessionException { + SessionDestroyedException(const std::string& msg) : SessionException(msg){} +}; + +/** Raised when a session is destroyed due to an execution.exception */ +struct SessionAbortedException : public SessionDestroyedException { + typedef execution::ErrorCode Code; + SessionAbortedException(Code c, const std::string m) + : SessionDestroyedException(m), code(c) {} + Code code; +}; + +/** + * Raised when a session with 0 timeout is unexpectedly detached + * and therefore expires and is destroyed. + */ +struct SessionExpiredException : public SessionDestroyedException { + typedef session::DetachCode Code; + SessionExpiredException(Code c, const std::string m) + : SessionDestroyedException(m), code(c) {} + Code code; +}; + +/** + * Raised when a session with non-0 timeout is unexpectedly detached + * or if an attempt is made to use a session that is not attached. + * + * The session is not necessarily destroyed, it may be possible to + * re-attach. + */ +struct SessionDetachedException : public SessionException { + typedef session::DetachCode Code; + SessionDetachedException(Code c, const std::string m) + : SessionException(m), code(c) {} + Code code; +}; + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_EXCEPTION_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/Frame.cpp b/qpid/cpp/src/qpid/amqp_0_10/Frame.cpp new file mode 100644 index 0000000000..1140b6058d --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Frame.cpp @@ -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. + * + */ +#include "Frame.h" + +namespace qpid { +namespace amqp_0_10 { + +bool Frame::match(const Frame& x) { +} +}} // namespace qpid::amqp_0_10 diff --git a/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.cpp b/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.cpp new file mode 100644 index 0000000000..f1a59b9e27 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.cpp @@ -0,0 +1,50 @@ +/* + * + * 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. + * + */ +#include "FrameHeader.h" +#include <ios> +#include <iomanip> +#include <ostream> + +using namespace std; + +namespace qpid { +namespace amqp_0_10 { + +bool FrameHeader::operator==(const FrameHeader& x) const { + return flags == x.flags && + type == x.type && + size == x.size && + track == x.track && + channel == x.channel; +} + +std::ostream& operator<<(std::ostream& o, const FrameHeader& f) { + std::ios::fmtflags saveFlags = o.flags(); + return o << "Frame[" + << "flags=" << std::hex << std::showbase << int(f.getFlags()) << std::setiosflags(saveFlags) + << " type=" << f.getType() + << " size=" << f.getSize() + << " track=" << int(f.getTrack()) + << " channel=" << f.getChannel() + << "]"; +} + +}} // namespace qpid::amqp_0_10 diff --git a/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.h b/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.h new file mode 100644 index 0000000000..b2f0619f9b --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/FrameHeader.h @@ -0,0 +1,90 @@ +#ifndef QPID_AMQP_0_10_FRAMEHEADER_H +#define QPID_AMQP_0_10_FRAMEHEADER_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. + * + */ + +#include "qpid/amqp_0_10/built_in_types.h" +#include <boost/shared_array.hpp> +#include <string.h> +#include <assert.h> +#include <iosfwd> + +namespace qpid { +namespace amqp_0_10 { + +enum FrameFlags { FIRST_SEGMENT=8, LAST_SEGMENT=4, FIRST_FRAME=2, LAST_FRAME=1 }; + +class FrameHeader { + public: + static const size_t SIZE=12; + static uint8_t trackFor(SegmentType type) { return type == 0 ? 0 : 1; } + + FrameHeader(uint8_t flags_=0, SegmentType type_=SegmentType(), uint16_t size_=0, uint8_t track_=0, uint16_t channel_=0) + : flags(flags_), type(type_), size(size_), track(track_), channel(channel_) + {} + + uint8_t getFlags() const { return flags; } + SegmentType getType() const { return type; } + /** @return size total size of of frame, including frame header. */ + uint16_t getSize() const { return size; } + /** @return size of frame data, excluding frame header. */ + uint16_t getDataSize() const { return size - SIZE; } + uint8_t getTrack() const { return track; } + uint16_t getChannel() const { return channel; } + + void setFlags(uint8_t flags_) { flags=flags_; } + /** Also sets the track. There is no setTrack() */ + void setType(SegmentType type_) { type=type_; track=trackFor(type); } + /** @param size total size of of frame, including frame header. */ + void setSize(uint16_t size_) { size = size_; } + /** @param size size of frame data, excluding frame header. */ + void setDataSize(uint16_t size_) { size = size_+SIZE; } + void setChannel(uint8_t channel_) { channel=channel_; } + + bool allFlags(uint8_t f) const { return (flags & f) == f; } + bool anyFlags(uint8_t f) const { return (flags & f); } + + void raiseFlags(uint8_t f) { flags |= f; } + void clearFlags(uint8_t f) { flags &= ~f; } + + bool isComplete() const { return allFlags(FIRST_FRAME | LAST_FRAME); } + + bool operator==(const FrameHeader&) const; + + template <class S> void serialize(S& s) { + uint8_t pad8=0; uint32_t pad32=0; + s(flags)(type)(size)(pad8)(track)(channel)(pad32); + } + + private: + uint8_t flags; + SegmentType type; + uint16_t size; + uint8_t track; + uint16_t channel; +}; + +std::ostream& operator<<(std::ostream&, const FrameHeader&); + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_FRAMEHEADER_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/Header.h b/qpid/cpp/src/qpid/amqp_0_10/Header.h new file mode 100644 index 0000000000..44edcb9f3d --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Header.h @@ -0,0 +1,42 @@ +#ifndef QPID_AMQP_0_10_HEADER_H +#define QPID_AMQP_0_10_HEADER_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. + * + */ +#include <ostream> + +namespace qpid { +namespace amqp_0_10 { + +// FIXME aconway 2008-03-27: TODO +class Header +{ + public: + template <class S> void serialize(S&) {} + private: +}; + +// FIXME aconway 2008-03-28: TODO +inline std::ostream& operator<<(std::ostream& o, const Header&) { return o; } + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_HEADER_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/Holder.h b/qpid/cpp/src/qpid/amqp_0_10/Holder.h new file mode 100644 index 0000000000..1664afcc8f --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Holder.h @@ -0,0 +1,95 @@ +#ifndef QPID_AMQP_0_10_HOLDER_H +#define QPID_AMQP_0_10_HOLDER_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. + * + */ +#include "qpid/framing/Blob.h" +#include "apply.h" + +namespace qpid { +namespace amqp_0_10 { + +using framing::in_place; + +template <class Invokable> struct InvokeVisitor { + typedef void result_type; + Invokable& target; + InvokeVisitor(Invokable& i) : target(i) {} + + template <class Action> + void operator()(const Action& action) { action.invoke(target); } +}; + +template <class DerivedHolder, class BaseHeld, size_t Size> +class Holder : public framing::Blob<Size, BaseHeld> { + typedef framing::Blob<Size, BaseHeld> Base; + + public: + + Holder() {} + template <class T> explicit Holder(const T& value) : Base(value) {} + + using Base::operator=; + Holder& operator=(const BaseHeld& rhs); + + uint8_t getCode() const { return this->get()->getCode(); } + uint8_t getClassCode() const { return this->get()->getClassCode(); } + + template <class Invokable> void invoke(Invokable& i) const { + InvokeVisitor<Invokable> v(i); + apply(v, *this->get()); + } + + template <class S> void encode(S& s) const { + s(getClassCode())(getCode()); + } + + template <class S> void decode(S& s) { + uint8_t code, classCode; + s(classCode)(code); + static_cast<DerivedHolder*>(this)->set(classCode, code); + } + + template <class S> void serialize(S& s) { + s.split(*this); + apply(s, *this->get()); + } + + private: + struct Assign : public ApplyFunctor<void> { + Holder& holder; + Assign(Holder& x) : holder(x) {} + template <class T> void operator()(const T& rhs) { holder=rhs; } + }; +}; + +template <class D, class B, size_t S> +Holder<D,B,S>& Holder<D,B,S>::operator=(const B& rhs) { + Assign assign(*this); + apply(assign, rhs); + return *this; +} + + + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_HOLDER_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/Map.cpp b/qpid/cpp/src/qpid/amqp_0_10/Map.cpp new file mode 100644 index 0000000000..2d32466c3f --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Map.cpp @@ -0,0 +1,64 @@ +/* + * + * 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. + * + */ +#include "all_built_in_types.h" +#include <ostream> + +namespace qpid { +namespace amqp_0_10 { + +MapValue::MapValue() : code(codeFor(uint8_t(0))), blob(in_place<uint8_t>(0)) {} + +MapValue::MapValue(const MapValue& x) : code(x.code), blob(x.blob) {} + +bool MapValue::operator==(const MapValue& x) const { + return code == x.code; // FIXME aconway 2008-04-01: incomplete +} + +struct OstreamVisitor : public MapValue::Visitor<std::ostream&> { + std::ostream& out; + OstreamVisitor(std::ostream& o) : out(o) {} + template <class T> std::ostream& operator()(const T& t) { + return out << t; + } +}; + +std::ostream& operator<<(std::ostream& o, const MapValue& m) { + o << typeName(m.getCode()) << ":"; + const_cast<MapValue&>(m).apply_visitor(OstreamVisitor(o)); + return o; +} + +std::ostream& operator<<(std::ostream& o, const Map::value_type& v) { + return o << v.first << "=" << v.second; +} +std::ostream& operator<<(std::ostream& o, const Map& map) { + o << "map["; + std::ostream_iterator<Map::value_type> i(o, " "); + std::copy(map.begin(), map.end(), i); + return o << "]"; +} + +uint32_t Map::contentSize() const { + // FIXME aconway 2008-04-03: preview to 0-10 mapping: +4 for count. + return /*4 +*/ Codec::Size()(begin(), end()); +} + +}} // namespace qpid::amqp_0_10 diff --git a/qpid/cpp/src/qpid/amqp_0_10/Map.h b/qpid/cpp/src/qpid/amqp_0_10/Map.h new file mode 100644 index 0000000000..d63eb0cc4e --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Map.h @@ -0,0 +1,184 @@ +#ifndef QPID_AMQP_0_10_MAP_H +#define QPID_AMQP_0_10_MAP_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 ang + * "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. + * + */ + +#include "qpid/Exception.h" +#include "qpid/amqp_0_10/built_in_types.h" +#include "qpid/amqp_0_10/UnknownType.h" +#include "qpid/amqp_0_10/CodeForType.h" +#include "qpid/amqp_0_10/TypeForCode.h" +#include "qpid/amqp_0_10/Codec.h" +#include "qpid/framing/Blob.h" +#include <map> +#include <string> +#include <iosfwd> + +namespace qpid { +namespace amqp_0_10 { + +class Map; + +class MapValue { + public: + struct BadTypeException : public Exception {}; + + template <class R> struct Visitor { typedef R result_type; }; + + MapValue(); + MapValue(const MapValue& x); + template <class T> explicit MapValue(const T& t); + template <class T> MapValue& operator=(const T& t); + + template <class T> T* get(); + template <class T> const T* get() const; + + template <class V> typename V::result_type apply_visitor(V&); + template <class V> typename V::result_type apply_visitor(const V&); + + uint8_t getCode() const { return code; } + + bool operator==(const MapValue&) const; + + template <class S> void serialize(S& s) { s(code); s.split(*this); } + template <class S> void encode(S& s) const { + const_cast<MapValue*>(this)->apply_visitor(s); + } + template <class S> void decode(S& s) { + DecodeVisitor<S> dv(blob, s); + qpid::amqp_0_10::apply_visitor(dv, code); + } + + + private: + static const size_t SIZE=128 < sizeof(Vbin32) ? sizeof(Vbin32) : 128; + typedef framing::Blob<SIZE> Blob; + + template <class V> struct VisitVisitor; + template <class T> struct GetVisitor; + template <class D> struct DecodeVisitor; + + uint8_t code; + Blob blob; +}; + +class Map : public std::map<Str8, MapValue> { + public: + template <class S> void serialize(S& s) { s.split(*this); } + template <class S> void encode(S& s) const; + // Shortcut calculation for size. + void encode(Codec::Size& s) const { s.raw(0, contentSize() + 4/*size*/); } + + template <class S> void decode(S& s); + + private: + uint32_t contentSize() const; +}; + +std::ostream& operator<<(std::ostream&, const MapValue&); +std::ostream& operator<<(std::ostream&, const Map::value_type&); +std::ostream& operator<<(std::ostream&, const Map&); + +using framing::in_place; + +template <class T> MapValue::MapValue(const T& t) : code(codeFor(t)), blob(in_place<t>()) {} + +template <class T> MapValue& MapValue::operator=(const T& t) { + code=codeFor(t); + blob=t; + return *this; +} + +template <class V> struct MapValue::VisitVisitor { + typedef typename V::result_type result_type; + V& visitor; + Blob& blob; + VisitVisitor(V& v, Blob& b) : visitor(v), blob(b) {} + + template <class T> result_type operator()(T*) { + return visitor(*reinterpret_cast<T*>(blob.get())); + } +}; + +template <class V> typename V::result_type MapValue::apply_visitor(V& v) { + VisitVisitor<V> visitor(v, blob); + return qpid::amqp_0_10::apply_visitor(visitor, code); +} + +template <class R> struct MapValue::GetVisitor { + typedef R* result_type; + const MapValue::Blob& blob; + + GetVisitor(const MapValue::Blob& b) : blob(b) {} + + R* operator()(R& r) { return &r; } + template <class T> R* operator()(T&) { return 0; } +}; + +template <class D> struct MapValue::DecodeVisitor { + typedef void result_type; + MapValue::Blob& blob; + D& decoder; + DecodeVisitor(Blob& b, D& d) : blob(b), decoder(d) {} + + template <class T> void operator()(T*) { + T t; + decoder(t); + blob = t; + } +}; + +template <class T> T* MapValue::get() { return apply_visitor(GetVisitor<T>(blob)); } +template <class T> const T* MapValue::get() const { return apply_visitor(GetVisitor<const T>()); } + +template <class V> typename V::result_type MapValue::apply_visitor(const V& v) { + return apply_visitor(const_cast<V&>(v)); +} + +template <class S> void Map::encode(S& s) const { + // FIXME aconway 2008-04-03: replace preview mapping with 0-10 mapping: + // s(contentSize())(uint32_t(size())); // size, count + s(contentSize()); + for (const_iterator i = begin(); i != end(); ++i) + s(i->first)(i->second); // key (type value) +} + +template <class S> void Map::decode(S& s) { + uint32_t decodedSize /*, count*/; + // FIXME aconway 2008-04-03: replace preview mapping with 0-10 mapping: + // s(contentSize())(uint32_t(size())); // size, count + // s(decodedSize)(count); + s(decodedSize); + typename S::ScopedLimit l(s, decodedSize); // Make sure we don't overrun. + // FIXME aconway 2008-04-03: replace preview with 0-10: + // for ( ; count > 0; --count) { + while (s.getLimit() > 0) { + key_type k; MapValue v; + s(k)(v); + insert(value_type(k,v)); + } +} + + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_MAP_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/Packer.h b/qpid/cpp/src/qpid/amqp_0_10/Packer.h new file mode 100644 index 0000000000..90d72408b5 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Packer.h @@ -0,0 +1,165 @@ +#ifndef QPID_PACKER_H +#define QPID_PACKER_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. + * + */ + +#include <boost/optional.hpp> +#include <boost/none.hpp> +#include "qpid/amqp_0_10/built_in_types.h" + +namespace qpid { +namespace amqp_0_10 { + +/** Serialization for optional values */ +template <class T> struct SerializableOptional { + boost::optional<T>& optional; + SerializableOptional(boost::optional<T>& x) : optional(x) {} + template <class S> void serialize(S& s) { + if (optional) + s(*optional); + } +}; + +}} + + +namespace boost { // For argument dependent lookup. + +template <class T> +qpid::amqp_0_10::SerializableOptional<T> serializable(boost::optional<T>& x) { + return qpid::amqp_0_10::SerializableOptional<T>(x); +} + +} // namespace boost + +namespace qpid { +namespace amqp_0_10 { + +/** "Encoder" that encodes a struct as a set of bit flags + * for all non-empty members. + */ +class PackBits { + public: + PackBits() : bit(1), bits(0) {} + + void setBit(bool b) { if (b) bits |= bit; bit <<= 1; } + uint32_t getBits() { return bits; } + + /** The bit is always set for non-optional values. */ + template <class T> + PackBits& operator()(const T&) { setBit(1); return *this; } + + /** For optional values the bit is set if the value is present. */ + template <class T> PackBits& operator()(const boost::optional<T>& opt) { + setBit(opt); return *this; + } + + /** Bits are special optional values */ + PackBits& operator()(Bit b) { setBit(b); return *this; } + + private: + uint32_t bit; + uint32_t bits; +}; + +/** Bit mask to encode a packable struct */ +template<class T> uint32_t packBits(const T& t) { + PackBits pack; + const_cast<T&>(t).serialize(pack); + return pack.getBits(); +} + +/** Decode members enabled by Bits */ +template <class Decoder, class Bits> +class PackedDecoder { + public: + PackedDecoder(Decoder& d, Bits b) : decode(d), bits(b) {} + + template <class T> PackedDecoder& operator()(T& t) { + if (bits & 1) + decode(t); + else + t = T(); + // FIXME aconway 2008-04-10: When we have all optionals + // represented by boost::optional the line above should be: + // throw CommandInvalidException("A required value was omitted."); + bits >>= 1; + return *this; + } + + template <class T> PackedDecoder& operator()(boost::optional<T>& opt) { + if (bits & 1) { + opt = T(); + decode(*opt); + } + else + opt = boost::none; + bits >>= 1; + return *this; + } + + private: + Decoder& decode; + Bits bits; +}; + +/** Metafunction to compute type to contain pack bits. */ +template <int PackBytes> struct PackBitsType; +template <> struct PackBitsType<1> { typedef uint8_t type; }; +template <> struct PackBitsType<2> { typedef uint16_t type; }; +template <> struct PackBitsType<4> { typedef uint32_t type; }; + +/** + * Helper to serialize packed structs. + */ +template <class T> class Packer +{ + public: + typedef typename PackBitsType<T::PACK>::type Bits; + + Packer(T& t) : data(t) {} + + template <class S> void serialize(S& s) { s.split(*this); } + + template <class S> void encode(S& s) const { + Bits bits = packBits(data); + s.littleEnd(bits); + data.serialize(s); + } + + template <class S> void decode(S& s) { + Bits bits; + s.littleEnd(bits); + PackedDecoder<S, Bits> decode(s, bits); + data.serialize(decode); + } + + + private: + T& data; +}; + +}} // namespace qpid::amqp_0_10 + + + +#endif /*!QPID_PACKER_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h b/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h new file mode 100644 index 0000000000..485b7ca6a8 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/SerializableString.h @@ -0,0 +1,62 @@ +#ifndef QPID_AMQP_0_10_SERIALIZABLESTRING_H +#define QPID_AMQP_0_10_SERIALIZABLESTRING_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. + * + */ +namespace qpid { +namespace amqp_0_10 { + +/** Template for length-prefixed strings/arrays. + * Unique parameter allows creation of distinct SerializableString + * types with the smae T/SizeType + */ +template <class T, class SizeType, int Unique=0> +struct SerializableString : public std::basic_string<T> { + SerializableString() {} + template <class U> SerializableString(const U& u) : std::basic_string<T>(u) {} + template <class I> SerializableString(const I& i, const I& j) : std::basic_string<T>(i,j) {} + + using std::basic_string<T>::operator=; + + template <class S> void serialize(S& s) { s.split(*this); } + + template <class S> void encode(S& s) const { + s(SizeType(this->size()))(this->begin(), this->end()); + } + + template <class S> void decode(S& s) { + SizeType newSize; + s(newSize); + this->resize(newSize); + s(this->begin(), this->end()); + } +}; + +// TODO aconway 2008-02-29: separate ostream ops +template <class T, class SizeType> +std::ostream& operator<<(std::ostream& o, const SerializableString<T,SizeType>& s) { + const std::basic_string<T> str(s); + return o << str.c_str(); // TODO aconway 2008-02-29: why doesn't o<<str work? +} + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_SERIALIZABLESTRING_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/Unit.cpp b/qpid/cpp/src/qpid/amqp_0_10/Unit.cpp new file mode 100644 index 0000000000..1fa6b2e085 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Unit.cpp @@ -0,0 +1,64 @@ +/* + * + * 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. + * + */ +#include "Unit.h" +#include "Codec.h" + +namespace qpid { +namespace amqp_0_10 { + +void Unit::updateVariant() { + switch (header.getType()) { + case CONTROL: variant=ControlHolder(); break; + case COMMAND: variant=CommandHolder(); + case HEADER: variant=Header(); + case BODY: variant=Body(header.getDataSize()); + } +} + +struct GetTypeVisitor : public boost::static_visitor<SegmentType> { + SegmentType operator()(const CommandHolder& ) const { return COMMAND; } + SegmentType operator()(const ControlHolder& ) const { return CONTROL; } + SegmentType operator()(const Header& ) const { return HEADER; } + SegmentType operator()(const Body&) const { return BODY; } +}; + +struct GetFlagsVisitor : public boost::static_visitor<uint8_t> { + uint8_t operator()(const CommandHolder& ) const { return FIRST_FRAME|LAST_FRAME|FIRST_SEGMENT; } + uint8_t operator()(const ControlHolder& ) const { return FIRST_FRAME|LAST_FRAME|FIRST_SEGMENT; } + uint8_t operator()(const Header& ) const { return FIRST_FRAME|LAST_FRAME; } + uint8_t operator()(const Body&) const { return 0; } +}; + +void Unit::updateHeader(uint8_t flags) { + GetFlagsVisitor flagger; + header.setFlags(flags | variant.apply_visitor(flagger)); + GetTypeVisitor getter; + header.setType(variant.apply_visitor(getter)); + header.setDataSize(Codec::size(*this)); + // track automatically set from type. + // no channel specified at this point. +} + +std::ostream& operator<<(std::ostream& o, const Unit& u) { + return o << u.getHeader() << " " << u.variant.type().name() << "[" << u.variant << "]"; +} + +}} // namespace qpid::amqp_0_10 diff --git a/qpid/cpp/src/qpid/amqp_0_10/Unit.h b/qpid/cpp/src/qpid/amqp_0_10/Unit.h new file mode 100644 index 0000000000..0229e07419 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/Unit.h @@ -0,0 +1,82 @@ +#ifndef QPID_AMQP_0_10_UNIT_H +#define QPID_AMQP_0_10_UNIT_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. + * + */ + +#include "qpid/amqp_0_10/ControlHolder.h" +#include "qpid/amqp_0_10/CommandHolder.h" +#include "qpid/amqp_0_10/Header.h" +#include "qpid/amqp_0_10/Body.h" +#include "qpid/amqp_0_10/FrameHeader.h" + +#include <boost/variant.hpp> +#include <ostream> + +namespace qpid { +namespace amqp_0_10 { + +/** + * A Unit contains a frame header and associated value. + * For all types except BODY the frame header is for a complete segment. + */ +class Unit { + public: + explicit Unit(const FrameHeader& h=FrameHeader()) : header(h) { updateVariant(); } + + /** + *@param flags: is ORed with the required flags for type T. + */ + template <class T> + explicit Unit(const T& t, uint8_t flags=0) : variant(t) { updateHeader(flags); } + + void setHeader(FrameHeader& h) { header = h; updateVariant(); } + const FrameHeader& getHeader() const { return header; } + + template<class T> const T* get() const { return boost::get<T>(&variant); } + template<class T> T* get() { return boost::get<T>(&variant); } + template<class T> Unit& operator=(const T& t) { variant=t; return *this; } + + template <class V> typename V::result_type applyVisitor(V& v) const { + variant.apply_visitor(v); + } + + template <class S> void serialize(S& s) { variant.apply_visitor(s); s.split(*this); } + template <class S> void encode(S&) const {} + template <class S> void decode(S&) { updateHeader(header.getFlags()); } + + private: + typedef boost::variant<ControlHolder, CommandHolder, Header, Body> Variant; + + void updateHeader(uint8_t flags); + void updateVariant(); + + Variant variant; + FrameHeader header; + + friend std::ostream& operator<<(std::ostream& o, const Unit& u); +}; + +std::ostream& operator<<(std::ostream& o, const Unit& u); + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_UNIT_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/UnitHandler.h b/qpid/cpp/src/qpid/amqp_0_10/UnitHandler.h new file mode 100644 index 0000000000..93a8ce573a --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/UnitHandler.h @@ -0,0 +1,35 @@ +#ifndef QPID_AMQP_0_10_UNITHANDLER_H +#define QPID_AMQP_0_10_UNITHANDLER_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. + * + */ + +#include "qpid/framing/Handler.h" + +namespace qpid { +namespace amqp_0_10 { + +class Unit; +typedef framing::Handler<const Unit&> UnitHandler; + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_UNITHANDLER_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp b/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp new file mode 100644 index 0000000000..844891d732 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/UnknownType.cpp @@ -0,0 +1,56 @@ +/* + * + * 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. + * + */ +#include "UnknownType.h" +#include <boost/range/iterator_range.hpp> +#include <ostream> + +namespace qpid { +namespace amqp_0_10 { + +UnknownType::Width UnknownType::WidthTable[16] = { + { 1, 0 }, + { 2, 0 }, + { 4, 0 }, + { 8, 0 }, + { 16, 0 }, + { 32, 0 }, + { 64, 0 }, + { 128, 0 }, + { 0, 1 }, + { 0, 2 }, + { 0, 4 }, + { -1, -1 }, // Invalid + { 5, 0 }, + { 9, 0 }, + { -1, -1 }, // Invalid + { 0, 0 } +}; + +int UnknownType::fixed() const { return WidthTable[code>>4].fixed; } +int UnknownType::variable() const { return WidthTable[code>>4].variable; } +UnknownType::UnknownType(uint8_t c) : code(c) { data.resize(fixed()); } + +std::ostream& operator<<(std::ostream& o, const UnknownType& u) { + return o << boost::make_iterator_range(u.begin(), u.end()) << std::endl; +} + +}} // namespace qpid::amqp_0_10 + diff --git a/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h b/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h new file mode 100644 index 0000000000..1e4aa04bf4 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/UnknownType.h @@ -0,0 +1,87 @@ +#ifndef QPID_AMQP_0_10_UNKNOWNTYPE_H +#define QPID_AMQP_0_10_UNKNOWNTYPE_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. + * + */ +#include <vector> +#include <iosfwd> +#include <stdint.h> + +namespace qpid { +namespace amqp_0_10 { + +/** Encode/decode an unknown type based on typecode. */ +class UnknownType { + public: + UnknownType(uint8_t code=0); + uint8_t getCode() const { return code; } + /** Size of fixed type or 0 if not fixed/0-length. -1 invalid */ + int fixed() const; + /** Bytes in size type for variable width. -1 invalid */ + int variable() const; + + typedef std::vector<char>::const_iterator const_iterator; + const_iterator begin() const { return data.begin(); } + const_iterator end() const { return data.end(); } + size_t size() const { return data.size(); } + + template <class S> void serialize(S& s) { s.split(*this); } + template <class S> void encode(S& s) const; + template <class S> void decode(S& s); + + private: + uint8_t code; + struct Width { int fixed; int variable; }; + static Width WidthTable[16]; + + std::vector<char> data; +}; + +template <class S> void UnknownType::encode(S& s) const { + switch (variable()) { + case 0: break; + case 1: s(uint8_t(data.size())); break; + case 2: s(uint16_t(data.size())); break; + case 4: s(uint32_t(data.size())); break; + } + s(data.begin(), data.end()); +} + +template <class S> void UnknownType::decode(S& s) { + uint32_t s8; + uint32_t s16; + uint32_t s32; + switch (variable()) { + case 0: break; + case 1: s(s8); data.resize(s8); break; + case 2: s(s16); data.resize(s16); break; + case 4: s(s32); data.resize(s32); break; + } + s(data.begin(), data.end()); +} + +inline uint8_t codeFor(const UnknownType& u) { return u.getCode(); } + +std::ostream& operator<<(std::ostream&, const UnknownType&); + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_UNKNOWNTYPE_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/all_built_in_types.h b/qpid/cpp/src/qpid/amqp_0_10/all_built_in_types.h new file mode 100644 index 0000000000..1568465004 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/all_built_in_types.h @@ -0,0 +1,31 @@ +#ifndef QPID_AMQP_0_10_ALL_BUILT_IN_TYPES_H +#define QPID_AMQP_0_10_ALL_BUILT_IN_TYPES_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. + * + */ + +#include "built_in_types.h" +#include "Map.h" +#include "Array.h" +#include "UnknownType.h" +#include "complex_types.h" + +#endif /*!QPID_AMQP_0_10_ALL_BUILT_IN_TYPES_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/apply.h b/qpid/cpp/src/qpid/amqp_0_10/apply.h new file mode 100644 index 0000000000..f32b3482ef --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/apply.h @@ -0,0 +1,86 @@ +#ifndef QPID_AMQP_0_10_APPLY_H +#define QPID_AMQP_0_10_APPLY_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. + * + */ +#include <boost/optional.hpp> + +namespace qpid { +namespace amqp_0_10 { + +template <class F, class R=typename F::result_type> struct FunctionAndResult { + F* functor; + boost::optional<R> result; + + FunctionAndResult() : functor(0) {} + template <class T> void invoke(T& t) { result=(*functor)(t); } + template <class T> void invoke(const T& t) { result=(*functor)(t); } + R getResult() { return *result; } +}; + +// void result is special case. +template <class F> struct FunctionAndResult<F, void> { + F* functor; + + FunctionAndResult() : functor(0) {} + template <class T> void invoke(T& t) { (*functor)(t); } + void getResult() {} +}; + +// Metafunction returning correct abstract visitor for Visitable type. +template <class Visitable> struct VisitorType { + typedef typename Visitable::Visitor type; +}; +template <class Visitable> struct VisitorType<const Visitable> { + typedef typename Visitable::ConstVisitor type; +}; + +template <class Visitor, class F> +struct ApplyVisitorBase : public Visitor, public FunctionAndResult<F> {}; + +// Specialize for each visitor type +template <class Visitable, class F> struct ApplyVisitor; + +/** Apply a functor to a visitable object. + * The functor can have operator() overloads for each visitable type + * and/or templated operator(). + */ +template <class F, class Visitable> +typename F::result_type apply(F& functor, Visitable& visitable) { + ApplyVisitor<typename VisitorType<Visitable>::type, F> visitor; + visitor.functor=&functor; + visitable.accept(visitor); + return visitor.getResult(); +} + +template <class F, class Visitable> +typename F::result_type apply(const F& functor, Visitable& visitable) { + ApplyVisitor<typename VisitorType<Visitable>::type, const F> visitor; + visitor.functor=&functor; + visitable.accept(visitor); + return visitor.getResult(); +} + +template <class R> struct ApplyFunctor { typedef R result_type; }; + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_APPLY_H*/ diff --git a/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h b/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h new file mode 100644 index 0000000000..dccb6a4785 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/built_in_types.h @@ -0,0 +1,165 @@ +#ifndef QPID_AMQP_0_10_BUILT_IN_TYPES_H +#define QPID_AMQP_0_10_BUILT_IN_TYPES_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. + * + */ + +#include "qpid/Serializer.h" +#include "qpid/framing/SequenceNumber.h" +#include "qpid/framing/Uuid.h" +#include "qpid/sys/Time.h" +#include "Decimal.h" +#include "SerializableString.h" +#include <boost/array.hpp> +#include <boost/range/iterator_range.hpp> +#include <string> +#include <ostream> +#include <vector> +#include <stdint.h> + +/**@file Mapping from built-in AMQP types to C++ types */ + +namespace qpid { +namespace amqp_0_10 { + +/** Wrapper that behaves like type T but is a distinct type for + * overloading purposes. Unique allows multiple distinc wrappers. + */ +template <class T, int Unique=0> struct Wrapper { + T value; + Wrapper() {} + Wrapper(const T& x) : value(x) {} + Wrapper& operator=(const T& x) { value=x; return *this; } + operator T&() { return value; } + operator const T&() const { return value; } + template <class S> void serialize(S& s) { s(value); } +}; + +template<class T> +inline std::ostream& operator<<(std::ostream& o, const Wrapper<T>& w) { + return o << w.value; +} + +/** Void type */ +struct Void { template <class S> void serialize(S&) {} }; +inline std::ostream& operator<<(std::ostream& o, const Void&) { return o; } + +/** Bit is a presence indicator - an optional value with no encoding. */ +struct Bit : public Wrapper<bool> { + Bit(bool b=false) : Wrapper<bool>(b) {} + using Wrapper<bool>::operator=; + template <class S> void serialize(S& s) { s.split(*this); } + template <class S> void encode(S&) const { } + template <class S> void decode(S&) { *this = true; } +}; + +inline std::ostream& operator<<(std::ostream& o, const Bit& b) { + return o << bool(b); +} + +// Fixed size types +typedef bool Boolean; +typedef char Char; +typedef int8_t Int8; +typedef int16_t Int16; +typedef int32_t Int32; +typedef int64_t Int64; +typedef uint8_t Uint8; +typedef uint16_t Uint16; +typedef uint32_t Uint32; +typedef uint64_t Uint64; +typedef Wrapper<uint32_t> CharUtf32; + +template <size_t N> struct Bin : public boost::array<char, N> { + template <class S> void serialize(S& s) { s.raw(this->begin(), this->size()); } +}; + +template <size_t N> std::ostream& operator<<(std::ostream& o, const Bin<N>& b) { + return o << boost::make_iterator_range(b.begin(), b.end()); +} + +template <> struct Bin<1> : public boost::array<char, 1> { + Bin(char c=0) { this->front() = c; } + operator char() { return this->front(); } + template <class S> void serialize(S& s) { s(front()); } +}; + +typedef Bin<1> Bin8; +typedef Bin<128> Bin1024; +typedef Bin<16> Bin128; +typedef Bin<2> Bin16; +typedef Bin<32> Bin256; +typedef Bin<4> Bin32; +typedef Bin<5> Bin40; +typedef Bin<64> Bin512; +typedef Bin<8> Bin64; +typedef Bin<9> Bin72; + +typedef double Double; +typedef float Float; +typedef framing::SequenceNumber SequenceNo; +using framing::Uuid; +typedef sys::AbsTime Datetime; + +typedef Decimal<Uint8, Int32> Dec32; +typedef Decimal<Uint8, Int64> Dec64; + +// Variable width types + +typedef SerializableString<Uint8, Uint8> Vbin8; +typedef SerializableString<char, Uint8, 1> Str8Latin; +typedef SerializableString<char, Uint8> Str8; +typedef SerializableString<Uint16, Uint8> Str8Utf16; + +typedef SerializableString<Uint8, Uint16> Vbin16; +typedef SerializableString<char, Uint16, 1> Str16Latin; +typedef SerializableString<char, Uint16> Str16; +typedef SerializableString<Uint16, Uint16> Str16Utf16; + +typedef SerializableString<Uint8, Uint32> Vbin32; + +// Forward declare class types. +class Map; +class UnknownType; +template <class T> struct ArrayDomain; +typedef ArrayDomain<UnknownType> Array; + +// FIXME aconway 2008-04-08: TODO +struct ByteRanges { template <class S> void serialize(S&) {} }; +struct SequenceSet { template <class S> void serialize(S&) {} }; +struct List { template <class S> void serialize(S&) {} }; +struct Struct32 { template <class S> void serialize(S&) {} }; + +// FIXME aconway 2008-03-10: dummy ostream operators +inline std::ostream& operator<<(std::ostream& o, const ByteRanges&) { return o; } +inline std::ostream& operator<<(std::ostream& o, const SequenceSet&) { return o; } +inline std::ostream& operator<<(std::ostream& o, const List&) { return o; } +inline std::ostream& operator<<(std::ostream& o, const Struct32&) { return o; } + +enum SegmentType { CONTROL, COMMAND, HEADER, BODY }; + +inline SerializeAs<SegmentType, uint8_t> serializable(SegmentType& st) { + return SerializeAs<SegmentType, uint8_t>(st); +} + + +}} // namespace qpid::amqp_0_10 + +#endif diff --git a/qpid/cpp/src/qpid/amqp_0_10/complex_types.cpp b/qpid/cpp/src/qpid/amqp_0_10/complex_types.cpp new file mode 100644 index 0000000000..78ddfeb026 --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/complex_types.cpp @@ -0,0 +1,73 @@ +/* + * + * 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. + * + */ + +#include "qpid/amqp_0_10/ApplyCommand.h" +#include "qpid/amqp_0_10/ApplyControl.h" +#include "qpid/amqp_0_10/ApplyStruct.h" +#include "qpid/amqp_0_10/apply.h" +#include <iostream> + +namespace qpid { +namespace amqp_0_10 { +// Functors for getting static values from a visitable base type. + +#define QPID_STATIC_VALUE_GETTER(NAME, TYPE, VALUE) \ + struct NAME : public ApplyFunctor<TYPE> { \ + template <class T> TYPE operator()(const T&) const { return T::VALUE; }\ + } + +QPID_STATIC_VALUE_GETTER(GetCode, uint8_t, CODE); +QPID_STATIC_VALUE_GETTER(GetSize, uint8_t, SIZE); +QPID_STATIC_VALUE_GETTER(GetPack, uint8_t, PACK); +QPID_STATIC_VALUE_GETTER(GetClassCode, uint8_t, CLASS_CODE); +QPID_STATIC_VALUE_GETTER(GetName, const char*, NAME); +QPID_STATIC_VALUE_GETTER(GetClassName, const char*, CLASS_NAME); + + +uint8_t Command::getCode() const { return apply(GetCode(), *this); } +uint8_t Command::getClassCode() const { return apply(GetClassCode(), *this); } +const char* Command::getName() const { return apply(GetName(), *this); } +const char* Command::getClassName() const { return apply(GetClassName(), *this); } + +uint8_t Control::getCode() const { return apply(GetCode(), *this); } +uint8_t Control::getClassCode() const { return apply(GetClassCode(), *this); } +const char* Control::getName() const { return apply(GetName(), *this); } +const char* Control::getClassName() const { return apply(GetClassName(), *this); } + + +uint8_t Struct::getCode() const { return apply(GetCode(), *this); } +uint8_t Struct::getPack() const { return apply(GetPack(), *this); } +uint8_t Struct::getSize() const { return apply(GetSize(), *this); } +uint8_t Struct::getClassCode() const { return apply(GetClassCode(), *this); } + +struct PrintVisitor { + typedef std::ostream& result_type; + std::ostream& out; + PrintVisitor(std::ostream& o) : out(o) {} + template <class T> result_type operator()(const T& t) const { return out << t; } +}; + +std::ostream& operator<<(std::ostream& o, const Command& x) { return apply(PrintVisitor(o), x); } +std::ostream& operator<<(std::ostream& o, const Control& x) { return apply(PrintVisitor(o), x); } +std::ostream& operator<<(std::ostream& o, const Struct& x) { return apply(PrintVisitor(o), x); } + +}} // namespace qpid::amqp_0_10 + diff --git a/qpid/cpp/src/qpid/amqp_0_10/complex_types.h b/qpid/cpp/src/qpid/amqp_0_10/complex_types.h new file mode 100644 index 0000000000..5d327cc46e --- /dev/null +++ b/qpid/cpp/src/qpid/amqp_0_10/complex_types.h @@ -0,0 +1,113 @@ +#ifndef QPID_AMQP_0_10_COMPLEX_TYPES_H +#define QPID_AMQP_0_10_COMPLEX_TYPES_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. + * + */ + +#include "built_in_types.h" +#include <iosfwd> + +namespace qpid { +namespace amqp_0_10 { + +// Base classes for complex types. + +template <class V, class CV, class H> struct Visitable { + typedef V Visitor; + typedef CV ConstVisitor; + typedef H Holder; + + virtual ~Visitable() {} + virtual void accept(Visitor&) = 0; + virtual void accept(ConstVisitor&) const = 0; +}; + +struct Command; +struct Control; + +struct Action { // Base for commands & controls + virtual ~Action() {} + virtual Command* getCommand() { return 0; } + virtual Control* getControl() { return 0; } + + virtual const Command* getCommand() const { + return const_cast<Action*>(this)->getCommand(); + } + virtual const Control* getControl() const { + return const_cast<Action*>(this)->getControl(); + } + static const uint8_t SIZE=0; + static const uint8_t PACK=2; +}; + +struct CommandVisitor; +struct ConstCommandVisitor; +struct CommandHolder; +struct Command + : public Action, + public Visitable<CommandVisitor, ConstCommandVisitor, CommandHolder> +{ + using Action::getCommand; + Command* getCommand() { return this; } + uint8_t getCode() const; + uint8_t getClassCode() const; + const char* getName() const; + const char* getClassName() const; +}; +std::ostream& operator<<(std::ostream&, const Command&); + +struct ControlVisitor; +struct ConstControlVisitor; +struct ControlHolder; +struct Control + : public Action, + public Visitable<ControlVisitor, ConstControlVisitor, ControlHolder> +{ + using Action::getControl; + Control* getControl() { return this; } + uint8_t getCode() const; + uint8_t getClassCode() const; + const char* getName() const; + const char* getClassName() const; +}; +std::ostream& operator<<(std::ostream&, const Control&); + +// Note: only coded structs inherit from Struct. +struct StructVisitor; +struct ConstStructVisitor; +struct StructHolder; +struct Struct + : public Visitable<StructVisitor, ConstStructVisitor, StructHolder> +{ + uint8_t getCode() const; + uint8_t getPack() const; + uint8_t getSize() const; + uint8_t getClassCode() const; +}; +std::ostream& operator<<(std::ostream&, const Struct&); + +template <SegmentType E> struct ActionType; +template <> struct ActionType<CONTROL> { typedef Control type; }; +template <> struct ActionType<COMMAND> { typedef Command type; }; + +}} // namespace qpid::amqp_0_10 + +#endif /*!QPID_AMQP_0_10_COMPLEX_TYPES_H*/ diff --git a/qpid/cpp/src/qpid/assert.cpp b/qpid/cpp/src/qpid/assert.cpp new file mode 100644 index 0000000000..5d039da528 --- /dev/null +++ b/qpid/cpp/src/qpid/assert.cpp @@ -0,0 +1,45 @@ +#ifndef QPID_ASSERT_CPP +#define QPID_ASSERT_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. + * + */ +#include <sstream> +#include <iostream> +#include "qpid/framing/reply_exceptions.h" +#include <stdlib.h> + +namespace qpid { + +void assert_fail(char const * expr, char const * function, char const * file, long line) { + std::ostringstream msg; + msg << "Internal error: " << expr << " in function " << function + << "(" << file << ":" << line << ")"; +#ifdef NDEBUG + throw framing::InternalErrorException(msg.str()); +#else + std::cerr << msg << std::endl; + abort(); +#endif +} + +} // namespace qpid + +#endif /*!QPID_ASSERT_CPP*/ diff --git a/qpid/cpp/src/qpid/assert.h b/qpid/cpp/src/qpid/assert.h new file mode 100644 index 0000000000..49e7c5355d --- /dev/null +++ b/qpid/cpp/src/qpid/assert.h @@ -0,0 +1,38 @@ +#ifndef QPID_ASSERT_H +#define QPID_ASSERT_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. + * + */ + +#include <boost/current_function.hpp> + +/** + * Abort if !expr in debug mode, throw an exception if NDEBUG is set. + */ +#define QPID_ASSERT(expr) ((expr) ? static_cast<void>(0) : ::qpid::assert_fail(#expr, BOOST_CURRENT_FUNCTION, __FILE__, __LINE__)) + +namespace qpid { + +void assert_fail(char const * expr, char const * function, char const * file, long line); + +} // namespace qpid + +#endif /*!QPID_ASSERT_H*/ diff --git a/qpid/cpp/src/qpid/broker/Bridge.cpp b/qpid/cpp/src/qpid/broker/Bridge.cpp new file mode 100644 index 0000000000..32819380de --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Bridge.cpp @@ -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. + * + */ +#include "Bridge.h" +#include "ConnectionState.h" + +#include "qpid/management/ManagementAgent.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/Uuid.h" + +using qpid::framing::FieldTable; +using qpid::framing::Uuid; + +namespace qpid { +namespace broker { + +Bridge::Bridge(framing::ChannelId id, ConnectionState& c, CancellationListener l, const management::ArgsLinkBridge& _args) : + args(_args), channel(id, &(c.getOutput())), peer(channel), + mgmtObject(new management::Bridge(this, &c, id, args.i_src, args.i_dest, args.i_key, args.i_src_is_queue, args.i_src_is_local)), + connection(c), listener(l) +{ + management::ManagementAgent::getAgent()->addObject(mgmtObject); +} + +Bridge::~Bridge() +{ + mgmtObject->resourceDestroy(); +} + +void Bridge::create() +{ + framing::AMQP_ServerProxy::Session session(channel); + session.open(0); + + if (args.i_src_is_local) { + //TODO: handle 'push' here... simplest way is to create frames and pass them to Connection::received() + } else { + if (args.i_src_is_queue) { + peer.getMessage().subscribe(0, args.i_src, args.i_dest, false, 0, 0, false, FieldTable()); + peer.getMessage().flow(args.i_dest, 0, 0xFFFFFFFF); + peer.getMessage().flow(args.i_dest, 1, 0xFFFFFFFF); + } else { + string queue = "bridge_queue_"; + queue += Uuid(true).str(); + peer.getQueue().declare(0, queue, "", false, false, true, true, FieldTable()); + peer.getQueue().bind(0, queue, args.i_src, args.i_key, FieldTable()); + peer.getMessage().subscribe(0, queue, args.i_dest, false, 0, 0, false, FieldTable()); + peer.getMessage().flow(args.i_dest, 0, 0xFFFFFFFF); + peer.getMessage().flow(args.i_dest, 1, 0xFFFFFFFF); + } + } + +} + +void Bridge::cancel() +{ + peer.getMessage().cancel(args.i_dest); + peer.getSession().close(); +} + +management::ManagementObject::shared_ptr Bridge::GetManagementObject (void) const +{ + return dynamic_pointer_cast<management::ManagementObject>(mgmtObject); +} + +management::Manageable::status_t Bridge::ManagementMethod(uint32_t methodId, management::Args& /*args*/) +{ + if (methodId == management::Bridge::METHOD_CLOSE) { + //notify that we are closed + listener(this); + //request time on the connections io thread + connection.getOutput().activateOutput(); + return management::Manageable::STATUS_OK; + } else { + return management::Manageable::STATUS_UNKNOWN_METHOD; + } +} + +}} diff --git a/qpid/cpp/src/qpid/broker/Bridge.h b/qpid/cpp/src/qpid/broker/Bridge.h new file mode 100644 index 0000000000..1198285c93 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Bridge.h @@ -0,0 +1,64 @@ +/* + * + * 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. + * + */ +#ifndef _Bridge_ +#define _Bridge_ + +#include "qpid/framing/AMQP_ServerProxy.h" +#include "qpid/framing/ChannelHandler.h" +#include "qpid/management/Manageable.h" +#include "qpid/management/ArgsLinkBridge.h" +#include "qpid/management/Bridge.h" + +#include <boost/function.hpp> + +namespace qpid { +namespace broker { + +class ConnectionState; + +class Bridge : public management::Manageable +{ +public: + typedef boost::function<void(Bridge*)> CancellationListener; + + Bridge(framing::ChannelId id, ConnectionState& c, CancellationListener l, + const management::ArgsLinkBridge& args); + ~Bridge(); + + void create(); + void cancel(); + + management::ManagementObject::shared_ptr GetManagementObject() const; + management::Manageable::status_t ManagementMethod(uint32_t methodId, management::Args& args); + +private: + management::ArgsLinkBridge args; + framing::ChannelHandler channel; + framing::AMQP_ServerProxy peer; + management::Bridge::shared_ptr mgmtObject; + ConnectionState& connection; + CancellationListener listener; +}; + + +}} + +#endif diff --git a/qpid/cpp/src/qpid/broker/Broker.cpp b/qpid/cpp/src/qpid/broker/Broker.cpp new file mode 100644 index 0000000000..b9268db9e5 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Broker.cpp @@ -0,0 +1,345 @@ +/* + * + * 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. + * + */ + +#include "config.h" +#include "Broker.h" +#include "Connection.h" +#include "DirectExchange.h" +#include "FanOutExchange.h" +#include "HeadersExchange.h" +#include "MessageStoreModule.h" +#include "NullMessageStore.h" +#include "RecoveryManagerImpl.h" +#include "TopicExchange.h" +#include "qpid/management/PackageQpid.h" +#include "qpid/management/ManagementExchange.h" +#include "qpid/management/ArgsBrokerEcho.h" + +#include "qpid/log/Statement.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/sys/Acceptor.h" +#include "qpid/sys/ConnectionInputHandler.h" +#include "qpid/sys/ConnectionInputHandlerFactory.h" +#include "qpid/sys/TimeoutHandler.h" +#include "qpid/sys/SystemInfo.h" +#include "qpid/Url.h" + +#include <boost/bind.hpp> + +#include <iostream> +#include <memory> + +#if HAVE_SASL +#include <sasl/sasl.h> +#endif + +using qpid::sys::Acceptor; +using qpid::framing::FrameHandler; +using qpid::framing::ChannelId; +using qpid::management::ManagementAgent; +using qpid::management::ManagementObject; +using qpid::management::Manageable; +using qpid::management::Args; +using qpid::management::ArgsBrokerEcho; + +namespace qpid { +namespace broker { + +Broker::Options::Options(const std::string& name) : + qpid::Options(name), + noDataDir(0), + dataDir("/var/lib/qpidd"), + port(DEFAULT_PORT), + workerThreads(5), + maxConnections(500), + connectionBacklog(10), + stagingThreshold(5000000), + enableMgmt(1), + mgmtPubInterval(10), +#if HAVE_SASL + //Authentication disabled by default for now to allow any + //scripts etc that might fail authentication to be updated. + //Note that this is a temporary measure (GS 14-APR-2008). + auth(false), + //auth(true), +#else + auth(false), +#endif + ack(0) +{ + int c = sys::SystemInfo::concurrency(); + workerThreads=c+1; + addOptions() + ("data-dir", optValue(dataDir,"DIR"), + "Directory to contain persistent data generated by the broker") + ("no-data-dir", optValue(noDataDir), + "Don't use a data directory. No persistent configuration will be loaded or stored") + ("port,p", optValue(port,"PORT"), + "Tells the broker to listen on PORT") + ("worker-threads", optValue(workerThreads, "N"), + "Sets the broker thread pool size") + ("max-connections", optValue(maxConnections, "N"), + "Sets the maximum allowed connections") + ("connection-backlog", optValue(connectionBacklog, "N"), + "Sets the connection backlog limit for the server socket") + ("staging-threshold", optValue(stagingThreshold, "N"), + "Stages messages over N bytes to disk") + ("mgmt-enable,m", optValue(enableMgmt,"yes|no"), + "Enable Management") + ("mgmt-pub-interval", optValue(mgmtPubInterval, "SECONDS"), + "Management Publish Interval") + ("auth", optValue(auth, "yes|no"), + "Enable authentication, if disabled all incoming connections will be trusted") + ("ack", optValue(ack, "N"), + "Send session.ack/solicit-ack at least every N frames. 0 disables voluntary ack/solitict-ack"); +} + +const std::string empty; +const std::string amq_direct("amq.direct"); +const std::string amq_topic("amq.topic"); +const std::string amq_fanout("amq.fanout"); +const std::string amq_match("amq.match"); +const std::string qpid_management("qpid.management"); + +Broker::Broker(const Broker::Options& conf) : + config(conf), + store(0), + dataDir(conf.noDataDir ? std::string () : conf.dataDir), + factory(*this), + sessionManager(conf.ack), + previewSessionManager(conf.ack) +{ + if(conf.enableMgmt){ + QPID_LOG(info, "Management enabled"); + ManagementAgent::enableManagement (dataDir.isEnabled () ? dataDir.getPath () : string (), + conf.mgmtPubInterval); + managementAgent = ManagementAgent::getAgent (); + managementAgent->setInterval (conf.mgmtPubInterval); + qpid::management::PackageQpid packageInitializer (managementAgent); + + System* system = new System (); + systemObject = System::shared_ptr (system); + + mgmtObject = management::Broker::shared_ptr (new management::Broker (this, system, conf.port)); + mgmtObject->set_workerThreads (conf.workerThreads); + mgmtObject->set_maxConns (conf.maxConnections); + mgmtObject->set_connBacklog (conf.connectionBacklog); + mgmtObject->set_stagingThreshold (conf.stagingThreshold); + mgmtObject->set_mgmtPubInterval (conf.mgmtPubInterval); + mgmtObject->set_version (PACKAGE_VERSION); + mgmtObject->set_dataDirEnabled (dataDir.isEnabled ()); + mgmtObject->set_dataDir (dataDir.getPath ()); + + managementAgent->addObject (mgmtObject, 1, 0); + + // Since there is currently no support for virtual hosts, a placeholder object + // representing the implied single virtual host is added here to keep the + // management schema correct. + Vhost* vhost = new Vhost (this); + vhostObject = Vhost::shared_ptr (vhost); + + queues.setParent (vhost); + exchanges.setParent (vhost); + } + + // Early-Initialize plugins + const Plugin::Plugins& plugins=Plugin::getPlugins(); + for (Plugin::Plugins::const_iterator i = plugins.begin(); + i != plugins.end(); + i++) + (*i)->earlyInitialize(*this); + + // If no plugin store module registered itself, set up the null store. + if (store == 0) + setStore (new NullMessageStore (false)); + + queues.setStore (store); + dtxManager.setStore (store); + + exchanges.declare(empty, DirectExchange::typeName); // Default exchange. + + if (store != 0) { + RecoveryManagerImpl recoverer(queues, exchanges, dtxManager, + conf.stagingThreshold); + store->recover(recoverer); + } + + //ensure standard exchanges exist (done after recovery from store) + declareStandardExchange(amq_direct, DirectExchange::typeName); + declareStandardExchange(amq_topic, TopicExchange::typeName); + declareStandardExchange(amq_fanout, FanOutExchange::typeName); + declareStandardExchange(amq_match, HeadersExchange::typeName); + + if(conf.enableMgmt) { + exchanges.declare(qpid_management, ManagementExchange::typeName); + Exchange::shared_ptr mExchange = exchanges.get (qpid_management); + Exchange::shared_ptr dExchange = exchanges.get (amq_direct); + managementAgent->setExchange (mExchange, dExchange); + dynamic_pointer_cast<ManagementExchange>(mExchange)->setManagmentAgent (managementAgent); + } + else + QPID_LOG(info, "Management not enabled"); + + /** + * SASL setup, can fail and terminate startup + */ + if (conf.auth) { +#if HAVE_SASL + int code = sasl_server_init(NULL, BROKER_SASL_NAME); + if (code != SASL_OK) { + // TODO: Figure out who owns the char* returned by + // sasl_errstring, though it probably does not matter much + throw Exception(sasl_errstring(code, NULL, NULL)); + } + QPID_LOG(info, "SASL enabled"); +#else + throw Exception("Requested authentication but SASL unavailable"); +#endif + } + + // Initialize plugins + for (Plugin::Plugins::const_iterator i = plugins.begin(); + i != plugins.end(); + i++) + (*i)->initialize(*this); +} + +void Broker::declareStandardExchange(const std::string& name, const std::string& type) +{ + bool storeEnabled = store != NULL; + std::pair<Exchange::shared_ptr, bool> status = exchanges.declare(name, type, storeEnabled); + if (status.second && storeEnabled) { + store->create(*status.first, framing::FieldTable ()); + } +} + + +shared_ptr<Broker> Broker::create(int16_t port) +{ + Options config; + config.port=port; + return create(config); +} + +shared_ptr<Broker> Broker::create(const Options& opts) +{ + return shared_ptr<Broker>(new Broker(opts)); +} + +void Broker::setStore (MessageStore* _store) +{ + assert (store == 0 && _store != 0); + if (store == 0 && _store != 0) + store = new MessageStoreModule (_store); +} + +void Broker::run() { + getAcceptor().run(&factory); +} + +void Broker::shutdown() { + // NB: this function must be async-signal safe, it must not + // call any function that is not async-signal safe. + // Any unsafe shutdown actions should be done in the destructor. + if (acceptor) + acceptor->shutdown(); +} + +Broker::~Broker() { + shutdown(); + ManagementAgent::shutdown (); + delete store; + if (config.auth) { +#if HAVE_SASL + sasl_done(); +#endif + } +} + +uint16_t Broker::getPort() const { return getAcceptor().getPort(); } + +Acceptor& Broker::getAcceptor() const { + if (!acceptor) { + const_cast<Acceptor::shared_ptr&>(acceptor) = + Acceptor::create(config.port, + config.connectionBacklog, + config.workerThreads); + QPID_LOG(info, "Listening on port " << getPort()); + } + return *acceptor; +} + +ManagementObject::shared_ptr Broker::GetManagementObject(void) const +{ + return dynamic_pointer_cast<ManagementObject> (mgmtObject); +} + +Manageable* Broker::GetVhostObject(void) const +{ + return vhostObject.get(); +} + +Manageable::status_t Broker::ManagementMethod (uint32_t methodId, + Args& args) +{ + Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; + + QPID_LOG (debug, "Broker::ManagementMethod [id=" << methodId << "]"); + + switch (methodId) + { + case management::Broker::METHOD_ECHO : + status = Manageable::STATUS_OK; + break; + case management::Broker::METHOD_CONNECT : { + management::ArgsBrokerConnect& hp= + dynamic_cast<management::ArgsBrokerConnect&>(args); + connect(hp.i_host, hp.i_port); + status = Manageable::STATUS_OK; + break; + } + case management::Broker::METHOD_JOINCLUSTER : + case management::Broker::METHOD_LEAVECLUSTER : + status = Manageable::STATUS_NOT_IMPLEMENTED; + break; + } + + return status; +} + +void Broker::connect( + const std::string& host, uint16_t port, + sys::ConnectionCodec::Factory* f) +{ + getAcceptor().connect(host, port, f ? f : &factory); +} + +void Broker::connect( + const Url& url, sys::ConnectionCodec::Factory* f) +{ + url.throwIfEmpty(); + TcpAddress addr=boost::get<TcpAddress>(url[0]); + connect(addr.host, addr.port, f); +} + +}} // namespace qpid::broker + diff --git a/qpid/cpp/src/qpid/broker/Broker.h b/qpid/cpp/src/qpid/broker/Broker.h new file mode 100644 index 0000000000..8d39c6005d --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Broker.h @@ -0,0 +1,157 @@ +#ifndef _Broker_ +#define _Broker_ + +/* + * + * 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. + * + */ + +#include "config.h" + +#include "ConnectionFactory.h" +#include "ConnectionToken.h" +#include "DirectExchange.h" +#include "DtxManager.h" +#include "ExchangeRegistry.h" +#include "MessageStore.h" +#include "QueueRegistry.h" +#include "SessionManager.h" +#include "PreviewSessionManager.h" +#include "Vhost.h" +#include "System.h" +#include "qpid/management/Manageable.h" +#include "qpid/management/ManagementAgent.h" +#include "qpid/management/Broker.h" +#include "qpid/management/ArgsBrokerConnect.h" +#include "qpid/Options.h" +#include "qpid/Plugin.h" +#include "qpid/DataDir.h" +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/OutputHandler.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/sys/Acceptor.h" +#include "qpid/sys/Runnable.h" + +#include <vector> + +namespace qpid { + +class Url; + +namespace broker { + +static const uint16_t DEFAULT_PORT=5672; + +/** + * A broker instance. + */ +class Broker : public sys::Runnable, public Plugin::Target, + public management::Manageable +{ + public: + + struct Options : public qpid::Options { + Options(const std::string& name="Broker Options"); + + bool noDataDir; + std::string dataDir; + uint16_t port; + int workerThreads; + int maxConnections; + int connectionBacklog; + uint64_t stagingThreshold; + bool enableMgmt; + uint16_t mgmtPubInterval; + bool auth; + uint32_t ack; + }; + + virtual ~Broker(); + + Broker(const Options& configuration); + static shared_ptr<Broker> create(const Options& configuration); + static shared_ptr<Broker> create(int16_t port = DEFAULT_PORT); + + /** + * Return listening port. If called before bind this is + * the configured port. If called after it is the actual + * port, which will be different if the configured port is + * 0. + */ + virtual uint16_t getPort() const; + + /** + * Run the broker. Implements Runnable::run() so the broker + * can be run in a separate thread. + */ + virtual void run(); + + /** Shut down the broker */ + virtual void shutdown(); + + void setStore (MessageStore*); + MessageStore& getStore() { return *store; } + QueueRegistry& getQueues() { return queues; } + ExchangeRegistry& getExchanges() { return exchanges; } + uint64_t getStagingThreshold() { return config.stagingThreshold; } + DtxManager& getDtxManager() { return dtxManager; } + DataDir& getDataDir() { return dataDir; } + Options& getOptions() { return config; } + + SessionManager& getSessionManager() { return sessionManager; } + PreviewSessionManager& getPreviewSessionManager() { return previewSessionManager; } + + management::ManagementObject::shared_ptr GetManagementObject (void) const; + management::Manageable* GetVhostObject (void) const; + management::Manageable::status_t + ManagementMethod (uint32_t methodId, management::Args& args); + + /** Create a connection to another broker. */ + void connect(const std::string& host, uint16_t port, + sys::ConnectionCodec::Factory* =0); + /** Create a connection to another broker. */ + void connect(const Url& url, sys::ConnectionCodec::Factory* =0); + + private: + sys::Acceptor& getAcceptor() const; + + Options config; + sys::Acceptor::shared_ptr acceptor; + MessageStore* store; + DataDir dataDir; + + QueueRegistry queues; + ExchangeRegistry exchanges; + ConnectionFactory factory; + DtxManager dtxManager; + SessionManager sessionManager; + PreviewSessionManager previewSessionManager; + management::ManagementAgent::shared_ptr managementAgent; + management::Broker::shared_ptr mgmtObject; + Vhost::shared_ptr vhostObject; + System::shared_ptr systemObject; + + void declareStandardExchange(const std::string& name, const std::string& type); +}; + +}} + + + +#endif /*!_Broker_*/ diff --git a/qpid/cpp/src/qpid/broker/BrokerAdapter.cpp b/qpid/cpp/src/qpid/broker/BrokerAdapter.cpp new file mode 100644 index 0000000000..ea964ef3a3 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/BrokerAdapter.cpp @@ -0,0 +1,353 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ +#include "BrokerAdapter.h" +#include "Connection.h" +#include "DeliveryToken.h" +#include "MessageDelivery.h" +#include "qpid/framing/AMQMethodBody.h" +#include "qpid/Exception.h" +#include "qpid/framing/reply_exceptions.h" + +namespace qpid { +namespace broker { + +using namespace qpid; +using namespace qpid::framing; + +typedef std::vector<Queue::shared_ptr> QueueVector; + +// TODO aconway 2007-08-31: now that functionality is distributed +// between different handlers, BrokerAdapter should be dropped. +// Instead the individual class Handler interfaces can be implemented +// by the handlers responsible for those classes. +// + +BrokerAdapter::BrokerAdapter(SemanticState& s) : + HandlerImpl(s), + basicHandler(s), + exchangeHandler(s), + bindingHandler(s), + messageHandler(s), + queueHandler(s), + txHandler(s), + dtxHandler(s) +{} + + +void BrokerAdapter::ExchangeHandlerImpl::declare(uint16_t /*ticket*/, const string& exchange, const string& type, + const string& alternateExchange, + bool passive, bool durable, bool /*autoDelete*/, const FieldTable& args){ + Exchange::shared_ptr alternate; + if (!alternateExchange.empty()) { + alternate = getBroker().getExchanges().get(alternateExchange); + } + if(passive){ + Exchange::shared_ptr actual(getBroker().getExchanges().get(exchange)); + checkType(actual, type); + checkAlternate(actual, alternate); + }else{ + try{ + std::pair<Exchange::shared_ptr, bool> response = getBroker().getExchanges().declare(exchange, type, durable, args); + if (response.second) { + if (durable) { + getBroker().getStore().create(*response.first, args); + } + if (alternate) { + response.first->setAlternate(alternate); + alternate->incAlternateUsers(); + } + } else { + checkType(response.first, type); + checkAlternate(response.first, alternate); + } + }catch(UnknownExchangeTypeException& e){ + throw CommandInvalidException(QPID_MSG("Exchange type not implemented: " << type)); + } + } +} + +void BrokerAdapter::ExchangeHandlerImpl::checkType(Exchange::shared_ptr exchange, const std::string& type) +{ + if (!type.empty() && exchange->getType() != type) { + throw NotAllowedException(QPID_MSG("Exchange declared to be of type " << exchange->getType() << ", requested " << type)); + } +} + +void BrokerAdapter::ExchangeHandlerImpl::checkAlternate(Exchange::shared_ptr exchange, Exchange::shared_ptr alternate) +{ + if (alternate && alternate != exchange->getAlternate()) + throw NotAllowedException( + QPID_MSG("Exchange declared with alternate-exchange " + << exchange->getAlternate()->getName() << ", requested " + << alternate->getName())); +} + +void BrokerAdapter::ExchangeHandlerImpl::delete_(uint16_t /*ticket*/, const string& name, bool /*ifUnused*/){ + //TODO: implement unused + Exchange::shared_ptr exchange(getBroker().getExchanges().get(name)); + if (exchange->inUseAsAlternate()) throw NotAllowedException(QPID_MSG("Exchange in use as alternate-exchange.")); + if (exchange->isDurable()) getBroker().getStore().destroy(*exchange); + if (exchange->getAlternate()) exchange->getAlternate()->decAlternateUsers(); + getBroker().getExchanges().destroy(name); +} + +ExchangeQueryResult BrokerAdapter::ExchangeHandlerImpl::query(u_int16_t /*ticket*/, const string& name) +{ + try { + Exchange::shared_ptr exchange(getBroker().getExchanges().get(name)); + return ExchangeQueryResult(exchange->getType(), exchange->isDurable(), false, exchange->getArgs()); + } catch (const ChannelException& e) { + return ExchangeQueryResult("", false, true, FieldTable()); + } +} + +BindingQueryResult BrokerAdapter::BindingHandlerImpl::query(u_int16_t /*ticket*/, + const std::string& exchangeName, + const std::string& queueName, + const std::string& key, + const framing::FieldTable& args) +{ + Exchange::shared_ptr exchange; + try { + exchange = getBroker().getExchanges().get(exchangeName); + } catch (const ChannelException&) {} + + Queue::shared_ptr queue; + if (!queueName.empty()) { + queue = getBroker().getQueues().find(queueName); + } + + if (!exchange) { + return BindingQueryResult(true, false, false, false, false); + } else if (!queueName.empty() && !queue) { + return BindingQueryResult(false, true, false, false, false); + } else if (exchange->isBound(queue, key.empty() ? 0 : &key, args.count() > 0 ? &args : &args)) { + return BindingQueryResult(false, false, false, false, false); + } else { + //need to test each specified option individually + bool queueMatched = queueName.empty() || exchange->isBound(queue, 0, 0); + bool keyMatched = key.empty() || exchange->isBound(Queue::shared_ptr(), &key, 0); + bool argsMatched = args.count() == 0 || exchange->isBound(Queue::shared_ptr(), 0, &args); + + return BindingQueryResult(false, false, !queueMatched, !keyMatched, !argsMatched); + } +} + +QueueQueryResult BrokerAdapter::QueueHandlerImpl::query(const string& name) +{ + Queue::shared_ptr queue = state.getQueue(name); + Exchange::shared_ptr alternateExchange = queue->getAlternateExchange(); + + return QueueQueryResult(queue->getName(), + alternateExchange ? alternateExchange->getName() : "", + queue->isDurable(), + queue->hasExclusiveOwner(), + queue->isAutoDelete(), + queue->getSettings(), + queue->getMessageCount(), + queue->getConsumerCount()); +} + +void BrokerAdapter::QueueHandlerImpl::declare(uint16_t /*ticket*/, const string& name, const string& alternateExchange, + bool passive, bool durable, bool exclusive, + bool autoDelete, const qpid::framing::FieldTable& arguments){ + + Exchange::shared_ptr alternate; + if (!alternateExchange.empty()) { + alternate = getBroker().getExchanges().get(alternateExchange); + } + Queue::shared_ptr queue; + if (passive && !name.empty()) { + queue = state.getQueue(name); + //TODO: check alternate-exchange is as expected + } else { + std::pair<Queue::shared_ptr, bool> queue_created = + getBroker().getQueues().declare( + name, durable, + autoDelete, + exclusive ? &getConnection() : 0); + queue = queue_created.first; + assert(queue); + if (queue_created.second) { // This is a new queue + if (alternate) { + queue->setAlternateExchange(alternate); + alternate->incAlternateUsers(); + } + + //apply settings & create persistent record if required + queue_created.first->create(arguments); + + //add default binding: + getBroker().getExchanges().getDefault()->bind(queue, name, 0); + queue->bound(getBroker().getExchanges().getDefault()->getName(), name, arguments); + + //handle automatic cleanup: + if (exclusive) { + getConnection().exclusiveQueues.push_back(queue); + } + } else { + if (exclusive && queue->setExclusiveOwner(&getConnection())) { + getConnection().exclusiveQueues.push_back(queue); + } + } + } + if (exclusive && !queue->isExclusiveOwner(&getConnection())) + throw ResourceLockedException( + QPID_MSG("Cannot grant exclusive access to queue " + << queue->getName())); +} + +void BrokerAdapter::QueueHandlerImpl::bind(uint16_t /*ticket*/, const string& queueName, + const string& exchangeName, const string& routingKey, + const FieldTable& arguments){ + + Queue::shared_ptr queue = state.getQueue(queueName); + Exchange::shared_ptr exchange = getBroker().getExchanges().get(exchangeName); + if(exchange){ + string exchangeRoutingKey = routingKey.empty() && queueName.empty() ? queue->getName() : routingKey; + if (exchange->bind(queue, exchangeRoutingKey, &arguments)) { + queue->bound(exchangeName, routingKey, arguments); + if (exchange->isDurable() && queue->isDurable()) { + getBroker().getStore().bind(*exchange, *queue, routingKey, arguments); + } + } + }else{ + throw NotFoundException( + "Bind failed. No such exchange: " + exchangeName); + } +} + +void +BrokerAdapter::QueueHandlerImpl::unbind(uint16_t /*ticket*/, + const string& queueName, + const string& exchangeName, + const string& routingKey, + const qpid::framing::FieldTable& arguments ) +{ + Queue::shared_ptr queue = state.getQueue(queueName); + if (!queue.get()) throw NotFoundException("Unbind failed. No such exchange: " + exchangeName); + + Exchange::shared_ptr exchange = getBroker().getExchanges().get(exchangeName); + if (!exchange.get()) throw NotFoundException("Unbind failed. No such exchange: " + exchangeName); + + if (exchange->unbind(queue, routingKey, &arguments) && exchange->isDurable() && queue->isDurable()) { + getBroker().getStore().unbind(*exchange, *queue, routingKey, arguments); + } + +} + +void BrokerAdapter::QueueHandlerImpl::purge(uint16_t /*ticket*/, const string& queue){ + state.getQueue(queue)->purge(); +} + +void BrokerAdapter::QueueHandlerImpl::delete_(uint16_t /*ticket*/, const string& queue, bool ifUnused, bool ifEmpty){ + ChannelException error(0, ""); + Queue::shared_ptr q = state.getQueue(queue); + if(ifEmpty && q->getMessageCount() > 0){ + throw PreconditionFailedException("Queue not empty."); + }else if(ifUnused && q->getConsumerCount() > 0){ + throw PreconditionFailedException("Queue in use."); + }else{ + //remove the queue from the list of exclusive queues if necessary + if(q->isExclusiveOwner(&getConnection())){ + QueueVector::iterator i = find(getConnection().exclusiveQueues.begin(), getConnection().exclusiveQueues.end(), q); + if(i < getConnection().exclusiveQueues.end()) getConnection().exclusiveQueues.erase(i); + } + q->destroy(); + getBroker().getQueues().destroy(queue); + q->unbind(getBroker().getExchanges(), q); + } +} + + + + +void BrokerAdapter::BasicHandlerImpl::qos(uint32_t prefetchSize, uint16_t prefetchCount, bool /*global*/){ + //TODO: handle global + state.setPrefetchSize(prefetchSize); + state.setPrefetchCount(prefetchCount); +} + +void BrokerAdapter::BasicHandlerImpl::consume(uint16_t /*ticket*/, + const string& queueName, const string& consumerTag, + bool noLocal, bool noAck, bool exclusive, + bool nowait, const FieldTable& fields) +{ + + Queue::shared_ptr queue = state.getQueue(queueName); + if(!consumerTag.empty() && state.exists(consumerTag)){ + throw NotAllowedException(QPID_MSG("Consumer tags must be unique")); + } + string newTag = consumerTag; + //need to generate name here, so we have it for the adapter (it is + //also version specific behaviour now) + if (newTag.empty()) newTag = tagGenerator.generate(); + DeliveryToken::shared_ptr token(MessageDelivery::getBasicConsumeToken(newTag)); + state.consume(token, newTag, queue, noLocal, !noAck, true, exclusive, &fields); + + if(!nowait) + getProxy().getBasic().consumeOk(newTag); +} + +void BrokerAdapter::BasicHandlerImpl::cancel(const string& consumerTag){ + state.cancel(consumerTag); +} + +void BrokerAdapter::BasicHandlerImpl::get(uint16_t /*ticket*/, const string& queueName, bool noAck){ + Queue::shared_ptr queue = state.getQueue(queueName); + DeliveryToken::shared_ptr token(MessageDelivery::getBasicGetToken(queue)); + if(!state.get(token, queue, !noAck)){ + string clusterId;//not used, part of an imatix hack + + getProxy().getBasic().getEmpty(clusterId); + } +} + +void BrokerAdapter::BasicHandlerImpl::ack(uint64_t deliveryTag, bool multiple){ + if (multiple) { + state.ackCumulative(deliveryTag); + } else { + state.ackRange(deliveryTag, deliveryTag); + } +} + +void BrokerAdapter::BasicHandlerImpl::reject(uint64_t /*deliveryTag*/, bool /*requeue*/){} + +void BrokerAdapter::BasicHandlerImpl::recover(bool requeue) +{ + state.recover(requeue); +} + +void BrokerAdapter::TxHandlerImpl::select() +{ + state.startTx(); +} + +void BrokerAdapter::TxHandlerImpl::commit() +{ + state.commit(&getBroker().getStore(), true); +} + +void BrokerAdapter::TxHandlerImpl::rollback() +{ + state.rollback(); + state.recover(true); +} + +}} // namespace qpid::broker + diff --git a/qpid/cpp/src/qpid/broker/BrokerAdapter.h b/qpid/cpp/src/qpid/broker/BrokerAdapter.h new file mode 100644 index 0000000000..b28c4ebdcc --- /dev/null +++ b/qpid/cpp/src/qpid/broker/BrokerAdapter.h @@ -0,0 +1,208 @@ +#ifndef _broker_BrokerAdapter_h +#define _broker_BrokerAdapter_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ +#include "DtxHandlerImpl.h" +#include "MessageHandlerImpl.h" + +#include "qpid/Exception.h" +#include "qpid/framing/AMQP_ServerOperations.h" +#include "qpid/framing/reply_exceptions.h" + +namespace qpid { +namespace broker { + +class Channel; +class Connection; +class Broker; +class ConnectionHandler; +class BasicHandler; +class ExchangeHandler; +class QueueHandler; +class TxHandler; +class MessageHandler; +class AccessHandler; +class FileHandler; +class StreamHandler; +class DtxHandler; +class TunnelHandler; +class MessageHandlerImpl; +class Exchange; + +/** + * Per-channel protocol adapter. + * + * A container for a collection of AMQP-class adapters that translate + * AMQP method bodies into calls on the core Broker objects. Each + * adapter class also provides a client proxy to send methods to the + * peer. + * + */ +class BrokerAdapter : public HandlerImpl, public framing::AMQP_ServerOperations +{ + public: + BrokerAdapter(SemanticState& session); + + BasicHandler* getBasicHandler() { return &basicHandler; } + ExchangeHandler* getExchangeHandler() { return &exchangeHandler; } + BindingHandler* getBindingHandler() { return &bindingHandler; } + QueueHandler* getQueueHandler() { return &queueHandler; } + TxHandler* getTxHandler() { return &txHandler; } + MessageHandler* getMessageHandler() { return &messageHandler; } + DtxCoordinationHandler* getDtxCoordinationHandler() { return &dtxHandler; } + DtxDemarcationHandler* getDtxDemarcationHandler() { return &dtxHandler; } + + framing::ProtocolVersion getVersion() const { return session.getConnection().getVersion();} + + + AccessHandler* getAccessHandler() { + throw framing::NotImplementedException("Access class not implemented"); } + FileHandler* getFileHandler() { + throw framing::NotImplementedException("File class not implemented"); } + StreamHandler* getStreamHandler() { + throw framing::NotImplementedException("Stream class not implemented"); } + TunnelHandler* getTunnelHandler() { + throw framing::NotImplementedException("Tunnel class not implemented"); } + + Exchange010Handler* getExchange010Handler() { throw framing::NotImplementedException("Class not implemented"); } + Queue010Handler* getQueue010Handler() { throw framing::NotImplementedException("Class not implemented"); } + Message010Handler* getMessage010Handler() { throw framing::NotImplementedException("Class not implemented"); } + Tx010Handler* getTx010Handler() { throw framing::NotImplementedException("Class not implemented"); } + Dtx010Handler* getDtx010Handler() { throw framing::NotImplementedException("Class not implemented"); } + Execution010Handler* getExecution010Handler() { throw framing::NotImplementedException("Class not implemented"); } + + // Handlers no longer implemented in BrokerAdapter: +#define BADHANDLER() assert(0); throw framing::NotImplementedException("") + ExecutionHandler* getExecutionHandler() { BADHANDLER(); } + ConnectionHandler* getConnectionHandler() { BADHANDLER(); } + SessionHandler* getSessionHandler() { BADHANDLER(); } + Connection010Handler* getConnection010Handler() { BADHANDLER(); } + Session010Handler* getSession010Handler() { BADHANDLER(); } +#undef BADHANDLER + + private: + class ExchangeHandlerImpl : + public ExchangeHandler, + public HandlerImpl + { + public: + ExchangeHandlerImpl(SemanticState& session) : HandlerImpl(session) {} + + void declare(uint16_t ticket, + const std::string& exchange, const std::string& type, + const std::string& alternateExchange, + bool passive, bool durable, bool autoDelete, + const qpid::framing::FieldTable& arguments); + void delete_(uint16_t ticket, + const std::string& exchange, bool ifUnused); + framing::ExchangeQueryResult query(u_int16_t ticket, + const std::string& name); + private: + void checkType(shared_ptr<Exchange> exchange, const std::string& type); + + void checkAlternate(shared_ptr<Exchange> exchange, + shared_ptr<Exchange> alternate); + }; + + class BindingHandlerImpl : + public BindingHandler, + public HandlerImpl + { + public: + BindingHandlerImpl(SemanticState& session) : HandlerImpl(session) {} + + framing::BindingQueryResult query(u_int16_t ticket, + const std::string& exchange, + const std::string& queue, + const std::string& routingKey, + const framing::FieldTable& arguments); + }; + + class QueueHandlerImpl : + public QueueHandler, + public HandlerImpl + { + public: + QueueHandlerImpl(SemanticState& session) : HandlerImpl(session) {} + + void declare(uint16_t ticket, const std::string& queue, + const std::string& alternateExchange, + bool passive, bool durable, bool exclusive, + bool autoDelete, + const qpid::framing::FieldTable& arguments); + void bind(uint16_t ticket, const std::string& queue, + const std::string& exchange, const std::string& routingKey, + const qpid::framing::FieldTable& arguments); + void unbind(uint16_t ticket, + const std::string& queue, + const std::string& exchange, + const std::string& routingKey, + const qpid::framing::FieldTable& arguments ); + framing::QueueQueryResult query(const std::string& queue); + void purge(uint16_t ticket, const std::string& queue); + void delete_(uint16_t ticket, const std::string& queue, + bool ifUnused, bool ifEmpty); + }; + + class BasicHandlerImpl : + public BasicHandler, + public HandlerImpl + { + NameGenerator tagGenerator; + public: + BasicHandlerImpl(SemanticState& session) : HandlerImpl(session), tagGenerator("sgen") {} + + void qos(uint32_t prefetchSize, + uint16_t prefetchCount, bool global); + void consume(uint16_t ticket, const std::string& queue, + const std::string& consumerTag, + bool noLocal, bool noAck, bool exclusive, bool nowait, + const qpid::framing::FieldTable& fields); + void cancel(const std::string& consumerTag); + void get(uint16_t ticket, const std::string& queue, bool noAck); + void ack(uint64_t deliveryTag, bool multiple); + void reject(uint64_t deliveryTag, bool requeue); + void recover(bool requeue); + }; + + class TxHandlerImpl : + public TxHandler, + public HandlerImpl + { + public: + TxHandlerImpl(SemanticState& session) : HandlerImpl(session) {} + + void select(); + void commit(); + void rollback(); + }; + + BasicHandlerImpl basicHandler; + ExchangeHandlerImpl exchangeHandler; + BindingHandlerImpl bindingHandler; + MessageHandlerImpl messageHandler; + QueueHandlerImpl queueHandler; + TxHandlerImpl txHandler; + DtxHandlerImpl dtxHandler; +}; +}} // namespace qpid::broker + + + +#endif /*!_broker_BrokerAdapter_h*/ diff --git a/qpid/cpp/src/qpid/broker/BrokerSingleton.cpp b/qpid/cpp/src/qpid/broker/BrokerSingleton.cpp new file mode 100644 index 0000000000..77200dd760 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/BrokerSingleton.cpp @@ -0,0 +1,36 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "BrokerSingleton.h" + +namespace qpid { +namespace broker { + +BrokerSingleton::BrokerSingleton() { + if (broker.get() == 0) + broker = Broker::create(); + shared_ptr<Broker>::operator=(broker); +} + +BrokerSingleton::~BrokerSingleton() { + broker->shutdown(); +} + +shared_ptr<Broker> BrokerSingleton::broker; + +}} // namespace qpid::broker diff --git a/qpid/cpp/src/qpid/broker/BrokerSingleton.h b/qpid/cpp/src/qpid/broker/BrokerSingleton.h new file mode 100644 index 0000000000..14b932df36 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/BrokerSingleton.h @@ -0,0 +1,52 @@ +#ifndef _broker_BrokerSingleton_h +#define _broker_BrokerSingleton_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "Broker.h" + +namespace qpid { +namespace broker { + +/** + * BrokerSingleton is a smart pointer to a process-wide singleton broker + * started on an os-chosen port. The broker starts the first time + * an instance of BrokerSingleton is created and runs untill the process exits. + * + * Useful for unit tests that want to share a broker between multiple + * tests to reduce overhead of starting/stopping a broker for every test. + * + * Tests that need a new broker can create it directly. + * + * THREAD UNSAFE. + */ +class BrokerSingleton : public shared_ptr<Broker> +{ + public: + BrokerSingleton(); + ~BrokerSingleton(); + private: + static shared_ptr<Broker> broker; +}; + +}} // namespace qpid::broker + + + +#endif /*!_broker_BrokerSingleton_h*/ diff --git a/qpid/cpp/src/qpid/broker/Connection.cpp b/qpid/cpp/src/qpid/broker/Connection.cpp new file mode 100644 index 0000000000..ef1100a2ec --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Connection.cpp @@ -0,0 +1,328 @@ +/* + * + * 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. + * + */ +#include "Connection.h" +#include "SessionState.h" +#include "BrokerAdapter.h" +#include "Bridge.h" +#include "SemanticHandler.h" + +#include "qpid/log/Statement.h" +#include "qpid/ptr_map.h" +#include "qpid/framing/AMQP_ClientProxy.h" +#include "qpid/management/ManagementAgent.h" + +#include <boost/bind.hpp> +#include <boost/ptr_container/ptr_vector.hpp> + +#include <algorithm> +#include <iostream> +#include <assert.h> + +using namespace boost; +using namespace qpid::sys; +using namespace qpid::framing; +using namespace qpid::sys; +using namespace qpid::ptr_map; +using qpid::management::ManagementAgent; +using qpid::management::ManagementObject; +using qpid::management::Manageable; +using qpid::management::Args; + +namespace qpid { +namespace broker { + +class Connection::MgmtClient : public Connection::MgmtWrapper +{ + management::Client::shared_ptr mgmtClient; + +public: + MgmtClient(Connection* conn, Manageable* parent, ManagementAgent::shared_ptr agent, const std::string& mgmtId); + ~MgmtClient(); + void received(framing::AMQFrame& frame); + management::ManagementObject::shared_ptr getManagementObject() const; + void closing(); +}; + +class Connection::MgmtLink : public Connection::MgmtWrapper +{ + typedef boost::ptr_vector<Bridge> Bridges; + + management::Link::shared_ptr mgmtLink; + Bridges created;//holds list of bridges pending creation + Bridges cancelled;//holds list of bridges pending cancellation + Bridges active;//holds active bridges + uint channelCounter; + sys::Mutex linkLock; + + void cancel(Bridge*); + +public: + MgmtLink(Connection* conn, Manageable* parent, ManagementAgent::shared_ptr agent, const std::string& mgmtId); + ~MgmtLink(); + void received(framing::AMQFrame& frame); + management::ManagementObject::shared_ptr getManagementObject() const; + void closing(); + void processPending(); + void process(Connection& connection, const management::Args& args); +}; + + +Connection::Connection(ConnectionOutputHandler* out_, Broker& broker_, const std::string& mgmtId_) : + ConnectionState(out_, broker_), + adapter(*this), + mgmtClosing(false), + mgmtId(mgmtId_) +{ + initMgmt(); +} + +void Connection::initMgmt(bool asLink) +{ + Manageable* parent = broker.GetVhostObject (); + + if (parent != 0) + { + ManagementAgent::shared_ptr agent = ManagementAgent::getAgent (); + + if (agent.get () != 0) + { + if (asLink) { + mgmtWrapper = std::auto_ptr<MgmtWrapper>(new MgmtLink(this, parent, agent, mgmtId)); + } else { + mgmtWrapper = std::auto_ptr<MgmtWrapper>(new MgmtClient(this, parent, agent, mgmtId)); + } + } + } +} + +Connection::~Connection () {} + +void Connection::received(framing::AMQFrame& frame){ + if (mgmtClosing) + close (403, "Closed by Management Request", 0, 0); + + if (frame.getChannel() == 0 && frame.getMethod()) { + adapter.handle(frame); + } else { + getChannel(frame.getChannel()).in(frame); + } + + if (mgmtWrapper.get()) mgmtWrapper->received(frame); +} + +void Connection::close( + ReplyCode code, const string& text, ClassId classId, MethodId methodId) +{ + adapter.close(code, text, classId, methodId); + channels.clear(); + getOutput().close(); +} + +void Connection::idleOut(){} + +void Connection::idleIn(){} + +void Connection::closed(){ // Physically closed, suspend open sessions. + try { + for (ChannelMap::iterator i = channels.begin(); i != channels.end(); ++i) + get_pointer(i)->localSuspend(); + while (!exclusiveQueues.empty()) { + Queue::shared_ptr q(exclusiveQueues.front()); + q->releaseExclusiveOwnership(); + if (q->canAutoDelete()) { + Queue::tryAutoDelete(broker, q); + } + exclusiveQueues.erase(exclusiveQueues.begin()); + } + } catch(std::exception& e) { + QPID_LOG(error, " Unhandled exception while closing session: " << + e.what()); + assert(0); + } +} + +bool Connection::doOutput() +{ + try{ + //process any pending mgmt commands: + if (mgmtWrapper.get()) mgmtWrapper->processPending(); + if (mgmtClosing) close (403, "Closed by Management Request", 0, 0); + + //then do other output as needed: + return outputTasks.doOutput(); + }catch(ConnectionException& e){ + close(e.code, e.what(), 0, 0); + }catch(std::exception& e){ + close(541/*internal error*/, e.what(), 0, 0); + } + return false; +} + +void Connection::closeChannel(uint16_t id) { + ChannelMap::iterator i = channels.find(id); + if (i != channels.end()) channels.erase(i); +} + +SessionHandler& Connection::getChannel(ChannelId id) { + ChannelMap::iterator i=channels.find(id); + if (i == channels.end()) { + i = channels.insert(id, new SessionHandler(*this, id)).first; + } + return *get_pointer(i); +} + +ManagementObject::shared_ptr Connection::GetManagementObject (void) const +{ + return mgmtWrapper.get() ? mgmtWrapper->getManagementObject() : ManagementObject::shared_ptr(); +} + +Manageable::status_t Connection::ManagementMethod (uint32_t methodId, + Args& args) +{ + Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; + + QPID_LOG (debug, "Connection::ManagementMethod [id=" << methodId << "]"); + + switch (methodId) + { + case management::Client::METHOD_CLOSE : + mgmtClosing = true; + if (mgmtWrapper.get()) mgmtWrapper->closing(); + out->activateOutput(); + status = Manageable::STATUS_OK; + break; + case management::Link::METHOD_BRIDGE : + //queue this up and request chance to do output (i.e. get connections thread of control): + mgmtWrapper->process(*this, args); + out->activateOutput(); + status = Manageable::STATUS_OK; + break; + } + + return status; +} + +Connection::MgmtLink::MgmtLink(Connection* conn, Manageable* parent, ManagementAgent::shared_ptr agent, const std::string& mgmtId) + : channelCounter(1) +{ + mgmtLink = management::Link::shared_ptr + (new management::Link(conn, parent, mgmtId)); + agent->addObject (mgmtLink); +} + +Connection::MgmtLink::~MgmtLink() +{ + if (mgmtLink.get () != 0) + mgmtLink->resourceDestroy (); +} + +void Connection::MgmtLink::received(framing::AMQFrame& frame) +{ + if (mgmtLink.get () != 0) + { + mgmtLink->inc_framesFromPeer (); + mgmtLink->inc_bytesFromPeer (frame.size ()); + } +} + +management::ManagementObject::shared_ptr Connection::MgmtLink::getManagementObject() const +{ + return dynamic_pointer_cast<ManagementObject>(mgmtLink); +} + +void Connection::MgmtLink::closing() +{ + if (mgmtLink) mgmtLink->set_closing (1); +} + +void Connection::MgmtLink::processPending() +{ + Mutex::ScopedLock l(linkLock); + //process any pending creates + if (!created.empty()) { + for (Bridges::iterator i = created.begin(); i != created.end(); ++i) { + i->create(); + } + active.transfer(active.end(), created.begin(), created.end(), created); + } + if (!cancelled.empty()) { + //process any pending cancellations + for (Bridges::iterator i = cancelled.begin(); i != cancelled.end(); ++i) { + i->cancel(); + } + cancelled.clear(); + } +} + +void Connection::MgmtLink::process(Connection& connection, const management::Args& args) +{ + Mutex::ScopedLock l(linkLock); + created.push_back(new Bridge(channelCounter++, connection, + boost::bind(&MgmtLink::cancel, this, _1), + dynamic_cast<const management::ArgsLinkBridge&>(args))); +} + +void Connection::MgmtLink::cancel(Bridge* b) +{ + Mutex::ScopedLock l(linkLock); + //need to take this out the active map and add it to the cancelled map + for (Bridges::iterator i = active.begin(); i != active.end(); i++) { + if (&(*i) == b) { + cancelled.transfer(cancelled.end(), i, active); + break; + } + } +} + +Connection::MgmtClient::MgmtClient(Connection* conn, Manageable* parent, ManagementAgent::shared_ptr agent, const std::string& mgmtId) +{ + mgmtClient = management::Client::shared_ptr + (new management::Client (conn, parent, mgmtId)); + agent->addObject (mgmtClient); +} + +Connection::MgmtClient::~MgmtClient() +{ + if (mgmtClient.get () != 0) + mgmtClient->resourceDestroy (); +} + +void Connection::MgmtClient::received(framing::AMQFrame& frame) +{ + if (mgmtClient.get () != 0) + { + mgmtClient->inc_framesFromClient (); + mgmtClient->inc_bytesFromClient (frame.size ()); + } +} + +management::ManagementObject::shared_ptr Connection::MgmtClient::getManagementObject() const +{ + return dynamic_pointer_cast<ManagementObject>(mgmtClient); +} + +void Connection::MgmtClient::closing() +{ + if (mgmtClient) mgmtClient->set_closing (1); +} + +}} + diff --git a/qpid/cpp/src/qpid/broker/Connection.h b/qpid/cpp/src/qpid/broker/Connection.h new file mode 100644 index 0000000000..a59df26c84 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Connection.h @@ -0,0 +1,115 @@ +/* + * + * 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. + * + */ +#ifndef _Connection_ +#define _Connection_ + +#include <memory> +#include <sstream> +#include <vector> + +#include <boost/ptr_container/ptr_map.hpp> + +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/AMQP_ServerOperations.h" +#include "qpid/framing/AMQP_ClientProxy.h" +#include "qpid/sys/AggregateOutput.h" +#include "qpid/sys/ConnectionOutputHandler.h" +#include "qpid/sys/ConnectionInputHandler.h" +#include "qpid/sys/TimeoutHandler.h" +#include "qpid/framing/ProtocolVersion.h" +#include "Broker.h" +#include "qpid/sys/Socket.h" +#include "qpid/Exception.h" +#include "ConnectionHandler.h" +#include "ConnectionState.h" +#include "SessionHandler.h" +#include "qpid/management/Manageable.h" +#include "qpid/management/Client.h" +#include "qpid/management/Link.h" + +#include <boost/ptr_container/ptr_map.hpp> + +namespace qpid { +namespace broker { + +class Connection : public sys::ConnectionInputHandler, + public ConnectionState +{ + public: + Connection(sys::ConnectionOutputHandler* out, Broker& broker, const std::string& mgmtId); + ~Connection (); + + /** Get the SessionHandler for channel. Create if it does not already exist */ + SessionHandler& getChannel(framing::ChannelId channel); + + /** Close the connection */ + void close(framing::ReplyCode code, const string& text, framing::ClassId classId, framing::MethodId methodId); + + // ConnectionInputHandler methods + void received(framing::AMQFrame& frame); + void idleOut(); + void idleIn(); + void closed(); + bool doOutput(); + + void closeChannel(framing::ChannelId channel); + + // Manageable entry points + management::ManagementObject::shared_ptr GetManagementObject (void) const; + management::Manageable::status_t + ManagementMethod (uint32_t methodId, management::Args& args); + + void initMgmt(bool asLink = false); + + private: + typedef boost::ptr_map<framing::ChannelId, SessionHandler> ChannelMap; + typedef std::vector<Queue::shared_ptr>::iterator queue_iterator; + + /** + * Connection may appear, for the purposes of management, as a + * normal client initiated connection or as an agent initiated + * inter-broker link. This wrapper abstracts the common interface + * for both. + */ + class MgmtWrapper + { + public: + virtual ~MgmtWrapper(){} + virtual void received(framing::AMQFrame& frame) = 0; + virtual management::ManagementObject::shared_ptr getManagementObject() const = 0; + virtual void closing() = 0; + virtual void processPending(){} + virtual void process(Connection&, const management::Args&){} + }; + class MgmtClient; + class MgmtLink; + + ChannelMap channels; + framing::AMQP_ClientProxy::Connection* client; + ConnectionHandler adapter; + std::auto_ptr<MgmtWrapper> mgmtWrapper; + bool mgmtClosing; + const std::string mgmtId; +}; + +}} + +#endif diff --git a/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp b/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp new file mode 100644 index 0000000000..cb831f3023 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/ConnectionFactory.cpp @@ -0,0 +1,53 @@ +/* + * + * 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. + * + */ +#include "ConnectionFactory.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/amqp_0_10/Connection.h" +#include "PreviewConnectionCodec.h" + +namespace qpid { +namespace broker { + +using framing::ProtocolVersion; + +ConnectionFactory::ConnectionFactory(Broker& b) : broker(b) {} + +ConnectionFactory::~ConnectionFactory() {} + +sys::ConnectionCodec* +ConnectionFactory::create(ProtocolVersion v, sys::OutputControl& out, const std::string& id) { + if (v == ProtocolVersion(99, 0)) + return new PreviewConnectionCodec(out, broker, id); + if (v == ProtocolVersion(0, 10)) + return new amqp_0_10::Connection(out, broker, id); + return 0; +} + +sys::ConnectionCodec* +ConnectionFactory::create(sys::OutputControl& out, const std::string& id) { + // FIXME aconway 2008-03-18: + + // gsim 2008-03-26 this seems only to be used when creating + // connections from one broker to another + return new PreviewConnectionCodec(out, broker, id, true); +} + +}} // namespace qpid::broker diff --git a/qpid/cpp/src/qpid/broker/ConnectionFactory.h b/qpid/cpp/src/qpid/broker/ConnectionFactory.h new file mode 100644 index 0000000000..5797495054 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/ConnectionFactory.h @@ -0,0 +1,49 @@ +/* + * + * 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. + * + */ +#ifndef _ConnectionFactory_ +#define _ConnectionFactory_ + +#include "qpid/sys/ConnectionCodec.h" + +namespace qpid { +namespace broker { +class Broker; + +class ConnectionFactory : public sys::ConnectionCodec::Factory { + public: + ConnectionFactory(Broker& b); + + virtual ~ConnectionFactory(); + + sys::ConnectionCodec* + create(framing::ProtocolVersion, sys::OutputControl&, const std::string& id); + + sys::ConnectionCodec* + create(sys::OutputControl&, const std::string& id); + + private: + Broker& broker; +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/ConnectionHandler.cpp b/qpid/cpp/src/qpid/broker/ConnectionHandler.cpp new file mode 100644 index 0000000000..315e03fb2b --- /dev/null +++ b/qpid/cpp/src/qpid/broker/ConnectionHandler.cpp @@ -0,0 +1,166 @@ + +/* + * + * 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. + * + */ + +#include "ConnectionHandler.h" +#include "Connection.h" +#include "qpid/framing/ConnectionStartBody.h" +#include "qpid/framing/Connection010StartBody.h" +#include "qpid/framing/ClientInvoker.h" +#include "qpid/framing/ServerInvoker.h" + +using namespace qpid; +using namespace qpid::broker; +using namespace qpid::framing; + + +namespace +{ +const std::string PLAIN = "PLAIN"; +const std::string en_US = "en_US"; +} + +void ConnectionHandler::close(ReplyCode code, const string& text, ClassId classId, MethodId methodId) +{ + handler->client.close(code, text, classId, methodId); +} + +void ConnectionHandler::handle(framing::AMQFrame& frame) +{ + AMQMethodBody* method=frame.getBody()->getMethod(); + try{ + bool handled = false; + if (handler->serverMode) { + handled = invoke(static_cast<AMQP_ServerOperations::Connection010Handler&>(*handler.get()), *method); + } else { + handled = invoke(static_cast<AMQP_ClientOperations::ConnectionHandler&>(*handler.get()), *method); + } + if (!handled) { + handler->connection.getChannel(frame.getChannel()).in(frame); + } + + }catch(ConnectionException& e){ + handler->client.close(e.code, e.what(), method->amqpClassId(), method->amqpMethodId()); + }catch(std::exception& e){ + handler->client.close(541/*internal error*/, e.what(), method->amqpClassId(), method->amqpMethodId()); + } +} + +ConnectionHandler::ConnectionHandler(Connection& connection) : handler(new Handler(connection)) { + FieldTable properties; + Array mechanisms(0x95); + boost::shared_ptr<FieldValue> m(new Str16Value(PLAIN)); + mechanisms.add(m); + Array locales(0x95); + boost::shared_ptr<FieldValue> l(new Str16Value(en_US)); + locales.add(l); + handler->serverMode = true; + handler->client.start(properties, mechanisms, locales); +} + + + +ConnectionHandler::Handler:: Handler(Connection& c) : client(c.getOutput()), server(c.getOutput()), + connection(c), serverMode(false) {} + +void ConnectionHandler::Handler::startOk(const framing::FieldTable& /*clientProperties*/, + const string& mechanism, + const string& response, const string& /*locale*/) +{ + //TODO: handle SASL mechanisms more cleverly + if (mechanism == PLAIN) { + if (response.size() > 0 && response[0] == (char) 0) { + string temp = response.substr(1); + string::size_type i = temp.find((char)0); + string uid = temp.substr(0, i); + string pwd = temp.substr(i + 1); + //TODO: authentication + connection.setUserId(uid); + } + } + client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), 0, 0); +} + +void ConnectionHandler::Handler::secureOk(const string& /*response*/){} + +void ConnectionHandler::Handler::tuneOk(uint16_t /*channelmax*/, + uint16_t framemax, uint16_t heartbeat) +{ + connection.setFrameMax(framemax); + connection.setHeartbeat(heartbeat); +} + +void ConnectionHandler::Handler::open(const string& /*virtualHost*/, + const framing::Array& /*capabilities*/, bool /*insist*/) +{ + framing::Array knownhosts; + client.openOk(knownhosts); +} + + +void ConnectionHandler::Handler::close(uint16_t /*replyCode*/, const string& /*replyText*/, + uint16_t /*classId*/, uint16_t /*methodId*/) +{ + client.closeOk(); + connection.getOutput().close(); +} + +void ConnectionHandler::Handler::closeOk(){ + connection.getOutput().close(); +} + + +void ConnectionHandler::Handler::start(uint8_t /*versionMajor*/, + uint8_t /*versionMinor*/, + const FieldTable& /*serverProperties*/, + const string& /*mechanisms*/, + const string& /*locales*/) +{ + string uid = "qpidd"; + string pwd = "qpidd"; + string response = ((char)0) + uid + ((char)0) + pwd; + server.startOk(FieldTable(), PLAIN, response, en_US); + connection.initMgmt(true); +} + +void ConnectionHandler::Handler::secure(const string& /*challenge*/) +{ + server.secureOk(""); +} + +void ConnectionHandler::Handler::tune(uint16_t channelMax, + uint32_t frameMax, + uint16_t heartbeat) +{ + connection.setFrameMax(frameMax); + connection.setHeartbeat(heartbeat); + server.tuneOk(channelMax, frameMax, heartbeat); + server.open("/", "", true); +} + +void ConnectionHandler::Handler::openOk(const string& /*knownHosts*/) +{ +} + +void ConnectionHandler::Handler::redirect(const string& /*host*/, const string& /*knownHosts*/) +{ + +} diff --git a/qpid/cpp/src/qpid/broker/ConnectionHandler.h b/qpid/cpp/src/qpid/broker/ConnectionHandler.h new file mode 100644 index 0000000000..d949d51c43 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/ConnectionHandler.h @@ -0,0 +1,91 @@ +/* + * + * 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. + * + */ +#ifndef _ConnectionAdapter_ +#define _ConnectionAdapter_ + +#include <memory> +#include "qpid/framing/amqp_types.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/AMQP_ClientOperations.h" +#include "qpid/framing/AMQP_ClientProxy.h" +#include "qpid/framing/AMQP_ServerOperations.h" +#include "qpid/framing/AMQP_ServerProxy.h" +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/Exception.h" + +namespace qpid { +namespace broker { + +class Connection; + +class ConnectionHandler : public framing::FrameHandler +{ + struct Handler : public framing::AMQP_ServerOperations::Connection010Handler, + public framing::AMQP_ClientOperations::ConnectionHandler + { + framing::AMQP_ClientProxy::Connection010 client; + framing::AMQP_ServerProxy::Connection server; + Connection& connection; + bool serverMode; + + Handler(Connection& connection); + void startOk(const qpid::framing::FieldTable& clientProperties, + const std::string& mechanism, const std::string& response, + const std::string& locale); + void secureOk(const std::string& response); + void tuneOk(uint16_t channelMax, uint16_t frameMax, uint16_t heartbeat); + void heartbeat() {} + void open(const std::string& virtualHost, + const framing::Array& capabilities, bool insist); + void close(uint16_t replyCode, const std::string& replyText, + uint16_t classId, uint16_t methodId); + void closeOk(); + + + void start(uint8_t versionMajor, + uint8_t versionMinor, + const qpid::framing::FieldTable& serverProperties, + const std::string& mechanisms, + const std::string& locales); + + void secure(const std::string& challenge); + + void tune(uint16_t channelMax, + uint32_t frameMax, + uint16_t heartbeat); + + void openOk(const std::string& knownHosts); + + void redirect(const std::string& host, const std::string& knownHosts); + }; + std::auto_ptr<Handler> handler; + public: + ConnectionHandler(Connection& connection); + void close(framing::ReplyCode code, const std::string& text, framing::ClassId classId, framing::MethodId methodId); + void handle(framing::AMQFrame& frame); +}; + + +}} + +#endif diff --git a/qpid/cpp/src/qpid/broker/ConnectionState.h b/qpid/cpp/src/qpid/broker/ConnectionState.h new file mode 100644 index 0000000000..691d47d866 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/ConnectionState.h @@ -0,0 +1,84 @@ +/* + * + * 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. + * + */ +#ifndef _ConnectionState_ +#define _ConnectionState_ + +#include <vector> + +#include "qpid/sys/AggregateOutput.h" +#include "qpid/sys/ConnectionOutputHandler.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/management/Manageable.h" +#include "Broker.h" + +namespace qpid { +namespace broker { + +class ConnectionState : public ConnectionToken, public management::Manageable +{ + public: + ConnectionState(qpid::sys::ConnectionOutputHandler* o, Broker& b) : + broker(b), + outputTasks(*o), + out(o), + framemax(65535), + heartbeat(0), + stagingThreshold(broker.getStagingThreshold()) + {} + + + + virtual ~ConnectionState () {} + + uint32_t getFrameMax() const { return framemax; } + uint16_t getHeartbeat() const { return heartbeat; } + uint64_t getStagingThreshold() const { return stagingThreshold; } + + void setFrameMax(uint32_t fm) { framemax = fm; } + void setHeartbeat(uint16_t hb) { heartbeat = hb; } + void setStagingThreshold(uint64_t st) { stagingThreshold = st; } + + void setUserId(const string& uid) { userId = uid; } + const string& getUserId() const { return userId; } + + Broker& getBroker() { return broker; } + + Broker& broker; + std::vector<Queue::shared_ptr> exclusiveQueues; + + //contained output tasks + sys::AggregateOutput outputTasks; + + sys::ConnectionOutputHandler& getOutput() const { return *out; } + framing::ProtocolVersion getVersion() const { return version; } + + protected: + framing::ProtocolVersion version; + sys::ConnectionOutputHandler* out; + uint32_t framemax; + uint16_t heartbeat; + uint64_t stagingThreshold; + string userId; +}; + +}} + +#endif diff --git a/qpid/cpp/src/qpid/broker/ConnectionToken.h b/qpid/cpp/src/qpid/broker/ConnectionToken.h new file mode 100644 index 0000000000..0e3b301897 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/ConnectionToken.h @@ -0,0 +1,40 @@ +/* + * + * 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. + * + */ +#ifndef _ConnectionToken_ +#define _ConnectionToken_ + +#include "OwnershipToken.h" +namespace qpid { + namespace broker { + /** + * An empty interface allowing opaque implementations of some + * form of token to identify a connection. + */ + class ConnectionToken : public OwnershipToken { + public: + virtual bool isLocal(const ConnectionToken* t) const { return this == t; } + virtual ~ConnectionToken(){} + }; + } +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/Consumer.h b/qpid/cpp/src/qpid/broker/Consumer.h new file mode 100644 index 0000000000..00eb41a428 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Consumer.h @@ -0,0 +1,65 @@ +/* + * + * 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. + * + */ +#ifndef _Consumer_ +#define _Consumer_ + +namespace qpid { + namespace broker { + class Queue; +}} + +#include "Message.h" + +namespace qpid { + namespace broker { + + struct QueuedMessage + { + boost::intrusive_ptr<Message> payload; + framing::SequenceNumber position; + Queue* queue; + + QueuedMessage(Queue* q, boost::intrusive_ptr<Message> msg, framing::SequenceNumber sn) : + payload(msg), position(sn), queue(q) {} + QueuedMessage(Queue* q) : queue(q) {} + }; + + + class Consumer { + const bool acquires; + public: + typedef shared_ptr<Consumer> ptr; + + framing::SequenceNumber position; + + Consumer(bool preAcquires = true) : acquires(preAcquires) {} + bool preAcquires() const { return acquires; } + virtual bool deliver(QueuedMessage& msg) = 0; + virtual void notify() = 0; + virtual bool filter(boost::intrusive_ptr<Message>) { return true; } + virtual bool accept(boost::intrusive_ptr<Message>) { return true; } + virtual ~Consumer(){} + }; + } +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/Daemon.cpp b/qpid/cpp/src/qpid/broker/Daemon.cpp new file mode 100644 index 0000000000..3fcc487324 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Daemon.cpp @@ -0,0 +1,192 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ +#include "Daemon.h" +#include "qpid/log/Statement.h" +#include "qpid/Exception.h" + +#include <boost/iostreams/stream.hpp> +#include <boost/iostreams/device/file_descriptor.hpp> + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> + +namespace qpid { +namespace broker { + +using namespace std; +typedef boost::iostreams::stream<boost::iostreams::file_descriptor> fdstream; + +namespace { +/** Throw an exception containing msg and strerror if throwIf is true. + * Name is supposed to be reminiscent of perror(). + */ +void throwIf(bool condition, const string& msg, int errNo=errno) { + if (condition) + throw Exception(msg + (errNo? ": "+strError(errNo) : string("."))); +} + + +struct LockFile : public fdstream { + + LockFile(const std::string& path_, bool create) + : path(path_), fd(-1), created(create) + { + errno = 0; + int flags=create ? O_WRONLY|O_CREAT|O_NOFOLLOW : O_RDWR; + fd = ::open(path.c_str(), flags, 0644); + throwIf(fd < 0,"Cannot open "+path); + throwIf(::lockf(fd, F_TLOCK, 0) < 0, "Cannot lock "+path); + open(boost::iostreams::file_descriptor(fd)); + } + + ~LockFile() { + if (fd >= 0) { + ::lockf(fd, F_ULOCK, 0); + close(); + } + } + + std::string path; + int fd; + bool created; +}; + +} // namespace + +Daemon::Daemon() { + pid = -1; + pipeFds[0] = pipeFds[1] = -1; +} + +string Daemon::dir() { + return (getuid() == 0 ? "/var/run" : "/tmp"); +} + +string Daemon::pidFile(uint16_t port) { + ostringstream path; + path << dir() << "/qpidd." << port << ".pid"; + return path.str(); +} + +void Daemon::fork() +{ + throwIf(pipe(pipeFds) < 0, "Can't create pipe"); + throwIf((pid = ::fork()) < 0, "Daemon fork failed"); + if (pid == 0) { // Child + try { + QPID_LOG(debug, "Forked daemon child process"); + + // File descriptors + throwIf(::close(pipeFds[0])<0, "Cannot close read pipe"); + throwIf(::close(0)<0, "Cannot close stdin"); + throwIf(::close(1)<0, "Cannot close stdout"); + throwIf(::close(2)<0, "Cannot close stderr"); + int fd=::open("/dev/null",O_RDWR); // stdin + throwIf(fd != 0, "Cannot re-open stdin"); + throwIf(::dup(fd)<0, "Cannot re-open stdout"); + throwIf(::dup(fd)<0, "Cannot re-open stderror"); + + // Misc + throwIf(setsid()<0, "Cannot set session ID"); + throwIf(chdir(dir().c_str()) < 0, "Cannot change directory to "+dir()); + umask(027); + + // Child behavior + child(); + } + catch (const exception& e) { + QPID_LOG(critical, "Daemon startup failed: " << e.what()); + fdstream pipe(pipeFds[1]); + assert(pipe.is_open()); + pipe << "0 " << e.what() << endl; + } + } + else { // Parent + close(pipeFds[1]); // Write side. + parent(); + } +} + +Daemon::~Daemon() { + if (!lockFile.empty()) + unlink(lockFile.c_str()); +} + +uint16_t Daemon::wait(int timeout) { // parent waits for child. + errno = 0; + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + + fd_set fds; + FD_ZERO(&fds); + FD_SET(pipeFds[0], &fds); + int n=select(FD_SETSIZE, &fds, 0, 0, &tv); + throwIf(n==0, "Timed out waiting for daemon"); + throwIf(n<0, "Error waiting for daemon"); + fdstream pipe(pipeFds[0]); + pipe.exceptions(ios::failbit|ios::badbit|ios::eofbit); + uint16_t port = 0; + try { + pipe >> port; + if (port == 0) { + string errmsg; + pipe >> skipws; + getline(pipe, errmsg); + throw Exception("Daemon startup failed"+ + (errmsg.empty() ? string(".") : ": " + errmsg)); + } + } + catch (const fdstream::failure& e) { + throw Exception(string("Failed to read daemon port: ")+e.what()); + } + return port; +} + +void Daemon::ready(uint16_t port) { // child + lockFile = pidFile(port); + LockFile lf(lockFile, true); + lf << getpid() << endl; + if (lf.fail()) + throw Exception("Cannot write lock file "+lockFile); + fdstream pipe(pipeFds[1]); + QPID_LOG(debug, "Daemon ready on port: " << port); + pipe << port << endl; + throwIf(!pipe.good(), "Error writing to parent"); +} + +pid_t Daemon::getPid(uint16_t port) { + string name = pidFile(port); + LockFile lockFile(name, false); + pid_t pid; + lockFile >> pid; + if (lockFile.fail()) + throw Exception("Cannot read lock file "+name); + if (kill(pid, 0) < 0 && errno != EPERM) { + unlink(name.c_str()); + throw Exception("Removing stale lock file "+name); + } + return pid; +} + + +}} // namespace qpid::broker diff --git a/qpid/cpp/src/qpid/broker/Daemon.h b/qpid/cpp/src/qpid/broker/Daemon.h new file mode 100644 index 0000000000..821334c11e --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Daemon.h @@ -0,0 +1,81 @@ +#ifndef _broker_Daemon_h +#define _broker_Daemon_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include <string> +#include <boost/scoped_ptr.hpp> +#include <boost/function.hpp> +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace broker { + +/** + * Tools for forking and managing a daemon process. + * NB: Only one Daemon instance is allowed in a process. + */ +class Daemon : private boost::noncopyable +{ + public: + /** Check daemon is running on port, throw exception if not */ + static pid_t getPid(uint16_t port); + + Daemon(); + + virtual ~Daemon(); + + /** + * Fork a daemon process. + * Call parent() in the parent process, child() in the child. + */ + void fork(); + + protected: + + /** Called in parent process */ + virtual void parent() = 0; + + /** Called in child process */ + virtual void child() = 0; + + /** Call from parent(): wait for child to indicate it is ready. + * @timeout in seconds to wait for response. + * @return port passed by child to ready(). + */ + uint16_t wait(int timeout); + + /** Call from child(): Notify the parent we are ready and write the + * PID file. + *@param port returned by parent call to wait(). + */ + void ready(uint16_t port); + + private: + static std::string dir(); + static std::string pidFile(uint16_t port); + + pid_t pid; + int pipeFds[2]; + std::string lockFile; +}; + +}} // namespace qpid::broker + +#endif /*!_broker_Daemon_h*/ diff --git a/qpid/cpp/src/qpid/broker/Deliverable.h b/qpid/cpp/src/qpid/broker/Deliverable.h new file mode 100644 index 0000000000..e46d2024bf --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Deliverable.h @@ -0,0 +1,40 @@ +/* + * + * 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. + * + */ +#ifndef _Deliverable_ +#define _Deliverable_ + +#include "Queue.h" + +namespace qpid { + namespace broker { + class Deliverable{ + public: + bool delivered; + Deliverable() : delivered(false) {} + virtual void deliverTo(Queue::shared_ptr& queue) = 0; + virtual uint64_t contentSize() { return 0; } + virtual ~Deliverable(){} + }; + } +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/DeliverableMessage.cpp b/qpid/cpp/src/qpid/broker/DeliverableMessage.cpp new file mode 100644 index 0000000000..fd15acf464 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DeliverableMessage.cpp @@ -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. + * + */ +#include "DeliverableMessage.h" + +using namespace qpid::broker; + +DeliverableMessage::DeliverableMessage(boost::intrusive_ptr<Message>& _msg) : msg(_msg) +{ +} + +void DeliverableMessage::deliverTo(Queue::shared_ptr& queue) +{ + queue->deliver(msg); + delivered = true; +} + +Message& DeliverableMessage::getMessage() +{ + return *msg; +} + +uint64_t DeliverableMessage::contentSize () +{ + return msg->contentSize (); +} diff --git a/qpid/cpp/src/qpid/broker/DeliverableMessage.h b/qpid/cpp/src/qpid/broker/DeliverableMessage.h new file mode 100644 index 0000000000..18e1ec5e29 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DeliverableMessage.h @@ -0,0 +1,45 @@ +/* + * + * 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. + * + */ +#ifndef _DeliverableMessage_ +#define _DeliverableMessage_ + +#include "Deliverable.h" +#include "Queue.h" +#include "Message.h" + +#include <boost/intrusive_ptr.hpp> + +namespace qpid { + namespace broker { + class DeliverableMessage : public Deliverable{ + boost::intrusive_ptr<Message> msg; + public: + DeliverableMessage(boost::intrusive_ptr<Message>& msg); + virtual void deliverTo(Queue::shared_ptr& queue); + Message& getMessage(); + uint64_t contentSize(); + virtual ~DeliverableMessage(){} + }; + } +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/DeliveryAdapter.h b/qpid/cpp/src/qpid/broker/DeliveryAdapter.h new file mode 100644 index 0000000000..4c2b2f615f --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DeliveryAdapter.h @@ -0,0 +1,52 @@ +/* + * + * 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. + * + */ +#ifndef _DeliveryAdapter_ +#define _DeliveryAdapter_ + +#include "DeliveryId.h" +#include "DeliveryToken.h" +#include "Message.h" +#include "qpid/framing/amqp_types.h" + +namespace qpid { +namespace broker { + + /** + * The intention behind this interface is to separate the generic + * handling of some form of message delivery to clients that is + * contained in the version independent Channel class from the + * details required for a particular situation or + * version. i.e. where the existing adapters allow (through + * supporting the generated interface for a version of the + * protocol) inputs of a channel to be adapted to the version + * independent part, this does the same for the outputs. + */ + class DeliveryAdapter + { + public: + virtual DeliveryId deliver(QueuedMessage& msg, DeliveryToken::shared_ptr token) = 0; + virtual ~DeliveryAdapter(){} + }; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/DeliveryId.h b/qpid/cpp/src/qpid/broker/DeliveryId.h new file mode 100644 index 0000000000..05b19f032e --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DeliveryId.h @@ -0,0 +1,35 @@ +/* + * + * 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. + * + */ +#ifndef _DeliveryId_ +#define _DeliveryId_ + +#include "qpid/framing/SequenceNumber.h" +#include "qpid/framing/SequenceNumberSet.h" + +namespace qpid { +namespace broker { + + typedef framing::SequenceNumber DeliveryId; + typedef framing::SequenceNumberSet DeliveryIds; +}} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/DeliveryRecord.cpp b/qpid/cpp/src/qpid/broker/DeliveryRecord.cpp new file mode 100644 index 0000000000..4406eccc44 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DeliveryRecord.cpp @@ -0,0 +1,211 @@ +/* + * + * 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. + * + */ +#include "DeliveryRecord.h" +#include "DeliverableMessage.h" +#include "SemanticState.h" +#include "Exchange.h" +#include "qpid/log/Statement.h" + +using namespace qpid::broker; +using std::string; + +DeliveryRecord::DeliveryRecord(const QueuedMessage& _msg, + Queue::shared_ptr _queue, + const std::string _tag, + DeliveryToken::shared_ptr _token, + const DeliveryId _id, + bool _acquired, bool accepted) : msg(_msg), + queue(_queue), + tag(_tag), + token(_token), + id(_id), + acquired(_acquired), + pull(false), + cancelled(false), + credit(msg.payload ? msg.payload->getRequiredCredit() : 0), + size(msg.payload ? msg.payload->contentSize() : 0), + completed(false), + ended(accepted) +{ + if (accepted) setEnded(); +} + +DeliveryRecord::DeliveryRecord(const QueuedMessage& _msg, + Queue::shared_ptr _queue, + const DeliveryId _id) : msg(_msg), + queue(_queue), + id(_id), + acquired(true), + pull(true), + cancelled(false), + credit(msg.payload ? msg.payload->getRequiredCredit() : 0), + size(msg.payload ? msg.payload->contentSize() : 0), + completed(false), + ended(false) +{} + +void DeliveryRecord::setEnded() +{ + ended = true; + //reset msg pointer, don't need to hold on to it anymore + msg.payload = boost::intrusive_ptr<Message>(); + + QPID_LOG(debug, "DeliveryRecord::setEnded() id=" << id); +} + +bool DeliveryRecord::matches(DeliveryId tag) const{ + return id == tag; +} + +bool DeliveryRecord::matchOrAfter(DeliveryId tag) const{ + return matches(tag) || after(tag); +} + +bool DeliveryRecord::after(DeliveryId tag) const{ + return id > tag; +} + +bool DeliveryRecord::coveredBy(const framing::AccumulatedAck* const range) const{ + return range->covers(id); +} + +void DeliveryRecord::redeliver(SemanticState* const session) { + if (!ended) { + if(pull || cancelled){ + //if message was originally sent as response to get, we must requeue it + + //or if subscription was cancelled, requeue it (waiting for + //final confirmation for AMQP WG on this case) + + requeue(); + }else{ + msg.payload->redeliver();//mark as redelivered + id = session->redeliver(msg, token); + } + } +} + +void DeliveryRecord::requeue() const +{ + if (acquired && !ended) { + msg.payload->redeliver(); + queue->requeue(msg); + } +} + +void DeliveryRecord::release(bool setRedelivered) +{ + if (acquired && !ended) { + if (setRedelivered) msg.payload->redeliver(); + queue->requeue(msg); + acquired = false; + setEnded(); + } +} + +void DeliveryRecord::complete() +{ + completed = true; +} + +void DeliveryRecord::accept(TransactionContext* ctxt) { + if (acquired && !ended) { + queue->dequeue(ctxt, msg.payload); + setEnded(); + } +} + +void DeliveryRecord::dequeue(TransactionContext* ctxt) const{ + if (acquired && !ended) { + queue->dequeue(ctxt, msg.payload); + } +} + +void DeliveryRecord::reject() +{ + Exchange::shared_ptr alternate = queue->getAlternateExchange(); + if (alternate) { + DeliverableMessage delivery(msg.payload); + alternate->route(delivery, msg.payload->getRoutingKey(), msg.payload->getApplicationHeaders()); + QPID_LOG(info, "Routed rejected message from " << queue->getName() << " to " + << alternate->getName()); + } else { + //just drop it + QPID_LOG(info, "Dropping rejected message from " << queue->getName()); + } +} + +uint32_t DeliveryRecord::getCredit() const +{ + return credit; +} + + +void DeliveryRecord::addTo(Prefetch& prefetch) const{ + if(!pull){ + //ignore 'pulled' messages (i.e. those that were sent in + //response to get) when calculating prefetch + prefetch.size += size; + prefetch.count++; + } +} + +void DeliveryRecord::subtractFrom(Prefetch& prefetch) const{ + if(!pull){ + //ignore 'pulled' messages (i.e. those that were sent in + //response to get) when calculating prefetch + prefetch.size -= size; + prefetch.count--; + } +} + +void DeliveryRecord::acquire(DeliveryIds& results) { + if (queue->acquire(msg)) { + acquired = true; + results.push_back(id); + } else { + QPID_LOG(info, "Message already acquired " << id.getValue()); + } +} + +void DeliveryRecord::cancel(const std::string& cancelledTag) +{ + if (tag == cancelledTag) + cancelled = true; +} + +namespace qpid { +namespace broker { + +std::ostream& operator<<(std::ostream& out, const DeliveryRecord& r) +{ + out << "{" << "id=" << r.id.getValue(); + out << ", tag=" << r.tag << "}"; + out << ", queue=" << r.queue->getName() << "}"; + return out; +} + +bool operator<(const DeliveryRecord& a, const DeliveryRecord& b) +{ + return a.id < b.id; +} + +}} diff --git a/qpid/cpp/src/qpid/broker/DeliveryRecord.h b/qpid/cpp/src/qpid/broker/DeliveryRecord.h new file mode 100644 index 0000000000..7d08a4b1f0 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DeliveryRecord.h @@ -0,0 +1,118 @@ +/* + * + * 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. + * + */ +#ifndef _DeliveryRecord_ +#define _DeliveryRecord_ + +#include <algorithm> +#include <list> +#include <vector> +#include <ostream> +#include "qpid/framing/AccumulatedAck.h" +#include "Queue.h" +#include "Consumer.h" +#include "DeliveryId.h" +#include "DeliveryToken.h" +#include "Message.h" +#include "Prefetch.h" + +namespace qpid { +namespace broker { +class SemanticState; + +/** + * Record of a delivery for which an ack is outstanding. + */ +class DeliveryRecord{ + QueuedMessage msg; + mutable Queue::shared_ptr queue; + const std::string tag; + DeliveryToken::shared_ptr token; + DeliveryId id; + bool acquired; + const bool pull; + bool cancelled; + const uint32_t credit; + const uint64_t size; + + bool completed; + bool ended; + + public: + DeliveryRecord(const QueuedMessage& msg, Queue::shared_ptr queue, const std::string tag, DeliveryToken::shared_ptr token, + const DeliveryId id, bool acquired, bool confirmed = false); + DeliveryRecord(const QueuedMessage& msg, Queue::shared_ptr queue, const DeliveryId id); + + bool matches(DeliveryId tag) const; + bool matchOrAfter(DeliveryId tag) const; + bool after(DeliveryId tag) const; + bool coveredBy(const framing::AccumulatedAck* const range) const; + + void dequeue(TransactionContext* ctxt = 0) const; + void requeue() const; + void release(bool setRedelivered); + void reject(); + void cancel(const std::string& tag); + void redeliver(SemanticState* const); + void acquire(DeliveryIds& results); + void complete(); + void accept(TransactionContext* ctxt); + void setEnded(); + + bool isAcquired() const { return acquired; } + bool isComplete() const { return completed; } + bool isRedundant() const { return ended && completed; } + + uint32_t getCredit() const; + void addTo(Prefetch&) const; + void subtractFrom(Prefetch&) const; + const std::string& getTag() const { return tag; } + bool isPull() const { return pull; } + friend bool operator<(const DeliveryRecord&, const DeliveryRecord&); + friend std::ostream& operator<<(std::ostream&, const DeliveryRecord&); +}; + +typedef std::list<DeliveryRecord> DeliveryRecords; +typedef std::list<DeliveryRecord>::iterator ack_iterator; + +struct AckRange +{ + ack_iterator start; + ack_iterator end; + AckRange(ack_iterator _start, ack_iterator _end) : start(_start), end(_end) {} +}; + +struct AcquireFunctor +{ + DeliveryIds& results; + + AcquireFunctor(DeliveryIds& _results) : results(_results) {} + + void operator()(DeliveryRecord& record) + { + record.acquire(results); + } +}; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/DeliveryToken.h b/qpid/cpp/src/qpid/broker/DeliveryToken.h new file mode 100644 index 0000000000..8bdf5e6359 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DeliveryToken.h @@ -0,0 +1,45 @@ +/* + * + * 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. + * + */ +#ifndef _DeliveryToken_ +#define _DeliveryToken_ + +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace broker { + + /** + * A DeliveryToken allows the delivery of a message to be + * associated with whatever mechanism caused it to be + * delivered. (i.e. its a form of Memento). + */ + class DeliveryToken + { + public: + typedef boost::shared_ptr<DeliveryToken> shared_ptr; + + virtual ~DeliveryToken(){} + }; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/DirectExchange.cpp b/qpid/cpp/src/qpid/broker/DirectExchange.cpp new file mode 100644 index 0000000000..72021b8d98 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DirectExchange.cpp @@ -0,0 +1,153 @@ +/* + * + * 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. + * + */ +#include "qpid/log/Statement.h" +#include "DirectExchange.h" +#include <iostream> + +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; +using qpid::management::Manageable; + +DirectExchange::DirectExchange(const string& _name, Manageable* _parent) : Exchange(_name, _parent) +{ + if (mgmtExchange.get() != 0) + mgmtExchange->set_type (typeName); +} + +DirectExchange::DirectExchange(const std::string& _name, bool _durable, + const FieldTable& _args, Manageable* _parent) : + Exchange(_name, _durable, _args, _parent) +{ + if (mgmtExchange.get() != 0) + mgmtExchange->set_type (typeName); +} + +bool DirectExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable*){ + RWlock::ScopedWlock l(lock); + std::vector<Binding::shared_ptr>& queues(bindings[routingKey]); + std::vector<Binding::shared_ptr>::iterator i; + + for (i = queues.begin(); i != queues.end(); i++) + if ((*i)->queue == queue) + break; + + if (i == queues.end()) { + Binding::shared_ptr binding (new Binding (routingKey, queue, this)); + bindings[routingKey].push_back(binding); + if (mgmtExchange.get() != 0) { + mgmtExchange->inc_bindings(); + dynamic_pointer_cast<management::Queue>(queue->GetManagementObject())->inc_bindings(); + } + return true; + } else{ + return false; + } +} + +bool DirectExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/){ + RWlock::ScopedWlock l(lock); + std::vector<Binding::shared_ptr>& queues(bindings[routingKey]); + std::vector<Binding::shared_ptr>::iterator i; + + for (i = queues.begin(); i != queues.end(); i++) + if ((*i)->queue == queue) + break; + + if (i < queues.end()) { + queues.erase(i); + if (queues.empty()) { + bindings.erase(routingKey); + } + if (mgmtExchange.get() != 0) { + mgmtExchange->dec_bindings (); + dynamic_pointer_cast<management::Queue>(queue->GetManagementObject())->dec_bindings(); + } + return true; + } else { + return false; + } +} + +void DirectExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/){ + RWlock::ScopedRlock l(lock); + std::vector<Binding::shared_ptr>& queues(bindings[routingKey]); + std::vector<Binding::shared_ptr>::iterator i; + int count(0); + + for(i = queues.begin(); i != queues.end(); i++, count++) { + msg.deliverTo((*i)->queue); + if ((*i)->mgmtBinding.get() != 0) + (*i)->mgmtBinding->inc_msgMatched (); + } + + if(!count){ + QPID_LOG(warning, "DirectExchange " << getName() << " could not route message with key " << routingKey); + if (mgmtExchange.get() != 0) { + mgmtExchange->inc_msgDrops (); + mgmtExchange->inc_byteDrops (msg.contentSize ()); + } + } + else { + if (mgmtExchange.get() != 0) { + mgmtExchange->inc_msgRoutes (count); + mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); + } + } + + if (mgmtExchange.get() != 0) { + mgmtExchange->inc_msgReceives (); + mgmtExchange->inc_byteReceives (msg.contentSize ()); + } +} + + +bool DirectExchange::isBound(Queue::shared_ptr queue, const string* const routingKey, const FieldTable* const) +{ + std::vector<Binding::shared_ptr>::iterator j; + + if (routingKey) { + Bindings::iterator i = bindings.find(*routingKey); + + if (i == bindings.end()) + return false; + if (!queue) + return true; + for (j = i->second.begin(); j != i->second.end(); j++) + if ((*j)->queue == queue) + return true; + } else if (!queue) { + //if no queue or routing key is specified, just report whether any bindings exist + return bindings.size() > 0; + } else { + for (Bindings::iterator i = bindings.begin(); i != bindings.end(); i++) + for (j = i->second.begin(); j != i->second.end(); j++) + if ((*j)->queue == queue) + return true; + return false; + } + + return false; +} + +DirectExchange::~DirectExchange() {} + +const std::string DirectExchange::typeName("direct"); diff --git a/qpid/cpp/src/qpid/broker/DirectExchange.h b/qpid/cpp/src/qpid/broker/DirectExchange.h new file mode 100644 index 0000000000..118f2ed4d3 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DirectExchange.h @@ -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. + * + */ +#ifndef _DirectExchange_ +#define _DirectExchange_ + +#include <map> +#include <vector> +#include "Exchange.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/Monitor.h" +#include "Queue.h" + +namespace qpid { +namespace broker { + class DirectExchange : public virtual Exchange{ + typedef std::vector<Binding::shared_ptr> Queues; + typedef std::map<string, Queues> Bindings; + Bindings bindings; + qpid::sys::RWlock lock; + + public: + static const std::string typeName; + + DirectExchange(const std::string& name, management::Manageable* parent = 0); + DirectExchange(const string& _name, bool _durable, + const qpid::framing::FieldTable& _args, management::Manageable* parent = 0); + + virtual std::string getType() const { return typeName; } + + virtual bool bind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual void route(Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args); + + virtual ~DirectExchange(); + }; +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/DtxAck.cpp b/qpid/cpp/src/qpid/broker/DtxAck.cpp new file mode 100644 index 0000000000..0e6f94d4e0 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DtxAck.cpp @@ -0,0 +1,58 @@ +/* + * + * 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. + * + */ +#include "DtxAck.h" +#include "qpid/log/Statement.h" + +using std::bind1st; +using std::bind2nd; +using std::mem_fun_ref; +using namespace qpid::broker; + +DtxAck::DtxAck(const framing::AccumulatedAck& acked, std::list<DeliveryRecord>& unacked) +{ + remove_copy_if(unacked.begin(), unacked.end(), inserter(pending, pending.end()), + not1(bind2nd(mem_fun_ref(&DeliveryRecord::coveredBy), &acked))); +} + +bool DtxAck::prepare(TransactionContext* ctxt) throw() +{ + try{ + //record dequeue in the store + for (ack_iterator i = pending.begin(); i != pending.end(); i++) { + i->dequeue(ctxt); + } + return true; + }catch(...){ + QPID_LOG(error, "Failed to prepare"); + return false; + } +} + +void DtxAck::commit() throw() +{ + pending.clear(); +} + +void DtxAck::rollback() throw() +{ + for_each(pending.begin(), pending.end(), mem_fun_ref(&DeliveryRecord::requeue)); + pending.clear(); +} diff --git a/qpid/cpp/src/qpid/broker/DtxAck.h b/qpid/cpp/src/qpid/broker/DtxAck.h new file mode 100644 index 0000000000..c61b279c42 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DtxAck.h @@ -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. + * + */ +#ifndef _DtxAck_ +#define _DtxAck_ + +#include <algorithm> +#include <functional> +#include <list> +#include "qpid/framing/AccumulatedAck.h" +#include "DeliveryRecord.h" +#include "TxOp.h" + +namespace qpid { + namespace broker { + class DtxAck : public TxOp{ + std::list<DeliveryRecord> pending; + + public: + DtxAck(const framing::AccumulatedAck& acked, std::list<DeliveryRecord>& unacked); + virtual bool prepare(TransactionContext* ctxt) throw(); + virtual void commit() throw(); + virtual void rollback() throw(); + virtual ~DtxAck(){} + }; + } +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/DtxBuffer.cpp b/qpid/cpp/src/qpid/broker/DtxBuffer.cpp new file mode 100644 index 0000000000..29a07ea6d9 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DtxBuffer.cpp @@ -0,0 +1,83 @@ +/* + * + * 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. + * + */ +#include "DtxBuffer.h" + +using namespace qpid::broker; +using qpid::sys::Mutex; + +DtxBuffer::DtxBuffer(const std::string& _xid) + : xid(_xid), ended(false), suspended(false), failed(false), expired(false) {} + +DtxBuffer::~DtxBuffer() {} + +void DtxBuffer::markEnded() +{ + Mutex::ScopedLock locker(lock); + ended = true; +} + +bool DtxBuffer::isEnded() +{ + Mutex::ScopedLock locker(lock); + return ended; +} + +void DtxBuffer::setSuspended(bool isSuspended) +{ + suspended = isSuspended; +} + +bool DtxBuffer::isSuspended() +{ + return suspended; +} + +void DtxBuffer::fail() +{ + Mutex::ScopedLock locker(lock); + rollback(); + failed = true; + ended = true; +} + +bool DtxBuffer::isRollbackOnly() +{ + Mutex::ScopedLock locker(lock); + return failed; +} + +const std::string& DtxBuffer::getXid() +{ + return xid; +} + +void DtxBuffer::timedout() +{ + Mutex::ScopedLock locker(lock); + expired = true; + fail(); +} + +bool DtxBuffer::isExpired() +{ + Mutex::ScopedLock locker(lock); + return expired; +} diff --git a/qpid/cpp/src/qpid/broker/DtxBuffer.h b/qpid/cpp/src/qpid/broker/DtxBuffer.h new file mode 100644 index 0000000000..b302632037 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DtxBuffer.h @@ -0,0 +1,56 @@ +/* + * + * 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. + * + */ +#ifndef _DtxBuffer_ +#define _DtxBuffer_ + +#include "TxBuffer.h" +#include "qpid/sys/Mutex.h" + +namespace qpid { + namespace broker { + class DtxBuffer : public TxBuffer{ + sys::Mutex lock; + const std::string xid; + bool ended; + bool suspended; + bool failed; + bool expired; + + public: + typedef boost::shared_ptr<DtxBuffer> shared_ptr; + + DtxBuffer(const std::string& xid = ""); + ~DtxBuffer(); + void markEnded(); + bool isEnded(); + void setSuspended(bool suspended); + bool isSuspended(); + void fail(); + bool isRollbackOnly(); + void timedout(); + bool isExpired(); + const std::string& getXid(); + }; + } +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/DtxHandlerImpl.cpp b/qpid/cpp/src/qpid/broker/DtxHandlerImpl.cpp new file mode 100644 index 0000000000..533872e849 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DtxHandlerImpl.cpp @@ -0,0 +1,171 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ +#include "DtxHandlerImpl.h" + +#include <boost/format.hpp> +#include "Broker.h" +#include "qpid/framing/constants.h" +#include "qpid/framing/Array.h" + +using namespace qpid::broker; +using namespace qpid::framing; +using std::string; + +DtxHandlerImpl::DtxHandlerImpl(SemanticState& s) : HandlerImpl(s) {} + +// DtxDemarcationHandler: + + +void DtxHandlerImpl::select() +{ + state.selectDtx(); +} + +DtxDemarcationEndResult DtxHandlerImpl::end(u_int16_t /*ticket*/, + const string& xid, + bool fail, + bool suspend) +{ + try { + if (fail) { + state.endDtx(xid, true); + if (suspend) { + throw CommandInvalidException(QPID_MSG("End and suspend cannot both be set.")); + } else { + return DtxDemarcationEndResult(XA_RBROLLBACK); + } + } else { + if (suspend) { + state.suspendDtx(xid); + } else { + state.endDtx(xid, false); + } + return DtxDemarcationEndResult(XA_OK); + } + } catch (const DtxTimeoutException& e) { + return DtxDemarcationEndResult(XA_RBTIMEOUT); + } +} + +DtxDemarcationStartResult DtxHandlerImpl::start(u_int16_t /*ticket*/, + const string& xid, + bool join, + bool resume) +{ + if (join && resume) { + throw CommandInvalidException(QPID_MSG("Join and resume cannot both be set.")); + } + try { + if (resume) { + state.resumeDtx(xid); + } else { + state.startDtx(xid, getBroker().getDtxManager(), join); + } + return DtxDemarcationStartResult(XA_OK); + } catch (const DtxTimeoutException& e) { + return DtxDemarcationStartResult(XA_RBTIMEOUT); + } +} + +// DtxCoordinationHandler: + +DtxCoordinationPrepareResult DtxHandlerImpl::prepare(u_int16_t /*ticket*/, + const string& xid) +{ + try { + bool ok = getBroker().getDtxManager().prepare(xid); + return DtxCoordinationPrepareResult(ok ? XA_OK : XA_RBROLLBACK); + } catch (const DtxTimeoutException& e) { + return DtxCoordinationPrepareResult(XA_RBTIMEOUT); + } +} + +DtxCoordinationCommitResult DtxHandlerImpl::commit(u_int16_t /*ticket*/, + const string& xid, + bool onePhase) +{ + try { + bool ok = getBroker().getDtxManager().commit(xid, onePhase); + return DtxCoordinationCommitResult(ok ? XA_OK : XA_RBROLLBACK); + } catch (const DtxTimeoutException& e) { + return DtxCoordinationCommitResult(XA_RBTIMEOUT); + } +} + + +DtxCoordinationRollbackResult DtxHandlerImpl::rollback(u_int16_t /*ticket*/, + const string& xid ) +{ + try { + getBroker().getDtxManager().rollback(xid); + return DtxCoordinationRollbackResult(XA_OK); + } catch (const DtxTimeoutException& e) { + return DtxCoordinationRollbackResult(XA_RBTIMEOUT); + } +} + +DtxCoordinationRecoverResult DtxHandlerImpl::recover(u_int16_t /*ticket*/, + bool /*startscan*/, + bool /*endscan*/ ) +{ + //TODO: what do startscan and endscan actually mean? + + // response should hold on key value pair with key = 'xids' and + // value = sequence of xids + + // until sequences are supported (0-10 encoding), an alternate + // scheme is used for testing: + // + // key = 'xids' and value = a longstr containing shortstrs for each xid + // + // note that this restricts the length of the xids more than is + // strictly 'legal', but that is ok for testing + std::set<std::string> xids; + getBroker().getStore().collectPreparedXids(xids); + + //TODO: remove the need to copy from one container type to another + std::vector<std::string> data; + for (std::set<std::string>::iterator i = xids.begin(); i != xids.end(); i++) { + data.push_back(*i); + } + Array indoubt(data); + return DtxCoordinationRecoverResult(indoubt); +} + +void DtxHandlerImpl::forget(u_int16_t /*ticket*/, + const string& xid) +{ + //Currently no heuristic completion is supported, so this should never be used. + throw CommandInvalidException(QPID_MSG("Forget is invalid. Branch with xid " << xid << " not heuristically completed!")); +} + +DtxCoordinationGetTimeoutResult DtxHandlerImpl::getTimeout(const string& xid) +{ + uint32_t timeout = getBroker().getDtxManager().getTimeout(xid); + return DtxCoordinationGetTimeoutResult(timeout); +} + + +void DtxHandlerImpl::setTimeout(u_int16_t /*ticket*/, + const string& xid, + u_int32_t timeout) +{ + getBroker().getDtxManager().setTimeout(xid, timeout); +} + + diff --git a/qpid/cpp/src/qpid/broker/DtxHandlerImpl.h b/qpid/cpp/src/qpid/broker/DtxHandlerImpl.h new file mode 100644 index 0000000000..5bc9d5142a --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DtxHandlerImpl.h @@ -0,0 +1,67 @@ +#ifndef _broker_DtxHandlerImpl_h +#define _broker_DtxHandlerImpl_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "qpid/framing/AMQP_ServerOperations.h" +#include "qpid/framing/AMQP_ClientProxy.h" +#include "HandlerImpl.h" + +namespace qpid { +namespace broker { + +class DtxHandlerImpl + : public HandlerImpl, + public framing::AMQP_ServerOperations::DtxCoordinationHandler, + public framing::AMQP_ServerOperations::DtxDemarcationHandler +{ +public: + DtxHandlerImpl(SemanticState&); + + // DtxCoordinationHandler: + + framing::DtxCoordinationCommitResult commit(u_int16_t ticket, const std::string& xid, bool onePhase); + + void forget(u_int16_t ticket, const std::string& xid); + + framing::DtxCoordinationGetTimeoutResult getTimeout(const std::string& xid); + + framing::DtxCoordinationPrepareResult prepare(u_int16_t ticket, const std::string& xid); + + framing::DtxCoordinationRecoverResult recover(u_int16_t ticket, bool startscan, bool endscan); + + framing::DtxCoordinationRollbackResult rollback(u_int16_t ticket, const std::string& xid); + + void setTimeout(u_int16_t ticket, const std::string& xid, u_int32_t timeout); + + // DtxDemarcationHandler: + + framing::DtxDemarcationEndResult end(u_int16_t ticket, const std::string& xid, bool fail, bool suspend); + + void select(); + + framing::DtxDemarcationStartResult start(u_int16_t ticket, const std::string& xid, bool join, bool resume); +}; + + +}} // namespace qpid::broker + + + +#endif /*!_broker_DtxHandlerImpl_h*/ diff --git a/qpid/cpp/src/qpid/broker/DtxManager.cpp b/qpid/cpp/src/qpid/broker/DtxManager.cpp new file mode 100644 index 0000000000..fb6b3f019e --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DtxManager.cpp @@ -0,0 +1,172 @@ +/* + * + * 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. + * + */ +#include "DtxManager.h" +#include "DtxTimeout.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/log/Statement.h" +#include "qpid/ptr_map.h" + +#include <boost/format.hpp> +#include <iostream> + +using boost::intrusive_ptr; +using qpid::sys::Mutex; +using namespace qpid::ptr_map; +using namespace qpid::broker; +using namespace qpid::framing; + +DtxManager::DtxManager() : store(0) {} + +DtxManager::~DtxManager() {} + +void DtxManager::start(const std::string& xid, DtxBuffer::shared_ptr ops) +{ + createWork(xid)->add(ops); +} + +void DtxManager::join(const std::string& xid, DtxBuffer::shared_ptr ops) +{ + getWork(xid)->add(ops); +} + +void DtxManager::recover(const std::string& xid, std::auto_ptr<TPCTransactionContext> txn, DtxBuffer::shared_ptr ops) +{ + createWork(xid)->recover(txn, ops); +} + +bool DtxManager::prepare(const std::string& xid) +{ + QPID_LOG(debug, "preparing: " << xid); + try { + return getWork(xid)->prepare(); + } catch (DtxTimeoutException& e) { + remove(xid); + throw e; + } +} + +bool DtxManager::commit(const std::string& xid, bool onePhase) +{ + QPID_LOG(debug, "committing: " << xid); + try { + bool result = getWork(xid)->commit(onePhase); + remove(xid); + return result; + } catch (DtxTimeoutException& e) { + remove(xid); + throw e; + } +} + +void DtxManager::rollback(const std::string& xid) +{ + QPID_LOG(debug, "rolling back: " << xid); + try { + getWork(xid)->rollback(); + remove(xid); + } catch (DtxTimeoutException& e) { + remove(xid); + throw e; + } +} + +DtxWorkRecord* DtxManager::getWork(const std::string& xid) +{ + Mutex::ScopedLock locker(lock); + WorkMap::iterator i = work.find(xid); + if (i == work.end()) { + throw InvalidArgumentException(QPID_MSG("Unrecognised xid " << xid)); + } + return get_pointer(i); +} + +void DtxManager::remove(const std::string& xid) +{ + Mutex::ScopedLock locker(lock); + WorkMap::iterator i = work.find(xid); + if (i == work.end()) { + throw InvalidArgumentException(QPID_MSG("Unrecognised xid " << xid)); + } else { + work.erase(i); + } +} + +DtxWorkRecord* DtxManager::createWork(std::string xid) +{ + Mutex::ScopedLock locker(lock); + WorkMap::iterator i = work.find(xid); + if (i != work.end()) { + throw CommandInvalidException(QPID_MSG("Xid " << xid << " is already known (use 'join' to add work to an existing xid)")); + } else { + return get_pointer(work.insert(xid, new DtxWorkRecord(xid, store)).first); + } +} + +void DtxManager::setTimeout(const std::string& xid, uint32_t secs) +{ + DtxWorkRecord* record = getWork(xid); + intrusive_ptr<DtxTimeout> timeout = record->getTimeout(); + if (timeout.get()) { + if (timeout->timeout == secs) return;//no need to do anything further if timeout hasn't changed + timeout->cancelled = true; + } + timeout = intrusive_ptr<DtxTimeout>(new DtxTimeout(secs, *this, xid)); + record->setTimeout(timeout); + timer.add(boost::static_pointer_cast<TimerTask>(timeout)); + +} + +uint32_t DtxManager::getTimeout(const std::string& xid) +{ + intrusive_ptr<DtxTimeout> timeout = getWork(xid)->getTimeout(); + return !timeout ? 0 : timeout->timeout; +} + +void DtxManager::timedout(const std::string& xid) +{ + Mutex::ScopedLock locker(lock); + WorkMap::iterator i = work.find(xid); + if (i == work.end()) { + QPID_LOG(warning, "Transaction timeout failed: no record for xid"); + } else { + get_pointer(i)->timedout(); + //TODO: do we want to have a timed task to cleanup, or can we rely on an explicit completion? + //timer.add(intrusive_ptr<TimerTask>(new DtxCleanup(60*30/*30 mins*/, *this, xid))); + } +} + +DtxManager::DtxCleanup::DtxCleanup(uint32_t _timeout, DtxManager& _mgr, const std::string& _xid) + : TimerTask(qpid::sys::Duration(_timeout * qpid::sys::TIME_SEC)), mgr(_mgr), xid(_xid) {} + +void DtxManager::DtxCleanup::fire() +{ + try { + mgr.remove(xid); + } catch (ConnectionException& e) { + //assume it was explicitly cleaned up after a call to prepare, commit or rollback + } +} + +void DtxManager::setStore (TransactionalStore* _store) +{ + assert (store == 0 && _store != 0); + store = _store; +} diff --git a/qpid/cpp/src/qpid/broker/DtxManager.h b/qpid/cpp/src/qpid/broker/DtxManager.h new file mode 100644 index 0000000000..fa5c62c233 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DtxManager.h @@ -0,0 +1,74 @@ +/* + * + * 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. + * + */ +#ifndef _DtxManager_ +#define _DtxManager_ + +#include <boost/ptr_container/ptr_map.hpp> +#include "DtxBuffer.h" +#include "DtxWorkRecord.h" +#include "Timer.h" +#include "TransactionalStore.h" +#include "qpid/framing/amqp_types.h" +#include "qpid/sys/Mutex.h" + +namespace qpid { +namespace broker { + +class DtxManager{ + typedef boost::ptr_map<std::string, DtxWorkRecord> WorkMap; + + struct DtxCleanup : public TimerTask + { + DtxManager& mgr; + const std::string& xid; + + DtxCleanup(uint32_t timeout, DtxManager& mgr, const std::string& xid); + void fire(); + }; + + WorkMap work; + TransactionalStore* store; + qpid::sys::Mutex lock; + Timer timer; + + void remove(const std::string& xid); + DtxWorkRecord* getWork(const std::string& xid); + DtxWorkRecord* createWork(std::string xid); + +public: + DtxManager(); + ~DtxManager(); + void start(const std::string& xid, DtxBuffer::shared_ptr work); + void join(const std::string& xid, DtxBuffer::shared_ptr work); + void recover(const std::string& xid, std::auto_ptr<TPCTransactionContext> txn, DtxBuffer::shared_ptr work); + bool prepare(const std::string& xid); + bool commit(const std::string& xid, bool onePhase); + void rollback(const std::string& xid); + void setTimeout(const std::string& xid, uint32_t secs); + uint32_t getTimeout(const std::string& xid); + void timedout(const std::string& xid); + void setStore(TransactionalStore* store); +}; + +} +} + +#endif diff --git a/qpid/cpp/src/qpid/broker/DtxTimeout.cpp b/qpid/cpp/src/qpid/broker/DtxTimeout.cpp new file mode 100644 index 0000000000..8e0a7741c4 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DtxTimeout.cpp @@ -0,0 +1,35 @@ +/* + * + * 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. + * + */ +#include "DtxTimeout.h" +#include "DtxManager.h" +#include "qpid/sys/Time.h" + +using namespace qpid::broker; + +DtxTimeout::DtxTimeout(uint32_t _timeout, DtxManager& _mgr, const std::string& _xid) + : TimerTask(qpid::sys::Duration(_timeout * qpid::sys::TIME_SEC)), timeout(_timeout), mgr(_mgr), xid(_xid) +{ +} + +void DtxTimeout::fire() +{ + mgr.timedout(xid); +} diff --git a/qpid/cpp/src/qpid/broker/DtxTimeout.h b/qpid/cpp/src/qpid/broker/DtxTimeout.h new file mode 100644 index 0000000000..6e949eab0d --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DtxTimeout.h @@ -0,0 +1,48 @@ +/* + * + * 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. + * + */ +#ifndef _DtxTimeout_ +#define _DtxTimeout_ + +#include "qpid/Exception.h" +#include "Timer.h" + +namespace qpid { +namespace broker { + +class DtxManager; + +struct DtxTimeoutException : public Exception {}; + +struct DtxTimeout : public TimerTask +{ + const uint32_t timeout; + DtxManager& mgr; + const std::string xid; + + DtxTimeout(uint32_t timeout, DtxManager& mgr, const std::string& xid); + void fire(); +}; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/DtxWorkRecord.cpp b/qpid/cpp/src/qpid/broker/DtxWorkRecord.cpp new file mode 100644 index 0000000000..fe9e42ca32 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DtxWorkRecord.cpp @@ -0,0 +1,177 @@ +/* + * + * 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. + * + */ +#include "DtxWorkRecord.h" +#include "qpid/framing/reply_exceptions.h" +#include <boost/format.hpp> +#include <boost/mem_fn.hpp> +using boost::mem_fn; +using qpid::sys::Mutex; + +using namespace qpid::broker; +using namespace qpid::framing; + +DtxWorkRecord::DtxWorkRecord(const std::string& _xid, TransactionalStore* const _store) : + xid(_xid), store(_store), completed(false), rolledback(false), prepared(false), expired(false) {} + +DtxWorkRecord::~DtxWorkRecord() +{ + if (timeout.get()) { + timeout->cancelled = true; + } +} + +bool DtxWorkRecord::prepare() +{ + Mutex::ScopedLock locker(lock); + if (check()) { + txn = store->begin(xid); + if (prepare(txn.get())) { + store->prepare(*txn); + prepared = true; + } else { + abort(); + //TODO: this should probably be flagged as internal error + } + } else { + //some part of the work has been marked rollback only + abort(); + } + return prepared; +} + +bool DtxWorkRecord::prepare(TransactionContext* _txn) +{ + bool succeeded(true); + for (Work::iterator i = work.begin(); succeeded && i != work.end(); i++) { + succeeded = (*i)->prepare(_txn); + } + return succeeded; +} + +bool DtxWorkRecord::commit(bool onePhase) +{ + Mutex::ScopedLock locker(lock); + if (check()) { + if (prepared) { + //already prepared i.e. 2pc + if (onePhase) { + throw CommandInvalidException(QPID_MSG("Branch with xid " << xid << " has been prepared, one-phase option not valid!")); + } + + store->commit(*txn); + txn.reset(); + + for_each(work.begin(), work.end(), mem_fn(&TxBuffer::commit)); + return true; + } else { + //1pc commit optimisation, don't need a 2pc transaction context: + if (!onePhase) { + throw CommandInvalidException(QPID_MSG("Branch with xid " << xid << " has not been prepared, one-phase option required!")); + } + std::auto_ptr<TransactionContext> localtxn = store->begin(); + if (prepare(localtxn.get())) { + store->commit(*localtxn); + for_each(work.begin(), work.end(), mem_fn(&TxBuffer::commit)); + return true; + } else { + store->abort(*localtxn); + abort(); + //TODO: this should probably be flagged as internal error + return false; + } + } + } else { + //some part of the work has been marked rollback only + abort(); + return false; + } +} + +void DtxWorkRecord::rollback() +{ + Mutex::ScopedLock locker(lock); + check(); + abort(); +} + +void DtxWorkRecord::add(DtxBuffer::shared_ptr ops) +{ + Mutex::ScopedLock locker(lock); + if (expired) { + throw DtxTimeoutException(); + } + if (completed) { + throw CommandInvalidException(QPID_MSG("Branch with xid " << xid << " has been completed!")); + } + work.push_back(ops); +} + +bool DtxWorkRecord::check() +{ + if (expired) { + throw DtxTimeoutException(); + } + if (!completed) { + //iterate through all DtxBuffers and ensure they are all ended + for (Work::iterator i = work.begin(); i != work.end(); i++) { + if (!(*i)->isEnded()) { + throw CommandInvalidException(QPID_MSG("Branch with xid " << xid << " not completed!")); + } else if ((*i)->isRollbackOnly()) { + rolledback = true; + } + } + completed = true; + } + return !rolledback; +} + +void DtxWorkRecord::abort() +{ + if (txn.get()) { + store->abort(*txn); + txn.reset(); + } + for_each(work.begin(), work.end(), mem_fn(&TxBuffer::rollback)); +} + +void DtxWorkRecord::recover(std::auto_ptr<TPCTransactionContext> _txn, DtxBuffer::shared_ptr ops) +{ + add(ops); + txn = _txn; + ops->markEnded(); + completed = true; + prepared = true; +} + +void DtxWorkRecord::timedout() +{ + Mutex::ScopedLock locker(lock); + expired = true; + rolledback = true; + if (!completed) { + for (Work::iterator i = work.begin(); i != work.end(); i++) { + if (!(*i)->isEnded()) { + (*i)->timedout(); + } + } + } + abort(); +} diff --git a/qpid/cpp/src/qpid/broker/DtxWorkRecord.h b/qpid/cpp/src/qpid/broker/DtxWorkRecord.h new file mode 100644 index 0000000000..6677784c32 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/DtxWorkRecord.h @@ -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. + * + */ +#ifndef _DtxWorkRecord_ +#define _DtxWorkRecord_ + +#include "DtxBuffer.h" +#include "DtxTimeout.h" +#include "TransactionalStore.h" + +#include "qpid/framing/amqp_types.h" +#include "qpid/sys/Mutex.h" + +#include <algorithm> +#include <functional> +#include <vector> + +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { + +/** + * Represents the work done under a particular distributed transaction + * across potentially multiple channels. Identified by a xid. Allows + * that work to be prepared, committed and rolled-back. + */ +class DtxWorkRecord +{ + typedef std::vector<DtxBuffer::shared_ptr> Work; + + const std::string xid; + TransactionalStore* const store; + bool completed; + bool rolledback; + bool prepared; + bool expired; + boost::intrusive_ptr<DtxTimeout> timeout; + Work work; + std::auto_ptr<TPCTransactionContext> txn; + qpid::sys::Mutex lock; + + bool check(); + void abort(); + bool prepare(TransactionContext* txn); +public: + DtxWorkRecord(const std::string& xid, TransactionalStore* const store); + ~DtxWorkRecord(); + bool prepare(); + bool commit(bool onePhase); + void rollback(); + void add(DtxBuffer::shared_ptr ops); + void recover(std::auto_ptr<TPCTransactionContext> txn, DtxBuffer::shared_ptr ops); + void timedout(); + void setTimeout(boost::intrusive_ptr<DtxTimeout> t) { timeout = t; } + boost::intrusive_ptr<DtxTimeout> getTimeout() { return timeout; } +}; + +} +} + +#endif diff --git a/qpid/cpp/src/qpid/broker/Exchange.cpp b/qpid/cpp/src/qpid/broker/Exchange.cpp new file mode 100644 index 0000000000..47d616cf16 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Exchange.cpp @@ -0,0 +1,141 @@ +/* + * + * 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. + * + */ + +#include "Exchange.h" +#include "ExchangeRegistry.h" +#include "qpid/management/ManagementAgent.h" + +using namespace qpid::broker; +using qpid::framing::Buffer; +using qpid::framing::FieldTable; +using qpid::management::ManagementAgent; +using qpid::management::ManagementObject; +using qpid::management::Manageable; +using qpid::management::Args; + +Exchange::Exchange (const string& _name, Manageable* parent) : + name(_name), durable(false), persistenceId(0) +{ + if (parent != 0) + { + ManagementAgent::shared_ptr agent = ManagementAgent::getAgent (); + if (agent.get () != 0) + { + mgmtExchange = management::Exchange::shared_ptr + (new management::Exchange (this, parent, _name)); + agent->addObject (mgmtExchange); + } + } +} + +Exchange::Exchange(const string& _name, bool _durable, const qpid::framing::FieldTable& _args, + Manageable* parent) + : name(_name), durable(_durable), args(_args), alternateUsers(0), persistenceId(0) +{ + if (parent != 0) + { + ManagementAgent::shared_ptr agent = ManagementAgent::getAgent (); + if (agent.get () != 0) + { + mgmtExchange = management::Exchange::shared_ptr + (new management::Exchange (this, parent, _name)); + agent->addObject (mgmtExchange); + } + } +} + +Exchange::~Exchange () +{ + if (mgmtExchange.get () != 0) + mgmtExchange->resourceDestroy (); +} + +Exchange::shared_ptr Exchange::decode(ExchangeRegistry& exchanges, Buffer& buffer) +{ + string name; + string type; + FieldTable args; + + buffer.getShortString(name); + bool durable(buffer.getOctet()); + buffer.getShortString(type); + buffer.get(args); + + return exchanges.declare(name, type, durable, args).first; +} + +void Exchange::encode(Buffer& buffer) const +{ + buffer.putShortString(name); + buffer.putOctet(durable); + buffer.putShortString(getType()); + buffer.put(args); +} + +uint32_t Exchange::encodedSize() const +{ + return name.size() + 1/*short string size*/ + + 1 /*durable*/ + + getType().size() + 1/*short string size*/ + + args.size(); +} + +ManagementObject::shared_ptr Exchange::GetManagementObject (void) const +{ + return dynamic_pointer_cast<ManagementObject> (mgmtExchange); +} + +Exchange::Binding::Binding(const string& _key, Queue::shared_ptr _queue, Exchange* parent, + FieldTable _args) + : queue(_queue), key(_key), args(_args) +{ + if (parent != 0) + { + ManagementAgent::shared_ptr agent = ManagementAgent::getAgent (); + if (agent.get() != 0) + { + ManagementObject::shared_ptr mo = queue->GetManagementObject(); + if (mo.get() != 0) + { + uint64_t queueId = mo->getObjectId(); + mgmtBinding = management::Binding::shared_ptr + (new management::Binding (this, (Manageable*) parent, queueId, key, args)); + agent->addObject (mgmtBinding); + } + } + } +} + +Exchange::Binding::~Binding () +{ + if (mgmtBinding.get () != 0) + mgmtBinding->resourceDestroy (); +} + +ManagementObject::shared_ptr Exchange::Binding::GetManagementObject () const +{ + return dynamic_pointer_cast<ManagementObject> (mgmtBinding); +} + +Manageable::status_t Exchange::Binding::ManagementMethod (uint32_t, Args&) +{ + return Manageable::STATUS_UNKNOWN_METHOD; +} diff --git a/qpid/cpp/src/qpid/broker/Exchange.h b/qpid/cpp/src/qpid/broker/Exchange.h new file mode 100644 index 0000000000..7902eb4219 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Exchange.h @@ -0,0 +1,109 @@ +#ifndef _broker_Exchange_h +#define _broker_Exchange_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. + * + */ + +#include <boost/shared_ptr.hpp> +#include "Deliverable.h" +#include "Queue.h" +#include "MessageStore.h" +#include "PersistableExchange.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/management/Manageable.h" +#include "qpid/management/Exchange.h" +#include "qpid/management/Binding.h" + +namespace qpid { + namespace broker { + using std::string; + class ExchangeRegistry; + + class Exchange : public PersistableExchange, public management::Manageable { + private: + const string name; + const bool durable; + qpid::framing::FieldTable args; + boost::shared_ptr<Exchange> alternate; + uint32_t alternateUsers; + mutable uint64_t persistenceId; + + protected: + struct Binding : public management::Manageable { + typedef boost::shared_ptr<Binding> shared_ptr; + typedef std::vector<Binding::shared_ptr> vector; + + Queue::shared_ptr queue; + const std::string key; + const framing::FieldTable args; + management::Binding::shared_ptr mgmtBinding; + + Binding(const std::string& key, const Queue::shared_ptr queue, Exchange* parent = 0, + framing::FieldTable args = framing::FieldTable ()); + ~Binding (); + management::ManagementObject::shared_ptr GetManagementObject () const; + management::Manageable::status_t ManagementMethod (uint32_t methodId, management::Args& args); + }; + + management::Exchange::shared_ptr mgmtExchange; + + public: + typedef boost::shared_ptr<Exchange> shared_ptr; + + explicit Exchange(const string& name, management::Manageable* parent = 0); + Exchange(const string& _name, bool _durable, const qpid::framing::FieldTable& _args, + management::Manageable* parent = 0); + virtual ~Exchange(); + + const string& getName() const { return name; } + bool isDurable() { return durable; } + qpid::framing::FieldTable& getArgs() { return args; } + + Exchange::shared_ptr getAlternate() { return alternate; } + void setAlternate(Exchange::shared_ptr _alternate) { alternate = _alternate; } + void incAlternateUsers() { alternateUsers++; } + void decAlternateUsers() { alternateUsers--; } + bool inUseAsAlternate() { return alternateUsers > 0; } + + virtual string getType() const = 0; + virtual bool bind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args) = 0; + virtual bool unbind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args) = 0; + virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args) = 0; + virtual void route(Deliverable& msg, const string& routingKey, const qpid::framing::FieldTable* args) = 0; + + //PersistableExchange: + void setPersistenceId(uint64_t id) const { persistenceId = id; } + uint64_t getPersistenceId() const { return persistenceId; } + uint32_t encodedSize() const; + void encode(framing::Buffer& buffer) const; + + static Exchange::shared_ptr decode(ExchangeRegistry& exchanges, framing::Buffer& buffer); + + // Manageable entry points + management::ManagementObject::shared_ptr GetManagementObject (void) const; + management::Manageable::status_t + ManagementMethod (uint32_t, management::Args&) { return management::Manageable::STATUS_UNKNOWN_METHOD; } + }; + } +} + + +#endif /*!_broker_Exchange.cpp_h*/ diff --git a/qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp b/qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp new file mode 100644 index 0000000000..58d9d5efb8 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/ExchangeRegistry.cpp @@ -0,0 +1,92 @@ +/* + * + * 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. + * + */ +#include "ExchangeRegistry.h" +#include "DirectExchange.h" +#include "FanOutExchange.h" +#include "HeadersExchange.h" +#include "TopicExchange.h" +#include "qpid/management/ManagementExchange.h" +#include "qpid/framing/reply_exceptions.h" + +using namespace qpid::broker; +using namespace qpid::sys; +using std::pair; +using qpid::framing::FieldTable; + +pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, const string& type) + throw(UnknownExchangeTypeException){ + + return declare(name, type, false, FieldTable()); +} + +pair<Exchange::shared_ptr, bool> ExchangeRegistry::declare(const string& name, const string& type, + bool durable, const FieldTable& args) + throw(UnknownExchangeTypeException){ + RWlock::ScopedWlock locker(lock); + ExchangeMap::iterator i = exchanges.find(name); + if (i == exchanges.end()) { + Exchange::shared_ptr exchange; + + if(type == TopicExchange::typeName){ + exchange = Exchange::shared_ptr(new TopicExchange(name, durable, args, parent)); + }else if(type == DirectExchange::typeName){ + exchange = Exchange::shared_ptr(new DirectExchange(name, durable, args, parent)); + }else if(type == FanOutExchange::typeName){ + exchange = Exchange::shared_ptr(new FanOutExchange(name, durable, args, parent)); + }else if (type == HeadersExchange::typeName) { + exchange = Exchange::shared_ptr(new HeadersExchange(name, durable, args, parent)); + }else if (type == ManagementExchange::typeName) { + exchange = Exchange::shared_ptr(new ManagementExchange(name, durable, args, parent)); + }else{ + throw UnknownExchangeTypeException(); + } + exchanges[name] = exchange; + return std::pair<Exchange::shared_ptr, bool>(exchange, true); + } else { + return std::pair<Exchange::shared_ptr, bool>(i->second, false); + } +} + +void ExchangeRegistry::destroy(const string& name){ + RWlock::ScopedWlock locker(lock); + ExchangeMap::iterator i = exchanges.find(name); + if (i != exchanges.end()) { + exchanges.erase(i); + } +} + +Exchange::shared_ptr ExchangeRegistry::get(const string& name){ + RWlock::ScopedRlock locker(lock); + ExchangeMap::iterator i = exchanges.find(name); + if (i == exchanges.end()) + throw framing::NotFoundException(QPID_MSG("Exchange not found: " << name)); + return i->second; +} + +namespace +{ +const std::string empty; +} + +Exchange::shared_ptr ExchangeRegistry::getDefault() +{ + return get(empty); +} diff --git a/qpid/cpp/src/qpid/broker/ExchangeRegistry.h b/qpid/cpp/src/qpid/broker/ExchangeRegistry.h new file mode 100644 index 0000000000..f39bd661fa --- /dev/null +++ b/qpid/cpp/src/qpid/broker/ExchangeRegistry.h @@ -0,0 +1,61 @@ +#ifndef _broker_ExchangeRegistry_h +#define _broker_ExchangeRegistry_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. + * + */ + +#include <map> +#include "Exchange.h" +#include "MessageStore.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/Monitor.h" +#include "qpid/management/Manageable.h" + +namespace qpid { +namespace broker { + struct UnknownExchangeTypeException{}; + + class ExchangeRegistry{ + typedef std::map<std::string, Exchange::shared_ptr> ExchangeMap; + ExchangeMap exchanges; + qpid::sys::RWlock lock; + management::Manageable* parent; + public: + ExchangeRegistry () : parent(0) {} + std::pair<Exchange::shared_ptr, bool> declare(const std::string& name, const std::string& type) + throw(UnknownExchangeTypeException); + std::pair<Exchange::shared_ptr, bool> declare(const std::string& name, const std::string& type, + bool durable, const qpid::framing::FieldTable& args = framing::FieldTable()) + throw(UnknownExchangeTypeException); + void destroy(const std::string& name); + Exchange::shared_ptr get(const std::string& name); + Exchange::shared_ptr getDefault(); + + /** + * Register the manageable parent for declared queues + */ + void setParent (management::Manageable* _parent) { parent = _parent; } + }; +} +} + + +#endif /*!_broker_ExchangeRegistry_h*/ diff --git a/qpid/cpp/src/qpid/broker/FanOutExchange.cpp b/qpid/cpp/src/qpid/broker/FanOutExchange.cpp new file mode 100644 index 0000000000..df723d2c8f --- /dev/null +++ b/qpid/cpp/src/qpid/broker/FanOutExchange.cpp @@ -0,0 +1,126 @@ +/* + * + * 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. + * + */ +#include "FanOutExchange.h" +#include <algorithm> + +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +FanOutExchange::FanOutExchange(const std::string& _name, Manageable* _parent) : + Exchange(_name, _parent) +{ + if (mgmtExchange.get() != 0) + mgmtExchange->set_type (typeName); +} + +FanOutExchange::FanOutExchange(const std::string& _name, bool _durable, + const FieldTable& _args, Manageable* _parent) : + Exchange(_name, _durable, _args, _parent) +{ + if (mgmtExchange.get() != 0) + mgmtExchange->set_type (typeName); +} + +bool FanOutExchange::bind(Queue::shared_ptr queue, const string& /*routingKey*/, const FieldTable* /*args*/){ + RWlock::ScopedWlock locker(lock); + std::vector<Binding::shared_ptr>::iterator i; + + // Add if not already present. + for (i = bindings.begin (); i != bindings.end(); i++) + if ((*i)->queue == queue) + break; + + if (i == bindings.end()) { + Binding::shared_ptr binding (new Binding ("", queue, this)); + bindings.push_back(binding); + if (mgmtExchange.get() != 0) { + mgmtExchange->inc_bindings (); + dynamic_pointer_cast<management::Queue>(queue->GetManagementObject())->inc_bindings(); + } + return true; + } else { + return false; + } +} + +bool FanOutExchange::unbind(Queue::shared_ptr queue, const string& /*routingKey*/, const FieldTable* /*args*/){ + RWlock::ScopedWlock locker(lock); + std::vector<Binding::shared_ptr>::iterator i; + + for (i = bindings.begin (); i != bindings.end(); i++) + if ((*i)->queue == queue) + break; + + if (i != bindings.end()) { + bindings.erase(i); + if (mgmtExchange.get() != 0) { + mgmtExchange->dec_bindings (); + dynamic_pointer_cast<management::Queue>(queue->GetManagementObject())->dec_bindings(); + } + return true; + } else { + return false; + } +} + +void FanOutExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* /*args*/){ + RWlock::ScopedRlock locker(lock); + uint32_t count(0); + + for(std::vector<Binding::shared_ptr>::iterator i = bindings.begin(); i != bindings.end(); ++i, count++){ + msg.deliverTo((*i)->queue); + if ((*i)->mgmtBinding.get() != 0) + (*i)->mgmtBinding->inc_msgMatched (); + } + + if (mgmtExchange.get() != 0) + { + mgmtExchange->inc_msgReceives (); + mgmtExchange->inc_byteReceives (msg.contentSize ()); + if (count == 0) + { + mgmtExchange->inc_msgDrops (); + mgmtExchange->inc_byteDrops (msg.contentSize ()); + } + else + { + mgmtExchange->inc_msgRoutes (count); + mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); + } + } +} + +bool FanOutExchange::isBound(Queue::shared_ptr queue, const string* const, const FieldTable* const) +{ + std::vector<Binding::shared_ptr>::iterator i; + + for (i = bindings.begin (); i != bindings.end(); i++) + if ((*i)->queue == queue) + break; + + return i != bindings.end(); +} + + +FanOutExchange::~FanOutExchange() {} + +const std::string FanOutExchange::typeName("fanout"); diff --git a/qpid/cpp/src/qpid/broker/FanOutExchange.h b/qpid/cpp/src/qpid/broker/FanOutExchange.h new file mode 100644 index 0000000000..4bc92f6b28 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/FanOutExchange.h @@ -0,0 +1,64 @@ +/* + * + * 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. + * + */ +#ifndef _FanOutExchange_ +#define _FanOutExchange_ + +#include <map> +#include <vector> +#include "Exchange.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/Monitor.h" +#include "Queue.h" + +namespace qpid { +namespace broker { + +class FanOutExchange : public virtual Exchange { + std::vector<Binding::shared_ptr> bindings; + qpid::sys::RWlock lock; + + public: + static const std::string typeName; + + FanOutExchange(const std::string& name, management::Manageable* parent = 0); + FanOutExchange(const string& _name, bool _durable, + const qpid::framing::FieldTable& _args, + management::Manageable* parent = 0); + + virtual std::string getType() const { return typeName; } + + virtual bool bind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual bool unbind(Queue::shared_ptr queue, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual void route(Deliverable& msg, const std::string& routingKey, const qpid::framing::FieldTable* args); + + virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args); + + virtual ~FanOutExchange(); +}; + +} +} + + + +#endif diff --git a/qpid/cpp/src/qpid/broker/HandlerImpl.h b/qpid/cpp/src/qpid/broker/HandlerImpl.h new file mode 100644 index 0000000000..4c51e2a826 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/HandlerImpl.h @@ -0,0 +1,53 @@ +#ifndef _broker_HandlerImpl_h +#define _broker_HandlerImpl_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "SemanticState.h" +#include "SessionContext.h" +#include "ConnectionState.h" + +namespace qpid { +namespace broker { + +class Broker; + +/** + * Base template for protocol handler implementations. + * Provides convenience methods for getting common session objects. + */ +class HandlerImpl { + protected: + SemanticState& state; + SessionContext& session; + + HandlerImpl(SemanticState& s) : state(s), session(s.getSession()) {} + + framing::AMQP_ClientProxy& getProxy() { return session.getProxy(); } + ConnectionState& getConnection() { return session.getConnection(); } + Broker& getBroker() { return session.getConnection().getBroker(); } +}; + +}} // namespace qpid::broker + + + +#endif /*!_broker_HandlerImpl_h*/ + + diff --git a/qpid/cpp/src/qpid/broker/HeadersExchange.cpp b/qpid/cpp/src/qpid/broker/HeadersExchange.cpp new file mode 100644 index 0000000000..5196099ed5 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/HeadersExchange.cpp @@ -0,0 +1,231 @@ +/* + * + * 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. + * + */ +#include "HeadersExchange.h" +#include "qpid/framing/FieldValue.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/log/Statement.h" +#include <algorithm> + + +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +// TODO aconway 2006-09-20: More efficient matching algorithm. +// The current search algorithm really sucks. +// Fieldtables are heavy, maybe use shared_ptr to do handle-body. + +using namespace qpid::broker; + +namespace { + const std::string all("all"); + const std::string any("any"); + const std::string x_match("x-match"); + const std::string empty; +} + +HeadersExchange::HeadersExchange(const string& _name, Manageable* _parent) : + Exchange(_name, _parent) +{ + if (mgmtExchange.get() != 0) + mgmtExchange->set_type (typeName); +} + +HeadersExchange::HeadersExchange(const std::string& _name, bool _durable, + const FieldTable& _args, Manageable* _parent) : + Exchange(_name, _durable, _args, _parent) +{ + if (mgmtExchange.get() != 0) + mgmtExchange->set_type (typeName); +} + +std::string HeadersExchange::getMatch(const FieldTable* args) +{ + if (!args) { + throw InternalErrorException(QPID_MSG("No arguments given.")); + } + FieldTable::ValuePtr what = args->get(x_match); + if (!what) { + return empty; + } + if (!what->convertsTo<std::string>()) { + throw InternalErrorException(QPID_MSG("Invalid x-match value binding to headers exchange.")); + } + return what->get<std::string>(); +} + +bool HeadersExchange::bind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable* args){ + RWlock::ScopedWlock locker(lock); + std::string what = getMatch(args); + if (what != all && what != any) + throw InternalErrorException(QPID_MSG("Invalid x-match value binding to headers exchange.")); + + Bindings::iterator i; + + for (i = bindings.begin(); i != bindings.end(); i++) + if (i->first == *args && i->second->queue == queue) + break; + + if (i == bindings.end()) { + Binding::shared_ptr binding (new Binding (bindingKey, queue, this, *args)); + HeaderMap headerMap(*args, binding); + + bindings.push_back(headerMap); + if (mgmtExchange.get() != 0) { + mgmtExchange->inc_bindings (); + dynamic_pointer_cast<management::Queue>(queue->GetManagementObject())->inc_bindings(); + } + return true; + } else { + return false; + } +} + +bool HeadersExchange::unbind(Queue::shared_ptr queue, const string& bindingKey, const FieldTable* args){ + RWlock::ScopedWlock locker(lock); + Bindings::iterator i; + for (i = bindings.begin(); i != bindings.end(); i++) { + if (bindingKey.empty() && args) { + if (i->first == *args && i->second->queue == queue) + break; + } else { + if (i->second->key == bindingKey && i->second->queue == queue) + break; + } + } + + if (i != bindings.end()) { + bindings.erase(i); + if (mgmtExchange.get() != 0) { + mgmtExchange->dec_bindings (); + dynamic_pointer_cast<management::Queue>(queue->GetManagementObject())->dec_bindings(); + } + return true; + } else { + return false; + } +} + + +void HeadersExchange::route(Deliverable& msg, const string& /*routingKey*/, const FieldTable* args){ + if (!args) return;//can't match if there were no headers passed in + + RWlock::ScopedRlock locker(lock); + uint32_t count(0); + + for (Bindings::iterator i = bindings.begin(); i != bindings.end(); ++i, count++) { + if (match(i->first, *args)) msg.deliverTo(i->second->queue); + if (i->second->mgmtBinding.get() != 0) + i->second->mgmtBinding->inc_msgMatched (); + } + + if (mgmtExchange.get() != 0) + { + mgmtExchange->inc_msgReceives (); + mgmtExchange->inc_byteReceives (msg.contentSize ()); + if (count == 0) + { + mgmtExchange->inc_msgDrops (); + mgmtExchange->inc_byteDrops (msg.contentSize ()); + } + else + { + mgmtExchange->inc_msgRoutes (count); + mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); + } + } +} + + +bool HeadersExchange::isBound(Queue::shared_ptr queue, const string* const, const FieldTable* const args) +{ + for (Bindings::iterator i = bindings.begin(); i != bindings.end(); ++i) { + if ( (!args || equal(i->first, *args)) && (!queue || i->second->queue == queue)) { + return true; + } + } + return false; +} + +HeadersExchange::~HeadersExchange() {} + +const std::string HeadersExchange::typeName("headers"); + +namespace +{ + + bool match_values(const FieldValue& bind, const FieldValue& msg) { + return bind.empty() || bind == msg; + } + +} + + +bool HeadersExchange::match(const FieldTable& bind, const FieldTable& msg) { + typedef FieldTable::ValueMap Map; + std::string what = getMatch(&bind); + if (what == all) { + for (Map::const_iterator i = bind.begin(); + i != bind.end(); + ++i) + { + if (i->first != x_match) + { + Map::const_iterator j = msg.find(i->first); + if (j == msg.end()) return false; + if (!match_values(*(i->second), *(j->second))) return false; + } + } + return true; + } else if (what == any) { + for (Map::const_iterator i = bind.begin(); + i != bind.end(); + ++i) + { + if (i->first != x_match) + { + Map::const_iterator j = msg.find(i->first); + if (j != msg.end()) { + if (match_values(*(i->second), *(j->second))) return true; + } + } + } + return false; + } else { + return false; + } +} + +bool HeadersExchange::equal(const FieldTable& a, const FieldTable& b) { + typedef FieldTable::ValueMap Map; + for (Map::const_iterator i = a.begin(); + i != a.end(); + ++i) + { + Map::const_iterator j = b.find(i->first); + if (j == b.end()) return false; + if (!match_values(*(i->second), *(j->second))) return false; + } + return true; +} + + + diff --git a/qpid/cpp/src/qpid/broker/HeadersExchange.h b/qpid/cpp/src/qpid/broker/HeadersExchange.h new file mode 100644 index 0000000000..6e101e193a --- /dev/null +++ b/qpid/cpp/src/qpid/broker/HeadersExchange.h @@ -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. + * + */ +#ifndef _HeadersExchange_ +#define _HeadersExchange_ + +#include <vector> +#include "Exchange.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/Monitor.h" +#include "Queue.h" + +namespace qpid { +namespace broker { + + +class HeadersExchange : public virtual Exchange { + typedef std::pair<qpid::framing::FieldTable, Binding::shared_ptr> HeaderMap; + typedef std::vector<HeaderMap> Bindings; + + Bindings bindings; + qpid::sys::RWlock lock; + + static std::string getMatch(const framing::FieldTable* args); + + public: + static const std::string typeName; + + HeadersExchange(const string& name, management::Manageable* parent = 0); + HeadersExchange(const string& _name, bool _durable, + const qpid::framing::FieldTable& _args, + management::Manageable* parent = 0); + + virtual std::string getType() const { return typeName; } + + virtual bool bind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual bool unbind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual void route(Deliverable& msg, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args); + + virtual ~HeadersExchange(); + + static bool match(const qpid::framing::FieldTable& bindArgs, const qpid::framing::FieldTable& msgArgs); + static bool equal(const qpid::framing::FieldTable& bindArgs, const qpid::framing::FieldTable& msgArgs); +}; + + + +} +} + +#endif diff --git a/qpid/cpp/src/qpid/broker/IncomingExecutionContext.cpp b/qpid/cpp/src/qpid/broker/IncomingExecutionContext.cpp new file mode 100644 index 0000000000..6c6cae6740 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/IncomingExecutionContext.cpp @@ -0,0 +1,143 @@ +/* + * + * 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. + * + */ + +#include "IncomingExecutionContext.h" +#include "qpid/Exception.h" + +namespace qpid { +namespace broker { + +using boost::intrusive_ptr; +using qpid::framing::AccumulatedAck; +using qpid::framing::SequenceNumber; +using qpid::framing::SequenceNumberSet; + +void IncomingExecutionContext::noop() +{ + complete(next()); +} + +void IncomingExecutionContext::flush() +{ + for (Messages::iterator i = incomplete.begin(); i != incomplete.end(); ) { + if ((*i)->isEnqueueComplete()) { + complete((*i)->getCommandId()); + i = incomplete.erase(i); + } else { + i++; + } + } + window.lwm = completed.mark; +} + +void IncomingExecutionContext::sync() +{ + while (completed.mark < window.hwm) { + wait(); + } +} + +void IncomingExecutionContext::sync(const SequenceNumber& point) +{ + while (!isComplete(point)) { + wait(); + } +} + +/** + * Every call to next() should be followed be either a call to + * complete() - in the case of commands, which are always synchronous + * - or track() - in the case of messages which may be asynchronously + * stored. + */ +SequenceNumber IncomingExecutionContext::next() +{ + return ++window.hwm; +} + +void IncomingExecutionContext::complete(const SequenceNumber& command) +{ + completed.update(command, command); +} + +void IncomingExecutionContext::track(intrusive_ptr<Message> msg) +{ + if (msg->isEnqueueComplete()) { + complete(msg->getCommandId()); + } else { + incomplete.push_back(msg); + } +} + +bool IncomingExecutionContext::isComplete(const SequenceNumber& command) +{ + if (command > window.hwm) { + throw Exception(QPID_MSG("Bad sync request: point exceeds last command received [" + << command.getValue() << " > " << window.hwm.getValue() << "]")); + } + + return completed.covers(command); +} + + +const SequenceNumber& IncomingExecutionContext::getMark() +{ + return completed.mark; +} + +SequenceNumberSet IncomingExecutionContext::getRange() +{ + SequenceNumberSet range; + completed.collectRanges(range); + return range; +} + +void IncomingExecutionContext::wait() +{ + check(); + // for IO flush on the store + for (Messages::iterator i = incomplete.begin(); i != incomplete.end(); i++) { + (*i)->flush(); + } + incomplete.front()->waitForEnqueueComplete(); + flush(); +} + +/** + * This is a check of internal state consistency. + */ +void IncomingExecutionContext::check() +{ + if (incomplete.empty()) { + if (window.hwm != completed.mark) { + //can only happen if there is a call to next() without a + //corresponding call to completed() or track() - or if + //there is a logical error in flush() or + //AccumulatedAck::update() + throw Exception(QPID_MSG("Completion tracking error: window.hwm=" + << window.hwm.getValue() << ", completed.mark=" + << completed.mark.getValue())); + } + } +} + +}} + diff --git a/qpid/cpp/src/qpid/broker/IncomingExecutionContext.h b/qpid/cpp/src/qpid/broker/IncomingExecutionContext.h new file mode 100644 index 0000000000..7380e9ae64 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/IncomingExecutionContext.h @@ -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. + * + */ +#ifndef _IncomingExecutionContext_ +#define _IncomingExecutionContext_ + +#include "Message.h" + +#include "qpid/framing/AccumulatedAck.h" +#include "qpid/framing/SequenceNumber.h" + +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { + +class IncomingExecutionContext +{ + typedef std::list<boost::intrusive_ptr<Message> > Messages; + framing::Window window; + framing::AccumulatedAck completed; + Messages incomplete; + + bool isComplete(const framing::SequenceNumber& command); + void check(); + void wait(); +public: + void noop(); + void flush(); + void sync(); + void sync(const framing::SequenceNumber& point); + framing::SequenceNumber next(); + void complete(const framing::SequenceNumber& command); + void track(boost::intrusive_ptr<Message>); + + const framing::SequenceNumber& getMark(); + framing::SequenceNumberSet getRange(); + +}; + + +}} + +#endif diff --git a/qpid/cpp/src/qpid/broker/IncompleteMessageList.cpp b/qpid/cpp/src/qpid/broker/IncompleteMessageList.cpp new file mode 100644 index 0000000000..dd7bbfc067 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/IncompleteMessageList.cpp @@ -0,0 +1,51 @@ +/* + * + * 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. + * + */ +#include "IncompleteMessageList.h" + +#include "Message.h" + +namespace qpid { +namespace broker { + +void IncompleteMessageList::add(boost::intrusive_ptr<Message> msg) +{ + incomplete.push_back(msg); +} + +void IncompleteMessageList::process(CompletionListener l, bool sync) +{ + while (!incomplete.empty()) { + boost::intrusive_ptr<Message>& msg = incomplete.front(); + if (!msg->isEnqueueComplete()) { + if (sync){ + msg->flush(); + msg->waitForEnqueueComplete(); + } else { + //leave the message as incomplete for now + return; + } + } + l(msg); + incomplete.pop_front(); + } +} + +}} diff --git a/qpid/cpp/src/qpid/broker/IncompleteMessageList.h b/qpid/cpp/src/qpid/broker/IncompleteMessageList.h new file mode 100644 index 0000000000..2cfd7bfee5 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/IncompleteMessageList.h @@ -0,0 +1,48 @@ +/* + * + * 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. + * + */ +#ifndef _IncompleteMessageList_ +#define _IncompleteMessageList_ + +#include <list> +#include <boost/intrusive_ptr.hpp> +#include <boost/function.hpp> + +namespace qpid { +namespace broker { + +class Message; + +class IncompleteMessageList +{ + typedef std::list< boost::intrusive_ptr<Message> > Messages; + Messages incomplete; + +public: + typedef boost::function<void(boost::intrusive_ptr<Message>)> CompletionListener; + + void add(boost::intrusive_ptr<Message> msg); + void process(CompletionListener l, bool sync); +}; + + +}} + +#endif diff --git a/qpid/cpp/src/qpid/broker/Message.cpp b/qpid/cpp/src/qpid/broker/Message.cpp new file mode 100644 index 0000000000..b60a95228d --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Message.cpp @@ -0,0 +1,249 @@ +/* + * + * 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. + * + */ + +#include "Message.h" +#include "ExchangeRegistry.h" +#include "qpid/framing/frame_functors.h" +#include "qpid/framing/BasicPublishBody.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/SendContent.h" +#include "qpid/framing/SequenceNumber.h" +#include "qpid/framing/TypeFilter.h" +#include "qpid/log/Statement.h" + +using boost::intrusive_ptr; +using namespace qpid::broker; +using namespace qpid::framing; +using std::string; + +TransferAdapter Message::TRANSFER; +PreviewAdapter Message::TRANSFER_99_0; + +Message::Message(const SequenceNumber& id) : frames(id), persistenceId(0), redelivered(false), loaded(false), staged(false), publisher(0), adapter(0) {} + +Message::~Message() +{ + if (staged) { + if (store) { + store->destroy(*this); + } else { + QPID_LOG(error, "Message content was staged but no store is set so it can't be destroyed"); + } + } +} + +std::string Message::getRoutingKey() const +{ + return getAdapter().getRoutingKey(frames); +} + +std::string Message::getExchangeName() const +{ + return getAdapter().getExchange(frames); +} + +const boost::shared_ptr<Exchange> Message::getExchange(ExchangeRegistry& registry) const +{ + if (!exchange) { + exchange = registry.get(getExchangeName()); + } + return exchange; +} + +bool Message::isImmediate() const +{ + return getAdapter().isImmediate(frames); +} + +const FieldTable* Message::getApplicationHeaders() const +{ + return getAdapter().getApplicationHeaders(frames); +} + +bool Message::isPersistent() +{ + return getAdapter().isPersistent(frames); +} + +bool Message::requiresAccept() +{ + return getAdapter().requiresAccept(frames); +} + +uint32_t Message::getRequiredCredit() const +{ + //add up payload for all header and content frames in the frameset + SumBodySize sum; + frames.map_if(sum, TypeFilter2<HEADER_BODY, CONTENT_BODY>()); + return sum.getSize(); +} + +void Message::encode(framing::Buffer& buffer) const +{ + //encode method and header frames + EncodeFrame f1(buffer); + frames.map_if(f1, TypeFilter2<METHOD_BODY, HEADER_BODY>()); + + //then encode the payload of each content frame + EncodeBody f2(buffer); + frames.map_if(f2, TypeFilter<CONTENT_BODY>()); +} + +void Message::encodeContent(framing::Buffer& buffer) const +{ + //encode the payload of each content frame + EncodeBody f2(buffer); + frames.map_if(f2, TypeFilter<CONTENT_BODY>()); +} + +uint32_t Message::encodedSize() const +{ + return encodedHeaderSize() + encodedContentSize(); +} + +uint32_t Message::encodedContentSize() const +{ + return frames.getContentSize(); +} + +uint32_t Message::encodedHeaderSize() const +{ + //add up the size for all method and header frames in the frameset + SumFrameSize sum; + frames.map_if(sum, TypeFilter2<METHOD_BODY, HEADER_BODY>()); + return sum.getSize(); +} + +void Message::decodeHeader(framing::Buffer& buffer) +{ + AMQFrame method; + method.decode(buffer); + frames.append(method); + + AMQFrame header; + header.decode(buffer); + frames.append(header); +} + +void Message::decodeContent(framing::Buffer& buffer) +{ + if (buffer.available()) { + //get the data as a string and set that as the content + //body on a frame then add that frame to the frameset + AMQFrame frame; + frame.setBody(AMQContentBody()); + frame.castBody<AMQContentBody>()->decode(buffer, buffer.available()); + frames.append(frame); + } else { + //adjust header flags + MarkLastSegment f; + frames.map_if(f, TypeFilter<HEADER_BODY>()); + } + //mark content loaded + loaded = true; +} + +void Message::releaseContent(MessageStore* _store) +{ + if (!store) { + store = _store; + } + if (store) { + if (!getPersistenceId()) { + intrusive_ptr<PersistableMessage> pmsg(this); + store->stage(pmsg); + staged = true; + } + //remove any content frames from the frameset + frames.remove(TypeFilter<CONTENT_BODY>()); + setContentReleased(); + } +} + +void Message::sendContent(Queue& queue, framing::FrameHandler& out, uint16_t maxFrameSize) const +{ + if (isContentReleased()) { + //load content from store in chunks of maxContentSize + uint16_t maxContentSize = maxFrameSize - AMQFrame::frameOverhead(); + uint64_t expectedSize(frames.getHeaders()->getContentLength()); + intrusive_ptr<const PersistableMessage> pmsg(this); + for (uint64_t offset = 0; offset < expectedSize; offset += maxContentSize) + { + uint64_t remaining = expectedSize - offset; + AMQFrame frame(in_place<AMQContentBody>()); + string& data = frame.castBody<AMQContentBody>()->getData(); + + store->loadContent(queue, pmsg, data, offset, + remaining > maxContentSize ? maxContentSize : remaining); + frame.setBof(false); + frame.setEof(true); + if (offset > 0) { + frame.setBos(false); + } + if (remaining > maxContentSize) { + frame.setEos(false); + } + out.handle(frame); + } + + } else { + Count c; + frames.map_if(c, TypeFilter<CONTENT_BODY>()); + + SendContent f(out, maxFrameSize, c.getCount()); + frames.map_if(f, TypeFilter<CONTENT_BODY>()); + } +} + +void Message::sendHeader(framing::FrameHandler& out, uint16_t /*maxFrameSize*/) const +{ + Relay f(out); + frames.map_if(f, TypeFilter<HEADER_BODY>()); +} + +// TODO aconway 2007-11-09: Obsolete, remove. Was used to cover over +// 0-8/0-9 message differences. +MessageAdapter& Message::getAdapter() const +{ + if (!adapter) { + if(frames.isA<MessageTransferBody>()) { + adapter = &TRANSFER_99_0; + } else if(frames.isA<Message010TransferBody>()) { + adapter = &TRANSFER; + } else { + const AMQMethodBody* method = frames.getMethod(); + if (!method) throw Exception("Can't adapt message with no method"); + else throw Exception(QPID_MSG("Can't adapt message based on " << *method)); + } + } + return *adapter; +} + +uint64_t Message::contentSize() const +{ + return frames.getContentSize(); +} + +bool Message::isContentLoaded() const +{ + return loaded; +} diff --git a/qpid/cpp/src/qpid/broker/Message.h b/qpid/cpp/src/qpid/broker/Message.h new file mode 100644 index 0000000000..561cdede59 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Message.h @@ -0,0 +1,145 @@ +#ifndef _broker_Message_h +#define _broker_Message_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. + * + */ + +#include <string> +#include <boost/shared_ptr.hpp> +#include <boost/variant.hpp> +#include "PersistableMessage.h" +#include "MessageAdapter.h" +#include "qpid/framing/amqp_types.h" + +namespace qpid { + +namespace framing { +class FieldTable; +class SequenceNumber; +} + +namespace broker { +class ConnectionToken; +class Exchange; +class ExchangeRegistry; +class MessageStore; +class Queue; + +class Message : public PersistableMessage { +public: + typedef boost::intrusive_ptr<Message> shared_ptr; + + Message(const framing::SequenceNumber& id = framing::SequenceNumber()); + ~Message(); + + uint64_t getPersistenceId() const { return persistenceId; } + void setPersistenceId(uint64_t _persistenceId) const { persistenceId = _persistenceId; } + + bool getRedelivered() const { return redelivered; } + void redeliver() { redelivered = true; } + + const ConnectionToken* getPublisher() const { return publisher; } + void setPublisher(ConnectionToken* p) { publisher = p; } + + const framing::SequenceNumber& getCommandId() { return frames.getId(); } + + uint64_t contentSize() const; + + std::string getRoutingKey() const; + const boost::shared_ptr<Exchange> getExchange(ExchangeRegistry&) const; + std::string getExchangeName() const; + bool isImmediate() const; + const framing::FieldTable* getApplicationHeaders() const; + bool isPersistent(); + bool requiresAccept(); + + framing::FrameSet& getFrames() { return frames; } + const framing::FrameSet& getFrames() const { return frames; } + + template <class T> T* getProperties() { + return frames.getHeaders()->get<T>(true); + } + + template <class T> const T* getProperties() const { + return frames.getHeaders()->get<T>(); + } + + template <class T> const T* getMethod() const { + return frames.as<T>(); + } + + template <class T> bool isA() const { + return frames.isA<T>(); + } + + uint32_t getRequiredCredit() const; + + void encode(framing::Buffer& buffer) const; + void encodeContent(framing::Buffer& buffer) const; + + /** + * @returns the size of the buffer needed to encode this + * message in its entirety + */ + uint32_t encodedSize() const; + /** + * @returns the size of the buffer needed to encode the + * 'header' of this message (not just the header frame, + * but other meta data e.g.routing key and exchange) + */ + uint32_t encodedHeaderSize() const; + uint32_t encodedContentSize() const; + + void decodeHeader(framing::Buffer& buffer); + void decodeContent(framing::Buffer& buffer); + + /** + * Releases the in-memory content data held by this + * message. Must pass in a store from which the data can + * be reloaded. + */ + void releaseContent(MessageStore* store); + + void sendContent(Queue& queue, framing::FrameHandler& out, uint16_t maxFrameSize) const; + void sendHeader(framing::FrameHandler& out, uint16_t maxFrameSize) const; + + bool isContentLoaded() const; + + private: + framing::FrameSet frames; + mutable boost::shared_ptr<Exchange> exchange; + mutable uint64_t persistenceId; + bool redelivered; + bool loaded; + bool staged; + ConnectionToken* publisher; + mutable MessageAdapter* adapter; + + static TransferAdapter TRANSFER; + static PreviewAdapter TRANSFER_99_0; + + MessageAdapter& getAdapter() const; +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/MessageAdapter.cpp b/qpid/cpp/src/qpid/broker/MessageAdapter.cpp new file mode 100644 index 0000000000..ea2882b474 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/MessageAdapter.cpp @@ -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. + * + */ + +#include "MessageAdapter.h" + +namespace { + const std::string empty; +} + +namespace qpid { +namespace broker{ + + std::string TransferAdapter::getRoutingKey(const framing::FrameSet& f) + { + const framing::DeliveryProperties010* p = f.getHeaders()->get<framing::DeliveryProperties010>(); + return p ? p->getRoutingKey() : empty; + } + + std::string TransferAdapter::getExchange(const framing::FrameSet& f) + { + return f.as<framing::Message010TransferBody>()->getDestination(); + } + + bool TransferAdapter::isImmediate(const framing::FrameSet&) + { + //TODO: delete this, immediate is no longer part of the spec + return false; + } + + const framing::FieldTable* TransferAdapter::getApplicationHeaders(const framing::FrameSet& f) + { + const framing::MessageProperties010* p = f.getHeaders()->get<framing::MessageProperties010>(); + return p ? &(p->getApplicationHeaders()) : 0; + } + + bool TransferAdapter::isPersistent(const framing::FrameSet& f) + { + const framing::DeliveryProperties010* p = f.getHeaders()->get<framing::DeliveryProperties010>(); + return p && p->getDeliveryMode() == 2; + } + + bool TransferAdapter::requiresAccept(const framing::FrameSet& f) + { + const framing::Message010TransferBody* b = f.as<framing::Message010TransferBody>(); + return b && b->getAcceptMode(); + } + + std::string PreviewAdapter::getExchange(const framing::FrameSet& f) + { + return f.as<framing::MessageTransferBody>()->getDestination(); + } + + std::string PreviewAdapter::getRoutingKey(const framing::FrameSet& f) + { + const framing::DeliveryProperties* p = f.getHeaders()->get<framing::DeliveryProperties>(); + return p ? p->getRoutingKey() : empty; + } + + const framing::FieldTable* PreviewAdapter::getApplicationHeaders(const framing::FrameSet& f) + { + const framing::MessageProperties* p = f.getHeaders()->get<framing::MessageProperties>(); + return p ? &(p->getApplicationHeaders()) : 0; + } + + bool PreviewAdapter::isPersistent(const framing::FrameSet& f) + { + const framing::DeliveryProperties* p = f.getHeaders()->get<framing::DeliveryProperties>(); + return p && p->getDeliveryMode() == 2; + } + +}} diff --git a/qpid/cpp/src/qpid/broker/MessageAdapter.h b/qpid/cpp/src/qpid/broker/MessageAdapter.h new file mode 100644 index 0000000000..9759f320ac --- /dev/null +++ b/qpid/cpp/src/qpid/broker/MessageAdapter.h @@ -0,0 +1,71 @@ +#ifndef _broker_MessageAdapter_h +#define _broker_MessageAdapter_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. + * + */ + +#include <string> +#include "qpid/framing/BasicPublishBody.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/framing/DeliveryProperties.h" +#include "qpid/framing/MessageProperties.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/Message010TransferBody.h" + +namespace qpid { +namespace broker { + +// TODO aconway 2007-11-09: No longer needed, we only have one type of message. +struct MessageAdapter +{ + virtual ~MessageAdapter() {} + + virtual std::string getRoutingKey(const framing::FrameSet& f) = 0; + virtual std::string getExchange(const framing::FrameSet& f) = 0; + virtual bool isImmediate(const framing::FrameSet& f) = 0; + virtual const framing::FieldTable* getApplicationHeaders(const framing::FrameSet& f) = 0; + virtual bool isPersistent(const framing::FrameSet& f) = 0; + virtual bool requiresAccept(const framing::FrameSet& f) = 0; +}; + +struct TransferAdapter : MessageAdapter +{ + virtual std::string getRoutingKey(const framing::FrameSet& f); + virtual std::string getExchange(const framing::FrameSet& f); + virtual const framing::FieldTable* getApplicationHeaders(const framing::FrameSet& f); + virtual bool isPersistent(const framing::FrameSet& f); + bool isImmediate(const framing::FrameSet&); + bool requiresAccept(const framing::FrameSet& f); +}; + +struct PreviewAdapter : TransferAdapter +{ + std::string getExchange(const framing::FrameSet& f); + std::string getRoutingKey(const framing::FrameSet& f); + const framing::FieldTable* getApplicationHeaders(const framing::FrameSet& f); + bool isPersistent(const framing::FrameSet& f); +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/MessageBuilder.cpp b/qpid/cpp/src/qpid/broker/MessageBuilder.cpp new file mode 100644 index 0000000000..eda71ed3da --- /dev/null +++ b/qpid/cpp/src/qpid/broker/MessageBuilder.cpp @@ -0,0 +1,123 @@ +/* + * + * 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. + * + */ +#include "MessageBuilder.h" + +#include "Message.h" +#include "MessageStore.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/reply_exceptions.h" + +using boost::intrusive_ptr; +using namespace qpid::broker; +using namespace qpid::framing; + +namespace +{ + std::string type_str(uint8_t type); +} +MessageBuilder::MessageBuilder(MessageStore* const _store, uint64_t _stagingThreshold) : + state(DORMANT), store(_store), stagingThreshold(_stagingThreshold), staging(false) {} + +void MessageBuilder::handle(AMQFrame& frame) +{ + uint8_t type = frame.getBody()->type(); + switch(state) { + case METHOD: + checkType(METHOD_BODY, type); + state = HEADER; + break; + case HEADER: + if (type == CONTENT_BODY) { + //TODO: rethink how to handle non-existent headers(?)... + //didn't get a header: add in a dummy + AMQFrame header; + header.setBody(AMQHeaderBody()); + header.setBof(false); + header.setEof(false); + message->getFrames().append(header); + } else if (type != HEADER_BODY) { + throw CommandInvalidException( + QPID_MSG("Invalid frame sequence for message, expected header or content got " + << type_str(type) << ")")); + } + state = CONTENT; + break; + case CONTENT: + checkType(CONTENT_BODY, type); + break; + default: + throw CommandInvalidException(QPID_MSG("Invalid frame sequence for message (state=" << state << ")")); + } + if (staging) { + intrusive_ptr<const PersistableMessage> cpmsg = boost::static_pointer_cast<const PersistableMessage>(message); + store->appendContent(cpmsg, frame.castBody<AMQContentBody>()->getData()); + } else { + message->getFrames().append(frame); + //have we reached the staging limit? if so stage message and release content + if (state == CONTENT && stagingThreshold && message->getFrames().getContentSize() >= stagingThreshold) { + message->releaseContent(store); + staging = true; + } + } +} + +void MessageBuilder::end() +{ + message = 0; + state = DORMANT; + staging = false; +} + +void MessageBuilder::start(const SequenceNumber& id) +{ + message = intrusive_ptr<Message>(new Message(id)); + state = METHOD; + staging = false; +} + +namespace { + +const std::string HEADER_BODY_S = "HEADER"; +const std::string METHOD_BODY_S = "METHOD"; +const std::string CONTENT_BODY_S = "CONTENT"; +const std::string HEARTBEAT_BODY_S = "HEARTBEAT"; +const std::string UNKNOWN = "unknown"; + +std::string type_str(uint8_t type) +{ + switch(type) { + case METHOD_BODY: return METHOD_BODY_S; + case HEADER_BODY: return HEADER_BODY_S; + case CONTENT_BODY: return CONTENT_BODY_S; + case HEARTBEAT_BODY: return HEARTBEAT_BODY_S; + } + return UNKNOWN; +} + +} + +void MessageBuilder::checkType(uint8_t expected, uint8_t actual) +{ + if (expected != actual) { + throw CommandInvalidException(QPID_MSG("Invalid frame sequence for message (expected " + << type_str(expected) << " got " << type_str(actual) << ")")); + } +} diff --git a/qpid/cpp/src/qpid/broker/MessageBuilder.h b/qpid/cpp/src/qpid/broker/MessageBuilder.h new file mode 100644 index 0000000000..395de024ab --- /dev/null +++ b/qpid/cpp/src/qpid/broker/MessageBuilder.h @@ -0,0 +1,57 @@ +/* + * + * 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. + * + */ +#ifndef _MessageBuilder_ +#define _MessageBuilder_ + +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/SequenceNumber.h" +#include "qpid/RefCounted.h" + +#include <boost/intrusive_ptr.hpp> + +namespace qpid { + namespace broker { + class Message; + class MessageStore; + + class MessageBuilder : public framing::FrameHandler{ + public: + MessageBuilder(MessageStore* const store, uint64_t stagingThreshold); + void handle(framing::AMQFrame& frame); + boost::intrusive_ptr<Message> getMessage() { return message; } + void start(const framing::SequenceNumber& id); + void end(); + private: + enum State {DORMANT, METHOD, HEADER, CONTENT}; + State state; + boost::intrusive_ptr<Message> message; + MessageStore* const store; + const uint64_t stagingThreshold; + bool staging; + + void checkType(uint8_t expected, uint8_t actual); + }; + } +} + + +#endif + diff --git a/qpid/cpp/src/qpid/broker/MessageDelivery.cpp b/qpid/cpp/src/qpid/broker/MessageDelivery.cpp new file mode 100644 index 0000000000..9ef7090cd9 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/MessageDelivery.cpp @@ -0,0 +1,151 @@ +/* + * + * 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. + * + */ +#include "MessageDelivery.h" + +#include "DeliveryToken.h" +#include "Message.h" +#include "Queue.h" +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/BasicDeliverBody.h" +#include "qpid/framing/BasicGetOkBody.h" +#include "qpid/framing/MessageTransferBody.h" + + +using namespace boost; +using namespace qpid::broker; +using namespace qpid::framing; + +namespace qpid{ +namespace broker{ + +struct BaseToken : DeliveryToken +{ + virtual ~BaseToken() {} + virtual AMQFrame sendMethod(intrusive_ptr<Message> msg, DeliveryId id) = 0; +}; + +struct BasicGetToken : BaseToken +{ + typedef boost::shared_ptr<BasicGetToken> shared_ptr; + + Queue::shared_ptr queue; + + BasicGetToken(Queue::shared_ptr q) : queue(q) {} + + AMQFrame sendMethod(intrusive_ptr<Message> msg, DeliveryId id) + { + return AMQFrame(in_place<BasicGetOkBody>( + ProtocolVersion(), id.getValue(), + msg->getRedelivered(), msg->getExchangeName(), + msg->getRoutingKey(), queue->getMessageCount())); + } +}; + +struct BasicConsumeToken : BaseToken +{ + typedef boost::shared_ptr<BasicConsumeToken> shared_ptr; + + const string consumer; + + BasicConsumeToken(const string c) : consumer(c) {} + + AMQFrame sendMethod(intrusive_ptr<Message> msg, DeliveryId id) + { + return AMQFrame(in_place<BasicDeliverBody>( + ProtocolVersion(), consumer, id.getValue(), + msg->getRedelivered(), msg->getExchangeName(), + msg->getRoutingKey())); + } + +}; + +struct MessageDeliveryToken : BaseToken +{ + const std::string destination; + const u_int8_t confirmMode; + const u_int8_t acquireMode; + const bool isPreview; + + MessageDeliveryToken(const std::string& d, u_int8_t c, u_int8_t a, bool p) : + destination(d), confirmMode(c), acquireMode(a), isPreview(p) {} + + AMQFrame sendMethod(intrusive_ptr<Message> msg, DeliveryId /*id*/) + { + //may need to set the redelivered flag: + if (isPreview) { + if (msg->getRedelivered()){ + msg->getProperties<DeliveryProperties>()->setRedelivered(true); + } + return AMQFrame(in_place<MessageTransferBody>( + ProtocolVersion(), 0, destination, + confirmMode, acquireMode)); + } else { + if (msg->getRedelivered()){ + msg->getProperties<DeliveryProperties010>()->setRedelivered(true); + } + return AMQFrame(in_place<Message010TransferBody>( + ProtocolVersion(), destination, confirmMode, acquireMode)); + } + } +}; + +} +} + +DeliveryToken::shared_ptr MessageDelivery::getBasicGetToken(Queue::shared_ptr queue) +{ + return DeliveryToken::shared_ptr(new BasicGetToken(queue)); +} + +DeliveryToken::shared_ptr MessageDelivery::getBasicConsumeToken(const string& consumer) +{ + return DeliveryToken::shared_ptr(new BasicConsumeToken(consumer)); +} + +DeliveryToken::shared_ptr MessageDelivery::getMessageDeliveryToken(const std::string& destination, + u_int8_t confirmMode, u_int8_t acquireMode) +{ + return DeliveryToken::shared_ptr(new MessageDeliveryToken(destination, confirmMode, acquireMode, false)); +} + +DeliveryToken::shared_ptr MessageDelivery::getPreviewMessageDeliveryToken(const std::string& destination, + u_int8_t confirmMode, u_int8_t acquireMode) +{ + return DeliveryToken::shared_ptr(new MessageDeliveryToken(destination, confirmMode, acquireMode, true)); +} + +void MessageDelivery::deliver(QueuedMessage& msg, + framing::FrameHandler& handler, + DeliveryId id, + DeliveryToken::shared_ptr token, + uint16_t framesize) +{ + //currently a message published from one class and delivered to + //another may well have the wrong headers; however we will only + //have one content class for 0-10 proper + + boost::shared_ptr<BaseToken> t = dynamic_pointer_cast<BaseToken>(token); + AMQFrame method = t->sendMethod(msg.payload, id); + method.setEof(false); + handler.handle(method); + msg.payload->sendHeader(handler, framesize); + msg.payload->sendContent(*(msg.queue), handler, framesize); +} diff --git a/qpid/cpp/src/qpid/broker/MessageDelivery.h b/qpid/cpp/src/qpid/broker/MessageDelivery.h new file mode 100644 index 0000000000..564e1456a0 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/MessageDelivery.h @@ -0,0 +1,58 @@ +#ifndef _broker_MessageDelivery_h +#define _broker_MessageDelivery_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. + * + */ +#include <boost/shared_ptr.hpp> +#include "DeliveryId.h" +#include "Consumer.h" +#include "qpid/framing/FrameHandler.h" + +namespace qpid { +namespace broker { + +class DeliveryToken; +class Message; +class Queue; + +/** + * Encapsulates the different options for message delivery currently supported. + */ +class MessageDelivery { +public: + static boost::shared_ptr<DeliveryToken> getBasicGetToken(boost::shared_ptr<Queue> queue); + static boost::shared_ptr<DeliveryToken> getBasicConsumeToken(const std::string& consumer); + static boost::shared_ptr<DeliveryToken> getPreviewMessageDeliveryToken(const std::string& destination, + u_int8_t confirmMode, + u_int8_t acquireMode); + static boost::shared_ptr<DeliveryToken> getMessageDeliveryToken(const std::string& destination, + u_int8_t confirmMode, + u_int8_t acquireMode); + + static void deliver(QueuedMessage& msg, framing::FrameHandler& out, + DeliveryId deliveryTag, boost::shared_ptr<DeliveryToken> token, uint16_t framesize); +}; + +} +} + + +#endif /*!_broker_MessageDelivery_h*/ diff --git a/qpid/cpp/src/qpid/broker/MessageHandlerImpl.cpp b/qpid/cpp/src/qpid/broker/MessageHandlerImpl.cpp new file mode 100644 index 0000000000..64c0282963 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/MessageHandlerImpl.cpp @@ -0,0 +1,210 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "qpid/Exception.h" +#include "qpid/log/Statement.h" +#include "MessageHandlerImpl.h" +#include "qpid/framing/FramingContent.h" +#include "Connection.h" +#include "Broker.h" +#include "MessageDelivery.h" +#include "qpid/framing/MessageAppendBody.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/reply_exceptions.h" +#include "BrokerAdapter.h" + +#include <boost/format.hpp> +#include <boost/cast.hpp> +#include <boost/bind.hpp> + +namespace qpid { +namespace broker { + +using namespace framing; + +MessageHandlerImpl::MessageHandlerImpl(SemanticState& s) : + HandlerImpl(s), + releaseOp(boost::bind(&SemanticState::release, &state, _1, _2, false)), + rejectOp(boost::bind(&SemanticState::reject, &state, _1, _2)) + {} + +// +// Message class method handlers +// + +void +MessageHandlerImpl::cancel(const string& destination ) +{ + state.cancel(destination); +} + +void +MessageHandlerImpl::open(const string& /*reference*/) +{ + throw NotImplementedException("References no longer supported"); +} + +void +MessageHandlerImpl::append(const std::string& /*reference*/, const std::string& /*bytes*/) +{ + throw NotImplementedException("References no longer supported"); +} + +void +MessageHandlerImpl::close(const string& /*reference*/) +{ + throw NotImplementedException("References no longer supported"); +} + +void +MessageHandlerImpl::checkpoint(const string& /*reference*/, + const string& /*identifier*/ ) +{ + throw NotImplementedException("References no longer supported"); +} + +void +MessageHandlerImpl::resume(const string& /*reference*/, + const string& /*identifier*/ ) +{ + throw NotImplementedException("References no longer supported"); +} + +void +MessageHandlerImpl::offset(uint64_t /*value*/ ) +{ + throw NotImplementedException("References no longer supported"); +} + +void +MessageHandlerImpl::get(uint16_t /*ticket*/, + const string& /*queueName*/, + const string& /*destination*/, + bool /*noAck*/ ) +{ + throw NotImplementedException("get no longer supported"); +} + +void +MessageHandlerImpl::empty() +{ + throw NotImplementedException("empty no longer supported"); +} + +void +MessageHandlerImpl::ok() +{ + throw NotImplementedException("Message.Ok no longer supported"); +} + +void +MessageHandlerImpl::qos(uint32_t prefetchSize, + uint16_t prefetchCount, + bool /*global*/ ) +{ + //TODO: handle global + state.setPrefetchSize(prefetchSize); + state.setPrefetchCount(prefetchCount); +} + +void +MessageHandlerImpl::subscribe(uint16_t /*ticket*/, + const string& queueName, + const string& destination, + bool noLocal, + u_int8_t confirmMode, + u_int8_t acquireMode, + bool exclusive, + const framing::FieldTable& filter ) +{ + Queue::shared_ptr queue = state.getQueue(queueName); + if(!destination.empty() && state.exists(destination)) + throw NotAllowedException(QPID_MSG("Consumer tags must be unique")); + + string tag = destination; + state.consume(MessageDelivery::getPreviewMessageDeliveryToken(destination, confirmMode, acquireMode), + tag, queue, noLocal, confirmMode == 1, acquireMode == 0, exclusive, &filter); +} + +void +MessageHandlerImpl::recover(bool requeue) +{ + state.recover(requeue); +} + +void +MessageHandlerImpl::reject(const SequenceNumberSet& transfers, uint16_t /*code*/, const string& /*text*/ ) +{ + transfers.processRanges(rejectOp); +} + +void MessageHandlerImpl::flow(const std::string& destination, u_int8_t unit, u_int32_t value) +{ + if (unit == 0) { + //message + state.addMessageCredit(destination, value); + } else if (unit == 1) { + //bytes + state.addByteCredit(destination, value); + } else { + //unknown + throw SyntaxErrorException(QPID_MSG("Invalid value for unit " << unit)); + } + +} + +void MessageHandlerImpl::flowMode(const std::string& destination, u_int8_t mode) +{ + if (mode == 0) { + //credit + state.setCreditMode(destination); + } else if (mode == 1) { + //window + state.setWindowMode(destination); + } else{ + throw SyntaxErrorException(QPID_MSG("Invalid value for mode " << mode)); + } +} + +void MessageHandlerImpl::flush(const std::string& destination) +{ + state.flush(destination); +} + +void MessageHandlerImpl::stop(const std::string& destination) +{ + state.stop(destination); +} + +void MessageHandlerImpl::acquire(const SequenceNumberSet& transfers, u_int8_t /*mode*/) +{ + //TODO: implement mode + + SequenceNumberSet results; + RangedOperation op = boost::bind(&SemanticState::acquire, &state, _1, _2, boost::ref(results)); + transfers.processRanges(op); + results = results.condense(); + getProxy().getMessage().acquired(results); +} + +void MessageHandlerImpl::release(const SequenceNumberSet& transfers) +{ + transfers.processRanges(releaseOp); +} + +}} // namespace qpid::broker diff --git a/qpid/cpp/src/qpid/broker/MessageHandlerImpl.h b/qpid/cpp/src/qpid/broker/MessageHandlerImpl.h new file mode 100644 index 0000000000..dd70f35dbb --- /dev/null +++ b/qpid/cpp/src/qpid/broker/MessageHandlerImpl.h @@ -0,0 +1,110 @@ +#ifndef _broker_MessageHandlerImpl_h +#define _broker_MessageHandlerImpl_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include <memory> + +#include "qpid/framing/AMQP_ServerOperations.h" +#include "qpid/framing/AMQP_ClientProxy.h" +#include "HandlerImpl.h" + +#include <boost/function.hpp> + +namespace qpid { +namespace broker { + +class Connection; +class Broker; +class MessageMessage; + +class MessageHandlerImpl : + public framing::AMQP_ServerOperations::MessageHandler, + public HandlerImpl +{ + typedef boost::function<void(DeliveryId, DeliveryId)> RangedOperation; + RangedOperation releaseOp; + RangedOperation rejectOp; + + public: + MessageHandlerImpl(SemanticState&); + + void append(const std::string& reference, const std::string& bytes); + + void cancel(const std::string& destination ); + + void checkpoint(const std::string& reference, + const std::string& identifier ); + + void close(const std::string& reference ); + + void empty(); + + void get(uint16_t ticket, + const std::string& queue, + const std::string& destination, + bool noAck ); + + void offset(uint64_t value); + + void ok(); + + void open(const std::string& reference ); + + void qos(uint32_t prefetchSize, + uint16_t prefetchCount, + bool global ); + + void recover(bool requeue ); + + void reject(const framing::SequenceNumberSet& transfers, + uint16_t code, + const std::string& text ); + + void resume(const std::string& reference, + const std::string& identifier ); + + void flow(const std::string& destination, u_int8_t unit, u_int32_t value); + + void flowMode(const std::string& destination, u_int8_t mode); + + void flush(const std::string& destination); + + void stop(const std::string& destination); + + void acquire(const framing::SequenceNumberSet& transfers, u_int8_t mode); + + void release(const framing::SequenceNumberSet& transfers); + + void subscribe(u_int16_t ticket, + const std::string& queue, + const std::string& destination, + bool noLocal, + u_int8_t confirmMode, + u_int8_t acquireMode, + bool exclusive, + const framing::FieldTable& filter); + +}; + +}} // namespace qpid::broker + + + +#endif /*!_broker_MessageHandlerImpl_h*/ diff --git a/qpid/cpp/src/qpid/broker/MessageStore.h b/qpid/cpp/src/qpid/broker/MessageStore.h new file mode 100644 index 0000000000..76469ccc50 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/MessageStore.h @@ -0,0 +1,195 @@ +/* + * + * 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. + * + */ +#ifndef _MessageStore_ +#define _MessageStore_ + +#include "PersistableExchange.h" +#include "PersistableMessage.h" +#include "PersistableQueue.h" +#include "RecoveryManager.h" +#include "TransactionalStore.h" +#include "qpid/framing/FieldTable.h" + +#include <qpid/Options.h> + +#include <boost/shared_ptr.hpp> +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { + +/** + * An abstraction of the persistent storage for messages. (In + * all methods, any pointers/references to queues or messages + * are valid only for the duration of the call). + */ +class MessageStore : public TransactionalStore, public Recoverable { +public: + + /** + * init the store, call before any other call. If not called, store + * is free to pick any defaults + * + * @param dir the directory to create logs/db's + * @param async true, enable async, false, enable sync + * @param force true, delete data on mode change, false, error on mode change + */ + virtual bool init(const Options* options) = 0; + + /** + * Record the existence of a durable queue + */ + virtual void create(PersistableQueue& queue, + const framing::FieldTable& args) = 0; + /** + * Destroy a durable queue + */ + virtual void destroy(PersistableQueue& queue) = 0; + + /** + * Record the existence of a durable exchange + */ + virtual void create(const PersistableExchange& exchange, + const framing::FieldTable& args) = 0; + /** + * Destroy a durable exchange + */ + virtual void destroy(const PersistableExchange& exchange) = 0; + + /** + * Record a binding + */ + virtual void bind(const PersistableExchange& exchange, const PersistableQueue& queue, + const std::string& key, const framing::FieldTable& args) = 0; + + /** + * Forget a binding + */ + virtual void unbind(const PersistableExchange& exchange, const PersistableQueue& queue, + const std::string& key, const framing::FieldTable& args) = 0; + + /** + * Stores a messages before it has been enqueued + * (enqueueing automatically stores the message so this is + * only required if storage is required prior to that + * point). If the message has not yet been stored it will + * store the headers as well as any content passed in. A + * persistence id will be set on the message which can be + * used to load the content or to append to it. + * + * TODO ::If it is know + * which queue the message is to be staged/ release to in cases + * of flowing tmp messages to disk for memory conservation set + * the queue ptr. This allows the store to optimize the read/writes + * for that queue and avoid searching based on id. Set queue = 0 for + * large message staging when the queue is not known. + */ + virtual void stage(boost::intrusive_ptr<PersistableMessage>& msg) = 0; + + /** + * Destroys a previously staged message. This only needs + * to be called if the message is never enqueued. (Once + * enqueued, deletion will be automatic when the message + * is dequeued from all queues it was enqueued onto). + */ + virtual void destroy(PersistableMessage& msg) = 0; + + /** + * Appends content to a previously staged message + */ + virtual void appendContent(boost::intrusive_ptr<const PersistableMessage>& msg, + const std::string& data) = 0; + + /** + * Loads (a section) of content data for the specified + * message (previously stored through a call to stage or + * enqueue) into data. The offset refers to the content + * only (i.e. an offset of 0 implies that the start of the + * content should be loaded, not the headers or related + * meta-data). + */ + virtual void loadContent(const qpid::broker::PersistableQueue& queue, + boost::intrusive_ptr<const PersistableMessage>& msg, + std::string& data, uint64_t offset, uint32_t length) = 0; + + /** + * Enqueues a message, storing the message if it has not + * been previously stored and recording that the given + * message is on the given queue. + * + * Note: that this is async so the return of the function does + * not mean the opperation is complete. + * + * @param msg the message to enqueue + * @param queue the name of the queue onto which it is to be enqueued + * @param xid (a pointer to) an identifier of the + * distributed transaction in which the operation takes + * place or null for 'local' transactions + */ + virtual void enqueue(TransactionContext* ctxt, boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue) = 0; + + /** + * Dequeues a message, recording that the given message is + * no longer on the given queue and deleting the message + * if it is no longer on any other queue. + * + * Note: that this is async so the return of the function does + * not mean the opperation is complete. + * + * @param msg the message to dequeue + * @param queue the name of the queue from which it is to be dequeued + * @param xid (a pointer to) an identifier of the + * distributed transaction in which the operation takes + * place or null for 'local' transactions + */ + virtual void dequeue(TransactionContext* ctxt, boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue) = 0; + + /** + * Flushes all async messages to disk for the specified queue + * + * Note: that this is async so the return of the function does + * not mean the opperation is complete. + * + * @param queue the name of the queue from which it is to be dequeued + */ + virtual void flush(const qpid::broker::PersistableQueue& queue)=0; + + /** + * Returns the number of outstanding AIO's for a given queue + * + * If 0, than all the enqueue / dequeues have been stored + * to disk + * + * @param queue the name of the queue to check for outstanding AIO + */ + virtual u_int32_t outstandingQueueAIO(const PersistableQueue& queue) = 0; + + + virtual ~MessageStore(){} +}; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/MessageStoreModule.cpp b/qpid/cpp/src/qpid/broker/MessageStoreModule.cpp new file mode 100644 index 0000000000..e02c87f069 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/MessageStoreModule.cpp @@ -0,0 +1,147 @@ +/* + * + * 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. + * + */ + +#include "MessageStoreModule.h" +#include <iostream> + +// This transfer protects against the unloading of the store lib prior to the handling of the exception +#define TRANSFER_EXCEPTION(fn) try { fn; } catch (std::exception& e) { throw Exception(e.what()); } + +using boost::intrusive_ptr; +using namespace qpid::broker; +using qpid::framing::FieldTable; + +MessageStoreModule::MessageStoreModule(MessageStore* _store) : store(_store) {} + +MessageStoreModule::~MessageStoreModule() +{ + delete store; +} + +bool MessageStoreModule::init(const Options*) { return true; } + +void MessageStoreModule::create(PersistableQueue& queue, const FieldTable& args) +{ + TRANSFER_EXCEPTION(store->create(queue, args)); +} + +void MessageStoreModule::destroy(PersistableQueue& queue) +{ + TRANSFER_EXCEPTION(store->destroy(queue)); +} + +void MessageStoreModule::create(const PersistableExchange& exchange, const FieldTable& args) +{ + TRANSFER_EXCEPTION(store->create(exchange, args)); +} + +void MessageStoreModule::destroy(const PersistableExchange& exchange) +{ + TRANSFER_EXCEPTION(store->destroy(exchange)); +} + +void MessageStoreModule::bind(const PersistableExchange& e, const PersistableQueue& q, + const std::string& k, const framing::FieldTable& a) +{ + TRANSFER_EXCEPTION(store->bind(e, q, k, a)); +} + +void MessageStoreModule::unbind(const PersistableExchange& e, const PersistableQueue& q, + const std::string& k, const framing::FieldTable& a) +{ + TRANSFER_EXCEPTION(store->unbind(e, q, k, a)); +} + +void MessageStoreModule::recover(RecoveryManager& registry) +{ + TRANSFER_EXCEPTION(store->recover(registry)); +} + +void MessageStoreModule::stage( intrusive_ptr<PersistableMessage>& msg) +{ + TRANSFER_EXCEPTION(store->stage(msg)); +} + +void MessageStoreModule::destroy(PersistableMessage& msg) +{ + TRANSFER_EXCEPTION(store->destroy(msg)); +} + +void MessageStoreModule::appendContent(intrusive_ptr<const PersistableMessage>& msg, const std::string& data) +{ + TRANSFER_EXCEPTION(store->appendContent(msg, data)); +} + +void MessageStoreModule::loadContent(const qpid::broker::PersistableQueue& queue, + intrusive_ptr<const PersistableMessage>& msg, string& data, uint64_t offset, uint32_t length) +{ + TRANSFER_EXCEPTION(store->loadContent(queue, msg, data, offset, length)); +} + +void MessageStoreModule::enqueue(TransactionContext* ctxt, intrusive_ptr<PersistableMessage>& msg, const PersistableQueue& queue) +{ + TRANSFER_EXCEPTION(store->enqueue(ctxt, msg, queue)); +} + +void MessageStoreModule::dequeue(TransactionContext* ctxt, intrusive_ptr<PersistableMessage>& msg, const PersistableQueue& queue) +{ + TRANSFER_EXCEPTION(store->dequeue(ctxt, msg, queue)); +} + +void MessageStoreModule::flush(const qpid::broker::PersistableQueue& queue) +{ + TRANSFER_EXCEPTION(store->flush(queue)); +} + +u_int32_t MessageStoreModule::outstandingQueueAIO(const PersistableQueue& queue) +{ + TRANSFER_EXCEPTION(return store->outstandingQueueAIO(queue)); +} + +std::auto_ptr<TransactionContext> MessageStoreModule::begin() +{ + TRANSFER_EXCEPTION(return store->begin()); +} + +std::auto_ptr<TPCTransactionContext> MessageStoreModule::begin(const std::string& xid) +{ + TRANSFER_EXCEPTION(return store->begin(xid)); +} + +void MessageStoreModule::prepare(TPCTransactionContext& txn) +{ + TRANSFER_EXCEPTION(store->prepare(txn)); +} + +void MessageStoreModule::commit(TransactionContext& ctxt) +{ + TRANSFER_EXCEPTION(store->commit(ctxt)); +} + +void MessageStoreModule::abort(TransactionContext& ctxt) +{ + TRANSFER_EXCEPTION(store->abort(ctxt)); +} + +void MessageStoreModule::collectPreparedXids(std::set<std::string>& xids) +{ + TRANSFER_EXCEPTION(store->collectPreparedXids(xids)); +} diff --git a/qpid/cpp/src/qpid/broker/MessageStoreModule.h b/qpid/cpp/src/qpid/broker/MessageStoreModule.h new file mode 100644 index 0000000000..c7ad76d8bb --- /dev/null +++ b/qpid/cpp/src/qpid/broker/MessageStoreModule.h @@ -0,0 +1,82 @@ +/* + * + * 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. + * + */ +#ifndef _MessageStoreModule_ +#define _MessageStoreModule_ + +#include "MessageStore.h" +#include "Queue.h" +#include "RecoveryManager.h" + +#include "qpid/sys/Module.h" + +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { + +/** + * A null implementation of the MessageStore interface + */ +class MessageStoreModule : public MessageStore +{ + MessageStore* store; +public: + MessageStoreModule(MessageStore* store); + + bool init(const Options* options); + std::auto_ptr<TransactionContext> begin(); + std::auto_ptr<TPCTransactionContext> begin(const std::string& xid); + void prepare(TPCTransactionContext& txn); + void commit(TransactionContext& txn); + void abort(TransactionContext& txn); + void collectPreparedXids(std::set<std::string>& xids); + + void create(PersistableQueue& queue, const framing::FieldTable& args); + void destroy(PersistableQueue& queue); + void create(const PersistableExchange& exchange, const framing::FieldTable& args); + void destroy(const PersistableExchange& exchange); + void bind(const PersistableExchange& exchange, const PersistableQueue& queue, + const std::string& key, const framing::FieldTable& args); + void unbind(const PersistableExchange& exchange, const PersistableQueue& queue, + const std::string& key, const framing::FieldTable& args); + void recover(RecoveryManager& queues); + void stage(boost::intrusive_ptr<PersistableMessage>& msg); + void destroy(PersistableMessage& msg); + void appendContent(boost::intrusive_ptr<const PersistableMessage>& msg, const std::string& data); + void loadContent(const qpid::broker::PersistableQueue& queue, + boost::intrusive_ptr<const PersistableMessage>& msg, std::string& data, + uint64_t offset, uint32_t length); + + void enqueue(TransactionContext* ctxt, boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue); + void dequeue(TransactionContext* ctxt, boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue); + u_int32_t outstandingQueueAIO(const PersistableQueue& queue); + void flush(const qpid::broker::PersistableQueue& queue); + + ~MessageStoreModule(); +}; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/NameGenerator.cpp b/qpid/cpp/src/qpid/broker/NameGenerator.cpp new file mode 100644 index 0000000000..8484f921e9 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/NameGenerator.cpp @@ -0,0 +1,32 @@ +/* + * + * 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. + * + */ +#include "NameGenerator.h" +#include <sstream> + +using namespace qpid::broker; + +NameGenerator::NameGenerator(const std::string& _base) : base(_base), counter(1) {} + +std::string NameGenerator::generate(){ + std::stringstream ss; + ss << base << counter++; + return ss.str(); +} diff --git a/qpid/cpp/src/qpid/broker/NameGenerator.h b/qpid/cpp/src/qpid/broker/NameGenerator.h new file mode 100644 index 0000000000..6ea25c9797 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/NameGenerator.h @@ -0,0 +1,39 @@ +/* + * + * 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. + * + */ +#ifndef _NameGenerator_ +#define _NameGenerator_ + +#include <string> + +namespace qpid { + namespace broker { + class NameGenerator{ + const std::string base; + unsigned int counter; + public: + NameGenerator(const std::string& base); + std::string generate(); + }; + } +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/NullMessageStore.cpp b/qpid/cpp/src/qpid/broker/NullMessageStore.cpp new file mode 100644 index 0000000000..8936b0440f --- /dev/null +++ b/qpid/cpp/src/qpid/broker/NullMessageStore.cpp @@ -0,0 +1,151 @@ +/* + * + * 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. + * + */ + +#include "NullMessageStore.h" +#include "RecoveryManager.h" +#include "qpid/log/Statement.h" + +#include <iostream> + +using boost::intrusive_ptr; + +namespace qpid{ +namespace broker{ + +const std::string nullxid = ""; + +class DummyCtxt : public TPCTransactionContext +{ + const std::string xid; +public: + DummyCtxt(const std::string& _xid) : xid(_xid) {} + static std::string getXid(TransactionContext& ctxt) + { + DummyCtxt* c(dynamic_cast<DummyCtxt*>(&ctxt)); + return c ? c->xid : nullxid; + } +}; + +} +} + +using namespace qpid::broker; + +NullMessageStore::NullMessageStore(bool _warn) : warn(_warn){} + +bool NullMessageStore::init(const Options* /*options*/) {return true;} + +void NullMessageStore::create(PersistableQueue& queue, const framing::FieldTable& /*args*/) +{ + QPID_LOG(info, "Queue '" << queue.getName() + << "' will not be durable. Persistence not enabled."); +} + +void NullMessageStore::destroy(PersistableQueue&) +{ +} + +void NullMessageStore::create(const PersistableExchange& exchange, const framing::FieldTable& /*args*/) +{ + QPID_LOG(info, "Exchange'" << exchange.getName() + << "' will not be durable. Persistence not enabled."); +} + +void NullMessageStore::destroy(const PersistableExchange& ) +{} + +void NullMessageStore::bind(const PersistableExchange&, const PersistableQueue&, const std::string&, const framing::FieldTable&){} + +void NullMessageStore::unbind(const PersistableExchange&, const PersistableQueue&, const std::string&, const framing::FieldTable&){} + +void NullMessageStore::recover(RecoveryManager&) +{ + QPID_LOG(info, "Persistence not enabled, no recovery attempted."); +} + +void NullMessageStore::stage(intrusive_ptr<PersistableMessage>&) +{ + QPID_LOG(info, "Can't stage message. Persistence not enabled."); +} + +void NullMessageStore::destroy(PersistableMessage&) +{ +} + +void NullMessageStore::appendContent(intrusive_ptr<const PersistableMessage>&, const string&) +{ + QPID_LOG(info, "Can't append content. Persistence not enabled."); +} + +void NullMessageStore::loadContent(const qpid::broker::PersistableQueue&, intrusive_ptr<const PersistableMessage>&, string&, uint64_t, uint32_t) +{ + QPID_LOG(info, "Can't load content. Persistence not enabled."); +} + +void NullMessageStore::enqueue(TransactionContext*, intrusive_ptr<PersistableMessage>& msg, const PersistableQueue& queue) +{ + msg->enqueueComplete(); + QPID_LOG(info, "Message is not durably recorded on '" << queue.getName() << "'. Persistence not enabled."); +} + +void NullMessageStore::dequeue(TransactionContext*, intrusive_ptr<PersistableMessage>& msg, const PersistableQueue&) +{ + msg->dequeueComplete(); +} + +void NullMessageStore::flush(const qpid::broker::PersistableQueue&) +{ +} + +u_int32_t NullMessageStore::outstandingQueueAIO(const PersistableQueue& ) +{ + return 0; +} + +std::auto_ptr<TransactionContext> NullMessageStore::begin() +{ + return std::auto_ptr<TransactionContext>(); +} + +std::auto_ptr<TPCTransactionContext> NullMessageStore::begin(const std::string& xid) +{ + return std::auto_ptr<TPCTransactionContext>(new DummyCtxt(xid)); +} + +void NullMessageStore::prepare(TPCTransactionContext& ctxt) +{ + prepared.insert(DummyCtxt::getXid(ctxt)); +} + +void NullMessageStore::commit(TransactionContext& ctxt) +{ + prepared.erase(DummyCtxt::getXid(ctxt)); +} + +void NullMessageStore::abort(TransactionContext& ctxt) +{ + prepared.erase(DummyCtxt::getXid(ctxt)); +} + +void NullMessageStore::collectPreparedXids(std::set<string>& out) +{ + out.insert(prepared.begin(), prepared.end()); +} diff --git a/qpid/cpp/src/qpid/broker/NullMessageStore.h b/qpid/cpp/src/qpid/broker/NullMessageStore.h new file mode 100644 index 0000000000..96d1c483a2 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/NullMessageStore.h @@ -0,0 +1,81 @@ +/* + * + * 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. + * + */ +#ifndef _NullMessageStore_ +#define _NullMessageStore_ + +#include <set> +#include "MessageStore.h" +#include "Queue.h" + +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { + +/** + * A null implementation of the MessageStore interface + */ +class NullMessageStore : public MessageStore +{ + std::set<std::string> prepared; + const bool warn; +public: + NullMessageStore(bool warn = false); + + virtual bool init(const Options* options); + virtual std::auto_ptr<TransactionContext> begin(); + virtual std::auto_ptr<TPCTransactionContext> begin(const std::string& xid); + virtual void prepare(TPCTransactionContext& txn); + virtual void commit(TransactionContext& txn); + virtual void abort(TransactionContext& txn); + virtual void collectPreparedXids(std::set<std::string>& xids); + + virtual void create(PersistableQueue& queue, const framing::FieldTable& args); + virtual void destroy(PersistableQueue& queue); + virtual void create(const PersistableExchange& exchange, const framing::FieldTable& args); + virtual void destroy(const PersistableExchange& exchange); + + virtual void bind(const PersistableExchange& exchange, const PersistableQueue& queue, + const std::string& key, const framing::FieldTable& args); + virtual void unbind(const PersistableExchange& exchange, const PersistableQueue& queue, + const std::string& key, const framing::FieldTable& args); + virtual void recover(RecoveryManager& queues); + virtual void stage(boost::intrusive_ptr<PersistableMessage>& msg); + virtual void destroy(PersistableMessage& msg); + virtual void appendContent(boost::intrusive_ptr<const PersistableMessage>& msg, + const std::string& data); + virtual void loadContent(const qpid::broker::PersistableQueue& queue, + boost::intrusive_ptr<const PersistableMessage>& msg, std::string& data, + uint64_t offset, uint32_t length); + virtual void enqueue(TransactionContext* ctxt, boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue); + virtual void dequeue(TransactionContext* ctxt, boost::intrusive_ptr<PersistableMessage>& msg, + const PersistableQueue& queue); + virtual u_int32_t outstandingQueueAIO(const PersistableQueue& queue); + virtual void flush(const qpid::broker::PersistableQueue& queue); + ~NullMessageStore(){} +}; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/OwnershipToken.h b/qpid/cpp/src/qpid/broker/OwnershipToken.h new file mode 100644 index 0000000000..effd2f5b3c --- /dev/null +++ b/qpid/cpp/src/qpid/broker/OwnershipToken.h @@ -0,0 +1,38 @@ +/* + * + * 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. + * + */ +#ifndef _OwnershipToken_ +#define _OwnershipToken_ + +namespace qpid { +namespace broker { + +class ConnectionToken; + +class OwnershipToken{ +public: + virtual bool isLocal(const ConnectionToken* t) const = 0; + virtual ~OwnershipToken(){} +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/Persistable.h b/qpid/cpp/src/qpid/broker/Persistable.h new file mode 100644 index 0000000000..36499c7a1a --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Persistable.h @@ -0,0 +1,63 @@ +#ifndef _broker_Persistable_h +#define _broker_Persistable_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. + * + */ + +#include "qpid/framing/amqp_types.h" +#include "qpid/framing/Buffer.h" +#include "qpid/RefCounted.h" + +namespace qpid { +namespace broker { + +/** + * Base class for all persistable objects + */ +class Persistable : public RefCounted +{ +public: + /** + * Allows the store to attach its own identifier to this object + */ + virtual void setPersistenceId(uint64_t id) const = 0; + /** + * Returns any identifier the store may have attached to this + * object + */ + virtual uint64_t getPersistenceId() const = 0; + /** + * Encodes the persistable state of this object into the supplied + * buffer + */ + virtual void encode(framing::Buffer& buffer) const = 0; + /** + * @returns the size of the buffer needed to encode this object + */ + virtual uint32_t encodedSize() const = 0; + + virtual ~Persistable() {}; +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/PersistableExchange.h b/qpid/cpp/src/qpid/broker/PersistableExchange.h new file mode 100644 index 0000000000..683b740ddc --- /dev/null +++ b/qpid/cpp/src/qpid/broker/PersistableExchange.h @@ -0,0 +1,45 @@ +#ifndef _broker_PersistableExchange_h +#define _broker_PersistableExchange_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. + * + */ + +#include <string> +#include "Persistable.h" + +namespace qpid { +namespace broker { + +/** + * The interface exchanges must expose to the MessageStore in order to be + * persistable. + */ +class PersistableExchange : public Persistable +{ +public: + virtual const std::string& getName() const = 0; + virtual ~PersistableExchange() {}; +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/PersistableMessage.cpp b/qpid/cpp/src/qpid/broker/PersistableMessage.cpp new file mode 100644 index 0000000000..3bf390faf3 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/PersistableMessage.cpp @@ -0,0 +1,48 @@ +/* + * + * 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. + * + */ + + +#include "PersistableMessage.h" +#include "MessageStore.h" +#include <iostream> + +using namespace qpid::broker; + +void PersistableMessage::flush() +{ + syncList copy; + { + sys::ScopedLock<sys::Mutex> l(storeLock); + if (store) { + copy = synclist; + } else { + return;//early exit as nothing to do + } + } + for (syncList::iterator i = copy.begin(); i != copy.end(); ++i) { + PersistableQueue::shared_ptr q(i->lock()); + if (q) { + store->flush(*q); + } + } +} + + diff --git a/qpid/cpp/src/qpid/broker/PersistableMessage.h b/qpid/cpp/src/qpid/broker/PersistableMessage.h new file mode 100644 index 0000000000..d5977665fe --- /dev/null +++ b/qpid/cpp/src/qpid/broker/PersistableMessage.h @@ -0,0 +1,188 @@ +#ifndef _broker_PersistableMessage_h +#define _broker_PersistableMessage_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. + * + */ + +#include <string> +#include <list> +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include "Persistable.h" +#include "qpid/framing/amqp_types.h" +#include "qpid/sys/Monitor.h" +#include "PersistableQueue.h" + +namespace qpid { +namespace broker { + +class MessageStore; + +/** + * The interface messages must expose to the MessageStore in order to + * be persistable. + */ +class PersistableMessage : public Persistable +{ + sys::Monitor asyncEnqueueLock; + sys::Monitor asyncDequeueLock; + sys::Mutex storeLock; + + /** + * Tracks the number of outstanding asynchronous enqueue + * operations. When the message is enqueued asynchronously the + * count is incremented; when that enqueue completes it is + * decremented. Thus when it is 0, there are no outstanding + * enqueues. + */ + int asyncEnqueueCounter; + + /** + * Tracks the number of outstanding asynchronous dequeue + * operations. When the message is dequeued asynchronously the + * count is incremented; when that dequeue completes it is + * decremented. Thus when it is 0, there are no outstanding + * dequeues. + */ + int asyncDequeueCounter; +protected: + typedef std::list< boost::weak_ptr<PersistableQueue> > syncList; + syncList synclist; + MessageStore* store; + bool contentReleased; + + inline void setContentReleased() {contentReleased = true; } + +public: + typedef boost::shared_ptr<PersistableMessage> shared_ptr; + + /** + * @returns the size of the headers when encoded + */ + virtual uint32_t encodedHeaderSize() const = 0; + + virtual ~PersistableMessage() {}; + + PersistableMessage(): + asyncEnqueueCounter(0), + asyncDequeueCounter(0), + store(0), + contentReleased(false) + {} + + void flush(); + + inline bool isContentReleased()const {return contentReleased; } + + inline void waitForEnqueueComplete() { + sys::ScopedLock<sys::Monitor> l(asyncEnqueueLock); + while (asyncEnqueueCounter > 0) { + asyncEnqueueLock.wait(); + } + } + + inline bool isEnqueueComplete() { + sys::ScopedLock<sys::Monitor> l(asyncEnqueueLock); + return asyncEnqueueCounter == 0; + } + + inline void enqueueComplete() { + bool notify = false; + { + sys::ScopedLock<sys::Monitor> l(asyncEnqueueLock); + if (asyncEnqueueCounter > 0) { + if (--asyncEnqueueCounter == 0) { + asyncEnqueueLock.notify(); + notify = true; + } + } + } + if (notify) { + sys::ScopedLock<sys::Mutex> l(storeLock); + if (store) { + for (syncList::iterator i = synclist.begin(); i != synclist.end(); ++i) { + PersistableQueue::shared_ptr q(i->lock()); + if (q) q->notifyDurableIOComplete(); + } + //synclist.clear(); + } + } + } + + inline void enqueueAsync(PersistableQueue::shared_ptr queue, MessageStore* _store) { + if (_store){ + sys::ScopedLock<sys::Mutex> l(storeLock); + store = _store; + boost::weak_ptr<PersistableQueue> q(queue); + synclist.push_back(q); + } + enqueueAsync(); + } + + inline void enqueueAsync() { + sys::ScopedLock<sys::Monitor> l(asyncEnqueueLock); + asyncEnqueueCounter++; + } + + inline bool isDequeueComplete() { + sys::ScopedLock<sys::Monitor> l(asyncDequeueLock); + return asyncDequeueCounter == 0; + } + + inline void dequeueComplete() { + + sys::ScopedLock<sys::Monitor> l(asyncDequeueLock); + if (asyncDequeueCounter > 0) { + if (--asyncDequeueCounter == 0) { + asyncDequeueLock.notify(); + } + } + } + + inline void waitForDequeueComplete() { + sys::ScopedLock<sys::Monitor> l(asyncDequeueLock); + while (asyncDequeueCounter > 0) { + asyncDequeueLock.wait(); + } + } + + inline void dequeueAsync(PersistableQueue::shared_ptr queue, MessageStore* _store) { + if (_store){ + sys::ScopedLock<sys::Mutex> l(storeLock); + store = _store; + boost::weak_ptr<PersistableQueue> q(queue); + synclist.push_back(q); + } + dequeueAsync(); + } + + inline void dequeueAsync() { + sys::ScopedLock<sys::Monitor> l(asyncDequeueLock); + asyncDequeueCounter++; + } + + +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/PersistableQueue.h b/qpid/cpp/src/qpid/broker/PersistableQueue.h new file mode 100644 index 0000000000..9236814ae3 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/PersistableQueue.h @@ -0,0 +1,87 @@ +#ifndef _broker_PersistableQueue_h +#define _broker_PersistableQueue_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. + * + */ + +#include <string> +#include "Persistable.h" +#include "qpid/management/Manageable.h" +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace broker { + + +/** +* Empty class to be used by any module that wanted to set an external per queue store into +* persistableQueue +*/ + +class ExternalQueueStore : public management::Manageable +{ +public: + virtual ~ExternalQueueStore() {}; + +}; + + +/** + * The interface queues must expose to the MessageStore in order to be + * persistable. + */ +class PersistableQueue : public Persistable +{ +public: + typedef boost::shared_ptr<PersistableQueue> shared_ptr; + + virtual const std::string& getName() const = 0; + virtual ~PersistableQueue() { + if (externalQueueStore) + delete externalQueueStore; + }; + + virtual void setExternalQueueStore(ExternalQueueStore* inst) = 0; + + inline ExternalQueueStore* getExternalQueueStore() const {return externalQueueStore;}; + + PersistableQueue():externalQueueStore(NULL){ + }; + + + /** + * call back to signal async AIO writes have + * completed (enqueue/dequeue etc) + * + * Note: DO NOT do work on this callback, if you block + * this callback you will block the store. + */ + virtual void notifyDurableIOComplete() = 0; +protected: + + ExternalQueueStore* externalQueueStore; + +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/Prefetch.h b/qpid/cpp/src/qpid/broker/Prefetch.h new file mode 100644 index 0000000000..8eb27a3e21 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Prefetch.h @@ -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. + * + */ +#ifndef _Prefetch_ +#define _Prefetch_ + +#include "qpid/framing/amqp_types.h" + +namespace qpid { + namespace broker { + /** + * Count and total size of asynchronously delivered + * (i.e. pushed) messages that have acks outstanding. + */ + struct Prefetch{ + uint32_t size; + uint16_t count; + + void reset() { size = 0; count = 0; } + }; + } +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/PreviewConnection.cpp b/qpid/cpp/src/qpid/broker/PreviewConnection.cpp new file mode 100644 index 0000000000..6f411c99d6 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/PreviewConnection.cpp @@ -0,0 +1,341 @@ +/* + * + * 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. + * + */ + +#include "config.h" + +#include "PreviewConnection.h" +#include "SessionState.h" +#include "BrokerAdapter.h" +#include "Bridge.h" +#include "SemanticHandler.h" + +#include "qpid/log/Statement.h" +#include "qpid/ptr_map.h" +#include "qpid/framing/AMQP_ClientProxy.h" +#include "qpid/management/ManagementAgent.h" + +#include <boost/bind.hpp> +#include <boost/ptr_container/ptr_vector.hpp> + +#include <algorithm> +#include <iostream> +#include <assert.h> + +#if HAVE_SASL +#include <sasl/sasl.h> +#endif + +using namespace boost; +using namespace qpid::sys; +using namespace qpid::framing; +using namespace qpid::sys; +using namespace qpid::ptr_map; +using qpid::management::ManagementAgent; +using qpid::management::ManagementObject; +using qpid::management::Manageable; +using qpid::management::Args; + +namespace qpid { +namespace broker { + +class PreviewConnection::MgmtClient : public PreviewConnection::MgmtWrapper +{ + management::Client::shared_ptr mgmtClient; + +public: + MgmtClient(PreviewConnection* conn, Manageable* parent, ManagementAgent::shared_ptr agent, const std::string& mgmtId); + ~MgmtClient(); + void received(framing::AMQFrame& frame); + management::ManagementObject::shared_ptr getManagementObject() const; + void closing(); +}; + +class PreviewConnection::MgmtLink : public PreviewConnection::MgmtWrapper +{ + typedef boost::ptr_vector<Bridge> Bridges; + + management::Link::shared_ptr mgmtLink; + Bridges created;//holds list of bridges pending creation + Bridges cancelled;//holds list of bridges pending cancellation + Bridges active;//holds active bridges + uint channelCounter; + sys::Mutex linkLock; + + void cancel(Bridge*); + +public: + MgmtLink(PreviewConnection* conn, Manageable* parent, ManagementAgent::shared_ptr agent, const std::string& mgmtId); + ~MgmtLink(); + void received(framing::AMQFrame& frame); + management::ManagementObject::shared_ptr getManagementObject() const; + void closing(); + void processPending(); + void process(PreviewConnection& connection, const management::Args& args); +}; + + +PreviewConnection::PreviewConnection(ConnectionOutputHandler* out_, Broker& broker_, const std::string& mgmtId_, bool isLink) : + ConnectionState(out_, broker_), +#if HAVE_SASL + sasl_conn(NULL), +#endif + adapter(*this, isLink), + mgmtClosing(false), + mgmtId(mgmtId_) +{ + Manageable* parent = broker.GetVhostObject (); + + if (parent != 0) + { + ManagementAgent::shared_ptr agent = ManagementAgent::getAgent (); + + if (agent.get () != 0) + { + if (isLink) { + mgmtWrapper = std::auto_ptr<MgmtWrapper>(new MgmtLink(this, parent, agent, mgmtId)); + } else { + mgmtWrapper = std::auto_ptr<MgmtWrapper>(new MgmtClient(this, parent, agent, mgmtId)); + } + } + } +} + +PreviewConnection::~PreviewConnection () { +#if HAVE_LIBSASL2 + if (NULL != sasl_conn) { + sasl_dispose(&sasl_conn); + sasl_conn = NULL; + } +#endif +} + +void PreviewConnection::received(framing::AMQFrame& frame){ + if (mgmtClosing) + close (403, "Closed by Management Request", 0, 0); + + if (frame.getChannel() == 0) { + adapter.handle(frame); + } else { + getChannel(frame.getChannel()).in(frame); + } + + if (mgmtWrapper.get()) mgmtWrapper->received(frame); +} + +void PreviewConnection::close( + ReplyCode code, const string& text, ClassId classId, MethodId methodId) +{ + adapter.close(code, text, classId, methodId); + channels.clear(); + getOutput().close(); +} + +void PreviewConnection::idleOut(){} + +void PreviewConnection::idleIn(){} + +void PreviewConnection::closed(){ // Physically closed, suspend open sessions. + try { + for (ChannelMap::iterator i = channels.begin(); i != channels.end(); ++i) + get_pointer(i)->localSuspend(); + while (!exclusiveQueues.empty()) { + Queue::shared_ptr q(exclusiveQueues.front()); + q->releaseExclusiveOwnership(); + if (q->canAutoDelete()) { + Queue::tryAutoDelete(broker, q); + } + exclusiveQueues.erase(exclusiveQueues.begin()); + } + } catch(std::exception& e) { + QPID_LOG(error, " Unhandled exception while closing session: " << + e.what()); + assert(0); + } +} + +bool PreviewConnection::doOutput() +{ + try{ + //process any pending mgmt commands: + if (mgmtWrapper.get()) mgmtWrapper->processPending(); + if (mgmtClosing) close (403, "Closed by Management Request", 0, 0); + + + //then do other output as needed: + return outputTasks.doOutput(); + }catch(ConnectionException& e){ + close(e.code, e.what(), 0, 0); + }catch(std::exception& e){ + close(541/*internal error*/, e.what(), 0, 0); + } + return false; +} + +void PreviewConnection::closeChannel(uint16_t id) { + ChannelMap::iterator i = channels.find(id); + if (i != channels.end()) channels.erase(i); +} + +PreviewSessionHandler& PreviewConnection::getChannel(ChannelId id) { + ChannelMap::iterator i=channels.find(id); + if (i == channels.end()) { + i = channels.insert(id, new PreviewSessionHandler(*this, id)).first; + } + return *get_pointer(i); +} + +ManagementObject::shared_ptr PreviewConnection::GetManagementObject (void) const +{ + return mgmtWrapper.get() ? mgmtWrapper->getManagementObject() : ManagementObject::shared_ptr(); +} + +Manageable::status_t PreviewConnection::ManagementMethod (uint32_t methodId, + Args& args) +{ + Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; + + QPID_LOG (debug, "PreviewConnection::ManagementMethod [id=" << methodId << "]"); + + switch (methodId) + { + case management::Client::METHOD_CLOSE : + mgmtClosing = true; + if (mgmtWrapper.get()) mgmtWrapper->closing(); + out->activateOutput(); + status = Manageable::STATUS_OK; + break; + case management::Link::METHOD_BRIDGE : + //queue this up and request chance to do output (i.e. get connections thread of control): + mgmtWrapper->process(*this, args); + out->activateOutput(); + status = Manageable::STATUS_OK; + break; + } + + return status; +} + +PreviewConnection::MgmtLink::MgmtLink(PreviewConnection* conn, Manageable* parent, ManagementAgent::shared_ptr agent, const std::string& mgmtId) + : channelCounter(1) +{ + mgmtLink = management::Link::shared_ptr + (new management::Link(conn, parent, mgmtId)); + agent->addObject (mgmtLink); +} + +PreviewConnection::MgmtLink::~MgmtLink() +{ + if (mgmtLink.get () != 0) + mgmtLink->resourceDestroy (); +} + +void PreviewConnection::MgmtLink::received(framing::AMQFrame& frame) +{ + if (mgmtLink.get () != 0) + { + mgmtLink->inc_framesFromPeer (); + mgmtLink->inc_bytesFromPeer (frame.size ()); + } +} + +management::ManagementObject::shared_ptr PreviewConnection::MgmtLink::getManagementObject() const +{ + return dynamic_pointer_cast<ManagementObject>(mgmtLink); +} + +void PreviewConnection::MgmtLink::closing() +{ + if (mgmtLink) mgmtLink->set_closing (1); +} + +void PreviewConnection::MgmtLink::processPending() +{ + Mutex::ScopedLock l(linkLock); + //process any pending creates + if (!created.empty()) { + for (Bridges::iterator i = created.begin(); i != created.end(); ++i) { + i->create(); + } + active.transfer(active.end(), created.begin(), created.end(), created); + } + if (!cancelled.empty()) { + //process any pending cancellations + for (Bridges::iterator i = cancelled.begin(); i != cancelled.end(); ++i) { + i->cancel(); + } + cancelled.clear(); + } +} + +void PreviewConnection::MgmtLink::process(PreviewConnection& connection, const management::Args& args) +{ + Mutex::ScopedLock l(linkLock); + created.push_back(new Bridge(channelCounter++, connection, + boost::bind(&MgmtLink::cancel, this, _1), + dynamic_cast<const management::ArgsLinkBridge&>(args))); +} + +void PreviewConnection::MgmtLink::cancel(Bridge* b) +{ + Mutex::ScopedLock l(linkLock); + //need to take this out the active map and add it to the cancelled map + for (Bridges::iterator i = active.begin(); i != active.end(); i++) { + if (&(*i) == b) { + cancelled.transfer(cancelled.end(), i, active); + break; + } + } +} + +PreviewConnection::MgmtClient::MgmtClient(PreviewConnection* conn, Manageable* parent, ManagementAgent::shared_ptr agent, const std::string& mgmtId) +{ + mgmtClient = management::Client::shared_ptr + (new management::Client (conn, parent, mgmtId)); + agent->addObject (mgmtClient); +} + +PreviewConnection::MgmtClient::~MgmtClient() +{ + if (mgmtClient.get () != 0) + mgmtClient->resourceDestroy (); +} + +void PreviewConnection::MgmtClient::received(framing::AMQFrame& frame) +{ + if (mgmtClient.get () != 0) + { + mgmtClient->inc_framesFromClient (); + mgmtClient->inc_bytesFromClient (frame.size ()); + } +} + +management::ManagementObject::shared_ptr PreviewConnection::MgmtClient::getManagementObject() const +{ + return dynamic_pointer_cast<ManagementObject>(mgmtClient); +} + +void PreviewConnection::MgmtClient::closing() +{ + if (mgmtClient) mgmtClient->set_closing (1); +} + +}} + diff --git a/qpid/cpp/src/qpid/broker/PreviewConnection.h b/qpid/cpp/src/qpid/broker/PreviewConnection.h new file mode 100644 index 0000000000..c9e8b115d3 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/PreviewConnection.h @@ -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. + * + */ +#ifndef _PreviewConnection_ +#define _PreviewConnection_ + +#include "config.h" + +#include <memory> +#include <sstream> +#include <vector> + +#include <boost/ptr_container/ptr_map.hpp> + +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/AMQP_ServerOperations.h" +#include "qpid/framing/AMQP_ClientProxy.h" +#include "qpid/sys/AggregateOutput.h" +#include "qpid/sys/ConnectionOutputHandler.h" +#include "qpid/sys/ConnectionInputHandler.h" +#include "qpid/sys/TimeoutHandler.h" +#include "qpid/framing/ProtocolVersion.h" +#include "Broker.h" +#include "qpid/sys/Socket.h" +#include "qpid/Exception.h" +#include "PreviewConnectionHandler.h" +#include "ConnectionState.h" +#include "PreviewSessionHandler.h" +#include "qpid/management/Manageable.h" +#include "qpid/management/Client.h" +#include "qpid/management/Link.h" + +#include <boost/ptr_container/ptr_map.hpp> + +#if HAVE_SASL +#include <sasl/sasl.h> +#endif + +namespace qpid { +namespace broker { + +class PreviewConnection : public sys::ConnectionInputHandler, public ConnectionState +{ +#if HAVE_SASL + friend class PreviewConnectionHandler; +#endif + + public: + PreviewConnection(sys::ConnectionOutputHandler* out, Broker& broker, const std::string& mgmtId, bool isLink = false); + ~PreviewConnection (); + + /** Get the PreviewSessionHandler for channel. Create if it does not already exist */ + PreviewSessionHandler& getChannel(framing::ChannelId channel); + + /** Close the connection */ + void close(framing::ReplyCode code, const string& text, framing::ClassId classId, framing::MethodId methodId); + + // ConnectionInputHandler methods + void received(framing::AMQFrame& frame); + void idleOut(); + void idleIn(); + void closed(); + bool doOutput(); + + void closeChannel(framing::ChannelId channel); + + // Manageable entry points + management::ManagementObject::shared_ptr GetManagementObject (void) const; + management::Manageable::status_t + ManagementMethod (uint32_t methodId, management::Args& args); + + protected: +#if HAVE_SASL + sasl_conn_t *sasl_conn; +#endif + + private: + typedef boost::ptr_map<framing::ChannelId, PreviewSessionHandler> ChannelMap; + typedef std::vector<Queue::shared_ptr>::iterator queue_iterator; + + /** + * Connection may appear, for the purposes of management, as a + * normal client initiated connection or as an agent initiated + * inter-broker link. This wrapper abstracts the common interface + * for both. + */ + class MgmtWrapper + { + public: + virtual ~MgmtWrapper(){} + virtual void received(framing::AMQFrame& frame) = 0; + virtual management::ManagementObject::shared_ptr getManagementObject() const = 0; + virtual void closing() = 0; + virtual void processPending(){} + virtual void process(PreviewConnection&, const management::Args&){} + }; + class MgmtClient; + class MgmtLink; + + ChannelMap channels; + framing::AMQP_ClientProxy::Connection* client; + uint64_t stagingThreshold; + PreviewConnectionHandler adapter; + std::auto_ptr<MgmtWrapper> mgmtWrapper; + bool mgmtClosing; + const std::string mgmtId; +}; + +}} + +#endif diff --git a/qpid/cpp/src/qpid/broker/PreviewConnectionCodec.cpp b/qpid/cpp/src/qpid/broker/PreviewConnectionCodec.cpp new file mode 100644 index 0000000000..b6c9b03776 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/PreviewConnectionCodec.cpp @@ -0,0 +1,91 @@ +/* + * + * 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. + * + */ +#include "PreviewConnectionCodec.h" +#include "qpid/log/Statement.h" + +namespace qpid { +namespace broker { + +using sys::Mutex; + +PreviewConnectionCodec::PreviewConnectionCodec(sys::OutputControl& o, Broker& broker, const std::string& id, bool isClient) + : frameQueueClosed(false), output(o), connection(this, broker, id, isClient), identifier(id) {} + +size_t PreviewConnectionCodec::decode(const char* buffer, size_t size) { + framing::Buffer in(const_cast<char*>(buffer), size); + framing::AMQFrame frame; + while(frame.decode(in)) { + QPID_LOG(trace, "RECV [" << identifier << "]: " << frame); + connection.received(frame); + } + return in.getPosition(); +} + +bool PreviewConnectionCodec::canEncode() { + if (!frameQueueClosed && frameQueue.empty()) connection.doOutput(); + return !frameQueue.empty(); +} + +bool PreviewConnectionCodec::isClosed() const { + Mutex::ScopedLock l(frameQueueLock); + return frameQueueClosed; +} + +size_t PreviewConnectionCodec::encode(const char* buffer, size_t size) { + Mutex::ScopedLock l(frameQueueLock); + framing::Buffer out(const_cast<char*>(buffer), size); + while (!frameQueue.empty() && (frameQueue.front().size() <= out.available())) { + frameQueue.front().encode(out); + QPID_LOG(trace, "SENT [" << identifier << "]: " << frameQueue.front()); + frameQueue.pop(); + if (!frameQueueClosed && frameQueue.empty()) connection.doOutput(); + } + if (!frameQueue.empty() && frameQueue.front().size() > size) + throw framing::ContentTooLargeException(QPID_MSG("Could not write frame, too large for buffer.")); + return out.getPosition(); +} + +void PreviewConnectionCodec::activateOutput() { output.activateOutput(); } + +void PreviewConnectionCodec::close() { + // Close the output queue. + Mutex::ScopedLock l(frameQueueLock); + frameQueueClosed = true; +} + +void PreviewConnectionCodec::closed() { + connection.closed(); +} + +void PreviewConnectionCodec::send(framing::AMQFrame& f) { + { + Mutex::ScopedLock l(frameQueueLock); + if (!frameQueueClosed) + frameQueue.push(f); + } + activateOutput(); +} + +framing::ProtocolVersion PreviewConnectionCodec::getVersion() const { + return framing::ProtocolVersion(99,0); +} + +}} // namespace qpid::broker diff --git a/qpid/cpp/src/qpid/broker/PreviewConnectionCodec.h b/qpid/cpp/src/qpid/broker/PreviewConnectionCodec.h new file mode 100644 index 0000000000..f2ab086d06 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/PreviewConnectionCodec.h @@ -0,0 +1,55 @@ +#ifndef QPID_BROKER_PREVIEWCONNECTIONCODEC_H +#define QPID_BROKER_PREVIEWCONNECTIONCODEC_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. + * + */ +#include "qpid/sys/ConnectionCodec.h" +#include "qpid/sys/ConnectionOutputHandler.h" +#include "qpid/sys/Mutex.h" +#include "PreviewConnection.h" + +namespace qpid { +namespace broker { + +class PreviewConnectionCodec : public sys::ConnectionCodec, public sys::ConnectionOutputHandler { + std::queue<framing::AMQFrame> frameQueue; + bool frameQueueClosed; + mutable sys::Mutex frameQueueLock; + sys::OutputControl& output; + PreviewConnection connection; + std::string identifier; + + public: + PreviewConnectionCodec(sys::OutputControl&, Broker&, const std::string& id, bool isClient = false); + size_t decode(const char* buffer, size_t size); + size_t encode(const char* buffer, size_t size); + bool isClosed() const; + bool canEncode(); + void activateOutput(); + void closed(); // connection closed by peer. + void close(); // closing from this end. + void send(framing::AMQFrame&); + framing::ProtocolVersion getVersion() const; +}; + +}} // namespace qpid::broker + +#endif /*!QPID_BROKER_PREVIEWCONNECTIONCODEC_H*/ diff --git a/qpid/cpp/src/qpid/broker/PreviewConnectionHandler.cpp b/qpid/cpp/src/qpid/broker/PreviewConnectionHandler.cpp new file mode 100644 index 0000000000..5c5f2f263e --- /dev/null +++ b/qpid/cpp/src/qpid/broker/PreviewConnectionHandler.cpp @@ -0,0 +1,212 @@ + +/* + * + * 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. + * + */ + +#include "config.h" + +#include "PreviewConnectionHandler.h" +#include "PreviewConnection.h" +#include "qpid/framing/ConnectionStartBody.h" +#include "qpid/framing/ClientInvoker.h" +#include "qpid/framing/ServerInvoker.h" +#include "qpid/log/Statement.h" + +#if HAVE_SASL +#include <sasl/sasl.h> +#endif + +using namespace qpid; +using namespace qpid::broker; +using namespace qpid::framing; + + +namespace +{ +const std::string PLAIN = "PLAIN"; +const std::string en_US = "en_US"; +} + +void PreviewConnectionHandler::close(ReplyCode code, const string& text, ClassId classId, MethodId methodId) +{ + handler->client.close(code, text, classId, methodId); +} + +void PreviewConnectionHandler::handle(framing::AMQFrame& frame) +{ + AMQMethodBody* method=frame.getBody()->getMethod(); + try{ + if (handler->serverMode) { + if (!invoke(static_cast<AMQP_ServerOperations::ConnectionHandler&>(*handler.get()), *method)) + throw ChannelErrorException(QPID_MSG("Class can't be accessed over channel 0")); + } else { + if (!invoke(static_cast<AMQP_ClientOperations::ConnectionHandler&>(*handler.get()), *method)) + throw ChannelErrorException(QPID_MSG("Class can't be accessed over channel 0")); + } + }catch(ConnectionException& e){ + handler->client.close(e.code, e.what(), method->amqpClassId(), method->amqpMethodId()); + }catch(std::exception& e){ + handler->client.close(541/*internal error*/, e.what(), method->amqpClassId(), method->amqpMethodId()); + } +} + +PreviewConnectionHandler::PreviewConnectionHandler(PreviewConnection& connection, bool isClient) : handler(new Handler(connection)) { + FieldTable properties; + string mechanisms(PLAIN); + string locales(en_US); + if (isClient) { + handler->serverMode = false; + }else { + handler->serverMode = true; + handler->client.start(99, 0, properties, mechanisms, locales); + } +} + +PreviewConnectionHandler::Handler:: Handler(PreviewConnection& c) : client(c.getOutput()), server(c.getOutput()), + connection(c), serverMode(false) {} + +void PreviewConnectionHandler::Handler::startOk(const framing::FieldTable& /*clientProperties*/, + const string& mechanism, + const string& response, const string& /*locale*/) +{ + //TODO: handle SASL mechanisms more cleverly + if (mechanism == PLAIN) { + QPID_LOG(info, "SASL Plain: Attempting authentication"); + if (response.size() > 0 && response[0] == (char) 0) { + string temp = response.substr(1); + string::size_type i = temp.find((char)0); + string uid = temp.substr(0, i); + string pwd = temp.substr(i + 1); + +#if HAVE_SASL + if (connection.getBroker().getOptions().auth) { + int code = sasl_server_new(BROKER_SASL_NAME, + NULL, NULL, NULL, NULL, NULL, 0, + &connection.sasl_conn); + + if (SASL_OK != code) { + QPID_LOG(info, "SASL Plain: Connection creation failed: " + << sasl_errdetail(connection.sasl_conn)); + + // TODO: Change this to an exception signaling + // server error, when one is available + throw CommandInvalidException("Unable to perform authentication"); + } + + code = sasl_checkpass(connection.sasl_conn, + uid.c_str(), uid.length(), + pwd.c_str(), pwd.length()); + if (SASL_OK == code) { + QPID_LOG(info, "SASL Plain: Authentication accepted for " << uid); + } else { + // See man sasl_errors(3) or sasl/sasl.h for possible errors + QPID_LOG(info, "SASL Plain: Authentication rejected for " + << uid << ": " + << sasl_errdetail(connection.sasl_conn)); + + // TODO: Change this to an exception signaling + // authentication failure, when one is available + throw ConnectionForcedException("Authentication failed"); + } + } else { +#endif + QPID_LOG(warning, + "SASL Plain Warning: No Authentication Performed for " + << uid); +#if HAVE_SASL + } +#endif + + connection.setUserId(uid); + } + } else { + // The 0-10 spec states that if the client requests a + // mechanism not proposed by the server the server MUST + // close the connection. Assumption here is if we proposed + // a mechanism we'd have a case for it above. + throw NotImplementedException("Unsupported authentication mechanism"); + } + client.tune(framing::CHANNEL_MAX, connection.getFrameMax(), connection.getHeartbeat()); +} + +void PreviewConnectionHandler::Handler::secureOk(const string& /*response*/){} + +void PreviewConnectionHandler::Handler::tuneOk(uint16_t /*channelmax*/, + uint32_t framemax, uint16_t heartbeat) +{ + connection.setFrameMax(framemax); + connection.setHeartbeat(heartbeat); +} + +void PreviewConnectionHandler::Handler::open(const string& /*virtualHost*/, + const string& /*capabilities*/, bool /*insist*/) +{ + string knownhosts; + client.openOk(knownhosts); +} + + +void PreviewConnectionHandler::Handler::close(uint16_t /*replyCode*/, const string& /*replyText*/, + uint16_t /*classId*/, uint16_t /*methodId*/) +{ + client.closeOk(); + connection.getOutput().close(); +} + +void PreviewConnectionHandler::Handler::closeOk(){ + connection.getOutput().close(); +} + + +void PreviewConnectionHandler::Handler::start(uint8_t /*versionMajor*/, + uint8_t /*versionMinor*/, + const FieldTable& /*serverProperties*/, + const string& /*mechanisms*/, + const string& /*locales*/) +{ + string uid = "qpidd"; + string pwd = "qpidd"; + string response = ((char)0) + uid + ((char)0) + pwd; + server.startOk(FieldTable(), PLAIN, response, en_US); +} + +void PreviewConnectionHandler::Handler::secure(const string& /*challenge*/) +{ + server.secureOk(""); +} + +void PreviewConnectionHandler::Handler::tune(uint16_t channelMax, + uint32_t frameMax, + uint16_t heartbeat) +{ + connection.setFrameMax(frameMax); + connection.setHeartbeat(heartbeat); + server.tuneOk(channelMax, frameMax, heartbeat); + server.open("/", "", true); +} + +void PreviewConnectionHandler::Handler::openOk(const string& /*knownHosts*/) +{ +} + +void PreviewConnectionHandler::Handler::redirect(const string& /*host*/, const string& /*knownHosts*/) +{ + +} diff --git a/qpid/cpp/src/qpid/broker/PreviewConnectionHandler.h b/qpid/cpp/src/qpid/broker/PreviewConnectionHandler.h new file mode 100644 index 0000000000..7c3636373a --- /dev/null +++ b/qpid/cpp/src/qpid/broker/PreviewConnectionHandler.h @@ -0,0 +1,91 @@ +/* + * + * 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. + * + */ +#ifndef _PreviewConnectionAdapter_ +#define _PreviewConnectionAdapter_ + +#include <memory> +#include "qpid/framing/amqp_types.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/AMQP_ClientOperations.h" +#include "qpid/framing/AMQP_ClientProxy.h" +#include "qpid/framing/AMQP_ServerOperations.h" +#include "qpid/framing/AMQP_ServerProxy.h" +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/Exception.h" + +namespace qpid { +namespace broker { + +class PreviewConnection; + +// TODO aconway 2007-09-18: Rename to ConnectionHandler +class PreviewConnectionHandler : public framing::FrameHandler +{ + struct Handler : public framing::AMQP_ServerOperations::ConnectionHandler, + public framing::AMQP_ClientOperations::ConnectionHandler + { + framing::AMQP_ClientProxy::Connection client; + framing::AMQP_ServerProxy::Connection server; + PreviewConnection& connection; + bool serverMode; + + Handler(PreviewConnection& connection); + void startOk(const qpid::framing::FieldTable& clientProperties, + const std::string& mechanism, const std::string& response, + const std::string& locale); + void secureOk(const std::string& response); + void tuneOk(uint16_t channelMax, uint32_t frameMax, uint16_t heartbeat); + void open(const std::string& virtualHost, + const std::string& capabilities, bool insist); + void close(uint16_t replyCode, const std::string& replyText, + uint16_t classId, uint16_t methodId); + void closeOk(); + + + void start(uint8_t versionMajor, + uint8_t versionMinor, + const qpid::framing::FieldTable& serverProperties, + const std::string& mechanisms, + const std::string& locales); + + void secure(const std::string& challenge); + + void tune(uint16_t channelMax, + uint32_t frameMax, + uint16_t heartbeat); + + void openOk(const std::string& knownHosts); + + void redirect(const std::string& host, const std::string& knownHosts); + }; + std::auto_ptr<Handler> handler; + public: + PreviewConnectionHandler(PreviewConnection& connection, bool isClient); + void close(framing::ReplyCode code, const std::string& text, framing::ClassId classId, framing::MethodId methodId); + void handle(framing::AMQFrame& frame); +}; + + +}} + +#endif diff --git a/qpid/cpp/src/qpid/broker/PreviewSessionHandler.cpp b/qpid/cpp/src/qpid/broker/PreviewSessionHandler.cpp new file mode 100644 index 0000000000..36092bb7f6 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/PreviewSessionHandler.cpp @@ -0,0 +1,210 @@ +/* + * 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. + * + */ + +#include "PreviewSessionHandler.h" +#include "PreviewSessionState.h" +#include "PreviewConnection.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/constants.h" +#include "qpid/framing/ClientInvoker.h" +#include "qpid/framing/ServerInvoker.h" +#include "qpid/log/Statement.h" + +#include <boost/bind.hpp> + +namespace qpid { +namespace broker { +using namespace framing; +using namespace std; +using namespace qpid::sys; + +PreviewSessionHandler::PreviewSessionHandler(PreviewConnection& c, ChannelId ch) + : InOutHandler(0, &out), + connection(c), channel(ch, &c.getOutput()), + proxy(out), // Via my own handleOut() for L2 data. + peerSession(channel), // Direct to channel for L2 commands. + ignoring(false) {} + +PreviewSessionHandler::~PreviewSessionHandler() {} + +namespace { +ClassId classId(AMQMethodBody* m) { return m ? m->amqpMethodId() : 0; } +MethodId methodId(AMQMethodBody* m) { return m ? m->amqpClassId() : 0; } +} // namespace + +void PreviewSessionHandler::handleIn(AMQFrame& f) { + // Note on channel states: a channel is open if session != 0. A + // channel that is closed (session == 0) can be in the "ignoring" + // state. This is a temporary state after we have sent a channel + // exception, where extra frames might arrive that should be + // ignored. + // + AMQMethodBody* m = f.getBody()->getMethod(); + try { + if (m && invoke(static_cast<AMQP_ServerOperations::SessionHandler&>(*this), *m)) { + return; + } else if (session.get()) { + boost::optional<SequenceNumber> ack=session->received(f); + session->in.handle(f); + if (ack) + peerSession.ack(*ack, SequenceNumberSet()); + } else if (m && invoke(static_cast<AMQP_ClientOperations::SessionHandler&>(*this), *m)) { + return; + } else if (!ignoring) { + throw ChannelErrorException( + QPID_MSG("Channel " << channel.get() << " is not open")); + } + } catch(const ChannelException& e) { + ignoring=true; // Ignore trailing frames sent by client. + session->detach(); + session.reset(); + peerSession.closed(e.code, e.what()); + }catch(const ConnectionException& e){ + connection.close(e.code, e.what(), classId(m), methodId(m)); + }catch(const std::exception& e){ + connection.close( + framing::INTERNAL_ERROR, e.what(), classId(m), methodId(m)); + } +} + +void PreviewSessionHandler::handleOut(AMQFrame& f) { + channel.handle(f); // Send it. + if (session->sent(f)) + peerSession.solicitAck(); +} + +void PreviewSessionHandler::assertAttached(const char* method) const { + if (!session.get()) + throw ChannelErrorException( + QPID_MSG(method << " failed: No session for channel " + << getChannel())); +} + +void PreviewSessionHandler::assertClosed(const char* method) const { + if (session.get()) + throw ChannelBusyException( + QPID_MSG(method << " failed: channel " << channel.get() + << " is already open.")); +} + +void PreviewSessionHandler::open(uint32_t detachedLifetime) { + assertClosed("open"); + std::auto_ptr<PreviewSessionState> state( + connection.broker.getPreviewSessionManager().open(*this, detachedLifetime)); + session.reset(state.release()); + peerSession.attached(session->getId(), session->getTimeout()); +} + +void PreviewSessionHandler::resume(const Uuid& id) { + assertClosed("resume"); + session = connection.broker.getPreviewSessionManager().resume(id); + session->attach(*this); + SequenceNumber seq = session->resuming(); + peerSession.attached(session->getId(), session->getTimeout()); + proxy.getSession().ack(seq, SequenceNumberSet()); +} + +void PreviewSessionHandler::flow(bool /*active*/) { + assertAttached("flow"); + // TODO aconway 2007-09-19: Removed in 0-10, remove + assert(0); throw NotImplementedException("session.flow"); +} + +void PreviewSessionHandler::flowOk(bool /*active*/) { + assertAttached("flowOk"); + // TODO aconway 2007-09-19: Removed in 0-10, remove + assert(0); throw NotImplementedException("session.flowOk"); +} + +void PreviewSessionHandler::close() { + assertAttached("close"); + QPID_LOG(info, "Received session.close"); + ignoring=false; + session->detach(); + session.reset(); + peerSession.closed(REPLY_SUCCESS, "ok"); + assert(&connection.getChannel(channel.get()) == this); + connection.closeChannel(channel.get()); +} + +void PreviewSessionHandler::closed(uint16_t replyCode, const string& replyText) { + QPID_LOG(warning, "Received session.closed: "<<replyCode<<" "<<replyText); + ignoring=false; + session->detach(); + session.reset(); +} + +void PreviewSessionHandler::localSuspend() { + if (session.get() && session->isAttached()) { + session->detach(); + connection.broker.getPreviewSessionManager().suspend(session); + session.reset(); + } +} + +void PreviewSessionHandler::suspend() { + assertAttached("suspend"); + localSuspend(); + peerSession.detached(); + assert(&connection.getChannel(channel.get()) == this); + connection.closeChannel(channel.get()); +} + +void PreviewSessionHandler::ack(uint32_t cumulativeSeenMark, + const SequenceNumberSet& /*seenFrameSet*/) +{ + assertAttached("ack"); + if (session->getState() == PreviewSessionState::RESUMING) { + session->receivedAck(cumulativeSeenMark); + framing::SessionState::Replay replay=session->replay(); + std::for_each(replay.begin(), replay.end(), + boost::bind(&PreviewSessionHandler::handleOut, this, _1)); + } + else + session->receivedAck(cumulativeSeenMark); +} + +void PreviewSessionHandler::highWaterMark(uint32_t /*lastSentMark*/) { + // TODO aconway 2007-10-02: may be removed from spec. + assert(0); throw NotImplementedException("session.high-water-mark"); +} + +void PreviewSessionHandler::solicitAck() { + assertAttached("solicit-ack"); + peerSession.ack(session->sendingAck(), SequenceNumberSet()); +} + +void PreviewSessionHandler::attached(const Uuid& /*sessionId*/, uint32_t detachedLifetime) +{ + std::auto_ptr<PreviewSessionState> state( + connection.broker.getPreviewSessionManager().open(*this, detachedLifetime)); + session.reset(state.release()); +} + +void PreviewSessionHandler::detached() +{ + connection.broker.getPreviewSessionManager().suspend(session); + session.reset(); +} + +ConnectionState& PreviewSessionHandler::getConnection() { return connection; } +const ConnectionState& PreviewSessionHandler::getConnection() const { return connection; } + +}} // namespace qpid::broker diff --git a/qpid/cpp/src/qpid/broker/PreviewSessionHandler.h b/qpid/cpp/src/qpid/broker/PreviewSessionHandler.h new file mode 100644 index 0000000000..4c517367d7 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/PreviewSessionHandler.h @@ -0,0 +1,111 @@ +#ifndef QPID_BROKER_PREVIEWSESSIONHANDLER_H +#define QPID_BROKER_PREVIEWSESSIONHANDLER_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. + * + */ + +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/AMQP_ClientOperations.h" +#include "qpid/framing/AMQP_ServerOperations.h" +#include "qpid/framing/AMQP_ClientProxy.h" +#include "qpid/framing/amqp_types.h" +#include "qpid/framing/ChannelHandler.h" +#include "SessionContext.h" + +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace broker { + +class PreviewConnection; +class PreviewSessionState; + +/** + * A SessionHandler is associated with each active channel. It + * receives incoming frames, handles session commands and manages the + * association between the channel and a session. + */ +class PreviewSessionHandler : public framing::AMQP_ServerOperations::SessionHandler, + public framing::AMQP_ClientOperations::SessionHandler, + public framing::FrameHandler::InOutHandler, + private boost::noncopyable +{ + public: + PreviewSessionHandler(PreviewConnection&, framing::ChannelId); + ~PreviewSessionHandler(); + + /** Returns 0 if not attached to a session */ + PreviewSessionState* getSession() { return session.get(); } + const PreviewSessionState* getSession() const { return session.get(); } + + framing::ChannelId getChannel() const { return channel.get(); } + + ConnectionState& getConnection(); + const ConnectionState& getConnection() const; + + framing::AMQP_ClientProxy& getProxy() { return proxy; } + const framing::AMQP_ClientProxy& getProxy() const { return proxy; } + + // Called by closing connection. + void localSuspend(); + void detach() { localSuspend(); } + + protected: + void handleIn(framing::AMQFrame&); + void handleOut(framing::AMQFrame&); + + private: + /// Session methods + void open(uint32_t detachedLifetime); + void flow(bool active); + void flowOk(bool active); + void close(); + void closed(uint16_t replyCode, const std::string& replyText); + void resume(const framing::Uuid& sessionId); + void suspend(); + void ack(uint32_t cumulativeSeenMark, + const framing::SequenceNumberSet& seenFrameSet); + void highWaterMark(uint32_t lastSentMark); + void solicitAck(); + + //extra methods required for assuming client role + void attached(const framing::Uuid& sessionId, uint32_t detachedLifetime); + void detached(); + + + void assertAttached(const char* method) const; + void assertActive(const char* method) const; + void assertClosed(const char* method) const; + + + PreviewConnection& connection; + framing::ChannelHandler channel; + framing::AMQP_ClientProxy proxy; + framing::AMQP_ClientProxy::Session peerSession; + bool ignoring; + std::auto_ptr<PreviewSessionState> session; +}; + +}} // namespace qpid::broker + + + +#endif /*!QPID_BROKER_SESSIONHANDLER_H*/ diff --git a/qpid/cpp/src/qpid/broker/PreviewSessionManager.cpp b/qpid/cpp/src/qpid/broker/PreviewSessionManager.cpp new file mode 100644 index 0000000000..97a7c87e34 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/PreviewSessionManager.cpp @@ -0,0 +1,113 @@ +/* + * + * 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. + * + */ + +#include "PreviewSessionManager.h" +#include "PreviewSessionState.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/log/Statement.h" +#include "qpid/log/Helpers.h" +#include "qpid/memory.h" + +#include <boost/bind.hpp> +#include <boost/range.hpp> +#include <boost/intrusive_ptr.hpp> + +#include <algorithm> +#include <functional> +#include <ostream> + +namespace qpid { +namespace broker { + +using namespace sys; +using namespace framing; + +PreviewSessionManager::PreviewSessionManager(uint32_t a) : ack(a) {} + +PreviewSessionManager::~PreviewSessionManager() {} + +// FIXME aconway 2008-02-01: pass handler*, allow open unattached. +std::auto_ptr<PreviewSessionState> PreviewSessionManager::open( + PreviewSessionHandler& h, uint32_t timeout_) +{ + Mutex::ScopedLock l(lock); + std::auto_ptr<PreviewSessionState> session( + new PreviewSessionState(this, &h, timeout_, ack)); + active.insert(session->getId()); + for_each(observers.begin(), observers.end(), + boost::bind(&Observer::opened, _1,boost::ref(*session))); + return session; +} + +void PreviewSessionManager::suspend(std::auto_ptr<PreviewSessionState> session) { + Mutex::ScopedLock l(lock); + active.erase(session->getId()); + session->suspend(); + session->expiry = AbsTime(now(),session->getTimeout()*TIME_SEC); + if (session->mgmtObject.get() != 0) + session->mgmtObject->set_expireTime ((uint64_t) Duration (session->expiry)); + suspended.push_back(session.release()); // In expiry order + eraseExpired(); +} + +std::auto_ptr<PreviewSessionState> PreviewSessionManager::resume(const Uuid& id) +{ + Mutex::ScopedLock l(lock); + eraseExpired(); + if (active.find(id) != active.end()) + throw SessionBusyException( + QPID_MSG("Session already active: " << id)); + Suspended::iterator i = std::find_if( + suspended.begin(), suspended.end(), + boost::bind(std::equal_to<Uuid>(), id, boost::bind(&PreviewSessionState::getId, _1)) + ); + if (i == suspended.end()) + throw InvalidArgumentException( + QPID_MSG("No suspended session with id=" << id)); + active.insert(id); + std::auto_ptr<PreviewSessionState> state(suspended.release(i).release()); + return state; +} + +void PreviewSessionManager::erase(const framing::Uuid& id) +{ + Mutex::ScopedLock l(lock); + active.erase(id); +} + +void PreviewSessionManager::eraseExpired() { + // Called with lock held. + if (!suspended.empty()) { + Suspended::iterator keep = std::lower_bound( + suspended.begin(), suspended.end(), now(), + boost::bind(std::less<AbsTime>(), boost::bind(&PreviewSessionState::expiry, _1), _2)); + if (suspended.begin() != keep) { + QPID_LOG(debug, "Expiring sessions: " << log::formatList(suspended.begin(), keep)); + suspended.erase(suspended.begin(), keep); + } + } +} + +void PreviewSessionManager::add(const boost::intrusive_ptr<Observer>& o) { + observers.push_back(o); +} + +}} // namespace qpid::broker diff --git a/qpid/cpp/src/qpid/broker/PreviewSessionManager.h b/qpid/cpp/src/qpid/broker/PreviewSessionManager.h new file mode 100644 index 0000000000..9bc6bc5bbc --- /dev/null +++ b/qpid/cpp/src/qpid/broker/PreviewSessionManager.h @@ -0,0 +1,101 @@ +#ifndef QPID_BROKER_PREVIEWSESSIONMANAGER_H +#define QPID_BROKER_PREVIEWSESSIONMANAGER_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. + * + */ + +#include <qpid/framing/Uuid.h> +#include <qpid/sys/Time.h> +#include <qpid/sys/Mutex.h> +#include <qpid/RefCounted.h> + +#include <set> +#include <vector> +#include <memory> + +#include <boost/noncopyable.hpp> +#include <boost/ptr_container/ptr_vector.hpp> +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { + +class PreviewSessionState; +class PreviewSessionHandler; + +/** + * Create and manage PreviewSessionState objects. + */ +class PreviewSessionManager : private boost::noncopyable { + public: + /** + * Observer notified of PreviewSessionManager events. + */ + struct Observer : public RefCounted { + virtual void opened(PreviewSessionState&) {} + }; + + PreviewSessionManager(uint32_t ack); + + ~PreviewSessionManager(); + + /** Open a new active session, caller takes ownership */ + std::auto_ptr<PreviewSessionState> open(PreviewSessionHandler& c, uint32_t timeout_); + + /** Suspend a session, start it's timeout counter. + * The factory takes ownership. + */ + void suspend(std::auto_ptr<PreviewSessionState> session); + + /** Resume a suspended session. + *@throw Exception if timed out or non-existant. + */ + std::auto_ptr<PreviewSessionState> resume(const framing::Uuid&); + + /** Add an Observer. */ + void add(const boost::intrusive_ptr<Observer>&); + + private: + typedef boost::ptr_vector<PreviewSessionState> Suspended; + typedef std::set<framing::Uuid> Active; + typedef std::vector<boost::intrusive_ptr<Observer> > Observers; + + void erase(const framing::Uuid&); + void eraseExpired(); + + sys::Mutex lock; + Suspended suspended; + Active active; + uint32_t ack; + Observers observers; + + friend class PreviewSessionState; // removes deleted sessions from active set. +}; + + + +}} // namespace qpid::broker + + + + + +#endif /*!QPID_BROKER_PREVIEWSESSIONMANAGER_H*/ diff --git a/qpid/cpp/src/qpid/broker/PreviewSessionState.cpp b/qpid/cpp/src/qpid/broker/PreviewSessionState.cpp new file mode 100644 index 0000000000..43c3b1509e --- /dev/null +++ b/qpid/cpp/src/qpid/broker/PreviewSessionState.cpp @@ -0,0 +1,174 @@ +/* + * + * 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. + * + */ +#include "PreviewSessionState.h" +#include "PreviewSessionManager.h" +#include "PreviewSessionHandler.h" +#include "ConnectionState.h" +#include "Broker.h" +#include "SemanticHandler.h" +#include "qpid/framing/reply_exceptions.h" + +namespace qpid { +namespace broker { + +using namespace framing; +using sys::Mutex; +using qpid::management::ManagementAgent; +using qpid::management::ManagementObject; +using qpid::management::Manageable; +using qpid::management::Args; + +PreviewSessionState::PreviewSessionState( + PreviewSessionManager* f, PreviewSessionHandler* h, uint32_t timeout_, uint32_t ack) + : framing::SessionState(ack, timeout_ > 0), + factory(f), handler(h), id(true), timeout(timeout_), + broker(h->getConnection().broker), + version(h->getConnection().getVersion()), + semanticHandler(new SemanticHandler(*this)) +{ + in.next = semanticHandler.get(); + out.next = &handler->out; + + getConnection().outputTasks.addOutputTask(&semanticHandler->getSemanticState()); + + Manageable* parent = broker.GetVhostObject (); + + if (parent != 0) + { + ManagementAgent::shared_ptr agent = ManagementAgent::getAgent (); + + if (agent.get () != 0) + { + mgmtObject = management::Session::shared_ptr + (new management::Session (this, parent, id.str ())); + mgmtObject->set_attached (1); + mgmtObject->set_clientRef (h->getConnection().GetManagementObject()->getObjectId()); + mgmtObject->set_channelId (h->getChannel()); + mgmtObject->set_detachedLifespan (getTimeout()); + agent->addObject (mgmtObject); + } + } +} + +PreviewSessionState::~PreviewSessionState() { + // Remove ID from active session list. + if (factory) + factory->erase(getId()); + if (mgmtObject.get () != 0) + mgmtObject->resourceDestroy (); +} + +PreviewSessionHandler* PreviewSessionState::getHandler() { + return handler; +} + +AMQP_ClientProxy& PreviewSessionState::getProxy() { + assert(isAttached()); + return getHandler()->getProxy(); +} + +ConnectionState& PreviewSessionState::getConnection() { + assert(isAttached()); + return getHandler()->getConnection(); +} + +bool PreviewSessionState::isLocal(const ConnectionToken* t) const +{ + return isAttached() && &(handler->getConnection()) == t; +} + +void PreviewSessionState::detach() { + getConnection().outputTasks.removeOutputTask(&semanticHandler->getSemanticState()); + Mutex::ScopedLock l(lock); + handler = 0; out.next = 0; + if (mgmtObject.get() != 0) + { + mgmtObject->set_attached (0); + } +} + +void PreviewSessionState::attach(PreviewSessionHandler& h) { + { + Mutex::ScopedLock l(lock); + handler = &h; + out.next = &handler->out; + if (mgmtObject.get() != 0) + { + mgmtObject->set_attached (1); + mgmtObject->set_clientRef (h.getConnection().GetManagementObject()->getObjectId()); + mgmtObject->set_channelId (h.getChannel()); + } + } + h.getConnection().outputTasks.addOutputTask(&semanticHandler->getSemanticState()); +} + +void PreviewSessionState::activateOutput() +{ + Mutex::ScopedLock l(lock); + if (isAttached()) { + getConnection().outputTasks.activateOutput(); + } +} + //This class could be used as the callback for queue notifications + //if not attached, it can simply ignore the callback, else pass it + //on to the connection + +ManagementObject::shared_ptr PreviewSessionState::GetManagementObject (void) const +{ + return dynamic_pointer_cast<ManagementObject> (mgmtObject); +} + +Manageable::status_t PreviewSessionState::ManagementMethod (uint32_t methodId, + Args& /*args*/) +{ + Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; + + switch (methodId) + { + case management::Session::METHOD_DETACH : + if (handler != 0) + { + handler->detach(); + } + status = Manageable::STATUS_OK; + break; + + case management::Session::METHOD_CLOSE : + /* + if (handler != 0) + { + handler->getConnection().closeChannel(handler->getChannel()); + } + status = Manageable::STATUS_OK; + break; + */ + + case management::Session::METHOD_SOLICITACK : + case management::Session::METHOD_RESETLIFESPAN : + status = Manageable::STATUS_NOT_IMPLEMENTED; + break; + } + + return status; +} + + +}} // namespace qpid::broker diff --git a/qpid/cpp/src/qpid/broker/PreviewSessionState.h b/qpid/cpp/src/qpid/broker/PreviewSessionState.h new file mode 100644 index 0000000000..1aecb12e72 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/PreviewSessionState.h @@ -0,0 +1,125 @@ +#ifndef QPID_BROKER_PREVIEWSESSION_H +#define QPID_BROKER_PREVIEWSESSION_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. + * + */ + +#include "qpid/framing/Uuid.h" +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/SessionState.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Time.h" +#include "qpid/management/Manageable.h" +#include "qpid/management/Session.h" +#include "SessionContext.h" + +#include <boost/noncopyable.hpp> +#include <boost/scoped_ptr.hpp> + +#include <set> +#include <vector> +#include <ostream> + +namespace qpid { + +namespace framing { +class AMQP_ClientProxy; +} + +namespace broker { + +class SemanticHandler; +class PreviewSessionHandler; +class PreviewSessionManager; +class Broker; +class ConnectionState; + +/** + * Broker-side session state includes sessions handler chains, which may + * themselves have state. + */ +class PreviewSessionState : public framing::SessionState, + public SessionContext, + public framing::FrameHandler::Chains, + public management::Manageable +{ + public: + ~PreviewSessionState(); + bool isAttached() const { return handler; } + + void detach(); + void attach(PreviewSessionHandler& handler); + + + PreviewSessionHandler* getHandler(); + + /** @pre isAttached() */ + framing::AMQP_ClientProxy& getProxy(); + + /** @pre isAttached() */ + ConnectionState& getConnection(); + bool isLocal(const ConnectionToken* t) const; + + uint32_t getTimeout() const { return timeout; } + Broker& getBroker() { return broker; } + framing::ProtocolVersion getVersion() const { return version; } + + /** OutputControl **/ + void activateOutput(); + + // Manageable entry points + management::ManagementObject::shared_ptr GetManagementObject (void) const; + management::Manageable::status_t + ManagementMethod (uint32_t methodId, management::Args& args); + + // Normally SessionManager creates sessions. + PreviewSessionState(PreviewSessionManager*, + PreviewSessionHandler* out, + uint32_t timeout, + uint32_t ackInterval); + + + private: + PreviewSessionManager* factory; + PreviewSessionHandler* handler; + framing::Uuid id; + uint32_t timeout; + sys::AbsTime expiry; // Used by SessionManager. + Broker& broker; + framing::ProtocolVersion version; + sys::Mutex lock; + boost::scoped_ptr<SemanticHandler> semanticHandler; + management::Session::shared_ptr mgmtObject; + + friend class PreviewSessionManager; +}; + + +inline std::ostream& operator<<(std::ostream& out, const PreviewSessionState& session) { + return out << session.getId(); +} + +}} // namespace qpid::broker + + + +#endif /*!QPID_BROKER_SESSION_H*/ diff --git a/qpid/cpp/src/qpid/broker/Queue.cpp b/qpid/cpp/src/qpid/broker/Queue.cpp new file mode 100644 index 0000000000..591e9796d6 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Queue.cpp @@ -0,0 +1,661 @@ +/* + * + * 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. + * + */ + +#include "Broker.h" +#include "Queue.h" +#include "Exchange.h" +#include "DeliverableMessage.h" +#include "MessageStore.h" +#include "QueueRegistry.h" + +#include "qpid/log/Statement.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/sys/Monitor.h" +#include "qpid/sys/Time.h" + +#include <iostream> +#include <algorithm> +#include <functional> + +#include <boost/bind.hpp> +#include <boost/intrusive_ptr.hpp> + +using namespace qpid::broker; +using namespace qpid::sys; +using namespace qpid::framing; +using qpid::management::ManagementAgent; +using qpid::management::ManagementObject; +using qpid::management::Manageable; +using qpid::management::Args; +using std::for_each; +using std::mem_fun; + +Queue::Queue(const string& _name, bool _autodelete, + MessageStore* const _store, + const OwnershipToken* const _owner, + Manageable* parent) : + + name(_name), + autodelete(_autodelete), + store(_store), + owner(_owner), + consumerCount(0), + exclusive(false), + noLocal(false), + persistenceId(0) +{ + if (parent != 0) + { + ManagementAgent::shared_ptr agent = ManagementAgent::getAgent (); + + if (agent.get () != 0) + { + mgmtObject = management::Queue::shared_ptr + (new management::Queue (this, parent, _name, _store != 0, _autodelete, 0)); + + // Add the object to the management agent only if this queue is not durable. + // If it's durable, we will add it later when the queue is assigned a persistenceId. + if (store == 0) + agent->addObject (mgmtObject); + } + } +} + +Queue::~Queue() +{ + if (mgmtObject.get () != 0) + mgmtObject->resourceDestroy (); +} + +void Queue::notifyDurableIOComplete() +{ + Mutex::ScopedLock locker(messageLock); + notify(); +} + +bool Queue::isLocal(boost::intrusive_ptr<Message>& msg) +{ + return noLocal && owner && owner->isLocal(msg->getPublisher()); +} + +void Queue::deliver(boost::intrusive_ptr<Message>& msg){ + if (msg->isImmediate() && getConsumerCount() == 0) { + if (alternateExchange) { + DeliverableMessage deliverable(msg); + alternateExchange->route(deliverable, msg->getRoutingKey(), msg->getApplicationHeaders()); + } + } else if (isLocal(msg)) { + //drop message + QPID_LOG(debug, "Dropping 'local' message from " << getName()); + } else { + // if no store then mark as enqueued + if (!enqueue(0, msg)){ + push(msg); + msg->enqueueComplete(); + if (mgmtObject.get() != 0) { + mgmtObject->inc_msgTotalEnqueues (); + mgmtObject->inc_byteTotalEnqueues (msg->contentSize ()); + mgmtObject->inc_msgDepth (); + mgmtObject->inc_byteDepth (msg->contentSize ()); + } + }else { + if (mgmtObject.get() != 0) { + mgmtObject->inc_msgTotalEnqueues (); + mgmtObject->inc_byteTotalEnqueues (msg->contentSize ()); + mgmtObject->inc_msgDepth (); + mgmtObject->inc_byteDepth (msg->contentSize ()); + mgmtObject->inc_msgPersistEnqueues (); + mgmtObject->inc_bytePersistEnqueues (msg->contentSize ()); + } + push(msg); + } + QPID_LOG(debug, "Message " << msg << " enqueued on " << name << "[" << this << "]"); + } +} + + +void Queue::recover(boost::intrusive_ptr<Message>& msg){ + push(msg); + msg->enqueueComplete(); // mark the message as enqueued + if (mgmtObject.get() != 0) { + mgmtObject->inc_msgTotalEnqueues (); + mgmtObject->inc_byteTotalEnqueues (msg->contentSize ()); + mgmtObject->inc_msgPersistEnqueues (); + mgmtObject->inc_bytePersistEnqueues (msg->contentSize ()); + mgmtObject->inc_msgDepth (); + mgmtObject->inc_byteDepth (msg->contentSize ()); + } + + if (store && !msg->isContentLoaded()) { + //content has not been loaded, need to ensure that lazy loading mode is set: + //TODO: find a nicer way to do this + msg->releaseContent(store); + } +} + +void Queue::process(boost::intrusive_ptr<Message>& msg){ + push(msg); + if (mgmtObject.get() != 0) { + mgmtObject->inc_msgTotalEnqueues (); + mgmtObject->inc_byteTotalEnqueues (msg->contentSize ()); + mgmtObject->inc_msgTxnEnqueues (); + mgmtObject->inc_byteTxnEnqueues (msg->contentSize ()); + mgmtObject->inc_msgDepth (); + mgmtObject->inc_byteDepth (msg->contentSize ()); + if (msg->isPersistent ()) { + mgmtObject->inc_msgPersistEnqueues (); + mgmtObject->inc_bytePersistEnqueues (msg->contentSize ()); + } + } +} + +void Queue::requeue(const QueuedMessage& msg){ + Mutex::ScopedLock locker(messageLock); + msg.payload->enqueueComplete(); // mark the message as enqueued + messages.push_front(msg); + notify(); +} + +bool Queue::acquire(const QueuedMessage& msg) { + Mutex::ScopedLock locker(messageLock); + QPID_LOG(debug, "attempting to acquire " << msg.position); + for (Messages::iterator i = messages.begin(); i != messages.end(); i++) { + if (i->position == msg.position) { + messages.erase(i); + QPID_LOG(debug, "Match found, acquire succeeded: " << i->position << " == " << msg.position); + return true; + } else { + QPID_LOG(debug, "No match: " << i->position << " != " << msg.position); + } + } + QPID_LOG(debug, "Acquire failed for " << msg.position); + return false; +} + +/** + * Return true if the message can be excluded. This is currently the + * case if the queue is exclusive and has an exclusive consumer that + * doesn't want the message or has a single consumer that doesn't want + * the message (covers the JMS topic case). + */ +bool Queue::canExcludeUnwanted() +{ + Mutex::ScopedLock locker(consumerLock); + return hasExclusiveOwner() && (exclusive || consumerCount == 1); +} + + +bool Queue::getNextMessage(QueuedMessage& m, Consumer& c) +{ + if (c.preAcquires()) { + return consumeNextMessage(m, c); + } else { + return browseNextMessage(m, c); + } +} + +bool Queue::consumeNextMessage(QueuedMessage& m, Consumer& c) +{ + while (true) { + Mutex::ScopedLock locker(messageLock); + if (messages.empty()) { + QPID_LOG(debug, "No messages to dispatch on queue '" << name << "'"); + addListener(c); + return false; + } else { + QueuedMessage msg = messages.front(); + if (!msg.payload->isEnqueueComplete()) { + QPID_LOG(debug, "Messages not ready to dispatch on queue '" << name << "'"); + addListener(c); + return false; + } + + if (c.filter(msg.payload)) { + if (c.accept(msg.payload)) { + m = msg; + pop(); + return true; + } else { + //message(s) are available but consumer hasn't got enough credit + QPID_LOG(debug, "Consumer can't currently accept message from '" << name << "'"); + return false; + } + } else { + //consumer will never want this message + if (canExcludeUnwanted()) { + //hack for no-local on JMS topics; get rid of this message + QPID_LOG(debug, "Excluding message from '" << name << "'"); + pop(); + } else { + //leave it for another consumer + QPID_LOG(debug, "Consumer doesn't want message from '" << name << "'"); + return false; + } + } + } + } +} + + +bool Queue::browseNextMessage(QueuedMessage& m, Consumer& c) +{ + QueuedMessage msg(this); + while (seek(msg, c)) { + if (c.filter(msg.payload)) { + if (c.accept(msg.payload)) { + //consumer wants the message + c.position = msg.position; + m = msg; + return true; + } else { + //browser hasn't got enough credit for the message + QPID_LOG(debug, "Browser can't currently accept message from '" << name << "'"); + return false; + } + } else { + //consumer will never want this message, continue seeking + c.position = msg.position; + QPID_LOG(debug, "Browser skipping message from '" << name << "'"); + } + } + return false; +} + +void Queue::notify() +{ + //notify listeners that there may be messages to process + for_each(listeners.begin(), listeners.end(), mem_fun(&Consumer::notify)); + listeners.clear(); +} + +void Queue::removeListener(Consumer& c) +{ + Mutex::ScopedLock locker(messageLock); + listeners.erase(&c); +} + +void Queue::addListener(Consumer& c) +{ + listeners.insert(&c); +} + +bool Queue::dispatch(Consumer& c) +{ + QueuedMessage msg(this); + if (getNextMessage(msg, c)) { + c.deliver(msg); + return true; + } else { + return false; + } +} + +bool Queue::seek(QueuedMessage& msg, Consumer& c) { + Mutex::ScopedLock locker(messageLock); + if (!messages.empty() && messages.back().position > c.position) { + if (c.position < messages.front().position) { + msg = messages.front(); + return true; + } else { + //TODO: can improve performance of this search, for now just searching linearly from end + Messages::reverse_iterator pos; + for (Messages::reverse_iterator i = messages.rbegin(); i != messages.rend() && i->position > c.position; i++) { + pos = i; + } + msg = *pos; + return true; + } + } + addListener(c); + return false; +} + +void Queue::consume(Consumer&, bool requestExclusive){ + Mutex::ScopedLock locker(consumerLock); + if(exclusive) { + throw AccessRefusedException( + QPID_MSG("Queue " << getName() << " has an exclusive consumer. No more consumers allowed.")); + } else if(requestExclusive) { + if(consumerCount) { + throw AccessRefusedException( + QPID_MSG("Queue " << getName() << " already has consumers. Exclusive access denied.")); + } else { + exclusive = true; + } + } + consumerCount++; + + if (mgmtObject.get() != 0){ + mgmtObject->inc_consumers (); + } +} + +void Queue::cancel(Consumer& c){ + removeListener(c); + Mutex::ScopedLock locker(consumerLock); + consumerCount--; + if(exclusive) exclusive = false; + if (mgmtObject.get() != 0){ + mgmtObject->dec_consumers (); + } +} + +QueuedMessage Queue::dequeue(){ + Mutex::ScopedLock locker(messageLock); + QueuedMessage msg(this); + + if(!messages.empty()){ + msg = messages.front(); + pop(); + } + return msg; +} + +uint32_t Queue::purge(){ + Mutex::ScopedLock locker(messageLock); + int count = messages.size(); + while(!messages.empty()) pop(); + return count; +} + +/** + * Assumes messageLock is held + */ +void Queue::pop(){ + QueuedMessage& msg = messages.front(); + + if (policy.get()) policy->dequeued(msg.payload->contentSize()); + if (mgmtObject.get() != 0){ + mgmtObject->inc_msgTotalDequeues (); + mgmtObject->inc_byteTotalDequeues (msg.payload->contentSize()); + mgmtObject->dec_msgDepth (); + mgmtObject->dec_byteDepth (msg.payload->contentSize()); + if (msg.payload->isPersistent ()){ + mgmtObject->inc_msgPersistDequeues (); + mgmtObject->inc_bytePersistDequeues (msg.payload->contentSize()); + } + } + messages.pop_front(); +} + +void Queue::push(boost::intrusive_ptr<Message>& msg){ + Mutex::ScopedLock locker(messageLock); + messages.push_back(QueuedMessage(this, msg, ++sequence)); + if (policy.get()) { + policy->enqueued(msg->contentSize()); + if (policy->limitExceeded()) { + if (store) { + QPID_LOG(debug, "Message " << msg << " on " << name << " released from memory"); + msg->releaseContent(store); + } else { + QPID_LOG(warning, "Message " << msg << " on " << name + << " exceeds the policy for the queue but can't be released from memory as the queue is not durable"); + } + } + } + notify(); +} + +/** function only provided for unit tests, or code not in critical message path */ +uint32_t Queue::getMessageCount() const{ + Mutex::ScopedLock locker(messageLock); + + uint32_t count =0; + for ( Messages::const_iterator i = messages.begin(); i != messages.end(); ++i ) { + if ( i->payload->isEnqueueComplete() ) count ++; + } + + return count; +} + +uint32_t Queue::getConsumerCount() const{ + Mutex::ScopedLock locker(consumerLock); + return consumerCount; +} + +bool Queue::canAutoDelete() const{ + Mutex::ScopedLock locker(consumerLock); + return autodelete && !consumerCount; +} + +// return true if store exists, +bool Queue::enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message> msg) +{ + if (msg->isPersistent() && store) { + msg->enqueueAsync(shared_from_this(), store); //increment to async counter -- for message sent to more than one queue + boost::intrusive_ptr<PersistableMessage> pmsg = boost::static_pointer_cast<PersistableMessage>(msg); + store->enqueue(ctxt, pmsg, *this); + return true; + } + //msg->enqueueAsync(); // increments intrusive ptr cnt + return false; +} + +// return true if store exists, +bool Queue::dequeue(TransactionContext* ctxt, boost::intrusive_ptr<Message> msg) +{ + if (msg->isPersistent() && store) { + msg->dequeueAsync(shared_from_this(), store); //increment to async counter -- for message sent to more than one queue + boost::intrusive_ptr<PersistableMessage> pmsg = boost::static_pointer_cast<PersistableMessage>(msg); + store->dequeue(ctxt, pmsg, *this); + return true; + } + //msg->dequeueAsync(); // decrements intrusive ptr cnt + return false; +} + + +namespace +{ + const std::string qpidMaxSize("qpid.max_size"); + const std::string qpidMaxCount("qpid.max_count"); + const std::string qpidNoLocal("no-local"); +} + +void Queue::create(const FieldTable& _settings) +{ + settings = _settings; + if (store) { + store->create(*this, _settings); + } + configure(_settings); +} + +void Queue::configure(const FieldTable& _settings) +{ + std::auto_ptr<QueuePolicy> _policy(new QueuePolicy(_settings)); + if (_policy->getMaxCount() || _policy->getMaxSize()) { + setPolicy(_policy); + } + if (owner) { + noLocal = _settings.get(qpidNoLocal); + QPID_LOG(debug, "Configured queue with no-local=" << noLocal); + } + if (mgmtObject.get() != 0) + mgmtObject->set_arguments (_settings); +} + +void Queue::destroy() +{ + if (alternateExchange.get()) { + Mutex::ScopedLock locker(messageLock); + while(!messages.empty()){ + DeliverableMessage msg(messages.front().payload); + alternateExchange->route(msg, msg.getMessage().getRoutingKey(), + msg.getMessage().getApplicationHeaders()); + pop(); + } + alternateExchange->decAlternateUsers(); + } + + if (store) { + store->flush(*this); + store->destroy(*this); + store = 0;//ensure we make no more calls to the store for this queue + } +} + +void Queue::bound(const string& exchange, const string& key, + const FieldTable& args) +{ + bindings.add(exchange, key, args); +} + +void Queue::unbind(ExchangeRegistry& exchanges, Queue::shared_ptr shared_ref) +{ + bindings.unbind(exchanges, shared_ref); +} + +void Queue::setPolicy(std::auto_ptr<QueuePolicy> _policy) +{ + policy = _policy; +} + +const QueuePolicy* Queue::getPolicy() +{ + return policy.get(); +} + +uint64_t Queue::getPersistenceId() const +{ + return persistenceId; +} + +void Queue::setPersistenceId(uint64_t _persistenceId) const +{ + if (mgmtObject != 0 && persistenceId == 0) + { + ManagementAgent::shared_ptr agent = ManagementAgent::getAgent (); + agent->addObject (mgmtObject, _persistenceId); + } + persistenceId = _persistenceId; +} + +void Queue::encode(framing::Buffer& buffer) const +{ + buffer.putShortString(name); + buffer.put(settings); +} + +uint32_t Queue::encodedSize() const +{ + return name.size() + 1/*short string size octet*/ + settings.size(); +} + +Queue::shared_ptr Queue::decode(QueueRegistry& queues, framing::Buffer& buffer) +{ + string name; + buffer.getShortString(name); + std::pair<Queue::shared_ptr, bool> result = queues.declare(name, true); + buffer.get(result.first->settings); + result.first->configure(result.first->settings); + return result.first; +} + + +void Queue::setAlternateExchange(boost::shared_ptr<Exchange> exchange) +{ + alternateExchange = exchange; +} + +boost::shared_ptr<Exchange> Queue::getAlternateExchange() +{ + return alternateExchange; +} + +void Queue::tryAutoDelete(Broker& broker, Queue::shared_ptr queue) +{ + if (broker.getQueues().destroyIf(queue->getName(), + boost::bind(boost::mem_fn(&Queue::canAutoDelete), queue))) { + queue->unbind(broker.getExchanges(), queue); + queue->destroy(); + } + +} + +bool Queue::isExclusiveOwner(const OwnershipToken* const o) const +{ + Mutex::ScopedLock locker(ownershipLock); + return o == owner; +} + +void Queue::releaseExclusiveOwnership() +{ + Mutex::ScopedLock locker(ownershipLock); + owner = 0; +} + +bool Queue::setExclusiveOwner(const OwnershipToken* const o) +{ + Mutex::ScopedLock locker(ownershipLock); + if (owner) { + return false; + } else { + owner = o; + return true; + } +} + +bool Queue::hasExclusiveOwner() const +{ + Mutex::ScopedLock locker(ownershipLock); + return owner != 0; +} + +bool Queue::hasExclusiveConsumer() const +{ + return exclusive; +} + +void Queue::setExternalQueueStore(ExternalQueueStore* inst) { + if (externalQueueStore!=inst && externalQueueStore) + delete externalQueueStore; + externalQueueStore = inst; + + if (inst) { + ManagementObject::shared_ptr childObj = inst->GetManagementObject(); + if (childObj.get() != 0) + mgmtObject->set_storeRef(childObj->getObjectId()); + } +} + +ManagementObject::shared_ptr Queue::GetManagementObject (void) const +{ + return dynamic_pointer_cast<ManagementObject> (mgmtObject); +} + +Manageable::status_t Queue::ManagementMethod (uint32_t methodId, + Args& /*args*/) +{ + Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; + + QPID_LOG (debug, "Queue::ManagementMethod [id=" << methodId << "]"); + + switch (methodId) + { + case management::Queue::METHOD_PURGE : + purge (); + status = Manageable::STATUS_OK; + break; + } + + return status; +} diff --git a/qpid/cpp/src/qpid/broker/Queue.h b/qpid/cpp/src/qpid/broker/Queue.h new file mode 100644 index 0000000000..8b92784b9a --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Queue.h @@ -0,0 +1,198 @@ +#ifndef _broker_Queue_h +#define _broker_Queue_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. + * + */ +#include "OwnershipToken.h" +#include "Consumer.h" +#include "Message.h" +#include "PersistableQueue.h" +#include "QueuePolicy.h" +#include "QueueBindings.h" + +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/Serializer.h" +#include "qpid/sys/Monitor.h" +#include "qpid/management/Manageable.h" +#include "qpid/management/Queue.h" +#include "qpid/framing/amqp_types.h" + +#include <vector> +#include <memory> +#include <deque> +#include <set> + +#include <boost/shared_ptr.hpp> +#include <boost/enable_shared_from_this.hpp> +#include <boost/intrusive_ptr.hpp> + +namespace qpid { + namespace broker { + class Broker; + class MessageStore; + class QueueRegistry; + class TransactionContext; + class Exchange; + + using std::string; + + /** + * The brokers representation of an amqp queue. Messages are + * delivered to a queue from where they can be dispatched to + * registered consumers or be stored until dequeued or until one + * or more consumers registers. + */ + class Queue : public boost::enable_shared_from_this<Queue>, + public PersistableQueue, public management::Manageable { + typedef std::set<Consumer*> Listeners; + typedef std::deque<QueuedMessage> Messages; + + const string name; + const bool autodelete; + MessageStore* store; + const OwnershipToken* owner; + uint32_t consumerCount; + bool exclusive; + bool noLocal; + Listeners listeners; + Messages messages; + mutable qpid::sys::Mutex consumerLock; + mutable qpid::sys::Mutex messageLock; + mutable qpid::sys::Mutex ownershipLock; + mutable uint64_t persistenceId; + framing::FieldTable settings; + std::auto_ptr<QueuePolicy> policy; + QueueBindings bindings; + boost::shared_ptr<Exchange> alternateExchange; + framing::SequenceNumber sequence; + management::Queue::shared_ptr mgmtObject; + + void pop(); + void push(boost::intrusive_ptr<Message>& msg); + void setPolicy(std::auto_ptr<QueuePolicy> policy); + bool seek(QueuedMessage& msg, Consumer& position); + bool getNextMessage(QueuedMessage& msg, Consumer& c); + bool consumeNextMessage(QueuedMessage& msg, Consumer& c); + bool browseNextMessage(QueuedMessage& msg, Consumer& c); + bool canExcludeUnwanted(); + + void notify(); + void removeListener(Consumer&); + void addListener(Consumer&); + + public: + virtual void notifyDurableIOComplete(); + typedef boost::shared_ptr<Queue> shared_ptr; + + typedef std::vector<shared_ptr> vector; + + Queue(const string& name, bool autodelete = false, + MessageStore* const store = 0, + const OwnershipToken* const owner = 0, + management::Manageable* parent = 0); + ~Queue(); + + bool dispatch(Consumer&); + + void create(const qpid::framing::FieldTable& settings); + void configure(const qpid::framing::FieldTable& settings); + void destroy(); + void bound(const string& exchange, const string& key, const qpid::framing::FieldTable& args); + void unbind(ExchangeRegistry& exchanges, Queue::shared_ptr shared_ref); + + bool acquire(const QueuedMessage& msg); + + bool isLocal(boost::intrusive_ptr<Message>& msg); + /** + * Delivers a message to the queue. Will record it as + * enqueued if persistent then process it. + */ + void deliver(boost::intrusive_ptr<Message>& msg); + /** + * Dispatches the messages immediately to a consumer if + * one is available or stores it for later if not. + */ + void process(boost::intrusive_ptr<Message>& msg); + /** + * Returns a message to the in-memory queue (due to lack + * of acknowledegement from a receiver). If a consumer is + * available it will be dispatched immediately, else it + * will be returned to the front of the queue. + */ + void requeue(const QueuedMessage& msg); + /** + * Used during recovery to add stored messages back to the queue + */ + void recover(boost::intrusive_ptr<Message>& msg); + + void consume(Consumer& c, bool exclusive = false); + void cancel(Consumer& c); + + uint32_t purge(); + uint32_t getMessageCount() const; + uint32_t getConsumerCount() const; + inline const string& getName() const { return name; } + bool isExclusiveOwner(const OwnershipToken* const o) const; + void releaseExclusiveOwnership(); + bool setExclusiveOwner(const OwnershipToken* const o); + bool hasExclusiveConsumer() const; + bool hasExclusiveOwner() const; + inline bool isDurable() const { return store != 0; } + inline const framing::FieldTable& getSettings() const { return settings; } + inline bool isAutoDelete() const { return autodelete; } + bool canAutoDelete() const; + + bool enqueue(TransactionContext* ctxt, boost::intrusive_ptr<Message> msg); + /** + * dequeue from store (only done once messages is acknowledged) + */ + bool dequeue(TransactionContext* ctxt, boost::intrusive_ptr<Message> msg); + /** + * dequeues from memory only + */ + QueuedMessage dequeue(); + + const QueuePolicy* getPolicy(); + + void setAlternateExchange(boost::shared_ptr<Exchange> exchange); + boost::shared_ptr<Exchange> getAlternateExchange(); + + //PersistableQueue support: + uint64_t getPersistenceId() const; + void setPersistenceId(uint64_t persistenceId) const; + void encode(framing::Buffer& buffer) const; + uint32_t encodedSize() const; + + static Queue::shared_ptr decode(QueueRegistry& queues, framing::Buffer& buffer); + static void tryAutoDelete(Broker& broker, Queue::shared_ptr); + + virtual void setExternalQueueStore(ExternalQueueStore* inst); + + // Manageable entry points + management::ManagementObject::shared_ptr GetManagementObject (void) const; + management::Manageable::status_t + ManagementMethod (uint32_t methodId, management::Args& args); + }; + } +} + + +#endif /*!_broker_Queue_h*/ diff --git a/qpid/cpp/src/qpid/broker/QueueBindings.cpp b/qpid/cpp/src/qpid/broker/QueueBindings.cpp new file mode 100644 index 0000000000..e2fcd493db --- /dev/null +++ b/qpid/cpp/src/qpid/broker/QueueBindings.cpp @@ -0,0 +1,45 @@ +/* + * + * 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. + * + */ +#include "QueueBindings.h" +#include "ExchangeRegistry.h" + +using qpid::framing::FieldTable; +using std::string; +using namespace qpid::broker; + +void QueueBindings::add(const string& exchange, const string& key, const FieldTable& args) +{ + bindings.push_back(new Binding(exchange, key, args)); +} + +void QueueBindings::unbind(ExchangeRegistry& exchanges, Queue::shared_ptr queue) +{ + for (Bindings::iterator i = bindings.begin(); i != bindings.end(); i++) { + try { + exchanges.get(i->exchange)->unbind(queue, i->key, &(i->args)); + } catch (ChannelException&) { + } + } +} + +QueueBindings::Binding::Binding(const string& _exchange, const string& _key, const FieldTable& _args) + : exchange(_exchange), key(_key), args(_args) +{} diff --git a/qpid/cpp/src/qpid/broker/QueueBindings.h b/qpid/cpp/src/qpid/broker/QueueBindings.h new file mode 100644 index 0000000000..b9b0f7c15c --- /dev/null +++ b/qpid/cpp/src/qpid/broker/QueueBindings.h @@ -0,0 +1,55 @@ +/* + * + * 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. + * + */ +#ifndef _QueueBindings_ +#define _QueueBindings_ + +#include "qpid/framing/FieldTable.h" +#include <boost/ptr_container/ptr_list.hpp> +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace broker { + +class ExchangeRegistry; +class Queue; +class QueueBindings +{ + struct Binding{ + const std::string exchange; + const std::string key; + const qpid::framing::FieldTable args; + Binding(const std::string& exchange, const std::string& key, const qpid::framing::FieldTable& args); + }; + + typedef boost::ptr_list<Binding> Bindings; + Bindings bindings; + +public: + void add(const std::string& exchange, const std::string& key, const qpid::framing::FieldTable& args); + void unbind(ExchangeRegistry& exchanges, boost::shared_ptr<Queue> queue); +}; + + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/QueuePolicy.cpp b/qpid/cpp/src/qpid/broker/QueuePolicy.cpp new file mode 100644 index 0000000000..9b9717def0 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/QueuePolicy.cpp @@ -0,0 +1,70 @@ +/* + * + * 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. + * + */ +#include "QueuePolicy.h" +#include "qpid/framing/FieldValue.h" + +using namespace qpid::broker; +using namespace qpid::framing; + +QueuePolicy::QueuePolicy(uint32_t _maxCount, uint64_t _maxSize) : + maxCount(_maxCount), maxSize(_maxSize), count(0), size(0) {} + +QueuePolicy::QueuePolicy(const FieldTable& settings) : + maxCount(getInt(settings, maxCountKey, 0)), + maxSize(getInt(settings, maxSizeKey, 0)), count(0), size(0) {} + +void QueuePolicy::enqueued(uint64_t _size) +{ + if (maxCount) count++; + if (maxSize) size += _size; +} + +void QueuePolicy::dequeued(uint64_t _size) +{ + if (maxCount) count--; + if (maxSize) size -= _size; +} + +bool QueuePolicy::limitExceeded() +{ + return (maxSize && size > maxSize) || (maxCount && count > maxCount); +} + +void QueuePolicy::update(FieldTable& settings) +{ + if (maxCount) settings.setInt(maxCountKey, maxCount); + if (maxSize) settings.setInt(maxSizeKey, maxSize); +} + + +int QueuePolicy::getInt(const FieldTable& settings, const std::string& key, int defaultValue) +{ + //Note: currently field table only contain signed 32 bit ints, which + // restricts the values that can be set on the queue policy. + try { + return settings.getInt(key); + } catch (FieldValueException& ignore) { + return defaultValue; + } +} + +const std::string QueuePolicy::maxCountKey("qpid.max_count"); +const std::string QueuePolicy::maxSizeKey("qpid.max_size"); diff --git a/qpid/cpp/src/qpid/broker/QueuePolicy.h b/qpid/cpp/src/qpid/broker/QueuePolicy.h new file mode 100644 index 0000000000..0cc7070e09 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/QueuePolicy.h @@ -0,0 +1,54 @@ +/* + * + * 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. + * + */ +#ifndef _QueuePolicy_ +#define _QueuePolicy_ + +#include "qpid/framing/FieldTable.h" + +namespace qpid { + namespace broker { + class QueuePolicy + { + static const std::string maxCountKey; + static const std::string maxSizeKey; + + const uint32_t maxCount; + const uint64_t maxSize; + uint32_t count; + uint64_t size; + + static int getInt(const qpid::framing::FieldTable& settings, const std::string& key, int defaultValue); + + public: + QueuePolicy(uint32_t maxCount, uint64_t maxSize); + QueuePolicy(const qpid::framing::FieldTable& settings); + void enqueued(uint64_t size); + void dequeued(uint64_t size); + void update(qpid::framing::FieldTable& settings); + bool limitExceeded(); + uint32_t getMaxCount() const { return maxCount; } + uint64_t getMaxSize() const { return maxSize; } + }; + } +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/QueueRegistry.cpp b/qpid/cpp/src/qpid/broker/QueueRegistry.cpp new file mode 100644 index 0000000000..61bdb0ffde --- /dev/null +++ b/qpid/cpp/src/qpid/broker/QueueRegistry.cpp @@ -0,0 +1,93 @@ +/* + * + * 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. + * + */ +#include "QueueRegistry.h" +#include "qpid/log/Statement.h" +#include <sstream> +#include <assert.h> + +using namespace qpid::broker; +using namespace qpid::sys; + +QueueRegistry::QueueRegistry() : + counter(1), store(0), parent(0) {} + +QueueRegistry::~QueueRegistry(){} + +std::pair<Queue::shared_ptr, bool> +QueueRegistry::declare(const string& declareName, bool durable, + bool autoDelete, const OwnershipToken* owner) +{ + RWlock::ScopedWlock locker(lock); + string name = declareName.empty() ? generateName() : declareName; + assert(!name.empty()); + QueueMap::iterator i = queues.find(name); + + if (i == queues.end()) { + Queue::shared_ptr queue(new Queue(name, autoDelete, durable ? store : 0, owner, parent)); + queues[name] = queue; + + return std::pair<Queue::shared_ptr, bool>(queue, true); + } else { + return std::pair<Queue::shared_ptr, bool>(i->second, false); + } +} + +void QueueRegistry::destroyLH (const string& name){ + queues.erase(name); +} + +void QueueRegistry::destroy (const string& name){ + RWlock::ScopedWlock locker(lock); + destroyLH (name); +} + +Queue::shared_ptr QueueRegistry::find(const string& name){ + RWlock::ScopedRlock locker(lock); + QueueMap::iterator i = queues.find(name); + + if (i == queues.end()) { + return Queue::shared_ptr(); + } else { + return i->second; + } +} + +string QueueRegistry::generateName(){ + string name; + do { + std::stringstream ss; + ss << "tmp_" << counter++; + name = ss.str(); + // Thread safety: Private function, only called with lock held + // so this is OK. + } while(queues.find(name) != queues.end()); + return name; +} + +void QueueRegistry::setStore (MessageStore* _store) +{ + assert (store == 0 && _store != 0); + store = _store; +} + +MessageStore* QueueRegistry::getStore() const { + return store; +} diff --git a/qpid/cpp/src/qpid/broker/QueueRegistry.h b/qpid/cpp/src/qpid/broker/QueueRegistry.h new file mode 100644 index 0000000000..60da0619f4 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/QueueRegistry.h @@ -0,0 +1,119 @@ +/* + * + * 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. + * + */ +#ifndef _QueueRegistry_ +#define _QueueRegistry_ + +#include <map> +#include "qpid/sys/Mutex.h" +#include "Queue.h" +#include "qpid/management/Manageable.h" + +namespace qpid { +namespace broker { + +/** + * A registry of queues indexed by queue name. + * + * Queues are reference counted using shared_ptr to ensure that they + * are deleted when and only when they are no longer in use. + * + */ +class QueueRegistry{ + public: + QueueRegistry(); + ~QueueRegistry(); + + /** + * Declare a queue. + * + * @return The queue and a boolean flag which is true if the queue + * was created by this declare call false if it already existed. + */ + std::pair<Queue::shared_ptr, bool> declare(const string& name, bool durable = false, bool autodelete = false, + const OwnershipToken* const owner = 0); + + /** + * Destroy the named queue. + * + * Note: if the queue is in use it is not actually destroyed until + * all shared_ptrs to it are destroyed. During that time it is + * possible that a new queue with the same name may be + * created. This should not create any problems as the new and + * old queues exist independently. The registry has + * forgotten the old queue so there can be no confusion for + * subsequent calls to find or declare with the same name. + * + */ + void destroy (const string& name); + template <class Test> bool destroyIf(const string& name, Test test) + { + qpid::sys::RWlock::ScopedWlock locker(lock); + if (test()) { + destroyLH (name); + return true; + } else { + return false; + } + } + + /** + * Find the named queue. Return 0 if not found. + */ + Queue::shared_ptr find(const string& name); + + /** + * Generate unique queue name. + */ + string generateName(); + + /** + * Set the store to use. May only be called once. + */ + void setStore (MessageStore*); + + /** + * Return the message store used. + */ + MessageStore* getStore() const; + + /** + * Register the manageable parent for declared queues + */ + void setParent (management::Manageable* _parent) { parent = _parent; } + +private: + typedef std::map<string, Queue::shared_ptr> QueueMap; + QueueMap queues; + qpid::sys::RWlock lock; + int counter; + MessageStore* store; + management::Manageable* parent; + + //destroy impl that assumes lock is already held: + void destroyLH (const string& name); +}; + + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/RecoverableExchange.h b/qpid/cpp/src/qpid/broker/RecoverableExchange.h new file mode 100644 index 0000000000..76d0d2ecdf --- /dev/null +++ b/qpid/cpp/src/qpid/broker/RecoverableExchange.h @@ -0,0 +1,50 @@ +#ifndef _broker_RecoverableExchange_h +#define _broker_RecoverableExchange_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. + * + */ + +#include <boost/shared_ptr.hpp> +#include "qpid/framing/FieldTable.h" + +namespace qpid { +namespace broker { + +/** + * The interface through which bindings are recovered. + */ +class RecoverableExchange +{ +public: + typedef boost::shared_ptr<RecoverableExchange> shared_ptr; + + virtual void setPersistenceId(uint64_t id) = 0; + /** + * Recover binding. Nb: queue must have been recovered earlier. + */ + virtual void bind(std::string& queue, std::string& routingKey, qpid::framing::FieldTable& args) = 0; + virtual ~RecoverableExchange() {}; +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/RecoverableMessage.h b/qpid/cpp/src/qpid/broker/RecoverableMessage.h new file mode 100644 index 0000000000..f755fdf727 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/RecoverableMessage.h @@ -0,0 +1,58 @@ +#ifndef _broker_RecoverableMessage_h +#define _broker_RecoverableMessage_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. + * + */ + +#include <boost/shared_ptr.hpp> +#include "qpid/framing/amqp_types.h" +#include "qpid/framing/Buffer.h" + +namespace qpid { +namespace broker { + +/** + * The interface through which messages are reloaded on recovery. + */ +class RecoverableMessage +{ +public: + typedef boost::shared_ptr<RecoverableMessage> shared_ptr; + virtual void setPersistenceId(uint64_t id) = 0; + /** + * Used by store to determine whether to load content on recovery + * or let message load its own content as and when it requires it. + * + * @returns true if the content of the message should be loaded + */ + virtual bool loadContent(uint64_t available) = 0; + /** + * Loads the content held in the supplied buffer (may do checking + * of length as necessary) + */ + virtual void decodeContent(framing::Buffer& buffer) = 0; + virtual ~RecoverableMessage() {}; +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/RecoverableQueue.h b/qpid/cpp/src/qpid/broker/RecoverableQueue.h new file mode 100644 index 0000000000..b32bae7f07 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/RecoverableQueue.h @@ -0,0 +1,59 @@ +#ifndef _broker_RecoverableQueue_h +#define _broker_RecoverableQueue_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. + * + */ + +#include "RecoverableMessage.h" +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace broker { + +class ExternalQueueStore; + +/** + * The interface through which messages are added back to queues on + * recovery. + */ +class RecoverableQueue +{ +public: + typedef boost::shared_ptr<RecoverableQueue> shared_ptr; + + virtual void setPersistenceId(uint64_t id) = 0; + virtual uint64_t getPersistenceId() const = 0; + /** + * Used during recovery to add stored messages back to the queue + */ + virtual void recover(RecoverableMessage::shared_ptr msg) = 0; + virtual ~RecoverableQueue() {}; + + virtual const std::string& getName() const = 0; + virtual void setExternalQueueStore(ExternalQueueStore* inst) = 0; + virtual ExternalQueueStore* getExternalQueueStore() const = 0; + +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/RecoverableTransaction.h b/qpid/cpp/src/qpid/broker/RecoverableTransaction.h new file mode 100644 index 0000000000..7fe34b6756 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/RecoverableTransaction.h @@ -0,0 +1,49 @@ +#ifndef _broker_RecoverableTransaction_h +#define _broker_RecoverableTransaction_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. + * + */ + +#include <boost/shared_ptr.hpp> + +#include "RecoverableMessage.h" +#include "RecoverableQueue.h" + +namespace qpid { +namespace broker { + +/** + * The interface through which prepared 2pc transactions are + * recovered. + */ +class RecoverableTransaction +{ +public: + typedef boost::shared_ptr<RecoverableTransaction> shared_ptr; + virtual void enqueue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message) = 0; + virtual void dequeue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message) = 0; + virtual ~RecoverableTransaction() {}; +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/RecoveredDequeue.cpp b/qpid/cpp/src/qpid/broker/RecoveredDequeue.cpp new file mode 100644 index 0000000000..e2d70964fb --- /dev/null +++ b/qpid/cpp/src/qpid/broker/RecoveredDequeue.cpp @@ -0,0 +1,40 @@ +/* + * + * 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. + * + */ +#include "RecoveredDequeue.h" + +using boost::intrusive_ptr; +using namespace qpid::broker; + +RecoveredDequeue::RecoveredDequeue(Queue::shared_ptr _queue, intrusive_ptr<Message> _msg) : queue(_queue), msg(_msg) {} + +bool RecoveredDequeue::prepare(TransactionContext*) throw(){ + //should never be called; transaction has already prepared if an enqueue is recovered + return false; +} + +void RecoveredDequeue::commit() throw(){ +} + +void RecoveredDequeue::rollback() throw(){ + msg->enqueueComplete(); + queue->process(msg); +} + diff --git a/qpid/cpp/src/qpid/broker/RecoveredDequeue.h b/qpid/cpp/src/qpid/broker/RecoveredDequeue.h new file mode 100644 index 0000000000..276e1f4c5c --- /dev/null +++ b/qpid/cpp/src/qpid/broker/RecoveredDequeue.h @@ -0,0 +1,53 @@ +/* + * + * 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. + * + */ +#ifndef _RecoveredDequeue_ +#define _RecoveredDequeue_ + +#include "Deliverable.h" +#include "Message.h" +#include "MessageStore.h" +#include "Queue.h" +#include "TxOp.h" + +#include <boost/intrusive_ptr.hpp> + +#include <algorithm> +#include <functional> +#include <list> + +namespace qpid { + namespace broker { + class RecoveredDequeue : public TxOp{ + Queue::shared_ptr queue; + boost::intrusive_ptr<Message> msg; + + public: + RecoveredDequeue(Queue::shared_ptr queue, boost::intrusive_ptr<Message> msg); + virtual bool prepare(TransactionContext* ctxt) throw(); + virtual void commit() throw(); + virtual void rollback() throw(); + virtual ~RecoveredDequeue(){} + }; + } +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/RecoveredEnqueue.cpp b/qpid/cpp/src/qpid/broker/RecoveredEnqueue.cpp new file mode 100644 index 0000000000..1984a5d4a8 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/RecoveredEnqueue.cpp @@ -0,0 +1,40 @@ +/* + * + * 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. + * + */ +#include "RecoveredEnqueue.h" + +using boost::intrusive_ptr; +using namespace qpid::broker; + +RecoveredEnqueue::RecoveredEnqueue(Queue::shared_ptr _queue, intrusive_ptr<Message> _msg) : queue(_queue), msg(_msg) {} + +bool RecoveredEnqueue::prepare(TransactionContext*) throw(){ + //should never be called; transaction has already prepared if an enqueue is recovered + return false; +} + +void RecoveredEnqueue::commit() throw(){ + msg->enqueueComplete(); + queue->process(msg); +} + +void RecoveredEnqueue::rollback() throw(){ +} + diff --git a/qpid/cpp/src/qpid/broker/RecoveredEnqueue.h b/qpid/cpp/src/qpid/broker/RecoveredEnqueue.h new file mode 100644 index 0000000000..6525179769 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/RecoveredEnqueue.h @@ -0,0 +1,53 @@ +/* + * + * 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. + * + */ +#ifndef _RecoveredEnqueue_ +#define _RecoveredEnqueue_ + +#include "Deliverable.h" +#include "Message.h" +#include "MessageStore.h" +#include "Queue.h" +#include "TxOp.h" + +#include <boost/intrusive_ptr.hpp> + +#include <algorithm> +#include <functional> +#include <list> + +namespace qpid { + namespace broker { + class RecoveredEnqueue : public TxOp{ + Queue::shared_ptr queue; + boost::intrusive_ptr<Message> msg; + + public: + RecoveredEnqueue(Queue::shared_ptr queue, boost::intrusive_ptr<Message> msg); + virtual bool prepare(TransactionContext* ctxt) throw(); + virtual void commit() throw(); + virtual void rollback() throw(); + virtual ~RecoveredEnqueue(){} + }; + } +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/RecoveryManager.h b/qpid/cpp/src/qpid/broker/RecoveryManager.h new file mode 100644 index 0000000000..bf1813a093 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/RecoveryManager.h @@ -0,0 +1,58 @@ +/* + * + * 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. + * + */ +#ifndef _RecoveryManager_ +#define _RecoveryManager_ + +#include "RecoverableExchange.h" +#include "RecoverableQueue.h" +#include "RecoverableMessage.h" +#include "RecoverableTransaction.h" +#include "TransactionalStore.h" +#include "qpid/framing/Buffer.h" + +namespace qpid { +namespace broker { + +class RecoveryManager{ + public: + virtual ~RecoveryManager(){} + virtual RecoverableExchange::shared_ptr recoverExchange(framing::Buffer& buffer) = 0; + virtual RecoverableQueue::shared_ptr recoverQueue(framing::Buffer& buffer) = 0; + virtual RecoverableMessage::shared_ptr recoverMessage(framing::Buffer& buffer) = 0; + virtual RecoverableTransaction::shared_ptr recoverTransaction(const std::string& xid, + std::auto_ptr<TPCTransactionContext> txn) = 0; + virtual void recoveryComplete() = 0; +}; + +class Recoverable { + public: + virtual ~Recoverable() {} + + /** + * Request recovery of queue and message state. + */ + virtual void recover(RecoveryManager& recoverer) = 0; +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp b/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp new file mode 100644 index 0000000000..97226ebc22 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.cpp @@ -0,0 +1,222 @@ +/* + * + * 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. + * + */ +#include "RecoveryManagerImpl.h" + +#include "Message.h" +#include "Queue.h" +#include "RecoveredEnqueue.h" +#include "RecoveredDequeue.h" + +using namespace qpid; +using namespace qpid::broker; +using boost::dynamic_pointer_cast; +using boost::intrusive_ptr; + +static const uint8_t BASIC = 1; +static const uint8_t MESSAGE = 2; + +RecoveryManagerImpl::RecoveryManagerImpl(QueueRegistry& _queues, ExchangeRegistry& _exchanges, + DtxManager& _dtxMgr, uint64_t _stagingThreshold) + : queues(_queues), exchanges(_exchanges), dtxMgr(_dtxMgr), stagingThreshold(_stagingThreshold) {} + +RecoveryManagerImpl::~RecoveryManagerImpl() {} + +class RecoverableMessageImpl : public RecoverableMessage +{ + intrusive_ptr<Message> msg; + const uint64_t stagingThreshold; +public: + RecoverableMessageImpl(Message::shared_ptr& _msg, uint64_t _stagingThreshold) + : msg(_msg), stagingThreshold(_stagingThreshold) {} + ~RecoverableMessageImpl() {}; + void setPersistenceId(uint64_t id); + bool loadContent(uint64_t available); + void decodeContent(framing::Buffer& buffer); + void recover(Queue::shared_ptr queue); + void enqueue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue); + void dequeue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue); +}; + +class RecoverableQueueImpl : public RecoverableQueue +{ + Queue::shared_ptr queue; +public: + RecoverableQueueImpl(Queue::shared_ptr& _queue) : queue(_queue) {} + ~RecoverableQueueImpl() {}; + void setPersistenceId(uint64_t id); + uint64_t getPersistenceId() const; + const std::string& getName() const; + void setExternalQueueStore(ExternalQueueStore* inst); + ExternalQueueStore* getExternalQueueStore() const; + void recover(RecoverableMessage::shared_ptr msg); + void enqueue(DtxBuffer::shared_ptr buffer, RecoverableMessage::shared_ptr msg); + void dequeue(DtxBuffer::shared_ptr buffer, RecoverableMessage::shared_ptr msg); +}; + +class RecoverableExchangeImpl : public RecoverableExchange +{ + Exchange::shared_ptr exchange; + QueueRegistry& queues; +public: + RecoverableExchangeImpl(Exchange::shared_ptr _exchange, QueueRegistry& _queues) : exchange(_exchange), queues(_queues) {} + void setPersistenceId(uint64_t id); + void bind(std::string& queue, std::string& routingKey, qpid::framing::FieldTable& args); +}; + +class RecoverableTransactionImpl : public RecoverableTransaction +{ + DtxBuffer::shared_ptr buffer; +public: + RecoverableTransactionImpl(DtxBuffer::shared_ptr _buffer) : buffer(_buffer) {} + void enqueue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message); + void dequeue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message); +}; + +RecoverableExchange::shared_ptr RecoveryManagerImpl::recoverExchange(framing::Buffer& buffer) +{ + return RecoverableExchange::shared_ptr(new RecoverableExchangeImpl(Exchange::decode(exchanges, buffer), queues)); +} + +RecoverableQueue::shared_ptr RecoveryManagerImpl::recoverQueue(framing::Buffer& buffer) +{ + Queue::shared_ptr queue = Queue::decode(queues, buffer); + try { + Exchange::shared_ptr exchange = exchanges.getDefault(); + if (exchange) { + exchange->bind(queue, queue->getName(), 0); + } + } catch (ChannelException& e) { + //assume no default exchange has been declared + } + return RecoverableQueue::shared_ptr(new RecoverableQueueImpl(queue)); +} + +RecoverableMessage::shared_ptr RecoveryManagerImpl::recoverMessage(framing::Buffer& buffer) +{ + Message::shared_ptr message(new Message()); + message->decodeHeader(buffer); + return RecoverableMessage::shared_ptr(new RecoverableMessageImpl(message, stagingThreshold)); +} + +RecoverableTransaction::shared_ptr RecoveryManagerImpl::recoverTransaction(const std::string& xid, + std::auto_ptr<TPCTransactionContext> txn) +{ + DtxBuffer::shared_ptr buffer(new DtxBuffer()); + dtxMgr.recover(xid, txn, buffer); + return RecoverableTransaction::shared_ptr(new RecoverableTransactionImpl(buffer)); +} + +void RecoveryManagerImpl::recoveryComplete() +{ + //TODO (finalise binding setup etc) +} + +bool RecoverableMessageImpl::loadContent(uint64_t available) +{ + return !stagingThreshold || available < stagingThreshold; +} + +void RecoverableMessageImpl::decodeContent(framing::Buffer& buffer) +{ + msg->decodeContent(buffer); +} + +void RecoverableMessageImpl::recover(Queue::shared_ptr queue) +{ + queue->recover(msg); +} + +void RecoverableMessageImpl::setPersistenceId(uint64_t id) +{ + msg->setPersistenceId(id); +} + +void RecoverableQueueImpl::recover(RecoverableMessage::shared_ptr msg) +{ + dynamic_pointer_cast<RecoverableMessageImpl>(msg)->recover(queue); +} + +void RecoverableQueueImpl::setPersistenceId(uint64_t id) +{ + queue->setPersistenceId(id); +} + +uint64_t RecoverableQueueImpl::getPersistenceId() const +{ + return queue->getPersistenceId(); +} + +const std::string& RecoverableQueueImpl::getName() const +{ + return queue->getName(); +} + +void RecoverableQueueImpl::setExternalQueueStore(ExternalQueueStore* inst) +{ + queue->setExternalQueueStore(inst); +} + +ExternalQueueStore* RecoverableQueueImpl::getExternalQueueStore() const +{ + return queue->getExternalQueueStore(); +} + +void RecoverableExchangeImpl::setPersistenceId(uint64_t id) +{ + exchange->setPersistenceId(id); +} + +void RecoverableExchangeImpl::bind(string& queueName, string& key, framing::FieldTable& args) +{ + Queue::shared_ptr queue = queues.find(queueName); + exchange->bind(queue, key, &args); +} + +void RecoverableMessageImpl::dequeue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue) +{ + buffer->enlist(TxOp::shared_ptr(new RecoveredDequeue(queue, msg))); +} + +void RecoverableMessageImpl::enqueue(DtxBuffer::shared_ptr buffer, Queue::shared_ptr queue) +{ + msg->enqueueComplete(); // recoved nmessage to enqueued in store already + buffer->enlist(TxOp::shared_ptr(new RecoveredEnqueue(queue, msg))); +} + +void RecoverableQueueImpl::dequeue(DtxBuffer::shared_ptr buffer, RecoverableMessage::shared_ptr message) +{ + dynamic_pointer_cast<RecoverableMessageImpl>(message)->dequeue(buffer, queue); +} + +void RecoverableQueueImpl::enqueue(DtxBuffer::shared_ptr buffer, RecoverableMessage::shared_ptr message) +{ + dynamic_pointer_cast<RecoverableMessageImpl>(message)->enqueue(buffer, queue); +} + +void RecoverableTransactionImpl::dequeue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message) +{ + dynamic_pointer_cast<RecoverableQueueImpl>(queue)->dequeue(buffer, message); +} + +void RecoverableTransactionImpl::enqueue(RecoverableQueue::shared_ptr queue, RecoverableMessage::shared_ptr message) +{ + dynamic_pointer_cast<RecoverableQueueImpl>(queue)->enqueue(buffer, message); +} diff --git a/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.h b/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.h new file mode 100644 index 0000000000..58ec63926c --- /dev/null +++ b/qpid/cpp/src/qpid/broker/RecoveryManagerImpl.h @@ -0,0 +1,55 @@ +/* + * + * 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. + * + */ +#ifndef _RecoveryManagerImpl_ +#define _RecoveryManagerImpl_ + +#include <list> +#include "DtxManager.h" +#include "ExchangeRegistry.h" +#include "QueueRegistry.h" +#include "RecoveryManager.h" + +namespace qpid { +namespace broker { + + class RecoveryManagerImpl : public RecoveryManager{ + QueueRegistry& queues; + ExchangeRegistry& exchanges; + DtxManager& dtxMgr; + const uint64_t stagingThreshold; + public: + RecoveryManagerImpl(QueueRegistry& queues, ExchangeRegistry& exchanges, DtxManager& dtxMgr, uint64_t stagingThreshold); + ~RecoveryManagerImpl(); + + RecoverableExchange::shared_ptr recoverExchange(framing::Buffer& buffer); + RecoverableQueue::shared_ptr recoverQueue(framing::Buffer& buffer); + RecoverableMessage::shared_ptr recoverMessage(framing::Buffer& buffer); + RecoverableTransaction::shared_ptr recoverTransaction(const std::string& xid, + std::auto_ptr<TPCTransactionContext> txn); + void recoveryComplete(); + }; + + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/SemanticHandler.cpp b/qpid/cpp/src/qpid/broker/SemanticHandler.cpp new file mode 100644 index 0000000000..eb45ff1492 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/SemanticHandler.cpp @@ -0,0 +1,196 @@ +/* + * + * 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. + * + */ + +#include "SemanticHandler.h" +#include "SemanticState.h" +#include "SessionContext.h" +#include "BrokerAdapter.h" +#include "MessageDelivery.h" +#include "qpid/framing/ExecutionCompleteBody.h" +#include "qpid/framing/ExecutionResultBody.h" +#include "qpid/framing/ServerInvoker.h" +#include "qpid/log/Statement.h" + +#include <boost/format.hpp> +#include <boost/bind.hpp> + +using boost::intrusive_ptr; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +SemanticHandler::SemanticHandler(SessionContext& s) : + state(*this,s), session(s), + msgBuilder(&s.getConnection().getBroker().getStore(), s.getConnection().getBroker().getStagingThreshold()), + ackOp(boost::bind(&SemanticState::ackRange, &state, _1, _2)) + {} + +void SemanticHandler::handle(framing::AMQFrame& frame) +{ + //TODO: assembly for method and headers + + //have potentially three separate tracks at this point: + // + // (1) execution controls + // (2) commands + // (3) data i.e. content-bearing commands + // + //framesets on each can be interleaved. framesets on the latter + //two share a command-id sequence. controls on the first track are + //used to communicate details about that command-id sequence. + // + //need to decide what to do if a frame on the command track + //arrives while a frameset on the data track is still + //open. execute it (i.e. out-of order execution with respect to + //the command id sequence) or queue it up? + + TrackId track = getTrack(frame);//will be replaced by field in 0-10 frame header + + switch(track) { + case EXECUTION_CONTROL_TRACK: + handleL3(frame.getMethod()); + break; + case MODEL_COMMAND_TRACK: + handleCommand(frame.getMethod()); + break; + case MODEL_CONTENT_TRACK: + handleContent(frame); + break; + } +} + +void SemanticHandler::complete(uint32_t cumulative, const SequenceNumberSet& range) +{ + //record: + SequenceNumber mark(cumulative); + if (outgoing.lwm < mark) { + outgoing.lwm = mark; + //ack messages: + state.ackCumulative(mark.getValue()); + } + range.processRanges(ackOp); +} + +void SemanticHandler::sendCompletion() +{ + SequenceNumber mark = incoming.getMark(); + SequenceNumberSet range = incoming.getRange(); + session.getProxy().getExecution().complete(mark.getValue(), range); +} + +void SemanticHandler::flush() +{ + incoming.flush(); + sendCompletion(); +} +void SemanticHandler::sync() +{ + incoming.sync(); + sendCompletion(); +} + +void SemanticHandler::noop() +{ + incoming.noop(); +} + +void SemanticHandler::result(uint32_t /*command*/, const std::string& /*data*/) +{ + //never actually sent by client at present +} + +void SemanticHandler::handleCommand(framing::AMQMethodBody* method) +{ + SequenceNumber id = incoming.next(); + BrokerAdapter adapter(state); + Invoker::Result invoker = invoke(adapter, *method); + incoming.complete(id); + + if (!invoker.wasHandled()) { + throw NotImplementedException("Not implemented"); + } else if (invoker.hasResult()) { + session.getProxy().getExecution().result(id.getValue(), invoker.getResult()); + } + if (method->isSync()) { + incoming.sync(id); + sendCompletion(); + } + //TODO: if window gets too large send unsolicited completion +} + +void SemanticHandler::handleL3(framing::AMQMethodBody* method) +{ + if (!invoke(*this, *method)) + throw NotImplementedException("Not implemented"); +} + +void SemanticHandler::handleContent(AMQFrame& frame) +{ + intrusive_ptr<Message> msg(msgBuilder.getMessage()); + if (!msg) {//start of frameset will be indicated by frame flags + msgBuilder.start(incoming.next()); + msg = msgBuilder.getMessage(); + } + msgBuilder.handle(frame); + if (frame.getEof() && frame.getEos()) {//end of frameset will be indicated by frame flags + msg->setPublisher(&session.getConnection()); + state.handle(msg); + msgBuilder.end(); + incoming.track(msg); + if (msg->getFrames().getMethod()->isSync()) { + incoming.sync(msg->getCommandId()); + sendCompletion(); + } + } +} + +DeliveryId SemanticHandler::deliver(QueuedMessage& msg, DeliveryToken::shared_ptr token) +{ + uint32_t maxFrameSize = session.getConnection().getFrameMax(); + MessageDelivery::deliver(msg, session.getProxy().getHandler(), ++outgoing.hwm, token, maxFrameSize); + return outgoing.hwm; +} + +SemanticHandler::TrackId SemanticHandler::getTrack(const AMQFrame& frame) +{ + //will be replaced by field in 0-10 frame header + uint8_t type = frame.getBody()->type(); + uint16_t classId; + switch(type) { + case METHOD_BODY: + if (frame.castBody<AMQMethodBody>()->isContentBearing()) { + return MODEL_CONTENT_TRACK; + } + + classId = frame.castBody<AMQMethodBody>()->amqpClassId(); + switch (classId) { + case ExecutionCompleteBody::CLASS_ID: + return EXECUTION_CONTROL_TRACK; + } + + return MODEL_COMMAND_TRACK; + case HEADER_BODY: + case CONTENT_BODY: + return MODEL_CONTENT_TRACK; + } + throw Exception("Could not determine track"); +} + diff --git a/qpid/cpp/src/qpid/broker/SemanticHandler.h b/qpid/cpp/src/qpid/broker/SemanticHandler.h new file mode 100644 index 0000000000..893a0cbded --- /dev/null +++ b/qpid/cpp/src/qpid/broker/SemanticHandler.h @@ -0,0 +1,102 @@ +/* + * + * 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. + * + */ +#ifndef _SemanticHandler_ +#define _SemanticHandler_ + +#include <memory> +#include "BrokerAdapter.h" +#include "DeliveryAdapter.h" +#include "MessageBuilder.h" +#include "IncomingExecutionContext.h" +#include "HandlerImpl.h" + +#include "qpid/framing/amqp_types.h" +#include "qpid/framing/AMQP_ServerOperations.h" +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/SequenceNumber.h" + +#include <boost/function.hpp> + +namespace qpid { + +namespace framing { +class AMQMethodBody; +class AMQHeaderBody; +class AMQContentBody; +class AMQHeaderBody; +} + +namespace broker { + +class SessionContext; + +class SemanticHandler : public DeliveryAdapter, + public framing::FrameHandler, + public framing::AMQP_ServerOperations::ExecutionHandler + +{ + typedef boost::function<void(DeliveryId, DeliveryId)> RangedOperation; + + SemanticState state; + SessionContext& session; + // TODO aconway 2007-09-20: Why are these on the handler rather than the + // state? + IncomingExecutionContext incoming; + framing::Window outgoing; + MessageBuilder msgBuilder; + RangedOperation ackOp; + + enum TrackId {EXECUTION_CONTROL_TRACK, MODEL_COMMAND_TRACK, MODEL_CONTENT_TRACK}; + TrackId getTrack(const framing::AMQFrame& frame); + + void handleL3(framing::AMQMethodBody* method); + void handleCommand(framing::AMQMethodBody* method); + void handleContent(framing::AMQFrame& frame); + + void sendCompletion(); + + //delivery adapter methods: + DeliveryId deliver(QueuedMessage& msg, DeliveryToken::shared_ptr token); + + framing::AMQP_ClientProxy& getProxy() { return session.getProxy(); } + //Connection& getConnection() { return session.getConnection(); } + Broker& getBroker() { return session.getConnection().getBroker(); } + +public: + SemanticHandler(SessionContext& session); + + //frame handler: + void handle(framing::AMQFrame& frame); + + //execution class method handlers: + void complete(uint32_t cumulativeExecutionMark, const framing::SequenceNumberSet& range); + void flush(); + void noop(); + void result(uint32_t command, const std::string& data); + void sync(); + + + SemanticState& getSemanticState() { return state; } +}; + +}} + +#endif diff --git a/qpid/cpp/src/qpid/broker/SemanticState.cpp b/qpid/cpp/src/qpid/broker/SemanticState.cpp new file mode 100644 index 0000000000..2251901340 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/SemanticState.cpp @@ -0,0 +1,696 @@ +/* + * + * 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. + * + */ + +#include "SessionContext.h" +#include "BrokerAdapter.h" +#include "Queue.h" +#include "Connection.h" +#include "DeliverableMessage.h" +#include "DtxAck.h" +#include "DtxTimeout.h" +#include "Message.h" +#include "SemanticHandler.h" +#include "SessionHandler.h" +#include "TxAccept.h" +#include "TxAck.h" +#include "TxPublish.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/Message010TransferBody.h" +#include "qpid/log/Statement.h" +#include "qpid/ptr_map.h" + +#include <boost/bind.hpp> +#include <boost/format.hpp> + +#include <iostream> +#include <sstream> +#include <algorithm> +#include <functional> + +#include <assert.h> + + +namespace qpid { +namespace broker { + +using std::mem_fun_ref; +using std::bind2nd; +using boost::intrusive_ptr; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; +using namespace qpid::ptr_map; + +SemanticState::SemanticState(DeliveryAdapter& da, SessionContext& ss) + : session(ss), + deliveryAdapter(da), + prefetchSize(0), + prefetchCount(0), + tagGenerator("sgen"), + dtxSelected(false), + accumulatedAck(0), + flowActive(true), + outputTasks(ss) +{ + outstanding.reset(); +} + +SemanticState::~SemanticState() { + //cancel all consumers + for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) { + cancel(*get_pointer(i)); + } + + if (dtxBuffer.get()) { + dtxBuffer->fail(); + } + recover(true); +} + +bool SemanticState::exists(const string& consumerTag){ + return consumers.find(consumerTag) != consumers.end(); +} + +void SemanticState::consume(DeliveryToken::shared_ptr token, string& tagInOut, + Queue::shared_ptr queue, bool nolocal, bool ackRequired, bool acquire, + bool exclusive, const FieldTable*) +{ + if(tagInOut.empty()) + tagInOut = tagGenerator.generate(); + std::auto_ptr<ConsumerImpl> c(new ConsumerImpl(this, token, tagInOut, queue, ackRequired, nolocal, acquire)); + queue->consume(*c, exclusive);//may throw exception + outputTasks.addOutputTask(c.get()); + consumers.insert(tagInOut, c.release()); +} + +void SemanticState::cancel(const string& tag){ + ConsumerImplMap::iterator i = consumers.find(tag); + if (i != consumers.end()) { + cancel(*get_pointer(i)); + consumers.erase(i); + //should cancel all unacked messages for this consumer so that + //they are not redelivered on recovery + for_each(unacked.begin(), unacked.end(), boost::bind(mem_fun_ref(&DeliveryRecord::cancel), _1, tag)); + + } +} + + +void SemanticState::startTx() +{ + txBuffer = TxBuffer::shared_ptr(new TxBuffer()); +} + +void SemanticState::commit(MessageStore* const store, bool completeOnCommit) +{ + if (!txBuffer) throw + CommandInvalidException(QPID_MSG("Session has not been selected for use with transactions")); + + TxOp::shared_ptr txAck(completeOnCommit ? + static_cast<TxOp*>(new TxAck(accumulatedAck, unacked)) : + static_cast<TxOp*>(new TxAccept(accumulatedAck, unacked))); + txBuffer->enlist(txAck); + if (txBuffer->commitLocal(store)) { + accumulatedAck.clear(); + } +} + +void SemanticState::rollback() +{ + if (!txBuffer) + throw CommandInvalidException(QPID_MSG("Session has not been selected for use with transactions")); + + txBuffer->rollback(); + accumulatedAck.clear(); +} + +void SemanticState::selectDtx() +{ + dtxSelected = true; +} + +void SemanticState::startDtx(const std::string& xid, DtxManager& mgr, bool join) +{ + if (!dtxSelected) { + throw CommandInvalidException(QPID_MSG("Session has not been selected for use with dtx")); + } + dtxBuffer = DtxBuffer::shared_ptr(new DtxBuffer(xid)); + txBuffer = static_pointer_cast<TxBuffer>(dtxBuffer); + if (join) { + mgr.join(xid, dtxBuffer); + } else { + mgr.start(xid, dtxBuffer); + } +} + +void SemanticState::endDtx(const std::string& xid, bool fail) +{ + if (!dtxBuffer) { + throw CommandInvalidException(QPID_MSG("xid " << xid << " not associated with this session")); + } + if (dtxBuffer->getXid() != xid) { + throw CommandInvalidException( + QPID_MSG("xid specified on start was " << dtxBuffer->getXid() << ", but " << xid << " specified on end")); + + } + + txBuffer.reset();//ops on this session no longer transactional + + checkDtxTimeout(); + if (fail) { + dtxBuffer->fail(); + } else { + dtxBuffer->markEnded(); + } + dtxBuffer.reset(); +} + +void SemanticState::suspendDtx(const std::string& xid) +{ + if (dtxBuffer->getXid() != xid) { + throw CommandInvalidException( + QPID_MSG("xid specified on start was " << dtxBuffer->getXid() << ", but " << xid << " specified on suspend")); + } + txBuffer.reset();//ops on this session no longer transactional + + checkDtxTimeout(); + dtxBuffer->setSuspended(true); + suspendedXids[xid] = dtxBuffer; + dtxBuffer.reset(); +} + +void SemanticState::resumeDtx(const std::string& xid) +{ + if (!dtxSelected) { + throw CommandInvalidException(QPID_MSG("Session has not been selected for use with dtx")); + } + + dtxBuffer = suspendedXids[xid]; + if (!dtxBuffer) { + throw CommandInvalidException(QPID_MSG("xid " << xid << " not attached")); + } else { + suspendedXids.erase(xid); + } + + if (dtxBuffer->getXid() != xid) { + throw CommandInvalidException( + QPID_MSG("xid specified on start was " << dtxBuffer->getXid() << ", but " << xid << " specified on resume")); + + } + if (!dtxBuffer->isSuspended()) { + throw CommandInvalidException(QPID_MSG("xid " << xid << " not suspended")); + } + + checkDtxTimeout(); + dtxBuffer->setSuspended(false); + txBuffer = static_pointer_cast<TxBuffer>(dtxBuffer); +} + +void SemanticState::checkDtxTimeout() +{ + if (dtxBuffer->isExpired()) { + dtxBuffer.reset(); + throw DtxTimeoutException(); + } +} + +void SemanticState::record(const DeliveryRecord& delivery) +{ + unacked.push_back(delivery); + delivery.addTo(outstanding); +} + +bool SemanticState::checkPrefetch(intrusive_ptr<Message>& msg) +{ + bool countOk = !prefetchCount || prefetchCount > unacked.size(); + bool sizeOk = !prefetchSize || prefetchSize > msg->contentSize() + outstanding.size || unacked.empty(); + return countOk && sizeOk; +} + +SemanticState::ConsumerImpl::ConsumerImpl(SemanticState* _parent, + DeliveryToken::shared_ptr _token, + const string& _name, + Queue::shared_ptr _queue, + bool ack, + bool _nolocal, + bool _acquire + ) : + Consumer(_acquire), + parent(_parent), + token(_token), + name(_name), + queue(_queue), + ackExpected(ack), + nolocal(_nolocal), + acquire(_acquire), + blocked(true), + windowing(true), + msgCredit(0), + byteCredit(0) {} + +bool SemanticState::ConsumerImpl::deliver(QueuedMessage& msg) +{ + allocateCredit(msg.payload); + DeliveryId deliveryTag = + parent->deliveryAdapter.deliver(msg, token); + if (windowing || ackExpected) { + parent->record(DeliveryRecord(msg, queue, name, token, deliveryTag, acquire, !ackExpected)); + } + if (acquire && !ackExpected) { + queue->dequeue(0, msg.payload); + } + return true; +} + +bool SemanticState::ConsumerImpl::filter(intrusive_ptr<Message> msg) +{ + return !(nolocal && + &parent->getSession().getConnection() == msg->getPublisher()); +} + +bool SemanticState::ConsumerImpl::accept(intrusive_ptr<Message> msg) +{ + //TODO: remove the now redundant checks (channel.flow & basic|message.qos removed): + blocked = !(filter(msg) && checkCredit(msg) && parent->flowActive && (!ackExpected || parent->checkPrefetch(msg))); + return !blocked; +} + +void SemanticState::ConsumerImpl::allocateCredit(intrusive_ptr<Message>& msg) +{ + uint32_t originalMsgCredit = msgCredit; + uint32_t originalByteCredit = byteCredit; + if (msgCredit != 0xFFFFFFFF) { + msgCredit--; + } + if (byteCredit != 0xFFFFFFFF) { + byteCredit -= msg->getRequiredCredit(); + } + QPID_LOG(debug, "Credit allocated for '" << name << "' on " << parent + << ", was " << " bytes: " << originalByteCredit << " msgs: " << originalMsgCredit + << " now bytes: " << byteCredit << " msgs: " << msgCredit); + +} + +bool SemanticState::ConsumerImpl::checkCredit(intrusive_ptr<Message>& msg) +{ + if (msgCredit == 0 || (byteCredit != 0xFFFFFFFF && byteCredit < msg->getRequiredCredit())) { + QPID_LOG(debug, "Not enough credit for '" << name << "' on " << parent + << ", bytes: " << byteCredit << " msgs: " << msgCredit); + return false; + } else { + QPID_LOG(debug, "Credit available for '" << name << "' on " << parent + << " bytes: " << byteCredit << " msgs: " << msgCredit); + return true; + } +} + +SemanticState::ConsumerImpl::~ConsumerImpl() {} + +void SemanticState::cancel(ConsumerImpl& c) +{ + outputTasks.removeOutputTask(&c); + Queue::shared_ptr queue = c.getQueue(); + if(queue) { + queue->cancel(c); + if (queue->canAutoDelete() && !queue->hasExclusiveOwner()) { + Queue::tryAutoDelete(session.getBroker(), queue); + } + } +} + +void SemanticState::handle(intrusive_ptr<Message> msg) { + if (txBuffer.get()) { + TxPublish* deliverable(new TxPublish(msg)); + TxOp::shared_ptr op(deliverable); + route(msg, *deliverable); + txBuffer->enlist(op); + } else { + DeliverableMessage deliverable(msg); + route(msg, deliverable); + } +} + +void SemanticState::route(intrusive_ptr<Message> msg, Deliverable& strategy) { + std::string exchangeName = msg->getExchangeName(); + if (msg->isA<MessageTransferBody>()) { + msg->getProperties<DeliveryProperties>()->setExchange(exchangeName); + } else if (msg->isA<Message010TransferBody>()) { + msg->getProperties<DeliveryProperties010>()->setExchange(exchangeName); + } + if (!cacheExchange || cacheExchange->getName() != exchangeName){ + cacheExchange = session.getBroker().getExchanges().get(exchangeName); + } + + cacheExchange->route(strategy, msg->getRoutingKey(), msg->getApplicationHeaders()); + + if (!strategy.delivered) { + //TODO:if reject-unroutable, then reject + //else route to alternate exchange + if (cacheExchange->getAlternate()) { + cacheExchange->getAlternate()->route(strategy, msg->getRoutingKey(), msg->getApplicationHeaders()); + } + } + +} + +void SemanticState::ackCumulative(DeliveryId id) +{ + ack(id, id, true); +} + +void SemanticState::ackRange(DeliveryId first, DeliveryId last) +{ + ack(first, last, false); +} + +void SemanticState::ack(DeliveryId first, DeliveryId last, bool cumulative) +{ + { + ack_iterator start = cumulative ? unacked.begin() : + find_if(unacked.begin(), unacked.end(), bind2nd(mem_fun_ref(&DeliveryRecord::matchOrAfter), first)); + ack_iterator end = start; + + if (cumulative || first != last) { + //need to find end (position it just after the last record in range) + end = find_if(start, unacked.end(), bind2nd(mem_fun_ref(&DeliveryRecord::after), last)); + } else if (start != unacked.end()) { + //just acked single element (move end past it) + ++end; + } + + for_each(start, end, boost::bind(&SemanticState::complete, this, _1)); + + if (txBuffer.get()) { + //in transactional mode, don't dequeue or remove, just + //maintain set of acknowledged messages: + accumulatedAck.update(cumulative ? accumulatedAck.mark : first, last); + + if (dtxBuffer.get()) { + //if enlisted in a dtx, copy the relevant slice from + //unacked and record it against that transaction: + TxOp::shared_ptr txAck(new DtxAck(accumulatedAck, unacked)); + //then remove that slice from the unacked record: + unacked.remove_if(bind2nd(mem_fun_ref(&DeliveryRecord::coveredBy), &accumulatedAck)); + accumulatedAck.clear(); + dtxBuffer->enlist(txAck); + } + } else { + for_each(start, end, bind2nd(mem_fun_ref(&DeliveryRecord::dequeue), 0)); + unacked.erase(start, end); + } + }//end of lock scope for delivery lock (TODO this is ugly, make it prettier) + + //if the prefetch limit had previously been reached, or credit + //had expired in windowing mode there may be messages that can + //be now be delivered + requestDispatch(); +} + +void SemanticState::requestDispatch() +{ + for (ConsumerImplMap::iterator i = consumers.begin(); i != consumers.end(); i++) { + requestDispatch(*get_pointer(i)); + } +} + +void SemanticState::requestDispatch(ConsumerImpl& c) +{ + if(c.isBlocked()) { + c.doOutput(); + } +} + +void SemanticState::complete(DeliveryRecord& delivery) +{ + delivery.subtractFrom(outstanding); + ConsumerImplMap::iterator i = consumers.find(delivery.getTag()); + if (i != consumers.end()) { + get_pointer(i)->complete(delivery); + } +} + +void SemanticState::ConsumerImpl::complete(DeliveryRecord& delivery) +{ + if (!delivery.isComplete()) { + delivery.complete(); + if (windowing) { + if (msgCredit != 0xFFFFFFFF) msgCredit++; + if (byteCredit != 0xFFFFFFFF) byteCredit += delivery.getCredit(); + } + } +} + +void SemanticState::recover(bool requeue) +{ + if(requeue){ + outstanding.reset(); + //take copy and clear unacked as requeue may result in redelivery to this session + //which will in turn result in additions to unacked + std::list<DeliveryRecord> copy = unacked; + unacked.clear(); + for_each(copy.rbegin(), copy.rend(), mem_fun_ref(&DeliveryRecord::requeue)); + }else{ + for_each(unacked.begin(), unacked.end(), bind2nd(mem_fun_ref(&DeliveryRecord::redeliver), this)); + //unconfirmed messages re redelivered and therefore have their + //id adjusted, confirmed messages are not and so the ordering + //w.r.t id is lost + unacked.sort(); + } +} + +bool SemanticState::get(DeliveryToken::shared_ptr token, Queue::shared_ptr queue, bool ackExpected) +{ + QueuedMessage msg = queue->dequeue(); + if(msg.payload){ + DeliveryId myDeliveryTag = deliveryAdapter.deliver(msg, token); + if(ackExpected){ + unacked.push_back(DeliveryRecord(msg, queue, myDeliveryTag)); + } + return true; + }else{ + return false; + } +} + +DeliveryId SemanticState::redeliver(QueuedMessage& msg, DeliveryToken::shared_ptr token) +{ + return deliveryAdapter.deliver(msg, token); +} + +void SemanticState::flow(bool active) +{ + bool requestDelivery(!flowActive && active); + flowActive = active; + if (requestDelivery) { + //there may be messages that can be now be delivered + requestDispatch(); + } +} + + +SemanticState::ConsumerImpl& SemanticState::find(const std::string& destination) +{ + ConsumerImplMap::iterator i = consumers.find(destination); + if (i == consumers.end()) { + throw NotFoundException(QPID_MSG("Unknown destination " << destination)); + } else { + return *get_pointer(i); + } +} + +void SemanticState::setWindowMode(const std::string& destination) +{ + find(destination).setWindowMode(); +} + +void SemanticState::setCreditMode(const std::string& destination) +{ + find(destination).setCreditMode(); +} + +void SemanticState::addByteCredit(const std::string& destination, uint32_t value) +{ + ConsumerImpl& c = find(destination); + c.addByteCredit(value); + requestDispatch(c); +} + + +void SemanticState::addMessageCredit(const std::string& destination, uint32_t value) +{ + ConsumerImpl& c = find(destination); + c.addMessageCredit(value); + requestDispatch(c); +} + +void SemanticState::flush(const std::string& destination) +{ + find(destination).flush(); +} + + +void SemanticState::stop(const std::string& destination) +{ + find(destination).stop(); +} + +void SemanticState::ConsumerImpl::setWindowMode() +{ + windowing = true; +} + +void SemanticState::ConsumerImpl::setCreditMode() +{ + windowing = false; +} + +void SemanticState::ConsumerImpl::addByteCredit(uint32_t value) +{ + if (byteCredit != 0xFFFFFFFF) { + byteCredit += value; + } +} + +void SemanticState::ConsumerImpl::addMessageCredit(uint32_t value) +{ + if (msgCredit != 0xFFFFFFFF) { + msgCredit += value; + } +} + +void SemanticState::ConsumerImpl::flush() +{ + while(queue->dispatch(*this)) + ; + stop(); +} + +void SemanticState::ConsumerImpl::stop() +{ + msgCredit = 0; + byteCredit = 0; +} + +Queue::shared_ptr SemanticState::getQueue(const string& name) const { + Queue::shared_ptr queue; + if (name.empty()) { + throw NotAllowedException(QPID_MSG("No queue name specified.")); + } else { + queue = session.getBroker().getQueues().find(name); + if (!queue) + throw NotFoundException(QPID_MSG("Queue not found: "<<name)); + } + return queue; +} + +AckRange SemanticState::findRange(DeliveryId first, DeliveryId last) +{ + ack_iterator start = find_if(unacked.begin(), unacked.end(), bind2nd(mem_fun_ref(&DeliveryRecord::matchOrAfter), first)); + ack_iterator end = start; + + if (start != unacked.end()) { + if (first == last) { + //just acked single element (move end past it) + ++end; + } else { + //need to find end (position it just after the last record in range) + end = find_if(start, unacked.end(), bind2nd(mem_fun_ref(&DeliveryRecord::after), last)); + } + } + return AckRange(start, end); +} + +void SemanticState::acquire(DeliveryId first, DeliveryId last, DeliveryIds& acquired) +{ + AckRange range = findRange(first, last); + for_each(range.start, range.end, AcquireFunctor(acquired)); +} + +void SemanticState::release(DeliveryId first, DeliveryId last, bool setRedelivered) +{ + AckRange range = findRange(first, last); + //release results in the message being added to the head so want + //to release in reverse order to keep the original transfer order + DeliveryRecords::reverse_iterator start(range.end); + DeliveryRecords::reverse_iterator end(range.start); + for_each(start, end, bind2nd(mem_fun_ref(&DeliveryRecord::release), setRedelivered)); +} + +void SemanticState::reject(DeliveryId first, DeliveryId last) +{ + AckRange range = findRange(first, last); + for_each(range.start, range.end, mem_fun_ref(&DeliveryRecord::reject)); + //need to remove the delivery records as well + unacked.erase(range.start, range.end); +} + +bool SemanticState::ConsumerImpl::doOutput() +{ + //TODO: think through properly + return queue->dispatch(*this); +} + +void SemanticState::ConsumerImpl::notify() +{ + //TODO: think through properly + parent->outputTasks.activateOutput(); +} + + +void SemanticState::accepted(DeliveryId first, DeliveryId last) +{ + AckRange range = findRange(first, last); + if (txBuffer.get()) { + //in transactional mode, don't dequeue or remove, just + //maintain set of acknowledged messages: + accumulatedAck.update(first, last);//TODO convert accumulatedAck to SequenceSet + + if (dtxBuffer.get()) { + //if enlisted in a dtx, copy the relevant slice from + //unacked and record it against that transaction + TxOp::shared_ptr txAck(new DtxAck(accumulatedAck, unacked)); + accumulatedAck.clear(); + dtxBuffer->enlist(txAck); + + //mark the relevant messages as 'ended' in unacked + for_each(range.start, range.end, mem_fun_ref(&DeliveryRecord::setEnded)); + + //if the messages are already completed, they can be + //removed from the record + unacked.remove_if(mem_fun_ref(&DeliveryRecord::isRedundant)); + + } + } else { + for_each(range.start, range.end, bind2nd(mem_fun_ref(&DeliveryRecord::accept), 0)); + unacked.remove_if(mem_fun_ref(&DeliveryRecord::isRedundant)); + } +} + +void SemanticState::completed(DeliveryId first, DeliveryId last) +{ + AckRange range = findRange(first, last); + for_each(range.start, range.end, boost::bind(&SemanticState::complete, this, _1)); + unacked.remove_if(mem_fun_ref(&DeliveryRecord::isRedundant)); + requestDispatch(); +} + +}} // namespace qpid::broker diff --git a/qpid/cpp/src/qpid/broker/SemanticState.h b/qpid/cpp/src/qpid/broker/SemanticState.h new file mode 100644 index 0000000000..20a0239db0 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/SemanticState.h @@ -0,0 +1,200 @@ +#ifndef QPID_BROKER_SEMANTICSTATE_H +#define QPID_BROKER_SEMANTICSTATE_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. + * + */ + +#include "Consumer.h" +#include "Deliverable.h" +#include "DeliveryAdapter.h" +#include "DeliveryRecord.h" +#include "DeliveryToken.h" +#include "DtxBuffer.h" +#include "DtxManager.h" +#include "NameGenerator.h" +#include "Prefetch.h" +#include "TxBuffer.h" + +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/AccumulatedAck.h" +#include "qpid/framing/Uuid.h" +#include "qpid/sys/AggregateOutput.h" +#include "qpid/shared_ptr.h" + +#include <list> +#include <map> +#include <vector> + +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { + +class SessionContext; + +/** + * SemanticState holds the L3 and L4 state of an open session, whether + * attached to a channel or suspended. + */ +class SemanticState : public framing::FrameHandler::Chains, + public sys::OutputTask, + private boost::noncopyable +{ + class ConsumerImpl : public Consumer, public sys::OutputTask + { + SemanticState* const parent; + const DeliveryToken::shared_ptr token; + const string name; + const Queue::shared_ptr queue; + const bool ackExpected; + const bool nolocal; + const bool acquire; + bool blocked; + bool windowing; + uint32_t msgCredit; + uint32_t byteCredit; + + bool checkCredit(boost::intrusive_ptr<Message>& msg); + void allocateCredit(boost::intrusive_ptr<Message>& msg); + + public: + ConsumerImpl(SemanticState* parent, DeliveryToken::shared_ptr token, + const string& name, Queue::shared_ptr queue, + bool ack, bool nolocal, bool acquire); + ~ConsumerImpl(); + bool deliver(QueuedMessage& msg); + bool filter(boost::intrusive_ptr<Message> msg); + bool accept(boost::intrusive_ptr<Message> msg); + void notify(); + + void setWindowMode(); + void setCreditMode(); + void addByteCredit(uint32_t value); + void addMessageCredit(uint32_t value); + void flush(); + void stop(); + void complete(DeliveryRecord&); + Queue::shared_ptr getQueue() { return queue; } + bool isBlocked() const { return blocked; } + + bool doOutput(); + }; + + typedef boost::ptr_map<std::string,ConsumerImpl> ConsumerImplMap; + typedef std::map<std::string, DtxBuffer::shared_ptr> DtxBufferMap; + + SessionContext& session; + DeliveryAdapter& deliveryAdapter; + Queue::shared_ptr defaultQueue; + ConsumerImplMap consumers; + uint32_t prefetchSize; + uint16_t prefetchCount; + Prefetch outstanding; + NameGenerator tagGenerator; + std::list<DeliveryRecord> unacked; + TxBuffer::shared_ptr txBuffer; + DtxBuffer::shared_ptr dtxBuffer; + bool dtxSelected; + DtxBufferMap suspendedXids; + framing::AccumulatedAck accumulatedAck; + bool flowActive; + boost::shared_ptr<Exchange> cacheExchange; + sys::AggregateOutput outputTasks; + + void route(boost::intrusive_ptr<Message> msg, Deliverable& strategy); + void record(const DeliveryRecord& delivery); + bool checkPrefetch(boost::intrusive_ptr<Message>& msg); + void checkDtxTimeout(); + ConsumerImpl& find(const std::string& destination); + void ack(DeliveryId deliveryTag, DeliveryId endTag, bool cumulative); + void complete(DeliveryRecord&); + AckRange findRange(DeliveryId first, DeliveryId last); + void requestDispatch(); + void requestDispatch(ConsumerImpl&); + void cancel(ConsumerImpl&); + + public: + SemanticState(DeliveryAdapter&, SessionContext&); + ~SemanticState(); + + SessionContext& getSession() { return session; } + + /** + * Get named queue, never returns 0. + * @return: named queue + * @exception: ChannelException if no queue of that name is found. + * @exception: ConnectionException if name="" and session has no default. + */ + Queue::shared_ptr getQueue(const std::string& name) const; + + uint32_t setPrefetchSize(uint32_t size){ return prefetchSize = size; } + uint16_t setPrefetchCount(uint16_t n){ return prefetchCount = n; } + + bool exists(const string& consumerTag); + + /** + *@param tagInOut - if empty it is updated with the generated token. + */ + void consume(DeliveryToken::shared_ptr token, string& tagInOut, Queue::shared_ptr queue, + bool nolocal, bool ackRequired, bool acquire, bool exclusive, const framing::FieldTable* = 0); + + void cancel(const string& tag); + + void setWindowMode(const std::string& destination); + void setCreditMode(const std::string& destination); + void addByteCredit(const std::string& destination, uint32_t value); + void addMessageCredit(const std::string& destination, uint32_t value); + void flush(const std::string& destination); + void stop(const std::string& destination); + + bool get(DeliveryToken::shared_ptr token, Queue::shared_ptr queue, bool ackExpected); + void startTx(); + void commit(MessageStore* const store, bool completeOnCommit); + void rollback(); + void selectDtx(); + void startDtx(const std::string& xid, DtxManager& mgr, bool join); + void endDtx(const std::string& xid, bool fail); + void suspendDtx(const std::string& xid); + void resumeDtx(const std::string& xid); + void recover(bool requeue); + void flow(bool active); + DeliveryId redeliver(QueuedMessage& msg, DeliveryToken::shared_ptr token); + void acquire(DeliveryId first, DeliveryId last, DeliveryIds& acquired); + void release(DeliveryId first, DeliveryId last, bool setRedelivered); + void reject(DeliveryId first, DeliveryId last); + void handle(boost::intrusive_ptr<Message> msg); + bool doOutput() { return outputTasks.doOutput(); } + + //preview only (completed == ack): + void ackCumulative(DeliveryId deliveryTag); + void ackRange(DeliveryId deliveryTag, DeliveryId endTag); + + //final 0-10 spec (completed and accepted are distinct): + void completed(DeliveryId deliveryTag, DeliveryId endTag); + void accepted(DeliveryId deliveryTag, DeliveryId endTag); +}; + +}} // namespace qpid::broker + + + + +#endif /*!QPID_BROKER_SEMANTICSTATE_H*/ diff --git a/qpid/cpp/src/qpid/broker/SessionAdapter.cpp b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp new file mode 100644 index 0000000000..8093c7c174 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/SessionAdapter.cpp @@ -0,0 +1,590 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ +#include "SessionAdapter.h" +#include "Connection.h" +#include "DeliveryToken.h" +#include "MessageDelivery.h" +#include "Queue.h" +#include "qpid/Exception.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/constants.h" +#include "qpid/log/Statement.h" +#include "qpid/amqp_0_10/exceptions.h" +#include <boost/format.hpp> +#include <boost/cast.hpp> +#include <boost/bind.hpp> + +namespace qpid { +namespace broker { + +using namespace qpid; +using namespace qpid::framing; + +typedef std::vector<Queue::shared_ptr> QueueVector; + +SessionAdapter::SessionAdapter(SemanticState& s) : + HandlerImpl(s), + exchangeImpl(s), + queueImpl(s), + messageImpl(s), + executionImpl(s), + txImpl(s), + dtxImpl(s) +{} + + +void SessionAdapter::ExchangeHandlerImpl::declare(const string& exchange, const string& type, + const string& alternateExchange, + bool passive, bool durable, bool /*autoDelete*/, const FieldTable& args){ + + //TODO: implement autoDelete + Exchange::shared_ptr alternate; + if (!alternateExchange.empty()) { + alternate = getBroker().getExchanges().get(alternateExchange); + } + if(passive){ + Exchange::shared_ptr actual(getBroker().getExchanges().get(exchange)); + checkType(actual, type); + checkAlternate(actual, alternate); + }else{ + try{ + std::pair<Exchange::shared_ptr, bool> response = getBroker().getExchanges().declare(exchange, type, durable, args); + if (response.second) { + if (durable) { + getBroker().getStore().create(*response.first, args); + } + if (alternate) { + response.first->setAlternate(alternate); + alternate->incAlternateUsers(); + } + } else { + checkType(response.first, type); + checkAlternate(response.first, alternate); + } + }catch(UnknownExchangeTypeException& e){ + throw CommandInvalidException(QPID_MSG("Exchange type not implemented: " << type)); + } + } +} + +void SessionAdapter::ExchangeHandlerImpl::checkType(Exchange::shared_ptr exchange, const std::string& type) +{ + if (!type.empty() && exchange->getType() != type) { + throw NotAllowedException(QPID_MSG("Exchange declared to be of type " << exchange->getType() << ", requested " << type)); + } +} + +void SessionAdapter::ExchangeHandlerImpl::checkAlternate(Exchange::shared_ptr exchange, Exchange::shared_ptr alternate) +{ + if (alternate && alternate != exchange->getAlternate()) + throw NotAllowedException( + QPID_MSG("Exchange declared with alternate-exchange " + << exchange->getAlternate()->getName() << ", requested " + << alternate->getName())); +} + +void SessionAdapter::ExchangeHandlerImpl::delete_(const string& name, bool /*ifUnused*/){ + //TODO: implement unused + Exchange::shared_ptr exchange(getBroker().getExchanges().get(name)); + if (exchange->inUseAsAlternate()) throw NotAllowedException(QPID_MSG("Exchange in use as alternate-exchange.")); + if (exchange->isDurable()) getBroker().getStore().destroy(*exchange); + if (exchange->getAlternate()) exchange->getAlternate()->decAlternateUsers(); + getBroker().getExchanges().destroy(name); +} + +Exchange010QueryResult SessionAdapter::ExchangeHandlerImpl::query(const string& name) +{ + try { + Exchange::shared_ptr exchange(getBroker().getExchanges().get(name)); + return Exchange010QueryResult(exchange->getType(), exchange->isDurable(), false, exchange->getArgs()); + } catch (const ChannelException& e) { + return Exchange010QueryResult("", false, true, FieldTable()); + } +} +void SessionAdapter::ExchangeHandlerImpl::bind(const string& queueName, + const string& exchangeName, const string& routingKey, + const FieldTable& arguments){ + + Queue::shared_ptr queue = getQueue(queueName); + Exchange::shared_ptr exchange = getBroker().getExchanges().get(exchangeName); + if(exchange){ + string exchangeRoutingKey = routingKey.empty() && queueName.empty() ? queue->getName() : routingKey; + if (exchange->bind(queue, exchangeRoutingKey, &arguments)) { + queue->bound(exchangeName, routingKey, arguments); + if (exchange->isDurable() && queue->isDurable()) { + getBroker().getStore().bind(*exchange, *queue, routingKey, arguments); + } + } + }else{ + throw NotFoundException( + "Bind failed. No such exchange: " + exchangeName); + } +} + +void +SessionAdapter::ExchangeHandlerImpl::unbind(const string& queueName, + const string& exchangeName, + const string& routingKey) +{ + Queue::shared_ptr queue = getQueue(queueName); + if (!queue.get()) throw NotFoundException("Unbind failed. No such exchange: " + exchangeName); + + Exchange::shared_ptr exchange = getBroker().getExchanges().get(exchangeName); + if (!exchange.get()) throw NotFoundException("Unbind failed. No such exchange: " + exchangeName); + + //TODO: revise unbind to rely solely on binding key (not args) + if (exchange->unbind(queue, routingKey, 0) && exchange->isDurable() && queue->isDurable()) { + getBroker().getStore().unbind(*exchange, *queue, routingKey, FieldTable()); + } + +} + +Exchange010BoundResult SessionAdapter::ExchangeHandlerImpl::bound(const std::string& exchangeName, + const std::string& queueName, + const std::string& key, + const framing::FieldTable& args) +{ + Exchange::shared_ptr exchange; + try { + exchange = getBroker().getExchanges().get(exchangeName); + } catch (const ChannelException&) {} + + Queue::shared_ptr queue; + if (!queueName.empty()) { + queue = getBroker().getQueues().find(queueName); + } + + if (!exchange) { + return Exchange010BoundResult(true, false, false, false, false); + } else if (!queueName.empty() && !queue) { + return Exchange010BoundResult(false, true, false, false, false); + } else if (exchange->isBound(queue, key.empty() ? 0 : &key, args.count() > 0 ? &args : &args)) { + return Exchange010BoundResult(false, false, false, false, false); + } else { + //need to test each specified option individually + bool queueMatched = queueName.empty() || exchange->isBound(queue, 0, 0); + bool keyMatched = key.empty() || exchange->isBound(Queue::shared_ptr(), &key, 0); + bool argsMatched = args.count() == 0 || exchange->isBound(Queue::shared_ptr(), 0, &args); + + return Exchange010BoundResult(false, false, !queueMatched, !keyMatched, !argsMatched); + } +} + +SessionAdapter::QueueHandlerImpl::QueueHandlerImpl(SemanticState& session) : HandlerHelper(session), broker(getBroker()) +{} + + +SessionAdapter::QueueHandlerImpl::~QueueHandlerImpl() +{ + while (!exclusiveQueues.empty()) { + Queue::shared_ptr q(exclusiveQueues.front()); + q->releaseExclusiveOwnership(); + if (q->canAutoDelete()) { + Queue::tryAutoDelete(broker, q); + } + exclusiveQueues.erase(exclusiveQueues.begin()); + } +} + +bool SessionAdapter::QueueHandlerImpl::isLocal(const ConnectionToken* t) const +{ + return session.isLocal(t); +} + + +Queue010QueryResult SessionAdapter::QueueHandlerImpl::query(const string& name) +{ + Queue::shared_ptr queue = getQueue(name); + Exchange::shared_ptr alternateExchange = queue->getAlternateExchange(); + + return Queue010QueryResult(queue->getName(), + alternateExchange ? alternateExchange->getName() : "", + queue->isDurable(), + queue->hasExclusiveOwner(), + queue->isAutoDelete(), + queue->getSettings(), + queue->getMessageCount(), + queue->getConsumerCount()); +} + +void SessionAdapter::QueueHandlerImpl::declare(const string& name, const string& alternateExchange, + bool passive, bool durable, bool exclusive, + bool autoDelete, const qpid::framing::FieldTable& arguments){ + + Exchange::shared_ptr alternate; + if (!alternateExchange.empty()) { + alternate = getBroker().getExchanges().get(alternateExchange); + } + Queue::shared_ptr queue; + if (passive && !name.empty()) { + queue = getQueue(name); + //TODO: check alternate-exchange is as expected + } else { + std::pair<Queue::shared_ptr, bool> queue_created = + getBroker().getQueues().declare( + name, durable, + autoDelete, + exclusive ? this : 0); + queue = queue_created.first; + assert(queue); + if (queue_created.second) { // This is a new queue + if (alternate) { + queue->setAlternateExchange(alternate); + alternate->incAlternateUsers(); + } + + //apply settings & create persistent record if required + queue_created.first->create(arguments); + + //add default binding: + getBroker().getExchanges().getDefault()->bind(queue, name, 0); + queue->bound(getBroker().getExchanges().getDefault()->getName(), name, arguments); + + //handle automatic cleanup: + if (exclusive) { + exclusiveQueues.push_back(queue); + } + } else { + if (exclusive && queue->setExclusiveOwner(this)) { + exclusiveQueues.push_back(queue); + } + } + } + if (exclusive && !queue->isExclusiveOwner(this)) + throw ResourceLockedException( + QPID_MSG("Cannot grant exclusive access to queue " + << queue->getName())); +} + + +void SessionAdapter::QueueHandlerImpl::purge(const string& queue){ + getQueue(queue)->purge(); +} + +void SessionAdapter::QueueHandlerImpl::delete_(const string& queue, bool ifUnused, bool ifEmpty){ + ChannelException error(0, ""); + Queue::shared_ptr q = getQueue(queue); + if(ifEmpty && q->getMessageCount() > 0){ + throw PreconditionFailedException("Queue not empty."); + }else if(ifUnused && q->getConsumerCount() > 0){ + throw PreconditionFailedException("Queue in use."); + }else{ + //remove the queue from the list of exclusive queues if necessary + if(q->isExclusiveOwner(&getConnection())){ + QueueVector::iterator i = find(getConnection().exclusiveQueues.begin(), getConnection().exclusiveQueues.end(), q); + if(i < getConnection().exclusiveQueues.end()) getConnection().exclusiveQueues.erase(i); + } + q->destroy(); + getBroker().getQueues().destroy(queue); + q->unbind(getBroker().getExchanges(), q); + } +} + + +SessionAdapter::MessageHandlerImpl::MessageHandlerImpl(SemanticState& s) : + HandlerHelper(s), + releaseRedeliveredOp(boost::bind(&SemanticState::release, &state, _1, _2, true)), + releaseOp(boost::bind(&SemanticState::release, &state, _1, _2, false)), + rejectOp(boost::bind(&SemanticState::reject, &state, _1, _2)), + acceptOp(boost::bind(&SemanticState::accepted, &state, _1, _2)) + {} + +// +// Message class method handlers +// + +void SessionAdapter::MessageHandlerImpl::transfer(const string& /*destination*/, + uint8_t /*acceptMode*/, + uint8_t /*acquireMode*/) +{ + //not yet used (content containing assemblies treated differently at present +} + +void SessionAdapter::MessageHandlerImpl::release(const SequenceSet& transfers, bool setRedelivered) +{ + transfers.for_each(setRedelivered ? releaseRedeliveredOp : releaseOp); +} + +void +SessionAdapter::MessageHandlerImpl::subscribe(const string& queueName, + const string& destination, + uint8_t acceptMode, + uint8_t acquireMode, + bool exclusive, + const string& /*resumeId*/,//TODO implement resume behaviour + uint64_t /*resumeTtl*/, + const FieldTable& arguments) +{ + Queue::shared_ptr queue = getQueue(queueName); + if(!destination.empty() && state.exists(destination)) + throw NotAllowedException(QPID_MSG("Consumer tags must be unique")); + + string tag = destination; + state.consume(MessageDelivery::getMessageDeliveryToken(destination, acceptMode, acquireMode), + tag, queue, false, //TODO get rid of no-local + acceptMode == 0, acquireMode == 0, exclusive, &arguments); +} + +void +SessionAdapter::MessageHandlerImpl::cancel(const string& destination ) +{ + state.cancel(destination); +} + +void +SessionAdapter::MessageHandlerImpl::reject(const SequenceSet& transfers, uint16_t /*code*/, const string& /*text*/ ) +{ + transfers.for_each(rejectOp); +} + +void SessionAdapter::MessageHandlerImpl::flow(const std::string& destination, u_int8_t unit, u_int32_t value) +{ + if (unit == 0) { + //message + state.addMessageCredit(destination, value); + } else if (unit == 1) { + //bytes + state.addByteCredit(destination, value); + } else { + //unknown + throw SyntaxErrorException(QPID_MSG("Invalid value for unit " << unit)); + } + +} + +void SessionAdapter::MessageHandlerImpl::setFlowMode(const std::string& destination, u_int8_t mode) +{ + if (mode == 0) { + //credit + state.setCreditMode(destination); + } else if (mode == 1) { + //window + state.setWindowMode(destination); + } else{ + throw SyntaxErrorException(QPID_MSG("Invalid value for mode " << mode)); + } +} + +void SessionAdapter::MessageHandlerImpl::flush(const std::string& destination) +{ + state.flush(destination); +} + +void SessionAdapter::MessageHandlerImpl::stop(const std::string& destination) +{ + state.stop(destination); +} + +void SessionAdapter::MessageHandlerImpl::accept(const framing::SequenceSet& commands) +{ + + commands.for_each(acceptOp); +} + +framing::Message010AcquireResult SessionAdapter::MessageHandlerImpl::acquire(const framing::SequenceSet& transfers) +{ + //TODO: change this when SequenceNumberSet is deleted along with preview code + SequenceNumberSet results; + RangedOperation f = boost::bind(&SemanticState::acquire, &state, _1, _2, boost::ref(results)); + transfers.for_each(f); + + results = results.condense(); + SequenceSet acquisitions; + RangedOperation g = boost::bind(&SequenceSet::add, &acquisitions, _1, _2); + results.processRanges(g); + + return Message010AcquireResult(acquisitions); +} + + +void SessionAdapter::ExecutionHandlerImpl::sync() +{ + //TODO +} + +void SessionAdapter::ExecutionHandlerImpl::result(uint32_t /*commandId*/, const string& /*value*/) +{ + //TODO +} + +void SessionAdapter::ExecutionHandlerImpl::exception(uint16_t /*errorCode*/, + uint32_t /*commandId*/, + uint8_t /*classCode*/, + uint8_t /*commandCode*/, + uint8_t /*fieldIndex*/, + const std::string& /*description*/, + const framing::FieldTable& /*errorInfo*/) +{ + //TODO +} + + + +void SessionAdapter::TxHandlerImpl::select() +{ + state.startTx(); +} + +void SessionAdapter::TxHandlerImpl::commit() +{ + state.commit(&getBroker().getStore(), false); +} + +void SessionAdapter::TxHandlerImpl::rollback() +{ + state.rollback(); +} + +std::string SessionAdapter::DtxHandlerImpl::convert(const framing::Xid010& xid) +{ + std::stringstream out; + out << xid.getFormat() << xid.getGlobalId() << xid.getBranchId(); + return out.str(); +} + + +void SessionAdapter::DtxHandlerImpl::select() +{ + state.selectDtx(); +} + +Dtx010EndResult SessionAdapter::DtxHandlerImpl::end(const Xid010& xid, + bool fail, + bool suspend) +{ + try { + if (fail) { + state.endDtx(convert(xid), true); + if (suspend) { + throw CommandInvalidException(QPID_MSG("End and suspend cannot both be set.")); + } else { + return Dtx010EndResult(XA_RBROLLBACK); + } + } else { + if (suspend) { + state.suspendDtx(convert(xid)); + } else { + state.endDtx(convert(xid), false); + } + return Dtx010EndResult(XA_OK); + } + } catch (const DtxTimeoutException& e) { + return Dtx010EndResult(XA_RBTIMEOUT); + } +} + +Dtx010StartResult SessionAdapter::DtxHandlerImpl::start(const Xid010& xid, + bool join, + bool resume) +{ + if (join && resume) { + throw CommandInvalidException(QPID_MSG("Join and resume cannot both be set.")); + } + try { + if (resume) { + state.resumeDtx(convert(xid)); + } else { + state.startDtx(convert(xid), getBroker().getDtxManager(), join); + } + return Dtx010StartResult(XA_OK); + } catch (const DtxTimeoutException& e) { + return Dtx010StartResult(XA_RBTIMEOUT); + } +} + +Dtx010PrepareResult SessionAdapter::DtxHandlerImpl::prepare(const Xid010& xid) +{ + try { + bool ok = getBroker().getDtxManager().prepare(convert(xid)); + return Dtx010PrepareResult(ok ? XA_OK : XA_RBROLLBACK); + } catch (const DtxTimeoutException& e) { + return Dtx010PrepareResult(XA_RBTIMEOUT); + } +} + +Dtx010CommitResult SessionAdapter::DtxHandlerImpl::commit(const Xid010& xid, + bool onePhase) +{ + try { + bool ok = getBroker().getDtxManager().commit(convert(xid), onePhase); + return Dtx010CommitResult(ok ? XA_OK : XA_RBROLLBACK); + } catch (const DtxTimeoutException& e) { + return Dtx010CommitResult(XA_RBTIMEOUT); + } +} + + +Dtx010RollbackResult SessionAdapter::DtxHandlerImpl::rollback(const Xid010& xid) +{ + try { + getBroker().getDtxManager().rollback(convert(xid)); + return Dtx010RollbackResult(XA_OK); + } catch (const DtxTimeoutException& e) { + return Dtx010RollbackResult(XA_RBTIMEOUT); + } +} + +Dtx010RecoverResult SessionAdapter::DtxHandlerImpl::recover() +{ + std::set<std::string> xids; + getBroker().getStore().collectPreparedXids(xids); + + //TODO: remove the need to copy from one container type to another + std::vector<std::string> data; + for (std::set<std::string>::iterator i = xids.begin(); i != xids.end(); i++) { + data.push_back(*i); + } + Array indoubt(data); + return Dtx010RecoverResult(indoubt); +} + +void SessionAdapter::DtxHandlerImpl::forget(const Xid010& xid) +{ + //Currently no heuristic completion is supported, so this should never be used. + throw CommandInvalidException(QPID_MSG("Forget is invalid. Branch with xid " << xid << " not heuristically completed!")); +} + +Dtx010GetTimeoutResult SessionAdapter::DtxHandlerImpl::getTimeout(const Xid010& xid) +{ + uint32_t timeout = getBroker().getDtxManager().getTimeout(convert(xid)); + return Dtx010GetTimeoutResult(timeout); +} + + +void SessionAdapter::DtxHandlerImpl::setTimeout(const Xid010& xid, + u_int32_t timeout) +{ + getBroker().getDtxManager().setTimeout(convert(xid), timeout); +} + + +Queue::shared_ptr SessionAdapter::HandlerHelper::getQueue(const string& name) const { + Queue::shared_ptr queue; + if (name.empty()) { + throw amqp_0_10::IllegalArgumentException(QPID_MSG("No queue name specified.")); + } else { + queue = session.getBroker().getQueues().find(name); + if (!queue) + throw amqp_0_10::NotFoundException(QPID_MSG("Queue not found: "<<name)); + } + return queue; +} + +}} // namespace qpid::broker + + diff --git a/qpid/cpp/src/qpid/broker/SessionAdapter.h b/qpid/cpp/src/qpid/broker/SessionAdapter.h new file mode 100644 index 0000000000..a77f1b5d77 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/SessionAdapter.h @@ -0,0 +1,269 @@ +#ifndef _broker_SessionAdapter_h +#define _broker_SessionAdapter_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "HandlerImpl.h" + +#include "ConnectionToken.h" +#include "OwnershipToken.h" +#include "qpid/Exception.h" +#include "qpid/framing/AMQP_ServerOperations.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/SequenceSet.h" + +#include <vector> +#include <boost/function.hpp> +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace broker { + +class Channel; +class Connection; +class Broker; +class Queue; + +/** + * Per-channel protocol adapter. + * + * A container for a collection of AMQP-class adapters that translate + * AMQP method bodies into calls on the core Broker objects. Each + * adapter class also provides a client proxy to send methods to the + * peer. + * + */ + class SessionAdapter : public HandlerImpl, public framing::AMQP_ServerOperations +{ + public: + SessionAdapter(SemanticState& session); + + + framing::ProtocolVersion getVersion() const { return session.getConnection().getVersion();} + + Message010Handler* getMessage010Handler(){ return &messageImpl; } + Exchange010Handler* getExchange010Handler(){ return &exchangeImpl; } + Queue010Handler* getQueue010Handler(){ return &queueImpl; } + Execution010Handler* getExecution010Handler(){ return &executionImpl; } + Tx010Handler* getTx010Handler(){ return &txImpl; } + Dtx010Handler* getDtx010Handler(){ return &dtxImpl; } + + BasicHandler* getBasicHandler() { throw framing::NotImplementedException("Class not implemented"); } + ExchangeHandler* getExchangeHandler(){ throw framing::NotImplementedException("Class not implemented"); } + BindingHandler* getBindingHandler(){ throw framing::NotImplementedException("Class not implemented"); } + QueueHandler* getQueueHandler(){ throw framing::NotImplementedException("Class not implemented"); } + TxHandler* getTxHandler(){ throw framing::NotImplementedException("Class not implemented"); } + MessageHandler* getMessageHandler(){ throw framing::NotImplementedException("Class not implemented"); } + DtxCoordinationHandler* getDtxCoordinationHandler(){ throw framing::NotImplementedException("Class not implemented"); } + DtxDemarcationHandler* getDtxDemarcationHandler(){ throw framing::NotImplementedException("Class not implemented"); } + AccessHandler* getAccessHandler() { throw framing::NotImplementedException("Class not implemented"); } + FileHandler* getFileHandler() { throw framing::NotImplementedException("Class not implemented"); } + StreamHandler* getStreamHandler() { throw framing::NotImplementedException("Class not implemented"); } + TunnelHandler* getTunnelHandler() { throw framing::NotImplementedException("Class not implemented"); } + ExecutionHandler* getExecutionHandler() { throw framing::NotImplementedException("Class not implemented"); } + ConnectionHandler* getConnectionHandler() { throw framing::NotImplementedException("Class not implemented"); } + SessionHandler* getSessionHandler() { throw framing::NotImplementedException("Class not implemented"); } + Connection010Handler* getConnection010Handler() { throw framing::NotImplementedException("Class not implemented"); } + Session010Handler* getSession010Handler() { throw framing::NotImplementedException("Class not implemented"); } + + private: + //common base for utility methods etc that are specific to this adapter + struct HandlerHelper : public HandlerImpl + { + HandlerHelper(SemanticState& s) : HandlerImpl(s) {} + + Queue::shared_ptr getQueue(const string& name) const; + }; + + + class ExchangeHandlerImpl : + public Exchange010Handler, + public HandlerHelper + { + public: + ExchangeHandlerImpl(SemanticState& session) : HandlerHelper(session) {} + + void declare(const std::string& exchange, const std::string& type, + const std::string& alternateExchange, + bool passive, bool durable, bool autoDelete, + const qpid::framing::FieldTable& arguments); + void delete_(const std::string& exchange, bool ifUnused); + framing::Exchange010QueryResult query(const std::string& name); + void bind(const std::string& queue, + const std::string& exchange, const std::string& routingKey, + const qpid::framing::FieldTable& arguments); + void unbind(const std::string& queue, + const std::string& exchange, + const std::string& routingKey); + framing::Exchange010BoundResult bound(const std::string& exchange, + const std::string& queue, + const std::string& routingKey, + const framing::FieldTable& arguments); + private: + void checkType(shared_ptr<Exchange> exchange, const std::string& type); + + void checkAlternate(shared_ptr<Exchange> exchange, + shared_ptr<Exchange> alternate); + }; + + class QueueHandlerImpl : public Queue010Handler, + public HandlerHelper, public OwnershipToken + { + Broker& broker; + std::vector< boost::shared_ptr<Queue> > exclusiveQueues; + + public: + QueueHandlerImpl(SemanticState& session); + ~QueueHandlerImpl(); + + void declare(const std::string& queue, + const std::string& alternateExchange, + bool passive, bool durable, bool exclusive, + bool autoDelete, + const qpid::framing::FieldTable& arguments); + void delete_(const std::string& queue, + bool ifUnused, bool ifEmpty); + void purge(const std::string& queue); + framing::Queue010QueryResult query(const std::string& queue); + bool isLocal(const ConnectionToken* t) const; + }; + + class MessageHandlerImpl : + public Message010Handler, + public HandlerHelper + { + typedef boost::function<void(DeliveryId, DeliveryId)> RangedOperation; + RangedOperation releaseRedeliveredOp; + RangedOperation releaseOp; + RangedOperation rejectOp; + RangedOperation acceptOp; + + public: + MessageHandlerImpl(SemanticState& session); + void transfer(const string& destination, + uint8_t acceptMode, + uint8_t acquireMode); + + void accept(const framing::SequenceSet& commands); + + void reject(const framing::SequenceSet& commands, + uint16_t code, + const string& text); + + void release(const framing::SequenceSet& commands, + bool setRedelivered); + + framing::Message010AcquireResult acquire(const framing::SequenceSet&); + + void subscribe(const string& queue, + const string& destination, + uint8_t acceptMode, + uint8_t acquireMode, + bool exclusive, + const string& resumeId, + uint64_t resumeTtl, + const framing::FieldTable& arguments); + + void cancel(const string& destination); + + void setFlowMode(const string& destination, + uint8_t flowMode); + + void flow(const string& destination, + uint8_t unit, + uint32_t value); + + void flush(const string& destination); + + void stop(const string& destination); + + }; + + class ExecutionHandlerImpl : public Execution010Handler, public HandlerHelper + { + public: + ExecutionHandlerImpl(SemanticState& session) : HandlerHelper(session) {} + + void sync(); + void result(uint32_t commandId, const string& value); + void exception(uint16_t errorCode, + uint32_t commandId, + uint8_t classCode, + uint8_t commandCode, + uint8_t fieldIndex, + const std::string& description, + const framing::FieldTable& errorInfo); + + }; + + class TxHandlerImpl : public Tx010Handler, public HandlerHelper + { + public: + TxHandlerImpl(SemanticState& session) : HandlerHelper(session) {} + + void select(); + void commit(); + void rollback(); + }; + + class DtxHandlerImpl : public Dtx010Handler, public HandlerHelper + { + std::string convert(const framing::Xid010& xid); + + public: + DtxHandlerImpl(SemanticState& session) : HandlerHelper(session) {} + + void select(); + + framing::Dtx010StartResult start(const framing::Xid010& xid, + bool join, + bool resume); + + framing::Dtx010EndResult end(const framing::Xid010& xid, + bool fail, + bool suspend); + + framing::Dtx010CommitResult commit(const framing::Xid010& xid, + bool onePhase); + + void forget(const framing::Xid010& xid); + + framing::Dtx010GetTimeoutResult getTimeout(const framing::Xid010& xid); + + framing::Dtx010PrepareResult prepare(const framing::Xid010& xid); + + framing::Dtx010RecoverResult recover(); + + framing::Dtx010RollbackResult rollback(const framing::Xid010& xid); + + void setTimeout(const framing::Xid010& xid, uint32_t timeout); + }; + + ExchangeHandlerImpl exchangeImpl; + QueueHandlerImpl queueImpl; + MessageHandlerImpl messageImpl; + ExecutionHandlerImpl executionImpl; + TxHandlerImpl txImpl; + DtxHandlerImpl dtxImpl; +}; +}} // namespace qpid::broker + + + +#endif /*!_broker_SessionAdapter_h*/ diff --git a/qpid/cpp/src/qpid/broker/SessionContext.h b/qpid/cpp/src/qpid/broker/SessionContext.h new file mode 100644 index 0000000000..e3cc0a5fa3 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/SessionContext.h @@ -0,0 +1,51 @@ +#ifndef QPID_BROKER_SESSIONCONTEXT_H +#define QPID_BROKER_SESSIONCONTEXT_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. + * + */ + +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/AMQP_ClientProxy.h" +#include "qpid/framing/amqp_types.h" +#include "qpid/sys/OutputControl.h" +#include "ConnectionState.h" + + +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace broker { + +class SessionContext : public sys::OutputControl +{ + public: + virtual ~SessionContext(){} + virtual bool isLocal(const ConnectionToken* t) const = 0; + virtual ConnectionState& getConnection() = 0; + virtual framing::AMQP_ClientProxy& getProxy() = 0; + virtual Broker& getBroker() = 0; +}; + +}} // namespace qpid::broker + + + +#endif /*!QPID_BROKER_SESSIONCONTEXT_H*/ diff --git a/qpid/cpp/src/qpid/broker/SessionHandler.cpp b/qpid/cpp/src/qpid/broker/SessionHandler.cpp new file mode 100644 index 0000000000..3baa3a89a7 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/SessionHandler.cpp @@ -0,0 +1,228 @@ +/* + * 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. + * + */ + +#include "SessionHandler.h" +#include "SessionState.h" +#include "Connection.h" +#include "ConnectionState.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/constants.h" +#include "qpid/framing/ClientInvoker.h" +#include "qpid/framing/ServerInvoker.h" +#include "qpid/log/Statement.h" + +#include <boost/bind.hpp> + +namespace qpid { +namespace broker { +using namespace framing; +using namespace std; +using namespace qpid::sys; + +SessionHandler::SessionHandler(Connection& c, ChannelId ch) + : InOutHandler(0, &out), + connection(c), channel(ch, &c.getOutput()), + proxy(out), // Via my own handleOut() for L2 data. + peerSession(channel), // Direct to channel for L2 commands. + ignoring(false) {} + +SessionHandler::~SessionHandler() {} + +namespace { +ClassId classId(AMQMethodBody* m) { return m ? m->amqpMethodId() : 0; } +MethodId methodId(AMQMethodBody* m) { return m ? m->amqpClassId() : 0; } +} // namespace + +void SessionHandler::handleIn(AMQFrame& f) { + // Note on channel states: a channel is open if session != 0. A + // channel that is closed (session == 0) can be in the "ignoring" + // state. This is a temporary state after we have sent a channel + // exception, where extra frames might arrive that should be + // ignored. + // + AMQMethodBody* m = f.getBody()->getMethod(); + try { + if (!ignoring) { + if (m && invoke(static_cast<AMQP_ServerOperations::Session010Handler&>(*this), *m)) { + return; + } else if (session.get()) { + session->handle(f); + } else { + throw ChannelErrorException( + QPID_MSG("Channel " << channel.get() << " is not open")); + } + } + }catch(const ConnectionException& e){ + connection.close(e.code, e.what(), classId(m), methodId(m)); + }catch(const std::exception& e){ + connection.close( + framing::INTERNAL_ERROR, e.what(), classId(m), methodId(m)); + } +} + +void SessionHandler::destroy() { + ignoring=true; // Ignore trailing frames sent by client. + session->detach(); + session.reset(); +} + +void SessionHandler::handleOut(AMQFrame& f) { + channel.handle(f); // Send it. + if (session->sent(f)) + peerSession.flush(false, false, true); +} + +void SessionHandler::assertAttached(const char* method) const { + if (!session.get()) { + std::cout << "SessionHandler::assertAttached() failed for " << method << std::endl; + throw ChannelErrorException( + QPID_MSG(method << " failed: No session for channel " + << getChannel())); + } +} + +void SessionHandler::assertClosed(const char* method) const { + if (session.get()) + throw ChannelBusyException( + QPID_MSG(method << " failed: channel " << channel.get() + << " is already open.")); +} + +void SessionHandler::localSuspend() { + if (session.get() && session->isAttached()) { + session->detach(); + connection.broker.getSessionManager().suspend(session); + session.reset(); + } +} + + +ConnectionState& SessionHandler::getConnection() { return connection; } +const ConnectionState& SessionHandler::getConnection() const { return connection; } + +//new methods: +void SessionHandler::attach(const std::string& name, bool /*force*/) +{ + //TODO: need to revise session manager to support resume as well + assertClosed("attach"); + std::auto_ptr<SessionState> state( + connection.broker.getSessionManager().open(*this, 0)); + session.reset(state.release()); + peerSession.attached(name); + peerSession.commandPoint(session->nextOut, 0); +} + +void SessionHandler::attached(const std::string& /*name*/) +{ + std::auto_ptr<SessionState> state(connection.broker.getSessionManager().open(*this, 0)); + session.reset(state.release()); +} + +void SessionHandler::detach(const std::string& name) +{ + assertAttached("detach"); + localSuspend(); + peerSession.detached(name, 0); + assert(&connection.getChannel(channel.get()) == this); + connection.closeChannel(channel.get()); +} + +void SessionHandler::detached(const std::string& name, uint8_t code) +{ + ignoring=false; + session->detach(); + session.reset(); + if (code) { + //no error + } else { + //error occured + QPID_LOG(warning, "Received session.closed: "<< name << " " << code); + } +} + +void SessionHandler::requestTimeout(uint32_t t) +{ + session->setTimeout(t); + //proxy.timeout(t); +} + +void SessionHandler::timeout(uint32_t) +{ + //not sure what we need to do on the server for this... +} + +void SessionHandler::commandPoint(const framing::SequenceNumber& id, uint64_t offset) +{ + if (offset) throw NotImplementedException("Non-zero byte offset not yet supported for command-point"); + + session->nextIn = id; +} + +void SessionHandler::expected(const framing::SequenceSet& commands, const framing::Array& fragments) +{ + if (!commands.empty() || fragments.size()) { + throw NotImplementedException("Session resumption not yet supported"); + } +} + +void SessionHandler::confirmed(const framing::SequenceSet& /*commands*/, const framing::Array& /*fragments*/) +{ + //don't really care too much about this yet +} + +void SessionHandler::completed(const framing::SequenceSet& commands, bool timelyReply) +{ + session->complete(commands); + if (timelyReply) { + peerSession.knownCompleted(session->knownCompleted); + session->knownCompleted.clear(); + } +} + +void SessionHandler::knownCompleted(const framing::SequenceSet& commands) +{ + session->completed.remove(commands); +} + +void SessionHandler::flush(bool expected, bool confirmed, bool completed) +{ + if (expected) { + peerSession.expected(SequenceSet(session->nextIn), Array()); + } + if (confirmed) { + peerSession.confirmed(session->completed, Array()); + } + if (completed) { + peerSession.completed(session->completed, true); + } +} + + +void SessionHandler::sendCompletion() +{ + peerSession.completed(session->completed, true); +} + +void SessionHandler::gap(const framing::SequenceSet& /*commands*/) +{ + throw NotImplementedException("gap not yet supported"); +} + +}} // namespace qpid::broker diff --git a/qpid/cpp/src/qpid/broker/SessionHandler.h b/qpid/cpp/src/qpid/broker/SessionHandler.h new file mode 100644 index 0000000000..fa013a1c15 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/SessionHandler.h @@ -0,0 +1,116 @@ +#ifndef QPID_BROKER_SESSIONHANDLER_H +#define QPID_BROKER_SESSIONHANDLER_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. + * + */ + +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/AMQP_ClientOperations.h" +#include "qpid/framing/AMQP_ServerOperations.h" +#include "qpid/framing/AMQP_ClientProxy.h" +#include "qpid/framing/amqp_types.h" +#include "qpid/framing/Array.h" +#include "qpid/framing/ChannelHandler.h" +#include "qpid/framing/SequenceNumber.h" +#include "qpid/framing/SequenceSet.h" + +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace broker { + +class Connection; +class ConnectionState; +class SessionState; + +/** + * A SessionHandler is associated with each active channel. It + * receives incoming frames, handles session controls and manages the + * association between the channel and a session. + */ +class SessionHandler : public framing::AMQP_ServerOperations::Session010Handler, + public framing::FrameHandler::InOutHandler, + private boost::noncopyable +{ + public: + SessionHandler(Connection&, framing::ChannelId); + ~SessionHandler(); + + /** Returns 0 if not attached to a session */ + SessionState* getSession() { return session.get(); } + const SessionState* getSession() const { return session.get(); } + + framing::ChannelId getChannel() const { return channel.get(); } + + ConnectionState& getConnection(); + const ConnectionState& getConnection() const; + + framing::AMQP_ClientProxy& getProxy() { return proxy; } + const framing::AMQP_ClientProxy& getProxy() const { return proxy; } + + // Called by closing connection. + void localSuspend(); + void detach() { localSuspend(); } + void sendCompletion(); + void destroy(); + + protected: + void handleIn(framing::AMQFrame&); + void handleOut(framing::AMQFrame&); + + private: + //new methods: + void attach(const std::string& name, bool force); + void attached(const std::string& name); + void detach(const std::string& name); + void detached(const std::string& name, uint8_t code); + + void requestTimeout(uint32_t t); + void timeout(uint32_t t); + + void commandPoint(const framing::SequenceNumber& id, uint64_t offset); + void expected(const framing::SequenceSet& commands, const framing::Array& fragments); + void confirmed(const framing::SequenceSet& commands,const framing::Array& fragments); + void completed(const framing::SequenceSet& commands, bool timelyReply); + void knownCompleted(const framing::SequenceSet& commands); + void flush(bool expected, bool confirmed, bool completed); + void gap(const framing::SequenceSet& commands); + + //hacks for old generator: + void commandPoint(uint32_t id, uint64_t offset) { commandPoint(framing::SequenceNumber(id), offset); } + + void assertAttached(const char* method) const; + void assertActive(const char* method) const; + void assertClosed(const char* method) const; + + Connection& connection; + framing::ChannelHandler channel; + framing::AMQP_ClientProxy proxy; + framing::AMQP_ClientProxy::Session010 peerSession; + bool ignoring; + std::auto_ptr<SessionState> session; +}; + +}} // namespace qpid::broker + + + +#endif /*!QPID_BROKER_SESSIONHANDLER_H*/ diff --git a/qpid/cpp/src/qpid/broker/SessionManager.cpp b/qpid/cpp/src/qpid/broker/SessionManager.cpp new file mode 100644 index 0000000000..6e235e32c3 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/SessionManager.cpp @@ -0,0 +1,113 @@ +/* + * + * 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. + * + */ + +#include "SessionManager.h" +#include "SessionState.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/log/Statement.h" +#include "qpid/log/Helpers.h" +#include "qpid/memory.h" + +#include <boost/bind.hpp> +#include <boost/range.hpp> + +#include <algorithm> +#include <functional> +#include <ostream> + +namespace qpid { +namespace broker { + +using boost::intrusive_ptr; +using namespace sys; +using namespace framing; + +SessionManager::SessionManager(uint32_t a) : ack(a) {} + +SessionManager::~SessionManager() {} + +// FIXME aconway 2008-02-01: pass handler*, allow open unattached. +std::auto_ptr<SessionState> SessionManager::open( + SessionHandler& h, uint32_t timeout_) +{ + Mutex::ScopedLock l(lock); + std::auto_ptr<SessionState> session( + new SessionState(this, &h, timeout_, ack)); + active.insert(session->getId()); + for_each(observers.begin(), observers.end(), + boost::bind(&Observer::opened, _1,boost::ref(*session))); + return session; +} + +void SessionManager::suspend(std::auto_ptr<SessionState> session) { + Mutex::ScopedLock l(lock); + active.erase(session->getId()); + session->suspend(); + session->expiry = AbsTime(now(),session->getTimeout()*TIME_SEC); + if (session->mgmtObject.get() != 0) + session->mgmtObject->set_expireTime ((uint64_t) Duration (session->expiry)); + suspended.push_back(session.release()); // In expiry order + eraseExpired(); +} + +std::auto_ptr<SessionState> SessionManager::resume(const Uuid& id) +{ + Mutex::ScopedLock l(lock); + eraseExpired(); + if (active.find(id) != active.end()) + throw SessionBusyException( + QPID_MSG("Session already active: " << id)); + Suspended::iterator i = std::find_if( + suspended.begin(), suspended.end(), + boost::bind(std::equal_to<Uuid>(), id, boost::bind(&SessionState::getId, _1)) + ); + if (i == suspended.end()) + throw InvalidArgumentException( + QPID_MSG("No suspended session with id=" << id)); + active.insert(id); + std::auto_ptr<SessionState> state(suspended.release(i).release()); + return state; +} + +void SessionManager::erase(const framing::Uuid& id) +{ + Mutex::ScopedLock l(lock); + active.erase(id); +} + +void SessionManager::eraseExpired() { + // Called with lock held. + if (!suspended.empty()) { + Suspended::iterator keep = std::lower_bound( + suspended.begin(), suspended.end(), now(), + boost::bind(std::less<AbsTime>(), boost::bind(&SessionState::expiry, _1), _2)); + if (suspended.begin() != keep) { + QPID_LOG(debug, "Expiring sessions: " << log::formatList(suspended.begin(), keep)); + suspended.erase(suspended.begin(), keep); + } + } +} + +void SessionManager::add(const intrusive_ptr<Observer>& o) { + observers.push_back(o); +} + +}} // namespace qpid::broker diff --git a/qpid/cpp/src/qpid/broker/SessionManager.h b/qpid/cpp/src/qpid/broker/SessionManager.h new file mode 100644 index 0000000000..cc2190c2d1 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/SessionManager.h @@ -0,0 +1,101 @@ +#ifndef QPID_BROKER_SESSIONMANAGER_H +#define QPID_BROKER_SESSIONMANAGER_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. + * + */ + +#include <qpid/framing/Uuid.h> +#include <qpid/sys/Time.h> +#include <qpid/sys/Mutex.h> +#include <qpid/RefCounted.h> + +#include <set> +#include <vector> +#include <memory> + +#include <boost/noncopyable.hpp> +#include <boost/ptr_container/ptr_vector.hpp> +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { + +class SessionState; +class SessionHandler; + +/** + * Create and manage SessionState objects. + */ +class SessionManager : private boost::noncopyable { + public: + /** + * Observer notified of SessionManager events. + */ + struct Observer : public RefCounted { + virtual void opened(SessionState&) {} + }; + + SessionManager(uint32_t ack); + + ~SessionManager(); + + /** Open a new active session, caller takes ownership */ + std::auto_ptr<SessionState> open(SessionHandler& c, uint32_t timeout_); + + /** Suspend a session, start it's timeout counter. + * The factory takes ownership. + */ + void suspend(std::auto_ptr<SessionState> session); + + /** Resume a suspended session. + *@throw Exception if timed out or non-existant. + */ + std::auto_ptr<SessionState> resume(const framing::Uuid&); + + /** Add an Observer. */ + void add(const boost::intrusive_ptr<Observer>&); + + private: + typedef boost::ptr_vector<SessionState> Suspended; + typedef std::set<framing::Uuid> Active; + typedef std::vector<boost::intrusive_ptr<Observer> > Observers; + + void erase(const framing::Uuid&); + void eraseExpired(); + + sys::Mutex lock; + Suspended suspended; + Active active; + uint32_t ack; + Observers observers; + + friend class SessionState; // removes deleted sessions from active set. +}; + + + +}} // namespace qpid::broker + + + + + +#endif /*!QPID_BROKER_SESSIONMANAGER_H*/ diff --git a/qpid/cpp/src/qpid/broker/SessionState.cpp b/qpid/cpp/src/qpid/broker/SessionState.cpp new file mode 100644 index 0000000000..d719bbe145 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/SessionState.cpp @@ -0,0 +1,288 @@ +/* + * + * 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. + * + */ +#include "SessionState.h" +#include "Broker.h" +#include "ConnectionState.h" +#include "MessageDelivery.h" +#include "SemanticHandler.h" +#include "SessionManager.h" +#include "SessionHandler.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/ServerInvoker.h" + +#include <boost/bind.hpp> + +namespace qpid { +namespace broker { + +using namespace framing; +using sys::Mutex; +using boost::intrusive_ptr; +using qpid::management::ManagementAgent; +using qpid::management::ManagementObject; +using qpid::management::Manageable; +using qpid::management::Args; + +SessionState::SessionState( + SessionManager* f, SessionHandler* h, uint32_t timeout_, uint32_t ack) + : framing::SessionState(ack, timeout_ > 0), nextOut(0), + factory(f), handler(h), id(true), timeout(timeout_), + broker(h->getConnection().broker), + version(h->getConnection().getVersion()), + semanticState(*this, *this), + adapter(semanticState), + msgBuilder(&broker.getStore(), broker.getStagingThreshold()), + ackOp(boost::bind(&SemanticState::completed, &semanticState, _1, _2)), + enqueuedOp(boost::bind(&SessionState::enqueued, this, _1)) +{ + getConnection().outputTasks.addOutputTask(&semanticState); + + Manageable* parent = broker.GetVhostObject (); + + if (parent != 0) + { + ManagementAgent::shared_ptr agent = ManagementAgent::getAgent (); + + if (agent.get () != 0) + { + mgmtObject = management::Session::shared_ptr + (new management::Session (this, parent, id.str ())); + mgmtObject->set_attached (1); + mgmtObject->set_clientRef (h->getConnection().GetManagementObject()->getObjectId()); + mgmtObject->set_channelId (h->getChannel()); + mgmtObject->set_detachedLifespan (getTimeout()); + agent->addObject (mgmtObject); + } + } +} + +SessionState::~SessionState() { + // Remove ID from active session list. + if (factory) + factory->erase(getId()); + if (mgmtObject.get () != 0) + mgmtObject->resourceDestroy (); +} + +SessionHandler* SessionState::getHandler() { + return handler; +} + +AMQP_ClientProxy& SessionState::getProxy() { + assert(isAttached()); + return getHandler()->getProxy(); +} + +ConnectionState& SessionState::getConnection() { + assert(isAttached()); + return getHandler()->getConnection(); +} + +bool SessionState::isLocal(const ConnectionToken* t) const +{ + return isAttached() && &(handler->getConnection()) == t; +} + +void SessionState::detach() { + getConnection().outputTasks.removeOutputTask(&semanticState); + Mutex::ScopedLock l(lock); + handler = 0; + if (mgmtObject.get() != 0) + { + mgmtObject->set_attached (0); + } +} + +void SessionState::attach(SessionHandler& h) { + { + Mutex::ScopedLock l(lock); + handler = &h; + if (mgmtObject.get() != 0) + { + mgmtObject->set_attached (1); + mgmtObject->set_clientRef (h.getConnection().GetManagementObject()->getObjectId()); + mgmtObject->set_channelId (h.getChannel()); + } + } + h.getConnection().outputTasks.addOutputTask(&semanticState); +} + +void SessionState::activateOutput() +{ + Mutex::ScopedLock l(lock); + if (isAttached()) { + getConnection().outputTasks.activateOutput(); + } +} + //This class could be used as the callback for queue notifications + //if not attached, it can simply ignore the callback, else pass it + //on to the connection + +ManagementObject::shared_ptr SessionState::GetManagementObject (void) const +{ + return dynamic_pointer_cast<ManagementObject> (mgmtObject); +} + +Manageable::status_t SessionState::ManagementMethod (uint32_t methodId, + Args& /*args*/) +{ + Manageable::status_t status = Manageable::STATUS_UNKNOWN_METHOD; + + switch (methodId) + { + case management::Session::METHOD_DETACH : + if (handler != 0) + { + handler->detach(); + } + status = Manageable::STATUS_OK; + break; + + case management::Session::METHOD_CLOSE : + /* + if (handler != 0) + { + handler->getConnection().closeChannel(handler->getChannel()); + } + status = Manageable::STATUS_OK; + break; + */ + + case management::Session::METHOD_SOLICITACK : + case management::Session::METHOD_RESETLIFESPAN : + status = Manageable::STATUS_NOT_IMPLEMENTED; + break; + } + + return status; +} + +void SessionState::handleCommand(framing::AMQMethodBody* method, SequenceNumber& id) +{ + id = nextIn++; + Invoker::Result invocation = invoke(adapter, *method); + completed.add(id); + + if (!invocation.wasHandled()) { + throw NotImplementedException("Not implemented"); + } else if (invocation.hasResult()) { + nextOut++;//execution result is now a command, so the counter must be incremented + getProxy().getExecution010().result(id, invocation.getResult()); + } + if (method->isSync()) { + incomplete.process(enqueuedOp, true); + sendCompletion(); + } + //TODO: if window gets too large send unsolicited completion +} + +void SessionState::handleContent(AMQFrame& frame, SequenceNumber& id) +{ + intrusive_ptr<Message> msg(msgBuilder.getMessage()); + if (frame.getBof() && frame.getBos()) {//start of frameset + id = nextIn++; + msgBuilder.start(id); + msg = msgBuilder.getMessage(); + } else { + id = msg->getCommandId(); + } + msgBuilder.handle(frame); + if (frame.getEof() && frame.getEos()) {//end of frameset + msg->setPublisher(&getConnection()); + semanticState.handle(msg); + msgBuilder.end(); + + if (msg->isEnqueueComplete()) { + enqueued(msg); + } else { + incomplete.add(msg); + } + + //hold up execution until async enqueue is complete + if (msg->getFrames().getMethod()->isSync()) { + incomplete.process(enqueuedOp, true); + sendCompletion(); + } else { + incomplete.process(enqueuedOp, false); + } + } +} + +void SessionState::enqueued(boost::intrusive_ptr<Message> msg) +{ + completed.add(msg->getCommandId()); + if (msg->requiresAccept()) { + getProxy().getMessage010().accept(SequenceSet(msg->getCommandId())); + } +} + +void SessionState::handle(AMQFrame& frame) +{ + received(frame); + + SequenceNumber commandId; + try { + //TODO: make command handling more uniform, regardless of whether + //commands carry content. (For now, assume all single frame + //assemblies are non-content bearing and all content-bearing + //assemblies will have more than one frame): + if (frame.getBof() && frame.getEof()) { + handleCommand(frame.getMethod(), commandId); + } else { + handleContent(frame, commandId); + } + } catch(const SessionException& e) { + //TODO: better implementation of new exception handling mechanism + + //0-10 final changes the types of exceptions, 'model layer' + //exceptions will all be session exceptions regardless of + //current channel/connection classification + + AMQMethodBody* m = frame.getMethod(); + if (m) { + getProxy().getExecution010().exception(e.code, commandId, m->amqpClassId(), m->amqpMethodId(), 0, e.what(), FieldTable()); + } else { + getProxy().getExecution010().exception(e.code, commandId, 0, 0, 0, e.what(), FieldTable()); + } + handler->destroy(); + } +} + +DeliveryId SessionState::deliver(QueuedMessage& msg, DeliveryToken::shared_ptr token) +{ + uint32_t maxFrameSize = getConnection().getFrameMax(); + MessageDelivery::deliver(msg, getProxy().getHandler(), nextOut, token, maxFrameSize); + return nextOut++; +} + +void SessionState::sendCompletion() +{ + handler->sendCompletion(); +} + +void SessionState::complete(const SequenceSet& commands) +{ + knownCompleted.add(commands); + commands.for_each(ackOp); +} + + +}} // namespace qpid::broker diff --git a/qpid/cpp/src/qpid/broker/SessionState.h b/qpid/cpp/src/qpid/broker/SessionState.h new file mode 100644 index 0000000000..4fc2ae4cc5 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/SessionState.h @@ -0,0 +1,159 @@ +#ifndef QPID_BROKER_SESSION_H +#define QPID_BROKER_SESSION_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. + * + */ + +#include "qpid/framing/Uuid.h" +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/SessionState.h" +#include "qpid/framing/SequenceSet.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Time.h" +#include "qpid/management/Manageable.h" +#include "qpid/management/Session.h" +#include "SessionAdapter.h" +#include "DeliveryAdapter.h" +#include "IncompleteMessageList.h" +#include "MessageBuilder.h" +#include "SessionContext.h" +#include "SemanticState.h" + +#include <boost/noncopyable.hpp> +#include <boost/scoped_ptr.hpp> + +#include <set> +#include <vector> +#include <ostream> + +namespace qpid { + +namespace framing { +class AMQP_ClientProxy; +} + +namespace broker { + +class Broker; +class ConnectionState; +class Message; +class SessionHandler; +class SessionManager; + +/** + * Broker-side session state includes sessions handler chains, which may + * themselves have state. + */ +class SessionState : public framing::SessionState, + public SessionContext, + public DeliveryAdapter, + public management::Manageable +{ + public: + ~SessionState(); + bool isAttached() const { return handler; } + + void detach(); + void attach(SessionHandler& handler); + + + SessionHandler* getHandler(); + + /** @pre isAttached() */ + framing::AMQP_ClientProxy& getProxy(); + + /** @pre isAttached() */ + ConnectionState& getConnection(); + bool isLocal(const ConnectionToken* t) const; + + uint32_t getTimeout() const { return timeout; } + void setTimeout(uint32_t t) { timeout = t; } + + Broker& getBroker() { return broker; } + framing::ProtocolVersion getVersion() const { return version; } + + /** OutputControl **/ + void activateOutput(); + + void handle(framing::AMQFrame& frame); + + void complete(const framing::SequenceSet& ranges); + void sendCompletion(); + + //delivery adapter methods: + DeliveryId deliver(QueuedMessage& msg, DeliveryToken::shared_ptr token); + + // Manageable entry points + management::ManagementObject::shared_ptr GetManagementObject (void) const; + management::Manageable::status_t + ManagementMethod (uint32_t methodId, management::Args& args); + + // Normally SessionManager creates sessions. + SessionState(SessionManager*, + SessionHandler* out, + uint32_t timeout, + uint32_t ackInterval); + + + framing::SequenceSet completed; + framing::SequenceSet knownCompleted; + framing::SequenceNumber nextIn; + framing::SequenceNumber nextOut; + + private: + typedef boost::function<void(DeliveryId, DeliveryId)> RangedOperation; + + SessionManager* factory; + SessionHandler* handler; + framing::Uuid id; + uint32_t timeout; + sys::AbsTime expiry; // Used by SessionManager. + Broker& broker; + framing::ProtocolVersion version; + sys::Mutex lock; + + SemanticState semanticState; + SessionAdapter adapter; + MessageBuilder msgBuilder; + IncompleteMessageList incomplete; + + RangedOperation ackOp; + IncompleteMessageList::CompletionListener enqueuedOp; + + management::Session::shared_ptr mgmtObject; + void handleCommand(framing::AMQMethodBody* method, framing::SequenceNumber& id); + void handleContent(framing::AMQFrame& frame, framing::SequenceNumber& id); + void enqueued(boost::intrusive_ptr<Message> msg); + + friend class SessionManager; +}; + + +inline std::ostream& operator<<(std::ostream& out, const SessionState& session) { + return out << session.getId(); +} + +}} // namespace qpid::broker + + + +#endif /*!QPID_BROKER_SESSION_H*/ diff --git a/qpid/cpp/src/qpid/broker/System.cpp b/qpid/cpp/src/qpid/broker/System.cpp new file mode 100644 index 0000000000..87d5185b97 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/System.cpp @@ -0,0 +1,48 @@ +// +// 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. +// + +#include "System.h" +#include "qpid/management/ManagementAgent.h" +#include <sys/utsname.h> + +using namespace qpid::broker; +using qpid::management::ManagementAgent; + +System::System () +{ + ManagementAgent::shared_ptr agent = ManagementAgent::getAgent (); + + if (agent.get () != 0) + { + mgmtObject = management::System::shared_ptr + (new management::System (this, "host")); + struct utsname _uname; + if (uname (&_uname) == 0) + { + mgmtObject->set_osName (std::string (_uname.sysname)); + mgmtObject->set_nodeName (std::string (_uname.nodename)); + mgmtObject->set_release (std::string (_uname.release)); + mgmtObject->set_version (std::string (_uname.version)); + mgmtObject->set_machine (std::string (_uname.machine)); + } + + agent->addObject (mgmtObject, 3, 0); + } +} + diff --git a/qpid/cpp/src/qpid/broker/System.h b/qpid/cpp/src/qpid/broker/System.h new file mode 100644 index 0000000000..a1a710f2b2 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/System.h @@ -0,0 +1,51 @@ +#ifndef _BrokerSystem_ +#define _BrokerSystem_ + +// +// 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. +// + +#include "qpid/management/Manageable.h" +#include "qpid/management/System.h" +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace broker { + +class System : public management::Manageable +{ + private: + + management::System::shared_ptr mgmtObject; + + public: + + typedef boost::shared_ptr<System> shared_ptr; + + System (); + + management::ManagementObject::shared_ptr GetManagementObject (void) const + { return mgmtObject; } + + management::Manageable::status_t ManagementMethod (uint32_t, management::Args&) + { return management::Manageable::STATUS_OK; } +}; + +}} + +#endif /*!_BrokerSystem_*/ diff --git a/qpid/cpp/src/qpid/broker/Timer.cpp b/qpid/cpp/src/qpid/broker/Timer.cpp new file mode 100644 index 0000000000..9005a7cd6e --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Timer.cpp @@ -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. + * + */ +#include "Timer.h" +#include <iostream> + +using boost::intrusive_ptr; +using qpid::sys::AbsTime; +using qpid::sys::Duration; +using qpid::sys::Monitor; +using qpid::sys::Thread; +using namespace qpid::broker; + +TimerTask::TimerTask(Duration timeout) : + duration(timeout), time(AbsTime::now(), timeout), cancelled(false) {} + +TimerTask::TimerTask(AbsTime _time) : + duration(0), time(_time), cancelled(false) {} + +TimerTask::~TimerTask(){} + +void TimerTask::reset() { time = AbsTime(AbsTime::now(), duration); } + +Timer::Timer() : active(false) +{ + start(); +} + +Timer::~Timer() +{ + stop(); +} + +void Timer::run() +{ + Monitor::ScopedLock l(monitor); + while(active){ + if (tasks.empty()) { + monitor.wait(); + } else { + intrusive_ptr<TimerTask> t = tasks.top(); + if (t->cancelled) { + tasks.pop(); + } else if(t->time < AbsTime::now()) { + tasks.pop(); + t->fire(); + } else { + monitor.wait(t->time); + } + } + } +} + +void Timer::add(intrusive_ptr<TimerTask> task) +{ + Monitor::ScopedLock l(monitor); + tasks.push(task); + monitor.notify(); +} + +void Timer::start() +{ + Monitor::ScopedLock l(monitor); + if (!active) { + active = true; + runner = Thread(this); + } +} + +void Timer::stop() +{ + { + Monitor::ScopedLock l(monitor); + if (!active) return; + active = false; + monitor.notifyAll(); + } + runner.join(); +} + +bool Later::operator()(const intrusive_ptr<TimerTask>& a, + const intrusive_ptr<TimerTask>& b) const +{ + return a.get() && b.get() && a->time > b->time; +} + diff --git a/qpid/cpp/src/qpid/broker/Timer.h b/qpid/cpp/src/qpid/broker/Timer.h new file mode 100644 index 0000000000..f702f0f32d --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Timer.h @@ -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. + * + */ +#ifndef _Timer_ +#define _Timer_ + +#include "qpid/sys/Monitor.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Runnable.h" +#include "qpid/RefCounted.h" + +#include <memory> +#include <queue> + +#include <boost/intrusive_ptr.hpp> + +namespace qpid { +namespace broker { + +struct TimerTask : public RefCounted { + const qpid::sys::Duration duration; + qpid::sys::AbsTime time; + volatile bool cancelled; + + TimerTask(qpid::sys::Duration timeout); + TimerTask(qpid::sys::AbsTime time); + virtual ~TimerTask(); + void reset(); + virtual void fire() = 0; +}; + +struct Later { + bool operator()(const boost::intrusive_ptr<TimerTask>& a, + const boost::intrusive_ptr<TimerTask>& b) const; +}; + +class Timer : private qpid::sys::Runnable { + protected: + qpid::sys::Monitor monitor; + std::priority_queue<boost::intrusive_ptr<TimerTask>, + std::vector<boost::intrusive_ptr<TimerTask> >, + Later> tasks; + qpid::sys::Thread runner; + bool active; + + virtual void run(); + + public: + Timer(); + virtual ~Timer(); + + void add(boost::intrusive_ptr<TimerTask> task); + void start(); + void stop(); + +}; + + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/TopicExchange.cpp b/qpid/cpp/src/qpid/broker/TopicExchange.cpp new file mode 100644 index 0000000000..1c4fa2ea7a --- /dev/null +++ b/qpid/cpp/src/qpid/broker/TopicExchange.cpp @@ -0,0 +1,242 @@ +/* + * + * 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. + * + */ +#include "TopicExchange.h" +#include <algorithm> + +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +// TODO aconway 2006-09-20: More efficient matching algorithm. +// Areas for improvement: +// - excessive string copying: should be 0 copy, match from original buffer. +// - match/lookup: use descision tree or other more efficient structure. + +Tokens& Tokens::operator=(const std::string& s) { + clear(); + if (s.empty()) return *this; + std::string::const_iterator i = s.begin(); + while (true) { + // Invariant: i is at the beginning of the next untokenized word. + std::string::const_iterator j = find(i, s.end(), '.'); + push_back(std::string(i, j)); + if (j == s.end()) return *this; + i = j + 1; + } + return *this; +} + +TopicPattern& TopicPattern::operator=(const Tokens& tokens) { + Tokens::operator=(tokens); + normalize(); + return *this; +} + +namespace { +const std::string hashmark("#"); +const std::string star("*"); +} + +void TopicPattern::normalize() { + std::string word; + Tokens::iterator i = begin(); + while (i != end()) { + if (*i == hashmark) { + ++i; + while (i != end()) { + // Invariant: *(i-1)==#, [begin()..i-1] is normalized. + if (*i == star) { // Move * before #. + std::swap(*i, *(i-1)); + ++i; + } else if (*i == hashmark) { + erase(i); // Remove extra # + } else { + break; + } + } + } else { + i ++; + } + } +} + + +namespace { +// TODO aconway 2006-09-20: Ineficient to convert every routingKey to a string. +// Need StringRef class that operates on a string in place witout copy. +// Should be applied everywhere strings are extracted from frames. +// +bool do_match(Tokens::const_iterator pattern_begin, Tokens::const_iterator pattern_end, Tokens::const_iterator target_begin, Tokens::const_iterator target_end) +{ + // Invariant: [pattern_begin..p) matches [target_begin..t) + Tokens::const_iterator p = pattern_begin; + Tokens::const_iterator t = target_begin; + while (p != pattern_end && t != target_end) + { + if (*p == star || *p == *t) { + ++p, ++t; + } else if (*p == hashmark) { + ++p; + if (do_match(p, pattern_end, t, target_end)) return true; + while (t != target_end) { + ++t; + if (do_match(p, pattern_end, t, target_end)) return true; + } + return false; + } else { + return false; + } + } + while (p != pattern_end && *p == hashmark) ++p; // Ignore trailing # + return t == target_end && p == pattern_end; +} +} + +bool TopicPattern::match(const Tokens& target) const +{ + return do_match(begin(), end(), target.begin(), target.end()); +} + +TopicExchange::TopicExchange(const string& _name, Manageable* _parent) : Exchange(_name, _parent) +{ + if (mgmtExchange.get() != 0) + mgmtExchange->set_type (typeName); +} + +TopicExchange::TopicExchange(const std::string& _name, bool _durable, + const FieldTable& _args, Manageable* _parent) : + Exchange(_name, _durable, _args, _parent) +{ + if (mgmtExchange.get() != 0) + mgmtExchange->set_type (typeName); +} + +bool TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/){ + RWlock::ScopedWlock l(lock); + TopicPattern routingPattern(routingKey); + if (isBound(queue, routingPattern)) { + return false; + } else { + Binding::shared_ptr binding (new Binding (routingKey, queue, this)); + bindings[routingPattern].push_back(binding); + if (mgmtExchange.get() != 0) { + mgmtExchange->inc_bindings (); + dynamic_pointer_cast<management::Queue>(queue->GetManagementObject())->inc_bindings(); + } + return true; + } +} + +bool TopicExchange::unbind(Queue::shared_ptr queue, const string& routingKey, const FieldTable* /*args*/){ + RWlock::ScopedWlock l(lock); + BindingMap::iterator bi = bindings.find(TopicPattern(routingKey)); + Binding::vector& qv(bi->second); + if (bi == bindings.end()) return false; + + Binding::vector::iterator q; + for (q = qv.begin(); q != qv.end(); q++) + if ((*q)->queue == queue) + break; + if(q == qv.end()) return false; + qv.erase(q); + if(qv.empty()) bindings.erase(bi); + if (mgmtExchange.get() != 0) { + mgmtExchange->dec_bindings (); + dynamic_pointer_cast<management::Queue>(queue->GetManagementObject())->dec_bindings(); + } + return true; +} + +bool TopicExchange::isBound(Queue::shared_ptr queue, TopicPattern& pattern) +{ + BindingMap::iterator bi = bindings.find(pattern); + if (bi == bindings.end()) return false; + Binding::vector& qv(bi->second); + Binding::vector::iterator q; + for (q = qv.begin(); q != qv.end(); q++) + if ((*q)->queue == queue) + break; + return q != qv.end(); +} + +void TopicExchange::route(Deliverable& msg, const string& routingKey, const FieldTable* /*args*/){ + RWlock::ScopedRlock l(lock); + uint32_t count(0); + Tokens tokens(routingKey); + + for (BindingMap::iterator i = bindings.begin(); i != bindings.end(); ++i) { + if (i->first.match(tokens)) { + Binding::vector& qv(i->second); + for(Binding::vector::iterator j = qv.begin(); j != qv.end(); j++, count++){ + msg.deliverTo((*j)->queue); + if ((*j)->mgmtBinding.get() != 0) + (*j)->mgmtBinding->inc_msgMatched (); + } + } + } + + if (mgmtExchange.get() != 0) + { + mgmtExchange->inc_msgReceives (); + mgmtExchange->inc_byteReceives (msg.contentSize ()); + if (count == 0) + { + mgmtExchange->inc_msgDrops (); + mgmtExchange->inc_byteDrops (msg.contentSize ()); + } + else + { + mgmtExchange->inc_msgRoutes (count); + mgmtExchange->inc_byteRoutes (count * msg.contentSize ()); + } + } +} + +bool TopicExchange::isBound(Queue::shared_ptr queue, const string* const routingKey, const FieldTable* const) +{ + if (routingKey && queue) { + TopicPattern key(*routingKey); + return isBound(queue, key); + } else if (!routingKey && !queue) { + return bindings.size() > 0; + } else if (routingKey) { + for (BindingMap::iterator i = bindings.begin(); i != bindings.end(); ++i) { + if (i->first.match(*routingKey)) { + return true; + } + } + } else { + for (BindingMap::iterator i = bindings.begin(); i != bindings.end(); ++i) { + Binding::vector& qv(i->second); + Binding::vector::iterator q; + for (q = qv.begin(); q != qv.end(); q++) + if ((*q)->queue == queue) + return true; + } + } + return false; +} + +TopicExchange::~TopicExchange() {} + +const std::string TopicExchange::typeName("topic"); + + diff --git a/qpid/cpp/src/qpid/broker/TopicExchange.h b/qpid/cpp/src/qpid/broker/TopicExchange.h new file mode 100644 index 0000000000..2e107142b7 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/TopicExchange.h @@ -0,0 +1,104 @@ +/* + * + * 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. + * + */ +#ifndef _TopicExchange_ +#define _TopicExchange_ + +#include <map> +#include <vector> +#include "Exchange.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/Monitor.h" +#include "Queue.h" + +namespace qpid { +namespace broker { + +/** A vector of string tokens */ +class Tokens : public std::vector<std::string> { + public: + Tokens() {}; + // Default copy, assign, dtor are sufficient. + + /** Tokenize s, provides automatic conversion of string to Tokens */ + Tokens(const std::string& s) { operator=(s); } + /** Tokenizing assignment operator s */ + Tokens & operator=(const std::string& s); + + private: + size_t hash; +}; + + +/** + * Tokens that have been normalized as a pattern and can be matched + * with topic Tokens. Normalized meands all sequences of mixed * and + * # are reduced to a series of * followed by at most one #. + */ +class TopicPattern : public Tokens +{ + public: + TopicPattern() {} + // Default copy, assign, dtor are sufficient. + TopicPattern(const Tokens& tokens) { operator=(tokens); } + TopicPattern(const std::string& str) { operator=(str); } + TopicPattern& operator=(const Tokens&); + TopicPattern& operator=(const std::string& str) { return operator=(Tokens(str)); } + + /** Match a topic */ + bool match(const std::string& topic) { return match(Tokens(topic)); } + bool match(const Tokens& topic) const; + + private: + void normalize(); +}; + +class TopicExchange : public virtual Exchange{ + typedef std::map<TopicPattern, Binding::vector> BindingMap; + BindingMap bindings; + qpid::sys::RWlock lock; + + bool isBound(Queue::shared_ptr queue, TopicPattern& pattern); + public: + static const std::string typeName; + + TopicExchange(const string& name, management::Manageable* parent = 0); + TopicExchange(const string& _name, bool _durable, + const qpid::framing::FieldTable& _args, management::Manageable* parent = 0); + + virtual std::string getType() const { return typeName; } + + virtual bool bind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual bool unbind(Queue::shared_ptr queue, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual void route(Deliverable& msg, const string& routingKey, const qpid::framing::FieldTable* args); + + virtual bool isBound(Queue::shared_ptr queue, const string* const routingKey, const qpid::framing::FieldTable* const args); + + virtual ~TopicExchange(); +}; + + + +} +} + +#endif diff --git a/qpid/cpp/src/qpid/broker/TransactionalStore.h b/qpid/cpp/src/qpid/broker/TransactionalStore.h new file mode 100644 index 0000000000..2a2bac0c51 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/TransactionalStore.h @@ -0,0 +1,60 @@ +/* + * + * 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. + * + */ +#ifndef _TransactionalStore_ +#define _TransactionalStore_ + +#include <memory> +#include <string> +#include <set> + +namespace qpid { +namespace broker { + +struct InvalidTransactionContextException : public std::exception {}; + +class TransactionContext { +public: + virtual ~TransactionContext(){} +}; + +class TPCTransactionContext : public TransactionContext { +public: + virtual ~TPCTransactionContext(){} +}; + +class TransactionalStore { +public: + virtual std::auto_ptr<TransactionContext> begin() = 0; + virtual std::auto_ptr<TPCTransactionContext> begin(const std::string& xid) = 0; + virtual void prepare(TPCTransactionContext& txn) = 0; + virtual void commit(TransactionContext& txn) = 0; + virtual void abort(TransactionContext& txn) = 0; + + virtual void collectPreparedXids(std::set<std::string>& xids) = 0; + + virtual ~TransactionalStore(){} +}; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/TxAccept.cpp b/qpid/cpp/src/qpid/broker/TxAccept.cpp new file mode 100644 index 0000000000..634d066ecc --- /dev/null +++ b/qpid/cpp/src/qpid/broker/TxAccept.cpp @@ -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. + * + */ +#include "TxAccept.h" +#include "qpid/log/Statement.h" + +using std::bind1st; +using std::bind2nd; +using std::mem_fun_ref; +using namespace qpid::broker; +using qpid::framing::AccumulatedAck; + +TxAccept::TxAccept(AccumulatedAck& _acked, std::list<DeliveryRecord>& _unacked) : + acked(_acked), unacked(_unacked) {} + +bool TxAccept::prepare(TransactionContext* ctxt) throw() +{ + try{ + //dequeue messages from their respective queues: + for (ack_iterator i = unacked.begin(); i != unacked.end(); i++) { + if (i->coveredBy(&acked)) { + i->dequeue(ctxt); + } + } + return true; + }catch(const std::exception& e){ + QPID_LOG(error, "Failed to prepare: " << e.what()); + return false; + }catch(...){ + QPID_LOG(error, "Failed to prepare"); + return false; + } +} + +void TxAccept::commit() throw() +{ + for (ack_iterator i = unacked.begin(); i != unacked.end(); i++) { + if (i->coveredBy(&acked)) i->setEnded(); + } + + unacked.remove_if(mem_fun_ref(&DeliveryRecord::isRedundant)); +} + +void TxAccept::rollback() throw() {} diff --git a/qpid/cpp/src/qpid/broker/TxAccept.h b/qpid/cpp/src/qpid/broker/TxAccept.h new file mode 100644 index 0000000000..011acf5d9e --- /dev/null +++ b/qpid/cpp/src/qpid/broker/TxAccept.h @@ -0,0 +1,57 @@ +/* + * + * 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. + * + */ +#ifndef _TxAccept_ +#define _TxAccept_ + +#include <algorithm> +#include <functional> +#include <list> +#include "qpid/framing/AccumulatedAck.h" +#include "DeliveryRecord.h" +#include "TxOp.h" + +namespace qpid { + namespace broker { + /** + * Defines the transactional behaviour for accepts received by + * a transactional channel. + */ + class TxAccept : public TxOp{ + framing::AccumulatedAck& acked; + std::list<DeliveryRecord>& unacked; + + public: + /** + * @param acked a representation of the accumulation of + * acks received + * @param unacked the record of delivered messages + */ + TxAccept(framing::AccumulatedAck& acked, std::list<DeliveryRecord>& unacked); + virtual bool prepare(TransactionContext* ctxt) throw(); + virtual void commit() throw(); + virtual void rollback() throw(); + virtual ~TxAccept(){} + }; + } +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/TxAck.cpp b/qpid/cpp/src/qpid/broker/TxAck.cpp new file mode 100644 index 0000000000..40b9b0ff33 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/TxAck.cpp @@ -0,0 +1,59 @@ +/* + * + * 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. + * + */ +#include "TxAck.h" +#include "qpid/log/Statement.h" + +using std::bind1st; +using std::bind2nd; +using std::mem_fun_ref; +using namespace qpid::broker; +using qpid::framing::AccumulatedAck; + +TxAck::TxAck(AccumulatedAck& _acked, std::list<DeliveryRecord>& _unacked) : + acked(_acked), unacked(_unacked){ + +} + +bool TxAck::prepare(TransactionContext* ctxt) throw(){ + try{ + //dequeue all acked messages from their queues + for (ack_iterator i = unacked.begin(); i != unacked.end(); i++) { + if (i->coveredBy(&acked)) { + i->dequeue(ctxt); + } + } + return true; + }catch(const std::exception& e){ + QPID_LOG(error, "Failed to prepare: " << e.what()); + return false; + }catch(...){ + QPID_LOG(error, "Failed to prepare"); + return false; + } +} + +void TxAck::commit() throw(){ + //remove all acked records from the list + unacked.remove_if(bind2nd(mem_fun_ref(&DeliveryRecord::coveredBy), &acked)); +} + +void TxAck::rollback() throw(){ +} diff --git a/qpid/cpp/src/qpid/broker/TxAck.h b/qpid/cpp/src/qpid/broker/TxAck.h new file mode 100644 index 0000000000..c8383b6314 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/TxAck.h @@ -0,0 +1,57 @@ +/* + * + * 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. + * + */ +#ifndef _TxAck_ +#define _TxAck_ + +#include <algorithm> +#include <functional> +#include <list> +#include "qpid/framing/AccumulatedAck.h" +#include "DeliveryRecord.h" +#include "TxOp.h" + +namespace qpid { + namespace broker { + /** + * Defines the transactional behaviour for acks received by a + * transactional channel. + */ + class TxAck : public TxOp{ + framing::AccumulatedAck& acked; + std::list<DeliveryRecord>& unacked; + + public: + /** + * @param acked a representation of the accumulation of + * acks received + * @param unacked the record of delivered messages + */ + TxAck(framing::AccumulatedAck& acked, std::list<DeliveryRecord>& unacked); + virtual bool prepare(TransactionContext* ctxt) throw(); + virtual void commit() throw(); + virtual void rollback() throw(); + virtual ~TxAck(){} + }; + } +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/TxBuffer.cpp b/qpid/cpp/src/qpid/broker/TxBuffer.cpp new file mode 100644 index 0000000000..5b5b00e929 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/TxBuffer.cpp @@ -0,0 +1,67 @@ +/* + * + * 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. + * + */ +#include "TxBuffer.h" + +#include <boost/mem_fn.hpp> +using boost::mem_fn; +using namespace qpid::broker; + +bool TxBuffer::prepare(TransactionContext* const ctxt) +{ + for(op_iterator i = ops.begin(); i < ops.end(); i++){ + if(!(*i)->prepare(ctxt)){ + return false; + } + } + return true; +} + +void TxBuffer::commit() +{ + for_each(ops.begin(), ops.end(), mem_fn(&TxOp::commit)); + ops.clear(); +} + +void TxBuffer::rollback() +{ + for_each(ops.begin(), ops.end(), mem_fn(&TxOp::rollback)); + ops.clear(); +} + +void TxBuffer::enlist(TxOp::shared_ptr op) +{ + ops.push_back(op); +} + +bool TxBuffer::commitLocal(TransactionalStore* const store) +{ + std::auto_ptr<TransactionContext> ctxt; + if(store) ctxt = store->begin(); + if (prepare(ctxt.get())) { + if(store) store->commit(*ctxt); + commit(); + return true; + } else { + if(store) store->abort(*ctxt); + rollback(); + return false; + } +} diff --git a/qpid/cpp/src/qpid/broker/TxBuffer.h b/qpid/cpp/src/qpid/broker/TxBuffer.h new file mode 100644 index 0000000000..361c47e92c --- /dev/null +++ b/qpid/cpp/src/qpid/broker/TxBuffer.h @@ -0,0 +1,115 @@ +/* + * + * 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. + * + */ +#ifndef _TxBuffer_ +#define _TxBuffer_ + +#include <algorithm> +#include <functional> +#include <vector> +#include "TransactionalStore.h" +#include "TxOp.h" + +/** + * Represents a single transaction. As such, an instance of this class + * will hold a list of operations representing the workload of the + * transaction. This work can be committed or rolled back. Committing + * is a two-stage process: first all the operations should be + * prepared, then if that succeeds they can be committed. + * + * In the 2pc case, a successful prepare may be followed by either a + * commit or a rollback. + * + * Atomicity of prepare is ensured by using a lower level + * transactional facility. This saves explicitly rolling back all the + * successfully prepared ops when one of them fails. i.e. we do not + * use 2pc internally, we instead ensure that prepare is atomic at a + * lower level. This makes individual prepare operations easier to + * code. + * + * Transactions on a messaging broker effect three types of 'action': + * (1) updates to persistent storage (2) updates to transient storage + * or cached data (3) network writes. + * + * Of these, (1) should always occur atomically during prepare to + * ensure that if the broker crashes while a transaction is being + * completed the persistent state (which is all that then remains) is + * consistent. (3) can only be done on commit, after a successful + * prepare. There is a little more flexibility with (2) but any + * changes made during prepare should be subject to the control of the + * TransactionalStore in use. + */ +namespace qpid { + namespace broker { + class TxBuffer{ + typedef std::vector<TxOp::shared_ptr>::iterator op_iterator; + std::vector<TxOp::shared_ptr> ops; + protected: + + public: + typedef boost::shared_ptr<TxBuffer> shared_ptr; + /** + * Adds an operation to the transaction. + */ + void enlist(TxOp::shared_ptr op); + + /** + * Requests that all ops are prepared. This should + * primarily involve making sure that a persistent record + * of the operations is stored where necessary. + * + * Once prepared, a transaction can be committed (or in + * the 2pc case, rolled back). + * + * @returns true if all the operations prepared + * successfully, false if not. + */ + bool prepare(TransactionContext* const ctxt); + + /** + * Signals that the ops all prepared successfully and can + * now commit, i.e. the operation can now be fully carried + * out. + * + * Should only be called after a call to prepare() returns + * true. + */ + void commit(); + + /** + * Signals that all ops can be rolled back. + * + * Should only be called either after a call to prepare() + * returns true (2pc) or instead of a prepare call + * ('server-local') + */ + void rollback(); + + /** + * Helper method for managing the process of server local + * commit + */ + bool commitLocal(TransactionalStore* const store); + }; + } +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/TxOp.h b/qpid/cpp/src/qpid/broker/TxOp.h new file mode 100644 index 0000000000..e687c437cc --- /dev/null +++ b/qpid/cpp/src/qpid/broker/TxOp.h @@ -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. + * + */ +#ifndef _TxOp_ +#define _TxOp_ + +#include "TransactionalStore.h" +#include <boost/shared_ptr.hpp> + +namespace qpid { + namespace broker { + class TxOp{ + public: + typedef boost::shared_ptr<TxOp> shared_ptr; + + virtual bool prepare(TransactionContext*) throw() = 0; + virtual void commit() throw() = 0; + virtual void rollback() throw() = 0; + virtual ~TxOp(){} + }; + } +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/TxPublish.cpp b/qpid/cpp/src/qpid/broker/TxPublish.cpp new file mode 100644 index 0000000000..1a8630eb54 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/TxPublish.cpp @@ -0,0 +1,78 @@ +/* + * + * 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. + * + */ +#include "qpid/log/Statement.h" +#include "TxPublish.h" + +using boost::intrusive_ptr; +using namespace qpid::broker; + +TxPublish::TxPublish(intrusive_ptr<Message> _msg) : msg(_msg) {} + +bool TxPublish::prepare(TransactionContext* ctxt) throw(){ + try{ + for_each(queues.begin(), queues.end(), Prepare(ctxt, msg)); + return true; + }catch(...){ + QPID_LOG(error, "Failed to prepare"); + return false; + } +} + +void TxPublish::commit() throw(){ + for_each(queues.begin(), queues.end(), Commit(msg)); +} + +void TxPublish::rollback() throw(){ +} + +void TxPublish::deliverTo(Queue::shared_ptr& queue){ + if (!queue->isLocal(msg)) { + queues.push_back(queue); + delivered = true; + } else { + QPID_LOG(debug, "Won't enqueue local message for " << queue->getName()); + } +} + +TxPublish::Prepare::Prepare(TransactionContext* _ctxt, intrusive_ptr<Message>& _msg) + : ctxt(_ctxt), msg(_msg){} + +void TxPublish::Prepare::operator()(Queue::shared_ptr& queue){ + if (!queue->enqueue(ctxt, msg)){ + /** + * if not store then mark message for ack and deleivery once + * commit happens, as async IO will never set it when no store + * exists + */ + msg->enqueueComplete(); + } +} + +TxPublish::Commit::Commit(intrusive_ptr<Message>& _msg) : msg(_msg){} + +void TxPublish::Commit::operator()(Queue::shared_ptr& queue){ + queue->process(msg); +} + +uint64_t TxPublish::contentSize () +{ + return msg->contentSize (); +} diff --git a/qpid/cpp/src/qpid/broker/TxPublish.h b/qpid/cpp/src/qpid/broker/TxPublish.h new file mode 100644 index 0000000000..680e0c7546 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/TxPublish.h @@ -0,0 +1,83 @@ +/* + * + * 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. + * + */ +#ifndef _TxPublish_ +#define _TxPublish_ + +#include "Queue.h" +#include "Deliverable.h" +#include "Message.h" +#include "MessageStore.h" +#include "TxOp.h" + +#include <algorithm> +#include <functional> +#include <list> + +#include <boost/intrusive_ptr.hpp> + +namespace qpid { + namespace broker { + /** + * Defines the behaviour for publish operations on a + * transactional channel. Messages are routed through + * exchanges when received but are not at that stage delivered + * to the matching queues, rather the queues are held in an + * instance of this class. On prepare() the message is marked + * enqueued to the relevant queues in the MessagesStore. On + * commit() the messages will be passed to the queue for + * dispatch or to be added to the in-memory queue. + */ + class TxPublish : public TxOp, public Deliverable{ + class Prepare{ + TransactionContext* ctxt; + boost::intrusive_ptr<Message>& msg; + public: + Prepare(TransactionContext* ctxt, boost::intrusive_ptr<Message>& msg); + void operator()(Queue::shared_ptr& queue); + }; + + class Commit{ + boost::intrusive_ptr<Message>& msg; + public: + Commit(boost::intrusive_ptr<Message>& msg); + void operator()(Queue::shared_ptr& queue); + }; + + boost::intrusive_ptr<Message> msg; + std::list<Queue::shared_ptr> queues; + + public: + TxPublish(boost::intrusive_ptr<Message> msg); + virtual bool prepare(TransactionContext* ctxt) throw(); + virtual void commit() throw(); + virtual void rollback() throw(); + + virtual void deliverTo(Queue::shared_ptr& queue); + + virtual ~TxPublish(){} + + uint64_t contentSize(); + }; + } +} + + +#endif diff --git a/qpid/cpp/src/qpid/broker/Vhost.cpp b/qpid/cpp/src/qpid/broker/Vhost.cpp new file mode 100644 index 0000000000..06a8c8eca3 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Vhost.cpp @@ -0,0 +1,40 @@ +// +// 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. +// + +#include "Vhost.h" +#include "qpid/management/ManagementAgent.h" + +using namespace qpid::broker; +using qpid::management::ManagementAgent; + +Vhost::Vhost (management::Manageable* parentBroker) +{ + if (parentBroker != 0) + { + ManagementAgent::shared_ptr agent = ManagementAgent::getAgent (); + + if (agent.get () != 0) + { + mgmtObject = management::Vhost::shared_ptr + (new management::Vhost (this, parentBroker, "/")); + agent->addObject (mgmtObject, 2, 0); + } + } +} + diff --git a/qpid/cpp/src/qpid/broker/Vhost.h b/qpid/cpp/src/qpid/broker/Vhost.h new file mode 100644 index 0000000000..b702dcebf0 --- /dev/null +++ b/qpid/cpp/src/qpid/broker/Vhost.h @@ -0,0 +1,51 @@ +#ifndef _Vhost_ +#define _Vhost_ + +// +// 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. +// + +#include "qpid/management/Manageable.h" +#include "qpid/management/Vhost.h" +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace broker { + +class Vhost : public management::Manageable +{ + private: + + management::Vhost::shared_ptr mgmtObject; + + public: + + typedef boost::shared_ptr<Vhost> shared_ptr; + + Vhost (management::Manageable* parentBroker); + + management::ManagementObject::shared_ptr GetManagementObject (void) const + { return mgmtObject; } + + management::Manageable::status_t ManagementMethod (uint32_t, management::Args&) + { return management::Manageable::STATUS_OK; } +}; + +}} + +#endif /*!_Vhost_*/ diff --git a/qpid/cpp/src/qpid/client/AckMode.h b/qpid/cpp/src/qpid/client/AckMode.h new file mode 100644 index 0000000000..944fba655d --- /dev/null +++ b/qpid/cpp/src/qpid/client/AckMode.h @@ -0,0 +1,53 @@ +#ifndef _client_AckMode_h +#define _client_AckMode_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. + * + */ + +namespace qpid { +namespace client { + +/** + * DEPRECATED + * + * The available acknowledgements modes for Channel (now also deprecated). + */ +enum AckMode { + /** No acknowledgement will be sent, broker can + discard messages as soon as they are delivered + to a consumer using this mode. **/ + NO_ACK = 0, + /** Each message will be automatically + acknowledged as soon as it is delivered to the + application **/ + AUTO_ACK = 1, + /** Acknowledgements will be sent automatically, + but not for each message. **/ + LAZY_ACK = 2, + /** The application is responsible for explicitly + acknowledging messages. **/ + CLIENT_ACK = 3 +}; + +}} // namespace qpid::client + +#endif diff --git a/qpid/cpp/src/qpid/client/AckPolicy.h b/qpid/cpp/src/qpid/client/AckPolicy.h new file mode 100644 index 0000000000..af17539ebe --- /dev/null +++ b/qpid/cpp/src/qpid/client/AckPolicy.h @@ -0,0 +1,56 @@ +#ifndef QPID_CLIENT_ACKPOLICY_H +#define QPID_CLIENT_ACKPOLICY_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. + * + */ + +namespace qpid { +namespace client { + +/** + * Policy for automatic acknowledgement of messages. + * + * \ingroup clientapi + */ +class AckPolicy +{ + size_t interval; + size_t count; + + public: + /** + *@param n: acknowledge every n messages. + *n==0 means no automatick acknowledgement. + */ + AckPolicy(size_t n=1) : interval(n), count(n) {} + + void ack(const Message& msg) { + if (!interval) return; + bool send=(--count==0); + msg.acknowledge(true, send); + if (send) count = interval; + } +}; + +}} // namespace qpid::client + + + +#endif /*!QPID_CLIENT_ACKPOLICY_H*/ diff --git a/qpid/cpp/src/qpid/client/ChainableFrameHandler.h b/qpid/cpp/src/qpid/client/ChainableFrameHandler.h new file mode 100644 index 0000000000..29e16d53dc --- /dev/null +++ b/qpid/cpp/src/qpid/client/ChainableFrameHandler.h @@ -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. + * + */ + +#ifndef _ChainableFrameHandler_ +#define _ChainableFrameHandler_ + +#include <boost/function.hpp> +#include "qpid/framing/AMQFrame.h" + +namespace qpid { +namespace client { + +struct ChainableFrameHandler +{ + typedef boost::function<void(framing::AMQFrame&)> FrameDelegate; + + FrameDelegate in; + FrameDelegate out; + + ChainableFrameHandler() {} + ChainableFrameHandler(FrameDelegate i, FrameDelegate o): in(i), out(o) {} + virtual ~ChainableFrameHandler() {} +}; + +}} + + + +#endif diff --git a/qpid/cpp/src/qpid/client/Channel.cpp b/qpid/cpp/src/qpid/client/Channel.cpp new file mode 100644 index 0000000000..4af69c8552 --- /dev/null +++ b/qpid/cpp/src/qpid/client/Channel.cpp @@ -0,0 +1,268 @@ +/* + * + * 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. + * + */ +#include "qpid/log/Statement.h" +#include <iostream> +#include <sstream> +#include "Channel.h" +#include "qpid/sys/Monitor.h" +#include "Message.h" +#include "Connection.h" +#include "Demux.h" +#include "FutureResponse.h" +#include "MessageListener.h" +#include "MessageQueue.h" +#include <boost/format.hpp> +#include <boost/bind.hpp> +#include "qpid/framing/all_method_bodies.h" + +using namespace std; +using namespace boost; +using namespace qpid::framing; +using namespace qpid::sys; + +namespace qpid{ +namespace client{ +using namespace arg; + +const std::string empty; + +class ScopedSync +{ + Session& session; + public: + ScopedSync(Session& s, bool enabled = true) : session(s) { session.setSynchronous(enabled); } + ~ScopedSync() { session.setSynchronous(false); } +}; + +Channel::Channel(bool _transactional, u_int16_t _prefetch) : + prefetch(_prefetch), transactional(_transactional), running(false), + uniqueId(true)/*could eventually be the session id*/, nameCounter(0), active(false) +{ +} + +Channel::~Channel() +{ + join(); +} + +void Channel::open(const Session& s) +{ + Mutex::ScopedLock l(stopLock); + if (isOpen()) + throw ChannelBusyException(); + active = true; + session = s; + if(isTransactional()) { + session.txSelect(); + } +} + +bool Channel::isOpen() const { + Mutex::ScopedLock l(stopLock); + return active; +} + +void Channel::setPrefetch(uint32_t _prefetch){ + prefetch = _prefetch; +} + +void Channel::declareExchange(Exchange& _exchange, bool synch){ + ScopedSync s(session, synch); + session.exchangeDeclare(exchange=_exchange.getName(), type=_exchange.getType()); +} + +void Channel::deleteExchange(Exchange& _exchange, bool synch){ + ScopedSync s(session, synch); + session.exchangeDelete(exchange=_exchange.getName(), ifUnused=false); +} + +void Channel::declareQueue(Queue& _queue, bool synch){ + if (_queue.getName().empty()) { + stringstream uniqueName; + uniqueName << uniqueId << "-queue-" << ++nameCounter; + _queue.setName(uniqueName.str()); + } + + ScopedSync s(session, synch); + session.queueDeclare(queue=_queue.getName(), passive=false/*passive*/, durable=_queue.isDurable(), + exclusive=_queue.isExclusive(), autoDelete=_queue.isAutoDelete()); + +} + +void Channel::deleteQueue(Queue& _queue, bool ifunused, bool ifempty, bool synch){ + ScopedSync s(session, synch); + session.queueDelete(queue=_queue.getName(), ifUnused=ifunused, ifEmpty=ifempty); +} + +void Channel::bind(const Exchange& exchange, const Queue& queue, const std::string& key, const FieldTable& args, bool synch){ + string e = exchange.getName(); + string q = queue.getName(); + ScopedSync s(session, synch); + session.queueBind(0, q, e, key, args); +} + +void Channel::commit(){ + session.txCommit(); +} + +void Channel::rollback(){ + session.txRollback(); +} + +void Channel::consume( + Queue& _queue, const std::string& tag, MessageListener* listener, + AckMode ackMode, bool noLocal, bool synch, const FieldTable* fields) { + + if (tag.empty()) { + throw Exception("A tag must be specified for a consumer."); + } + { + Mutex::ScopedLock l(lock); + ConsumerMap::iterator i = consumers.find(tag); + if (i != consumers.end()) + throw NotAllowedException(QPID_MSG("Consumer already exists with tag " << tag )); + Consumer& c = consumers[tag]; + c.listener = listener; + c.ackMode = ackMode; + c.count = 0; + } + uint8_t confirmMode = ackMode == NO_ACK ? 0 : 1; + ScopedSync s(session, synch); + session.messageSubscribe(0, _queue.getName(), tag, noLocal, + confirmMode, 0/*pre-acquire*/, + false, fields ? *fields : FieldTable()); + if (!prefetch) { + session.messageFlowMode(tag, 0/*credit based*/); + } + + //allocate some credit: + session.messageFlow(tag, 1/*BYTES*/, 0xFFFFFFFF); + session.messageFlow(tag, 0/*MESSAGES*/, prefetch ? prefetch : 0xFFFFFFFF); +} + +void Channel::cancel(const std::string& tag, bool synch) { + Consumer c; + { + Mutex::ScopedLock l(lock); + ConsumerMap::iterator i = consumers.find(tag); + if (i == consumers.end()) + return; + c = i->second; + consumers.erase(i); + } + ScopedSync s(session, synch); + session.messageCancel(tag); +} + +bool Channel::get(Message& msg, const Queue& _queue, AckMode ackMode) { + string tag = "get-handler"; + ScopedDivert handler(tag, session.getExecution().getDemux()); + Demux::QueuePtr incoming = handler.getQueue(); + + session.messageSubscribe(destination=tag, queue=_queue.getName(), confirmMode=(ackMode == NO_ACK ? 0 : 1)); + session.messageFlow(tag, 1/*BYTES*/, 0xFFFFFFFF); + session.messageFlow(tag, 0/*MESSAGES*/, 1); + Completion status = session.messageFlush(tag); + status.sync(); + session.messageCancel(tag); + + FrameSet::shared_ptr p; + if (incoming->tryPop(p)) { + msg.populate(*p); + if (ackMode == AUTO_ACK) msg.acknowledge(session, false, true); + return true; + } + else + return false; +} + +void Channel::publish(Message& msg, const Exchange& exchange, + const std::string& routingKey, + bool mandatory, bool /*?TODO-restore immediate?*/) { + + msg.getDeliveryProperties().setRoutingKey(routingKey); + msg.getDeliveryProperties().setDiscardUnroutable(!mandatory); + session.messageTransfer(destination=exchange.getName(), content=msg); +} + +void Channel::close() +{ + session.close(); + { + Mutex::ScopedLock l(stopLock); + active = false; + } + stop(); +} + +void Channel::start(){ + running = true; + dispatcher = Thread(*this); +} + +void Channel::stop() { + gets.close(); + join(); +} + +void Channel::join() { + Mutex::ScopedLock l(stopLock); + if(running && dispatcher.id()) { + dispatcher.join(); + running = false; + } +} + +void Channel::dispatch(FrameSet& content, const std::string& destination) +{ + ConsumerMap::iterator i = consumers.find(destination); + if (i != consumers.end()) { + Message msg; + msg.populate(content); + MessageListener* listener = i->second.listener; + listener->received(msg); + if (isOpen() && i->second.ackMode != CLIENT_ACK) { + bool send = i->second.ackMode == AUTO_ACK + || (prefetch && ++(i->second.count) > (prefetch / 2)); + if (send) i->second.count = 0; + session.getExecution().completed(content.getId(), true, send); + } + } else { + QPID_LOG(warning, "Dropping message for unrecognised consumer: " << destination); + } +} + +void Channel::run() { + try { + while (true) { + FrameSet::shared_ptr content = session.get(); + //need to dispatch this to the relevant listener: + if (content->isA<MessageTransferBody>()) { + dispatch(*content, content->as<MessageTransferBody>()->getDestination()); + } else { + QPID_LOG(warning, "Dropping unsupported message type: " << content->getMethod()); + } + } + } catch (const ClosedException&) {} +} + +}} + diff --git a/qpid/cpp/src/qpid/client/Channel.h b/qpid/cpp/src/qpid/client/Channel.h new file mode 100644 index 0000000000..2cda97dc63 --- /dev/null +++ b/qpid/cpp/src/qpid/client/Channel.h @@ -0,0 +1,316 @@ +#ifndef _client_Channel_h +#define _client_Channel_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. + * + */ +#include <memory> +#include <boost/scoped_ptr.hpp> +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/Uuid.h" +#include "Exchange.h" +#include "Message.h" +#include "Queue.h" +#include "ConnectionImpl.h" +#include "qpid/client/Session.h" +#include "qpid/Exception.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Thread.h" +#include "AckMode.h" + +namespace qpid { + +namespace framing { +class ChannelCloseBody; +class AMQMethodBody; +} + +namespace client { + +class Connection; +class MessageChannel; +class MessageListener; +class ReturnedMessageHandler; + +/** + * THIS CLASS IS DEPRECATED AND WILL BE SHORTLY REMOVED + * + * Represents an AMQP channel, i.e. loosely a session of work. It + * is through a channel that most of the AMQP 'methods' are + * exposed. + * + */ +class Channel : private sys::Runnable +{ + private: + struct Consumer{ + MessageListener* listener; + AckMode ackMode; + uint32_t count; + }; + typedef std::map<std::string, Consumer> ConsumerMap; + + mutable sys::Mutex lock; + sys::Thread dispatcher; + + uint32_t prefetch; + const bool transactional; + framing::ProtocolVersion version; + + mutable sys::Mutex stopLock; + bool running; + + ConsumerMap consumers; + Session session; + framing::ChannelId channelId; + sys::BlockingQueue<framing::FrameSet::shared_ptr> gets; + framing::Uuid uniqueId; + uint32_t nameCounter; + bool active; + + void stop(); + + void open(const Session& session); + void closeInternal(); + void join(); + + void dispatch(framing::FrameSet& msg, const std::string& destination); + + friend class Connection; + + public: + /** + * Creates a channel object. + * + * @param transactional if true, the publishing and acknowledgement + * of messages will be transactional and can be committed or + * aborted in atomic units (@see commit(), @see rollback()) + * + * @param prefetch specifies the number of unacknowledged + * messages the channel is willing to have sent to it + * asynchronously + */ + Channel(bool transactional = false, u_int16_t prefetch = 0); + + ~Channel(); + + /** + * Declares an exchange. + * + * In AMQP Exchanges are the destinations to which messages + * are published. They have Queues bound to them and route + * messages they receive to those queues. The routing rules + * depend on the type of the exchange. + * + * @param exchange an Exchange object representing the + * exchange to declare + * + * @param synch if true this call will block until a response + * is received from the broker + */ + void declareExchange(Exchange& exchange, bool synch = true); + /** + * Deletes an exchange + * + * @param exchange an Exchange object representing the exchange to delete + * + * @param synch if true this call will block until a response + * is received from the broker + */ + void deleteExchange(Exchange& exchange, bool synch = true); + /** + * Declares a Queue + * + * @param queue a Queue object representing the queue to declare + * + * @param synch if true this call will block until a response + * is received from the broker + */ + void declareQueue(Queue& queue, bool synch = true); + /** + * Deletes a Queue + * + * @param queue a Queue object representing the queue to delete + * + * @param synch if true this call will block until a response + * is received from the broker + */ + void deleteQueue(Queue& queue, bool ifunused = false, bool ifempty = false, bool synch = true); + /** + * Binds a queue to an exchange. The exact semantics of this + * (in particular how 'routing keys' and 'binding arguments' + * are used) depends on the type of the exchange. + * + * @param exchange an Exchange object representing the + * exchange to bind to + * + * @param queue a Queue object representing the queue to be + * bound + * + * @param key the 'routing key' for the binding + * + * @param args the 'binding arguments' for the binding + * + * @param synch if true this call will block until a response + * is received from the broker + */ + void bind(const Exchange& exchange, const Queue& queue, + const std::string& key, + const framing::FieldTable& args=framing::FieldTable(), + bool synch = true); + + /** + * For a transactional channel this will commit all + * publications and acknowledgements since the last commit (or + * the channel was opened if there has been no previous + * commit). This will cause published messages to become + * available to consumers and acknowledged messages to be + * consumed and removed from the queues they were dispatched + * from. + * + * Transactionailty of a channel is specified when the channel + * object is created (@see Channel()). + */ + void commit(); + + /** + * For a transactional channel, this will rollback any + * publications or acknowledgements. It will be as if the + * ppblished messages were never sent and the acknowledged + * messages were never consumed. + */ + void rollback(); + + /** + * Change the prefetch in use. + */ + void setPrefetch(uint32_t prefetch); + + uint32_t getPrefetch() { return prefetch; } + + /** + * Start message dispatching on a new thread + */ + void start(); + + /** + * Close the channel. Closing a channel that is not open has no + * effect. + */ + void close(); + + /** True if the channel is transactional */ + bool isTransactional() { return transactional; } + + /** True if the channel is open */ + bool isOpen() const; + + /** Return the protocol version */ + framing::ProtocolVersion getVersion() const { return version ; } + + /** + * Creates a 'consumer' for a queue. Messages in (or arriving + * at) that queue will be delivered to consumers + * asynchronously. + * + * @param queue a Queue instance representing the queue to + * consume from + * + * @param tag an identifier to associate with the consumer + * that can be used to cancel its subscription (if empty, this + * will be assigned by the broker) + * + * @param listener a pointer to an instance of an + * implementation of the MessageListener interface. Messages + * received from this queue for this consumer will result in + * invocation of the received() method on the listener, with + * the message itself passed in. + * + * @param ackMode the mode of acknowledgement that the broker + * should assume for this consumer. @see AckMode + * + * @param noLocal if true, this consumer will not be sent any + * message published by this connection + * + * @param synch if true this call will block until a response + * is received from the broker + */ + void consume( + Queue& queue, const std::string& tag, MessageListener* listener, + AckMode ackMode = NO_ACK, bool noLocal = false, bool synch = true, + const framing::FieldTable* fields = 0); + + /** + * Cancels a subscription previously set up through a call to consume(). + * + * @param tag the identifier used (or assigned) in the consume + * request that set up the subscription to be cancelled. + * + * @param synch if true this call will block until a response + * is received from the broker + */ + void cancel(const std::string& tag, bool synch = true); + /** + * Synchronous pull of a message from a queue. + * + * @param msg a message object that will contain the message + * headers and content if the call completes. + * + * @param queue the queue to consume from + * + * @param ackMode the acknowledgement mode to use (@see + * AckMode) + * + * @return true if a message was succcessfully dequeued from + * the queue, false if the queue was empty. + */ + bool get(Message& msg, const Queue& queue, AckMode ackMode = NO_ACK); + + /** + * Publishes (i.e. sends a message to the broker). + * + * @param msg the message to publish + * + * @param exchange the exchange to publish the message to + * + * @param routingKey the routing key to publish with + * + * @param mandatory if true and the exchange to which this + * publish is directed has no matching bindings, the message + * will be returned (see setReturnedMessageHandler()). + * + * @param immediate if true and there is no consumer to + * receive this message on publication, the message will be + * returned (see setReturnedMessageHandler()). + */ + void publish(Message& msg, const Exchange& exchange, + const std::string& routingKey, + bool mandatory = false, bool immediate = false); + + /** + * Deliver incoming messages to the appropriate MessageListener. + */ + void run(); +}; + +}} + +#endif /*!_client_Channel_h*/ diff --git a/qpid/cpp/src/qpid/client/Completion.h b/qpid/cpp/src/qpid/client/Completion.h new file mode 100644 index 0000000000..4d324aaf28 --- /dev/null +++ b/qpid/cpp/src/qpid/client/Completion.h @@ -0,0 +1,64 @@ +/* + * + * 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. + * + */ + +#ifndef _Completion_ +#define _Completion_ + +#include <boost/shared_ptr.hpp> +#include "Future.h" +#include "SessionCore.h" + +namespace qpid { +namespace client { + +class Completion +{ +protected: + Future future; + shared_ptr<SessionCore> session; + +public: + Completion() {} + + Completion(Future f, shared_ptr<SessionCore> s) : future(f), session(s) {} + + void sync() + { + future.sync(*session); + } + + void wait() + { + future.wait(*session); + } + + bool isComplete() { + return future.isComplete(*session); + } + + bool isCompleteUpTo() { + return future.isCompleteUpTo(*session); + } +}; + +}} + +#endif diff --git a/qpid/cpp/src/qpid/client/CompletionTracker.cpp b/qpid/cpp/src/qpid/client/CompletionTracker.cpp new file mode 100644 index 0000000000..76ea9dec51 --- /dev/null +++ b/qpid/cpp/src/qpid/client/CompletionTracker.cpp @@ -0,0 +1,121 @@ +/* + * + * 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. + * + */ + +#include "CompletionTracker.h" +#include <algorithm> + +using qpid::client::CompletionTracker; +using namespace qpid::framing; +using namespace boost; + +namespace +{ +const std::string empty; +} + +CompletionTracker::CompletionTracker() : closed(false) {} +CompletionTracker::CompletionTracker(const SequenceNumber& m) : mark(m) {} + +void CompletionTracker::close() +{ + sys::Mutex::ScopedLock l(lock); + closed=true; + while (!listeners.empty()) { + Record r(listeners.front()); + { + sys::Mutex::ScopedUnlock u(lock); + r.completed(); + } + listeners.pop_front(); + } +} + + +void CompletionTracker::completed(const SequenceNumber& _mark) +{ + sys::Mutex::ScopedLock l(lock); + mark = _mark; + while (!listeners.empty() && !(listeners.front().id > mark)) { + Record r(listeners.front()); + listeners.pop_front(); + { + sys::Mutex::ScopedUnlock u(lock); + r.completed(); + } + } +} + +void CompletionTracker::received(const SequenceNumber& id, const std::string& result) +{ + sys::Mutex::ScopedLock l(lock); + Listeners::iterator i = seek(id); + if (i != listeners.end() && i->id == id) { + i->received(result); + listeners.erase(i); + } +} + +void CompletionTracker::listenForCompletion(const SequenceNumber& point, CompletionListener listener) +{ + if (!add(Record(point, listener))) { + listener(); + } +} + +void CompletionTracker::listenForResult(const SequenceNumber& point, ResultListener listener) +{ + if (!add(Record(point, listener))) { + listener(empty); + } +} + +bool CompletionTracker::add(const Record& record) +{ + sys::Mutex::ScopedLock l(lock); + if (record.id <= mark || closed) { + return false; + } else { + //insert at the correct position + Listeners::iterator i = seek(record.id); + if (i == listeners.end()) i = listeners.begin(); + listeners.insert(i, record); + return true; + } +} + +CompletionTracker::Listeners::iterator CompletionTracker::seek(const framing::SequenceNumber& point) +{ + Listeners::iterator i = listeners.begin(); + while (i != listeners.end() && i->id < point) i++; + return i; +} + + +void CompletionTracker::Record::completed() +{ + if (f) f(); + else if(g) g(empty);//won't get a result if command is now complete +} + +void CompletionTracker::Record::received(const std::string& result) +{ + if (g) g(result); +} diff --git a/qpid/cpp/src/qpid/client/CompletionTracker.h b/qpid/cpp/src/qpid/client/CompletionTracker.h new file mode 100644 index 0000000000..55f7ff7531 --- /dev/null +++ b/qpid/cpp/src/qpid/client/CompletionTracker.h @@ -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. + * + */ + +#include <list> +#include <boost/function.hpp> +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/SequenceNumber.h" +#include "qpid/sys/Mutex.h" + +#ifndef _CompletionTracker_ +#define _CompletionTracker_ + +namespace qpid { +namespace client { + +class CompletionTracker +{ +public: + typedef boost::function<void()> CompletionListener; + typedef boost::function<void(const std::string&)> ResultListener; + + CompletionTracker(); + CompletionTracker(const framing::SequenceNumber& mark); + void completed(const framing::SequenceNumber& mark); + void received(const framing::SequenceNumber& id, const std::string& result); + void listenForCompletion(const framing::SequenceNumber& point, CompletionListener l); + void listenForResult(const framing::SequenceNumber& point, ResultListener l); + void close(); + +private: + struct Record + { + framing::SequenceNumber id; + CompletionListener f; + ResultListener g; + + Record(const framing::SequenceNumber& _id, CompletionListener l) : id(_id), f(l) {} + Record(const framing::SequenceNumber& _id, ResultListener l) : id(_id), g(l) {} + void completed(); + void received(const std::string& result); + + }; + + typedef std::list<Record> Listeners; + bool closed; + + sys::Mutex lock; + framing::SequenceNumber mark; + Listeners listeners; + + bool add(const Record& r); + Listeners::iterator seek(const framing::SequenceNumber&); +}; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/client/Connection.cpp b/qpid/cpp/src/qpid/client/Connection.cpp new file mode 100644 index 0000000000..872e04b3b5 --- /dev/null +++ b/qpid/cpp/src/qpid/client/Connection.cpp @@ -0,0 +1,97 @@ +/* + * + * 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. + * + */ +#include <algorithm> +#include <boost/format.hpp> +#include <boost/bind.hpp> + +#include "Connection.h" +#include "Channel.h" +#include "Message.h" +#include "SessionCore.h" +#include "qpid/log/Logger.h" +#include "qpid/log/Options.h" +#include "qpid/log/Statement.h" +#include "qpid/shared_ptr.h" +#include <iostream> +#include <sstream> +#include <functional> + +using namespace qpid::framing; +using namespace qpid::sys; + + +namespace qpid { +namespace client { + +Connection::Connection(bool _debug, uint32_t _max_frame_size, framing::ProtocolVersion _version) : + channelIdCounter(0), version(_version), + max_frame_size(_max_frame_size), + isOpen(false), + impl(new ConnectionImpl( + shared_ptr<Connector>(new Connector(_version, _debug)))) +{} + +Connection::Connection(shared_ptr<Connector> c) : + channelIdCounter(0), version(framing::highestProtocolVersion), + max_frame_size(65535), + isOpen(false), + impl(new ConnectionImpl(c)) +{} + +Connection::~Connection(){ } + +void Connection::open( + const std::string& host, int port, + const std::string& uid, const std::string& pwd, const std::string& vhost) +{ + if (isOpen) + throw Exception(QPID_MSG("Channel object is already open")); + + impl->open(host, port, uid, pwd, vhost); + isOpen = true; +} + +void Connection::openChannel(Channel& channel) { + channel.open(newSession(ASYNC)); +} + +Session Connection::newSession(SynchronousMode sync, + uint32_t detachedLifetime) +{ + shared_ptr<SessionCore> core( + new SessionCore(impl, ++channelIdCounter, max_frame_size)); + core->setSync(sync); + impl->addSession(core); + core->open(detachedLifetime); + return Session(core); +} + +void Connection::resume(Session& session) { + session.impl->setChannel(++channelIdCounter); + impl->addSession(session.impl); + session.impl->resume(impl); +} + +void Connection::close() { + impl->close(); +} + +}} // namespace qpid::client diff --git a/qpid/cpp/src/qpid/client/Connection.h b/qpid/cpp/src/qpid/client/Connection.h new file mode 100644 index 0000000000..81d9b972b6 --- /dev/null +++ b/qpid/cpp/src/qpid/client/Connection.h @@ -0,0 +1,149 @@ +#ifndef _client_Connection_ +#define _client_Connection_ + +/* + * + * 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. + * + */ +#include <map> +#include <string> +#include "Channel.h" +#include "ConnectionImpl.h" +#include "qpid/client/Session.h" +#include "qpid/framing/AMQP_HighestVersion.h" +#include "qpid/framing/Uuid.h" + +namespace qpid { + +/** + * The client namespace contains all classes that make up a client + * implementation of the AMQP protocol. The key classes that form + * the basis of the client API to be used by applications are + * Connection and Channel. + */ +namespace client { + +/** + * \defgroup clientapi Application API for an AMQP client + */ + +/** + * Represents a connection to an AMQP broker. All communication is + * initiated by establishing a connection, then opening one or + * more Channels over that connection. + * + * \ingroup clientapi + */ +class Connection +{ + framing::ChannelId channelIdCounter; + framing::ProtocolVersion version; + const uint32_t max_frame_size; + bool isOpen; + bool debug; + + protected: + boost::shared_ptr<ConnectionImpl> impl; + + public: + /** + * Creates a connection object, but does not open the + * connection. + * + * @param _version the version of the protocol to connect with + * + * @param debug turns on tracing for the connection + * (i.e. prints details of the frames sent and received to std + * out). Optional and defaults to false. + * + * @param max_frame_size the maximum frame size that the + * client will accept. Optional and defaults to 65535. + */ + Connection(bool debug = false, uint32_t max_frame_size = 65535, + framing::ProtocolVersion=framing::highestProtocolVersion); + Connection(boost::shared_ptr<Connector>); + ~Connection(); + + /** + * Opens a connection to a broker. + * + * @param host the host on which the broker is running + * + * @param port the port on the which the broker is listening + * + * @param uid the userid to connect with + * + * @param pwd the password to connect with (currently SASL + * PLAIN is the only authentication method supported so this + * is sent in clear text) + * + * @param virtualhost the AMQP virtual host to use (virtual + * hosts, where implemented(!), provide namespace partitioning + * within a single broker). + */ + void open(const std::string& host, int port = 5672, + const std::string& uid = "guest", + const std::string& pwd = "guest", + const std::string& virtualhost = "/"); + + /** + * Close the connection + * + * Any further use of this connection (without reopening it) will + * not succeed. + */ + void close(); + + /** + * Associate a Channel with this connection and open it for use. + * + * In AMQP channels are like multi-plexed 'sessions' of work over + * a connection. Almost all the interaction with AMQP is done over + * a channel. + * + * @param connection the connection object to be associated with + * the channel. Call Channel::close() to close the channel. + */ + void openChannel(Channel&); + + /** + * Create a new session on this connection. Sessions allow + * multiple streams of work to be multiplexed over the same + * connection. + * + *@param detachedLifetime: A session may be detached from its + * channel, either by calling Session::suspend() or because of a + * network failure. The session state is perserved for + * detachedLifetime seconds to allow a call to resume(). After + * that the broker may discard the session state. Default is 0, + * meaning the session cannot be resumed. + */ + Session newSession(SynchronousMode sync, uint32_t detachedLifetime=0); + + /** + * Resume a suspendded session. A session may be resumed + * on a different connection to the one that created it. + */ + void resume(Session& session); +}; + +}} // namespace qpid::client + + +#endif diff --git a/qpid/cpp/src/qpid/client/ConnectionHandler.cpp b/qpid/cpp/src/qpid/client/ConnectionHandler.cpp new file mode 100644 index 0000000000..e1c50c14fc --- /dev/null +++ b/qpid/cpp/src/qpid/client/ConnectionHandler.cpp @@ -0,0 +1,209 @@ +/* + * + * 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. + * + */ + +#include "ConnectionHandler.h" +#include "qpid/log/Statement.h" +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/AMQP_HighestVersion.h" +#include "qpid/framing/all_method_bodies.h" + +using namespace qpid::client; +using namespace qpid::framing; +using namespace boost; + +namespace { +const std::string OK("OK"); +} + +ConnectionHandler::ConnectionHandler() + : StateManager(NOT_STARTED) +{ + + mechanism = "PLAIN"; + locale = "en_US"; + heartbeat = 0; + maxChannels = 32767; + maxFrameSize = 65535; + insist = true; + version = framing::highestProtocolVersion; + + ESTABLISHED.insert(FAILED); + ESTABLISHED.insert(OPEN); +} + +void ConnectionHandler::incoming(AMQFrame& frame) +{ + if (getState() == CLOSED) { + throw Exception("Connection is closed."); + } + + AMQBody* body = frame.getBody(); + if (frame.getChannel() == 0) { + if (body->getMethod()) { + handle(body->getMethod()); + } else { + error(503, "Cannot send content on channel zero."); + } + } else { + switch(getState()) { + case OPEN: + try { + in(frame); + }catch(ConnectionException& e){ + error(e.code, e.what(), body); + }catch(std::exception& e){ + error(541/*internal error*/, e.what(), body); + } + break; + case CLOSING: + QPID_LOG(warning, "Received frame on non-zero channel while closing connection; frame ignored."); + break; + default: + //must be in connection initialisation: + fail("Cannot receive frames on non-zero channel until connection is established."); + } + } +} + +void ConnectionHandler::outgoing(AMQFrame& frame) +{ + if (getState() == OPEN) { + out(frame); + } else { + throw Exception("Connection is not open."); + } +} + +void ConnectionHandler::waitForOpen() +{ + waitFor(ESTABLISHED); + if (getState() == FAILED) { + throw Exception("Failed to establish connection."); + } +} + +void ConnectionHandler::close() +{ + switch (getState()) { + case NEGOTIATING: + case OPENING: + setState(FAILED); + break; + case OPEN: + setState(CLOSING); + send(ConnectionCloseBody(version, 200, OK, 0, 0)); + waitFor(CLOSED); + break; + // Nothing to do for CLOSING, CLOSED, FAILED or NOT_STARTED + } +} + +void ConnectionHandler::send(const framing::AMQBody& body) +{ + AMQFrame f(body); + out(f); +} + +void ConnectionHandler::error(uint16_t code, const std::string& message, uint16_t classId, uint16_t methodId) +{ + setState(CLOSING); + send(ConnectionCloseBody(version, code, message, classId, methodId)); +} + +void ConnectionHandler::error(uint16_t code, const std::string& message, AMQBody* body) +{ + if (onError) + onError(code, message); + AMQMethodBody* method = body->getMethod(); + if (method) + error(code, message, method->amqpClassId(), method->amqpMethodId()); + else + error(code, message); +} + + +void ConnectionHandler::fail(const std::string& message) +{ + QPID_LOG(warning, message); + setState(FAILED); +} + +void ConnectionHandler::handle(AMQMethodBody* method) +{ + switch (getState()) { + case NOT_STARTED: + if (method->isA<ConnectionStartBody>()) { + setState(NEGOTIATING); + string response = ((char)0) + uid + ((char)0) + pwd; + send(ConnectionStartOkBody(version, properties, mechanism, response, locale)); + } else { + fail("Bad method sequence, expected connection-start."); + } + break; + case NEGOTIATING: + if (method->isA<ConnectionTuneBody>()) { + ConnectionTuneBody* proposal=polymorphic_downcast<ConnectionTuneBody*>(method); + heartbeat = proposal->getHeartbeat(); + maxChannels = proposal->getChannelMax(); + send(ConnectionTuneOkBody(version, maxChannels, maxFrameSize, heartbeat)); + setState(OPENING); + send(ConnectionOpenBody(version, vhost, capabilities, insist)); + //TODO: support for further security challenges + //} else if (method->isA<ConnectionSecureBody>()) { + } else { + fail("Unexpected method sequence, expected connection-tune."); + } + break; + case OPENING: + if (method->isA<ConnectionOpenOkBody>()) { + setState(OPEN); + //TODO: support for redirection + //} else if (method->isA<ConnectionRedirectBody>()) { + } else { + fail("Unexpected method sequence, expected connection-open-ok."); + } + break; + case OPEN: + if (method->isA<ConnectionCloseBody>()) { + send(ConnectionCloseOkBody(version)); + setState(CLOSED); + ConnectionCloseBody* c=polymorphic_downcast<ConnectionCloseBody*>(method); + QPID_LOG(warning, "Broker closed connection: " << c->getReplyCode() + << ", " << c->getReplyText()); + if (onError) { + onError(c->getReplyCode(), c->getReplyText()); + } + } else { + error(503, "Unexpected method on channel zero.", method->amqpClassId(), method->amqpMethodId()); + } + break; + case CLOSING: + if (method->isA<ConnectionCloseOkBody>()) { + if (onClose) { + onClose(); + } + setState(CLOSED); + } else { + QPID_LOG(warning, "Received frame on channel zero while closing connection; frame ignored."); + } + break; + } +} diff --git a/qpid/cpp/src/qpid/client/ConnectionHandler.h b/qpid/cpp/src/qpid/client/ConnectionHandler.h new file mode 100644 index 0000000000..bb50495c06 --- /dev/null +++ b/qpid/cpp/src/qpid/client/ConnectionHandler.h @@ -0,0 +1,85 @@ +/* + * + * 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. + * + */ +#ifndef _ConnectionHandler_ +#define _ConnectionHandler_ + +#include "Connector.h" +#include "StateManager.h" +#include "ChainableFrameHandler.h" +#include "qpid/framing/InputHandler.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/AMQMethodBody.h" + +namespace qpid { +namespace client { + +struct ConnectionProperties +{ + std::string uid; + std::string pwd; + std::string vhost; + framing::FieldTable properties; + std::string mechanism; + std::string locale; + std::string capabilities; + uint16_t heartbeat; + uint16_t maxChannels; + uint64_t maxFrameSize; + bool insist; + framing::ProtocolVersion version; +}; + +class ConnectionHandler : private StateManager, + public ConnectionProperties, + public ChainableFrameHandler, + public framing::InputHandler +{ + enum STATES {NOT_STARTED, NEGOTIATING, OPENING, OPEN, CLOSING, CLOSED, FAILED}; + std::set<int> ESTABLISHED; + + void handle(framing::AMQMethodBody* method); + void send(const framing::AMQBody& body); + void error(uint16_t code, const std::string& message, uint16_t classId = 0, uint16_t methodId = 0); + void error(uint16_t code, const std::string& message, framing::AMQBody* body); + +public: + using InputHandler::handle; + typedef boost::function<void()> CloseListener; + typedef boost::function<void(uint16_t, const std::string&)> ErrorListener; + + ConnectionHandler(); + + void received(framing::AMQFrame& f) { incoming(f); } + + void incoming(framing::AMQFrame& frame); + void outgoing(framing::AMQFrame& frame); + + void waitForOpen(); + void close(); + void fail(const std::string& message); + + CloseListener onClose; + ErrorListener onError; +}; + +}} + +#endif diff --git a/qpid/cpp/src/qpid/client/ConnectionImpl.cpp b/qpid/cpp/src/qpid/client/ConnectionImpl.cpp new file mode 100644 index 0000000000..b248de8744 --- /dev/null +++ b/qpid/cpp/src/qpid/client/ConnectionImpl.cpp @@ -0,0 +1,158 @@ +/* + * + * 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. + * + */ +#include "qpid/log/Statement.h" +#include "qpid/framing/constants.h" +#include "qpid/framing/reply_exceptions.h" + +#include "ConnectionImpl.h" +#include "SessionCore.h" + +#include <boost/bind.hpp> +#include <boost/format.hpp> + +using namespace qpid::client; +using namespace qpid::framing; +using namespace qpid::sys; + +ConnectionImpl::ConnectionImpl(boost::shared_ptr<Connector> c) + : connector(c), isClosed(false), isClosing(false) +{ + handler.in = boost::bind(&ConnectionImpl::incoming, this, _1); + handler.out = boost::bind(&Connector::send, connector, _1); + handler.onClose = boost::bind(&ConnectionImpl::closed, this, + REPLY_SUCCESS, std::string()); + handler.onError = boost::bind(&ConnectionImpl::closed, this, _1, _2); + connector->setInputHandler(&handler); + connector->setTimeoutHandler(this); + connector->setShutdownHandler(this); +} + +ConnectionImpl::~ConnectionImpl() { + // Important to close the connector first, to ensure the + // connector thread does not call on us while the destructor + // is running. + connector->close(); +} + +void ConnectionImpl::addSession(const boost::shared_ptr<SessionCore>& session) +{ + Mutex::ScopedLock l(lock); + boost::weak_ptr<SessionCore>& s = sessions[session->getChannel()]; + if (s.lock()) throw ChannelBusyException(); + s = session; +} + +void ConnectionImpl::handle(framing::AMQFrame& frame) +{ + handler.outgoing(frame); +} + +void ConnectionImpl::incoming(framing::AMQFrame& frame) +{ + boost::shared_ptr<SessionCore> s; + { + Mutex::ScopedLock l(lock); + s = sessions[frame.getChannel()].lock(); + } + if (!s) + throw ChannelErrorException(QPID_MSG("Invalid channel: " << frame.getChannel())); + s->in(frame); +} + +void ConnectionImpl::open(const std::string& host, int port, + const std::string& uid, const std::string& pwd, + const std::string& vhost) +{ + //TODO: better management of connection properties + handler.uid = uid; + handler.pwd = pwd; + handler.vhost = vhost; + + QPID_LOG(info, "Connecting to " << host << ":" << port); + connector->connect(host, port); + connector->init(); + handler.waitForOpen(); +} + +void ConnectionImpl::idleIn() +{ + close(); +} + +void ConnectionImpl::idleOut() +{ + AMQFrame frame(in_place<AMQHeartbeatBody>()); + connector->send(frame); +} + +void ConnectionImpl::close() +{ + Mutex::ScopedLock l(lock); + if (isClosing || isClosed) return; + isClosing = true; + { + Mutex::ScopedUnlock u(lock); + handler.close(); + } + closed(REPLY_SUCCESS, "Closed by client"); +} + +// Set closed flags and erase the sessions map, but keep the contents +// so sessions can be updated outside the lock. +ConnectionImpl::SessionVector ConnectionImpl::closeInternal(const Mutex::ScopedLock&) { + isClosed = true; + connector->close(); + SessionVector save; + for (SessionMap::iterator i = sessions.begin(); i != sessions.end(); ++i) { + boost::shared_ptr<SessionCore> s = i->second.lock(); + if (s) save.push_back(s); + } + sessions.clear(); + return save; +} + +void ConnectionImpl::closed(uint16_t code, const std::string& text) +{ + Mutex::ScopedLock l(lock); + if (isClosed) return; + SessionVector save(closeInternal(l)); + Mutex::ScopedUnlock u(lock); + std::for_each(save.begin(), save.end(), boost::bind(&SessionCore::connectionClosed, _1, code, text)); +} + +static const std::string CONN_CLOSED("Connection closed by broker"); + +void ConnectionImpl::shutdown() +{ + Mutex::ScopedLock l(lock); + if (isClosed) return; + SessionVector save(closeInternal(l)); + handler.fail(CONN_CLOSED); + Mutex::ScopedUnlock u(lock); + std::for_each(save.begin(), save.end(), + boost::bind(&SessionCore::connectionBroke, _1, INTERNAL_ERROR, CONN_CLOSED)); +} + +void ConnectionImpl::erase(uint16_t ch) { + Mutex::ScopedLock l(lock); + sessions.erase(ch); +} + diff --git a/qpid/cpp/src/qpid/client/ConnectionImpl.h b/qpid/cpp/src/qpid/client/ConnectionImpl.h new file mode 100644 index 0000000000..bf8226a776 --- /dev/null +++ b/qpid/cpp/src/qpid/client/ConnectionImpl.h @@ -0,0 +1,87 @@ +/* + * + * 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. + * + */ + +#ifndef _ConnectionImpl_ +#define _ConnectionImpl_ + +#include <map> +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include "qpid/framing/FrameHandler.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/ShutdownHandler.h" +#include "qpid/sys/TimeoutHandler.h" +#include "ConnectionHandler.h" +#include "Connector.h" + +namespace qpid { +namespace client { + +class SessionCore; + +class ConnectionImpl : public framing::FrameHandler, + public sys::TimeoutHandler, + public sys::ShutdownHandler + +{ + typedef std::map<uint16_t, boost::weak_ptr<SessionCore> > SessionMap; + typedef std::vector<boost::shared_ptr<SessionCore> > SessionVector; + + SessionMap sessions; + ConnectionHandler handler; + boost::shared_ptr<Connector> connector; + framing::ProtocolVersion version; + sys::Mutex lock; + bool isClosed; + bool isClosing; + + template <class F> void detachAll(const F&); + + SessionVector closeInternal(const sys::Mutex::ScopedLock&); + void incoming(framing::AMQFrame& frame); + void closed(uint16_t, const std::string&); + void idleOut(); + void idleIn(); + void shutdown(); + bool setClosing(); + + public: + typedef boost::shared_ptr<ConnectionImpl> shared_ptr; + + ConnectionImpl(boost::shared_ptr<Connector> c); + ~ConnectionImpl(); + + void addSession(const boost::shared_ptr<SessionCore>&); + + void open(const std::string& host, int port = 5672, + const std::string& uid = "guest", + const std::string& pwd = "guest", + const std::string& virtualhost = "/"); + void close(); + void handle(framing::AMQFrame& frame); + void erase(uint16_t channel); + boost::shared_ptr<Connector> getConnector() { return connector; } +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/client/Connector.cpp b/qpid/cpp/src/qpid/client/Connector.cpp new file mode 100644 index 0000000000..a0be05fbbc --- /dev/null +++ b/qpid/cpp/src/qpid/client/Connector.cpp @@ -0,0 +1,297 @@ +/* + * + * 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. + * + */ +#include <iostream> +#include "qpid/log/Statement.h" +#include "qpid/sys/Time.h" +#include "qpid/framing/AMQFrame.h" +#include "Connector.h" + +#include "qpid/sys/AsynchIO.h" +#include "qpid/sys/Dispatcher.h" +#include "qpid/sys/Poller.h" +#include "qpid/Msg.h" +#include <boost/bind.hpp> +#include <boost/format.hpp> + +namespace qpid { +namespace client { + +using namespace qpid::sys; +using namespace qpid::framing; +using boost::format; +using boost::str; + +Connector::Connector( + ProtocolVersion ver, bool _debug, uint32_t buffer_size +) : debug(_debug), + receive_buffer_size(buffer_size), + send_buffer_size(buffer_size), + version(ver), + closed(true), + joined(true), + timeout(0), + idleIn(0), idleOut(0), + timeoutHandler(0), + shutdownHandler(0), + aio(0) +{} + +Connector::~Connector() { + close(); +} + +void Connector::connect(const std::string& host, int port){ + Mutex::ScopedLock l(closedLock); + assert(closed); + socket.connect(host, port); + identifier=str(format("[%1% %2%]") % socket.getLocalPort() % socket.getPeerAddress()); + closed = false; + poller = Poller::shared_ptr(new Poller); + aio = new AsynchIO(socket, + boost::bind(&Connector::readbuff, this, _1, _2), + boost::bind(&Connector::eof, this, _1), + boost::bind(&Connector::eof, this, _1), + 0, // closed + 0, // nobuffs + boost::bind(&Connector::writebuff, this, _1)); + writer.setAio(aio); +} + +void Connector::init(){ + Mutex::ScopedLock l(closedLock); + assert(joined); + ProtocolInitiation init(version); + writeDataBlock(init); + joined = false; + receiver = Thread(this); +} + +bool Connector::closeInternal() { + Mutex::ScopedLock l(closedLock); + bool ret = !closed; + if (!closed) { + closed = true; + poller->shutdown(); + } + if (!joined && receiver.id() != Thread::current().id()) { + joined = true; + Mutex::ScopedUnlock u(closedLock); + receiver.join(); + } + return ret; +} + +void Connector::close() { + closeInternal(); +} + +void Connector::setInputHandler(InputHandler* handler){ + input = handler; +} + +void Connector::setShutdownHandler(ShutdownHandler* handler){ + shutdownHandler = handler; +} + +OutputHandler* Connector::getOutputHandler(){ + return this; +} + +void Connector::send(AMQFrame& frame) { + writer.handle(frame); +} + +void Connector::handleClosed() { + if (closeInternal() && shutdownHandler) + shutdownHandler->shutdown(); +} + +// TODO: astitcher 20070908: This version of the code can never time out, so the idle processing +// can never be called. The timeut processing needs to be added into the underlying Dispatcher code +// +// TODO: astitcher 20070908: EOF is dealt with separately now via a callback to eof +void Connector::checkIdle(ssize_t status){ + if(timeoutHandler){ + AbsTime t = now(); + if(status == Socket::SOCKET_TIMEOUT) { + if(idleIn && (Duration(lastIn, t) > idleIn)){ + timeoutHandler->idleIn(); + } + } + else if(status == 0 || status == Socket::SOCKET_EOF) { + handleClosed(); + } + else { + lastIn = t; + } + if(idleOut && (Duration(lastOut, t) > idleOut)){ + timeoutHandler->idleOut(); + } + } +} + +void Connector::setReadTimeout(uint16_t t){ + idleIn = t * TIME_SEC;//t is in secs + if(idleIn && (!timeout || idleIn < timeout)){ + timeout = idleIn; + setSocketTimeout(); + } + +} + +void Connector::setWriteTimeout(uint16_t t){ + idleOut = t * TIME_SEC;//t is in secs + if(idleOut && (!timeout || idleOut < timeout)){ + timeout = idleOut; + setSocketTimeout(); + } +} + +void Connector::setSocketTimeout(){ + socket.setTimeout(timeout); +} + +void Connector::setTimeoutHandler(TimeoutHandler* handler){ + timeoutHandler = handler; +} + +struct Connector::Buff : public AsynchIO::BufferBase { + Buff() : AsynchIO::BufferBase(new char[65536], 65536) {} + ~Buff() { delete [] bytes;} +}; + +Connector::Writer::Writer() : aio(0), buffer(0), lastEof(0) +{ +} + +Connector::Writer::~Writer() { delete buffer; } + +void Connector::Writer::setAio(sys::AsynchIO* a) { + Mutex::ScopedLock l(lock); + aio = a; + newBuffer(l); + identifier = str(format("[%1% %2%]") % aio->getSocket().getLocalPort() % aio->getSocket().getPeerAddress()); +} + +void Connector::Writer::handle(framing::AMQFrame& frame) { + Mutex::ScopedLock l(lock); + frames.push_back(frame); + if (frame.getEof()) { + lastEof = frames.size(); + aio->notifyPendingWrite(); + } + QPID_LOG(trace, "SENT " << identifier << ": " << frame); +} + +void Connector::Writer::writeOne(const Mutex::ScopedLock& l) { + assert(buffer); + QPID_LOG(trace, "Write buffer " << encode.getPosition() + << " bytes " << framesEncoded << " frames "); + framesEncoded = 0; + + buffer->dataStart = 0; + buffer->dataCount = encode.getPosition(); + aio->queueWrite(buffer); + newBuffer(l); +} + +void Connector::Writer::newBuffer(const Mutex::ScopedLock&) { + buffer = aio->getQueuedBuffer(); + if (!buffer) buffer = new Buff(); + encode = framing::Buffer(buffer->bytes, buffer->byteCount); + framesEncoded = 0; +} + +// Called in IO thread. +void Connector::Writer::write(sys::AsynchIO&) { + Mutex::ScopedLock l(lock); + assert(buffer); + for (size_t i = 0; i < lastEof; ++i) { + AMQFrame& frame = frames[i]; + if (frame.size() > encode.available()) writeOne(l); + assert(frame.size() <= encode.available()); + frame.encode(encode); + ++framesEncoded; + } + frames.erase(frames.begin(), frames.begin()+lastEof); + lastEof = 0; + if (encode.getPosition() > 0) writeOne(l); +} + +void Connector::readbuff(AsynchIO& aio, AsynchIO::BufferBase* buff) { + framing::Buffer in(buff->bytes+buff->dataStart, buff->dataCount); + + AMQFrame frame; + while(frame.decode(in)){ + QPID_LOG(trace, "RECV " << identifier << ": " << frame); + input->received(frame); + } + // TODO: unreading needs to go away, and when we can cope + // with multiple sub-buffers in the general buffer scheme, it will + if (in.available() != 0) { + // Adjust buffer for used bytes and then "unread them" + buff->dataStart += buff->dataCount-in.available(); + buff->dataCount = in.available(); + aio.unread(buff); + } else { + // Give whole buffer back to aio subsystem + aio.queueReadBuffer(buff); + } +} + +void Connector::writebuff(AsynchIO& aio_) { + writer.write(aio_); +} + +void Connector::writeDataBlock(const AMQDataBlock& data) { + AsynchIO::BufferBase* buff = new Buff; + framing::Buffer out(buff->bytes, buff->byteCount); + data.encode(out); + buff->dataCount = data.size(); + aio->queueWrite(buff); +} + +void Connector::eof(AsynchIO&) { + handleClosed(); +} + +// TODO: astitcher 20070908 This version of the code can never time out, so the idle processing +// will never be called +void Connector::run(){ + try { + Dispatcher d(poller); + + for (int i = 0; i < 32; i++) { + aio->queueReadBuffer(new Buff); + } + + aio->start(poller); + d.run(); + aio->queueForDeletion(); + socket.close(); + } catch (const std::exception& e) { + QPID_LOG(error, e.what()); + handleClosed(); + } +} + + +}} // namespace qpid::client diff --git a/qpid/cpp/src/qpid/client/Connector.h b/qpid/cpp/src/qpid/client/Connector.h new file mode 100644 index 0000000000..ffddbfd1be --- /dev/null +++ b/qpid/cpp/src/qpid/client/Connector.h @@ -0,0 +1,143 @@ +/* + * + * 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. + * + */ +#ifndef _Connector_ +#define _Connector_ + + +#include "qpid/framing/InputHandler.h" +#include "qpid/framing/OutputHandler.h" +#include "qpid/framing/InitiationHandler.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/sys/ShutdownHandler.h" +#include "qpid/sys/TimeoutHandler.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Monitor.h" +#include "qpid/sys/Socket.h" +#include "qpid/sys/Time.h" +#include "qpid/sys/AsynchIO.h" + +#include <queue> + +namespace qpid { + +namespace client { + +class Connector : public framing::OutputHandler, + private sys::Runnable +{ + struct Buff; + + /** Batch up frames for writing to aio. */ + class Writer : public framing::FrameHandler { + typedef sys::AsynchIO::BufferBase BufferBase; + typedef std::vector<framing::AMQFrame> Frames; + + sys::Mutex lock; + sys::AsynchIO* aio; + BufferBase* buffer; + Frames frames; + size_t lastEof; // Position after last EOF in frames + framing::Buffer encode; + size_t framesEncoded; + std::string identifier; + + void writeOne(const sys::Mutex::ScopedLock&); + void newBuffer(const sys::Mutex::ScopedLock&); + + public: + + Writer(); + ~Writer(); + void setAio(sys::AsynchIO*); + void handle(framing::AMQFrame&); + void write(sys::AsynchIO&); + }; + + const bool debug; + const int receive_buffer_size; + const int send_buffer_size; + framing::ProtocolVersion version; + + sys::Mutex closedLock; + bool closed; + bool joined; + + sys::AbsTime lastIn; + sys::AbsTime lastOut; + sys::Duration timeout; + sys::Duration idleIn; + sys::Duration idleOut; + + sys::TimeoutHandler* timeoutHandler; + sys::ShutdownHandler* shutdownHandler; + framing::InputHandler* input; + framing::InitiationHandler* initialiser; + framing::OutputHandler* output; + + Writer writer; + + sys::Thread receiver; + + sys::Socket socket; + + sys::AsynchIO* aio; + sys::Poller::shared_ptr poller; + + void checkIdle(ssize_t status); + void setSocketTimeout(); + + void run(); + void handleClosed(); + bool closeInternal(); + + void readbuff(qpid::sys::AsynchIO&, qpid::sys::AsynchIO::BufferBase*); + void writebuff(qpid::sys::AsynchIO&); + void writeDataBlock(const framing::AMQDataBlock& data); + void eof(qpid::sys::AsynchIO&); + + std::string identifier; + + friend class Channel; + + public: + Connector(framing::ProtocolVersion pVersion, + bool debug = false, uint32_t buffer_size = 1024); + virtual ~Connector(); + virtual void connect(const std::string& host, int port); + virtual void init(); + virtual void close(); + virtual void setInputHandler(framing::InputHandler* handler); + virtual void setTimeoutHandler(sys::TimeoutHandler* handler); + virtual void setShutdownHandler(sys::ShutdownHandler* handler); + virtual sys::ShutdownHandler* getShutdownHandler() { return shutdownHandler; } + virtual framing::OutputHandler* getOutputHandler(); + virtual void send(framing::AMQFrame& frame); + virtual void setReadTimeout(uint16_t timeout); + virtual void setWriteTimeout(uint16_t timeout); + const std::string& getIdentifier() const { return identifier; } +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/client/Correlator.cpp b/qpid/cpp/src/qpid/client/Correlator.cpp new file mode 100644 index 0000000000..f30c92b992 --- /dev/null +++ b/qpid/cpp/src/qpid/client/Correlator.cpp @@ -0,0 +1,45 @@ +/* + * + * 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. + * + */ + +#include "Correlator.h" + +using qpid::client::Correlator; +using namespace qpid::framing; +using namespace boost; + +bool Correlator::receive(const AMQMethodBody* response) +{ + if (listeners.empty()) { + return false; + } else { + Listener l = listeners.front(); + if (l) l(response); + listeners.pop(); + return true; + } +} + +void Correlator::listen(Listener l) +{ + listeners.push(l); +} + + diff --git a/qpid/cpp/src/qpid/client/Correlator.h b/qpid/cpp/src/qpid/client/Correlator.h new file mode 100644 index 0000000000..45b22fb2fe --- /dev/null +++ b/qpid/cpp/src/qpid/client/Correlator.h @@ -0,0 +1,52 @@ +/* + * + * 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. + * + */ + +#include <memory> +#include <queue> +#include <set> +#include <boost/function.hpp> +#include "qpid/framing/AMQMethodBody.h" +#include "qpid/sys/Monitor.h" + +#ifndef _Correlator_ +#define _Correlator_ + +namespace qpid { +namespace client { + + +class Correlator +{ +public: + typedef boost::function<void(const framing::AMQMethodBody*)> Listener; + + bool receive(const framing::AMQMethodBody*); + void listen(Listener l); + +private: + std::queue<Listener> listeners; +}; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/client/Demux.cpp b/qpid/cpp/src/qpid/client/Demux.cpp new file mode 100644 index 0000000000..cb9372cee7 --- /dev/null +++ b/qpid/cpp/src/qpid/client/Demux.cpp @@ -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. + * + */ + +#include "Demux.h" +#include "qpid/Exception.h" +#include "qpid/framing/MessageTransferBody.h" + +#include <iostream> + +namespace qpid { +namespace client { + +ByTransferDest::ByTransferDest(const std::string& d) : dest(d) {} +bool ByTransferDest::operator()(const framing::FrameSet& frameset) const +{ + return frameset.isA<framing::MessageTransferBody>() && + frameset.as<framing::MessageTransferBody>()->getDestination() == dest; +} + +ScopedDivert::ScopedDivert(const std::string& _dest, Demux& _demuxer) : dest(_dest), demuxer(_demuxer) +{ + queue = demuxer.add(dest, ByTransferDest(dest)); +} + +ScopedDivert::~ScopedDivert() +{ + demuxer.remove(dest); +} + +Demux::Demux() : defaultQueue(new Queue()) {} + +Demux::~Demux() { close(); } + +Demux::QueuePtr ScopedDivert::getQueue() +{ + return queue; +} + +void Demux::handle(framing::FrameSet::shared_ptr frameset) +{ + sys::Mutex::ScopedLock l(lock); + bool matched = false; + for (iterator i = records.begin(); i != records.end() && !matched; i++) { + if (i->condition && i->condition(*frameset)) { + matched = true; + i->queue->push(frameset); + } + } + if (!matched) { + defaultQueue->push(frameset); + } +} + +void Demux::close() +{ + sys::Mutex::ScopedLock l(lock); + for (iterator i = records.begin(); i != records.end(); i++) { + i->queue->close(); + } + defaultQueue->close(); +} + +void Demux::open() +{ + sys::Mutex::ScopedLock l(lock); + for (iterator i = records.begin(); i != records.end(); i++) { + i->queue->open(); + } + defaultQueue->open(); +} + +Demux::QueuePtr Demux::add(const std::string& name, Condition condition) +{ + sys::Mutex::ScopedLock l(lock); + iterator i = std::find_if(records.begin(), records.end(), Find(name)); + if (i == records.end()) { + Record r(name, condition); + records.push_back(r); + return r.queue; + } else { + throw Exception("Queue already exists for " + name); + } +} + +void Demux::remove(const std::string& name) +{ + sys::Mutex::ScopedLock l(lock); + records.remove_if(Find(name)); +} + +Demux::QueuePtr Demux::get(const std::string& name) +{ + sys::Mutex::ScopedLock l(lock); + iterator i = std::find_if(records.begin(), records.end(), Find(name)); + if (i == records.end()) { + throw Exception("No queue for " + name); + } + return i->queue; +} + +Demux::QueuePtr Demux::getDefault() +{ + return defaultQueue; +} + +Demux::Find::Find(const std::string& n) : name(n) {} + +bool Demux::Find::operator()(const Record& record) const +{ + return record.name == name; +} + +}} + diff --git a/qpid/cpp/src/qpid/client/Demux.h b/qpid/cpp/src/qpid/client/Demux.h new file mode 100644 index 0000000000..dce24223f2 --- /dev/null +++ b/qpid/cpp/src/qpid/client/Demux.h @@ -0,0 +1,100 @@ +/* + * + * 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. + * + */ + +#include <list> +#include <boost/function.hpp> +#include <boost/shared_ptr.hpp> +#include "qpid/framing/FrameSet.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/BlockingQueue.h" + +#ifndef _Demux_ +#define _Demux_ + +namespace qpid { +namespace client { + +class ByTransferDest +{ + const std::string dest; +public: + ByTransferDest(const std::string& dest); + bool operator()(const framing::FrameSet& frameset) const; +}; + +class Demux +{ +public: + typedef boost::function<bool(const framing::FrameSet&)> Condition; + typedef sys::BlockingQueue<framing::FrameSet::shared_ptr> Queue; + typedef boost::shared_ptr<Queue> QueuePtr; + + Demux(); + ~Demux(); + + void handle(framing::FrameSet::shared_ptr); + void close(); + void open(); + + QueuePtr add(const std::string& name, Condition); + void remove(const std::string& name); + QueuePtr get(const std::string& name); + QueuePtr getDefault(); + +private: + struct Record + { + const std::string name; + Condition condition; + QueuePtr queue; + + Record(const std::string& n, Condition c) : name(n), condition(c), queue(new Queue()) {} + }; + + sys::Mutex lock; + std::list<Record> records; + QueuePtr defaultQueue; + + typedef std::list<Record>::iterator iterator; + + struct Find + { + const std::string name; + Find(const std::string& name); + bool operator()(const Record& record) const; + }; +}; + +class ScopedDivert +{ + const std::string dest; + Demux& demuxer; + Demux::QueuePtr queue; +public: + ScopedDivert(const std::string& dest, Demux& demuxer); + ~ScopedDivert(); + Demux::QueuePtr getQueue(); +}; + +}} // namespace qpid::client + + +#endif diff --git a/qpid/cpp/src/qpid/client/Dispatcher.cpp b/qpid/cpp/src/qpid/client/Dispatcher.cpp new file mode 100644 index 0000000000..2484dabf1f --- /dev/null +++ b/qpid/cpp/src/qpid/client/Dispatcher.cpp @@ -0,0 +1,142 @@ +/* + * + * 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. + * + */ +#include "Dispatcher.h" + +#include "qpid/framing/FrameSet.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/log/Statement.h" +#include "qpid/sys/BlockingQueue.h" +#include "Message.h" + +#include <boost/state_saver.hpp> + +using qpid::framing::FrameSet; +using qpid::framing::MessageTransferBody; +using qpid::sys::Mutex; +using qpid::sys::ScopedLock; +using qpid::sys::Thread; + +namespace qpid { +namespace client { + +Subscriber::Subscriber(Session& s, MessageListener* l, AckPolicy a) : session(s), listener(l), autoAck(a) {} + +void Subscriber::received(Message& msg) +{ + if (listener) { + listener->received(msg); + autoAck.ack(msg); + } +} + +Dispatcher::Dispatcher(Session& s, const std::string& q) + : session(s), running(false), autoStop(true) +{ + queue = q.empty() ? + session.getExecution().getDemux().getDefault() : + session.getExecution().getDemux().get(q); +} + +void Dispatcher::start() +{ + worker = Thread(this); +} + +void Dispatcher::run() +{ + Mutex::ScopedLock l(lock); + if (running) + throw Exception("Dispatcher is already running."); + boost::state_saver<bool> reset(running); // Reset to false on exit. + running = true; + try { + while (!queue->isClosed()) { + Mutex::ScopedUnlock u(lock); + FrameSet::shared_ptr content = queue->pop(); + if (content->isA<MessageTransferBody>()) { + Message msg(*content, session); + Subscriber::shared_ptr listener = find(msg.getDestination()); + if (!listener) { + QPID_LOG(error, "No listener found for destination " << msg.getDestination()); + } else { + assert(listener); + listener->received(msg); + } + } else { + if (handler.get()) { + handler->handle(*content); + } else { + QPID_LOG(error, "No handler found for " << *(content->getMethod())); + } + } + } + } catch (const ClosedException&) { + //ignore it and return + } +} + +void Dispatcher::stop() +{ + ScopedLock<Mutex> l(lock); + queue->close(); // Will interrupt thread blocked in pop() +} + +void Dispatcher::setAutoStop(bool b) +{ + ScopedLock<Mutex> l(lock); + autoStop = b; +} + +Subscriber::shared_ptr Dispatcher::find(const std::string& name) +{ + ScopedLock<Mutex> l(lock); + Listeners::iterator i = listeners.find(name); + if (i == listeners.end()) { + return defaultListener; + } + return i->second; +} + +void Dispatcher::listen( + MessageListener* listener, AckPolicy autoAck +) +{ + ScopedLock<Mutex> l(lock); + defaultListener = Subscriber::shared_ptr( + new Subscriber(session, listener, autoAck)); +} + +void Dispatcher::listen(const std::string& destination, MessageListener* listener, AckPolicy autoAck) +{ + ScopedLock<Mutex> l(lock); + listeners[destination] = Subscriber::shared_ptr( + new Subscriber(session, listener, autoAck)); +} + +void Dispatcher::cancel(const std::string& destination) +{ + ScopedLock<Mutex> l(lock); + listeners.erase(destination); + if (autoStop && listeners.empty()) + queue->close(); +} + +}} diff --git a/qpid/cpp/src/qpid/client/Dispatcher.h b/qpid/cpp/src/qpid/client/Dispatcher.h new file mode 100644 index 0000000000..e23d0c198c --- /dev/null +++ b/qpid/cpp/src/qpid/client/Dispatcher.h @@ -0,0 +1,84 @@ +/* + * + * 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. + * + */ +#ifndef _Dispatcher_ +#define _Dispatcher_ + +#include <map> +#include <memory> +#include <string> +#include <boost/shared_ptr.hpp> +#include "qpid/client/Session.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Thread.h" +#include "MessageListener.h" +#include "AckPolicy.h" + +namespace qpid { +namespace client { + +class Subscriber : public MessageListener +{ + Session& session; + MessageListener* const listener; + AckPolicy autoAck; + +public: + typedef boost::shared_ptr<Subscriber> shared_ptr; + Subscriber(Session& session, MessageListener* listener, AckPolicy); + void received(Message& msg); + +}; + +typedef framing::Handler<framing::FrameSet> FrameSetHandler; + +class Dispatcher : public sys::Runnable +{ + typedef std::map<std::string, Subscriber::shared_ptr> Listeners; + sys::Mutex lock; + sys::Thread worker; + Session& session; + Demux::QueuePtr queue; + bool running; + bool autoStop; + Listeners listeners; + Subscriber::shared_ptr defaultListener; + std::auto_ptr<FrameSetHandler> handler; + + Subscriber::shared_ptr find(const std::string& name); + bool isStopped(); + +public: + Dispatcher(Session& session, const std::string& queue = ""); + + void start(); + void run(); + void stop(); + void setAutoStop(bool b); + + void listen(MessageListener* listener, AckPolicy autoAck=AckPolicy()); + void listen(const std::string& destination, MessageListener* listener, AckPolicy autoAck=AckPolicy()); + void cancel(const std::string& destination); +}; + +}} + +#endif diff --git a/qpid/cpp/src/qpid/client/Exchange.cpp b/qpid/cpp/src/qpid/client/Exchange.cpp new file mode 100644 index 0000000000..e7fbdeb47e --- /dev/null +++ b/qpid/cpp/src/qpid/client/Exchange.cpp @@ -0,0 +1,34 @@ +/* + * + * 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. + * + */ +#include "Exchange.h" + +qpid::client::Exchange::Exchange(std::string _name, std::string _type) : name(_name), type(_type){} +const std::string& qpid::client::Exchange::getName() const { return name; } +const std::string& qpid::client::Exchange::getType() const { return type; } + +const std::string qpid::client::Exchange::DIRECT_EXCHANGE = "direct"; +const std::string qpid::client::Exchange::TOPIC_EXCHANGE = "topic"; +const std::string qpid::client::Exchange::HEADERS_EXCHANGE = "headers"; + +const qpid::client::Exchange qpid::client::Exchange::DEFAULT_EXCHANGE("", DIRECT_EXCHANGE); +const qpid::client::Exchange qpid::client::Exchange::STANDARD_DIRECT_EXCHANGE("amq.direct", DIRECT_EXCHANGE); +const qpid::client::Exchange qpid::client::Exchange::STANDARD_TOPIC_EXCHANGE("amq.topic", TOPIC_EXCHANGE); +const qpid::client::Exchange qpid::client::Exchange::STANDARD_HEADERS_EXCHANGE("amq.headers", HEADERS_EXCHANGE); diff --git a/qpid/cpp/src/qpid/client/Exchange.h b/qpid/cpp/src/qpid/client/Exchange.h new file mode 100644 index 0000000000..0640e4fe2c --- /dev/null +++ b/qpid/cpp/src/qpid/client/Exchange.h @@ -0,0 +1,106 @@ +/* + * + * 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. + * + */ +#include <string> + +#ifndef _Exchange_ +#define _Exchange_ + +namespace qpid { +namespace client { + + /** + * DEPRECATED + * + * A 'handle' used to represent an AMQP exchange in the Channel + * methods. Exchanges are the destinations to which messages are + * published. + * + * There are different types of exchange (the standard types are + * available as static constants, see DIRECT_EXCHANGE, + * TOPIC_EXCHANGE and HEADERS_EXCHANGE). A Queue can be bound to + * an exchange using Channel::bind() and messages published to + * that exchange are then routed to the queue based on the details + * of the binding and the type of exchange. + * + * There are some standard exchange instances that are predeclared + * on all AMQP brokers. These are defined as static members + * STANDARD_DIRECT_EXCHANGE, STANDARD_TOPIC_EXCHANGE and + * STANDARD_HEADERS_EXCHANGE. There is also the 'default' exchange + * (member DEFAULT_EXCHANGE) which is nameless and of type + * 'direct' and has every declared queue bound to it by queue + * name. + */ + class Exchange{ + const std::string name; + const std::string type; + + public: + /** + * A direct exchange routes messages published with routing + * key X to any queue bound with key X (i.e. an exact match is + * used). + */ + static const std::string DIRECT_EXCHANGE; + /** + * A topic exchange treat the key with which a queue is bound + * as a pattern and routes all messages whose routing keys + * match that pattern to the bound queue. The routing key for + * a message must consist of zero or more alpha-numeric words + * delimited by dots. The pattern is of a similar form but * + * can be used to match excatly one word and # can be used to + * match zero or more words. + */ + static const std::string TOPIC_EXCHANGE; + /** + * The headers exchange routes messages based on whether their + * headers match the binding arguments specified when + * binding. (see the AMQP spec for more details). + */ + static const std::string HEADERS_EXCHANGE; + + /** + * The 'default' exchange, nameless and of type 'direct'. Has + * every declared queue bound to it by name. + */ + static const Exchange DEFAULT_EXCHANGE; + /** + * The standard direct exchange, named amq.direct. + */ + static const Exchange STANDARD_DIRECT_EXCHANGE; + /** + * The standard topic exchange, named amq.topic. + */ + static const Exchange STANDARD_TOPIC_EXCHANGE; + /** + * The standard headers exchange, named amq.header. + */ + static const Exchange STANDARD_HEADERS_EXCHANGE; + + Exchange(std::string name, std::string type = DIRECT_EXCHANGE); + const std::string& getName() const; + const std::string& getType() const; + }; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/client/Execution.h b/qpid/cpp/src/qpid/client/Execution.h new file mode 100644 index 0000000000..5f717de586 --- /dev/null +++ b/qpid/cpp/src/qpid/client/Execution.h @@ -0,0 +1,50 @@ +/* + * + * 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. + * + */ +#ifndef _Execution_ +#define _Execution_ + +#include "qpid/framing/SequenceNumber.h" +#include "Demux.h" + +namespace qpid { +namespace client { + +/** + * Provides more detailed access to the amqp 'execution layer'. + */ +class Execution +{ +public: + virtual ~Execution() {} + virtual void sendSyncRequest() = 0; + virtual void sendFlushRequest() = 0; + virtual void completed(const framing::SequenceNumber& id, bool cumulative, bool send) = 0; + virtual Demux& getDemux() = 0; + virtual bool isComplete(const framing::SequenceNumber& id) = 0; + virtual bool isCompleteUpTo(const framing::SequenceNumber& id) = 0; + virtual void setCompletionListener(boost::function<void()>) = 0; + virtual void syncWait(const framing::SequenceNumber& id) = 0; + virtual framing::SequenceNumber lastSent() const = 0; +}; + +}} + +#endif diff --git a/qpid/cpp/src/qpid/client/ExecutionHandler.cpp b/qpid/cpp/src/qpid/client/ExecutionHandler.cpp new file mode 100644 index 0000000000..afdd13c9e9 --- /dev/null +++ b/qpid/cpp/src/qpid/client/ExecutionHandler.cpp @@ -0,0 +1,267 @@ +/* + * + * 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. + * + */ + +#include "ExecutionHandler.h" +#include "qpid/Exception.h" +#include "qpid/framing/BasicDeliverBody.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/AMQP_HighestVersion.h" +#include "qpid/framing/all_method_bodies.h" +#include "qpid/framing/ServerInvoker.h" +#include "qpid/client/FutureCompletion.h" +#include <boost/bind.hpp> + +using namespace qpid::client; +using namespace qpid::framing; +using namespace boost; +using qpid::sys::Mutex; + +bool isMessageMethod(AMQMethodBody* method) +{ + return method->isA<BasicDeliverBody>() || method->isA<MessageTransferBody>() || method->isA<BasicGetOkBody>(); +} + +bool isMessageMethod(AMQBody* body) +{ + AMQMethodBody* method=body->getMethod(); + return method && isMessageMethod(method); +} + +bool isContentFrame(AMQFrame& frame) +{ + AMQBody* body = frame.getBody(); + uint8_t type = body->type(); + return type == HEADER_BODY || type == CONTENT_BODY || isMessageMethod(body); +} + +ExecutionHandler::ExecutionHandler(uint64_t _maxFrameSize) : + version(framing::highestProtocolVersion), maxFrameSize(_maxFrameSize) {} + +//incoming: +void ExecutionHandler::handle(AMQFrame& frame) +{ + if (!invoke(*this, *frame.getBody())) { + if (!arriving) { + arriving = FrameSet::shared_ptr(new FrameSet(++incomingCounter)); + } + arriving->append(frame); + if (arriving->isComplete()) { + if (arriving->isContentBearing() || !correlation.receive(arriving->getMethod())) { + demux.handle(arriving); + } + arriving.reset(); + } + } +} + +void ExecutionHandler::complete(uint32_t cumulative, const SequenceNumberSet& range) +{ + if (range.size() % 2) { //must be even number + throw NotAllowedException(QPID_MSG("Received odd number of elements in ranged mark")); + } else { + SequenceNumber mark(cumulative); + { + Mutex::ScopedLock l(lock); + outgoingCompletionStatus.update(mark, range); + } + if (completionListener) completionListener(); + completion.completed(outgoingCompletionStatus.mark); + //TODO: signal listeners of early notification? + } +} + +void ExecutionHandler::flush() +{ + sendCompletion(); +} + +void ExecutionHandler::noop() +{ + //do nothing +} + +void ExecutionHandler::result(uint32_t command, const std::string& data) +{ + completion.received(command, data); +} + +void ExecutionHandler::sync() +{ + //TODO: implement - need to note the mark requested and then + //remember to send a response when that point is reached +} + +void ExecutionHandler::flushTo(const framing::SequenceNumber& point) +{ + Mutex::ScopedLock l(lock); + if (point > outgoingCompletionStatus.mark) { + sendFlushRequest(); + } +} + +void ExecutionHandler::sendFlushRequest() +{ + Mutex::ScopedLock l(lock); + AMQFrame frame(in_place<ExecutionFlushBody>()); + out(frame); +} + +void ExecutionHandler::syncTo(const framing::SequenceNumber& point) +{ + Mutex::ScopedLock l(lock); + if (point > outgoingCompletionStatus.mark) { + sendSyncRequest(); + } +} + + +void ExecutionHandler::sendSyncRequest() +{ + Mutex::ScopedLock l(lock); + AMQFrame frame(in_place<ExecutionSyncBody>()); + out(frame); +} + +void ExecutionHandler::completed(const SequenceNumber& id, bool cumulative, bool send) +{ + { + Mutex::ScopedLock l(lock); + if (id > incomingCompletionStatus.mark) { + if (cumulative) { + incomingCompletionStatus.update(incomingCompletionStatus.mark, id); + } else { + incomingCompletionStatus.update(id, id); + } + } + } + if (send) { + sendCompletion(); + } +} + + +void ExecutionHandler::sendCompletion() +{ + Mutex::ScopedLock l(lock); + SequenceNumberSet range; + incomingCompletionStatus.collectRanges(range); + AMQFrame frame( + in_place<ExecutionCompleteBody>( + version, incomingCompletionStatus.mark.getValue(), range)); + out(frame); +} + +SequenceNumber ExecutionHandler::lastSent() const { return outgoingCounter; } + +SequenceNumber ExecutionHandler::send(const AMQBody& command, CompletionTracker::ResultListener listener) +{ + Mutex::ScopedLock l(lock); + return send(command, listener, false); +} + +SequenceNumber ExecutionHandler::send(const AMQBody& command, CompletionTracker::ResultListener l, bool hasContent) +{ + SequenceNumber id = ++outgoingCounter; + if(l) { + completion.listenForResult(id, l); + } + AMQFrame frame(command); + if (hasContent) { + frame.setEof(false); + } + out(frame); + return id; +} + +SequenceNumber ExecutionHandler::send(const AMQBody& command, const MethodContent& content, + CompletionTracker::ResultListener listener) +{ + Mutex::ScopedLock l(lock); + SequenceNumber id = send(command, listener, true); + sendContent(content); + return id; +} + +void ExecutionHandler::sendContent(const MethodContent& content) +{ + AMQFrame header(content.getHeader()); + header.setBof(false); + u_int64_t data_length = content.getData().length(); + if(data_length > 0){ + header.setEof(false); + out(header); + const u_int32_t frag_size = maxFrameSize - (AMQFrame::frameOverhead() - 1 /*end of frame marker included in overhead but not in size*/); + if(data_length < frag_size){ + AMQFrame frame(in_place<AMQContentBody>(content.getData())); + frame.setBof(false); + out(frame); + }else{ + u_int32_t offset = 0; + u_int32_t remaining = data_length - offset; + while (remaining > 0) { + u_int32_t length = remaining > frag_size ? frag_size : remaining; + string frag(content.getData().substr(offset, length)); + AMQFrame frame(in_place<AMQContentBody>(frag)); + frame.setBof(false); + if (offset > 0) { + frame.setBos(false); + } + offset += length; + remaining = data_length - offset; + if (remaining) { + frame.setEos(false); + frame.setEof(false); + } + out(frame); + } + } + } else { + out(header); + } +} + +bool ExecutionHandler::isComplete(const SequenceNumber& id) +{ + Mutex::ScopedLock l(lock); + return outgoingCompletionStatus.covers(id); +} + +bool ExecutionHandler::isCompleteUpTo(const SequenceNumber& id) +{ + Mutex::ScopedLock l(lock); + return outgoingCompletionStatus.mark >= id; +} + +void ExecutionHandler::setCompletionListener(boost::function<void()> l) +{ + completionListener = l; +} + + +void ExecutionHandler::syncWait(const SequenceNumber& id) { + syncTo(id); + FutureCompletion fc; + completion.listenForCompletion( + id, boost::bind(&FutureCompletion::completed, &fc) + ); + fc.waitForCompletion(); + assert(isCompleteUpTo(id)); +} diff --git a/qpid/cpp/src/qpid/client/ExecutionHandler.h b/qpid/cpp/src/qpid/client/ExecutionHandler.h new file mode 100644 index 0000000000..d9113b683b --- /dev/null +++ b/qpid/cpp/src/qpid/client/ExecutionHandler.h @@ -0,0 +1,104 @@ +/* + * + * 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. + * + */ +#ifndef _ExecutionHandler_ +#define _ExecutionHandler_ + +#include <queue> +#include "qpid/framing/AccumulatedAck.h" +#include "qpid/framing/AMQP_ServerOperations.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/framing/MethodContent.h" +#include "qpid/framing/SequenceNumber.h" +#include "qpid/sys/Mutex.h" +#include "ChainableFrameHandler.h" +#include "CompletionTracker.h" +#include "Correlator.h" +#include "Demux.h" +#include "Execution.h" + +namespace qpid { +namespace client { + +class ExecutionHandler : + public framing::AMQP_ServerOperations::ExecutionHandler, + public framing::FrameHandler, + public Execution +{ + framing::SequenceNumber incomingCounter; + framing::AccumulatedAck incomingCompletionStatus; + framing::SequenceNumber outgoingCounter; + framing::AccumulatedAck outgoingCompletionStatus; + framing::FrameSet::shared_ptr arriving; + Correlator correlation; + CompletionTracker completion; + Demux demux; + sys::Mutex lock; + framing::ProtocolVersion version; + uint64_t maxFrameSize; + boost::function<void()> completionListener; + + void complete(uint32_t mark, const framing::SequenceNumberSet& range); + void flush(); + void noop(); + void result(uint32_t command, const std::string& data); + void sync(); + + void sendCompletion(); + + framing::SequenceNumber send(const framing::AMQBody&, CompletionTracker::ResultListener, bool hasContent); + void sendContent(const framing::MethodContent&); + +public: + typedef CompletionTracker::ResultListener ResultListener; + + // Allow other classes to set the out handler. + framing::FrameHandler::Chain out; + + ExecutionHandler(uint64_t maxFrameSize = 65535); + + // Incoming handler. + void handle(framing::AMQFrame& frame); + + framing::SequenceNumber send(const framing::AMQBody& command, ResultListener=ResultListener()); + framing::SequenceNumber send(const framing::AMQBody& command, const framing::MethodContent& content, + ResultListener=ResultListener()); + framing::SequenceNumber lastSent() const; + void sendSyncRequest(); + void sendFlushRequest(); + void completed(const framing::SequenceNumber& id, bool cumulative, bool send); + void syncTo(const framing::SequenceNumber& point); + void flushTo(const framing::SequenceNumber& point); + void syncWait(const framing::SequenceNumber& id); + + bool isComplete(const framing::SequenceNumber& id); + bool isCompleteUpTo(const framing::SequenceNumber& id); + + void setMaxFrameSize(uint64_t size) { maxFrameSize = size; } + Correlator& getCorrelator() { return correlation; } + CompletionTracker& getCompletionTracker() { return completion; } + Demux& getDemux() { return demux; } + + void setCompletionListener(boost::function<void()>); +}; + +}} + +#endif diff --git a/qpid/cpp/src/qpid/client/Future.h b/qpid/cpp/src/qpid/client/Future.h new file mode 100644 index 0000000000..d07f9f149c --- /dev/null +++ b/qpid/cpp/src/qpid/client/Future.h @@ -0,0 +1,108 @@ +/* + * + * 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. + * + */ + +#ifndef _Future_ +#define _Future_ + +#include <boost/bind.hpp> +#include <boost/shared_ptr.hpp> +#include "qpid/Exception.h" +#include "qpid/framing/SequenceNumber.h" +#include "qpid/framing/StructHelper.h" +#include "FutureCompletion.h" +#include "FutureResponse.h" +#include "FutureResult.h" +#include "SessionCore.h" + +namespace qpid { +namespace client { + +class Future : private framing::StructHelper +{ + framing::SequenceNumber command; + boost::shared_ptr<FutureResponse> response; + boost::shared_ptr<FutureResult> result; + bool complete; + +public: + Future() : complete(false) {} + Future(const framing::SequenceNumber& id) : command(id), complete(false) {} + + void sync(SessionCore& session) + { + if (!isComplete(session)) { + session.getExecution().syncTo(command); + wait(session); + } + } + + void wait(SessionCore& session) + { + if (!isComplete(session)) { + FutureCompletion callback; + session.getExecution().getCompletionTracker().listenForCompletion( + command, + boost::bind(&FutureCompletion::completed, &callback) + ); + callback.waitForCompletion(); + session.assertOpen(); + complete = true; + } + } + + framing::AMQMethodBody* getResponse(SessionCore& session) + { + if (response) { + session.getExecution().getCompletionTracker().listenForCompletion( + command, + boost::bind(&FutureResponse::completed, response) + ); + return response->getResponse(session); + } else { + throw Exception("Response not expected"); + } + } + + template <class T> void decodeResult(T& value, SessionCore& session) + { + if (result) { + decode(value, result->getResult(session)); + } else { + throw Exception("Result not expected"); + } + } + + bool isComplete(SessionCore& session) { + return complete || session.getExecution().isComplete(command); + } + + bool isCompleteUpTo(SessionCore& session) { + return complete || session.getExecution().isCompleteUpTo(command); + } + + void setCommandId(const framing::SequenceNumber& id) { command = id; } + void setFutureResponse(boost::shared_ptr<FutureResponse> r) { response = r; } + void setFutureResult(boost::shared_ptr<FutureResult> r) { result = r; } +}; + +}} + +#endif diff --git a/qpid/cpp/src/qpid/client/FutureCompletion.cpp b/qpid/cpp/src/qpid/client/FutureCompletion.cpp new file mode 100644 index 0000000000..130c65d6aa --- /dev/null +++ b/qpid/cpp/src/qpid/client/FutureCompletion.cpp @@ -0,0 +1,48 @@ +/* + * + * 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. + * + */ + +#include "FutureCompletion.h" + +using namespace qpid::client; +using namespace qpid::sys; + +FutureCompletion::FutureCompletion() : complete(false) {} + +bool FutureCompletion::isComplete() const +{ + Monitor::ScopedLock l(lock); + return complete; +} + +void FutureCompletion::completed() +{ + Monitor::ScopedLock l(lock); + complete = true; + lock.notifyAll(); +} + +void FutureCompletion::waitForCompletion() const +{ + Monitor::ScopedLock l(lock); + while (!complete) { + lock.wait(); + } +} diff --git a/qpid/cpp/src/qpid/client/FutureCompletion.h b/qpid/cpp/src/qpid/client/FutureCompletion.h new file mode 100644 index 0000000000..1897230230 --- /dev/null +++ b/qpid/cpp/src/qpid/client/FutureCompletion.h @@ -0,0 +1,48 @@ +/* + * + * 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. + * + */ + +#ifndef _FutureCompletion_ +#define _FutureCompletion_ + +#include "qpid/framing/amqp_framing.h" +#include "qpid/sys/Monitor.h" + +namespace qpid { +namespace client { + +class FutureCompletion +{ +protected: + mutable sys::Monitor lock; + bool complete; + +public: + FutureCompletion(); + virtual ~FutureCompletion(){} + bool isComplete() const; + void waitForCompletion() const; + void completed(); +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/client/FutureFactory.cpp b/qpid/cpp/src/qpid/client/FutureFactory.cpp new file mode 100644 index 0000000000..7f9d51e77f --- /dev/null +++ b/qpid/cpp/src/qpid/client/FutureFactory.cpp @@ -0,0 +1,51 @@ +/* + * + * 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. + * + */ + +#include "FutureFactory.h" + +using namespace qpid::client; +using namespace boost; + +shared_ptr<FutureCompletion> FutureFactory::createCompletion() +{ + shared_ptr<FutureCompletion> f(new FutureCompletion()); + weak_ptr<FutureCompletion> w(f); + set.push_back(w); + return f; +} + +shared_ptr<FutureResponse> FutureFactory::createResponse() +{ + shared_ptr<FutureResponse> f(new FutureResponse()); + weak_ptr<FutureCompletion> w(static_pointer_cast<FutureCompletion>(f)); + set.push_back(w); + return f; +} + +void FutureFactory::close(uint16_t code, const std::string& text) +{ + for (WeakPtrSet::iterator i = set.begin(); i != set.end(); i++) { + shared_ptr<FutureCompletion> p = i->lock(); + if (p) { + p->close(code, text); + } + } +} diff --git a/qpid/cpp/src/qpid/client/FutureFactory.h b/qpid/cpp/src/qpid/client/FutureFactory.h new file mode 100644 index 0000000000..b126e296fd --- /dev/null +++ b/qpid/cpp/src/qpid/client/FutureFactory.h @@ -0,0 +1,48 @@ +/* + * + * 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. + * + */ + +#ifndef _FutureFactory_ +#define _FutureFactory_ + +#include <vector> +#include <boost/shared_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include "FutureCompletion.h" +#include "FutureResponse.h" + +namespace qpid { +namespace client { + +class FutureFactory +{ + typedef std::vector< boost::weak_ptr<FutureCompletion> > WeakPtrSet; + WeakPtrSet set; + +public: + boost::shared_ptr<FutureCompletion> createCompletion(); + boost::shared_ptr<FutureResponse> createResponse(); + void close(uint16_t code, const std::string& text); +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/client/FutureResponse.cpp b/qpid/cpp/src/qpid/client/FutureResponse.cpp new file mode 100644 index 0000000000..32d99531fa --- /dev/null +++ b/qpid/cpp/src/qpid/client/FutureResponse.cpp @@ -0,0 +1,45 @@ +/* + * + * 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. + * + */ + +#include "FutureResponse.h" + +#include "SessionCore.h" + +using namespace qpid::client; +using namespace qpid::framing; +using namespace qpid::sys; + + +AMQMethodBody* FutureResponse::getResponse(SessionCore& session) +{ + waitForCompletion(); + session.assertOpen(); + return response.getMethod(); +} + +void FutureResponse::received(const AMQMethodBody* r) +{ + Monitor::ScopedLock l(lock); + response.setBody(*r); + complete = true; + lock.notifyAll(); +} + diff --git a/qpid/cpp/src/qpid/client/FutureResponse.h b/qpid/cpp/src/qpid/client/FutureResponse.h new file mode 100644 index 0000000000..534ca01bb7 --- /dev/null +++ b/qpid/cpp/src/qpid/client/FutureResponse.h @@ -0,0 +1,46 @@ +/* + * + * 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. + * + */ + +#ifndef _FutureResponse_ +#define _FutureResponse_ + +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/BodyHolder.h" +#include "FutureCompletion.h" + +namespace qpid { +namespace client { + +class SessionCore; + +class FutureResponse : public FutureCompletion +{ + framing::BodyHolder response; +public: + framing::AMQMethodBody* getResponse(SessionCore& session); + void received(const framing::AMQMethodBody* response); +}; + +}} + + + +#endif diff --git a/qpid/cpp/src/qpid/client/FutureResult.cpp b/qpid/cpp/src/qpid/client/FutureResult.cpp new file mode 100644 index 0000000000..681202edea --- /dev/null +++ b/qpid/cpp/src/qpid/client/FutureResult.cpp @@ -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. + * + */ + +#include "FutureResult.h" + +#include "SessionCore.h" + +using namespace qpid::client; +using namespace qpid::framing; +using namespace qpid::sys; + +const std::string& FutureResult::getResult(SessionCore& session) const +{ + waitForCompletion(); + session.assertOpen(); + return result; +} + +void FutureResult::received(const std::string& r) +{ + Monitor::ScopedLock l(lock); + result = r; + complete = true; + lock.notifyAll(); +} diff --git a/qpid/cpp/src/qpid/client/FutureResult.h b/qpid/cpp/src/qpid/client/FutureResult.h new file mode 100644 index 0000000000..3117b63802 --- /dev/null +++ b/qpid/cpp/src/qpid/client/FutureResult.h @@ -0,0 +1,46 @@ +/* + * + * 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. + * + */ + +#ifndef _FutureResult_ +#define _FutureResult_ + +#include <string> +#include "qpid/framing/amqp_framing.h" +#include "FutureCompletion.h" + +namespace qpid { +namespace client { + +class SessionCore; + +class FutureResult : public FutureCompletion +{ + std::string result; +public: + const std::string& getResult(SessionCore& session) const; + void received(const std::string& result); +}; + +}} + + + +#endif diff --git a/qpid/cpp/src/qpid/client/LocalQueue.cpp b/qpid/cpp/src/qpid/client/LocalQueue.cpp new file mode 100644 index 0000000000..951996f005 --- /dev/null +++ b/qpid/cpp/src/qpid/client/LocalQueue.cpp @@ -0,0 +1,64 @@ +/* + * + * 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. + * + */ +#include "LocalQueue.h" +#include "qpid/Exception.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/framing/reply_exceptions.h" + +namespace qpid { +namespace client { + +using namespace framing; + +LocalQueue::LocalQueue(AckPolicy a) : autoAck(a) {} +LocalQueue::~LocalQueue() {} + +Message LocalQueue::pop() { + if (!queue) + throw ClosedException(); + FrameSet::shared_ptr content = queue->pop(); + if (content->isA<MessageTransferBody>()) { + Message m(*content, session); + autoAck.ack(m); + return m; + } + else + throw CommandInvalidException( + QPID_MSG("Unexpected method: " << content->getMethod())); +} + +void LocalQueue::setAckPolicy(AckPolicy a) { autoAck=a; } + +bool LocalQueue::empty() const +{ + if (!queue) + throw ClosedException(); + return queue->empty(); +} + +size_t LocalQueue::size() const +{ + if (!queue) + throw ClosedException(); + return queue->size(); +} + +}} // namespace qpid::client diff --git a/qpid/cpp/src/qpid/client/LocalQueue.h b/qpid/cpp/src/qpid/client/LocalQueue.h new file mode 100644 index 0000000000..f8b2c2e0b3 --- /dev/null +++ b/qpid/cpp/src/qpid/client/LocalQueue.h @@ -0,0 +1,60 @@ +#ifndef QPID_CLIENT_LOCALQUEUE_H +#define QPID_CLIENT_LOCALQUEUE_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. + * + */ + +#include "qpid/client/Message.h" +#include "qpid/client/Demux.h" +#include "qpid/client/AckPolicy.h" + +namespace qpid { +namespace client { + +/** + * Local representation of a remote queue. + * + * \ingroup clientapi + */ +class LocalQueue +{ + public: + LocalQueue(AckPolicy=AckPolicy()); + ~LocalQueue(); + + /** Pop the next message off the queue. + *@exception ClosedException if subscription has been closed. + */ + Message pop(); + bool empty() const; + size_t size() const; + void setAckPolicy(AckPolicy); + + private: + friend class SubscriptionManager; + Session session; + Demux::QueuePtr queue; + AckPolicy autoAck; +}; + +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_LOCALQUEUE_H*/ diff --git a/qpid/cpp/src/qpid/client/Message.h b/qpid/cpp/src/qpid/client/Message.h new file mode 100644 index 0000000000..daac30ba36 --- /dev/null +++ b/qpid/cpp/src/qpid/client/Message.h @@ -0,0 +1,104 @@ +#ifndef _client_Message_h +#define _client_Message_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. + * + */ +#include <string> +#include "qpid/client/Session.h" +#include "qpid/framing/MessageTransferBody.h" +#include "qpid/framing/TransferContent.h" + +namespace qpid { +namespace client { + +/** + * A representation of messages for sent or recived through the + * client api. + * + * \ingroup clientapi + */ +class Message : public framing::TransferContent +{ +public: + Message(const std::string& data_=std::string(), + const std::string& routingKey=std::string(), + const std::string& exchange=std::string() + ) : TransferContent(data_, routingKey, exchange) {} + + std::string getDestination() const + { + return method.getDestination(); + } + + bool isRedelivered() const + { + return hasDeliveryProperties() && getDeliveryProperties().getRedelivered(); + } + + void setRedelivered(bool redelivered) + { + getDeliveryProperties().setRedelivered(redelivered); + } + + framing::FieldTable& getHeaders() + { + return getMessageProperties().getApplicationHeaders(); + } + + void acknowledge(Session& session, bool cumulative = true, bool send = true) const + { + session.getExecution().completed(id, cumulative, send); + } + + void acknowledge(bool cumulative = true, bool send = true) const + { + const_cast<Session&>(session).getExecution().completed(id, cumulative, send); + } + + /**@internal for incoming messages */ + Message(const framing::FrameSet& frameset, Session s) : + method(*frameset.as<framing::MessageTransferBody>()), id(frameset.getId()), session(s) + { + populate(frameset); + } + + const framing::MessageTransferBody& getMethod() const + { + return method; + } + + const framing::SequenceNumber& getId() const + { + return id; + } + + /**@internal use for incoming messages. */ + void setSession(Session s) { session=s; } +private: + //method and id are only set for received messages: + framing::MessageTransferBody method; + framing::SequenceNumber id; + Session session; +}; + +}} + +#endif /*!_client_Message_h*/ diff --git a/qpid/cpp/src/qpid/client/MessageListener.cpp b/qpid/cpp/src/qpid/client/MessageListener.cpp new file mode 100644 index 0000000000..68ebedeb0d --- /dev/null +++ b/qpid/cpp/src/qpid/client/MessageListener.cpp @@ -0,0 +1,24 @@ +/* + * + * 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. + * + */ + +#include "MessageListener.h" + +qpid::client::MessageListener::~MessageListener() {} diff --git a/qpid/cpp/src/qpid/client/MessageListener.h b/qpid/cpp/src/qpid/client/MessageListener.h new file mode 100644 index 0000000000..86e5dd63dc --- /dev/null +++ b/qpid/cpp/src/qpid/client/MessageListener.h @@ -0,0 +1,49 @@ +/* + * + * 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. + * + */ +#include <string> + +#ifndef _MessageListener_ +#define _MessageListener_ + +#include "Message.h" + +namespace qpid { +namespace client { + + /** + * An interface through which asynchronously delivered messages + * can be received by an application. + * + * @see Channel::consume() + * + * \ingroup clientapi + */ + class MessageListener{ + public: + virtual ~MessageListener(); + virtual void received(Message& msg) = 0; + }; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/client/MessageQueue.h b/qpid/cpp/src/qpid/client/MessageQueue.h new file mode 100644 index 0000000000..e9b7a9fe58 --- /dev/null +++ b/qpid/cpp/src/qpid/client/MessageQueue.h @@ -0,0 +1,50 @@ +/* + * + * 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. + * + */ + +#ifndef _MessageQueue_ +#define _MessageQueue_ +#include <iostream> +#include "qpid/sys/BlockingQueue.h" +#include "MessageListener.h" + +namespace qpid { +namespace client { + +/** + * A MessageListener implementation that simply queues up + * messages. + * + * \ingroup clientapi + */ +class MessageQueue : public sys::BlockingQueue<Message>, public MessageListener +{ + public: + void received(Message& msg) + { + push(msg); + } +}; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/client/Queue.cpp b/qpid/cpp/src/qpid/client/Queue.cpp new file mode 100644 index 0000000000..1752a48a3a --- /dev/null +++ b/qpid/cpp/src/qpid/client/Queue.cpp @@ -0,0 +1,58 @@ +/* + * + * 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. + * + */ +#include "Queue.h" + +qpid::client::Queue::Queue() : name(""), autodelete(true), exclusive(true), durable(false){} + +qpid::client::Queue::Queue(std::string _name) : name(_name), autodelete(false), exclusive(false), durable(false){} + +qpid::client::Queue::Queue(std::string _name, bool temp) : name(_name), autodelete(temp), exclusive(temp), durable(false){} + +qpid::client::Queue::Queue(std::string _name, bool _autodelete, bool _exclusive, bool _durable) + : name(_name), autodelete(_autodelete), exclusive(_exclusive), durable(_durable){} + +const std::string& qpid::client::Queue::getName() const{ + return name; +} + +void qpid::client::Queue::setName(const std::string& _name){ + name = _name; +} + +bool qpid::client::Queue::isAutoDelete() const{ + return autodelete; +} + +bool qpid::client::Queue::isExclusive() const{ + return exclusive; +} + +bool qpid::client::Queue::isDurable() const{ + return durable; +} + +void qpid::client::Queue::setDurable(bool _durable){ + durable = _durable; +} + + + + diff --git a/qpid/cpp/src/qpid/client/Queue.h b/qpid/cpp/src/qpid/client/Queue.h new file mode 100644 index 0000000000..078e04c29e --- /dev/null +++ b/qpid/cpp/src/qpid/client/Queue.h @@ -0,0 +1,103 @@ +#ifndef _client_Queue_h +#define _client_Queue_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. + * + */ +#include <string> + +namespace qpid { +namespace client { + + /** + * DEPRECATED + * + * A 'handle' used to represent an AMQP queue in the Channel + * methods. Creating an instance of this class does not cause the + * queue to be created on the broker. Rather, an instance of this + * class should be passed to Channel::declareQueue() to ensure + * that the queue exists or is created. + * + * Queues hold messages and allow clients to consume + * (see Channel::consume()) or get (see Channel::get()) those messags. A + * queue receives messages by being bound to one or more Exchange; + * messages published to that exchange may then be routed to the + * queue based on the details of the binding and the type of the + * exchange (see Channel::bind()). + * + * Queues are identified by a name. They can be exclusive (in which + * case they can only be used in the context of the connection + * over which they were declared, and are deleted when then + * connection closes), or they can be shared. Shared queues can be + * auto deleted when they have no consumers. + * + * We use the term 'temporary queue' to refer to an exclusive + * queue. + */ + class Queue{ + std::string name; + const bool autodelete; + const bool exclusive; + bool durable; + + public: + + /** + * Creates an unnamed, non-durable, temporary queue. A name + * will be assigned to this queue instance by a call to + * Channel::declareQueue(). + */ + Queue(); + /** + * Creates a shared, non-durable, queue with a given name, + * that will not be autodeleted. + * + * @param name the name of the queue + */ + Queue(std::string name); + /** + * Creates a non-durable queue with a given name. + * + * @param name the name of the queue + * + * @param temp if true the queue will be a temporary queue, if + * false it will be shared and not autodeleted. + */ + Queue(std::string name, bool temp); + /** + * This constructor allows the autodelete, exclusive and + * durable propeties to be explictly set. Note however that if + * exclusive is true, autodelete has no meaning as exclusive + * queues are always destroyed when the connection that + * created them is closed. + */ + Queue(std::string name, bool autodelete, bool exclusive, bool durable); + const std::string& getName() const; + void setName(const std::string&); + bool isAutoDelete() const; + bool isExclusive() const; + bool isDurable() const; + void setDurable(bool durable); + }; + +} +} + +#endif /*!_client_Queue_h*/ diff --git a/qpid/cpp/src/qpid/client/Response.h b/qpid/cpp/src/qpid/client/Response.h new file mode 100644 index 0000000000..2b7d55ec1f --- /dev/null +++ b/qpid/cpp/src/qpid/client/Response.h @@ -0,0 +1,52 @@ +/* + * + * 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. + * + */ + +#ifndef _Response_ +#define _Response_ + +#include <boost/shared_ptr.hpp> +#include "qpid/framing/amqp_framing.h" +#include "Completion.h" + +namespace qpid { +namespace client { + +class Response : public Completion +{ +public: + Response(Future f, shared_ptr<SessionCore> s) : Completion(f, s) {} + + template <class T> T& as() + { + framing::AMQMethodBody* response(future.getResponse(*session)); + return *boost::polymorphic_downcast<T*>(response); + } + + template <class T> bool isA() + { + framing::AMQMethodBody* response(future.getResponse(*session)); + return response && response->isA<T>(); + } +}; + +}} + +#endif diff --git a/qpid/cpp/src/qpid/client/Session.h b/qpid/cpp/src/qpid/client/Session.h new file mode 100644 index 0000000000..5d91f289e2 --- /dev/null +++ b/qpid/cpp/src/qpid/client/Session.h @@ -0,0 +1,38 @@ +#ifndef QPID_CLIENT_SESSION_H +#define QPID_CLIENT_SESSION_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. + * + */ +#include "qpid/client/Session_99_0.h" + +namespace qpid { +namespace client { + +/** + * Session is currently just an alias for Session_99_0 + * + * \ingroup clientapi + */ +typedef Session_99_0 Session; + +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_SESSION_H*/ diff --git a/qpid/cpp/src/qpid/client/SessionBase.cpp b/qpid/cpp/src/qpid/client/SessionBase.cpp new file mode 100644 index 0000000000..0e1fa67bda --- /dev/null +++ b/qpid/cpp/src/qpid/client/SessionBase.cpp @@ -0,0 +1,50 @@ +/* + * + * 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. + * + */ +#include "SessionBase.h" + +namespace qpid { +namespace client { +using namespace framing; + +SessionBase::SessionBase() {} +SessionBase::~SessionBase() {} +SessionBase::SessionBase(shared_ptr<SessionCore> core) : impl(core) {} +void SessionBase::suspend() { impl->suspend(); } +void SessionBase::close() { impl->close(); } + +void SessionBase::setSynchronous(bool isSync) { impl->setSync(isSync); } +void SessionBase::setSynchronous(SynchronousMode m) { impl->setSync(m); } +bool SessionBase::isSynchronous() const { return impl->isSync(); } +SynchronousMode SessionBase::getSynchronous() const { + return SynchronousMode(impl->isSync()); +} + +Execution& SessionBase::getExecution() { return impl->getExecution(); } +Uuid SessionBase::getId() const { return impl->getId(); } +framing::FrameSet::shared_ptr SessionBase::get() { return impl->get(); } + +void SessionBase::sync() { + Execution& ex = getExecution(); + ex.syncWait(ex.lastSent()); + impl->assertOpen(); +} + +}} // namespace qpid::client diff --git a/qpid/cpp/src/qpid/client/SessionBase.h b/qpid/cpp/src/qpid/client/SessionBase.h new file mode 100644 index 0000000000..3565145bb9 --- /dev/null +++ b/qpid/cpp/src/qpid/client/SessionBase.h @@ -0,0 +1,134 @@ +#ifndef QPID_CLIENT_SESSIONBASE_H +#define QPID_CLIENT_SESSIONBASE_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. + * + */ + +#include "qpid/framing/Uuid.h" +#include "qpid/framing/amqp_structs.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/framing/MethodContent.h" +#include "qpid/framing/TransferContent.h" +#include "qpid/client/Completion.h" +#include "qpid/client/ConnectionImpl.h" +#include "qpid/client/Response.h" +#include "qpid/client/SessionCore.h" +#include "qpid/client/TypedResult.h" +#include "qpid/shared_ptr.h" +#include <string> + +namespace qpid { +namespace client { + +using std::string; +using framing::Content; +using framing::FieldTable; +using framing::MethodContent; +using framing::SequenceNumberSet; +using framing::Uuid; + +/** \defgroup clientapi Synchronous mode of a session. + * + * SYNC means that Session functions do not return until the remote + * broker has confirmed that the command was executed. + * + * ASYNC means that the client sends commands asynchronously, Session + * functions return immediately. + * + * ASYNC mode gives better performance for high-volume traffic, but + * requires some additional caution: + * + * Session functions return immediately. If the command causes an + * exception on the broker, the exception will be thrown on a + * <em>later</em> function call. + * + * If you need to notify some extenal agent that some actions have + * been taken (e.g. binding queues to exchanages), you must call + * Session::sync() first, to ensure that all the commands are complete. + * + * You can freely switch between modes by calling Session::setSynchronous() + * + * @see Session::sync(), Session::setSynchronous() + */ +enum SynchronousMode { SYNC=true, ASYNC=false }; + + +/** + * Basic session operations that are not derived from AMQP XML methods. + */ +class SessionBase +{ + public: + SessionBase(); + ~SessionBase(); + + /** Get the next message frame-set from the session. */ + framing::FrameSet::shared_ptr get(); + + /** Get the session ID */ + Uuid getId() const; + + /** + * In synchronous mode, wait for the broker's response before + * returning. Note this gives lower throughput than asynchronous + * mode. + * + * In asynchronous mode commands are sent without waiting + * for a respose (you can use the returned Completion object + * to wait for completion.) + * + * @see SynchronousMode + */ + void setSynchronous(SynchronousMode mode); + void setSynchronous(bool set); + bool isSynchronous() const; + SynchronousMode getSynchronous() const; + + /** + * Suspend the session, can be resumed on a different connection. + * @see Connection::resume() + */ + void suspend(); + + /** Close the session */ + void close(); + + /** + * Synchronize with the broker. Wait for all commands issued so far in + * the session to complete. + * @see SynchronousMode + */ + void sync(); + + Execution& getExecution(); + + typedef framing::TransferContent DefaultContent; + + protected: + shared_ptr<SessionCore> impl; + framing::ProtocolVersion version; + friend class Connection; + SessionBase(shared_ptr<SessionCore>); +}; + +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_SESSIONBASE_H*/ diff --git a/qpid/cpp/src/qpid/client/SessionCore.cpp b/qpid/cpp/src/qpid/client/SessionCore.cpp new file mode 100644 index 0000000000..5079c47b5e --- /dev/null +++ b/qpid/cpp/src/qpid/client/SessionCore.cpp @@ -0,0 +1,440 @@ +/* + * + * 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. + * + */ + +#include "SessionCore.h" +#include "Future.h" +#include "FutureResponse.h" +#include "FutureResult.h" +#include "ConnectionImpl.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/framing/constants.h" +#include "qpid/framing/ClientInvoker.h" +#include "qpid/log/Statement.h" + +#include <boost/bind.hpp> + +namespace qpid { +namespace client { + +using namespace qpid::framing; + +namespace { const std::string OK="ok"; } + +typedef sys::Monitor::ScopedLock Lock; +typedef sys::Monitor::ScopedUnlock UnLock; + +inline void SessionCore::invariant() const { + switch (state.get()) { + case OPENING: + assert(!session); + assert(code==REPLY_SUCCESS); + assert(connection); + assert(channel.get()); + assert(channel.next == connection.get()); + break; + case RESUMING: + assert(session); + assert(session->getState() == SessionState::RESUMING); + assert(code==REPLY_SUCCESS); + assert(connection); + assert(channel.get()); + assert(channel.next == connection.get()); + break; + case OPEN: + case CLOSING: + case SUSPENDING: + assert(session); + assert(connection); + assert(channel.get()); + assert(channel.next == connection.get()); + break; + case SUSPENDED: + assert(session); + assert(!connection); + break; + case CLOSED: + assert(!session); + assert(!connection); + break; + } +} + +inline void SessionCore::setState(State s) { + state = s; + invariant(); +} + +inline void SessionCore::waitFor(State s) { + invariant(); + // We can be CLOSED or SUSPENDED by error at any time. + state.waitFor(States(s, CLOSED, SUSPENDED)); + check(); + invariant(); +} + +SessionCore::SessionCore(shared_ptr<ConnectionImpl> conn, + uint16_t ch, uint64_t maxFrameSize) + : l3(maxFrameSize), + sync(false), + channel(ch), + proxy(channel), + state(OPENING), + detachedLifetime(0) +{ + l3.out = &out; + attaching(conn); +} + +void SessionCore::attaching(shared_ptr<ConnectionImpl> c) { + assert(c); + assert(channel.get()); + connection = c; + channel.next = connection.get(); + code = REPLY_SUCCESS; + text = OK; + state = session ? RESUMING : OPENING; + invariant(); +} + +SessionCore::~SessionCore() { + Lock l(state); + detach(COMMAND_INVALID, "Session deleted"); + state.waitWaiters(); +} + +void SessionCore::detach(int c, const std::string& t) { + connection.reset(); + channel.next = 0; + code=c; + text=t; + l3.getDemux().close(); +} + +void SessionCore::doClose(int code, const std::string& text) { + if (state != CLOSED) { + session.reset(); + detach(code, text); + setState(CLOSED); + l3.getCompletionTracker().close(); + } + invariant(); +} + +void SessionCore::doSuspend(int code, const std::string& text) { + if (state != CLOSED && state != SUSPENDED) { + detach(code, text); + session->suspend(); + setState(SUSPENDED); + } + invariant(); +} + +ExecutionHandler& SessionCore::getExecution() { // user thread + return l3; +} + +void SessionCore::setSync(bool s) { // user thread + sync = s; +} + +bool SessionCore::isSync() { // user thread + return sync; +} + +FrameSet::shared_ptr SessionCore::get() { // user thread + // No lock here: pop does a blocking wait. + return l3.getDemux().getDefault()->pop(); +} + +static const std::string CANNOT_REOPEN_SESSION="Cannot re-open a session."; + +void SessionCore::open(uint32_t timeout) { // user thread + Lock l(state); + check(state==OPENING && !session, + COMMAND_INVALID, CANNOT_REOPEN_SESSION); + detachedLifetime=timeout; + proxy.open(detachedLifetime); + waitFor(OPEN); +} + +void SessionCore::close() { // user thread + Lock l(state); + check(); + if (state==OPEN) { + setState(CLOSING); + proxy.close(); + waitFor(CLOSED); + } + else + doClose(REPLY_SUCCESS, OK); +} + +void SessionCore::suspend() { // user thread + Lock l(state); + checkOpen(); + setState(SUSPENDING); + proxy.suspend(); + waitFor(SUSPENDED); +} + +void SessionCore::setChannel(uint16_t ch) { channel=ch; } + +static const std::string CANNOT_RESUME_SESSION("Session cannot be resumed."); + +void SessionCore::resume(shared_ptr<ConnectionImpl> c) { + // user thread + { + Lock l(state); + if (state==SUSPENDED) { // Clear error that caused suspend + code=REPLY_SUCCESS; + text=OK; + } + check(state==SUSPENDED, COMMAND_INVALID, CANNOT_RESUME_SESSION); + SequenceNumber sendAck=session->resuming(); + attaching(c); + proxy.resume(getId()); + waitFor(OPEN); + proxy.ack(sendAck, SequenceNumberSet()); + // TODO aconway 2007-10-23: Replay inside the lock might be a prolem + // for large replay sets. + SessionState::Replay replay=session->replay(); + for (SessionState::Replay::iterator i = replay.begin(); + i != replay.end(); ++i) + { + invariant(); + channel.handle(*i); // Direct to channel. + check(); + } + l3.getDemux().open(); + } +} + +void SessionCore::assertOpen() const { + Lock l(state); + checkOpen(); +} + +static const std::string UNEXPECTED_SESSION_ATTACHED( + "Received unexpected session.attached"); + +static const std::string INVALID_SESSION_RESUME_ID( + "session.resumed has invalid ID."); + +// network thread +void SessionCore::attached(const Uuid& sessionId, + uint32_t /*detachedLifetime*/) +{ + Lock l(state); + invariant(); + check(state == OPENING || state == RESUMING, + COMMAND_INVALID, UNEXPECTED_SESSION_ATTACHED); + if (state==OPENING) { // New session + // TODO aconway 2007-10-17: 0 disables sesskon.ack for now. + // If AMQP WG decides to keep it, we need to add configuration + // for the ack rate. + session=in_place<SessionState>(0, detachedLifetime > 0, sessionId); + setState(OPEN); + } + else { // RESUMING + check(sessionId == session->getId(), + INVALID_ARGUMENT, INVALID_SESSION_RESUME_ID); + // Don't setState yet, wait for first incoming ack. + } +} + +static const std::string UNEXPECTED_SESSION_DETACHED( + "Received unexpected session.detached."); + +static const std::string UNEXPECTED_SESSION_ACK( + "Received unexpected session.ack"); + +void SessionCore::detached() { // network thread + Lock l(state); + check(state == SUSPENDING, + COMMAND_INVALID, UNEXPECTED_SESSION_DETACHED); + doSuspend(REPLY_SUCCESS, OK); +} + +void SessionCore::ack(uint32_t ack, const SequenceNumberSet&) { + Lock l(state); + invariant(); + check(state==OPEN || state==RESUMING, + COMMAND_INVALID, UNEXPECTED_SESSION_ACK); + session->receivedAck(ack); + if (state==RESUMING) { + setState(OPEN); + } + invariant(); +} + +void SessionCore::closed(uint16_t code, const std::string& text) +{ // network thread + Lock l(state); + invariant(); + doClose(code, text); +} + +// closed by connection +void SessionCore::connectionClosed(uint16_t code, const std::string& text) { + Lock l(state); + try { + doClose(code, text); + } catch(...) { assert (0); } +} + +void SessionCore::connectionBroke(uint16_t code, const std::string& text) { + Lock l(state); + try { + doSuspend(code, text); + } catch (...) { assert(0); } +} + +void SessionCore::check() const { // Called with lock held. + invariant(); + if (code != REPLY_SUCCESS) + throwReplyException(code, text); +} + +void SessionCore::check(bool cond, int newCode, const std::string& msg) const { + check(); + if (!cond) { + const_cast<SessionCore*>(this)->doClose(newCode, msg); + throwReplyException(code, text); + } +} + +static const std::string SESSION_NOT_OPEN("Session is not open"); + +void SessionCore::checkOpen() const { + if (state==SUSPENDED) { + std::string cause; + if (code != REPLY_SUCCESS) + cause=" by :"+text; + throw CommandInvalidException(QPID_MSG("Session is suspended" << cause)); + } + check(state==OPEN, COMMAND_INVALID, SESSION_NOT_OPEN); +} + +Future SessionCore::send(const AMQBody& command) +{ + Lock l(state); + checkOpen(); + command.getMethod()->setSync(sync); + Future f; + //any result/response listeners must be set before the command is sent + if (command.getMethod()->resultExpected()) { + boost::shared_ptr<FutureResult> r(new FutureResult()); + f.setFutureResult(r); + //result listener is tied to command id, and is set when that + //is allocated by the execution handler, so pass it to send + f.setCommandId(l3.send(command, boost::bind(&FutureResult::received, r, _1))); + } else { + if (command.getMethod()->responseExpected()) { + boost::shared_ptr<FutureResponse> r(new FutureResponse()); + f.setFutureResponse(r); + l3.getCorrelator().listen(boost::bind(&FutureResponse::received, r, _1)); + } + + f.setCommandId(l3.send(command)); + } + return f; +} + +Future SessionCore::send(const AMQBody& command, const MethodContent& content) +{ + Lock l(state); + checkOpen(); + //content bearing methods don't currently have responses or + //results, if that changes should follow procedure for the other + //send method impl: + return Future(l3.send(command, content)); +} + +namespace { +bool isCloseResponse(const AMQFrame& frame) { + return frame.getMethod() && + frame.getMethod()->amqpClassId() == SESSION_CLASS_ID && + frame.getMethod()->amqpMethodId() == SESSION_CLOSED_METHOD_ID; +} +} + +// Network thread. +void SessionCore::handleIn(AMQFrame& frame) { + ConnectionImpl::shared_ptr save; + { + Lock l(state); + save=connection; + // Ignore frames received while closing other than closed response. + if (state==CLOSING && !isCloseResponse(frame)) + return; + } + try { + // Cast to expose private SessionHandler functions. + if (invoke(static_cast<SessionHandler&>(*this), *frame.getBody())) { + // If we were detached by a session command, tell the connection. + if (!connection) save->erase(channel); + } + else { + session->received(frame); + l3.handle(frame); + } + } catch (const ChannelException& e) { + QPID_LOG(error, "Channel exception:" << e.what()); + doClose(e.code, e.what()); + } +} + +void SessionCore::handleOut(AMQFrame& frame) +{ + Lock l(state); + if (state==OPEN) { + if (detachedLifetime > 0 && session->sent(frame)) + proxy.solicitAck(); + channel.handle(frame); + } +} + +void SessionCore::solicitAck( ) { + Lock l(state); + checkOpen(); + proxy.ack(session->sendingAck(), SequenceNumberSet()); +} + +void SessionCore::flow(bool) { + assert(0); throw NotImplementedException("session.flow"); +} + +void SessionCore::flowOk(bool /*active*/) { + assert(0); throw NotImplementedException("session.flow"); +} + +void SessionCore::highWaterMark(uint32_t /*lastSentMark*/) { + // TODO aconway 2007-10-02: may be removed from spec. + assert(0); throw NotImplementedException("session.highWaterMark"); +} + +const Uuid SessionCore::getId() const { + if (session) + return session->getId(); + throw Exception(QPID_MSG("Closed session, no ID.")); +} + +}} // namespace qpid::client diff --git a/qpid/cpp/src/qpid/client/SessionCore.h b/qpid/cpp/src/qpid/client/SessionCore.h new file mode 100644 index 0000000000..2bb0f41fbf --- /dev/null +++ b/qpid/cpp/src/qpid/client/SessionCore.h @@ -0,0 +1,141 @@ +/* + * + * 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. + * + */ + +#ifndef _SessionCore_ +#define _SessionCore_ + +#include "qpid/shared_ptr.h" +#include "qpid/framing/FrameHandler.h" +#include "qpid/framing/ChannelHandler.h" +#include "qpid/framing/SessionState.h" +#include "qpid/framing/SequenceNumber.h" +#include "qpid/framing/AMQP_ClientOperations.h" +#include "qpid/framing/AMQP_ServerProxy.h" +#include "qpid/sys/StateMonitor.h" +#include "ExecutionHandler.h" + +#include <boost/optional.hpp> + +namespace qpid { +namespace framing { +class FrameSet; +class MethodContent; +class SequenceNumberSet; +} + +namespace client { + +class Future; +class ConnectionImpl; + +/** + * Session implementation, sets up handler chains. + * Attaches to a SessionHandler when active, detaches + * when closed. + */ +class SessionCore : public framing::FrameHandler::InOutHandler, + private framing::AMQP_ClientOperations::SessionHandler +{ + public: + SessionCore(shared_ptr<ConnectionImpl>, uint16_t channel, uint64_t maxFrameSize); + ~SessionCore(); + + framing::FrameSet::shared_ptr get(); + const framing::Uuid getId() const; + uint16_t getChannel() const { return channel; } + void assertOpen() const; + + // NOTE: Public functions called in user thread. + void open(uint32_t detachedLifetime); + void close(); + void resume(shared_ptr<ConnectionImpl>); + void suspend(); + void setChannel(uint16_t channel); + + void setSync(bool s); + bool isSync(); + ExecutionHandler& getExecution(); + + Future send(const framing::AMQBody& command); + + Future send(const framing::AMQBody& command, const framing::MethodContent& content); + + void connectionClosed(uint16_t code, const std::string& text); + void connectionBroke(uint16_t code, const std::string& text); + + private: + enum State { + OPENING, + RESUMING, + OPEN, + CLOSING, + SUSPENDING, + SUSPENDED, + CLOSED + }; + typedef framing::AMQP_ClientOperations::SessionHandler SessionHandler; + typedef sys::StateMonitor<State, CLOSED> StateMonitor; + typedef StateMonitor::Set States; + + inline void invariant() const; + inline void setState(State s); + inline void waitFor(State); + void doClose(int code, const std::string& text); + void doSuspend(int code, const std::string& text); + + /** If there is an error, throw the exception */ + void check(bool condition, int code, const std::string& text) const; + /** Throw if *error */ + void check() const; + + void handleIn(framing::AMQFrame& frame); + void handleOut(framing::AMQFrame& frame); + + // Private functions are called by broker in network thread. + void attached(const framing::Uuid& sessionId, uint32_t detachedLifetime); + void flow(bool active); + void flowOk(bool active); + void detached(); + void ack(uint32_t cumulativeSeenMark, + const framing::SequenceNumberSet& seenFrameSet); + void highWaterMark(uint32_t lastSentMark); + void solicitAck(); + void closed(uint16_t code, const std::string& text); + + void attaching(shared_ptr<ConnectionImpl>); + void detach(int code, const std::string& text); + void checkOpen() const; + + int code; // Error code + std::string text; // Error text + boost::optional<framing::SessionState> session; + shared_ptr<ConnectionImpl> connection; + ExecutionHandler l3; + volatile bool sync; + framing::ChannelHandler channel; + framing::AMQP_ServerProxy::Session proxy; + mutable StateMonitor state; + uint32_t detachedLifetime; +}; + +}} // namespace qpid::client + +#endif diff --git a/qpid/cpp/src/qpid/client/StateManager.cpp b/qpid/cpp/src/qpid/client/StateManager.cpp new file mode 100644 index 0000000000..0cb3c6b9d4 --- /dev/null +++ b/qpid/cpp/src/qpid/client/StateManager.cpp @@ -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. + * + */ + +#include "StateManager.h" +#include "qpid/framing/amqp_framing.h" + +using namespace qpid::client; +using namespace qpid::framing; +using namespace qpid::sys; + +StateManager::StateManager(int s) : state(s) {} + +void StateManager::waitForStateChange(int current) +{ + Monitor::ScopedLock l(stateLock); + while (state == current) { + stateLock.wait(); + } +} + +void StateManager::waitFor(int desired) +{ + Monitor::ScopedLock l(stateLock); + while (state != desired) { + stateLock.wait(); + } +} + +void StateManager::waitFor(std::set<int> desired) +{ + Monitor::ScopedLock l(stateLock); + while (desired.find(state) == desired.end()) { + stateLock.wait(); + } +} + + +void StateManager::setState(int s) +{ + Monitor::ScopedLock l(stateLock); + state = s; + stateLock.notifyAll(); +} + +int StateManager::getState() const +{ + Monitor::ScopedLock l(stateLock); + return state; +} + diff --git a/qpid/cpp/src/qpid/client/StateManager.h b/qpid/cpp/src/qpid/client/StateManager.h new file mode 100644 index 0000000000..2f8ecb772c --- /dev/null +++ b/qpid/cpp/src/qpid/client/StateManager.h @@ -0,0 +1,46 @@ +/* + * + * 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. + * + */ +#ifndef _StateManager_ +#define _StateManager_ + +#include <set> +#include "qpid/sys/Monitor.h" + +namespace qpid { +namespace client { + +class StateManager +{ + int state; + mutable sys::Monitor stateLock; + +public: + StateManager(int initial); + void setState(int state); + int getState() const ; + void waitForStateChange(int current); + void waitFor(std::set<int> states); + void waitFor(int state); +}; + +}} + +#endif diff --git a/qpid/cpp/src/qpid/client/SubscriptionManager.cpp b/qpid/cpp/src/qpid/client/SubscriptionManager.cpp new file mode 100644 index 0000000000..f14344225c --- /dev/null +++ b/qpid/cpp/src/qpid/client/SubscriptionManager.cpp @@ -0,0 +1,111 @@ +/* + * + * 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. + * + */ +#ifndef _Subscription_ +#define _Subscription_ + +#include "SubscriptionManager.h" +#include <qpid/client/Dispatcher.h> +#include <qpid/client/Session.h> +#include <qpid/client/MessageListener.h> +#include <set> +#include <sstream> + + +namespace qpid { +namespace client { + +SubscriptionManager::SubscriptionManager(Session& s) + : dispatcher(s), session(s), + messages(UNLIMITED), bytes(UNLIMITED), window(true), + confirmMode(true), acquireMode(false), + autoStop(true) +{} + +Completion SubscriptionManager::subscribeInternal( + const std::string& q, const std::string& dest) +{ + Completion c = session.messageSubscribe(arg::queue=q, arg::destination=dest, + arg::confirmMode=confirmMode, arg::acquireMode=acquireMode); + setFlowControl(dest, messages, bytes, window); + return c; +} + +Completion SubscriptionManager::subscribe( + MessageListener& listener, const std::string& q, const std::string& d) +{ + std::string dest=d.empty() ? q:d; + dispatcher.listen(dest, &listener, autoAck); + return subscribeInternal(q, dest); +} + +Completion SubscriptionManager::subscribe( + LocalQueue& lq, const std::string& q, const std::string& d) +{ + std::string dest=d.empty() ? q:d; + lq.session=session; + lq.queue=session.getExecution().getDemux().add(dest, ByTransferDest(dest)); + return subscribeInternal(q, dest); +} + +void SubscriptionManager::setFlowControl( + const std::string& dest, uint32_t messages, uint32_t bytes, bool window) +{ + session.messageFlowMode(dest, window); + session.messageFlow(dest, 0, messages); + session.messageFlow(dest, 1, bytes); +} + +void SubscriptionManager::setFlowControl( + uint32_t messages_, uint32_t bytes_, bool window_) +{ + messages=messages_; + bytes=bytes_; + window=window_; +} + +void SubscriptionManager::setConfirmMode(bool c) { confirmMode=c; } + +void SubscriptionManager::setAcquireMode(bool a) { acquireMode=a; } + +void SubscriptionManager::setAckPolicy(const AckPolicy& a) { autoAck=a; } + +void SubscriptionManager::cancel(const std::string dest) +{ + dispatcher.cancel(dest); + session.messageCancel(dest); +} + +void SubscriptionManager::setAutoStop(bool set) { autoStop=set; } + +void SubscriptionManager::run() +{ + dispatcher.setAutoStop(autoStop); + dispatcher.run(); +} + +void SubscriptionManager::stop() +{ + dispatcher.stop(); +} + +}} // namespace qpid::client + +#endif diff --git a/qpid/cpp/src/qpid/client/SubscriptionManager.h b/qpid/cpp/src/qpid/client/SubscriptionManager.h new file mode 100644 index 0000000000..1741796f4f --- /dev/null +++ b/qpid/cpp/src/qpid/client/SubscriptionManager.h @@ -0,0 +1,141 @@ +#ifndef QPID_CLIENT_SUBSCRIPTIONMANAGER_H +#define QPID_CLIENT_SUBSCRIPTIONMANAGER_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. + * + */ +#include "qpid/sys/Mutex.h" +#include <qpid/client/Dispatcher.h> +#include <qpid/client/Completion.h> +#include <qpid/client/Session.h> +#include <qpid/client/MessageListener.h> +#include <qpid/client/LocalQueue.h> +#include <qpid/sys/Runnable.h> + +#include <set> +#include <sstream> + +namespace qpid { +namespace client { + +/** + * Utility to assist with creating subscriptions. + * + * \ingroup clientapi + */ +class SubscriptionManager : public sys::Runnable +{ + typedef sys::Mutex::ScopedLock Lock; + typedef sys::Mutex::ScopedUnlock Unlock; + + Completion subscribeInternal(const std::string& q, const std::string& dest); + + qpid::client::Dispatcher dispatcher; + qpid::client::Session& session; + uint32_t messages; + uint32_t bytes; + bool window; + AckPolicy autoAck; + bool confirmMode; + bool acquireMode; + bool autoStop; + + public: + SubscriptionManager(Session& session); + + /** + * Subscribe a MessagesListener to receive messages from queue. + * + *@param listener Listener object to receive messages. + *@param queue Name of the queue to subscribe to. + *@param tag Unique destination tag for the listener. + * If not specified, the queue name is used. + */ + Completion subscribe(MessageListener& listener, + const std::string& queue, + const std::string& tag=std::string()); + + /** + * Subscribe a LocalQueue to receive messages from queue. + * + *@param queue Name of the queue to subscribe to. + *@param tag Unique destination tag for the listener. + * If not specified, the queue name is used. + */ + Completion subscribe(LocalQueue& localQueue, + const std::string& queue, + const std::string& tag=std::string()); + + /** Cancel a subscription. */ + void cancel(const std::string tag); + + /** Deliver messages until stop() is called. */ + void run(); + + /** If set true, run() will stop when all subscriptions + * are cancelled. If false, run will only stop when stop() + * is called. True by default. + */ + void setAutoStop(bool set=true); + + /** Cause run() to return */ + void stop(); + + + static const uint32_t UNLIMITED=0xFFFFFFFF; + + /** Set the flow control for destination tag. + *@param tag: name of the destination. + *@param messages: message credit. + *@param bytes: byte credit. + *@param window: if true use window-based flow control. + */ + void setFlowControl(const std::string& tag, uint32_t messages, uint32_t bytes, bool window=true); + + /** Set the initial flow control settings to be applied to each new subscribtion. + *@param messages: message credit. + *@param bytes: byte credit. + *@param window: if true use window-based flow control. + */ + void setFlowControl(uint32_t messages, uint32_t bytes, bool window=true); + + /** Set the confirm-mode for new subscriptions. Defaults to true. + *@param confirm: if true messages must be confirmed by calling + *Message::acknowledge() or automatically, see setAckPolicy() + */ + void setConfirmMode(bool confirm); + + /** Set the acquire-mode for new subscriptions. Defaults to false. + *@param acquire: if false messages pre-acquired, if true + * messages are dequed on acknowledgement or on transfer + * depending on confirmMode. + */ + void setAcquireMode(bool acquire); + + /** Set the acknowledgement policy for new subscriptions. + * Default is to acknowledge every message automatically. + */ + void setAckPolicy(const AckPolicy& autoAck); +}; + + +}} // namespace qpid::client + +#endif /*!QPID_CLIENT_SUBSCRIPTIONMANAGER_H*/ diff --git a/qpid/cpp/src/qpid/client/TypedResult.h b/qpid/cpp/src/qpid/client/TypedResult.h new file mode 100644 index 0000000000..edcf728c54 --- /dev/null +++ b/qpid/cpp/src/qpid/client/TypedResult.h @@ -0,0 +1,51 @@ +/* + * + * 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. + * + */ + +#ifndef _TypedResult_ +#define _TypedResult_ + +#include "Completion.h" + +namespace qpid { +namespace client { + +template <class T> class TypedResult : public Completion +{ + T result; + bool decoded; + +public: + TypedResult(Future f, shared_ptr<SessionCore> s) : Completion(f, s), decoded(false) {} + + T& get() + { + if (!decoded) { + future.decodeResult(result, *session); + decoded = true; + } + + return result; + } +}; + +}} + +#endif diff --git a/qpid/cpp/src/qpid/cluster/ClassifierHandler.cpp b/qpid/cpp/src/qpid/cluster/ClassifierHandler.cpp new file mode 100644 index 0000000000..a4ea257f0c --- /dev/null +++ b/qpid/cpp/src/qpid/cluster/ClassifierHandler.cpp @@ -0,0 +1,51 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "ClassifierHandler.h" + +#include "qpid/framing/FrameDefaultVisitor.h" +#include "qpid/framing/AMQFrame.h" + +namespace qpid { +namespace cluster { + +using namespace framing; + +struct ClassifierHandler::Visitor : public FrameDefaultVisitor { + Visitor(AMQFrame& f, ClassifierHandler& c) + : chosen(0), frame(f), classifier(c) { f.getBody()->accept(*this); } + + void visit(const ExchangeDeclareBody&) { chosen=&classifier.wiring; } + void visit(const ExchangeDeleteBody&) { chosen=&classifier.wiring; } + void visit(const QueueBindBody&) { chosen=&classifier.wiring; } + void visit(const QueueDeclareBody&) { chosen=&classifier.wiring; } + void visit(const QueueDeleteBody&) { chosen=&classifier.wiring; } + void visit(const QueueUnbindBody&) { chosen=&classifier.wiring; } + void defaultVisit(const AMQBody&) { chosen=&classifier.other; } + + using framing::FrameDefaultVisitor::visit; + using framing::FrameDefaultVisitor::defaultVisit; + + FrameHandler::Chain chosen; + AMQFrame& frame; + ClassifierHandler& classifier; +}; + +void ClassifierHandler::handle(AMQFrame& f) { Visitor(f, *this).chosen(f); } + +}} // namespace qpid::cluster diff --git a/qpid/cpp/src/qpid/cluster/ClassifierHandler.h b/qpid/cpp/src/qpid/cluster/ClassifierHandler.h new file mode 100644 index 0000000000..696e457c04 --- /dev/null +++ b/qpid/cpp/src/qpid/cluster/ClassifierHandler.h @@ -0,0 +1,50 @@ +#ifndef QPID_CLUSTER_CLASSIFIERHANDLER_H +#define QPID_CLUSTER_CLASSIFIERHANDLER_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "qpid/framing/FrameHandler.h" + +namespace qpid { +namespace cluster { + +/** + * Classify frames and forward to the appropriate handler. + */ +class ClassifierHandler : public framing::FrameHandler +{ + public: + ClassifierHandler(framing::FrameHandler& wiring_, + framing::FrameHandler& other_) + : wiring(wiring_), other(other_) {} + + void handle(framing::AMQFrame&); + + private: + struct Visitor; + friend struct Visitor; + framing::FrameHandler& wiring; + framing::FrameHandler& other; +}; + +}} // namespace qpid::cluster + + + +#endif /*!QPID_CLUSTER_CLASSIFIERHANDLER_H*/ diff --git a/qpid/cpp/src/qpid/cluster/Cluster.cpp b/qpid/cpp/src/qpid/cluster/Cluster.cpp new file mode 100644 index 0000000000..5152aa2e43 --- /dev/null +++ b/qpid/cpp/src/qpid/cluster/Cluster.cpp @@ -0,0 +1,260 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "Cluster.h" +#include "qpid/broker/PreviewSessionState.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/ClusterNotifyBody.h" +#include "qpid/log/Statement.h" +#include <boost/bind.hpp> +#include <boost/scoped_array.hpp> +#include <algorithm> +#include <iterator> +#include <map> + +namespace qpid { +namespace cluster { +using namespace qpid::framing; +using namespace qpid::sys; +using namespace std; +using broker::PreviewSessionState; + +namespace { + +// Beginning of inbound chain: send to cluster. +struct ClusterSendHandler : public FrameHandler { + PreviewSessionState& session; + Cluster& cluster; + bool busy; + Monitor lock; + + ClusterSendHandler(PreviewSessionState& s, Cluster& c) : session(s), cluster(c), busy(false) {} + + void handle(AMQFrame& f) { + Mutex::ScopedLock l(lock); + assert(!busy); + // FIXME aconway 2008-01-29: refcount Sessions. + // session.addRef(); // Keep the session till the message is self delivered. + cluster.send(f, next); // Indirectly send to next via cluster. + + // FIXME aconway 2008-01-29: need to get this blocking out of the loop. + // But cluster needs to agree on order of side-effects on the shared model. + // OK for wiring to block, for messages use queue tokens? + // Both in & out transfers must be orderd per queue. + // May need out-of-order completion. + busy=true; + while (busy) lock.wait(); + } +}; + +// Next in inbound chain, self delivered from cluster. +struct ClusterDeliverHandler : public FrameHandler { + Cluster& cluster; + ClusterSendHandler& sender; + + ClusterDeliverHandler(ClusterSendHandler& prev, Cluster& c) : cluster(c), sender(prev) {} + + void handle(AMQFrame& f) { + next->handle(f); + Mutex::ScopedLock l(sender.lock); + sender.busy=false; + sender.lock.notify(); + } +}; + +// FIXME aconway 2008-01-29: IList +void insert(FrameHandler::Chain& c, FrameHandler* h) { + h->next = c.next; + c.next = h; +} + +struct SessionObserver : public broker::PreviewSessionManager::Observer { + Cluster& cluster; + SessionObserver(Cluster& c) : cluster(c) {} + + void opened(PreviewSessionState& s) { + // FIXME aconway 2008-01-29: IList for memory management. + ClusterSendHandler* sender=new ClusterSendHandler(s, cluster); + ClusterDeliverHandler* deliverer=new ClusterDeliverHandler(*sender, cluster); + insert(s.in, deliverer); + insert(s.in, sender); + } +}; +} + +ostream& operator <<(ostream& out, const Cluster& cluster) { + return out << "cluster[" << cluster.name.str() << " " << cluster.self << "]"; +} + +ostream& operator<<(ostream& out, const Cluster::MemberMap::value_type& m) { + return out << m.first << "=" << m.second.url; +} + +ostream& operator <<(ostream& out, const Cluster::MemberMap& members) { + ostream_iterator<Cluster::MemberMap::value_type> o(out, " "); + copy(members.begin(), members.end(), o); + return out; +} + +Cluster::Cluster(const std::string& name_, const Url& url_, broker::Broker&) : + cpg(*this), + name(name_), + url(url_), + observer(new SessionObserver(*this)) +{ + QPID_LOG(trace, *this << " Joining cluster: " << name_); + cpg.join(name); + notify(); + dispatcher=Thread(*this); + // Wait till we show up in the cluster map. + { + Mutex::ScopedLock l(lock); + while (empty()) + lock.wait(); + } +} + +Cluster::~Cluster() { + QPID_LOG(trace, *this << " Leaving cluster."); + try { + cpg.leave(name); + dispatcher.join(); + } + catch (const std::exception& e) { + QPID_LOG(error, "Exception leaving cluster " << *this << ": " + << e.what()); + } +} + +void Cluster::send(AMQFrame& frame, FrameHandler* next) { + QPID_LOG(trace, *this << " SEND: " << frame); + char data[65536]; // FIXME aconway 2008-01-29: Better buffer handling. + Buffer buf(data); + frame.encode(buf); + buf.putRawData((uint8_t*)&next, sizeof(next)); // Tag the frame with the next pointer. + iovec iov = { data, frame.size()+sizeof(next) }; + cpg.mcast(name, &iov, 1); +} + +void Cluster::notify() { + AMQFrame frame(in_place<ClusterNotifyBody>(ProtocolVersion(), url.str())); + send(frame, 0); +} + +size_t Cluster::size() const { + Mutex::ScopedLock l(lock); + return members.size(); +} + +Cluster::MemberList Cluster::getMembers() const { + Mutex::ScopedLock l(lock); + MemberList result(members.size()); + std::transform(members.begin(), members.end(), result.begin(), + boost::bind(&MemberMap::value_type::second, _1)); + return result; +} + +void Cluster::deliver( + cpg_handle_t /*handle*/, + cpg_name* /*group*/, + uint32_t nodeid, + uint32_t pid, + void* msg, + int msg_len) +{ + try { + Id from(nodeid, pid); + Buffer buf(static_cast<char*>(msg), msg_len); + AMQFrame frame; + frame.decode(buf); + QPID_LOG(trace, *this << " RECV: " << frame << " from: " << from); + if (frame.getChannel() == 0) + handleClusterFrame(from, frame); + else if (from == self) { + FrameHandler* next; + buf.getRawData((uint8_t*)&next, sizeof(next)); + next->handle(frame); + } + // FIXME aconway 2008-01-30: apply frames from foreign sessions. + } + catch (const std::exception& e) { + // FIXME aconway 2008-01-30: exception handling. + QPID_LOG(error, "Error handling frame from cluster " << e.what()); + } +} + +bool Cluster::wait(boost::function<bool(const Cluster&)> predicate, + Duration timeout) const +{ + AbsTime deadline(now(), timeout); + Mutex::ScopedLock l(lock); + while (!predicate(*this) && lock.wait(deadline)) + ; + return (predicate(*this)); +} + +// Handle cluster control frame from the null session. +void Cluster::handleClusterFrame(Id from, AMQFrame& frame) { + // TODO aconway 2007-06-20: use visitor pattern here. + ClusterNotifyBody* notifyIn= + dynamic_cast<ClusterNotifyBody*>(frame.getBody()); + assert(notifyIn); + MemberList list; + { + Mutex::ScopedLock l(lock); + members[from].url=notifyIn->getUrl(); + if (!self.id && notifyIn->getUrl() == url.str()) + self=from; + lock.notifyAll(); + QPID_LOG(trace, *this << ": members joined: " << members); + } +} + +void Cluster::configChange( + cpg_handle_t /*handle*/, + cpg_name */*group*/, + cpg_address */*current*/, int /*nCurrent*/, + cpg_address *left, int nLeft, + cpg_address *joined, int nJoined) +{ + bool newMembers=false; + MemberList updated; + { + Mutex::ScopedLock l(lock); + if (nLeft) { + for (int i = 0; i < nLeft; ++i) + members.erase(Id(left[i])); + QPID_LOG(trace, *this << ": members left: " << members); + lock.notifyAll(); + } + newMembers = nJoined > 1 || (nJoined==1 && Id(joined[0]) != self); + // We don't record members joining here, we record them when + // we get their ClusterNotify message. + } + if (newMembers) // Notify new members of my presence. + notify(); +} + +void Cluster::run() { + cpg.dispatchBlocking(); +} + +}} // namespace qpid::cluster + + + diff --git a/qpid/cpp/src/qpid/cluster/Cluster.h b/qpid/cpp/src/qpid/cluster/Cluster.h new file mode 100644 index 0000000000..1b0c1b1689 --- /dev/null +++ b/qpid/cpp/src/qpid/cluster/Cluster.h @@ -0,0 +1,131 @@ +#ifndef QPID_CLUSTER_CLUSTER_H +#define QPID_CLUSTER_CLUSTER_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "Cpg.h" + +#include "qpid/broker/Broker.h" +#include "qpid/sys/Monitor.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Thread.h" +#include "qpid/log/Logger.h" +#include "qpid/Url.h" + + +#include <boost/optional.hpp> +#include <boost/function.hpp> +#include <boost/intrusive_ptr.hpp> + +#include <map> +#include <vector> + +namespace qpid { namespace cluster { + +/** + * Connection to the cluster. + * Keeps cluster membership data. + */ +class Cluster : private sys::Runnable, private Cpg::Handler +{ + public: + /** Details of a cluster member */ + struct Member { + Member(const Url& url_=Url()) : url(url_) {} + Url url; ///< Broker address. + }; + + typedef std::vector<Member> MemberList; + + /** + * Join a cluster. + * @param name of the cluster. + * @param url of this broker, sent to the cluster. + */ + Cluster(const std::string& name, const Url& url, broker::Broker&); + + virtual ~Cluster(); + + // FIXME aconway 2008-01-29: + boost::intrusive_ptr<broker::PreviewSessionManager::Observer> getObserver() { return observer; } + + /** Get the current cluster membership. */ + MemberList getMembers() const; + + /** Number of members in the cluster. */ + size_t size() const; + + bool empty() const { return size() == 0; } + + /** Wait for predicate(*this) to be true, up to timeout. + *@return True if predicate became true, false if timed out. + *Note the predicate may not be true after wait returns, + *all the caller can say is it was true at some earlier point. + */ + bool wait(boost::function<bool(const Cluster&)> predicate, + sys::Duration timeout=sys::TIME_INFINITE) const; + + /** Send frame to the cluster */ + void send(framing::AMQFrame&, framing::FrameHandler*); + + private: + typedef Cpg::Id Id; + typedef std::map<Id, Member> MemberMap; + + void notify(); ///< Notify cluster of my details. + + void deliver( + cpg_handle_t /*handle*/, + struct cpg_name *group, + uint32_t /*nodeid*/, + uint32_t /*pid*/, + void* /*msg*/, + int /*msg_len*/); + + void configChange( + cpg_handle_t /*handle*/, + struct cpg_name */*group*/, + struct cpg_address */*members*/, int /*nMembers*/, + struct cpg_address */*left*/, int /*nLeft*/, + struct cpg_address */*joined*/, int /*nJoined*/ + ); + + void run(); + void handleClusterFrame(Id from, framing::AMQFrame&); + + mutable sys::Monitor lock; + Cpg cpg; + Cpg::Name name; + Url url; + Id self; + MemberMap members; + sys::Thread dispatcher; + boost::function<void()> callback; + boost::intrusive_ptr<broker::PreviewSessionManager::Observer> observer; + + friend std::ostream& operator <<(std::ostream&, const Cluster&); + friend std::ostream& operator <<(std::ostream&, const MemberMap::value_type&); + friend std::ostream& operator <<(std::ostream&, const MemberMap&); +}; + +}} // namespace qpid::cluster + + + +#endif /*!QPID_CLUSTER_CLUSTER_H*/ diff --git a/qpid/cpp/src/qpid/cluster/ClusterPlugin.cpp b/qpid/cpp/src/qpid/cluster/ClusterPlugin.cpp new file mode 100644 index 0000000000..0ea3953175 --- /dev/null +++ b/qpid/cpp/src/qpid/cluster/ClusterPlugin.cpp @@ -0,0 +1,79 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ +#include <boost/program_options/value_semantic.hpp> + + + +#include "qpid/broker/Broker.h" +#include "qpid/cluster/Cluster.h" +#include "qpid/Plugin.h" +#include "qpid/Options.h" +#include "qpid/shared_ptr.h" + +#include <boost/optional.hpp> +#include <boost/utility/in_place_factory.hpp> + + +namespace qpid { +namespace cluster { + +using namespace std; + +struct ClusterOptions : public Options { + string name; + string url; + + ClusterOptions() : Options("Cluster Options") { + addOptions() + ("cluster-name", optValue(name, "NAME"), "Name of cluster to join") + ("cluster-url", optValue(url,"URL"), + "URL of this broker, advertized to the cluster.\n" + "Defaults to a URL listing all the local IP addresses\n"); + } + + Url getUrl(uint16_t port) const { + if (url.empty()) return Url::getIpAddressesUrl(port); + return Url(url); + } +}; + +struct ClusterPlugin : public Plugin { + + ClusterOptions options; + boost::optional<Cluster> cluster; + + Options* getOptions() { return &options; } + + void earlyInitialize(Plugin::Target&) {} + + void initialize(Plugin::Target& target) { + broker::Broker* broker = dynamic_cast<broker::Broker*>(&target); + // Only provide to a Broker, and only if the --cluster config is set. + if (broker && !options.name.empty()) { + assert(!cluster); // A process can only belong to one cluster. + cluster = boost::in_place(options.name, + options.getUrl(broker->getPort()), + boost::ref(*broker)); + broker->getPreviewSessionManager().add(cluster->getObserver()); + } + } +}; + +static ClusterPlugin instance; // Static initialization. + +}} // namespace qpid::cluster diff --git a/qpid/cpp/src/qpid/cluster/Cpg.cpp b/qpid/cpp/src/qpid/cluster/Cpg.cpp new file mode 100644 index 0000000000..01d97d2a17 --- /dev/null +++ b/qpid/cpp/src/qpid/cluster/Cpg.cpp @@ -0,0 +1,176 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "Cpg.h" + +#include "qpid/sys/Mutex.h" +#include "qpid/log/Statement.h" + +#include <vector> +#include <limits> +#include <iterator> + +#include <unistd.h> + +namespace qpid { +namespace cluster { + +using namespace std; + +// Global vector of Cpg pointers by handle. +// TODO aconway 2007-06-12: Replace this with cpg_get/set_context, +// coming in in RHEL 5.1. +class Cpg::Handles +{ + public: + void put(cpg_handle_t handle, Cpg::Handler* handler) { + sys::Mutex::ScopedLock l(lock); + uint32_t index=uint32_t(handle); // Lower 32 bits is an array index. + if (index >= handles.size()) + handles.resize(index+1, 0); + handles[index] = handler; + } + + Cpg::Handler* get(cpg_handle_t handle) { + sys::Mutex::ScopedLock l(lock); + uint32_t index=uint32_t(handle); // Lower 32 bits is an array index. + assert(index < handles.size()); + assert(handles[index]); + return handles[index]; + } + + private: + sys::Mutex lock; + vector<Cpg::Handler*> handles; +}; + +Cpg::Handles Cpg::handles; + +// Global callback functions call per-object callbacks via handles vector. +void Cpg::globalDeliver ( + cpg_handle_t handle, + struct cpg_name *group, + uint32_t nodeid, + uint32_t pid, + void* msg, + int msg_len) +{ + Cpg::Handler* handler=handles.get(handle); + if (handler) + handler->deliver(handle, group, nodeid, pid, msg, msg_len); +} + +void Cpg::globalConfigChange( + cpg_handle_t handle, + struct cpg_name *group, + struct cpg_address *members, int nMembers, + struct cpg_address *left, int nLeft, + struct cpg_address *joined, int nJoined +) +{ + Cpg::Handler* handler=handles.get(handle); + if (handler) + handler->configChange(handle, group, members, nMembers, left, nLeft, joined, nJoined); +} + +Cpg::Cpg(Handler& h) : handler(h) { + cpg_callbacks_t callbacks = { &globalDeliver, &globalConfigChange }; + check(cpg_initialize(&handle, &callbacks), "Cannot initialize CPG"); + handles.put(handle, &handler); + QPID_LOG(debug, "Initialize CPG handle " << handle); +} + +Cpg::~Cpg() { + try { + shutdown(); + } catch (const std::exception& e) { + QPID_LOG(error, "Exception in Cpg destructor: " << e.what()); + } +} + +void Cpg::shutdown() { + QPID_LOG(debug, "Shutdown CPG handle " << handle); + if (handles.get(handle)) { + QPID_LOG(debug, "Finalize CPG handle " << handle); + handles.put(handle, 0); + check(cpg_finalize(handle), "Error in shutdown of CPG"); + } +} + +string Cpg::errorStr(cpg_error_t err, const std::string& msg) { + switch (err) { + case CPG_OK: return msg+": ok"; + case CPG_ERR_LIBRARY: return msg+": library"; + case CPG_ERR_TIMEOUT: return msg+": timeout"; + case CPG_ERR_TRY_AGAIN: return msg+": timeout. The aisexec daemon may not be running"; + case CPG_ERR_INVALID_PARAM: return msg+": invalid param"; + case CPG_ERR_NO_MEMORY: return msg+": no memory"; + case CPG_ERR_BAD_HANDLE: return msg+": bad handle"; + case CPG_ERR_ACCESS: return msg+": access denied. You may need to set your group ID to 'ais'"; + case CPG_ERR_NOT_EXIST: return msg+": not exist"; + case CPG_ERR_EXIST: return msg+": exist"; + case CPG_ERR_NOT_SUPPORTED: return msg+": not supported"; + case CPG_ERR_SECURITY: return msg+": security"; + case CPG_ERR_TOO_MANY_GROUPS: return msg+": too many groups"; + default: + assert(0); + return ": unknown"; + }; +} + +std::string Cpg::cantJoinMsg(const Name& group) { + return "Cannot join CPG group "+group.str(); +} + +std::string Cpg::cantLeaveMsg(const Name& group) { + return "Cannot leave CPG group "+group.str(); +} + +std::string Cpg::cantMcastMsg(const Name& group) { + return "Cannot mcast to CPG group "+group.str(); +} + +ostream& operator<<(ostream& o, std::pair<cpg_address*,int> a) { + ostream_iterator<Cpg::Id> i(o, " "); + std::copy(a.first, a.first+a.second, i); + return o; +} + +static int popbyte(uint32_t& n) { + uint8_t b=n&0xff; + n>>=8; + return b; +} + +ostream& operator <<(ostream& out, const Cpg::Id& id) { + uint32_t node=id.nodeId(); + out << popbyte(node); + for (int i = 0; i < 3; i++) + out << "." << popbyte(node); + return out << ":" << id.pid(); +} + +ostream& operator <<(ostream& out, const cpg_name& name) { + return out << string(name.value, name.length); +} + + +}} // namespace qpid::cluster + + + diff --git a/qpid/cpp/src/qpid/cluster/Cpg.h b/qpid/cpp/src/qpid/cluster/Cpg.h new file mode 100644 index 0000000000..2a7ec459d3 --- /dev/null +++ b/qpid/cpp/src/qpid/cluster/Cpg.h @@ -0,0 +1,185 @@ +#ifndef CPG_H +#define CPG_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "qpid/Exception.h" +#include "qpid/cluster/Dispatchable.h" + +#include <cassert> +#include <string.h> + +extern "C" { +#include <openais/cpg.h> +} + +namespace qpid { +namespace cluster { + +/** + * Lightweight C++ interface to cpg.h operations. + * Manages a single CPG handle, initialized in ctor, finialzed in destructor. + * On error all functions throw Cpg::Exception + */ +class Cpg : public Dispatchable { + public: + struct Exception : public ::qpid::Exception { + Exception(const std::string& msg) : ::qpid::Exception(msg) {} + }; + + struct Name : public cpg_name { + Name(const char* s) { copy(s, strlen(s)); } + Name(const char* s, size_t n) { copy(s,n); } + Name(const std::string& s) { copy(s.data(), s.size()); } + void copy(const char* s, size_t n) { + assert(n < CPG_MAX_NAME_LENGTH); + memcpy(value, s, n); + length=n; + } + + std::string str() const { return std::string(value, length); } + }; + + struct Id { + uint64_t id; + Id(uint64_t n=0) : id(n) {} + Id(uint32_t nodeid, uint32_t pid) { id=(uint64_t(nodeid)<<32)+ pid; } + Id(const cpg_address& addr) : id(Id(addr.nodeid, addr.pid)) {} + + operator uint64_t() const { return id; } + uint32_t nodeId() const { return id >> 32; } + pid_t pid() const { return id & 0xFFFF; } + }; + + static std::string str(const cpg_name& n) { + return std::string(n.value, n.length); + } + + struct Handler { + virtual ~Handler() {}; + virtual void deliver( + cpg_handle_t /*handle*/, + struct cpg_name *group, + uint32_t /*nodeid*/, + uint32_t /*pid*/, + void* /*msg*/, + int /*msg_len*/) = 0; + + virtual void configChange( + cpg_handle_t /*handle*/, + struct cpg_name */*group*/, + struct cpg_address */*members*/, int /*nMembers*/, + struct cpg_address */*left*/, int /*nLeft*/, + struct cpg_address */*joined*/, int /*nJoined*/ + ) = 0; + }; + + /** Open a CPG handle. + *@param handler for CPG events. + */ + Cpg(Handler&); + + /** Destructor calls shutdown. */ + ~Cpg(); + + /** Disconnect from CPG */ + void shutdown(); + + /** Dispatch CPG events. + *@param type one of + * - CPG_DISPATCH_ONE - dispatch exactly one event. + * - CPG_DISPATCH_ALL - dispatch all available events, don't wait. + * - CPG_DISPATCH_BLOCKING - blocking dispatch loop. + */ + void dispatch(cpg_dispatch_t type) { + check(cpg_dispatch(handle,type), "Error in CPG dispatch"); + } + + void dispatchOne() { dispatch(CPG_DISPATCH_ONE); } + void dispatchAll() { dispatch(CPG_DISPATCH_ALL); } + void dispatchBlocking() { dispatch(CPG_DISPATCH_BLOCKING); } + + void join(const Name& group) { + check(cpg_join(handle, const_cast<Name*>(&group)),cantJoinMsg(group)); + }; + + void leave(const Name& group) { + check(cpg_leave(handle,const_cast<Name*>(&group)),cantLeaveMsg(group)); + } + + void mcast(const Name& group, const iovec* iov, int iovLen) { + check(cpg_mcast_joined( + handle, CPG_TYPE_AGREED, const_cast<iovec*>(iov), iovLen), + cantMcastMsg(group)); + } + + cpg_handle_t getHandle() const { return handle; } + + private: + class Handles; + struct ClearHandleOnExit; + friend class Handles; + friend struct ClearHandleOnExit; + + static std::string errorStr(cpg_error_t err, const std::string& msg); + static std::string cantJoinMsg(const Name&); + static std::string cantLeaveMsg(const Name&); + static std::string cantMcastMsg(const Name&); + + static void check(cpg_error_t result, const std::string& msg) { + // TODO aconway 2007-06-01: Logging and exceptions. + if (result != CPG_OK) + throw Exception(errorStr(result, msg)); + } + + static void globalDeliver( + cpg_handle_t /*handle*/, + struct cpg_name *group, + uint32_t /*nodeid*/, + uint32_t /*pid*/, + void* /*msg*/, + int /*msg_len*/); + + static void globalConfigChange( + cpg_handle_t /*handle*/, + struct cpg_name */*group*/, + struct cpg_address */*members*/, int /*nMembers*/, + struct cpg_address */*left*/, int /*nLeft*/, + struct cpg_address */*joined*/, int /*nJoined*/ + ); + + static Handles handles; + cpg_handle_t handle; + Handler& handler; +}; + +std::ostream& operator <<(std::ostream& out, const cpg_name& name); +std::ostream& operator <<(std::ostream& out, const Cpg::Id& id); +std::ostream& operator <<(std::ostream& out, const std::pair<cpg_address*,int> addresses); + +inline bool operator==(const cpg_name& a, const cpg_name& b) { + return a.length==b.length && strncmp(a.value, b.value, a.length) == 0; +} +inline bool operator!=(const cpg_name& a, const cpg_name& b) { return !(a == b); } + +}} // namespace qpid::cluster + + + +#endif /*!CPG_H*/ diff --git a/qpid/cpp/src/qpid/cluster/Dispatchable.h b/qpid/cpp/src/qpid/cluster/Dispatchable.h new file mode 100644 index 0000000000..e7f0df4218 --- /dev/null +++ b/qpid/cpp/src/qpid/cluster/Dispatchable.h @@ -0,0 +1,52 @@ +#ifndef QPID_CLUSTER_DISPATCHABLE_H +#define QPID_CLUSTER_DISPATCHABLE_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +namespace qpid { +namespace cluster { + +/** + * Interface for classes that have some "events" that need dispatching + * in a thread. + */ +class Dispatchable +{ + public: + virtual ~Dispatchable() {} + + /** Dispatch one event in current thread. */ + virtual void dispatchOne() = 0; + /** Dispatch all available events, don't block. */ + virtual void dispatchAll() = 0; + /** Blocking loop to dispatch cluster events */ + virtual void dispatchBlocking() = 0; + + /** Wait for at least one event, then dispatch all available events. + * Don't block. Useful for tests. + */ + virtual void dispatchSome() { dispatchOne(); dispatchAll(); } + +}; + +}} // namespace qpid::cluster + + + +#endif /*!QPID_CLUSTER_DISPATCHABLE_H*/ diff --git a/qpid/cpp/src/qpid/doxygen_mainpage.h b/qpid/cpp/src/qpid/doxygen_mainpage.h new file mode 100644 index 0000000000..b354238cd0 --- /dev/null +++ b/qpid/cpp/src/qpid/doxygen_mainpage.h @@ -0,0 +1,45 @@ +// This header file is just for doxygen documentation purposes. + +/*!\mainpage Qpid C++ Developer Kit. + * + *\section intro_sec Introduction + * + * The <a href=http://incubator.apache.org/qpid/index.html>Qpid project</a> provides implementations of the <a href="http://amqp.org/">AMQP messaging specification</a> in several programming language. + * + * Qpidc provides APIs and libraries to implement AMQP + * clients in C++. Qpidc clients can interact with any compliant AMQP + * message broker. The Qpid project also provides an AMQP broker + * daemon called qpidd that you can use with your qpidc clients. + * + *\section install_sec Installation + * + * If you are installing from the source distribution + <pre> + > ./configure && make + > make install </pre> + * This will build and install the client development kit and the broker + * in standard places. Use + * <code>./configure --help</code> for more options. + * + * You can also install from RPMs with the <code>rpm -i</code> command. + * You will need + * - <code>qpidc</code> for core libraries. + * - <code>qpidc-devel</code> for header files and developer documentation. + * - <code>qpidd</code> for the broker daemon. + * + *\section getstart_sec Getting Started + * + * If you have installed in the standard places you should use + * these compile flags: + * + *<code> -I/usr/include/qpidc -I/usr/include/qpidc/framing -I/usr/include/qpidc/sys</code> + * + * and these link flags: + * + *<code> -lqpidcommon -lqpidclient</code> + * + * If you have installed somewhere else you should modify the flags + * appropriately. + * + * See the \ref clientapi "client API module" for more on the client API. + */ diff --git a/qpid/cpp/src/qpid/framing/AMQBody.cpp b/qpid/cpp/src/qpid/framing/AMQBody.cpp new file mode 100644 index 0000000000..b3eeae0615 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/AMQBody.cpp @@ -0,0 +1,64 @@ +/* + * + * 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. + * + */ + +#include "qpid/framing/AMQBody.h" +#include "qpid/framing/AMQMethodBody.h" +#include "qpid/framing/AMQHeaderBody.h" +#include "qpid/framing/AMQContentBody.h" +#include "qpid/framing/AMQHeartbeatBody.h" +#include <iostream> + +namespace qpid { +namespace framing { + +std::ostream& operator<<(std::ostream& out, const AMQBody& body) +{ + body.print(out); + return out; +} + +AMQBody::~AMQBody() {} + +namespace { +struct MatchBodies : public AMQBodyConstVisitor { + const AMQBody& body; + bool match; + + MatchBodies(const AMQBody& b) : body(b), match(false) {} + virtual ~MatchBodies() {} + + virtual void visit(const AMQHeaderBody&) { match=dynamic_cast<const AMQHeaderBody*>(&body); } + virtual void visit(const AMQContentBody&) { match=dynamic_cast<const AMQContentBody*>(&body); } + virtual void visit(const AMQHeartbeatBody&) { match=dynamic_cast<const AMQHeartbeatBody*>(&body); } + virtual void visit(const AMQMethodBody& x) { + const AMQMethodBody* y=dynamic_cast<const AMQMethodBody*>(&body); + match = (y && y->amqpMethodId() == x.amqpMethodId() && y->amqpClassId() == x.amqpClassId()); + } +}; + +} +bool AMQBody::match(const AMQBody& a, const AMQBody& b) { + MatchBodies matcher(a); + b.accept(matcher); + return matcher.match; +} + +}} // namespace diff --git a/qpid/cpp/src/qpid/framing/AMQBody.h b/qpid/cpp/src/qpid/framing/AMQBody.h new file mode 100644 index 0000000000..f3bf65470c --- /dev/null +++ b/qpid/cpp/src/qpid/framing/AMQBody.h @@ -0,0 +1,78 @@ +#ifndef QPID_FRAMING_AMQBODY_H +#define QPID_FRAMING_AMQBODY_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. + * + */ +#include "qpid/framing/amqp_types.h" + +#include <ostream> + +namespace qpid { +namespace framing { + +class Buffer; + +class AMQMethodBody; +class AMQHeaderBody; +class AMQContentBody; +class AMQHeartbeatBody; + +struct AMQBodyConstVisitor { + virtual ~AMQBodyConstVisitor() {} + virtual void visit(const AMQHeaderBody&) = 0; + virtual void visit(const AMQContentBody&) = 0; + virtual void visit(const AMQHeartbeatBody&) = 0; + virtual void visit(const AMQMethodBody&) = 0; +}; + +class AMQBody +{ + public: + virtual ~AMQBody(); + + virtual uint8_t type() const = 0; + + virtual void encode(Buffer& buffer) const = 0; + virtual void decode(Buffer& buffer, uint32_t=0) = 0; + virtual uint32_t size() const = 0; + + virtual void print(std::ostream& out) const = 0; + virtual void accept(AMQBodyConstVisitor&) const = 0; + + virtual AMQMethodBody* getMethod() { return 0; } + virtual const AMQMethodBody* getMethod() const { return 0; } + + /** Match if same type and same class/method ID for methods */ + static bool match(const AMQBody& , const AMQBody& ); +}; + +std::ostream& operator<<(std::ostream& out, const AMQBody& body) ; + +enum BodyTypes { + METHOD_BODY = 1, + HEADER_BODY = 2, + CONTENT_BODY = 3, + HEARTBEAT_BODY = 8 +}; + +}} // namespace qpid::framing + +#endif /*!QPID_FRAMING_AMQBODY_H*/ diff --git a/qpid/cpp/src/qpid/framing/AMQCommandControlBody.h b/qpid/cpp/src/qpid/framing/AMQCommandControlBody.h new file mode 100644 index 0000000000..388fb48299 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/AMQCommandControlBody.h @@ -0,0 +1,70 @@ +#ifndef QPID_FRAMING_AMQCOMMANDCONTROLBODY_H +#define QPID_FRAMING_AMQCOMMANDCONTROLBODY_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. + * + */ + +#include "qpid/amqp_0_10/helpers.h" +#include "qpid/framing/AMQBody.h" + +namespace qpid { +namespace framing { + +/** + * AMQBody wrapper for Command and Control. + * Temporary measure to fit with old code. + */ +template <class T> class AMQCommandControlBody : public AMQBody, public T +{ + public: + virtual uint8_t type() const { return 100+T::SEGMENT_TYPE; } + + virtual void encode(Buffer& buffer) const { + Codec::encode(buffer.getIterator(), static_cast<const T&>(*this)); + } + virtual void decode(Buffer& buffer, uint32_t=0) { + Codec::decode(buffer.getIterator(), static_cast<T&>(*this)); + } + virtual uint32_t size() const { + Codec::size(buffer.getIterator(), static_cast<const T&>(*this)); + } + + virtual void print(std::ostream& out) const { + out << static_cast<const T&>(*this) << endl; + } + virtual void AMQBody::accept(AMQBodyConstVisitor&) const { assert(0); } +}; + +class CommandBody : public AMQCommandControlBody<amqp_0_10::Command> { + using Command::accept; // Hide AMQBody::accept + virtual Command* getCommand() { return this; } + virtual const Command* getCommand() const { return this; } +}; + +class ControlBody : public AMQCommandControlBody<amqp_0_10::Control> { + using Control::accept; // Hide AMQBody::accept + virtual Control* getControl() { return this; } + virtual const Control* getControl() const { return this; } +}; + +}} // namespace qpid::framing + +#endif /*!QPID_FRAMING_AMQCOMMANDCONTROLBODY_H*/ diff --git a/qpid/cpp/src/qpid/framing/AMQContentBody.cpp b/qpid/cpp/src/qpid/framing/AMQContentBody.cpp new file mode 100644 index 0000000000..59f3619ef2 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/AMQContentBody.cpp @@ -0,0 +1,44 @@ +/* + * + * 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. + * + */ +#include "AMQContentBody.h" +#include <iostream> + +qpid::framing::AMQContentBody::AMQContentBody(){ +} + +qpid::framing::AMQContentBody::AMQContentBody(const string& _data) : data(_data){ +} + +uint32_t qpid::framing::AMQContentBody::size() const{ + return data.size(); +} +void qpid::framing::AMQContentBody::encode(Buffer& buffer) const{ + buffer.putRawData(data); +} +void qpid::framing::AMQContentBody::decode(Buffer& buffer, uint32_t _size){ + buffer.getRawData(data, _size); +} + +void qpid::framing::AMQContentBody::print(std::ostream& out) const +{ + out << "content (" << size() << " bytes)"; + out << " " << data.substr(0,16) << "..."; +} diff --git a/qpid/cpp/src/qpid/framing/AMQContentBody.h b/qpid/cpp/src/qpid/framing/AMQContentBody.h new file mode 100644 index 0000000000..5d530a1b9a --- /dev/null +++ b/qpid/cpp/src/qpid/framing/AMQContentBody.h @@ -0,0 +1,53 @@ +/* + * + * 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. + * + */ +#include "amqp_types.h" +#include "AMQBody.h" +#include "Buffer.h" + +#ifndef _AMQContentBody_ +#define _AMQContentBody_ + +namespace qpid { +namespace framing { + +class AMQContentBody : public AMQBody +{ + string data; + +public: + AMQContentBody(); + AMQContentBody(const string& data); + inline virtual ~AMQContentBody(){} + inline uint8_t type() const { return CONTENT_BODY; }; + inline const string& getData() const { return data; } + inline string& getData() { return data; } + uint32_t size() const; + void encode(Buffer& buffer) const; + void decode(Buffer& buffer, uint32_t size); + void print(std::ostream& out) const; + void accept(AMQBodyConstVisitor& v) const { v.visit(*this); } +}; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/framing/AMQDataBlock.h b/qpid/cpp/src/qpid/framing/AMQDataBlock.h new file mode 100644 index 0000000000..9b6fdfd966 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/AMQDataBlock.h @@ -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. + * + */ +#include "Buffer.h" + +#ifndef _AMQDataBlock_ +#define _AMQDataBlock_ + +namespace qpid { +namespace framing { + +class AMQDataBlock +{ +public: + virtual ~AMQDataBlock() {} + virtual void encode(Buffer& buffer) const = 0; + virtual bool decode(Buffer& buffer) = 0; + virtual uint32_t size() const = 0; +}; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/framing/AMQFrame.cpp b/qpid/cpp/src/qpid/framing/AMQFrame.cpp new file mode 100644 index 0000000000..eeb658600d --- /dev/null +++ b/qpid/cpp/src/qpid/framing/AMQFrame.cpp @@ -0,0 +1,123 @@ +/* + * + * 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. + * + */ +#include "AMQFrame.h" + +#include "qpid/framing/variant.h" +#include "qpid/framing/AMQMethodBody.h" +#include "qpid/framing/reply_exceptions.h" + +#include <boost/format.hpp> + +#include <iostream> + +namespace qpid { +namespace framing { + +AMQFrame::~AMQFrame() {} + +void AMQFrame::setBody(const AMQBody& b) { body = new BodyHolder(b); } + +void AMQFrame::setMethod(ClassId c, MethodId m) { body = new BodyHolder(c,m); } + +// This is now misleadingly named as it is not the frame size as +// defined in the spec (as it also includes the end marker) +uint32_t AMQFrame::size() const { + return frameOverhead() + body->size(); +} + +uint32_t AMQFrame::frameOverhead() { + return 12 /*frame header*/ + 1/*0xCE*/; +} + +void AMQFrame::encode(Buffer& buffer) const +{ + //set track first (controls on track 0, everything else on 1): + uint8_t track = getBody()->type() ? 1 : 0; + + uint8_t flags = (bof ? 0x08 : 0) | (eof ? 0x04 : 0) | (bos ? 0x02 : 0) | (eos ? 0x01 : 0); + buffer.putOctet(flags); + buffer.putOctet(getBody()->type()); + buffer.putShort(size() - 1); // Don't include end marker (it's not part of the frame itself) + buffer.putOctet(0); + buffer.putOctet(0x0f & track); + buffer.putShort(channel); + buffer.putLong(0); + body->encode(buffer); + buffer.putOctet(0xCE); +} + +bool AMQFrame::decode(Buffer& buffer) +{ + if(buffer.available() < frameOverhead() - 1) + return false; + buffer.record(); + + uint8_t flags = buffer.getOctet(); + uint8_t framing_version = (flags & 0xc0) >> 6; + if (framing_version != 0) + throw SyntaxErrorException(QPID_MSG("Framing version unsupported")); + bof = flags & 0x08; + eof = flags & 0x04; + bos = flags & 0x02; + eos = flags & 0x01; + uint8_t type = buffer.getOctet(); + uint16_t frame_size = buffer.getShort(); + if (frame_size < frameOverhead()-1) + throw SyntaxErrorException(QPID_MSG("Frame size too small")); + uint8_t reserved1 = buffer.getOctet(); + uint8_t field1 = buffer.getOctet(); + subchannel = field1 & 0x0f; + channel = buffer.getShort(); + (void) buffer.getLong(); // reserved2 + + // Verify that the protocol header meets current spec + // TODO: should we check reserved2 against zero as well? - the + // spec isn't clear + if ((flags & 0x30) != 0 || reserved1 != 0 || (field1 & 0xf0) != 0) + throw SyntaxErrorException(QPID_MSG("Reserved bits not zero")); + + // TODO: should no longer care about body size and only pass up + // B,E,b,e flags + uint16_t body_size = frame_size + 1 - frameOverhead(); + if (buffer.available() < body_size+1u){ + buffer.restore(); + return false; + } + body = new BodyHolder(); + body->decode(type,buffer, body_size); + uint8_t end = buffer.getOctet(); + if (end != 0xCE) + throw SyntaxErrorException(QPID_MSG("Frame end not found")); + return true; +} + +std::ostream& operator<<(std::ostream& out, const AMQFrame& f) +{ + return + out << "Frame[" + << (f.getBof() ? "B" : "") << (f.getEof() ? "E" : "") + << (f.getBos() ? "b" : "") << (f.getEos() ? "e" : "") << "; " + << "channel=" << f.getChannel() << "; " << *f.getBody() + << "]"; +} + + +}} // namespace qpid::framing diff --git a/qpid/cpp/src/qpid/framing/AMQFrame.h b/qpid/cpp/src/qpid/framing/AMQFrame.h new file mode 100644 index 0000000000..649a65bce4 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/AMQFrame.h @@ -0,0 +1,112 @@ +#ifndef _AMQFrame_ +#define _AMQFrame_ + +/* + * + * 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. + * + */ +#include "AMQDataBlock.h" +#include "AMQHeaderBody.h" +#include "AMQContentBody.h" +#include "AMQHeartbeatBody.h" +#include "ProtocolVersion.h" +#include "BodyHolder.h" + +#include <boost/intrusive_ptr.hpp> +#include <boost/cast.hpp> + +namespace qpid { +namespace framing { + +class BodyHolder; + +class AMQFrame : public AMQDataBlock +{ + public: + AMQFrame(boost::intrusive_ptr<BodyHolder> b=0) : body(b) { init(); } + AMQFrame(const AMQBody& b) { setBody(b); init(); } + ~AMQFrame(); + + template <class InPlace> + AMQFrame(const InPlace& ip, typename EnableInPlace<InPlace>::type* =0) { + init(); setBody(ip); + } + + ChannelId getChannel() const { return channel; } + void setChannel(ChannelId c) { channel = c; } + + boost::intrusive_ptr<BodyHolder> getHolder() { return body; } + + AMQBody* getBody() { return body ? body->get() : 0; } + const AMQBody* getBody() const { return body ? body->get() : 0; } + + AMQMethodBody* getMethod() { return getBody()->getMethod(); } + const AMQMethodBody* getMethod() const { return getBody()->getMethod(); } + + void setBody(const AMQBody& b); + + template <class InPlace> + typename EnableInPlace<InPlace>::type setBody(const InPlace& ip) { + body = new BodyHolder(ip); + } + + void setMethod(ClassId c, MethodId m); + + template <class T> T* castBody() { + return boost::polymorphic_downcast<T*>(getBody()); + } + + template <class T> const T* castBody() const { + return boost::polymorphic_downcast<const T*>(getBody()); + } + + void encode(Buffer& buffer) const; + bool decode(Buffer& buffer); + uint32_t size() const; + + bool getBof() const { return bof; } + void setBof(bool isBof) { bof = isBof; } + bool getEof() const { return eof; } + void setEof(bool isEof) { eof = isEof; } + + bool getBos() const { return bos; } + void setBos(bool isBos) { bos = isBos; } + bool getEos() const { return eos; } + void setEos(bool isEos) { eos = isEos; } + + static uint32_t frameOverhead(); + + private: + void init() { bof = eof = bos = eos = true; subchannel=0; channel=0; } + + boost::intrusive_ptr<BodyHolder> body; + uint16_t channel : 16; + uint8_t subchannel : 8; + bool bof : 1; + bool eof : 1; + bool bos : 1; + bool eos : 1; +}; + +std::ostream& operator<<(std::ostream&, const AMQFrame&); + +}} // namespace qpid::framing + + +#endif diff --git a/qpid/cpp/src/qpid/framing/AMQHeaderBody.cpp b/qpid/cpp/src/qpid/framing/AMQHeaderBody.cpp new file mode 100644 index 0000000000..724c288705 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/AMQHeaderBody.cpp @@ -0,0 +1,63 @@ +/* + * + * 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. + * + */ +#include "AMQHeaderBody.h" +#include "qpid/Exception.h" +#include "qpid/log/Statement.h" + +uint32_t qpid::framing::AMQHeaderBody::size() const { + return properties.size(); +} + +void qpid::framing::AMQHeaderBody::encode(Buffer& buffer) const { + properties.encode(buffer); +} + +void qpid::framing::AMQHeaderBody::decode(Buffer& buffer, uint32_t size) { + uint32_t limit = buffer.available() - size; + while (buffer.available() > limit + 2) { + uint32_t len = buffer.getLong(); + uint16_t type = buffer.getShort(); + if (!properties.decode(buffer, len, type)) { + // TODO: should just skip & keep for later dispatch. + throw Exception(QPID_MSG("Unexpected property type: " << type)); + } + } +} + +uint64_t qpid::framing::AMQHeaderBody::getContentLength() const +{ + const MessageProperties* mProps = get<MessageProperties>(); + if (mProps) + return mProps->getContentLength(); + return 0; +} + +void qpid::framing::AMQHeaderBody::print(std::ostream& out) const +{ + out << "header (" << size() << " bytes)"; + out << "; properties={"; + properties.print(out); + out << "}"; +} + +void qpid::framing::AMQHeaderBody::accept(AMQBodyConstVisitor& v) const { + v.visit(*this); +} diff --git a/qpid/cpp/src/qpid/framing/AMQHeaderBody.h b/qpid/cpp/src/qpid/framing/AMQHeaderBody.h new file mode 100644 index 0000000000..8a3a92936e --- /dev/null +++ b/qpid/cpp/src/qpid/framing/AMQHeaderBody.h @@ -0,0 +1,113 @@ +#ifndef QPID_FRAMING_AMQHEADERBODY_H +#define QPID_FRAMING_AMQHEADERBODY_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. + * + */ +#include "amqp_types.h" +#include "AMQBody.h" +#include "Buffer.h" +#include "qpid/framing/DeliveryProperties.h" +#include "qpid/framing/MessageProperties.h" +#include "qpid/framing/DeliveryProperties010.h" +#include "qpid/framing/MessageProperties010.h" +#include <iostream> + +#include <boost/optional.hpp> + + +namespace qpid { +namespace framing { + +enum DeliveryMode { TRANSIENT = 1, PERSISTENT = 2}; + +class AMQHeaderBody : public AMQBody +{ + template <class T> struct OptProps { boost::optional<T> props; }; + template <class Base, class T> + struct PropSet : public Base, public OptProps<T> { + uint32_t size() const { + const boost::optional<T>& p=this->OptProps<T>::props; + return (p ? p->size() : 0) + Base::size(); + } + void encode(Buffer& buffer) const { + const boost::optional<T>& p=this->OptProps<T>::props; + if (p) p->encode(buffer); + Base::encode(buffer); + } + bool decode(Buffer& buffer, uint32_t size, uint16_t type) { + boost::optional<T>& p=this->OptProps<T>::props; + if (type == T::TYPE) { + p=T(); + p->decodeStructBody(buffer, size); + return true; + } + else + return Base::decode(buffer, size, type); + } + void print(std::ostream& out) const { + const boost::optional<T>& p=this->OptProps<T>::props; + if (p) out << *p; + Base::print(out); + } + }; + + struct Empty { + uint32_t size() const { return 0; } + void encode(Buffer&) const {}; + bool decode(Buffer&, uint32_t, uint16_t) const { return false; }; + void print(std::ostream&) const {} + }; + + // Could use boost::mpl::fold to construct a larger set. + typedef PropSet< PropSet< PropSet<PropSet<Empty, DeliveryProperties>, + MessageProperties>, + DeliveryProperties010>, + MessageProperties010> Properties; + + Properties properties; + +public: + + inline uint8_t type() const { return HEADER_BODY; } + + uint32_t size() const; + void encode(Buffer& buffer) const; + void decode(Buffer& buffer, uint32_t size); + uint64_t getContentLength() const; + void print(std::ostream& out) const; + void accept(AMQBodyConstVisitor&) const; + + template <class T> T* get(bool create) { + boost::optional<T>& p=properties.OptProps<T>::props; + if (create && !p) p=T(); + return p.get_ptr(); + } + + template <class T> const T* get() const { + return properties.OptProps<T>::props.get_ptr(); + } +}; + +}} + + + +#endif /*!QPID_FRAMING_AMQHEADERBODY_H*/ diff --git a/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.cpp b/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.cpp new file mode 100644 index 0000000000..140ce2e794 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.cpp @@ -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. + * + */ + +#include "AMQHeartbeatBody.h" +#include <iostream> + +qpid::framing::AMQHeartbeatBody::~AMQHeartbeatBody() {} + +void qpid::framing::AMQHeartbeatBody::print(std::ostream& out) const { + out << "heartbeat"; +} diff --git a/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.h b/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.h new file mode 100644 index 0000000000..a2701c3398 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/AMQHeartbeatBody.h @@ -0,0 +1,46 @@ +/* + * + * 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. + * + */ +#include "amqp_types.h" +#include "AMQBody.h" +#include "Buffer.h" + +#ifndef _AMQHeartbeatBody_ +#define _AMQHeartbeatBody_ + +namespace qpid { +namespace framing { + +class AMQHeartbeatBody : public AMQBody +{ +public: + virtual ~AMQHeartbeatBody(); + inline uint32_t size() const { return 0; } + inline uint8_t type() const { return HEARTBEAT_BODY; } + inline void encode(Buffer& ) const {} + inline void decode(Buffer& , uint32_t /*size*/) {} + virtual void print(std::ostream& out) const; + void accept(AMQBodyConstVisitor& v) const { v.visit(*this); } +}; + +} +} + +#endif diff --git a/qpid/cpp/src/qpid/framing/AMQMethodBody.cpp b/qpid/cpp/src/qpid/framing/AMQMethodBody.cpp new file mode 100644 index 0000000000..924d906d43 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/AMQMethodBody.cpp @@ -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. + * + */ +#include "AMQMethodBody.h" + +namespace qpid { +namespace framing { + +AMQMethodBody::~AMQMethodBody() {} + +}} // namespace qpid::framing diff --git a/qpid/cpp/src/qpid/framing/AMQMethodBody.h b/qpid/cpp/src/qpid/framing/AMQMethodBody.h new file mode 100644 index 0000000000..da28ee3aa9 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/AMQMethodBody.h @@ -0,0 +1,72 @@ +#ifndef _AMQMethodBody_ +#define _AMQMethodBody_ + +/* + * + * 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. + * + */ +#include "amqp_types.h" +#include "AMQBody.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/shared_ptr.h" + +#include <ostream> + +#include <assert.h> + +namespace qpid { +namespace framing { + +class Buffer; +class AMQP_ServerOperations; +class MethodBodyConstVisitor; + +class AMQMethodBody : public AMQBody { + public: + AMQMethodBody() {} + virtual ~AMQMethodBody(); + + virtual void accept(MethodBodyConstVisitor&) const = 0; + + virtual MethodId amqpMethodId() const = 0; + virtual ClassId amqpClassId() const = 0; + virtual bool isContentBearing() const = 0; + virtual bool resultExpected() const = 0; + virtual bool responseExpected() const = 0; + + template <class T> bool isA() const { + return amqpClassId()==T::CLASS_ID && amqpMethodId()==T::METHOD_ID; + } + + virtual uint32_t size() const = 0; + virtual uint8_t type() const { return METHOD_BODY; } + + virtual bool isSync() const { return false; /*only ModelMethods can have the sync flag set*/ } + virtual void setSync(bool) const { /*only ModelMethods can have the sync flag set*/ } + + AMQMethodBody* getMethod() { return this; } + const AMQMethodBody* getMethod() const { return this; } + void accept(AMQBodyConstVisitor& v) const { v.visit(*this); } +}; + + +}} // namespace qpid::framing + + +#endif diff --git a/qpid/cpp/src/qpid/framing/AMQP_HighestVersion.h b/qpid/cpp/src/qpid/framing/AMQP_HighestVersion.h new file mode 100644 index 0000000000..b15e14d6f6 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/AMQP_HighestVersion.h @@ -0,0 +1,41 @@ +/* + * + * 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 used to be auto-generated by Qpid Gentools v.0.1 + * its here temporarily until we get a full solution to multi-version support + */ +#ifndef qpid_framing_highestProtocolVersion__ +#define qpid_framing_highestProtocolVersion__ + +#include "qpid/framing/ProtocolVersion.h" + + +namespace qpid { +namespace framing { + +static ProtocolVersion highestProtocolVersion(99, 0); +//static ProtocolVersion highestProtocolVersion(0, 10); + +} /* namespace framing */ +} /* namespace qpid */ + +#endif diff --git a/qpid/cpp/src/qpid/framing/AccumulatedAck.cpp b/qpid/cpp/src/qpid/framing/AccumulatedAck.cpp new file mode 100644 index 0000000000..2d3ecf3f6a --- /dev/null +++ b/qpid/cpp/src/qpid/framing/AccumulatedAck.cpp @@ -0,0 +1,164 @@ +/* + * + * 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. + * + */ +#include "AccumulatedAck.h" + +#include <assert.h> +#include <iostream> +#include <boost/bind.hpp> + +using std::list; +using std::max; +using std::min; +using namespace qpid::framing; + +AccumulatedAck::AccumulatedAck(SequenceNumber r) : mark(r) {} + +void AccumulatedAck::update(SequenceNumber first, SequenceNumber last){ + assert(first <= last); + if (last < mark) return; + + + Range r(first, last); + bool handled = false; + bool markMerged = false; + list<Range>::iterator merged = ranges.end(); + if (r.mergeable(mark)) { + mark = r.end; + markMerged = true; + handled = true; + } else { + for (list<Range>::iterator i = ranges.begin(); i != ranges.end() && !handled; i++) { + if (i->merge(r)) { + merged = i; + handled = true; + } else if (r.start < i->start) { + ranges.insert(i, r); + handled = true; + } + } + } + if (!handled) { + ranges.push_back(r); + } else { + while (!ranges.empty() && ranges.front().end <= mark) { + ranges.pop_front(); + } + if (markMerged) { + //new range is incorporated, but may be possible to consolidate + merged = ranges.begin(); + while (merged != ranges.end() && merged->mergeable(mark)) { + mark = merged->end; + merged = ranges.erase(merged); + } + } + if (merged != ranges.end()) { + //consolidate ranges + list<Range>::iterator i = merged; + list<Range>::iterator j = i++; + while (i != ranges.end() && j->merge(*i)) { + j = i++; + } + } + } +} + +void AccumulatedAck::consolidate(){} + +void AccumulatedAck::clear(){ + mark = SequenceNumber(0);//not sure that this is valid when wraparound is a possibility + ranges.clear(); +} + +bool AccumulatedAck::covers(SequenceNumber tag) const{ + if (tag <= mark) return true; + for (list<Range>::const_iterator i = ranges.begin(); i != ranges.end(); i++) { + if (i->contains(tag)) return true; + } + return false; +} + +void AccumulatedAck::collectRanges(SequenceNumberSet& set) const +{ + for (list<Range>::const_iterator i = ranges.begin(); i != ranges.end(); i++) { + set.push_back(i->start); + set.push_back(i->end); + } +} + +void AccumulatedAck::update(const SequenceNumber cumulative, const SequenceNumberSet& range) +{ + update(mark, cumulative); + range.processRanges(*this); +} + + +bool Range::contains(SequenceNumber i) const +{ + return i >= start && i <= end; +} + +bool Range::intersect(const Range& r) const +{ + return r.contains(start) || r.contains(end) || contains(r.start) || contains(r.end); +} + +bool Range::merge(const Range& r) +{ + if (intersect(r) || mergeable(r.end) || r.mergeable(end)) { + start = min(start, r.start); + end = max(end, r.end); + return true; + } else { + return false; + } +} + +bool Range::mergeable(const SequenceNumber& s) const +{ + if (contains(s) || start - s == 1) { + return true; + } else { + return false; + } +} + +Range::Range(SequenceNumber s, SequenceNumber e) : start(s), end(e) {} + + +namespace qpid{ +namespace framing{ + std::ostream& operator<<(std::ostream& out, const Range& r) + { + out << "[" << r.start.getValue() << "-" << r.end.getValue() << "]"; + return out; + } + + std::ostream& operator<<(std::ostream& out, const AccumulatedAck& a) + { + out << "{mark: " << a.mark.getValue() << ", ranges: ("; + for (list<Range>::const_iterator i = a.ranges.begin(); i != a.ranges.end(); i++) { + if (i != a.ranges.begin()) out << ", "; + out << *i; + } + out << ")]"; + return out; + } +}} diff --git a/qpid/cpp/src/qpid/framing/AccumulatedAck.h b/qpid/cpp/src/qpid/framing/AccumulatedAck.h new file mode 100644 index 0000000000..a635d2ea04 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/AccumulatedAck.h @@ -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. + * + */ +#ifndef _AccumulatedAck_ +#define _AccumulatedAck_ + +#include <algorithm> +#include <functional> +#include <list> +#include <ostream> +#include "SequenceNumber.h" +#include "SequenceNumberSet.h" + +namespace qpid { + namespace framing { + + struct Range + { + SequenceNumber start; + SequenceNumber end; + + Range(SequenceNumber s, SequenceNumber e); + bool contains(SequenceNumber i) const; + bool intersect(const Range& r) const; + bool merge(const Range& r); + bool mergeable(const SequenceNumber& r) const; + }; + /** + * Keeps an accumulated record of acked messages (by delivery + * tag). + */ + class AccumulatedAck { + public: + /** + * Everything up to this value has been acked. + */ + SequenceNumber mark; + /** + * List of individually acked messages greater than the + * 'mark'. + */ + std::list<Range> ranges; + + explicit AccumulatedAck(SequenceNumber r = SequenceNumber()); + void update(SequenceNumber firstTag, SequenceNumber lastTag); + void consolidate(); + void clear(); + bool covers(SequenceNumber tag) const; + void collectRanges(SequenceNumberSet& set) const; + void update(const SequenceNumber cumulative, const SequenceNumberSet& range); + void operator()(SequenceNumber first, SequenceNumber last) { update(first, last); } + }; + std::ostream& operator<<(std::ostream&, const Range&); + std::ostream& operator<<(std::ostream&, const AccumulatedAck&); + } +} + + +#endif diff --git a/qpid/cpp/src/qpid/framing/Array.cpp b/qpid/cpp/src/qpid/framing/Array.cpp new file mode 100644 index 0000000000..71281c7a52 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/Array.cpp @@ -0,0 +1,127 @@ +/* + * + * 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. + * + */ +#include "Array.h" +#include "Buffer.h" +#include "FieldValue.h" +#include "qpid/Exception.h" +#include "qpid/framing/reply_exceptions.h" +#include <assert.h> + +namespace qpid { +namespace framing { + +Array::Array() : typeOctet(0xF0/*void*/) {} + +Array::Array(uint8_t type) : typeOctet(type) {} + +Array::Array(const std::vector<std::string>& in) +{ + typeOctet = 0xA4; + for (std::vector<std::string>::const_iterator i = in.begin(); i != in.end(); ++i) { + ValuePtr value(new StringValue(*i)); + values.push_back(value); + } +} + + +uint32_t Array::size() const { + //note: size is only included when used as a 'top level' type + uint32_t len(4/*size*/ + 1/*type*/ + 4/*count*/); + for(ValueVector::const_iterator i = values.begin(); i != values.end(); ++i) { + len += (*i)->getData().size(); + } + return len; +} + +int Array::count() const { + return values.size(); +} + +std::ostream& operator<<(std::ostream& out, const Array& t) { + out << "{"; + for(Array::ValueVector::const_iterator i = t.values.begin(); i != t.values.end(); ++i) { + if (i != t.values.begin()) out << ", "; + out << *(i->get()); + } + return out << "}"; +} + +void Array::encode(Buffer& buffer) const{ + buffer.putLong(size() - 4);//size added only when array is a top-level type + buffer.putOctet(typeOctet); + buffer.putLong(count()); + for (ValueVector::const_iterator i = values.begin(); i!=values.end(); ++i) { + (*i)->getData().encode(buffer); + } +} + +void Array::decode(Buffer& buffer){ + uint32_t size = buffer.getLong();//size added only when array is a top-level type + uint32_t available = buffer.available(); + if (available < size) { + throw SyntaxErrorException(QPID_MSG("Not enough data for array, expected " + << size << " bytes but only " << available << " available")); + } + if (size) { + typeOctet = buffer.getOctet(); + uint32_t count = buffer.getLong(); + + FieldValue dummy; + dummy.setType(typeOctet); + available = buffer.available(); + if (available < count * dummy.getData().size()) { + throw SyntaxErrorException(QPID_MSG("Not enough data for array, expected " + << count << " items of " << dummy.getData().size() + << " bytes each but only " << available << " bytes available")); + } + + for (uint32_t i = 0; i < count; i++) { + ValuePtr value(new FieldValue); + value->setType(typeOctet); + value->getData().decode(buffer); + values.push_back(ValuePtr(value)); + } + } +} + + +bool Array::operator==(const Array& x) const { + if (typeOctet != x.typeOctet) return false; + if (values.size() != x.values.size()) return false; + + for (ValueVector::const_iterator i = values.begin(), j = x.values.begin(); i != values.end(); ++i, ++j) { + if (*(i->get()) != *(j->get())) return false; + } + + return true; +} + +void Array::add(ValuePtr value) +{ + if (typeOctet != value->getType()) { + throw SyntaxErrorException(QPID_MSG("Wrong type of value, expected " << typeOctet)); + } + values.push_back(value); +} + + +} +} diff --git a/qpid/cpp/src/qpid/framing/Array.h b/qpid/cpp/src/qpid/framing/Array.h new file mode 100644 index 0000000000..1367a023f2 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/Array.h @@ -0,0 +1,78 @@ +/* + * + * 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. + * + */ +#include <iostream> +#include <vector> +#include <boost/shared_ptr.hpp> +#include <map> +#include "amqp_types.h" +#include "FieldValue.h" + +#ifndef _Array_ +#define _Array_ + +namespace qpid { +namespace framing { + +class Buffer; + +class Array +{ + public: + typedef boost::shared_ptr<FieldValue> ValuePtr; + typedef std::vector<ValuePtr> ValueVector; + + uint32_t size() const; + void encode(Buffer& buffer) const; + void decode(Buffer& buffer); + + int count() const; + bool operator==(const Array& other) const; + + Array(); + Array(uint8_t type); + //creates a longstr array + Array(const std::vector<std::string>& in); + + void add(ValuePtr value); + + template <class T> + void collect(std::vector<T>& out) + { + for (ValueVector::const_iterator i = values.begin(); i != values.end(); ++i) { + out.push_back((*i)->get<T>()); + } + } + + private: + uint8_t typeOctet; + ValueVector values; + + ValueVector::const_iterator begin() const { return values.begin(); } + ValueVector::const_iterator end() const { return values.end(); } + + friend std::ostream& operator<<(std::ostream& out, const Array& body); +}; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/framing/Blob.cpp b/qpid/cpp/src/qpid/framing/Blob.cpp new file mode 100644 index 0000000000..388d4b64ef --- /dev/null +++ b/qpid/cpp/src/qpid/framing/Blob.cpp @@ -0,0 +1,31 @@ +/* + * 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. + * + */ + +#include "Blob.h" + + +namespace qpid { +namespace framing { + +void BlobHelper<void>::destroy(void*) {} + +void BlobHelper<void>::copy(void*, const void*) {} + +}} // namespace qpid::framing diff --git a/qpid/cpp/src/qpid/framing/Blob.h b/qpid/cpp/src/qpid/framing/Blob.h new file mode 100644 index 0000000000..cf81f693b0 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/Blob.h @@ -0,0 +1,193 @@ +#ifndef QPID_FRAMING_BLOB_H +#define QPID_FRAMING_BLOB_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. + * + */ + +#include <boost/static_assert.hpp> +#include <boost/aligned_storage.hpp> +#include <boost/checked_delete.hpp> +#include <boost/utility/typed_in_place_factory.hpp> +#include <boost/type_traits/is_base_and_derived.hpp> +#include <boost/utility/enable_if.hpp> + +#include <new> + +#include <assert.h> + + +namespace qpid { +namespace framing { + +using boost::in_place; +using boost::typed_in_place_factory_base; + +/** 0-arg typed_in_place_factory, missing in boost. */ +template <class T> +struct typed_in_place_factory0 : public typed_in_place_factory_base { + typedef T value_type ; + void apply ( void* address ) const { new (address) T(); } +}; + +/** 0-arg in_place<T>() function, missing from boost. */ +template<class T> +typed_in_place_factory0<T> in_place() { return typed_in_place_factory0<T>(); } + +template <class T, class R=void> +struct EnableInPlace + : public boost::enable_if<boost::is_base_and_derived< + typed_in_place_factory_base, T>, + R> +{}; + +template <class T, class R=void> +struct DisableInPlace + : public boost::disable_if<boost::is_base_and_derived< + typed_in_place_factory_base, T>, + R> +{}; + +template <class T> struct BlobHelper { + static void destroy(void* ptr) { static_cast<T*>(ptr)->~T(); } + static void copy(void* dest, const void* src) { + new (dest) T(*static_cast<const T*>(src)); + } +}; + +template <> struct BlobHelper<void> { + static void destroy(void*); + static void copy(void* to, const void* from); +}; + +/** + * A "blob" is a chunk of memory which can contain a single object at + * a time arbitrary type, provided sizeof(T)<=blob.size(). Blob + * ensures proper construction and destruction of its contents, + * and proper copying between Blobs, but nothing else. + * + * In particular the user must ensure the blob is big enough for its + * contents and must know the type of object in the blob to cast get(). + * + * If BaseType is specified then only object that can be + * safely static_cast to BaseType may be stored in the Blob. + */ +template <size_t Size, class BaseType=void> +class Blob +{ + boost::aligned_storage<Size> store; + BaseType* basePtr; + + void (*destroy)(void*); + void (*copy)(void*, const void*); + + template <class T>void setType() { + BOOST_STATIC_ASSERT(sizeof(T) <= Size); + destroy=&BlobHelper<T>::destroy; + copy=&BlobHelper<T>::copy; + // Base pointer may be offeset from store.address() + basePtr = reinterpret_cast<T*>(store.address()); + } + + void initialize() { + destroy=&BlobHelper<void>::destroy; + copy=&BlobHelper<void>::copy; + basePtr=0; + } + + template<class Factory> + typename EnableInPlace<Factory>::type apply(const Factory& factory) + { + typedef typename Factory::value_type T; + assert(empty()); + factory.apply(store.address()); + setType<T>(); + } + + void assign(const Blob& b) { + assert(empty()); + b.copy(this->store.address(), b.store.address()); + copy = b.copy; + destroy = b.destroy; + basePtr = reinterpret_cast<BaseType*>( + ((char*)this)+ ((char*)(b.basePtr) - (char*)(&b))); + } + + public: + /** Construct an empty blob. */ + Blob() { initialize(); } + + /** Copy a blob. */ + Blob(const Blob& b) { initialize(); assign(b); } + + /** Construct from in_place constructor */ + template<class InPlace> + Blob(const InPlace & expr, typename EnableInPlace<InPlace>::type* =0) { + initialize(); apply(expr); + } + + /** Construct by copying an objecct constructor */ + template<class T> + Blob(const T & t, typename DisableInPlace<T>::type* =0) { + initialize(); apply(in_place<T>(t)); + } + + ~Blob() { clear(); } + + /** Assign from another blob. */ + Blob& operator=(const Blob& b) { + clear(); + assign(b); + return *this; + } + + /** Assign from an in_place constructor expression. */ + template<class InPlace> + typename EnableInPlace<InPlace,Blob&>::type operator=(const InPlace& expr) { + clear(); apply(expr); return *this; + } + + /** Assign from an object of type T. */ + template <class T> + typename DisableInPlace<T, Blob&>::type operator=(const T& x) { + clear(); apply(in_place<T>(x)); return *this; + } + + /** Get pointer to blob contents, returns 0 if empty. */ + BaseType* get() { return basePtr; } + + /** Get pointer to blob contents, returns 0 if empty. */ + const BaseType* get() const { return basePtr; } + + /** Destroy the object in the blob making it empty. */ + void clear() { + void (*oldDestroy)(void*) = destroy; + initialize(); + oldDestroy(store.address()); + } + + bool empty() const { return destroy==BlobHelper<void>::destroy; } + + static size_t size() { return Size; } +}; + +}} // namespace qpid::framing + + +#endif /*!QPID_FRAMING_BLOB_H*/ diff --git a/qpid/cpp/src/qpid/framing/BodyHandler.cpp b/qpid/cpp/src/qpid/framing/BodyHandler.cpp new file mode 100644 index 0000000000..fb84be7cd6 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/BodyHandler.cpp @@ -0,0 +1,55 @@ +/* + * + * 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. + * + */ +#include "BodyHandler.h" +#include "AMQMethodBody.h" +#include "AMQHeaderBody.h" +#include "AMQContentBody.h" +#include "AMQHeartbeatBody.h" +#include <boost/cast.hpp> +#include "qpid/framing/reply_exceptions.h" + +using namespace qpid::framing; +using namespace boost; + +BodyHandler::~BodyHandler() {} + +// TODO aconway 2007-08-13: Replace with visitor. +void BodyHandler::handleBody(AMQBody* body) { + switch(body->type()) + { + case METHOD_BODY: + handleMethod(polymorphic_downcast<AMQMethodBody*>(body)); + break; + case HEADER_BODY: + handleHeader(polymorphic_downcast<AMQHeaderBody*>(body)); + break; + case CONTENT_BODY: + handleContent(polymorphic_downcast<AMQContentBody*>(body)); + break; + case HEARTBEAT_BODY: + handleHeartbeat(polymorphic_downcast<AMQHeartbeatBody*>(body)); + break; + default: + throw SyntaxErrorException( + QPID_MSG("Invalid frame type " << body->type())); + } +} + diff --git a/qpid/cpp/src/qpid/framing/BodyHandler.h b/qpid/cpp/src/qpid/framing/BodyHandler.h new file mode 100644 index 0000000000..9ded737195 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/BodyHandler.h @@ -0,0 +1,56 @@ +#ifndef _BodyHandler_ +#define _BodyHandler_ + +/* + * + * 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. + * + */ + +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace framing { +class AMQBody; +class AMQMethodBody; +class AMQHeaderBody; +class AMQContentBody; +class AMQHeartbeatBody; + +// TODO aconway 2007-08-10: rework using Visitor pattern? + +/** + * Interface to handle incoming frame bodies. + * Derived classes provide logic for each frame type. + */ +class BodyHandler { + public: + virtual ~BodyHandler(); + virtual void handleBody(AMQBody* body); + + protected: + virtual void handleMethod(AMQMethodBody*) = 0; + virtual void handleHeader(AMQHeaderBody*) = 0; + virtual void handleContent(AMQContentBody*) = 0; + virtual void handleHeartbeat(AMQHeartbeatBody*) = 0; +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/framing/BodyHolder.cpp b/qpid/cpp/src/qpid/framing/BodyHolder.cpp new file mode 100644 index 0000000000..de971b5b28 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/BodyHolder.cpp @@ -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. + * + */ +#include "BodyHolder.h" +#include "AMQMethodBody.h" +#include "AMQHeaderBody.h" +#include "AMQContentBody.h" +#include "AMQHeartbeatBody.h" +#include "Buffer.h" +#include "qpid/framing/reply_exceptions.h" + +namespace qpid { +namespace framing { + + +// BodyHolder::operator=(const AMQBody&) is defined +// in generated file BodyHolder_gen.cpp + + +void BodyHolder::encode(Buffer& b) const { + const AMQMethodBody* method=getMethod(); + if (method) { + b.putOctet(method->amqpClassId()); + b.putOctet(method->amqpMethodId()); + method->encode(b); + } + else + get()->encode(b); +} + +void BodyHolder::decode(uint8_t type, Buffer& buffer, uint32_t size) { + switch(type) + { + case 0://CONTROL + case METHOD_BODY: { + ClassId c = buffer.getOctet(); + MethodId m = buffer.getOctet(); + setMethod(c, m); + break; + } + case HEADER_BODY: *this=in_place<AMQHeaderBody>(); break; + case CONTENT_BODY: *this=in_place<AMQContentBody>(); break; + case HEARTBEAT_BODY: *this=in_place<AMQHeartbeatBody>(); break; + default: + throw SyntaxErrorException(QPID_MSG("Invalid frame type " << type)); + } + get()->decode(buffer, size); +} + +uint32_t BodyHolder::size() const { + const AMQMethodBody* method=getMethod(); + if (method) + return sizeof(ClassId)+sizeof(MethodId)+method->size(); + else + return get()->size(); +} + +}} // namespace qpid::framing + diff --git a/qpid/cpp/src/qpid/framing/BodyHolder.h b/qpid/cpp/src/qpid/framing/BodyHolder.h new file mode 100644 index 0000000000..65029e5675 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/BodyHolder.h @@ -0,0 +1,88 @@ +#ifndef QPID_FRAMING_BODYHOLDER_H +#define QPID_FRAMING_BODYHOLDER_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. + * + */ + +#include "qpid/framing/AMQBody.h" +#include "qpid/framing/Blob.h" +#include "qpid/framing/MaxMethodBodySize.h" // Generated file. +#include "qpid/framing/amqp_types.h" +#include "qpid/RefCounted.h" + + +namespace qpid { +namespace framing { + +class AMQMethodBody; +class AMQBody; +class Buffer; + +/** + * Holder for arbitrary frame body. + */ +class BodyHolder : public RefCounted +{ + public: + // default copy, assign dtor ok. + BodyHolder() {} + BodyHolder(const AMQBody& b) { setBody(b); } + BodyHolder(ClassId c, MethodId m) { setMethod(c,m); } + + /** Construct from an in_place constructor expression */ + template <class InPlace> + BodyHolder(const InPlace& ip, typename EnableInPlace<InPlace>::type* =0) + : blob(ip) {} + + void setBody(const AMQBody& b); + + /** Assign from an in_place constructor expression */ + template <class InPlace> + typename EnableInPlace<InPlace,BodyHolder&>::type + operator=(const InPlace& ip) { blob=ip; return *this; } + + /** Assign by copying. */ + template <class T> + typename DisableInPlace<T,BodyHolder&>::type operator=(const T& x) + { blob=in_place<T>(x); return *this; } + + /** Set to method with ClassId c, MethodId m. */ + void setMethod(ClassId c, MethodId m); + + void encode(Buffer&) const; + void decode(uint8_t frameType, Buffer&, uint32_t=0); + uint32_t size() const; + + /** Return body pointer or 0 if empty. */ + AMQBody* get() { return blob.get(); } + const AMQBody* get() const { return blob.get(); } + + /** Return method pointer or 0 if not a method. */ + AMQMethodBody* getMethod() { return get()->getMethod(); } + const AMQMethodBody* getMethod() const { return get()->getMethod(); } + + private: + Blob<MAX_METHOD_BODY_SIZE, AMQBody> blob; +}; + +}} // namespace qpid::framing + +#endif /*!QPID_FRAMING_BODYHOLDER_H*/ diff --git a/qpid/cpp/src/qpid/framing/Buffer.cpp b/qpid/cpp/src/qpid/framing/Buffer.cpp new file mode 100644 index 0000000000..69168d462a --- /dev/null +++ b/qpid/cpp/src/qpid/framing/Buffer.cpp @@ -0,0 +1,276 @@ +/* + * + * 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. + * + */ +#include "Buffer.h" +#include "FramingContent.h" +#include "FieldTable.h" +#include <string.h> +#include <boost/format.hpp> +namespace qpid { + +namespace framing { + +Buffer::Buffer(char* _data, uint32_t _size) + : size(_size), data(_data), position(0) { +} + +void Buffer::record(){ + r_position = position; +} + +void Buffer::restore(bool reRecord){ + uint32_t savedPosition = position; + + position = r_position; + + if (reRecord) + r_position = savedPosition; +} + +void Buffer::reset(){ + position = 0; +} + +/////////////////////////////////////////////////// + +void Buffer::putOctet(uint8_t i){ + data[position++] = i; +} + +void Buffer::putShort(uint16_t i){ + uint16_t b = i; + data[position++] = (uint8_t) (0xFF & (b >> 8)); + data[position++] = (uint8_t) (0xFF & b); +} + +void Buffer::putLong(uint32_t i){ + uint32_t b = i; + data[position++] = (uint8_t) (0xFF & (b >> 24)); + data[position++] = (uint8_t) (0xFF & (b >> 16)); + data[position++] = (uint8_t) (0xFF & (b >> 8)); + data[position++] = (uint8_t) (0xFF & b); +} + +void Buffer::putLongLong(uint64_t i){ + uint32_t hi = i >> 32; + uint32_t lo = i; + putLong(hi); + putLong(lo); +} + +void Buffer::putFloat(float f){ + union { + uint32_t i; + float f; + } val; + + val.f = f; + putLong (val.i); +} + +void Buffer::putDouble(double f){ + union { + uint64_t i; + double f; + } val; + + val.f = f; + putLongLong (val.i); +} + +void Buffer::putBin128(uint8_t* b){ + memcpy (data + position, b, 16); + position += 16; +} + +uint8_t Buffer::getOctet(){ + return (uint8_t) data[position++]; +} + +uint16_t Buffer::getShort(){ + uint16_t hi = (unsigned char) data[position++]; + hi = hi << 8; + hi |= (unsigned char) data[position++]; + return hi; +} + +uint32_t Buffer::getLong(){ + uint32_t a = (unsigned char) data[position++]; + uint32_t b = (unsigned char) data[position++]; + uint32_t c = (unsigned char) data[position++]; + uint32_t d = (unsigned char) data[position++]; + a = a << 24; + a |= b << 16; + a |= c << 8; + a |= d; + return a; +} + +uint64_t Buffer::getLongLong(){ + uint64_t hi = getLong(); + uint64_t lo = getLong(); + hi = hi << 32; + return hi | lo; +} + +float Buffer::getFloat(){ + union { + uint32_t i; + float f; + } val; + val.i = getLong(); + return val.f; +} + +double Buffer::getDouble(){ + union { + uint64_t i; + double f; + } val; + val.i = getLongLong(); + return val.f; +} + +template <> +uint64_t Buffer::getUInt<1>() { + return getOctet(); +} + +template <> +uint64_t Buffer::getUInt<2>() { + return getShort(); +} + +template <> +uint64_t Buffer::getUInt<4>() { + return getLong(); +} + +template <> +uint64_t Buffer::getUInt<8>() { + return getLongLong(); +} + +template <> +void Buffer::putUInt<1>(uint64_t i) { + putOctet(i); +} + +template <> +void Buffer::putUInt<2>(uint64_t i) { + putShort(i); +} + +template <> +void Buffer::putUInt<4>(uint64_t i) { + putLong(i); +} + +template <> +void Buffer::putUInt<8>(uint64_t i) { + putLongLong(i); +} + +void Buffer::putShortString(const string& s){ + uint8_t len = s.length(); + putOctet(len); + s.copy(data + position, len); + position += len; +} + +void Buffer::putMediumString(const string& s){ + uint16_t len = s.length(); + putShort(len); + s.copy(data + position, len); + position += len; +} + +void Buffer::putLongString(const string& s){ + uint32_t len = s.length(); + putLong(len); + s.copy(data + position, len); + position += len; +} + +void Buffer::getShortString(string& s){ + uint8_t len = getOctet(); + checkAvailable(len); + s.assign(data + position, len); + position += len; +} + +void Buffer::getMediumString(string& s){ + uint16_t len = getShort(); + checkAvailable(len); + s.assign(data + position, len); + position += len; +} + +void Buffer::getLongString(string& s){ + uint32_t len = getLong(); + checkAvailable(len); + s.assign(data + position, len); + position += len; +} + +void Buffer::getBin128(uint8_t* b){ + memcpy (b, data + position, 16); + position += 16; +} + +void Buffer::putRawData(const string& s){ + uint32_t len = s.length(); + s.copy(data + position, len); + position += len; +} + +void Buffer::getRawData(string& s, uint32_t len){ + checkAvailable(len); + s.assign(data + position, len); + position += len; +} + +void Buffer::putRawData(const uint8_t* s, size_t len){ + memcpy(data + position, s, len); + position += len; +} + +void Buffer::getRawData(uint8_t* s, size_t len){ + checkAvailable(len); + memcpy(s, data + position, len); + position += len; +} + +void Buffer::dump(std::ostream& out) const { + for (uint32_t i = position; i < size; i++) + { + if (i != position) + out << " "; + out << boost::format("%02x") % ((unsigned) (uint8_t) data[i]); + } +} + +std::ostream& operator<<(std::ostream& out, const Buffer& b){ + out << "Buffer["; + b.dump(out); + return out << "]"; +} + +}} diff --git a/qpid/cpp/src/qpid/framing/Buffer.h b/qpid/cpp/src/qpid/framing/Buffer.h new file mode 100644 index 0000000000..94cc2d320f --- /dev/null +++ b/qpid/cpp/src/qpid/framing/Buffer.h @@ -0,0 +1,124 @@ +/* + * + * 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. + * + */ +#include "amqp_types.h" +#include "qpid/Exception.h" +#include <boost/iterator/iterator_facade.hpp> + +#ifndef _Buffer_ +#define _Buffer_ + +namespace qpid { +namespace framing { + +struct OutOfBounds : qpid::Exception {}; + +class Content; +class FieldTable; + +class Buffer +{ + uint32_t size; + char* data; + uint32_t position; + uint32_t r_position; + + void checkAvailable(uint32_t count) { if (position + count > size) throw OutOfBounds(); } + + public: + + /** Buffer input/output iterator. + * Supports using an amqp_0_10::Codec with a framing::Buffer. + */ + class Iterator : public boost::iterator_facade< + Iterator, char, boost::random_access_traversal_tag> + { + public: + Iterator(Buffer& b) : buffer(&b) {} + + private: + friend class boost::iterator_core_access; + char& dereference() const { return buffer->data[buffer->position]; } + void increment() { ++buffer->position; } + bool equal(const Iterator& x) const { return buffer == x.buffer; } + + Buffer* buffer; + }; + + friend class Iterator; + + Buffer(char* data=0, uint32_t size=0); + + void record(); + void restore(bool reRecord = false); + void reset(); + + uint32_t available() { return size - position; } + uint32_t getSize() { return size; } + uint32_t getPosition() { return position; } + Iterator getIterator() { return Iterator(*this); } + + void putOctet(uint8_t i); + void putShort(uint16_t i); + void putLong(uint32_t i); + void putLongLong(uint64_t i); + void putFloat(float f); + void putDouble(double f); + void putBin128(uint8_t* b); + + uint8_t getOctet(); + uint16_t getShort(); + uint32_t getLong(); + uint64_t getLongLong(); + float getFloat(); + double getDouble(); + + template <int n> + uint64_t getUInt(); + + template <int n> + void putUInt(uint64_t); + + void putShortString(const string& s); + void putMediumString(const string& s); + void putLongString(const string& s); + void getShortString(string& s); + void getMediumString(string& s); + void getLongString(string& s); + void getBin128(uint8_t* b); + + void putRawData(const string& s); + void getRawData(string& s, uint32_t size); + + void putRawData(const uint8_t* data, size_t size); + void getRawData(uint8_t* data, size_t size); + + template <class T> void put(const T& data) { data.encode(*this); } + template <class T> void get(T& data) { data.decode(*this); } + + void dump(std::ostream&) const; +}; + +std::ostream& operator<<(std::ostream&, const Buffer&); + +}} // namespace qpid::framing + + +#endif diff --git a/qpid/cpp/src/qpid/framing/ChannelHandler.h b/qpid/cpp/src/qpid/framing/ChannelHandler.h new file mode 100644 index 0000000000..69aaeac492 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/ChannelHandler.h @@ -0,0 +1,53 @@ +#ifndef QPID_FRAMING_CHANNELHANDLER_H +#define QPID_FRAMING_CHANNELHANDLER_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. + * + */ +#include "FrameHandler.h" +#include "AMQFrame.h" + +namespace qpid { +namespace framing { + +/** + * Sets the channel number on outgoing frames. + */ +class ChannelHandler : public FrameHandler +{ + public: + ChannelHandler(uint16_t channelId=0, FrameHandler* next=0) + : FrameHandler(next), channel(channelId) {} + void handle(AMQFrame& frame) { + frame.setChannel(channel); + next->handle(frame); + } + uint16_t get() const { return channel; } + ChannelHandler& set(uint16_t ch) { channel=ch; return *this; } + operator uint16_t() const { return get(); } + ChannelHandler& operator=(uint16_t ch) { return set(ch); } + + private: + uint16_t channel; +}; + +}} // namespace qpid::framing + +#endif /*!QPID_FRAMING_CHANNELHANDLER_H*/ diff --git a/qpid/cpp/src/qpid/framing/FieldTable.cpp b/qpid/cpp/src/qpid/framing/FieldTable.cpp new file mode 100644 index 0000000000..089bc5d4a5 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/FieldTable.cpp @@ -0,0 +1,165 @@ +/* + * + * 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. + * + */ +#include "FieldTable.h" +#include "Buffer.h" +#include "FieldValue.h" +#include "qpid/Exception.h" +#include "qpid/framing/reply_exceptions.h" +#include <assert.h> + +namespace qpid { +namespace framing { + +FieldTable::~FieldTable() {} + +uint32_t FieldTable::size() const { + uint32_t len(4); + for(ValueMap::const_iterator i = values.begin(); i != values.end(); ++i) { + // shortstr_len_byte + key size + value size + len += 1 + (i->first).size() + (i->second)->size(); + } + return len; +} + +int FieldTable::count() const { + return values.size(); +} + +namespace +{ +std::ostream& operator<<(std::ostream& out, const FieldTable::ValueMap::value_type& i) { + return out << i.first << ":" << *i.second; +} +} + +std::ostream& operator<<(std::ostream& out, const FieldTable& t) { + out << "{"; + FieldTable::ValueMap::const_iterator i = t.begin(); + if (i != t.end()) out << *i++; + while (i != t.end()) + { + out << "," << *i++; + } + return out << "}"; +} + +void FieldTable::set(const std::string& name, const ValuePtr& value){ + values[name] = value; +} + +void FieldTable::setString(const std::string& name, const std::string& value){ + values[name] = ValuePtr(new StringValue(value)); +} + +void FieldTable::setInt(const std::string& name, int value){ + values[name] = ValuePtr(new IntegerValue(value)); +} + +void FieldTable::setTimestamp(const std::string& name, uint64_t value){ + values[name] = ValuePtr(new TimeValue(value)); +} + +void FieldTable::setTable(const std::string& name, const FieldTable& value){ + values[name] = ValuePtr(new FieldTableValue(value)); +} + +FieldTable::ValuePtr FieldTable::get(const std::string& name) const +{ + ValuePtr value; + ValueMap::const_iterator i = values.find(name); + if ( i!=values.end() ) + value = i->second; + return value; +} + +namespace { + template <class T> T default_value() { return T(); } + template <> int default_value<int>() { return 0; } + template <> uint64_t default_value<uint64_t>() { return 0; } +} + +template <class T> +T getValue(const FieldTable::ValuePtr value) +{ + if (!value || !value->convertsTo<T>()) + return default_value<T>(); + + return value->get<T>(); +} + +std::string FieldTable::getString(const std::string& name) const { + return getValue<std::string>(get(name)); +} + +int FieldTable::getInt(const std::string& name) const { + return getValue<int>(get(name)); +} + +//uint64_t FieldTable::getTimestamp(const std::string& name) const { +// return getValue<uint64_t>(name); +//} +// +//void FieldTable::getTable(const std::string& name, FieldTable& value) const { +// value = getValue<FieldTable>(name); +//} + +void FieldTable::encode(Buffer& buffer) const{ + buffer.putLong(size() - 4); + for (ValueMap::const_iterator i = values.begin(); i!=values.end(); ++i) { + buffer.putShortString(i->first); + i->second->encode(buffer); + } +} + +void FieldTable::decode(Buffer& buffer){ + uint32_t len = buffer.getLong(); + uint32_t available = buffer.available(); + if (available < len) + throw SyntaxErrorException(QPID_MSG("Not enough data for field table.")); + uint32_t leftover = available - len; + while(buffer.available() > leftover){ + std::string name; + ValuePtr value(new FieldValue); + + buffer.getShortString(name); + value->decode(buffer); + values[name] = ValuePtr(value); + } +} + + +bool FieldTable::operator==(const FieldTable& x) const { + if (values.size() != x.values.size()) return false; + for (ValueMap::const_iterator i = values.begin(); i != values.end(); ++i) { + ValueMap::const_iterator j = x.values.find(i->first); + if (j == x.values.end()) return false; + if (*(i->second) != *(j->second)) return false; + } + return true; +} + +//void FieldTable::erase(const std::string& name) +//{ +// values.erase(values.find(name)); +//} + +} +} diff --git a/qpid/cpp/src/qpid/framing/FieldTable.h b/qpid/cpp/src/qpid/framing/FieldTable.h new file mode 100644 index 0000000000..707496a861 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/FieldTable.h @@ -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. + * + */ +#include <iostream> +#include <vector> +#include <boost/shared_ptr.hpp> +#include <map> +#include "amqp_types.h" + +#ifndef _FieldTable_ +#define _FieldTable_ + +namespace qpid { + /** + * The framing namespace contains classes that are used to create, + * send and receive the basic packets from which AMQP is built. + */ +namespace framing { + +class FieldValue; +class Buffer; + +/** + * A set of name-value pairs. (See the AMQP spec for more details on + * AMQP field tables). + * + * \ingroup clientapi + */ +class FieldTable +{ + public: + typedef boost::shared_ptr<FieldValue> ValuePtr; + typedef std::map<std::string, ValuePtr> ValueMap; + + ~FieldTable(); + uint32_t size() const; + void encode(Buffer& buffer) const; + void decode(Buffer& buffer); + + int count() const; + void set(const std::string& name, const ValuePtr& value); + ValuePtr get(const std::string& name) const; + + void setString(const std::string& name, const std::string& value); + void setInt(const std::string& name, int value); + void setTimestamp(const std::string& name, uint64_t value); + void setTable(const std::string& name, const FieldTable& value); + //void setDecimal(string& name, xxx& value); + + std::string getString(const std::string& name) const; + int getInt(const std::string& name) const; +// uint64_t getTimestamp(const std::string& name) const; +// void getTable(const std::string& name, FieldTable& value) const; +// //void getDecimal(string& name, xxx& value); +// //void erase(const std::string& name); + + + bool operator==(const FieldTable& other) const; + + // Map-like interface. + // TODO: may need to duplicate into versions that return mutable iterator + ValueMap::const_iterator begin() const { return values.begin(); } + ValueMap::const_iterator end() const { return values.end(); } + ValueMap::const_iterator find(const std::string& s) const { return values.find(s); } + + private: + ValueMap values; + + friend std::ostream& operator<<(std::ostream& out, const FieldTable& body); +}; + +//class FieldNotFoundException{}; +//class UnknownFieldName : public FieldNotFoundException{}; +//class IncorrectFieldType : public FieldNotFoundException{}; +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/framing/FieldValue.cpp b/qpid/cpp/src/qpid/framing/FieldValue.cpp new file mode 100644 index 0000000000..cbda061209 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/FieldValue.cpp @@ -0,0 +1,136 @@ +/* + * + * 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. + * + */ +#include "FieldValue.h" +#include "Buffer.h" +#include "qpid/framing/reply_exceptions.h" + +namespace qpid { +namespace framing { + +uint8_t FieldValue::getType() +{ + return typeOctet; +} + +void FieldValue::setType(uint8_t type) +{ + typeOctet = type; + + uint8_t lenType = typeOctet >> 4; + switch(lenType){ + case 0: + data.reset(new FixedWidthValue<1>()); + break; + case 1: + data.reset(new FixedWidthValue<2>()); + break; + case 2: + data.reset(new FixedWidthValue<4>()); + break; + case 3: + data.reset(new FixedWidthValue<8>()); + break; + case 4: + data.reset(new FixedWidthValue<16>()); + break; + case 5: + data.reset(new FixedWidthValue<32>()); + break; + case 6: + data.reset(new FixedWidthValue<64>()); + break; + case 7: + data.reset(new FixedWidthValue<128>()); + break; + case 8: + data.reset(new VariableWidthValue<1>()); + break; + case 9: + data.reset(new VariableWidthValue<2>()); + break; + case 0xA: + data.reset(new VariableWidthValue<4>()); + break; + case 0xC: + data.reset(new FixedWidthValue<5>()); + break; + case 0xD: + data.reset(new FixedWidthValue<9>()); + break; + case 0xF: + data.reset(new FixedWidthValue<0>()); + break; + default: + throw SyntaxErrorException(QPID_MSG("Unknown field table value type: " << (int)typeOctet)); + } +} + +void FieldValue::decode(Buffer& buffer) +{ + setType(buffer.getOctet()); + data->decode(buffer); +} + +void FieldValue::encode(Buffer& buffer) +{ + buffer.putOctet(typeOctet); + data->encode(buffer); +} + +bool FieldValue::operator==(const FieldValue& v) const +{ + return + typeOctet == v.typeOctet && + *data == *v.data; +} + +StringValue::StringValue(const std::string& v) : + FieldValue( + 0xA4, + new VariableWidthValue<4>( + reinterpret_cast<const uint8_t*>(v.data()), + reinterpret_cast<const uint8_t*>(v.data()+v.size()))) +{ +} + +Str16Value::Str16Value(const std::string& v) : + FieldValue( + 0x95, + new VariableWidthValue<2>( + reinterpret_cast<const uint8_t*>(v.data()), + reinterpret_cast<const uint8_t*>(v.data()+v.size()))) +{} + +IntegerValue::IntegerValue(int v) : + FieldValue(0x21, new FixedWidthValue<4>(v)) +{ +} + +TimeValue::TimeValue(uint64_t v) : + FieldValue(0x32, new FixedWidthValue<8>(v)) +{ +} + +FieldTableValue::FieldTableValue(const FieldTable&) : FieldValue() +{ +} + +}} diff --git a/qpid/cpp/src/qpid/framing/FieldValue.h b/qpid/cpp/src/qpid/framing/FieldValue.h new file mode 100644 index 0000000000..272670d102 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/FieldValue.h @@ -0,0 +1,235 @@ +#ifndef _framing_FieldValue_h +#define _framing_FieldValue_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. + * + */ + +#include "qpid/Exception.h" +#include "Buffer.h" +#include "amqp_types.h" + +#include "assert.h" + +#include <iostream> +#include <memory> +#include <vector> + +namespace qpid { +namespace framing { + +/** + * Exception that is base exception for all field table errors + * + * \ingroup clientapi + */ +class FieldValueException : public qpid::Exception {}; + +/** + * Exception thrown when we can't perform requested conversion + * + * \ingroup clientapi + */ +struct InvalidConversionException : public FieldValueException { + InvalidConversionException() {} +}; + +/** + * Value that can appear in an AMQP field table + * + * \ingroup clientapi + */ +class FieldValue { + public: + /* + * Abstract type for content of different types + */ + class Data { + public: + virtual ~Data() {}; + virtual uint32_t size() const = 0; + virtual void encode(Buffer& buffer) = 0; + virtual void decode(Buffer& buffer) = 0; + virtual bool operator==(const Data&) const = 0; + + virtual bool convertsToInt() const { return false; } + virtual bool convertsToString() const { return false; } + virtual int64_t getInt() const { throw InvalidConversionException();} + virtual std::string getString() const { throw InvalidConversionException(); } + + virtual void print(std::ostream& out) const = 0; + }; + + FieldValue(): data(0) {}; + // Default assignment operator is fine + void setType(uint8_t type); + uint8_t getType(); + Data& getData() { return *data; } + uint32_t size() const { return 1 + data->size(); }; + bool empty() const { return data.get() == 0; } + void encode(Buffer& buffer); + void decode(Buffer& buffer); + bool operator==(const FieldValue&) const; + bool operator!=(const FieldValue& v) const { return !(*this == v); } + void print(std::ostream& out) const { out << "(0x" << std::hex << int(typeOctet) << ")"; data->print(out); } + + template <typename T> bool convertsTo() const { return false; } + template <typename T> T get() const { throw InvalidConversionException(); } + + protected: + FieldValue(uint8_t t, Data* d): typeOctet(t), data(d) {} + + private: + uint8_t typeOctet; + std::auto_ptr<Data> data; +}; + +template <> +inline bool FieldValue::convertsTo<int>() const { return data->convertsToInt(); } + +template <> +inline bool FieldValue::convertsTo<std::string>() const { return data->convertsToString(); } + +template <> +inline int FieldValue::get<int>() const { return data->getInt(); } + +template <> +inline std::string FieldValue::get<std::string>() const { return data->getString(); } + +inline std::ostream& operator<<(std::ostream& out, const FieldValue& v) { + v.print(out); + return out; +} + +template <int width> +class FixedWidthValue : public FieldValue::Data { + uint8_t octets[width]; + + public: + FixedWidthValue() {} + FixedWidthValue(const uint8_t (&data)[width]) : octets(data) {} + FixedWidthValue(uint64_t v) + { + for (int i = width; i > 0; --i) { + octets[i-1] = (uint8_t) (0xFF & v); v >>= 8; + } + octets[0] = (uint8_t) (0xFF & v); + } + + uint32_t size() const { return width; } + void encode(Buffer& buffer) { buffer.putRawData(octets, width); } + void decode(Buffer& buffer) { buffer.getRawData(octets, width); } + bool operator==(const Data& d) const { + const FixedWidthValue<width>* rhs = dynamic_cast< const FixedWidthValue<width>* >(&d); + if (rhs == 0) return false; + else return std::equal(&octets[0], &octets[width], &rhs->octets[0]); + } + + bool convertsToInt() const { return true; } + int64_t getInt() const + { + int64_t v = 0; + for (int i = 0; i < width-1; ++i) { + v |= octets[i]; v <<= 8; + } + v |= octets[width-1]; + return v; + } + + void print(std::ostream& o) const { o << "F" << width << ":"; }; +}; + +template <> +class FixedWidthValue<0> : public FieldValue::Data { + public: + // Implicit default constructor is fine + uint32_t size() const { return 0; } + void encode(Buffer&) {}; + void decode(Buffer&) {}; + bool operator==(const Data& d) const { + const FixedWidthValue<0>* rhs = dynamic_cast< const FixedWidthValue<0>* >(&d); + return rhs != 0; + } + void print(std::ostream& o) const { o << "F0"; }; +}; + +template <int lenwidth> +class VariableWidthValue : public FieldValue::Data { + std::vector<uint8_t> octets; + + public: + VariableWidthValue() {} + VariableWidthValue(const std::vector<uint8_t>& data) : octets(data) {} + VariableWidthValue(const uint8_t* start, const uint8_t* end) : octets(start, end) {} + uint32_t size() const { return lenwidth + octets.size(); } + void encode(Buffer& buffer) { + buffer.putUInt<lenwidth>(octets.size()); + buffer.putRawData(&octets[0], octets.size()); + }; + void decode(Buffer& buffer) { + uint32_t len = buffer.getUInt<lenwidth>(); + octets.resize(len); + buffer.getRawData(&octets[0], len); + } + bool operator==(const Data& d) const { + const VariableWidthValue<lenwidth>* rhs = dynamic_cast< const VariableWidthValue<lenwidth>* >(&d); + if (rhs == 0) return false; + else return octets==rhs->octets; + } + + bool convertsToString() const { return true; } + std::string getString() const { return std::string(octets.begin(), octets.end()); } + + void print(std::ostream& o) const { o << "V" << lenwidth << ":" << octets.size() << ":"; }; +}; + +/* + * Basic string value encodes as iso-8859-15 with 32 bit length + */ +class StringValue : public FieldValue { + public: + StringValue(const std::string& v); +}; + +class Str16Value : public FieldValue { + public: + Str16Value(const std::string& v); +}; + +/* + * Basic integer value encodes as signed 32 bit + */ +class IntegerValue : public FieldValue { + public: + IntegerValue(int v); +}; + +class TimeValue : public FieldValue { + public: + TimeValue(uint64_t v); +}; + +class FieldTableValue : public FieldValue { + public: + FieldTableValue(const FieldTable&); +}; + +}} // qpid::framing + +#endif diff --git a/qpid/cpp/src/qpid/framing/FrameDefaultVisitor.h b/qpid/cpp/src/qpid/framing/FrameDefaultVisitor.h new file mode 100644 index 0000000000..07e1d6d997 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/FrameDefaultVisitor.h @@ -0,0 +1,60 @@ +#ifndef QPID_FRAMING_FRAMEVISITOR_H +#define QPID_FRAMING_FRAMEVISITOR_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. + * + */ + +#include "qpid/framing/MethodBodyDefaultVisitor.h" +#include "qpid/framing/AMQBody.h" +#include "qpid/framing/AMQMethodBody.h" +#include "qpid/framing/AMQHeaderBody.h" +#include "qpid/framing/AMQContentBody.h" +#include "qpid/framing/AMQHeartbeatBody.h" + +namespace qpid { +namespace framing { +/** + * Visitor for all concrete frame body types, combines + * AMQBodyConstVisitor and MethodBodyDefaultVisitor. + * + * Derived classes may override visit methods to specify actions. + * Derived classes must override defaultVisit(), which is called + * for any non-overridden visit functions. + * + */ +struct FrameDefaultVisitor : public AMQBodyConstVisitor, + protected MethodBodyDefaultVisitor +{ + virtual void defaultVisit(const AMQBody&) = 0; + void defaultVisit(const AMQMethodBody& method) { defaultVisit(static_cast<const AMQBody&>(method)); } + + void visit(const AMQHeaderBody& b) { defaultVisit(b); } + void visit(const AMQContentBody& b) { defaultVisit(b); } + void visit(const AMQHeartbeatBody& b) { defaultVisit(b); } + void visit(const AMQMethodBody& b) { b.accept(static_cast<MethodBodyDefaultVisitor&>(*this)); } + + using AMQBodyConstVisitor::visit; + using MethodBodyDefaultVisitor::visit; +}; + +}} // namespace qpid::framing + + +#endif /*!QPID_FRAMING_FRAMEVISITOR_H*/ diff --git a/qpid/cpp/src/qpid/framing/FrameHandler.h b/qpid/cpp/src/qpid/framing/FrameHandler.h new file mode 100644 index 0000000000..457968c35e --- /dev/null +++ b/qpid/cpp/src/qpid/framing/FrameHandler.h @@ -0,0 +1,33 @@ +#ifndef QPID_FRAMING_FRAMEHANDLER_H +#define QPID_FRAMING_FRAMEHANDLER_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. + * + */ +#include "Handler.h" + +namespace qpid { +namespace framing { + +class AMQFrame; +typedef Handler<AMQFrame&> FrameHandler; + + +}} +#endif /*!QPID_FRAMING_FRAMEHANDLER_H*/ diff --git a/qpid/cpp/src/qpid/framing/FrameSet.cpp b/qpid/cpp/src/qpid/framing/FrameSet.cpp new file mode 100644 index 0000000000..e6f15f2c4e --- /dev/null +++ b/qpid/cpp/src/qpid/framing/FrameSet.cpp @@ -0,0 +1,84 @@ +/* + * + * 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. + * + */ + +#include "FrameSet.h" +#include "qpid/framing/all_method_bodies.h" +#include "qpid/framing/frame_functors.h" +#include "qpid/framing/MessageProperties.h" +#include "qpid/framing/TypeFilter.h" + +using namespace qpid::framing; +using namespace boost; + +FrameSet::FrameSet(const SequenceNumber& _id) : id(_id) {parts.reserve(4);} + +void FrameSet::append(const AMQFrame& part) +{ + parts.push_back(part); +} + +bool FrameSet::isComplete() const +{ + return !parts.empty() && parts.back().getEof() && parts.back().getEos(); +} + +bool FrameSet::isContentBearing() const +{ + const AMQMethodBody* method = getMethod(); + return method && method->isContentBearing(); +} + +const AMQMethodBody* FrameSet::getMethod() const +{ + return parts.empty() ? 0 : parts[0].getMethod(); +} + +const AMQHeaderBody* FrameSet::getHeaders() const +{ + return parts.size() < 2 ? 0 : parts[1].castBody<AMQHeaderBody>(); +} + +AMQHeaderBody* FrameSet::getHeaders() +{ + return parts.size() < 2 ? 0 : parts[1].castBody<AMQHeaderBody>(); +} + +uint64_t FrameSet::getContentSize() const +{ + SumBodySize sum; + map_if(sum, TypeFilter<CONTENT_BODY>()); + return sum.getSize(); +} + +void FrameSet::getContent(std::string& out) const { + out.clear(); + out.reserve(getContentSize()); + for(Frames::const_iterator i = parts.begin(); i != parts.end(); i++) { + if (i->getBody()->type() == CONTENT_BODY) + out += i->castBody<AMQContentBody>()->getData(); + } +} + +std::string FrameSet::getContent() const { + std::string out; + getContent(out); + return out; +} diff --git a/qpid/cpp/src/qpid/framing/FrameSet.h b/qpid/cpp/src/qpid/framing/FrameSet.h new file mode 100644 index 0000000000..454d292d9d --- /dev/null +++ b/qpid/cpp/src/qpid/framing/FrameSet.h @@ -0,0 +1,105 @@ +/* + * + * 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. + * + */ +#include <string> +#include <vector> +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/SequenceNumber.h" + +#ifndef _FrameSet_ +#define _FrameSet_ + +namespace qpid { +namespace framing { + +/** + * Collects the frames representing a message. + */ +class FrameSet +{ + typedef std::vector<AMQFrame> Frames; + const SequenceNumber id; + Frames parts; + +public: + typedef boost::shared_ptr<FrameSet> shared_ptr; + + FrameSet(const SequenceNumber& id); + void append(const AMQFrame& part); + bool isComplete() const; + + uint64_t getContentSize() const; + void getContent(std::string&) const; + std::string getContent() const; + + bool isContentBearing() const; + + const AMQMethodBody* getMethod() const; + const AMQHeaderBody* getHeaders() const; + AMQHeaderBody* getHeaders(); + + template <class T> bool isA() const { + const AMQMethodBody* method = getMethod(); + return method && method->isA<T>(); + } + + template <class T> const T* as() const { + const AMQMethodBody* method = getMethod(); + return (method && method->isA<T>()) ? dynamic_cast<const T*>(method) : 0; + } + + template <class T> const T* getHeaderProperties() const { + const AMQHeaderBody* header = getHeaders(); + return header ? header->get<T>() : 0; + } + + const SequenceNumber& getId() const { return id; } + + template <class P> void remove(P predicate) { + parts.erase(remove_if(parts.begin(), parts.end(), predicate), parts.end()); + } + + template <class F> void map(F& functor) { + for_each(parts.begin(), parts.end(), functor); + } + + template <class F> void map(F& functor) const { + for_each(parts.begin(), parts.end(), functor); + } + + template <class F, class P> void map_if(F& functor, P predicate) { + for(Frames::iterator i = parts.begin(); i != parts.end(); i++) { + if (predicate(*i)) functor(*i); + } + } + + template <class F, class P> void map_if(F& functor, P predicate) const { + for(Frames::const_iterator i = parts.begin(); i != parts.end(); i++) { + if (predicate(*i)) functor(*i); + } + } +}; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/framing/FramingContent.cpp b/qpid/cpp/src/qpid/framing/FramingContent.cpp new file mode 100644 index 0000000000..cd134b0e89 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/FramingContent.cpp @@ -0,0 +1,73 @@ +/* + * + * 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. + * + */ +#include "Buffer.h" +#include "FramingContent.h" +#include "qpid/Exception.h" +#include "qpid/framing/reply_exceptions.h" + +namespace qpid { +namespace framing { + +Content::Content() : discriminator(0) {} + +Content::Content(uint8_t _discriminator, const string& _value): discriminator(_discriminator), value(_value) { + validate(); +} + +void Content::validate() { + if (discriminator == REFERENCE) { + if(value.empty()) { + throw InvalidArgumentException( + QPID_MSG("Reference cannot be empty")); + } + }else if (discriminator != INLINE) { + throw SyntaxErrorException( + QPID_MSG("Invalid discriminator: " << discriminator)); + } +} + +Content::~Content() {} + +void Content::encode(Buffer& buffer) const { + buffer.putOctet(discriminator); + buffer.putLongString(value); +} + +void Content::decode(Buffer& buffer) { + discriminator = buffer.getOctet(); + buffer.getLongString(value); + validate(); +} + +size_t Content::size() const { + return 1/*discriminator*/ + 4/*for recording size of long string*/ + value.size(); +} + +std::ostream& operator<<(std::ostream& out, const Content& content) { + if (content.discriminator == REFERENCE) { + out << "{REF:" << content.value << "}"; + } else if (content.discriminator == INLINE) { + out << "{INLINE:" << content.value.size() << " bytes}"; + } + return out; +} + +}} // namespace framing::qpid diff --git a/qpid/cpp/src/qpid/framing/FramingContent.h b/qpid/cpp/src/qpid/framing/FramingContent.h new file mode 100644 index 0000000000..9315a7716f --- /dev/null +++ b/qpid/cpp/src/qpid/framing/FramingContent.h @@ -0,0 +1,63 @@ +/* + * + * 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. + * + */ +#ifndef _framing_FramingContent_h +#define _framing_FramingContent_h + +#include <ostream> + +namespace qpid { +namespace framing { + +class Buffer; + +enum discriminator_types { INLINE = 0, REFERENCE = 1 }; + +/** + * A representation of the AMQP 'content' data type (used for message + * bodies) which can hold inline data or a reference. + */ +class Content +{ + uint8_t discriminator; + string value; + + void validate(); + + public: + Content(); + Content(uint8_t _discriminator, const string& _value); + ~Content(); + + void encode(Buffer& buffer) const; + void decode(Buffer& buffer); + size_t size() const; + bool isInline() const { return discriminator == INLINE; } + bool isReference() const { return discriminator == REFERENCE; } + const string& getValue() const { return value; } + void setValue(const string& newValue) { value = newValue; } + + friend std::ostream& operator<<(std::ostream&, const Content&); +}; + +}} // namespace qpid::framing + + +#endif /*!_framing_FramingContent_h*/ diff --git a/qpid/cpp/src/qpid/framing/Handler.h b/qpid/cpp/src/qpid/framing/Handler.h new file mode 100644 index 0000000000..fbf3c0b7ca --- /dev/null +++ b/qpid/cpp/src/qpid/framing/Handler.h @@ -0,0 +1,122 @@ +#ifndef QPID_FRAMING_HANDLER_H +#define QPID_FRAMING_HANDLER_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. + * + */ +#include "qpid/shared_ptr.h" +#include <boost/type_traits/remove_reference.hpp> +#include <assert.h> + +namespace qpid { +namespace framing { + +/** Generic handler that can be linked into chains. */ +template <class T> +struct Handler { + typedef T HandledType; + typedef void handleFptr(T); + typedef void result_type; // Compatible with std/boost functors. + + Handler(Handler<T>* next_=0) : next(next_) {} + virtual ~Handler() {} + virtual void handle(T) = 0; + + /** Allow functor syntax for calling handle */ + void operator()(T t) { handle(t); } + + + /** Pointer to next handler in a linked list. */ + Handler<T>* next; + + /** A Chain is a handler that forwards to a modifiable + * linked list of handlers. + */ + struct Chain : public Handler<T> { + Chain(Handler<T>* first=0) : Handler(first) {} + void operator=(Handler<T>* h) { next = h; } + void handle(T t) { next->handle(t); } + // TODO aconway 2007-08-29: chain modifier ops here. + }; + + /** In/out pair of handler chains. */ + struct Chains { + Chains(Handler<T>* in_=0, Handler<T>* out_=0) : in(in_), out(out_) {} + void reset(Handler<T>* in_=0, Handler<T>* out_=0) { in = in_; out = out_; } + Chain in; + Chain out; + }; + + /** Adapt any void(T) functor as a Handler. + * Functor<F>(f) will copy f. + * Functor<F&>(f) will only take a reference to x. + */ + template <class F> class Functor : public Handler<T> { + public: + Functor(F f, Handler<T>* next=0) : Handler<T>(next), functor(f) {} + void handle(T t) { functor(t); } + private: + F functor; + }; + + /** Adapt a member function of X as a Handler. + * Only holds a reference to its target, not a copy. + */ + template <class X, void (X::*F)(T)> + class MemFunRef : public Handler<T> { + public: + MemFunRef(X& x, Handler<T>* next=0) : Handler(next), target(x) {} + void handle(T t) { (target.*F)(t); } + + /** Allow calling with -> syntax, compatible with Chains */ + MemFunRef* operator->() { return this; } + + private: + X& target; + }; + + /** Interface for a handler that implements a + * pair of in/out handle operations. + * @see InOutHandler + */ + class InOutHandlerInterface { + public: + virtual ~InOutHandlerInterface() {} + virtual void handleIn(T) = 0; + virtual void handleOut(T) = 0; + }; + + /** Support for implementing an in-out handler pair as a single class. + * Public interface is Handler<T>::Chains pair, but implementation + * overrides handleIn, handleOut functions in a single class. + */ + struct InOutHandler : protected InOutHandlerInterface { + InOutHandler(Handler<T>* nextIn=0, Handler<T>* nextOut=0) : in(*this, nextIn), out(*this, nextOut) {} + MemFunRef<InOutHandlerInterface, &InOutHandlerInterface::handleIn> in; + MemFunRef<InOutHandlerInterface, &InOutHandlerInterface::handleOut> out; + }; + +}; + + + +}} +#endif /*!QPID_FRAMING_HANDLER_H*/ +// diff --git a/qpid/cpp/src/qpid/framing/HeaderProperties.h b/qpid/cpp/src/qpid/framing/HeaderProperties.h new file mode 100644 index 0000000000..0c805922e8 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/HeaderProperties.h @@ -0,0 +1,46 @@ +/* + * + * 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. + * + */ +#include "amqp_types.h" +#include "Buffer.h" + +#ifndef _HeaderProperties_ +#define _HeaderProperties_ + +namespace qpid { +namespace framing { + + enum header_classes{BASIC = 60}; + + class HeaderProperties + { + + public: + inline virtual ~HeaderProperties(){} + virtual uint8_t classId() const = 0; + virtual uint32_t size() const = 0; + virtual void encode(Buffer& buffer) const = 0; + virtual void decode(Buffer& buffer, uint32_t size) = 0; + }; +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/framing/InitiationHandler.cpp b/qpid/cpp/src/qpid/framing/InitiationHandler.cpp new file mode 100644 index 0000000000..eceeaf4abc --- /dev/null +++ b/qpid/cpp/src/qpid/framing/InitiationHandler.cpp @@ -0,0 +1,24 @@ +/* + * + * 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. + * + */ + +#include "InitiationHandler.h" + +qpid::framing::InitiationHandler::~InitiationHandler() {} diff --git a/qpid/cpp/src/qpid/framing/InitiationHandler.h b/qpid/cpp/src/qpid/framing/InitiationHandler.h new file mode 100644 index 0000000000..16a6b502e8 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/InitiationHandler.h @@ -0,0 +1,41 @@ +/* + * + * 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. + * + */ +#include <string> + +#ifndef _InitiationHandler_ +#define _InitiationHandler_ + +#include "ProtocolInitiation.h" + +namespace qpid { +namespace framing { + + class InitiationHandler{ + public: + virtual ~InitiationHandler(); + virtual void initiated(const ProtocolInitiation&) = 0; + }; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/framing/InputHandler.h b/qpid/cpp/src/qpid/framing/InputHandler.h new file mode 100644 index 0000000000..3a6d786a24 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/InputHandler.h @@ -0,0 +1,41 @@ +#ifndef _InputHandler_ +#define _InputHandler_ +/* + * + * 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. + * + */ + +#include "FrameHandler.h" +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace framing { + +// TODO aconway 2007-08-29: Eliminate, replace with FrameHandler. +class InputHandler : public FrameHandler { + public: + virtual ~InputHandler() {} + virtual void received(AMQFrame&) = 0; + void handle(AMQFrame& f) { received(f); } +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/framing/Invoker.h b/qpid/cpp/src/qpid/framing/Invoker.h new file mode 100644 index 0000000000..6c2b972c13 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/Invoker.h @@ -0,0 +1,86 @@ +#ifndef QPID_FRAMING_INVOKER_H +#define QPID_FRAMING_INVOKER_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. + * + */ +#include "qpid/framing/AMQMethodBody.h" +#include "qpid/framing/MethodBodyDefaultVisitor.h" +#include "qpid/framing/StructHelper.h" + +#include <boost/optional.hpp> + +namespace qpid { +namespace framing { + +class AMQMethodBody; + +/** + * Base class for invoker visitors. + */ +class Invoker: public MethodBodyDefaultVisitor, protected StructHelper +{ + public: + struct Result { + public: + Result() : handled(false) {} + const std::string& getResult() const { return result; } + bool hasResult() const { return !result.empty(); } + bool wasHandled() const { return handled; } + operator bool() const { return handled; } + + std::string result; + bool handled; + }; + + void defaultVisit(const AMQMethodBody&) {} + Result getResult() const { return result; } + + protected: + Result result; +}; + +/** + * Invoke on an invocable object. + * Invocable classes must provide a nested type Invoker. + */ +template <class Invocable> +Invoker::Result invoke(Invocable& target, const AMQMethodBody& body) { + typename Invocable::Invoker invoker(target); + body.accept(invoker); + return invoker.getResult(); +} + +/** + * Invoke on an invocable object. + * Invocable classes must provide a nested type Invoker. + */ +template <class Invocable> +Invoker::Result invoke(Invocable& target, const AMQBody& body) { + typename Invocable::Invoker invoker(target); + const AMQMethodBody* method = body.getMethod(); + if (method) + method->accept(invoker); + return invoker.getResult(); +} + +}} // namespace qpid::framing + +#endif /*!QPID_FRAMING_INVOKER_H*/ diff --git a/qpid/cpp/src/qpid/framing/MethodContent.h b/qpid/cpp/src/qpid/framing/MethodContent.h new file mode 100644 index 0000000000..737c0d6b7b --- /dev/null +++ b/qpid/cpp/src/qpid/framing/MethodContent.h @@ -0,0 +1,40 @@ +/* + * + * 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. + * + */ +#ifndef _MethodContent_ +#define _MethodContent_ + +#include <string> +#include "AMQHeaderBody.h" + +namespace qpid { +namespace framing { + +class MethodContent +{ +public: + virtual ~MethodContent() {} + //TODO: rethink this interface + virtual AMQHeaderBody getHeader() const = 0; + virtual const std::string& getData() const = 0; +}; + +}} +#endif diff --git a/qpid/cpp/src/qpid/framing/ModelMethod.h b/qpid/cpp/src/qpid/framing/ModelMethod.h new file mode 100644 index 0000000000..07600aadca --- /dev/null +++ b/qpid/cpp/src/qpid/framing/ModelMethod.h @@ -0,0 +1,49 @@ +#ifndef _ModelMethod_ +#define _ModelMethod_ + +/* + * + * 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. + * + */ +#include "AMQMethodBody.h" +#include "qpid/framing/ExecutionHeader.h" + +namespace qpid { +namespace framing { + + +class ModelMethod : public AMQMethodBody +{ + mutable ExecutionHeader header; +public: + virtual ~ModelMethod() {} + virtual void encodeHeader(Buffer& buffer) const { header.encode(buffer); } + virtual void decodeHeader(Buffer& buffer, uint32_t size=0) { header.decode(buffer, size); } + virtual uint32_t headerSize() const { return header.size(); } + virtual bool isSync() const { return header.getSync(); } + virtual void setSync(bool on) const { header.setSync(on); } + ExecutionHeader& getHeader() { return header; } + const ExecutionHeader& getHeader() const { return header; } +}; + + +}} // namespace qpid::framing + + +#endif diff --git a/qpid/cpp/src/qpid/framing/OutputHandler.h b/qpid/cpp/src/qpid/framing/OutputHandler.h new file mode 100644 index 0000000000..6f4b27fb72 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/OutputHandler.h @@ -0,0 +1,42 @@ +#ifndef _OutputHandler_ +#define _OutputHandler_ + +/* + * + * 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. + * + */ +#include <boost/noncopyable.hpp> +#include "FrameHandler.h" + +namespace qpid { +namespace framing { + +// TODO aconway 2007-08-29: Replace with FrameHandler. +class OutputHandler : public FrameHandler { + public: + virtual ~OutputHandler() {} + virtual void send(AMQFrame&) = 0; + void handle(AMQFrame& f) { send(f); } +}; + + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp b/qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp new file mode 100644 index 0000000000..50617de017 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/ProtocolInitiation.cpp @@ -0,0 +1,66 @@ +/* + * + * 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. + * + */ +#include "ProtocolInitiation.h" + +namespace qpid { +namespace framing { + +ProtocolInitiation::ProtocolInitiation(){} + +ProtocolInitiation::ProtocolInitiation(uint8_t _major, uint8_t _minor) : version(_major, _minor) {} + +ProtocolInitiation::ProtocolInitiation(ProtocolVersion p) : version(p) {} + +ProtocolInitiation::~ProtocolInitiation(){} + +void ProtocolInitiation::encode(Buffer& buffer) const { + buffer.putOctet('A'); + buffer.putOctet('M'); + buffer.putOctet('Q'); + buffer.putOctet('P'); + buffer.putOctet(1);//class + buffer.putOctet(1);//instance + buffer.putOctet(version.getMajor()); + buffer.putOctet(version.getMinor()); +} + +bool ProtocolInitiation::decode(Buffer& buffer){ + if(buffer.available() >= 8){ + buffer.getOctet();//A + buffer.getOctet();//M + buffer.getOctet();//Q + buffer.getOctet();//P + buffer.getOctet();//class + buffer.getOctet();//instance + version.setMajor(buffer.getOctet()); + version.setMinor(buffer.getOctet()); + return true; + }else{ + return false; + } +} + + +std::ostream& operator<<(std::ostream& o, const framing::ProtocolInitiation& pi) { + return o << int(pi.getMajor()) << "-" << int(pi.getMinor()); +} + +}} // namespace qpid::framing diff --git a/qpid/cpp/src/qpid/framing/ProtocolInitiation.h b/qpid/cpp/src/qpid/framing/ProtocolInitiation.h new file mode 100644 index 0000000000..43e32da4cf --- /dev/null +++ b/qpid/cpp/src/qpid/framing/ProtocolInitiation.h @@ -0,0 +1,58 @@ +/* + * + * 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. + * + */ +#include "amqp_types.h" +#include "Buffer.h" +#include "AMQDataBlock.h" +#include "ProtocolVersion.h" + +#ifndef _ProtocolInitiation_ +#define _ProtocolInitiation_ + +namespace qpid { +namespace framing { + +class ProtocolInitiation : public AMQDataBlock +{ +private: + ProtocolVersion version; + +public: + ProtocolInitiation(); + ProtocolInitiation(uint8_t major, uint8_t minor); + ProtocolInitiation(ProtocolVersion p); + virtual ~ProtocolInitiation(); + virtual void encode(Buffer& buffer) const; + virtual bool decode(Buffer& buffer); + inline virtual uint32_t size() const { return 8; } + inline uint8_t getMajor() const { return version.getMajor(); } + inline uint8_t getMinor() const { return version.getMinor(); } + inline ProtocolVersion getVersion() const { return version; } + bool operator==(ProtocolVersion v) const { return v == getVersion(); } +}; + +std::ostream& operator<<(std::ostream& o, const framing::ProtocolInitiation& pi); + + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/framing/ProtocolVersion.cpp b/qpid/cpp/src/qpid/framing/ProtocolVersion.cpp new file mode 100644 index 0000000000..7a96bfa925 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/ProtocolVersion.cpp @@ -0,0 +1,44 @@ +/* + * + * 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. + * + */ +#include "ProtocolVersion.h" +#include <sstream> + +using namespace qpid::framing; + +const std::string ProtocolVersion::toString() const +{ + std::stringstream ss; + ss << major_ << "-" << minor_; + return ss.str(); +} + +ProtocolVersion& ProtocolVersion::operator=(ProtocolVersion p) +{ + major_ = p.major_; + minor_ = p.minor_; + return *this; +} + +bool ProtocolVersion::operator==(ProtocolVersion p) const +{ + return major_ == p.major_ && minor_ == p.minor_; +} + diff --git a/qpid/cpp/src/qpid/framing/ProtocolVersion.h b/qpid/cpp/src/qpid/framing/ProtocolVersion.h new file mode 100644 index 0000000000..a2a755397b --- /dev/null +++ b/qpid/cpp/src/qpid/framing/ProtocolVersion.h @@ -0,0 +1,57 @@ +/* + * + * 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. + * + */ +#ifndef _ProtocolVersion_ +#define _ProtocolVersion_ + +#include "amqp_types.h" + +namespace qpid +{ +namespace framing +{ + +class ProtocolVersion +{ +private: + uint8_t major_; + uint8_t minor_; + +public: + ProtocolVersion(uint8_t _major=0, uint8_t _minor=0) + : major_(_major), minor_(_minor) {} + + uint8_t getMajor() const { return major_; } + void setMajor(uint8_t major) { major_ = major; } + uint8_t getMinor() const { return minor_; } + void setMinor(uint8_t minor) { minor_ = minor; } + const std::string toString() const; + + ProtocolVersion& operator=(ProtocolVersion p); + + bool operator==(ProtocolVersion p) const; + bool operator!=(ProtocolVersion p) const { return ! (*this == p); } +}; + +} // namespace framing +} // namespace qpid + + +#endif // ifndef _ProtocolVersion_ diff --git a/qpid/cpp/src/qpid/framing/Proxy.cpp b/qpid/cpp/src/qpid/framing/Proxy.cpp new file mode 100644 index 0000000000..b47060028f --- /dev/null +++ b/qpid/cpp/src/qpid/framing/Proxy.cpp @@ -0,0 +1,37 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "Proxy.h" +#include "AMQFrame.h" + +namespace qpid { +namespace framing { + +Proxy::~Proxy() {} + +void Proxy::send(const AMQBody& b) { + AMQFrame f(b); + out.handle(f); +} + + +ProtocolVersion Proxy::getVersion() const { + return ProtocolVersion(); +} + +}} // namespace qpid::framing diff --git a/qpid/cpp/src/qpid/framing/Proxy.h b/qpid/cpp/src/qpid/framing/Proxy.h new file mode 100644 index 0000000000..86b99a83b0 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/Proxy.h @@ -0,0 +1,52 @@ +#ifndef _framing_Proxy_h +#define _framing_Proxy_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "FrameHandler.h" +#include "ProtocolVersion.h" + +namespace qpid { +namespace framing { + +class AMQBody; + +/** + * Base class for proxies. + */ +class Proxy +{ + public: + Proxy(FrameHandler& h) : out(h) {} + virtual ~Proxy(); + + void send(const AMQBody&); + + ProtocolVersion getVersion() const; + FrameHandler& getHandler() { return out; } + + protected: + FrameHandler& out; +}; + +}} // namespace qpid::framing + + + +#endif /*!_framing_Proxy_h*/ diff --git a/qpid/cpp/src/qpid/framing/SendContent.cpp b/qpid/cpp/src/qpid/framing/SendContent.cpp new file mode 100644 index 0000000000..a62e4eeb72 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/SendContent.cpp @@ -0,0 +1,69 @@ +/* + * + * 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. + * + */ + +#include "SendContent.h" + +qpid::framing::SendContent::SendContent(FrameHandler& h, uint16_t mfs, uint efc) : handler(h), + maxFrameSize(mfs), + expectedFrameCount(efc), frameCount(0) {} + +void qpid::framing::SendContent::operator()(const AMQFrame& f) +{ + bool first = frameCount == 0; + bool last = ++frameCount == expectedFrameCount; + + /*end of frame marker is included in frameOverhead() but not in + real frame size, hence substract -1 from frameOverhead()*/ + uint16_t maxContentSize = maxFrameSize - (AMQFrame::frameOverhead() - 1); + const AMQContentBody* body(f.castBody<AMQContentBody>()); + if (body->size() > maxContentSize) { + uint32_t offset = 0; + for (int chunk = body->size() / maxContentSize; chunk > 0; chunk--) { + sendFragment(*body, offset, maxContentSize, first && offset == 0, last && offset + maxContentSize == body->size()); + offset += maxContentSize; + } + uint32_t remainder = body->size() % maxContentSize; + if (remainder) { + sendFragment(*body, offset, remainder, first && offset == 0, last); + } + } else { + AMQFrame copy(f); + setFlags(copy, first, last); + handler.handle(copy); + } +} + +void qpid::framing::SendContent::sendFragment(const AMQContentBody& body, uint32_t offset, uint16_t size, bool first, bool last) const +{ + AMQFrame fragment(in_place<AMQContentBody>( + body.getData().substr(offset, size))); + setFlags(fragment, first, last); + handler.handle(fragment); +} + +void qpid::framing::SendContent::setFlags(AMQFrame& f, bool first, bool last) const +{ + f.setBof(false); + f.setBos(first); + f.setEof(true);//content is always the last segment + f.setEos(last); +} + diff --git a/qpid/cpp/src/qpid/framing/SendContent.h b/qpid/cpp/src/qpid/framing/SendContent.h new file mode 100644 index 0000000000..dcd5202b3e --- /dev/null +++ b/qpid/cpp/src/qpid/framing/SendContent.h @@ -0,0 +1,55 @@ +/* + * + * 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. + * + */ +#include <string> +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/FrameHandler.h" + +#ifndef _SendContent_ +#define _SendContent_ + +namespace qpid { +namespace framing { + +/** + * Functor that sends frame to handler, refragmenting if + * necessary. Currently only works on content frames but this could be + * changed once we support multi-frame segments in general. + */ +class SendContent +{ + mutable FrameHandler& handler; + const uint16_t maxFrameSize; + uint expectedFrameCount; + uint frameCount; + + void sendFragment(const AMQContentBody& body, uint32_t offset, uint16_t size, bool first, bool last) const; + void setFlags(AMQFrame& f, bool first, bool last) const; +public: + SendContent(FrameHandler& _handler, uint16_t _maxFrameSize, uint frameCount); + void operator()(const AMQFrame& f); +}; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/framing/SequenceNumber.cpp b/qpid/cpp/src/qpid/framing/SequenceNumber.cpp new file mode 100644 index 0000000000..1b62d296c6 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/SequenceNumber.cpp @@ -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. + * + */ + +#include "SequenceNumber.h" + +using qpid::framing::SequenceNumber; + +SequenceNumber::SequenceNumber() : value(0 - 1) {} + +SequenceNumber::SequenceNumber(uint32_t v) : value((int32_t) v) {} + +bool SequenceNumber::operator==(const SequenceNumber& other) const +{ + return value == other.value; +} + +bool SequenceNumber::operator!=(const SequenceNumber& other) const +{ + return !(value == other.value); +} + + +SequenceNumber& SequenceNumber::operator++() +{ + value = value + 1; + return *this; +} + +const SequenceNumber SequenceNumber::operator++(int) +{ + SequenceNumber old(value); + value = value + 1; + return old; +} + +SequenceNumber& SequenceNumber::operator--() +{ + value = value - 1; + return *this; +} + +bool SequenceNumber::operator<(const SequenceNumber& other) const +{ + return (value - other.value) < 0; +} + +bool SequenceNumber::operator>(const SequenceNumber& other) const +{ + return other < *this; +} + +bool SequenceNumber::operator<=(const SequenceNumber& other) const +{ + return *this == other || *this < other; +} + +bool SequenceNumber::operator>=(const SequenceNumber& other) const +{ + return *this == other || *this > other; +} + +namespace qpid { +namespace framing { + +int32_t operator-(const SequenceNumber& a, const SequenceNumber& b) +{ + int32_t result = a.value - b.value; + return result; +} + +}} diff --git a/qpid/cpp/src/qpid/framing/SequenceNumber.h b/qpid/cpp/src/qpid/framing/SequenceNumber.h new file mode 100644 index 0000000000..0ed591b804 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/SequenceNumber.h @@ -0,0 +1,66 @@ +/* + * + * 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. + * + */ +#ifndef _framing_SequenceNumber_h +#define _framing_SequenceNumber_h + +#include "amqp_types.h" + +namespace qpid { +namespace framing { + +/** + * 4-byte sequence number that 'wraps around'. + */ +class SequenceNumber +{ + int32_t value; + + public: + SequenceNumber(); + SequenceNumber(uint32_t v); + + SequenceNumber& operator++();//prefix ++ + const SequenceNumber operator++(int);//postfix ++ + SequenceNumber& operator--();//prefix ++ + bool operator==(const SequenceNumber& other) const; + bool operator!=(const SequenceNumber& other) const; + bool operator<(const SequenceNumber& other) const; + bool operator>(const SequenceNumber& other) const; + bool operator<=(const SequenceNumber& other) const; + bool operator>=(const SequenceNumber& other) const; + uint32_t getValue() const { return (uint32_t) value; } + operator uint32_t() const { return (uint32_t) value; } + + friend int32_t operator-(const SequenceNumber& a, const SequenceNumber& b); + + template <class S> void serialize(S& s) { s(value); } +}; + +struct Window +{ + SequenceNumber hwm; + SequenceNumber lwm; +}; + +}} // namespace qpid::framing + + +#endif diff --git a/qpid/cpp/src/qpid/framing/SequenceNumberSet.cpp b/qpid/cpp/src/qpid/framing/SequenceNumberSet.cpp new file mode 100644 index 0000000000..afab9033e5 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/SequenceNumberSet.cpp @@ -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. + * + */ + +#include "SequenceNumberSet.h" + +using namespace qpid::framing; + +void SequenceNumberSet::encode(Buffer& buffer) const +{ + buffer.putShort(size() * 4); + for (const_iterator i = begin(); i != end(); i++) { + buffer.putLong(i->getValue()); + } +} + +void SequenceNumberSet::decode(Buffer& buffer) +{ + uint16_t count = (buffer.getShort() / 4); + for (uint16_t i = 0; i < count; i++) { + push_back(SequenceNumber(buffer.getLong())); + } +} + +uint32_t SequenceNumberSet::encodedSize() const +{ + return 2 /*count*/ + (size() * 4); +} + +SequenceNumberSet SequenceNumberSet::condense() const +{ + SequenceNumberSet result; + const_iterator last = end(); + const_iterator start = end(); + for (const_iterator i = begin(); i != end(); i++) { + if (start == end()) { + start = i; + } else if (*i - *last > 1) { + result.push_back(*start); + result.push_back(*last); + start = i; + } + last = i; + } + if (start != end()) { + result.push_back(*start); + result.push_back(*last); + } + return result; +} + +void SequenceNumberSet::addRange(const SequenceNumber& start, const SequenceNumber& end) +{ + push_back(start); + push_back(end); +} + +namespace qpid{ +namespace framing{ + +std::ostream& operator<<(std::ostream& out, const SequenceNumberSet& set) { + out << "{"; + for (SequenceNumberSet::const_iterator i = set.begin(); i != set.end(); i++) { + if (i != set.begin()) out << ", "; + out << (i->getValue()); + } + out << "}"; + return out; +} + +} +} diff --git a/qpid/cpp/src/qpid/framing/SequenceNumberSet.h b/qpid/cpp/src/qpid/framing/SequenceNumberSet.h new file mode 100644 index 0000000000..666307f9d9 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/SequenceNumberSet.h @@ -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. + * + */ +#ifndef _framing_SequenceNumberSet_h +#define _framing_SequenceNumberSet_h + +#include <ostream> +#include "amqp_types.h" +#include "Buffer.h" +#include "SequenceNumber.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/InlineVector.h" + +namespace qpid { +namespace framing { + +class SequenceNumberSet : public InlineVector<SequenceNumber, 2> +{ + typedef InlineVector<SequenceNumber, 2> Base; +public: + typedef Base::const_iterator const_iterator; + typedef Base::iterator iterator; + + void encode(Buffer& buffer) const; + void decode(Buffer& buffer); + uint32_t encodedSize() const; + SequenceNumberSet condense() const; + void addRange(const SequenceNumber& start, const SequenceNumber& end); + + template <class T> + void processRanges(T& t) const + { + if (size() % 2) { //must be even number + throw InvalidArgumentException("SequenceNumberSet contains odd number of elements"); + } + + for (SequenceNumberSet::const_iterator i = begin(); i != end(); i++) { + SequenceNumber first = *(i); + SequenceNumber last = *(++i); + t(first, last); + } + } + + friend std::ostream& operator<<(std::ostream&, const SequenceNumberSet&); +}; + + +}} // namespace qpid::framing + + +#endif diff --git a/qpid/cpp/src/qpid/framing/SequenceSet.cpp b/qpid/cpp/src/qpid/framing/SequenceSet.cpp new file mode 100644 index 0000000000..1858467de6 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/SequenceSet.cpp @@ -0,0 +1,226 @@ +/* + * + * 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. + * + */ + +#include "SequenceSet.h" + +using namespace qpid::framing; +using std::max; +using std::min; + +namespace { +//each range contains 2 numbers, 4 bytes each +uint16_t RANGE_SIZE = 2 * 4; +} + +void SequenceSet::encode(Buffer& buffer) const +{ + buffer.putShort(ranges.size() * RANGE_SIZE); + for (Ranges::const_iterator i = ranges.begin(); i != ranges.end(); i++) { + i->encode(buffer); + } +} + +void SequenceSet::decode(Buffer& buffer) +{ + uint16_t size = buffer.getShort(); + uint16_t count = size / RANGE_SIZE;//number of ranges + if (size % RANGE_SIZE) throw FrameErrorException(QPID_MSG("Invalid size for sequence set: " << size)); + + for (uint16_t i = 0; i < count; i++) { + add(SequenceNumber(buffer.getLong()), SequenceNumber(buffer.getLong())); + } +} + +uint32_t SequenceSet::size() const +{ + return 2 /*size field*/ + (ranges.size() * RANGE_SIZE); +} + +bool SequenceSet::contains(const SequenceNumber& point) const +{ + for (Ranges::const_iterator i = ranges.begin(); i != ranges.end(); i++) { + if (i->contains(point)) return true; + } + return false; +} + +void SequenceSet::add(const SequenceNumber& s) +{ + add(s, s); +} + +void SequenceSet::add(const SequenceNumber& start, const SequenceNumber& end) +{ + if (start > end) { + add(end, start); + } else { + Range r(start, end); + Ranges::iterator merged = ranges.end(); + Ranges::iterator i = ranges.begin(); + while (i != ranges.end() && merged == ranges.end() && i->start < start) { + if (i->merge(r)) merged = i; + i++; + } + if (merged == ranges.end()) { + i = merged = ranges.insert(i, r); + i++; + } + while (i != ranges.end() && merged->merge(*i)) { + i = ranges.erase(i); + } + } +} + +void SequenceSet::add(const SequenceSet& set) +{ + for (Ranges::const_iterator i = set.ranges.begin(); i != set.ranges.end(); i++) { + add(i->start, i->end); + } +} + +void SequenceSet::remove(const SequenceSet& set) +{ + for (Ranges::const_iterator i = set.ranges.begin(); i != set.ranges.end(); i++) { + remove(i->start, i->end); + } +} + +void SequenceSet::remove(const SequenceNumber& start, const SequenceNumber& end) +{ + if (start > end) { + remove(end, start); + } else { + Ranges::iterator i = ranges.begin(); + while (i != ranges.end() && i->start < start) { + if (start <= i->end) { + if (end > i->end) { + //i.e. start is within the range pointed to by i, but end is not + i->end = (uint32_t)start - 1; + } else { + //whole of range to be deleted is contained within that pointed to be i + if (end == i->end) { + //just shrink range pointed to by i + i->end = (uint32_t)start - 1; + } else { + //need to split the range pointed to by i + Range r(i->start, (uint32_t)start - 1); + i->start = end + 1; + ranges.insert(i, r); + } + return;//no need to go any further + } + } + i++; + } + Ranges::iterator j = i; + while (j != ranges.end() && j->end < end) { + j++; + } + if (j->start <= end){ + j->start = end + 1; + } + ranges.erase(i, j); + } +} + +void SequenceSet::remove(const SequenceNumber& s) +{ + for (Ranges::iterator i = ranges.begin(); i != ranges.end() && s >= i->start; i++) { + if (i->start == s) { + if (i->start == i->end) { + ranges.erase(i); + } else { + ++(i->start); + } + } else if (i->end == s) { + --(i->end); + } else if (i->contains(s)) { + //need to split range pointed to by i + Range r(i->start, (uint32_t)s - 1); + i->start = s + 1; + ranges.insert(i, r); + } + } +} + +bool SequenceSet::empty() const +{ + return ranges.empty(); +} + +void SequenceSet::clear() +{ + return ranges.clear(); +} + +bool SequenceSet::Range::contains(SequenceNumber i) const +{ + return i >= start && i <= end; +} + +bool SequenceSet::Range::intersects(const Range& r) const +{ + return r.contains(start) || r.contains(end) || contains(r.start) || contains(r.end); +} + +bool SequenceSet::Range::merge(const Range& r) +{ + if (intersects(r) || mergeable(r.end) || r.mergeable(end)) { + start = min(start, r.start); + end = max(end, r.end); + return true; + } else { + return false; + } +} + +bool SequenceSet::Range::mergeable(const SequenceNumber& s) const +{ + if (contains(s) || start - s == 1 || s - end == 1) { + return true; + } else { + return false; + } +} + +void SequenceSet::Range::encode(Buffer& buffer) const +{ + buffer.putLong(start); + buffer.putLong(end); +} + +SequenceSet::Range::Range(SequenceNumber s, SequenceNumber e) : start(s), end(e) {} + +namespace qpid{ +namespace framing{ + +std::ostream& operator<<(std::ostream& out, const SequenceSet& set) { + out << "{"; + for (SequenceSet::Ranges::const_iterator i = set.ranges.begin(); i != set.ranges.end(); i++) { + if (i != set.ranges.begin()) out << ", "; + out << i->start.getValue() << "-" << i->end.getValue(); + } + out << "}"; + return out; +} + +} +} diff --git a/qpid/cpp/src/qpid/framing/SequenceSet.h b/qpid/cpp/src/qpid/framing/SequenceSet.h new file mode 100644 index 0000000000..2f34cb5cba --- /dev/null +++ b/qpid/cpp/src/qpid/framing/SequenceSet.h @@ -0,0 +1,86 @@ +/* + * + * 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. + * + */ +#ifndef _framing_SequenceSet_h +#define _framing_SequenceSet_h + +#include <ostream> +#include <list> +#include "amqp_types.h" +#include "Buffer.h" +#include "SequenceNumber.h" +#include "qpid/framing/reply_exceptions.h" + +namespace qpid { +namespace framing { + +class SequenceSet +{ + struct Range + { + SequenceNumber start; + SequenceNumber end; + + Range(SequenceNumber s, SequenceNumber e); + bool contains(SequenceNumber i) const; + bool intersects(const Range& r) const; + bool merge(const Range& r); + bool mergeable(const SequenceNumber& r) const; + void encode(Buffer& buffer) const; + }; + + typedef std::list<Range> Ranges; + Ranges ranges; + +public: + SequenceSet() {} + SequenceSet(const SequenceNumber& s) { add(s); } + + void encode(Buffer& buffer) const; + void decode(Buffer& buffer); + uint32_t size() const; + + bool contains(const SequenceNumber& s) const; + void add(const SequenceNumber& s); + void add(const SequenceNumber& start, const SequenceNumber& end); + void add(const SequenceSet& set); + void remove(const SequenceNumber& s); + void remove(const SequenceNumber& start, const SequenceNumber& end); + void remove(const SequenceSet& set); + + void clear(); + bool empty() const; + + template <class T> + void for_each(T& t) const + { + for (Ranges::const_iterator i = ranges.begin(); i != ranges.end(); i++) { + t(i->start, i->end); + } + } + + friend std::ostream& operator<<(std::ostream&, const SequenceSet&); +}; + + +}} // namespace qpid::framing + + +#endif diff --git a/qpid/cpp/src/qpid/framing/SerializeHandler.h b/qpid/cpp/src/qpid/framing/SerializeHandler.h new file mode 100644 index 0000000000..55bd7da08c --- /dev/null +++ b/qpid/cpp/src/qpid/framing/SerializeHandler.h @@ -0,0 +1,49 @@ +#ifndef QPID_FRAMING_SERIALIZEHANDLER_H +#define QPID_FRAMING_SERIALIZEHANDLER_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. + * + */ +#include "qpid/sys/Serializer.h" +#include "qpid/framing/Handler.h" + +#include <boost/bind.hpp> + +namespace qpid { +namespace framing { + + +/** Serializer that can be inserted into a Handler chain */ +template <class T> +struct SerializeHandler : public framing::Handler<T>, public sys::Serializer { + SerializeHandler(typename framing::Handler<T>::Chain next) + : framing::Handler<T>(next) {} + void handle(T value) { + execute(boost::bind(&framing::Handler<T>::handle, this->next.get(), value)); + } +}; + +}} // namespace qpid::framing + + + + + +#endif /*!QPID_FRAMING_SERIALIZEHANDLER_H*/ diff --git a/qpid/cpp/src/qpid/framing/SessionState.cpp b/qpid/cpp/src/qpid/framing/SessionState.cpp new file mode 100644 index 0000000000..f9019b036c --- /dev/null +++ b/qpid/cpp/src/qpid/framing/SessionState.cpp @@ -0,0 +1,137 @@ +/* + * + * 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 WARRANTIE4bS OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +#include "SessionState.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/constants.h" +#include "qpid/framing/AMQMethodBody.h" +#include "qpid/log/Statement.h" + +#include <algorithm> + +#include <boost/bind.hpp> +#include <boost/none.hpp> + +namespace qpid { +namespace framing { + +SessionState::SessionState(uint32_t ack, bool enableReplay, const Uuid& uuid) : + state(ATTACHED), + id(uuid), + lastReceived(-1), + lastSent(-1), + ackInterval(ack), + sendAckAt(lastReceived+ackInterval), + solicitAckAt(lastSent+ackInterval), + ackSolicited(false), + resumable(enableReplay) +{} + +SessionState::SessionState(const Uuid& uuid) : + state(ATTACHED), + id(uuid), + lastReceived(-1), + lastSent(-1), + ackInterval(0), + sendAckAt(0), + solicitAckAt(0), + ackSolicited(false), + resumable(false) +{ +} +namespace { +bool isSessionCommand(const AMQFrame& f) { + return f.getMethod() && f.getMethod()->amqpClassId() == SESSION_CLASS_ID; +} +} + +boost::optional<SequenceNumber> SessionState::received(const AMQFrame& f) { + if (isSessionCommand(f)) + return boost::none; + if (state==RESUMING) + throw CommandInvalidException( + QPID_MSG("Invalid frame: Resuming session, expected session-ack")); + assert(state = ATTACHED); + ++lastReceived; + if (ackInterval && lastReceived == sendAckAt) + return sendingAck(); + else + return boost::none; +} + +bool SessionState::sent(const AMQFrame& f) { + if (isSessionCommand(f)) + return false; + if (resumable) { + sys::Mutex::ScopedLock l(unackedLock); + unackedOut.push_back(f); + } + ++lastSent; + return ackInterval && + (state!=RESUMING) && + (lastSent == solicitAckAt) && + sendingSolicit(); +} + +SessionState::Replay SessionState::replay() { + sys::Mutex::ScopedLock l(unackedLock); + Replay r(unackedOut.size()); + std::copy(unackedOut.begin(), unackedOut.end(), r.begin()); + return r; +} + +void SessionState::receivedAck(SequenceNumber acked) { + if (state==RESUMING) state=ATTACHED; + assert(state==ATTACHED); + if (lastSent < acked) + throw InvalidArgumentException("Invalid sequence number in ack"); + size_t keep = lastSent - acked; + if (keep < unackedOut.size()) { + sys::Mutex::ScopedLock l(unackedLock); + unackedOut.erase(unackedOut.begin(), unackedOut.end()-keep); + } + solicitAckAt = std::max(solicitAckAt, SequenceNumber(acked+ackInterval)); +} + +SequenceNumber SessionState::sendingAck() { + sendAckAt = lastReceived+ackInterval; + return lastReceived; +} + +bool SessionState::sendingSolicit() { + assert(state == ATTACHED); + if (ackSolicited) + return false; + solicitAckAt = lastSent + ackInterval; + return ackInterval != 0; +} + +SequenceNumber SessionState::resuming() { + if (!resumable) + throw InternalErrorException("Session is not resumable"); + state = RESUMING; + return sendingAck(); +} + +void SessionState::suspend() { + state = SUSPENDED; +} + +}} // namespace qpid::framing diff --git a/qpid/cpp/src/qpid/framing/SessionState.h b/qpid/cpp/src/qpid/framing/SessionState.h new file mode 100644 index 0000000000..066bece003 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/SessionState.h @@ -0,0 +1,138 @@ +#ifndef QPID_FRAMING_SESSIONSTATE_H +#define QPID_FRAMING_SESSIONSTATE_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. + * + */ + +#include "qpid/framing/SequenceNumber.h" +#include "qpid/framing/Uuid.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/sys/Mutex.h" + +#include <boost/optional.hpp> + +#include <deque> + +namespace qpid { +namespace framing { + +/** + * Session state common to client and broker. + * Stores replay frames, implements session ack/resume protcools. + * + * A SessionState is always associated with an _open_ session (attached or + * suspended) it is destroyed when the session is closed. + * + */ +class SessionState +{ + public: + typedef std::vector<AMQFrame> Replay; + + /** States of a session. */ + enum State { + SUSPENDED, ///< Suspended, detached from any channel. + RESUMING, ///< Resuming: waiting for initial ack from peer. + ATTACHED ///< Attached to channel and operating normally. + }; + + /** + *Create a newly opened active session. + *@param ackInterval send/solicit an ack whenever N unacked frames + * have been received/sent. + * + * N=0 disables voluntary send/solict ack. + */ + SessionState(uint32_t ackInterval, bool enableReplay = true, const framing::Uuid& id=framing::Uuid(true)); + + /** + * Create a non-resumable session. Does not store session frames, + * never volunteers ack or solicit-ack. + */ + SessionState(const framing::Uuid& id=framing::Uuid(true)); + + const framing::Uuid& getId() const { return id; } + State getState() const { return state; } + + /** Received incoming L3 frame. + * @return SequenceNumber if an ack should be sent, empty otherwise. + * SessionState assumes that acks are sent whenever it returns + * a seq. number. + */ + boost::optional<SequenceNumber> received(const AMQFrame&); + + /** Sent outgoing L3 frame. + *@return true if solicit-ack should be sent. Note the SessionState + *assumes that a solicit-ack is sent every time it returns true. + */ + bool sent(const AMQFrame&); + + /** Received normal incoming ack. */ + void receivedAck(SequenceNumber); + + /** Frames to replay + *@pre getState()==ATTACHED + */ + Replay replay(); + + /** Suspend the session. */ + void suspend(); + + /** Start resume protocol for the session. + *@returns sequence number to ack immediately. */ + SequenceNumber resuming(); + + /** About to send an unscheduled ack, e.g. to respond to a solicit-ack. + * + * Note: when received() returns a sequence number this function + * should not be called. SessionState assumes that the ack is sent + * every time received() returns a sequence number. + */ + SequenceNumber sendingAck(); + + SequenceNumber getLastSent() const { return lastSent; } + SequenceNumber getLastReceived() const { return lastReceived; } + + private: + typedef std::deque<AMQFrame> Unacked; + + bool sendingSolicit(); + + State state; + framing::Uuid id; + + Unacked unackedOut; + SequenceNumber lastReceived; + SequenceNumber lastSent; + uint32_t ackInterval; + SequenceNumber sendAckAt; + SequenceNumber solicitAckAt; + bool ackSolicited; + bool suspending; + bool resumable; + sys::Mutex unackedLock; +}; + + +}} // namespace qpid::common + + +#endif /*!QPID_FRAMING_SESSIONSTATE_H*/ diff --git a/qpid/cpp/src/qpid/framing/StructHelper.h b/qpid/cpp/src/qpid/framing/StructHelper.h new file mode 100644 index 0000000000..ad6ba89906 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/StructHelper.h @@ -0,0 +1,56 @@ +/* + * + * 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. + * + */ +#ifndef _StructHelper_ +#define _StructHelper_ + +#include "qpid/Exception.h" +#include "Buffer.h" + +#include <stdlib.h> // For alloca + +namespace qpid { +namespace framing { + +class StructHelper +{ +public: + + template <class T> void encode(const T t, std::string& data) { + uint32_t size = t.size() + 2/*type*/; + data.resize(size); + Buffer wbuffer(const_cast<char*>(data.data()), size); + wbuffer.putShort(T::TYPE); + t.encodeStructBody(wbuffer); + } + + template <class T> void decode(T& t, const std::string& data) { + Buffer rbuffer(const_cast<char*>(data.data()), data.length()); + uint16_t type = rbuffer.getShort(); + if (type == T::TYPE) { + t.decodeStructBody(rbuffer); + } else { + throw Exception("Type code does not match"); + } + } +}; + +}} +#endif diff --git a/qpid/cpp/src/qpid/framing/TemplateVisitor.h b/qpid/cpp/src/qpid/framing/TemplateVisitor.h new file mode 100644 index 0000000000..8c719e5110 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/TemplateVisitor.h @@ -0,0 +1,89 @@ +#ifndef QPID_FRAMING_TEMPLATEVISITOR_H +#define QPID_FRAMING_TEMPLATEVISITOR_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. + * + */ +#include <boost/mpl/fold.hpp> +#include <boost/utility/value_init.hpp> + +namespace qpid { +namespace framing { + +/** + * Metafunction to generate a visitor class derived from Base with a + * visit for each type in TypeList calling functor F. TypeList may be + * any boost::mpl type collection e.g. mpl::list. + * + * Generated class is: TemplateVisitor<Base, F, TypeList>::type + * + * @see make_visitor + */ +template <class VisitTemplate, class TypeList, class F> +class TemplateVisitor +{ + struct Base : public VisitorBase { + F action; + Base(F f) : action(f) {} + using VisitorBase::visit; + }; + + template <class B, class T> struct Visit : public B { + Visit(F action) : B(action) {} + using B::visit; + void visit(const T& body) { action(body); } + }; + + typedef typename boost::mpl::fold< + TypeList, Base, Visit<boost::mpl::placeholders::_1, + boost::mpl::placeholders::_2> + >::type type; +}; + +/** + * Construct a TemplateVisitor to perform the given action, + * for example: + * @code + */ +template <class VisitorBase, class TypeList, class F> +TemplateVisitor<VisitorBase,TypeList,F>::type make_visitor(F action) { + return TemplateVisitor<VisitorBase,TypeList,F>::type(action); +}; + +/** + * For method body classes in TypeList, invoke the corresponding function + * on Target and return true. For other body types return false. + */ +template <class TypeList, class Target> +bool invoke(const AMQBody& body, Target& target) { + typename InvokeVisitor<TypeList, Target>::type v(target); + body.accept(v); + return v.target; +} + +}} // namespace qpid::framing + + +#endif /*!QPID_FRAMING_INVOKEVISITOR_H*/ + +}} // namespace qpid::framing + + + +#endif /*!QPID_FRAMING_TEMPLATEVISITOR_H*/ diff --git a/qpid/cpp/src/qpid/framing/TransferContent.cpp b/qpid/cpp/src/qpid/framing/TransferContent.cpp new file mode 100644 index 0000000000..99f5d365e8 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/TransferContent.cpp @@ -0,0 +1,102 @@ +/* + * + * 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. + * + */ + +#include "TransferContent.h" + +namespace qpid { +namespace framing { + +TransferContent::TransferContent(const std::string& data, + const std::string& routingKey, + const std::string& exchange) +{ + setData(data); + if (routingKey.size()) getDeliveryProperties().setRoutingKey(routingKey); + if (exchange.size()) getDeliveryProperties().setExchange(exchange); +} + +AMQHeaderBody TransferContent::getHeader() const +{ + return header; +} + +const std::string& TransferContent::getData() const +{ + return data; +} + +void TransferContent::setData(const std::string& _data) +{ + data = _data; + header.get<MessageProperties>(true)->setContentLength(data.size()); +} + +void TransferContent::appendData(const std::string& _data) +{ + data += _data; + header.get<MessageProperties>(true)->setContentLength(data.size()); +} + +MessageProperties& TransferContent::getMessageProperties() +{ + return *header.get<MessageProperties>(true); +} + +DeliveryProperties& TransferContent::getDeliveryProperties() +{ + return *header.get<DeliveryProperties>(true); +} + +void TransferContent::populate(const FrameSet& frameset) +{ + const AMQHeaderBody* h = frameset.getHeaders(); + if (h) { + header = *h; + } + frameset.getContent(data); +} + +const MessageProperties& TransferContent::getMessageProperties() const +{ + const MessageProperties* props = header.get<MessageProperties>(); + if (!props) throw Exception("No message properties."); + return *props; +} + +const DeliveryProperties& TransferContent::getDeliveryProperties() const +{ + const DeliveryProperties* props = header.get<DeliveryProperties>(); + if (!props) throw Exception("No message properties."); + return *props; +} + +bool TransferContent::hasMessageProperties() const +{ + return header.get<MessageProperties>(); +} + +bool TransferContent::hasDeliveryProperties() const +{ + return header.get<DeliveryProperties>(); +} + + +}} diff --git a/qpid/cpp/src/qpid/framing/TransferContent.h b/qpid/cpp/src/qpid/framing/TransferContent.h new file mode 100644 index 0000000000..88f45b7e0a --- /dev/null +++ b/qpid/cpp/src/qpid/framing/TransferContent.h @@ -0,0 +1,58 @@ +/* + * + * 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. + * + */ +#ifndef _TransferContent_ +#define _TransferContent_ + +#include "FrameSet.h" +#include "MethodContent.h" +#include "qpid/Exception.h" +#include "qpid/framing/MessageProperties.h" +#include "qpid/framing/DeliveryProperties.h" + +namespace qpid { +namespace framing { + +class TransferContent : public MethodContent +{ + AMQHeaderBody header; + std::string data; +public: + TransferContent(const std::string& data = std::string(), + const std::string& routingKey = std::string(), + const std::string& exchange = std::string()); + + AMQHeaderBody getHeader() const; + void setData(const std::string&); + void appendData(const std::string&); + MessageProperties& getMessageProperties(); + DeliveryProperties& getDeliveryProperties(); + + const std::string& getData() const; + const MessageProperties& getMessageProperties() const; + const DeliveryProperties& getDeliveryProperties() const; + bool hasMessageProperties() const; + bool hasDeliveryProperties() const; + + void populate(const FrameSet& frameset); +}; + +}} +#endif diff --git a/qpid/cpp/src/qpid/framing/TypeFilter.h b/qpid/cpp/src/qpid/framing/TypeFilter.h new file mode 100644 index 0000000000..d1c42de583 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/TypeFilter.h @@ -0,0 +1,51 @@ +#ifndef QPID_FRAMING_TYPEFILTER_H +#define QPID_FRAMING_TYPEFILTER_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. + * + */ +#include <string> +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/FrameHandler.h" + +namespace qpid { +namespace framing { + +/** + * Predicate that selects frames by type + */ +template <uint8_t Type> +struct TypeFilter { + bool operator()(const AMQFrame& f) const { + return f.getBody()->type() == Type; + } +}; + +template <uint8_t T1, uint8_t T2> +struct TypeFilter2 { + bool operator()(const AMQFrame& f) const { + return f.getBody()->type() == T1 || f.getBody()->type() == T2; + } +}; + +}} // namespace qpid::framing + +#endif /*!QPID_FRAMING_TYPEFILTER_H*/ diff --git a/qpid/cpp/src/qpid/framing/Uuid.cpp b/qpid/cpp/src/qpid/framing/Uuid.cpp new file mode 100644 index 0000000000..2918c48ce3 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/Uuid.cpp @@ -0,0 +1,61 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "Uuid.h" +#include "qpid/Exception.h" +#include "qpid/framing/Buffer.h" +#include "qpid/framing/reply_exceptions.h" + +namespace qpid { +namespace framing { + +using namespace std; + +static const size_t UNPARSED_SIZE=36; + +void Uuid::encode(Buffer& buf) const { + buf.putRawData(data(), size()); +} + +void Uuid::decode(Buffer& buf) { + if (buf.available() < size()) + throw SyntaxErrorException(QPID_MSG("Not enough data for UUID.")); + buf.getRawData(c_array(), size()); +} + +ostream& operator<<(ostream& out, const Uuid& uuid) { + char unparsed[UNPARSED_SIZE + 1]; + uuid_unparse(uuid.data(), unparsed); + return out << unparsed; +} + +istream& operator>>(istream& in, Uuid& uuid) { + char unparsed[UNPARSED_SIZE + 1] = {0}; + in.get(unparsed, sizeof(unparsed)); + if (uuid_parse(unparsed, uuid.c_array()) != 0) + in.setstate(ios::failbit); + return in; +} + +std::string Uuid::str() const { + std::ostringstream os; + os << *this; + return os.str(); +} + +}} // namespace qpid::framing diff --git a/qpid/cpp/src/qpid/framing/Uuid.h b/qpid/cpp/src/qpid/framing/Uuid.h new file mode 100644 index 0000000000..bce18f55b3 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/Uuid.h @@ -0,0 +1,84 @@ +#ifndef QPID_FRAMING_UUID_H +#define QPID_FRAMING_UUID_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include <boost/array.hpp> + +#include <ostream> +#include <istream> + +#include <uuid/uuid.h> + +namespace qpid { +namespace framing { + +class Buffer; + +/** + * A UUID is represented as a boost::array of 16 bytes. + * + * Full value semantics, operators ==, < etc. are provided by + * boost::array so Uuid can be the key type in a map etc. + */ +struct Uuid : public boost::array<uint8_t, 16> { + /** If unique is true, generate a unique ID else a null ID. */ + Uuid(bool unique=false) { if (unique) generate(); else clear(); } + + /** Copy from 16 bytes of data */ + Uuid(const uint8_t* data) { assign(data); } + + /** Copy from 16 bytes of data */ + void assign(const uint8_t* data) { uuid_copy(c_array(), data); } + + /** Set to a new unique identifier */ + void generate() { uuid_generate(c_array()); } + + /** Set to all zeros */ + void clear() { uuid_clear(c_array()); } + + /** Test for null (all zeros) */ + bool isNull() const { return uuid_is_null(data()); } + + // Default op= and copy ctor are fine. + // boost::array gives us ==, < etc. + + void encode(framing::Buffer& buf) const; + + void decode(framing::Buffer& buf); + + /** String value in format 1b4e28ba-2fa1-11d2-883f-b9a761bde3fb */ + std::string str() const; + + template <class S> void serialize(S& s) { + s.raw(begin(), size()); + } +}; + +/** Print in format 1b4e28ba-2fa1-11d2-883f-b9a761bde3fb */ +std::ostream& operator<<(std::ostream&, const Uuid&); + +/** Read from format 1b4e28ba-2fa1-11d2-883f-b9a761bde3fb */ +std::istream& operator>>(std::istream&, Uuid&); + +}} // namespace qpid::framing + + + +#endif /*!QPID_FRAMING_UUID_H*/ diff --git a/qpid/cpp/src/qpid/framing/Visitor.h b/qpid/cpp/src/qpid/framing/Visitor.h new file mode 100644 index 0000000000..9753b21954 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/Visitor.h @@ -0,0 +1,91 @@ +#ifndef QPID_FRAMING_VISITOR_H +#define QPID_FRAMING_VISITOR_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. + * + */ + +#include <boost/mpl/vector.hpp> +#include <boost/type_traits/remove_reference.hpp> +#include <boost/preprocessor/seq/for_each.hpp> + +namespace qpid { +namespace framing { + +/** @file Generic visitor pattern. */ + +/** visit() interface for type T (optional return type R, default void.) + * To create a visitor for a set of types T1, T2 ... do this: + * struct MyVisitor : public Visit<T1>, public Visit<T2> ... {}; + *@param T Type to visit, must be forward declared, need not be defined. + */ +template <class T, class R=void> struct Visit { + typedef R ReturnType; + typedef T VisitType; + + virtual ~Visit() {} + virtual R visit(T&) = 0; +}; + + +#define QPID_VISITOR_DECL(_1,_2,T) class T; + +#define QPID_VISITOR_BASE(_1,_2,T) , public ::qpid::framing::Visit<T> + +/** Convenience macro to generate a visitor interface. + * QPID_VISITOR(MyVisitor,(A)(B)(C)); is equivalent to: + * @code + * class A; class B; class C; + * class MyVisitor : public Visit<A> , public Visit<B> , public Visit<C> {}; + * @endcode + * @param visitor name of the generated visitor class. + * @param bases a sequence of visitable types in the form (T1)(T2)... + * The odd parenthesized notation is due to quirks of the preprocesser. + */ +#define QPID_VISITOR(visitor,types) \ + BOOST_PP_SEQ_FOR_EACH(QPID_VISITOR_DECL, _, types) \ + class visitor : public ::qpid::framing::Visit<BOOST_PP_SEQ_HEAD(types)> \ + BOOST_PP_SEQ_FOR_EACH(QPID_VISITOR_BASE, _, BOOST_PP_SEQ_TAIL(types)) \ + {} + +/** Root class for hierarchy of objects visitable by Visitor V. + * Defines virtual accept() + */ +template <class V, class R=void> +struct VisitableRoot { + typedef V VisitorType; + typedef R ReturnType; + virtual ~VisitableRoot() {} + virtual R accept(V& v) = 0; +}; + +/** Base class for concrete visitable classes, implements accept(). + * @param T type of visitable class (CRTP) + * @param Base base class to inherit from. + */ +template <class T, class Base> +struct Visitable : public Base { + void accept(typename Base::VisitorType& v) { + static_cast<Visit<T>& >(v).visit(static_cast<T&>(*this)); + } +}; + +}} // namespace qpid::framing + +#endif /*!QPID_FRAMING_VISITOR_H*/ diff --git a/qpid/cpp/src/qpid/framing/amqp_framing.h b/qpid/cpp/src/qpid/framing/amqp_framing.h new file mode 100644 index 0000000000..4e4747c3f4 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/amqp_framing.h @@ -0,0 +1,32 @@ +/* + * + * 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. + * + */ +#include "amqp_types.h" +#include "AMQFrame.h" +#include "AMQBody.h" +#include "BodyHandler.h" +#include "AMQMethodBody.h" +#include "AMQHeaderBody.h" +#include "AMQContentBody.h" +#include "AMQHeartbeatBody.h" +#include "InputHandler.h" +#include "OutputHandler.h" +#include "ProtocolInitiation.h" +#include "ProtocolVersion.h" diff --git a/qpid/cpp/src/qpid/framing/amqp_types.h b/qpid/cpp/src/qpid/framing/amqp_types.h new file mode 100644 index 0000000000..943970cc56 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/amqp_types.h @@ -0,0 +1,72 @@ +#ifndef AMQP_TYPES_H +#define AMQP_TYPES_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. + * + */ + +/** \file + * Type definitions and forward declarations of all types used to + * in AMQP messages. + */ + +#include <string> +#ifdef _WINDOWS +#include "windows.h" +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned __int64 uint64_t; +#endif +#ifndef _WINDOWS +#include <stdint.h> +#endif + +namespace qpid { +namespace framing { + +using std::string; +typedef uint8_t FrameType; +typedef uint16_t ChannelId; +typedef uint32_t BatchOffset; +typedef uint8_t ClassId; +typedef uint8_t MethodId; +typedef uint16_t ReplyCode; + +// Types represented by classes. +class Content; +class FieldTable; +class SequenceNumberSet; +class Uuid; + +// Useful constants + +/** Maximum channel ID used by broker. Reserve high bit for internal use.*/ +const ChannelId CHANNEL_MAX=(ChannelId(~1))>>1; +const ChannelId CHANNEL_HIGH_BIT= ChannelId(~CHANNEL_MAX); + +// Forward declare class types +class FramingContent; +class FieldTable; +class SequenceNumberSet; +class SequenceSet; +class Uuid; + +}} // namespace qpid::framing +#endif diff --git a/qpid/cpp/src/qpid/framing/amqp_types_full.h b/qpid/cpp/src/qpid/framing/amqp_types_full.h new file mode 100644 index 0000000000..da7bdc876d --- /dev/null +++ b/qpid/cpp/src/qpid/framing/amqp_types_full.h @@ -0,0 +1,40 @@ +#ifndef _framing_amqp_types_decl_h +#define _framing_amqp_types_decl_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +/** \file + * Type definitions and full declarations of all types used to + * in AMQP messages. + * + * Its better to include amqp_types.h in another header instead of this file + * unless the header actually needs the full declarations. Including + * full declarations when forward declarations would do increases compile + * times. + */ + +#include "amqp_types.h" +#include "Array.h" +#include "FramingContent.h" +#include "FieldTable.h" +#include "SequenceNumberSet.h" +#include "SequenceSet.h" +#include "Uuid.h" + +#endif /*!_framing_amqp_types_decl_h*/ diff --git a/qpid/cpp/src/qpid/framing/frame_functors.h b/qpid/cpp/src/qpid/framing/frame_functors.h new file mode 100644 index 0000000000..d915a270c2 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/frame_functors.h @@ -0,0 +1,116 @@ +/* + * + * 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. + * + */ +#include <string> +#include <ostream> +#include <iostream> +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/Buffer.h" + +#ifndef _frame_functors_ +#define _frame_functors_ + +namespace qpid { +namespace framing { + +class SumFrameSize +{ + uint64_t size; +public: + SumFrameSize() : size(0) {} + void operator()(const AMQFrame& f) { size += f.size(); } + uint64_t getSize() { return size; } +}; + +class SumBodySize +{ + uint64_t size; +public: + SumBodySize() : size(0) {} + void operator()(const AMQFrame& f) { size += f.getBody()->size(); } + uint64_t getSize() { return size; } +}; + +class Count +{ + uint count; +public: + Count() : count(0) {} + void operator()(const AMQFrame&) { count++; } + uint getCount() { return count; } +}; + +class EncodeFrame +{ + Buffer& buffer; +public: + EncodeFrame(Buffer& b) : buffer(b) {} + void operator()(const AMQFrame& f) { f.encode(buffer); } +}; + +class EncodeBody +{ + Buffer& buffer; +public: + EncodeBody(Buffer& b) : buffer(b) {} + void operator()(const AMQFrame& f) { f.getBody()->encode(buffer); } +}; + +/** + * Sends a copy of the frame its applied to to the specified handler + */ +class Relay +{ + FrameHandler& handler; + +public: + Relay(FrameHandler& h) : handler(h) {} + + void operator()(const AMQFrame& f) + { + AMQFrame copy(f); + handler.handle(copy); + } +}; + +class Print +{ + std::ostream& out; +public: + Print(std::ostream& o) : out(o) {} + + void operator()(const AMQFrame& f) + { + out << f << std::endl; + } +}; + +class MarkLastSegment +{ +public: + void operator()(AMQFrame& f) const { f.setEof(true); } +}; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/framing/variant.h b/qpid/cpp/src/qpid/framing/variant.h new file mode 100644 index 0000000000..ceaed2c529 --- /dev/null +++ b/qpid/cpp/src/qpid/framing/variant.h @@ -0,0 +1,91 @@ +#ifndef QPID_FRAMING_VARIANT_H +#define QPID_FRAMING_VARIANT_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. + * + */ + +/**@file Tools for using boost::variant */ + + +#include <boost/variant.hpp> + +namespace qpid { +namespace framing { +class Buffer; + +/** boost::static_visitor that throws exception if variant contains blank. + * Sublclasses need to have a using() declaration, can be generated + * with QPID_USING_NOBLANK(R) + */ +template <class R=void> +struct NoBlankVisitor : public boost::static_visitor<R> { + R foundBlank() const { + assert(0); + throw Exception(QPID_MSG("Invalid variant value.")); + } + R operator()(const boost::blank&) const { return foundBlank(); } + R operator()(boost::blank&) const { return foundBlank(); } +}; + + +}} // qpid::framing + + +/** Generate using statement needed in visitors inheriting NoBlankVisitor + * @param R return type. + */ +#define QPID_USING_NOBLANK(R) using ::qpid::framing::NoBlankVisitor<R>::operator() + +namespace qpid { +namespace framing { + +/** Convert the variant value to type R. */ +template <class R> struct ConvertVisitor : public NoBlankVisitor<R> { + QPID_USING_NOBLANK(R); + template <class T> R operator()(T& t) const { return t; } +}; + +/** Convert address of variant value to type R. */ +template <class R> struct AddressVisitor : public NoBlankVisitor<R> { + QPID_USING_NOBLANK(R); + template <class T> R operator()(T& t) const { return &t; } +}; + +/** Apply a visitor to the nested variant in a variant of variants */ +template<class V> +struct ApplyVisitor : public NoBlankVisitor<typename V::result_type> { + QPID_USING_NOBLANK(typename V::result_type); + const V& visitor; + ApplyVisitor(const V& v) : visitor(v) {} + template <class T> typename V::result_type operator()(T& t) const { + return boost::apply_visitor(visitor, t); + } +}; + +/** Convenience function to construct and apply an ApplyVisitor */ +template <class Visitor, class Visitable> +typename Visitor::result_type applyApplyVisitor(const Visitor& visitor, Visitable& visitable) { + return boost::apply_visitor(ApplyVisitor<Visitor>(visitor), visitable); +} + +}} // namespace qpid::framing + + +#endif /*!QPID_FRAMING_VARIANT_H*/ diff --git a/qpid/cpp/src/qpid/log/Helpers.h b/qpid/cpp/src/qpid/log/Helpers.h new file mode 100644 index 0000000000..82ef8244be --- /dev/null +++ b/qpid/cpp/src/qpid/log/Helpers.h @@ -0,0 +1,79 @@ +#ifndef QPID_LOG_HELPERS_H +#define QPID_LOG_HELPERS_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. + * + */ + +#include <boost/range.hpp> + +#include <ostream> + +namespace qpid { +namespace log { + +/** @file Helper classes for logging complex types */ + +/// @internal +template <class Range> +struct ListFormatter { + typedef typename boost::range_const_iterator<Range>::type Iterator; + boost::iterator_range<Iterator> range; + const char* separator; + + ListFormatter(const Range& r, const char* s=", ") : range(r), separator(s) {} +}; + +/// @internal +template <class Range> +std::ostream& operator<<(std::ostream& out, const ListFormatter<Range>& sl) { + typename ListFormatter<Range>::Iterator i = sl.range.begin(); + if (i != sl.range.end()) out << *(i++); + while (i != sl.range.end()) out << sl.separator << *(i++); + return out; +} + +/** Return a formatting object with operator << + * to stream range as a separated list. + *@param range: a range - all standard containers are ranges, + * as is a pair of iterators. + *@param separator: printed between elements, default ", " + */ +template <class Range> +ListFormatter<Range> formatList(const Range& range, const char* separator=", ") { + return ListFormatter<Range>(range, separator); +} + +/** Return a formatting object with operator << + * to stream the range defined by iterators [begin, end) + * as a separated list. + *@param begin, end: Beginning and end of range. + *@param separator: printed between elements, default ", " + */ +template <class U, class V> +ListFormatter<std::pair<U,V> > formatList(U begin, V end, const char* separator=", ") { + return formatList(std::make_pair(begin,end), separator); +} + + +}} // namespace qpid::log + + + +#endif /*!QPID_LOG_HELPERS_H*/ diff --git a/qpid/cpp/src/qpid/log/Logger.cpp b/qpid/cpp/src/qpid/log/Logger.cpp new file mode 100644 index 0000000000..c0fc8ac959 --- /dev/null +++ b/qpid/cpp/src/qpid/log/Logger.cpp @@ -0,0 +1,229 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "Logger.h" +#include "Options.h" +#include "qpid/memory.h" +#include "qpid/sys/Thread.h" +#include <boost/pool/detail/singleton.hpp> +#include <boost/bind.hpp> +#include <boost/function.hpp> +#include <boost/scoped_ptr.hpp> +#include <algorithm> +#include <sstream> +#include <fstream> +#include <iomanip> +#include <stdexcept> +#include <syslog.h> +#include <time.h> + + +namespace qpid { +namespace log { + +using namespace std; + +typedef sys::Mutex::ScopedLock ScopedLock; + +inline void Logger::enable_unlocked(Statement* s) { + s->enabled=selector.isEnabled(s->level, s->function); +} + +struct OstreamOutput : public Logger::Output { + OstreamOutput(std::ostream& o) : out(&o) {} + + OstreamOutput(const string& file) + : out(new ofstream(file.c_str())), mine(out) + { + if (!out->good()) + throw std::runtime_error("Can't open log file: "+file); + } + + void log(const Statement&, const std::string& m) { + *out << m << flush; + } + + ostream* out; + boost::scoped_ptr<ostream> mine; +}; + +struct SyslogOutput : public Logger::Output { + SyslogOutput(const std::string& name, int facility_=LOG_USER) + : progName(name), facility(facility_) + { + ::openlog(name.c_str(), LOG_PID, facility); + } + + ~SyslogOutput() { + ::closelog(); + } + + void log(const Statement& s, const std::string& m) + { + syslog(LevelTraits::priority(s.level), "%s", m.c_str()); + } + + std::string progName; + int facility; +}; + +Logger& Logger::instance() { + return boost::details::pool::singleton_default<Logger>::instance(); +} + +Logger::Logger() : flags(0) { + // Initialize myself from env variables so all programs + // (e.g. tests) can use logging even if they don't parse + // command line args. + Options opts; + opts.parse(0, 0); + configure(opts,""); +} + +Logger::~Logger() {} + +void Logger::select(const Selector& s) { + ScopedLock l(lock); + selector=s; + std::for_each(statements.begin(), statements.end(), + boost::bind(&Logger::enable_unlocked, this, _1)); +} + +Logger::Output::Output() {} +Logger::Output::~Output() {} + +void Logger::log(const Statement& s, const std::string& msg) { + // Format the message outside the lock. + std::ostringstream os; + if (flags&TIME) + { + const char * month_abbrevs[] = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" }; + time_t rawtime; + struct tm * timeinfo; + + time ( & rawtime ); + timeinfo = localtime ( &rawtime ); + char time_string[100]; + sprintf ( time_string, + "%d-%s-%02d %02d:%02d:%02d", + 1900 + timeinfo->tm_year, + month_abbrevs[timeinfo->tm_mon], + timeinfo->tm_mday, + timeinfo->tm_hour, + timeinfo->tm_min, + timeinfo->tm_sec + ); + os << time_string << " "; + } + if (flags&LEVEL) + os << LevelTraits::name(s.level) << " "; + if (flags&THREAD) + os << "[" << qpid::sys::Thread::logId() << "] "; + if (flags&FILE) + os << s.file << ":"; + if (flags&LINE) + os << dec << s.line << ":"; + if (flags&FUNCTION) + os << s.function << ":"; + if (flags & (FILE|LINE|FUNCTION)) + os << " "; + os << msg << endl; + std::string formatted=os.str(); + + { + ScopedLock l(lock); + std::for_each(outputs.begin(), outputs.end(), + boost::bind(&Output::log, _1, s, formatted)); + } +} + +void Logger::output(std::auto_ptr<Output> out) { + ScopedLock l(lock); + outputs.push_back(out.release()); +} + +void Logger::output(std::ostream& out) { + output(make_auto_ptr<Output>(new OstreamOutput(out))); +} + +void Logger::syslog(const std::string& progName) { + output(make_auto_ptr<Output>(new SyslogOutput(progName))); +} + +void Logger::output(const std::string& name) { + if (name=="stderr") + output(clog); + else if (name=="stdout") + output(cout); + else if (name=="syslog") + syslog(syslogName); + else + output(make_auto_ptr<Output>(new OstreamOutput(name))); +} + +void Logger::clear() { + select(Selector()); // locked + format(0); // locked + ScopedLock l(lock); + outputs.clear(); +} + +void Logger::format(int formatFlags) { + ScopedLock l(lock); + flags=formatFlags; +} + +static int bitIf(bool test, int bit) { + return test ? bit : 0; +} + +int Logger::format(const Options& opts) { + int flags= + bitIf(opts.level, LEVEL) | + bitIf(opts.time, TIME) | + bitIf(opts.source, (FILE|LINE)) | + bitIf(opts.function, FUNCTION) | + bitIf(opts.thread, THREAD); + format(flags); + return flags; +} + +void Logger::add(Statement& s) { + ScopedLock l(lock); + enable_unlocked(&s); + statements.insert(&s); +} + +void Logger::configure(const Options& opts, const std::string& prog) +{ + clear(); + Options o(opts); + if (o.trace) + o.selectors.push_back("trace+"); + { + ScopedLock l(lock); + syslogName=prog; + } + format(o); + select(Selector(o)); + void (Logger::* outputFn)(const std::string&) = &Logger::output; + for_each(o.outputs.begin(), o.outputs.end(), + boost::bind(outputFn, this, _1)); +} + +}} // namespace qpid::log diff --git a/qpid/cpp/src/qpid/log/Logger.h b/qpid/cpp/src/qpid/log/Logger.h new file mode 100644 index 0000000000..7851c65406 --- /dev/null +++ b/qpid/cpp/src/qpid/log/Logger.h @@ -0,0 +1,113 @@ +#ifndef LOGGER_H +#define LOGGER_H + +/* + * 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. + * + */ + +#include "Selector.h" +#include "qpid/sys/Mutex.h" +#include <boost/ptr_container/ptr_vector.hpp> +#include <boost/noncopyable.hpp> +#include <set> + +namespace qpid { +namespace log { + +class Options; + +/** + * Central logging agent. + * + * Thread safe, singleton. + */ +class Logger : private boost::noncopyable { + public: + /** Flags indicating what to include in the log output */ + enum FormatFlag { FILE=1, LINE=2, FUNCTION=4, LEVEL=8, TIME=16, THREAD=32}; + + /** Interface for log output destination. + * + * Implementations must be thread safe. + */ + class Output { + public: + Output(); + virtual ~Output(); + /** Receives the statemnt of origin and formatted message to log. */ + virtual void log(const Statement&, const std::string&) =0; + }; + + static Logger& instance(); + + Logger(); + ~Logger(); + + /** Select the messages to be logged. */ + void select(const Selector& s); + + /** Set the formatting flags, bitwise OR of FormatFlag values. */ + void format(int formatFlags); + + /** Set format flags from options object. + *@returns computed flags. + */ + int format(const Options&); + + /** Configure logger from Options */ + void configure(const Options& o, const std::string& progname); + + /** Add a statement. */ + void add(Statement& s); + + /** Log a message. */ + void log(const Statement&, const std::string&); + + /** Add an ostream to outputs. + * + * The ostream must not be destroyed while the Logger might + * still be using it. This is the case for std streams cout, + * cerr, clog. + */ + void output(std::ostream&); + + /** Add syslog to outputs. */ + void syslog(const std::string& programName); + + /** Add an output. + *@param name a file name or one of the special tokens: + *stdout, stderr, syslog. + */ + void output(const std::string& name); + + /** Add an output destination for messages */ + void output(std::auto_ptr<Output> out); + + /** Reset the logger to it's original state. */ + void clear(); + + private: + typedef boost::ptr_vector<Output> Outputs; + typedef std::set<Statement*> Statements; + + sys::Mutex lock; + inline void enable_unlocked(Statement* s); + + std::string syslogName; + Statements statements; + Outputs outputs; + Selector selector; + int flags; +}; + +}} // namespace qpid::log + + +#endif /*!LOGGER_H*/ diff --git a/qpid/cpp/src/qpid/log/Options.cpp b/qpid/cpp/src/qpid/log/Options.cpp new file mode 100644 index 0000000000..dd296f3a93 --- /dev/null +++ b/qpid/cpp/src/qpid/log/Options.cpp @@ -0,0 +1,66 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "Options.h" +#include "Statement.h" +#include "qpid/Options.h" + +namespace qpid { +namespace log { + +using namespace std; + +Options::Options(const std::string& name) : qpid::Options(name), + time(true), level(true), thread(false), source(false), function(false), trace(false) +{ + outputs.push_back("stderr"); + selectors.push_back("error+"); + + ostringstream levels; + levels << LevelTraits::name(Level(0)); + for (int i = 1; i < LevelTraits::COUNT; ++i) + levels << " " << LevelTraits::name(Level(i)); + addOptions() + ("log-output", optValue(outputs, "FILE"), + "Send log output to FILE. " + "FILE can be a file name or one of the special values:\n" + "stderr, stdout, syslog") + ("trace,t", optValue(trace), "Enables all logging" ) + ("log-enable", optValue(selectors, "RULE"), + ("Enables logging for selected levels and components. " + "RULE is in the form 'LEVEL[+][:PATTERN]' " + "Levels are one of: \n\t "+levels.str()+"\n" + "For example:\n" + "\t'--log-enable warning+' " + "logs all warning, error and critical messages.\n" + "\t'--log-enable debug:framing' " + "logs debug messages from the framing namespace. " + "This option can be used multiple times").c_str()) + ("log-time", optValue(time, "yes|no"), + "Include time in log messages") + ("log-level", optValue(level,"yes|no"), + "Include severity level in log messages") + ("log-source", optValue(source,"yes|no"), + "Include source file:line in log messages") + ("log-thread", optValue(thread,"yes|no"), + "Include thread ID in log messages") + ("log-function", optValue(function,"yes|no"), + "Include function signature in log messages"); +} + +}} // namespace qpid::log diff --git a/qpid/cpp/src/qpid/log/Options.h b/qpid/cpp/src/qpid/log/Options.h new file mode 100644 index 0000000000..441c6e8a8d --- /dev/null +++ b/qpid/cpp/src/qpid/log/Options.h @@ -0,0 +1,42 @@ +#ifndef OPTIONS_H +#define OPTIONS_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ +#include "qpid/Options.h" + + +namespace qpid { +namespace log { + +/** Logging options for config parser. */ +struct Options : public qpid::Options { + Options(const std::string& name="Logging options"); + + std::vector<std::string> selectors; + std::vector<std::string> outputs; + bool time, level, thread, source, function; + bool trace; +}; + + +}} // namespace qpid::log + + + +#endif /*!OPTIONS_H*/ diff --git a/qpid/cpp/src/qpid/log/Selector.cpp b/qpid/cpp/src/qpid/log/Selector.cpp new file mode 100644 index 0000000000..994421d0ff --- /dev/null +++ b/qpid/cpp/src/qpid/log/Selector.cpp @@ -0,0 +1,66 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "Selector.h" +#include "Options.h" +#include <boost/bind.hpp> +#include <algorithm> + +namespace qpid { +namespace log { + +using namespace std; + +void Selector::enable(const string& enableStr) { + string level, pattern; + size_t c=enableStr.find(':'); + if (c==string::npos) { + level=enableStr; + } + else { + level=enableStr.substr(0,c); + pattern=enableStr.substr(c+1); + } + if (!level.empty() && level[level.size()-1]=='+') { + for (int i = LevelTraits::level(level.substr(0,level.size()-1)); + i < LevelTraits::COUNT; + ++i) + enable(Level(i), pattern); + } + else { + enable(LevelTraits::level(level), pattern); + } +} + +Selector::Selector(const Options& opt){ + for_each(opt.selectors.begin(), opt.selectors.end(), + boost::bind(&Selector::enable, this, _1)); +} + +bool Selector::isEnabled(Level level, const std::string& function) { + for (std::vector<std::string>::iterator i=substrings[level].begin(); + i != substrings[level].end(); + ++i) + { + if (function.find(*i) != std::string::npos) + return true; + } + return false; +} + +}} // namespace qpid::log diff --git a/qpid/cpp/src/qpid/log/Selector.h b/qpid/cpp/src/qpid/log/Selector.h new file mode 100644 index 0000000000..7c98bc6f8f --- /dev/null +++ b/qpid/cpp/src/qpid/log/Selector.h @@ -0,0 +1,70 @@ +#ifndef SELECTOR_H +#define SELECTOR_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "Statement.h" +#include <vector> + +namespace qpid { +namespace log { +class Options; + +/** + * A selector identifies the set of log messages to enable. + * + * Thread object unsafe, pass-by-value type. + */ +class Selector { + public: + /** Empty selector selects nothing */ + Selector() {} + + /** Set selector from Options */ + Selector(const Options&); + + /** Equavlient to: Selector s; s.enable(l, s) */ + Selector(Level l, const std::string& s=std::string()) { + enable(l,s); + } + + Selector(const std::string& enableStr) { enable(enableStr); } + /** + * Enable messages with level in levels where the file + * name contains substring. Empty string matches all. + */ + void enable(Level level, const std::string& substring=std::string()) { + substrings[level].push_back(substring); + } + + /** Enable based on a 'level[+]:file' string */ + void enable(const std::string& enableStr); + + /** True if level is enabld for file. */ + bool isEnabled(Level level, const std::string& function); + + private: + std::vector<std::string> substrings[LevelTraits::COUNT]; +}; + + +}} // namespace qpid::log + + +#endif /*!SELECTOR_H*/ diff --git a/qpid/cpp/src/qpid/log/Statement.cpp b/qpid/cpp/src/qpid/log/Statement.cpp new file mode 100644 index 0000000000..db5d92c50a --- /dev/null +++ b/qpid/cpp/src/qpid/log/Statement.cpp @@ -0,0 +1,92 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "Statement.h" +#include "Logger.h" +#include <boost/bind.hpp> +#include <stdexcept> +#include <algorithm> +#include <syslog.h> +#include <ctype.h> + +namespace qpid { +namespace log { + +namespace { +using namespace std; + +struct NonPrint { bool operator()(unsigned char c) { return !isprint(c); } }; + +const char hex[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + +std::string quote(const std::string& str) { + NonPrint nonPrint; + size_t n = std::count_if(str.begin(), str.end(), nonPrint); + if (n==0) return str; + std::string ret; + ret.reserve(str.size()+2*n); // Avoid extra allocations. + for (string::const_iterator i = str.begin(); i != str.end(); ++i) { + if (nonPrint(*i)) { + ret.push_back('\\'); + ret.push_back(hex[((*i) >> 4)&0xf]); + ret.push_back(hex[(*i) & 0xf]); + } + else ret.push_back(*i); + } + return ret; +} + +} + +void Statement::log(const std::string& message) { + Logger::instance().log(*this, quote(message)); +} + +Statement::Initializer::Initializer(Statement& s) : statement(s) { + Logger::instance().add(s); +} + +namespace { +const char* names[LevelTraits::COUNT] = { + "trace", "debug", "info", "notice", "warning", "error", "critical" +}; + +int priorities[LevelTraits::COUNT] = { + LOG_DEBUG, LOG_DEBUG, LOG_INFO, LOG_NOTICE, + LOG_WARNING, LOG_ERR, LOG_CRIT +}; + +} // namespace + +Level LevelTraits::level(const char* name) { + for (int i =0; i < LevelTraits::COUNT; ++i) { + if (strcmp(names[i], name)==0) + return Level(i); + } + throw std::runtime_error(std::string("Invalid log level name: ")+name); +} + +const char* LevelTraits::name(Level l) { + return names[l]; +} + +int LevelTraits::priority(Level l) { + return priorities[l]; +} + +}} // namespace qpid::log diff --git a/qpid/cpp/src/qpid/log/Statement.h b/qpid/cpp/src/qpid/log/Statement.h new file mode 100644 index 0000000000..18162971b0 --- /dev/null +++ b/qpid/cpp/src/qpid/log/Statement.h @@ -0,0 +1,114 @@ +#ifndef STATEMENT_H +#define STATEMENT_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "qpid/Msg.h" + +#include <boost/current_function.hpp> + +namespace qpid { +namespace log { + +/** Debugging severity levels + * - trace: High-volume debugging messages. + * - debug: Debugging messages. + * - info: Informational messages. + * - notice: Normal but significant condition. + * - warning: Warn of a possible problem. + * - error: A definite error has occured. + * - critical: System in danger of severe failure. + */ +enum Level { trace, debug, info, notice, warning, error, critical }; +struct LevelTraits { + static const int COUNT=critical+1; + + /** Get level from string name. + *@exception if name invalid. + */ + static Level level(const char* name); + + /** Get level from string name. + *@exception if name invalid. + */ + static Level level(const std::string& name) { + return level(name.c_str()); + } + + /** String name of level */ + static const char* name(Level); + + /** Syslog priority of level */ + static int priority(Level); +}; + +/** POD struct representing a logging statement in source code. */ +struct Statement { + bool enabled; + const char* file; + int line; + const char* function; + Level level; + + void log(const std::string& message); + + struct Initializer { + Initializer(Statement& s); + Statement& statement; + }; +}; + +///@internal static initializer for a Statement. +#define QPID_LOG_STATEMENT_INIT(level) \ + { 0, __FILE__, __LINE__, BOOST_CURRENT_FUNCTION, (::qpid::log::level) } + +/** + * Macro for log statements. Example of use: + * @code + * QPID_LOG(debug, "There are " << foocount << " foos in the bar."); + * QPID_LOG(error, boost::format("Dohickey %s exploded") % dohicky.name()); + * @endcode + * + * All code with logging statements should be built with + * -DQPID_COMPONENT=<component name> + * where component name is the name of the component this file belongs to. + * + * You can subscribe to log messages by level, by component, by filename + * or a combination @see Configuration. + + *@param LEVEL severity Level for message, should be one of: + * debug, info, notice, warning, error, critical. NB no qpid::log:: prefix. + *@param MESSAGE any object with an @eostream operator<<, or a sequence + * like of ostreamable objects separated by @e<<. + */ +#define QPID_LOG(level, message) \ + do { \ + static ::qpid::log::Statement stmt_= QPID_LOG_STATEMENT_INIT(level); \ + static ::qpid::log::Statement::Initializer init_(stmt_); \ + if (stmt_.enabled) \ + stmt_.log(::qpid::Msg() << message); \ + } while(0) + +}} // namespace qpid::log + + + + +#endif /*!STATEMENT_H*/ + diff --git a/qpid/cpp/src/qpid/management/Args.h b/qpid/cpp/src/qpid/management/Args.h new file mode 100644 index 0000000000..da1fb033b9 --- /dev/null +++ b/qpid/cpp/src/qpid/management/Args.h @@ -0,0 +1,44 @@ +#ifndef _Args_ +#define _Args_ + +// +// 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. +// + + +namespace qpid { +namespace management { + +class Args +{ + public: + + virtual ~Args (void) = 0; + +}; + +inline Args::~Args (void) {} + +class ArgsNone : public Args +{ +}; + +}} + + +#endif /*!_Args_*/ diff --git a/qpid/cpp/src/qpid/management/Manageable.cpp b/qpid/cpp/src/qpid/management/Manageable.cpp new file mode 100644 index 0000000000..479cb4e0ce --- /dev/null +++ b/qpid/cpp/src/qpid/management/Manageable.cpp @@ -0,0 +1,37 @@ +// +// 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. +// + +#include "Manageable.h" + +using namespace qpid::management; + +std::string Manageable::StatusText (status_t status) +{ + switch (status) + { + case STATUS_OK : return "OK"; + case STATUS_UNKNOWN_OBJECT : return "UnknownObject"; + case STATUS_UNKNOWN_METHOD : return "UnknownMethod"; + case STATUS_NOT_IMPLEMENTED : return "NotImplemented"; + case STATUS_INVALID_PARAMETER : return "InvalidParameter"; + } + + return "??"; +} + diff --git a/qpid/cpp/src/qpid/management/Manageable.h b/qpid/cpp/src/qpid/management/Manageable.h new file mode 100644 index 0000000000..836ba03b23 --- /dev/null +++ b/qpid/cpp/src/qpid/management/Manageable.h @@ -0,0 +1,68 @@ +#ifndef _Manageable_ +#define _Manageable_ + +// +// 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. +// + +#include "ManagementObject.h" +#include "Args.h" +#include <string> +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace management { + +class Manageable +{ + public: + + virtual ~Manageable (void) = 0; + + // status_t is a type used to pass completion status from the method handler. + // + typedef uint32_t status_t; + static std::string StatusText (status_t status); + + static const status_t STATUS_OK = 0; + static const status_t STATUS_UNKNOWN_OBJECT = 1; + static const status_t STATUS_UNKNOWN_METHOD = 2; + static const status_t STATUS_NOT_IMPLEMENTED = 3; + static const status_t STATUS_INVALID_PARAMETER = 4; + + // Every "Manageable" object must hold a reference to exactly one + // management object. This object is always of a class derived from + // the pure-virtual "ManagementObject". + // + // This accessor function returns a shared_ptr to the management object. + // + virtual ManagementObject::shared_ptr GetManagementObject (void) const = 0; + + // Every "Manageable" object must implement ManagementMethod. This + // function is called when a remote management client invokes a method + // on this object. The input and output arguments are specific to the + // method being called and must be down-cast to the appropriate sub class + // before use. + virtual status_t ManagementMethod (uint32_t methodId, Args& args) = 0; +}; + +inline Manageable::~Manageable (void) {} + +}} + +#endif /*!_Manageable_*/ diff --git a/qpid/cpp/src/qpid/management/ManagementAgent.cpp b/qpid/cpp/src/qpid/management/ManagementAgent.cpp new file mode 100644 index 0000000000..ee0eb27bf6 --- /dev/null +++ b/qpid/cpp/src/qpid/management/ManagementAgent.cpp @@ -0,0 +1,669 @@ +/* + * + * 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. + * + */ + +#include "ManagementAgent.h" +#include "qpid/broker/DeliverableMessage.h" +#include "qpid/log/Statement.h" +#include <qpid/broker/Message.h> +#include <qpid/broker/MessageDelivery.h> +#include <list> +#include <iostream> +#include <fstream> + +using boost::intrusive_ptr; +using namespace qpid::framing; +using namespace qpid::management; +using namespace qpid::broker; +using namespace qpid::sys; +using namespace std; + +ManagementAgent::shared_ptr ManagementAgent::agent; +bool ManagementAgent::enabled = 0; + +ManagementAgent::ManagementAgent (string _dataDir, uint16_t _interval) : + dataDir (_dataDir), interval (_interval) +{ + timer.add (intrusive_ptr<TimerTask> (new Periodic(*this, interval))); + nextObjectId = uint64_t (qpid::sys::Duration (qpid::sys::now ())); + nextRemotePrefix = 101; + + // Get from file or generate and save to file. + if (dataDir.empty ()) + { + uuid.generate (); + QPID_LOG (info, "ManagementAgent has no data directory, generated new broker ID: " + << uuid); + } + else + { + string filename (dataDir + "/brokerId"); + ifstream inFile (filename.c_str ()); + + if (inFile.good ()) + { + inFile >> uuid; + inFile.close (); + QPID_LOG (debug, "ManagementAgent restored broker ID: " << uuid); + } + else + { + uuid.generate (); + QPID_LOG (info, "ManagementAgent generated broker ID: " << uuid); + + ofstream outFile (filename.c_str ()); + if (outFile.good ()) + { + outFile << uuid << endl; + outFile.close (); + QPID_LOG (debug, "ManagementAgent saved broker ID"); + } + else + { + QPID_LOG (warning, "ManagementAgent unable to save broker ID"); + } + } + } +} + +ManagementAgent::~ManagementAgent () {} + +void ManagementAgent::enableManagement (string dataDir, uint16_t interval) +{ + enabled = 1; + if (agent.get () == 0) + agent = shared_ptr (new ManagementAgent (dataDir, interval)); +} + +ManagementAgent::shared_ptr ManagementAgent::getAgent (void) +{ + return agent; +} + +void ManagementAgent::shutdown (void) +{ + if (agent.get () != 0) + { + agent->mExchange.reset (); + agent->dExchange.reset (); + agent.reset (); + } +} + +void ManagementAgent::setExchange (broker::Exchange::shared_ptr _mexchange, + broker::Exchange::shared_ptr _dexchange) +{ + mExchange = _mexchange; + dExchange = _dexchange; +} + +void ManagementAgent::RegisterClass (string packageName, + string className, + uint8_t* md5Sum, + ManagementObject::writeSchemaCall_t schemaCall) +{ + Mutex::ScopedLock lock (userLock); + PackageMap::iterator pIter = FindOrAddPackage (packageName); + AddClassLocal (pIter, className, md5Sum, schemaCall); +} + +void ManagementAgent::addObject (ManagementObject::shared_ptr object, + uint64_t /*persistenceId*/, + uint64_t /*idOffset*/) +{ + Mutex::ScopedLock lock (userLock); + uint64_t objectId; + +// if (persistenceId == 0) + objectId = nextObjectId++; +// else +// objectId = 0x8000000000000000ULL | (persistenceId + idOffset); + + object->setObjectId (objectId); + managementObjects[objectId] = object; +} + +ManagementAgent::Periodic::Periodic (ManagementAgent& _agent, uint32_t _seconds) + : TimerTask (qpid::sys::Duration (_seconds * qpid::sys::TIME_SEC)), agent(_agent) {} + +ManagementAgent::Periodic::~Periodic () {} + +void ManagementAgent::Periodic::fire () +{ + agent.timer.add (intrusive_ptr<TimerTask> (new Periodic (agent, agent.interval))); + agent.PeriodicProcessing (); +} + +void ManagementAgent::clientAdded (void) +{ + for (ManagementObjectMap::iterator iter = managementObjects.begin (); + iter != managementObjects.end (); + iter++) + { + ManagementObject::shared_ptr object = iter->second; + object->setAllChanged (); + } +} + +void ManagementAgent::EncodeHeader (Buffer& buf, uint8_t opcode, uint32_t seq) +{ + buf.putOctet ('A'); + buf.putOctet ('M'); + buf.putOctet ('1'); + buf.putOctet (opcode); + buf.putLong (seq); +} + +bool ManagementAgent::CheckHeader (Buffer& buf, uint8_t *opcode, uint32_t *seq) +{ + uint8_t h1 = buf.getOctet (); + uint8_t h2 = buf.getOctet (); + uint8_t h3 = buf.getOctet (); + + *opcode = buf.getOctet (); + *seq = buf.getLong (); + + return h1 == 'A' && h2 == 'M' && h3 == '1'; +} + +void ManagementAgent::SendBuffer (Buffer& buf, + uint32_t length, + broker::Exchange::shared_ptr exchange, + string routingKey) +{ + if (exchange.get() == 0) + return; + + intrusive_ptr<Message> msg (new Message ()); + AMQFrame method (in_place<MessageTransferBody>( + ProtocolVersion(), 0, exchange->getName (), 0, 0)); + AMQFrame header (in_place<AMQHeaderBody>()); + AMQFrame content(in_place<AMQContentBody>()); + + content.castBody<AMQContentBody>()->decode(buf, length); + + method.setEof (false); + header.setBof (false); + header.setEof (false); + content.setBof (false); + + msg->getFrames().append(method); + msg->getFrames().append(header); + + MessageProperties* props = + msg->getFrames().getHeaders()->get<MessageProperties>(true); + props->setContentLength(length); + msg->getFrames().append(content); + + DeliverableMessage deliverable (msg); + exchange->route (deliverable, routingKey, 0); +} + +void ManagementAgent::PeriodicProcessing (void) +{ +#define BUFSIZE 65536 + Mutex::ScopedLock lock (userLock); + char msgChars[BUFSIZE]; + uint32_t contentSize; + string routingKey; + std::list<uint64_t> deleteList; + + if (managementObjects.empty ()) + return; + + for (ManagementObjectMap::iterator iter = managementObjects.begin (); + iter != managementObjects.end (); + iter++) + { + ManagementObject::shared_ptr object = iter->second; + + if (object->getConfigChanged () || object->isDeleted ()) + { + Buffer msgBuffer (msgChars, BUFSIZE); + EncodeHeader (msgBuffer, 'c'); + object->writeConfig (msgBuffer); + + contentSize = BUFSIZE - msgBuffer.available (); + msgBuffer.reset (); + routingKey = "mgmt." + uuid.str() + ".config." + object->getClassName (); + SendBuffer (msgBuffer, contentSize, mExchange, routingKey); + } + + if (object->getInstChanged ()) + { + Buffer msgBuffer (msgChars, BUFSIZE); + EncodeHeader (msgBuffer, 'i'); + object->writeInstrumentation (msgBuffer); + + contentSize = BUFSIZE - msgBuffer.available (); + msgBuffer.reset (); + routingKey = "mgmt." + uuid.str () + ".inst." + object->getClassName (); + SendBuffer (msgBuffer, contentSize, mExchange, routingKey); + } + + if (object->isDeleted ()) + deleteList.push_back (iter->first); + } + + // Delete flagged objects + for (std::list<uint64_t>::reverse_iterator iter = deleteList.rbegin (); + iter != deleteList.rend (); + iter++) + managementObjects.erase (*iter); + + deleteList.clear (); +} + +void ManagementAgent::sendCommandComplete (string replyToKey, uint32_t sequence, + uint32_t code, string text) +{ + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + EncodeHeader (outBuffer, 'z', sequence); + outBuffer.putLong (code); + outBuffer.putShortString (text); + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + SendBuffer (outBuffer, outLen, dExchange, replyToKey); +} + +void ManagementAgent::dispatchCommand (Deliverable& deliverable, + const string& routingKey, + const FieldTable* /*args*/) +{ + Mutex::ScopedLock lock (userLock); + Message& msg = ((DeliverableMessage&) deliverable).getMessage (); + + if (routingKey.compare (0, 13, "agent.method.") == 0) + dispatchMethod (msg, routingKey, 13); + + else if (routingKey.length () == 5 && + routingKey.compare (0, 5, "agent") == 0) + dispatchAgentCommand (msg); + + else + { + QPID_LOG (debug, "Illegal routing key for dispatch: " << routingKey); + return; + } +} + +void ManagementAgent::dispatchMethod (Message& msg, + const string& routingKey, + size_t first) +{ + size_t pos, start = first; + uint32_t contentSize; + + if (routingKey.length () == start) + { + QPID_LOG (debug, "Missing package-name in routing key: " << routingKey); + return; + } + + pos = routingKey.find ('.', start); + if (pos == string::npos || routingKey.length () == pos + 1) + { + QPID_LOG (debug, "Missing class-name in routing key: " << routingKey); + return; + } + + string packageName = routingKey.substr (start, pos - start); + + start = pos + 1; + pos = routingKey.find ('.', start); + if (pos == string::npos || routingKey.length () == pos + 1) + { + QPID_LOG (debug, "Missing method-name in routing key: " << routingKey); + return; + } + + string className = routingKey.substr (start, pos - start); + + start = pos + 1; + string methodName = routingKey.substr (start, routingKey.length () - start); + + contentSize = msg.encodedContentSize (); + if (contentSize < 8 || contentSize > MA_BUFFER_SIZE) + return; + + Buffer inBuffer (inputBuffer, MA_BUFFER_SIZE); + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen, sequence; + uint8_t opcode; + + msg.encodeContent (inBuffer); + inBuffer.reset (); + + if (!CheckHeader (inBuffer, &opcode, &sequence)) + { + QPID_LOG (debug, " Invalid content header"); + return; + } + + if (opcode != 'M') + { + QPID_LOG (debug, " Unexpected opcode " << opcode); + return; + } + + uint64_t objId = inBuffer.getLongLong (); + string replyToKey; + + const framing::MessageProperties* p = + msg.getFrames().getHeaders()->get<framing::MessageProperties>(); + if (p && p->hasReplyTo()) + { + const framing::ReplyTo& rt = p->getReplyTo (); + replyToKey = rt.getRoutingKey (); + } + else + { + QPID_LOG (debug, " Reply-to missing"); + return; + } + + EncodeHeader (outBuffer, 'm', sequence); + + ManagementObjectMap::iterator iter = managementObjects.find (objId); + if (iter == managementObjects.end ()) + { + outBuffer.putLong (Manageable::STATUS_UNKNOWN_OBJECT); + outBuffer.putShortString (Manageable::StatusText (Manageable::STATUS_UNKNOWN_OBJECT)); + } + else + { + iter->second->doMethod (methodName, inBuffer, outBuffer); + } + + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + SendBuffer (outBuffer, outLen, dExchange, replyToKey); +} + +void ManagementAgent::handleBrokerRequest (Buffer&, string replyToKey, uint32_t sequence) +{ + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + EncodeHeader (outBuffer, 'b', sequence); + uuid.encode (outBuffer); + + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + SendBuffer (outBuffer, outLen, dExchange, replyToKey); +} + +void ManagementAgent::handlePackageQuery (Buffer&, string replyToKey, uint32_t sequence) +{ + for (PackageMap::iterator pIter = packages.begin (); + pIter != packages.end (); + pIter++) + { + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + EncodeHeader (outBuffer, 'p', sequence); + EncodePackageIndication (outBuffer, pIter); + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + SendBuffer (outBuffer, outLen, dExchange, replyToKey); + } + + sendCommandComplete (replyToKey, sequence); +} + +void ManagementAgent::handlePackageInd (Buffer& inBuffer, string /*replyToKey*/, uint32_t /*sequence*/) +{ + std::string packageName; + + inBuffer.getShortString (packageName); + FindOrAddPackage (packageName); +} + +void ManagementAgent::handleClassQuery (Buffer& inBuffer, string replyToKey, uint32_t sequence) +{ + std::string packageName; + + inBuffer.getShortString (packageName); + PackageMap::iterator pIter = packages.find (packageName); + if (pIter != packages.end ()) + { + ClassMap cMap = pIter->second; + for (ClassMap::iterator cIter = cMap.begin (); + cIter != cMap.end (); + cIter++) + { + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + EncodeHeader (outBuffer, 'q', sequence); + EncodeClassIndication (outBuffer, pIter, cIter); + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + SendBuffer (outBuffer, outLen, dExchange, replyToKey); + } + } + + sendCommandComplete (replyToKey, sequence); +} + +void ManagementAgent::handleSchemaQuery (Buffer& inBuffer, string replyToKey, uint32_t sequence) +{ + string packageName; + SchemaClassKey key; + + inBuffer.getShortString (packageName); + inBuffer.getShortString (key.name); + inBuffer.getBin128 (key.hash); + + PackageMap::iterator pIter = packages.find (packageName); + if (pIter != packages.end ()) + { + ClassMap cMap = pIter->second; + ClassMap::iterator cIter = cMap.find (key); + if (cIter != cMap.end ()) + { + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + SchemaClass classInfo = cIter->second; + + if (classInfo.writeSchemaCall != 0) + { + EncodeHeader (outBuffer, 's', sequence); + classInfo.writeSchemaCall (outBuffer); + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + SendBuffer (outBuffer, outLen, dExchange, replyToKey); + } + else + { + // TODO: Forward request to remote agent. + } + + clientAdded (); + // TODO: Send client-added to each remote agent. + } + } +} + +uint32_t ManagementAgent::assignPrefix (uint32_t /*requestedPrefix*/) +{ + // TODO: Allow remote agents to keep their requested prefixes if able. + return nextRemotePrefix++; +} + +void ManagementAgent::handleAttachRequest (Buffer& inBuffer, string replyToKey, uint32_t sequence) +{ + string label; + uint32_t requestedPrefix; + uint32_t assignedPrefix; + + inBuffer.getShortString (label); + requestedPrefix = inBuffer.getLong (); + assignedPrefix = assignPrefix (requestedPrefix); + + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + EncodeHeader (outBuffer, 'a', sequence); + outBuffer.putLong (assignedPrefix); + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + SendBuffer (outBuffer, outLen, dExchange, replyToKey); +} + +void ManagementAgent::handleGetRequest (Buffer& inBuffer, string replyToKey, uint32_t sequence) +{ + FieldTable ft; + FieldTable::ValuePtr value; + + ft.decode (inBuffer); + value = ft.get ("_class"); + if (value->empty () || !value->convertsTo<string> ()) + { + // TODO: Send completion with an error code + return; + } + + string className (value->get<string> ()); + + for (ManagementObjectMap::iterator iter = managementObjects.begin (); + iter != managementObjects.end (); + iter++) + { + ManagementObject::shared_ptr object = iter->second; + if (object->getClassName () == className) + { + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + EncodeHeader (outBuffer, 'g', sequence); + object->writeConfig (outBuffer); + object->writeInstrumentation (outBuffer, true); + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + SendBuffer (outBuffer, outLen, dExchange, replyToKey); + } + } + + sendCommandComplete (replyToKey, sequence); +} + +void ManagementAgent::dispatchAgentCommand (Message& msg) +{ + Buffer inBuffer (inputBuffer, MA_BUFFER_SIZE); + uint8_t opcode; + uint32_t sequence; + string replyToKey; + + const framing::MessageProperties* p = + msg.getFrames().getHeaders()->get<framing::MessageProperties>(); + if (p && p->hasReplyTo()) + { + const framing::ReplyTo& rt = p->getReplyTo (); + replyToKey = rt.getRoutingKey (); + } + else + return; + + msg.encodeContent (inBuffer); + inBuffer.reset (); + + if (!CheckHeader (inBuffer, &opcode, &sequence)) + return; + + if (opcode == 'B') handleBrokerRequest (inBuffer, replyToKey, sequence); + else if (opcode == 'P') handlePackageQuery (inBuffer, replyToKey, sequence); + else if (opcode == 'p') handlePackageInd (inBuffer, replyToKey, sequence); + else if (opcode == 'Q') handleClassQuery (inBuffer, replyToKey, sequence); + else if (opcode == 'S') handleSchemaQuery (inBuffer, replyToKey, sequence); + else if (opcode == 'A') handleAttachRequest (inBuffer, replyToKey, sequence); + else if (opcode == 'G') handleGetRequest (inBuffer, replyToKey, sequence); +} + +ManagementAgent::PackageMap::iterator ManagementAgent::FindOrAddPackage (std::string name) +{ + PackageMap::iterator pIter = packages.find (name); + if (pIter != packages.end ()) + return pIter; + + // No such package found, create a new map entry. + pair<PackageMap::iterator, bool> result = + packages.insert (pair<string, ClassMap> (name, ClassMap ())); + QPID_LOG (debug, "ManagementAgent added package " << name); + + // Publish a package-indication message + Buffer outBuffer (outputBuffer, MA_BUFFER_SIZE); + uint32_t outLen; + + EncodeHeader (outBuffer, 'p'); + EncodePackageIndication (outBuffer, result.first); + outLen = MA_BUFFER_SIZE - outBuffer.available (); + outBuffer.reset (); + SendBuffer (outBuffer, outLen, mExchange, "mgmt." + uuid.str() + ".schema.package"); + + return result.first; +} + +void ManagementAgent::AddClassLocal (PackageMap::iterator pIter, + string className, + uint8_t* md5Sum, + ManagementObject::writeSchemaCall_t schemaCall) +{ + SchemaClassKey key; + ClassMap& cMap = pIter->second; + + key.name = className; + memcpy (&key.hash, md5Sum, 16); + + ClassMap::iterator cIter = cMap.find (key); + if (cIter != cMap.end ()) + return; + + // No such class found, create a new class with local information. + QPID_LOG (debug, "ManagementAgent added class " << pIter->first << "." << + key.name); + SchemaClass classInfo; + + classInfo.writeSchemaCall = schemaCall; + cMap[key] = classInfo; + + // TODO: Publish a class-indication message +} + +void ManagementAgent::EncodePackageIndication (Buffer& buf, + PackageMap::iterator pIter) +{ + buf.putShortString ((*pIter).first); +} + +void ManagementAgent::EncodeClassIndication (Buffer& buf, + PackageMap::iterator pIter, + ClassMap::iterator cIter) +{ + SchemaClassKey key = (*cIter).first; + + buf.putShortString ((*pIter).first); + buf.putShortString (key.name); + buf.putBin128 (key.hash); +} + diff --git a/qpid/cpp/src/qpid/management/ManagementAgent.h b/qpid/cpp/src/qpid/management/ManagementAgent.h new file mode 100644 index 0000000000..bd86d4e773 --- /dev/null +++ b/qpid/cpp/src/qpid/management/ManagementAgent.h @@ -0,0 +1,189 @@ +#ifndef _ManagementAgent_ +#define _ManagementAgent_ + +/* + * + * 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. + * + */ + +#include "qpid/Options.h" +#include "qpid/broker/Exchange.h" +#include "qpid/broker/Timer.h" +#include "qpid/framing/Uuid.h" +#include "qpid/sys/Mutex.h" +#include "ManagementObject.h" +#include <qpid/framing/AMQFrame.h> +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace management { + +class ManagementAgent +{ + private: + + ManagementAgent (std::string dataDir, uint16_t interval); + + public: + + virtual ~ManagementAgent (); + + typedef boost::shared_ptr<ManagementAgent> shared_ptr; + + static void enableManagement (std::string dataDir, uint16_t interval); + static shared_ptr getAgent (void); + static void shutdown (void); + + void setInterval (uint16_t _interval) { interval = _interval; } + void setExchange (broker::Exchange::shared_ptr mgmtExchange, + broker::Exchange::shared_ptr directExchange); + void RegisterClass (std::string packageName, + std::string className, + uint8_t* md5Sum, + ManagementObject::writeSchemaCall_t schemaCall); + void addObject (ManagementObject::shared_ptr object, + uint64_t persistenceId = 0, + uint64_t idOffset = 10); + void clientAdded (void); + void dispatchCommand (broker::Deliverable& msg, + const std::string& routingKey, + const qpid::framing::FieldTable* args); + + private: + + struct Periodic : public broker::TimerTask + { + ManagementAgent& agent; + + Periodic (ManagementAgent& agent, uint32_t seconds); + virtual ~Periodic (); + void fire (); + }; + + // Storage for tracking remote management agents, attached via the client + // management agent API. + // + struct RemoteAgent + { + std::string name; + uint64_t objIdBase; + }; + + // TODO: Eventually replace string with entire reply-to structure. reply-to + // currently assumes that the exchange is "amq.direct" even though it could + // in theory be specified differently. + typedef std::map<std::string, RemoteAgent> RemoteAgentMap; + typedef std::vector<std::string> ReplyToVector; + + // Storage for known schema classes: + // + // SchemaClassKey -- Key elements for map lookups + // SchemaClassKeyComp -- Comparison class for SchemaClassKey + // SchemaClass -- Non-key elements for classes + // + struct SchemaClassKey + { + std::string name; + uint8_t hash[16]; + }; + + struct SchemaClassKeyComp + { + bool operator() (const SchemaClassKey& lhs, const SchemaClassKey& rhs) const + { + if (lhs.name != rhs.name) + return lhs.name < rhs.name; + else + for (int i = 0; i < 16; i++) + if (lhs.hash[i] != rhs.hash[i]) + return lhs.hash[i] < rhs.hash[i]; + return false; + } + }; + + struct SchemaClass + { + ManagementObject::writeSchemaCall_t writeSchemaCall; + ReplyToVector remoteAgents; + + SchemaClass () : writeSchemaCall(0) {} + }; + + typedef std::map<SchemaClassKey, SchemaClass, SchemaClassKeyComp> ClassMap; + typedef std::map<std::string, ClassMap> PackageMap; + + RemoteAgentMap remoteAgents; + PackageMap packages; + ManagementObjectMap managementObjects; + + static shared_ptr agent; + static bool enabled; + + qpid::framing::Uuid uuid; + qpid::sys::Mutex userLock; + broker::Timer timer; + broker::Exchange::shared_ptr mExchange; + broker::Exchange::shared_ptr dExchange; + std::string dataDir; + uint16_t interval; + uint64_t nextObjectId; + uint32_t nextRemotePrefix; + +# define MA_BUFFER_SIZE 65536 + char inputBuffer[MA_BUFFER_SIZE]; + char outputBuffer[MA_BUFFER_SIZE]; + + void PeriodicProcessing (void); + void EncodeHeader (qpid::framing::Buffer& buf, uint8_t opcode, uint32_t seq = 0); + bool CheckHeader (qpid::framing::Buffer& buf, uint8_t *opcode, uint32_t *seq); + void SendBuffer (qpid::framing::Buffer& buf, + uint32_t length, + broker::Exchange::shared_ptr exchange, + std::string routingKey); + + void dispatchMethod (broker::Message& msg, + const std::string& routingKey, + size_t first); + void dispatchAgentCommand (broker::Message& msg); + + PackageMap::iterator FindOrAddPackage (std::string name); + void AddClassLocal (PackageMap::iterator pIter, + std::string className, + uint8_t* md5Sum, + ManagementObject::writeSchemaCall_t schemaCall); + void EncodePackageIndication (qpid::framing::Buffer& buf, + PackageMap::iterator pIter); + void EncodeClassIndication (qpid::framing::Buffer& buf, + PackageMap::iterator pIter, + ClassMap::iterator cIter); + uint32_t assignPrefix (uint32_t requestedPrefix); + void sendCommandComplete (std::string replyToKey, uint32_t sequence, + uint32_t code = 0, std::string text = std::string("OK")); + void handleBrokerRequest (qpid::framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence); + void handlePackageQuery (qpid::framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence); + void handlePackageInd (qpid::framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence); + void handleClassQuery (qpid::framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence); + void handleSchemaQuery (qpid::framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence); + void handleAttachRequest (qpid::framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence); + void handleGetRequest (qpid::framing::Buffer& inBuffer, std::string replyToKey, uint32_t sequence); +}; + +}} + +#endif /*!_ManagementAgent_*/ diff --git a/qpid/cpp/src/qpid/management/ManagementExchange.cpp b/qpid/cpp/src/qpid/management/ManagementExchange.cpp new file mode 100644 index 0000000000..c589aefba0 --- /dev/null +++ b/qpid/cpp/src/qpid/management/ManagementExchange.cpp @@ -0,0 +1,65 @@ +/* + * + * 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. + * + */ + +#include "ManagementExchange.h" +#include "qpid/log/Statement.h" + +using namespace qpid::management; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +ManagementExchange::ManagementExchange (const string& _name, Manageable* _parent) : + Exchange (_name, _parent), TopicExchange(_name, _parent) {} +ManagementExchange::ManagementExchange (const std::string& _name, + bool _durable, + const FieldTable& _args, + Manageable* _parent) : + Exchange (_name, _durable, _args, _parent), + TopicExchange(_name, _durable, _args, _parent) {} + +void ManagementExchange::route (Deliverable& msg, + const string& routingKey, + const FieldTable* args) +{ + // Intercept management agent commands + if ((routingKey.length () > 6 && + routingKey.substr (0, 6).compare ("agent.") == 0) || + (routingKey.length () == 5 && + routingKey.substr (0, 5).compare ("agent") == 0)) + { + managementAgent->dispatchCommand (msg, routingKey, args); + return; + } + + TopicExchange::route (msg, routingKey, args); +} + +void ManagementExchange::setManagmentAgent (ManagementAgent::shared_ptr agent) +{ + managementAgent = agent; +} + + +ManagementExchange::~ManagementExchange() {} + +const std::string ManagementExchange::typeName("management"); + diff --git a/qpid/cpp/src/qpid/management/ManagementExchange.h b/qpid/cpp/src/qpid/management/ManagementExchange.h new file mode 100644 index 0000000000..7faec32b0f --- /dev/null +++ b/qpid/cpp/src/qpid/management/ManagementExchange.h @@ -0,0 +1,58 @@ +/* + * + * 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. + * + */ +#ifndef _ManagementExchange_ +#define _ManagementExchange_ + +#include "qpid/broker/TopicExchange.h" +#include "ManagementAgent.h" + +namespace qpid { +namespace broker { + +class ManagementExchange : public virtual TopicExchange +{ + private: + management::ManagementAgent::shared_ptr managementAgent; + + public: + static const std::string typeName; + + ManagementExchange (const string& name, Manageable* _parent = 0); + ManagementExchange (const string& _name, bool _durable, + const qpid::framing::FieldTable& _args, + Manageable* _parent = 0); + + virtual std::string getType() const { return typeName; } + + virtual void route (Deliverable& msg, + const string& routingKey, + const qpid::framing::FieldTable* args); + + void setManagmentAgent (management::ManagementAgent::shared_ptr agent); + + virtual ~ManagementExchange(); +}; + + +} +} + +#endif diff --git a/qpid/cpp/src/qpid/management/ManagementObject.cpp b/qpid/cpp/src/qpid/management/ManagementObject.cpp new file mode 100644 index 0000000000..6af5412b99 --- /dev/null +++ b/qpid/cpp/src/qpid/management/ManagementObject.cpp @@ -0,0 +1,39 @@ +/* + * + * 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. + * + */ + +#include "Manageable.h" +#include "ManagementObject.h" +#include "qpid/framing/FieldTable.h" + +using namespace qpid::framing; +using namespace qpid::management; +using namespace qpid::sys; + +void ManagementObject::writeTimestamps (Buffer& buf) +{ + buf.putShortString (getPackageName ()); + buf.putShortString (getClassName ()); + buf.putBin128 (getMd5Sum ()); + buf.putLongLong (uint64_t (Duration (now ()))); + buf.putLongLong (createTime); + buf.putLongLong (destroyTime); + buf.putLongLong (objectId); +} diff --git a/qpid/cpp/src/qpid/management/ManagementObject.h b/qpid/cpp/src/qpid/management/ManagementObject.h new file mode 100644 index 0000000000..48a3372d16 --- /dev/null +++ b/qpid/cpp/src/qpid/management/ManagementObject.h @@ -0,0 +1,125 @@ +#ifndef _ManagementObject_ +#define _ManagementObject_ + +/* + * + * 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. + * + */ + +#include "Manageable.h" +#include "qpid/sys/Time.h" +#include "qpid/sys/Mutex.h" +#include <qpid/framing/Buffer.h> +#include <boost/shared_ptr.hpp> +#include <map> + +namespace qpid { +namespace management { + +class Manageable; + +class ManagementObject +{ + protected: + + uint64_t createTime; + uint64_t destroyTime; + uint64_t objectId; + bool configChanged; + bool instChanged; + bool deleted; + Manageable* coreObject; + sys::RWlock accessLock; + + static const uint8_t TYPE_U8 = 1; + static const uint8_t TYPE_U16 = 2; + static const uint8_t TYPE_U32 = 3; + static const uint8_t TYPE_U64 = 4; + static const uint8_t TYPE_SSTR = 6; + static const uint8_t TYPE_LSTR = 7; + static const uint8_t TYPE_ABSTIME = 8; + static const uint8_t TYPE_DELTATIME = 9; + static const uint8_t TYPE_REF = 10; + static const uint8_t TYPE_BOOL = 11; + static const uint8_t TYPE_FLOAT = 12; + static const uint8_t TYPE_DOUBLE = 13; + static const uint8_t TYPE_UUID = 14; + static const uint8_t TYPE_FTABLE = 15; + + static const uint8_t ACCESS_RC = 1; + static const uint8_t ACCESS_RW = 2; + static const uint8_t ACCESS_RO = 3; + + static const uint8_t DIR_I = 1; + static const uint8_t DIR_O = 2; + static const uint8_t DIR_IO = 3; + + static const uint8_t FLAG_CONFIG = 0x01; + static const uint8_t FLAG_INDEX = 0x02; + static const uint8_t FLAG_END = 0x80; + + void writeTimestamps (qpid::framing::Buffer& buf); + + public: + typedef boost::shared_ptr<ManagementObject> shared_ptr; + typedef void (*writeSchemaCall_t) (qpid::framing::Buffer&); + + ManagementObject (Manageable* _core) : + destroyTime(0), objectId (0), configChanged(true), + instChanged(true), deleted(false), coreObject(_core) + { createTime = uint64_t (qpid::sys::Duration (qpid::sys::now ())); } + virtual ~ManagementObject () {} + + virtual writeSchemaCall_t getWriteSchemaCall (void) = 0; + virtual void writeConfig (qpid::framing::Buffer& buf) = 0; + virtual void writeInstrumentation (qpid::framing::Buffer& buf, + bool skipHeaders = false) = 0; + virtual void doMethod (std::string methodName, + qpid::framing::Buffer& inBuf, + qpid::framing::Buffer& outBuf) = 0; + + virtual std::string getClassName (void) = 0; + virtual std::string getPackageName (void) = 0; + virtual uint8_t* getMd5Sum (void) = 0; + + void setObjectId (uint64_t oid) { objectId = oid; } + uint64_t getObjectId (void) { return objectId; } + inline bool getConfigChanged (void) { return configChanged; } + virtual bool getInstChanged (void) { return instChanged; } + inline void setAllChanged (void) + { + configChanged = true; + instChanged = true; + } + + inline void resourceDestroy (void) { + destroyTime = uint64_t (qpid::sys::Duration (qpid::sys::now ())); + deleted = true; + } + bool isDeleted (void) { return deleted; } + +}; + +typedef std::map<uint64_t,ManagementObject::shared_ptr> ManagementObjectMap; + +}} + + + +#endif /*!_ManagementObject_*/ diff --git a/qpid/cpp/src/qpid/memory.h b/qpid/cpp/src/qpid/memory.h new file mode 100644 index 0000000000..99d7a71e7b --- /dev/null +++ b/qpid/cpp/src/qpid/memory.h @@ -0,0 +1,32 @@ +#ifndef QPID_AUTO_PTR_H +#define QPID_AUTO_PTR_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include <memory> +namespace qpid { +/** Convenient template for creating auto_ptr in-place in an argument list. */ +template <class T> +std::auto_ptr<T> make_auto_ptr(T* ptr) { return std::auto_ptr<T>(ptr); } + +} // namespace qpid + + + +#endif /*!QPID_AUTO_PTR_H*/ diff --git a/qpid/cpp/src/qpid/pointer_to_other.h b/qpid/cpp/src/qpid/pointer_to_other.h new file mode 100644 index 0000000000..a99dc89658 --- /dev/null +++ b/qpid/cpp/src/qpid/pointer_to_other.h @@ -0,0 +1,62 @@ +#ifndef QPID_POINTERTOOTHER_H +#define QPID_POINTERTOOTHER_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. + * + */ + +namespace qpid { + +// Defines the same pointer type (raw or smart) to another pointee type + +template<class T, class U> +struct pointer_to_other; + +template<class T, class U, + template<class> class Sp> +struct pointer_to_other< Sp<T>, U > + { + typedef Sp<U> type; + }; + +template<class T, class T2, class U, + template<class, class> class Sp> +struct pointer_to_other< Sp<T, T2>, U > + { + typedef Sp<U, T2> type; + }; + +template<class T, class T2, class T3, class U, + template<class, class, class> class Sp> +struct pointer_to_other< Sp<T, T2, T3>, U > + { + typedef Sp<U, T2, T3> type; + }; + +template<class T, class U> +struct pointer_to_other< T*, U > +{ + typedef U* type; +}; + +} // namespace qpid + + + +#endif /*!QPID_POINTERTOOTHER_H*/ diff --git a/qpid/cpp/src/qpid/ptr_map.h b/qpid/cpp/src/qpid/ptr_map.h new file mode 100644 index 0000000000..e9a1d507a6 --- /dev/null +++ b/qpid/cpp/src/qpid/ptr_map.h @@ -0,0 +1,120 @@ +#ifndef QPID_PTR_MAP +#define QPID_PTR_MAP + +/* + * + * 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. + * + */ + +#include <boost/ptr_container/ptr_map.hpp> +#include <boost/version.hpp> + +namespace qpid { +namespace ptr_map { + +/** @file + * Workaround for API change between boost 1.33 and 1.34. + * + * To be portable across these versions, code using boost::ptr_map + * iterators should use get_pointer(i) to get the pointer from + * a boost::ptr_map iterator. + * + * Can be removed when we no longer support platforms on 1.33. + * + * @see http://www.boost.org/libs/ptr_container/doc/ptr_container.html#upgrading-from-boost-v-1-33 + */ + +#include <boost/type_traits/remove_const.hpp> + +#if (BOOST_VERSION < 103400) + +template <class PtrMapIter> +typename PtrMapIter::pointer get_pointer(const PtrMapIter& i) +{ return &*i; } + +#else + +template <class PtrMapIter> +typename boost::remove_const<typename PtrMapIter::value_type::second_type>::type +get_pointer(const PtrMapIter& i) +{ return i->second; } + +#endif + +}} // namespace qpid::ptr_map + +#endif /*!QPID_PTR_MAP*/ +#ifndef QPID_PTR_MAP +#define QPID_PTR_MAP + +/* + * + * 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. + * + */ + +#include <boost/ptr_container/ptr_map.hpp> +#include <boost/version.hpp> + +namespace qpid { +namespace ptr_map { + +/** @file + * Workaround for API change between boost 1.33 and 1.34. + * + * To be portable across these versions, code using boost::ptr_map + * iterators should use get_pointer(i) to get the pointer from + * a boost::ptr_map iterator. + * + * Can be removed when we no longer support platforms on 1.33. + * + * @see http://www.boost.org/libs/ptr_container/doc/ptr_container.html#upgrading-from-boost-v-1-33 + */ +#if (BOOST_VERSION < 103400) + +template <class PtrMapIter> +typename PtrMapIter::pointer get_pointer(const PtrMapIter& i) +{ return &*i; } + +#else + +template <class PtrMapIter> +typename PtrMapIter::value_type::second_type get_pointer(const PtrMapIter& i) +{ return i->second; } + +#endif + +}} // namespace qpid::ptr_map + +#endif /*!QPID_PTR_MAP*/ diff --git a/qpid/cpp/src/qpid/shared_ptr.h b/qpid/cpp/src/qpid/shared_ptr.h new file mode 100644 index 0000000000..0c933ea6a6 --- /dev/null +++ b/qpid/cpp/src/qpid/shared_ptr.h @@ -0,0 +1,51 @@ +#ifndef _common_shared_ptr_h +#define _common_shared_ptr_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include <boost/shared_ptr.hpp> +#include <boost/cast.hpp> + +namespace qpid { + +// Import shared_ptr definitions into qpid namespace and define some +// useful shared_ptr templates for convenience. + +using boost::shared_ptr; +using boost::dynamic_pointer_cast; +using boost::static_pointer_cast; +using boost::const_pointer_cast; +using boost::shared_polymorphic_downcast; + +template <class T> shared_ptr<T> make_shared_ptr(T* ptr) { + return shared_ptr<T>(ptr); +} + +template <class T, class D> +shared_ptr<T> make_shared_ptr(T* ptr, D deleter) { + return shared_ptr<T>(ptr, deleter); +} + +inline void nullDeleter(void const *) {} + +} // namespace qpid + + + +#endif /*!_common_shared_ptr_h*/ diff --git a/qpid/cpp/src/qpid/sys/Acceptor.h b/qpid/cpp/src/qpid/sys/Acceptor.h new file mode 100644 index 0000000000..1e7827e60c --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Acceptor.h @@ -0,0 +1,53 @@ +#ifndef _sys_Acceptor_h +#define _sys_Acceptor_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. + * + */ + +#include <stdint.h> +#include "qpid/SharedObject.h" +#include "ConnectionCodec.h" + +namespace qpid { +namespace sys { + +class Acceptor : public qpid::SharedObject<Acceptor> +{ + public: + static Acceptor::shared_ptr create(int16_t port, int backlog, int threads); + virtual ~Acceptor() = 0; + virtual uint16_t getPort() const = 0; + virtual std::string getHost() const = 0; + virtual void run(ConnectionCodec::Factory*) = 0; + virtual void connect( + const std::string& host, int16_t port, ConnectionCodec::Factory* codec) = 0; + + /** Note: this function is async-signal safe */ + virtual void shutdown() = 0; +}; + +inline Acceptor::~Acceptor() {} + +}} + + + +#endif /*!_sys_Acceptor_h*/ diff --git a/qpid/cpp/src/qpid/sys/AggregateOutput.cpp b/qpid/cpp/src/qpid/sys/AggregateOutput.cpp new file mode 100644 index 0000000000..57cc0c5a33 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/AggregateOutput.cpp @@ -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. + * + */ + +#include "qpid/sys/AggregateOutput.h" +#include "qpid/log/Statement.h" +#include <algorithm> + +namespace qpid { +namespace sys { + +void AggregateOutput::activateOutput() +{ + control.activateOutput(); +} + +bool AggregateOutput::doOutput() +{ + bool result = false; + if (!tasks.empty()) { + if (next >= tasks.size()) next = next % tasks.size(); + + size_t start = next; + //loop until a task generated some output + while (!result) { + result = tasks[next++]->doOutput(); + if (next >= tasks.size()) next = next % tasks.size(); + if (start == next) break; + } + } + return result; +} + +void AggregateOutput::addOutputTask(OutputTask* t) +{ + tasks.push_back(t); +} + +void AggregateOutput::removeOutputTask(OutputTask* t) +{ + TaskList::iterator i = std::find(tasks.begin(), tasks.end(), t); + if (i != tasks.end()) tasks.erase(i); +} + +}} // namespace qpid::sys diff --git a/qpid/cpp/src/qpid/sys/AggregateOutput.h b/qpid/cpp/src/qpid/sys/AggregateOutput.h new file mode 100644 index 0000000000..a870fcb95a --- /dev/null +++ b/qpid/cpp/src/qpid/sys/AggregateOutput.h @@ -0,0 +1,54 @@ +/* + * + * 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. + * + */ +#ifndef _AggregateOutput_ +#define _AggregateOutput_ + +#include <vector> +#include "Mutex.h" +#include "OutputControl.h" +#include "OutputTask.h" + +namespace qpid { +namespace sys { + + class AggregateOutput : public OutputTask, public OutputControl + { + typedef std::vector<OutputTask*> TaskList; + + TaskList tasks; + size_t next; + OutputControl& control; + + public: + AggregateOutput(OutputControl& c) : next(0), control(c) {}; + //this may be called on any thread + void activateOutput(); + //all the following will be called on the same thread + bool doOutput(); + void addOutputTask(OutputTask* t); + void removeOutputTask(OutputTask* t); + }; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/sys/AsynchIO.h b/qpid/cpp/src/qpid/sys/AsynchIO.h new file mode 100644 index 0000000000..ca34d82741 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/AsynchIO.h @@ -0,0 +1,134 @@ +#ifndef _sys_AsynchIO +#define _sys_AsynchIO +/* + * + * 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. + * + */ + +#include "Dispatcher.h" + +#include <boost/function.hpp> +#include <deque> + +namespace qpid { +namespace sys { + +/* + * Asynchronous acceptor: accepts connections then does a callback with the + * accepted fd + */ +class AsynchAcceptor { +public: + typedef boost::function1<void, const Socket&> Callback; + +private: + Callback acceptedCallback; + DispatchHandle handle; + +public: + AsynchAcceptor(const Socket& s, Callback callback); + void start(Poller::shared_ptr poller); + +private: + void readable(DispatchHandle& handle); +}; + +/* + * Asycnchronous reader/writer: + * Reader accepts buffers to read into; reads into the provided buffers + * and then does a callback with the buffer and amount read. Optionally it can callback + * when there is something to read but no buffer to read it into. + * + * Writer accepts a buffer and queues it for writing; can also be given + * a callback for when writing is "idle" (ie fd is writable, but nothing to write) + * + * The class is implemented in terms of DispatchHandle to allow it to be deleted by deleting + * the contained DispatchHandle + */ +class AsynchIO : private DispatchHandle { +public: + struct BufferBase { + char* const bytes; + const int32_t byteCount; + int32_t dataStart; + int32_t dataCount; + + BufferBase(char* const b, const int32_t s) : + bytes(b), + byteCount(s), + dataStart(0), + dataCount(0) + {} + + virtual ~BufferBase() + {} + }; + + typedef boost::function2<void, AsynchIO&, BufferBase*> ReadCallback; + typedef boost::function1<void, AsynchIO&> EofCallback; + typedef boost::function1<void, AsynchIO&> DisconnectCallback; + typedef boost::function2<void, AsynchIO&, const Socket&> ClosedCallback; + typedef boost::function1<void, AsynchIO&> BuffersEmptyCallback; + typedef boost::function1<void, AsynchIO&> IdleCallback; + +private: + ReadCallback readCallback; + EofCallback eofCallback; + DisconnectCallback disCallback; + ClosedCallback closedCallback; + BuffersEmptyCallback emptyCallback; + IdleCallback idleCallback; + std::deque<BufferBase*> bufferQueue; + std::deque<BufferBase*> writeQueue; + bool queuedClose; + /** + * This flag is used to detect and handle concurrency between + * calls to notifyPendingWrite() (which can be made from any thread) and + * the execution of the writeable() method (which is always on the + * thread processing this handle. + */ + volatile bool writePending; + +public: + AsynchIO(const Socket& s, + ReadCallback rCb, EofCallback eofCb, DisconnectCallback disCb, + ClosedCallback cCb = 0, BuffersEmptyCallback eCb = 0, IdleCallback iCb = 0); + void queueForDeletion(); + + void start(Poller::shared_ptr poller); + void queueReadBuffer(BufferBase* buff); + void unread(BufferBase* buff); + void queueWrite(BufferBase* buff); + void notifyPendingWrite(); + void queueWriteClose(); + bool writeQueueEmpty() { return writeQueue.empty(); } + BufferBase* getQueuedBuffer(); + const Socket& getSocket() const { return DispatchHandle::getSocket(); } + +private: + ~AsynchIO(); + void readable(DispatchHandle& handle); + void writeable(DispatchHandle& handle); + void disconnected(DispatchHandle& handle); + void close(DispatchHandle& handle); +}; + +}} + +#endif // _sys_AsynchIO diff --git a/qpid/cpp/src/qpid/sys/AsynchIOAcceptor.cpp b/qpid/cpp/src/qpid/sys/AsynchIOAcceptor.cpp new file mode 100644 index 0000000000..153557c5e5 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/AsynchIOAcceptor.cpp @@ -0,0 +1,316 @@ +/* + * + * 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. + * + */ + +#include "Acceptor.h" + +#include "Socket.h" +#include "AsynchIO.h" +#include "Mutex.h" +#include "Thread.h" + +#include "qpid/sys/ConnectionOutputHandler.h" +#include "qpid/framing/AMQP_HighestVersion.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid/framing/ProtocolInitiation.h" +#include "qpid/log/Statement.h" + +#include <boost/bind.hpp> +#include <boost/assert.hpp> +#include <queue> +#include <vector> +#include <memory> +#include <ostream> + +namespace qpid { +namespace sys { + +class AsynchIOAcceptor : public Acceptor { + Poller::shared_ptr poller; + Socket listener; + int numIOThreads; + const uint16_t listeningPort; + + public: + AsynchIOAcceptor(int16_t port, int backlog, int threads); + ~AsynchIOAcceptor() {} + void run(ConnectionCodec::Factory*); + void connect(const std::string& host, int16_t port, ConnectionCodec::Factory*); + + void shutdown(); + + uint16_t getPort() const; + std::string getHost() const; + + private: + void accepted(Poller::shared_ptr, const Socket&, ConnectionCodec::Factory*); +}; + +Acceptor::shared_ptr Acceptor::create(int16_t port, int backlog, int threads) +{ + return Acceptor::shared_ptr(new AsynchIOAcceptor(port, backlog, threads)); +} + +AsynchIOAcceptor::AsynchIOAcceptor(int16_t port, int backlog, int threads) : + poller(new Poller), + numIOThreads(threads), + listeningPort(listener.listen(port, backlog)) +{} + +// Buffer definition +struct Buff : public AsynchIO::BufferBase { + Buff() : + AsynchIO::BufferBase(new char[65536], 65536) + {} + ~Buff() + { delete [] bytes;} +}; + +class AsynchIOHandler : public OutputControl { + AsynchIO* aio; + ConnectionCodec::Factory* factory; + ConnectionCodec* codec; + bool readError; + std::string identifier; + bool isClient; + + void write(const framing::ProtocolInitiation&); + + public: + AsynchIOHandler() : + aio(0), + factory(0), + codec(0), + readError(false), + isClient(false) + {} + + ~AsynchIOHandler() { + if (codec) + codec->closed(); + delete codec; + } + + void setClient() { isClient = true; } + + void init(AsynchIO* a, ConnectionCodec::Factory* f) { + aio = a; + factory = f; + identifier = aio->getSocket().getPeerAddress(); + + } + + // Output side + void close(); + void activateOutput(); + + // Input side + void readbuff(AsynchIO& aio, AsynchIO::BufferBase* buff); + void eof(AsynchIO& aio); + void disconnect(AsynchIO& aio); + + // Notifications + void nobuffs(AsynchIO& aio); + void idle(AsynchIO& aio); + void closedSocket(AsynchIO& aio, const Socket& s); +}; + +void AsynchIOAcceptor::accepted(Poller::shared_ptr poller, const Socket& s, ConnectionCodec::Factory* f) { + AsynchIOHandler* async = new AsynchIOHandler; + AsynchIO* aio = new AsynchIO(s, + boost::bind(&AsynchIOHandler::readbuff, async, _1, _2), + boost::bind(&AsynchIOHandler::eof, async, _1), + boost::bind(&AsynchIOHandler::disconnect, async, _1), + boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2), + boost::bind(&AsynchIOHandler::nobuffs, async, _1), + boost::bind(&AsynchIOHandler::idle, async, _1)); + async->init(aio, f); + // Give connection some buffers to use + for (int i = 0; i < 4; i++) { + aio->queueReadBuffer(new Buff); + } + aio->start(poller); +} + + +uint16_t AsynchIOAcceptor::getPort() const { + return listeningPort; // Immutable no need for lock. +} + +std::string AsynchIOAcceptor::getHost() const { + return listener.getSockname(); +} + +void AsynchIOAcceptor::run(ConnectionCodec::Factory* fact) { + Dispatcher d(poller); + AsynchAcceptor + acceptor(listener, + boost::bind(&AsynchIOAcceptor::accepted, this, poller, _1, fact)); + acceptor.start(poller); + + std::vector<Thread> t(numIOThreads-1); + + // Run n-1 io threads + for (int i=0; i<numIOThreads-1; ++i) + t[i] = Thread(d); + + // Run final thread + d.run(); + + // Now wait for n-1 io threads to exit + for (int i=0; i<numIOThreads-1; ++i) { + t[i].join(); + } +} + +void AsynchIOAcceptor::connect( + const std::string& host, int16_t port, ConnectionCodec::Factory* f) +{ + Socket* socket = new Socket();//Should be deleted by handle when socket closes + socket->connect(host, port); + AsynchIOHandler* async = new AsynchIOHandler; + async->setClient(); + AsynchIO* aio = new AsynchIO(*socket, + boost::bind(&AsynchIOHandler::readbuff, async, _1, _2), + boost::bind(&AsynchIOHandler::eof, async, _1), + boost::bind(&AsynchIOHandler::disconnect, async, _1), + boost::bind(&AsynchIOHandler::closedSocket, async, _1, _2), + boost::bind(&AsynchIOHandler::nobuffs, async, _1), + boost::bind(&AsynchIOHandler::idle, async, _1)); + async->init(aio, f); + // Give connection some buffers to use + for (int i = 0; i < 4; i++) { + aio->queueReadBuffer(new Buff); + } + aio->start(poller); +} + + +void AsynchIOAcceptor::shutdown() { + // NB: this function must be async-signal safe, it must not + // call any function that is not async-signal safe. + poller->shutdown(); +} + + +void AsynchIOHandler::write(const framing::ProtocolInitiation& data) +{ + QPID_LOG(debug, "SENT [" << identifier << "] INIT(" << data << ")"); + AsynchIO::BufferBase* buff = aio->getQueuedBuffer(); + if (!buff) + buff = new Buff; + framing::Buffer out(buff->bytes, buff->byteCount); + data.encode(out); + buff->dataCount = data.size(); + aio->queueWrite(buff); +} + +void AsynchIOHandler::activateOutput() { + aio->notifyPendingWrite(); +} + +// Input side +void AsynchIOHandler::readbuff(AsynchIO& , AsynchIO::BufferBase* buff) { + if (readError) { + return; + } + size_t decoded = 0; + if (codec) { // Already initiated + try { + decoded = codec->decode(buff->bytes+buff->dataStart, buff->dataCount); + }catch(const std::exception& e){ + QPID_LOG(error, e.what()); + readError = true; + aio->queueWriteClose(); + } + }else{ + framing::Buffer in(buff->bytes+buff->dataStart, buff->dataCount); + framing::ProtocolInitiation protocolInit; + if (protocolInit.decode(in)) { + decoded = in.getPosition(); + QPID_LOG(debug, "RECV [" << identifier << "] INIT(" << protocolInit << ")"); + codec = factory->create(protocolInit.getVersion(), *this, identifier); + if (!codec) { + //TODO: may still want to revise this... + //send valid version header & close connection. + write(framing::ProtocolInitiation(framing::highestProtocolVersion)); + readError = true; + aio->queueWriteClose(); + } + } + } + // TODO: unreading needs to go away, and when we can cope + // with multiple sub-buffers in the general buffer scheme, it will + if (decoded != size_t(buff->dataCount)) { + // Adjust buffer for used bytes and then "unread them" + buff->dataStart += decoded; + buff->dataCount -= decoded; + aio->unread(buff); + } else { + // Give whole buffer back to aio subsystem + aio->queueReadBuffer(buff); + } +} + +void AsynchIOHandler::eof(AsynchIO&) { + QPID_LOG(debug, "DISCONNECTED [" << identifier << "]"); + if (codec) codec->closed(); + aio->queueWriteClose(); +} + +void AsynchIOHandler::closedSocket(AsynchIO&, const Socket& s) { + // If we closed with data still to send log a warning + if (!aio->writeQueueEmpty()) { + QPID_LOG(warning, "CLOSING [" << identifier << "] unsent data (probably due to client disconnect)"); + } + delete &s; + aio->queueForDeletion(); + delete this; +} + +void AsynchIOHandler::disconnect(AsynchIO& a) { + // treat the same as eof + eof(a); +} + +// Notifications +void AsynchIOHandler::nobuffs(AsynchIO&) { +} + +void AsynchIOHandler::idle(AsynchIO&){ + if (isClient && codec == 0) { + codec = factory->create(*this, identifier); + write(framing::ProtocolInitiation(codec->getVersion())); + return; + } + if (codec == 0) return; + if (codec->canEncode()) { + // Try and get a queued buffer if not then construct new one + AsynchIO::BufferBase* buff = aio->getQueuedBuffer(); + if (!buff) buff = new Buff; + size_t encoded=codec->encode(buff->bytes, buff->byteCount); + buff->dataCount = encoded; + aio->queueWrite(buff); + } + if (codec->isClosed()) + aio->queueWriteClose(); +} + +}} // namespace qpid::sys diff --git a/qpid/cpp/src/qpid/sys/AtomicCount.h b/qpid/cpp/src/qpid/sys/AtomicCount.h new file mode 100644 index 0000000000..54081092c8 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/AtomicCount.h @@ -0,0 +1,53 @@ +#ifndef _posix_AtomicCount_h +#define _posix_AtomicCount_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include <boost/detail/atomic_count.hpp> +#include "ScopedIncrement.h" + +namespace qpid { +namespace sys { + +/** + * Atomic counter. + */ +class AtomicCount { + public: + typedef ::qpid::sys::ScopedDecrement<AtomicCount> ScopedDecrement; + typedef ::qpid::sys::ScopedIncrement<AtomicCount> ScopedIncrement; + + AtomicCount(long value = 0) : count(value) {} + + void operator++() { ++count ; } + + long operator--() { return --count; } + + operator long() const { return count; } + + + private: + boost::detail::atomic_count count; +}; + + +}} + + +#endif // _posix_AtomicCount_h diff --git a/qpid/cpp/src/qpid/sys/BlockingQueue.h b/qpid/cpp/src/qpid/sys/BlockingQueue.h new file mode 100644 index 0000000000..dd709c6bff --- /dev/null +++ b/qpid/cpp/src/qpid/sys/BlockingQueue.h @@ -0,0 +1,142 @@ +#ifndef QPID_SYS_BLOCKINGQUEUE_H +#define QPID_SYS_BLOCKINGQUEUE_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. + * + */ + +#include "Waitable.h" + +#include <queue> + +namespace qpid { +namespace sys { + +/** + * A simple blocking queue template + */ +template <class T> +class BlockingQueue +{ + mutable sys::Waitable lock; + std::queue<T> queue; + bool closed; + +public: + BlockingQueue() : closed(false) {} + ~BlockingQueue() { close(); } + + /** Block until there is a value to pop */ + T pop() + { + Waitable::ScopedLock l(lock); + if (!queueWait()) throw ClosedException(); + return popInternal(); + } + + /** Non-blocking pop. If there is a value set outValue and return + * true, else return false; + */ + bool tryPop(T& outValue) { + Waitable::ScopedLock l(lock); + if (queue.empty()) return false; + outValue = popInternal(); + return true; + } + + /** Non-blocking pop. If there is a value return it, else return + * valueIfEmpty. + */ + T tryPop(const T& valueIfEmpty=T()) { + T result=valueIfEmpty; + tryPop(result); + return result; + } + + /** Push a value onto the queue */ + void push(const T& t) + { + Waitable::ScopedLock l(lock); + queue.push(t); + queueNotify(0); + } + + /** + * Close the queue. Throws ClosedException in threads waiting in pop(). + * Blocks till all waiting threads have been notified. + */ + void close() + { + Waitable::ScopedLock l(lock); + if (!closed) { + closed = true; + lock.notifyAll(); + lock.waitWaiters(); // Ensure no threads are still waiting. + } + } + + /** Open a closed queue. */ + void open() { + Waitable::ScopedLock l(lock); + closed=false; + } + + bool isClosed() const { + Waitable::ScopedLock l(lock); + return closed; + } + + bool empty() const { + Waitable::ScopedLock l(lock); + return queue.empty(); + } + size_t size() const { + Waitable::ScopedLock l(lock); + return queue.size(); + } + + private: + + void queueNotify(size_t ignore) { + if (!queue.empty() && lock.hasWaiters()>ignore) + lock.notify(); // Notify another waiter. + } + + bool queueWait() { + Waitable::ScopedWait w(lock); + while (!closed && queue.empty()) + lock.wait(); + return !queue.empty(); + } + + T popInternal() { + T t=queue.front(); + queue.pop(); + queueNotify(1); + return t; + } + +}; + +}} + + + +#endif /*!QPID_SYS_BLOCKINGQUEUE_H*/ diff --git a/qpid/cpp/src/qpid/sys/Condition.h b/qpid/cpp/src/qpid/sys/Condition.h new file mode 100644 index 0000000000..961c15e1ee --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Condition.h @@ -0,0 +1,31 @@ +#ifndef _sys_Condition_h +#define _sys_Condition_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. + * + */ + +#ifdef USE_APR_PLATFORM +#include "apr/Condition.h" +#else +#include "posix/Condition.h" +#endif + +#endif /*!_sys_Condition_h*/ diff --git a/qpid/cpp/src/qpid/sys/ConnectionCodec.h b/qpid/cpp/src/qpid/sys/ConnectionCodec.h new file mode 100644 index 0000000000..205596c709 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/ConnectionCodec.h @@ -0,0 +1,80 @@ +#ifndef QPID_SYS_CONNECTION_CODEC_H +#define QPID_SYS_CONNECTION_CODEC_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. + * + */ +#include "qpid/framing/ProtocolVersion.h" +#include "OutputControl.h" +#include <memory> +#include <map> + +namespace qpid { + +namespace broker { class Broker; } + +namespace sys { + +/** + * Interface of coder/decoder for a connection of a specific protocol + * version. + */ +class ConnectionCodec { + public: + virtual ~ConnectionCodec() {} + + /** Decode from buffer, return number of bytes decoded. + * @return may be less than size if there was incomplete + * data at the end of the buffer. + */ + virtual size_t decode(const char* buffer, size_t size) = 0; + + + /** Encode into buffer, return number of bytes encoded */ + virtual size_t encode(const char* buffer, size_t size) = 0; + + /** Return true if we have data to encode */ + virtual bool canEncode() = 0; + + /** Network connection was closed from other end. */ + virtual void closed() = 0; + + virtual bool isClosed() const = 0; + + virtual framing::ProtocolVersion getVersion() const = 0; + + struct Factory { + virtual ~Factory() {} + + /** Return 0 if version unknown */ + virtual ConnectionCodec* create( + framing::ProtocolVersion, OutputControl&, const std::string& id + ) = 0; + + /** Return "preferred" codec for outbound connections. */ + virtual ConnectionCodec* create( + OutputControl&, const std::string& id + ) = 0; + }; +}; + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_CONNECTION_CODEC_H*/ diff --git a/qpid/cpp/src/qpid/sys/ConnectionInputHandler.h b/qpid/cpp/src/qpid/sys/ConnectionInputHandler.h new file mode 100644 index 0000000000..a2c18d6d9a --- /dev/null +++ b/qpid/cpp/src/qpid/sys/ConnectionInputHandler.h @@ -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. + * + */ +#ifndef _ConnectionInputHandler_ +#define _ConnectionInputHandler_ + +#include "qpid/framing/InputHandler.h" +#include "OutputTask.h" +#include "TimeoutHandler.h" + +namespace qpid { +namespace sys { + + class ConnectionInputHandler : + public qpid::framing::InputHandler, + public TimeoutHandler, public OutputTask + { + public: + virtual void closed() = 0; + }; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/sys/ConnectionInputHandlerFactory.h b/qpid/cpp/src/qpid/sys/ConnectionInputHandlerFactory.h new file mode 100644 index 0000000000..2b309b5758 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/ConnectionInputHandlerFactory.h @@ -0,0 +1,53 @@ +/* + * + * 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. + * + */ +#ifndef _ConnectionInputHandlerFactory_ +#define _ConnectionInputHandlerFactory_ + +#include <boost/noncopyable.hpp> +#include <string> + +namespace qpid { +namespace sys { + +class ConnectionOutputHandler; +class ConnectionInputHandler; + +/** + * Callback interface used by the Acceptor to + * create a ConnectionInputHandler for each new connection. + */ +class ConnectionInputHandlerFactory : private boost::noncopyable +{ + public: + /** + *@param out handler for connection output. + *@param id identify the connection for management purposes. + */ + virtual ConnectionInputHandler* create(ConnectionOutputHandler* out, + const std::string& id) = 0; + + virtual ~ConnectionInputHandlerFactory(){} +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/sys/ConnectionOutputHandler.h b/qpid/cpp/src/qpid/sys/ConnectionOutputHandler.h new file mode 100644 index 0000000000..5a60ae4998 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/ConnectionOutputHandler.h @@ -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. + * + */ +#ifndef _ConnectionOutputHandler_ +#define _ConnectionOutputHandler_ + +#include "qpid/framing/OutputHandler.h" +#include "OutputControl.h" + +namespace qpid { +namespace sys { + +/** + * Provides the output handler associated with a connection. + */ +class ConnectionOutputHandler : public virtual qpid::framing::OutputHandler, public OutputControl +{ + public: + virtual void close() = 0; +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/sys/DeletionManager.h b/qpid/cpp/src/qpid/sys/DeletionManager.h new file mode 100644 index 0000000000..43154eb98e --- /dev/null +++ b/qpid/cpp/src/qpid/sys/DeletionManager.h @@ -0,0 +1,138 @@ +#ifndef _sys_DeletionManager_h +#define _sys_DeletionManager_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. + * + */ + +#include <vector> +#include <algorithm> +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace sys { + +struct deleter +{ + template <typename T> + void operator()(T* ptr){ delete ptr;} +}; + +/** + * DeletionManager keeps track of handles that need to be deleted but may still be + * in use by one of the threads concurrently. + * + * The mode of operation is like this: + * - When we want to delete but we might still be using the handle we + * * Transfer ownership of the handle to this class + * * Mark the handle as (potentially) in use by every thread + * - Then subsequently at points where the thread code knows it isn't + * using any handles it declares that it is using no handles + * - When the last thread declares no use of a handle it automatically + * gets deleted by the shared_ptr implementation + * + * The class only has static members and data and so can only be used once for + * any particular handle type + */ +template <typename H> +class DeletionManager +{ +public: + // Mark every thread as using the handle - it will be deleted + // below after every thread marks the handle as unused + static void markForDeletion(H* handle) { + allThreadsStatuses.addHandle(shared_ptr(handle)); + } + + // Mark this thread is not using any handle - + // handles get deleted here when no one else + // is using them either + static void markAllUnusedInThisThread() { + static __thread ThreadStatus* threadStatus = 0; + + // Thread local vars can't be dynamically constructed so we need + // to check whether we've made it yet and construct it if not + // (no locking necessary for the check as it's thread local!) + if (!threadStatus) { + threadStatus = new ThreadStatus; + allThreadsStatuses.addThreadStatus(threadStatus); + } + + ScopedLock<Mutex> l(threadStatus->lock); + + // The actual deletions will happen here when all the shared_ptr + // ref counts hit 0 (that is when every thread marks the handle unused) + threadStatus->handles.clear(); + } + +private: + typedef boost::shared_ptr<H> shared_ptr; + + // In theory we know that we never need more handles than the number of + // threads runnning so we could use a fixed size array. However at this point + // in the code we don't have easy access to this information. + struct ThreadStatus + { + Mutex lock; + std::vector<shared_ptr> handles; + }; + + class AllThreadsStatuses + { + Mutex lock; + std::vector<ThreadStatus*> statuses; + + struct handleAdder + { + shared_ptr handle; + + handleAdder(shared_ptr h): handle(h) {} + + void operator()(ThreadStatus* ptr) { + ScopedLock<Mutex> l(ptr->lock); + ptr->handles.push_back(handle); + } + }; + + public: + // Need this to be able to do static initialisation + explicit AllThreadsStatuses(int) {} + + ~AllThreadsStatuses() { + ScopedLock<Mutex> l(lock); + std::for_each(statuses.begin(), statuses.end(), deleter()); + } + + void addThreadStatus(ThreadStatus* t) { + ScopedLock<Mutex> l(lock); + statuses.push_back(t); + } + + void addHandle(shared_ptr h) { + ScopedLock<Mutex> l(lock); + std::for_each(statuses.begin(), statuses.end(), handleAdder(h)); + } + }; + + static AllThreadsStatuses allThreadsStatuses; +}; + +}} +#endif // _sys_DeletionManager_h diff --git a/qpid/cpp/src/qpid/sys/Dispatcher.cpp b/qpid/cpp/src/qpid/sys/Dispatcher.cpp new file mode 100644 index 0000000000..c55f808b42 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Dispatcher.cpp @@ -0,0 +1,441 @@ +/* + * + * 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. + * + */ + +#include "Dispatcher.h" + +#include <boost/cast.hpp> + +#include <assert.h> + +namespace qpid { +namespace sys { + +Dispatcher::Dispatcher(Poller::shared_ptr poller0) : + poller(poller0) { +} + +Dispatcher::~Dispatcher() { +} + +void Dispatcher::run() { + do { + Poller::Event event = poller->wait(); + DispatchHandle* h = + boost::polymorphic_downcast<DispatchHandle*>(event.handle); + + // If can read/write then dispatch appropriate callbacks + if (h) { + h->dispatchCallbacks(event.type); + } else { + // Handle shutdown + switch (event.type) { + case Poller::SHUTDOWN: + goto dispatcher_shutdown; + default: + // This should be impossible + assert(false); + } + } + } while (true); + +dispatcher_shutdown: + ; +} + +DispatchHandle::~DispatchHandle() { + stopWatch(); +} + +void DispatchHandle::startWatch(Poller::shared_ptr poller0) { + bool r = readableCallback; + bool w = writableCallback; + + ScopedLock<Mutex> lock(stateLock); + assert(state == IDLE); + + // If no callbacks set then do nothing (that is what we were asked to do!) + // TODO: Maybe this should be an assert instead + if (!r && !w) { + state = INACTIVE; + return; + } + + Poller::Direction d = r ? + (w ? Poller::INOUT : Poller::IN) : + Poller::OUT; + + poller = poller0; + poller->addFd(*this, d); + + state = r ? + (w ? ACTIVE_RW : ACTIVE_R) : + ACTIVE_W; +} + +void DispatchHandle::rewatch() { + bool r = readableCallback; + bool w = writableCallback; + + ScopedLock<Mutex> lock(stateLock); + switch(state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_R: + case DELAYED_W: + case DELAYED_INACTIVE: + state = r ? + (w ? DELAYED_RW : DELAYED_R) : + DELAYED_W; + break; + case DELAYED_DELETE: + break; + case INACTIVE: + case ACTIVE_R: + case ACTIVE_W: { + assert(poller); + Poller::Direction d = r ? + (w ? Poller::INOUT : Poller::IN) : + Poller::OUT; + poller->modFd(*this, d); + state = r ? + (w ? ACTIVE_RW : ACTIVE_R) : + ACTIVE_W; + break; + } + case DELAYED_RW: + case ACTIVE_RW: + // Don't need to do anything already waiting for readable/writable + break; + } +} + +void DispatchHandle::rewatchRead() { + if (!readableCallback) { + return; + } + + ScopedLock<Mutex> lock(stateLock); + switch(state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_R: + case DELAYED_RW: + case DELAYED_DELETE: + break; + case DELAYED_W: + state = DELAYED_RW; + break; + case DELAYED_INACTIVE: + state = DELAYED_R; + break; + case ACTIVE_R: + case ACTIVE_RW: + // Nothing to do: already waiting for readable + break; + case INACTIVE: + assert(poller); + poller->modFd(*this, Poller::IN); + state = ACTIVE_R; + break; + case ACTIVE_W: + assert(poller); + poller->modFd(*this, Poller::INOUT); + state = ACTIVE_RW; + break; + } +} + +void DispatchHandle::rewatchWrite() { + if (!writableCallback) { + return; + } + + ScopedLock<Mutex> lock(stateLock); + switch(state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_W: + case DELAYED_RW: + case DELAYED_DELETE: + break; + case DELAYED_R: + state = DELAYED_RW; + break; + case DELAYED_INACTIVE: + state = DELAYED_W; + break; + case INACTIVE: + assert(poller); + poller->modFd(*this, Poller::OUT); + state = ACTIVE_W; + break; + case ACTIVE_R: + assert(poller); + poller->modFd(*this, Poller::INOUT); + state = ACTIVE_RW; + break; + case ACTIVE_W: + case ACTIVE_RW: + // Nothing to do: already waiting for writable + break; + } +} + +void DispatchHandle::unwatchRead() { + if (!readableCallback) { + return; + } + + ScopedLock<Mutex> lock(stateLock); + switch(state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_R: + state = DELAYED_INACTIVE; + break; + case DELAYED_RW: + state = DELAYED_W; + break; + case DELAYED_W: + case DELAYED_INACTIVE: + case DELAYED_DELETE: + break; + case ACTIVE_R: + assert(poller); + poller->modFd(*this, Poller::NONE); + state = INACTIVE; + break; + case ACTIVE_RW: + assert(poller); + poller->modFd(*this, Poller::OUT); + state = ACTIVE_W; + break; + case ACTIVE_W: + case INACTIVE: + break; + } +} + +void DispatchHandle::unwatchWrite() { + if (!writableCallback) { + return; + } + + ScopedLock<Mutex> lock(stateLock); + switch(state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_W: + state = DELAYED_INACTIVE; + break; + case DELAYED_RW: + state = DELAYED_R; + break; + case DELAYED_R: + case DELAYED_INACTIVE: + case DELAYED_DELETE: + break; + case ACTIVE_W: + assert(poller); + poller->modFd(*this, Poller::NONE); + state = INACTIVE; + break; + case ACTIVE_RW: + assert(poller); + poller->modFd(*this, Poller::IN); + state = ACTIVE_R; + break; + case ACTIVE_R: + case INACTIVE: + break; + } +} + +void DispatchHandle::unwatch() { + ScopedLock<Mutex> lock(stateLock); + switch (state) { + case IDLE: + case DELAYED_IDLE: + break; + case DELAYED_R: + case DELAYED_W: + case DELAYED_RW: + case DELAYED_INACTIVE: + state = DELAYED_INACTIVE; + break; + case DELAYED_DELETE: + break; + default: + assert(poller); + poller->modFd(*this, Poller::NONE); + state = INACTIVE; + break; + } +} + +void DispatchHandle::stopWatch() { + ScopedLock<Mutex> lock(stateLock); + switch (state) { + case IDLE: + case DELAYED_IDLE: + case DELAYED_DELETE: + return; + case DELAYED_R: + case DELAYED_W: + case DELAYED_RW: + case DELAYED_INACTIVE: + state = DELAYED_IDLE; + break; + default: + state = IDLE; + break; + } + assert(poller); + poller->delFd(*this); + poller.reset(); +} + +// The slightly strange switch structure +// is to ensure that the lock is released before +// we do the delete +void DispatchHandle::doDelete() { + // Ensure that we're no longer watching anything + stopWatch(); + + // If we're in the middle of a callback defer the delete + { + ScopedLock<Mutex> lock(stateLock); + switch (state) { + case DELAYED_IDLE: + case DELAYED_DELETE: + state = DELAYED_DELETE; + return; + case IDLE: + break; + default: + // Can only get out of stopWatch() in DELAYED_IDLE/DELAYED_DELETE/IDLE states + assert(false); + } + } + // If we're not then do it right away + deferDelete(); +} + +void DispatchHandle::dispatchCallbacks(Poller::EventType type) { + // Note that we are now doing the callbacks + { + ScopedLock<Mutex> lock(stateLock); + + // Set up to wait for same events next time unless reset + switch(state) { + case ACTIVE_R: + state = DELAYED_R; + break; + case ACTIVE_W: + state = DELAYED_W; + break; + case ACTIVE_RW: + state = DELAYED_RW; + break; + // Can only get here in a DELAYED_* state in the rare case + // that we're already here for reading and we get activated for + // writing and we can write (it might be possible the other way + // round too). In this case we're already processing the handle + // in a different thread in this function so return right away + case DELAYED_R: + case DELAYED_W: + case DELAYED_RW: + case DELAYED_INACTIVE: + case DELAYED_IDLE: + case DELAYED_DELETE: + return; + default: + assert(false); + } + } + + // Do callbacks - whilst we are doing the callbacks we are prevented from processing + // the same handle until we re-enable it. To avoid rentering the callbacks for a single + // handle re-enabling in the callbacks is actually deferred until they are complete. + switch (type) { + case Poller::READABLE: + readableCallback(*this); + break; + case Poller::WRITABLE: + writableCallback(*this); + break; + case Poller::READ_WRITABLE: + readableCallback(*this); + writableCallback(*this); + break; + case Poller::DISCONNECTED: + { + ScopedLock<Mutex> lock(stateLock); + state = DELAYED_INACTIVE; + } + if (disconnectedCallback) { + disconnectedCallback(*this); + } + break; + default: + assert(false); + } + + // If any of the callbacks re-enabled reading/writing then actually + // do it now + { + ScopedLock<Mutex> lock(stateLock); + switch (state) { + case DELAYED_R: + poller->modFd(*this, Poller::IN); + state = ACTIVE_R; + return; + case DELAYED_W: + poller->modFd(*this, Poller::OUT); + state = ACTIVE_W; + return; + case DELAYED_RW: + poller->modFd(*this, Poller::INOUT); + state = ACTIVE_RW; + return; + case DELAYED_INACTIVE: + state = INACTIVE; + return; + case DELAYED_IDLE: + state = IDLE; + return; + default: + // This should be impossible + assert(false); + return; + case DELAYED_DELETE: + break; + } + } + deferDelete(); +} + +}} diff --git a/qpid/cpp/src/qpid/sys/Dispatcher.h b/qpid/cpp/src/qpid/sys/Dispatcher.h new file mode 100644 index 0000000000..7cc4873068 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Dispatcher.h @@ -0,0 +1,96 @@ +#ifndef _sys_Dispatcher_h +#define _sys_Dispatcher_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. + * + */ + +#include "Poller.h" +#include "Runnable.h" +#include "Mutex.h" + +#include <memory> +#include <queue> +#include <boost/function.hpp> + +#include <assert.h> + + +namespace qpid { +namespace sys { + +class Dispatcher; +class DispatchHandle : public PollerHandle { + friend class Dispatcher; +public: + typedef boost::function1<void, DispatchHandle&> Callback; + +private: + Callback readableCallback; + Callback writableCallback; + Callback disconnectedCallback; + Poller::shared_ptr poller; + Mutex stateLock; + enum { + IDLE, INACTIVE, ACTIVE_R, ACTIVE_W, ACTIVE_RW, + DELAYED_IDLE, DELAYED_INACTIVE, DELAYED_R, DELAYED_W, DELAYED_RW, + DELAYED_DELETE + } state; + +public: + DispatchHandle(const Socket& s, Callback rCb, Callback wCb, Callback dCb) : + PollerHandle(s), + readableCallback(rCb), + writableCallback(wCb), + disconnectedCallback(dCb), + state(IDLE) + {} + + ~DispatchHandle(); + + void startWatch(Poller::shared_ptr poller); + void rewatch(); + void rewatchRead(); + void rewatchWrite(); + void unwatch(); + void unwatchRead(); + void unwatchWrite(); + void stopWatch(); + +protected: + void doDelete(); + +private: + void dispatchCallbacks(Poller::EventType dir); +}; + +class Dispatcher : public Runnable { + const Poller::shared_ptr poller; + +public: + Dispatcher(Poller::shared_ptr poller); + ~Dispatcher(); + + void run(); +}; + +}} + +#endif // _sys_Dispatcher_h diff --git a/qpid/cpp/src/qpid/sys/Module.h b/qpid/cpp/src/qpid/sys/Module.h new file mode 100644 index 0000000000..79793ed0ca --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Module.h @@ -0,0 +1,50 @@ +#ifndef QPID_SYS_MODULE_H +#define QPID_SYS_MODULE_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. + * + */ + +#include "qpid/sys/Shlib.h" +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace sys { + +template <class T> class Module : public AutoShlib, private boost::noncopyable +{ + public: + Module(const std::string& name) : + AutoShlib(name), + ptr(getSymbol<T*(*)()>("create")()) {} + + T* get() { return ptr; } + T* operator->() { return ptr; } + ~Module() throw() { + getSymbol<void (*)(T*)>("destroy")(ptr); + } + + private: + T* ptr; +}; + +}} + +#endif /*!QPID_SYS_MODULE_H*/ diff --git a/qpid/cpp/src/qpid/sys/Monitor.h b/qpid/cpp/src/qpid/sys/Monitor.h new file mode 100644 index 0000000000..1d9835675c --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Monitor.h @@ -0,0 +1,51 @@ +#ifndef _sys_Monitor_h +#define _sys_Monitor_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. + * + */ + +#include <sys/errno.h> +#include "Condition.h" + +namespace qpid { +namespace sys { + +/** + * A monitor is a condition variable and a mutex + */ +class Monitor : public Mutex, public Condition { + public: + using Condition::wait; + inline void wait(); + inline bool wait(const AbsTime& absoluteTime); +}; + + +void Monitor::wait() { + Condition::wait(*this); +} + +bool Monitor::wait(const AbsTime& absoluteTime) { + return Condition::wait(*this, absoluteTime); +} + +}} +#endif /*!_sys_Monitor_h*/ diff --git a/qpid/cpp/src/qpid/sys/Mutex.h b/qpid/cpp/src/qpid/sys/Mutex.h new file mode 100644 index 0000000000..b4bd3a9b4a --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Mutex.h @@ -0,0 +1,89 @@ +#ifndef _sys_Mutex_h +#define _sys_Mutex_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +namespace qpid { +namespace sys { + +/** + * Scoped lock template: calls lock() in ctor, unlock() in dtor. + * L can be any class with lock() and unlock() functions. + */ +template <class L> +class ScopedLock +{ + public: + ScopedLock(L& l) : mutex(l) { l.lock(); } + ~ScopedLock() { mutex.unlock(); } + private: + L& mutex; +}; + +template <class L> +class ScopedUnlock +{ + public: + ScopedUnlock(L& l) : mutex(l) { l.unlock(); } + ~ScopedUnlock() { mutex.lock(); } + private: + L& mutex; +}; + +template <class L> +class ScopedRlock +{ + public: + ScopedRlock(L& l) : mutex(l) { l.rlock(); } + ~ScopedRlock() { mutex.unlock(); } + private: + L& mutex; +}; + +template <class L> +class ScopedWlock +{ + public: + ScopedWlock(L& l) : mutex(l) { l.wlock(); } + ~ScopedWlock() { mutex.unlock(); } + private: + L& mutex; +}; + +template <class L> +class ConditionalScopedLock +{ + public: + ConditionalScopedLock(L& l) : mutex(l) { acquired = l.trylock(); } + ~ConditionalScopedLock() { if (acquired) mutex.unlock(); } + bool lockAcquired() { return acquired; } + private: + L& mutex; + bool acquired; +}; + +}} + +#ifdef USE_APR_PLATFORM +#include "apr/Mutex.h" +#else +#include "posix/Mutex.h" +#endif + +#endif /*!_sys_Mutex_h*/ diff --git a/qpid/cpp/src/qpid/sys/OutputControl.h b/qpid/cpp/src/qpid/sys/OutputControl.h new file mode 100644 index 0000000000..d922a0d85c --- /dev/null +++ b/qpid/cpp/src/qpid/sys/OutputControl.h @@ -0,0 +1,38 @@ +/* + * + * 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. + * + */ +#ifndef _OutputControl_ +#define _OutputControl_ + +namespace qpid { +namespace sys { + + class OutputControl + { + public: + virtual ~OutputControl() {} + virtual void activateOutput() = 0; + }; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/sys/OutputTask.h b/qpid/cpp/src/qpid/sys/OutputTask.h new file mode 100644 index 0000000000..109765b8c3 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/OutputTask.h @@ -0,0 +1,38 @@ +/* + * + * 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. + * + */ +#ifndef _OutputTask_ +#define _OutputTask_ + +namespace qpid { +namespace sys { + + class OutputTask + { + public: + virtual ~OutputTask() {} + virtual bool doOutput() = 0; + }; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/sys/Poller.h b/qpid/cpp/src/qpid/sys/Poller.h new file mode 100644 index 0000000000..0d6b4f9308 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Poller.h @@ -0,0 +1,110 @@ +#ifndef _sys_Poller_h +#define _sys_Poller_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. + * + */ + +#include "Time.h" +#include "Socket.h" + +#include <stdint.h> + +#include <boost/shared_ptr.hpp> + +namespace qpid { +namespace sys { + +/** + * Handle class to use for polling + */ +class Poller; +class PollerHandlePrivate; +class PollerHandle { + friend class Poller; + + PollerHandlePrivate* const impl; + const Socket& socket; + +public: + PollerHandle(const Socket& s); + + // Usual way to delete (will defer deletion until we + // can't be returned from a Poller::wait any more) + void deferDelete(); + + // Class clients shouldn't ever use this + virtual ~PollerHandle(); + + const Socket& getSocket() const {return socket;} +}; + +/** + * Poller: abstract class to encapsulate a file descriptor poll to be used + * by a reactor + */ +class PollerPrivate; +class Poller { + PollerPrivate* const impl; + +public: + typedef boost::shared_ptr<Poller> shared_ptr; + + enum Direction { + NONE = 0, + IN, + OUT, + INOUT + }; + + enum EventType { + INVALID = 0, + READABLE, + WRITABLE, + READ_WRITABLE, + DISCONNECTED, + SHUTDOWN, + TIMEOUT + }; + + struct Event { + PollerHandle* handle; + EventType type; + + Event(PollerHandle* handle0, EventType type0) : + handle(handle0), + type(type0) { + } + }; + + Poller(); + ~Poller(); + /** Note: this function is async-signal safe */ + void shutdown(); + + void addFd(PollerHandle& handle, Direction dir); + void delFd(PollerHandle& handle); + void modFd(PollerHandle& handle, Direction dir); + void rearmFd(PollerHandle& handle); + Event wait(Duration timeout = TIME_INFINITE); +}; + +}} +#endif // _sys_Poller_h diff --git a/qpid/cpp/src/qpid/sys/Runnable.cpp b/qpid/cpp/src/qpid/sys/Runnable.cpp new file mode 100644 index 0000000000..30122c682f --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Runnable.cpp @@ -0,0 +1,32 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "Runnable.h" +#include <boost/bind.hpp> + +namespace qpid { +namespace sys { + +Runnable::~Runnable() {} + +Runnable::Functor Runnable::functor() +{ + return boost::bind(&Runnable::run, this); +} + +}} diff --git a/qpid/cpp/src/qpid/sys/Runnable.h b/qpid/cpp/src/qpid/sys/Runnable.h new file mode 100644 index 0000000000..fb3927c612 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Runnable.h @@ -0,0 +1,50 @@ +#ifndef _Runnable_ +#define _Runnable_ +/* + * + * 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. + * + */ + +#include <boost/function.hpp> + +namespace qpid { +namespace sys { + +/** + * Interface for objects that can be run, e.g. in a thread. + */ +class Runnable +{ + public: + /** Type to represent a runnable as a Functor */ + typedef boost::function0<void> Functor; + + virtual ~Runnable(); + + /** Derived classes override run(). */ + virtual void run() = 0; + + /** Create a functor object that will call this->run(). */ + Functor functor(); +}; + +}} + + +#endif diff --git a/qpid/cpp/src/qpid/sys/ScopedIncrement.h b/qpid/cpp/src/qpid/sys/ScopedIncrement.h new file mode 100644 index 0000000000..8645ab2484 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/ScopedIncrement.h @@ -0,0 +1,67 @@ +#ifndef _posix_ScopedIncrement_h +#define _posix_ScopedIncrement_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include <boost/noncopyable.hpp> +#include <boost/function.hpp> + +namespace qpid { +namespace sys { + +/** + * Increment counter in constructor and decrement in destructor. + * Optionally call a function if the decremented counter value is 0. + * Note the function must not throw, it is called in the destructor. + */ +template <class T, class F=boost::function<void()> > +class ScopedIncrement : boost::noncopyable +{ + public: + ScopedIncrement(T& c, F f=0) + : count(c), callback(f) { ++count; } + ~ScopedIncrement() { if (--count == 0 && callback) callback(); } + + private: + T& count; + F callback; +}; + + +/** Decrement counter in constructor and increment in destructor. */ +template <class T> +class ScopedDecrement : boost::noncopyable +{ + public: + ScopedDecrement(T& c) : count(c) { value = --count; } + ~ScopedDecrement() { ++count; } + + /** Return the value after the decrement. */ + operator long() { return value; } + + private: + T& count; + long value; +}; + + +}} + + +#endif // _posix_ScopedIncrement_h diff --git a/qpid/cpp/src/qpid/sys/Semaphore.h b/qpid/cpp/src/qpid/sys/Semaphore.h new file mode 100644 index 0000000000..3efb7ce2df --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Semaphore.h @@ -0,0 +1,67 @@ +#ifndef _sys_Semaphore_h +#define _sys_Semaphore_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "Monitor.h" + +namespace qpid { +namespace sys { + +class Semaphore +{ +public: + Semaphore(uint c = 1) : count(c) {} + + void lock() { acquire(); } + void unlock() { release(); } + bool trylock() { return tryAcquire(); } + + bool tryAcquire() + { + Monitor::ScopedLock l(monitor); + if (count) { + count--; + return true; + } else { + return false; + } + } + + void acquire() + { + Monitor::ScopedLock l(monitor); + while (count == 0) monitor.wait(); + count--; + } + + void release() + { + Monitor::ScopedLock l(monitor); + if (!count++) monitor.notifyAll(); + } + +private: + Monitor monitor; + uint count; +}; + +}} + +#endif /*!_sys_Semaphore_h*/ diff --git a/qpid/cpp/src/qpid/sys/Serializer.cpp b/qpid/cpp/src/qpid/sys/Serializer.cpp new file mode 100644 index 0000000000..86f901aa78 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Serializer.cpp @@ -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. + * + */ + +#include "qpid/sys/Serializer.h" +#include "qpid/log/Statement.h" + +#include <boost/bind.hpp> + +#include <assert.h> + +namespace qpid { +namespace sys { + +SerializerBase::SerializerBase(bool allowImmediate) + : state(IDLE), immediate(allowImmediate) {} + +void SerializerBase::shutdown() { + { + Mutex::ScopedLock l(lock); + if (state == SHUTDOWN) return; + state = SHUTDOWN; + lock.notify(); + } + if (worker.id() != 0) + worker.join(); +} + +void SerializerBase::notifyWorker() { + // Call with lock held. + if (!worker.id()) + worker = Thread(*this); + else + lock.notify(); +} + +bool SerializerBase::running() { + Mutex::ScopedLock l(lock); + return state != SHUTDOWN; +} + +void SerializerBase::wait() { + Mutex::ScopedLock l(lock); + if (state == IDLE) lock.wait(); +} + +void SerializerBase::run() { + while (running()) { + dispatch(); + wait(); + } +} + +}} // namespace qpid::sys diff --git a/qpid/cpp/src/qpid/sys/Serializer.h b/qpid/cpp/src/qpid/sys/Serializer.h new file mode 100644 index 0000000000..fe4afc85cb --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Serializer.h @@ -0,0 +1,181 @@ +#ifndef SERIALIZER_H +#define SERIALIZER_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. + * + */ + +#include "qpid/Exception.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Monitor.h" +#include "qpid/sys/Thread.h" + +#include <boost/function.hpp> +#include <boost/noncopyable.hpp> + +#include <deque> + +namespace qpid { +namespace sys { + +/** Abstract base class for Serializer below. */ +class SerializerBase : private boost::noncopyable, private Runnable +{ + public: + typedef boost::function<void()> VoidFn0; + struct ShutdownException : public Exception {}; + + /** @see Serializer::Serializer */ + SerializerBase(bool immediate=true); + + virtual ~SerializerBase() { shutdown(); } + + virtual void dispatch() = 0; + protected: + enum State { + IDLE, ///< No threads are active. + EXECUTING, ///< execute() is executing a single task. + DISPATCHING, ///< dispatch() is draining the queue. + SHUTDOWN ///< SerailizerBase is being destroyed. + }; + + void shutdown(); + void notifyWorker(); + void run(); + virtual bool empty() = 0; + bool running(); + void wait(); + + Monitor lock; + State state; + bool immediate; + Thread worker; +}; + + +/** + * Execute tasks sequentially, queuing tasks when necessary to + * ensure only one thread at a time executes a task and tasks + * are executed in order. + * + * Task is a void returning 0-arg functor. It must not throw exceptions. + * + * Note we deliberately do not use boost::function as the task type + * because copying a boost::functor allocates the target object on the + * heap. + */ +template <class Task> +class Serializer : public SerializerBase { + + std::deque<Task> queue; + + bool empty() { return queue.empty(); } + void dispatch(Task& task); + + public: + /** Start a serializer. + * + * @param immediate Allow execute() to execute a task immediatly + * in the current thread. + */ + Serializer(bool immediate=true) + : SerializerBase(immediate) {} + + ~Serializer() { shutdown(); } + /** + * Task may be executed immediately in the calling thread if there + * are no other tasks pending or executing and the "immediate" + * paramater to the constructor was true. Otherwise task will be + * enqueued for execution by a dispatch thread. + */ + void execute(Task& task); + + + /** Execute pending tasks sequentially in calling thread. + * Drains the task queue and returns, does not block for more tasks. + * + * @exception ShutdownException if the serializer is being destroyed. + */ + void dispatch(); + }; + + +template <class Task> +void Serializer<Task>::execute(Task& task) { + Mutex::ScopedLock l(lock); + assert(state != SHUTDOWN); + if (immediate && state == IDLE) { + state = EXECUTING; + dispatch(task); + if (state != SHUTDOWN) { + assert(state == EXECUTING); + state = IDLE; + } + } + else + queue.push_back(task); + if (!queue.empty() && state == IDLE) { + state = DISPATCHING; + notifyWorker(); + } +} + +template <class Task> +void Serializer<Task>::dispatch() { + Mutex::ScopedLock l(lock); + // TODO aconway 2007-07-16: This loop could be unbounded + // if other threads add work while we're in dispatch(Task&). + // If we need to bound it we could dispatch just the elements + // that were enqueued when dispatch() was first called - save + // begin() iterator and pop only up to that. + while (!queue.empty() && state != SHUTDOWN) { + assert(state == DISPATCHING); + dispatch(queue.front()); + queue.pop_front(); + } + if (state != SHUTDOWN) { + assert(state == DISPATCHING); + state = IDLE; + } +} + +template <class Task> +void Serializer<Task>::dispatch(Task& task) { + // Preconditions: lock is held, state is EXECUTING or DISPATCHING + assert(state != IDLE); + assert(state != SHUTDOWN); + assert(state == EXECUTING || state == DISPATCHING); + Mutex::ScopedUnlock u(lock); + // No exceptions allowed in task. + notifyWorker(); + try { task(); } catch (...) { assert(0); } +} + + + + +}} // namespace qpid::sys + + + + + +#endif /*!SERIALIZER_H*/ diff --git a/qpid/cpp/src/qpid/sys/Shlib.cpp b/qpid/cpp/src/qpid/sys/Shlib.cpp new file mode 100644 index 0000000000..8fd3f42cc6 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Shlib.cpp @@ -0,0 +1,38 @@ +/* + * 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. + * + */ + +#include "Shlib.h" + +#include "qpid/log/Statement.h" + +namespace qpid { +namespace sys { + +AutoShlib::~AutoShlib() throw() { + try { + unload(); + } catch(const std::exception& e) { + QPID_LOG(error, "Unloading shared library: " << e.what()); + } +} + +// Note: Other functions are defined in apr/Shlib.cpp or posix/Shlib.cpp. + +}} // namespace qpid::sys diff --git a/qpid/cpp/src/qpid/sys/Shlib.h b/qpid/cpp/src/qpid/sys/Shlib.h new file mode 100644 index 0000000000..e2752dc7d6 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Shlib.h @@ -0,0 +1,76 @@ +#ifndef QPID_SYS_SHLIB_H +#define QPID_SYS_SHLIB_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. + * + */ + +#include <boost/noncopyable.hpp> +#include <iostream> +#include <dlfcn.h> + +namespace qpid { +namespace sys { + +/** Encapsulates a shared library handle. + *@see AutoShlib + */ +class Shlib { + public: + /** Load a shared library */ + Shlib(const char* libname) { load(libname); } + + /** Load a shared library */ + Shlib(const std::string& libname) { load(libname.c_str()); } + + /** Unload shared library. */ + void unload(); + + /** Look up symbol. */ + void* getSymbol(const char* symbol); + + /** Look up symbol in shared library, cast it to the desired + * pointer type, void* by default. + */ + template <class T> + T getSymbol(const char* symbol) { + // Double cast avoids warning about casting object to function pointer + return reinterpret_cast<T>(reinterpret_cast<intptr_t>( + this->getSymbol(symbol))); + } + + private: + void* handle; + void load(const char* libname); +}; + +/** A shared library handle that unloads the shlib in it's dtor */ +class AutoShlib : public Shlib { + public: + /** Load shared library */ + AutoShlib(const std::string& libname) : Shlib(libname) {} + /** Calls unload() */ + ~AutoShlib() throw(); +}; + + +}} // namespace qpid::sys + +#endif /*!QPID_SYS_SHLIB_H*/ diff --git a/qpid/cpp/src/qpid/sys/ShutdownHandler.h b/qpid/cpp/src/qpid/sys/ShutdownHandler.h new file mode 100644 index 0000000000..88baecb5b6 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/ShutdownHandler.h @@ -0,0 +1,37 @@ +/* + * + * 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. + * + */ +#ifndef _ShutdownHandler_ +#define _ShutdownHandler_ + +namespace qpid { +namespace sys { + + class ShutdownHandler + { + public: + virtual void shutdown() = 0; + virtual ~ShutdownHandler(){} + }; + +} +} + +#endif diff --git a/qpid/cpp/src/qpid/sys/Socket.h b/qpid/cpp/src/qpid/sys/Socket.h new file mode 100644 index 0000000000..0ebfc0c330 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Socket.h @@ -0,0 +1,116 @@ +#ifndef _sys_Socket_h +#define _sys_Socket_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. + * + */ +#include <string> +#include "qpid/sys/Time.h" + +struct sockaddr; + +namespace qpid { +namespace sys { + +class SocketPrivate; +class Socket +{ + friend class Poller; + + SocketPrivate* const impl; + +public: + /** Create a socket wrapper for descriptor. */ + Socket(); + ~Socket(); + + /** Create an initialized TCP socket */ + void createTcp() const; + + /** Set timeout for read and write */ + void setTimeout(const Duration& interval) const; + + /** Set socket non blocking */ + void setNonblocking() const; + + void connect(const std::string& host, int port) const; + + void close() const; + + enum { SOCKET_TIMEOUT=-2, SOCKET_EOF=-3 } ErrorCode; + + /** Returns bytes sent or an ErrorCode value < 0. */ + ssize_t send(const void* data, size_t size) const; + + /** + * Returns bytes received, an ErrorCode value < 0 or 0 + * if the connection closed in an orderly manner. + */ + ssize_t recv(void* data, size_t size) const; + + /** Bind to a port and start listening. + *@param port 0 means choose an available port. + *@param backlog maximum number of pending connections. + *@return The bound port. + */ + int listen(int port = 0, int backlog = 10) const; + + /** Returns the "socket name" ie the address bound to + * the near end of the socket + */ + std::string getSockname() const; + + /** Returns the "peer name" ie the address bound to + * the remote end of the socket + */ + std::string getPeername() const; + + /** + * Returns an address (host and port) for the remote end of the + * socket + */ + std::string getPeerAddress() const; + /** + * Returns an address (host and port) for the local end of the + * socket + */ + std::string getLocalAddress() const; + + uint16_t getLocalPort() const; + uint16_t getRemotePort() const; + + + /** Accept a connection from a socket that is already listening + * and has an incoming connection + */ + Socket* accept(struct sockaddr *addr, socklen_t *addrlen) const; + + // TODO The following are raw operations, maybe they need better wrapping? + int read(void *buf, size_t count) const; + int write(const void *buf, size_t count) const; + + int toFd() const; + +private: + Socket(SocketPrivate*); +}; + +}} +#endif /*!_sys_Socket_h*/ diff --git a/qpid/cpp/src/qpid/sys/StateMonitor.h b/qpid/cpp/src/qpid/sys/StateMonitor.h new file mode 100644 index 0000000000..5a92756f3a --- /dev/null +++ b/qpid/cpp/src/qpid/sys/StateMonitor.h @@ -0,0 +1,78 @@ +#ifndef QPID_SYS_STATEMONITOR_H +#define QPID_SYS_STATEMONITOR_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. + * + */ +#include "qpid/sys/Waitable.h" + +#include <bitset> + +namespace qpid { +namespace sys { + +/** + * A monitor with an enum state value. + * + *@param Enum: enum type to use for states. + *@param EnumMax: Highest enum value. + */ +template <class Enum, size_t MaxEnum> +class StateMonitor : public Waitable +{ + public: + struct Set : public std::bitset<MaxEnum + 1> { + Set() {} + Set(Enum s) { set(s); } + Set(Enum s, Enum t) { set(s).set(t); } + Set(Enum s, Enum t, Enum u) { set(s).set(t).set(u); } + Set(Enum s, Enum t, Enum u, Enum v) { set(s).set(t).set(u).set(v); } + }; + + + StateMonitor(Enum initial) { state=initial; } + + /** @pre Caller holds a ScopedLock. */ + void set(Enum s) { state=s; notifyAll(); } + /** @pre Caller holds a ScopedLock. */ + StateMonitor& operator=(Enum s) { set(s); return *this; } + + /** @pre Caller holds a ScopedLock. */ + Enum get() const { return state; } + /** @pre Caller holds a ScopedLock. */ + operator Enum() const { return state; } + + /** @pre Caller holds a ScopedLock */ + void waitFor(Enum s) { ScopedWait(*this); while (s != state) wait(); } + /** @pre Caller holds a ScopedLock */ + void waitFor(Set s) { ScopedWait(*this); while (!s.test(state)) wait(); } + /** @pre Caller holds a ScopedLock */ + void waitNot(Enum s) { ScopedWait(*this); while (s == state) wait(); } + /** @pre Caller holds a ScopedLock */ + void waitNot(Set s) { ScopedWait(*this); while (s.test(state)) wait(); } + + private: + Enum state; +}; + +}} + + +#endif /*!QPID_SYS_STATEMONITOR_H*/ diff --git a/qpid/cpp/src/qpid/sys/SystemInfo.cpp b/qpid/cpp/src/qpid/sys/SystemInfo.cpp new file mode 100644 index 0000000000..dcc7ad9985 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/SystemInfo.cpp @@ -0,0 +1,35 @@ +/* + * 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. + * + */ + +#include "SystemInfo.h" +#include <unistd.h> + +namespace qpid { +namespace sys { + +long SystemInfo::concurrency() { +#ifdef _SC_NPROCESSORS_ONLN // Linux specific. + return sysconf(_SC_NPROCESSORS_ONLN); +#else + return -1; +#endif +} + +}} // namespace qpid::sys diff --git a/qpid/cpp/src/qpid/sys/SystemInfo.h b/qpid/cpp/src/qpid/sys/SystemInfo.h new file mode 100644 index 0000000000..73c3ca3c17 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/SystemInfo.h @@ -0,0 +1,44 @@ +#ifndef QPID_SYS_SYSTEMINFO_H +#define QPID_SYS_SYSTEMINFO_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. + * + */ + +namespace qpid { +namespace sys { + +/** + * Retrieve information about the system we are running on. + * Results may be dependent on OS/hardware. + */ +class SystemInfo +{ + public: + /** Estimate available concurrency, e.g. number of CPU cores. + * -1 means estimate not available on this platform. + */ + static long concurrency(); +}; + +}} // namespace qpid::sys + + + +#endif /*!QPID_SYS_SYSTEMINFO_H*/ diff --git a/qpid/cpp/src/qpid/sys/Thread.h b/qpid/cpp/src/qpid/sys/Thread.h new file mode 100644 index 0000000000..fd9be5617e --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Thread.h @@ -0,0 +1,31 @@ +#ifndef _sys_Thread_h +#define _sys_Thread_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. + * + */ + +#ifdef USE_APR_PLATFORM +#include "apr/Thread.h" +#else +#include "posix/Thread.h" +#endif + +#endif /*!_sys_Thread_h*/ diff --git a/qpid/cpp/src/qpid/sys/Time.h b/qpid/cpp/src/qpid/sys/Time.h new file mode 100644 index 0000000000..6501cd0806 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Time.h @@ -0,0 +1,154 @@ +#ifndef _sys_Time_h +#define _sys_Time_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. + * + */ + +#include <stdint.h> +#include <limits> +#include <iosfwd> + +namespace qpid { +namespace sys { + +class Duration; + +/** Class to represent an instant in time: + * The time resolution is in nanosecs, and this is held with 64 bits + * giving a total time span from about 25 million years ago to 25 million + * years hence. As an aside the internal time can sensibly be negative + * meaning before the epoch (probably 1/1/1970 although this class doesn't + * care). + * + * The AbsTime class is a value class and so you don't need to add any accessors + * to its internal state. If you think you want to replace its value,i + * You need to construct a new AbsTime and assign it, viz: + * + * AbsTime when = AbsTime::now(); + * ... + * when = AbsTime(when, 2*TIME_SEC); // Advance timer 2 secs + * + * If for some reason you need access to the internal nanosec value you need + * to convert the AbsTime to a Duration and use its conversion to int64_t, viz: + * + * AbsTime now = AbsTime::now(); + * + * int64_t ns = Duration(now); + * + * However note that the nanosecond value that is returned here is not defined to be + * anything in particular and could vary from platform to platform. + * + * There are some sensible operations that are currently missing from AbsTime, but + * nearly all that's needed can be done with a mixture of AbsTimes and Durations. + * + * For example, convenience operators to add a Duration and AbsTime returning an AbsTime + * would fit here (although you can already perform the operation with one of the AbsTime + * constructors). However trying to add 2 AbsTimes doesn't make sense. + */ +class AbsTime { + static int64_t max() { return std::numeric_limits<int64_t>::max(); } + int64_t time_ns; + + friend class Duration; + +public: + inline AbsTime() {} + inline AbsTime(const AbsTime& time0, const Duration& duration); + // Default assignment operation fine + // Default copy constructor fine + + static AbsTime now(); + inline static AbsTime FarFuture(); + bool operator==(const AbsTime& t) const { return t.time_ns == time_ns; } + template <class S> void serialize(S& s) { s(time_ns); } + + friend bool operator<(const AbsTime& a, const AbsTime& b); + friend bool operator>(const AbsTime& a, const AbsTime& b); + friend std::ostream& operator << (std::ostream&, const AbsTime&); +}; + +std::ostream& operator << (std::ostream&, const AbsTime&); + +/** Class to represent the duration between instants of time: + * As AbsTime this class also uses nanosecs for its time + * resolution. For the most part a duration can be dealt with like a + * 64 bit integer, and indeed there is an implicit conversion which + * makes this quite conveient. + */ +class Duration { + static int64_t max() { return std::numeric_limits<int64_t>::max(); } + int64_t nanosecs; + + friend class AbsTime; + +public: + inline Duration(int64_t time0); + inline explicit Duration(const AbsTime& time0); + inline explicit Duration(const AbsTime& start, const AbsTime& finish); + inline operator int64_t() const; +}; + +std::ostream& operator << (std::ostream&, const Duration&); + +AbsTime::AbsTime(const AbsTime& t, const Duration& d) : + time_ns(d == Duration::max() ? max() : t.time_ns+d.nanosecs) +{} + +AbsTime AbsTime::FarFuture() { AbsTime ff; ff.time_ns = max(); return ff;} + +inline AbsTime now() { return AbsTime::now(); } + +inline bool operator<(const AbsTime& a, const AbsTime& b) { return a.time_ns < b.time_ns; } +inline bool operator>(const AbsTime& a, const AbsTime& b) { return a.time_ns > b.time_ns; } + +Duration::Duration(int64_t time0) : + nanosecs(time0) +{} + +Duration::Duration(const AbsTime& time0) : + nanosecs(time0.time_ns) +{} + +Duration::Duration(const AbsTime& start, const AbsTime& finish) : + nanosecs(finish.time_ns - start.time_ns) +{} + +Duration::operator int64_t() const +{ return nanosecs; } + +/** Nanoseconds per second. */ +const Duration TIME_SEC = 1000*1000*1000; +/** Nanoseconds per millisecond */ +const Duration TIME_MSEC = 1000*1000; +/** Nanoseconds per microseconds. */ +const Duration TIME_USEC = 1000; +/** Nanoseconds per nanosecond. */ +const Duration TIME_NSEC = 1; + +/** Value to represent an infinite timeout */ +const Duration TIME_INFINITE = std::numeric_limits<int64_t>::max(); + +/** Time greater than any other time */ +const AbsTime FAR_FUTURE = AbsTime::FarFuture(); + +}} + +#endif /*!_sys_Time_h*/ diff --git a/qpid/cpp/src/qpid/sys/TimeoutHandler.h b/qpid/cpp/src/qpid/sys/TimeoutHandler.h new file mode 100644 index 0000000000..0c10709bbf --- /dev/null +++ b/qpid/cpp/src/qpid/sys/TimeoutHandler.h @@ -0,0 +1,39 @@ +/* + * + * 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. + * + */ +#ifndef _TimeoutHandler_ +#define _TimeoutHandler_ + +namespace qpid { +namespace sys { + + class TimeoutHandler + { + public: + virtual void idleOut() = 0; + virtual void idleIn() = 0; + virtual ~TimeoutHandler(){} + }; + +} +} + + +#endif diff --git a/qpid/cpp/src/qpid/sys/Waitable.h b/qpid/cpp/src/qpid/sys/Waitable.h new file mode 100644 index 0000000000..37392ed761 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/Waitable.h @@ -0,0 +1,71 @@ +#ifndef QPID_SYS_WAITABLE_H +#define QPID_SYS_WAITABLE_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. + * + */ + +#include "Monitor.h" + +#include <assert.h> + +namespace qpid { +namespace sys { + +/** + * A monitor that keeps track of waiting threads. Threads declare a + * ScopedWait around wait() inside a ScopedLock to be considered + * waiters. + */ +class Waitable : public Monitor { + public: + Waitable() : waiters(0) {} + + /** Use this inside a scoped lock around the + * call to Monitor::wait to be counted as a waiter + */ + struct ScopedWait { + Waitable& w; + ScopedWait(Waitable& w_) : w(w_) { ++w.waiters; } + ~ScopedWait() { if (--w.waiters==0) w.notifyAll(); } + }; + + /** Block till there are no more ScopedWaits. + *@pre Must be called inside a ScopedLock but NOT a ScopedWait. + */ + void waitWaiters() { + while (waiters != 0) + wait(); + } + + /** Returns the number of outstanding ScopedWaits. + * Must be called with the lock held. + */ + size_t hasWaiters() { return waiters; } + + private: + friend struct ScopedWait; + size_t waiters; +}; + +}} // namespace qpid::sys + + + +#endif /*!QPID_SYS_WAITABLE_H*/ diff --git a/qpid/cpp/src/qpid/sys/apr/APRBase.cpp b/qpid/cpp/src/qpid/sys/apr/APRBase.cpp new file mode 100644 index 0000000000..724c489303 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/apr/APRBase.cpp @@ -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. + * + */ +#include <iostream> +#include "qpid/log/Statement.h" +#include "APRBase.h" + +using namespace qpid::sys; + +APRBase* APRBase::instance = 0; + +APRBase* APRBase::getInstance(){ + if(instance == 0){ + instance = new APRBase(); + } + return instance; +} + + +APRBase::APRBase() : count(0){ + apr_initialize(); + CHECK_APR_SUCCESS(apr_pool_create(&pool, 0)); + CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, pool)); +} + +APRBase::~APRBase(){ + CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); + apr_pool_destroy(pool); + apr_terminate(); +} + +bool APRBase::_increment(){ + bool deleted(false); + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); + if(this == instance){ + count++; + }else{ + deleted = true; + } + CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); + return !deleted; +} + +void APRBase::_decrement(){ + APRBase* copy = 0; + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); + if(--count == 0){ + copy = instance; + instance = 0; + } + CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); + if(copy != 0){ + delete copy; + } +} + +void APRBase::increment(){ + int count = 0; + while(count++ < 2 && !getInstance()->_increment()) + QPID_LOG(warning, "APR initialization triggered concurrently with termination."); +} + +void APRBase::decrement(){ + getInstance()->_decrement(); +} + +std::string qpid::sys::get_desc(apr_status_t status){ + const int size = 50; + char tmp[size]; + return std::string(apr_strerror(status, tmp, size)); +} + diff --git a/qpid/cpp/src/qpid/sys/apr/APRBase.h b/qpid/cpp/src/qpid/sys/apr/APRBase.h new file mode 100644 index 0000000000..7b5644a129 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/apr/APRBase.h @@ -0,0 +1,74 @@ +/* + * + * 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. + * + */ +#ifndef _APRBase_ +#define _APRBase_ + +#include <string> +#include <apr_thread_mutex.h> +#include <apr_errno.h> + +namespace qpid { +namespace sys { + + /** + * Use of APR libraries necessitates explicit init and terminate + * calls. Any class using APR libs should obtain the reference to + * this singleton and increment on construction, decrement on + * destruction. This class can then correctly initialise apr + * before the first use and terminate after the last use. + */ + class APRBase{ + static APRBase* instance; + apr_pool_t* pool; + apr_thread_mutex_t* mutex; + int count; + + APRBase(); + ~APRBase(); + static APRBase* getInstance(); + bool _increment(); + void _decrement(); + public: + static void increment(); + static void decrement(); + }; + + //this is also a convenient place for a helper function for error checking: + void check(apr_status_t status, const char* file, const int line); + std::string get_desc(apr_status_t status); + +#define CHECK_APR_SUCCESS(A) qpid::sys::check(A, __FILE__, __LINE__); + +} +} + +// Inlined as it is called *a lot* +void inline qpid::sys::check(apr_status_t status, const char* file, const int line){ + if (status != APR_SUCCESS){ + char tmp[256]; + throw Exception(QPID_MSG(apr_strerror(status, tmp, size))) + } +} + + + + +#endif diff --git a/qpid/cpp/src/qpid/sys/apr/APRPool.cpp b/qpid/cpp/src/qpid/sys/apr/APRPool.cpp new file mode 100644 index 0000000000..e8b71f6e8a --- /dev/null +++ b/qpid/cpp/src/qpid/sys/apr/APRPool.cpp @@ -0,0 +1,41 @@ +/* + * + * 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. + * + */ + +#include "APRPool.h" +#include "APRBase.h" +#include <boost/pool/detail/singleton.hpp> + +using namespace qpid::sys; + +APRPool::APRPool(){ + APRBase::increment(); + CHECK_APR_SUCCESS(apr_pool_create(&pool, NULL)); +} + +APRPool::~APRPool(){ + apr_pool_destroy(pool); + APRBase::decrement(); +} + +apr_pool_t* APRPool::get() { + return boost::details::pool::singleton_default<APRPool>::instance().pool; +} + diff --git a/qpid/cpp/src/qpid/sys/apr/APRPool.h b/qpid/cpp/src/qpid/sys/apr/APRPool.h new file mode 100644 index 0000000000..da7661fcfa --- /dev/null +++ b/qpid/cpp/src/qpid/sys/apr/APRPool.h @@ -0,0 +1,50 @@ +#ifndef _APRPool_ +#define _APRPool_ + +/* + * + * 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. + * + */ +#include <boost/noncopyable.hpp> +#include <apr_pools.h> + +namespace qpid { +namespace sys { +/** + * Singleton APR memory pool. + */ +class APRPool : private boost::noncopyable { + public: + APRPool(); + ~APRPool(); + + /** Get singleton instance */ + static apr_pool_t* get(); + + private: + apr_pool_t* pool; +}; + +}} + + + + + +#endif /*!_APRPool_*/ diff --git a/qpid/cpp/src/qpid/sys/apr/Condition.h b/qpid/cpp/src/qpid/sys/apr/Condition.h new file mode 100644 index 0000000000..5e544219ab --- /dev/null +++ b/qpid/cpp/src/qpid/sys/apr/Condition.h @@ -0,0 +1,84 @@ +#ifndef _sys_apr_Condition_h +#define _sys_apr_Condition_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. + * + */ + +#include "APRPool.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Time.h" + +#include <sys/errno.h> +#include <boost/noncopyable.hpp> +#include <apr_thread_cond.h> + +namespace qpid { +namespace sys { + +/** + * A condition variable for thread synchronization. + */ +class Condition +{ + public: + inline Condition(); + inline ~Condition(); + inline void wait(Mutex&); + inline bool wait(Mutex&, const AbsTime& absoluteTime); + inline void notify(); + inline void notifyAll(); + + private: + apr_thread_cond_t* condition; +}; + + +Condition::Condition() { + CHECK_APR_SUCCESS(apr_thread_cond_create(&condition, APRPool::get())); +} + +Condition::~Condition() { + CHECK_APR_SUCCESS(apr_thread_cond_destroy(condition)); +} + +void Condition::wait(Mutex& mutex) { + CHECK_APR_SUCCESS(apr_thread_cond_wait(condition, mutex.mutex)); +} + +bool Condition::wait(Mutex& mutex, const AbsTime& absoluteTime){ + // APR uses microseconds. + apr_status_t status = + apr_thread_cond_timedwait( + condition, mutex.mutex, Duration(now(), absoluteTime)/TIME_USEC); + if(status != APR_TIMEUP) CHECK_APR_SUCCESS(status); + return status == 0; +} + +void Condition::notify(){ + CHECK_APR_SUCCESS(apr_thread_cond_signal(condition)); +} + +void Condition::notifyAll(){ + CHECK_APR_SUCCESS(apr_thread_cond_broadcast(condition)); +} + +}} +#endif /*!_sys_apr_Condition_h*/ diff --git a/qpid/cpp/src/qpid/sys/apr/Mutex.h b/qpid/cpp/src/qpid/sys/apr/Mutex.h new file mode 100644 index 0000000000..51089c98ff --- /dev/null +++ b/qpid/cpp/src/qpid/sys/apr/Mutex.h @@ -0,0 +1,124 @@ +#ifndef _sys_apr_Mutex_h +#define _sys_apr_Mutex_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "APRBase.h" +#include "APRPool.h" + +#include <boost/noncopyable.hpp> +#include <apr_thread_mutex.h> + +namespace qpid { +namespace sys { + +class Condition; + +/** + * Mutex lock. + */ +class Mutex : private boost::noncopyable { + public: + typedef ScopedLock<Mutex> ScopedLock; + typedef ScopedUnlock<Mutex> ScopedUnlock; + + inline Mutex(); + inline ~Mutex(); + inline void lock(); + inline void unlock(); + inline bool trylock(); + + protected: + apr_thread_mutex_t* mutex; + friend class Condition; +}; + +Mutex::Mutex() { + CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, APRPool::get())); +} + +Mutex::~Mutex(){ + CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); +} + +void Mutex::lock() { + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); +} +void Mutex::unlock() { + CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); +} + +bool Mutex::trylock() { + return apr_thread_mutex_trylock(mutex) == 0; +} + + +/** + * RW lock. + */ +class RWlock : private boost::noncopyable { + friend class Condition; + +public: + typedef ScopedRlock<RWlock> ScopedRlock; + typedef ScopedWlock<RWlock> ScopedWlock; + + inline RWlock(); + inline ~RWlock(); + inline void wlock(); // will write-lock + inline void rlock(); // will read-lock + inline void unlock(); + inline bool trywlock(); // will write-try + inline bool tryrlock(); // will read-try + + protected: + apr_thread_mutex_t* mutex; +}; + +RWlock::RWlock() { + CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, APRPool::get())); +} + +RWlock::~RWlock(){ + CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); +} + +void RWlock::wlock() { + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); +} + +void RWlock::rlock() { + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); +} + +void RWlock::unlock() { + CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); +} + +bool RWlock::trywlock() { + return apr_thread_mutex_trylock(mutex) == 0; +} + +bool RWlock::tryrlock() { + return apr_thread_mutex_trylock(mutex) == 0; +} + + +}} +#endif /*!_sys_apr_Mutex_h*/ diff --git a/qpid/cpp/src/qpid/sys/apr/Shlib.cpp b/qpid/cpp/src/qpid/sys/apr/Shlib.cpp new file mode 100644 index 0000000000..b0ba706713 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/apr/Shlib.cpp @@ -0,0 +1,49 @@ +/* + * 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. + * + */ + +#include "qpid/sys/Shlib.h" +#include "APRBase.h" +#include "APRPool.h" +#include <apr_dso.h> + +namespace qpid { +namespace sys { + +void Shlib::load(const char* libname) { + apr_dso_handle_t* aprHandle; + CHECK_APR_SUCCESS( + apr_dso_load(&aprHandle, libname, APRPool::get())); + handle=aprHandle; +} + +void Shlib::unload() { + CHECK_APR_SUCCESS( + apr_dso_unload(static_cast<apr_dso_handle_t*>(handle))); +} + +void* Shlib::getSymbol(const char* name) { + apr_dso_handle_sym_t symbol; + CHECK_APR_SUCCESS(apr_dso_sym(&symbol, + static_cast<apr_dso_handle_t*>(handle), + name)); + return (void*) symbol; +} + +}} // namespace qpid::sys diff --git a/qpid/cpp/src/qpid/sys/apr/Socket.cpp b/qpid/cpp/src/qpid/sys/apr/Socket.cpp new file mode 100644 index 0000000000..577268844a --- /dev/null +++ b/qpid/cpp/src/qpid/sys/apr/Socket.cpp @@ -0,0 +1,114 @@ +/* + * + * 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. + * + */ + + +#include "qpid/sys/Socket.h" + +#include "APRBase.h" +#include "APRPool.h" + +#include <apr_network_io.h> + +namespace qpid { +namespace sys { + +class SocketPrivate { +public: + SocketPrivate(apr_socket_t* s = 0) : + socket(s) + {} + + apr_socket_t* socket; +}; + +Socket::Socket() : + impl(new SocketPrivate) +{ + createTcp(); +} + +Socket::Socket(SocketPrivate* sp) : + impl(sp) +{} + +Socket::~Socket() { + delete impl; +} + +void Socket::createTcp() const { + apr_socket_t*& socket = impl->socket; + apr_socket_t* s; + CHECK_APR_SUCCESS( + apr_socket_create( + &s, APR_INET, SOCK_STREAM, APR_PROTO_TCP, + APRPool::get())); + socket = s; +} + +void Socket::setTimeout(const Duration& interval) const { + apr_socket_t*& socket = impl->socket; + apr_socket_timeout_set(socket, interval/TIME_USEC); +} + +void Socket::connect(const std::string& host, int port) const { + apr_socket_t*& socket = impl->socket; + apr_sockaddr_t* address; + CHECK_APR_SUCCESS( + apr_sockaddr_info_get( + &address, host.c_str(), APR_UNSPEC, port, APR_IPV4_ADDR_OK, + APRPool::get())); + CHECK_APR_SUCCESS(apr_socket_connect(socket, address)); +} + +void Socket::close() const { + apr_socket_t*& socket = impl->socket; + if (socket == 0) return; + CHECK_APR_SUCCESS(apr_socket_close(socket)); + socket = 0; +} + +ssize_t Socket::send(const void* data, size_t size) const +{ + apr_socket_t*& socket = impl->socket; + apr_size_t sent = size; + apr_status_t status = + apr_socket_send(socket, reinterpret_cast<const char*>(data), &sent); + if (APR_STATUS_IS_TIMEUP(status)) return SOCKET_TIMEOUT; + if (APR_STATUS_IS_EOF(status)) return SOCKET_EOF; + CHECK_APR_SUCCESS(status); + return sent; +} + +ssize_t Socket::recv(void* data, size_t size) const +{ + apr_socket_t*& socket = impl->socket; + apr_size_t received = size; + apr_status_t status = + apr_socket_recv(socket, reinterpret_cast<char*>(data), &received); + if (APR_STATUS_IS_TIMEUP(status)) + return SOCKET_TIMEOUT; + if (APR_STATUS_IS_EOF(status)) + return SOCKET_EOF; + CHECK_APR_SUCCESS(status); + return received; +} + +}} // namespace qpid::sys diff --git a/qpid/cpp/src/qpid/sys/apr/Thread.cpp b/qpid/cpp/src/qpid/sys/apr/Thread.cpp new file mode 100644 index 0000000000..3369ef7eb1 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/apr/Thread.cpp @@ -0,0 +1,34 @@ +/* + * + * 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. + * + */ + +#include "Thread.h" +#include "qpid/sys/Runnable.h" + +using namespace qpid::sys; +using qpid::sys::Runnable; + +void* APR_THREAD_FUNC Thread::runRunnable(apr_thread_t* thread, void *data) { + reinterpret_cast<Runnable*>(data)->run(); + CHECK_APR_SUCCESS(apr_thread_exit(thread, APR_SUCCESS)); + return NULL; +} + + diff --git a/qpid/cpp/src/qpid/sys/apr/Thread.h b/qpid/cpp/src/qpid/sys/apr/Thread.h new file mode 100644 index 0000000000..8cbbc0456e --- /dev/null +++ b/qpid/cpp/src/qpid/sys/apr/Thread.h @@ -0,0 +1,106 @@ +#ifndef _sys_apr_Thread_h +#define _sys_apr_Thread_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. + * + */ + +#include "APRPool.h" +#include "APRBase.h" + +#include <apr_thread_proc.h> +#include <apr_portable.h> + +namespace qpid { +namespace sys { + +class Runnable; + +class Thread +{ + public: + inline static Thread current(); + + /** ID of current thread for logging. + * Workaround for broken Thread::current() in APR + */ + inline static long logId(); + + inline static void yield(); + + inline Thread(); + inline explicit Thread(qpid::sys::Runnable*); + inline explicit Thread(qpid::sys::Runnable&); + + inline void join(); + + inline long id(); + + private: + static void* APR_THREAD_FUNC runRunnable(apr_thread_t* thread, void *data); + inline Thread(apr_thread_t* t); + apr_thread_t* thread; +}; + +Thread::Thread() : thread(0) {} + +Thread::Thread(Runnable* runnable) { + CHECK_APR_SUCCESS( + apr_thread_create(&thread, 0, runRunnable, runnable, APRPool::get())); +} + +Thread::Thread(Runnable& runnable) { + CHECK_APR_SUCCESS( + apr_thread_create(&thread, 0, runRunnable, &runnable, APRPool::get())); +} + +void Thread::join(){ + apr_status_t status; + if (thread != 0) + CHECK_APR_SUCCESS(apr_thread_join(&status, thread)); +} + +long Thread::id() { + return long(thread); +} + +/** ID of current thread for logging. + * Workaround for broken Thread::current() in APR + */ +long Thread::logId() { + return static_cast<long>(apr_os_thread_current()); +} + +Thread::Thread(apr_thread_t* t) : thread(t) {} + +Thread Thread::current(){ + apr_thread_t* thr; + apr_os_thread_t osthr = apr_os_thread_current(); + CHECK_APR_SUCCESS(apr_os_thread_put(&thr, &osthr, APRPool::get())); + return Thread(thr); +} + +void Thread::yield() +{ + apr_thread_yield(); +} + +}} +#endif /*!_sys_apr_Thread_h*/ diff --git a/qpid/cpp/src/qpid/sys/apr/Time.cpp b/qpid/cpp/src/qpid/sys/apr/Time.cpp new file mode 100644 index 0000000000..34e740b144 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/apr/Time.cpp @@ -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. + * + */ + +#include "qpid/sys/Time.h" + +#include <apr_time.h> + +namespace qpid { +namespace sys { + +AbsTime AbsTime::now() { + AbsTime time_now; + time_now.time_ns = apr_time_now() * TIME_USEC; + return time_now; +} + +}} + diff --git a/qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp b/qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp new file mode 100644 index 0000000000..8936251f94 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/epoll/EpollPoller.cpp @@ -0,0 +1,338 @@ +/* + * + * 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. + * + */ + +#include "qpid/sys/Poller.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/DeletionManager.h" +#include "qpid/sys/posix/check.h" +#include "qpid/sys/posix/PrivatePosix.h" + +#include <sys/epoll.h> +#include <errno.h> + +#include <assert.h> +#include <vector> +#include <exception> + +namespace qpid { +namespace sys { + +// Deletion manager to handle deferring deletion of PollerHandles to when they definitely aren't being used +DeletionManager<PollerHandle> PollerHandleDeletionManager; + +// Instantiate (and define) class static for DeletionManager +template <> +DeletionManager<PollerHandle>::AllThreadsStatuses DeletionManager<PollerHandle>::allThreadsStatuses(0); + +class PollerHandlePrivate { + friend class Poller; + friend class PollerHandle; + + enum FDStat { + ABSENT, + MONITORED, + INACTIVE, + HUNGUP, + MONITORED_HUNGUP + }; + + ::__uint32_t events; + FDStat stat; + Mutex lock; + + PollerHandlePrivate() : + events(0), + stat(ABSENT) { + } + + bool isActive() const { + return stat == MONITORED || stat == MONITORED_HUNGUP; + } + + void setActive() { + stat = (stat == HUNGUP) ? MONITORED_HUNGUP : MONITORED; + } + + bool isInactive() const { + return stat == INACTIVE || stat == HUNGUP; + } + + void setInactive() { + stat = INACTIVE; + } + + bool isIdle() const { + return stat == ABSENT; + } + + void setIdle() { + stat = ABSENT; + } + + bool isHungup() const { + return stat == MONITORED_HUNGUP || stat == HUNGUP; + } + + void setHungup() { + assert(stat == MONITORED); + stat = HUNGUP; + } +}; + +PollerHandle::PollerHandle(const Socket& s) : + impl(new PollerHandlePrivate), + socket(s) +{} + +PollerHandle::~PollerHandle() { + delete impl; +} + +void PollerHandle::deferDelete() { + PollerHandleDeletionManager.markForDeletion(this); +} + +/** + * Concrete implementation of Poller to use the Linux specific epoll + * interface + */ +class PollerPrivate { + friend class Poller; + + static const int DefaultFds = 256; + + struct ReadablePipe { + int fds[2]; + + /** + * This encapsulates an always readable pipe which we can add + * to the epoll set to force epoll_wait to return + */ + ReadablePipe() { + QPID_POSIX_CHECK(::pipe(fds)); + // Just write the pipe's fds to the pipe + QPID_POSIX_CHECK(::write(fds[1], fds, 2)); + } + + ~ReadablePipe() { + ::close(fds[0]); + ::close(fds[1]); + } + + int getFD() { + return fds[0]; + } + }; + + static ReadablePipe alwaysReadable; + + const int epollFd; + bool isShutdown; + + static ::__uint32_t directionToEpollEvent(Poller::Direction dir) { + switch (dir) { + case Poller::IN: return ::EPOLLIN; + case Poller::OUT: return ::EPOLLOUT; + case Poller::INOUT: return ::EPOLLIN | ::EPOLLOUT; + default: return 0; + } + } + + static Poller::EventType epollToDirection(::__uint32_t events) { + // POLLOUT & POLLHUP are mutually exclusive really, but at least socketpairs + // can give you both! + events = (events & ::EPOLLHUP) ? events & ~::EPOLLOUT : events; + ::__uint32_t e = events & (::EPOLLIN | ::EPOLLOUT); + switch (e) { + case ::EPOLLIN: return Poller::READABLE; + case ::EPOLLOUT: return Poller::WRITABLE; + case ::EPOLLIN | ::EPOLLOUT: return Poller::READ_WRITABLE; + default: + return (events & (::EPOLLHUP | ::EPOLLERR)) ? + Poller::DISCONNECTED : Poller::INVALID; + } + } + + PollerPrivate() : + epollFd(::epoll_create(DefaultFds)), + isShutdown(false) { + QPID_POSIX_CHECK(epollFd); + } + + ~PollerPrivate() { + // It's probably okay to ignore any errors here as there can't be data loss + ::close(epollFd); + } +}; + +PollerPrivate::ReadablePipe PollerPrivate::alwaysReadable; + +void Poller::addFd(PollerHandle& handle, Direction dir) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + ::epoll_event epe; + int op; + + if (eh.isIdle()) { + op = EPOLL_CTL_ADD; + epe.events = PollerPrivate::directionToEpollEvent(dir) | ::EPOLLONESHOT; + } else { + assert(eh.isActive()); + op = EPOLL_CTL_MOD; + epe.events = eh.events | PollerPrivate::directionToEpollEvent(dir); + } + epe.data.ptr = &handle; + + QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, op, toFd(handle.socket.impl), &epe)); + + // Record monitoring state of this fd + eh.events = epe.events; + eh.setActive(); +} + +void Poller::delFd(PollerHandle& handle) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + assert(!eh.isIdle()); + int rc = ::epoll_ctl(impl->epollFd, EPOLL_CTL_DEL, toFd(handle.socket.impl), 0); + // Ignore EBADF since deleting a nonexistent fd has the overall required result! + // And allows the case where a sloppy program closes the fd and then does the delFd() + if (rc == -1 && errno != EBADF) { + QPID_POSIX_CHECK(rc); + } + eh.setIdle(); +} + +// modFd is equivalent to delFd followed by addFd +void Poller::modFd(PollerHandle& handle, Direction dir) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + assert(!eh.isIdle()); + + ::epoll_event epe; + epe.events = PollerPrivate::directionToEpollEvent(dir) | ::EPOLLONESHOT; + epe.data.ptr = &handle; + + QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_MOD, toFd(handle.socket.impl), &epe)); + + // Record monitoring state of this fd + eh.events = epe.events; + eh.setActive(); +} + +void Poller::rearmFd(PollerHandle& handle) { + PollerHandlePrivate& eh = *handle.impl; + ScopedLock<Mutex> l(eh.lock); + assert(eh.isInactive()); + + ::epoll_event epe; + epe.events = eh.events; + epe.data.ptr = &handle; + + QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_MOD, toFd(handle.socket.impl), &epe)); + + eh.setActive(); +} + +void Poller::shutdown() { + // NB: this function must be async-signal safe, it must not + // call any function that is not async-signal safe. + + // Allow sloppy code to shut us down more than once + if (impl->isShutdown) + return; + + // Don't use any locking here - isshutdown will be visible to all + // after the epoll_ctl() anyway (it's a memory barrier) + impl->isShutdown = true; + + // Add always readable fd to epoll (not EPOLLONESHOT) + int fd = impl->alwaysReadable.getFD(); + ::epoll_event epe; + epe.events = ::EPOLLIN; + epe.data.ptr = 0; + QPID_POSIX_CHECK(::epoll_ctl(impl->epollFd, EPOLL_CTL_ADD, fd, &epe)); +} + +Poller::Event Poller::wait(Duration timeout) { + epoll_event epe; + int timeoutMs = (timeout == TIME_INFINITE) ? -1 : timeout / TIME_MSEC; + + // Repeat until we weren't interupted + do { + PollerHandleDeletionManager.markAllUnusedInThisThread(); + int rc = ::epoll_wait(impl->epollFd, &epe, 1, timeoutMs); + + if (impl->isShutdown) { + return Event(0, SHUTDOWN); + } + + if (rc ==-1 && errno != EINTR) { + QPID_POSIX_CHECK(rc); + } else if (rc > 0) { + assert(rc == 1); + PollerHandle* handle = static_cast<PollerHandle*>(epe.data.ptr); + PollerHandlePrivate& eh = *handle->impl; + + ScopedLock<Mutex> l(eh.lock); + + // the handle could have gone inactive since we left the epoll_wait + if (eh.isActive()) { + // If the connection has been hungup we could still be readable + // (just not writable), allow us to readable until we get here again + if (epe.events & ::EPOLLHUP) { + if (eh.isHungup()) { + return Event(handle, DISCONNECTED); + } + eh.setHungup(); + } else { + eh.setInactive(); + } + return Event(handle, PollerPrivate::epollToDirection(epe.events)); + } + } + // We only get here if one of the following: + // * epoll_wait was interrupted by a signal + // * epoll_wait timed out + // * the state of the handle changed after being returned by epoll_wait + // + // The only things we can do here are return a timeout or wait more. + // Obviously if we timed out we return timeout; if the wait was meant to + // be indefinite then we should never return with a time out so we go again. + // If the wait wasn't indefinite, but we were interrupted then we have to return + // with a timeout as we don't know how long we've waited so far and so we can't + // continue the wait. + if (rc == 0 || timeoutMs != -1) { + return Event(0, TIMEOUT); + } + } while (true); +} + +// Concrete constructors +Poller::Poller() : + impl(new PollerPrivate()) +{} + +Poller::~Poller() { + delete impl; +} + +}} diff --git a/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp b/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp new file mode 100644 index 0000000000..94c68bd5d0 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/posix/AsynchIO.cpp @@ -0,0 +1,364 @@ +/* + * + * 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. + * + */ + +#include "qpid/sys/AsynchIO.h" +#include "qpid/sys/Time.h" + +#include "check.h" + +// TODO The basic algorithm here is not really POSIX specific and with a bit more abstraction +// could (should) be promoted to be platform portable +#include <unistd.h> +#include <sys/socket.h> +#include <signal.h> +#include <errno.h> + +#include <boost/bind.hpp> + +using namespace qpid::sys; + +namespace { + +/* + * Make *process* not generate SIGPIPE when writing to closed + * pipe/socket (necessary as default action is to terminate process) + */ +void ignoreSigpipe() { + ::signal(SIGPIPE, SIG_IGN); +} + +/* + * We keep per thread state to avoid locking overhead. The assumption is that + * on average all the connections are serviced by all the threads so the state + * recorded in each thread is about the same. If this turns out not to be the + * case we could rebalance the info occasionally. + */ +__thread int threadReadTotal = 0; +__thread int threadMaxRead = 0; +__thread int threadReadCount = 0; +__thread int threadWriteTotal = 0; +__thread int threadWriteCount = 0; +__thread int64_t threadMaxReadTimeNs = 2 * 1000000; // start at 2ms +} + +/* + * Asynch Acceptor + */ + +AsynchAcceptor::AsynchAcceptor(const Socket& s, Callback callback) : + acceptedCallback(callback), + handle(s, boost::bind(&AsynchAcceptor::readable, this, _1), 0, 0) { + + s.setNonblocking(); + ignoreSigpipe(); +} + +void AsynchAcceptor::start(Poller::shared_ptr poller) { + handle.startWatch(poller); +} + +/* + * We keep on accepting as long as there is something to accept + */ +void AsynchAcceptor::readable(DispatchHandle& h) { + Socket* s; + do { + errno = 0; + // TODO: Currently we ignore the peers address, perhaps we should + // log it or use it for connection acceptance. + s = h.getSocket().accept(0, 0); + if (s) { + acceptedCallback(*s); + } else { + break; + } + } while (true); + + h.rewatch(); +} + +/* + * Asynch reader/writer + */ +AsynchIO::AsynchIO(const Socket& s, + ReadCallback rCb, EofCallback eofCb, DisconnectCallback disCb, + ClosedCallback cCb, BuffersEmptyCallback eCb, IdleCallback iCb) : + + DispatchHandle(s, + boost::bind(&AsynchIO::readable, this, _1), + boost::bind(&AsynchIO::writeable, this, _1), + boost::bind(&AsynchIO::disconnected, this, _1)), + readCallback(rCb), + eofCallback(eofCb), + disCallback(disCb), + closedCallback(cCb), + emptyCallback(eCb), + idleCallback(iCb), + queuedClose(false), + writePending(false) { + + s.setNonblocking(); +} + +struct deleter +{ + template <typename T> + void operator()(T *ptr){ delete ptr;} +}; + +AsynchIO::~AsynchIO() { + std::for_each( bufferQueue.begin(), bufferQueue.end(), deleter()); + std::for_each( writeQueue.begin(), writeQueue.end(), deleter()); +} + +void AsynchIO::queueForDeletion() { + DispatchHandle::doDelete(); +} + +void AsynchIO::start(Poller::shared_ptr poller) { + DispatchHandle::startWatch(poller); +} + +void AsynchIO::queueReadBuffer(BufferBase* buff) { + assert(buff); + buff->dataStart = 0; + buff->dataCount = 0; + bufferQueue.push_back(buff); + DispatchHandle::rewatchRead(); +} + +void AsynchIO::unread(BufferBase* buff) { + assert(buff); + if (buff->dataStart != 0) { + memmove(buff->bytes, buff->bytes+buff->dataStart, buff->dataCount); + buff->dataStart = 0; + } + bufferQueue.push_front(buff); + DispatchHandle::rewatchRead(); +} + +void AsynchIO::queueWrite(BufferBase* buff) { + assert(buff); + // If we've already closed the socket then throw the write away + if (queuedClose) { + bufferQueue.push_front(buff); + return; + } else { + writeQueue.push_front(buff); + } + writePending = false; + DispatchHandle::rewatchWrite(); +} + +void AsynchIO::notifyPendingWrite() { + writePending = true; + DispatchHandle::rewatchWrite(); +} + +void AsynchIO::queueWriteClose() { + queuedClose = true; + DispatchHandle::rewatchWrite(); +} + +/** Return a queued buffer if there are enough + * to spare + */ +AsynchIO::BufferBase* AsynchIO::getQueuedBuffer() { + // Always keep at least one buffer (it might have data that was "unread" in it) + if (bufferQueue.size()<=1) + return 0; + BufferBase* buff = bufferQueue.back(); + assert(buff); + buff->dataStart = 0; + buff->dataCount = 0; + bufferQueue.pop_back(); + return buff; +} + +/* + * We keep on reading as long as we have something to read and a buffer to put + * it in + */ +void AsynchIO::readable(DispatchHandle& h) { + int readTotal = 0; + AbsTime readStartTime = AbsTime::now(); + do { + // (Try to) get a buffer + if (!bufferQueue.empty()) { + // Read into buffer + BufferBase* buff = bufferQueue.front(); + assert(buff); + bufferQueue.pop_front(); + errno = 0; + int readCount = buff->byteCount-buff->dataCount; + int rc = h.getSocket().read(buff->bytes + buff->dataCount, readCount); + if (rc > 0) { + buff->dataCount += rc; + threadReadTotal += rc; + readTotal += rc; + + readCallback(*this, buff); + if (rc != readCount) { + // If we didn't fill the read buffer then time to stop reading + break; + } + + // Stop reading if we've overrun our timeslot + if (Duration(readStartTime, AbsTime::now()) > threadMaxReadTimeNs) { + break; + } + + } else { + // Put buffer back (at front so it doesn't interfere with unread buffers) + bufferQueue.push_front(buff); + assert(buff); + + // Eof or other side has gone away + if (rc == 0 || errno == ECONNRESET) { + eofCallback(*this); + h.unwatchRead(); + break; + } else if (errno == EAGAIN) { + // We have just put a buffer back so we know + // we can carry on watching for reads + break; + } else { + QPID_POSIX_CHECK(rc); + } + } + } else { + // Something to read but no buffer + if (emptyCallback) { + emptyCallback(*this); + } + // If we still have no buffers we can't do anything more + if (bufferQueue.empty()) { + h.unwatchRead(); + break; + } + + } + } while (true); + + ++threadReadCount; + threadMaxRead = std::max(threadMaxRead, readTotal); + return; +} + +/* + * We carry on writing whilst we have data to write and we can write + */ +void AsynchIO::writeable(DispatchHandle& h) { + int writeTotal = 0; + do { + // See if we've got something to write + if (!writeQueue.empty()) { + // Write buffer + BufferBase* buff = writeQueue.back(); + writeQueue.pop_back(); + errno = 0; + assert(buff->dataStart+buff->dataCount <= buff->byteCount); + int rc = h.getSocket().write(buff->bytes+buff->dataStart, buff->dataCount); + if (rc >= 0) { + threadWriteTotal += rc; + writeTotal += rc; + + // If we didn't write full buffer put rest back + if (rc != buff->dataCount) { + buff->dataStart += rc; + buff->dataCount -= rc; + writeQueue.push_back(buff); + break; + } + + // Recycle the buffer + queueReadBuffer(buff); + + // If we've already written more than the max for reading then stop + // (this is to stop writes dominating reads) + if (writeTotal > threadMaxRead) + break; + } else { + // Put buffer back + writeQueue.push_back(buff); + if (errno == ECONNRESET || errno == EPIPE) { + // Just stop watching for write here - we'll get a + // disconnect callback soon enough + h.unwatchWrite(); + break; + } else if (errno == EAGAIN) { + // We have just put a buffer back so we know + // we can carry on watching for writes + break; + } else { + QPID_POSIX_CHECK(rc); + } + } + } else { + // If we're waiting to close the socket then can do it now as there is nothing to write + if (queuedClose) { + close(h); + break; + } + // Fd is writable, but nothing to write + if (idleCallback) { + writePending = false; + idleCallback(*this); + } + // If we still have no buffers to write we can't do anything more + if (writeQueue.empty() && !writePending && !queuedClose) { + h.unwatchWrite(); + // The following handles the case where writePending is + // set to true after the test above; in this case its + // possible that the unwatchWrite overwrites the + // desired rewatchWrite so we correct that here + if (writePending) + h.rewatchWrite(); + break; + } + } + } while (true); + + ++threadWriteCount; + return; +} + +void AsynchIO::disconnected(DispatchHandle& h) { + // If we've already queued close do it instead of disconnected callback + if (queuedClose) { + close(h); + } else if (disCallback) { + disCallback(*this); + h.unwatch(); + } +} + +/* + * Close the socket and callback to say we've done it + */ +void AsynchIO::close(DispatchHandle& h) { + h.stopWatch(); + h.getSocket().close(); + if (closedCallback) { + closedCallback(*this, getSocket()); + } +} + diff --git a/qpid/cpp/src/qpid/sys/posix/Condition.h b/qpid/cpp/src/qpid/sys/posix/Condition.h new file mode 100644 index 0000000000..86d6500ee9 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/posix/Condition.h @@ -0,0 +1,86 @@ +#ifndef _sys_posix_Condition_h +#define _sys_posix_Condition_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. + * + */ + +#include "PrivatePosix.h" + +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Time.h" + +#include <time.h> +#include <sys/errno.h> +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace sys { + +/** + * A condition variable for thread synchronization. + */ +class Condition +{ + public: + inline Condition(); + inline ~Condition(); + inline void wait(Mutex&); + inline bool wait(Mutex&, const AbsTime& absoluteTime); + inline void notify(); + inline void notifyAll(); + + private: + pthread_cond_t condition; +}; + +Condition::Condition() { + QPID_POSIX_ASSERT_THROW_IF(pthread_cond_init(&condition, 0)); +} + +Condition::~Condition() { + QPID_POSIX_ASSERT_THROW_IF(pthread_cond_destroy(&condition)); +} + +void Condition::wait(Mutex& mutex) { + QPID_POSIX_ASSERT_THROW_IF(pthread_cond_wait(&condition, &mutex.mutex)); +} + +bool Condition::wait(Mutex& mutex, const AbsTime& absoluteTime){ + struct timespec ts; + toTimespec(ts, Duration(absoluteTime)); + int status = pthread_cond_timedwait(&condition, &mutex.mutex, &ts); + if (status != 0) { + if (status == ETIMEDOUT) return false; + throw QPID_POSIX_ERROR(status); + } + return true; +} + +void Condition::notify(){ + QPID_POSIX_ASSERT_THROW_IF(pthread_cond_signal(&condition)); +} + +void Condition::notifyAll(){ + QPID_POSIX_ASSERT_THROW_IF(pthread_cond_broadcast(&condition)); +} + +}} +#endif /*!_sys_posix_Condition_h*/ diff --git a/qpid/cpp/src/qpid/sys/posix/Mutex.h b/qpid/cpp/src/qpid/sys/posix/Mutex.h new file mode 100644 index 0000000000..ceb2794abe --- /dev/null +++ b/qpid/cpp/src/qpid/sys/posix/Mutex.h @@ -0,0 +1,201 @@ +#ifndef _sys_posix_Mutex_h +#define _sys_posix_Mutex_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "check.h" + +#include <pthread.h> +#include <boost/noncopyable.hpp> + +namespace qpid { +namespace sys { + +class Condition; + +/** + * Mutex lock. + */ +class Mutex : private boost::noncopyable { + friend class Condition; + +public: + typedef ::qpid::sys::ScopedLock<Mutex> ScopedLock; + typedef ::qpid::sys::ScopedUnlock<Mutex> ScopedUnlock; + + inline Mutex(); + inline ~Mutex(); + inline void lock(); + inline void unlock(); + inline bool trylock(); + + +protected: + pthread_mutex_t mutex; +}; + +/** + * RW lock. + */ +class RWlock : private boost::noncopyable { + friend class Condition; + +public: + typedef ::qpid::sys::ScopedRlock<RWlock> ScopedRlock; + typedef ::qpid::sys::ScopedWlock<RWlock> ScopedWlock; + + inline RWlock(); + inline ~RWlock(); + inline void wlock(); // will write-lock + inline void rlock(); // will read-lock + inline void unlock(); + inline void trywlock(); // will write-try + inline void tryrlock(); // will read-try + +protected: + pthread_rwlock_t rwlock; +}; + + +/** + * Initialise a recursive mutex attr for use in creating mutexes later + * (we use pthread_once to make sure it is initialised exactly once) + */ +namespace { + pthread_once_t onceControl = PTHREAD_ONCE_INIT; + pthread_rwlockattr_t rwlockattr; + pthread_mutexattr_t mutexattr; + + void initMutexattr() { + pthread_mutexattr_init(&mutexattr); + pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE); + } + + void initRWlockattr() { + pthread_rwlockattr_init(&rwlockattr); + } + + struct RecursiveMutexattr { + RecursiveMutexattr() { + pthread_once(&onceControl, initMutexattr); + } + + operator const pthread_mutexattr_t*() const { + return &mutexattr; + } + }; + struct RecursiveRWlockattr { + RecursiveRWlockattr() { + pthread_once(&onceControl, initRWlockattr); + } + + operator const pthread_rwlockattr_t*() const { + return &rwlockattr; + } + }; + + RecursiveMutexattr recursiveMutexattr; + RecursiveRWlockattr recursiveRWlockattr; + + + +} + +/** + * PODMutex is a POD, can be static-initialized with + * PODMutex m = QPID_PODMUTEX_INITIALIZER + */ +struct PODMutex +{ + typedef ::qpid::sys::ScopedLock<PODMutex> ScopedLock; + + inline void lock(); + inline void unlock(); + inline bool trylock(); + + // Must be public to be a POD: + pthread_mutex_t mutex; +}; + +#define QPID_MUTEX_INITIALIZER { PTHREAD_MUTEX_INITIALIZER } + +void PODMutex::lock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_lock(&mutex)); +} + +void PODMutex::unlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_unlock(&mutex)); +} + +bool PODMutex::trylock() { + return pthread_mutex_trylock(&mutex) == 0; +} + +Mutex::Mutex() { + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_init(&mutex, recursiveMutexattr)); +} + +Mutex::~Mutex(){ + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_destroy(&mutex)); +} + +void Mutex::lock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_lock(&mutex)); +} + +void Mutex::unlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_mutex_unlock(&mutex)); +} + +bool Mutex::trylock() { + return pthread_mutex_trylock(&mutex) == 0; +} + + +RWlock::RWlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_init(&rwlock, recursiveRWlockattr)); +} + +RWlock::~RWlock(){ + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_destroy(&rwlock)); +} + +void RWlock::wlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_wrlock(&rwlock)); +} + +void RWlock::rlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_rdlock(&rwlock)); +} + +void RWlock::unlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_unlock(&rwlock)); +} + +void RWlock::trywlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_trywrlock(&rwlock)); +} + +void RWlock::tryrlock() { + QPID_POSIX_ASSERT_THROW_IF(pthread_rwlock_tryrdlock(&rwlock)); +} + + +}} +#endif /*!_sys_posix_Mutex_h*/ diff --git a/qpid/cpp/src/qpid/sys/posix/PrivatePosix.h b/qpid/cpp/src/qpid/sys/posix/PrivatePosix.h new file mode 100644 index 0000000000..9ec9770cab --- /dev/null +++ b/qpid/cpp/src/qpid/sys/posix/PrivatePosix.h @@ -0,0 +1,44 @@ +#ifndef _sys_posix_PrivatePosix_h +#define _sys_posix_PrivatePosix_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. + * + */ + +#include "qpid/sys/Time.h" + +struct timespec; +struct timeval; + +namespace qpid { +namespace sys { + +// Private Time related implementation details +struct timespec& toTimespec(struct timespec& ts, const Duration& t); +struct timeval& toTimeval(struct timeval& tv, const Duration& t); +Duration toTime(const struct timespec& ts); + +// Private socket related implementation details +class SocketPrivate; +int toFd(const SocketPrivate* s); + +}} + +#endif /*!_sys_posix_PrivatePosix_h*/ diff --git a/qpid/cpp/src/qpid/sys/posix/Shlib.cpp b/qpid/cpp/src/qpid/sys/posix/Shlib.cpp new file mode 100644 index 0000000000..1552aa06b5 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/posix/Shlib.cpp @@ -0,0 +1,59 @@ +/* + * 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. + * + */ + +#include "qpid/sys/Shlib.h" +#include "qpid/Exception.h" +#include <dlfcn.h> + + +namespace qpid { +namespace sys { + +void Shlib::load(const char* name) { + dlerror(); + handle = ::dlopen(name, RTLD_NOW); + const char* error = ::dlerror(); + if (error) { + throw Exception(QPID_MSG(error)); + } +} + +void Shlib::unload() { + if (handle) { + ::dlerror(); + ::dlclose(handle); + const char* error = ::dlerror(); + if (error) { + throw Exception(QPID_MSG(error)); + } + handle = 0; + } +} + +void* Shlib::getSymbol(const char* name) { + ::dlerror(); + void* sym = ::dlsym(handle, name); + const char* error = ::dlerror(); + if (error) + throw Exception(QPID_MSG(error)); + return sym; +} + +}} // namespace qpid::sys diff --git a/qpid/cpp/src/qpid/sys/posix/Socket.cpp b/qpid/cpp/src/qpid/sys/posix/Socket.cpp new file mode 100644 index 0000000000..c286ebce27 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/posix/Socket.cpp @@ -0,0 +1,283 @@ +/* + * + * 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. + * + */ + +#include "qpid/sys/Socket.h" + +#include "check.h" +#include "PrivatePosix.h" + +#include <fcntl.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/errno.h> +#include <netinet/in.h> +#include <netdb.h> +#include <cstdlib> +#include <string.h> + +#include <boost/format.hpp> + +namespace qpid { +namespace sys { + +class SocketPrivate { +public: + SocketPrivate(int f = -1) : + fd(f) + {} + + int fd; + + std::string getName(bool local, bool includeService = false) const; + std::string getService(bool local) const; +}; + +std::string SocketPrivate::getName(bool local, bool includeService) const +{ + ::sockaddr_storage name; // big enough for any socket address + ::socklen_t namelen = sizeof(name); + + int result = -1; + if (local) { + result = ::getsockname(fd, (::sockaddr*)&name, &namelen); + } else { + result = ::getpeername(fd, (::sockaddr*)&name, &namelen); + } + + QPID_POSIX_CHECK(result); + + char servName[NI_MAXSERV]; + char dispName[NI_MAXHOST]; + if (includeService) { + if (int rc=::getnameinfo((::sockaddr*)&name, namelen, dispName, sizeof(dispName), + servName, sizeof(servName), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + throw QPID_POSIX_ERROR(rc); + return std::string(dispName) + ":" + std::string(servName); + + } else { + if (int rc=::getnameinfo((::sockaddr*)&name, namelen, dispName, sizeof(dispName), 0, 0, NI_NUMERICHOST) != 0) + throw QPID_POSIX_ERROR(rc); + return dispName; + } +} + +std::string SocketPrivate::getService(bool local) const +{ + ::sockaddr_storage name; // big enough for any socket address + ::socklen_t namelen = sizeof(name); + + int result = -1; + if (local) { + result = ::getsockname(fd, (::sockaddr*)&name, &namelen); + } else { + result = ::getpeername(fd, (::sockaddr*)&name, &namelen); + } + + QPID_POSIX_CHECK(result); + + char servName[NI_MAXSERV]; + if (int rc=::getnameinfo((::sockaddr*)&name, namelen, 0, 0, + servName, sizeof(servName), + NI_NUMERICHOST | NI_NUMERICSERV) != 0) + throw QPID_POSIX_ERROR(rc); + return servName; +} + +Socket::Socket() : + impl(new SocketPrivate) +{ + createTcp(); +} + +Socket::Socket(SocketPrivate* sp) : + impl(sp) +{} + +Socket::~Socket() { + delete impl; +} + +void Socket::createTcp() const +{ + int& socket = impl->fd; + if (socket != -1) Socket::close(); + int s = ::socket (PF_INET, SOCK_STREAM, 0); + if (s < 0) throw QPID_POSIX_ERROR(errno); + socket = s; +} + +void Socket::setTimeout(const Duration& interval) const +{ + const int& socket = impl->fd; + struct timeval tv; + toTimeval(tv, interval); + setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); + setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); +} + +void Socket::setNonblocking() const { + QPID_POSIX_CHECK(::fcntl(impl->fd, F_SETFL, O_NONBLOCK)); +} + +namespace { +const char* h_errstr(int e) { + switch (e) { + case HOST_NOT_FOUND: return "Host not found"; + case NO_ADDRESS: return "Name does not have an IP address"; + case TRY_AGAIN: return "A temporary error occurred on an authoritative name server."; + case NO_RECOVERY: return "Non-recoverable name server error"; + default: return "Unknown error"; + } +} +} + +void Socket::connect(const std::string& host, int port) const +{ + const int& socket = impl->fd; + struct sockaddr_in name; + name.sin_family = AF_INET; + name.sin_port = htons(port); + // TODO: Be good to make this work for IPv6 as well as IPv4 + // Use more modern lookup functions + struct hostent* hp = gethostbyname ( host.c_str() ); + if (hp == 0) + throw Exception(QPID_MSG("Cannot resolve " << host << ": " << h_errstr(h_errno))); + ::memcpy(&name.sin_addr.s_addr, hp->h_addr_list[0], hp->h_length); + if (::connect(socket, (struct sockaddr*)(&name), sizeof(name)) < 0) + throw qpid::Exception(QPID_MSG(strError(errno) << ": " << host << ":" << port)); +} + +void +Socket::close() const +{ + int& socket = impl->fd; + if (socket == -1) return; + if (::close(socket) < 0) throw QPID_POSIX_ERROR(errno); + socket = -1; +} + +ssize_t +Socket::send(const void* data, size_t size) const +{ + const int& socket = impl->fd; + ssize_t sent = ::send(socket, data, size, 0); + if (sent < 0) { + if (errno == ECONNRESET) return SOCKET_EOF; + if (errno == ETIMEDOUT) return SOCKET_TIMEOUT; + throw QPID_POSIX_ERROR(errno); + } + return sent; +} + +ssize_t +Socket::recv(void* data, size_t size) const +{ + const int& socket = impl->fd; + ssize_t received = ::recv(socket, data, size, 0); + if (received < 0) { + if (errno == ETIMEDOUT) return SOCKET_TIMEOUT; + throw QPID_POSIX_ERROR(errno); + } + return received; +} + +int Socket::listen(int port, int backlog) const +{ + const int& socket = impl->fd; + int yes=1; + QPID_POSIX_CHECK(setsockopt(socket,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof(yes))); + struct sockaddr_in name; + name.sin_family = AF_INET; + name.sin_port = htons(port); + name.sin_addr.s_addr = 0; + if (::bind(socket, (struct sockaddr*)&name, sizeof(name)) < 0) + throw QPID_POSIX_ERROR(errno); + if (::listen(socket, backlog) < 0) + throw QPID_POSIX_ERROR(errno); + + socklen_t namelen = sizeof(name); + if (::getsockname(socket, (struct sockaddr*)&name, &namelen) < 0) + throw QPID_POSIX_ERROR(errno); + + return ntohs(name.sin_port); +} + +Socket* Socket::accept(struct sockaddr *addr, socklen_t *addrlen) const +{ + int afd = ::accept(impl->fd, addr, addrlen); + if ( afd >= 0) + return new Socket(new SocketPrivate(afd)); + else if (errno == EAGAIN) + return 0; + else throw QPID_POSIX_ERROR(errno); +} + +int Socket::read(void *buf, size_t count) const +{ + return ::read(impl->fd, buf, count); +} + +int Socket::write(const void *buf, size_t count) const +{ + return ::write(impl->fd, buf, count); +} + +std::string Socket::getSockname() const +{ + return impl->getName(true); +} + +std::string Socket::getPeername() const +{ + return impl->getName(false); +} + +std::string Socket::getPeerAddress() const +{ + return impl->getName(false, true); +} + +std::string Socket::getLocalAddress() const +{ + return impl->getName(true, true); +} + +uint16_t Socket::getLocalPort() const +{ + return atoi(impl->getService(true).c_str()); +} + +uint16_t Socket::getRemotePort() const +{ + return atoi(impl->getService(true).c_str()); +} + +int Socket::toFd() const { + return impl->fd; +} + +int toFd(const SocketPrivate* s) +{ + return s->fd; +} + +}} // namespace qpid::sys diff --git a/qpid/cpp/src/qpid/sys/posix/Thread.cpp b/qpid/cpp/src/qpid/sys/posix/Thread.cpp new file mode 100644 index 0000000000..dc9b21448f --- /dev/null +++ b/qpid/cpp/src/qpid/sys/posix/Thread.cpp @@ -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. + * + */ + +#include "Thread.h" +#include "qpid/sys/Runnable.h" + +void* qpid::sys::Thread::runRunnable(void* p) +{ + static_cast<Runnable*>(p)->run(); + return 0; +} diff --git a/qpid/cpp/src/qpid/sys/posix/Thread.h b/qpid/cpp/src/qpid/sys/posix/Thread.h new file mode 100644 index 0000000000..7d51ec73aa --- /dev/null +++ b/qpid/cpp/src/qpid/sys/posix/Thread.h @@ -0,0 +1,92 @@ +#ifndef _sys_posix_Thread_h +#define _sys_posix_Thread_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. + * + */ + +#include "check.h" +#include <pthread.h> + +namespace qpid { +namespace sys { + +class Runnable; + +class Thread +{ + public: + inline static Thread current(); + + /** ID of current thread for logging. + * Workaround for broken Thread::current() in APR + */ + static unsigned long logId() { return current().id(); } + + inline static void yield(); + + inline Thread(); + inline explicit Thread(qpid::sys::Runnable*); + inline explicit Thread(qpid::sys::Runnable&); + + inline void join(); + + inline unsigned long id(); + + private: + static void* runRunnable(void* runnable); + inline Thread(pthread_t); + pthread_t thread; +}; + + +Thread::Thread() : thread(0) {} + +Thread::Thread(Runnable* runnable) { + QPID_POSIX_ASSERT_THROW_IF(pthread_create(&thread, NULL, runRunnable, runnable)); +} + +Thread::Thread(Runnable& runnable) { + QPID_POSIX_ASSERT_THROW_IF(pthread_create(&thread, NULL, runRunnable, &runnable)); +} + +void Thread::join(){ + if (thread != 0) + QPID_POSIX_ASSERT_THROW_IF(pthread_join(thread, 0)); +} + +unsigned long Thread::id() { + return thread; +} + +Thread::Thread(pthread_t thr) : thread(thr) {} + +Thread Thread::current() { + return Thread(pthread_self()); +} + +void Thread::yield() +{ + QPID_POSIX_ASSERT_THROW_IF(pthread_yield()); +} + + +}} +#endif /*!_sys_posix_Thread_h*/ diff --git a/qpid/cpp/src/qpid/sys/posix/Time.cpp b/qpid/cpp/src/qpid/sys/posix/Time.cpp new file mode 100644 index 0000000000..09627cdc6b --- /dev/null +++ b/qpid/cpp/src/qpid/sys/posix/Time.cpp @@ -0,0 +1,81 @@ +/* + * + * 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. + * + */ + +#include "PrivatePosix.h" + +#include "qpid/sys/Time.h" +#include <ostream> +#include <time.h> +#include <sys/time.h> + +namespace qpid { +namespace sys { + +AbsTime AbsTime::now() { + struct timespec ts; + ::clock_gettime(CLOCK_REALTIME, &ts); + AbsTime time_now; + time_now.time_ns = toTime(ts).nanosecs; + return time_now; +} + +struct timespec& toTimespec(struct timespec& ts, const Duration& t) { + ts.tv_sec = t / TIME_SEC; + ts.tv_nsec = t % TIME_SEC; + return ts; +} + +struct timeval& toTimeval(struct timeval& tv, const Duration& t) { + tv.tv_sec = t/TIME_SEC; + tv.tv_usec = (t%TIME_SEC)/TIME_USEC; + return tv; +} + +Duration toTime(const struct timespec& ts) { + return ts.tv_sec*TIME_SEC + ts.tv_nsec; +} + +std::ostream& operator<<(std::ostream& o, const Duration& d) { + return o << int64_t(d) << "ns"; +} + +std::ostream& operator<<(std::ostream& o, const AbsTime& t) { + static const char * month_abbrevs[] = { + "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" + }; + struct tm * timeinfo; + time_t rawtime(t.time_ns/TIME_SEC); + timeinfo = localtime (&rawtime); + char time_string[100]; + sprintf ( time_string, + "%d-%s-%02d %02d:%02d:%02d", + 1900 + timeinfo->tm_year, + month_abbrevs[timeinfo->tm_mon], + timeinfo->tm_mday, + timeinfo->tm_hour, + timeinfo->tm_min, + timeinfo->tm_sec + ); + return o << time_string; +} + +}} + diff --git a/qpid/cpp/src/qpid/sys/posix/check.h b/qpid/cpp/src/qpid/sys/posix/check.h new file mode 100644 index 0000000000..aa93c37205 --- /dev/null +++ b/qpid/cpp/src/qpid/sys/posix/check.h @@ -0,0 +1,47 @@ +#ifndef _posix_check_h +#define _posix_check_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. + * + */ + +#include "qpid/Exception.h" +#include <cerrno> +#include <assert.h> + +#define QPID_POSIX_ERROR(ERRNO) qpid::Exception(QPID_MSG(qpid::strError(ERRNO))) + +/** THROW QPID_POSIX_ERROR(errno) if RESULT is less than zero */ +#define QPID_POSIX_CHECK(RESULT) \ + if ((RESULT) < 0) throw QPID_POSIX_ERROR((errno)) + +/** Throw a posix error if ERRNO is non-zero */ +#define QPID_POSIX_THROW_IF(ERRNO) \ + do { int e=(ERRNO); if (e) throw QPID_POSIX_ERROR(e); } while(0) + +/** Same as _THROW_IF in a release build, but abort a debug build */ +#ifdef NDEBUG +#define QPID_POSIX_ASSERT_THROW_IF(ERRNO) QPID_POSIX_THROW_IF(ERRNO) +#else +#define QPID_POSIX_ASSERT_THROW_IF(ERRNO) \ + do { int e=(ERRNO); if (e) { errno=e; perror(0); assert(0); } } while(0) +#endif + +#endif /*!_posix_check_h*/ diff --git a/qpid/cpp/src/qpidd.cpp b/qpid/cpp/src/qpidd.cpp new file mode 100644 index 0000000000..08b907cbe2 --- /dev/null +++ b/qpid/cpp/src/qpidd.cpp @@ -0,0 +1,260 @@ +/* + * + * 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. + * + */ +#include "qpid/broker/Broker.h" +#include "qpid/sys/posix/check.h" +#include "qpid/broker/Daemon.h" +#include "qpid/log/Statement.h" +#include "qpid/log/Options.h" +#include "qpid/log/Logger.h" +#include "qpid/Plugin.h" +#include "qpid/sys/Shlib.h" +#include "config.h" +#include <boost/filesystem/operations.hpp> +#include <boost/filesystem/path.hpp> +#include <iostream> +#include <fstream> +#include <signal.h> +#include <unistd.h> + +using namespace qpid; +using namespace qpid::broker; +using namespace qpid::sys; +using namespace qpid::log; +using namespace std; +namespace fs=boost::filesystem; + +struct ModuleOptions : public qpid::Options { + string loadDir; + vector<string> load; + bool noLoad; + ModuleOptions() : qpid::Options("Module options"), loadDir("/usr/lib/qpidd"), noLoad(false) + { + addOptions() + ("module-dir", optValue(loadDir, "DIR"), "Load all .so modules in this directory") + ("load-module", optValue(load, "FILE"), "Specifies additional module(s) to be loaded") + ("no-module-dir", optValue(noLoad), "Don't load modules from module directory"); + } +}; + + +struct DaemonOptions : public qpid::Options { + bool daemon; + bool quit; + bool check; + int wait; + + DaemonOptions() : qpid::Options("Daemon options"), daemon(false), quit(false), check(false), wait(10) + { + addOptions() + ("daemon,d", optValue(daemon), "Run as a daemon.") + ("wait,w", optValue(wait, "SECONDS"), "Sets the maximum wait time to initialize the daemon. If the daemon fails to initialize, prints an error and returns 1") + ("check,c", optValue(check), "Prints the daemon's process ID to stdout and returns 0 if the daemon is running, otherwise returns 1") + ("quit,q", optValue(quit), "Tells the daemon to shut down"); + } +}; + + +struct QpiddOptions : public qpid::Options { + CommonOptions common; + ModuleOptions module; + Broker::Options broker; + DaemonOptions daemon; + qpid::log::Options log; + + QpiddOptions() : qpid::Options("Options"), common("", "/etc/qpidd.conf") { + add(common); + add(module); + add(broker); + add(daemon); + add(log); + const Plugin::Plugins& plugins= + Plugin::getPlugins(); + for (Plugin::Plugins::const_iterator i = plugins.begin(); + i != plugins.end(); + ++i) + if ((*i)->getOptions() != 0) + add(*(*i)->getOptions()); + } + + void usage() const { + cout << "Usage: qpidd [OPTIONS]" << endl << endl << *this << endl; + }; +}; + +// BootstrapOptions is a minimal subset of options used for a pre-parse +// of the command line to discover which plugin modules need to be loaded. +// The pre-parse is necessary because plugin modules may supply their own +// set of options. CommonOptions is needed to properly support loading +// from a configuration file. +struct BootstrapOptions : public qpid::Options { + CommonOptions common; + ModuleOptions module; + qpid::log::Options log; + + BootstrapOptions() : qpid::Options("Options"), common("", "/etc/qpidd.conf") { + add(common); + add(module); + add(log); + } +}; + +// Globals +shared_ptr<Broker> brokerPtr; +auto_ptr<QpiddOptions> options; + +void shutdownHandler(int /*signal*/){ + // Note: do not call any async-signal unsafe functions here. + // Do any extra shutdown actions in main() after broker->run() + brokerPtr->shutdown(); +} + +struct QpiddDaemon : public Daemon { + /** Code for parent process */ + void parent() { + uint16_t port = wait(options->daemon.wait); + if (options->broker.port == 0) + cout << port << endl; + } + + /** Code for forked child process */ + void child() { + brokerPtr.reset(new Broker(options->broker)); + uint16_t port=brokerPtr->getPort(); + ready(port); // Notify parent. + brokerPtr->run(); + brokerPtr.reset(); + } +}; + +void tryShlib(const char* libname, bool noThrow) { + try { + Shlib shlib(libname); + QPID_LOG (info, "Loaded Module: " << libname); + } + catch (const exception& e) { + if (!noThrow) + throw; + } +} + +void loadModuleDir (string dirname, bool isDefault) +{ + fs::path dirPath (dirname, fs::native); + + if (!fs::exists (dirPath)) + { + if (isDefault) + return; + throw Exception ("Directory not found: " + dirname); + } + + fs::directory_iterator endItr; + for (fs::directory_iterator itr (dirPath); itr != endItr; ++itr) + { + if (!fs::is_directory(*itr) && + itr->string().find (".so") == itr->string().length() - 3) + tryShlib (itr->string().data(), true); + } +} + + +int main(int argc, char* argv[]) +{ + try + { + { + BootstrapOptions bootOptions; + string defaultPath (bootOptions.module.loadDir); + + // Parse only the common, load, and log options to see which modules need + // to be loaded. Once the modules are loaded, the command line will + // be re-parsed with all of the module-supplied options. + bootOptions.parse (argc, argv, bootOptions.common.config, true); + qpid::log::Logger::instance().configure(bootOptions.log, argv[0]); + + for (vector<string>::iterator iter = bootOptions.module.load.begin(); + iter != bootOptions.module.load.end(); + iter++) + tryShlib (iter->data(), false); + + if (!bootOptions.module.noLoad) { + bool isDefault = defaultPath == bootOptions.module.loadDir; + loadModuleDir (bootOptions.module.loadDir, isDefault); + } + } + + // Parse options + options.reset(new QpiddOptions()); + options->parse(argc, argv, options->common.config); + + // Options that just print information. + if(options->common.help || options->common.version) { + if (options->common.version) + cout << "qpidd (" << PACKAGE_NAME << ") version " + << PACKAGE_VERSION << endl; + else if (options->common.help) + options->usage(); + return 0; + } + + // Options that affect a running daemon. + if (options->daemon.check || options->daemon.quit) { + pid_t pid = Daemon::getPid(options->broker.port); + if (pid < 0) + return 1; + if (options->daemon.check) + cout << pid << endl; + if (options->daemon.quit && kill(pid, SIGINT) < 0) + throw Exception("Failed to stop daemon: " + strError(errno)); + return 0; + } + + // Starting the broker. + + // Signal handling + signal(SIGINT,shutdownHandler); + signal(SIGTERM,shutdownHandler); + signal(SIGHUP,SIG_IGN); // TODO aconway 2007-07-18: reload config. + + signal(SIGCHLD,SIG_IGN); + signal(SIGTSTP,SIG_IGN); + signal(SIGTTOU,SIG_IGN); + signal(SIGTTIN,SIG_IGN); + + if (options->daemon.daemon) { + // Fork the daemon + QpiddDaemon d; + d.fork(); + } + else { // Non-daemon broker. + brokerPtr.reset(new Broker(options->broker)); + if (options->broker.port == 0) + cout << uint16_t(brokerPtr->getPort()) << endl; + brokerPtr->run(); + QPID_LOG(notice, "Shutting down."); + } + return 0; + } + catch(const exception& e) { + cerr << e.what() << endl; + } + return 1; +} diff --git a/qpid/cpp/src/tests/.valgrind.supp b/qpid/cpp/src/tests/.valgrind.supp new file mode 100644 index 0000000000..e0abf0dd43 --- /dev/null +++ b/qpid/cpp/src/tests/.valgrind.supp @@ -0,0 +1,32 @@ +{ + Benign error in libcpg. + Memcheck:Param + socketcall.sendmsg(msg.msg_iov[i]) + obj:*/libpthread-2.5.so + obj:*/libcpg.so.2.0.0 +} + +{ + Uninitialised value problem in _dl_relocate (F7, F8) + Memcheck:Cond + fun:_dl_relocate_object + fun:*dl_* +} + +{ + False "possibly leaked" in boost program_options - global std::string var. + Memcheck:Leak + fun:_Znwj + fun:_ZNSs4_Rep9_S_createEjjRKSaIcE + obj:/usr/lib/libstdc++.so.6.0.8 + fun:_ZNSsC1EPKcRKSaIcE + obj:/usr/lib/libboost_program_options.so.1.33.1 +} + +{ + Bogus epoll_ctl error on i386 + Memcheck:Param + epoll_ctl(event) + fun:epoll_ctl +} + diff --git a/qpid/cpp/src/tests/.valgrindrc b/qpid/cpp/src/tests/.valgrindrc new file mode 100644 index 0000000000..4aba7661de --- /dev/null +++ b/qpid/cpp/src/tests/.valgrindrc @@ -0,0 +1,7 @@ +--gen-suppressions=all +--leak-check=full +--demangle=yes +--suppressions=.valgrind.supp +--num-callers=25 +--trace-children=yes + diff --git a/qpid/cpp/src/tests/AccumulatedAckTest.cpp b/qpid/cpp/src/tests/AccumulatedAckTest.cpp new file mode 100644 index 0000000000..cbe44e6814 --- /dev/null +++ b/qpid/cpp/src/tests/AccumulatedAckTest.cpp @@ -0,0 +1,250 @@ + +/* + * + * 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. + * + */ +#include "qpid/framing/AccumulatedAck.h" +#include "qpid_test_plugin.h" +#include <iostream> +#include <list> + +using std::list; +using namespace qpid::framing; + +class AccumulatedAckTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(AccumulatedAckTest); + CPPUNIT_TEST(testGeneral); + CPPUNIT_TEST(testCovers); + CPPUNIT_TEST(testUpdateFromCompletionData); + CPPUNIT_TEST(testCase1); + CPPUNIT_TEST(testCase2); + CPPUNIT_TEST(testCase3); + CPPUNIT_TEST(testCase4); + CPPUNIT_TEST(testConsolidation1); + CPPUNIT_TEST(testConsolidation2); + CPPUNIT_TEST(testConsolidation3); + CPPUNIT_TEST(testConsolidation4); + CPPUNIT_TEST_SUITE_END(); + +public: + bool covers(const AccumulatedAck& ack, int i) + { + return ack.covers(SequenceNumber(i)); + } + + void update(AccumulatedAck& ack, int start, int end) + { + ack.update(SequenceNumber(start), SequenceNumber(end)); + } + + void testGeneral() + { + AccumulatedAck ack(0); + ack.clear(); + update(ack, 3,3); + update(ack, 7,7); + update(ack, 9,9); + update(ack, 1,2); + update(ack, 4,5); + update(ack, 6,6); + + for(int i = 1; i <= 7; i++) CPPUNIT_ASSERT(covers(ack, i)); + CPPUNIT_ASSERT(covers(ack, 9)); + + CPPUNIT_ASSERT(!covers(ack, 8)); + CPPUNIT_ASSERT(!covers(ack, 10)); + + ack.consolidate(); + + for(int i = 1; i <= 7; i++) CPPUNIT_ASSERT(covers(ack, i)); + CPPUNIT_ASSERT(covers(ack, 9)); + + CPPUNIT_ASSERT(!covers(ack, 8)); + CPPUNIT_ASSERT(!covers(ack, 10)); + } + + void testCovers() + { + AccumulatedAck ack(5); + update(ack, 7, 7); + update(ack, 9, 9); + + CPPUNIT_ASSERT(covers(ack, 1)); + CPPUNIT_ASSERT(covers(ack, 2)); + CPPUNIT_ASSERT(covers(ack, 3)); + CPPUNIT_ASSERT(covers(ack, 4)); + CPPUNIT_ASSERT(covers(ack, 5)); + CPPUNIT_ASSERT(covers(ack, 7)); + CPPUNIT_ASSERT(covers(ack, 9)); + + CPPUNIT_ASSERT(!covers(ack, 6)); + CPPUNIT_ASSERT(!covers(ack, 8)); + CPPUNIT_ASSERT(!covers(ack, 10)); + } + + void testUpdateFromCompletionData() + { + AccumulatedAck ack(0); + SequenceNumber mark(2); + SequenceNumberSet ranges; + ranges.addRange(SequenceNumber(5), SequenceNumber(8)); + ranges.addRange(SequenceNumber(10), SequenceNumber(15)); + ranges.addRange(SequenceNumber(9), SequenceNumber(9)); + ranges.addRange(SequenceNumber(3), SequenceNumber(4)); + + ack.update(mark, ranges); + + for(int i = 0; i <= 15; i++) { + CPPUNIT_ASSERT(covers(ack, i)); + } + CPPUNIT_ASSERT(!covers(ack, 16)); + CPPUNIT_ASSERT_EQUAL((uint32_t) 15, ack.mark.getValue()); + } + + void testCase1() + { + AccumulatedAck ack(3); + update(ack, 1,2); + for(int i = 1; i <= 3; i++) CPPUNIT_ASSERT(covers(ack, i)); + CPPUNIT_ASSERT(!covers(ack, 4)); + } + + void testCase2() + { + AccumulatedAck ack(3); + update(ack, 3,6); + for(int i = 1; i <= 6; i++) CPPUNIT_ASSERT(covers(ack, i)); + CPPUNIT_ASSERT(!covers(ack, 7)); + } + + void testCase3() + { + AccumulatedAck ack(3); + update(ack, 4,6); + for(int i = 1; i <= 6; i++) { + CPPUNIT_ASSERT(covers(ack, i)); + } + CPPUNIT_ASSERT(!covers(ack, 7)); + } + + void testCase4() + { + AccumulatedAck ack(3); + update(ack, 5,6); + for(int i = 1; i <= 6; i++) { + if (i == 4) CPPUNIT_ASSERT(!covers(ack, i)); + else CPPUNIT_ASSERT(covers(ack, i)); + } + CPPUNIT_ASSERT(!covers(ack, 7)); + } + + void testConsolidation1() + { + AccumulatedAck ack(3); + update(ack, 7,7); + CPPUNIT_ASSERT_EQUAL((uint32_t) 3, ack.mark.getValue()); + CPPUNIT_ASSERT_EQUAL((size_t) 1, ack.ranges.size()); + + update(ack, 8,9); + CPPUNIT_ASSERT_EQUAL((uint32_t) 3, ack.mark.getValue()); + CPPUNIT_ASSERT_EQUAL((size_t) 1, ack.ranges.size()); + + update(ack, 1,2); + CPPUNIT_ASSERT_EQUAL((uint32_t) 3, ack.mark.getValue()); + CPPUNIT_ASSERT_EQUAL((size_t) 1, ack.ranges.size()); + + update(ack, 4,5); + CPPUNIT_ASSERT_EQUAL((uint32_t) 5, ack.mark.getValue()); + CPPUNIT_ASSERT_EQUAL((size_t) 1, ack.ranges.size()); + + update(ack, 6,6); + CPPUNIT_ASSERT_EQUAL((uint32_t) 9, ack.mark.getValue()); + CPPUNIT_ASSERT_EQUAL((size_t) 0, ack.ranges.size()); + + for(int i = 1; i <= 9; i++) CPPUNIT_ASSERT(covers(ack, i)); + CPPUNIT_ASSERT(!covers(ack, 10)); + } + + void testConsolidation2() + { + AccumulatedAck ack(0); + update(ack, 10,12); + CPPUNIT_ASSERT_EQUAL((uint32_t) 0, ack.mark.getValue()); + CPPUNIT_ASSERT_EQUAL((size_t) 1, ack.ranges.size()); + + update(ack, 7,9); + CPPUNIT_ASSERT_EQUAL((uint32_t) 0, ack.mark.getValue()); + CPPUNIT_ASSERT_EQUAL((size_t) 1, ack.ranges.size()); + CPPUNIT_ASSERT_EQUAL((uint32_t) 7, ack.ranges.front().start.getValue()); + CPPUNIT_ASSERT_EQUAL((uint32_t) 12, ack.ranges.front().end.getValue()); + + update(ack, 5,7); + CPPUNIT_ASSERT_EQUAL((uint32_t) 0, ack.mark.getValue()); + CPPUNIT_ASSERT_EQUAL((size_t) 1, ack.ranges.size()); + CPPUNIT_ASSERT_EQUAL((uint32_t) 5, ack.ranges.front().start.getValue()); + CPPUNIT_ASSERT_EQUAL((uint32_t) 12, ack.ranges.front().end.getValue()); + + update(ack, 3,4); + CPPUNIT_ASSERT_EQUAL((uint32_t) 0, ack.mark.getValue()); + CPPUNIT_ASSERT_EQUAL((size_t) 1, ack.ranges.size()); + CPPUNIT_ASSERT_EQUAL((uint32_t) 3, ack.ranges.front().start.getValue()); + CPPUNIT_ASSERT_EQUAL((uint32_t) 12, ack.ranges.front().end.getValue()); + + update(ack, 1,2); + CPPUNIT_ASSERT_EQUAL((uint32_t) 12, ack.mark.getValue()); + CPPUNIT_ASSERT_EQUAL((size_t) 0, ack.ranges.size()); + + for(int i = 1; i <= 12; i++) CPPUNIT_ASSERT(covers(ack, i)); + CPPUNIT_ASSERT(!covers(ack, 13)); + } + + void testConsolidation3() + { + AccumulatedAck ack(0); + update(ack, 10,12); + update(ack, 6,7); + update(ack, 3,4); + update(ack, 1,15); + CPPUNIT_ASSERT_EQUAL((uint32_t) 15, ack.mark.getValue()); + CPPUNIT_ASSERT_EQUAL((size_t) 0, ack.ranges.size()); + } + + void testConsolidation4() + { + AccumulatedAck ack(0); + ack.update(SequenceNumber(0), SequenceNumber(2)); + ack.update(SequenceNumber(5), SequenceNumber(8)); + ack.update(SequenceNumber(10), SequenceNumber(15)); + ack.update(SequenceNumber(9), SequenceNumber(9)); + ack.update(SequenceNumber(3), SequenceNumber(4)); + + for(int i = 0; i <= 15; i++) { + CPPUNIT_ASSERT(covers(ack, i)); + } + CPPUNIT_ASSERT(!covers(ack, 16)); + CPPUNIT_ASSERT_EQUAL((uint32_t) 15, ack.mark.getValue()); + } + +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(AccumulatedAckTest); + diff --git a/qpid/cpp/src/tests/Array.cpp b/qpid/cpp/src/tests/Array.cpp new file mode 100644 index 0000000000..336e57b485 --- /dev/null +++ b/qpid/cpp/src/tests/Array.cpp @@ -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. + * + */ +#include <iostream> +#include <sstream> +#include "qpid/framing/Array.h" +#include "qpid/framing/FieldValue.h" + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(ArrayTestSuite) + +using namespace qpid::framing; + +void populate(std::vector<std::string>& data, int count = 10) +{ + for (int i = 0; i < count; i++) { + std::stringstream out; + out << "item-" << i; + data.push_back(out.str()); + } +} + +BOOST_AUTO_TEST_CASE(testEncodeDecode) +{ + std::vector<std::string> data; + populate(data); + + Array a(data); + + char buff[200]; + Buffer wbuffer(buff, 200); + a.encode(wbuffer); + + Array b; + Buffer rbuffer(buff, 200); + b.decode(rbuffer); + BOOST_CHECK_EQUAL(a, b); + + std::vector<std::string> data2; + b.collect(data2); + //BOOST_CHECK_EQUAL(data, data2); + BOOST_CHECK(data == data2); +} + +BOOST_AUTO_TEST_CASE(testAssignment) +{ + std::vector<std::string> data; + populate(data); + Array b; + { + Array a(data); + b = a; + BOOST_CHECK_EQUAL(a, b); + } + std::vector<std::string> data2; + b.collect(data2); + //BOOST_CHECK_EQUAL(data, data2); + BOOST_CHECK(data == data2); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/BasicP2PTest.cpp b/qpid/cpp/src/tests/BasicP2PTest.cpp new file mode 100644 index 0000000000..b202f88ca6 --- /dev/null +++ b/qpid/cpp/src/tests/BasicP2PTest.cpp @@ -0,0 +1,66 @@ +/* + * + * 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. + * + */ + +#include "BasicP2PTest.h" + +using namespace qpid; +using namespace qpid::client; + +class BasicP2PTest::Receiver : public Worker, public MessageListener +{ + const std::string queue; + std::string tag; +public: + Receiver(TestOptions& options, const std::string& _queue, const int _messages) + : Worker(options, _messages), queue(_queue){} + void init() + { + Queue q(queue, true); + channel.declareQueue(q); + framing::FieldTable args; + channel.bind(Exchange::STANDARD_DIRECT_EXCHANGE, q, queue, args); + channel.consume(q, tag, this); + channel.start(); + } + + void start() + { + } + + void received(Message&) + { + count++; + } +}; + +void BasicP2PTest::assign(const std::string& role, framing::FieldTable& params, TestOptions& options) +{ + std::string queue = params.getString("P2P_QUEUE_AND_KEY_NAME"); + int messages = params.getInt("P2P_NUM_MESSAGES"); + if (role == "SENDER") { + worker = std::auto_ptr<Worker>(new Sender(options, Exchange::STANDARD_DIRECT_EXCHANGE, queue, messages)); + } else if(role == "RECEIVER"){ + worker = std::auto_ptr<Worker>(new Receiver(options, queue, messages)); + } else { + throw Exception("unrecognised role"); + } + worker->init(); +} diff --git a/qpid/cpp/src/tests/BasicP2PTest.h b/qpid/cpp/src/tests/BasicP2PTest.h new file mode 100644 index 0000000000..3f0a3704f5 --- /dev/null +++ b/qpid/cpp/src/tests/BasicP2PTest.h @@ -0,0 +1,46 @@ +#ifndef _BasicP2PTest_ +#define _BasicP2PTest_ +/* + * + * 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. + * + */ + +#include <memory> +#include <sstream> + +#include "qpid/Exception.h" +#include "qpid/client/Channel.h" +#include "qpid/client/Message.h" +#include "qpid/client/Connection.h" +#include "qpid/client/MessageListener.h" +#include "SimpleTestCaseBase.h" + + +namespace qpid { + +class BasicP2PTest : public SimpleTestCaseBase +{ + class Receiver; +public: + void assign(const std::string& role, framing::FieldTable& params, TestOptions& options); +}; + +} + +#endif diff --git a/qpid/cpp/src/tests/BasicPubSubTest.cpp b/qpid/cpp/src/tests/BasicPubSubTest.cpp new file mode 100644 index 0000000000..623194d331 --- /dev/null +++ b/qpid/cpp/src/tests/BasicPubSubTest.cpp @@ -0,0 +1,121 @@ +/* + * + * 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. + * + */ + +#include "BasicPubSubTest.h" + +using namespace qpid; + +class BasicPubSubTest::Receiver : public Worker, public MessageListener +{ + const Exchange& exchange; + const std::string queue; + const std::string key; + std::string tag; +public: + Receiver(TestOptions& options, const Exchange& _exchange, const std::string& _queue, const std::string& _key, const int _messages) + : Worker(options, _messages), exchange(_exchange), queue(_queue), key(_key){} + + void init() + { + Queue q(queue, true); + channel.declareQueue(q); + framing::FieldTable args; + channel.bind(exchange, q, key, args); + channel.consume(q, tag, this); + channel.start(); + } + + void start(){ + } + + void received(Message&) + { + count++; + } +}; + +class BasicPubSubTest::MultiReceiver : public Worker, public MessageListener +{ + typedef boost::ptr_vector<Receiver> ReceiverList; + ReceiverList receivers; + +public: + MultiReceiver(TestOptions& options, const Exchange& exchange, const std::string& key, const int _messages, int receiverCount) + : Worker(options, _messages) + { + for (int i = 0; i != receiverCount; i++) { + std::string queue = (boost::format("%1%_%2%") % options.clientid % i).str(); + receivers.push_back(new Receiver(options, exchange, queue, key, _messages)); + } + } + + void init() + { + for (ReceiverList::size_type i = 0; i != receivers.size(); i++) { + receivers[i].init(); + } + } + + void start() + { + for (ReceiverList::size_type i = 0; i != receivers.size(); i++) { + receivers[i].start(); + } + } + + void received(Message& msg) + { + for (ReceiverList::size_type i = 0; i != receivers.size(); i++) { + receivers[i].received(msg); + } + } + + virtual int getCount() + { + count = 0; + for (ReceiverList::size_type i = 0; i != receivers.size(); i++) { + count += receivers[i].getCount(); + } + return count; + } + virtual void stop() + { + for (ReceiverList::size_type i = 0; i != receivers.size(); i++) { + receivers[i].stop(); + } + } +}; + +void BasicPubSubTest::assign(const std::string& role, framing::FieldTable& params, TestOptions& options) +{ + std::string key = params.getString("PUBSUB_KEY"); + int messages = params.getInt("PUBSUB_NUM_MESSAGES"); + int receivers = params.getInt("PUBSUB_NUM_RECEIVERS"); + if (role == "SENDER") { + worker = std::auto_ptr<Worker>(new Sender(options, Exchange::STANDARD_TOPIC_EXCHANGE, key, messages)); + } else if(role == "RECEIVER"){ + worker = std::auto_ptr<Worker>(new MultiReceiver(options, Exchange::STANDARD_TOPIC_EXCHANGE, key, messages, receivers)); + } else { + throw Exception("unrecognised role"); + } + worker->init(); +} + diff --git a/qpid/cpp/src/tests/BasicPubSubTest.h b/qpid/cpp/src/tests/BasicPubSubTest.h new file mode 100644 index 0000000000..c3f8020b3a --- /dev/null +++ b/qpid/cpp/src/tests/BasicPubSubTest.h @@ -0,0 +1,51 @@ +#ifndef _BasicPubSubTest_ +#define _BasicPubSubTest_ +/* + * + * 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. + * + */ + +#include <memory> +#include <sstream> + +#include "qpid/Exception.h" +#include "qpid/client/Channel.h" +#include "qpid/client/Message.h" +#include "qpid/client/Connection.h" +#include "qpid/client/MessageListener.h" +#include "SimpleTestCaseBase.h" +#include <boost/ptr_container/ptr_vector.hpp> +#include <boost/format.hpp> + + +namespace qpid { + +using namespace qpid::client; + +class BasicPubSubTest : public SimpleTestCaseBase +{ + class Receiver; + class MultiReceiver; +public: + void assign(const std::string& role, framing::FieldTable& params, TestOptions& options); +}; + +} + +#endif diff --git a/qpid/cpp/src/tests/Blob.cpp b/qpid/cpp/src/tests/Blob.cpp new file mode 100644 index 0000000000..d27c0bbd85 --- /dev/null +++ b/qpid/cpp/src/tests/Blob.cpp @@ -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. + */ +#include "qpid/framing/Blob.h" + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(BlobTestSuite) + +using namespace std; +using namespace qpid::framing; + +struct Base { + int id; + int magic; + + Base(int n) : id(n), magic(42) {} + Base(const Base& c) : id(c.id), magic(42) {} + ~Base() { BOOST_CHECK_EQUAL(42, magic); } // Detect random data. +}; + +template <class T> struct Count : public Base { + static int instances; + bool destroyed; + + Count(int n) : Base(n), destroyed(false) { ++instances; } + Count(const Count& c) : Base(c), destroyed(false) { ++instances; } + ~Count() { + BOOST_CHECK(!destroyed); // Detect double-destructor + destroyed=true; + BOOST_CHECK(--instances >= 0); + } +}; + +template <class T> int Count<T>::instances = 0; + +struct Foo : public Count<Foo> { Foo(int n) : Count<Foo>(n) {}; }; +struct Bar : public Count<Bar> { Bar(int n) : Count<Bar>(n) {}; }; + +typedef Blob<sizeof(Foo), Base> TestBlob ; + +BOOST_AUTO_TEST_CASE(testCtor) { + { + TestBlob empty; + BOOST_CHECK(empty.empty()); + BOOST_CHECK(empty.get() == 0); + + TestBlob empty2(empty); + BOOST_CHECK(empty2.empty()); + + TestBlob foo(in_place<Foo>(1)); + BOOST_CHECK(!foo.empty()); + BOOST_CHECK_EQUAL(1, foo.get()->id); + BOOST_CHECK_EQUAL(1, Foo::instances); + + TestBlob foo2(foo); + BOOST_CHECK(!foo2.empty()); + BOOST_CHECK_EQUAL(1, foo2.get()->id); + BOOST_CHECK_EQUAL(2, Foo::instances); + } + + BOOST_CHECK_EQUAL(0, Foo::instances); + BOOST_CHECK_EQUAL(0, Bar::instances); +} + + +BOOST_AUTO_TEST_CASE(testAssign) { + { + TestBlob b; + b = Foo(2); + BOOST_CHECK_EQUAL(2, b.get()->id); + BOOST_CHECK_EQUAL(1, Foo::instances); + + TestBlob b2(b); + BOOST_CHECK_EQUAL(2, b.get()->id); + BOOST_CHECK_EQUAL(2, Foo::instances); + + b2 = Bar(3); + BOOST_CHECK_EQUAL(3, b2.get()->id); + BOOST_CHECK_EQUAL(1, Foo::instances); + BOOST_CHECK_EQUAL(1, Bar::instances); + + b2 = in_place<Foo>(4); + BOOST_CHECK_EQUAL(4, b2.get()->id); + BOOST_CHECK_EQUAL(2, Foo::instances); + BOOST_CHECK_EQUAL(0, Bar::instances); + + b2.clear(); + BOOST_CHECK(b2.empty()); + BOOST_CHECK_EQUAL(1, Foo::instances); + } + BOOST_CHECK_EQUAL(0, Foo::instances); + BOOST_CHECK_EQUAL(0, Bar::instances); +} + + +BOOST_AUTO_TEST_CASE(testClear) { + TestBlob b(in_place<Foo>(5)); + TestBlob c(b); + BOOST_CHECK(!c.empty()); + BOOST_CHECK(!b.empty()); + BOOST_CHECK_EQUAL(2, Foo::instances); + + c.clear(); + BOOST_CHECK(c.empty()); + BOOST_CHECK_EQUAL(1, Foo::instances); + + b.clear(); + BOOST_CHECK(b.empty()); + BOOST_CHECK_EQUAL(0, Foo::instances); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/BrokerFixture.h b/qpid/cpp/src/tests/BrokerFixture.h new file mode 100644 index 0000000000..83b3f621c7 --- /dev/null +++ b/qpid/cpp/src/tests/BrokerFixture.h @@ -0,0 +1,108 @@ +#ifndef TESTS_BROKERFIXTURE_H +#define TESTS_BROKERFIXTURE_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. + * + */ + +#include "SocketProxy.h" +#include "qpid/sys/Thread.h" +#include "qpid/broker/Broker.h" +#include "qpid/client/Connection.h" +#include "qpid/client/ConnectionImpl.h" +#include "qpid/client/Session.h" +#include "qpid/client/SubscriptionManager.h" + +/** + * A fixture with an in-process broker. + */ +struct BrokerFixture { + typedef qpid::broker::Broker Broker; + typedef boost::shared_ptr<Broker> BrokerPtr; + + BrokerPtr broker; + qpid::sys::Thread brokerThread; + + BrokerFixture() { + Broker::Options opts; + opts.port=0; + // Management doesn't play well with multiple in-process brokers. + opts.enableMgmt=false; + opts.workerThreads=1; + opts.dataDir=""; + opts.auth=false; + broker = Broker::create(opts); + // TODO aconway 2007-12-05: At one point BrokerFixture + // tests could hang in Connection ctor if the following + // line is removed. This may not be an issue anymore. + broker->getPort(); + brokerThread = qpid::sys::Thread(*broker); + }; + + ~BrokerFixture() { + broker->shutdown(); + brokerThread.join(); + } + + /** Open a connection to the broker. */ + void open(qpid::client::Connection& c) { + c.open("localhost", broker->getPort()); + } +}; + +struct LocalConnection : public qpid::client::Connection { + LocalConnection(uint16_t port) { open("localhost", port); } +}; + +/** A local client connection via a socket proxy. */ +struct ProxyConnection : public qpid::client::Connection { + SocketProxy proxy; + ProxyConnection(int brokerPort) : proxy(brokerPort) { + open("localhost", proxy.getPort()); + } + ~ProxyConnection() { close(); } +}; + +/** + * A BrokerFixture with open Connection, Session and + * SubscriptionManager and LocalQueue for convenience. + */ +template <class ConnectionType> +struct SessionFixtureT : BrokerFixture { + ConnectionType connection; + qpid::client::Session session; + qpid::client::SubscriptionManager subs; + qpid::client::LocalQueue lq; + + SessionFixtureT() : connection(broker->getPort()), + session(connection.newSession(qpid::client::ASYNC)), + subs(session) + {} + + ~SessionFixtureT() { + connection.close(); + } +}; + +typedef SessionFixtureT<LocalConnection> SessionFixture; +typedef SessionFixtureT<ProxyConnection> ProxySessionFixture; + + +#endif /*!TESTS_BROKERFIXTURE_H*/ diff --git a/qpid/cpp/src/tests/ClientChannelTest.cpp b/qpid/cpp/src/tests/ClientChannelTest.cpp new file mode 100644 index 0000000000..605d5e4885 --- /dev/null +++ b/qpid/cpp/src/tests/ClientChannelTest.cpp @@ -0,0 +1,220 @@ +/* + * + * 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. + * + */ +#include <vector> +#include "qpid_test_plugin.h" +#include "BrokerFixture.h" +#include "qpid/client/Channel.h" +#include "qpid/client/Message.h" +#include "qpid/client/Queue.h" +#include "qpid/client/Exchange.h" +#include "qpid/client/MessageListener.h" +#include "qpid/client/BasicMessageChannel.h" +#include "qpid/client/MessageMessageChannel.h" + +using namespace std; +using namespace boost; +using namespace qpid::client; +using namespace qpid::sys; +using namespace qpid::framing; + +/// Small frame size so we can create fragmented messages. +const size_t FRAME_MAX = 256; + + +/** + * Test base for client API using an in-process broker. + * The test base defines the tests methods, derived classes + * instantiate the channel in Basic or Message mode. + */ +class ChannelTestBase : public CppUnit::TestCase, public SessionFixture +{ + struct Listener: public qpid::client::MessageListener { + vector<Message> messages; + Monitor monitor; + void received(Message& msg) { + Mutex::ScopedLock l(monitor); + messages.push_back(msg); + monitor.notifyAll(); + } + }; + + const std::string qname; + const std::string data; + Queue queue; + Exchange exchange; + Listener listener; + + protected: + boost::scoped_ptr<Channel> channel; + + public: + + ChannelTestBase() + : qname("testq"), data("hello"), + queue(qname, true), exchange("", Exchange::DIRECT_EXCHANGE) + {} + + void setUp() { + CPPUNIT_ASSERT(channel); + connection.openChannel(*channel); + CPPUNIT_ASSERT(channel->getId() != 0); + channel->declareQueue(queue); + } + + void testPublishGet() { + Message pubMsg(data); + pubMsg.getHeaders().setString("hello", "world"); + channel->publish(pubMsg, exchange, qname); + Message getMsg; + CPPUNIT_ASSERT(channel->get(getMsg, queue)); + CPPUNIT_ASSERT_EQUAL(data, getMsg.getData()); + CPPUNIT_ASSERT_EQUAL(string("world"), + getMsg.getHeaders().getString("hello")); + CPPUNIT_ASSERT(!channel->get(getMsg, queue)); // Empty queue + } + + void testGetNoContent() { + Message pubMsg; + pubMsg.getHeaders().setString("hello", "world"); + channel->publish(pubMsg, exchange, qname); + Message getMsg; + CPPUNIT_ASSERT(channel->get(getMsg, queue)); + CPPUNIT_ASSERT(getMsg.getData().empty()); + CPPUNIT_ASSERT_EQUAL(string("world"), + getMsg.getHeaders().getString("hello")); + } + + void testConsumeCancel() { + string tag; // Broker assigned + channel->consume(queue, tag, &listener); + channel->start(); + CPPUNIT_ASSERT_EQUAL(size_t(0), listener.messages.size()); + channel->publish(Message("a"), exchange, qname); + { + Mutex::ScopedLock l(listener.monitor); + Time deadline(now() + 1*TIME_SEC); + while (listener.messages.size() != 1) { + CPPUNIT_ASSERT(listener.monitor.wait(deadline)); + } + } + CPPUNIT_ASSERT_EQUAL(size_t(1), listener.messages.size()); + CPPUNIT_ASSERT_EQUAL(string("a"), listener.messages[0].getData()); + + channel->publish(Message("b"), exchange, qname); + channel->publish(Message("c"), exchange, qname); + { + Mutex::ScopedLock l(listener.monitor); + while (listener.messages.size() != 3) { + CPPUNIT_ASSERT(listener.monitor.wait(1*TIME_SEC)); + } + } + CPPUNIT_ASSERT_EQUAL(size_t(3), listener.messages.size()); + CPPUNIT_ASSERT_EQUAL(string("b"), listener.messages[1].getData()); + CPPUNIT_ASSERT_EQUAL(string("c"), listener.messages[2].getData()); + + channel->cancel(tag); + channel->publish(Message("d"), exchange, qname); + CPPUNIT_ASSERT_EQUAL(size_t(3), listener.messages.size()); + { + Mutex::ScopedLock l(listener.monitor); + CPPUNIT_ASSERT(!listener.monitor.wait(TIME_SEC/2)); + } + Message msg; + CPPUNIT_ASSERT(channel->get(msg, queue)); + CPPUNIT_ASSERT_EQUAL(string("d"), msg.getData()); + } + + // Consume already-published messages + void testConsumePublished() { + Message pubMsg("x"); + pubMsg.getHeaders().setString("y", "z"); + channel->publish(pubMsg, exchange, qname); + string tag; + channel->consume(queue, tag, &listener); + CPPUNIT_ASSERT_EQUAL(size_t(0), listener.messages.size()); + channel->start(); + { + Mutex::ScopedLock l(listener.monitor); + while (listener.messages.size() != 1) + CPPUNIT_ASSERT(listener.monitor.wait(1*TIME_SEC)); + } + CPPUNIT_ASSERT_EQUAL(string("x"), listener.messages[0].getData()); + CPPUNIT_ASSERT_EQUAL(string("z"), + listener.messages[0].getHeaders().getString("y")); + } + + void testGetFragmentedMessage() { + string longStr(FRAME_MAX*2, 'x'); // Longer than max frame size. + channel->publish(Message(longStr), exchange, qname); + Message getMsg; + CPPUNIT_ASSERT(channel->get(getMsg, queue)); + } + + void testConsumeFragmentedMessage() { + string xx(FRAME_MAX*2, 'x'); + channel->publish(Message(xx), exchange, qname); + channel->start(); + string tag; + channel->consume(queue, tag, &listener); + string yy(FRAME_MAX*2, 'y'); + channel->publish(Message(yy), exchange, qname); + { + Mutex::ScopedLock l(listener.monitor); + while (listener.messages.size() != 2) + CPPUNIT_ASSERT(listener.monitor.wait(1*TIME_SEC)); + } + CPPUNIT_ASSERT_EQUAL(xx, listener.messages[0].getData()); + CPPUNIT_ASSERT_EQUAL(yy, listener.messages[1].getData()); + } +}; + +class BasicChannelTest : public ChannelTestBase { + CPPUNIT_TEST_SUITE(BasicChannelTest); + CPPUNIT_TEST(testPublishGet); + CPPUNIT_TEST(testGetNoContent); + CPPUNIT_TEST(testConsumeCancel); + CPPUNIT_TEST(testConsumePublished); + CPPUNIT_TEST(testGetFragmentedMessage); + CPPUNIT_TEST(testConsumeFragmentedMessage); + CPPUNIT_TEST_SUITE_END(); + + public: + BasicChannelTest(){ + channel.reset(new Channel(false, 500, Channel::AMQP_08)); + } +}; + +class MessageChannelTest : public ChannelTestBase { + CPPUNIT_TEST_SUITE(MessageChannelTest); + CPPUNIT_TEST(testPublishGet); + CPPUNIT_TEST(testGetNoContent); + CPPUNIT_TEST(testGetFragmentedMessage); + CPPUNIT_TEST_SUITE_END(); + public: + MessageChannelTest() { + channel.reset(new Channel(false, 500, Channel::AMQP_09)); + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(BasicChannelTest); +CPPUNIT_TEST_SUITE_REGISTRATION(MessageChannelTest); diff --git a/qpid/cpp/src/tests/ClientSessionTest.cpp b/qpid/cpp/src/tests/ClientSessionTest.cpp new file mode 100644 index 0000000000..7a997db327 --- /dev/null +++ b/qpid/cpp/src/tests/ClientSessionTest.cpp @@ -0,0 +1,220 @@ +/* + * + * 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. + * + */ +#include "unit_test.h" +#include "BrokerFixture.h" +#include "qpid/client/Dispatcher.h" +#include "qpid/sys/Monitor.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Runnable.h" +#include "qpid/client/Session.h" +#include "qpid/framing/TransferContent.h" +#include "qpid/framing/reply_exceptions.h" + +#include <boost/optional.hpp> +#include <boost/lexical_cast.hpp> + +#include <vector> + +QPID_AUTO_TEST_SUITE(ClientSessionTest) + +using namespace qpid::client; +using namespace qpid::client::arg; +using namespace qpid::framing; +using namespace qpid; +using qpid::sys::Monitor; +using std::string; +using std::cout; +using std::endl; +using namespace boost; + + +struct DummyListener : public sys::Runnable, public MessageListener { + std::vector<Message> messages; + string name; + uint expected; + Dispatcher dispatcher; + + DummyListener(Session& session, const string& n, uint ex) : + name(n), expected(ex), dispatcher(session) {} + + void run() + { + dispatcher.listen(name, this); + dispatcher.run(); + } + + void received(Message& msg) + { + messages.push_back(msg); + if (--expected == 0) + dispatcher.stop(); + } +}; + +struct SimpleListener : public MessageListener +{ + Monitor lock; + std::vector<Message> messages; + + void received(Message& msg) + { + Monitor::ScopedLock l(lock); + messages.push_back(msg); + lock.notifyAll(); + } + + void waitFor(const uint n) + { + Monitor::ScopedLock l(lock); + while (messages.size() < n) { + lock.wait(); + } + } +}; + +struct ClientSessionFixture : public ProxySessionFixture +{ + void declareSubscribe(const string& q="my-queue", + const string& dest="my-dest") + { + session.queueDeclare(queue=q); + session.messageSubscribe(queue=q, destination=dest, acquireMode=1); + session.messageFlow(destination=dest, unit=0, value=0xFFFFFFFF);//messages + session.messageFlow(destination=dest, unit=1, value=0xFFFFFFFF);//bytes + } +}; + +BOOST_FIXTURE_TEST_CASE(testQueueQuery, ClientSessionFixture) { + session =connection.newSession(ASYNC); + session.queueDeclare(queue="my-queue", alternateExchange="amq.fanout", exclusive=true, autoDelete=true); + TypedResult<QueueQueryResult> result = session.queueQuery(string("my-queue")); + BOOST_CHECK_EQUAL(false, result.get().getDurable()); + BOOST_CHECK_EQUAL(true, result.get().getExclusive()); + BOOST_CHECK_EQUAL(string("amq.fanout"), + result.get().getAlternateExchange()); +} + +BOOST_FIXTURE_TEST_CASE(testTransfer, ClientSessionFixture) +{ + session=connection.newSession(ASYNC); + declareSubscribe(); + session.messageTransfer(content=TransferContent("my-message", "my-queue")); + //get & test the message: + FrameSet::shared_ptr msg = session.get(); + BOOST_CHECK(msg->isA<MessageTransferBody>()); + BOOST_CHECK_EQUAL(string("my-message"), msg->getContent()); + //confirm receipt: + session.getExecution().completed(msg->getId(), true, true); +} + +BOOST_FIXTURE_TEST_CASE(testDispatcher, ClientSessionFixture) +{ + session =connection.newSession(ASYNC); + declareSubscribe(); + size_t count = 100; + for (size_t i = 0; i < count; ++i) + session.messageTransfer(content=TransferContent(lexical_cast<string>(i), "my-queue")); + DummyListener listener(session, "my-dest", count); + listener.run(); + BOOST_REQUIRE_EQUAL(count, listener.messages.size()); + for (size_t i = 0; i < count; ++i) + BOOST_CHECK_EQUAL(lexical_cast<string>(i), listener.messages[i].getData()); +} + +/* FIXME aconway 2008-01-28: hangs +BOOST_FIXTURE_TEST_CASE(testDispatcherThread, ClientSessionFixture) +{ + session =connection.newSession(ASYNC); + declareSubscribe(); + size_t count = 10000; + DummyListener listener(session, "my-dest", count); + sys::Thread t(listener); + for (size_t i = 0; i < count; ++i) { + session.messageTransfer(content=TransferContent(lexical_cast<string>(i), "my-queue")); + if (i%100 == 0) cout << "T" << i << std::flush; + } + t.join(); + BOOST_REQUIRE_EQUAL(count, listener.messages.size()); + for (size_t i = 0; i < count; ++i) + BOOST_CHECK_EQUAL(lexical_cast<string>(i), listener.messages[i].getData()); +} +*/ + +BOOST_FIXTURE_TEST_CASE(_FIXTURE, ClientSessionFixture) +{ + session =connection.newSession(ASYNC, 0); + session.suspend(); // session has 0 timeout. + try { + connection.resume(session); + BOOST_FAIL("Expected InvalidArgumentException."); + } catch(const InternalErrorException&) {} +} + +BOOST_FIXTURE_TEST_CASE(testUseSuspendedError, ClientSessionFixture) +{ + session =connection.newSession(ASYNC, 60); + session.suspend(); + try { + session.exchangeQuery(name="amq.fanout"); + BOOST_FAIL("Expected session suspended exception"); + } catch(const CommandInvalidException&) {} +} + +BOOST_FIXTURE_TEST_CASE(testSuspendResume, ClientSessionFixture) +{ + session =connection.newSession(ASYNC, 60); + declareSubscribe(); + session.suspend(); + // Make sure we are still subscribed after resume. + connection.resume(session); + session.messageTransfer(content=TransferContent("my-message", "my-queue")); + FrameSet::shared_ptr msg = session.get(); + BOOST_CHECK_EQUAL(string("my-message"), msg->getContent()); +} + +/** + * Currently broken due to a deadlock in SessionCore + * +BOOST_FIXTURE_TEST_CASE(testSendToSelf, SessionFixture) { + // Deadlock if SubscriptionManager run() concurrent with session ack. + SimpleListener mylistener; + session.queueDeclare(queue="myq", exclusive=true, autoDelete=true); + subs.subscribe(mylistener, "myq", "myq"); + sys::Thread runner(subs);//start dispatcher thread + string data("msg"); + Message msg(data, "myq"); + const uint count=10000; + for (uint i = 0; i < count; ++i) { + session.messageTransfer(content=msg); + } + mylistener.waitFor(count); + subs.cancel("myq"); + subs.stop(); + session.close(); + BOOST_CHECK_EQUAL(mylistener.messages.size(), count); + for (uint j = 0; j < count; ++j) { + BOOST_CHECK_EQUAL(mylistener.messages[j].getData(), data); + } +} +*/ + +QPID_AUTO_TEST_SUITE_END() + diff --git a/qpid/cpp/src/tests/ConcurrentQueue.cpp b/qpid/cpp/src/tests/ConcurrentQueue.cpp new file mode 100644 index 0000000000..c6ca40e897 --- /dev/null +++ b/qpid/cpp/src/tests/ConcurrentQueue.cpp @@ -0,0 +1,208 @@ +/* + * + * 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. + * + */ + +/**@file + * Compare alternative implementations for BlockingQueue. + */ + +#include "qpid/sys/BlockingQueue.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Monitor.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Time.h" + +#include <boost/test/test_tools.hpp> +#include <boost/bind.hpp> + +#include <deque> +#include <vector> +#include <iostream> + +#include "time.h" + +using namespace qpid::sys; +using namespace std; + +template <class T> class DualVectorDualLockQueue { + public: + /** Optionally specify initial capacity of the queue to minimize + * re-allocation. + */ + DualVectorDualLockQueue(size_t capacity=16) { + pushVec.reserve(capacity); + popVec.reserve(capacity); + popIter = popVec.end(); + } + + /** Push a data item onto the back of the queue */ + void push(const T& data) { + Mutex::ScopedLock l(pushLock); + pushVec.push_back(data); + } + + /** If the queue is non-empty, pop the front item into data and + * return true. If the queue is empty, return false + */ + bool tryPop(T& data) { + Mutex::ScopedLock l(popLock); + if (popIter == popVec.end()) { + popVec.clear(); + Mutex::ScopedLock l(pushLock); + pushVec.swap(popVec); + popIter = popVec.begin(); + } + if (popIter == popVec.end()) + return false; + else { + data = *popIter++; + return true; + } + } + + private: + Mutex pushLock, popLock; + std::vector<T> pushVec, popVec; + typename std::vector<T>::iterator popIter; +}; + +template <class T> struct LockedDequeQueue : public BlockingQueue<T> { + /** size_t ignored, can't pre-allocate space in a dequeue */ + LockedDequeQueue(size_t=0) {}; +}; + +// ================ Test code. + +/** Pause by sleeping */ +void nsleep(const Duration& delay) { + static Monitor m; + AbsTime stop(now(), delay); + while (now() < stop) + m.wait(stop); +} + +/** Pause by spinning */ +void nspin(const Duration& delay) { + AbsTime stop(now(), delay); + while (now() < stop) + ; +} + +/** Unlocked fake queue for comparison */ +struct NullQueue { + NullQueue(int items=0) : npush(items), npop(items) {} + void push(int) { --npush; } + bool tryPop(int& n) { + if (npop == 0) + return false; + else { + n=npop--; + return true; + } + } + volatile int npush, npop; +}; + + +// Global test parameters. +int items; +Duration delay(0); +boost::function<void()> npause; + +template <class Q> +struct Pusher : public Runnable { + Pusher(Q& q) : queue(q) {} + void run() { + for (int i=items; i > 0; i--) { + queue.push(i); + npause(); + } + } + Q& queue; +}; + +template <class Q> +struct Popper : public Runnable { + Popper(Q& q) : queue(q) {} + void run() { + for (int i=items; i > 0; i--) { + int n; + if (queue.tryPop(n)) + BOOST_REQUIRE_EQUAL(i,n); + npause(); + } + } + Q& queue; +}; + +ostream& operator<<(ostream& out, const Duration& d) { + return out << double(d)/TIME_MSEC << " msecs"; +} + +void report(const char* s, const Duration &d) { + cout << s << ": " << d + << " (" << (double(items)*TIME_SEC)/d << " push-pops/sec" << ")" + << endl; +} + +template <class Q, class PusherT=Pusher<Q>, class PopperT=Popper<Q> > +struct Timer { + static Duration time() { + cout << endl << "==" << typeid(Q).name() << endl; + + Q queue(items); + PusherT pusher(queue); + PopperT popper(queue); + + // Serial + AbsTime start=now(); + pusher.run(); + popper.run(); + Duration serial(start,now()); + report ("Serial", serial); + + // Concurrent + start=now(); + Thread pushThread(pusher); + Thread popThread(popper); + pushThread.join(); + popThread.join(); + Duration concurrent(start,now()); + report ("Concurrent", concurrent); + + cout << "Serial/concurrent: " << double(serial)/concurrent << endl; + return concurrent; + } +}; + +int test_main(int argc, char** argv) { + items = (argc > 1) ? atoi(argv[1]) : 250*1000; + delay = (argc > 2) ? atoi(argv[2]) : 4*1000; + npause=boost::bind(nspin, delay); + + cout << "Push/pop " << items << " items, delay=" << delay << endl; + Timer<NullQueue>::time(); + Duration dv = Timer<DualVectorDualLockQueue<int> >::time(); + Duration d = Timer<LockedDequeQueue<int> >::time(); + cout << endl; + cout << "Ratio deque/dual vector=" << double(d)/dv << endl; + return 0; +} +// namespace diff --git a/qpid/cpp/src/tests/Cpg.cpp b/qpid/cpp/src/tests/Cpg.cpp new file mode 100644 index 0000000000..e8339bf773 --- /dev/null +++ b/qpid/cpp/src/tests/Cpg.cpp @@ -0,0 +1,115 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + + +#include "test_tools.h" +#include "qpid/cluster/Cpg.h" +#include "qpid/framing/AMQBody.h" + +#include <boost/bind.hpp> +#include "unit_test.h" + +#include <string> +#include <iostream> +#include <iterator> +#include <vector> +#include <algorithm> + +QPID_AUTO_TEST_SUITE(CpgTestSuite) + + +using namespace std; +using namespace qpid::cluster; +using namespace qpid::framing; + +// For debugging: op << for CPG types. + +ostream& operator<<(ostream& o, const cpg_name* n) { + return o << qpid::cluster::Cpg::str(*n); +} + +ostream& operator<<(ostream& o, const cpg_address& a) { + return o << "(" << a.nodeid <<","<<a.pid<<","<<a.reason<<")"; +} + +template <class T> +ostream& operator<<(ostream& o, const pair<T*, int>& array) { + o << "{ "; + ostream_iterator<cpg_address> i(o, " "); + copy(array.first, array.first+array.second, i); + o << "}"; + return o; +} + +struct Callback : public Cpg::Handler { + Callback(const string group_) : group(group_) {} + string group; + vector<string> delivered; + vector<int> configChanges; + + void deliver ( + cpg_handle_t /*handle*/, + struct cpg_name *grp, + uint32_t /*nodeid*/, + uint32_t /*pid*/, + void* msg, + int msg_len) + { + BOOST_CHECK_EQUAL(group, Cpg::str(*grp)); + delivered.push_back(string((char*)msg,msg_len)); + } + + void configChange( + cpg_handle_t /*handle*/, + struct cpg_name *grp, + struct cpg_address */*members*/, int nMembers, + struct cpg_address */*left*/, int nLeft, + struct cpg_address */*joined*/, int nJoined + ) + { + BOOST_CHECK_EQUAL(group, Cpg::str(*grp)); + configChanges.push_back(nMembers); + BOOST_MESSAGE("configChange: "<< + nLeft<<" left "<< + nJoined<<" joined "<< + nMembers<<" members."); + } +}; + +BOOST_AUTO_TEST_CASE(CpgBasic) { + // Verify basic functionality of cpg. This will catch any + // openais configuration or permission errors. + // + Cpg::Name group("CpgBasic"); + Callback cb(group.str()); + Cpg cpg(cb); + cpg.join(group); + iovec iov = { (void*)"Hello!", 6 }; + cpg.mcast(group, &iov, 1); + cpg.leave(group); + cpg.dispatchSome(); + + BOOST_REQUIRE_EQUAL(1u, cb.delivered.size()); + BOOST_CHECK_EQUAL("Hello!", cb.delivered.front()); + BOOST_REQUIRE_EQUAL(2u, cb.configChanges.size()); + BOOST_CHECK_EQUAL(1, cb.configChanges[0]); + BOOST_CHECK_EQUAL(0, cb.configChanges[1]); +} + + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/DeliveryRecordTest.cpp b/qpid/cpp/src/tests/DeliveryRecordTest.cpp new file mode 100644 index 0000000000..9487f743d6 --- /dev/null +++ b/qpid/cpp/src/tests/DeliveryRecordTest.cpp @@ -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. + * + */ +#include "qpid/broker/DeliveryRecord.h" +#include "qpid_test_plugin.h" +#include <iostream> +#include <memory> +#include <boost/format.hpp> + +using namespace qpid::broker; +using namespace qpid::sys; +using namespace qpid::framing; +using boost::dynamic_pointer_cast; +using std::list; + +class DeliveryRecordTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(DeliveryRecordTest); + CPPUNIT_TEST(testSort); + CPPUNIT_TEST_SUITE_END(); + +public: + + void testSort() + { + list<SequenceNumber> ids; + ids.push_back(SequenceNumber(6)); + ids.push_back(SequenceNumber(2)); + ids.push_back(SequenceNumber(4)); + ids.push_back(SequenceNumber(5)); + ids.push_back(SequenceNumber(1)); + ids.push_back(SequenceNumber(3)); + + list<DeliveryRecord> records; + for (list<SequenceNumber>::iterator i = ids.begin(); i != ids.end(); i++) { + records.push_back(DeliveryRecord(QueuedMessage(0), Queue::shared_ptr(), "tag", DeliveryToken::shared_ptr(), *i, false, false)); + } + records.sort(); + + SequenceNumber expected(0); + for (list<DeliveryRecord>::iterator i = records.begin(); i != records.end(); i++) { + CPPUNIT_ASSERT(i->matches(++expected)); + } + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(DeliveryRecordTest); + diff --git a/qpid/cpp/src/tests/DispatcherTest.cpp b/qpid/cpp/src/tests/DispatcherTest.cpp new file mode 100644 index 0000000000..7631956acc --- /dev/null +++ b/qpid/cpp/src/tests/DispatcherTest.cpp @@ -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. + * + */ + +#include "qpid/sys/Poller.h" +#include "qpid/sys/Dispatcher.h" +#include "qpid/sys/Thread.h" + +#include <sys/types.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +#include <iostream> +#include <boost/bind.hpp> + +using namespace std; +using namespace qpid::sys; + +int writeALot(int fd, const string& s) { + int bytesWritten = 0; + do { + errno = 0; + int lastWrite = ::write(fd, s.c_str(), s.size()); + if ( lastWrite >= 0) { + bytesWritten += lastWrite; + } + } while (errno != EAGAIN); + return bytesWritten; +} + +int readALot(int fd) { + int bytesRead = 0; + char buf[10240]; + + do { + errno = 0; + int lastRead = ::read(fd, buf, sizeof(buf)); + if ( lastRead >= 0) { + bytesRead += lastRead; + } + } while (errno != EAGAIN); + return bytesRead; +} + +int64_t writtenBytes = 0; +int64_t readBytes = 0; + +void writer(DispatchHandle& h, int fd, const string& s) { + writtenBytes += writeALot(fd, s); + h.rewatch(); +} + +void reader(DispatchHandle& h, int fd) { + readBytes += readALot(fd); + h.rewatch(); +} + +int main(int argc, char** argv) +{ + // Create poller + Poller::shared_ptr poller(new Poller); + + // Create dispatcher thread + Dispatcher d(poller); + Dispatcher d1(poller); + //Dispatcher d2(poller); + //Dispatcher d3(poller); + Thread dt(d); + Thread dt1(d1); + //Thread dt2(d2); + //Thread dt3(d3); + + // Setup sender and receiver + int sv[2]; + int rc = ::socketpair(AF_LOCAL, SOCK_STREAM, 0, sv); + assert(rc >= 0); + + // Set non-blocking + rc = ::fcntl(sv[0], F_SETFL, O_NONBLOCK); + assert(rc >= 0); + + rc = ::fcntl(sv[1], F_SETFL, O_NONBLOCK); + assert(rc >= 0); + + // Make up a large string + string testString = "This is only a test ... 1,2,3,4,5,6,7,8,9,10;"; + for (int i = 0; i < 8; i++) + testString += testString; + + DispatchHandle rh(sv[0], boost::bind(reader, _1, sv[0]), 0); + DispatchHandle wh(sv[1], 0, boost::bind(writer, _1, sv[1], testString)); + + rh.watch(poller); + wh.watch(poller); + + // wait 2 minutes then shutdown + sleep(60); + + poller->shutdown(); + dt.join(); + dt1.join(); + //dt2.join(); + //dt3.join(); + + cout << "Wrote: " << writtenBytes << "\n"; + cout << "Read: " << readBytes << "\n"; + + return 0; +} diff --git a/qpid/cpp/src/tests/DtxWorkRecordTest.cpp b/qpid/cpp/src/tests/DtxWorkRecordTest.cpp new file mode 100644 index 0000000000..d7d151f8d6 --- /dev/null +++ b/qpid/cpp/src/tests/DtxWorkRecordTest.cpp @@ -0,0 +1,202 @@ +/* + * + * 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. + * + */ +#include "qpid/broker/DtxWorkRecord.h" +#include "qpid_test_plugin.h" +#include <iostream> +#include <vector> +#include "TxMocks.h" + +using namespace qpid::broker; +using boost::static_pointer_cast; + +class DtxWorkRecordTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(DtxWorkRecordTest); + CPPUNIT_TEST(testOnePhaseCommit); + CPPUNIT_TEST(testFailOnOnePhaseCommit); + CPPUNIT_TEST(testTwoPhaseCommit); + CPPUNIT_TEST(testFailOnTwoPhaseCommit); + CPPUNIT_TEST(testRollback); + CPPUNIT_TEST_SUITE_END(); + + public: + + void testOnePhaseCommit(){ + MockTransactionalStore store; + store.expectBegin().expectCommit(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectCommit(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectPrepare().expectCommit(); + + DtxBuffer::shared_ptr bufferA(new DtxBuffer()); + bufferA->enlist(static_pointer_cast<TxOp>(opA)); + bufferA->markEnded(); + DtxBuffer::shared_ptr bufferB(new DtxBuffer()); + bufferB->enlist(static_pointer_cast<TxOp>(opB)); + bufferB->markEnded(); + + DtxWorkRecord work("my-xid", &store); + work.add(bufferA); + work.add(bufferB); + + work.commit(true); + + store.check(); + CPPUNIT_ASSERT(store.isCommitted()); + opA->check(); + opB->check(); + } + + void testFailOnOnePhaseCommit(){ + MockTransactionalStore store; + store.expectBegin().expectAbort(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp(true)); + opB->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opC(new MockTxOp()); + opC->expectRollback(); + + DtxBuffer::shared_ptr bufferA(new DtxBuffer()); + bufferA->enlist(static_pointer_cast<TxOp>(opA)); + bufferA->markEnded(); + DtxBuffer::shared_ptr bufferB(new DtxBuffer()); + bufferB->enlist(static_pointer_cast<TxOp>(opB)); + bufferB->markEnded(); + DtxBuffer::shared_ptr bufferC(new DtxBuffer()); + bufferC->enlist(static_pointer_cast<TxOp>(opC)); + bufferC->markEnded(); + + DtxWorkRecord work("my-xid", &store); + work.add(bufferA); + work.add(bufferB); + work.add(bufferC); + + work.commit(true); + + CPPUNIT_ASSERT(store.isAborted()); + store.check(); + + opA->check(); + opB->check(); + opC->check(); + } + + void testTwoPhaseCommit(){ + MockTransactionalStore store; + store.expectBegin2PC().expectPrepare().expectCommit(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectCommit(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectPrepare().expectCommit(); + + DtxBuffer::shared_ptr bufferA(new DtxBuffer()); + bufferA->enlist(static_pointer_cast<TxOp>(opA)); + bufferA->markEnded(); + DtxBuffer::shared_ptr bufferB(new DtxBuffer()); + bufferB->enlist(static_pointer_cast<TxOp>(opB)); + bufferB->markEnded(); + + DtxWorkRecord work("my-xid", &store); + work.add(bufferA); + work.add(bufferB); + + CPPUNIT_ASSERT(work.prepare()); + CPPUNIT_ASSERT(store.isPrepared()); + work.commit(false); + store.check(); + CPPUNIT_ASSERT(store.isCommitted()); + opA->check(); + opB->check(); + } + + void testFailOnTwoPhaseCommit(){ + MockTransactionalStore store; + store.expectBegin2PC().expectAbort(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp(true)); + opB->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opC(new MockTxOp()); + opC->expectRollback(); + + DtxBuffer::shared_ptr bufferA(new DtxBuffer()); + bufferA->enlist(static_pointer_cast<TxOp>(opA)); + bufferA->markEnded(); + DtxBuffer::shared_ptr bufferB(new DtxBuffer()); + bufferB->enlist(static_pointer_cast<TxOp>(opB)); + bufferB->markEnded(); + DtxBuffer::shared_ptr bufferC(new DtxBuffer()); + bufferC->enlist(static_pointer_cast<TxOp>(opC)); + bufferC->markEnded(); + + DtxWorkRecord work("my-xid", &store); + work.add(bufferA); + work.add(bufferB); + work.add(bufferC); + + CPPUNIT_ASSERT(!work.prepare()); + CPPUNIT_ASSERT(store.isAborted()); + store.check(); + opA->check(); + opB->check(); + opC->check(); + } + + void testRollback(){ + MockTransactionalStore store; + store.expectBegin2PC().expectPrepare().expectAbort(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectPrepare().expectRollback(); + + DtxBuffer::shared_ptr bufferA(new DtxBuffer()); + bufferA->enlist(static_pointer_cast<TxOp>(opA)); + bufferA->markEnded(); + DtxBuffer::shared_ptr bufferB(new DtxBuffer()); + bufferB->enlist(static_pointer_cast<TxOp>(opB)); + bufferB->markEnded(); + + DtxWorkRecord work("my-xid", &store); + work.add(bufferA); + work.add(bufferB); + + CPPUNIT_ASSERT(work.prepare()); + CPPUNIT_ASSERT(store.isPrepared()); + work.rollback(); + store.check(); + CPPUNIT_ASSERT(store.isAborted()); + opA->check(); + opB->check(); + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(DtxWorkRecordTest); + diff --git a/qpid/cpp/src/tests/EventChannelTest.cpp b/qpid/cpp/src/tests/EventChannelTest.cpp new file mode 100644 index 0000000000..6d8d64e165 --- /dev/null +++ b/qpid/cpp/src/tests/EventChannelTest.cpp @@ -0,0 +1,187 @@ +/* + * + * 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. + * + */ +#include "qpid/sys/posix/EventChannel.h" +#include "qpid/sys/posix/check.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Socket.h" +#include "qpid/sys/Thread.h" +#include "qpid_test_plugin.h" + +#include <sys/socket.h> +#include <signal.h> +#include <netinet/in.h> +#include <netdb.h> +#include <iostream> + +using namespace qpid::sys; + + +const char hello[] = "hello"; +const size_t size = sizeof(hello); + +struct RunMe : public Runnable +{ + bool ran; + RunMe() : ran(false) {} + void run() { ran = true; } +}; + +class EventChannelTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(EventChannelTest); + CPPUNIT_TEST(testEvent); + CPPUNIT_TEST(testRead); + CPPUNIT_TEST(testFailedRead); + CPPUNIT_TEST(testWrite); + CPPUNIT_TEST(testFailedWrite); + CPPUNIT_TEST(testReadWrite); + CPPUNIT_TEST(testAccept); + CPPUNIT_TEST_SUITE_END(); + + private: + EventChannel::shared_ptr ec; + int pipe[2]; + char readBuf[size]; + + public: + + void setUp() + { + memset(readBuf, size, 0); + ec = EventChannel::create(); + if (::pipe(pipe) != 0) throw QPID_POSIX_ERROR(errno); + // Ignore SIGPIPE, otherwise we will crash writing to broken pipe. + signal(SIGPIPE, SIG_IGN); + } + + // Verify that calling getEvent returns event. + template <class T> bool isNextEvent(T& event) + { + return &event == dynamic_cast<T*>(ec->getEvent()); + } + + template <class T> bool isNextEventOk(T& event) + { + Event* next = ec->getEvent(); + if (next) next->throwIfError(); + return &event == next; + } + + void testEvent() + { + RunMe runMe; + CPPUNIT_ASSERT(!runMe.ran); + // Instances of Event just pass thru the channel immediately. + Event e(runMe.functor()); + ec->postEvent(e); + CPPUNIT_ASSERT(isNextEventOk(e)); + e.dispatch(); + CPPUNIT_ASSERT(runMe.ran); + } + + void testRead() { + ReadEvent re(pipe[0], readBuf, size); + ec->postEvent(re); + CPPUNIT_ASSERT_EQUAL(ssize_t(size), ::write(pipe[1], hello, size)); + CPPUNIT_ASSERT(isNextEventOk(re)); + CPPUNIT_ASSERT_EQUAL(size, re.getSize()); + CPPUNIT_ASSERT_EQUAL(std::string(hello), std::string(readBuf)); + } + + void testFailedRead() + { + ReadEvent re(pipe[0], readBuf, size); + ec->postEvent(re); + + // EOF before all data read. + ::close(pipe[1]); + CPPUNIT_ASSERT(isNextEvent(re)); + CPPUNIT_ASSERT(re.hasError()); + try { + re.throwIfError(); + CPPUNIT_FAIL("Expected Exception."); + } + catch (const qpid::Exception&) { } + + // Bad file descriptor. Note in this case we fail + // in postEvent and throw immediately. + try { + ReadEvent bad; + ec->postEvent(bad); + CPPUNIT_FAIL("Expected Exception."); + } + catch (const qpid::Exception&) { } + } + + void testWrite() { + WriteEvent wr(pipe[1], hello, size); + ec->postEvent(wr); + CPPUNIT_ASSERT(isNextEventOk(wr)); + CPPUNIT_ASSERT_EQUAL(ssize_t(size), ::read(pipe[0], readBuf, size));; + CPPUNIT_ASSERT_EQUAL(std::string(hello), std::string(readBuf)); + } + + void testFailedWrite() { + WriteEvent wr(pipe[1], hello, size); + ::close(pipe[0]); + ec->postEvent(wr); + CPPUNIT_ASSERT(isNextEvent(wr)); + CPPUNIT_ASSERT(wr.hasError()); + } + + void testReadWrite() + { + ReadEvent re(pipe[0], readBuf, size); + WriteEvent wr(pipe[1], hello, size); + ec->postEvent(re); + ec->postEvent(wr); + ec->getEvent(); + ec->getEvent(); + CPPUNIT_ASSERT_EQUAL(std::string(hello), std::string(readBuf)); + } + + void testAccept() { + Socket s = Socket::createTcp(); + int port = s.listen(0, 10); + CPPUNIT_ASSERT(port != 0); + + AcceptEvent ae(s.fd()); + ec->postEvent(ae); + Socket client = Socket::createTcp(); + client.connect("localhost", port); + CPPUNIT_ASSERT(isNextEvent(ae)); + ae.dispatch(); + + // Verify client writes are read by the accepted descriptor. + char readBuf[size]; + ReadEvent re(ae.getAcceptedDesscriptor(), readBuf, size); + ec->postEvent(re); + CPPUNIT_ASSERT_EQUAL(ssize_t(size), client.send(hello, sizeof(hello))); + CPPUNIT_ASSERT(isNextEvent(re)); + re.dispatch(); + CPPUNIT_ASSERT_EQUAL(std::string(hello), std::string(readBuf)); + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(EventChannelTest); + diff --git a/qpid/cpp/src/tests/EventChannelThreadsTest.cpp b/qpid/cpp/src/tests/EventChannelThreadsTest.cpp new file mode 100644 index 0000000000..22ea57d675 --- /dev/null +++ b/qpid/cpp/src/tests/EventChannelThreadsTest.cpp @@ -0,0 +1,247 @@ +/* + * + * 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. + * + */ +#include <iostream> +#include <boost/bind.hpp> + +#include "qpid/sys/Socket.h" +#include "qpid/sys/posix/EventChannelThreads.h" +#include "qpid_test_plugin.h" + + +using namespace std; + +using namespace qpid::sys; + +const int nConnections = 5; +const int nMessages = 10; // Messages read/written per connection. + + +// Accepts + reads + writes. +const int totalEvents = nConnections+2*nConnections*nMessages; + +/** + * Messages are numbered 0..nMessages. + * We count the total number of events, and the + * number of reads and writes for each message number. + */ +class TestResults : public Monitor { + public: + TestResults() : isShutdown(false), nEventsRemaining(totalEvents) {} + + void countEvent() { + if (--nEventsRemaining == 0) + shutdown(); + } + + void countRead(int messageNo) { + ++reads[messageNo]; + countEvent(); + } + + void countWrite(int messageNo) { + ++writes[messageNo]; + countEvent(); + } + + void shutdown(const std::string& exceptionMsg = std::string()) { + ScopedLock lock(*this); + exception = exceptionMsg; + isShutdown = true; + notifyAll(); + } + + void wait() { + ScopedLock lock(*this); + Time deadline = now() + 10*TIME_SEC; + while (!isShutdown) { + CPPUNIT_ASSERT(Monitor::wait(deadline)); + } + } + + bool isShutdown; + std::string exception; + AtomicCount reads[nMessages]; + AtomicCount writes[nMessages]; + AtomicCount nEventsRemaining; +}; + +TestResults results; + +EventChannelThreads::shared_ptr threads; + +// Functor to wrap callbacks in try/catch. +class SafeCallback { + public: + SafeCallback(Runnable& r) : callback(r.functor()) {} + SafeCallback(Event::Callback cb) : callback(cb) {} + + void operator()() { + std::string exception; + try { + callback(); + return; + } + catch (const std::exception& e) { + exception = e.what(); + } + catch (...) { + exception = "Unknown exception."; + } + results.shutdown(exception); + } + + private: + Event::Callback callback; +}; + +/** Repost an event N times. */ +class Repost { + public: + Repost(int n) : count (n) {} + virtual ~Repost() {} + + void repost(Event* event) { + if (--count==0) { + delete event; + } else { + threads->postEvent(event); + } + } + private: + int count; +}; + + + +/** Repeating read event. */ +class TestReadEvent : public ReadEvent, public Runnable, private Repost { + public: + explicit TestReadEvent(int fd=-1) : + ReadEvent(fd, &value, sizeof(value), SafeCallback(*this)), + Repost(nMessages) + {} + + void run() { + CPPUNIT_ASSERT_EQUAL(sizeof(value), getSize()); + CPPUNIT_ASSERT(0 <= value); + CPPUNIT_ASSERT(value < nMessages); + results.countRead(value); + repost(this); + } + + private: + int value; + ReadEvent original; +}; + + +/** Fire and forget write event */ +class TestWriteEvent : public WriteEvent, public Runnable, private Repost { + public: + TestWriteEvent(int fd=-1) : + WriteEvent(fd, &value, sizeof(value), SafeCallback(*this)), + Repost(nMessages), + value(0) + {} + + void run() { + CPPUNIT_ASSERT_EQUAL(sizeof(int), getSize()); + results.countWrite(value++); + repost(this); + } + + private: + int value; +}; + +/** Fire-and-forget Accept event, posts reads on the accepted connection. */ +class TestAcceptEvent : public AcceptEvent, public Runnable, private Repost { + public: + TestAcceptEvent(int fd=-1) : + AcceptEvent(fd, SafeCallback(*this)), + Repost(nConnections) + {} + + void run() { + threads->postEvent(new TestReadEvent(getAcceptedDesscriptor())); + results.countEvent(); + repost(this); + } +}; + +class EventChannelThreadsTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(EventChannelThreadsTest); + CPPUNIT_TEST(testThreads); + CPPUNIT_TEST_SUITE_END(); + + public: + + void setUp() { + threads = EventChannelThreads::create(EventChannel::create()); + } + + void tearDown() { + threads.reset(); + } + + void testThreads() + { + Socket listener = Socket::createTcp(); + int port = listener.listen(); + + // Post looping accept events, will repost nConnections times. + // The accept event will automatically post read events. + threads->postEvent(new TestAcceptEvent(listener.fd())); + + // Make connections. + Socket connections[nConnections]; + for (int i = 0; i < nConnections; ++i) { + connections[i] = Socket::createTcp(); + connections[i].connect("localhost", port); + } + + // Post looping write events. + for (int i = 0; i < nConnections; ++i) { + threads->postEvent(new TestWriteEvent(connections[i].fd())); + } + + // Wait for all events to be dispatched. + results.wait(); + + if (!results.exception.empty()) CPPUNIT_FAIL(results.exception); + CPPUNIT_ASSERT_EQUAL(0, int(results.nEventsRemaining)); + + // Expect a read and write for each messageNo from each connection. + for (int messageNo = 0; messageNo < nMessages; ++messageNo) { + CPPUNIT_ASSERT_EQUAL(nConnections, int(results.reads[messageNo])); + CPPUNIT_ASSERT_EQUAL(nConnections, int(results.writes[messageNo])); + } + + threads->shutdown(); + threads->join(); + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(EventChannelThreadsTest); + diff --git a/qpid/cpp/src/tests/ExchangeTest.cpp b/qpid/cpp/src/tests/ExchangeTest.cpp new file mode 100644 index 0000000000..2904424d5c --- /dev/null +++ b/qpid/cpp/src/tests/ExchangeTest.cpp @@ -0,0 +1,179 @@ +/* + * + * 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. + * + */ + +#include "qpid/Exception.h" +#include "qpid/broker/Exchange.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/DeliverableMessage.h" +#include "qpid/broker/DirectExchange.h" +#include "qpid/broker/ExchangeRegistry.h" +#include "qpid/broker/FanOutExchange.h" +#include "qpid/broker/HeadersExchange.h" +#include "qpid/broker/TopicExchange.h" +#include "qpid_test_plugin.h" +#include <iostream> +#include "qpid/framing/BasicGetBody.h" +#include "MessageUtils.h" + +using boost::intrusive_ptr; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; +using namespace qpid; + +class ExchangeTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(ExchangeTest); + CPPUNIT_TEST(testMe); + CPPUNIT_TEST(testIsBound); + CPPUNIT_TEST(testDeleteGetAndRedeclare); + CPPUNIT_TEST_SUITE_END(); + + public: + + void testMe() + { + Queue::shared_ptr queue(new Queue("queue", true)); + Queue::shared_ptr queue2(new Queue("queue2", true)); + + TopicExchange topic("topic"); + topic.bind(queue, "abc", 0); + topic.bind(queue2, "abc", 0); + + DirectExchange direct("direct"); + direct.bind(queue, "abc", 0); + direct.bind(queue2, "abc", 0); + + queue.reset(); + queue2.reset(); + + intrusive_ptr<Message> msgPtr(MessageUtils::createMessage("exchange", "key", "id")); + DeliverableMessage msg(msgPtr); + topic.route(msg, "abc", 0); + direct.route(msg, "abc", 0); + + } + + void testIsBound() + { + Queue::shared_ptr a(new Queue("a", true)); + Queue::shared_ptr b(new Queue("b", true)); + Queue::shared_ptr c(new Queue("c", true)); + Queue::shared_ptr d(new Queue("d", true)); + + string k1("abc"); + string k2("def"); + string k3("xyz"); + + FanOutExchange fanout("fanout"); + fanout.bind(a, "", 0); + fanout.bind(b, "", 0); + fanout.bind(c, "", 0); + + CPPUNIT_ASSERT(fanout.isBound(a, 0, 0)); + CPPUNIT_ASSERT(fanout.isBound(b, 0, 0)); + CPPUNIT_ASSERT(fanout.isBound(c, 0, 0)); + CPPUNIT_ASSERT(!fanout.isBound(d, 0, 0)); + + DirectExchange direct("direct"); + direct.bind(a, k1, 0); + direct.bind(a, k3, 0); + direct.bind(b, k2, 0); + direct.bind(c, k1, 0); + + CPPUNIT_ASSERT(direct.isBound(a, 0, 0)); + CPPUNIT_ASSERT(direct.isBound(a, &k1, 0)); + CPPUNIT_ASSERT(direct.isBound(a, &k3, 0)); + CPPUNIT_ASSERT(!direct.isBound(a, &k2, 0)); + CPPUNIT_ASSERT(direct.isBound(b, 0, 0)); + CPPUNIT_ASSERT(direct.isBound(b, &k2, 0)); + CPPUNIT_ASSERT(direct.isBound(c, &k1, 0)); + CPPUNIT_ASSERT(!direct.isBound(d, 0, 0)); + CPPUNIT_ASSERT(!direct.isBound(d, &k1, 0)); + CPPUNIT_ASSERT(!direct.isBound(d, &k2, 0)); + CPPUNIT_ASSERT(!direct.isBound(d, &k3, 0)); + + TopicExchange topic("topic"); + topic.bind(a, k1, 0); + topic.bind(a, k3, 0); + topic.bind(b, k2, 0); + topic.bind(c, k1, 0); + + CPPUNIT_ASSERT(topic.isBound(a, 0, 0)); + CPPUNIT_ASSERT(topic.isBound(a, &k1, 0)); + CPPUNIT_ASSERT(topic.isBound(a, &k3, 0)); + CPPUNIT_ASSERT(!topic.isBound(a, &k2, 0)); + CPPUNIT_ASSERT(topic.isBound(b, 0, 0)); + CPPUNIT_ASSERT(topic.isBound(b, &k2, 0)); + CPPUNIT_ASSERT(topic.isBound(c, &k1, 0)); + CPPUNIT_ASSERT(!topic.isBound(d, 0, 0)); + CPPUNIT_ASSERT(!topic.isBound(d, &k1, 0)); + CPPUNIT_ASSERT(!topic.isBound(d, &k2, 0)); + CPPUNIT_ASSERT(!topic.isBound(d, &k3, 0)); + + HeadersExchange headers("headers"); + FieldTable args1; + args1.setString("x-match", "all"); + args1.setString("a", "A"); + args1.setInt("b", 1); + FieldTable args2; + args2.setString("x-match", "any"); + args2.setString("a", "A"); + args2.setInt("b", 1); + FieldTable args3; + args3.setString("x-match", "any"); + args3.setString("c", "C"); + args3.setInt("b", 6); + + headers.bind(a, "", &args1); + headers.bind(a, "", &args3); + headers.bind(b, "", &args2); + headers.bind(c, "", &args1); + + CPPUNIT_ASSERT(headers.isBound(a, 0, 0)); + CPPUNIT_ASSERT(headers.isBound(a, 0, &args1)); + CPPUNIT_ASSERT(headers.isBound(a, 0, &args3)); + CPPUNIT_ASSERT(!headers.isBound(a, 0, &args2)); + CPPUNIT_ASSERT(headers.isBound(b, 0, 0)); + CPPUNIT_ASSERT(headers.isBound(b, 0, &args2)); + CPPUNIT_ASSERT(headers.isBound(c, 0, &args1)); + CPPUNIT_ASSERT(!headers.isBound(d, 0, 0)); + CPPUNIT_ASSERT(!headers.isBound(d, 0, &args1)); + CPPUNIT_ASSERT(!headers.isBound(d, 0, &args2)); + CPPUNIT_ASSERT(!headers.isBound(d, 0, &args3)); + } + + void testDeleteGetAndRedeclare() { + ExchangeRegistry exchanges; + exchanges.declare("my-exchange", "direct", false, FieldTable()); + exchanges.destroy("my-exchange"); + try { + exchanges.get("my-exchange"); + } catch (const ChannelException&) {} + std::pair<Exchange::shared_ptr, bool> response = exchanges.declare("my-exchange", "direct", false, FieldTable()); + CPPUNIT_ASSERT_EQUAL(string("direct"), response.first->getType()); + + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(ExchangeTest); diff --git a/qpid/cpp/src/tests/FieldTable.cpp b/qpid/cpp/src/tests/FieldTable.cpp new file mode 100644 index 0000000000..db4c4906fa --- /dev/null +++ b/qpid/cpp/src/tests/FieldTable.cpp @@ -0,0 +1,84 @@ +/* + * + * 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. + * + */ +#include <iostream> +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" + +#include "unit_test.h" + +using namespace qpid::framing; + +QPID_AUTO_TEST_SUITE(FieldTableTestSuite) + +BOOST_AUTO_TEST_CASE(testMe) +{ + FieldTable ft; + ft.setString("A", "BCDE"); + BOOST_CHECK(StringValue("BCDE") == *ft.get("A")); + + char buff[100]; + Buffer wbuffer(buff, 100); + wbuffer.put(ft); + + Buffer rbuffer(buff, 100); + FieldTable ft2; + rbuffer.get(ft2); + BOOST_CHECK(StringValue("BCDE") == *ft2.get("A")); + +} + +BOOST_AUTO_TEST_CASE(testAssignment) +{ + FieldTable a; + FieldTable b; + + a.setString("A", "BBBB"); + a.setInt("B", 1234); + b = a; + a.setString("A", "CCCC"); + + BOOST_CHECK(StringValue("CCCC") == *a.get("A")); + BOOST_CHECK(StringValue("BBBB") == *b.get("A")); + BOOST_CHECK_EQUAL(1234, a.getInt("B")); + BOOST_CHECK_EQUAL(1234, b.getInt("B")); + BOOST_CHECK(IntegerValue(1234) == *a.get("B")); + BOOST_CHECK(IntegerValue(1234) == *b.get("B")); + + FieldTable d; + { + FieldTable c; + c = a; + + char* buff = static_cast<char*>(::alloca(c.size())); + Buffer wbuffer(buff, c.size()); + wbuffer.put(c); + + Buffer rbuffer(buff, c.size()); + rbuffer.get(d); + BOOST_CHECK_EQUAL(c, d); + BOOST_CHECK(StringValue("CCCC") == *c.get("A")); + BOOST_CHECK(IntegerValue(1234) == *c.get("B")); + } + BOOST_CHECK(StringValue("CCCC") == *d.get("A")); + BOOST_CHECK(IntegerValue(1234) == *d.get("B")); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/FieldValue.cpp b/qpid/cpp/src/tests/FieldValue.cpp new file mode 100644 index 0000000000..a820ae57bd --- /dev/null +++ b/qpid/cpp/src/tests/FieldValue.cpp @@ -0,0 +1,90 @@ +/* + * 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. + */ +#include "qpid/framing/FieldValue.h" + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(FieldValueTestSuite) + +using namespace qpid::framing; + +StringValue s("abc"); +IntegerValue i(42); +//DecimalValue d(1234,2); +//FieldTableValue ft; +//EmptyValue e; + +BOOST_AUTO_TEST_CASE(testStringValueEquals) +{ + + BOOST_CHECK(StringValue("abc") == s); + BOOST_CHECK(StringValue("foo") != s); + BOOST_CHECK(s != i); + BOOST_CHECK(s.convertsTo<std::string>() == true); + BOOST_CHECK(s.convertsTo<int>() == false); + BOOST_CHECK(s.get<std::string>() == "abc"); + BOOST_CHECK_THROW(s.get<int>(), InvalidConversionException); +// BOOST_CHECK(s != ft); + +} + +BOOST_AUTO_TEST_CASE(testIntegerValueEquals) +{ + BOOST_CHECK(IntegerValue(42) == i); + BOOST_CHECK(IntegerValue(5) != i); + BOOST_CHECK(i != s); + BOOST_CHECK(i.convertsTo<std::string>() == false); + BOOST_CHECK(i.convertsTo<int>() == true); + BOOST_CHECK_THROW(i.get<std::string>(), InvalidConversionException); + BOOST_CHECK(i.get<int>() == 42); +// BOOST_CHECK(i != ft); +} + +#if 0 +BOOST_AUTO_TEST_CASE(testDecimalValueEquals) +{ + BOOST_CHECK(DecimalValue(1234, 2) == d); + BOOST_CHECK(DecimalValue(12345, 2) != d); + BOOST_CHECK(DecimalValue(1234, 3) != d); + BOOST_CHECK(d != s); +} + +BOOST_AUTO_TEST_CASE(testFieldTableValueEquals) +{ + ft.getValue().setString("foo", "FOO"); + ft.getValue().setInt("magic", 7); + + BOOST_CHECK_EQUAL(std::string("FOO"), + ft.getValue().getString("foo")); + BOOST_CHECK_EQUAL(7, ft.getValue().getInt("magic")); + + FieldTableValue f2; + BOOST_CHECK(ft != f2); + f2.getValue().setString("foo", "FOO"); + BOOST_CHECK(ft != f2); + f2.getValue().setInt("magic", 7); + BOOST_CHECK_EQUAL(ft,f2); + BOOST_CHECK(ft == f2); + f2.getValue().setString("foo", "BAR"); + BOOST_CHECK(ft != f2); + BOOST_CHECK(ft != i); +} +#endif + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/Frame.cpp b/qpid/cpp/src/tests/Frame.cpp new file mode 100644 index 0000000000..0484dc4b2a --- /dev/null +++ b/qpid/cpp/src/tests/Frame.cpp @@ -0,0 +1,80 @@ +/* + * + * 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. + * + */ +#include "qpid/framing/Frame.h" + +#include <boost/lexical_cast.hpp> +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(FrameTestSuite) + +using namespace std; +using namespace qpid::framing; +using namespace boost; + +BOOST_AUTO_TEST_CASE(testContentBody) { + Frame f(42, AMQContentBody("foobar")); + AMQBody* body=f.getBody(); + BOOST_CHECK(dynamic_cast<AMQContentBody*>(body)); + Buffer b(f.size()); + f.encode(b); + b.flip(); + Frame g; + g.decode(b); + AMQContentBody* content=dynamic_cast<AMQContentBody*>(g.getBody()); + BOOST_REQUIRE(content); + BOOST_CHECK_EQUAL(content->getData(), "foobar"); +} + +BOOST_AUTO_TEST_CASE(testMethodBody) { + FieldTable args; + args.setString("foo", "bar"); + Frame f( + 42, QueueDeclareBody(ProtocolVersion(), 1, "q", "altex", + true, false, true, false, true, args)); + BOOST_CHECK_EQUAL(f.getChannel(), 42); + Buffer b(f.size()); + f.encode(b); + b.flip(); + Frame g; + g.decode(b); + BOOST_CHECK_EQUAL(f.getChannel(), g.getChannel()); + QueueDeclareBody* declare=dynamic_cast<QueueDeclareBody*>(g.getBody()); + BOOST_REQUIRE(declare); + BOOST_CHECK_EQUAL(declare->getAlternateExchange(), "altex"); + BOOST_CHECK_EQUAL(lexical_cast<string>(*f.getBody()), lexical_cast<string>(*g.getBody())); +} + +BOOST_AUTO_TEST_CASE(testLoop) { + // Run in a loop so heap profiler can spot any allocations. + Buffer b(1024); + for (int i = 0; i < 100; ++i) { + Frame ctor(2, AccessRequestOkBody(ProtocolVersion(), 42)); + Frame assign(3); + assign.body = AccessRequestOkBody(ProtocolVersion(), 42); + assign.encode(b); + b.flip(); + Frame g; + g.decode(b); + BOOST_REQUIRE(dynamic_cast<AccessRequestOkBody*>(g.getBody())->getTicket() == 42); + } +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/FramingTest.cpp b/qpid/cpp/src/tests/FramingTest.cpp new file mode 100644 index 0000000000..0c7adb2af8 --- /dev/null +++ b/qpid/cpp/src/tests/FramingTest.cpp @@ -0,0 +1,232 @@ +/* + * + * 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. + * + */ +#include "qpid/client/Exchange.h" +#include "qpid/client/Queue.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Connector.h" +#include "qpid/framing/AMQP_HighestVersion.h" +#include "qpid/framing/BasicGetOkBody.h" +#include "qpid/framing/ConnectionRedirectBody.h" +#include "qpid/framing/ProtocolVersion.h" +#include "qpid/framing/all_method_bodies.h" +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/reply_exceptions.h" +#include "qpid_test_plugin.h" + +#include <boost/bind.hpp> +#include <boost/lexical_cast.hpp> +#include <iostream> + +#include <memory> +#include <sstream> +#include <typeinfo> + +using namespace qpid; +using namespace qpid::framing; +using namespace std; + +template <class T> +std::string tostring(const T& x) +{ + std::ostringstream out; + out << x; + return out.str(); +} + +class FramingTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(FramingTest); + CPPUNIT_TEST(testBasicQosBody); + CPPUNIT_TEST(testConnectionSecureBody); + CPPUNIT_TEST(testConnectionRedirectBody); + CPPUNIT_TEST(testAccessRequestBody); + CPPUNIT_TEST(testBasicConsumeBody); + CPPUNIT_TEST(testConnectionRedirectBodyFrame); + CPPUNIT_TEST(testBasicConsumeOkBodyFrame); + CPPUNIT_TEST(testInlineContent); + CPPUNIT_TEST(testContentReference); + CPPUNIT_TEST(testContentValidation); + CPPUNIT_TEST_SUITE_END(); + + private: + char buffer[1024]; + ProtocolVersion version; + + public: + + FramingTest() : version(highestProtocolVersion) {} + + void testBasicQosBody() + { + Buffer wbuff(buffer, sizeof(buffer)); + BasicQosBody in(version, 0xCAFEBABE, 0xABBA, true); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + BasicQosBody out(version); + out.decode(rbuff); + CPPUNIT_ASSERT_EQUAL(tostring(in), tostring(out)); + } + + void testConnectionSecureBody() + { + Buffer wbuff(buffer, sizeof(buffer)); + std::string s = "security credential"; + ConnectionSecureBody in(version, s); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + ConnectionSecureBody out(version); + out.decode(rbuff); + CPPUNIT_ASSERT_EQUAL(tostring(in), tostring(out)); + } + + void testConnectionRedirectBody() + { + Buffer wbuff(buffer, sizeof(buffer)); + std::string a = "hostA"; + std::string b = "hostB"; + ConnectionRedirectBody in(version, a, b); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + ConnectionRedirectBody out(version); + out.decode(rbuff); + CPPUNIT_ASSERT_EQUAL(tostring(in), tostring(out)); + } + + void testAccessRequestBody() + { + Buffer wbuff(buffer, sizeof(buffer)); + std::string s = "text"; + AccessRequestBody in(version, s, true, false, true, false, true); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + AccessRequestBody out(version); + out.decode(rbuff); + CPPUNIT_ASSERT_EQUAL(tostring(in), tostring(out)); + } + + void testBasicConsumeBody() + { + Buffer wbuff(buffer, sizeof(buffer)); + std::string q = "queue"; + std::string t = "tag"; + BasicConsumeBody in(version, 0, q, t, false, true, false, false, + FieldTable()); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + BasicConsumeBody out(version); + out.decode(rbuff); + CPPUNIT_ASSERT_EQUAL(tostring(in), tostring(out)); + } + + + void testConnectionRedirectBodyFrame() + { + Buffer wbuff(buffer, sizeof(buffer)); + std::string a = "hostA"; + std::string b = "hostB"; + AMQFrame in(in_place<ConnectionRedirectBody>(version, a, b)); + in.setChannel(999); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + AMQFrame out; + out.decode(rbuff); + CPPUNIT_ASSERT_EQUAL(tostring(in), tostring(out)); + } + + void testBasicConsumeOkBodyFrame() + { + Buffer wbuff(buffer, sizeof(buffer)); + std::string s = "hostA"; + AMQFrame in(in_place<BasicConsumeOkBody>(version, s)); + in.setChannel(999); + in.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + AMQFrame out; + out.decode(rbuff); + CPPUNIT_ASSERT_EQUAL(tostring(in), tostring(out)); + } + + void testInlineContent() { + Buffer wbuff(buffer, sizeof(buffer)); + Content content(INLINE, "MyData"); + CPPUNIT_ASSERT(content.isInline()); + content.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + Content recovered; + recovered.decode(rbuff); + CPPUNIT_ASSERT(recovered.isInline()); + CPPUNIT_ASSERT_EQUAL(content.getValue(), recovered.getValue()); + } + + void testContentReference() { + Buffer wbuff(buffer, sizeof(buffer)); + Content content(REFERENCE, "MyRef"); + CPPUNIT_ASSERT(content.isReference()); + content.encode(wbuff); + + Buffer rbuff(buffer, sizeof(buffer)); + Content recovered; + recovered.decode(rbuff); + CPPUNIT_ASSERT(recovered.isReference()); + CPPUNIT_ASSERT_EQUAL(content.getValue(), recovered.getValue()); + } + + void testContentValidation() { + try { + Content content(REFERENCE, ""); + CPPUNIT_ASSERT(false);//fail, expected exception + } catch (const InvalidArgumentException& e) {} + + try { + Content content(2, "Blah"); + CPPUNIT_ASSERT(false);//fail, expected exception + } catch (const SyntaxErrorException& e) {} + + try { + Buffer wbuff(buffer, sizeof(buffer)); + wbuff.putOctet(2); + wbuff.putLongString("blah, blah"); + + Buffer rbuff(buffer, sizeof(buffer)); + Content content; + content.decode(rbuff); + CPPUNIT_FAIL("Expected exception"); + } catch (Exception& e) {} + + } + + }; + + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(FramingTest); + + + diff --git a/qpid/cpp/src/tests/HeaderTest.cpp b/qpid/cpp/src/tests/HeaderTest.cpp new file mode 100644 index 0000000000..9e2bddb4de --- /dev/null +++ b/qpid/cpp/src/tests/HeaderTest.cpp @@ -0,0 +1,126 @@ +/* + * + * 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. + * + */ +#include <iostream> +#include "qpid/framing/amqp_framing.h" +#include "qpid/framing/FieldValue.h" +#include "qpid_test_plugin.h" + +using namespace qpid::framing; +using namespace std; + +class HeaderTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(HeaderTest); + CPPUNIT_TEST(testGenericProperties); + CPPUNIT_TEST(testMessageProperties); + CPPUNIT_TEST(testDeliveryProperies); + CPPUNIT_TEST_SUITE_END(); + + public: + + void testGenericProperties() + { + AMQHeaderBody body; + body.get<MessageProperties>(true)->getApplicationHeaders().setString( + "A", "BCDE"); + char buff[100]; + Buffer wbuffer(buff, 100); + body.encode(wbuffer); + + Buffer rbuffer(buff, 100); + AMQHeaderBody body2; + body2.decode(rbuffer, body.size()); + MessageProperties* props = + body2.get<MessageProperties>(true); + CPPUNIT_ASSERT_EQUAL( + string("BCDE"), + props->getApplicationHeaders().get("A")->get<string>()); + } + + void testMessageProperties() { + AMQFrame out(in_place<AMQHeaderBody>()); + MessageProperties* props1 = + out.castBody<AMQHeaderBody>()->get<MessageProperties>(true); + + props1->setContentLength(42); + props1->setMessageId("messageId"); + props1->setCorrelationId("correlationId"); + props1->setReplyTo(ReplyTo("ex","key")); + props1->setContentType("contentType"); + props1->setContentEncoding("contentEncoding"); + props1->setType("type"); + props1->setUserId("userId"); + props1->setAppId("appId"); + props1->setTransactionId("transactionId"); + props1->setSecurityToken("securityToken"); + + char buff[10000]; + Buffer wbuffer(buff, 10000); + out.encode(wbuffer); + + Buffer rbuffer(buff, 10000); + AMQFrame in; + in.decode(rbuffer); + MessageProperties* props2 = + in.castBody<AMQHeaderBody>()->get<MessageProperties>(true); + + CPPUNIT_ASSERT_EQUAL(props1->getContentLength(), props2->getContentLength()); + CPPUNIT_ASSERT_EQUAL(props1->getMessageId(), props2->getMessageId()); + CPPUNIT_ASSERT_EQUAL(props1->getCorrelationId(), props2->getCorrelationId()); + CPPUNIT_ASSERT_EQUAL(props1->getContentType(), props2->getContentType()); + CPPUNIT_ASSERT_EQUAL(props1->getContentEncoding(), props2->getContentEncoding()); + CPPUNIT_ASSERT_EQUAL(props1->getType(), props2->getType()); + CPPUNIT_ASSERT_EQUAL(props1->getUserId(), props2->getUserId()); + CPPUNIT_ASSERT_EQUAL(props1->getAppId(), props2->getAppId()); + CPPUNIT_ASSERT_EQUAL(props1->getTransactionId(), props2->getTransactionId()); + CPPUNIT_ASSERT_EQUAL(props1->getSecurityToken(), props2->getSecurityToken()); + + } + + void testDeliveryProperies() { + AMQFrame out(in_place<AMQHeaderBody>()); + DeliveryProperties* props1 = + out.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true); + + props1->setDiscardUnroutable(true); + props1->setExchange("foo"); + + char buff[10000]; + Buffer wbuffer(buff, 10000); + out.encode(wbuffer); + + Buffer rbuffer(buff, 10000); + AMQFrame in; + in.decode(rbuffer); + DeliveryProperties* props2 = + in.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true); + + CPPUNIT_ASSERT(props2->getDiscardUnroutable()); + CPPUNIT_ASSERT_EQUAL(string("foo"), props2->getExchange()); + CPPUNIT_ASSERT(!props2->hasTimestamp()); + } + +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(HeaderTest); + diff --git a/qpid/cpp/src/tests/HeadersExchangeTest.cpp b/qpid/cpp/src/tests/HeadersExchangeTest.cpp new file mode 100644 index 0000000000..f07f238ee4 --- /dev/null +++ b/qpid/cpp/src/tests/HeadersExchangeTest.cpp @@ -0,0 +1,131 @@ +/* + * + * 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. + * + */ + +#include "qpid/Exception.h" +#include "qpid/broker/HeadersExchange.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/framing/FieldValue.h" +#include "qpid_test_plugin.h" + +using namespace qpid::broker; +using namespace qpid::framing; + +class HeadersExchangeTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(HeadersExchangeTest); + CPPUNIT_TEST(testMatchAll); + CPPUNIT_TEST(testMatchAny); + CPPUNIT_TEST(testMatchEmptyValue); + CPPUNIT_TEST(testMatchEmptyArgs); + CPPUNIT_TEST(testMatchNoXMatch); + CPPUNIT_TEST(testBindNoXMatch); + CPPUNIT_TEST_SUITE_END(); + + public: + + void testMatchAll() + { + FieldTable b, m, n; + b.setString("x-match", "all"); + b.setString("foo", "FOO"); + b.setInt("n", 42); + m.setString("foo", "FOO"); + m.setInt("n", 42); + CPPUNIT_ASSERT(HeadersExchange::match(b, m)); + + // Ignore extras. + m.setString("extra", "x"); + CPPUNIT_ASSERT(HeadersExchange::match(b, m)); + + // Fail mismatch, wrong value. + m.setString("foo", "NotFoo"); + CPPUNIT_ASSERT(!HeadersExchange::match(b, m)); + + // Fail mismatch, missing value + n.setInt("n", 42); + n.setString("extra", "x"); + CPPUNIT_ASSERT(!HeadersExchange::match(b, n)); + } + + void testMatchAny() + { + FieldTable b, m, n; + b.setString("x-match", "any"); + b.setString("foo", "FOO"); + b.setInt("n", 42); + m.setString("foo", "FOO"); + CPPUNIT_ASSERT(!HeadersExchange::match(b, n)); + CPPUNIT_ASSERT(HeadersExchange::match(b, m)); + m.setInt("n", 42); + CPPUNIT_ASSERT(HeadersExchange::match(b, m)); + } + + void testMatchEmptyValue() + { + FieldTable b, m; + b.setString("x-match", "all"); + b.set("foo", FieldTable::ValuePtr()); + b.set("n", FieldTable::ValuePtr()); + CPPUNIT_ASSERT(!HeadersExchange::match(b, m)); + m.setString("foo", "blah"); + m.setInt("n", 123); + } + + void testMatchEmptyArgs() + { + FieldTable b, m; + m.setString("foo", "FOO"); + + b.setString("x-match", "all"); + CPPUNIT_ASSERT(HeadersExchange::match(b, m)); + b.setString("x-match", "any"); + CPPUNIT_ASSERT(!HeadersExchange::match(b, m)); + } + + + void testMatchNoXMatch() + { + FieldTable b, m; + b.setString("foo", "FOO"); + m.setString("foo", "FOO"); + CPPUNIT_ASSERT(!HeadersExchange::match(b, m)); + } + + void testBindNoXMatch() + { + HeadersExchange exchange("test"); + Queue::shared_ptr queue; + std::string key; + FieldTable args; + try { + //just checking this doesn't cause assertion etc + exchange.bind(queue, key, &args); + } catch(qpid::Exception&) { + //expected + } + } + + +}; + +// make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(HeadersExchangeTest); diff --git a/qpid/cpp/src/tests/IList.cpp b/qpid/cpp/src/tests/IList.cpp new file mode 100644 index 0000000000..2e872d0e05 --- /dev/null +++ b/qpid/cpp/src/tests/IList.cpp @@ -0,0 +1,164 @@ +/* + * + * 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. + * + */ +#include "qpid/IList.h" +#include "unit_test.h" +#include "test_tools.h" +#include <boost/assign/list_of.hpp> +#include <vector> + +QPID_AUTO_TEST_SUITE(IListTestSuite) + +using namespace qpid; +using namespace std; +using boost::assign::list_of; + +// Comparison, op== and << for ILists in qpid namespace for template lookup. + +template <class T, class S> bool operator==(const IList<T>& a, const S& b) { return seqEqual(a, b); } +template <class T> ostream& operator<<(std::ostream& o, const IList<T>& l) { return seqPrint(o, l); } +template <class T> +ostream& operator<<(ostream& o, typename IList<T>::iterator i) { + return i? o << "(nil)" : o << *i; +} + +struct IListFixture { + struct Node : public IListNode<Node*> { + char value; + Node(char c) { value=c; } + bool operator==(const Node& n) const { return value == n.value; } + }; + typedef IList<Node> List; + Node a, b, c, d, e; + IListFixture() :a('a'),b('b'),c('c'),d('d'),e('e') {} +}; + +ostream& operator<<(ostream& o, const IListFixture::Node& n) { return o << n.value; } + +BOOST_FIXTURE_TEST_CASE(IList_default_ctor, IListFixture) { + List l; + BOOST_CHECK(l.empty()); + BOOST_CHECK(l.begin() == l.end()); + BOOST_CHECK_EQUAL(0u, l.size()); +} + +BOOST_FIXTURE_TEST_CASE(IList_push_front, IListFixture) { + List l; + l.push_front(&a); + BOOST_CHECK_EQUAL(1u, l.size()); + BOOST_CHECK_EQUAL(l, list_of(a)); + l.push_front(&b); + BOOST_CHECK_EQUAL(2u, l.size()); + BOOST_CHECK_EQUAL(l, list_of(b)(a)); +} + +BOOST_FIXTURE_TEST_CASE(IList_push_back, IListFixture) { + List l; + l.push_back(&a); + BOOST_CHECK_EQUAL(1u, l.size()); + BOOST_CHECK_EQUAL(l, list_of(a)); + l.push_back(&b); + BOOST_CHECK_EQUAL(2u, l.size()); + BOOST_CHECK_EQUAL(l, list_of(a)(b)); +} + +BOOST_FIXTURE_TEST_CASE(IList_insert, IListFixture) { + List l; + List::iterator i(l.begin()); + i = l.insert(i, &a); + BOOST_CHECK_EQUAL(l, list_of(a)); + BOOST_CHECK(i == l.begin()); + + i = l.insert(i, &b); + BOOST_CHECK_EQUAL(l, list_of(b)(a)); + BOOST_CHECK(i == l.begin()); + + i++; + BOOST_CHECK_EQUAL(*i, a); + i = l.insert(i, &c); + BOOST_CHECK_EQUAL(l, list_of(b)(c)(a)); + BOOST_CHECK_EQUAL(*i, c); + + i = l.insert(i, &d); + BOOST_CHECK_EQUAL(l, list_of(b)(d)(c)(a)); + BOOST_CHECK_EQUAL(*i, d); +} + +BOOST_FIXTURE_TEST_CASE(IList_iterator_test, IListFixture) { + List l; + l.push_back(&a); + l.push_back(&b); + + List::iterator i = l.begin(); + BOOST_CHECK_EQUAL(*i, a); + BOOST_CHECK_EQUAL(static_cast<Node*>(i), &a); + List::const_iterator ci = i; + BOOST_CHECK_EQUAL(static_cast<const Node*>(ci), &a); + + i++; + BOOST_CHECK_EQUAL(*i, b); + BOOST_CHECK_EQUAL(static_cast<Node*>(i), &b); + i++; + BOOST_CHECK(i == l.end()); +} + +BOOST_FIXTURE_TEST_CASE(IList_pop_front, IListFixture) { + List l; + l.push_back(&a); + l.push_back(&b); + BOOST_CHECK_EQUAL(l, list_of(a)(b)); + l.pop_front(); + BOOST_CHECK_EQUAL(l, list_of(b)); + l.pop_front(); + BOOST_CHECK(l.empty()); +} + +BOOST_FIXTURE_TEST_CASE(IList_pop_back, IListFixture) { + List l; + l.push_back(&a); + l.push_back(&b); + l.pop_back(); + BOOST_CHECK_EQUAL(l, list_of(a)); + l.pop_back(); + BOOST_CHECK(l.empty()); +} + +BOOST_FIXTURE_TEST_CASE(IList_erase, IListFixture) { + List l; + l.push_back(&a); + l.push_back(&b); + l.push_back(&c); + + List::iterator i=l.begin(); + i++; + l.erase(i); + BOOST_CHECK_EQUAL(l, list_of(a)(c)); + + i=l.begin(); + i++; + l.erase(i); + BOOST_CHECK_EQUAL(l, list_of(a)); + + l.erase(l.begin()); + BOOST_CHECK(l.empty()); +} + +QPID_AUTO_TEST_SUITE_END() + diff --git a/qpid/cpp/src/tests/ISList.cpp b/qpid/cpp/src/tests/ISList.cpp new file mode 100644 index 0000000000..81301e3732 --- /dev/null +++ b/qpid/cpp/src/tests/ISList.cpp @@ -0,0 +1,209 @@ +/* + * + * 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. + * + */ +#include "qpid/ISList.h" +#include "qpid/RefCounted.h" +#include "unit_test.h" +#include "test_tools.h" +#include <boost/assign/list_of.hpp> +#include <boost/shared_ptr.hpp> +#include <boost/intrusive_ptr.hpp> +#include <vector> + +QPID_AUTO_TEST_SUITE(ISListTestSuite) + +using namespace qpid; +using namespace std; +using boost::assign::list_of; +using boost::intrusive_ptr; + +// Comparison, op== and << for ILists in qpid namespace for template lookup. + +template <class T, class S> bool operator==(const ISList<T>& a, const S& b) { return seqEqual(a, b); } +template <class T> ostream& operator<<(std::ostream& o, const ISList<T>& l) { return seqPrint(o, l); } +template <class T> +ostream& operator<<(ostream& o, typename ISList<T>::iterator i) { + return i? o << "(nil)" : o << *i; +} + +struct NodeBase { + static int instances; + char value; + + NodeBase(char c) { value=c; ++instances; } + NodeBase(const NodeBase& n) { value=n.value; ++instances; } + ~NodeBase() { --instances; } + bool operator==(const NodeBase& n) const { return value == n.value; } +}; + +int NodeBase::instances = 0; + +ostream& operator<<(ostream& o, const NodeBase& n) { return o << n.value; } + +struct Fixture { + struct Node : public NodeBase, public ISListNode<Node*> { + Node(char c) : NodeBase(c) {} + }; + typedef ISList<Node> List; + Node a, b, c, d, e; + List l; + Fixture() :a('a'),b('b'),c('c'),d('d'),e('e') {} +}; + +BOOST_FIXTURE_TEST_CASE(default_ctor, Fixture) { + BOOST_CHECK(l.empty()); + BOOST_CHECK(l.begin() == l.end()); + BOOST_CHECK_EQUAL(0u, l.size()); +} + +BOOST_FIXTURE_TEST_CASE(push_front, Fixture) { + l.push_front(&a); + BOOST_CHECK_EQUAL(1u, l.size()); + BOOST_CHECK_EQUAL(l, list_of(a)); + l.push_front(&b); + BOOST_CHECK_EQUAL(2u, l.size()); + BOOST_CHECK_EQUAL(l, list_of(b)(a)); +} + +BOOST_FIXTURE_TEST_CASE(push_back, Fixture) { + l.push_back(&a); + BOOST_CHECK_EQUAL(1u, l.size()); + BOOST_CHECK_EQUAL(l, list_of(a)); + l.push_back(&b); + BOOST_CHECK_EQUAL(2u, l.size()); + BOOST_CHECK_EQUAL(l, list_of(a)(b)); +} + +BOOST_FIXTURE_TEST_CASE(insert, Fixture) { + List::iterator i(l.begin()); + i = l.insert(i, &a); + BOOST_CHECK_EQUAL(l, list_of(a)); + BOOST_CHECK(i == l.begin()); + + i = l.insert(i, &b); + BOOST_CHECK_EQUAL(l, list_of(b)(a)); + BOOST_CHECK(i == l.begin()); + + i++; + BOOST_CHECK_EQUAL(*i, a); + i = l.insert(i, &c); + BOOST_CHECK_EQUAL(l, list_of(b)(c)(a)); + BOOST_CHECK_EQUAL(*i, c); + + i = l.insert(i, &d); + BOOST_CHECK_EQUAL(l, list_of(b)(d)(c)(a)); + BOOST_CHECK_EQUAL(*i, d); +} + +BOOST_FIXTURE_TEST_CASE(iterator_test, Fixture) { + l.push_back(&a); + l.push_back(&b); + + List::iterator i = l.begin(); + BOOST_CHECK_EQUAL(*i, a); + BOOST_CHECK_EQUAL(static_cast<Node*>(i), &a); + List::const_iterator ci = i; + BOOST_CHECK_EQUAL(static_cast<const Node*>(ci), &a); + + i++; + BOOST_CHECK_EQUAL(*i, b); + BOOST_CHECK_EQUAL(static_cast<Node*>(i), &b); + i++; + BOOST_CHECK(i == l.end()); +} + +BOOST_FIXTURE_TEST_CASE(pop_front, Fixture) { + l.push_back(&a); + l.push_back(&b); + l.pop_front(); + BOOST_CHECK_EQUAL(l, list_of(b)); + l.pop_front(); + BOOST_CHECK(l.empty()); +} + +BOOST_FIXTURE_TEST_CASE(erase, Fixture) { + l.push_back(&a); + l.push_back(&b); + l.push_back(&c); + + List::iterator i=l.begin(); + i++; + l.erase(i); + BOOST_CHECK_EQUAL(l, list_of(a)(c)); + + i=l.begin(); + i++; + l.erase(i); + BOOST_CHECK_EQUAL(l, list_of(a)); + + l.erase(l.begin()); + BOOST_CHECK(l.empty()); +} + + +// ================ Test smart pointer types. + +template <class Node> void smart_pointer_test() { + typedef typename Node::pointer pointer; + typedef ISList<Node> List; + List l; + + BOOST_CHECK_EQUAL(0, NodeBase::instances); + l.push_back(pointer(new Node())); + l.push_back(pointer(new Node())); + BOOST_CHECK_EQUAL(2, NodeBase::instances); // maintains a reference. + + pointer p = l.begin(); + l.pop_front(); + BOOST_CHECK_EQUAL(2, NodeBase::instances); // transfers ownership. + p = pointer(); + BOOST_CHECK_EQUAL(1, NodeBase::instances); + + l.clear(); + BOOST_CHECK_EQUAL(0, NodeBase::instances); + { // Dtor cleans up + List ll; + ll.push_back(pointer(new Node())); + BOOST_CHECK_EQUAL(1, NodeBase::instances); + } + BOOST_CHECK_EQUAL(0, NodeBase::instances); +} + +struct IntrusiveNode : public NodeBase, public RefCounted, + public ISListNode<intrusive_ptr<IntrusiveNode> > +{ + IntrusiveNode() : NodeBase(0) {} +}; + + +BOOST_AUTO_TEST_CASE(intrusive_ptr_test) { + smart_pointer_test<IntrusiveNode>(); +} + + +struct SharedNode : public NodeBase, public ISListNode<boost::shared_ptr<SharedNode> > { + SharedNode() : NodeBase(0) {} +}; + +BOOST_AUTO_TEST_CASE(shared_ptr_test) { + smart_pointer_test<SharedNode>(); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/IncompleteMessageList.cpp b/qpid/cpp/src/tests/IncompleteMessageList.cpp new file mode 100644 index 0000000000..65cca4a628 --- /dev/null +++ b/qpid/cpp/src/tests/IncompleteMessageList.cpp @@ -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. + * + */ +#include <iostream> +#include <sstream> +#include "qpid/broker/Message.h" +#include "qpid/broker/NullMessageStore.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/IncompleteMessageList.h" + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(IncompleteMessageListTestSuite) + +using namespace qpid::broker; +using namespace qpid::framing; + +struct Checker +{ + std::list<SequenceNumber> ids; + + Checker() { } + + Checker(uint start, uint end) { + for (uint i = start; i <= end; i++) { + ids.push_back(i); + } + } + + Checker& expect(const SequenceNumber& id) { + ids.push_back(id); + return *this; + } + + void operator()(boost::intrusive_ptr<Message> msg) { + BOOST_CHECK(!ids.empty()); + BOOST_CHECK_EQUAL(msg->getCommandId(), ids.front()); + ids.pop_front(); + } +}; + +BOOST_AUTO_TEST_CASE(testProcessSimple) +{ + IncompleteMessageList list; + SequenceNumber counter(1); + //fill up list with messages + for (int i = 0; i < 5; i++) { + boost::intrusive_ptr<Message> msg(new Message(counter++)); + list.add(msg); + } + //process and ensure they are all passed to completion listener + list.process(Checker(1, 5), false); + //process again and ensure none are resent to listener + list.process(Checker(), false); +} + +BOOST_AUTO_TEST_CASE(testProcessWithIncomplete) +{ + IncompleteMessageList list; + SequenceNumber counter(1); + boost::intrusive_ptr<Message> middle; + //fill up list with messages + for (int i = 0; i < 5; i++) { + boost::intrusive_ptr<Message> msg(new Message(counter++)); + list.add(msg); + if (i == 2) { + //mark a message in the middle as incomplete + msg->enqueueAsync(); + middle = msg; + } + } + //process and ensure only message upto incomplete message are passed to listener + list.process(Checker(1, 2), false); + //mark message complete and re-process to get remaining messages sent to listener + middle->enqueueComplete(); + list.process(Checker(3, 5), false); +} + + +struct MockStore : public NullMessageStore +{ + Queue::shared_ptr queue; + boost::intrusive_ptr<Message> msg; + + void flush(const qpid::broker::PersistableQueue& q) { + BOOST_CHECK_EQUAL(queue.get(), &q); + msg->enqueueComplete(); + } +}; + +BOOST_AUTO_TEST_CASE(testSyncProcessWithIncomplete) +{ + IncompleteMessageList list; + SequenceNumber counter(1); + MockStore store; + store.queue = Queue::shared_ptr(new Queue("mock-queue")); + //fill up list with messages + for (int i = 0; i < 5; i++) { + boost::intrusive_ptr<Message> msg(new Message(counter++)); + list.add(msg); + if (i == 2) { + //mark a message in the middle as incomplete + msg->enqueueAsync(store.queue, &store); + store.msg = msg; + } + } + //process with sync bit specified and ensure that all messages are passed to listener + list.process(Checker(1, 5), true); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/InlineVector.cpp b/qpid/cpp/src/tests/InlineVector.cpp new file mode 100644 index 0000000000..d1b3ebf7de --- /dev/null +++ b/qpid/cpp/src/tests/InlineVector.cpp @@ -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. + * + */ + +#include "qpid/InlineVector.h" +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(InlineVectorTestSuite) + +using namespace qpid; +using namespace std; + +typedef InlineVector<int, 3> Vec; + +bool isInline(const Vec& v) { + return (char*)&v <= (char*)v.data() && + (char*)v.data() < (char*)&v+sizeof(v); +} + +BOOST_AUTO_TEST_CASE(testCtor) { + { + Vec v; + BOOST_CHECK(isInline(v)); + BOOST_CHECK(v.empty()); + } + { + Vec v(3, 42); + BOOST_CHECK(isInline(v)); + BOOST_CHECK_EQUAL(3u, v.size()); + BOOST_CHECK_EQUAL(v[0], 42); + BOOST_CHECK_EQUAL(v[2], 42); + + Vec u(v); + BOOST_CHECK(isInline(u)); + BOOST_CHECK_EQUAL(3u, u.size()); + BOOST_CHECK_EQUAL(u[0], 42); + BOOST_CHECK_EQUAL(u[2], 42); + } + + { + Vec v(4, 42); + + BOOST_CHECK_EQUAL(v.size(), 4u); + BOOST_CHECK(!isInline(v)); + Vec u(v); + BOOST_CHECK_EQUAL(u.size(), 4u); + BOOST_CHECK(!isInline(u)); + } +} + +BOOST_AUTO_TEST_CASE(testInsert) { + Vec v; + v.push_back(1); + BOOST_CHECK_EQUAL(v.size(), 1u); + BOOST_CHECK_EQUAL(v.back(), 1); + BOOST_CHECK(isInline(v)); + + v.insert(v.begin(), 2); + BOOST_CHECK_EQUAL(v.size(), 2u); + BOOST_CHECK_EQUAL(v.back(), 1); + BOOST_CHECK(isInline(v)); + + v.push_back(3); + BOOST_CHECK(isInline(v)); + + v.push_back(4); + BOOST_CHECK_EQUAL(v.size(), 4u); + BOOST_CHECK(!isInline(v)); +} + + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/Makefile.am b/qpid/cpp/src/tests/Makefile.am new file mode 100644 index 0000000000..7eddd9932e --- /dev/null +++ b/qpid/cpp/src/tests/Makefile.am @@ -0,0 +1,182 @@ +AM_CXXFLAGS = $(WARNING_CFLAGS) $(CPPUNIT_CXXFLAGS) $(APR_CXXFLAGS) -DBOOST_TEST_DYN_LINK +INCLUDES = -I$(srcdir)/.. -I$(srcdir)/../gen -I$(top_builddir)/src/gen + +abs_builddir=@abs_builddir@ +extra_libs = $(CPPUNIT_LIBS) +lib_client = $(abs_builddir)/../libqpidclient.la +lib_common = $(abs_builddir)/../libqpidcommon.la +lib_broker = $(abs_builddir)/../libqpidbroker.la + +# +# Initialize variables that are incremented with += +# +check_PROGRAMS= +check_LTLIBRARIES= +TESTS= +EXTRA_DIST= +CLEANFILES= + +# +# Unit test program +# +# Unit tests are built as a single program to reduce valgrind overhead +# when running the tests. If you want to build a subset of the tests do +# rm -f unit_test; make unit_test unit_test_OBJECTS="unit_test.o SelectedTest.o" +# + +TESTS+=unit_test +check_PROGRAMS+=unit_test +unit_test_LDADD=-lboost_unit_test_framework -lboost_regex \ + $(lib_client) $(lib_broker) +unit_test_SOURCES= unit_test.cpp unit_test.h \ + BrokerFixture.h SocketProxy.h \ + exception_test.cpp \ + RefCounted.cpp \ + SessionState.cpp Blob.cpp logging.cpp \ + Url.cpp Uuid.cpp \ + Shlib.cpp FieldValue.cpp FieldTable.cpp Array.cpp \ + InlineVector.cpp \ + ISList.cpp IList.cpp \ + ClientSessionTest.cpp \ + SequenceSet.cpp \ + amqp_0_10/serialize.cpp \ + amqp_0_10/ProxyTemplate.cpp \ + amqp_0_10/apply.cpp \ + IncompleteMessageList.cpp \ + amqp_0_10/Map.cpp \ + amqp_0_10/handlers.cpp + +check_LTLIBRARIES += libshlibtest.la +libshlibtest_la_LDFLAGS = -module -rpath $(abs_builddir) +libshlibtest_la_SOURCES = shlibtest.cpp + +include cluster.mk + +# +# Other test programs +# +check_PROGRAMS+=perftest +perftest_SOURCES=perftest.cpp test_tools.h TestOptions.h +perftest_LDADD=$(lib_client) + +check_PROGRAMS+=txtest +txtest_SOURCES=txtest.cpp TestOptions.h +txtest_LDADD=$(lib_client) + +check_PROGRAMS+=latencytest +latencytest_SOURCES=latencytest.cpp TestOptions.h +latencytest_LDADD=$(lib_client) + +# NB: CppUnit test libraries below will be migrated to boost test programs. +# + +# cppunit tests +broker_unit_tests = \ + AccumulatedAckTest \ + DtxWorkRecordTest \ + DeliveryRecordTest \ + ExchangeTest \ + HeadersExchangeTest \ + MessageTest \ + QueueRegistryTest \ + QueueTest \ + QueuePolicyTest \ + TimerTest \ + TopicExchangeTest \ + TxAckTest \ + TxBufferTest \ + TxPublishTest \ + MessageBuilderTest + +#client_unit_tests = \ +# ClientChannelTest + +framing_unit_tests = \ + FramingTest \ + HeaderTest \ + SequenceNumberTest + +posix_unit_tests = \ + EventChannelTest \ + EventChannelThreadsTest + +unit_tests = \ + $(broker_unit_tests) \ + $(client_unit_tests) \ + $(framing_unit_tests) \ + $(misc_unit_tests) + +# Executables for client tests + +testprogs= \ + client_test \ + topic_listener \ + topic_publisher +# echo_service + +check_PROGRAMS += $(testprogs) interop_runner + +TESTS_ENVIRONMENT = VALGRIND=$(VALGRIND) srcdir=$(srcdir) QPID_DATA_DIR= $(srcdir)/run_test + +system_tests = client_test quick_perftest quick_topictest +TESTS += run-unit-tests start_broker $(system_tests) python_tests stop_broker run_federation_tests + +EXTRA_DIST += \ + run_test vg_check \ + run-unit-tests start_broker python_tests stop_broker \ + quick_topictest \ + quick_perftest \ + topictest \ + run_federation_tests \ + .valgrind.supp \ + .valgrindrc \ + MessageUtils.h \ + MockConnectionInputHandler.h \ + TxMocks.h \ + qpid_test_plugin.h + +include gen.mk + +check_LTLIBRARIES += libdlclose_noop.la +libdlclose_noop_la_LDFLAGS = -module -rpath $(abs_builddir) +libdlclose_noop_la_SOURCES = dlclose_noop.c + +gen.mk: Makefile.am + ( \ + for i in $(testprogs); do \ + echo $${i}_SOURCES = $$i.cpp; \ + echo $${i}_LDADD = '$$(lib_client) $$(lib_common) $$(extra_libs)'; \ + done; \ + libs=; \ + for i in $(unit_tests); do \ + echo "check_LTLIBRARIES +=$${i}.la"; \ + echo $${i}_la_SOURCES = $$i.cpp; \ + echo $${i}_la_LIBADD = '$$(lib_common) $$(lib_client)'; \ + echo $${i}_la_LIBADD += '$$(lib_broker) $$(extra_libs)'; \ + echo $${i}_la_LDFLAGS = "-module -rpath `pwd`"; \ + done; \ + ) \ + > $@-t + mv $@-t $@ + +CLEANFILES+=valgrind.out *.log *.vglog dummy_test $(unit_wrappers) +MAINTAINERCLEANFILES=gen.mk + +interop_runner_SOURCES = \ + interop_runner.cpp \ + SimpleTestCaseBase.cpp \ + BasicP2PTest.cpp \ + BasicPubSubTest.cpp \ + SimpleTestCaseBase.h \ + BasicP2PTest.h \ + BasicPubSubTest.h \ + TestCase.h \ + TestOptions.h +interop_runner_LDADD = $(lib_client) $(lib_common) $(extra_libs) + +# Longer running stability tests, not run by default check: target. +# Not run under valgrind, too slow +LONG_TESTS=fanout_perftest shared_perftest multiq_perftest topic_perftest +EXTRA_DIST+=$(LONG_TESTS) run_perftest +check-long: + $(MAKE) check TESTS="start_broker $(LONG_TESTS) stop_broker" VALGRIND= diff --git a/qpid/cpp/src/tests/MessageBuilderTest.cpp b/qpid/cpp/src/tests/MessageBuilderTest.cpp new file mode 100644 index 0000000000..092e02cc2f --- /dev/null +++ b/qpid/cpp/src/tests/MessageBuilderTest.cpp @@ -0,0 +1,224 @@ +/* + * + * 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. + * + */ +#include "qpid/broker/Message.h" +#include "qpid/broker/MessageBuilder.h" +#include "qpid/broker/NullMessageStore.h" +#include "qpid/framing/frame_functors.h" +#include "qpid/framing/TypeFilter.h" +#include "qpid_test_plugin.h" +#include <list> + +using namespace boost; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + +class MessageBuilderTest : public CppUnit::TestCase +{ + class MockMessageStore : public NullMessageStore + { + enum Op {STAGE=1, APPEND=2}; + + uint64_t id; + intrusive_ptr<PersistableMessage> expectedMsg; + string expectedData; + std::list<Op> ops; + + void checkExpectation(Op actual) + { + CPPUNIT_ASSERT_EQUAL(ops.front(), actual); + ops.pop_front(); + } + + public: + MockMessageStore() : id(0), expectedMsg(0) {} + + void expectStage(PersistableMessage& msg) + { + expectedMsg = &msg; + ops.push_back(STAGE); + } + + void expectAppendContent(PersistableMessage& msg, const string& data) + { + expectedMsg = &msg; + expectedData = data; + ops.push_back(APPEND); + } + + void stage(intrusive_ptr<PersistableMessage>& msg) + { + checkExpectation(STAGE); + CPPUNIT_ASSERT_EQUAL(expectedMsg, msg); + msg->setPersistenceId(++id); + } + + void appendContent(intrusive_ptr<const PersistableMessage>& msg, const string& data) + { + checkExpectation(APPEND); + CPPUNIT_ASSERT_EQUAL(static_pointer_cast<const PersistableMessage>(expectedMsg), msg); + CPPUNIT_ASSERT_EQUAL(expectedData, data); + } + + bool expectationsMet() + { + return ops.empty(); + } + }; + + CPPUNIT_TEST_SUITE(MessageBuilderTest); + CPPUNIT_TEST(testHeaderOnly); + CPPUNIT_TEST(test1ContentFrame); + CPPUNIT_TEST(test2ContentFrames); + CPPUNIT_TEST(testStaging); + CPPUNIT_TEST_SUITE_END(); + + public: + + void testHeaderOnly(){ + MessageBuilder builder(0, 0); + builder.start(SequenceNumber()); + + std::string exchange("builder-exchange"); + std::string key("builder-exchange"); + + AMQFrame method(in_place<MessageTransferBody>( + ProtocolVersion(), 0, exchange, 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + + header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(0); + header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); + + builder.handle(method); + builder.handle(header); + + CPPUNIT_ASSERT(builder.getMessage()); + CPPUNIT_ASSERT_EQUAL(exchange, builder.getMessage()->getExchangeName()); + CPPUNIT_ASSERT_EQUAL(key, builder.getMessage()->getRoutingKey()); + CPPUNIT_ASSERT(builder.getMessage()->getFrames().isComplete()); + } + + void test1ContentFrame(){ + MessageBuilder builder(0, 0); + builder.start(SequenceNumber()); + + std::string data("abcdefg"); + std::string exchange("builder-exchange"); + std::string key("builder-exchange"); + + AMQFrame method(in_place<MessageTransferBody>(ProtocolVersion(), 0, exchange, 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + AMQFrame content(in_place<AMQContentBody>(data)); + method.setEof(false); + header.setBof(false); + header.setEof(false); + content.setBof(false); + + header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data.size()); + header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); + + builder.handle(method); + CPPUNIT_ASSERT(builder.getMessage()); + CPPUNIT_ASSERT(!builder.getMessage()->getFrames().isComplete()); + + builder.handle(header); + CPPUNIT_ASSERT(builder.getMessage()); + CPPUNIT_ASSERT(!builder.getMessage()->getFrames().isComplete()); + + builder.handle(content); + CPPUNIT_ASSERT(builder.getMessage()); + CPPUNIT_ASSERT(builder.getMessage()->getFrames().isComplete()); + } + + void test2ContentFrames(){ + MessageBuilder builder(0, 0); + builder.start(SequenceNumber()); + + std::string data1("abcdefg"); + std::string data2("hijklmn"); + std::string exchange("builder-exchange"); + std::string key("builder-exchange"); + + AMQFrame method(in_place<MessageTransferBody>( + ProtocolVersion(), 0, exchange, 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + AMQFrame content1(in_place<AMQContentBody>(data1)); + AMQFrame content2(in_place<AMQContentBody>(data2)); + method.setEof(false); + header.setBof(false); + header.setEof(false); + content1.setBof(false); + content1.setEof(false); + content2.setBof(false); + + header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size() + data2.size()); + header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); + + builder.handle(method); + builder.handle(header); + builder.handle(content1); + CPPUNIT_ASSERT(builder.getMessage()); + CPPUNIT_ASSERT(!builder.getMessage()->getFrames().isComplete()); + + builder.handle(content2); + CPPUNIT_ASSERT(builder.getMessage()); + CPPUNIT_ASSERT(builder.getMessage()->getFrames().isComplete()); + } + + void testStaging(){ + MockMessageStore store; + MessageBuilder builder(&store, 5); + builder.start(SequenceNumber()); + + std::string data1("abcdefg"); + std::string data2("hijklmn"); + std::string exchange("builder-exchange"); + std::string key("builder-exchange"); + + AMQFrame method(in_place<MessageTransferBody>( + ProtocolVersion(), 0, exchange, 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + AMQFrame content1(in_place<AMQContentBody>(data1)); + AMQFrame content2(in_place<AMQContentBody>(data2)); + + header.castBody<AMQHeaderBody>()->get<MessageProperties>(true)->setContentLength(data1.size() + data2.size()); + header.castBody<AMQHeaderBody>()->get<DeliveryProperties>(true)->setRoutingKey(key); + + builder.handle(method); + builder.handle(header); + + store.expectStage(*builder.getMessage()); + builder.handle(content1); + CPPUNIT_ASSERT(store.expectationsMet()); + CPPUNIT_ASSERT_EQUAL((uint64_t) 1, builder.getMessage()->getPersistenceId()); + + store.expectAppendContent(*builder.getMessage(), data2); + builder.handle(content2); + CPPUNIT_ASSERT(store.expectationsMet()); + + //were the content frames dropped? + CPPUNIT_ASSERT_EQUAL((uint64_t) 0, builder.getMessage()->contentSize()); + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(MessageBuilderTest); diff --git a/qpid/cpp/src/tests/MessageTest.cpp b/qpid/cpp/src/tests/MessageTest.cpp new file mode 100644 index 0000000000..a19080e1ce --- /dev/null +++ b/qpid/cpp/src/tests/MessageTest.cpp @@ -0,0 +1,97 @@ +/* + * + * 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. + * + */ +#include "qpid/broker/Message.h" +#include "qpid/framing/AMQP_HighestVersion.h" +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/FieldValue.h" + +#include "qpid_test_plugin.h" + +#include <iostream> + +using namespace boost; +using namespace qpid::broker; +using namespace qpid::framing; + + +class MessageTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(MessageTest); + CPPUNIT_TEST(testEncodeDecode); + CPPUNIT_TEST_SUITE_END(); + + public: + + void testEncodeDecode() + { + string exchange = "MyExchange"; + string routingKey = "MyRoutingKey"; + string messageId = "MyMessage"; + string data1("abcdefg"); + string data2("hijklmn"); + + intrusive_ptr<Message> msg(new Message()); + + AMQFrame method(in_place<MessageTransferBody>( + ProtocolVersion(), 0, exchange, 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + AMQFrame content1(in_place<AMQContentBody>(data1)); + AMQFrame content2(in_place<AMQContentBody>(data2)); + + msg->getFrames().append(method); + msg->getFrames().append(header); + msg->getFrames().append(content1); + msg->getFrames().append(content2); + + MessageProperties* mProps = msg->getFrames().getHeaders()->get<MessageProperties>(true); + mProps->setContentLength(data1.size() + data2.size()); + mProps->setMessageId(messageId); + FieldTable applicationHeaders; + applicationHeaders.setString("abc", "xyz"); + mProps->setApplicationHeaders(applicationHeaders); + DeliveryProperties* dProps = msg->getFrames().getHeaders()->get<DeliveryProperties>(true); + dProps->setRoutingKey(routingKey); + dProps->setDeliveryMode(PERSISTENT); + CPPUNIT_ASSERT(msg->isPersistent()); + + char* buff = static_cast<char*>(::alloca(msg->encodedSize())); + Buffer wbuffer(buff, msg->encodedSize()); + msg->encode(wbuffer); + + Buffer rbuffer(buff, msg->encodedSize()); + msg = new Message(); + msg->decodeHeader(rbuffer); + msg->decodeContent(rbuffer); + CPPUNIT_ASSERT_EQUAL(exchange, msg->getExchangeName()); + CPPUNIT_ASSERT_EQUAL(routingKey, msg->getRoutingKey()); + CPPUNIT_ASSERT_EQUAL((uint64_t) data1.size() + data2.size(), msg->contentSize()); + CPPUNIT_ASSERT_EQUAL((uint64_t) data1.size() + data2.size(), msg->getProperties<MessageProperties>()->getContentLength()); + CPPUNIT_ASSERT_EQUAL(messageId, msg->getProperties<MessageProperties>()->getMessageId()); + CPPUNIT_ASSERT(StringValue("xyz") == *msg->getProperties<MessageProperties>()->getApplicationHeaders().get("abc")); + CPPUNIT_ASSERT_EQUAL((uint8_t) PERSISTENT, msg->getProperties<DeliveryProperties>()->getDeliveryMode()); + CPPUNIT_ASSERT(msg->isPersistent()); + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(MessageTest); + diff --git a/qpid/cpp/src/tests/MessageUtils.h b/qpid/cpp/src/tests/MessageUtils.h new file mode 100644 index 0000000000..3def8cd41b --- /dev/null +++ b/qpid/cpp/src/tests/MessageUtils.h @@ -0,0 +1,54 @@ +/* + * + * 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. + * + */ + +#include "qpid/broker/Message.h" +#include "qpid/broker/MessageDelivery.h" +#include "qpid/framing/AMQFrame.h" + +using namespace qpid; +using namespace broker; +using namespace framing; + +struct MessageUtils +{ + static boost::intrusive_ptr<Message> createMessage(const string& exchange, const string& routingKey, + const string& messageId, uint64_t contentSize = 0) + { + boost::intrusive_ptr<Message> msg(new Message()); + + AMQFrame method(in_place<MessageTransferBody>(ProtocolVersion(), 0, exchange, 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + + msg->getFrames().append(method); + msg->getFrames().append(header); + MessageProperties* props = msg->getFrames().getHeaders()->get<MessageProperties>(true); + props->setContentLength(contentSize); + props->setMessageId(messageId); + msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey); + return msg; + } + + static void addContent(boost::intrusive_ptr<Message> msg, const string& data) + { + AMQFrame content(in_place<AMQContentBody>(data)); + msg->getFrames().append(content); + } +}; diff --git a/qpid/cpp/src/tests/MockConnectionInputHandler.h b/qpid/cpp/src/tests/MockConnectionInputHandler.h new file mode 100644 index 0000000000..89b6155355 --- /dev/null +++ b/qpid/cpp/src/tests/MockConnectionInputHandler.h @@ -0,0 +1,100 @@ +#ifndef _tests_MockConnectionInputHandler_h +#define _tests_MockConnectionInputHandler_h + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "qpid/sys/ConnectionInputHandler.h" +#include "qpid/sys/ConnectionInputHandlerFactory.h" +#include "qpid/sys/Monitor.h" + +struct MockConnectionInputHandler : public qpid::sys::ConnectionInputHandler { + + MockConnectionInputHandler() : state(START) {} + + ~MockConnectionInputHandler() {} + + void received(qpid::framing::AMQFrame* framep) { + qpid::sys::Monitor::ScopedLock l(monitor); + frame = *framep; + setState(GOT_FRAME); + } + + qpid::framing::AMQFrame waitForFrame() { + waitFor(GOT_FRAME); + return frame; + } + + void waitForClosed() { + waitFor(CLOSED); + } + + void closed() { + qpid::sys::Monitor::ScopedLock l(monitor); + setState(CLOSED); + } + + void idleOut() {} + void idleIn() {} + + private: + typedef enum { START, GOT_FRAME, CLOSED } State; + + void setState(State s) { + state = s; + monitor.notify(); + } + + void waitFor(State s) { + qpid::sys::Monitor::ScopedLock l(monitor); + qpid::sys::Time deadline = qpid::sys::now() + 10*qpid::sys::TIME_SEC; + while (state != s) + CPPUNIT_ASSERT(monitor.wait(deadline)); + } + + qpid::sys::Monitor monitor; + State state; + qpid::framing::AMQFrame frame; +}; + + +struct MockConnectionInputHandlerFactory : public qpid::sys::ConnectionInputHandlerFactory { + MockConnectionInputHandlerFactory() : handler(0) {} + + qpid::sys::ConnectionInputHandler* create(qpid::sys::ConnectionOutputHandler*) { + qpid::sys::Monitor::ScopedLock lock(monitor); + handler = new MockConnectionInputHandler(); + monitor.notifyAll(); + return handler; + } + + void waitForHandler() { + qpid::sys::Monitor::ScopedLock lock(monitor); + qpid::sys::Time deadline = + qpid::sys::now() + 500 * qpid::sys::TIME_SEC; + while (handler == 0) + CPPUNIT_ASSERT(monitor.wait(deadline)); + } + + MockConnectionInputHandler* handler; + qpid::sys::Monitor monitor; +}; + + + +#endif /*!_tests_MockConnectionInputHandler_h*/ diff --git a/qpid/cpp/src/tests/PollerTest.cpp b/qpid/cpp/src/tests/PollerTest.cpp new file mode 100644 index 0000000000..fcb1d0dadf --- /dev/null +++ b/qpid/cpp/src/tests/PollerTest.cpp @@ -0,0 +1,164 @@ +/* + * + * 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. + * + */ + +/** + * Use socketpair to test the poller + */ + +#include "qpid/sys/Poller.h" + +#include <string> +#include <iostream> +#include <memory> +#include <exception> + +#include <assert.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <fcntl.h> +#include <unistd.h> +#include <errno.h> + +using namespace std; +using namespace qpid::sys; + +int writeALot(int fd, const string& s) { + int bytesWritten = 0; + do { + errno = 0; + int lastWrite = ::write(fd, s.c_str(), s.size()); + if ( lastWrite >= 0) { + bytesWritten += lastWrite; + } + } while (errno != EAGAIN); + return bytesWritten; +} + +int readALot(int fd) { + int bytesRead = 0; + char buf[1024]; + + do { + errno = 0; + int lastRead = ::read(fd, buf, sizeof(buf)); + if ( lastRead >= 0) { + bytesRead += lastRead; + } + } while (errno != EAGAIN); + return bytesRead; +} + +int main(int argc, char** argv) +{ + try + { + int sv[2]; + int rc = ::socketpair(AF_LOCAL, SOCK_STREAM, 0, sv); + assert(rc >= 0); + + // Set non-blocking + rc = ::fcntl(sv[0], F_SETFL, O_NONBLOCK); + assert(rc >= 0); + + rc = ::fcntl(sv[1], F_SETFL, O_NONBLOCK); + assert(rc >= 0); + + // Make up a large string + string testString = "This is only a test ... 1,2,3,4,5,6,7,8,9,10;"; + for (int i = 0; i < 6; i++) + testString += testString; + + // Read as much as we can from socket 0 + int bytesRead = readALot(sv[0]); + assert(bytesRead == 0); + cout << "Read(0): " << bytesRead << " bytes\n"; + + // Write as much as we can to socket 0 + int bytesWritten = writeALot(sv[0], testString); + cout << "Wrote(0): " << bytesWritten << " bytes\n"; + + // Read as much as we can from socket 1 + bytesRead = readALot(sv[1]); + assert(bytesRead == bytesWritten); + cout << "Read(1): " << bytesRead << " bytes\n"; + + auto_ptr<Poller> poller(new Poller); + + PollerHandle h0(sv[0]); + PollerHandle h1(sv[1]); + + poller->addFd(h0, Poller::INOUT); + + // Wait for 500ms - h0 should be writable + Poller::Event event = poller->wait(); + assert(event.handle == &h0); + assert(event.dir == Poller::OUT); + + // Write as much as we can to socket 0 + bytesWritten = writeALot(sv[0], testString); + cout << "Wrote(0): " << bytesWritten << " bytes\n"; + + // Wait for 500ms - h0 no longer writable + poller->rearmFd(h0); + event = poller->wait(500000000); + assert(event.handle == 0); + + // Test we can read it all now + poller->addFd(h1, Poller::INOUT); + event = poller->wait(); + assert(event.handle == &h1); + assert(event.dir == Poller::INOUT); + + bytesRead = readALot(sv[1]); + assert(bytesRead == bytesWritten); + cout << "Read(1): " << bytesRead << " bytes\n"; + + // At this point h1 should have been disabled from the poller + // (as it was just returned) and h0 can write again + event = poller->wait(); + assert(event.handle == &h0); + assert(event.dir == Poller::OUT); + + // Now both the handles should be disabled + event = poller->wait(500000000); + assert(event.handle == 0); + + // Test shutdown + poller->shutdown(); + event = poller->wait(); + assert(event.handle == 0); + assert(event.dir == Poller::SHUTDOWN); + + event = poller->wait(); + assert(event.handle == 0); + assert(event.dir == Poller::SHUTDOWN); + + poller->delFd(h1); + poller->delFd(h0); + + return 0; + } catch (exception& e) { + cout << "Caught exception " << e.what() << "\n"; + } +} + + diff --git a/qpid/cpp/src/tests/QueuePolicyTest.cpp b/qpid/cpp/src/tests/QueuePolicyTest.cpp new file mode 100644 index 0000000000..467f43638f --- /dev/null +++ b/qpid/cpp/src/tests/QueuePolicyTest.cpp @@ -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. + * + */ +#include "qpid/broker/QueuePolicy.h" +#include "qpid_test_plugin.h" + +using namespace qpid::broker; +using namespace qpid::framing; + +class QueuePolicyTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(QueuePolicyTest); + CPPUNIT_TEST(testCount); + CPPUNIT_TEST(testSize); + CPPUNIT_TEST(testBoth); + CPPUNIT_TEST(testSettings); + CPPUNIT_TEST_SUITE_END(); + + public: + void testCount(){ + QueuePolicy policy(5, 0); + CPPUNIT_ASSERT(!policy.limitExceeded()); + for (int i = 0; i < 5; i++) policy.enqueued(10); + CPPUNIT_ASSERT_EQUAL((uint64_t) 0, policy.getMaxSize()); + CPPUNIT_ASSERT_EQUAL((uint32_t) 5, policy.getMaxCount()); + CPPUNIT_ASSERT(!policy.limitExceeded()); + policy.enqueued(10); + CPPUNIT_ASSERT(policy.limitExceeded()); + policy.dequeued(10); + CPPUNIT_ASSERT(!policy.limitExceeded()); + policy.enqueued(10); + CPPUNIT_ASSERT(policy.limitExceeded()); + } + + void testSize(){ + QueuePolicy policy(0, 50); + for (int i = 0; i < 5; i++) policy.enqueued(10); + CPPUNIT_ASSERT(!policy.limitExceeded()); + policy.enqueued(10); + CPPUNIT_ASSERT(policy.limitExceeded()); + policy.dequeued(10); + CPPUNIT_ASSERT(!policy.limitExceeded()); + policy.enqueued(10); + CPPUNIT_ASSERT(policy.limitExceeded()); + } + + void testBoth(){ + QueuePolicy policy(5, 50); + for (int i = 0; i < 5; i++) policy.enqueued(11); + CPPUNIT_ASSERT(policy.limitExceeded()); + policy.dequeued(20); + CPPUNIT_ASSERT(!policy.limitExceeded());//fails + policy.enqueued(5); + policy.enqueued(10); + CPPUNIT_ASSERT(policy.limitExceeded()); + } + + void testSettings(){ + //test reading and writing the policy from/to field table + FieldTable settings; + QueuePolicy a(101, 303); + a.update(settings); + QueuePolicy b(settings); + CPPUNIT_ASSERT_EQUAL(a.getMaxCount(), b.getMaxCount()); + CPPUNIT_ASSERT_EQUAL(a.getMaxSize(), b.getMaxSize()); + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(QueuePolicyTest); + diff --git a/qpid/cpp/src/tests/QueueRegistryTest.cpp b/qpid/cpp/src/tests/QueueRegistryTest.cpp new file mode 100644 index 0000000000..5fd861d6be --- /dev/null +++ b/qpid/cpp/src/tests/QueueRegistryTest.cpp @@ -0,0 +1,95 @@ +/* + * 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. + */ + +#include "qpid/broker/QueueRegistry.h" +#include "qpid_test_plugin.h" +#include <string> + +using namespace qpid::broker; + +class QueueRegistryTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(QueueRegistryTest); + CPPUNIT_TEST(testDeclare); + CPPUNIT_TEST(testDeclareTmp); + CPPUNIT_TEST(testFind); + CPPUNIT_TEST(testDestroy); + CPPUNIT_TEST_SUITE_END(); + + private: + std::string foo, bar; + QueueRegistry reg; + std::pair<Queue::shared_ptr, bool> qc; + + public: + void setUp() { + foo = "foo"; + bar = "bar"; + } + + void testDeclare() { + qc = reg.declare(foo, false, 0, 0); + Queue::shared_ptr q = qc.first; + CPPUNIT_ASSERT(q); + CPPUNIT_ASSERT(qc.second); // New queue + CPPUNIT_ASSERT_EQUAL(foo, q->getName()); + + qc = reg.declare(foo, false, 0, 0); + CPPUNIT_ASSERT_EQUAL(q, qc.first); + CPPUNIT_ASSERT(!qc.second); + + qc = reg.declare(bar, false, 0, 0); + q = qc.first; + CPPUNIT_ASSERT(q); + CPPUNIT_ASSERT_EQUAL(true, qc.second); + CPPUNIT_ASSERT_EQUAL(bar, q->getName()); + } + + void testDeclareTmp() + { + qc = reg.declare(std::string(), false, 0, 0); + CPPUNIT_ASSERT(qc.second); + CPPUNIT_ASSERT_EQUAL(std::string("tmp_1"), qc.first->getName()); + } + + void testFind() { + CPPUNIT_ASSERT(reg.find(foo) == 0); + + reg.declare(foo, false, 0, 0); + reg.declare(bar, false, 0, 0); + Queue::shared_ptr q = reg.find(bar); + CPPUNIT_ASSERT(q); + CPPUNIT_ASSERT_EQUAL(bar, q->getName()); + } + + void testDestroy() { + qc = reg.declare(foo, false, 0, 0); + reg.destroy(foo); + // Queue is gone from the registry. + CPPUNIT_ASSERT(reg.find(foo) == 0); + // Queue is not actually destroyed till we drop our reference. + CPPUNIT_ASSERT_EQUAL(foo, qc.first->getName()); + // We shoud be the only reference. + CPPUNIT_ASSERT_EQUAL(1L, qc.first.use_count()); + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(QueueRegistryTest); diff --git a/qpid/cpp/src/tests/QueueTest.cpp b/qpid/cpp/src/tests/QueueTest.cpp new file mode 100644 index 0000000000..70132bce76 --- /dev/null +++ b/qpid/cpp/src/tests/QueueTest.cpp @@ -0,0 +1,258 @@ + /* + * + * 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. + * + */ +#include "qpid/Exception.h" +#include "qpid/broker/Queue.h" +#include "qpid/broker/Deliverable.h" +#include "qpid/broker/ExchangeRegistry.h" +#include "qpid/broker/QueueRegistry.h" +#include "qpid_test_plugin.h" +#include <iostream> +#include "boost/format.hpp" + +using boost::intrusive_ptr; +using namespace qpid; +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::sys; + + +class TestConsumer : public virtual Consumer{ +public: + typedef boost::shared_ptr<TestConsumer> shared_ptr; + + intrusive_ptr<Message> last; + bool received; + TestConsumer(): received(false) {}; + + virtual bool deliver(QueuedMessage& msg){ + last = msg.payload; + received = true; + return true; + }; + void notify() {} +}; + +class FailOnDeliver : public Deliverable +{ +public: + void deliverTo(Queue::shared_ptr& queue) + { + throw Exception(QPID_MSG("Invalid delivery to " << queue->getName())); + } +}; + +class QueueTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(QueueTest); + CPPUNIT_TEST(testConsumers); + CPPUNIT_TEST(testRegistry); + CPPUNIT_TEST(testDequeue); + CPPUNIT_TEST(testBound); + CPPUNIT_TEST(testAsyncMessage); + CPPUNIT_TEST(testAsyncMessageCount); + CPPUNIT_TEST_SUITE_END(); + + + public: + intrusive_ptr<Message> message(std::string exchange, std::string routingKey) { + intrusive_ptr<Message> msg(new Message()); + AMQFrame method(in_place<MessageTransferBody>( + ProtocolVersion(), 0, exchange, 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + msg->getFrames().append(method); + msg->getFrames().append(header); + msg->getFrames().getHeaders()->get<DeliveryProperties>(true)->setRoutingKey(routingKey); + return msg; + } + + + void testAsyncMessage(){ + + Queue::shared_ptr queue(new Queue("my_test_queue", true)); + intrusive_ptr<Message> received; + + TestConsumer c1; + queue->consume(c1); + + + //Test basic delivery: + intrusive_ptr<Message> msg1 = message("e", "A"); + msg1->enqueueAsync();//this is done on enqueue which is not called from process + queue->process(msg1); + sleep(2); + + CPPUNIT_ASSERT(!c1.received); + msg1->enqueueComplete(); + + received = queue->dequeue().payload; + CPPUNIT_ASSERT_EQUAL(msg1.get(), received.get()); + + + } + + + void testAsyncMessageCount(){ + Queue::shared_ptr queue(new Queue("my_test_queue", true)); + intrusive_ptr<Message> msg1 = message("e", "A"); + msg1->enqueueAsync();//this is done on enqueue which is not called from process + + queue->process(msg1); + sleep(2); + uint32_t compval=0; + CPPUNIT_ASSERT_EQUAL(compval, queue->getMessageCount()); + msg1->enqueueComplete(); + compval=1; + CPPUNIT_ASSERT_EQUAL(compval, queue->getMessageCount()); + + } + + void testConsumers(){ + Queue::shared_ptr queue(new Queue("my_queue", true)); + + //Test adding consumers: + TestConsumer c1; + TestConsumer c2; + queue->consume(c1); + queue->consume(c2); + + CPPUNIT_ASSERT_EQUAL(uint32_t(2), queue->getConsumerCount()); + + //Test basic delivery: + intrusive_ptr<Message> msg1 = message("e", "A"); + intrusive_ptr<Message> msg2 = message("e", "B"); + intrusive_ptr<Message> msg3 = message("e", "C"); + + queue->deliver(msg1); + CPPUNIT_ASSERT(queue->dispatch(c1)); + CPPUNIT_ASSERT_EQUAL(msg1.get(), c1.last.get()); + + queue->deliver(msg2); + CPPUNIT_ASSERT(queue->dispatch(c2)); + CPPUNIT_ASSERT_EQUAL(msg2.get(), c2.last.get()); + + c1.received = false; + queue->deliver(msg3); + CPPUNIT_ASSERT(queue->dispatch(c1)); + CPPUNIT_ASSERT_EQUAL(msg3.get(), c1.last.get()); + + //Test cancellation: + queue->cancel(c1); + CPPUNIT_ASSERT_EQUAL(uint32_t(1), queue->getConsumerCount()); + queue->cancel(c2); + CPPUNIT_ASSERT_EQUAL(uint32_t(0), queue->getConsumerCount()); + } + + void testRegistry(){ + //Test use of queues in registry: + QueueRegistry registry; + registry.declare("queue1", true, true); + registry.declare("queue2", true, true); + registry.declare("queue3", true, true); + + CPPUNIT_ASSERT(registry.find("queue1")); + CPPUNIT_ASSERT(registry.find("queue2")); + CPPUNIT_ASSERT(registry.find("queue3")); + + registry.destroy("queue1"); + registry.destroy("queue2"); + registry.destroy("queue3"); + + CPPUNIT_ASSERT(!registry.find("queue1")); + CPPUNIT_ASSERT(!registry.find("queue2")); + CPPUNIT_ASSERT(!registry.find("queue3")); + } + + void testDequeue(){ + Queue::shared_ptr queue(new Queue("my_queue", true)); + intrusive_ptr<Message> msg1 = message("e", "A"); + intrusive_ptr<Message> msg2 = message("e", "B"); + intrusive_ptr<Message> msg3 = message("e", "C"); + intrusive_ptr<Message> received; + + queue->deliver(msg1); + queue->deliver(msg2); + queue->deliver(msg3); + + CPPUNIT_ASSERT_EQUAL(uint32_t(3), queue->getMessageCount()); + + received = queue->dequeue().payload; + CPPUNIT_ASSERT_EQUAL(msg1.get(), received.get()); + CPPUNIT_ASSERT_EQUAL(uint32_t(2), queue->getMessageCount()); + + received = queue->dequeue().payload; + CPPUNIT_ASSERT_EQUAL(msg2.get(), received.get()); + CPPUNIT_ASSERT_EQUAL(uint32_t(1), queue->getMessageCount()); + + TestConsumer consumer; + queue->consume(consumer); + queue->dispatch(consumer); + if (!consumer.received) + sleep(2); + + CPPUNIT_ASSERT_EQUAL(msg3.get(), consumer.last.get()); + CPPUNIT_ASSERT_EQUAL(uint32_t(0), queue->getMessageCount()); + + received = queue->dequeue().payload; + CPPUNIT_ASSERT(!received); + CPPUNIT_ASSERT_EQUAL(uint32_t(0), queue->getMessageCount()); + + } + + void testBound() + { + //test the recording of bindings, and use of those to allow a queue to be unbound + string key("my-key"); + FieldTable args; + + Queue::shared_ptr queue(new Queue("my-queue", true)); + ExchangeRegistry exchanges; + //establish bindings from exchange->queue and notify the queue as it is bound: + Exchange::shared_ptr exchange1 = exchanges.declare("my-exchange-1", "direct").first; + exchange1->bind(queue, key, &args); + queue->bound(exchange1->getName(), key, args); + + Exchange::shared_ptr exchange2 = exchanges.declare("my-exchange-2", "fanout").first; + exchange2->bind(queue, key, &args); + queue->bound(exchange2->getName(), key, args); + + Exchange::shared_ptr exchange3 = exchanges.declare("my-exchange-3", "topic").first; + exchange3->bind(queue, key, &args); + queue->bound(exchange3->getName(), key, args); + + //delete one of the exchanges: + exchanges.destroy(exchange2->getName()); + exchange2.reset(); + + //unbind the queue from all exchanges it knows it has been bound to: + queue->unbind(exchanges, queue); + + //ensure the remaining exchanges don't still have the queue bound to them: + FailOnDeliver deliverable; + exchange1->route(deliverable, key, &args); + exchange3->route(deliverable, key, &args); + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(QueueTest); + + diff --git a/qpid/cpp/src/tests/README b/qpid/cpp/src/tests/README new file mode 100644 index 0000000000..3cf6fab2ad --- /dev/null +++ b/qpid/cpp/src/tests/README @@ -0,0 +1,71 @@ += Running Qpid C++ tests = + +General philosophy is that "make check" run all tests by default, but +developers can run tests selectively as explained below. + +== Valgrind == + +By default we run tests under valgrind to detect memory errors if valgrind +is present. ./configure --disable-valgrind will disable it. + +Default valgrind options are specified in .valgrindrc-default, which a +checked-in file. The actual options used are in .valgrindrc which is a +local file. Normally it is a copy of valgrindrc-default but you can +modify at will. + +Supressed errors are listed in .valgrind.supp. If you want to change +suppressions for local testing, just modify .valgrindrc to point to a +different file. Do NOT add suppressions to .valgrindrc.supp unless +they are known problems outside of Qpid that can't reasonably be +worked around in Qpid. + + +== Unit Tests == + +Unit tests shared libraries containing CppUnit test plug-ins, run by +the CppUnit DllPlugInTester program. + +run-unit-tests runs tests under valgrind, you can run it directly. +Library names (without path or .so) and CppUnit test paths can be +specified on the command line or in env var UNIT_TESTS. For example: + +Selected test classes: +./run-unit-tests ValueTest ClientChannelTest + +Individual test method +./run-unit-tests ValueTest :ValueTest::testStringValueEquals + +You can also Build and run selected tests via make: +make check TESTS=run-unit-tests UNIT_TESTS=ClientChannelTest + +NOTE: If you use DllPlugInTester directly note that if foobar.so is in +the current directory then this will fail with "can't load plugin": + # DllPluginTester foobar.so + +Instead you need to say: + # DllPluginTester ./foobar.so + +DllPluginTester uses dlopen() which only searches for shlibs +in the standard places unless the filename contains a "/". In that +case it just tries to open the filename. + +== System Tests == + +System tests are self contained AMQP client executables or scripts. +They are listed in the TESTS make variable, which can be over-ridden. + +The ./start_broker "test" launches the broker, ./stop_broker" stops it. +Tests in between assume the broker is running. + +./python_tests: runs ../python/run_tests. This is the main set of +system testss for the broker. + +Other C++ client test executables and scripts under client/test are +system tests for the client. + +By setting TESTS in a make command you can run a different subset of tests +against an already-running broker. + + + + diff --git a/qpid/cpp/src/tests/RefCounted.cpp b/qpid/cpp/src/tests/RefCounted.cpp new file mode 100644 index 0000000000..cd08a4491a --- /dev/null +++ b/qpid/cpp/src/tests/RefCounted.cpp @@ -0,0 +1,50 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "qpid/RefCounted.h" +#include <boost/intrusive_ptr.hpp> + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(RefCountedTestSuiteTestSuite) + +using boost::intrusive_ptr; +using namespace std; +using namespace qpid; + +struct CountMe : public RefCounted { + static int instances; + CountMe() { ++instances; } + ~CountMe() { --instances; } +}; + +int CountMe::instances=0; + +BOOST_AUTO_TEST_CASE(testRefCounted) { + BOOST_CHECK_EQUAL(0, CountMe::instances); + intrusive_ptr<CountMe> p(new CountMe()); + BOOST_CHECK_EQUAL(1, CountMe::instances); + intrusive_ptr<CountMe> q(p); + BOOST_CHECK_EQUAL(1, CountMe::instances); + q=0; + BOOST_CHECK_EQUAL(1, CountMe::instances); + p=0; + BOOST_CHECK_EQUAL(0, CountMe::instances); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/SequenceNumberTest.cpp b/qpid/cpp/src/tests/SequenceNumberTest.cpp new file mode 100644 index 0000000000..d227b78323 --- /dev/null +++ b/qpid/cpp/src/tests/SequenceNumberTest.cpp @@ -0,0 +1,220 @@ +/* + * + * 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. + * + */ + +#include "qpid_test_plugin.h" +#include <iostream> +#include "qpid/framing/SequenceNumber.h" +#include "qpid/framing/SequenceNumberSet.h" + +using namespace qpid::framing; + +class SequenceNumberTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(SequenceNumberTest); + CPPUNIT_TEST(testIncrementPostfix); + CPPUNIT_TEST(testIncrementPrefix); + CPPUNIT_TEST(testWrapAround); + CPPUNIT_TEST(testCondense); + CPPUNIT_TEST(testCondenseSingleRange); + CPPUNIT_TEST(testCondenseSingleItem); + CPPUNIT_TEST(testDifference); + CPPUNIT_TEST(testDifferenceWithWrapAround1); + CPPUNIT_TEST(testDifferenceWithWrapAround2); + CPPUNIT_TEST_SUITE_END(); + + public: + + void testIncrementPostfix() + { + SequenceNumber a; + SequenceNumber b; + CPPUNIT_ASSERT(!(a > b)); + CPPUNIT_ASSERT(!(b < a)); + CPPUNIT_ASSERT(a == b); + + SequenceNumber c = a++; + CPPUNIT_ASSERT(a > b); + CPPUNIT_ASSERT(b < a); + CPPUNIT_ASSERT(a != b); + CPPUNIT_ASSERT(c < a); + CPPUNIT_ASSERT(a != c); + + b++; + CPPUNIT_ASSERT(!(a > b)); + CPPUNIT_ASSERT(!(b < a)); + CPPUNIT_ASSERT(a == b); + CPPUNIT_ASSERT(c < b); + CPPUNIT_ASSERT(b != c); + } + + void testIncrementPrefix() + { + SequenceNumber a; + SequenceNumber b; + CPPUNIT_ASSERT(!(a > b)); + CPPUNIT_ASSERT(!(b < a)); + CPPUNIT_ASSERT(a == b); + + SequenceNumber c = ++a; + CPPUNIT_ASSERT(a > b); + CPPUNIT_ASSERT(b < a); + CPPUNIT_ASSERT(a != b); + CPPUNIT_ASSERT(a == c); + + ++b; + CPPUNIT_ASSERT(!(a > b)); + CPPUNIT_ASSERT(!(b < a)); + CPPUNIT_ASSERT(a == b); + } + + void testWrapAround() + { + const uint32_t max = 0xFFFFFFFF; + SequenceNumber a(max - 10); + SequenceNumber b(max - 5); + checkComparison(a, b, 5); + + const uint32_t max_signed = 0x7FFFFFFF; + SequenceNumber c(max_signed - 10); + SequenceNumber d(max_signed - 5); + checkComparison(c, d, 5); + } + + void checkComparison(SequenceNumber& a, SequenceNumber& b, int gap) + { + //increment until b wraps around + for (int i = 0; i < (gap + 2); i++) { + CPPUNIT_ASSERT(++a < ++b);//test prefix + } + //keep incrementing until a also wraps around + for (int i = 0; i < (gap + 2); i++) { + CPPUNIT_ASSERT(a++ < b++);//test postfix + } + //let a 'catch up' + for (int i = 0; i < gap; i++) { + a++; + } + CPPUNIT_ASSERT(a == b); + CPPUNIT_ASSERT(++a > b); + } + + void testCondense() + { + SequenceNumberSet set; + for (uint i = 0; i < 6; i++) { + set.push_back(SequenceNumber(i)); + } + set.push_back(SequenceNumber(7)); + for (uint i = 9; i < 13; i++) { + set.push_back(SequenceNumber(i)); + } + set.push_back(SequenceNumber(13)); + SequenceNumberSet actual = set.condense(); + + SequenceNumberSet expected; + expected.addRange(SequenceNumber(0), SequenceNumber(5)); + expected.addRange(SequenceNumber(7), SequenceNumber(7)); + expected.addRange(SequenceNumber(9), SequenceNumber(13)); + CPPUNIT_ASSERT_EQUAL(expected, actual); + } + + void testCondenseSingleRange() + { + SequenceNumberSet set; + for (uint i = 0; i < 6; i++) { + set.push_back(SequenceNumber(i)); + } + SequenceNumberSet actual = set.condense(); + + SequenceNumberSet expected; + expected.addRange(SequenceNumber(0), SequenceNumber(5)); + CPPUNIT_ASSERT_EQUAL(expected, actual); + } + + void testCondenseSingleItem() + { + SequenceNumberSet set; + set.push_back(SequenceNumber(1)); + SequenceNumberSet actual = set.condense(); + + SequenceNumberSet expected; + expected.addRange(SequenceNumber(1), SequenceNumber(1)); + CPPUNIT_ASSERT_EQUAL(expected, actual); + } + + void testDifference() + { + SequenceNumber a; + SequenceNumber b; + + for (int i = 0; i < 10; i++, ++a) { + CPPUNIT_ASSERT_EQUAL(i, a - b); + CPPUNIT_ASSERT_EQUAL(-i, b - a); + } + + b = a; + + for (int i = 0; i < 10; i++, ++b) { + CPPUNIT_ASSERT_EQUAL(-i, a - b); + CPPUNIT_ASSERT_EQUAL(i, b - a); + } + } + + void testDifferenceWithWrapAround1() + { + const uint32_t max = 0xFFFFFFFF; + SequenceNumber a(max - 5); + SequenceNumber b(max - 10); + checkDifference(a, b, 5); + } + + void testDifferenceWithWrapAround2() + { + const uint32_t max_signed = 0x7FFFFFFF; + SequenceNumber c(max_signed - 5); + SequenceNumber d(max_signed - 10); + checkDifference(c, d, 5); + } + + void checkDifference(SequenceNumber& a, SequenceNumber& b, int gap) + { + CPPUNIT_ASSERT_EQUAL(gap, a - b); + CPPUNIT_ASSERT_EQUAL(-gap, b - a); + + //increment until b wraps around + for (int i = 0; i < (gap + 2); i++, ++a, ++b) { + CPPUNIT_ASSERT_EQUAL(gap, a - b); + } + //keep incrementing until a also wraps around + for (int i = 0; i < (gap + 2); i++, ++a, ++b) { + CPPUNIT_ASSERT_EQUAL(gap, a - b); + } + //let b catch up and overtake + for (int i = 0; i < (gap*2); i++, ++b) { + CPPUNIT_ASSERT_EQUAL(gap - i, a - b); + CPPUNIT_ASSERT_EQUAL(i - gap, b - a); + } + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(SequenceNumberTest); diff --git a/qpid/cpp/src/tests/SequenceSet.cpp b/qpid/cpp/src/tests/SequenceSet.cpp new file mode 100644 index 0000000000..c98b02b4b7 --- /dev/null +++ b/qpid/cpp/src/tests/SequenceSet.cpp @@ -0,0 +1,139 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "qpid/framing/SequenceSet.h" +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(SequenceSetTestSuite) + +using namespace qpid::framing; + +struct RangeExpectations +{ + typedef std::pair<SequenceNumber, SequenceNumber> Range; + typedef std::list<Range> Ranges; + + Ranges ranges; + + RangeExpectations& expect(const SequenceNumber& start, const SequenceNumber& end) { + ranges.push_back(Range(start, end)); + return *this; + } + + void operator()(const SequenceNumber& start, const SequenceNumber& end) { + BOOST_CHECK(!ranges.empty()); + if (!ranges.empty()) { + BOOST_CHECK_EQUAL(start, ranges.front().first); + BOOST_CHECK_EQUAL(end, ranges.front().second); + ranges.pop_front(); + } + } + + void check(SequenceSet& set) { + set.for_each(*this); + BOOST_CHECK(ranges.empty()); + } +}; + +BOOST_AUTO_TEST_CASE(testAdd) { + SequenceSet s; + s.add(2); + s.add(8,8); + s.add(3,5); + + for (uint32_t i = 0; i <= 1; i++) + BOOST_CHECK(!s.contains(i)); + + for (uint32_t i = 2; i <= 5; i++) + BOOST_CHECK(s.contains(i)); + + for (uint32_t i = 6; i <= 7; i++) + BOOST_CHECK(!s.contains(i)); + + BOOST_CHECK(s.contains(8)); + + for (uint32_t i = 9; i <= 10; i++) + BOOST_CHECK(!s.contains(i)); + + RangeExpectations().expect(2, 5).expect(8, 8).check(s); + + SequenceSet t; + t.add(6, 10); + t.add(s); + + for (uint32_t i = 0; i <= 1; i++) + BOOST_CHECK(!t.contains(i)); + + for (uint32_t i = 2; i <= 10; i++) + BOOST_CHECK(t.contains(i)); + + RangeExpectations().expect(2, 10).check(t); +} + +BOOST_AUTO_TEST_CASE(testAdd2) { + SequenceSet s; + s.add(7,6); + s.add(4,4); + s.add(3,10); + s.add(2); + RangeExpectations().expect(2, 10).check(s); +} + +BOOST_AUTO_TEST_CASE(testRemove) { + SequenceSet s; + SequenceSet t; + s.add(0, 10); + t.add(0, 10); + + s.remove(7); + s.remove(3, 5); + s.remove(9, 10); + + t.remove(s); + + for (uint32_t i = 0; i <= 2; i++) { + BOOST_CHECK(s.contains(i)); + BOOST_CHECK(!t.contains(i)); + } + + for (uint32_t i = 3; i <= 5; i++) { + BOOST_CHECK(!s.contains(i)); + BOOST_CHECK(t.contains(i)); + } + + BOOST_CHECK(s.contains(6)); + BOOST_CHECK(!t.contains(6)); + + BOOST_CHECK(!s.contains(7)); + BOOST_CHECK(t.contains(7)); + + BOOST_CHECK(s.contains(8)); + BOOST_CHECK(!t.contains(8)); + + for (uint32_t i = 9; i <= 10; i++) { + BOOST_CHECK(!s.contains(i)); + BOOST_CHECK(t.contains(i)); + } + + RangeExpectations().expect(0, 2).expect(6, 6).expect(8, 8).check(s); + RangeExpectations().expect(3, 5).expect(7, 7).expect(9, 10).check(t); +} + +QPID_AUTO_TEST_SUITE_END() + + diff --git a/qpid/cpp/src/tests/Serializer.cpp b/qpid/cpp/src/tests/Serializer.cpp new file mode 100644 index 0000000000..51b739218d --- /dev/null +++ b/qpid/cpp/src/tests/Serializer.cpp @@ -0,0 +1,157 @@ +/* + * + * 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. + * + */ + +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Mutex.h" +#include "qpid/sys/Serializer.h" + +#include <boost/bind.hpp> +#include <boost/utility/value_init.hpp> +#include "unit_test.h" + +#include <set> + +#include <unistd.h> + +QPID_AUTO_TEST_SUITE(SerializerTestSuite) + + +using namespace qpid; +using namespace qpid::sys; +using namespace qpid::framing; +using namespace std; + +typedef Serializer<boost::function<void()> > BoostFunctionSerializer; + +/** Test for concurrent calls */ +struct Tester { + Monitor lock; + size_t count; + size_t collisions; + set<long> threads; + + Tester() : count(0), collisions(0) {} + + void test() { + if (lock.trylock()) { // Check for concurrent calls. + ++count; + threads.insert(Thread::logId()); // Record thread. + usleep(1000); // Encourage overlap. + lock.notify(); + lock.unlock(); + } + else + ++collisions; + } +}; + +void execute(BoostFunctionSerializer& s, boost::function<void()> t) +{ + s.execute(t); +} + +BOOST_AUTO_TEST_CASE(testSingleThread) { + // Verify that we call in the same thread by default. + Tester tester; + BoostFunctionSerializer s; + for (int i = 0; i < 100; ++i) + execute(s, boost::bind(&Tester::test, &tester)); + // All should be executed in this thread. + BOOST_CHECK_EQUAL(0u, tester.collisions); + BOOST_CHECK_EQUAL(100u, tester.count); + BOOST_REQUIRE_EQUAL(1u, tester.threads.size()); + BOOST_CHECK_EQUAL(Thread::logId(), *tester.threads.begin()); +} + + +BOOST_AUTO_TEST_CASE(testSingleThreadNoImmediate) { + // Verify that we call in different thread if immediate=false. + Tester tester; + BoostFunctionSerializer s(false); + for (int i = 0; i < 100; ++i) + execute(s, boost::bind(&Tester::test, &tester)); + { + // Wait for dispatch thread to complete. + Mutex::ScopedLock l(tester.lock); + while (tester.count != 100) + tester.lock.wait(); + } + BOOST_CHECK_EQUAL(0u, tester.collisions); + BOOST_CHECK_EQUAL(100u, tester.count); + BOOST_REQUIRE_EQUAL(1u, tester.threads.size()); + BOOST_CHECK(Thread::logId() != *tester.threads.begin()); +} + +struct Caller : public Runnable, public Tester { + Caller(BoostFunctionSerializer& s) : serializer(s) {} + void run() { execute(serializer, boost::bind(&Tester::test, this)); } + BoostFunctionSerializer& serializer; +}; + +BOOST_AUTO_TEST_CASE(testDispatchThread) { + BoostFunctionSerializer s; + Caller caller(s); + Thread threads[100]; + // Concurrent calls. + for (size_t i = 0; i < 100; ++i) + threads[i] = Thread(caller); + for (size_t i = 0; i < 100; ++i) + threads[i].join(); + + // At least one task should have been queued. + BOOST_CHECK_EQUAL(0u, caller.collisions); + BOOST_CHECK(caller.threads.size() > 2u); + BOOST_CHECK(caller.threads.size() < 100u); +} + + +std::auto_ptr<BoostFunctionSerializer> serializer; + +struct CallDispatch : public Runnable { + void run() { + serializer->dispatch(); + } +}; + +void notifyDispatch() { + static CallDispatch cd; + Thread t(cd); +} + +// Use externally created threads. +BOOST_AUTO_TEST_CASE(testExternalDispatch) { + serializer.reset(new BoostFunctionSerializer(false, ¬ifyDispatch)); + Tester tester; + for (int i = 0; i < 100; ++i) + execute(*serializer, boost::bind(&Tester::test, &tester)); + { + // Wait for dispatch thread to complete. + Mutex::ScopedLock l(tester.lock); + while (tester.count != 100) + tester.lock.wait(); + } + BOOST_CHECK_EQUAL(0u, tester.collisions); + BOOST_CHECK_EQUAL(100u, tester.count); + BOOST_CHECK(Thread::logId() != *tester.threads.begin()); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/SessionState.cpp b/qpid/cpp/src/tests/SessionState.cpp new file mode 100644 index 0000000000..56d0055ed8 --- /dev/null +++ b/qpid/cpp/src/tests/SessionState.cpp @@ -0,0 +1,146 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "qpid/framing/SessionState.h" +#include "qpid/Exception.h" + +#include <boost/bind.hpp> +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(SessionStateTestSuite) + +using namespace std; +using namespace qpid::framing; +using namespace boost; + +// Create a frame with a one-char string. +AMQFrame& frame(char s) { + static AMQFrame frame; + frame.setBody(AMQContentBody(string(&s, 1))); + return frame; +} + +// Extract the one-char string from a frame. +char charFromFrame(const AMQFrame& f) { + const AMQContentBody* b=dynamic_cast<const AMQContentBody*>(f.getBody()); + BOOST_REQUIRE(b && b->getData().size() > 0); + return b->getData()[0]; +} + +// Sent chars as frames +void sent(SessionState& session, const std::string& frames) { + for_each(frames.begin(), frames.end(), + bind(&SessionState::sent, ref(session), bind(frame, _1))); +} + +// Received chars as frames +void received(SessionState& session, const std::string& frames) { + for_each(frames.begin(), frames.end(), + bind(&SessionState::received, ref(session), bind(frame, _1))); +} + +// Make a string from a ReplayRange. +std::string replayChars(const SessionState::Replay& frames) { + string result(frames.size(), ' '); + transform(frames.begin(), frames.end(), result.begin(), + bind(&charFromFrame, _1)); + return result; +} + +namespace qpid { +namespace framing { + +bool operator==(const AMQFrame& a, const AMQFrame& b) { + const AMQContentBody* ab=dynamic_cast<const AMQContentBody*>(a.getBody()); + const AMQContentBody* bb=dynamic_cast<const AMQContentBody*>(b.getBody()); + return ab && bb && ab->getData() == bb->getData(); +} + +}} // namespace qpid::framing + + +BOOST_AUTO_TEST_CASE(testSent) { + // Test that we send solicit-ack at the right interval. + AMQContentBody f; + SessionState s1(1); + BOOST_CHECK(s1.sent(f)); + BOOST_CHECK(s1.sent(f)); + BOOST_CHECK(s1.sent(f)); + + SessionState s3(3); + BOOST_CHECK(!s3.sent(f)); + BOOST_CHECK(!s3.sent(f)); + BOOST_CHECK(s3.sent(f)); + + BOOST_CHECK(!s3.sent(f)); + BOOST_CHECK(!s3.sent(f)); + s3.receivedAck(4); + BOOST_CHECK(!s3.sent(f)); + BOOST_CHECK(!s3.sent(f)); + BOOST_CHECK(s3.sent(f)); +} + +BOOST_AUTO_TEST_CASE(testReplay) { + // Replay of all frames. + SessionState session(100); + sent(session, "abc"); + session.suspend(); session.resuming(); + session.receivedAck(-1); + BOOST_CHECK_EQUAL(replayChars(session.replay()), "abc"); + + // Replay with acks + session.receivedAck(0); // ack a. + session.suspend(); + session.resuming(); + session.receivedAck(1); // ack b. + BOOST_CHECK_EQUAL(replayChars(session.replay()), "c"); + + // Replay after further frames. + sent(session, "def"); + session.suspend(); + session.resuming(); + session.receivedAck(3); + BOOST_CHECK_EQUAL(replayChars(session.replay()), "ef"); + + // Bad ack, too high + try { + session.receivedAck(6); + BOOST_FAIL("expected exception"); + } catch(const std::exception&) {} + +} + +BOOST_AUTO_TEST_CASE(testReceived) { + // Check that we request acks at the right interval. + AMQContentBody f; + SessionState s1(1); + BOOST_CHECK_EQUAL(0u, *s1.received(f)); + BOOST_CHECK_EQUAL(1u, *s1.received(f)); + BOOST_CHECK_EQUAL(2u, *s1.received(f)); + + SessionState s3(3); + BOOST_CHECK(!s3.received(f)); + BOOST_CHECK(!s3.received(f)); + BOOST_CHECK_EQUAL(2u, *s3.received(f)); + + BOOST_CHECK(!s3.received(f)); + BOOST_CHECK(!s3.received(f)); + BOOST_CHECK_EQUAL(5u, *s3.received(f)); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/Shlib.cpp b/qpid/cpp/src/tests/Shlib.cpp new file mode 100644 index 0000000000..3adc33aac9 --- /dev/null +++ b/qpid/cpp/src/tests/Shlib.cpp @@ -0,0 +1,60 @@ +/* + * 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. + * + */ + +#include "test_tools.h" +#include "qpid/sys/Shlib.h" +#include "qpid/Exception.h" + +#include "unit_test.h" + +QPID_AUTO_TEST_SUITE(ShlibTestSuite) + +using namespace qpid::sys; +typedef void (*CallMe)(int*); + +BOOST_AUTO_TEST_CASE(testShlib) { + Shlib sh(".libs/libshlibtest.so"); + // Double cast to avoid ISO warning. + CallMe callMe=sh.getSymbol<CallMe>("callMe"); + BOOST_REQUIRE(callMe != 0); + int unloaded=0; + callMe(&unloaded); + sh.unload(); + BOOST_CHECK_EQUAL(42, unloaded); + try { + sh.getSymbol("callMe"); + BOOST_FAIL("Expected exception"); + } + catch (const qpid::Exception&) {} +} + +BOOST_AUTO_TEST_CASE(testAutoShlib) { + int unloaded = 0; + { + AutoShlib sh(".libs/libshlibtest.so"); + CallMe callMe=sh.getSymbol<CallMe>("callMe"); + BOOST_REQUIRE(callMe != 0); + callMe(&unloaded); + } + BOOST_CHECK_EQUAL(42, unloaded); +} + + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/SimpleTestCaseBase.cpp b/qpid/cpp/src/tests/SimpleTestCaseBase.cpp new file mode 100644 index 0000000000..4f071cd02b --- /dev/null +++ b/qpid/cpp/src/tests/SimpleTestCaseBase.cpp @@ -0,0 +1,87 @@ +/* + * + * 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. + * + */ +#include "SimpleTestCaseBase.h" + +using namespace qpid; + +void SimpleTestCaseBase::start() +{ + if (worker.get()) { + worker->start(); + } +} + +void SimpleTestCaseBase::stop() +{ + if (worker.get()) { + worker->stop(); + } +} + +void SimpleTestCaseBase::report(client::Message& report) +{ + if (worker.get()) { + report.getHeaders().setInt("MESSAGE_COUNT", worker->getCount()); + //add number of messages sent or received + std::stringstream reportstr; + reportstr << worker->getCount(); + report.setData(reportstr.str()); + } +} + +SimpleTestCaseBase::Sender::Sender(TestOptions& options, + const Exchange& _exchange, + const std::string& _key, + const int _messages) + : Worker(options, _messages), exchange(_exchange), key(_key) {} + +void SimpleTestCaseBase::Sender::init() +{ + channel.start(); +} + +void SimpleTestCaseBase::Sender::start(){ + Message msg; + while (count < messages) { + channel.publish(msg, exchange, key); + count++; + } + stop(); +} + +SimpleTestCaseBase::Worker::Worker(TestOptions& options, const int _messages) : + connection(options.trace), messages(_messages), count(0) +{ + connection.open(options.host, options.port); + connection.openChannel(channel); +} + +void SimpleTestCaseBase::Worker::stop() +{ + channel.close(); + connection.close(); +} + +int SimpleTestCaseBase::Worker::getCount() +{ + return count; +} + diff --git a/qpid/cpp/src/tests/SimpleTestCaseBase.h b/qpid/cpp/src/tests/SimpleTestCaseBase.h new file mode 100644 index 0000000000..7f94fa7e1c --- /dev/null +++ b/qpid/cpp/src/tests/SimpleTestCaseBase.h @@ -0,0 +1,88 @@ +#ifndef _SimpleTestCaseBase_ +#define _SimpleTestCaseBase_ +/* + * + * 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. + * + */ + +#include <memory> +#include <sstream> + +#include "qpid/Exception.h" +#include "qpid/client/Channel.h" +#include "qpid/client/Message.h" +#include "qpid/client/Connection.h" +#include "qpid/client/MessageListener.h" +#include "TestCase.h" + + +namespace qpid { + +using namespace qpid::client; + +class SimpleTestCaseBase : public TestCase +{ +protected: + class Worker + { + protected: + client::Connection connection; + client::Channel channel; + const int messages; + int count; + + public: + + Worker(TestOptions& options, const int messages); + virtual ~Worker(){} + + virtual void stop(); + virtual int getCount(); + virtual void init() = 0; + virtual void start() = 0; + }; + + class Sender : public Worker + { + const Exchange& exchange; + const std::string key; + public: + Sender(TestOptions& options, + const Exchange& exchange, + const std::string& key, + const int messages); + void init(); + void start(); + }; + + std::auto_ptr<Worker> worker; + +public: + virtual void assign(const std::string& role, framing::FieldTable& params, TestOptions& options) = 0; + + virtual ~SimpleTestCaseBase() {} + + void start(); + void stop(); + void report(client::Message& report); +}; + +} + +#endif diff --git a/qpid/cpp/src/tests/SocketProxy.h b/qpid/cpp/src/tests/SocketProxy.h new file mode 100644 index 0000000000..a37c1f2c3e --- /dev/null +++ b/qpid/cpp/src/tests/SocketProxy.h @@ -0,0 +1,164 @@ +#ifndef SOCKETPROXY_H +#define SOCKETPROXY_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. + * + */ + +#include "qpid/sys/Socket.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Mutex.h" +#include "qpid/client/Connection.h" +#include "qpid/log/Statement.h" + +#include <algorithm> + +/** + * A simple socket proxy that forwards to another socket. + * Used between client & local broker to simulate network failures. + */ +class SocketProxy : private qpid::sys::Runnable +{ + public: + /** Connect to connectPort on host, start a forwarding thread. + * Listen for connection on getPort(). + */ + SocketProxy(int connectPort, const std::string host="localhost") + : closed(false), port(listener.listen()) + { + int r=::pipe(closePipe); + if (r<0) throwErrno(QPID_MSG("::pipe returned " << r)); + client.connect(host, connectPort); + thread = qpid::sys::Thread(static_cast<qpid::sys::Runnable*>(this)); + } + + ~SocketProxy() { close(); } + + /** Simulate a network disconnect. */ + void close() { + { + qpid::sys::Mutex::ScopedLock l(lock); + if (closed) return; + closed=true; + } + write(closePipe[1], this, 1); // Random byte to closePipe + thread.join(); + client.close(); + ::close(closePipe[0]); + ::close(closePipe[1]); + } + + bool isClosed() const { + qpid::sys::Mutex::ScopedLock l(lock); + return closed; + } + + uint16_t getPort() const { return port; } + + private: + static void throwErrno(const std::string& msg) { + throw qpid::Exception(msg+":"+qpid::strError(errno)); + } + static void throwIf(bool condition, const std::string& msg) { + if (condition) throw qpid::Exception(msg); + } + + struct FdSet : fd_set { + FdSet() : maxFd(0) { clear(); } + void clear() { FD_ZERO(this); } + void set(int fd) { FD_SET(fd, this); maxFd = std::max(maxFd, fd); } + bool isSet(int fd) const { return FD_ISSET(fd, this); } + bool operator[](int fd) const { return isSet(fd); } + + int maxFd; + }; + + enum { RD=1, WR=2, ER=4 }; + + struct Selector { + FdSet rd, wr, er; + + void set(int fd, int sets) { + if (sets & RD) rd.set(fd); + if (sets & WR) wr.set(fd); + if (sets & ER) er.set(fd); + } + + int select() { + for (;;) { + int maxFd = std::max(rd.maxFd, std::max(wr.maxFd, er.maxFd)); + int r = ::select(maxFd + 1, &rd, &wr, &er, NULL); + if (r == -1 && errno == EINTR) continue; + if (r < 0) throwErrno(QPID_MSG("select returned " <<r)); + return r; + } + } + }; + + void run() { + std::auto_ptr<qpid::sys::Socket> server; + try { + // Accept incoming connections, watch closePipe. + Selector accept; + accept.set(listener.toFd(), RD|ER); + accept.set(closePipe[0], RD|ER); + accept.select(); + throwIf(accept.rd[closePipe[0]], "Closed by close()"); + throwIf(!accept.rd[listener.toFd()],"Accept failed"); + server.reset(listener.accept(0, 0)); + + // Pump data between client & server sockets, watch closePipe. + char buffer[1024]; + for (;;) { + Selector select; + select.set(server->toFd(), RD|ER); + select.set(client.toFd(), RD|ER); + select.set(closePipe[0], RD|ER); + select.select(); + throwIf(select.rd[closePipe[0]], "Closed by close()"); + // Read even if fd is in error to throw a useful exception. + bool gotData=false; + if (select.rd[server->toFd()] || select.er[server->toFd()]) { + client.write(buffer, server->read(buffer, sizeof(buffer))); + gotData=true; + } + if (select.rd[client.toFd()] || select.er[client.toFd()]) { + server->write(buffer, client.read(buffer, sizeof(buffer))); + gotData=true; + } + throwIf(!gotData, "No data from select()"); + } + } + catch (const std::exception& e) { + QPID_LOG(debug, "SocketProxy::run exiting: " << e.what()); + } + if (server.get()) server->close(); + close(); + } + + mutable qpid::sys::Mutex lock; + bool closed; + qpid::sys::Socket client, listener; + uint16_t port; + int closePipe[2]; + qpid::sys::Thread thread; +}; + +#endif diff --git a/qpid/cpp/src/tests/TestCase.h b/qpid/cpp/src/tests/TestCase.h new file mode 100644 index 0000000000..07bdd68933 --- /dev/null +++ b/qpid/cpp/src/tests/TestCase.h @@ -0,0 +1,64 @@ +#ifndef _TestCase_ +#define _TestCase_ +/* + * + * 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. + * + */ + +#include "qpid/client/Message.h" +#include "TestOptions.h" + + +namespace qpid { + +/** + * Interface to be implemented by test cases for use with the test + * runner. + */ +class TestCase +{ +public: + /** + * Directs the test case to act in a particular role. Some roles + * may be 'activated' at this stage others may require an explicit + * start request. + */ + virtual void assign(const std::string& role, framing::FieldTable& params, TestOptions& options) = 0; + /** + * Each test will be started on its own thread, which should block + * until the test completes (this may or may not require an + * explicit stop() request). + */ + virtual void start() = 0; + /** + * Requests that the test be stopped if still running. + */ + virtual void stop() = 0; + /** + * Allows the test to fill in details on the final report + * message. Will be called only after start has returned. + */ + virtual void report(client::Message& report) = 0; + + virtual ~TestCase() {} +}; + +} + +#endif diff --git a/qpid/cpp/src/tests/TestOptions.h b/qpid/cpp/src/tests/TestOptions.h new file mode 100644 index 0000000000..87710964d6 --- /dev/null +++ b/qpid/cpp/src/tests/TestOptions.h @@ -0,0 +1,94 @@ +#ifndef _TestOptions_ +#define _TestOptions_ +/* + * + * 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. + * + */ + +#include "qpid/Options.h" +#include "qpid/log/Options.h" +#include "qpid/Url.h" +#include "qpid/log/Logger.h" +#include "qpid/client/Connection.h" + +#include <iostream> +#include <exception> + +namespace qpid { + +struct TestOptions : public qpid::Options +{ + TestOptions(const std::string& helpText_=std::string()) : + Options("Test Options"), + host("localhost"), port(TcpAddress::DEFAULT_PORT), + clientid("cpp"), username("guest"), password("guest"), + help(false), helpText(helpText_) + { + addOptions() + ("host,h", optValue(host, "HOST"), "Broker host to connect to") + // TODO aconway 2007-06-26: broker is synonym for host. Drop broker? + ("broker,b", optValue(host, "HOST"), "Broker host to connect to") + ("port,p", optValue(port, "PORT"), "Broker port to connect to") + ("virtualhost,v", optValue(virtualhost, "VHOST"), "virtual host") + ("clientname,n", optValue(clientid, "ID"), "unique client identifier") + ("username", optValue(username, "USER"), "user name for broker log in.") + ("password", optValue(password, "USER"), "password for broker log in.") + ("help", optValue(help), "print this usage statement"); + add(log); + } + + /** As well as parsing, throw help message if requested. */ + void parse(int argc, char** argv) { + try { + qpid::Options::parse(argc, argv); + } catch (const std::exception& e) { + std::ostringstream msg; + msg << *this << std::endl << std::endl << e.what() << std::endl; + throw qpid::Options::Exception(msg.str()); + } + trace = log.trace; + qpid::log::Logger::instance().configure(log, argv[0]); + if (help) { + std::ostringstream msg; + msg << *this << std::endl << std::endl << helpText << std::endl; + throw qpid::Options::Exception(msg.str()); + } + } + + /** Open a connection using option values */ + void open(qpid::client::Connection& connection) { + connection.open(host, port, username, password, virtualhost); + } + + + std::string host; + uint16_t port; + std::string virtualhost; + std::string clientid; + std::string username; + std::string password; + bool trace; + bool help; + log::Options log; + std::string helpText; +}; + +} + +#endif diff --git a/qpid/cpp/src/tests/TimerTest.cpp b/qpid/cpp/src/tests/TimerTest.cpp new file mode 100644 index 0000000000..2693d4a787 --- /dev/null +++ b/qpid/cpp/src/tests/TimerTest.cpp @@ -0,0 +1,130 @@ + +/* + * + * 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. + * + */ +#include "qpid/broker/Timer.h" +#include "qpid/sys/Monitor.h" +#include "qpid_test_plugin.h" +#include <math.h> +#include <iostream> +#include <memory> +#include <boost/format.hpp> +#include <boost/lexical_cast.hpp> + +using namespace qpid::broker; +using namespace qpid::sys; +using boost::intrusive_ptr; +using boost::dynamic_pointer_cast; + +class TimerTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(TimerTest); + CPPUNIT_TEST(testGeneral); + CPPUNIT_TEST_SUITE_END(); + + class Counter + { + Mutex lock; + uint counter; + public: + Counter() : counter(0) {} + uint next() + { + Mutex::ScopedLock l(lock); + return ++counter; + } + }; + + class TestTask : public TimerTask + { + const AbsTime start; + const Duration expected; + AbsTime end; + bool fired; + uint position; + Monitor monitor; + Counter& counter; + + public: + TestTask(Duration timeout, Counter& _counter) + : TimerTask(timeout), start(now()), expected(timeout), end(start), fired(false), counter(_counter) {} + + void fire() + { + Monitor::ScopedLock l(monitor); + fired = true; + position = counter.next(); + end = now(); + monitor.notify(); + } + + void check(uint expected_position, uint64_t tolerance = 500 * TIME_MSEC) + { + Monitor::ScopedLock l(monitor); + CPPUNIT_ASSERT(fired); + CPPUNIT_ASSERT_EQUAL(expected_position, position); + Duration actual(start, end); + uint64_t difference = abs(expected - actual); + std::string msg(boost::lexical_cast<std::string>(boost::format("tolerance = %1%, difference = %2%") % tolerance % difference)); + CPPUNIT_ASSERT_MESSAGE(msg, difference < tolerance); + } + + void wait(Duration d) + { + Monitor::ScopedLock l(monitor); + monitor.wait(AbsTime(now(), d)); + } + }; + + class DummyRunner : public Runnable + { + public: + void run() {} + }; + +public: + + void testGeneral() + { + Counter counter; + Timer timer; + intrusive_ptr<TestTask> task1(new TestTask(Duration(3 * TIME_SEC), counter)); + intrusive_ptr<TestTask> task2(new TestTask(Duration(1 * TIME_SEC), counter)); + intrusive_ptr<TestTask> task3(new TestTask(Duration(4 * TIME_SEC), counter)); + intrusive_ptr<TestTask> task4(new TestTask(Duration(2 * TIME_SEC), counter)); + + timer.add(task1); + timer.add(task2); + timer.add(task3); + timer.add(task4); + + dynamic_pointer_cast<TestTask>(task3)->wait(Duration(6 * TIME_SEC)); + + dynamic_pointer_cast<TestTask>(task1)->check(3); + dynamic_pointer_cast<TestTask>(task2)->check(1); + dynamic_pointer_cast<TestTask>(task3)->check(4); + dynamic_pointer_cast<TestTask>(task4)->check(2); + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(TimerTest); + diff --git a/qpid/cpp/src/tests/TopicExchangeTest.cpp b/qpid/cpp/src/tests/TopicExchangeTest.cpp new file mode 100644 index 0000000000..adb937179f --- /dev/null +++ b/qpid/cpp/src/tests/TopicExchangeTest.cpp @@ -0,0 +1,200 @@ +/* + * 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. + */ +#include "qpid/broker/TopicExchange.h" +#include "qpid_test_plugin.h" + +using namespace qpid::broker; + +Tokens makeTokens(const char** begin, const char** end) +{ + Tokens t; + t.insert(t.end(), begin, end); + return t; +} + +// Calculate size of an array. +#define LEN(a) (sizeof(a)/sizeof(a[0])) + +// Convert array to token vector +#define TOKENS(a) makeTokens(a, a + LEN(a)) + +// Allow CPPUNIT_EQUALS to print a Tokens. +CppUnit::OStringStream& operator <<(CppUnit::OStringStream& out, const Tokens& v) +{ + out << "[ "; + for (Tokens::const_iterator i = v.begin(); + i != v.end(); ++i) + { + out << '"' << *i << '"' << (i+1 == v.end() ? "]" : ", "); + } + return out; +} + + +class TokensTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(TokensTest); + CPPUNIT_TEST(testTokens); + CPPUNIT_TEST_SUITE_END(); + + public: + void testTokens() + { + Tokens tokens("hello.world"); + const char* expect[] = {"hello", "world"}; + CPPUNIT_ASSERT_EQUAL(TOKENS(expect), tokens); + + tokens = "a.b.c"; + const char* expect2[] = { "a", "b", "c" }; + CPPUNIT_ASSERT_EQUAL(TOKENS(expect2), tokens); + + tokens = ""; + CPPUNIT_ASSERT(tokens.empty()); + + tokens = "x"; + const char* expect3[] = { "x" }; + CPPUNIT_ASSERT_EQUAL(TOKENS(expect3), tokens); + + tokens = (".x"); + const char* expect4[] = { "", "x" }; + CPPUNIT_ASSERT_EQUAL(TOKENS(expect4), tokens); + + tokens = ("x."); + const char* expect5[] = { "x", "" }; + CPPUNIT_ASSERT_EQUAL(TOKENS(expect5), tokens); + + tokens = ("."); + const char* expect6[] = { "", "" }; + CPPUNIT_ASSERT_EQUAL(TOKENS(expect6), tokens); + + tokens = (".."); + const char* expect7[] = { "", "", "" }; + CPPUNIT_ASSERT_EQUAL(TOKENS(expect7), tokens); + } + +}; + +#define ASSERT_NORMALIZED(expect, pattern) \ + CPPUNIT_ASSERT_EQUAL(Tokens(expect), static_cast<Tokens>(TopicPattern(pattern))) +class TopicPatternTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(TopicPatternTest); + CPPUNIT_TEST(testNormalize); + CPPUNIT_TEST(testPlain); + CPPUNIT_TEST(testStar); + CPPUNIT_TEST(testHash); + CPPUNIT_TEST(testMixed); + CPPUNIT_TEST(testCombo); + CPPUNIT_TEST_SUITE_END(); + + public: + + void testNormalize() + { + CPPUNIT_ASSERT(TopicPattern("").empty()); + ASSERT_NORMALIZED("a.b.c", "a.b.c"); + ASSERT_NORMALIZED("a.*.c", "a.*.c"); + ASSERT_NORMALIZED("#", "#"); + ASSERT_NORMALIZED("#", "#.#.#.#"); + ASSERT_NORMALIZED("*.*.*.#", "#.*.#.*.#.#.*"); + ASSERT_NORMALIZED("a.*.*.*.#", "a.*.#.*.#.*.#"); + ASSERT_NORMALIZED("a.*.*.*.#", "a.*.#.*.#.*"); + } + + void testPlain() { + TopicPattern p("ab.cd.e"); + CPPUNIT_ASSERT(p.match("ab.cd.e")); + CPPUNIT_ASSERT(!p.match("abx.cd.e")); + CPPUNIT_ASSERT(!p.match("ab.cd")); + CPPUNIT_ASSERT(!p.match("ab.cd..e.")); + CPPUNIT_ASSERT(!p.match("ab.cd.e.")); + CPPUNIT_ASSERT(!p.match(".ab.cd.e")); + + p = ""; + CPPUNIT_ASSERT(p.match("")); + + p = "."; + CPPUNIT_ASSERT(p.match(".")); + } + + + void testStar() + { + TopicPattern p("a.*.b"); + CPPUNIT_ASSERT(p.match("a.xx.b")); + CPPUNIT_ASSERT(!p.match("a.b")); + + p = "*.x"; + CPPUNIT_ASSERT(p.match("y.x")); + CPPUNIT_ASSERT(p.match(".x")); + CPPUNIT_ASSERT(!p.match("x")); + + p = "x.x.*"; + CPPUNIT_ASSERT(p.match("x.x.y")); + CPPUNIT_ASSERT(p.match("x.x.")); + CPPUNIT_ASSERT(!p.match("x.x")); + CPPUNIT_ASSERT(!p.match("q.x.y")); + } + + void testHash() + { + TopicPattern p("a.#.b"); + CPPUNIT_ASSERT(p.match("a.b")); + CPPUNIT_ASSERT(p.match("a.x.b")); + CPPUNIT_ASSERT(p.match("a..x.y.zz.b")); + CPPUNIT_ASSERT(!p.match("a.b.")); + CPPUNIT_ASSERT(!p.match("q.x.b")); + + p = "a.#"; + CPPUNIT_ASSERT(p.match("a")); + CPPUNIT_ASSERT(p.match("a.b")); + CPPUNIT_ASSERT(p.match("a.b.c")); + + p = "#.a"; + CPPUNIT_ASSERT(p.match("a")); + CPPUNIT_ASSERT(p.match("x.y.a")); + } + + void testMixed() + { + TopicPattern p("*.x.#.y"); + CPPUNIT_ASSERT(p.match("a.x.y")); + CPPUNIT_ASSERT(p.match("a.x.p.qq.y")); + CPPUNIT_ASSERT(!p.match("a.a.x.y")); + CPPUNIT_ASSERT(!p.match("aa.x.b.c")); + + p = "a.#.b.*"; + CPPUNIT_ASSERT(p.match("a.b.x")); + CPPUNIT_ASSERT(p.match("a.x.x.x.b.x")); + } + + void testCombo() { + TopicPattern p("*.#.#.*.*.#"); + CPPUNIT_ASSERT(p.match("x.y.z")); + CPPUNIT_ASSERT(p.match("x.y.z.a.b.c")); + CPPUNIT_ASSERT(!p.match("x.y")); + CPPUNIT_ASSERT(!p.match("x")); + } +}; + + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(TopicPatternTest); +CPPUNIT_TEST_SUITE_REGISTRATION(TokensTest); diff --git a/qpid/cpp/src/tests/TxAckTest.cpp b/qpid/cpp/src/tests/TxAckTest.cpp new file mode 100644 index 0000000000..89b98cb93c --- /dev/null +++ b/qpid/cpp/src/tests/TxAckTest.cpp @@ -0,0 +1,119 @@ +/* + * + * 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. + * + */ +#include "qpid/broker/NullMessageStore.h" +#include "qpid/broker/RecoveryManager.h" +#include "qpid/broker/TxAck.h" +#include "qpid_test_plugin.h" +#include <iostream> +#include <list> +#include <vector> + +using std::list; +using std::vector; +using boost::intrusive_ptr; +using namespace qpid; +using namespace qpid::broker; +using namespace qpid::framing; + +class TxAckTest : public CppUnit::TestCase +{ + + class TestMessageStore : public NullMessageStore + { + public: + vector<intrusive_ptr<PersistableMessage> > dequeued; + + void dequeue(TransactionContext*, intrusive_ptr<PersistableMessage>& msg, const PersistableQueue& /*queue*/) + { + dequeued.push_back(msg); + } + + TestMessageStore() : NullMessageStore() {} + ~TestMessageStore(){} + }; + + CPPUNIT_TEST_SUITE(TxAckTest); + CPPUNIT_TEST(testPrepare); + CPPUNIT_TEST(testCommit); + CPPUNIT_TEST_SUITE_END(); + + + AccumulatedAck acked; + TestMessageStore store; + Queue::shared_ptr queue; + vector<intrusive_ptr<Message> > messages; + list<DeliveryRecord> deliveries; + TxAck op; + + +public: + + TxAckTest() : acked(0), queue(new Queue("my_queue", false, &store, 0)), op(acked, deliveries) + { + for(int i = 0; i < 10; i++){ + intrusive_ptr<Message> msg(new Message()); + AMQFrame method(in_place<MessageTransferBody>( + ProtocolVersion(), 0, "exchange", 0, 0)); + AMQFrame header(in_place<AMQHeaderBody>()); + msg->getFrames().append(method); + msg->getFrames().append(header); + msg->getProperties<DeliveryProperties>()->setDeliveryMode(PERSISTENT); + msg->getProperties<DeliveryProperties>()->setRoutingKey("routing_key"); + messages.push_back(msg); + QueuedMessage qm(queue.get()); + qm.payload = msg; + deliveries.push_back(DeliveryRecord(qm, queue, "xyz", DeliveryToken::shared_ptr(), (i+1), true)); + } + + //assume msgs 1-5, 7 and 9 are all acked (i.e. 6, 8 & 10 are not) + acked.mark = 5; + acked.update(7, 7); + acked.update(9, 9); + } + + void testPrepare() + { + //ensure acked messages are discarded, i.e. dequeued from store + op.prepare(0); + CPPUNIT_ASSERT_EQUAL((size_t) 7, store.dequeued.size()); + CPPUNIT_ASSERT_EQUAL((size_t) 10, deliveries.size()); + int dequeued[] = {0, 1, 2, 3, 4, 6, 8}; + for (int i = 0; i < 7; i++) { + CPPUNIT_ASSERT_EQUAL(static_pointer_cast<PersistableMessage>(messages[dequeued[i]]), store.dequeued[i]); + } + } + + void testCommit() + { + //emsure acked messages are removed from list + op.commit(); + CPPUNIT_ASSERT_EQUAL((size_t) 3, deliveries.size()); + list<DeliveryRecord>::iterator i = deliveries.begin(); + CPPUNIT_ASSERT(i->matches(6));//msg 6 + CPPUNIT_ASSERT((++i)->matches(8));//msg 8 + CPPUNIT_ASSERT((++i)->matches(10));//msg 10 + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(TxAckTest); + diff --git a/qpid/cpp/src/tests/TxBufferTest.cpp b/qpid/cpp/src/tests/TxBufferTest.cpp new file mode 100644 index 0000000000..afe6d2b0fc --- /dev/null +++ b/qpid/cpp/src/tests/TxBufferTest.cpp @@ -0,0 +1,185 @@ +/* + * + * 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. + * + */ +#include "qpid/broker/TxBuffer.h" +#include "qpid_test_plugin.h" +#include <iostream> +#include <vector> +#include "TxMocks.h" + +using namespace qpid::broker; +using boost::static_pointer_cast; + +class TxBufferTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(TxBufferTest); + CPPUNIT_TEST(testCommitLocal); + CPPUNIT_TEST(testFailOnCommitLocal); + CPPUNIT_TEST(testPrepare); + CPPUNIT_TEST(testFailOnPrepare); + CPPUNIT_TEST(testRollback); + CPPUNIT_TEST(testBufferIsClearedAfterRollback); + CPPUNIT_TEST(testBufferIsClearedAfterCommit); + CPPUNIT_TEST_SUITE_END(); + + public: + + void testCommitLocal(){ + MockTransactionalStore store; + store.expectBegin().expectCommit(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectCommit(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectPrepare().expectPrepare().expectCommit().expectCommit();//opB enlisted twice to test relative order + MockTxOp::shared_ptr opC(new MockTxOp()); + opC->expectPrepare().expectCommit(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + buffer.enlist(static_pointer_cast<TxOp>(opB));//opB enlisted twice + buffer.enlist(static_pointer_cast<TxOp>(opC)); + + CPPUNIT_ASSERT(buffer.commitLocal(&store)); + store.check(); + CPPUNIT_ASSERT(store.isCommitted()); + opA->check(); + opB->check(); + opC->check(); + } + + void testFailOnCommitLocal(){ + MockTransactionalStore store; + store.expectBegin().expectAbort(); + + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp(true)); + opB->expectPrepare().expectRollback(); + MockTxOp::shared_ptr opC(new MockTxOp());//will never get prepare as b will fail + opC->expectRollback(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + buffer.enlist(static_pointer_cast<TxOp>(opC)); + + CPPUNIT_ASSERT(!buffer.commitLocal(&store)); + CPPUNIT_ASSERT(store.isAborted()); + store.check(); + opA->check(); + opB->check(); + opC->check(); + } + + void testPrepare(){ + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectPrepare(); + MockTxOp::shared_ptr opC(new MockTxOp()); + opC->expectPrepare(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + buffer.enlist(static_pointer_cast<TxOp>(opC)); + + CPPUNIT_ASSERT(buffer.prepare(0)); + opA->check(); + opB->check(); + opC->check(); + } + + void testFailOnPrepare(){ + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectPrepare(); + MockTxOp::shared_ptr opB(new MockTxOp(true)); + opB->expectPrepare(); + MockTxOp::shared_ptr opC(new MockTxOp());//will never get prepare as b will fail + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + buffer.enlist(static_pointer_cast<TxOp>(opC)); + + CPPUNIT_ASSERT(!buffer.prepare(0)); + opA->check(); + opB->check(); + opC->check(); + } + + void testRollback(){ + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp(true)); + opB->expectRollback(); + MockTxOp::shared_ptr opC(new MockTxOp()); + opC->expectRollback(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + buffer.enlist(static_pointer_cast<TxOp>(opC)); + + buffer.rollback(); + opA->check(); + opB->check(); + opC->check(); + } + + void testBufferIsClearedAfterRollback(){ + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectRollback(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectRollback(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + + buffer.rollback(); + buffer.commit();//second call should not reach ops + opA->check(); + opB->check(); + } + + void testBufferIsClearedAfterCommit(){ + MockTxOp::shared_ptr opA(new MockTxOp()); + opA->expectCommit(); + MockTxOp::shared_ptr opB(new MockTxOp()); + opB->expectCommit(); + + TxBuffer buffer; + buffer.enlist(static_pointer_cast<TxOp>(opA)); + buffer.enlist(static_pointer_cast<TxOp>(opB)); + + buffer.commit(); + buffer.rollback();//second call should not reach ops + opA->check(); + opB->check(); + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(TxBufferTest); + diff --git a/qpid/cpp/src/tests/TxMocks.h b/qpid/cpp/src/tests/TxMocks.h new file mode 100644 index 0000000000..127a27c005 --- /dev/null +++ b/qpid/cpp/src/tests/TxMocks.h @@ -0,0 +1,226 @@ +/* + * + * 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. + * + */ +#ifndef _tests_TxMocks_h +#define _tests_TxMocks_h + + +#include "qpid/Exception.h" +#include "qpid/broker/TransactionalStore.h" +#include "qpid/broker/TxOp.h" +#include <iostream> +#include <vector> + +using namespace qpid::broker; +using boost::static_pointer_cast; +using std::string; + +template <class T> void assertEqualVector(std::vector<T>& expected, std::vector<T>& actual){ + unsigned int i = 0; + while(i < expected.size() && i < actual.size()){ + CPPUNIT_ASSERT_EQUAL(expected[i], actual[i]); + i++; + } + if (i < expected.size()) { + throw qpid::Exception(QPID_MSG("Missing " << expected[i])); + } else if (i < actual.size()) { + throw qpid::Exception(QPID_MSG("Extra " << actual[i])); + } + CPPUNIT_ASSERT_EQUAL(expected.size(), actual.size()); +} + +class TxOpConstants{ +protected: + const string PREPARE; + const string COMMIT; + const string ROLLBACK; + + TxOpConstants() : PREPARE("PREPARE"), COMMIT("COMMIT"), ROLLBACK("ROLLBACK") {} +}; + +class MockTxOp : public TxOp, public TxOpConstants{ + std::vector<string> expected; + std::vector<string> actual; + bool failOnPrepare; + string debugName; +public: + typedef boost::shared_ptr<MockTxOp> shared_ptr; + + MockTxOp() : failOnPrepare(false) {} + MockTxOp(bool _failOnPrepare) : failOnPrepare(_failOnPrepare) {} + + void setDebugName(string name){ + debugName = name; + } + + void printExpected(){ + std::cout << std::endl << "MockTxOp[" << debugName << "] expects: "; + for (std::vector<string>::iterator i = expected.begin(); i < expected.end(); i++) { + if(i != expected.begin()) std::cout << ", "; + std::cout << *i; + } + std::cout << std::endl; + } + + void printActual(){ + std::cout << std::endl << "MockTxOp[" << debugName << "] actual: "; + for (std::vector<string>::iterator i = actual.begin(); i < actual.end(); i++) { + if(i != actual.begin()) std::cout << ", "; + std::cout << *i; + } + std::cout << std::endl; + } + + bool prepare(TransactionContext*) throw(){ + actual.push_back(PREPARE); + return !failOnPrepare; + } + void commit() throw(){ + actual.push_back(COMMIT); + } + void rollback() throw(){ + if(!debugName.empty()) std::cout << std::endl << "MockTxOp[" << debugName << "]::rollback()" << std::endl; + actual.push_back(ROLLBACK); + } + MockTxOp& expectPrepare(){ + expected.push_back(PREPARE); + return *this; + } + MockTxOp& expectCommit(){ + expected.push_back(COMMIT); + return *this; + } + MockTxOp& expectRollback(){ + expected.push_back(ROLLBACK); + return *this; + } + void check(){ + assertEqualVector(expected, actual); + } + ~MockTxOp(){} +}; + +class MockTransactionalStore : public TransactionalStore{ + const string BEGIN; + const string BEGIN2PC; + const string PREPARE; + const string COMMIT; + const string ABORT; + std::vector<string> expected; + std::vector<string> actual; + + enum states {OPEN = 1, PREPARED = 2, COMMITTED = 3, ABORTED = 4}; + int state; + + class TestTransactionContext : public TPCTransactionContext{ + MockTransactionalStore* store; + public: + TestTransactionContext(MockTransactionalStore* _store) : store(_store) {} + void prepare(){ + if(!store->isOpen()) throw "txn already completed"; + store->state = PREPARED; + } + + void commit(){ + if(!store->isOpen() && !store->isPrepared()) throw "txn already completed"; + store->state = COMMITTED; + } + + void abort(){ + if(!store->isOpen() && !store->isPrepared()) throw "txn already completed"; + store->state = ABORTED; + } + ~TestTransactionContext(){} + }; + +public: + MockTransactionalStore() : + BEGIN("BEGIN"), BEGIN2PC("BEGIN2PC"), PREPARE("PREPARE"), COMMIT("COMMIT"), ABORT("ABORT"), state(OPEN){} + + void collectPreparedXids(std::set<std::string>&) + { + throw "Operation not supported"; + } + + std::auto_ptr<TPCTransactionContext> begin(const std::string&){ + actual.push_back(BEGIN2PC); + std::auto_ptr<TPCTransactionContext> txn(new TestTransactionContext(this)); + return txn; + } + std::auto_ptr<TransactionContext> begin(){ + actual.push_back(BEGIN); + std::auto_ptr<TransactionContext> txn(new TestTransactionContext(this)); + return txn; + } + void prepare(TPCTransactionContext& ctxt){ + actual.push_back(PREPARE); + dynamic_cast<TestTransactionContext&>(ctxt).prepare(); + } + void commit(TransactionContext& ctxt){ + actual.push_back(COMMIT); + dynamic_cast<TestTransactionContext&>(ctxt).commit(); + } + void abort(TransactionContext& ctxt){ + actual.push_back(ABORT); + dynamic_cast<TestTransactionContext&>(ctxt).abort(); + } + MockTransactionalStore& expectBegin(){ + expected.push_back(BEGIN); + return *this; + } + MockTransactionalStore& expectBegin2PC(){ + expected.push_back(BEGIN2PC); + return *this; + } + MockTransactionalStore& expectPrepare(){ + expected.push_back(PREPARE); + return *this; + } + MockTransactionalStore& expectCommit(){ + expected.push_back(COMMIT); + return *this; + } + MockTransactionalStore& expectAbort(){ + expected.push_back(ABORT); + return *this; + } + void check(){ + assertEqualVector(expected, actual); + } + + bool isPrepared(){ + return state == PREPARED; + } + + bool isCommitted(){ + return state == COMMITTED; + } + + bool isAborted(){ + return state == ABORTED; + } + + bool isOpen() const{ + return state == OPEN; + } + ~MockTransactionalStore(){} +}; + +#endif diff --git a/qpid/cpp/src/tests/TxPublishTest.cpp b/qpid/cpp/src/tests/TxPublishTest.cpp new file mode 100644 index 0000000000..af7761acee --- /dev/null +++ b/qpid/cpp/src/tests/TxPublishTest.cpp @@ -0,0 +1,114 @@ +/* + * + * 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. + * + */ +#include "qpid/broker/NullMessageStore.h" +#include "qpid/broker/RecoveryManager.h" +#include "qpid/broker/TxPublish.h" +#include "qpid_test_plugin.h" +#include <iostream> +#include <list> +#include <vector> +#include "MessageUtils.h" + +using std::list; +using std::pair; +using std::vector; +using boost::intrusive_ptr; +using namespace qpid::broker; +using namespace qpid::framing; + +class TxPublishTest : public CppUnit::TestCase +{ + typedef std::pair<string, intrusive_ptr<PersistableMessage> > msg_queue_pair; + + class TestMessageStore : public NullMessageStore + { + public: + vector<msg_queue_pair> enqueued; + + void enqueue(TransactionContext*, intrusive_ptr<PersistableMessage>& msg, const PersistableQueue& queue) + { + msg->enqueueComplete(); + enqueued.push_back(msg_queue_pair(queue.getName(), msg)); + } + + //dont care about any of the other methods: + TestMessageStore() : NullMessageStore(false) {} + ~TestMessageStore(){} + }; + + CPPUNIT_TEST_SUITE(TxPublishTest); + CPPUNIT_TEST(testPrepare); + CPPUNIT_TEST(testCommit); + CPPUNIT_TEST_SUITE_END(); + + + TestMessageStore store; + Queue::shared_ptr queue1; + Queue::shared_ptr queue2; + intrusive_ptr<Message> msg; + TxPublish op; + +public: + + TxPublishTest() : + queue1(new Queue("queue1", false, &store, 0)), + queue2(new Queue("queue2", false, &store, 0)), + msg(MessageUtils::createMessage("exchange", "routing_key", "id")), + op(msg) + { + msg->getProperties<DeliveryProperties>()->setDeliveryMode(PERSISTENT); + op.deliverTo(queue1); + op.deliverTo(queue2); + } + + void testPrepare() + { + intrusive_ptr<PersistableMessage> pmsg = static_pointer_cast<PersistableMessage>(msg); + //ensure messages are enqueued in store + op.prepare(0); + CPPUNIT_ASSERT_EQUAL((size_t) 2, store.enqueued.size()); + CPPUNIT_ASSERT_EQUAL(string("queue1"), store.enqueued[0].first); + CPPUNIT_ASSERT_EQUAL(pmsg, store.enqueued[0].second); + CPPUNIT_ASSERT_EQUAL(string("queue2"), store.enqueued[1].first); + CPPUNIT_ASSERT_EQUAL(pmsg, store.enqueued[1].second); + CPPUNIT_ASSERT_EQUAL( true, ( static_pointer_cast<PersistableMessage>(msg))->isEnqueueComplete()); + } + + void testCommit() + { + //ensure messages are delivered to queue + op.prepare(0); + op.commit(); + CPPUNIT_ASSERT_EQUAL((uint32_t) 1, queue1->getMessageCount()); + intrusive_ptr<Message> msg_dequeue = queue1->dequeue().payload; + + CPPUNIT_ASSERT_EQUAL( true, (static_pointer_cast<PersistableMessage>(msg_dequeue))->isEnqueueComplete()); + CPPUNIT_ASSERT_EQUAL(msg, msg_dequeue); + + CPPUNIT_ASSERT_EQUAL((uint32_t) 1, queue2->getMessageCount()); + CPPUNIT_ASSERT_EQUAL(msg, queue2->dequeue().payload); + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(TxPublishTest); + diff --git a/qpid/cpp/src/tests/Url.cpp b/qpid/cpp/src/tests/Url.cpp new file mode 100644 index 0000000000..bc07737520 --- /dev/null +++ b/qpid/cpp/src/tests/Url.cpp @@ -0,0 +1,64 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + + +#include "unit_test.h" +#include "test_tools.h" +#include "qpid/Url.h" +#include <boost/assign.hpp> + +using namespace std; +using namespace qpid; +using namespace boost::assign; + +QPID_AUTO_TEST_SUITE(UrlTestSuite) + +BOOST_AUTO_TEST_CASE(testUrl_str) { + Url url; + url.push_back(TcpAddress("foo.com")); + url.push_back(TcpAddress("bar.com", 6789)); + BOOST_CHECK_EQUAL("amqp:tcp:foo.com:5672,tcp:bar.com:6789", url.str()); + BOOST_CHECK(Url().str().empty()); +} + + +BOOST_AUTO_TEST_CASE(testUrl_parse) { + Url url; + url.parse("amqp:foo.com,tcp:bar.com:1234"); + BOOST_CHECK_EQUAL(2u, url.size()); + BOOST_CHECK_EQUAL("foo.com", boost::get<TcpAddress>(url[0]).host); + BOOST_CHECK_EQUAL("amqp:tcp:foo.com:5672,tcp:bar.com:1234", url.str()); + + url.parse("amqp:foo/ignorethis"); + BOOST_CHECK_EQUAL("amqp:tcp:foo:5672", url.str()); + + url.parse("amqp:"); + BOOST_CHECK_EQUAL("amqp:tcp::5672", url.str()); + + try { + url.parse("invalid url"); + BOOST_FAIL("Expected InvalidUrl exception"); + } + catch (const Url::InvalidUrl&) {} + + url.parseNoThrow("invalid url"); + BOOST_CHECK(url.empty()); +} + + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/Uuid.cpp b/qpid/cpp/src/tests/Uuid.cpp new file mode 100644 index 0000000000..c158cf53d2 --- /dev/null +++ b/qpid/cpp/src/tests/Uuid.cpp @@ -0,0 +1,78 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "qpid/framing/Uuid.h" +#include "qpid/framing/Buffer.h" + +#include "unit_test.h" + +#include <set> + +QPID_AUTO_TEST_SUITE(UuidTestSuite) + +using namespace std; +using namespace qpid::framing; + +struct UniqueSet : public std::set<Uuid> { + void operator()(const Uuid& uuid) { + BOOST_REQUIRE(find(uuid) == end()); + insert(uuid); + } +}; + +BOOST_AUTO_TEST_CASE(testUuidCtor) { + // Uniqueness + boost::array<Uuid,1000> uuids; + for_each(uuids.begin(), uuids.end(), mem_fun_ref(&Uuid::generate)); + UniqueSet unique; + for_each(uuids.begin(), uuids.end(), unique); +} + +boost::array<uint8_t, 16> sample = {{'\x1b', '\x4e', '\x28', '\xba', '\x2f', '\xa1', '\x11', '\xd2', '\x88', '\x3f', '\xb9', '\xa7', '\x61', '\xbd', '\xe3', '\xfb'}}; +const string sampleStr("1b4e28ba-2fa1-11d2-883f-b9a761bde3fb"); + +BOOST_AUTO_TEST_CASE(testUuidIstream) { + Uuid uuid; + istringstream in(sampleStr); + in >> uuid; + BOOST_CHECK(!in.fail()); + BOOST_CHECK(uuid == sample); +} + +BOOST_AUTO_TEST_CASE(testUuidOstream) { + Uuid uuid(sample.c_array()); + ostringstream out; + out << uuid; + BOOST_CHECK(out.good()); + BOOST_CHECK_EQUAL(out.str(), sampleStr); +} + +BOOST_AUTO_TEST_CASE(testUuidEncodeDecode) { + char* buff = static_cast<char*>(::alloca(Uuid::size())); + Buffer wbuf(buff, Uuid::size()); + Uuid uuid(sample.c_array()); + uuid.encode(wbuf); + + Buffer rbuf(buff, Uuid::size()); + Uuid decoded; + decoded.decode(rbuf); + BOOST_CHECK_EQUAL(string(sample.begin(), sample.end()), + string(decoded.begin(), decoded.end())); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/ais_check b/qpid/cpp/src/tests/ais_check new file mode 100755 index 0000000000..ae0edf88c1 --- /dev/null +++ b/qpid/cpp/src/tests/ais_check @@ -0,0 +1,27 @@ +#!/bin/sh +# Check for requirements, run AIS tests if found. +# + +id -nG | grep '\<ais\>' || \ + NOGROUP="You are not a member of the ais group." +ps -u root | grep aisexec >/dev/null || \ + NOAISEXEC="The aisexec daemon is not running as root" + +if test -n "$NOGROUP" -o -n "$NOAISEXEC"; then + cat <<EOF + + =========== WARNING: NOT RUNNING AIS TESTS ============== + + Tests that depend on the openais library (used for clustering) + will not be run because: + + $NOGROUP + $NOAISEXEC + + ========================================================== + +EOF + exit 0; # A warning, not a failure. +fi + +echo ./ais_run | newgrp ais diff --git a/qpid/cpp/src/tests/ais_run b/qpid/cpp/src/tests/ais_run new file mode 100755 index 0000000000..0f45edc39c --- /dev/null +++ b/qpid/cpp/src/tests/ais_run @@ -0,0 +1,15 @@ +#!/bin/sh +# +# Run AIS tests, assumes that ais_check has passed and we are +# running with the ais group ID. +# + +# FIXME aconway 2008-01-30: we should valgrind the cluster brokers. + +srcdir=`dirname $0` +$srcdir/start_cluster 4 +./ais_test +ret=$? +$srcdir/stop_cluster +exit $ret + diff --git a/qpid/cpp/src/tests/ais_test.cpp b/qpid/cpp/src/tests/ais_test.cpp new file mode 100644 index 0000000000..00c61242e4 --- /dev/null +++ b/qpid/cpp/src/tests/ais_test.cpp @@ -0,0 +1,23 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +// Defines test_main function to link with actual unit test code. +#define BOOST_AUTO_TEST_MAIN // Boost 1.33 +#define BOOST_TEST_MAIN +#include "unit_test.h" + diff --git a/qpid/cpp/src/tests/amqp_0_10/Map.cpp b/qpid/cpp/src/tests/amqp_0_10/Map.cpp new file mode 100644 index 0000000000..dcba6e38c2 --- /dev/null +++ b/qpid/cpp/src/tests/amqp_0_10/Map.cpp @@ -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. + * + */ +#include "unit_test.h" +#include "qpid/amqp_0_10/all_built_in_types.h" +//FIXME aconway 2008-04-08: #include "qpid/amqp_0_10/allSegmentTypes.h" +#include "qpid/amqp_0_10/Codec.h" +#include <iostream> + +using namespace qpid::amqp_0_10; +using namespace std; + +QPID_AUTO_TEST_SUITE(MapTestSuite) + + BOOST_AUTO_TEST_CASE(testGetSet) { + MapValue v; + v = Str8("foo"); + BOOST_CHECK(v.get<Str8>()); + BOOST_CHECK(!v.get<uint8_t>()); + BOOST_CHECK_EQUAL(*v.get<Str8>(), "foo"); + + v = uint8_t(42); + BOOST_CHECK(!v.get<Str8>()); + BOOST_CHECK(v.get<uint8_t>()); + BOOST_CHECK_EQUAL(*v.get<uint8_t>(), 42); + + v = uint16_t(12); + BOOST_CHECK(v.get<uint16_t>()); + BOOST_CHECK_EQUAL(*v.get<uint16_t>(), 12); +} + +template <class R> struct TestVisitor : public MapValue::Visitor<R> { + template <class T> R operator()(const T&) const { throw MapValue::BadTypeException(); } + R operator()(const R& r) const { return r; } +}; + +BOOST_AUTO_TEST_CASE(testVisit) { + MapValue v; + v = Str8("foo"); + BOOST_CHECK_EQUAL(v.apply_visitor(TestVisitor<Str8>()), "foo"); + v = Uint16(42); + BOOST_CHECK_EQUAL(v.apply_visitor(TestVisitor<Uint16>()), 42); + try { + v.apply_visitor(TestVisitor<bool>()); + BOOST_FAIL("Expecting exception"); + } + catch(const MapValue::BadTypeException&) {} +} + + +BOOST_AUTO_TEST_CASE(testEncodeMapValue) { + MapValue mv; + std::string data; + mv = Str8("hello"); + Codec::encode(back_inserter(data))(mv); + BOOST_CHECK_EQUAL(data.size(), Codec::size(mv)); + MapValue mv2; + Codec::decode(data.begin())(mv2); + BOOST_CHECK_EQUAL(mv2.getCode(), 0x85); + BOOST_REQUIRE(mv2.get<Str8>()); + BOOST_CHECK_EQUAL(*mv2.get<Str8>(), "hello"); +} + +BOOST_AUTO_TEST_CASE(testEncode) { + Map map; + std::string data; + map["A"] = true; + map["b"] = Str8("hello"); + Codec::encode(back_inserter(data))(map); + BOOST_CHECK_EQUAL(Codec::size(map), data.size()); + Map map2; + Codec::decode(data.begin())(map2); + BOOST_CHECK_EQUAL(map.size(), 2u); + BOOST_CHECK(map["A"].get<bool>()); + BOOST_CHECK_EQUAL(*map["b"].get<Str8>(), "hello"); +} + + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp b/qpid/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp new file mode 100644 index 0000000000..cab87b6511 --- /dev/null +++ b/qpid/cpp/src/tests/amqp_0_10/ProxyTemplate.cpp @@ -0,0 +1,49 @@ +/* + * + * 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. + * + */ + +#include "unit_test.h" +#include "qpid/amqp_0_10/ProxyTemplate.h" +#include <boost/any.hpp> + +QPID_AUTO_TEST_SUITE(ProxyTemplateTestSuite) + +using namespace qpid::amqp_0_10; + +struct ToAny { + template <class T> + boost::any operator()(const T& t) { return boost::any(t); } +}; + +struct AnyProxy : public ProxyTemplate<ToAny, boost::any> {}; + +BOOST_AUTO_TEST_CASE(testAnyProxy) { + AnyProxy p; + boost::any a=p.connectionTune(1,2,3,4); + BOOST_CHECK_EQUAL(a.type().name(), typeid(connection::Tune).name()); + connection::Tune* tune=boost::any_cast<connection::Tune>(&a); + BOOST_REQUIRE(tune); + BOOST_CHECK_EQUAL(tune->channelMax, 1u); + BOOST_CHECK_EQUAL(tune->maxFrameSize, 2u); + BOOST_CHECK_EQUAL(tune->heartbeatMin, 3u); + BOOST_CHECK_EQUAL(tune->heartbeatMax, 4u); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/amqp_0_10/apply.cpp b/qpid/cpp/src/tests/amqp_0_10/apply.cpp new file mode 100644 index 0000000000..450aeac356 --- /dev/null +++ b/qpid/cpp/src/tests/amqp_0_10/apply.cpp @@ -0,0 +1,99 @@ +/* + * + * 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. + * + */ +#include "unit_test.h" +#include "qpid/amqp_0_10/specification.h" +#include "qpid/amqp_0_10/ApplyControl.h" + +QPID_AUTO_TEST_SUITE(VisitorTestSuite) + +using namespace qpid::amqp_0_10; + +struct GetCode : public ApplyFunctor<uint8_t> { + template <class T> uint8_t operator()(const T&) const { return T::CODE; } +}; + +struct SetChannelMax : ApplyFunctor<void> { + template <class T> void operator()(T&) const { BOOST_FAIL(""); } + void operator()(connection::Tune& t) const { t.channelMax=42; } +}; + +struct TestFunctor { + typedef bool result_type; + bool operator()(const connection::Tune& tune) { + BOOST_CHECK_EQUAL(tune.channelMax, 1u); + BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u); + BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u); + BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u); + return true; + } + template <class T> + bool operator()(const T&) { return false; } +}; + +BOOST_AUTO_TEST_CASE(testApply) { + connection::Tune tune(1,2,3,4); + Control* p = &tune; + + // boost oddity - without the cast we get undefined symbol errors. + BOOST_CHECK_EQUAL(apply(GetCode(), *p), (uint8_t)connection::Tune::CODE); + + TestFunctor tf; + BOOST_CHECK(apply(tf, *p)); + + connection::Start start; + p = &start; + BOOST_CHECK(!apply(tf, *p)); + + apply(SetChannelMax(), tune); + BOOST_CHECK_EQUAL(tune.channelMax, 42); +} + +struct VoidTestFunctor { + typedef void result_type; + + int code; + VoidTestFunctor() : code() {} + + void operator()(const connection::Tune& tune) { + BOOST_CHECK_EQUAL(tune.channelMax, 1u); + BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u); + BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u); + BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u); + code=connection::Tune::CODE; + } + template <class T> + void operator()(const T&) { code=0xFF; } +}; + +BOOST_AUTO_TEST_CASE(testApplyVoid) { + connection::Tune tune(1,2,3,4); + Control* p = &tune; + VoidTestFunctor tf; + apply(tf, *p); + BOOST_CHECK_EQUAL(uint8_t(connection::Tune::CODE), tf.code); + + connection::Start start; + p = &start; + apply(tf, *p); + BOOST_CHECK_EQUAL(0xFF, tf.code); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/amqp_0_10/handlers.cpp b/qpid/cpp/src/tests/amqp_0_10/handlers.cpp new file mode 100644 index 0000000000..035429a15d --- /dev/null +++ b/qpid/cpp/src/tests/amqp_0_10/handlers.cpp @@ -0,0 +1,125 @@ +/* + * + * 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. + * + */ + +#include "unit_test.h" +#include "qpid/Exception.h" +#include "qpid/amqp_0_10/Unit.h" +#include "qpid/amqp_0_10/ControlHolder.h" +#include "qpid/amqp_0_10/CommandHolder.h" +#include "qpid/amqp_0_10/handlers.h" +#include "qpid/amqp_0_10/specification.h" + +QPID_AUTO_TEST_SUITE(handler_tests) + +using namespace qpid::amqp_0_10; +using namespace std; + +string called; // Set by called handler function + +// Note on handlers: +// +// Control and Command handlers are separate, both behave the same way, +// so substitute "control or command" for command in the following. +// +// Command handlers derive from CommandHandler and implement functions +// for all the commands they handle. Handling an unimplemented command +// will raise NotImplementedException. +// +// Using virtual inheritance from CommandHandler allows multiple +// handlers to be aggregated into one with multiple inheritance, +// See test code for example. +// +// E.g. the existing broker model would have two control handlers: +// - ConnectionHandler: ControlHandler for connection controls. +// - SessionHandler: ControlHandler for session controls. +// It would have class-command handlers for each AMQP class: +// - QueueHandler, MessageHandler etc.. handle each class. +// And an aggregate handler in place of BrokerAdapter +// - BrokerCommandHandler: public QueueHandler, MessageHandler ... +// +// In other applications (e.g. cluster) any combination of commands +// can be handled by a given handler. It _might_ simplify the code +// to collaps ConnectionHandler and SessionHandler into a single +// ControlHandler (or it might not.) + +struct TestExecutionHandler : public virtual CommandHandler { + void executionSync() { called = "executionSync"; } + // ... etc. for all execution commands +}; + +struct TestMessageHandler : public virtual CommandHandler { + void messageCancel(const Str8&) { called="messageCancel"; } + // ... etc. +}; + +// Aggregate handler for all recognised commands. +struct TestCommandHandler : + public TestExecutionHandler, + public TestMessageHandler + // ... etc. handlers for all command classes. +{}; // Nothing to do. + + +// Sample unit handler, written as a static_visitor. +// Note it could equally be written with if/else statements +// in handle. +// +struct TestUnitHandler : public boost::static_visitor<void> { + TestCommandHandler handler; + void handle(const Unit& u) { u.applyVisitor(*this); } + + void operator()(const Body&) { called="Body"; } + void operator()(const Header&) { called="Header"; } + void operator()(const ControlHolder&) { throw qpid::Exception("I don't do controls."); } + void operator()(const CommandHolder& c) { c.invoke(handler); } +}; + +BOOST_AUTO_TEST_CASE(testHandlers) { + TestUnitHandler handler; + Unit u; + + u = Body(); + handler.handle(u); + BOOST_CHECK_EQUAL("Body", called); + + u = Header(); + handler.handle(u); + BOOST_CHECK_EQUAL("Header", called); + + // in_place<Foo>(...) is equivalent to Foo(...) but + // constructs Foo directly in the holder, avoiding + // a copy. + + u = CommandHolder(in_place<execution::Sync>()); + handler.handle(u); + BOOST_CHECK_EQUAL("executionSync", called); + + u = ControlHolder(in_place<connection::Start>(Map(), Str16Array(), Str16Array())); + try { + handler.handle(u); + } catch (const qpid::Exception&) {} + + u = CommandHolder(in_place<message::Cancel>(Str8())); + handler.handle(u); + BOOST_CHECK_EQUAL("messageCancel", called); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/amqp_0_10/serialize.cpp b/qpid/cpp/src/tests/amqp_0_10/serialize.cpp new file mode 100644 index 0000000000..8928a9fbc9 --- /dev/null +++ b/qpid/cpp/src/tests/amqp_0_10/serialize.cpp @@ -0,0 +1,383 @@ +/* + * + * 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. + * + */ + +#include "unit_test.h" +#include "tests/allSegmentTypes.h" + +#include "qpid/framing/AMQFrame.h" +#include "qpid/framing/Buffer.h" + +#include "qpid/amqp_0_10/Packer.h" +#include "qpid/amqp_0_10/built_in_types.h" +#include "qpid/amqp_0_10/Codec.h" +#include "qpid/amqp_0_10/specification.h" +#include "qpid/amqp_0_10/ControlHolder.h" +#include "qpid/amqp_0_10/StructHolder.h" +#include "qpid/amqp_0_10/FrameHeader.h" +#include "qpid/amqp_0_10/Map.h" +#include "qpid/amqp_0_10/Unit.h" +#include "tests/allSegmentTypes.h" + +#include <boost/test/test_case_template.hpp> +#include <boost/type_traits/is_arithmetic.hpp> +#include <boost/utility/enable_if.hpp> +#include <boost/optional.hpp> +#include <boost/mpl/vector.hpp> +#include <boost/mpl/back_inserter.hpp> +#include <boost/mpl/copy.hpp> +#include <boost/mpl/empty_sequence.hpp> +#include <boost/current_function.hpp> +#include <iterator> +#include <string> +#include <sstream> +#include <iostream> +#include <netinet/in.h> + +// Missing operators needed for tests. +namespace boost { +template <class T, size_t N> +std::ostream& operator<<(std::ostream& out, const array<T,N>& a) { + std::ostream_iterator<T> o(out, " "); + std::copy(a.begin(), a.end(), o); + return out; +} +} // boost + +QPID_AUTO_TEST_SUITE(SerializeTestSuite) + +using namespace std; +namespace mpl=boost::mpl; +using namespace qpid::amqp_0_10; +using qpid::framing::in_place; + +template <class A, class B> struct concat2 { typedef typename mpl::copy<B, typename mpl::back_inserter<A> >::type type; }; +template <class A, class B, class C> struct concat3 { typedef typename concat2<A, typename concat2<B, C>::type>::type type; }; +template <class A, class B, class C, class D> struct concat4 { typedef typename concat2<A, typename concat3<B, C, D>::type>::type type; }; + +typedef mpl::vector<Boolean, Char, Int32, Int64, Int8, Uint16, CharUtf32, Uint32, Uint64, Bin8, Uint8>::type IntegralTypes; +typedef mpl::vector<Bin1024, Bin128, Bin16, Bin256, Bin32, Bin40, Bin512, Bin64, Bin72>::type BinTypes; +typedef mpl::vector<Double, Float>::type FloatTypes; +typedef mpl::vector<SequenceNo, Uuid, Datetime, Dec32, Dec64> FixedSizeClassTypes; +typedef mpl::vector<Map, Vbin8, Str8Latin, Str8, Str8Utf16, Vbin16, Str16Latin, Str16, Str16Utf16, Vbin32> VariableSizeTypes; + +typedef concat4<IntegralTypes, BinTypes, FloatTypes, FixedSizeClassTypes>::type FixedSizeTypes; +typedef concat2<FixedSizeTypes, VariableSizeTypes>::type AllTypes; + +// TODO aconway 2008-02-20: should test 64 bit integrals for order also. +BOOST_AUTO_TEST_CASE(testNetworkByteOrder) { + string data; + + uint32_t l = 0x11223344; + Codec::encode(std::back_inserter(data))(l); + uint32_t enc=reinterpret_cast<const uint32_t&>(*data.data()); + uint32_t l2 = ntohl(enc); + BOOST_CHECK_EQUAL(l, l2); + + data.clear(); + uint16_t s = 0x1122; + Codec::encode(std::back_inserter(data))(s); + uint32_t s2 = ntohs(*reinterpret_cast<const uint32_t*>(data.data())); + BOOST_CHECK_EQUAL(s, s2); +} + +BOOST_AUTO_TEST_CASE(testSetLimit) { + typedef Codec::Encoder<back_insert_iterator<string> > Encoder; + string data; + Encoder encode(back_inserter(data), 3); + encode('1')('2')('3'); + try { + encode('4'); + BOOST_FAIL("Expected exception"); + } catch (...) {} // FIXME aconway 2008-04-03: catch proper exception + BOOST_CHECK_EQUAL(data, "123"); +} + +BOOST_AUTO_TEST_CASE(testScopedLimit) { + typedef Codec::Encoder<back_insert_iterator<string> > Encoder; + string data; + Encoder encode(back_inserter(data), 10); + encode(Str8("123")); // 4 bytes + { + Encoder::ScopedLimit l(encode, 3); + encode('a')('b')('c'); + try { + encode('d'); + BOOST_FAIL("Expected exception"); + } catch(...) {} // FIXME aconway 2008-04-03: catch proper exception + } + BOOST_CHECK_EQUAL(data, "\003123abc"); + encode('x')('y')('z'); + try { + encode('!'); + BOOST_FAIL("Expected exception"); + } catch(...) {} // FIXME aconway 2008-04-03: catch proper exception + BOOST_CHECK_EQUAL(data.size(), 10u); +} + +// Assign test values to the various types. +void testValue(bool& b) { b = true; } +void testValue(Bit&) { } +template <class T> typename boost::enable_if<boost::is_arithmetic<T> >::type testValue(T& n) { n=42; } +void testValue(CharUtf32& c) { c = 43; } +void testValue(long long& l) { l = 0x012345; } +void testValue(Datetime& dt) { dt = qpid::sys::now(); } +void testValue(Uuid& uuid) { uuid=Uuid(true); } +template <class E, class M> void testValue(Decimal<E,M>& d) { d.exponent=2; d.mantissa=0x1122; } +void testValue(SequenceNo& s) { s = 42; } +template <size_t N> void testValue(Bin<N>& a) { a.assign(42); } +template <class T, class S, int Unique> void testValue(SerializableString<T, S, Unique>& s) { + char msg[]="foobar"; + s.assign(msg, msg+sizeof(msg)); +} +void testValue(Str16& s) { s = "the quick brown fox jumped over the lazy dog"; } +void testValue(Str8& s) { s = "foobar"; } +void testValue(Map& m) { m["s"] = Str8("foobar"); m["b"] = true; m["c"] = uint16_t(42); } + +//typedef mpl::vector<Str8, Str16>::type TestTypes; +BOOST_AUTO_TEST_CASE_TEMPLATE(testEncodeDecode, T, AllTypes) +{ + string data; + T t; + testValue(t); + Codec::encode(std::back_inserter(data))(t); + + BOOST_CHECK_EQUAL(Codec::size(t), data.size()); + + T t2; + Codec::decode(data.begin())(t2); + BOOST_CHECK_EQUAL(t,t2); +} + +struct TestMe { + bool encoded, decoded; + char value; + TestMe(char v) : encoded(), decoded(), value(v) {} + template <class S> void encode(S& s) const { + const_cast<TestMe*>(this)->encoded=true; s(value); + } + template <class S> void decode(S& s) { decoded=true; s(value); } + template <class S> void serialize(S& s) { s.split(*this); } +}; + +BOOST_AUTO_TEST_CASE(testSplit) { + string data; + TestMe t1('x'); + Codec::encode(std::back_inserter(data))(t1); + BOOST_CHECK(t1.encoded); + BOOST_CHECK(!t1.decoded); + BOOST_CHECK_EQUAL(data, "x"); + + TestMe t2('y'); + Codec::decode(data.begin())(t2); + BOOST_CHECK(!t2.encoded); + BOOST_CHECK(t2.decoded); + BOOST_CHECK_EQUAL(t2.value, 'x'); +} + +BOOST_AUTO_TEST_CASE(testControlEncodeDecode) { + string data; + Control::Holder h(in_place<connection::Tune>(1,2,3,4)); + Codec::encode(std::back_inserter(data))(h); + + BOOST_CHECK_EQUAL(data.size(), Codec::size(h)); + + Codec::Decoder<string::iterator> decode(data.begin()); + Control::Holder h2; + decode(h2); + + BOOST_REQUIRE(h2.get()); + BOOST_CHECK_EQUAL(h2.get()->getClassCode(), connection::CODE); + BOOST_CHECK_EQUAL(h2.get()->getCode(), uint8_t(connection::Tune::CODE)); + connection::Tune& tune=static_cast<connection::Tune&>(*h2.get()); + BOOST_CHECK_EQUAL(tune.channelMax, 1u); + BOOST_CHECK_EQUAL(tune.maxFrameSize, 2u); + BOOST_CHECK_EQUAL(tune.heartbeatMin, 3u); + BOOST_CHECK_EQUAL(tune.heartbeatMax, 4u); +} + +struct DummyPacked { + static const uint8_t PACK=1; + boost::optional<char> i, j; + char k; + Bit l,m; + DummyPacked(char a=0, char b=0, char c=0) : i(a), j(b), k(c), l(), m() {} + template <class S> void serialize(S& s) { s(i)(j)(k)(l)(m); } +}; + +Packer<DummyPacked> serializable(DummyPacked& d) { return Packer<DummyPacked>(d); } + +BOOST_AUTO_TEST_CASE(testPackBits) { + DummyPacked d('a','b','c'); + BOOST_CHECK_EQUAL(packBits(d), 7u); + d.j = boost::none; + BOOST_CHECK_EQUAL(packBits(d), 5u); + d.m = true; + BOOST_CHECK_EQUAL(packBits(d), 0x15u); +} + + +BOOST_AUTO_TEST_CASE(testPacked) { + string data; + + Codec::encode(back_inserter(data))('a')(boost::optional<char>('b'))(boost::optional<char>())('c'); + BOOST_CHECK_EQUAL(data, "abc"); + data.clear(); + + DummyPacked dummy('a','b','c'); + + Codec::encode(back_inserter(data))(dummy); + BOOST_CHECK_EQUAL(data.size(), 4u); + BOOST_CHECK_EQUAL(data, string("\007abc")); + data.clear(); + + dummy.i = boost::none; + Codec::encode(back_inserter(data))(dummy); + BOOST_CHECK_EQUAL(data, string("\6bc")); + data.clear(); + + const char* missing = "\5xy"; + Codec::decode(missing)(dummy); + BOOST_CHECK(dummy.i); + BOOST_CHECK_EQUAL(*dummy.i, 'x'); + BOOST_CHECK(!dummy.j); + BOOST_CHECK_EQUAL(dummy.k, 'y'); +} + +BOOST_AUTO_TEST_CASE(testUnit) { + string data; + Control::Holder h(in_place<connection::Tune>(1,2,3,4)); + Codec::encode(std::back_inserter(data))(h); + + Unit unit(FrameHeader(FIRST_FRAME|LAST_FRAME, CONTROL)); + Codec::decode(data.begin())(unit); + + BOOST_REQUIRE(unit.get<ControlHolder>()); + + string data2; + Codec::encode(back_inserter(data2))(unit); + + BOOST_CHECK_EQUAL(data, data2); +} + +BOOST_AUTO_TEST_CASE(testArray) { + ArrayDomain<char> a; + a.resize(3, 'x'); + string data; + Codec::encode(back_inserter(data))(a); + + ArrayDomain<char> b; + Codec::decode(data.begin())(b); + BOOST_CHECK_EQUAL(b.size(), 3u); + string data3; + Codec::encode(back_inserter(data3))(a); + BOOST_CHECK_EQUAL(data, data3); + + Array x; + Codec::decode(data.begin())(x); + BOOST_CHECK_EQUAL(x.size(), 3u); + BOOST_CHECK_EQUAL(x[0].size(), 1u); + BOOST_CHECK_EQUAL(*x[0].begin(), 'x'); + BOOST_CHECK_EQUAL(*x[2].begin(), 'x'); + + string data2; + Codec::encode(back_inserter(data2))(x); + BOOST_CHECK_EQUAL(data,data2); +} + +BOOST_AUTO_TEST_CASE(testStruct) { + string data; + + message::DeliveryProperties dp; + BOOST_CHECK(!dp.discardUnroutable); + dp.immediate = true; + dp.redelivered = false; + dp.priority = message::MEDIUM; + dp.exchange = "foo"; + + Codec::encode(back_inserter(data))(dp); + uint16_t encodedBits=uint8_t(data[1]); // Little-endian + encodedBits <<= 8; + encodedBits += uint8_t(data[0]); + BOOST_CHECK_EQUAL(encodedBits, packBits(dp)); + + data.clear(); + Struct::Holder h(dp); + Codec::encode(back_inserter(data))(h); + + Struct::Holder h2; + Codec::decode(data.begin())(h2); + BOOST_CHECK_EQUAL(h2.getClassCode(), Uint8(message::DeliveryProperties::CLASS_CODE)); + BOOST_CHECK_EQUAL(h2.getCode(), Uint8(message::DeliveryProperties::CODE)); + message::DeliveryProperties* dp2 = + dynamic_cast<message::DeliveryProperties*>(h2.get()); + BOOST_CHECK(dp2); + BOOST_CHECK(!dp2->discardUnroutable); + BOOST_CHECK(dp2->immediate); + BOOST_CHECK(!dp2->redelivered); + BOOST_CHECK_EQUAL(dp2->priority, message::MEDIUM); + BOOST_CHECK_EQUAL(dp2->exchange, "foo"); +} + +struct RecodeUnit { + template <class T> + void operator() (const T& t) { + BOOST_MESSAGE(BOOST_CURRENT_FUNCTION); + using qpid::framing::Buffer; + using qpid::framing::AMQFrame; + + Unit u(t); + connection::Start s; + + string data; + Codec::encode(back_inserter(data))(u.getHeader())(u); + data.push_back(char(0xCE)); // Preview end-of-frame + + Buffer buf(&data[0], data.size()); + AMQFrame f; + f.decode(buf); + + string data2(f.size(), ' '); + Buffer buf2(&data2[0], data.size()); + f.encode(buf2); + + BOOST_CHECK_MESSAGE(data == data2, BOOST_CURRENT_FUNCTION); + BOOST_CHECK_EQUAL(data, data2); + + Codec::Decoder<string::iterator> decode(data2.begin()); + FrameHeader h; + decode(h); + Unit u2(h); + decode(u2); + + string data3; + Codec::encode(back_inserter(data3))(u.getHeader())(u); + + BOOST_CHECK_EQUAL(data3, data2); + BOOST_CHECK_MESSAGE(data3 == data2, BOOST_CURRENT_FUNCTION); + } +}; + +// BOOST_AUTO_TEST_CASE(testSerializeAllSegmentTypes) { +// RecodeUnit recode; +// allSegmentTypes(recode); +// } + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/client_test.cpp b/qpid/cpp/src/tests/client_test.cpp new file mode 100644 index 0000000000..bd2a541c92 --- /dev/null +++ b/qpid/cpp/src/tests/client_test.cpp @@ -0,0 +1,147 @@ +/* + * + * 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 provides a simple test (and example) of basic + * functionality including declaring an exchange and a queue, binding + * these together, publishing a message and receiving that message + * asynchronously. + */ + +#include <iostream> + +#include "TestOptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/Session.h" +#include "qpid/framing/FrameSet.h" +#include "qpid/framing/MessageTransferBody.h" + +using namespace qpid; +using namespace qpid::client; +using qpid::framing::FrameSet; +using qpid::framing::MessageTransferBody; +using std::string; + +struct Args : public qpid::TestOptions { + uint msgSize; + uint maxFrameSize; + + Args() : msgSize(26), maxFrameSize(65535) + { + addOptions() + ("size", optValue(msgSize, "N"), "message size") + ("max-frame-size", optValue(maxFrameSize, "N"), "max frame size"); + } +}; + +const std::string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + +std::string generateData(uint size) +{ + if (size < chars.length()) { + return chars.substr(0, size); + } + std::string data; + for (uint i = 0; i < (size / chars.length()); i++) { + data += chars; + } + data += chars.substr(0, size % chars.length()); + return data; +} + +void print(const std::string& text, const Message& msg) +{ + std::cout << text; + if (msg.getData().size() > 16) { + std::cout << msg.getData().substr(0, 16) << "..."; + } else { + std::cout << msg.getData(); + } + std::cout << std::endl; +} + +int main(int argc, char** argv) +{ + try { + Args opts; + opts.parse(argc, argv); + + //Connect to the broker: + Connection connection(opts.trace, opts.maxFrameSize); + opts.open(connection); + if (opts.trace) std::cout << "Opened connection." << std::endl; + + //Create and open a session on the connection through which + //most functionality is exposed: + Session session = connection.newSession(ASYNC); + if (opts.trace) std::cout << "Opened session." << std::endl; + + + //'declare' the exchange and the queue, which will create them + //as they don't exist + session.exchangeDeclare(arg::exchange="MyExchange", arg::type="direct"); + if (opts.trace) std::cout << "Declared exchange." << std::endl; + session.queueDeclare(arg::queue="MyQueue", arg::autoDelete=true, arg::exclusive=true); + if (opts.trace) std::cout << "Declared queue." << std::endl; + + //now bind the queue to the exchange + session.queueBind(arg::exchange="MyExchange", arg::queue="MyQueue", arg::routingKey="MyKey"); + if (opts.trace) std::cout << "Bound queue to exchange." << std::endl; + + //create and send a message to the exchange using the routing + //key we bound our queue with: + Message msgOut(generateData(opts.msgSize)); + msgOut.getDeliveryProperties().setRoutingKey("MyKey"); + session.messageTransfer(arg::destination="MyExchange", arg::content=msgOut); + if (opts.trace) print("Published message: ", msgOut); + + //subscribe to the queue, add sufficient credit and then get + //incoming 'frameset', check that its a message transfer and + //then convert it to a message (see Dispatcher and + //SubscriptionManager utilties for common reusable patterns at + //a higher level) + session.messageSubscribe(arg::queue="MyQueue", arg::destination="MyId"); + session.messageFlow(arg::destination="MyId", arg::unit=0, arg::value=1); //credit for one message + session.messageFlow(arg::destination="MyId", arg::unit=1, arg::value=0xFFFFFFFF); //credit for infinite bytes + if (opts.trace) std::cout << "Subscribed to queue." << std::endl; + FrameSet::shared_ptr incoming = session.get(); + if (incoming->isA<MessageTransferBody>()) { + Message msgIn(*incoming, session); + if (msgIn.getData() == msgOut.getData()) { + if (opts.trace) std::cout << "Received the exepected message." << std::endl; + msgIn.acknowledge(); + } else { + print("Received an unexepected message: ", msgIn); + } + } + + //close the session & connection + session.close(); + if (opts.trace) std::cout << "Closed session." << std::endl; + connection.close(); + if (opts.trace) std::cout << "Closed connection." << std::endl; + return 0; + } catch(const std::exception& e) { + std::cout << e.what() << std::endl; + } + return 1; +} diff --git a/qpid/cpp/src/tests/cluster.mk b/qpid/cpp/src/tests/cluster.mk new file mode 100644 index 0000000000..ba8d99935f --- /dev/null +++ b/qpid/cpp/src/tests/cluster.mk @@ -0,0 +1,20 @@ +if CPG +# +# Cluster tests makefile fragment, to be included in Makefile.am +# + +lib_cluster = $(abs_builddir)/../libqpidcluster.la + +# NOTE: Programs using the openais library must be run with gid=ais +# You should do "newgrp ais" before running the tests to run these. +# + +# ais_check checks conditions for AIS tests and runs if ok. +TESTS+=ais_check +EXTRA_DIST+=ais_check ais_run + +check_PROGRAMS+=ais_test +ais_test_SOURCES=ais_test.cpp Cpg.cpp +ais_test_LDADD=$(lib_client) $(lib_cluster) -lboost_unit_test_framework + +endif diff --git a/qpid/cpp/src/tests/cluster_client.cpp b/qpid/cpp/src/tests/cluster_client.cpp new file mode 100644 index 0000000000..cd048d1651 --- /dev/null +++ b/qpid/cpp/src/tests/cluster_client.cpp @@ -0,0 +1,84 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "unit_test.h" +#include "BrokerFixture.h" +#include "qpid/client/Session.h" + +#include <fstream> +#include <vector> +#include <functional> + +QPID_AUTO_TEST_SUITE(cluster_clientTestSuite) + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::framing; +using namespace qpid::client::arg; +using framing::TransferContent; +using std::vector; +using std::string; +using std::ifstream; +using std::ws; + +struct ClusterConnections : public vector<shared_ptr<Connection> > { + ClusterConnections() { + ifstream portfile("cluster.ports"); + BOOST_REQUIRE(portfile.good()); + portfile >> ws; + while (portfile.good()) { + uint16_t port; + portfile >> port >> ws; + push_back(make_shared_ptr(new Connection(port))); + back()->open("localhost", port); + } + BOOST_REQUIRE(size() > 1); + } + + ~ClusterConnections() { + for (iterator i = begin(); i != end(); ++i ){ + (*i)->close(); + } + } +}; + +BOOST_AUTO_TEST_CASE(testWiringReplication) { + // Declare on one broker, use on others. + ClusterConnections cluster; + BOOST_REQUIRE(cluster.size() > 1); + + Session broker0 = cluster[0]->newSession(ASYNC); + broker0.exchangeDeclare(exchange="ex"); + broker0.queueDeclare(queue="q"); + broker0.queueBind(exchange="ex", queue="q", routingKey="key"); + broker0.close(); + + for (size_t i = 1; i < cluster.size(); ++i) { + Session s = cluster[i]->newSession(ASYNC); + s.messageTransfer(content=TransferContent("data", "key", "ex")); + s.messageSubscribe(queue="q", destination="q"); + s.messageFlow(destination="q", unit=0, value=1);//messages + FrameSet::shared_ptr msg = s.get(); + BOOST_CHECK(msg->isA<MessageTransferBody>()); + BOOST_CHECK_EQUAL(string("data"), msg->getContent()); + s.getExecution().completed(msg->getId(), true, true); + cluster[i]->close(); + } +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/dlclose_noop.c b/qpid/cpp/src/tests/dlclose_noop.c new file mode 100644 index 0000000000..ba2fa75891 --- /dev/null +++ b/qpid/cpp/src/tests/dlclose_noop.c @@ -0,0 +1,30 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +/* + * Loaded via LD_PRELOAD this will turn dlclose into a no-op. + * + * Allows valgrind to generate useful reports from programs that + * dynamically unload libraries before exit, such as CppUnit's + * DllPlugInTester. + * + */ + +#include <stdio.h> +void* dlclose(void* handle) {} + diff --git a/qpid/cpp/src/tests/echo_service.cpp b/qpid/cpp/src/tests/echo_service.cpp new file mode 100644 index 0000000000..c3569d5fd4 --- /dev/null +++ b/qpid/cpp/src/tests/echo_service.cpp @@ -0,0 +1,229 @@ +/* + * + * 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 class provides an example of using AMQP for a request-response + * style system. 'Requests' are messages sent to a well known + * destination. A 'service' process consumes these message and + * responds by echoing the message back to the sender on a + * sender-specified private queue. + */ + +#include "qpid/client/Channel.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Exchange.h" +#include "qpid/client/MessageListener.h" +#include "qpid/client/Queue.h" +#include "qpid/sys/Time.h" +#include <iostream> +#include <sstream> + +using namespace qpid::client; +using namespace qpid::sys; +using std::string; + + +/** + * A message listener implementation representing the 'service', this + * will 'echo' any requests received. + */ +class EchoServer : public MessageListener{ + Channel* const channel; +public: + EchoServer(Channel* channel); + virtual void received(Message& msg); +}; + +/** + * A message listener implementation that merely prints received + * messages to the console. Used to report on 'echo' responses. + */ +class LoggingListener : public MessageListener{ +public: + virtual void received(Message& msg); +}; + +/** + * A utility class that manages the command line options needed to run + * the example confirgurably. + */ +class Args{ + string host; + int port; + bool trace; + bool help; + bool client; +public: + inline Args() : host("localhost"), port(5672), trace(false), help(false), client(false){} + void parse(int argc, char** argv); + void usage(); + + inline const string& getHost() const { return host;} + inline int getPort() const { return port; } + inline bool getTrace() const { return trace; } + inline bool getHelp() const { return help; } + inline bool getClient() const { return client; } +}; + +/** + * The main test path. There are two basic modes: 'client' and + * 'service'. First one or more services are started, then one or more + * clients are started and messages can be sent. + */ +int main(int argc, char** argv){ + const std::string echo_service("echo_service"); + Args args; + args.parse(argc, argv); + if (args.getHelp()) { + args.usage(); + } else if (args.getClient()) { + //we have been started in 'client' mode, i.e. we will send an + //echo requests and print responses received. + try { + //Create connection & open a channel + Connection connection(args.getTrace()); + connection.open(args.getHost(), args.getPort()); + Channel channel; + connection.openChannel(channel); + + //Setup: declare the private 'response' queue and bind it + //to the direct exchange by its name which will be + //generated by the server + Queue response; + channel.declareQueue(response); + qpid::framing::FieldTable emptyArgs; + channel.bind(Exchange::STANDARD_DIRECT_EXCHANGE, response, response.getName(), emptyArgs); + + //Consume from the response queue, logging all echoed message to console: + LoggingListener listener; + std::string tag; + channel.consume(response, tag, &listener); + + //Process incoming requests on a new thread + channel.start(); + + //get messages from console and send them: + std::string text; + std::cout << "Enter text to send:" << std::endl; + while (std::getline(std::cin, text)) { + std::cout << "Sending " << text << " to echo server." << std::endl; + Message msg; + msg.getHeaders().setString("RESPONSE_QUEUE", response.getName()); + msg.setData(text); + channel.publish(msg, Exchange::STANDARD_DIRECT_EXCHANGE, echo_service); + + std::cout << "Enter text to send:" << std::endl; + } + + connection.close(); + } catch(std::exception& error) { + std::cout << error.what() << std::endl; + } + } else { + // we are in 'service' mode, i.e. we will consume messages + // from the request queue and echo each request back to the + // senders own private response queue. + try { + //Create connection & open a channel + Connection connection(args.getTrace()); + connection.open(args.getHost(), args.getPort()); + Channel channel; + connection.openChannel(channel); + + //Setup: declare the 'request' queue and bind it to the direct exchange with a 'well known' name + Queue request("request"); + channel.declareQueue(request); + qpid::framing::FieldTable emptyArgs; + channel.bind(Exchange::STANDARD_DIRECT_EXCHANGE, request, echo_service, emptyArgs); + + //Consume from the request queue, echoing back all messages received to the client that sent them + EchoServer server(&channel); + std::string tag = "server_tag"; + channel.consume(request, tag, &server); + + //Process incoming requests on the main thread + channel.run(); + + connection.close(); + } catch(std::exception& error) { + std::cout << error.what() << std::endl; + } + } +} + +EchoServer::EchoServer(Channel* _channel) : channel(_channel){} + +void EchoServer::received(Message& message) +{ + //get name of response queues binding to the default direct exchange: + const std::string name = message.getHeaders().getString("RESPONSE_QUEUE"); + + if (name.empty()) { + std::cout << "Cannot echo " << message.getData() << ", no response queue specified." << std::endl; + } else { + //print message to console: + std::cout << "Echoing " << message.getData() << " back to " << name << std::endl; + + //'echo' the message back: + channel->publish(message, Exchange::STANDARD_DIRECT_EXCHANGE, name); + } +} + +void LoggingListener::received(Message& message) +{ + //print message to console: + std::cout << "Received echo: " << message.getData() << std::endl; +} + + +void Args::parse(int argc, char** argv){ + for(int i = 1; i < argc; i++){ + string name(argv[i]); + if("-help" == name){ + help = true; + break; + }else if("-host" == name){ + host = argv[++i]; + }else if("-port" == name){ + port = atoi(argv[++i]); + }else if("-trace" == name){ + trace = true; + }else if("-client" == name){ + client = true; + }else{ + std::cout << "Warning: unrecognised option " << name << std::endl; + } + } +} + +void Args::usage(){ + std::cout << "Options:" << std::endl; + std::cout << " -help" << std::endl; + std::cout << " Prints this usage message" << std::endl; + std::cout << " -host <host>" << std::endl; + std::cout << " Specifies host to connect to (default is localhost)" << std::endl; + std::cout << " -port <port>" << std::endl; + std::cout << " Specifies port to conect to (default is 5762)" << std::endl; + std::cout << " -trace" << std::endl; + std::cout << " Indicates that the frames sent and received should be logged" << std::endl; + std::cout << " -client" << std::endl; + std::cout << " Run as a client (else will run as a server)" << std::endl; +} diff --git a/qpid/cpp/src/tests/exception_test.cpp b/qpid/cpp/src/tests/exception_test.cpp new file mode 100644 index 0000000000..715cdaec2a --- /dev/null +++ b/qpid/cpp/src/tests/exception_test.cpp @@ -0,0 +1,99 @@ +/* + * + * 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. + * + */ + +#include "unit_test.h" +#include "BrokerFixture.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/sys/Runnable.h" +#include "qpid/sys/Thread.h" +#include "qpid/framing/reply_exceptions.h" + +QPID_AUTO_TEST_SUITE(exception_test) + + +using namespace std; +using namespace qpid; +using namespace sys; +using namespace client; +using namespace framing; + +using boost::bind; +using boost::function; + +template <class Ex> +struct Catcher : public Runnable { + function<void ()> f; + bool caught; + Thread thread; + + Catcher(function<void ()> f_) : f(f_), caught(false), thread(this) {} + ~Catcher() { join(); } + + void run() { + try { f(); } + catch(const Ex& e) { + caught=true; + BOOST_MESSAGE(string("Caught expected exception: ")+e.what()); + } + catch(const std::exception& e) { + BOOST_ERROR(string("Bad exception: ")+e.what()); + } + catch(...) { + BOOST_ERROR(string("Bad exception: unknown")); + } + } + + bool join() { + if (thread.id()) { + thread.join(); + thread=Thread(); + } + return caught; + } +}; + +BOOST_FIXTURE_TEST_CASE(DisconnectedPop, ProxySessionFixture) { + ProxyConnection c(broker->getPort()); + session.queueDeclare(arg::queue="q"); + subs.subscribe(lq, "q"); + Catcher<ClosedException> pop(bind(&LocalQueue::pop, boost::ref(lq))); + connection.proxy.close(); + BOOST_CHECK(pop.join()); +} + +BOOST_FIXTURE_TEST_CASE(DisconnectedListen, ProxySessionFixture) { + struct NullListener : public MessageListener { + void received(Message&) { BOOST_FAIL("Unexpected message"); } + } l; + ProxyConnection c(broker->getPort()); + session.queueDeclare(arg::queue="q"); + subs.subscribe(l, "q"); + Thread t(subs); + connection.proxy.close(); + t.join(); + BOOST_CHECK_THROW(session.close(), InternalErrorException); +} + +BOOST_FIXTURE_TEST_CASE(NoSuchQueueTest, ProxySessionFixture) { + BOOST_CHECK_THROW(subs.subscribe(lq, "no such queue").sync(), NotFoundException); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/fanout_perftest b/qpid/cpp/src/tests/fanout_perftest new file mode 100755 index 0000000000..602aac25b7 --- /dev/null +++ b/qpid/cpp/src/tests/fanout_perftest @@ -0,0 +1,2 @@ +#!/bin/sh +exec `dirname $0`/run_perftest 10000 --mode fanout --npubs 16 --nsubs 16 diff --git a/qpid/cpp/src/tests/federation.py b/qpid/cpp/src/tests/federation.py new file mode 100755 index 0000000000..28ebc4a24d --- /dev/null +++ b/qpid/cpp/src/tests/federation.py @@ -0,0 +1,191 @@ +#!/usr/bin/env python +# +# 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. +# + +import sys +from qpid.testlib import TestBase, testrunner +from qpid.management import managementChannel, managementClient +from qpid.queue import Empty +from qpid.content import Content + + +def scan_args(name, default=None, args=sys.argv[1:]): + if (name in args): + pos = args.index(name) + return args[pos + 1] + elif default: + return default + else: + print "Please specify extra argument: %s" % name + sys.exit(2) + +def extract_args(name, args): + if (name in args): + pos = args.index(name) + del args[pos:pos+2] + else: + return None + +def remote_host(): + return scan_args("--remote-host", "localhost") + +def remote_port(): + return int(scan_args("--remote-port")) + +class Helper: + def __init__(self, parent): + self.parent = parent + self.channel = parent.client.channel(2) + self.mc = managementClient(self.channel.spec) + self.mch = self.mc.addChannel(self.channel) + self.mc.syncWaitForStable(self.mch) + + def get_objects(self, type): + return self.mc.syncGetObjects(self.mch, type) + + def get_object(self, type, position = 1, expected = None): + objects = self.get_objects(type) + if not expected: expected = position + self.assertEqual(len(objects), expected) + return objects[(position - 1)] + + + def call_method(self, object, method, args=None): + res = self.mc.syncCallMethod(self.mch, object.id, object.classKey, method, args) + self.assertEqual(res.status, 0) + self.assertEqual(res.statusText, "OK") + return res + + def assertEqual(self, a, b): + self.parent.assertEqual(a, b) + +class FederationTests(TestBase): + + def test_bridge_create_and_close(self): + mgmt = Helper(self) + broker = mgmt.get_object("broker") + + for i in range(10): + mgmt.call_method(broker, "connect", {"host":remote_host(), "port":remote_port()}) + link = mgmt.get_object("link") + + mgmt.call_method(link, "bridge", {"src":"amq.direct", "dest":"amq.direct", "key":"my-key"}) + bridge = mgmt.get_object("bridge") + + mgmt.call_method(bridge, "close") + self.assertEqual(len(mgmt.get_objects("bridge")), 0) + + mgmt.call_method(link, "close") + self.assertEqual(len(mgmt.get_objects("link")), 0) + + def test_pull_from_exchange(self): + channel = self.channel + + mgmt = Helper(self) + broker = mgmt.get_object("broker") + + mgmt.call_method(broker, "connect", {"host":remote_host(), "port":remote_port()}) + link = mgmt.get_object("link") + + mgmt.call_method(link, "bridge", {"src":"amq.direct", "dest":"amq.fanout", "key":"my-key"}) + bridge = mgmt.get_object("bridge") + + #setup queue to receive messages from local broker + channel.queue_declare(queue="fed1", exclusive=True, auto_delete=True) + channel.queue_bind(queue="fed1", exchange="amq.fanout") + self.subscribe(queue="fed1", destination="f1") + queue = self.client.queue("f1") + + #send messages to remote broker and confirm it is routed to local broker + r_conn = self.connect(host=remote_host(), port=remote_port()) + r_channel = r_conn.channel(1) + r_channel.session_open() + + for i in range(1, 11): + r_channel.message_transfer(destination="amq.direct", content=Content(properties={'routing_key' : "my-key"}, body="Message %d" % i)) + + for i in range(1, 11): + msg = queue.get(timeout=5) + self.assertEqual("Message %d" % i, msg.content.body) + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in queue: " + extra.content.body) + except Empty: None + + + mgmt.call_method(bridge, "close") + self.assertEqual(len(mgmt.get_objects("bridge")), 0) + + mgmt.call_method(link, "close") + self.assertEqual(len(mgmt.get_objects("link")), 0) + + def test_pull_from_queue(self): + channel = self.channel + + #setup queue on remote broker and add some messages + r_conn = self.connect(host=remote_host(), port=remote_port()) + r_channel = r_conn.channel(1) + r_channel.session_open() + r_channel.queue_declare(queue="my-bridge-queue", exclusive=True, auto_delete=True) + for i in range(1, 6): + r_channel.message_transfer(content=Content(properties={'routing_key' : "my-bridge-queue"}, body="Message %d" % i)) + + #setup queue to receive messages from local broker + channel.queue_declare(queue="fed1", exclusive=True, auto_delete=True) + channel.queue_bind(queue="fed1", exchange="amq.fanout") + self.subscribe(queue="fed1", destination="f1") + queue = self.client.queue("f1") + + mgmt = Helper(self) + broker = mgmt.get_object("broker") + + mgmt.call_method(broker, "connect", {"host":remote_host(), "port":remote_port()}) + link = mgmt.get_object("link") + + mgmt.call_method(link, "bridge", {"src":"my-bridge-queue", "dest":"amq.fanout", "key":"", "src_is_queue":1}) + bridge = mgmt.get_object("bridge") + + #add some more messages (i.e. after bridge was created) + for i in range(6, 11): + r_channel.message_transfer(content=Content(properties={'routing_key' : "my-bridge-queue"}, body="Message %d" % i)) + + for i in range(1, 11): + msg = queue.get(timeout=5) + self.assertEqual("Message %d" % i, msg.content.body) + try: + extra = queue.get(timeout=1) + self.fail("Got unexpected message in queue: " + extra.content.body) + except Empty: None + + + mgmt.call_method(bridge, "close") + self.assertEqual(len(mgmt.get_objects("bridge")), 0) + + mgmt.call_method(link, "close") + self.assertEqual(len(mgmt.get_objects("link")), 0) + +if __name__ == '__main__': + args = sys.argv[1:] + #need to remove the extra options from args as test runner doesn't recognise them + extract_args("--remote-port", args) + extract_args("--remote-host", args) + #add module(s) to run to testrunners args + args.append("federation") + + if not testrunner.run(args): sys.exit(1) diff --git a/qpid/cpp/src/tests/interop_runner.cpp b/qpid/cpp/src/tests/interop_runner.cpp new file mode 100644 index 0000000000..824af7f3b7 --- /dev/null +++ b/qpid/cpp/src/tests/interop_runner.cpp @@ -0,0 +1,240 @@ +/* + * + * 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. + * + */ + +#include "qpid/Options.h" +#include "qpid/ptr_map.h" +#include "qpid/Exception.h" +#include "qpid/client/Channel.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Exchange.h" +#include "qpid/client/MessageListener.h" +#include "qpid/client/Queue.h" +#include "qpid/sys/Thread.h" +#include "qpid/sys/Time.h" +#include <iostream> +#include <memory> +#include "BasicP2PTest.h" +#include "BasicPubSubTest.h" +#include "TestCase.h" +#include <boost/ptr_container/ptr_map.hpp> + +/** + * Framework for interop tests. + * + * [see http://cwiki.apache.org/confluence/display/qpid/Interop+Testing+Specification for details]. + */ + +using namespace qpid::client; +using namespace qpid::sys; +using qpid::TestCase; +using qpid::TestOptions; +using qpid::framing::FieldTable; +using qpid::framing::ReplyTo; +using namespace std; + +class DummyRun : public TestCase +{ +public: + DummyRun() {} + void assign(const string&, FieldTable&, TestOptions&) {} + void start() {} + void stop() {} + void report(qpid::client::Message&) {} +}; + +string parse_next_word(const string& input, const string& delims, string::size_type& position); + +/** + */ +class Listener : public MessageListener, private Runnable{ + typedef boost::ptr_map<string, TestCase> TestMap; + + Channel& channel; + TestOptions& options; + TestMap tests; + const string name; + const string topic; + TestCase* test; + auto_ptr<Thread> runner; + ReplyTo reportTo; + string reportCorrelator; + + void shutdown(); + bool invite(const string& name); + void run(); + + void sendResponse(Message& response, ReplyTo replyTo); + void sendResponse(Message& response, Message& request); + void sendSimpleResponse(const string& type, Message& request); + void sendReport(); +public: + Listener(Channel& channel, TestOptions& options); + void received(Message& msg); + void bindAndConsume(); + void registerTest(string name, TestCase* test); +}; + +int main(int argc, char** argv) { + try { + TestOptions options; + options.parse(argc, argv); + if (options.help) + cout << options; + else { + Connection connection(options.trace); + connection.open(options.host, options.port, "guest", "guest", options.virtualhost); + + Channel channel; + connection.openChannel(channel); + + Listener listener(channel, options); + listener.registerTest("TC1_DummyRun", new DummyRun()); + listener.registerTest("TC2_BasicP2P", new qpid::BasicP2PTest()); + listener.registerTest("TC3_BasicPubSub", new qpid::BasicPubSubTest()); + + listener.bindAndConsume(); + + channel.run(); + connection.close(); + } + } catch(const exception& error) { + cout << error.what() << endl << "Type " << argv[0] << " --help for help" << endl; + } +} + +Listener::Listener(Channel& _channel, TestOptions& _options) : channel(_channel), options(_options), name(options.clientid), topic("iop.control." + name) +{} + +void Listener::registerTest(string name, TestCase* test) +{ + tests.insert(name, test); +} + +void Listener::bindAndConsume() +{ + Queue control(name, true); + channel.declareQueue(control); + qpid::framing::FieldTable bindArgs; + //replace these separate binds with a wildcard once that is supported on java broker + channel.bind(Exchange::STANDARD_TOPIC_EXCHANGE, control, "iop.control", bindArgs); + channel.bind(Exchange::STANDARD_TOPIC_EXCHANGE, control, topic, bindArgs); + + string tag; + channel.consume(control, tag, this); +} + +void Listener::sendSimpleResponse(const string& type, Message& request) +{ + Message response; + response.getHeaders().setString("CONTROL_TYPE", type); + response.getHeaders().setString("CLIENT_NAME", name); + response.getHeaders().setString("CLIENT_PRIVATE_CONTROL_KEY", topic); + response.getMessageProperties().setCorrelationId(request.getMessageProperties().getCorrelationId()); + sendResponse(response, request); +} + +void Listener::sendResponse(Message& response, Message& request) +{ + sendResponse(response, request.getMessageProperties().getReplyTo()); +} + +void Listener::sendResponse(Message& response, ReplyTo replyTo) +{ + string exchange = replyTo.getExchangeName(); + string routingKey = replyTo.getRoutingKey(); + channel.publish(response, exchange, routingKey); +} + +void Listener::received(Message& message) +{ + string type(message.getHeaders().getString("CONTROL_TYPE")); + + if (type == "INVITE") { + string name(message.getHeaders().getString("TEST_NAME")); + if (name.empty() || invite(name)) { + sendSimpleResponse("ENLIST", message); + } else { + cout << "Can't take part in '" << name << "'" << endl; + } + } else if (type == "ASSIGN_ROLE") { + test->assign(message.getHeaders().getString("ROLE"), message.getHeaders(), options); + sendSimpleResponse("ACCEPT_ROLE", message); + } else if (type == "START") { + reportTo = message.getMessageProperties().getReplyTo(); + reportCorrelator = message.getMessageProperties().getCorrelationId(); + runner = auto_ptr<Thread>(new Thread(this)); + } else if (type == "STATUS_REQUEST") { + reportTo = message.getMessageProperties().getReplyTo(); + reportCorrelator = message.getMessageProperties().getCorrelationId(); + test->stop(); + sendReport(); + } else if (type == "TERMINATE") { + if (test) test->stop(); + shutdown(); + } else { + cerr <<"ERROR!: Received unknown control message: " << type << endl; + shutdown(); + } +} + +void Listener::shutdown() +{ + channel.close(); +} + +bool Listener::invite(const string& name) +{ + TestMap::iterator i = tests.find(name); + test = (i != tests.end()) ? qpid::ptr_map::get_pointer(i) : 0; + return test; +} + +void Listener::run() +{ + //NB: this method will be called in its own thread + //start test and when start returns... + test->start(); + sendReport(); +} + +void Listener::sendReport() +{ + Message report; + report.getHeaders().setString("CONTROL_TYPE", "REPORT"); + test->report(report); + report.getMessageProperties().setCorrelationId(reportCorrelator); + sendResponse(report, reportTo); +} + +string parse_next_word(const string& input, const string& delims, string::size_type& position) +{ + string::size_type start = input.find_first_not_of(delims, position); + if (start == string::npos) { + return ""; + } else { + string::size_type end = input.find_first_of(delims, start); + if (end == string::npos) { + end = input.length(); + } + position = end; + return input.substr(start, end - start); + } +} diff --git a/qpid/cpp/src/tests/latencytest.cpp b/qpid/cpp/src/tests/latencytest.cpp new file mode 100644 index 0000000000..2b44a5477a --- /dev/null +++ b/qpid/cpp/src/tests/latencytest.cpp @@ -0,0 +1,372 @@ +/* + * + * 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. + * + */ + + +#include <algorithm> +#include <iostream> +#include <memory> +#include <sstream> +#include <vector> +#include <unistd.h> + +#include "TestOptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/Session.h" +#include "qpid/client/SubscriptionManager.h" + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::sys; +using std::string; + +typedef std::vector<std::string> StringSet; + +struct Args : public qpid::TestOptions { + uint size; + uint count; + uint rate; + uint reportFrequency; + uint queues; + uint prefetch; + uint ack; + bool durable; + string base; + + Args() : size(256), count(1000), rate(0), reportFrequency(1), queues(1), + prefetch(100), ack(0), + durable(false), base("latency-test") + { + addOptions() + + ("size", optValue(size, "N"), "message size") + ("queues", optValue(queues, "N"), "number of queues") + ("count", optValue(count, "N"), "number of messages to send") + ("rate", optValue(rate, "N"), "target message rate (causes count to be ignored)") + ("report-frequency", optValue(reportFrequency, "N"), + "number of seconds to wait between reports (ignored unless rate specified)") + ("prefetch", optValue(prefetch, "N"), "prefetch count (0 implies no flow control, and no acking)") + ("ack", optValue(ack, "N"), "Ack frequency in messages (defaults to half the prefetch value)") + ("durable", optValue(durable, "yes|no"), "use durable messages") + ("queue-base-name", optValue(base, "<name>"), "base name for queues"); + } +}; + +const std::string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + +Args opts; + +uint64_t current_time() +{ + Duration t(now()); + return t; +} + +struct Stats +{ + Mutex lock; + uint count; + double minLatency; + double maxLatency; + double totalLatency; + + Stats(); + void update(double l); + void print(); + void reset(); +}; + +class Client : public Runnable +{ +protected: + Connection connection; + Session session; + Thread thread; + string queue; + +public: + Client(const string& q); + virtual ~Client() {} + + void start(); + void join(); + void run(); + virtual void test() = 0; +}; + +class Receiver : public Client, public MessageListener +{ + SubscriptionManager mgr; + uint count; + Stats& stats; + +public: + Receiver(const string& queue, Stats& stats); + void test(); + void received(Message& msg); + Stats getStats(); +}; + + +class Sender : public Client +{ + string generateData(uint size); + void sendByRate(); + void sendByCount(); +public: + Sender(const string& queue); + void test(); +}; + + +class Test +{ + const string queue; + Stats stats; + Receiver receiver; + Sender sender; + AbsTime begin; + +public: + Test(const string& q) : queue(q), receiver(queue, stats), sender(queue), begin(now()) {} + void start(); + void join(); + void report(); +}; + + +Client::Client(const string& q) : queue(q) +{ + opts.open(connection); + session = connection.newSession(ASYNC); +} + +void Client::start() +{ + thread = Thread(this); +} + +void Client::join() +{ + thread.join(); +} + +void Client::run() +{ + try{ + test(); + session.close(); + connection.close(); + } catch(const std::exception& e) { + std::cout << "Error in receiver: " << e.what() << std::endl; + } +} + +Receiver::Receiver(const string& q, Stats& s) : Client(q), mgr(session), count(0), stats(s) +{ + session.queueDeclare(arg::queue=queue, arg::durable=opts.durable, arg::autoDelete=true); + uint msgCount = session.queueQuery(arg::queue=queue).get().getMessageCount(); + if (msgCount) { + std::cout << "Warning: found " << msgCount << " msgs on " << queue << ". Purging..." << std::endl; + session.queuePurge(arg::queue=queue); + } + if (opts.prefetch) { + mgr.setAckPolicy(AckPolicy(opts.ack ? opts.ack : (opts.prefetch / 2))); + mgr.setFlowControl(opts.prefetch, SubscriptionManager::UNLIMITED, true); + } else { + mgr.setConfirmMode(false); + mgr.setFlowControl(SubscriptionManager::UNLIMITED, SubscriptionManager::UNLIMITED, false); + } + mgr.subscribe(*this, queue); +} + +void Receiver::test() +{ + mgr.run(); + mgr.cancel(queue); +} + +void Receiver::received(Message& msg) +{ + ++count; + uint64_t sentAt = msg.getDeliveryProperties().getTimestamp(); + //uint64_t sentAt = msg.getHeaders().getTimestamp("sent-at");// TODO: add support for uint64_t as a field table type + uint64_t receivedAt = current_time(); + + stats.update(((double) (receivedAt - sentAt)) / TIME_MSEC); + + if (!opts.rate && count >= opts.count) { + mgr.stop(); + } +} + +void Stats::update(double latency) +{ + Mutex::ScopedLock l(lock); + count++; + if (minLatency == 0 || minLatency > latency) minLatency = latency; + if (maxLatency == 0 || maxLatency < latency) maxLatency = latency; + totalLatency += latency; +} + +Stats::Stats() : count(0), minLatency(0), maxLatency(0), totalLatency(0) {} + +void Stats::print() +{ + Mutex::ScopedLock l(lock); + std::cout << "Latency(ms): min=" << minLatency << ", max=" << maxLatency << ", avg=" << (totalLatency / count); +} + +void Stats::reset() +{ + Mutex::ScopedLock l(lock); + count = 0; + totalLatency = maxLatency = minLatency = 0; +} + +Sender::Sender(const string& q) : Client(q) {} + +void Sender::test() +{ + if (opts.rate) sendByRate(); + else sendByCount(); +} + +void Sender::sendByCount() +{ + Message msg(generateData(opts.size), queue); + if (opts.durable) { + msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); + } + + Completion c; + for (uint i = 0; i < opts.count; i++) { + uint64_t sentAt(current_time()); + msg.getDeliveryProperties().setTimestamp(sentAt); + //msg.getHeaders().setTimestamp("sent-at", sentAt);//TODO add support for uint64_t to field tables + c = session.messageTransfer(arg::content=msg); + } + c.sync(); +} + +void Sender::sendByRate() +{ + Message msg(generateData(opts.size), queue); + if (opts.durable) { + msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); + } + + //calculate metrics required for target rate + uint msgsPerMsec = opts.rate / 1000; + + while (true) { + uint64_t start(current_time()); + for (uint i = 0; i < msgsPerMsec; i++) { + uint64_t sentAt(current_time()); + msg.getDeliveryProperties().setTimestamp(sentAt); + //msg.getHeaders().setTimestamp("sent-at", sentAt);//TODO add support for uint64_t to field tables + session.messageTransfer(arg::content=msg); + } + uint64_t timeTaken = (current_time() - start) / TIME_USEC; + if (timeTaken < 1000) { + usleep(1000 - timeTaken); + } else if (timeTaken > 1000) { + double timeMsecs = (double) timeTaken / 1000; + std::cout << "Could not achieve desired rate. Sent " << msgsPerMsec << " in " + << (timeMsecs) << "ms (" << ((msgsPerMsec * 1000 * 1000) / timeTaken) << " msgs/s)" << std::endl; + } + } +} + +string Sender::generateData(uint size) +{ + if (size < chars.length()) { + return chars.substr(0, size); + } + std::string data; + for (uint i = 0; i < (size / chars.length()); i++) { + data += chars; + } + data += chars.substr(0, size % chars.length()); + return data; +} + + +void Test::start() +{ + receiver.start(); + begin = AbsTime(now()); + sender.start(); +} + +void Test::join() +{ + sender.join(); + receiver.join(); + AbsTime end = now(); + Duration time(begin, end); + double msecs(time / TIME_MSEC); + std::cout << "Sent " << opts.count << " msgs through " << queue + << " in " << msecs << "ms (" << (opts.count * 1000 / msecs) << " msgs/s) "; + stats.print(); + std::cout << std::endl; +} + +void Test::report() +{ + stats.print(); + std::cout << std::endl; + stats.reset(); +} + +int main(int argc, char** argv) +{ + try { + opts.parse(argc, argv); + boost::ptr_vector<Test> tests(opts.queues); + for (uint i = 0; i < opts.queues; i++) { + std::ostringstream out; + out << opts.base << "-" << (i+1); + tests.push_back(new Test(out.str())); + } + for (boost::ptr_vector<Test>::iterator i = tests.begin(); i != tests.end(); i++) { + i->start(); + } + if (opts.rate) { + while (true) { + usleep(opts.reportFrequency * 1000 * 1000); + //print latency report: + for (boost::ptr_vector<Test>::iterator i = tests.begin(); i != tests.end(); i++) { + i->report(); + } + } + } else { + for (boost::ptr_vector<Test>::iterator i = tests.begin(); i != tests.end(); i++) { + i->join(); + } + } + + return 0; + } catch(const std::exception& e) { + std::cout << e.what() << std::endl; + } + return 1; +} diff --git a/qpid/cpp/src/tests/logging.cpp b/qpid/cpp/src/tests/logging.cpp new file mode 100644 index 0000000000..718ab01d9a --- /dev/null +++ b/qpid/cpp/src/tests/logging.cpp @@ -0,0 +1,384 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include "test_tools.h" +#include "qpid/log/Logger.h" +#include "qpid/log/Options.h" +#include "qpid/memory.h" +#include "qpid/Options.h" + +#include <boost/test/floating_point_comparison.hpp> +#include <boost/format.hpp> +#include "unit_test.h" + +#include <exception> +#include <fstream> +#include <time.h> + + +QPID_AUTO_TEST_SUITE(loggingTestSuite) + +using namespace std; +using namespace boost; +using namespace qpid::log; + +BOOST_AUTO_TEST_CASE(testStatementInit) { + Statement s=QPID_LOG_STATEMENT_INIT(debug); int line=__LINE__; + BOOST_CHECK(!s.enabled); + BOOST_CHECK_EQUAL(string(__FILE__), s.file); + BOOST_CHECK_EQUAL(line, s.line); + BOOST_CHECK_EQUAL(debug, s.level); +} + + +BOOST_AUTO_TEST_CASE(testSelector_enable) { + Selector s; + // Simple enable + s.enable(debug,"foo"); + BOOST_CHECK(s.isEnabled(debug,"foo")); + BOOST_CHECK(!s.isEnabled(error,"foo")); + BOOST_CHECK(!s.isEnabled(error,"bar")); + + // Substring match + BOOST_CHECK(s.isEnabled(debug, "bazfoobar")); + BOOST_CHECK(!s.isEnabled(debug, "bazbar")); + + // Different levels for different substrings. + s.enable(info, "bar"); + BOOST_CHECK(s.isEnabled(debug, "foobar")); + BOOST_CHECK(s.isEnabled(info, "foobar")); + BOOST_CHECK(!s.isEnabled(debug, "bar")); + BOOST_CHECK(!s.isEnabled(info, "foo")); + + // Enable-strings + s.enable("notice:blob"); + BOOST_CHECK(s.isEnabled(notice, "blob")); + s.enable("error+:oops"); + BOOST_CHECK(s.isEnabled(error, "oops")); + BOOST_CHECK(s.isEnabled(critical, "oops")); +} + +BOOST_AUTO_TEST_CASE(testStatementEnabled) { + // Verify that the singleton enables and disables static + // log statements. + Logger& l = Logger::instance(); + l.select(Selector(debug)); + static Statement s=QPID_LOG_STATEMENT_INIT(debug); + BOOST_CHECK(!s.enabled); + static Statement::Initializer init(s); + BOOST_CHECK(s.enabled); + + static Statement s2=QPID_LOG_STATEMENT_INIT(warning); + static Statement::Initializer init2(s2); + BOOST_CHECK(!s2.enabled); + + l.select(Selector(warning)); + BOOST_CHECK(!s.enabled); + BOOST_CHECK(s2.enabled); +} + +struct TestOutput : public Logger::Output { + vector<string> msg; + vector<Statement> stmt; + + TestOutput(Logger& l) { + l.output(std::auto_ptr<Logger::Output>(this)); + } + + void log(const Statement& s, const string& m) { + msg.push_back(m); + stmt.push_back(s); + } + string last() { return msg.back(); } +}; + +using boost::assign::list_of; + +BOOST_AUTO_TEST_CASE(testLoggerOutput) { + Logger l; + l.clear(); + l.select(Selector(debug)); + Statement s=QPID_LOG_STATEMENT_INIT(debug); + + TestOutput* out=new TestOutput(l); + + // Verify message is output. + l.log(s, "foo"); + vector<string> expect=list_of("foo\n"); + BOOST_CHECK_EQUAL(expect, out->msg); + + // Verify multiple outputs + TestOutput* out2=new TestOutput(l); + l.log(Statement(), "baz"); + expect.push_back("baz\n"); + BOOST_CHECK_EQUAL(expect, out->msg); + expect.erase(expect.begin()); + BOOST_CHECK_EQUAL(expect, out2->msg); +} + +BOOST_AUTO_TEST_CASE(testMacro) { + Logger& l=Logger::instance(); + l.clear(); + l.select(Selector(info)); + TestOutput* out=new TestOutput(l); + QPID_LOG(info, "foo"); + vector<string> expect=list_of("foo\n"); + BOOST_CHECK_EQUAL(expect, out->msg); + BOOST_CHECK_EQUAL(__FILE__, out->stmt.front().file); + + // Not enabled: + QPID_LOG(debug, "bar"); + BOOST_CHECK_EQUAL(expect, out->msg); + + QPID_LOG(info, 42 << " bingo"); + expect.push_back("42 bingo\n"); + BOOST_CHECK_EQUAL(expect, out->msg); +} + +BOOST_AUTO_TEST_CASE(testLoggerFormat) { + Logger& l = Logger::instance(); + l.select(Selector(critical)); + TestOutput* out=new TestOutput(l); + + l.format(Logger::FILE); + QPID_LOG(critical, "foo"); + BOOST_CHECK_EQUAL(out->last(), string(__FILE__)+": foo\n"); + + l.format(Logger::FILE|Logger::LINE); + QPID_LOG(critical, "foo"); + BOOST_CHECK_REGEX(string(__FILE__)+":\\d+: foo\n", out->last()); + + l.format(Logger::FUNCTION); + QPID_LOG(critical, "foo"); + + l.format(Logger::LEVEL); + QPID_LOG(critical, "foo"); + BOOST_CHECK_EQUAL("critical foo\n", out->last()); + + l.format(~0); // Everything + QPID_LOG(critical, "foo"); + string re=".* critical -?\\[[0-9a-f]*] "+string(__FILE__)+":\\d+:void .*testLoggerFormat.*\\(\\): foo\n"; + BOOST_CHECK_REGEX(re, out->last()); +} + +BOOST_AUTO_TEST_CASE(testOstreamOutput) { + Logger& l=Logger::instance(); + l.clear(); + l.select(Selector(error)); + ostringstream os; + l.output(os); + QPID_LOG(error, "foo"); + QPID_LOG(error, "bar"); + QPID_LOG(error, "baz"); + BOOST_CHECK_EQUAL("foo\nbar\nbaz\n", os.str()); +} + +#if 0 // This test requires manual intervention. Normally disabled. +BOOST_AUTO_TEST_CASE(testSyslogOutput) { + Logger& l=Logger::instance(); + l.clear(); + l.select(Selector(info)); + l.syslog("qpid_test"); + QPID_LOG(info, "Testing QPID"); + BOOST_ERROR("Manually verify that /var/log/messages contains a recent line 'Testing QPID'"); +} +#endif // 0 + +int count() { + static int n = 0; + return n++; +} + +int loggedCount() { + static int n = 0; + QPID_LOG(debug, "counting: " << n); + return n++; +} + + +using namespace qpid::sys; + +// Measure CPU time. +clock_t timeLoop(int times, int (*fp)()) { + clock_t start=clock(); + while (times-- > 0) + (*fp)(); + return clock() - start; +} + +// Overhead test disabled because it consumes a ton of CPU and takes +// forever under valgrind. Not friendly for regular test runs. +// +#if 0 +BOOST_AUTO_TEST_CASE(testOverhead) { + // Ensure that the ratio of CPU time for an incrementing loop + // with and without disabled log statements is in acceptable limits. + // + int times=100000000; + clock_t noLog=timeLoop(times, count); + clock_t withLog=timeLoop(times, loggedCount); + double ratio=double(withLog)/double(noLog); + + // NB: in initial tests the ratio was consistently below 1.5, + // 2.5 is reasonable and should avoid spurios failures + // due to machine load. + // + BOOST_CHECK_SMALL(ratio, 2.5); +} +#endif // 0 + +Statement statement( + Level level, const char* file="", int line=0, const char* fn=0) +{ + Statement s={0, file, line, fn, level}; + return s; +} + + +#define ARGC(argv) (sizeof(argv)/sizeof(char*)) + +BOOST_AUTO_TEST_CASE(testOptionsParse) { + const char* argv[]={ + 0, + "--log-enable", "error+:foo", + "--log-enable", "debug:bar", + "--log-enable", "info", + "--log-output", "x", + "--log-output", "y", + "--log-level", "yes", + "--log-source", "1", + "--log-thread", "true", + "--log-function", "YES" + }; + qpid::log::Options opts; + opts.parse(ARGC(argv), const_cast<char**>(argv)); + vector<string> expect=list_of("error+:foo")("debug:bar")("info"); + BOOST_CHECK_EQUAL(expect, opts.selectors); + expect=list_of("x")("y"); + BOOST_CHECK_EQUAL(expect, opts.outputs); + BOOST_CHECK(opts.level); + BOOST_CHECK(opts.source); + BOOST_CHECK(opts.function); + BOOST_CHECK(opts.thread); +} + +BOOST_AUTO_TEST_CASE(testOptionsDefault) { + Options opts; + vector<string> expect=list_of("stderr"); + BOOST_CHECK_EQUAL(expect, opts.outputs); + expect=list_of("error+"); + BOOST_CHECK_EQUAL(expect, opts.selectors); + BOOST_CHECK(opts.time && opts.level); + BOOST_CHECK(!(opts.source || opts.function || opts.thread)); +} + +BOOST_AUTO_TEST_CASE(testSelectorFromOptions) { + const char* argv[]={ + 0, + "--log-enable", "error+:foo", + "--log-enable", "debug:bar", + "--log-enable", "info" + }; + qpid::log::Options opts; + opts.parse(ARGC(argv), const_cast<char**>(argv)); + vector<string> expect=list_of("error+:foo")("debug:bar")("info"); + BOOST_CHECK_EQUAL(expect, opts.selectors); + Selector s(opts); + BOOST_CHECK(!s.isEnabled(warning, "x")); + BOOST_CHECK(!s.isEnabled(debug, "x")); + BOOST_CHECK(s.isEnabled(debug, "bar")); + BOOST_CHECK(s.isEnabled(error, "foo")); + BOOST_CHECK(s.isEnabled(critical, "foo")); +} + +BOOST_AUTO_TEST_CASE(testOptionsFormat) { + Logger l; + { + Options opts; + BOOST_CHECK_EQUAL(Logger::TIME|Logger::LEVEL, l.format(opts)); + const char* argv[]={ + 0, + "--log-time", "no", + "--log-level", "no", + "--log-source", "1", + "--log-thread", "1" + }; + opts.parse(ARGC(argv), const_cast<char**>(argv)); + BOOST_CHECK_EQUAL( + Logger::FILE|Logger::LINE|Logger::THREAD, l.format(opts)); + } + { + Options opts; // Clear. + const char* argv[]={ + 0, + "--log-level", "no", + "--log-thread", "true", + "--log-function", "YES", + "--log-time", "YES" + }; + opts.parse(ARGC(argv), const_cast<char**>(argv)); + BOOST_CHECK_EQUAL( + Logger::THREAD|Logger::FUNCTION|Logger::TIME, + l.format(opts)); + } +} + +BOOST_AUTO_TEST_CASE(testLoggerConfigure) { + Logger& l=Logger::instance(); + l.clear(); + Options opts; + const char* argv[]={ + 0, + "--log-time", "no", + "--log-source", "yes", + "--log-output", "logging.tmp", + "--log-enable", "critical" + }; + opts.parse(ARGC(argv), const_cast<char**>(argv)); + l.configure(opts, "test"); + QPID_LOG(critical, "foo"); int srcline=__LINE__; + ifstream log("logging.tmp"); + string line; + getline(log, line); + string expect=(format("critical %s:%d: foo")%__FILE__%srcline).str(); + BOOST_CHECK_EQUAL(expect, line); + log.close(); + unlink("logging.tmp"); +} + +BOOST_AUTO_TEST_CASE(testQuoteNonPrintable) { + Logger& l=Logger::instance(); + l.clear(); + Options opts; + opts.outputs.clear(); + opts.outputs.push_back("logging.tmp"); + opts.time=false; + l.configure(opts, "test"); + char s[] = "null\0tab\tspace newline\nret\r\x80\x99\xff"; + string str(s, sizeof(s)); + QPID_LOG(critical, str); + ifstream log("logging.tmp"); + string line; + getline(log, line); + string expect="critical null\\00tab\\09space newline\\0Aret\\0D\\80\\99\\FF\\00"; + BOOST_CHECK_EQUAL(expect, line); + log.close(); + unlink("logging.tmp"); +} + +QPID_AUTO_TEST_SUITE_END() diff --git a/qpid/cpp/src/tests/multiq_perftest b/qpid/cpp/src/tests/multiq_perftest new file mode 100755 index 0000000000..f6644e740c --- /dev/null +++ b/qpid/cpp/src/tests/multiq_perftest @@ -0,0 +1,2 @@ +#!/bin/sh +exec `dirname $0`/run_perftest 10000 --mode shared --qt 16 diff --git a/qpid/cpp/src/tests/perfdist b/qpid/cpp/src/tests/perfdist new file mode 100755 index 0000000000..816d2d99f6 --- /dev/null +++ b/qpid/cpp/src/tests/perfdist @@ -0,0 +1,50 @@ +#!/bin/bash +# +# Distributed perftest. +# Runs perftest clients on multiple hosts using ssh. +# + +set -e +usage() { +cat <<EOF +usage: $0 <perftest-args> -- <client-hosts ...> + +Run perftest with clients running on the listed hosts. Clients are +assigned to hosts publishers first, then subscribers the host list is +used round-robin if there are more clients than hosts. perftest-args should +include a --host <brokerhost> flag. + +Do not pass preftest action flags: --setup, --control, --publish, --subscribe. +The script will pass them to the appropriate client processes. + +Note all perftest args must come before --. + +Error: $* +EOF +exit 1 +} + +collect() { eval $COLLECT=\""\$$COLLECT $*"\"; } +NPUBS=1 +NSUBS=1 +COLLECT=ARGS +while test $# -gt 0; do + case $1 in + --publish|--subscribe|--setup|--control) usage "Don't pass perftest action flags: $1" ;; + --npubs) collect $1 $2; NPUBS=$2; shift 2 ;; + --nsubs) collect $1 $2; NSUBS=$2; shift 2 ;; + --) COLLECT=HOSTS; shift ;; + *) collect $1; shift ;; + esac +done +if [ -z "$HOSTS" ]; then usage "No hosts listed after --"; fi +PATH="`dirname $0`:$PATH" +PERFTEST="`which perftest` $ARGS" || usage "Can't find perftest executable" + +HOSTS=($HOSTS) +start() { ssh ${HOSTS[i % ${#HOSTS[*]}]} $PERFTEST $*& } + +$PERFTEST --setup +for (( i=0 ; i < $NPUBS ; ++i)); do start --publish; done +for (( ; i < $NPUBS+$NSUBS ; ++i)); do start --subscribe; done +$PERFTEST --control diff --git a/qpid/cpp/src/tests/perftest.cpp b/qpid/cpp/src/tests/perftest.cpp new file mode 100644 index 0000000000..b950e432f5 --- /dev/null +++ b/qpid/cpp/src/tests/perftest.cpp @@ -0,0 +1,640 @@ +/* + * + * 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. + * + */ + +#include "TestOptions.h" + +#include "qpid/client/Session.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Completion.h" +#include "qpid/client/Message.h" +#include "qpid/framing/FieldTable.h" +#include "qpid/sys/Time.h" + +#include <boost/lexical_cast.hpp> +#include <boost/bind.hpp> +#include <boost/function.hpp> +#include <boost/ptr_container/ptr_vector.hpp> + +#include <iostream> +#include <sstream> +#include <numeric> +#include <algorithm> +#include <unistd.h> + + +using namespace std; +using namespace qpid; +using namespace client; +using namespace sys; +using boost::lexical_cast; +using boost::bind; + +enum Mode { SHARED, FANOUT, TOPIC }; +const char* modeNames[] = { "shared", "fanout", "topic" }; + +// istream/ostream ops so Options can read/display Mode. +istream& operator>>(istream& in, Mode& mode) { + string s; + in >> s; + int i = find(modeNames, modeNames+3, s) - modeNames; + if (i >= 3) throw Exception("Invalid mode: "+s); + mode = Mode(i); + return in; +} + +ostream& operator<<(ostream& out, Mode mode) { + return out << modeNames[mode]; +} + + +struct Opts : public TestOptions { + + // Actions + bool setup, control, publish, subscribe; + + // Queue policy + uint32_t queueMaxCount; + uint64_t queueMaxSize; + + // Publisher + size_t pubs; + size_t count ; + size_t size; + bool confirm; + bool durable; + bool uniqueData; + + // Subscriber + size_t subs; + size_t ack; + + // General + size_t qt; + size_t iterations; + Mode mode; + bool summary; + uint32_t intervalSub; + uint32_t intervalPub; + + static const std::string helpText; + + Opts() : + TestOptions(helpText), + setup(false), control(false), publish(false), subscribe(false), + pubs(1), count(500000), size(1024), confirm(true), durable(false), uniqueData(false), + subs(1), ack(0), + qt(1), iterations(1), mode(SHARED), summary(false), + intervalSub(0), intervalPub(0) + { + addOptions() + ("setup", optValue(setup), "Create shared queues.") + ("control", optValue(control), "Run test, print report.") + ("publish", optValue(publish), "Publish messages.") + ("subscribe", optValue(subscribe), "Subscribe for messages.") + + ("mode", optValue(mode, "shared|fanout|topic"), "Test mode." + "\nshared: --qt queues, --npubs publishers and --nsubs subscribers per queue.\n" + "\nfanout: --npubs publishers, --nsubs subscribers, fanout exchange." + "\ntopic: --qt topics, --npubs publishers and --nsubs subscribers per topic.\n") + + ("npubs", optValue(pubs, "N"), "Create N publishers.") + ("count", optValue(count, "N"), "Each publisher sends N messages.") + ("size", optValue(size, "BYTES"), "Size of messages in bytes.") + ("pub-confirm", optValue(confirm, "yes|no"), "Publisher use confirm-mode.") + ("durable", optValue(durable, "yes|no"), "Publish messages as durable.") + ("unique-data", optValue(uniqueData, "yes|no"), "Make data for each message unique.") + + ("nsubs", optValue(subs, "N"), "Create N subscribers.") + ("sub-ack", optValue(ack, "N"), "N>0: Subscriber acks batches of N.\n" + "N==0: Subscriber uses unconfirmed mode") + + ("qt", optValue(qt, "N"), "Create N queues or topics.") + ("iterations", optValue(iterations, "N"), "Desired number of iterations of the test.") + ("summary,s", optValue(summary), "Summary output: pubs/sec subs/sec transfers/sec Mbytes/sec") + + ("queue_max_count", optValue(queueMaxCount, "N"), "queue policy: count to trigger 'flow to disk'") + ("queue_max_size", optValue(queueMaxSize, "N"), "queue policy: accumulated size to trigger 'flow to disk'") + + ("interval_sub", optValue(intervalSub, "ms"), ">=0 delay between msg consume") + ("interval_pub", optValue(intervalPub, "ms"), ">=0 delay between msg publish"); + } + + // Computed values + size_t totalPubs; + size_t totalSubs; + size_t transfers; + size_t subQuota; + + void parse(int argc, char** argv) { + TestOptions::parse(argc, argv); + switch (mode) { + case SHARED: + if (count % subs) { + count += subs - (count % subs); + cout << "WARNING: Adjusted --count to " << count + << " the nearest multiple of --nsubs" << endl; + } + totalPubs = pubs*qt; + totalSubs = subs*qt; + subQuota = (pubs*count)/subs; + break; + case FANOUT: + if (qt != 1) cerr << "WARNING: Fanout mode, ignoring --qt=" + << qt << endl; + qt=1; + totalPubs = pubs; + totalSubs = subs; + subQuota = totalPubs*count; + break; + case TOPIC: + totalPubs = pubs*qt; + totalSubs = subs*qt; + subQuota = pubs*count; + break; + } + transfers=(totalPubs*count) + (totalSubs*subQuota); + } +}; + +const std::string Opts::helpText= +"There are two ways to use perftest: single process or multi-process.\n\n" +"If none of the --setup, --publish, --subscribe or --control options\n" +"are given perftest will run a single-process test.\n" +"For a multi-process test first run:\n" +" perftest --setup <other options>\n" +"and wait for it to complete. The remaining process should run concurrently::\n" +"Run --npubs times: perftest --publish <other options>\n" +"Run --nsubs times: perftest --subscribe <other options>\n" +"Run once: perftest --control <other options>\n" +"Note the <other options> must be identical for all processes.\n"; + +Opts opts; + +struct Client : public Runnable { + Connection connection; + Session session; + Thread thread; + + Client() { + opts.open(connection); + session = connection.newSession(ASYNC); + } + + ~Client() { + session.close(); + connection.close(); + } +}; + +struct Setup : public Client { + + void queueInit(string name, bool durable=false, const framing::FieldTable& settings=framing::FieldTable()) { + session.queueDeclare(arg::queue=name, arg::durable=durable, arg::arguments=settings); + session.queuePurge(arg::queue=name).sync(); + } + + void run() { + queueInit("pub_start"); + queueInit("pub_done"); + queueInit("sub_ready"); + queueInit("sub_done"); + if (opts.mode==SHARED) { + framing::FieldTable settings;//queue policy settings + settings.setInt("qpid.max_count", opts.queueMaxCount); + settings.setInt("qpid.max_size", opts.queueMaxSize); + for (size_t i = 0; i < opts.qt; ++i) { + ostringstream qname; + qname << "perftest" << i; + queueInit(qname.str(), opts.durable, settings); + } + } + } +}; + +void expect(string actual, string expect) { + if (expect != actual) + throw Exception("Expecting "+expect+" but received "+actual); + +} + +double secs(Duration d) { return double(d)/TIME_SEC; } +double secs(AbsTime start, AbsTime finish) { + return secs(Duration(start,finish)); +} + + +// Collect rates & print stats. +class Stats { + vector<double> values; + double sum; + + public: + Stats() : sum(0) {} + + // Functor to collect rates. + void operator()(const string& data) { + try { + double d=lexical_cast<double>(data); + values.push_back(d); + sum += d; + } catch (const std::exception&) { + throw Exception("Bad report: "+data); + } + } + + double mean() const { + return sum/values.size(); + } + + double stdev() const { + if (values.size() <= 1) return 0; + double avg = mean(); + double ssq = 0; + for (vector<double>::const_iterator i = values.begin(); + i != values.end(); ++i) { + double x=*i; + x -= avg; + ssq += x*x; + } + return sqrt(ssq/(values.size()-1)); + } + + ostream& print(ostream& out) { + ostream_iterator<double> o(out, "\n"); + copy(values.begin(), values.end(), o); + out << "Average: " << mean(); + if (values.size() > 1) + out << " (std.dev. " << stdev() << ")"; + return out << endl; + } +}; + + +// Manage control queues, collect and print reports. +struct Controller : public Client { + + SubscriptionManager subs; + + Controller() : subs(session) {} + + /** Process messages from queue by applying a functor. */ + void process(size_t n, string queue, + boost::function<void (const string&)> msgFn) + { + if (!opts.summary) + cout << "Processing " << n << " messages from " + << queue << " " << flush; + LocalQueue lq; + subs.setFlowControl(n, SubscriptionManager::UNLIMITED, false); + subs.subscribe(lq, queue); + for (size_t i = 0; i < n; ++i) { + if (!opts.summary) cout << "." << flush; + msgFn(lq.pop().getData()); + } + if (!opts.summary) cout << " done." << endl; + } + + void process(size_t n, LocalQueue lq, string queue, + boost::function<void (const string&)> msgFn) + { + session.messageFlow(queue, 0, n); + if (!opts.summary) + cout << "Processing " << n << " messages from " + << queue << " " << flush; + for (size_t i = 0; i < n; ++i) { + if (!opts.summary) cout << "." << flush; + msgFn(lq.pop().getData()); + } + if (!opts.summary) cout << " done." << endl; + } + + void send(size_t n, string queue, string data) { + if (!opts.summary) + cout << "Sending " << data << " " << n << " times to " << queue + << endl; + Message msg(data, queue); + for (size_t i = 0; i < n; ++i) + session.messageTransfer(arg::content=msg); + } + + void run() { // Controller + try { + // Wait for subscribers to be ready. + process(opts.totalSubs, "sub_ready", bind(expect, _1, "ready")); + + LocalQueue pubDone; + LocalQueue subDone; + subs.setFlowControl(0, SubscriptionManager::UNLIMITED, false); + subs.subscribe(pubDone, "pub_done"); + subs.subscribe(subDone, "sub_done"); + + double txrateTotal(0); + double mbytesTotal(0); + double pubRateTotal(0); + double subRateTotal(0); + + for (size_t j = 0; j < opts.iterations; ++j) { + AbsTime start=now(); + send(opts.totalPubs, "pub_start", "start"); // Start publishers + + Stats pubRates; + Stats subRates; + + process(opts.totalPubs, pubDone, "pub_done", boost::ref(pubRates)); + process(opts.totalSubs, subDone, "sub_done", boost::ref(subRates)); + + AbsTime end=now(); + + double time=secs(start, end); + double txrate=opts.transfers/time; + double mbytes=(txrate*opts.size)/(1024*1024); + + if (!opts.summary) { + cout << endl << "Total " << opts.transfers << " transfers of " + << opts.size << " bytes in " + << time << " seconds." << endl; + cout << endl << "Publish transfers/sec: " << endl; + pubRates.print(cout); + cout << endl << "Subscribe transfers/sec: " << endl; + subRates.print(cout); + cout << endl + << "Total transfers/sec: " << txrate << endl + << "Total Mbytes/sec: " << mbytes << endl; + } + else { + cout << pubRates.mean() << "\t" + << subRates.mean() << "\t" + << txrate << "\t" + << mbytes << endl; + } + + txrateTotal += txrate; + mbytesTotal += mbytes; + pubRateTotal += pubRates.mean(); + subRateTotal += subRates.mean(); + } + if (opts.iterations > 1) { + cout << "Averages: "<< endl + << (pubRateTotal / opts.iterations) << "\t" + << (subRateTotal / opts.iterations) << "\t" + << (txrateTotal / opts.iterations) << "\t" + << (mbytesTotal / opts.iterations) << endl; + } + } + catch (const std::exception& e) { + cout << "Controller exception: " << e.what() << endl; + exit(1); + } + } +}; + + +struct PublishThread : public Client { + string destination; + string routingKey; + + PublishThread() {}; + + PublishThread(string key, string dest=string()) { + destination=dest; + routingKey=key; + } + + void run() { // Publisher + Completion completion; + try { + string data; + size_t offset(0); + if (opts.uniqueData) { + offset = 5; + data += "data:";//marker (requested for latency testing tool scripts) + data += string(sizeof(size_t), 'X');//space for seq no + data += string(reinterpret_cast<const char*>(session.getId().data()), session.getId().size()); + if (opts.size > data.size()) { + data += string(opts.size - data.size(), 'X'); + } else if(opts.size < data.size()) { + cout << "WARNING: Increased --size to " << data.size() + << " to honour --unique-data" << endl; + } + } else { + size_t msgSize=max(opts.size, sizeof(size_t)); + data = string(msgSize, 'X'); + } + + Message msg(data, routingKey); + if (opts.durable) + msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); + + + SubscriptionManager subs(session); + LocalQueue lq; + subs.setFlowControl(1, SubscriptionManager::UNLIMITED, true); + subs.subscribe(lq, "pub_start"); + + for (size_t j = 0; j < opts.iterations; ++j) { + expect(lq.pop().getData(), "start"); + AbsTime start=now(); + for (size_t i=0; i<opts.count; i++) { + // Stamp the iteration into the message data, avoid + // any heap allocation. + const_cast<std::string&>(msg.getData()).replace(offset, sizeof(uint32_t), + reinterpret_cast<const char*>(&i), sizeof(uint32_t)); + completion = session.messageTransfer( + arg::destination=destination, + arg::content=msg, + arg::confirmMode=opts.confirm); + if (opts.intervalPub) ::usleep(opts.intervalPub*1000); + } + if (opts.confirm) completion.sync(); + AbsTime end=now(); + double time=secs(start,end); + + // Send result to controller. + Message report(lexical_cast<string>(opts.count/time), "pub_done"); + session.messageTransfer(arg::content=report); + } + session.close(); + } + catch (const std::exception& e) { + cout << "PublishThread exception: " << e.what() << endl; + exit(1); + } + } +}; + +struct SubscribeThread : public Client { + + string queue; + + SubscribeThread() {} + + SubscribeThread(string q) { queue = q; } + + SubscribeThread(string key, string ex) { + queue=session.getId().str(); // Unique name. + session.queueDeclare(arg::queue=queue, + arg::exclusive=true, + arg::autoDelete=true, + arg::durable=opts.durable); + session.queueBind(arg::queue=queue, + arg::exchange=ex, + arg::routingKey=key); + } + + void verify(bool cond, const char* test, uint32_t expect, uint32_t actual) { + if (!cond) { + Message error( + QPID_MSG("Sequence error: expected n" << test << expect << " but got " << actual), + "sub_done"); + session.messageTransfer(arg::content=error); + throw Exception(error.getData()); + } + } + + void run() { // Subscribe + try { + SubscriptionManager subs(session); + LocalQueue lq(AckPolicy(opts.ack)); + subs.setConfirmMode(opts.ack > 0); + subs.setFlowControl(opts.subQuota, SubscriptionManager::UNLIMITED, + false); + subs.subscribe(lq, queue); + // Notify controller we are ready. + session.messageTransfer(arg::content=Message("ready", "sub_ready")); + + + for (size_t j = 0; j < opts.iterations; ++j) { + if (j > 0) { + //need to allocate some more credit + session.messageFlow(queue, 0, opts.subQuota); + } + Message msg; + AbsTime start=now(); + size_t expect=0; + for (size_t i = 0; i < opts.subQuota; ++i) { + msg=lq.pop(); + if (opts.intervalSub) ::usleep(opts.intervalSub*1000); + // TODO aconway 2007-11-23: check message order for. + // multiple publishers. Need an acorray of counters, + // one per publisher and a publisher ID in the + // message. Careful not to introduce a lot of overhead + // here, e.g. no std::map, std::string etc. + // + // For now verify order only for a single publisher. + size_t offset = opts.uniqueData ? 5 /*marker is 'data:'*/ : 0; + size_t n = *reinterpret_cast<const uint32_t*>(msg.getData().data() + offset); + if (opts.pubs == 1) { + if (opts.subs == 1 || opts.mode == FANOUT) verify(n==expect, "==", expect, n); + else verify(n>=expect, ">=", expect, n); + expect = n+1; + } + } + if (opts.ack !=0) + msg.acknowledge(); // Cumulative ack for final batch. + AbsTime end=now(); + + // Report to publisher. + Message result(lexical_cast<string>(opts.subQuota/secs(start,end)), + "sub_done"); + session.messageTransfer(arg::content=result); + } + session.close(); + } + catch (const std::exception& e) { + cout << "SubscribeThread exception: " << e.what() << endl; + exit(1); + } + } +}; + +int main(int argc, char** argv) { + + try { + opts.parse(argc, argv); + + string exchange; + switch (opts.mode) { + case FANOUT: exchange="amq.fanout"; break; + case TOPIC: exchange="amq.topic"; break; + case SHARED: break; + } + + bool singleProcess= + (!opts.setup && !opts.control && !opts.publish && !opts.subscribe); + if (singleProcess) + opts.setup = opts.control = opts.publish = opts.subscribe = true; + + if (opts.setup) Setup().run(); // Set up queues + + boost::ptr_vector<Client> subs(opts.subs); + boost::ptr_vector<Client> pubs(opts.pubs); + + // Start pubs/subs for each queue/topic. + for (size_t i = 0; i < opts.qt; ++i) { + ostringstream key; + key << "perftest" << i; // Queue or topic name. + if (opts.publish) { + size_t n = singleProcess ? opts.pubs : 1; + for (size_t j = 0; j < n; ++j) { + pubs.push_back(new PublishThread(key.str(), exchange)); + pubs.back().thread=Thread(pubs.back()); + } + } + if (opts.subscribe) { + size_t n = singleProcess ? opts.subs : 1; + for (size_t j = 0; j < n; ++j) { + if (opts.mode==SHARED) + subs.push_back(new SubscribeThread(key.str())); + else + subs.push_back(new SubscribeThread(key.str(),exchange)); + subs.back().thread=Thread(subs.back()); + } + } + } + + if (opts.control) Controller().run(); + + + // Wait for started threads. + if (opts.publish) { + for (boost::ptr_vector<Client>::iterator i=pubs.begin(); + i != pubs.end(); + ++i) + i->thread.join(); + } + + + if (opts.subscribe) { + for (boost::ptr_vector<Client>::iterator i=subs.begin(); + i != subs.end(); + ++i) + i->thread.join(); + } + return 0; + } + catch (const std::exception& e) { + cout << endl << e.what() << endl; + } + return 1; +} + + diff --git a/qpid/cpp/src/tests/python_tests b/qpid/cpp/src/tests/python_tests new file mode 100755 index 0000000000..f35cb16480 --- /dev/null +++ b/qpid/cpp/src/tests/python_tests @@ -0,0 +1,20 @@ +#!/bin/sh +# Run the python tests. +QPID_PORT=${QPID_PORT:-5672} +PYTHON_TESTS=${PYTHON_TESTS:-$*} + + +run() { + SPEC=$1 + FAILING=$2 + ./run-tests --skip-self-test -v -s $SPEC -I $FAILING -b localhost:$QPID_PORT $PYTHON_TESTS || { echo "FAIL python tests for $SPEC"; exit 1; } +} + +if test -d ../../../python ; then + cd ../../../python + run 0-10 cpp_failing_0-10.txt + test -z "$QPID_NO_PREVIEW" && run ../specs/amqp.0-10-preview.xml cpp_failing_0-10_preview.txt +else + echo Warning: python tests not found. +fi + diff --git a/qpid/cpp/src/tests/qpid_test_plugin.h b/qpid/cpp/src/tests/qpid_test_plugin.h new file mode 100644 index 0000000000..b2f4a8ffed --- /dev/null +++ b/qpid/cpp/src/tests/qpid_test_plugin.h @@ -0,0 +1,43 @@ +#ifndef _qpid_test_plugin_ +#define _qpid_test_plugin_ + +/* + * + * 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. + * + */ + +/** + * Convenience to include cppunit headers needed by qpid test plugins and + * workaround for warning from superfluous main() declaration + * in cppunit/TestPlugIn.h + */ + +#include <cppunit/TestCase.h> +#include <cppunit/TextTestRunner.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> + +// Redefine CPPUNIT_PLUGIN_IMPLEMENT_MAIN to a dummy typedef to avoid warnings. +// +#if defined(CPPUNIT_HAVE_UNIX_DLL_LOADER) || defined(CPPUNIT_HAVE_UNIX_SHL_LOADER) +#undef CPPUNIT_PLUGIN_IMPLEMENT_MAIN +#define CPPUNIT_PLUGIN_IMPLEMENT_MAIN() typedef char __CppUnitPlugInImplementMainDummyTypeDef +#endif + +#endif /*!_qpid_test_plugin_*/ diff --git a/qpid/cpp/src/tests/quick_perftest b/qpid/cpp/src/tests/quick_perftest new file mode 100755 index 0000000000..676436fdc7 --- /dev/null +++ b/qpid/cpp/src/tests/quick_perftest @@ -0,0 +1,2 @@ +#!/bin/sh +exec `dirname $0`/run_test ./perftest --summary --count 100 diff --git a/qpid/cpp/src/tests/quick_topictest b/qpid/cpp/src/tests/quick_topictest new file mode 100755 index 0000000000..b1e63b9350 --- /dev/null +++ b/qpid/cpp/src/tests/quick_topictest @@ -0,0 +1,9 @@ +#!/bin/sh +# Quick and quiet topic test for make check. +test -z "$srcdir" && srcdir=. +$srcdir/topictest -s2 -m2 -b1 > topictest.log 2>&1 || { + echo $0 FAILED: + cat topictest.log + exit 1 +} +rm topictest.log diff --git a/qpid/cpp/src/tests/run-unit-tests b/qpid/cpp/src/tests/run-unit-tests new file mode 100755 index 0000000000..464ce131f5 --- /dev/null +++ b/qpid/cpp/src/tests/run-unit-tests @@ -0,0 +1,28 @@ +#!/bin/sh +# +# Library names (without path or .so) and CppUnit test paths can be +# specified on the command line or in env var UNIT_TESTS. For example: +# +# Selected test classes: +# ./run-unit-tests ValueTest ClientChannelTest +# +# Individual test method +# ./run-unit-tests ValueTest :ValueTest::testStringValueEquals +# +# Build and run selected tests: +# make check TESTS=run-unit-tests UNIT_TESTS=ClientChannelTest +# + +for u in $* $UNIT_TESTS ; do + case $u in + :*) TEST_ARGS="$TEST_ARGS $u" ;; # A test path. + *) TEST_ARGS="$TEST_ARGS .libs/$u.so" ;; # A test library. + esac +done +test -z "$TEST_ARGS" && TEST_ARGS=".libs/*Test.so" + +test -z "$srcdir" && srcdir=. + +# libdlclose_noop prevents unloading symbols needed for valgrind output. +export LD_PRELOAD=.libs/libdlclose_noop.so +source $srcdir/run_test DllPlugInTester -c -b $TEST_ARGS diff --git a/qpid/cpp/src/tests/run_federation_tests b/qpid/cpp/src/tests/run_federation_tests new file mode 100755 index 0000000000..1f5917af0e --- /dev/null +++ b/qpid/cpp/src/tests/run_federation_tests @@ -0,0 +1,24 @@ +#!/bin/sh +# Run the federation tests. + +trap stop_brokers EXIT + +start_brokers() { + ../qpidd --daemon --port 0 --no-data-dir --auth no > qpidd.port + LOCAL_PORT=`cat qpidd.port` + ../qpidd --daemon --port 0 --no-data-dir --auth no > qpidd.port + REMOTE_PORT=`cat qpidd.port` +} + +stop_brokers() { + ../qpidd -q --port $LOCAL_PORT + ../qpidd -q --port $REMOTE_PORT +} + +if test -d ../../../python ; then + start_brokers + echo "Running federation tests using brokers on ports $LOCAL_PORT $REMOTE_PORT" + export PYTHONPATH=../../../python + ./federation.py -v -s ../../../specs/amqp.0-10-preview.xml -b localhost:$LOCAL_PORT --remote-port $REMOTE_PORT || { echo "FAIL federation tests"; exit 1; } +fi + diff --git a/qpid/cpp/src/tests/run_perftest b/qpid/cpp/src/tests/run_perftest new file mode 100755 index 0000000000..c1e66294c1 --- /dev/null +++ b/qpid/cpp/src/tests/run_perftest @@ -0,0 +1,8 @@ +#!/bin/sh +# Args: count [perftest options...] +# Run a perftest with count multiplied. +# +MULTIPLIER=3 +COUNT=`expr $1 \* $MULTIPLIER` +shift +exec `dirname $0`/run_test ./perftest --summary --count $COUNT "$@" diff --git a/qpid/cpp/src/tests/run_test b/qpid/cpp/src/tests/run_test new file mode 100755 index 0000000000..0f59509dab --- /dev/null +++ b/qpid/cpp/src/tests/run_test @@ -0,0 +1,51 @@ +#!/bin/sh +# +# Set up environment and run a test executable or script. +# +# Output nothing if test passes, show the output if it fails and +# leave output in <test>.log for examination. +# +# If qpidd.port exists run test with QPID_PORT=`cat qpidd.port` +# +# If $VALGRIND if is set run under valgrind. If there are valgrind +# erros show valgrind output, also leave it in <test>.valgrind for +# examination. +# + +source `dirname $0`/vg_check + + +# Export variables from makefile. +export VALGRIND srcdir + +# Export QPID_PORT if qpidd.port exists. +test -f qpidd.port && export QPID_PORT=`cat qpidd.port` + +# Avoid silly libtool error messages if these are not defined +test -z "$LC_ALL" && export LC_ALL= +test -z "$LC_CTYPE" && export LC_CTYPE= +test -z "$LC_COLLATE" && export LC_COLLATE= +test -z "$LC_MESSAGES" && export LC_MESSAGES= + +VG_LOG="$1.vglog" +rm -f $VG_LOG + +if grep -l "^# Generated by .*libtool" "$1" >/dev/null 2>&1; then + # This is a libtool "executable". Valgrind it if VALGRIND specified. + test -n "$VALGRIND" && VALGRIND="$VALGRIND --log-file-exactly=$VG_LOG --" + # Hide output unless there's an error. + libtool --mode=execute $VALGRIND "$@" 2>&1 || ERROR=$? + test -n "$VALGRIND" && vg_check +else + # This is a non-libtool shell script, just execute it. + export VALGRIND srcdir + exec "$@" +fi + +if test -z "$ERROR"; then + # Clean up logs if there was no error. + rm -f $VG_LOG + exit 0 +else + exit $ERROR +fi diff --git a/qpid/cpp/src/tests/shared_perftest b/qpid/cpp/src/tests/shared_perftest new file mode 100755 index 0000000000..212ba22df4 --- /dev/null +++ b/qpid/cpp/src/tests/shared_perftest @@ -0,0 +1,2 @@ +#!/bin/sh +exec `dirname $0`/run_perftest 100000 --mode shared --npubs 16 --nsubs 16 diff --git a/qpid/cpp/src/tests/shlibtest.cpp b/qpid/cpp/src/tests/shlibtest.cpp new file mode 100644 index 0000000000..80320ea7be --- /dev/null +++ b/qpid/cpp/src/tests/shlibtest.cpp @@ -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. + * + */ + +int* loaderData = 0; +extern "C" void callMe(int *i) { loaderData=i; } + +struct OnUnload { ~OnUnload() { *loaderData=42; } }; +OnUnload unloader; // For destructor. + + + diff --git a/qpid/cpp/src/tests/start_broker b/qpid/cpp/src/tests/start_broker new file mode 100755 index 0000000000..7d5cb7d73d --- /dev/null +++ b/qpid/cpp/src/tests/start_broker @@ -0,0 +1,4 @@ +#!/bin/sh +rm -f qpidd.vglog qpidd.log +test -n "$VALGRIND" && VALGRIND="$VALGRIND --log-file-exactly=qpidd.vglog --" +exec libtool --mode=execute $VALGRIND ../qpidd --auth no --daemon --port 0 --log-output qpidd.log "$@" > qpidd.port diff --git a/qpid/cpp/src/tests/start_cluster b/qpid/cpp/src/tests/start_cluster new file mode 100755 index 0000000000..03fb671bdf --- /dev/null +++ b/qpid/cpp/src/tests/start_cluster @@ -0,0 +1,19 @@ +#!/bin/sh +# Start a cluster of brokers on local host. +# Print the cluster's URL. +# + +test -f cluster.ports && { echo "cluster.ports file already exists" ; exit 1; } +test -z "$*" && { echo "Usage: $0 cluster-size [options]"; exit 1; } + +rm -f cluster*.log cluster.ports +SIZE=$1 +shift +OPTS=$* +CLUSTER=`whoami` # Cluster name=user name, avoid clashes. +for (( i=0; i<SIZE; ++i )); do + PORT=`../qpidd --load-module ../.libs/libqpidcluster.so -dp0 --log-output=cluster$i.log --cluster-name $CLUSTER $OPTS` || exit 1 + echo $PORT >> cluster.ports +done + + diff --git a/qpid/cpp/src/tests/stop_broker b/qpid/cpp/src/tests/stop_broker new file mode 100755 index 0000000000..e141ef9841 --- /dev/null +++ b/qpid/cpp/src/tests/stop_broker @@ -0,0 +1,20 @@ +#!/bin/sh +# Stop the broker, check for errors. +# +export QPID_PORT=`cat qpidd.port` +rm -f qpidd.port + +../qpidd --quit || ERROR=$? + +# Check qpidd.log. +grep -a 'warning\|error\|critical' qpidd.log && { + echo "WARNING: Suspicious broker log entries in qpidd.log, above." +} + +# Check valgrind log. +if test -n "$VALGRIND"; then + source `dirname $0`/vg_check + vg_check qpidd.vglog +fi + +exit $ERROR diff --git a/qpid/cpp/src/tests/stop_cluster b/qpid/cpp/src/tests/stop_cluster new file mode 100755 index 0000000000..6afcb527e5 --- /dev/null +++ b/qpid/cpp/src/tests/stop_cluster @@ -0,0 +1,14 @@ +#!/bin/sh +# Stop brokers on ports listed in cluster.ports + + +PORTS=`cat cluster.ports` +for PORT in $PORTS ; do + ../qpidd -qp $PORT || ERROR="$ERROR $PORT" +done +rm -f cluster.ports + +if [ -n "$ERROR" ]; then + echo "Errors stopping brokers on ports: $ERROR" + exit 1 +fi diff --git a/qpid/cpp/src/tests/test_tools.h b/qpid/cpp/src/tests/test_tools.h new file mode 100644 index 0000000000..c5451643be --- /dev/null +++ b/qpid/cpp/src/tests/test_tools.h @@ -0,0 +1,78 @@ +#ifndef TEST_TOOLS_H +#define TEST_TOOLS_H + +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +#include <limits.h> // Include before boost/test headers. + +#include <boost/test/test_tools.hpp> +#include <boost/assign/list_of.hpp> +#include <boost/regex.hpp> +#include <boost/assign/list_of.hpp> +#include <vector> +#include <ostream> + +// Print a sequence +template <class T> std::ostream& seqPrint(std::ostream& o, const T& seq) { + std::copy(seq.begin(), seq.end(), std::ostream_iterator<typename T::value_type>(o, " ")); + return o; +} + +// Compare sequences +template <class T, class U> +bool seqEqual(const T& a, const U& b) { + typename T::const_iterator i = a.begin(); + typename U::const_iterator j = b.begin(); + while (i != a.end() && j != b.end() && *i == *j) { ++i; ++j; } + return (i == a.end()) && (j == b.end()); +} + +// ostream and == operators so we can compare vectors and boost::assign::list_of +// with BOOST_CHECK_EQUALS +namespace std { // In namespace std so boost can find them. + +template <class T> +ostream& operator<<(ostream& o, const vector<T>& v) { return seqPrint(o, v); } + +template <class T> +ostream& operator<<(ostream& o, const boost::assign_detail::generic_list<T>& l) { return seqPrint(o, l); } + +template <class T> +bool operator == (const vector<T>& a, const boost::assign_detail::generic_list<T>& b) { return seqEqual(a, b); } + +template <class T> +bool operator == (const boost::assign_detail::generic_list<T>& b, const vector<T>& a) { return seqEqual(a, b); } +} + +/** NB: order of parameters is regex first, in line with + * CHECK(expected, actual) convention. + */ +inline bool regexPredicate(const std::string& re, const std::string& text) { + return boost::regex_match(text, boost::regex(re)); +} + +/** Check for regular expression match. You must #include <boost/regex.hpp> */ +#define BOOST_CHECK_REGEX(re, text) \ + BOOST_CHECK_PREDICATE(regexPredicate, (re)(text)) + +/** Check if types of two objects (as given by typeinfo::name()) match. */ +#define BOOST_CHECK_TYPEID_EQUAL(a,b) BOOST_CHECK_EQUAL(typeid(a).name(),typeid(b).name()) + +#endif /*!TEST_TOOLS_H*/ + diff --git a/qpid/cpp/src/tests/topic_listener.cpp b/qpid/cpp/src/tests/topic_listener.cpp new file mode 100644 index 0000000000..e5e7d24112 --- /dev/null +++ b/qpid/cpp/src/tests/topic_listener.cpp @@ -0,0 +1,189 @@ +/* + * + * 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 provides one half of a test and example of a pub-sub + * style of interaction. See topic_publisher.cpp for the other half, + * in which the logic for publishing is defined. + * + * This file contains the listener logic. A listener will subscribe to + * a logical 'topic'. It will count the number of messages it receives + * and the time elapsed between the first one and the last one. It + * recognises two types of 'special' message that tell it to (a) send + * a report containing this information, (b) shutdown (i.e. stop + * listening). + */ + +#include "TestOptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/MessageListener.h" +#include "qpid/client/Session.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/sys/Time.h" +#include "qpid/framing/FieldValue.h" +#include <iostream> +#include <sstream> + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::sys; +using namespace qpid::framing; +using namespace std; + +/** + * A message listener implementation in which the runtime logic is + * defined. + */ +class Listener : public MessageListener{ + Session& session; + SubscriptionManager& mgr; + const string responseQueue; + const bool transactional; + bool init; + int count; + AbsTime start; + + void shutdown(); + void report(); +public: + Listener(Session& session, SubscriptionManager& mgr, const string& reponseQueue, bool tx); + virtual void received(Message& msg); +}; + +/** + * A utility class for managing the options passed in. + */ +struct Args : public qpid::TestOptions { + int ack; + bool transactional; + bool durable; + int prefetch; + + Args() : ack(0), transactional(false), durable(false), prefetch(0) { + addOptions() + ("ack", optValue(ack, "MODE"), "Ack frequency in messages (defaults to half the prefetch value)") + ("transactional", optValue(transactional), "Use transactions") + ("durable", optValue(durable), "subscribers should use durable queues") + ("prefetch", optValue(prefetch, "N"), "prefetch count (0 implies no flow control, and no acking)"); + } +}; + + +/** + * The main routine creates a Listener instance and sets it up to + * consume from a private queue bound to the exchange with the + * appropriate topic name. + */ +int main(int argc, char** argv){ + try{ + Args args; + args.parse(argc, argv); + if(args.help) + cout << args << endl; + else { + Connection connection(args.trace); + args.open(connection); + Session session = connection.newSession(ASYNC); + if (args.transactional) { + session.txSelect(); + } + + //declare exchange, queue and bind them: + session.queueDeclare(arg::queue="response"); + std::string control = "control_" + session.getId().str(); + if (args.durable) { + session.queueDeclare(arg::queue=control, arg::durable=true); + } else { + session.queueDeclare(arg::queue=control, arg::exclusive=true, arg::autoDelete=true); + } + session.queueBind(arg::exchange="amq.topic", arg::queue=control, arg::routingKey="topic_control"); + + //set up listener + SubscriptionManager mgr(session); + Listener listener(session, mgr, "response", args.transactional); + if (args.prefetch) { + mgr.setAckPolicy(AckPolicy(args.ack ? args.ack : (args.prefetch / 2))); + mgr.setFlowControl(args.prefetch, SubscriptionManager::UNLIMITED, true); + } else { + mgr.setConfirmMode(false); + mgr.setFlowControl(SubscriptionManager::UNLIMITED, SubscriptionManager::UNLIMITED, false); + } + mgr.subscribe(listener, control); + + cout << "topic_listener: listening..." << endl; + mgr.run(); + if (args.durable) { + session.queueDelete(arg::queue=control); + } + session.close(); + cout << "closing connection" << endl; + connection.close(); + } + return 0; + } catch (const std::exception& error) { + cout << "topic_listener: " << error.what() << endl; + } + return 1; +} + +Listener::Listener(Session& s, SubscriptionManager& m, const string& _responseq, bool tx) : + session(s), mgr(m), responseQueue(_responseq), transactional(tx), init(false), count(0){} + +void Listener::received(Message& message){ + if(!init){ + start = now(); + count = 0; + init = true; + cout << "Batch started." << endl; + } + FieldTable::ValuePtr type(message.getHeaders().get("TYPE")); + + if(!!type && StringValue("TERMINATION_REQUEST") == *type){ + shutdown(); + }else if(!!type && StringValue("REPORT_REQUEST") == *type){ + message.acknowledge();//acknowledge everything upto this point + cout <<"Batch ended, sending report." << endl; + //send a report: + report(); + init = false; + }else if (++count % 1000 == 0){ + cout <<"Received " << count << " messages." << endl; + } +} + +void Listener::shutdown(){ + mgr.stop(); +} + +void Listener::report(){ + AbsTime finish = now(); + Duration time(start, finish); + stringstream reportstr; + reportstr << "Received " << count << " messages in " + << time/TIME_MSEC << " ms."; + Message msg(reportstr.str(), responseQueue); + msg.getHeaders().setString("TYPE", "REPORT"); + session.messageTransfer(arg::content=msg); + if(transactional){ + session.txCommit(); + } +} + diff --git a/qpid/cpp/src/tests/topic_perftest b/qpid/cpp/src/tests/topic_perftest new file mode 100755 index 0000000000..5d317610f3 --- /dev/null +++ b/qpid/cpp/src/tests/topic_perftest @@ -0,0 +1,2 @@ +#!/bin/sh +exec `dirname $0`/run_perftest 10000 --mode topic --qt 16 diff --git a/qpid/cpp/src/tests/topic_publisher.cpp b/qpid/cpp/src/tests/topic_publisher.cpp new file mode 100644 index 0000000000..2271849c35 --- /dev/null +++ b/qpid/cpp/src/tests/topic_publisher.cpp @@ -0,0 +1,206 @@ +/* + * + * 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 provides one half of a test and example of a pub-sub + * style of interaction. See topic_listener.cpp for the other half, in + * which the logic for subscribers is defined. + * + * This file contains the publisher logic. The publisher will send a + * number of messages to the exchange with the appropriate routing key + * for the logical 'topic'. Once it has done this it will then send a + * request that each subscriber report back with the number of message + * it has received and the time that elapsed between receiving the + * first one and receiving the report request. Once the expected + * number of reports are received, it sends out a request that each + * subscriber shutdown. + */ + +#include "TestOptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/MessageListener.h" +#include "qpid/client/Session.h" +#include "qpid/client/SubscriptionManager.h" +#include "qpid/sys/Monitor.h" +#include <unistd.h> +#include "qpid/sys/Time.h" +#include <cstdlib> +#include <iostream> + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::sys; +using namespace std; + +/** + * The publishing logic is defined in this class. It implements + * message listener and can therfore be used to receive messages sent + * back by the subscribers. + */ +class Publisher { + Session& session; + SubscriptionManager mgr; + LocalQueue queue; + const string controlTopic; + const bool transactional; + const bool durable; + + string generateData(int size); + +public: + Publisher(Session& session, const string& controlTopic, bool tx, bool durable); + int64_t publish(int msgs, int listeners, int size); + void terminate(); +}; + +/** + * A utility class for managing the options passed in to the test + */ +struct Args : public TestOptions { + int messages; + int subscribers; + bool transactional; + bool durable; + int batches; + int delay; + int size; + + Args() : messages(1000), subscribers(1), + transactional(false), durable(false), + batches(1), delay(0), size(256) + { + addOptions() + ("messages", optValue(messages, "N"), "how many messages to send") + ("subscribers", optValue(subscribers, "N"), "how many subscribers to expect reports from") + ("transactional", optValue(transactional), "client should use transactions") + ("durable", optValue(durable), "messages should be durable") + ("batches", optValue(batches, "N"), "how many batches to run") + ("delay", optValue(delay, "SECONDS"), "Causes a delay between each batch") + ("size", optValue(size, "BYTES"), "size of the published messages"); + } +}; + +int main(int argc, char** argv) { + try{ + Args args; + args.parse(argc, argv); + if(args.help) + cout << args << endl; + else { + Connection connection(args.trace); + args.open(connection); + Session session = connection.newSession(ASYNC); + if (args.transactional) { + session.txSelect(); + } + + + //declare queue (relying on default binding): + session.queueDeclare(arg::queue="response"); + + Publisher publisher(session, "topic_control", args.transactional, args.durable); + + int batchSize(args.batches); + int64_t max(0); + int64_t min(0); + int64_t sum(0); + for(int i = 0; i < batchSize; i++){ + if(i > 0 && args.delay) sleep(args.delay); + int64_t msecs = + publisher.publish(args.messages, + args.subscribers, + args.size) / TIME_MSEC; + if(!max || msecs > max) max = msecs; + if(!min || msecs < min) min = msecs; + sum += msecs; + cout << "Completed " << (i+1) << " of " << batchSize + << " in " << msecs << "ms" << endl; + } + publisher.terminate(); + int64_t avg = sum / batchSize; + if(batchSize > 1){ + cout << batchSize << " batches completed. avg=" << avg << + ", max=" << max << ", min=" << min << endl; + } + session.close(); + connection.close(); + } + return 0; + }catch(exception& error) { + cout << error.what() << endl; + } + return 1; +} + +Publisher::Publisher(Session& _session, const string& _controlTopic, bool tx, bool d) : + session(_session), mgr(session), controlTopic(_controlTopic), transactional(tx), durable(d) +{ + mgr.subscribe(queue, "response"); +} + +int64_t Publisher::publish(int msgs, int listeners, int size){ + Message msg(generateData(size), controlTopic); + if (durable) { + msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); + } + AbsTime start = now(); + + for(int i = 0; i < msgs; i++){ + session.messageTransfer(arg::content=msg, arg::destination="amq.topic"); + } + //send report request + Message reportRequest("", controlTopic); + reportRequest.getHeaders().setString("TYPE", "REPORT_REQUEST"); + session.messageTransfer(arg::content=reportRequest, arg::destination="amq.topic"); + if(transactional){ + session.txCommit(); + } + //wait for a response from each listener (TODO, could log these) + for (int i = 0; i < listeners; i++) { + Message report = queue.pop(); + } + + if(transactional){ + session.txCommit(); + } + + AbsTime finish = now(); + return Duration(start, finish); +} + +string Publisher::generateData(int size){ + string data; + for(int i = 0; i < size; i++){ + data += ('A' + (i / 26)); + } + return data; +} + +void Publisher::terminate(){ + //send termination request + Message terminationRequest("", controlTopic); + terminationRequest.getHeaders().setString("TYPE", "TERMINATION_REQUEST"); + session.messageTransfer(arg::content=terminationRequest, arg::destination="amq.topic"); + if(transactional){ + session.txCommit(); + } +} + diff --git a/qpid/cpp/src/tests/topictest b/qpid/cpp/src/tests/topictest new file mode 100755 index 0000000000..c36aa319ba --- /dev/null +++ b/qpid/cpp/src/tests/topictest @@ -0,0 +1,40 @@ +#!/bin/bash +# Run the C++ topic test + +# Clean up old log files +rm -f subscriber_*.log + +# Defaults values +SUBSCRIBERS=10 +MESSAGES=2000 +BATCHES=10 + +while getopts "s:m:b:h:" opt ; do + case $opt in + s) SUBSCRIBERS=$OPTARG ;; + m) MESSAGES=$OPTARG ;; + b) BATCHES=$OPTARG ;; + h) HOST=-h$OPTARG ;; + ?) + echo "Usage: %0 [-s <subscribers>] [-m <messages.] [-b <batches>]" + exit 1 + ;; + esac +done + +subscribe() { + echo Start subscriber $1 + LOG="subscriber_$1.log" + ./topic_listener > $LOG 2>&1 && rm -f $LOG +} + +publish() { + ./topic_publisher --messages $MESSAGES --batches $BATCHES --subscribers $SUBSCRIBERS $HOST +} + +for ((i=$SUBSCRIBERS ; i--; )); do + subscribe $i & +done +# FIXME aconway 2007-03-27: Hack around startup race. Fix topic test. +sleep 1 +publish 2>&1 || exit 1 diff --git a/qpid/cpp/src/tests/txtest.cpp b/qpid/cpp/src/tests/txtest.cpp new file mode 100644 index 0000000000..4c5814986c --- /dev/null +++ b/qpid/cpp/src/tests/txtest.cpp @@ -0,0 +1,270 @@ +/* + * + * 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. + * + */ + +#include <algorithm> +#include <iostream> +#include <memory> +#include <sstream> +#include <vector> + +#include "TestOptions.h" +#include "qpid/client/Connection.h" +#include "qpid/client/Message.h" +#include "qpid/client/Session.h" +#include "qpid/client/SubscriptionManager.h" + +using namespace qpid; +using namespace qpid::client; +using namespace qpid::sys; +using std::string; + +typedef std::vector<std::string> StringSet; + +struct Args : public qpid::TestOptions { + bool init, transfer, check;//actions + uint size; + bool durable; + uint queues; + string base; + uint msgsPerTx; + uint txCount; + uint totalMsgCount; + + Args() : init(true), transfer(true), check(true), + size(256), durable(true), queues(2), + base("tx-test"), msgsPerTx(1), txCount(1), totalMsgCount(10) + { + addOptions() + + ("init", optValue(init, "yes|no"), "Declare queues and populate one with the initial set of messages.") + ("transfer", optValue(transfer, "yes|no"), "'Move' messages from one queue to another using transactions to ensure no message loss.") + ("check", optValue(check, "yes|no"), "Check that the initial messages are all still available.") + ("size", optValue(size, "N"), "message size") + ("durable", optValue(durable, "yes|no"), "use durable messages") + ("queues", optValue(queues, "N"), "number of queues") + ("queue-base-name", optValue(base, "<name>"), "base name for queues") + ("messages-per-tx", optValue(msgsPerTx, "N"), "number of messages transferred per transaction") + ("tx-count", optValue(txCount, "N"), "number of transactions per 'agent'") + ("total-messages", optValue(totalMsgCount, "N"), "total number of messages in 'circulation'"); + } +}; + +const std::string chars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); + +std::string generateData(uint size) +{ + if (size < chars.length()) { + return chars.substr(0, size); + } + std::string data; + for (uint i = 0; i < (size / chars.length()); i++) { + data += chars; + } + data += chars.substr(0, size % chars.length()); + return data; +} + +void generateSet(const std::string& base, uint count, StringSet& collection) +{ + for (uint i = 0; i < count; i++) { + std::ostringstream out; + out << base << "-" << (i+1); + collection.push_back(out.str()); + } +} + +Args opts; + +struct Client +{ + Connection connection; + Session session; + + Client() + { + opts.open(connection); + session = connection.newSession(ASYNC); + } + + ~Client() + { + try{ + session.close(); + connection.close(); + } catch(const std::exception& e) { + std::cout << e.what() << std::endl; + } + } +}; + +struct Transfer : public Client, public Runnable +{ + std::string src; + std::string dest; + Thread thread; + + Transfer(const std::string& to, const std::string& from) : src(to), dest(from) {} + + void run() + { + try { + + session.txSelect(); + SubscriptionManager subs(session); + + LocalQueue lq(AckPolicy(0));//manual acking + subs.setFlowControl(opts.msgsPerTx, SubscriptionManager::UNLIMITED, true); + subs.subscribe(lq, src); + + for (uint t = 0; t < opts.txCount; t++) { + Message in; + Message out("", dest); + for (uint m = 0; m < opts.msgsPerTx; m++) { + in = lq.pop(); + out.setData(in.getData()); + out.getMessageProperties().setCorrelationId(in.getMessageProperties().getCorrelationId()); + out.getDeliveryProperties().setDeliveryMode(in.getDeliveryProperties().getDeliveryMode()); + session.messageTransfer(arg::content=out); + } + in.acknowledge(); + session.txCommit(); + } + } catch(const std::exception& e) { + std::cout << "Transfer interrupted: " << e.what() << std::endl; + } + } +}; + +struct Controller : public Client +{ + StringSet ids; + StringSet queues; + + Controller() + { + generateSet(opts.base, opts.queues, queues); + generateSet("msg", opts.totalMsgCount, ids); + } + + void init() + { + //declare queues + for (StringSet::iterator i = queues.begin(); i != queues.end(); i++) { + session.queueDeclare(arg::queue=*i, arg::durable=opts.durable).sync(); + } + + Message msg(generateData(opts.size), *queues.begin()); + if (opts.durable) { + msg.getDeliveryProperties().setDeliveryMode(framing::PERSISTENT); + } + + //publish messages + for (StringSet::iterator i = ids.begin(); i != ids.end(); i++) { + msg.getMessageProperties().setCorrelationId(*i); + session.messageTransfer(arg::content=msg); + } + } + + void transfer() + { + boost::ptr_vector<Transfer> agents(opts.queues); + //launch transfer agents + for (StringSet::iterator i = queues.begin(); i != queues.end(); i++) { + StringSet::iterator next = i + 1; + if (next == queues.end()) next = queues.begin(); + + std::cout << "Transfering from " << *i << " to " << *next << std::endl; + agents.push_back(new Transfer(*i, *next)); + agents.back().thread = Thread(agents.back()); + } + + for (boost::ptr_vector<Transfer>::iterator i = agents.begin(); i != agents.end(); i++) { + i->thread.join(); + } + } + + void check() + { + SubscriptionManager subs(session); + subs.setFlowControl(SubscriptionManager::UNLIMITED, SubscriptionManager::UNLIMITED, false); + subs.setConfirmMode(false); + + StringSet drained; + //drain each queue and verify the correct set of messages are available + for (StringSet::iterator i = queues.begin(); i != queues.end(); i++) { + //subscribe, allocate credit and flush + LocalQueue lq(AckPolicy(0));//manual acking + subs.subscribe(lq, *i, *i); + session.messageFlush(arg::destination=*i).sync(); + + uint count(0); + while (!lq.empty()) { + Message m = lq.pop(); + //add correlation ids of received messages to drained + drained.push_back(m.getMessageProperties().getCorrelationId()); + ++count; + } + std::cout << "Drained " << count << " messages from " << *i << std::endl; + } + + sort(ids.begin(), ids.end()); + sort(drained.begin(), drained.end()); + + //check that drained == ids + StringSet missing; + set_difference(ids.begin(), ids.end(), drained.begin(), drained.end(), back_inserter(missing)); + + StringSet extra; + set_difference(drained.begin(), drained.end(), ids.begin(), ids.end(), back_inserter(extra)); + + if (missing.empty() && extra.empty()) { + std::cout << "All expected messages were retrieved." << std::endl; + } else { + if (!missing.empty()) { + std::cout << "The following ids were missing:" << std::endl; + for (StringSet::iterator i = missing.begin(); i != missing.end(); i++) { + std::cout << " '" << *i << "'" << std::endl; + } + } + if (!extra.empty()) { + std::cout << "The following extra ids were encountered:" << std::endl; + for (StringSet::iterator i = extra.begin(); i != extra.end(); i++) { + std::cout << " '" << *i << "'" << std::endl; + } + } + } + } +}; + +int main(int argc, char** argv) +{ + try { + opts.parse(argc, argv); + Controller controller; + if (opts.init) controller.init(); + if (opts.transfer) controller.transfer(); + if (opts.check) controller.check(); + return 0; + } catch(const std::exception& e) { + std::cout << e.what() << std::endl; + } + return 1; +} diff --git a/qpid/cpp/src/tests/unit_test.cpp b/qpid/cpp/src/tests/unit_test.cpp new file mode 100644 index 0000000000..00c61242e4 --- /dev/null +++ b/qpid/cpp/src/tests/unit_test.cpp @@ -0,0 +1,23 @@ +/* + * + * Copyright (c) 2006 The Apache Software Foundation + * + * 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. + * + */ + +// Defines test_main function to link with actual unit test code. +#define BOOST_AUTO_TEST_MAIN // Boost 1.33 +#define BOOST_TEST_MAIN +#include "unit_test.h" + diff --git a/qpid/cpp/src/tests/unit_test.h b/qpid/cpp/src/tests/unit_test.h new file mode 100644 index 0000000000..58ec20d26c --- /dev/null +++ b/qpid/cpp/src/tests/unit_test.h @@ -0,0 +1,46 @@ +#ifndef QPIPD_TEST_UNIT_TEST_H_ +#define QPIPD_TEST_UNIT_TEST_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. + * + */ + +// Workaround so we can build against boost 1.33 and boost 1.34. +// Remove when we no longer need to support 1.33. +// +#include <boost/version.hpp> +#include <limits.h> // Must be inclued beofre boost/test headers. + +#if (BOOST_VERSION < 103400) + +# include <boost/test/auto_unit_test.hpp> + +# define QPID_AUTO_TEST_SUITE(name) BOOST_AUTO_TEST_SUITE(name); +# define QPID_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END(); + +#else + +# define QPID_AUTO_TEST_SUITE(name) BOOST_AUTO_TEST_SUITE(name) +# define QPID_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END() + +# include <boost/test/unit_test.hpp> +#endif + +#endif /*!QPIPD_TEST_UNIT_TEST_H_*/ diff --git a/qpid/cpp/src/tests/vg_check b/qpid/cpp/src/tests/vg_check new file mode 100644 index 0000000000..e9a691abe6 --- /dev/null +++ b/qpid/cpp/src/tests/vg_check @@ -0,0 +1,23 @@ +# Check for valgrind errors. Sourced by test scripts. + +vg_failed() { + cat $VG_LOG 1>&2 + echo $1 1>&2 + exit 1 +} + +vg_check() +{ + test -z "$1" || VG_LOG=$1 + test -f $VG_LOG || vg_failed Valgrind log file $VG_LOG missing. + # Ensure there is an ERROR SUMMARY line. + grep -E '^==[0-9]+== ERROR SUMMARY:' $VG_LOG > /dev/null || \ + vg_failed "No valgrind ERROR SUMMARY line in $$vg_failed." + # Ensure that the number of errors is 0. + grep -E '^==[0-9]+== ERROR SUMMARY: [^0]' $VG_LOG > /dev/null && \ + vg_failed "Valgrind reported errors in $vg_out; see above." + # Check for leaks. + grep -E '^==[0-9]+== +.* lost: [^0]' $VG_LOG && \ + vg_failed "Found memory leaks (see log file, $VG_LOG); see above." + true +} diff --git a/qpid/cpp/versions b/qpid/cpp/versions new file mode 100755 index 0000000000..2c82e3fd80 --- /dev/null +++ b/qpid/cpp/versions @@ -0,0 +1,12 @@ +#!/bin/sh +# Utility to print out currently installed versions of qpid developer +# dependencies. Assumes that some dependencies are installed with RPM. +# + +for p in pkg-config doxygen help2man autoconf automake libtool ; do + echo `which $p` `$p --version | head -n1` +done + +for r in apr boost boost-devel cppunit cppunit-devel graphviz; do + rpm -q $r +done diff --git a/qpid/cpp/xml/cluster.xml b/qpid/cpp/xml/cluster.xml new file mode 100644 index 0000000000..d1e3293a3e --- /dev/null +++ b/qpid/cpp/xml/cluster.xml @@ -0,0 +1,37 @@ +<?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="99" minor="0" port="5672"> + +<class name = "cluster" index = "201"> + +<doc>Qpid extension class to allow clustered brokers to communicate.</doc> + +<method name = "notify" index="10"> + <doc>Notify the cluster of a members URL</doc> + <!-- No chassis element, this is handled by separte cluster code for now.--> + <field name="url" domain="longstr" /> +</method> + +</class> + +</amqp> diff --git a/qpid/cpp/xml/extra.xml b/qpid/cpp/xml/extra.xml new file mode 100644 index 0000000000..3e2f84e7bd --- /dev/null +++ b/qpid/cpp/xml/extra.xml @@ -0,0 +1,938 @@ +<?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="99" minor="0" port="5672"> + + <domain name="mediumstr" type="mediumstr" label="string with 16bit size field" /> + + <domain name="sequence-set" type="sequence-set" label="ranged set representation"> + <doc> + Set of pairs of RFC-1982 numbers representing a discontinuous range. Each pair represents a + closed interval within the list. + + For example, the set (1,3), (6,6), (8,9) represents the sequence 1,2,3,6,8,9. + </doc> + </domain> + + <domain name="xid010"> + <struct size="short" pack="short"> + <field name="format" domain="long" /> + <field name="global-id" domain="shortstr" /> + <field name="branch-id" domain="shortstr" /> + </struct> + </domain> + + <domain name="delivery-properties-010"> + <struct size="long" pack="short" type="1025"> + <field name="discard-unroutable" domain="bit" label="controls discard of unroutable messages"/> + <field name="immediate" domain="bit" label="Consider message unroutable if it cannot be + processed immediately"/> + <field name="redelivered" domain="bit" label="redelivery flag"/> + <field name="priority" domain="octet" label="message priority, 0 to 9" + required="true"/> + <field name="delivery-mode" domain="octet" label="message persistence requirement" + required="true"/> + <field name="ttl" domain="longlong" label="time to live in ms"/> + <field name="timestamp" domain="longlong" label="message timestamp"/> + <field name="expiration" domain="longlong" label="message expiration time"/> + <field name="exchange" domain="shortstr" label="originating exchange"/> + <field name="routing-key" domain="shortstr" label="message routing key"/> + <field name="resume-id" domain="mediumstr" label="global id for message transfer"/> + <field name="resume-ttl" domain="longlong" label="ttl in ms for interrupted message data"/> + </struct> + </domain> + + <domain name="message-properties-010"> + <struct size="long" pack="short" type="1027"> + <field name="content-length" domain="longlong" label="length of the body segment in bytes"/> + <field name="message-id" domain="uuid" label="application message identifier"/> + <field name="correlation-id" domain="mediumstr" label="application correlation identifier"/> + <field name="reply-to" domain="reply-to" label="destination to reply to"/> + <field name="content-type" domain="shortstr" label="MIME content type"/> + <field name="content-encoding" domain="shortstr" label="MIME content encoding"/> + <field name="user-id" domain="mediumstr" label="creating user id"/> + <field name="app-id" domain="mediumstr" label="creating application id"/> + <field name="application-headers" domain="table" label="application specific headers table"/> + </struct> + </domain> + +<class name = "connection010" index = "1"> + +<method name = "start" index="1"> + <doc>new start method</doc> + <chassis name="client" implement="MUST" /> + + <response name="start-ok" /> + + <field name="server-properties" domain="table" label="server properties"> + <doc>blah, blah</doc> + </field> + + <field name="mechanisms" domain="array" label="available security mechanisms"> + <doc>blah, blah</doc> + </field> + + <field name="locales" domain="array" label="available message locales"> + </field> + +</method> + +<method name = "start-ok" index="2"> + <doc>new start-ok method</doc> + <chassis name="server" implement="MUST" /> + + <field name="client-properties" domain="table" label="server properties"> + <doc>blah, blah</doc> + </field> + + <field name="mechanism" domain="shortstr" label="chosen security mechanism"> + <doc>blah, blah</doc> + </field> + + <field name="response" domain="longstr" label="security response data"> + <doc>blah blah</doc> + </field> + + <field name="locale" domain="shortstr" label="chosen locale"> + <doc>blah, blah</doc> + </field> + +</method> + + <method name="secure" synchronous="1" index="3" 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: connection.secure-ok - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name="secure-ok" synchronous="1" index="4" 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="5" 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="short" 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-min" domain="short" label="desired heartbeat delay"/> + <field name="heartbeat-max" domain="short" label="desired heartbeat delay"/> + </method> + + <!-- - Method: connection.tune-ok - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name="tune-ok" synchronous="1" index="6" + 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" value="channel-max" /> + </field> + + <field name="frame-max" domain="short" 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="7" 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 + --> + <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> + <assert check="regexp" value="^[a-zA-Z0-9/-_]+$" /> + </field> + + <field name="capabilities" domain="array" 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="8" 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="array" /> + </method> + + <method name="redirect" synchronous="1" index="9" 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 = "heartbeat" index="10"> + <doc>new start-ok method</doc> + <chassis name="server" implement="MUST" /> +</method> + + <!-- - Method: connection.close - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name="close" synchronous="1" index="11" 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: connection.close-ok - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> + + <method name="close-ok" synchronous="1" index="12" 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> + + + +<class name = "session010" index = "2"> + +<method name = "attach" index="1"> + + <doc>blah, blah</doc> + <chassis name="client" implement="MUST" /> + <chassis name="server" implement="MUST" /> + + <response name="start-ok" /> + + <field name="name" domain="mediumstr" label="blah"> + <doc>blah, blah</doc> + </field> + + <field name="force" domain="bit" label="blah"> + <doc>blah, blah</doc> + </field> + +</method> + +<method name = "attached" index="2"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <chassis name="client" implement="MUST" /> + + <field name="name" domain="mediumstr" label="blah"> + <doc>blah, blah</doc> + </field> + +</method> + +<method name = "detach" index="3"> + + <doc>blah, blah</doc> + <chassis name="client" implement="MUST" /> + <chassis name="server" implement="MUST" /> + + <response name="start-ok" /> + + <field name="name" domain="mediumstr" label="blah"> + <doc>blah, blah</doc> + </field> + +</method> + +<method name = "detached" index="4"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <chassis name="client" implement="MUST" /> + + <field name="name" domain="mediumstr" label="blah"> + <doc>blah, blah</doc> + </field> + + + <field name="detach-code" domain="octet" label="blah"> + <doc>blah, blah</doc> + </field> + +</method> + +<method name = "request-timeout" index="5"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <chassis name="client" implement="MUST" /> + + <field name="timeout" domain="long" label="blah"> + <doc>blah, blah</doc> + </field> +</method> + +<method name = "timeout" index="6"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <chassis name="client" implement="MUST" /> + + <field name="timeout" domain="long" label="blah"> + <doc>blah, blah</doc> + </field> +</method> + + +<method name = "command-point" index="7"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <chassis name="client" implement="MUST" /> + + <field name="command-id" domain="rfc1982-long" label="blah"> + <doc>blah, blah</doc> + </field> + + + <field name="command-offset" domain="longlong" label="blah"> + <doc>blah, blah</doc> + </field> +</method> + +<method name = "expected" index="8"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <chassis name="client" implement="MUST" /> + + <field name="commands" domain="sequence-set" label="blah"> + <doc>blah, blah</doc> + </field> + + <field name="fragments" domain="array" label="blah"> + <doc>blah, blah</doc> + </field> +</method> + +<method name = "confirmed" index="9"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <chassis name="client" implement="MUST" /> + + <field name="commands" domain="sequence-set" label="blah"> + <doc>blah, blah</doc> + </field> + + <field name="fragments" domain="array" label="blah"> + <doc>blah, blah</doc> + </field> +</method> + +<method name = "completed" index="10"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <chassis name="client" implement="MUST" /> + + <field name="commands" domain="sequence-set" label="blah"> + <doc>blah, blah</doc> + </field> + + <field name="timely-reply" domain="bit" label="blah"> + <doc>blah, blah</doc> + </field> +</method> + +<method name = "known-completed" index="11"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <chassis name="client" implement="MUST" /> + + <field name="commands" domain="sequence-set" label="blah"> + <doc>blah, blah</doc> + </field> +</method> + +<method name = "flush" index="12"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <chassis name="client" implement="MUST" /> + + <field name="expected" domain="bit" label="blah"> + <doc>blah, blah</doc> + </field> + <field name="confirmed" domain="bit" label="blah"> + <doc>blah, blah</doc> + </field> + <field name="completed" domain="bit" label="blah"> + <doc>blah, blah</doc> + </field> +</method> + +<method name = "gap" index="13"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <chassis name="client" implement="MUST" /> + + <field name="commands" domain="sequence-set" label="blah"> + <doc>blah, blah</doc> + </field> +</method> + +</class> + +<class name="execution010" index="3"> + <method name = "sync" index="1"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <chassis name="client" implement="MUST" /> + </method> + <method name = "result" index="2"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <chassis name="client" implement="MUST" /> + <field name="command-id" domain="command-id"/> + <field name="value" domain="long-struct"/> + </method> + <method name = "exception" index="3"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <chassis name="client" implement="MUST" /> + <field name="error-code" domain="short"/> + <field name="command-id" domain="long"/> + <field name="class-code" domain="octet"/> + <field name="command-code" domain="octet"/> + <field name="field-index" domain="octet"/> + <field name="description" domain="mediumstr"/> + <field name="error-info" domain="table"/> + </method> +</class> + +<class name="message010" index="4"> + <doc>blah, blah</doc> + <method name = "transfer" index="1"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <chassis name="client" implement="MUST" /> + <field name="destination" domain="shortstr"/> + <field name="accept-mode" domain="octet"/> + <field name="acquire-mode" domain="octet"/> + </method> + <method name = "accept" index="2"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <chassis name="client" implement="MUST" /> + <field name="commands" domain="sequence-set"/> + </method> + <method name = "reject" index="3"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <chassis name="client" implement="MUST" /> + <field name="commands" domain="sequence-set"/> + <field name="code" domain="short"/> + <field name="text" domain="shortstr"/> + </method> + <method name = "release" index="4"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="commands" domain="sequence-set"/> + <field name="set-redelivered" domain="bit"/> + </method> + <method name = "acquire" index="5"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="transfers" domain="sequence-set"/> + <result> + <struct size="long" type="4"> + <field name="transfers" domain="sequence-set"/> + </struct> + </result> + </method> + + <method name = "subscribe" index="7"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="queue" domain="shortstr"/> + <field name="destination" domain="shortstr"/> + <field name="accept-mode" domain="octet"/> + <field name="acquire-mode" domain="octet"/> + <field name="exclusive" domain="bit"/> + <field name="resume-id" domain="mediumstr"/> + <field name="resume-ttl" domain="longlong"/> + <field name="arguments" domain="table"/> + </method> + <method name = "cancel" index="8"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="destination" domain="shortstr"/> + </method> + <method name = "set-flow-mode" index="9"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="destination" domain="shortstr"/> + <field name="flow-mode" domain="octet"/> + </method> + <method name = "flow" index="10"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="destination" domain="shortstr"/> + <field name="unit" domain="octet"/> + <field name="value" domain="long"/> + </method> + <method name = "flush" index="11"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="destination" domain="shortstr"/> + </method> + <method name = "stop" index="12"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="destination" domain="shortstr"/> + </method> +</class> + +<class name="tx010" index="5"> + <doc>blah, blah</doc> + <method name = "select" index="1"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + </method> + <method name = "commit" index="2"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + </method> + <method name = "rollback" index="3"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + </method> +</class> + +<class name="dtx010" index="6"> + <doc>blah, blah</doc> + <method name = "select" index="1"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + </method> + <method name = "start" index="2"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="xid" domain="xid010"/> + <field name="join" domain="bit"/> + <field name="resume" domain="bit"/> + <result> + <struct size="long" pack="short" type="1"> + <field name="status" domain="short" /> + </struct> + </result> + </method> + <method name = "end" index="3"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="xid" domain="xid010"/> + <field name="fail" domain="bit"/> + <field name="suspend" domain="bit"/> + <result> + <struct size="long" pack="short" type="1"> + <field name="status" domain="short" /> + </struct> + </result> + </method> + <method name = "commit" index="4"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="xid" domain="xid010"/> + <field name="one-phase" domain="bit"/> + <result> + <struct size="long" pack="short" type="1"> + <field name="status" domain="short" /> + </struct> + </result> + </method> + <method name = "forget" index="5"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="xid" domain="xid010"/> + </method> + <method name = "get-timeout" index="6"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="xid" domain="xid010"/> + <result> + <struct size="long" pack="short" type="2"> + <field name="timeout" domain="long" /> + </struct> + </result> + </method> + <method name = "prepare" index="7"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="xid" domain="xid010"/> + <result> + <struct size="long" pack="short" type="1"> + <field name="status" domain="short" /> + </struct> + </result> + </method> + <method name = "recover" index="8"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <result> + <struct size="long" pack="short" type="3"> + <field name="in-doubt" domain="array" /> + </struct> + </result> + </method> + <method name = "rollback" index="9"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="xid" domain="xid010"/> + <result> + <struct size="long" pack="short" type="1"> + <field name="status" domain="short" /> + </struct> + </result> + </method> + <method name = "set-timeout" index="10"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="xid" domain="xid010"/> + <field name="timeout" domain="long"/> + </method> +</class> + +<class name="exchange010" index="7"> + <doc>blah, blah</doc> + <method name = "declare" index="1"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="name" domain="shortstr"/> + <field name="type" domain="shortstr"/> + <field name="alternate-exchange" domain="shortstr"/> + <field name="passive" domain="bit"/> + <field name="durable" domain="bit"/> + <field name="auto-delete" domain="bit"/> + <field name="arguments" domain="table"/> + </method> + <method name = "delete" index="2"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="name" domain="shortstr"/> + <field name="if-unused" domain="bit"/> + </method> + <method name = "query" index="3"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="name" domain="shortstr"/> + <result> + <struct size="long" type="1"> + <field name="type" domain="shortstr"/> + <field name="durable" domain="bit"/> + <field name="not-found" domain="bit"/> + <field name="arguments" domain="table"/> + </struct> + </result> + </method> + <method name = "bind" index="4"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="queue" domain="shortstr"/> + <field name="exchange" domain="shortstr"/> + <field name="binding-key" domain="shortstr"/> + <field name="arguments" domain="table"/> + </method> + <method name = "unbind" index="5"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="queue" domain="shortstr"/> + <field name="exchange" domain="shortstr"/> + <field name="binding-key" domain="shortstr"/> + </method> + <method name = "bound" index="6"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="queue" domain="shortstr"/> + <field name="exchange" domain="shortstr"/> + <field name="binding-key" domain="shortstr"/> + <field name="arguments" domain="table"/> + <result> + <struct size="long" type="2"> + <field name="exchange-not-found" domain="bit"/> + <field name="queue-not-found" domain="bit"/> + <field name="queue-not-matched" domain="bit"/> + <field name="key-not-matched" domain="bit"/> + <field name="arguments-not-matched" domain="bit"/> + </struct> + </result> + </method> +</class> + +<class name="queue010" index="8"> + <doc>blah, blah</doc> + <method name = "declare" index="1"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="name" domain="shortstr"/> + <field name="alternate-exchange" domain="shortstr"/> + <field name="passive" domain="bit"/> + <field name="durable" domain="bit"/> + <field name="exclusive" domain="bit"/> + <field name="auto-delete" domain="bit"/> + <field name="arguments" domain="table"/> + </method> + <method name = "delete" index="2"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="name" domain="shortstr"/> + <field name="if-unused" domain="bit"/> + <field name="if-empty" domain="bit"/> + </method> + <method name = "purge" index="3"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="name" domain="shortstr"/> + </method> + <method name = "query" index="4"> + <doc>blah, blah</doc> + <chassis name="server" implement="MUST" /> + <field name="name" domain="shortstr"/> + <result> + <struct size="long" type="1"> + <field name="name" domain="shortstr"/> + <field name="alternate-exchange" domain="shortstr"/> + <field name="passive" domain="bit"/> + <field name="durable" domain="bit"/> + <field name="auto-delete" domain="bit"/> + <field name="arguments" domain="table"/> + <field name="message-count" domain="long"/> + <field name="subscriber-count" domain="long"/> + </struct> + </result> + </method> +</class> + +</amqp> |