diff options
Diffstat (limited to 'cpp')
180 files changed, 16067 insertions, 0 deletions
diff --git a/cpp/DESIGN b/cpp/DESIGN new file mode 100644 index 0000000000..476fd42bfa --- /dev/null +++ b/cpp/DESIGN @@ -0,0 +1,89 @@ +Qpid C++ AMQP implementation +============================= + +The following is a brief description of the logical design of the +Qpid C++ code. + +Layout + +There are three top level modules. The first two, client and broker, +containi the code required for an AMQP client and an AMQP broker +respectively. The third, common, contains code that is common to both +client and broker implementations. [Note that at present only the +client has been started]. + +Within the common module there are currently four sub-modules. The +largest of these is framing, containing the definitions of classes +corresponding to key AMQP concepts such as frames, content & header +bodies, particular method bodies etc as well as some interfaces and +utilities used in the encoding and decoding of the wire protocol. + +Two of the other sub-modules in common, io and concurrent, provide +abstractions of core io and concurrency constructs used in the client +and broker code. The intention is to allow these to be implemented in +different ways.interaction with the wire protocol. At present the +implementation of the io and concurrency abstractions is based on APR +(Apache Portable Runtime). [Note: the io module currently only +contains the abstractions as seen from the client - the Connector. It +will in due time likely have the analogous broker-side abstraction - +the Acceptor]. + +The final common sub-module is error, containing a simple exception +definition used in all the error handling. + +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/cpp/Makefile b/cpp/Makefile new file mode 100644 index 0000000000..7f83847b04 --- /dev/null +++ b/cpp/Makefile @@ -0,0 +1,53 @@ +# +# 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. + +# +# Master make file for c++ Qpid project (AMQP) +# +# Calls the makefiles in the various subdirectories in order to +# build them in the correct sequence. +# + +include options.mk + +UNITTESTS=$(wildcard common/*/test/*.so broker/test/*.so) + +.PHONY: all clean doxygen + +test: all + @$(MAKE) -C common test + @$(MAKE) -C broker test + @$(MAKE) -C client test + @$(MAKE) runtests + +runtests: + $(CPPUNIT_HOME)/bin/DllPlugInTester -t -b $(UNITTESTS) + bin/qpidd >> qpidd.log & + cd ../python ; ./run-tests -v -I cpp_failing.txt + +all: + @$(MAKE) -C common all + @$(MAKE) -C broker all + @$(MAKE) -C client all + +clean: + @$(MAKE) -C common clean + @$(MAKE) -C broker clean + @$(MAKE) -C client clean + @$(MAKE) -C doxygen clean + -@rm qpidd.log + +doxygen: + @$(MAKE) -C doxygen all diff --git a/cpp/README b/cpp/README new file mode 100644 index 0000000000..427a0c15c8 --- /dev/null +++ b/cpp/README @@ -0,0 +1,48 @@ += Developer guide to C++ codebase = + +== Prerequisites == + +Apache Portable Runtime 1.2.7: http://apr.apache.org/ +Install in /usr/local/apr or update options.mk if installed elsewhere. + +CppUnit: http://cppunit.sourceforge.net + +Optional: to generate source code documentation you need: + * doxygen: http://sourceforge.net/projects/doxygen/ + * graphviz - http://www.graphviz.org/ + +== Build and test == + +make + +Default target builds and tests everything, see Makefile for other +targets. + +=== Unit tests === +Unit tests are built as .so files containing CppUnit plugins. + +DllPlugInTester is provided as part of cppunit. You can use it to run +any subset of the unit tests. See Makefile for examples. + +=== System tests === + +The Python test suite ../python/run_tests is the main set of broker +system tests. + +There are some C++ client test executables built under client/test. + +== 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 + + + diff --git a/cpp/broker/Makefile b/cpp/broker/Makefile new file mode 100644 index 0000000000..58ba3a41b5 --- /dev/null +++ b/cpp/broker/Makefile @@ -0,0 +1,47 @@ +# +# 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. +# + +# +# Build broker library and executable. +# + +QPID_HOME = ../.. +include ${QPID_HOME}/cpp/options.mk + +SOURCES= $(wildcard src/*.cpp) +OBJECTS= $(subst .cpp,.o,$(SOURCES)) +LIB_OBJECTS= $(subst src/Broker.o,,$(OBJECTS)) +EXE_OBJECTS= src/Broker.o + + +.PHONY: all clean test + +all: $(BROKER) + +test: + @$(MAKE) -C test all + +clean: + -@rm -f ${OBJECTS} src/*.d ${BROKER} $(BROKER_LIB) + @$(MAKE) -C test clean + +$(BROKER): $(BROKER_LIB) $(EXE_OBJECTS) + ${CXX} -o $@ $(EXE_OBJECTS) $(LDFLAGS) -lapr-1 $(COMMON_LIB) $(BROKER_LIB) + +$(BROKER_LIB): $(LIB_OBJECTS) + $(CXX) -shared -o $@ $(LDFLAGS) $(LIB_OBJECTS) -lapr-1 $(COMMON_LIB) $(LIBDIR) + +-include $(SOURCES:.cpp=.d) diff --git a/cpp/broker/inc/AutoDelete.h b/cpp/broker/inc/AutoDelete.h new file mode 100644 index 0000000000..864d68358f --- /dev/null +++ b/cpp/broker/inc/AutoDelete.h @@ -0,0 +1,54 @@ +/* + * + * 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. + * + */ +#ifndef _AutoDelete_ +#define _AutoDelete_ + +#include <iostream> +#include <queue> +#include "MonitorImpl.h" +#include "Queue.h" +#include "QueueRegistry.h" +#include "ThreadFactoryImpl.h" + +namespace qpid { + namespace broker{ + class AutoDelete : private virtual qpid::concurrent::Runnable{ + qpid::concurrent::ThreadFactoryImpl factory; + qpid::concurrent::MonitorImpl lock; + qpid::concurrent::MonitorImpl monitor; + std::queue<Queue::shared_ptr> queues; + QueueRegistry* const registry; + const u_int32_t period; + volatile bool stopped; + qpid::concurrent::Thread* runner; + + Queue::shared_ptr const pop(); + void process(); + virtual void run(); + + public: + AutoDelete(QueueRegistry* const registry, u_int32_t period); + void add(Queue::shared_ptr const); + void start(); + void stop(); + }; + } +} + + +#endif diff --git a/cpp/broker/inc/Binding.h b/cpp/broker/inc/Binding.h new file mode 100644 index 0000000000..b11419e92c --- /dev/null +++ b/cpp/broker/inc/Binding.h @@ -0,0 +1,35 @@ +/* + * + * 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. + * + */ +#ifndef _Binding_ +#define _Binding_ + +#include "FieldTable.h" + +namespace qpid { + namespace broker { + class Binding{ + public: + virtual void cancel() = 0; + virtual ~Binding(){} + }; + } +} + + +#endif + diff --git a/cpp/broker/inc/Channel.h b/cpp/broker/inc/Channel.h new file mode 100644 index 0000000000..aaf2ce569b --- /dev/null +++ b/cpp/broker/inc/Channel.h @@ -0,0 +1,87 @@ +/* + * + * 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. + * + */ +#ifndef _Channel_ +#define _Channel_ + +#include <map> +#include "AMQContentBody.h" +#include "AMQHeaderBody.h" +#include "BasicPublishBody.h" +#include "Binding.h" +#include "Consumer.h" +#include "Message.h" +#include "MonitorImpl.h" +#include "NameGenerator.h" +#include "OutputHandler.h" +#include "Queue.h" + +namespace qpid { + namespace broker { + class Channel{ + private: + class ConsumerImpl : public virtual Consumer{ + ConnectionToken* const connection; + Channel* parent; + string tag; + Queue::shared_ptr queue; + public: + ConsumerImpl(Channel* parent, string& tag, Queue::shared_ptr queue, ConnectionToken* const connection); + virtual bool deliver(Message::shared_ptr& msg); + void cancel(); + }; + + typedef std::map<string,ConsumerImpl*>::iterator consumer_iterator; + + const int id; + qpid::framing::OutputHandler* out; + u_int64_t deliveryTag; + Queue::shared_ptr defaultQueue; + bool transactional; + std::map<string, ConsumerImpl*> consumers; + u_int32_t prefetchSize; + u_int16_t prefetchCount; + u_int32_t framesize; + Message::shared_ptr message; + NameGenerator tagGenerator; + + void deliver(Message::shared_ptr& msg, string& tag); + void publish(ExchangeRegistry* exchanges); + + public: + Channel(qpid::framing::OutputHandler* out, int id, u_int32_t framesize); + ~Channel(); + inline void setDefaultQueue(Queue::shared_ptr queue){ defaultQueue = queue; } + inline Queue::shared_ptr getDefaultQueue(){ return defaultQueue; } + inline u_int32_t setPrefetchSize(u_int32_t size){ prefetchSize = size; } + inline u_int16_t setPrefetchCount(u_int16_t count){ prefetchCount = count; } + void handlePublish(Message* msg); + void handleHeader(qpid::framing::AMQHeaderBody::shared_ptr header, ExchangeRegistry* exchanges); + void handleContent(qpid::framing::AMQContentBody::shared_ptr content, ExchangeRegistry* exchanges); + bool exists(string& consumerTag); + void consume(string& tag, Queue::shared_ptr queue, bool acks, bool exclusive, ConnectionToken* const connection = 0); + void cancel(string& tag); + void begin(); + void close(); + void commit(); + void rollback(); + }; + } +} + + +#endif diff --git a/cpp/broker/inc/Configuration.h b/cpp/broker/inc/Configuration.h new file mode 100644 index 0000000000..5ec70a839b --- /dev/null +++ b/cpp/broker/inc/Configuration.h @@ -0,0 +1,125 @@ +/* + * + * 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. + * + */ +#ifndef _Configuration_ +#define _Configuration_ + +#include <cstdlib> +#include <iostream> +#include <vector> + +namespace qpid { + namespace broker { + class Configuration{ + class Option{ + const std::string flag; + const std::string name; + const std::string desc; + + bool match(const std::string& arg); + + protected: + virtual bool needsValue() const = 0; + virtual void setValue(const std::string& value) = 0; + + public: + Option(const char flag, const std::string& name, const std::string& desc); + Option(const std::string& name, const std::string& desc); + virtual ~Option(); + + bool parse(int& i, char** argv, int argc); + void print(std::ostream& out) const; + }; + + class IntOption : public Option{ + const int defaultValue; + int value; + public: + IntOption(char flag, const std::string& name, const std::string& desc, const int value = 0); + IntOption(const std::string& name, const std::string& desc, const int value = 0); + virtual ~IntOption(); + + int getValue() const; + virtual bool needsValue() const; + virtual void setValue(const std::string& value); + }; + + class StringOption : public Option{ + const std::string defaultValue; + std::string value; + public: + StringOption(char flag, const std::string& name, const std::string& desc, const std::string value = ""); + StringOption(const std::string& name, const std::string& desc, const std::string value = ""); + virtual ~StringOption(); + + const std::string& getValue() const; + virtual bool needsValue() const; + virtual void setValue(const std::string& value); + }; + + class BoolOption : public Option{ + const bool defaultValue; + bool value; + public: + BoolOption(char flag, const std::string& name, const std::string& desc, const bool value = 0); + BoolOption(const std::string& name, const std::string& desc, const bool value = 0); + virtual ~BoolOption(); + + bool getValue() const; + virtual bool needsValue() const; + virtual void setValue(const std::string& value); + }; + + BoolOption trace; + IntOption port; + IntOption workerThreads; + IntOption maxConnections; + IntOption connectionBacklog; + StringOption acceptor; + BoolOption help; + + typedef std::vector<Option*>::iterator op_iterator; + std::vector<Option*> options; + + public: + class ParseException{ + public: + const std::string& error; + ParseException(const std::string& _error) : error(_error) {} + }; + + + Configuration(); + ~Configuration(); + + void parse(int argc, char** argv); + + bool isHelp(); + bool isTrace(); + int getPort(); + int getWorkerThreads(); + int getMaxConnections(); + int getConnectionBacklog(); + const std::string& getAcceptor(); + + void usage(); + }; + } +} + + +#endif diff --git a/cpp/broker/inc/ConnectionToken.h b/cpp/broker/inc/ConnectionToken.h new file mode 100644 index 0000000000..1faefec2cc --- /dev/null +++ b/cpp/broker/inc/ConnectionToken.h @@ -0,0 +1,35 @@ +/* + * + * 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. + * + */ +#ifndef _ConnectionToken_ +#define _ConnectionToken_ + +namespace qpid { + namespace broker { + /** + * An empty interface allowing opaque implementations of some + * form of token to identify a connection. + */ + class ConnectionToken{ + public: + virtual ~ConnectionToken(){} + }; + } +} + + +#endif diff --git a/cpp/broker/inc/Consumer.h b/cpp/broker/inc/Consumer.h new file mode 100644 index 0000000000..af2d5d7812 --- /dev/null +++ b/cpp/broker/inc/Consumer.h @@ -0,0 +1,34 @@ +/* + * + * 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. + * + */ +#ifndef _Consumer_ +#define _Consumer_ + +#include "Message.h" + +namespace qpid { + namespace broker { + class Consumer{ + public: + virtual bool deliver(Message::shared_ptr& msg) = 0; + virtual ~Consumer(){} + }; + } +} + + +#endif diff --git a/cpp/broker/inc/DirectExchange.h b/cpp/broker/inc/DirectExchange.h new file mode 100644 index 0000000000..bf8c5f0b37 --- /dev/null +++ b/cpp/broker/inc/DirectExchange.h @@ -0,0 +1,55 @@ +/* + * + * 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. + * + */ +#ifndef _DirectExchange_ +#define _DirectExchange_ + +#include <map> +#include <vector> +#include "Exchange.h" +#include "FieldTable.h" +#include "Message.h" +#include "MonitorImpl.h" +#include "Queue.h" + +namespace qpid { +namespace broker { + class DirectExchange : public virtual Exchange{ + const string name; + std::map<string, std::vector<Queue::shared_ptr> > bindings; + qpid::concurrent::MonitorImpl lock; + + public: + static const std::string typeName; + + DirectExchange(const string& name); + + inline virtual const string& getName(){ return name; } + + virtual void bind(Queue::shared_ptr queue, const string& routingKey, qpid::framing::FieldTable* args); + + virtual void unbind(Queue::shared_ptr queue, const string& routingKey, qpid::framing::FieldTable* args); + + virtual void route(Message::shared_ptr& msg, const string& routingKey, qpid::framing::FieldTable* args); + + virtual ~DirectExchange(); + }; +} +} + + +#endif diff --git a/cpp/broker/inc/Exchange.h b/cpp/broker/inc/Exchange.h new file mode 100644 index 0000000000..5f5dc5ce71 --- /dev/null +++ b/cpp/broker/inc/Exchange.h @@ -0,0 +1,39 @@ +/* + * + * 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. + * + */ +#ifndef _Exchange_ +#define _Exchange_ + +#include "FieldTable.h" +#include "Message.h" +#include "Queue.h" + +namespace qpid { +namespace broker { + class Exchange{ + public: + virtual const string& getName() = 0; + virtual void bind(Queue::shared_ptr queue, const string& routingKey, qpid::framing::FieldTable* args) = 0; + virtual void unbind(Queue::shared_ptr queue, const string& routingKey, qpid::framing::FieldTable* args) = 0; + virtual void route(Message::shared_ptr& msg, const string& routingKey, qpid::framing::FieldTable* args) = 0; + virtual ~Exchange(){} + }; +} +} + + +#endif diff --git a/cpp/broker/inc/ExchangeBinding.h b/cpp/broker/inc/ExchangeBinding.h new file mode 100644 index 0000000000..4cbb73acbf --- /dev/null +++ b/cpp/broker/inc/ExchangeBinding.h @@ -0,0 +1,45 @@ +/* + * + * 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. + * + */ +#ifndef _ExchangeBinding_ +#define _ExchangeBinding_ + +#include "Binding.h" +#include "FieldTable.h" +#include "Queue.h" + +namespace qpid { + namespace broker { + class Exchange; + class Queue; + + class ExchangeBinding : public virtual Binding{ + Exchange* e; + Queue::shared_ptr q; + const string key; + qpid::framing::FieldTable* args; + public: + ExchangeBinding(Exchange* _e, Queue::shared_ptr _q, const string& _key, qpid::framing::FieldTable* _args); + virtual void cancel(); + virtual ~ExchangeBinding(); + }; + } +} + + +#endif + diff --git a/cpp/broker/inc/ExchangeRegistry.h b/cpp/broker/inc/ExchangeRegistry.h new file mode 100644 index 0000000000..0f0eaae0d0 --- /dev/null +++ b/cpp/broker/inc/ExchangeRegistry.h @@ -0,0 +1,42 @@ +/* + * + * 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. + * + */ +#ifndef _ExchangeRegistry_ +#define _ExchangeRegistry_ + +#include <map> +#include "Exchange.h" +#include "Monitor.h" + +namespace qpid { +namespace broker { + class ExchangeRegistry{ + std::map<string, Exchange*> exchanges; + qpid::concurrent::Monitor* lock; + public: + ExchangeRegistry(); + void declare(Exchange* exchange); + void destroy(const string& name); + Exchange* get(const string& name); + inline qpid::concurrent::Monitor* getLock(){ return lock; } + ~ExchangeRegistry(); + }; +} +} + + +#endif diff --git a/cpp/broker/inc/FanOutExchange.h b/cpp/broker/inc/FanOutExchange.h new file mode 100644 index 0000000000..9d0d32bbf8 --- /dev/null +++ b/cpp/broker/inc/FanOutExchange.h @@ -0,0 +1,58 @@ +/* + * + * 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. + * + */ +#ifndef _FanOutExchange_ +#define _FanOutExchange_ + +#include <map> +#include <vector> +#include "Exchange.h" +#include "FieldTable.h" +#include "Message.h" +#include "MonitorImpl.h" +#include "Queue.h" + +namespace qpid { +namespace broker { + +class FanOutExchange : public virtual Exchange { + const string name; + std::vector<Queue::shared_ptr> bindings; + qpid::concurrent::MonitorImpl lock; + + public: + static const std::string typeName; + + FanOutExchange(const string& name); + + inline virtual const string& getName(){ return name; } + + virtual void bind(Queue::shared_ptr queue, const string& routingKey, qpid::framing::FieldTable* args); + + virtual void unbind(Queue::shared_ptr queue, const string& routingKey, qpid::framing::FieldTable* args); + + virtual void route(Message::shared_ptr& msg, const string& routingKey, qpid::framing::FieldTable* args); + + virtual ~FanOutExchange(); +}; + +} +} + + + +#endif diff --git a/cpp/broker/inc/Message.h b/cpp/broker/inc/Message.h new file mode 100644 index 0000000000..37a0c9b2c8 --- /dev/null +++ b/cpp/broker/inc/Message.h @@ -0,0 +1,73 @@ +/* + * + * 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. + * + */ +#ifndef _Message_ +#define _Message_ + +#include "memory.h" +#include "AMQContentBody.h" +#include "AMQHeaderBody.h" +#include "BasicHeaderProperties.h" +#include "BasicPublishBody.h" +#include "ConnectionToken.h" +#include "OutputHandler.h" + +namespace qpid { + namespace broker { + class ExchangeRegistry; + + class Message{ + typedef std::vector<qpid::framing::AMQContentBody::shared_ptr> content_list; + typedef content_list::iterator content_iterator; + + const ConnectionToken* const publisher; + string exchange; + string routingKey; + const bool mandatory; + const bool immediate; + qpid::framing::AMQHeaderBody::shared_ptr header; + content_list content; + + u_int64_t contentSize(); + qpid::framing::BasicHeaderProperties* getHeaderProperties(); + + + public: + typedef std::tr1::shared_ptr<Message> shared_ptr; + + Message(const ConnectionToken* const publisher, + const string& exchange, const string& routingKey, + bool mandatory, bool immediate); + ~Message(); + void setHeader(qpid::framing::AMQHeaderBody::shared_ptr header); + void addContent(qpid::framing::AMQContentBody::shared_ptr data); + bool isComplete(); + const ConnectionToken* const getPublisher(); + + void deliver(qpid::framing::OutputHandler* out, int channel, + string& consumerTag, u_int64_t deliveryTag, + u_int32_t framesize); + + friend bool route(Message::shared_ptr& msg, ExchangeRegistry* registry); + + }; + bool route(Message::shared_ptr& msg, ExchangeRegistry* registry); + } +} + + +#endif diff --git a/cpp/broker/inc/NameGenerator.h b/cpp/broker/inc/NameGenerator.h new file mode 100644 index 0000000000..6e6e0acf28 --- /dev/null +++ b/cpp/broker/inc/NameGenerator.h @@ -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. + * + */ +#ifndef _NameGenerator_ +#define _NameGenerator_ + +#include "Message.h" + +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/cpp/broker/inc/Queue.h b/cpp/broker/inc/Queue.h new file mode 100644 index 0000000000..2229ba6235 --- /dev/null +++ b/cpp/broker/inc/Queue.h @@ -0,0 +1,106 @@ +/* + * + * 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. + * + */ +#ifndef _Queue_ +#define _Queue_ + +#include <vector> +#include <queue> +#include "memory.h" +#include "apr_time.h" +#include "amqp_types.h" +#include "Binding.h" +#include "ConnectionToken.h" +#include "Consumer.h" +#include "Message.h" +#include "MonitorImpl.h" + +namespace qpid { + namespace broker { + + /** + * Thrown when exclusive access would be violated. + */ + struct ExclusiveAccessException{}; + + /** + * 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{ + const string name; + const u_int32_t autodelete; + const bool durable; + const ConnectionToken* const owner; + std::vector<Consumer*> consumers; + std::queue<Binding*> bindings; + std::queue<Message::shared_ptr> messages; + bool queueing; + bool dispatching; + int next; + mutable qpid::concurrent::MonitorImpl lock; + apr_time_t lastUsed; + Consumer* exclusive; + + bool startDispatching(); + bool dispatch(Message::shared_ptr& msg); + + public: + + typedef std::tr1::shared_ptr<Queue> shared_ptr; + + typedef std::vector<shared_ptr> vector; + + Queue(const string& name, bool durable = false, u_int32_t autodelete = 0, const ConnectionToken* const owner = 0); + ~Queue(); + /** + * Informs the queue of a binding that should be cancelled on + * destruction of the queue. + */ + void bound(Binding* b); + /** + * Delivers a message to the queue from where it will be + * dispatched to immediately to a consumer if one is + * available or stored for dequeue or later dispatch if + * not. + */ + void deliver(Message::shared_ptr& msg); + /** + * Dispatch any queued messages providing there are + * consumers for them. Only one thread can be dispatching + * at any time, but this method (rather than the caller) + * is responsible for ensuring that. + */ + void dispatch(); + void consume(Consumer* c, bool exclusive = false); + void cancel(Consumer* c); + Message::shared_ptr dequeue(); + u_int32_t purge(); + u_int32_t getMessageCount() const; + u_int32_t getConsumerCount() const; + inline const string& getName() const { return name; } + inline const bool isExclusiveOwner(const ConnectionToken* const o) const { return o == owner; } + inline bool hasExclusiveConsumer() const { return exclusive; } + bool canAutoDelete() const; + }; + } +} + + +#endif diff --git a/cpp/broker/inc/QueueRegistry.h b/cpp/broker/inc/QueueRegistry.h new file mode 100644 index 0000000000..ac12aa8f88 --- /dev/null +++ b/cpp/broker/inc/QueueRegistry.h @@ -0,0 +1,88 @@ +/* + * + * 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. + * + */ +#ifndef _QueueRegistry_ +#define _QueueRegistry_ + +#include <map> +#include "MonitorImpl.h" +#include "Queue.h" + +namespace qpid { +namespace broker { + +class SessionHandlerImpl; + +/** + * 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, u_int32_t autodelete = 0, const ConnectionToken* 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); + + /** + * Find the named queue. Return 0 if not found. + */ + Queue::shared_ptr find(const string& name); + + /** + * Generate unique queue name. + */ + string generateName(); + + private: + typedef std::map<string, Queue::shared_ptr> QueueMap; + QueueMap queues; + qpid::concurrent::MonitorImpl lock; + int counter; + +}; + + +} +} + + +#endif diff --git a/cpp/broker/inc/SessionHandlerFactoryImpl.h b/cpp/broker/inc/SessionHandlerFactoryImpl.h new file mode 100644 index 0000000000..2317a6667b --- /dev/null +++ b/cpp/broker/inc/SessionHandlerFactoryImpl.h @@ -0,0 +1,49 @@ +/* + * + * 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. + * + */ +#ifndef _SessionHandlerFactoryImpl_ +#define _SessionHandlerFactoryImpl_ + +#include "AMQFrame.h" +#include "AutoDelete.h" +#include "DirectExchange.h" +#include "ExchangeRegistry.h" +#include "ProtocolInitiation.h" +#include "QueueRegistry.h" +#include "SessionHandlerFactory.h" +#include "TimeoutHandler.h" + +namespace qpid { + namespace broker { + + class SessionHandlerFactoryImpl : public virtual qpid::io::SessionHandlerFactory + { + QueueRegistry queues; + ExchangeRegistry exchanges; + const u_int32_t timeout;//timeout for auto-deleted queues (in ms) + AutoDelete cleaner; + public: + SessionHandlerFactoryImpl(u_int32_t timeout = 30000); + virtual qpid::io::SessionHandler* create(qpid::io::SessionContext* ctxt); + virtual ~SessionHandlerFactoryImpl(); + }; + + } +} + + +#endif diff --git a/cpp/broker/inc/SessionHandlerImpl.h b/cpp/broker/inc/SessionHandlerImpl.h new file mode 100644 index 0000000000..14a6404c78 --- /dev/null +++ b/cpp/broker/inc/SessionHandlerImpl.h @@ -0,0 +1,230 @@ +/* + * + * 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. + * + */ +#ifndef _SessionHandlerImpl_ +#define _SessionHandlerImpl_ + +#include <map> +#include <sstream> +#include <vector> +#include <exception> +#include "AMQFrame.h" +#include "AMQP_ServerOperations.h" +#include "AutoDelete.h" +#include "ExchangeRegistry.h" +#include "Channel.h" +#include "ConnectionToken.h" +#include "DirectExchange.h" +#include "OutputHandler.h" +#include "ProtocolInitiation.h" +#include "QueueRegistry.h" +#include "SessionContext.h" +#include "SessionHandler.h" +#include "TimeoutHandler.h" +#include "TopicExchange.h" + +namespace qpid { +namespace broker { + +struct ChannelException : public std::exception { + u_int16_t code; + string text; + ChannelException(u_int16_t _code, string _text) : code(_code), text(_text) {} + ~ChannelException() throw() {} + const char* what() const throw() { return text.c_str(); } +}; + +struct ConnectionException : public std::exception { + u_int16_t code; + string text; + ConnectionException(u_int16_t _code, string _text) : code(_code), text(_text) {} + ~ConnectionException() throw() {} + const char* what() const throw() { return text.c_str(); } +}; + +class SessionHandlerImpl : public virtual qpid::io::SessionHandler, + public virtual qpid::framing::AMQP_ServerOperations, + public virtual ConnectionToken +{ + typedef std::map<u_int16_t, Channel*>::iterator channel_iterator; + typedef std::vector<Queue::shared_ptr>::iterator queue_iterator; + + qpid::io::SessionContext* context; + QueueRegistry* queues; + ExchangeRegistry* const exchanges; + AutoDelete* const cleaner; + const u_int32_t timeout;//timeout for auto-deleted queues (in ms) + + ConnectionHandler* connectionHandler; + ChannelHandler* channelHandler; + BasicHandler* basicHandler; + ExchangeHandler* exchangeHandler; + QueueHandler* queueHandler; + + std::map<u_int16_t, Channel*> channels; + std::vector<Queue::shared_ptr> exclusiveQueues; + + u_int32_t framemax; + u_int16_t heartbeat; + + void handleHeader(u_int16_t channel, qpid::framing::AMQHeaderBody::shared_ptr body); + void handleContent(u_int16_t channel, qpid::framing::AMQContentBody::shared_ptr body); + void handleHeartbeat(qpid::framing::AMQHeartbeatBody::shared_ptr body); + + /** + * Get named queue, never returns 0. + * @return: named queue or default queue for channel if name="" + * @exception: ChannelException if no queue of that name is found. + * @exception: ConnectionException if no queue specified and channel has not declared one. + */ + Queue::shared_ptr getQueue(const string& name, u_int16_t channel); + + Exchange* findExchange(const string& name); + + public: + SessionHandlerImpl(qpid::io::SessionContext* context, QueueRegistry* queues, + ExchangeRegistry* exchanges, AutoDelete* cleaner, const u_int32_t timeout); + virtual void received(qpid::framing::AMQFrame* frame); + virtual void initiated(qpid::framing::ProtocolInitiation* header); + virtual void idleOut(); + virtual void idleIn(); + virtual void closed(); + virtual ~SessionHandlerImpl(); + + class ConnectionHandlerImpl : public virtual ConnectionHandler{ + SessionHandlerImpl* parent; + public: + inline ConnectionHandlerImpl(SessionHandlerImpl* _parent) : parent(_parent) {} + + virtual void startOk(u_int16_t channel, qpid::framing::FieldTable& clientProperties, string& mechanism, + string& response, string& locale); + + virtual void secureOk(u_int16_t channel, string& response); + + virtual void tuneOk(u_int16_t channel, u_int16_t channelMax, u_int32_t frameMax, u_int16_t heartbeat); + + virtual void open(u_int16_t channel, string& virtualHost, string& capabilities, bool insist); + + virtual void close(u_int16_t channel, u_int16_t replyCode, string& replyText, u_int16_t classId, + u_int16_t methodId); + + virtual void closeOk(u_int16_t channel); + + virtual ~ConnectionHandlerImpl(){} + }; + + class ChannelHandlerImpl : public virtual ChannelHandler{ + SessionHandlerImpl* parent; + public: + inline ChannelHandlerImpl(SessionHandlerImpl* _parent) : parent(_parent) {} + + virtual void open(u_int16_t channel, string& outOfBand); + + virtual void flow(u_int16_t channel, bool active); + + virtual void flowOk(u_int16_t channel, bool active); + + virtual void close(u_int16_t channel, u_int16_t replyCode, string& replyText, + u_int16_t classId, u_int16_t methodId); + + virtual void closeOk(u_int16_t channel); + + virtual ~ChannelHandlerImpl(){} + }; + + class ExchangeHandlerImpl : public virtual ExchangeHandler{ + SessionHandlerImpl* parent; + public: + inline ExchangeHandlerImpl(SessionHandlerImpl* _parent) : parent(_parent) {} + + virtual void declare(u_int16_t channel, u_int16_t ticket, string& exchange, string& type, + bool passive, bool durable, bool autoDelete, bool internal, bool nowait, + qpid::framing::FieldTable& arguments); + + virtual void delete_(u_int16_t channel, u_int16_t ticket, string& exchange, bool ifUnused, bool nowait); + + virtual ~ExchangeHandlerImpl(){} + }; + + + class QueueHandlerImpl : public virtual QueueHandler{ + SessionHandlerImpl* parent; + public: + inline QueueHandlerImpl(SessionHandlerImpl* _parent) : parent(_parent) {} + + virtual void declare(u_int16_t channel, u_int16_t ticket, string& queue, + bool passive, bool durable, bool exclusive, + bool autoDelete, bool nowait, qpid::framing::FieldTable& arguments); + + virtual void bind(u_int16_t channel, u_int16_t ticket, string& queue, + string& exchange, string& routingKey, bool nowait, + qpid::framing::FieldTable& arguments); + + virtual void purge(u_int16_t channel, u_int16_t ticket, string& queue, + bool nowait); + + virtual void delete_(u_int16_t channel, u_int16_t ticket, string& queue, bool ifUnused, bool ifEmpty, + bool nowait); + + virtual ~QueueHandlerImpl(){} + }; + + class BasicHandlerImpl : public virtual BasicHandler{ + SessionHandlerImpl* parent; + public: + inline BasicHandlerImpl(SessionHandlerImpl* _parent) : parent(_parent) {} + + virtual void qos(u_int16_t channel, u_int32_t prefetchSize, u_int16_t prefetchCount, bool global); + + virtual void consume(u_int16_t channel, u_int16_t ticket, string& queue, string& consumerTag, + bool noLocal, bool noAck, bool exclusive, bool nowait); + + virtual void cancel(u_int16_t channel, string& consumerTag, bool nowait); + + virtual void publish(u_int16_t channel, u_int16_t ticket, string& exchange, string& routingKey, + bool mandatory, bool immediate); + + virtual void get(u_int16_t channel, u_int16_t ticket, string& queue, bool noAck); + + virtual void ack(u_int16_t channel, u_int64_t deliveryTag, bool multiple); + + virtual void reject(u_int16_t channel, u_int64_t deliveryTag, bool requeue); + + virtual void recover(u_int16_t channel, bool requeue); + + virtual ~BasicHandlerImpl(){} + }; + + inline virtual ChannelHandler* getChannelHandler(){ return channelHandler; } + inline virtual ConnectionHandler* getConnectionHandler(){ return connectionHandler; } + inline virtual BasicHandler* getBasicHandler(){ return basicHandler; } + inline virtual ExchangeHandler* getExchangeHandler(){ return exchangeHandler; } + inline virtual QueueHandler* getQueueHandler(){ return queueHandler; } + + inline virtual AccessHandler* getAccessHandler(){ return 0; } + inline virtual FileHandler* getFileHandler(){ return 0; } + inline virtual StreamHandler* getStreamHandler(){ return 0; } + inline virtual TxHandler* getTxHandler(){ return 0; } + inline virtual DtxHandler* getDtxHandler(){ return 0; } + inline virtual TunnelHandler* getTunnelHandler(){ return 0; } +}; + +} +} + + +#endif diff --git a/cpp/broker/inc/TopicExchange.h b/cpp/broker/inc/TopicExchange.h new file mode 100644 index 0000000000..d9ff62ecc6 --- /dev/null +++ b/cpp/broker/inc/TopicExchange.h @@ -0,0 +1,55 @@ +/* + * + * 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. + * + */ +#ifndef _TopicExchange_ +#define _TopicExchange_ + +#include <map> +#include <vector> +#include "Exchange.h" +#include "FieldTable.h" +#include "Message.h" +#include "MonitorImpl.h" +#include "Queue.h" + +namespace qpid { +namespace broker { + class TopicExchange : public virtual Exchange{ + const string name; + std::map<string, std::vector<Queue::shared_ptr> > bindings;//NOTE: pattern matching not yet supported + qpid::concurrent::MonitorImpl lock; + + public: + static const std::string typeName; + + TopicExchange(const string& name); + + inline virtual const string& getName(){ return name; } + + virtual void bind(Queue::shared_ptr queue, const string& routingKey, qpid::framing::FieldTable* args); + + virtual void unbind(Queue::shared_ptr queue, const string& routingKey, qpid::framing::FieldTable* args); + + virtual void route(Message::shared_ptr& msg, const string& routingKey, qpid::framing::FieldTable* args); + + virtual ~TopicExchange(); + }; +} +} + + +#endif diff --git a/cpp/broker/src/AutoDelete.cpp b/cpp/broker/src/AutoDelete.cpp new file mode 100644 index 0000000000..6793ec449d --- /dev/null +++ b/cpp/broker/src/AutoDelete.cpp @@ -0,0 +1,93 @@ +/* + * + * 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 "AutoDelete.h" + +using namespace qpid::broker; + +AutoDelete::AutoDelete(QueueRegistry* const _registry, u_int32_t _period) : registry(_registry), + period(_period), + stopped(true), + runner(0){} + +void AutoDelete::add(Queue::shared_ptr const queue){ + lock.acquire(); + queues.push(queue); + lock.release(); +} + +Queue::shared_ptr const AutoDelete::pop(){ + Queue::shared_ptr next; + lock.acquire(); + if(!queues.empty()){ + next = queues.front(); + queues.pop(); + } + lock.release(); + return next; +} + +void AutoDelete::process(){ + Queue::shared_ptr seen; + for(Queue::shared_ptr q = pop(); q; q = pop()){ + if(seen == q){ + add(q); + break; + }else if(q->canAutoDelete()){ + std::string name(q->getName()); + registry->destroy(name); + std::cout << "INFO: Auto-deleted queue named " << name << std::endl; + }else{ + add(q); + if(!seen) seen = q; + } + } +} + +void AutoDelete::run(){ + monitor.acquire(); + while(!stopped){ + process(); + monitor.wait(period); + } + monitor.release(); +} + +void AutoDelete::start(){ + monitor.acquire(); + if(stopped){ + runner = factory.create(this); + stopped = false; + monitor.release(); + runner->start(); + }else{ + monitor.release(); + } +} + +void AutoDelete::stop(){ + monitor.acquire(); + if(!stopped){ + stopped = true; + monitor.notify(); + monitor.release(); + runner->join(); + delete runner; + }else{ + monitor.release(); + } +} diff --git a/cpp/broker/src/Broker.cpp b/cpp/broker/src/Broker.cpp new file mode 100644 index 0000000000..5d59b63622 --- /dev/null +++ b/cpp/broker/src/Broker.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 <iostream> +#include <memory> +#include "apr_signal.h" + +#include "Acceptor.h" +#include "Configuration.h" +#include "QpidError.h" +#include "SessionHandlerFactoryImpl.h" + +//optional includes: +#ifdef _USE_APR_IO_ + +#include "BlockingAPRAcceptor.h" +#include "LFAcceptor.h" + +#endif + +using namespace qpid::broker; +using namespace qpid::io; + +void handle_signal(int signal); + +Acceptor* createAcceptor(Configuration& config); + +int main(int argc, char** argv) +{ + SessionHandlerFactoryImpl factory; + Configuration config; + try{ + + config.parse(argc, argv); + if(config.isHelp()){ + config.usage(); + }else{ +#ifdef _USE_APR_IO_ + apr_signal(SIGINT, handle_signal); +#endif + try{ + std::auto_ptr<Acceptor> acceptor(createAcceptor(config)); + try{ + acceptor->bind(config.getPort(), &factory); + }catch(qpid::QpidError error){ + std::cout << "Error [" << error.code << "] " << error.msg << " (" << error.file << ":" << error.line << ")" << std::endl; + } + }catch(qpid::QpidError error){ + std::cout << "Error [" << error.code << "] " << error.msg << " (" << error.file << ":" << error.line << ")" << std::endl; + } + } + }catch(Configuration::ParseException error){ + std::cout << "Error: " << error.error << std::endl; + } + + return 1; +} + +Acceptor* createAcceptor(Configuration& config){ + const string type(config.getAcceptor()); +#ifdef _USE_APR_IO_ + if("blocking" == type){ + std::cout << "Using blocking acceptor " << std::endl; + return new BlockingAPRAcceptor(config.isTrace(), config.getConnectionBacklog()); + }else if("non-blocking" == type){ + std::cout << "Using non-blocking acceptor " << std::endl; + return new LFAcceptor(config.isTrace(), + config.getConnectionBacklog(), + config.getWorkerThreads(), + config.getMaxConnections()); + } +#endif + throw Configuration::ParseException("Unrecognised acceptor: " + type); +} + +void handle_signal(int signal){ + std::cout << "Shutting down..." << std::endl; +} diff --git a/cpp/broker/src/Channel.cpp b/cpp/broker/src/Channel.cpp new file mode 100644 index 0000000000..6980fe5a1b --- /dev/null +++ b/cpp/broker/src/Channel.cpp @@ -0,0 +1,148 @@ +/* + * + * 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 "Channel.h" +#include "QpidError.h" +#include <iostream> +#include <sstream> +#include <assert.h> + +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::concurrent; + +Channel::Channel(OutputHandler* _out, int _id, u_int32_t _framesize) : out(_out), + id(_id), + framesize(_framesize), + transactional(false), + deliveryTag(1), + tagGenerator("sgen"){} + +Channel::~Channel(){ + for(consumer_iterator i = consumers.begin(); i != consumers.end(); i = consumers.begin() ){ + std::cout << "ERROR: Channel consumer appears not to have been cancelled before channel was destroyed." << std::endl; + delete (i->second); + } +} + +bool Channel::exists(string& consumerTag){ + return consumers.find(consumerTag) != consumers.end(); +} + +void Channel::consume(string& tag, Queue::shared_ptr queue, bool acks, bool exclusive, ConnectionToken* const connection){ + if(tag.empty()) tag = tagGenerator.generate(); + + ConsumerImpl* c(new ConsumerImpl(this, tag, queue, connection)); + try{ + queue->consume(c, exclusive);//may throw exception + consumers[tag] = c; + }catch(ExclusiveAccessException& e){ + delete c; + throw e; + } +} + +void Channel::cancel(string& tag){ + ConsumerImpl* c = consumers[tag]; + if(c){ + c->cancel(); + consumers.erase(tag); + delete c; + } +} + +void Channel::close(){ + //cancel all consumers + for(consumer_iterator i = consumers.begin(); i != consumers.end(); i = consumers.begin() ){ + ConsumerImpl* c = i->second; + c->cancel(); + consumers.erase(i); + delete c; + } +} + +void Channel::begin(){ + transactional = true; +} + +void Channel::commit(){ + +} + +void Channel::rollback(){ + +} + +void Channel::deliver(Message::shared_ptr& msg, string& consumerTag){ + //send deliver method, header and content(s) + msg->deliver(out, id, consumerTag, deliveryTag++, framesize); +} + +Channel::ConsumerImpl::ConsumerImpl(Channel* _parent, string& _tag, + Queue::shared_ptr _queue, + ConnectionToken* const _connection) : parent(_parent), + tag(_tag), + queue(_queue), + connection(_connection){ +} + +bool Channel::ConsumerImpl::deliver(Message::shared_ptr& msg){ + if(connection != msg->getPublisher()){ + parent->deliver(msg, tag); + return true; + }else{ + return false; + } +} + +void Channel::ConsumerImpl::cancel(){ + if(queue) queue->cancel(this); +} + +void Channel::handlePublish(Message* msg){ + if(message.get()){ + THROW_QPID_ERROR(PROTOCOL_ERROR + 504, "Invalid message sequence: got publish before previous content was completed."); + } + message = Message::shared_ptr(msg); +} + +void Channel::handleHeader(AMQHeaderBody::shared_ptr header, ExchangeRegistry* exchanges){ + if(!message.get()){ + THROW_QPID_ERROR(PROTOCOL_ERROR + 504, "Invalid message sequence: got header before publish."); + } + message->setHeader(header); + if(message->isComplete()){ + publish(exchanges); + } +} + +void Channel::handleContent(AMQContentBody::shared_ptr content, ExchangeRegistry* exchanges){ + if(!message.get()){ + THROW_QPID_ERROR(PROTOCOL_ERROR + 504, "Invalid message sequence: got content before publish."); + } + message->addContent(content); + if(message->isComplete()){ + publish(exchanges); + } +} + +void Channel::publish(ExchangeRegistry* exchanges){ + if(!route(message, exchanges)){ + std::cout << "WARNING: Could not route message." << std::endl; + } + message.reset(); +} diff --git a/cpp/broker/src/Configuration.cpp b/cpp/broker/src/Configuration.cpp new file mode 100644 index 0000000000..aceb35bc87 --- /dev/null +++ b/cpp/broker/src/Configuration.cpp @@ -0,0 +1,195 @@ +/* + * + * 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 "Configuration.h" + +using namespace qpid::broker; +using namespace std; + +Configuration::Configuration() : + trace('t', "trace", "Print incoming & outgoing frames to the console (default=false)", false), + port('p', "port", "Sets the port to listen on (default=5672)", 5672), + workerThreads("worker-threads", "Sets the number of worker threads to use (default=5). Only valid for non-blocking acceptor.", 5), + maxConnections("max-connections", "Sets the maximum number of connections the broker can accept (default=500). Only valid for non-blocking acceptor.", 500), + connectionBacklog("connection-backlog", "Sets the connection backlog for the servers socket (default=10)", 10), + acceptor('a', "acceptor", "Sets the acceptor to use. Currently only two values are recognised, blocking and non-blocking (which is the default)", "non-blocking"), + help("help", "Prints usage information", false) +{ + options.push_back(&trace); + options.push_back(&port); + options.push_back(&workerThreads); + options.push_back(&maxConnections); + options.push_back(&connectionBacklog); + options.push_back(&acceptor); + options.push_back(&help); +} + +Configuration::~Configuration(){} + +void Configuration::parse(int argc, char** argv){ + int position = 1; + while(position < argc){ + bool matched(false); + for(op_iterator i = options.begin(); i < options.end() && !matched; i++){ + matched = (*i)->parse(position, argv, argc); + } + if(!matched){ + std::cout << "Warning: skipping unrecognised option " << argv[position] << std::endl; + position++; + } + } +} + +void Configuration::usage(){ + for(op_iterator i = options.begin(); i < options.end(); i++){ + (*i)->print(std::cout); + } +} + +bool Configuration::isHelp(){ + return help.getValue(); +} + +bool Configuration::isTrace(){ + return trace.getValue(); +} + +int Configuration::getPort(){ + return port.getValue(); +} + +int Configuration::getWorkerThreads(){ + return workerThreads.getValue(); +} + +int Configuration::getMaxConnections(){ + return maxConnections.getValue(); +} + +int Configuration::getConnectionBacklog(){ + return connectionBacklog.getValue(); +} + +const string& Configuration::getAcceptor(){ + return acceptor.getValue(); +} + +Configuration::Option::Option(const char _flag, const string& _name, const string& _desc) : + flag(string("-") + _flag), name("--" +_name), desc(_desc) {} + +Configuration::Option::Option(const string& _name, const string& _desc) : + flag(""), name("--" + _name), desc(_desc) {} + +Configuration::Option::~Option(){} + +bool Configuration::Option::match(const string& arg){ + return flag == arg || name == arg; +} + +bool Configuration::Option::parse(int& i, char** argv, int argc){ + const string arg(argv[i]); + if(match(arg)){ + if(needsValue()){ + if(++i < argc) setValue(argv[i]); + else throw ParseException("Argument " + arg + " requires a value!"); + }else{ + setValue(""); + } + i++; + return true; + }else{ + return false; + } +} + +void Configuration::Option::print(ostream& out) const { + out << " "; + if(flag.length() > 0){ + out << flag << " or "; + } + out << name; + if(needsValue()) out << "<value>"; + out << std::endl; + out << " " << desc << std::endl; +} + + +// String Option: + +Configuration::StringOption::StringOption(const char _flag, const string& _name, const string& _desc, const string _value) : + Option(_flag,_name,_desc), defaultValue(_value), value(_value) {} + +Configuration::StringOption::StringOption(const string& _name, const string& _desc, const string _value) : + Option(_name,_desc), defaultValue(_value), value(_value) {} + +Configuration::StringOption::~StringOption(){} + +const string& Configuration::StringOption::getValue() const { + return value; +} + +bool Configuration::StringOption::needsValue() const { + return true; +} + +void Configuration::StringOption::setValue(const std::string& _value){ + value = _value; +} + +// Int Option: + +Configuration::IntOption::IntOption(const char _flag, const string& _name, const string& _desc, const int _value) : + Option(_flag,_name,_desc), defaultValue(_value), value(_value) {} + +Configuration::IntOption::IntOption(const string& _name, const string& _desc, const int _value) : + Option(_name,_desc), defaultValue(_value), value(_value) {} + +Configuration::IntOption::~IntOption(){} + +int Configuration::IntOption::getValue() const { + return value; +} + +bool Configuration::IntOption::needsValue() const { + return true; +} + +void Configuration::IntOption::setValue(const std::string& _value){ + value = atoi(_value.c_str()); +} + +// Bool Option: + +Configuration::BoolOption::BoolOption(const char _flag, const string& _name, const string& _desc, const bool _value) : + Option(_flag,_name,_desc), defaultValue(_value), value(_value) {} + +Configuration::BoolOption::BoolOption(const string& _name, const string& _desc, const bool _value) : + Option(_name,_desc), defaultValue(_value), value(_value) {} + +Configuration::BoolOption::~BoolOption(){} + +bool Configuration::BoolOption::getValue() const { + return value; +} + +bool Configuration::BoolOption::needsValue() const { + return false; +} + +void Configuration::BoolOption::setValue(const std::string& _value){ + value = true; +} diff --git a/cpp/broker/src/DirectExchange.cpp b/cpp/broker/src/DirectExchange.cpp new file mode 100644 index 0000000000..70f7ee838f --- /dev/null +++ b/cpp/broker/src/DirectExchange.cpp @@ -0,0 +1,72 @@ +/* + * + * 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 "DirectExchange.h" +#include "ExchangeBinding.h" +#include <iostream> + +using namespace qpid::broker; +using namespace qpid::framing; + +DirectExchange::DirectExchange(const string& _name) : name(_name) { + +} + +void DirectExchange::bind(Queue::shared_ptr queue, const string& routingKey, FieldTable* args){ + lock.acquire(); + std::vector<Queue::shared_ptr>& queues(bindings[routingKey]); + std::vector<Queue::shared_ptr>::iterator i = find(queues.begin(), queues.end(), queue); + if(i == queues.end()){ + bindings[routingKey].push_back(queue); + queue->bound(new ExchangeBinding(this, queue, routingKey, args)); + } + lock.release(); +} + +void DirectExchange::unbind(Queue::shared_ptr queue, const string& routingKey, FieldTable* args){ + lock.acquire(); + std::vector<Queue::shared_ptr>& queues(bindings[routingKey]); + + std::vector<Queue::shared_ptr>::iterator i = find(queues.begin(), queues.end(), queue); + if(i < queues.end()){ + queues.erase(i); + if(queues.empty()){ + bindings.erase(routingKey); + } + } + lock.release(); +} + +void DirectExchange::route(Message::shared_ptr& msg, const string& routingKey, FieldTable* args){ + lock.acquire(); + std::vector<Queue::shared_ptr>& queues(bindings[routingKey]); + int count(0); + for(std::vector<Queue::shared_ptr>::iterator i = queues.begin(); i != queues.end(); i++, count++){ + (*i)->deliver(msg); + } + if(!count){ + std::cout << "WARNING: DirectExchange " << name << " could not route message with key " << routingKey << std::endl; + } + lock.release(); +} + +DirectExchange::~DirectExchange(){ + +} + + +const std::string DirectExchange::typeName("direct"); diff --git a/cpp/broker/src/ExchangeBinding.cpp b/cpp/broker/src/ExchangeBinding.cpp new file mode 100644 index 0000000000..6160a67fd3 --- /dev/null +++ b/cpp/broker/src/ExchangeBinding.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 "ExchangeBinding.h" +#include "Exchange.h" + +using namespace qpid::broker; +using namespace qpid::framing; + +ExchangeBinding::ExchangeBinding(Exchange* _e, Queue::shared_ptr _q, const string& _key, FieldTable* _args) : e(_e), q(_q), key(_key), args(_args){} + +void ExchangeBinding::cancel(){ + e->unbind(q, key, args); + delete this; +} + +ExchangeBinding::~ExchangeBinding(){ +} diff --git a/cpp/broker/src/ExchangeRegistry.cpp b/cpp/broker/src/ExchangeRegistry.cpp new file mode 100644 index 0000000000..0ee581af2f --- /dev/null +++ b/cpp/broker/src/ExchangeRegistry.cpp @@ -0,0 +1,43 @@ +/* + * + * 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 "ExchangeRegistry.h" +#include "MonitorImpl.h" + +using namespace qpid::broker; +using namespace qpid::concurrent; + +ExchangeRegistry::ExchangeRegistry() : lock(new MonitorImpl()){} + +ExchangeRegistry::~ExchangeRegistry(){ + delete lock; +} + +void ExchangeRegistry::declare(Exchange* exchange){ + exchanges[exchange->getName()] = exchange; +} + +void ExchangeRegistry::destroy(const string& name){ + if(exchanges[name]){ + delete exchanges[name]; + exchanges.erase(name); + } +} + +Exchange* ExchangeRegistry::get(const string& name){ + return exchanges[name]; +} diff --git a/cpp/broker/src/FanOutExchange.cpp b/cpp/broker/src/FanOutExchange.cpp new file mode 100644 index 0000000000..7f261d5eda --- /dev/null +++ b/cpp/broker/src/FanOutExchange.cpp @@ -0,0 +1,56 @@ +/* + * + * 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 "FanOutExchange.h" +#include "ExchangeBinding.h" +#include <algorithm> + +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::concurrent; + +FanOutExchange::FanOutExchange(const string& _name) : name(_name) {} + +void FanOutExchange::bind(Queue::shared_ptr queue, const string& routingKey, FieldTable* args){ + Locker locker(lock); + // Add if not already present. + Queue::vector::iterator i = std::find(bindings.begin(), bindings.end(), queue); + if (i == bindings.end()) { + bindings.push_back(queue); + queue->bound(new ExchangeBinding(this, queue, routingKey, args)); + } +} + +void FanOutExchange::unbind(Queue::shared_ptr queue, const string& routingKey, FieldTable* args){ + Locker locker(lock); + Queue::vector::iterator i = std::find(bindings.begin(), bindings.end(), queue); + if (i != bindings.end()) { + bindings.erase(i); + // TODO aconway 2006-09-14: What about the ExchangeBinding object? Don't we have to verify routingKey/args match? + } +} + +void FanOutExchange::route(Message::shared_ptr& msg, const string& routingKey, FieldTable* args){ + Locker locker(lock); + for(Queue::vector::iterator i = bindings.begin(); i != bindings.end(); ++i){ + (*i)->deliver(msg); + } +} + +FanOutExchange::~FanOutExchange() {} + +const std::string FanOutExchange::typeName("fanout"); diff --git a/cpp/broker/src/Message.cpp b/cpp/broker/src/Message.cpp new file mode 100644 index 0000000000..7afcd97934 --- /dev/null +++ b/cpp/broker/src/Message.cpp @@ -0,0 +1,97 @@ +/* + * + * 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 "MonitorImpl.h" +#include "Message.h" +#include "ExchangeRegistry.h" +#include <iostream> + +using namespace std::tr1;//for *_pointer_cast methods +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::concurrent; + + +Message::Message(const ConnectionToken* const _publisher, + const string& _exchange, const string& _routingKey, + bool _mandatory, bool _immediate) : publisher(_publisher), + exchange(_exchange), + routingKey(_routingKey), + mandatory(_mandatory), + immediate(_immediate){ + +} + +Message::~Message(){ +} + +void Message::setHeader(AMQHeaderBody::shared_ptr header){ + this->header = header; +} + +void Message::addContent(AMQContentBody::shared_ptr data){ + content.push_back(data); +} + +bool Message::isComplete(){ + return header.get() && (header->getContentSize() == contentSize()); +} + +void Message::deliver(OutputHandler* out, int channel, + string& consumerTag, u_int64_t deliveryTag, + u_int32_t framesize){ + + out->send(new AMQFrame(channel, new BasicDeliverBody(consumerTag, deliveryTag, false, exchange, routingKey))); + AMQBody::shared_ptr headerBody = static_pointer_cast<AMQBody, AMQHeaderBody>(header); + out->send(new AMQFrame(channel, headerBody)); + for(content_iterator i = content.begin(); i != content.end(); i++){ + if((*i)->size() > framesize){ + //TODO: need to split it + std::cout << "WARNING: Dropped message. Re-fragmentation not yet implemented." << std::endl; + }else{ + AMQBody::shared_ptr contentBody = static_pointer_cast<AMQBody, AMQContentBody>(*i); + out->send(new AMQFrame(channel, contentBody)); + } + } +} + +BasicHeaderProperties* Message::getHeaderProperties(){ + return dynamic_cast<BasicHeaderProperties*>(header->getProperties()); +} + +u_int64_t Message::contentSize(){ + u_int64_t size(0); + for(content_iterator i = content.begin(); i != content.end(); i++){ + size += (*i)->size(); + } + return size; +} + +const ConnectionToken* const Message::getPublisher(){ + return publisher; +} + +bool qpid::broker::route(Message::shared_ptr& msg, ExchangeRegistry* registry){ + Exchange* exchange = registry->get(msg->exchange); + if(exchange){ + exchange->route(msg, msg->routingKey, &(msg->getHeaderProperties()->getHeaders())); + return true; + }else{ + return false; + } +} + diff --git a/cpp/broker/src/NameGenerator.cpp b/cpp/broker/src/NameGenerator.cpp new file mode 100644 index 0000000000..46aa385a7e --- /dev/null +++ b/cpp/broker/src/NameGenerator.cpp @@ -0,0 +1,29 @@ +/* + * + * 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 "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/cpp/broker/src/Queue.cpp b/cpp/broker/src/Queue.cpp new file mode 100644 index 0000000000..f7b8605b03 --- /dev/null +++ b/cpp/broker/src/Queue.cpp @@ -0,0 +1,148 @@ +/* + * + * 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 "Queue.h" +#include "MonitorImpl.h" +#include <iostream> + +using namespace qpid::broker; +using namespace qpid::concurrent; + +Queue::Queue(const string& _name, bool _durable, u_int32_t _autodelete, const ConnectionToken* const _owner) : name(_name), + durable(_durable), + autodelete(_autodelete), + owner(_owner), + queueing(false), + dispatching(false), + next(0), + lastUsed(0), + exclusive(0){ + + if(autodelete) lastUsed = apr_time_as_msec(apr_time_now()); +} + +Queue::~Queue(){ + for(Binding* b = bindings.front(); !bindings.empty(); b = bindings.front()){ + b->cancel(); + bindings.pop(); + } +} + +void Queue::bound(Binding* b){ + bindings.push(b); +} + +void Queue::deliver(Message::shared_ptr& msg){ + Locker locker(lock); + if(queueing || !dispatch(msg)){ + queueing = true; + messages.push(msg); + } +} + +bool Queue::dispatch(Message::shared_ptr& msg){ + if(consumers.empty()){ + return false; + }else if(exclusive){ + if(!exclusive->deliver(msg)){ + std::cout << "WARNING: Dropping undeliverable message from queue with exclusive consumer." << std::endl; + } + return true; + }else{ + //deliver to next consumer + next = next % consumers.size(); + Consumer* c = consumers[next]; + int start = next; + while(c){ + next++; + if(c->deliver(msg)) return true; + + next = next % consumers.size(); + c = next == start ? 0 : consumers[next]; + } + return false; + } +} + +bool Queue::startDispatching(){ + Locker locker(lock); + if(queueing && !dispatching){ + dispatching = true; + return true; + }else{ + return false; + } +} + +void Queue::dispatch(){ + bool proceed = startDispatching(); + while(proceed){ + Locker locker(lock); + if(!messages.empty() && dispatch(messages.front())){ + messages.pop(); + }else{ + dispatching = false; + proceed = false; + queueing = !messages.empty(); + } + } +} + +void Queue::consume(Consumer* c, bool requestExclusive){ + Locker locker(lock); + if(exclusive) throw ExclusiveAccessException(); + if(requestExclusive){ + if(!consumers.empty()) throw ExclusiveAccessException(); + exclusive = c; + } + + if(autodelete && consumers.empty()) lastUsed = 0; + consumers.push_back(c); +} + +void Queue::cancel(Consumer* c){ + Locker locker(lock); + consumers.erase(find(consumers.begin(), consumers.end(), c)); + if(autodelete && consumers.empty()) lastUsed = apr_time_as_msec(apr_time_now()); + if(exclusive == c) exclusive = 0; +} + +Message::shared_ptr Queue::dequeue(){ + +} + +u_int32_t Queue::purge(){ + Locker locker(lock); + int count = messages.size(); + while(!messages.empty()) messages.pop(); + return count; +} + +u_int32_t Queue::getMessageCount() const{ + Locker locker(lock); + return messages.size(); +} + +u_int32_t Queue::getConsumerCount() const{ + Locker locker(lock); + return consumers.size(); +} + +bool Queue::canAutoDelete() const{ + Locker locker(lock); + return lastUsed && ((apr_time_as_msec(apr_time_now()) - lastUsed) > autodelete); +} diff --git a/cpp/broker/src/QueueRegistry.cpp b/cpp/broker/src/QueueRegistry.cpp new file mode 100644 index 0000000000..f807415314 --- /dev/null +++ b/cpp/broker/src/QueueRegistry.cpp @@ -0,0 +1,72 @@ +/* + * + * 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 "QueueRegistry.h" +#include "MonitorImpl.h" +#include "SessionHandlerImpl.h" +#include <sstream> +#include <assert.h> + +using namespace qpid::broker; +using namespace qpid::concurrent; + +QueueRegistry::QueueRegistry() : counter(1){} + +QueueRegistry::~QueueRegistry(){} + +std::pair<Queue::shared_ptr, bool> +QueueRegistry::declare(const string& declareName, bool durable, u_int32_t autoDelete, const ConnectionToken* owner) +{ + Locker 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, durable, autoDelete, owner)); + 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::destroy(const string& name){ + Locker locker(lock); + queues.erase(name); +} + +Queue::shared_ptr QueueRegistry::find(const string& name){ + Locker 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; +} diff --git a/cpp/broker/src/SessionHandlerFactoryImpl.cpp b/cpp/broker/src/SessionHandlerFactoryImpl.cpp new file mode 100644 index 0000000000..661cb4ef81 --- /dev/null +++ b/cpp/broker/src/SessionHandlerFactoryImpl.cpp @@ -0,0 +1,40 @@ +/* + * + * 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 "SessionHandlerFactoryImpl.h" +#include "SessionHandlerImpl.h" +#include "FanOutExchange.h" + +using namespace qpid::broker; +using namespace qpid::io; + +SessionHandlerFactoryImpl::SessionHandlerFactoryImpl(u_int32_t _timeout) : timeout(_timeout), cleaner(&queues, timeout/10){ + exchanges.declare(new DirectExchange("amq.direct")); + exchanges.declare(new TopicExchange("amq.topic")); + exchanges.declare(new FanOutExchange("amq.fanout")); + cleaner.start(); +} + +SessionHandler* SessionHandlerFactoryImpl::create(SessionContext* ctxt){ + return new SessionHandlerImpl(ctxt, &queues, &exchanges, &cleaner, timeout); +} + +SessionHandlerFactoryImpl::~SessionHandlerFactoryImpl(){ + cleaner.stop(); + exchanges.destroy("amq.direct"); + exchanges.destroy("amq.topic"); +} diff --git a/cpp/broker/src/SessionHandlerImpl.cpp b/cpp/broker/src/SessionHandlerImpl.cpp new file mode 100644 index 0000000000..19e243a01b --- /dev/null +++ b/cpp/broker/src/SessionHandlerImpl.cpp @@ -0,0 +1,378 @@ +/* + * + * 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 <iostream> +#include "SessionHandlerImpl.h" +#include "FanOutExchange.h" +#include "assert.h" + +using namespace std::tr1; +using namespace qpid::broker; +using namespace qpid::io; +using namespace qpid::framing; +using namespace qpid::concurrent; + +SessionHandlerImpl::SessionHandlerImpl(SessionContext* _context, + QueueRegistry* _queues, + ExchangeRegistry* _exchanges, + AutoDelete* _cleaner, + const u_int32_t _timeout) : context(_context), + queues(_queues), + exchanges(_exchanges), + cleaner(_cleaner), + timeout(_timeout), + channelHandler(new ChannelHandlerImpl(this)), + connectionHandler(new ConnectionHandlerImpl(this)), + basicHandler(new BasicHandlerImpl(this)), + exchangeHandler(new ExchangeHandlerImpl(this)), + queueHandler(new QueueHandlerImpl(this)), + framemax(65536), + heartbeat(0){ + +} + +SessionHandlerImpl::~SessionHandlerImpl(){ + // TODO aconway 2006-09-07: Should be auto_ptr or plain members. + delete channelHandler; + delete connectionHandler; + delete basicHandler; + delete exchangeHandler; + delete queueHandler; +} + +Queue::shared_ptr SessionHandlerImpl::getQueue(const string& name, u_int16_t channel){ + Queue::shared_ptr queue; + if (name.empty()) { + queue = channels[channel]->getDefaultQueue(); + if (!queue) throw ConnectionException( 530, "Queue must be specified or previously declared" ); + } else { + queue = queues->find(name); + if (queue == 0) { + throw ChannelException( 404, "Queue not found: " + name); + } + } + return queue; +} + + +Exchange* SessionHandlerImpl::findExchange(const string& name){ + exchanges->getLock()->acquire(); + Exchange* exchange(exchanges->get(name)); + exchanges->getLock()->release(); + return exchange; +} + +void SessionHandlerImpl::received(qpid::framing::AMQFrame* frame){ + u_int16_t channel = frame->getChannel(); + AMQBody::shared_ptr body = frame->getBody(); + AMQMethodBody::shared_ptr method; + + switch(body->type()) + { + case METHOD_BODY: + method = dynamic_pointer_cast<AMQMethodBody, AMQBody>(body); + try{ + method->invoke(*this, channel); + }catch(ChannelException& e){ + channels[channel]->close(); + channels.erase(channel); + context->send(new AMQFrame(channel, new ChannelCloseBody(e.code, e.text, method->amqpClassId(), method->amqpMethodId()))); + }catch(ConnectionException& e){ + context->send(new AMQFrame(0, new ConnectionCloseBody(e.code, e.text, method->amqpClassId(), method->amqpMethodId()))); + } + break; + + case HEADER_BODY: + this->handleHeader(channel, dynamic_pointer_cast<AMQHeaderBody, AMQBody>(body)); + break; + + case CONTENT_BODY: + this->handleContent(channel, dynamic_pointer_cast<AMQContentBody, AMQBody>(body)); + break; + + case HEARTBEAT_BODY: + //channel must be 0 + this->handleHeartbeat(dynamic_pointer_cast<AMQHeartbeatBody, AMQBody>(body)); + break; + } +} + +void SessionHandlerImpl::initiated(qpid::framing::ProtocolInitiation* header){ + //send connection start + FieldTable properties; + string mechanisms("PLAIN"); + string locales("en_US"); + context->send(new AMQFrame(0, new ConnectionStartBody(8, 0, properties, mechanisms, locales))); +} + +void SessionHandlerImpl::idleOut(){ + +} + +void SessionHandlerImpl::idleIn(){ + +} + +void SessionHandlerImpl::closed(){ + for(channel_iterator i = channels.begin(); i != channels.end(); i = channels.begin()){ + Channel* c = i->second; + channels.erase(i); + c->close(); + delete c; + } + for(queue_iterator i = exclusiveQueues.begin(); i < exclusiveQueues.end(); i = exclusiveQueues.begin()){ + string name = (*i)->getName(); + queues->destroy(name); + exclusiveQueues.erase(i); + } +} + +void SessionHandlerImpl::handleHeader(u_int16_t channel, AMQHeaderBody::shared_ptr body){ + channels[channel]->handleHeader(body, exchanges); +} + +void SessionHandlerImpl::handleContent(u_int16_t channel, AMQContentBody::shared_ptr body){ + channels[channel]->handleContent(body, exchanges); +} + +void SessionHandlerImpl::handleHeartbeat(AMQHeartbeatBody::shared_ptr body){ + std::cout << "SessionHandlerImpl::handleHeartbeat()" << std::endl; +} + +void SessionHandlerImpl::ConnectionHandlerImpl::startOk(u_int16_t channel, FieldTable& clientProperties, string& mechanism, + string& response, string& locale){ + + parent->context->send(new AMQFrame(0, new ConnectionTuneBody(100, parent->framemax, parent->heartbeat))); +} + +void SessionHandlerImpl::ConnectionHandlerImpl::secureOk(u_int16_t channel, string& response){} + +void SessionHandlerImpl::ConnectionHandlerImpl::tuneOk(u_int16_t channel, u_int16_t channelmax, u_int32_t framemax, u_int16_t heartbeat){ + parent->framemax = framemax; + parent->heartbeat = heartbeat; +} + +void SessionHandlerImpl::ConnectionHandlerImpl::open(u_int16_t channel, string& virtualHost, string& capabilities, bool insist){ + string knownhosts; + parent->context->send(new AMQFrame(0, new ConnectionOpenOkBody(knownhosts))); +} + +void SessionHandlerImpl::ConnectionHandlerImpl::close(u_int16_t channel, u_int16_t replyCode, string& replyText, + u_int16_t classId, u_int16_t methodId){ + + parent->context->send(new AMQFrame(0, new ConnectionCloseOkBody())); + parent->context->close(); +} + +void SessionHandlerImpl::ConnectionHandlerImpl::closeOk(u_int16_t channel){ + parent->context->close(); +} + + + +void SessionHandlerImpl::ChannelHandlerImpl::open(u_int16_t channel, string& outOfBand){ + parent->channels[channel] = new Channel(parent->context, channel, parent->framemax); + parent->context->send(new AMQFrame(channel, new ChannelOpenOkBody())); +} + +void SessionHandlerImpl::ChannelHandlerImpl::flow(u_int16_t channel, bool active){} +void SessionHandlerImpl::ChannelHandlerImpl::flowOk(u_int16_t channel, bool active){} + +void SessionHandlerImpl::ChannelHandlerImpl::close(u_int16_t channel, u_int16_t replyCode, string& replyText, + u_int16_t classId, u_int16_t methodId){ + Channel* c = parent->channels[channel]; + parent->channels.erase(channel); + c->close(); + delete c; + parent->context->send(new AMQFrame(channel, new ChannelCloseOkBody())); +} + +void SessionHandlerImpl::ChannelHandlerImpl::closeOk(u_int16_t channel){} + + + +void SessionHandlerImpl::ExchangeHandlerImpl::declare(u_int16_t channel, u_int16_t ticket, string& exchange, string& type, + bool passive, bool durable, bool autoDelete, bool internal, bool nowait, + FieldTable& arguments){ + + if(!passive && ( + type != TopicExchange::typeName && + type != DirectExchange::typeName && + type != FanOutExchange::typeName) + ) + { + throw ChannelException(540, "Exchange type not implemented: " + type); + } + + parent->exchanges->getLock()->acquire(); + if(!parent->exchanges->get(exchange)){ + if(type == TopicExchange::typeName){ + parent->exchanges->declare(new TopicExchange(exchange)); + }else if(type == DirectExchange::typeName){ + parent->exchanges->declare(new DirectExchange(exchange)); + }else if(type == FanOutExchange::typeName){ + parent->exchanges->declare(new DirectExchange(exchange)); + } + } + parent->exchanges->getLock()->release(); + if(!nowait){ + parent->context->send(new AMQFrame(channel, new ExchangeDeclareOkBody())); + } +} + +void SessionHandlerImpl::ExchangeHandlerImpl::delete_(u_int16_t channel, u_int16_t ticket, string& exchange, bool ifUnused, bool nowait){ + //TODO: implement unused + parent->exchanges->getLock()->acquire(); + parent->exchanges->destroy(exchange); + parent->exchanges->getLock()->release(); + if(!nowait) parent->context->send(new AMQFrame(channel, new ExchangeDeleteOkBody())); +} + + + + +void SessionHandlerImpl::QueueHandlerImpl::declare(u_int16_t channel, u_int16_t ticket, string& name, + bool passive, bool durable, bool exclusive, + bool autoDelete, bool nowait, FieldTable& arguments){ + Queue::shared_ptr queue; + if (passive && !name.empty()) { + queue = parent->getQueue(name, channel); + } else { + std::pair<Queue::shared_ptr, bool> queue_created = parent->queues->declare(name, durable, autoDelete ? parent->timeout : 0, exclusive ? parent : 0); + queue = queue_created.first; + assert(queue); + if (queue_created.second) { // This is a new queue + parent->channels[channel]->setDefaultQueue(queue); + //add default binding: + parent->exchanges->get("amq.direct")->bind(queue, name, 0); + if(exclusive){ + parent->exclusiveQueues.push_back(queue); + } else if(autoDelete){ + parent->cleaner->add(queue); + } + } + } + if(exclusive && !queue->isExclusiveOwner(parent)){ + throw ChannelException(405, "Cannot grant exclusive access to queue"); + } + if(!nowait){ + name = queue->getName(); + QueueDeclareOkBody* response = new QueueDeclareOkBody(name, queue->getMessageCount(), queue->getConsumerCount()); + parent->context->send(new AMQFrame(channel, response)); + } +} + +void SessionHandlerImpl::QueueHandlerImpl::bind(u_int16_t channel, u_int16_t ticket, string& queueName, + string& exchangeName, string& routingKey, bool nowait, + FieldTable& arguments){ + + Queue::shared_ptr queue = parent->getQueue(queueName, channel); + Exchange* exchange = parent->exchanges->get(exchangeName); + if(exchange){ + if(routingKey.size() == 0 && queueName.size() == 0) routingKey = queue->getName(); + exchange->bind(queue, routingKey, &arguments); + if(!nowait) parent->context->send(new AMQFrame(channel, new QueueBindOkBody())); + }else{ + throw ChannelException(404, "Bind failed. No such exchange: " + exchangeName); + } +} + +void SessionHandlerImpl::QueueHandlerImpl::purge(u_int16_t channel, u_int16_t ticket, string& queueName, bool nowait){ + + Queue::shared_ptr queue = parent->getQueue(queueName, channel); + int count = queue->purge(); + if(!nowait) parent->context->send(new AMQFrame(channel, new QueuePurgeOkBody(count))); +} + +void SessionHandlerImpl::QueueHandlerImpl::delete_(u_int16_t channel, u_int16_t ticket, string& queue, + bool ifUnused, bool ifEmpty, bool nowait){ + ChannelException error(0, ""); + int count(0); + Queue::shared_ptr q = parent->getQueue(queue, channel); + if(ifEmpty && q->getMessageCount() > 0){ + throw ChannelException(406, "Queue not empty."); + }else if(ifUnused && q->getConsumerCount() > 0){ + throw ChannelException(406, "Queue in use."); + }else{ + //remove the queue from the list of exclusive queues if necessary + if(q->isExclusiveOwner(parent)){ + queue_iterator i = find(parent->exclusiveQueues.begin(), parent->exclusiveQueues.end(), q); + if(i < parent->exclusiveQueues.end()) parent->exclusiveQueues.erase(i); + } + count = q->getMessageCount(); + parent->queues->destroy(queue); + } + if(!nowait) parent->context->send(new AMQFrame(channel, new QueueDeleteOkBody(count))); +} + + + + +void SessionHandlerImpl::BasicHandlerImpl::qos(u_int16_t channel, u_int32_t prefetchSize, u_int16_t prefetchCount, bool global){ + //TODO: handle global + //TODO: channel doesn't do anything with these qos parameters yet + parent->channels[channel]->setPrefetchSize(prefetchSize); + parent->channels[channel]->setPrefetchCount(prefetchCount); + parent->context->send(new AMQFrame(channel, new BasicQosOkBody())); +} + +void SessionHandlerImpl::BasicHandlerImpl::consume(u_int16_t channelId, u_int16_t ticket, + string& queueName, string& consumerTag, + bool noLocal, bool noAck, bool exclusive, + bool nowait){ + + //TODO: implement nolocal + Queue::shared_ptr queue = parent->getQueue(queueName, channelId); + Channel* channel = parent->channels[channelId]; + if(!consumerTag.empty() && channel->exists(consumerTag)){ + throw ConnectionException(530, "Consumer tags must be unique"); + } + + try{ + channel->consume(consumerTag, queue, !noAck, exclusive, noLocal ? parent : 0); + if(!nowait) parent->context->send(new AMQFrame(channelId, new BasicConsumeOkBody(consumerTag))); + + //allow messages to be dispatched if required as there is now a consumer: + queue->dispatch(); + }catch(ExclusiveAccessException& e){ + if(exclusive) throw ChannelException(403, "Exclusive access cannot be granted"); + else throw ChannelException(403, "Access would violate previously granted exclusivity"); + } + +} + +void SessionHandlerImpl::BasicHandlerImpl::cancel(u_int16_t channel, string& consumerTag, bool nowait){ + parent->channels[channel]->cancel(consumerTag); + if(!nowait) parent->context->send(new AMQFrame(channel, new BasicCancelOkBody(consumerTag))); +} + +void SessionHandlerImpl::BasicHandlerImpl::publish(u_int16_t channel, u_int16_t ticket, + string& exchange, string& routingKey, + bool mandatory, bool immediate){ + + Message* msg = new Message(parent, exchange.length() ? exchange : "amq.direct", routingKey, mandatory, immediate); + parent->channels[channel]->handlePublish(msg); +} + +void SessionHandlerImpl::BasicHandlerImpl::get(u_int16_t channel, u_int16_t ticket, string& queue, bool noAck){} + +void SessionHandlerImpl::BasicHandlerImpl::ack(u_int16_t channel, u_int64_t deliveryTag, bool multiple){} + +void SessionHandlerImpl::BasicHandlerImpl::reject(u_int16_t channel, u_int64_t deliveryTag, bool requeue){} + +void SessionHandlerImpl::BasicHandlerImpl::recover(u_int16_t channel, bool requeue){} + diff --git a/cpp/broker/src/TopicExchange.cpp b/cpp/broker/src/TopicExchange.cpp new file mode 100644 index 0000000000..e0248958f9 --- /dev/null +++ b/cpp/broker/src/TopicExchange.cpp @@ -0,0 +1,62 @@ +/* + * + * 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 "TopicExchange.h" +#include "ExchangeBinding.h" + +using namespace qpid::broker; +using namespace qpid::framing; + +TopicExchange::TopicExchange(const string& _name) : name(_name) { + +} + +void TopicExchange::bind(Queue::shared_ptr queue, const string& routingKey, FieldTable* args){ + lock.acquire(); + bindings[routingKey].push_back(queue); + queue->bound(new ExchangeBinding(this, queue, routingKey, args)); + lock.release(); +} + +void TopicExchange::unbind(Queue::shared_ptr queue, const string& routingKey, FieldTable* args){ + lock.acquire(); + std::vector<Queue::shared_ptr>& queues(bindings[routingKey]); + + std::vector<Queue::shared_ptr>::iterator i = find(queues.begin(), queues.end(), queue); + if(i < queues.end()){ + queues.erase(i); + if(queues.empty()){ + bindings.erase(routingKey); + } + } + lock.release(); +} + +void TopicExchange::route(Message::shared_ptr& msg, const string& routingKey, FieldTable* args){ + lock.acquire(); + std::vector<Queue::shared_ptr>& queues(bindings[routingKey]); + for(std::vector<Queue::shared_ptr>::iterator i = queues.begin(); i != queues.end(); i++){ + (*i)->deliver(msg); + } + lock.release(); +} + +TopicExchange::~TopicExchange(){ + +} + +const std::string TopicExchange::typeName("topic"); diff --git a/cpp/broker/test/Makefile b/cpp/broker/test/Makefile new file mode 100644 index 0000000000..172ce564bf --- /dev/null +++ b/cpp/broker/test/Makefile @@ -0,0 +1,20 @@ +# +# 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. +# + +QPID_HOME = ../../.. +LDLIBS=-lapr-1 -lcppunit $(COMMON_LIB) $(BROKER_LIB) +include ${QPID_HOME}/cpp/test_plugins.mk + diff --git a/cpp/broker/test/QueueRegistryTest.cpp b/cpp/broker/test/QueueRegistryTest.cpp new file mode 100644 index 0000000000..c4ad40b5cd --- /dev/null +++ b/cpp/broker/test/QueueRegistryTest.cpp @@ -0,0 +1,79 @@ +#include "QueueRegistry.h" +#include <cppunit/TestCase.h> +#include <cppunit/TextTestRunner.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.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/cpp/broker/test/exchange_test.cpp b/cpp/broker/test/exchange_test.cpp new file mode 100644 index 0000000000..6605f2685b --- /dev/null +++ b/cpp/broker/test/exchange_test.cpp @@ -0,0 +1,68 @@ +/* + * + * 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 "DirectExchange.h" +#include "Exchange.h" +#include "Queue.h" +#include "TopicExchange.h" +#include <cppunit/TestCase.h> +#include <cppunit/TextTestRunner.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> +#include <iostream> + +using namespace qpid::broker; +using namespace qpid::concurrent; + +class ExchangeTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(ExchangeTest); + CPPUNIT_TEST(testMe); + CPPUNIT_TEST_SUITE_END(); + + public: + + // TODO aconway 2006-09-12: Need more detailed tests. + + void testMe() + { + Queue::shared_ptr queue(new Queue("queue", true, true)); + Queue::shared_ptr queue2(new Queue("queue2", true, 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(); + + Message::shared_ptr msg = Message::shared_ptr(new Message(0, "e", "A", true, true)); + topic.route(msg, "abc", 0); + direct.route(msg, "abc", 0); + + // TODO aconway 2006-09-12: TODO Why no assertions? + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(ExchangeTest); diff --git a/cpp/broker/test/message_test.cpp b/cpp/broker/test/message_test.cpp new file mode 100644 index 0000000000..94d25a831e --- /dev/null +++ b/cpp/broker/test/message_test.cpp @@ -0,0 +1,57 @@ +/* + * + * 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 "Message.h" +#include <cppunit/TestCase.h> +#include <cppunit/TextTestRunner.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> +#include <iostream> + +using namespace qpid::broker; +using namespace qpid::framing; +using namespace qpid::concurrent; + +class MessageTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(MessageTest); + CPPUNIT_TEST(testMe); + CPPUNIT_TEST_SUITE_END(); + + public: + + // TODO aconway 2006-09-12: Need more detailed tests, + // need tests to assert something! + // + void testMe() + { + APRBase::increment(); + const int size(10); + for(int i = 0; i < size; i++){ + Message::shared_ptr msg = Message::shared_ptr(new Message(0, "A", "B", true, true)); + msg->setHeader(AMQHeaderBody::shared_ptr(new AMQHeaderBody())); + msg->addContent(AMQContentBody::shared_ptr(new AMQContentBody())); + msg.reset(); + } + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(MessageTest); + diff --git a/cpp/broker/test/queue_test.cpp b/cpp/broker/test/queue_test.cpp new file mode 100644 index 0000000000..aa423e7e08 --- /dev/null +++ b/cpp/broker/test/queue_test.cpp @@ -0,0 +1,138 @@ + /* + * + * 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 "Queue.h" +#include "QueueRegistry.h" +#include <cppunit/TestCase.h> +#include <cppunit/TextTestRunner.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> +#include <iostream> + +using namespace qpid::broker; +using namespace qpid::concurrent; + + +class TestBinding : public virtual Binding{ + bool cancelled; + +public: + TestBinding(); + virtual void cancel(); + bool isCancelled(); +}; + +class TestConsumer : public virtual Consumer{ +public: + Message::shared_ptr last; + + virtual bool deliver(Message::shared_ptr& msg); +}; + + +class QueueTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(QueueTest); + CPPUNIT_TEST(testMe); + CPPUNIT_TEST_SUITE_END(); + + public: + void testMe() + { + Queue::shared_ptr queue(new Queue("my_queue", true, true)); + + //Test adding consumers: + TestConsumer c1; + TestConsumer c2; + queue->consume(&c1); + queue->consume(&c2); + + CPPUNIT_ASSERT_EQUAL(u_int32_t(2), queue->getConsumerCount()); + + //Test basic delivery: + Message::shared_ptr msg1 = Message::shared_ptr(new Message(0, "e", "A", true, true)); + Message::shared_ptr msg2 = Message::shared_ptr(new Message(0, "e", "B", true, true)); + Message::shared_ptr msg3 = Message::shared_ptr(new Message(0, "e", "C", true, true)); + + queue->deliver(msg1); + CPPUNIT_ASSERT_EQUAL(msg1.get(), c1.last.get()); + + queue->deliver(msg2); + CPPUNIT_ASSERT_EQUAL(msg2.get(), c2.last.get()); + + queue->deliver(msg3); + CPPUNIT_ASSERT_EQUAL(msg3.get(), c1.last.get()); + + //Test cancellation: + queue->cancel(&c1); + CPPUNIT_ASSERT_EQUAL(u_int32_t(1), queue->getConsumerCount()); + queue->cancel(&c2); + CPPUNIT_ASSERT_EQUAL(u_int32_t(0), queue->getConsumerCount()); + + //Test bindings: + TestBinding a; + TestBinding b; + queue->bound(&a); + queue->bound(&b); + + queue.reset(); + + CPPUNIT_ASSERT(a.isCancelled()); + CPPUNIT_ASSERT(b.isCancelled()); + + //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")); + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(QueueTest); + +//TestBinding +TestBinding::TestBinding() : cancelled(false) {} + +void TestBinding::cancel(){ + CPPUNIT_ASSERT(!cancelled); + cancelled = true; +} + +bool TestBinding::isCancelled(){ + return cancelled; +} + +//TestConsumer +bool TestConsumer::deliver(Message::shared_ptr& msg){ + last = msg; + return true; +} + diff --git a/cpp/client/Makefile b/cpp/client/Makefile new file mode 100644 index 0000000000..d08b92fe2b --- /dev/null +++ b/cpp/client/Makefile @@ -0,0 +1,43 @@ +# +# 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. +# + +# +# Build client library. +# + +QPID_HOME = ../.. +include ${QPID_HOME}/cpp/options.mk + +SOURCES := $(wildcard src/*.cpp) +OBJECTS := $(subst .cpp,.o,$(SOURCES)) +CLIENT_LIB=$(LIB_DIR)/libqpid_client.so.1.0 + +.PHONY: all test clean + +all: $(CLIENT_LIB) + +test: + @$(MAKE) -C test all + +clean: + -@rm -f $(CLIENT_LIB) $(OBJECTS) src/*.d + $(MAKE) -C test clean + +$(CLIENT_LIB): $(OBJECTS) + $(CXX) -shared -o $@ $^ $(LDFLAGS) $(COMMON_LIB) + +# Dependencies +-include $(SOURCES:.cpp=.d) diff --git a/cpp/client/inc/Channel.h b/cpp/client/inc/Channel.h new file mode 100644 index 0000000000..debecf922e --- /dev/null +++ b/cpp/client/inc/Channel.h @@ -0,0 +1,127 @@ +/* + * + * 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 <map> +#include <string> +#include <queue> +#include "sys/types.h" + +#ifndef _Channel_ +#define _Channel_ + +#include "amqp_framing.h" + +#include "ThreadFactory.h" + +#include "Connection.h" +#include "Exchange.h" +#include "IncomingMessage.h" +#include "Message.h" +#include "MessageListener.h" +#include "Queue.h" +#include "ResponseHandler.h" +#include "ReturnedMessageHandler.h" + +namespace qpid { +namespace client { + enum ack_modes {NO_ACK=0, AUTO_ACK=1, LAZY_ACK=2, CLIENT_ACK=3}; + + class Channel : private virtual qpid::framing::BodyHandler, public virtual qpid::concurrent::Runnable{ + struct Consumer{ + MessageListener* listener; + int ackMode; + int count; + u_int64_t lastDeliveryTag; + }; + typedef std::map<string,Consumer*>::iterator consumer_iterator; + + u_int16_t id; + Connection* con; + qpid::concurrent::ThreadFactory* threadFactory; + qpid::concurrent::Thread* dispatcher; + qpid::framing::OutputHandler* out; + IncomingMessage* incoming; + ResponseHandler responses; + std::queue<IncomingMessage*> messages;//holds returned messages or those delivered for a consume + IncomingMessage* retrieved;//holds response to basic.get + qpid::concurrent::Monitor* dispatchMonitor; + qpid::concurrent::Monitor* retrievalMonitor; + std::map<std::string, Consumer*> consumers; + ReturnedMessageHandler* returnsHandler; + bool closed; + + u_int16_t prefetch; + const bool transactional; + + void enqueue(); + void retrieve(Message& msg); + IncomingMessage* dequeue(); + void dispatch(); + void stop(); + void sendAndReceive(qpid::framing::AMQFrame* frame, const qpid::framing::AMQMethodBody& body); + void deliver(Consumer* consumer, Message& msg); + void setQos(); + void cancelAll(); + + virtual void handleMethod(qpid::framing::AMQMethodBody::shared_ptr body); + virtual void handleHeader(qpid::framing::AMQHeaderBody::shared_ptr body); + virtual void handleContent(qpid::framing::AMQContentBody::shared_ptr body); + virtual void handleHeartbeat(qpid::framing::AMQHeartbeatBody::shared_ptr body); + + public: + Channel(bool transactional = false, u_int16_t prefetch = 500); + ~Channel(); + + void declareExchange(Exchange& exchange, bool synch = true); + void deleteExchange(Exchange& exchange, bool synch = true); + void declareQueue(Queue& queue, bool synch = true); + void deleteQueue(Queue& queue, bool ifunused = false, bool ifempty = false, bool synch = true); + void bind(const Exchange& exchange, const Queue& queue, const std::string& key, + const qpid::framing::FieldTable& args, bool synch = true); + void consume(Queue& queue, std::string& tag, MessageListener* listener, + int ackMode = NO_ACK, bool noLocal = false, bool synch = true); + void cancel(std::string& tag, bool synch = true); + bool get(Message& msg, const Queue& queue, int ackMode = NO_ACK); + void publish(Message& msg, const Exchange& exchange, const std::string& routingKey, + bool mandatory = false, bool immediate = false); + + void commit(); + void rollback(); + + void setPrefetch(u_int16_t prefetch); + + /** + * Start message dispatching on a new thread + */ + void start(); + /** + * Do message dispatching on this thread + */ + void run(); + + void close(); + + void setReturnedMessageHandler(ReturnedMessageHandler* handler); + + friend class Connection; + }; + +} +} + + +#endif diff --git a/cpp/client/inc/Connection.h b/cpp/client/inc/Connection.h new file mode 100644 index 0000000000..89169e92b1 --- /dev/null +++ b/cpp/client/inc/Connection.h @@ -0,0 +1,105 @@ +/* + * + * 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 <map> +#include <string> + +#ifndef _Connection_ +#define _Connection_ + +#include "QpidError.h" +#include "Connector.h" +#include "ShutdownHandler.h" +#include "TimeoutHandler.h" + +#include "amqp_framing.h" +#include "Exchange.h" +#include "IncomingMessage.h" +#include "Message.h" +#include "MessageListener.h" +#include "Queue.h" +#include "ResponseHandler.h" + +namespace qpid { +namespace client { + + class Channel; + + class Connection : public virtual qpid::framing::InputHandler, + public virtual qpid::io::TimeoutHandler, + public virtual qpid::io::ShutdownHandler, + private virtual qpid::framing::BodyHandler{ + + typedef std::map<int, Channel*>::iterator iterator; + + static u_int16_t channelIdCounter; + + std::string host; + int port; + const u_int32_t max_frame_size; + std::map<int, Channel*> channels; + qpid::io::Connector* connector; + qpid::framing::OutputHandler* out; + ResponseHandler responses; + volatile bool closed; + + void channelException(Channel* channel, qpid::framing::AMQMethodBody* body, QpidError& e); + void error(int code, const string& msg, int classid = 0, int methodid = 0); + void closeChannel(Channel* channel, u_int16_t code, string& text, u_int16_t classId = 0, u_int16_t methodId = 0); + void sendAndReceive(qpid::framing::AMQFrame* frame, const qpid::framing::AMQMethodBody& body); + + virtual void handleMethod(qpid::framing::AMQMethodBody::shared_ptr body); + virtual void handleHeader(qpid::framing::AMQHeaderBody::shared_ptr body); + virtual void handleContent(qpid::framing::AMQContentBody::shared_ptr body); + virtual void handleHeartbeat(qpid::framing::AMQHeartbeatBody::shared_ptr body); + + public: + + Connection(bool debug = false, u_int32_t max_frame_size = 65536); + ~Connection(); + 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 openChannel(Channel* channel); + /* + * Requests that the server close this channel, then removes + * the association to the channel from this connection + */ + void closeChannel(Channel* channel); + /* + * Removes the channel from association with this connection, + * without sending a close request to the server. + */ + void removeChannel(Channel* channel); + + virtual void received(qpid::framing::AMQFrame* frame); + + virtual void idleOut(); + virtual void idleIn(); + + virtual void shutdown(); + + inline u_int32_t getMaxFrameSize(){ return max_frame_size; } + }; + + +} +} + + +#endif diff --git a/cpp/client/inc/Exchange.h b/cpp/client/inc/Exchange.h new file mode 100644 index 0000000000..66593a41cc --- /dev/null +++ b/cpp/client/inc/Exchange.h @@ -0,0 +1,49 @@ +/* + * + * 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> + +#ifndef _Exchange_ +#define _Exchange_ + +namespace qpid { +namespace client { + + class Exchange{ + const std::string name; + const std::string type; + + public: + + static const std::string DIRECT_EXCHANGE; + static const std::string TOPIC_EXCHANGE; + static const std::string HEADERS_EXCHANGE; + + static const Exchange DEFAULT_DIRECT_EXCHANGE; + static const Exchange DEFAULT_TOPIC_EXCHANGE; + static const Exchange DEFAULT_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/cpp/client/inc/IncomingMessage.h b/cpp/client/inc/IncomingMessage.h new file mode 100644 index 0000000000..1fee6af433 --- /dev/null +++ b/cpp/client/inc/IncomingMessage.h @@ -0,0 +1,60 @@ +/* + * + * 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 <vector> +#include "amqp_framing.h" + +#ifndef _IncomingMessage_ +#define _IncomingMessage_ + +#include "Message.h" + +namespace qpid { +namespace client { + + class IncomingMessage{ + //content will be preceded by one of these method frames + qpid::framing::BasicDeliverBody::shared_ptr delivered; + qpid::framing::BasicReturnBody::shared_ptr returned; + qpid::framing::BasicGetOkBody::shared_ptr response; + qpid::framing::AMQHeaderBody::shared_ptr header; + std::vector<qpid::framing::AMQContentBody::shared_ptr> content; + + long contentSize(); + public: + IncomingMessage(qpid::framing::BasicDeliverBody::shared_ptr intro); + IncomingMessage(qpid::framing::BasicReturnBody::shared_ptr intro); + IncomingMessage(qpid::framing::BasicGetOkBody::shared_ptr intro); + ~IncomingMessage(); + void setHeader(qpid::framing::AMQHeaderBody::shared_ptr header); + void addContent(qpid::framing::AMQContentBody::shared_ptr content); + bool isComplete(); + bool isReturn(); + bool isDelivery(); + bool isResponse(); + string& getConsumerTag();//only relevant if isDelivery() + qpid::framing::AMQHeaderBody::shared_ptr& getHeader(); + u_int64_t getDeliveryTag(); + void getData(string& data); + }; + +} +} + + +#endif diff --git a/cpp/client/inc/Message.h b/cpp/client/inc/Message.h new file mode 100644 index 0000000000..f8a5aef565 --- /dev/null +++ b/cpp/client/inc/Message.h @@ -0,0 +1,86 @@ +/* + * + * 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 "amqp_framing.h" + +#ifndef _Message_ +#define _Message_ + + +namespace qpid { +namespace client { + + class Message{ + qpid::framing::AMQHeaderBody::shared_ptr header; + string data; + bool redelivered; + u_int64_t deliveryTag; + + qpid::framing::BasicHeaderProperties* getHeaderProperties(); + Message(qpid::framing::AMQHeaderBody::shared_ptr& header); + public: + Message(); + ~Message(); + + inline std::string getData(){ return data; } + inline void setData(const std::string& data){ this->data = data; } + + inline bool isRedelivered(){ return redelivered; } + inline void setRedelivered(bool redelivered){ this->redelivered = redelivered; } + + inline u_int64_t getDeliveryTag(){ return deliveryTag; } + + std::string& getContentType(); + std::string& getContentEncoding(); + qpid::framing::FieldTable& getHeaders(); + u_int8_t getDeliveryMode(); + u_int8_t getPriority(); + std::string& getCorrelationId(); + std::string& getReplyTo(); + std::string& getExpiration(); + std::string& getMessageId(); + u_int64_t getTimestamp(); + std::string& getType(); + std::string& getUserId(); + std::string& getAppId(); + std::string& getClusterId(); + + void setContentType(std::string& type); + void setContentEncoding(std::string& encoding); + void setHeaders(qpid::framing::FieldTable& headers); + void setDeliveryMode(u_int8_t mode); + void setPriority(u_int8_t priority); + void setCorrelationId(std::string& correlationId); + void setReplyTo(std::string& replyTo); + void setExpiration(std::string& expiration); + void setMessageId(std::string& messageId); + void setTimestamp(u_int64_t timestamp); + void setType(std::string& type); + void setUserId(std::string& userId); + void setAppId(std::string& appId); + void setClusterId(std::string& clusterId); + + + friend class Channel; + }; + +} +} + + +#endif diff --git a/cpp/client/inc/MessageListener.h b/cpp/client/inc/MessageListener.h new file mode 100644 index 0000000000..47307a4df5 --- /dev/null +++ b/cpp/client/inc/MessageListener.h @@ -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 <string> + +#ifndef _MessageListener_ +#define _MessageListener_ + +#include "Message.h" + +namespace qpid { +namespace client { + + class MessageListener{ + public: + virtual void received(Message& msg) = 0; + }; + +} +} + + +#endif diff --git a/cpp/client/inc/Queue.h b/cpp/client/inc/Queue.h new file mode 100644 index 0000000000..e0964af774 --- /dev/null +++ b/cpp/client/inc/Queue.h @@ -0,0 +1,47 @@ +/* + * + * 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> + +#ifndef _Queue_ +#define _Queue_ + +namespace qpid { +namespace client { + + class Queue{ + std::string name; + const bool autodelete; + const bool exclusive; + + public: + + Queue(); + Queue(std::string name); + Queue(std::string name, bool temp); + Queue(std::string name, bool autodelete, bool exclusive); + const std::string& getName() const; + void setName(const std::string&); + bool isAutoDelete() const; + bool isExclusive() const; + }; + +} +} + + +#endif diff --git a/cpp/client/inc/ResponseHandler.h b/cpp/client/inc/ResponseHandler.h new file mode 100644 index 0000000000..f5392c954d --- /dev/null +++ b/cpp/client/inc/ResponseHandler.h @@ -0,0 +1,49 @@ +/* + * + * 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 "amqp_framing.h" +#include "Monitor.h" + +#ifndef _ResponseHandler_ +#define _ResponseHandler_ + +namespace qpid { + namespace client { + + class ResponseHandler{ + bool waiting; + qpid::framing::AMQMethodBody::shared_ptr response; + qpid::concurrent::Monitor* monitor; + + public: + ResponseHandler(); + ~ResponseHandler(); + inline bool isWaiting(){ return waiting; } + inline qpid::framing::AMQMethodBody::shared_ptr getResponse(){ return response; } + bool validate(const qpid::framing::AMQMethodBody& expected); + void waitForResponse(); + void signalResponse(qpid::framing::AMQMethodBody::shared_ptr response); + void receive(const qpid::framing::AMQMethodBody& expected); + void expect();//must be called before calling receive + }; + + } +} + + +#endif diff --git a/cpp/client/inc/ReturnedMessageHandler.h b/cpp/client/inc/ReturnedMessageHandler.h new file mode 100644 index 0000000000..0117778fde --- /dev/null +++ b/cpp/client/inc/ReturnedMessageHandler.h @@ -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 <string> + +#ifndef _ReturnedMessageHandler_ +#define _ReturnedMessageHandler_ + +#include "Message.h" + +namespace qpid { +namespace client { + + class ReturnedMessageHandler{ + public: + virtual void returned(Message& msg) = 0; + }; + +} +} + + +#endif diff --git a/cpp/client/src/Channel.cpp b/cpp/client/src/Channel.cpp new file mode 100644 index 0000000000..e965f7e5dd --- /dev/null +++ b/cpp/client/src/Channel.cpp @@ -0,0 +1,432 @@ +/* + * + * 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 "Channel.h" +#include "MonitorImpl.h" +#include "ThreadFactoryImpl.h" +#include "Message.h" +#include "QpidError.h" + +using namespace std::tr1;//to use dynamic_pointer_cast +using namespace qpid::client; +using namespace qpid::framing; +using namespace qpid::concurrent; + +Channel::Channel(bool _transactional, u_int16_t _prefetch) : id(0), incoming(0), con(0), out(0), + prefetch(_prefetch), + transactional(_transactional), + dispatcher(0), + closed(true){ + threadFactory = new ThreadFactoryImpl(); + dispatchMonitor = new MonitorImpl(); + retrievalMonitor = new MonitorImpl(); +} + +Channel::~Channel(){ + if(dispatcher){ + stop(); + delete dispatcher; + } + delete retrievalMonitor; + delete dispatchMonitor; + delete threadFactory; +} + +void Channel::setPrefetch(u_int16_t prefetch){ + this->prefetch = prefetch; + if(con != 0 && out != 0){ + setQos(); + } +} + +void Channel::setQos(){ + sendAndReceive(new AMQFrame(id, new BasicQosBody(0, prefetch, false)), basic_qos_ok); + if(transactional){ + sendAndReceive(new AMQFrame(id, new TxSelectBody()), tx_select_ok); + } +} + +void Channel::declareExchange(Exchange& exchange, bool synch){ + string name = exchange.getName(); + string type = exchange.getType(); + FieldTable args; + AMQFrame* frame = new AMQFrame(id, new ExchangeDeclareBody(0, name, type, false, false, false, false, !synch, args)); + if(synch){ + sendAndReceive(frame, exchange_declare_ok); + }else{ + out->send(frame); + } +} + +void Channel::deleteExchange(Exchange& exchange, bool synch){ + string name = exchange.getName(); + AMQFrame* frame = new AMQFrame(id, new ExchangeDeleteBody(0, name, false, !synch)); + if(synch){ + sendAndReceive(frame, exchange_delete_ok); + }else{ + out->send(frame); + } +} + +void Channel::declareQueue(Queue& queue, bool synch){ + string name = queue.getName(); + FieldTable args; + AMQFrame* frame = new AMQFrame(id, new QueueDeclareBody(0, name, false, false, + queue.isExclusive(), + queue.isAutoDelete(), !synch, args)); + if(synch){ + sendAndReceive(frame, queue_declare_ok); + if(queue.getName().length() == 0){ + QueueDeclareOkBody::shared_ptr response = + dynamic_pointer_cast<QueueDeclareOkBody, AMQMethodBody>(responses.getResponse()); + queue.setName(response->getQueue()); + } + }else{ + out->send(frame); + } +} + +void Channel::deleteQueue(Queue& queue, bool ifunused, bool ifempty, bool synch){ + //ticket, queue, ifunused, ifempty, nowait + string name = queue.getName(); + AMQFrame* frame = new AMQFrame(id, new QueueDeleteBody(0, name, ifunused, ifempty, !synch)); + if(synch){ + sendAndReceive(frame, queue_delete_ok); + }else{ + out->send(frame); + } +} + +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(); + AMQFrame* frame = new AMQFrame(id, new QueueBindBody(0, q, e, (string&) key,!synch, (FieldTable&) args)); + if(synch){ + sendAndReceive(frame, queue_bind_ok); + }else{ + out->send(frame); + } +} + +void Channel::consume(Queue& queue, std::string& tag, MessageListener* listener, + int ackMode, bool noLocal, bool synch){ + + string q = queue.getName(); + AMQFrame* frame = new AMQFrame(id, new BasicConsumeBody(0, q, (string&) tag, noLocal, ackMode == NO_ACK, false, !synch)); + if(synch){ + sendAndReceive(frame, basic_consume_ok); + BasicConsumeOkBody::shared_ptr response = dynamic_pointer_cast<BasicConsumeOkBody, AMQMethodBody>(responses.getResponse()); + tag = response->getConsumerTag(); + }else{ + out->send(frame); + } + Consumer* c = new Consumer(); + c->listener = listener; + c->ackMode = ackMode; + c->lastDeliveryTag = 0; + consumers[tag] = c; +} + +void Channel::cancel(std::string& tag, bool synch){ + Consumer* c = consumers[tag]; + if(c->ackMode == LAZY_ACK && c->lastDeliveryTag > 0){ + out->send(new AMQFrame(id, new BasicAckBody(c->lastDeliveryTag, true))); + } + + AMQFrame* frame = new AMQFrame(id, new BasicCancelBody((string&) tag, !synch)); + if(synch){ + sendAndReceive(frame, basic_cancel_ok); + }else{ + out->send(frame); + } + consumers.erase(tag); + if(c != 0){ + delete c; + } +} + +void Channel::cancelAll(){ + int count(consumers.size()); + for(consumer_iterator i = consumers.begin(); i != consumers.end(); i = consumers.begin()){ + Consumer* c = i->second; + if((c->ackMode == LAZY_ACK || c->ackMode == AUTO_ACK) && c->lastDeliveryTag > 0){ + out->send(new AMQFrame(id, new BasicAckBody(c->lastDeliveryTag, true))); + } + consumers.erase(i); + delete c; + } +} + +void Channel::retrieve(Message& msg){ + retrievalMonitor->acquire(); + while(retrieved == 0){ + retrievalMonitor->wait(); + } + + msg.header = retrieved->getHeader(); + msg.deliveryTag = retrieved->getDeliveryTag(); + retrieved->getData(msg.data); + delete retrieved; + retrieved = 0; + + retrievalMonitor->release(); +} + +bool Channel::get(Message& msg, const Queue& queue, int ackMode){ + string name = queue.getName(); + AMQFrame* frame = new AMQFrame(id, new BasicGetBody(0, name, ackMode)); + responses.expect(); + out->send(frame); + responses.waitForResponse(); + AMQMethodBody::shared_ptr response = responses.getResponse(); + if(basic_get_ok.match(response.get())){ + if(incoming != 0){ + std::cout << "Existing message not complete" << std::endl; + THROW_QPID_ERROR(PROTOCOL_ERROR + 504, "Existing message not complete"); + }else{ + incoming = new IncomingMessage(dynamic_pointer_cast<BasicGetOkBody, AMQMethodBody>(response)); + } + retrieve(msg); + return true; + }if(basic_get_empty.match(response.get())){ + return false; + }else{ + THROW_QPID_ERROR(PROTOCOL_ERROR + 500, "Unexpected response to basic.get."); + } +} + + +void Channel::publish(Message& msg, const Exchange& exchange, const std::string& routingKey, bool mandatory, bool immediate){ + string e = exchange.getName(); + string key = routingKey; + + out->send(new AMQFrame(id, new BasicPublishBody(0, e, key, mandatory, immediate))); + //break msg up into header frame and content frame(s) and send these + string data = msg.getData(); + msg.header->setContentSize(data.length()); + AMQBody::shared_ptr body(static_pointer_cast<AMQBody, AMQHeaderBody>(msg.header)); + out->send(new AMQFrame(id, body)); + + int data_length = data.length(); + if(data_length > 0){ + //TODO fragmentation of messages, need to know max frame size for connection + int frag_size = con->getMaxFrameSize() - 4; + if(data_length < frag_size){ + out->send(new AMQFrame(id, new AMQContentBody(data))); + }else{ + int frag_count = data_length / frag_size; + for(int i = 0; i < frag_count; i++){ + int pos = i*frag_size; + int len = i < frag_count - 1 ? frag_size : data_length - pos; + string frag(data.substr(pos, len)); + out->send(new AMQFrame(id, new AMQContentBody(frag))); + } + } + } +} + +void Channel::commit(){ + AMQFrame* frame = new AMQFrame(id, new TxCommitBody()); + sendAndReceive(frame, tx_commit_ok); +} + +void Channel::rollback(){ + AMQFrame* frame = new AMQFrame(id, new TxRollbackBody()); + sendAndReceive(frame, tx_rollback_ok); +} + +void Channel::handleMethod(AMQMethodBody::shared_ptr body){ + //channel.flow, channel.close, basic.deliver, basic.return or a response to a synchronous request + if(responses.isWaiting()){ + responses.signalResponse(body); + }else if(basic_deliver.match(body.get())){ + if(incoming != 0){ + std::cout << "Existing message not complete [deliveryTag=" << incoming->getDeliveryTag() << "]" << std::endl; + THROW_QPID_ERROR(PROTOCOL_ERROR + 504, "Existing message not complete"); + }else{ + incoming = new IncomingMessage(dynamic_pointer_cast<BasicDeliverBody, AMQMethodBody>(body)); + } + }else if(basic_return.match(body.get())){ + if(incoming != 0){ + std::cout << "Existing message not complete" << std::endl; + THROW_QPID_ERROR(PROTOCOL_ERROR + 504, "Existing message not complete"); + }else{ + incoming = new IncomingMessage(dynamic_pointer_cast<BasicReturnBody, AMQMethodBody>(body)); + } + }else if(channel_close.match(body.get())){ + con->removeChannel(this); + //need to signal application that channel has been closed through exception + + }else if(channel_flow.match(body.get())){ + + }else{ + //signal error + std::cout << "Unhandled method: " << *body << std::endl; + THROW_QPID_ERROR(PROTOCOL_ERROR + 504, "Unhandled method"); + } +} + +void Channel::handleHeader(AMQHeaderBody::shared_ptr body){ + if(incoming == 0){ + //handle invalid frame sequence + std::cout << "Invalid message sequence: got header before return or deliver." << std::endl; + THROW_QPID_ERROR(PROTOCOL_ERROR + 504, "Invalid message sequence: got header before return or deliver."); + }else{ + incoming->setHeader(body); + if(incoming->isComplete()){ + enqueue(); + } + } +} + +void Channel::handleContent(AMQContentBody::shared_ptr body){ + if(incoming == 0){ + //handle invalid frame sequence + std::cout << "Invalid message sequence: got content before return or deliver." << std::endl; + THROW_QPID_ERROR(PROTOCOL_ERROR + 504, "Invalid message sequence: got content before return or deliver."); + }else{ + incoming->addContent(body); + if(incoming->isComplete()){ + enqueue(); + } + } +} + +void Channel::handleHeartbeat(AMQHeartbeatBody::shared_ptr body){ + THROW_QPID_ERROR(PROTOCOL_ERROR + 504, "Channel received heartbeat"); +} + +void Channel::start(){ + dispatcher = threadFactory->create(this); + dispatcher->start(); +} + +void Channel::stop(){ + closed = true; + dispatchMonitor->acquire(); + dispatchMonitor->notify(); + dispatchMonitor->release(); + if(dispatcher){ + dispatcher->join(); + } +} + +void Channel::run(){ + dispatch(); +} + +void Channel::enqueue(){ + if(incoming->isResponse()){ + retrievalMonitor->acquire(); + retrieved = incoming; + retrievalMonitor->notify(); + retrievalMonitor->release(); + }else{ + dispatchMonitor->acquire(); + messages.push(incoming); + dispatchMonitor->notify(); + dispatchMonitor->release(); + } + incoming = 0; +} + +IncomingMessage* Channel::dequeue(){ + dispatchMonitor->acquire(); + while(messages.empty() && !closed){ + dispatchMonitor->wait(); + } + IncomingMessage* msg = 0; + if(!messages.empty()){ + msg = messages.front(); + messages.pop(); + } + dispatchMonitor->release(); + return msg; +} + +void Channel::deliver(Consumer* consumer, Message& msg){ + //record delivery tag: + consumer->lastDeliveryTag = msg.getDeliveryTag(); + + //allow registered listener to handle the message + consumer->listener->received(msg); + + //if the handler calls close on the channel or connection while + //handling this message, then consumer will now have been deleted. + if(!closed){ + bool multiple(false); + switch(consumer->ackMode){ + case LAZY_ACK: + multiple = true; + if(++(consumer->count) < prefetch) break; + //else drop-through + case AUTO_ACK: + out->send(new AMQFrame(id, new BasicAckBody(msg.getDeliveryTag(), multiple))); + consumer->lastDeliveryTag = 0; + } + } + + //as it stands, transactionality is entirely orthogonal to ack + //mode, though the acks will not be processed by the broker under + //a transaction until it commits. +} + +void Channel::dispatch(){ + while(!closed){ + IncomingMessage* incomingMsg = dequeue(); + if(incomingMsg){ + //Note: msg is currently only valid for duration of this call + Message msg(incomingMsg->getHeader()); + incomingMsg->getData(msg.data); + if(incomingMsg->isReturn()){ + if(returnsHandler == 0){ + //print warning to log/console + std::cout << "Message returned: " << msg.getData() << std::endl; + }else{ + returnsHandler->returned(msg); + } + }else{ + msg.deliveryTag = incomingMsg->getDeliveryTag(); + std::string tag = incomingMsg->getConsumerTag(); + + if(consumers[tag] == 0){ + //signal error + std::cout << "Unknown consumer: " << tag << std::endl; + }else{ + deliver(consumers[tag], msg); + } + } + delete incomingMsg; + } + } +} + +void Channel::setReturnedMessageHandler(ReturnedMessageHandler* handler){ + returnsHandler = handler; +} + +void Channel::sendAndReceive(AMQFrame* frame, const AMQMethodBody& body){ + responses.expect(); + out->send(frame); + responses.receive(body); +} + +void Channel::close(){ + if(con != 0){ + con->closeChannel(this); + } +} diff --git a/cpp/client/src/Connection.cpp b/cpp/client/src/Connection.cpp new file mode 100644 index 0000000000..eeb2330561 --- /dev/null +++ b/cpp/client/src/Connection.cpp @@ -0,0 +1,237 @@ +/* + * + * 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 "Connection.h" +#include "Channel.h" +#include "ConnectorImpl.h" +#include "Message.h" +#include "QpidError.h" +#include <iostream> + +using namespace qpid::client; +using namespace qpid::framing; +using namespace qpid::io; +using namespace qpid::concurrent; + +u_int16_t Connection::channelIdCounter; + +Connection::Connection(bool debug, u_int32_t _max_frame_size) : max_frame_size(_max_frame_size), closed(true){ + connector = new ConnectorImpl(debug, _max_frame_size); +} + +Connection::~Connection(){ + delete connector; +} + +void Connection::open(const std::string& host, int port, const std::string& uid, const std::string& pwd, const std::string& virtualhost){ + this->host = host; + this->port = port; + connector->setInputHandler(this); + connector->setTimeoutHandler(this); + connector->setShutdownHandler(this); + out = connector->getOutputHandler(); + connector->connect(host, port); + + ProtocolInitiation* header = new ProtocolInitiation(8, 0); + responses.expect(); + connector->init(header); + responses.receive(connection_start); + + FieldTable props; + string mechanism("PLAIN"); + string response = ((char)0) + uid + ((char)0) + pwd; + string locale("en_US"); + responses.expect(); + out->send(new AMQFrame(0, new ConnectionStartOkBody(props, mechanism, response, locale))); + + /** + * Assume for now that further challenges will not be required + //receive connection.secure + responses.receive(connection_secure)); + //send connection.secure-ok + out->send(new AMQFrame(0, new ConnectionSecureOkBody(response))); + **/ + + responses.receive(connection_tune); + + ConnectionTuneBody::shared_ptr proposal = std::tr1::dynamic_pointer_cast<ConnectionTuneBody, AMQMethodBody>(responses.getResponse()); + out->send(new AMQFrame(0, new ConnectionTuneOkBody(proposal->getChannelMax(), max_frame_size, proposal->getHeartbeat()))); + + u_int16_t heartbeat = proposal->getHeartbeat(); + connector->setReadTimeout(heartbeat * 2); + connector->setWriteTimeout(heartbeat); + + //send connection.open + string capabilities; + string vhost = virtualhost; + responses.expect(); + out->send(new AMQFrame(0, new ConnectionOpenBody(vhost, capabilities, true))); + //receive connection.open-ok (or redirect, but ignore that for now esp. as using force=true). + responses.waitForResponse(); + if(responses.validate(connection_open_ok)){ + //ok + }else if(responses.validate(connection_redirect)){ + //ignore for now + ConnectionRedirectBody::shared_ptr redirect(std::tr1::dynamic_pointer_cast<ConnectionRedirectBody, AMQMethodBody>(responses.getResponse())); + std::cout << "Received redirection to " << redirect->getHost() << std::endl; + }else{ + THROW_QPID_ERROR(PROTOCOL_ERROR, "Bad response"); + } + +} + +void Connection::close(){ + if(!closed){ + u_int16_t code(200); + string text("Ok"); + u_int16_t classId(0); + u_int16_t methodId(0); + + sendAndReceive(new AMQFrame(0, new ConnectionCloseBody(code, text, classId, methodId)), connection_close_ok); + connector->close(); + } +} + +void Connection::openChannel(Channel* channel){ + channel->con = this; + channel->id = ++channelIdCounter; + channel->out = out; + channels[channel->id] = channel; + //now send frame to open channel and wait for response + string oob; + channel->sendAndReceive(new AMQFrame(channel->id, new ChannelOpenBody(oob)), channel_open_ok); + channel->setQos(); + channel->closed = false; +} + +void Connection::closeChannel(Channel* channel){ + //send frame to close channel + u_int16_t code(200); + string text("Ok"); + u_int16_t classId(0); + u_int16_t methodId(0); + closeChannel(channel, code, text, classId, methodId); +} + +void Connection::closeChannel(Channel* channel, u_int16_t code, string& text, u_int16_t classId, u_int16_t methodId){ + //send frame to close channel + channel->cancelAll(); + channel->closed = true; + channel->sendAndReceive(new AMQFrame(channel->id, new ChannelCloseBody(code, text, classId, methodId)), channel_close_ok); + channel->con = 0; + channel->out = 0; + removeChannel(channel); +} + +void Connection::removeChannel(Channel* channel){ + //send frame to close channel + + channels.erase(channel->id); + channel->out = 0; + channel->id = 0; + channel->con = 0; +} + +void Connection::received(AMQFrame* frame){ + u_int16_t channelId = frame->getChannel(); + + if(channelId == 0){ + this->handleBody(frame->getBody()); + }else{ + Channel* channel = channels[channelId]; + if(channel == 0){ + error(504, "Unknown channel"); + }else{ + try{ + channel->handleBody(frame->getBody()); + }catch(qpid::QpidError e){ + channelException(channel, dynamic_cast<AMQMethodBody*>(frame->getBody().get()), e); + } + } + } +} + +void Connection::handleMethod(AMQMethodBody::shared_ptr body){ + //connection.close, basic.deliver, basic.return or a response to a synchronous request + if(responses.isWaiting()){ + responses.signalResponse(body); + }else if(connection_close.match(body.get())){ + //send back close ok + //close socket + ConnectionCloseBody* request = dynamic_cast<ConnectionCloseBody*>(body.get()); + std::cout << "Connection closed by server: " << request->getReplyCode() << ":" << request->getReplyText() << std::endl; + connector->close(); + }else{ + std::cout << "Unhandled method for connection: " << *body << std::endl; + error(504, "Unrecognised method", body->amqpClassId(), body->amqpMethodId()); + } +} + +void Connection::handleHeader(AMQHeaderBody::shared_ptr body){ + error(504, "Channel error: received header body with channel 0."); +} + +void Connection::handleContent(AMQContentBody::shared_ptr body){ + error(504, "Channel error: received content body with channel 0."); +} + +void Connection::handleHeartbeat(AMQHeartbeatBody::shared_ptr body){ +} + +void Connection::sendAndReceive(AMQFrame* frame, const AMQMethodBody& body){ + responses.expect(); + out->send(frame); + responses.receive(body); +} + +void Connection::error(int code, const string& msg, int classid, int methodid){ + std::cout << "Connection exception generated: " << code << msg; + if(classid || methodid){ + std::cout << " [" << methodid << ":" << classid << "]"; + } + std::cout << std::endl; + sendAndReceive(new AMQFrame(0, new ConnectionCloseBody(code, (string&) msg, classid, methodid)), connection_close_ok); + connector->close(); +} + +void Connection::channelException(Channel* channel, AMQMethodBody* method, QpidError& e){ + std::cout << "Caught error from channel [" << e.code << "] " << e.msg << " (" << e.file << ":" << e.line << ")" << std::endl; + int code = e.code == PROTOCOL_ERROR ? e.code - PROTOCOL_ERROR : 500; + string msg = e.msg; + if(method == 0){ + closeChannel(channel, code, msg); + }else{ + closeChannel(channel, code, msg, method->amqpClassId(), method->amqpMethodId()); + } +} + +void Connection::idleIn(){ + std::cout << "Connection timed out due to abscence of heartbeat." << std::endl; + connector->close(); +} + +void Connection::idleOut(){ + out->send(new AMQFrame(0, new AMQHeartbeatBody())); +} + +void Connection::shutdown(){ + closed = true; + //close all channels + for(iterator i = channels.begin(); i != channels.end(); i++){ + i->second->stop(); + } +} diff --git a/cpp/client/src/Exchange.cpp b/cpp/client/src/Exchange.cpp new file mode 100644 index 0000000000..681068dc4c --- /dev/null +++ b/cpp/client/src/Exchange.cpp @@ -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. + * + */ +#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_DIRECT_EXCHANGE("amq.direct", DIRECT_EXCHANGE); +const qpid::client::Exchange qpid::client::Exchange::DEFAULT_TOPIC_EXCHANGE("amq.topic", TOPIC_EXCHANGE); +const qpid::client::Exchange qpid::client::Exchange::DEFAULT_HEADERS_EXCHANGE("amq.headers", HEADERS_EXCHANGE); diff --git a/cpp/client/src/IncomingMessage.cpp b/cpp/client/src/IncomingMessage.cpp new file mode 100644 index 0000000000..8e2604c4cb --- /dev/null +++ b/cpp/client/src/IncomingMessage.cpp @@ -0,0 +1,85 @@ +/* + * + * 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 "IncomingMessage.h" +#include "QpidError.h" +#include <iostream> + +using namespace qpid::client; +using namespace qpid::framing; + +IncomingMessage::IncomingMessage(BasicDeliverBody::shared_ptr intro) : delivered(intro){} +IncomingMessage::IncomingMessage(BasicReturnBody::shared_ptr intro): returned(intro){} +IncomingMessage::IncomingMessage(BasicGetOkBody::shared_ptr intro): response(intro){} + +IncomingMessage::~IncomingMessage(){ +} + +void IncomingMessage::setHeader(AMQHeaderBody::shared_ptr header){ + this->header = header; +} + +void IncomingMessage::addContent(AMQContentBody::shared_ptr content){ + this->content.push_back(content); +} + +bool IncomingMessage::isComplete(){ + return header != 0 && header->getContentSize() == contentSize(); +} + +bool IncomingMessage::isReturn(){ + return returned; +} + +bool IncomingMessage::isDelivery(){ + return delivered; +} + +bool IncomingMessage::isResponse(){ + return response; +} + +string& IncomingMessage::getConsumerTag(){ + if(!isDelivery()) THROW_QPID_ERROR(CLIENT_ERROR, "Consumer tag only valid for delivery"); + return delivered->getConsumerTag(); +} + +u_int64_t IncomingMessage::getDeliveryTag(){ + if(!isDelivery()) THROW_QPID_ERROR(CLIENT_ERROR, "Delivery tag only valid for delivery"); + return delivered->getDeliveryTag(); +} + +AMQHeaderBody::shared_ptr& IncomingMessage::getHeader(){ + return header; +} + +void IncomingMessage::getData(string& s){ + int count(content.size()); + for(int i = 0; i < count; i++){ + if(i == 0) s = content[i]->getData(); + else s += content[i]->getData(); + } +} + +long IncomingMessage::contentSize(){ + long size(0); + int count(content.size()); + for(int i = 0; i < count; i++){ + size += content[i]->size(); + } + return size; +} diff --git a/cpp/client/src/Message.cpp b/cpp/client/src/Message.cpp new file mode 100644 index 0000000000..71befe57b1 --- /dev/null +++ b/cpp/client/src/Message.cpp @@ -0,0 +1,147 @@ +/* + * + * 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 "Message.h" + +using namespace qpid::client; +using namespace qpid::framing; + +Message::Message(){ + header = AMQHeaderBody::shared_ptr(new AMQHeaderBody(BASIC)); +} + +Message::Message(AMQHeaderBody::shared_ptr& _header) : header(_header){ +} + +Message::~Message(){ +} + +BasicHeaderProperties* Message::getHeaderProperties(){ + return dynamic_cast<BasicHeaderProperties*>(header->getProperties()); +} + +std::string& Message::getContentType(){ + return getHeaderProperties()->getContentType(); +} + +std::string& Message::getContentEncoding(){ + return getHeaderProperties()->getContentEncoding(); +} + +FieldTable& Message::getHeaders(){ + return getHeaderProperties()->getHeaders(); +} + +u_int8_t Message::getDeliveryMode(){ + return getHeaderProperties()->getDeliveryMode(); +} + +u_int8_t Message::getPriority(){ + return getHeaderProperties()->getPriority(); +} + +std::string& Message::getCorrelationId(){ + return getHeaderProperties()->getCorrelationId(); +} + +std::string& Message::getReplyTo(){ + return getHeaderProperties()->getReplyTo(); +} + +std::string& Message::getExpiration(){ + return getHeaderProperties()->getExpiration(); +} + +std::string& Message::getMessageId(){ + return getHeaderProperties()->getMessageId(); +} + +u_int64_t Message::getTimestamp(){ + return getHeaderProperties()->getTimestamp(); +} + +std::string& Message::getType(){ + return getHeaderProperties()->getType(); +} + +std::string& Message::getUserId(){ + return getHeaderProperties()->getUserId(); +} + +std::string& Message::getAppId(){ + return getHeaderProperties()->getAppId(); +} + +std::string& Message::getClusterId(){ + return getHeaderProperties()->getClusterId(); +} + +void Message::setContentType(std::string& type){ + getHeaderProperties()->setContentType(type); +} + +void Message::setContentEncoding(std::string& encoding){ + getHeaderProperties()->setContentEncoding(encoding); +} + +void Message::setHeaders(FieldTable& headers){ + getHeaderProperties()->setHeaders(headers); +} + +void Message::setDeliveryMode(u_int8_t mode){ + getHeaderProperties()->setDeliveryMode(mode); +} + +void Message::setPriority(u_int8_t priority){ + getHeaderProperties()->setPriority(priority); +} + +void Message::setCorrelationId(std::string& correlationId){ + getHeaderProperties()->setCorrelationId(correlationId); +} + +void Message::setReplyTo(std::string& replyTo){ + getHeaderProperties()->setReplyTo(replyTo); +} + +void Message::setExpiration(std::string& expiration){ + getHeaderProperties()->setExpiration(expiration); +} + +void Message::setMessageId(std::string& messageId){ + getHeaderProperties()->setMessageId(messageId); +} + +void Message::setTimestamp(u_int64_t timestamp){ + getHeaderProperties()->setTimestamp(timestamp); +} + +void Message::setType(std::string& type){ + getHeaderProperties()->setType(type); +} + +void Message::setUserId(std::string& userId){ + getHeaderProperties()->setUserId(userId); +} + +void Message::setAppId(std::string& appId){ + getHeaderProperties()->setAppId(appId); +} + +void Message::setClusterId(std::string& clusterId){ + getHeaderProperties()->setClusterId(clusterId); +} diff --git a/cpp/client/src/Queue.cpp b/cpp/client/src/Queue.cpp new file mode 100644 index 0000000000..cb957dd993 --- /dev/null +++ b/cpp/client/src/Queue.cpp @@ -0,0 +1,47 @@ +/* + * + * 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 "Queue.h" + +qpid::client::Queue::Queue() : name(""), autodelete(true), exclusive(true){} + +qpid::client::Queue::Queue(std::string _name) : name(_name), autodelete(false), exclusive(false){} + +qpid::client::Queue::Queue(std::string _name, bool temp) : name(_name), autodelete(temp), exclusive(temp){} + +qpid::client::Queue::Queue(std::string _name, bool _autodelete, bool _exclusive) + : name(_name), autodelete(_autodelete), exclusive(_exclusive){} + +const std::string& qpid::client::Queue::getName() const{ + return name; +} + +void qpid::client::Queue::setName(const std::string& name){ + this->name = name; +} + +bool qpid::client::Queue::isAutoDelete() const{ + return autodelete; +} + +bool qpid::client::Queue::isExclusive() const{ + return exclusive; +} + + + + diff --git a/cpp/client/src/ResponseHandler.cpp b/cpp/client/src/ResponseHandler.cpp new file mode 100644 index 0000000000..837bba37fd --- /dev/null +++ b/cpp/client/src/ResponseHandler.cpp @@ -0,0 +1,63 @@ +/* + * + * 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 "ResponseHandler.h" +#include "MonitorImpl.h" +#include "QpidError.h" + +qpid::client::ResponseHandler::ResponseHandler() : waiting(false){ + monitor = new qpid::concurrent::MonitorImpl(); +} + +qpid::client::ResponseHandler::~ResponseHandler(){ + delete monitor; +} + +bool qpid::client::ResponseHandler::validate(const qpid::framing::AMQMethodBody& expected){ + return expected.match(response.get()); +} + +void qpid::client::ResponseHandler::waitForResponse(){ + monitor->acquire(); + if(waiting){ + monitor->wait(); + } + monitor->release(); +} + +void qpid::client::ResponseHandler::signalResponse(qpid::framing::AMQMethodBody::shared_ptr response){ + this->response = response; + monitor->acquire(); + waiting = false; + monitor->notify(); + monitor->release(); +} + +void qpid::client::ResponseHandler::receive(const qpid::framing::AMQMethodBody& expected){ + monitor->acquire(); + if(waiting){ + monitor->wait(); + } + monitor->release(); + if(!validate(expected)){ + THROW_QPID_ERROR(PROTOCOL_ERROR, "Protocol Error"); + } +} + +void qpid::client::ResponseHandler::expect(){ + waiting = true; +} diff --git a/cpp/client/test/Makefile b/cpp/client/test/Makefile new file mode 100644 index 0000000000..f35aab3e17 --- /dev/null +++ b/cpp/client/test/Makefile @@ -0,0 +1,45 @@ +# +# 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. +# + +QPID_HOME = ../../.. +include ${QPID_HOME}/cpp/options.mk + +# TODO aconway 2006-09-12: These are system tests, not unit tests. +# We need client side unit tests. +# We should separate them from the system tets. +# We need an approach to automate the C++ client/server system tests. +# + +SOURCES=$(wildcard *.cpp) +TESTS=$(SOURCES:.cpp=) +DEPS= $(SOURCES:.cpp=.d) + +INCLUDES = $(TEST_INCLUDES) +LDLIBS= -lapr-1 $(COMMON_LIB) $(CLIENT_LIB) + +.PHONY: all clean + +all: $(TESTS) + +clean: + -@rm -f $(TESTS) $(DEPS) + +# Rule to build test programs. +%: %.cpp + $(CXX) -o $@ $< $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) + +# Dependencies +-include $(DEPS) diff --git a/cpp/client/test/client_test.cpp b/cpp/client/test/client_test.cpp new file mode 100644 index 0000000000..e33beb3b67 --- /dev/null +++ b/cpp/client/test/client_test.cpp @@ -0,0 +1,97 @@ +/* + * + * 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 <iostream> + +#include "QpidError.h" +#include "Channel.h" +#include "Connection.h" +#include "FieldTable.h" +#include "Message.h" +#include "MessageListener.h" + +#include "MonitorImpl.h" + + +using namespace qpid::client; +using namespace qpid::concurrent; + +class SimpleListener : public virtual MessageListener{ + Monitor* monitor; + +public: + inline SimpleListener(Monitor* _monitor) : monitor(_monitor){} + + inline virtual void received(Message& msg){ + std::cout << "Received message " /**<< msg **/<< std::endl; + monitor->acquire(); + monitor->notify(); + monitor->release(); + } +}; + +int main(int argc, char** argv) +{ + try{ + Connection con(argc > 1); + Channel channel; + Exchange exchange("MyExchange", Exchange::TOPIC_EXCHANGE); + Queue queue("MyQueue", true); + + string host("localhost"); + + con.open(host); + std::cout << "Opened connection." << std::endl; + con.openChannel(&channel); + std::cout << "Opened channel." << std::endl; + channel.declareExchange(exchange); + std::cout << "Declared exchange." << std::endl; + channel.declareQueue(queue); + std::cout << "Declared queue." << std::endl; + qpid::framing::FieldTable args; + channel.bind(exchange, queue, "MyTopic", args); + std::cout << "Bound queue to exchange." << std::endl; + + //set up a message listener + MonitorImpl monitor; + SimpleListener listener(&monitor); + string tag("MyTag"); + channel.consume(queue, tag, &listener); + channel.start(); + std::cout << "Registered consumer." << std::endl; + + Message msg; + string data("MyMessage"); + msg.setData(data); + channel.publish(msg, exchange, "MyTopic"); + std::cout << "Published message." << std::endl; + + monitor.acquire(); + monitor.wait(); + monitor.release(); + + + con.closeChannel(&channel); + std::cout << "Closed channel." << std::endl; + con.close(); + std::cout << "Closed connection." << std::endl; + }catch(qpid::QpidError error){ + std::cout << "Error [" << error.code << "] " << error.msg << " (" << error.file << ":" << error.line << ")" << std::endl; + return 1; + } + return 0; +} diff --git a/cpp/client/test/topic_listener.cpp b/cpp/client/test/topic_listener.cpp new file mode 100644 index 0000000000..707b3443a1 --- /dev/null +++ b/cpp/client/test/topic_listener.cpp @@ -0,0 +1,180 @@ +/* + * + * 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 <iostream> +#include <sstream> +#include "apr_time.h" +#include "QpidError.h" +#include "Channel.h" +#include "Connection.h" +#include "Exchange.h" +#include "MessageListener.h" +#include "Queue.h" + +using namespace qpid::client; + +class Listener : public MessageListener{ + Channel* const channel; + const std::string responseQueue; + const bool transactional; + bool init; + int count; + apr_time_t start; + + void shutdown(); + void report(); +public: + Listener(Channel* channel, const std::string& reponseQueue, bool tx); + virtual void received(Message& msg); +}; + +class Args{ + string host; + int port; + int ackMode; + bool transactional; + int prefetch; + bool trace; + bool help; +public: + inline Args() : host("localhost"), port(5672), ackMode(NO_ACK), transactional(false), prefetch(1000), trace(false), help(false){} + void parse(int argc, char** argv); + void usage(); + + inline const string& getHost() const { return host;} + inline int getPort() const { return port; } + inline int getAckMode(){ return ackMode; } + inline bool getTransactional() const { return transactional; } + inline int getPrefetch(){ return prefetch; } + inline bool getTrace() const { return trace; } + inline bool getHelp() const { return help; } +}; + +int main(int argc, char** argv){ + Args args; + args.parse(argc, argv); + if(args.getHelp()){ + args.usage(); + }else{ + try{ + Connection connection(args.getTrace()); + connection.open(args.getHost(), args.getPort()); + Channel channel(args.getTransactional(), args.getPrefetch()); + connection.openChannel(&channel); + + //declare exchange, queue and bind them: + Queue response("response"); + channel.declareQueue(response); + + Queue control; + channel.declareQueue(control); + qpid::framing::FieldTable bindArgs; + channel.bind(Exchange::DEFAULT_TOPIC_EXCHANGE, control, "topic_control", bindArgs); + //set up listener + Listener listener(&channel, response.getName(), args.getTransactional()); + std::string tag; + channel.consume(control, tag, &listener, args.getAckMode()); + channel.run(); + connection.close(); + }catch(qpid::QpidError error){ + std::cout << "Error [" << error.code << "] " << error.msg << " (" << error.file << ":" << error.line << ")" << std::endl; + } + } +} + +Listener::Listener(Channel* _channel, const std::string& _responseq, bool tx) : + channel(_channel), responseQueue(_responseq), transactional(tx), init(false), count(0){} + +void Listener::received(Message& message){ + if(!init){ + start = apr_time_as_msec(apr_time_now()); + count = 0; + init = true; + } + std::string type(message.getHeaders().getString("TYPE")); + + if(type == "TERMINATION_REQUEST"){ + shutdown(); + }else if(type == "REPORT_REQUEST"){ + //send a report: + report(); + init = false; + }else if (++count % 100 == 0){ + std::cout <<"Received " << count << " messages." << std::endl; + } +} + +void Listener::shutdown(){ + channel->close(); +} + +void Listener::report(){ + apr_time_t finish = apr_time_as_msec(apr_time_now()); + apr_time_t time = finish - start; + std::stringstream report; + report << "Received " << count << " messages in " << time << " ms."; + Message msg; + msg.setData(report.str()); + channel->publish(msg, Exchange::DEFAULT_DIRECT_EXCHANGE, responseQueue); + if(transactional){ + channel->commit(); + } +} + + +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("-ack_mode" == name){ + ackMode = atoi(argv[++i]); + }else if("-transactional" == name){ + transactional = true; + }else if("-prefetch" == name){ + prefetch = atoi(argv[++i]); + }else if("-trace" == name){ + trace = 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 << " -ack_mode <mode>" << std::endl; + std::cout << " Sets the acknowledgement mode" << std::endl; + std::cout << " 0=NO_ACK (default), 1=AUTO_ACK, 2=LAZY_ACK" << std::endl; + std::cout << " -transactional" << std::endl; + std::cout << " Indicates the client should use transactions" << std::endl; + std::cout << " -prefetch <count>" << std::endl; + std::cout << " Specifies the prefetch count (default is 1000)" << std::endl; + std::cout << " -trace" << std::endl; + std::cout << " Indicates that the frames sent and received should be logged" << std::endl; +} diff --git a/cpp/client/test/topic_publisher.cpp b/cpp/client/test/topic_publisher.cpp new file mode 100644 index 0000000000..fc6b7f3b30 --- /dev/null +++ b/cpp/client/test/topic_publisher.cpp @@ -0,0 +1,253 @@ +/* + * + * 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 <iostream> +#include <cstdlib> +#include "unistd.h" +#include "apr_time.h" +#include "MonitorImpl.h" +#include "QpidError.h" +#include "Channel.h" +#include "Connection.h" +#include "Exchange.h" +#include "MessageListener.h" +#include "Queue.h" + +using namespace qpid::client; +using namespace qpid::concurrent; + +class Publisher : public MessageListener{ + Channel* const channel; + const std::string controlTopic; + const bool transactional; + MonitorImpl monitor; + int count; + + void waitForCompletion(int msgs); + string generateData(int size); + +public: + Publisher(Channel* channel, const std::string& controlTopic, bool tx); + virtual void received(Message& msg); + apr_time_t publish(int msgs, int listeners, int size); + void terminate(); +}; + +class Args{ + string host; + int port; + int messages; + int subscribers; + int ackMode; + bool transactional; + int prefetch; + int batches; + int delay; + int size; + bool trace; + bool help; +public: + inline Args() : host("localhost"), port(5672), messages(1000), subscribers(1), + ackMode(NO_ACK), transactional(false), prefetch(1000), batches(1), + delay(0), size(256), trace(false), help(false){} + + void parse(int argc, char** argv); + void usage(); + + inline const string& getHost() const { return host;} + inline int getPort() const { return port; } + inline int getMessages() const { return messages; } + inline int getSubscribers() const { return subscribers; } + inline int getAckMode(){ return ackMode; } + inline bool getTransactional() const { return transactional; } + inline int getPrefetch(){ return prefetch; } + inline int getBatches(){ return batches; } + inline int getDelay(){ return delay; } + inline int getSize(){ return size; } + inline bool getTrace() const { return trace; } + inline bool getHelp() const { return help; } +}; + +int main(int argc, char** argv){ + Args args; + args.parse(argc, argv); + if(args.getHelp()){ + args.usage(); + }else{ + try{ + Connection connection(args.getTrace()); + connection.open(args.getHost(), args.getPort()); + Channel channel(args.getTransactional(), args.getPrefetch()); + connection.openChannel(&channel); + + //declare queue (relying on default binding): + Queue response("response"); + channel.declareQueue(response); + + //set up listener + Publisher publisher(&channel, "topic_control", args.getTransactional()); + std::string tag("mytag"); + channel.consume(response, tag, &publisher, args.getAckMode()); + channel.start(); + + int batchSize(args.getBatches()); + apr_time_t max(0); + apr_time_t min(0); + apr_time_t sum(0); + for(int i = 0; i < batchSize; i++){ + if(i > 0 && args.getDelay()) sleep(args.getDelay()); + apr_time_t time = publisher.publish(args.getMessages(), args.getSubscribers(), args.getSize()); + if(!max || time > max) max = time; + if(!min || time < min) min = time; + sum += time; + std::cout << "Completed " << (i+1) << " of " << batchSize << " in " << time << "ms" << std::endl; + } + publisher.terminate(); + apr_time_t avg = sum / batchSize; + if(batchSize > 1){ + std::cout << batchSize << " batches completed. avg=" << avg << + ", max=" << max << ", min=" << min << std::endl; + } + channel.close(); + connection.close(); + }catch(qpid::QpidError error){ + std::cout << "Error [" << error.code << "] " << error.msg << " (" << error.file << ":" << error.line << ")" << std::endl; + } + } +} + +Publisher::Publisher(Channel* _channel, const std::string& _controlTopic, bool tx) : + channel(_channel), controlTopic(_controlTopic), transactional(tx){} + +void Publisher::received(Message& msg){ + //count responses and when all are received end the current batch + monitor.acquire(); + if(--count == 0){ + monitor.notify(); + } + std::cout << "Received report: " << msg.getData() << " (" << count << " remaining)." << std::endl; + monitor.release(); +} + +void Publisher::waitForCompletion(int msgs){ + count = msgs; + monitor.wait(); +} + +apr_time_t Publisher::publish(int msgs, int listeners, int size){ + monitor.acquire(); + Message msg; + msg.setData(generateData(size)); + apr_time_t start(apr_time_as_msec(apr_time_now())); + for(int i = 0; i < msgs; i++){ + channel->publish(msg, Exchange::DEFAULT_TOPIC_EXCHANGE, controlTopic); + } + //send report request + Message reportRequest; + reportRequest.getHeaders().setString("TYPE", "REPORT_REQUEST"); + channel->publish(reportRequest, Exchange::DEFAULT_TOPIC_EXCHANGE, controlTopic); + if(transactional){ + channel->commit(); + } + + waitForCompletion(listeners); + monitor.release(); + apr_time_t finish(apr_time_as_msec(apr_time_now())); + + return finish - start; +} + +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; + terminationRequest.getHeaders().setString("TYPE", "TERMINATION_REQUEST"); + channel->publish(terminationRequest, Exchange::DEFAULT_TOPIC_EXCHANGE, controlTopic); + if(transactional){ + channel->commit(); + } +} + +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("-messages" == name){ + messages = atoi(argv[++i]); + }else if("-subscribers" == name){ + subscribers = atoi(argv[++i]); + }else if("-ack_mode" == name){ + ackMode = atoi(argv[++i]); + }else if("-transactional" == name){ + transactional = true; + }else if("-prefetch" == name){ + prefetch = atoi(argv[++i]); + }else if("-batches" == name){ + batches = atoi(argv[++i]); + }else if("-delay" == name){ + delay = atoi(argv[++i]); + }else if("-size" == name){ + size = atoi(argv[++i]); + }else if("-trace" == name){ + trace = 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 << " -messages <count>" << std::endl; + std::cout << " Specifies how many messages to send" << std::endl; + std::cout << " -subscribers <count>" << std::endl; + std::cout << " Specifies how many subscribers to expect reports from" << std::endl; + std::cout << " -ack_mode <mode>" << std::endl; + std::cout << " Sets the acknowledgement mode" << std::endl; + std::cout << " 0=NO_ACK (default), 1=AUTO_ACK, 2=LAZY_ACK" << std::endl; + std::cout << " -transactional" << std::endl; + std::cout << " Indicates the client should use transactions" << std::endl; + std::cout << " -prefetch <count>" << std::endl; + std::cout << " Specifies the prefetch count (default is 1000)" << std::endl; + std::cout << " -batches <count>" << std::endl; + std::cout << " Specifies how many batches to run" << std::endl; + std::cout << " -delay <seconds>" << std::endl; + std::cout << " Causes a delay between each batch" << std::endl; + std::cout << " -size <bytes>" << std::endl; + std::cout << " Sets the size of the published messages (default is 256 bytes)" << std::endl; + std::cout << " -trace" << std::endl; + std::cout << " Indicates that the frames sent and received should be logged" << std::endl; +} diff --git a/cpp/common/Makefile b/cpp/common/Makefile new file mode 100644 index 0000000000..5fe815b8da --- /dev/null +++ b/cpp/common/Makefile @@ -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. +# + +# +# Make file to build qpid_common library. +# + +QPID_HOME=../.. +include $(QPID_HOME)/cpp/options.mk + +TARGET = $(LIB_DIR)/libqpid_common.so.1.0 + +CXXFLAGS = $(DEBUG) $(OPT) -MMD -fpic $(COMMON_INCLUDES) + +SOURCES = $(wildcard */src/*.cpp framing/generated/*.cpp) +OBJECTS = $(SOURCES:.cpp=.o) +DEPS = $(SOURCES:.cpp=.d) + +GENERATED_OBJECTS = framing/generated/amqp_methods.o + +.PHONY: all test clean + +# We have to do two separate makes to ensure we pick up all generated files. +all: + @$(MAKE) -C framing all + @make $(TARGET) + +test: + @$(MAKE) -C framing test + +clean: + @$(MAKE) -C framing clean + -@rm -f $(TARGET) $(OBJECTS) $(DEPS) + +$(TARGET): $(OBJECTS) + $(CXX) -shared -o $@ $(OBJECTS) $(LDFLAGS) -lapr-1 + +-include $(DEPS) diff --git a/cpp/common/concurrent/inc/APRBase.h b/cpp/common/concurrent/inc/APRBase.h new file mode 100644 index 0000000000..e0b526faa1 --- /dev/null +++ b/cpp/common/concurrent/inc/APRBase.h @@ -0,0 +1,63 @@ +/* + * + * 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. + * + */ +#ifndef _APRBase_ +#define _APRBase_ + +#include <string> +#include "apr_thread_mutex.h" +#include "apr_errno.h" + +namespace qpid { +namespace concurrent { + + /** + * 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 std::string& file, const int line); + std::string get_desc(apr_status_t status); + +#define CHECK_APR_SUCCESS(A) check(A, __FILE__, __LINE__); + +} +} + + + + +#endif diff --git a/cpp/common/concurrent/inc/APRMonitor.h b/cpp/common/concurrent/inc/APRMonitor.h new file mode 100644 index 0000000000..bf72596564 --- /dev/null +++ b/cpp/common/concurrent/inc/APRMonitor.h @@ -0,0 +1,48 @@ +/* + * + * 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. + * + */ +#ifndef _APRMonitor_ +#define _APRMonitor_ + +#include "apr_thread_mutex.h" +#include "apr_thread_cond.h" +#include "Monitor.h" + +namespace qpid { +namespace concurrent { + + class APRMonitor : public virtual Monitor + { + apr_pool_t* pool; + apr_thread_mutex_t* mutex; + apr_thread_cond_t* condition; + + public: + APRMonitor(); + virtual ~APRMonitor(); + virtual void wait(); + virtual void wait(u_int64_t time); + virtual void notify(); + virtual void notifyAll(); + virtual void acquire(); + virtual void release(); + }; +} +} + + +#endif diff --git a/cpp/common/concurrent/inc/APRThread.h b/cpp/common/concurrent/inc/APRThread.h new file mode 100644 index 0000000000..d5034ce3b7 --- /dev/null +++ b/cpp/common/concurrent/inc/APRThread.h @@ -0,0 +1,48 @@ +/* + * + * 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. + * + */ +#ifndef _APRThread_ +#define _APRThread_ + +#include "apr_thread_proc.h" +#include "APRThread.h" +#include "Runnable.h" +#include "Thread.h" + +namespace qpid { +namespace concurrent { + + class APRThread : public virtual Thread + { + const Runnable* runnable; + apr_pool_t* pool; + apr_thread_t* runner; + + public: + APRThread(apr_pool_t* pool, Runnable* runnable); + virtual ~APRThread(); + virtual void start(); + virtual void join(); + virtual void interrupt(); + static unsigned int currentThread(); + }; + +} +} + + +#endif diff --git a/cpp/common/concurrent/inc/APRThreadFactory.h b/cpp/common/concurrent/inc/APRThreadFactory.h new file mode 100644 index 0000000000..87b240025d --- /dev/null +++ b/cpp/common/concurrent/inc/APRThreadFactory.h @@ -0,0 +1,44 @@ +/* + * + * 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. + * + */ +#ifndef _APRThreadFactory_ +#define _APRThreadFactory_ + +#include "apr_thread_proc.h" + +#include "APRThread.h" +#include "Thread.h" +#include "ThreadFactory.h" +#include "Runnable.h" + +namespace qpid { +namespace concurrent { + + class APRThreadFactory : public virtual ThreadFactory + { + apr_pool_t* pool; + public: + APRThreadFactory(); + virtual ~APRThreadFactory(); + virtual Thread* create(Runnable* runnable); + }; + +} +} + + +#endif diff --git a/cpp/common/concurrent/inc/APRThreadPool.h b/cpp/common/concurrent/inc/APRThreadPool.h new file mode 100644 index 0000000000..cf6d30774c --- /dev/null +++ b/cpp/common/concurrent/inc/APRThreadPool.h @@ -0,0 +1,67 @@ +/* + * + * 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. + * + */ +#ifndef _APRThreadPool_ +#define _APRThreadPool_ + +#include <queue> +#include <vector> +#include "APRMonitor.h" +#include "Thread.h" +#include "ThreadFactory.h" +#include "ThreadPool.h" +#include "Runnable.h" + +namespace qpid { +namespace concurrent { + + class APRThreadPool : public virtual ThreadPool + { + class Worker : public virtual Runnable{ + APRThreadPool* pool; + public: + inline Worker(APRThreadPool* _pool) : pool(_pool){} + inline virtual void run(){ + while(pool->running){ + pool->runTask(); + } + } + }; + const bool deleteFactory; + const int size; + ThreadFactory* factory; + APRMonitor lock; + std::vector<Thread*> threads; + std::queue<Runnable*> tasks; + Worker* worker; + volatile bool running; + + void runTask(); + public: + APRThreadPool(int size); + APRThreadPool(int size, ThreadFactory* factory); + virtual void start(); + virtual void stop(); + virtual void addTask(Runnable* task); + virtual ~APRThreadPool(); + }; + +} +} + + +#endif diff --git a/cpp/common/concurrent/inc/LMonitor.h b/cpp/common/concurrent/inc/LMonitor.h new file mode 100644 index 0000000000..8e2569921d --- /dev/null +++ b/cpp/common/concurrent/inc/LMonitor.h @@ -0,0 +1,44 @@ +/* + * + * 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. + * + */ +#ifndef _LMonitor_ +#define _LMonitor_ + +/* Native Linux Monitor - Based of Kernel patch 19/20 */ + +#include "Monitor.h" + +namespace qpid { +namespace concurrent { + + class LMonitor : public virtual Monitor + { + + public: + LMonitor(); + virtual ~LMonitor(); + virtual void wait(); + virtual void notify(); + virtual void notifyAll(); + virtual void acquire(); + virtual void release(); + }; +} +} + + +#endif diff --git a/cpp/common/concurrent/inc/LThreadFactory.h b/cpp/common/concurrent/inc/LThreadFactory.h new file mode 100644 index 0000000000..4a573d1bd1 --- /dev/null +++ b/cpp/common/concurrent/inc/LThreadFactory.h @@ -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. + * + */ +#ifndef _LAPRThreadFactory_ +#define _LAPRThreadFactory_ + + +namespace qpid { +namespace concurrent { + + class LThreadFactory + { + public: + LThreadFactory(); + virtual ~LThreadFactory(); + virtual Thread* create(Runnable* runnable); + }; + +} +} + + +#endif diff --git a/cpp/common/concurrent/inc/LockedQueue.h b/cpp/common/concurrent/inc/LockedQueue.h new file mode 100644 index 0000000000..ef3f0b8381 --- /dev/null +++ b/cpp/common/concurrent/inc/LockedQueue.h @@ -0,0 +1,68 @@ +/* + * + * 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. + * + */ +#ifndef _LockedQueue_ +#define _LockedQueue_ + +#include <queue> +#include "Monitor.h" + +/** + * A threadsafe queue abstraction + */ +namespace qpid { +namespace concurrent { + template<class T, class L> class LockedQueue + { + L lock; + std::queue<T*> queue; + + public: + void put(T* item); + T* take(); + bool empty(); + }; + + template<class T, class L> void LockedQueue<T, L>::put(T* item){ + lock.acquire(); + queue.push(item); + lock.release(); + } + + template<class T, class L> T* LockedQueue<T, L>::take(){ + lock.acquire(); + T* item = 0; + if(!queue.empty()){ + item = queue.front(); + queue.pop(); + } + lock.release(); + return item; + } + + template<class T, class L> bool LockedQueue<T, L>::empty(){ + lock.acquire(); + bool result = queue.empty(); + lock.release(); + return result; + } + +} +} + + +#endif diff --git a/cpp/common/concurrent/inc/Monitor.h b/cpp/common/concurrent/inc/Monitor.h new file mode 100644 index 0000000000..7f1a299c6a --- /dev/null +++ b/cpp/common/concurrent/inc/Monitor.h @@ -0,0 +1,59 @@ +/* + * + * 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. + * + */ +#ifndef _Monitor_ +#define _Monitor_ + +#include "amqp_types.h" + +namespace qpid { +namespace concurrent { + +class Monitor +{ + public: + virtual ~Monitor(){} + virtual void wait() = 0; + virtual void wait(u_int64_t time) = 0; + virtual void notify() = 0; + virtual void notifyAll() = 0; + virtual void acquire() = 0; + virtual void release() = 0; +}; + +/** + * Scoped locker for a monitor. + */ +class Locker +{ + public: + Locker(Monitor& lock_) : lock(lock_) { lock.acquire(); } + ~Locker() { lock.release(); } + + private: + Monitor& lock; + + // private and unimplemented to prevent copying + Locker(const Locker&); + void operator=(const Locker&); +}; + +} +} + + +#endif diff --git a/cpp/common/concurrent/inc/MonitorImpl.h b/cpp/common/concurrent/inc/MonitorImpl.h new file mode 100644 index 0000000000..e96e81d795 --- /dev/null +++ b/cpp/common/concurrent/inc/MonitorImpl.h @@ -0,0 +1,57 @@ +/* + * + * 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. + * + */ + + +#ifndef _MonitorImpl_ +#define _MonitorImpl_ + +#ifdef _USE_APR_IO_ +#include "APRMonitor.h" +#else /* use POSIX Monitor */ +#include "LMonitor.h" +#endif + + +namespace qpid { +namespace concurrent { + +#ifdef _USE_APR_IO_ + class MonitorImpl : public virtual APRMonitor + { + + public: + MonitorImpl() : APRMonitor(){}; + virtual ~MonitorImpl(){}; + + }; +#else + class MonitorImpl : public virtual LMonitor + { + + public: + MonitorImpl() : LMonitor(){}; + virtual ~MonitorImpl(){}; + + }; +#endif + +} +} + + +#endif diff --git a/cpp/common/concurrent/inc/Runnable.h b/cpp/common/concurrent/inc/Runnable.h new file mode 100644 index 0000000000..523ad813f7 --- /dev/null +++ b/cpp/common/concurrent/inc/Runnable.h @@ -0,0 +1,34 @@ +/* + * + * 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. + * + */ +#ifndef _Runnable_ +#define _Runnable_ + +namespace qpid { +namespace concurrent { + + class Runnable + { + public: + virtual void run() = 0; + }; + +} +} + + +#endif diff --git a/cpp/common/concurrent/inc/TaskQueue.h b/cpp/common/concurrent/inc/TaskQueue.h new file mode 100644 index 0000000000..e06a3ce069 --- /dev/null +++ b/cpp/common/concurrent/inc/TaskQueue.h @@ -0,0 +1,200 @@ +/* + * + * 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. + * + */ +#ifndef _TaskQueue_ +#define _TaskQueue_ + +#include <iostream> +#include <memory> +#include <queue> +#include "LockedQueue.h" +#include "Runnable.h" +#include "ThreadPool.h" + +namespace qpid { +namespace concurrent { + template<class T, class L> class TaskQueue : public virtual Runnable + { + const int max_iterations_per_run; + L lock; + //LockedQueue<T, L> queue; + std::queue<T*> queue; + ThreadPool* const pool; + T* work; + bool running; + volatile bool stopped; + TaskQueue<T, L>* next; + + volatile bool inrun; + + bool hasWork(); + void completed(); + + T* take(); + + protected: + /** + * Callback though which the task is executed + */ + virtual void execute(T* t) = 0; + /** + * Allows a task to be completed asynchronously to the + * execute() call if required. + */ + virtual bool isComplete(T* t); + /** + * Should be called to signal completion of a task that was + * signalled as not complete through the isComplete() methods + * return value. This will allow normal processing to resume. + */ + virtual void complete(); + + public: + TaskQueue(ThreadPool* const pool, int max_iterations_per_run = 100); + virtual void run(); + void trigger(); + bool append(T* t); + void stop(bool drain); + inline void setNext(TaskQueue<T, L>* next){ this->next = next; } + }; + + template<class T, class L> TaskQueue<T, L>::TaskQueue(ThreadPool* const _pool, int _max_iterations_per_run) : + pool(_pool), + max_iterations_per_run(_max_iterations_per_run), + work(0), + running(false), + stopped(false), + next(0), inrun(false){ + } + + template<class T, class L> void TaskQueue<T, L>::run(){ + if(inrun) std::cout << "Already running" << std::endl; + inrun = true; + + bool blocked = false; + int count = max_iterations_per_run; + while(!blocked && hasWork() && count){ + execute(work); + if(isComplete(work)){ + completed(); + }else{ + blocked = true; + } + count--; + } + inrun = false; + + if(!blocked && count == 0){//performed max_iterations_per_run, requeue task to ensure fairness + //running will still be true at this point + lock.acquire(); + running = false; + if(stopped) lock.notify(); + lock.release(); + + trigger(); + }else if(hasWork()){//task was added to queue after we exited the loop above; should not need this? + trigger(); + } + } + + template<class T, class L> void TaskQueue<T, L>::trigger(){ + lock.acquire(); + if(!running){ + running = true; + pool->addTask(this); + } + lock.release(); + } + + template<class T, class L> bool TaskQueue<T, L>::hasWork(){ + lock.acquire(); + if(!work) work = take();//queue.take(); + if(!work){ + running = false; + if(stopped) lock.notify(); + } + lock.release(); + return work; + } + + template<class T, class L> bool TaskQueue<T, L>::append(T* item){ + if(!stopped){ + lock.acquire(); + + //queue.put(item); + queue.push(item); + + if(!running){ + running = true; + pool->addTask(this); + } + lock.release(); + //} + return true; + }else{ + return false; + } + } + + template<class T, class L> bool TaskQueue<T, L>::isComplete(T* item){ + return true;//by default assume all tasks are synchronous w.r.t. execute() + } + + + template<class T, class L> void TaskQueue<T, L>::completed(){ + if(next){ + if(!next->append(work)){ + std::cout << "Warning: dropping task as next queue appears to have stopped." << std::endl; + } + }else{ + delete work; + } + work = 0; + } + + template<class T, class L> void TaskQueue<T, L>::complete(){ + completed(); + lock.acquire(); + running = false; + if(stopped) lock.notify(); + lock.release(); + } + + template<class T, class L> void TaskQueue<T, L>::stop(bool drain){ + //prevent new tasks from being added + stopped = true; + //wait until no longer running + lock.acquire(); + while(running && (drain && hasWork())){ + lock.wait(); + } + lock.release(); + } + + template<class T, class L> T* TaskQueue<T, L>::take(){ + T* item = 0; + if(!queue.empty()){ + item = queue.front(); + queue.pop(); + } + return item; + } +} +} + + +#endif diff --git a/cpp/common/concurrent/inc/Thread.h b/cpp/common/concurrent/inc/Thread.h new file mode 100644 index 0000000000..6bd2a379ce --- /dev/null +++ b/cpp/common/concurrent/inc/Thread.h @@ -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. + * + */ +#ifndef _Thread_ +#define _Thread_ + +namespace qpid { +namespace concurrent { + + class Thread + { + public: + virtual ~Thread(){} + virtual void start() = 0; + virtual void join() = 0; + virtual void interrupt() = 0; + }; + +} +} + + +#endif diff --git a/cpp/common/concurrent/inc/ThreadFactory.h b/cpp/common/concurrent/inc/ThreadFactory.h new file mode 100644 index 0000000000..53be000ff3 --- /dev/null +++ b/cpp/common/concurrent/inc/ThreadFactory.h @@ -0,0 +1,38 @@ +/* + * + * 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. + * + */ +#ifndef _ThreadFactory_ +#define _ThreadFactory_ + +#include "Thread.h" +#include "Runnable.h" + +namespace qpid { +namespace concurrent { + + class ThreadFactory + { + public: + virtual ~ThreadFactory(){} + virtual Thread* create(Runnable* runnable) = 0; + }; + +} +} + + +#endif diff --git a/cpp/common/concurrent/inc/ThreadFactoryImpl.h b/cpp/common/concurrent/inc/ThreadFactoryImpl.h new file mode 100644 index 0000000000..a534b3c1e2 --- /dev/null +++ b/cpp/common/concurrent/inc/ThreadFactoryImpl.h @@ -0,0 +1,52 @@ +/* + * + * 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. + * + */ +#ifndef _ThreadFactoryImpl_ +#define _ThreadFactoryImpl_ + + +#ifdef _USE_APR_IO_ +#include "APRThreadFactory.h" +#else +#include "LThreadFactory.h" +#endif + + +namespace qpid { +namespace concurrent { + + +#ifdef _USE_APR_IO_ + class ThreadFactoryImpl : public virtual APRThreadFactory + { + public: + ThreadFactoryImpl(): APRThreadFactory() {}; + virtual ~ThreadFactoryImpl() {}; + }; +#else + class ThreadFactoryImpl : public virtual LThreadFactory + { + public: + ThreadFactoryImpl(): LThreadFactory() {}; + virtual ~ThreadFactoryImpl() {}; + }; +#endif +} +} + + +#endif diff --git a/cpp/common/concurrent/inc/ThreadPool.h b/cpp/common/concurrent/inc/ThreadPool.h new file mode 100644 index 0000000000..679c889ff3 --- /dev/null +++ b/cpp/common/concurrent/inc/ThreadPool.h @@ -0,0 +1,40 @@ +/* + * + * 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. + * + */ +#ifndef _ThreadPool_ +#define _ThreadPool_ + +#include "Thread.h" +#include "Runnable.h" + +namespace qpid { +namespace concurrent { + + class ThreadPool + { + public: + virtual void start() = 0; + virtual void stop() = 0; + virtual void addTask(Runnable* runnable) = 0; + virtual ~ThreadPool(){} + }; + +} +} + + +#endif diff --git a/cpp/common/concurrent/src/APRBase.cpp b/cpp/common/concurrent/src/APRBase.cpp new file mode 100644 index 0000000000..f87ea9e25f --- /dev/null +++ b/cpp/common/concurrent/src/APRBase.cpp @@ -0,0 +1,97 @@ +/* + * + * 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 <iostream> +#include "APRBase.h" +#include "QpidError.h" + +using namespace qpid::concurrent; + +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()){ + std::cout << "WARNING: APR initialization triggered concurrently with termination." << std::endl; + } +} + +void APRBase::decrement(){ + getInstance()->_decrement(); +} + +void qpid::concurrent::check(apr_status_t status, const std::string& file, const int line){ + if (status != APR_SUCCESS){ + const int size = 50; + char tmp[size]; + std::string msg(apr_strerror(status, tmp, size)); + throw QpidError(APR_ERROR + ((int) status), msg, file, line); + } +} + +std::string qpid::concurrent::get_desc(apr_status_t status){ + const int size = 50; + char tmp[size]; + std::string msg(apr_strerror(status, tmp, size)); + return msg; +} + diff --git a/cpp/common/concurrent/src/APRMonitor.cpp b/cpp/common/concurrent/src/APRMonitor.cpp new file mode 100644 index 0000000000..428d76dff9 --- /dev/null +++ b/cpp/common/concurrent/src/APRMonitor.cpp @@ -0,0 +1,60 @@ +/* + * + * 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 "APRMonitor.h" +#include <iostream> + +qpid::concurrent::APRMonitor::APRMonitor(){ + APRBase::increment(); + CHECK_APR_SUCCESS(apr_pool_create(&pool, NULL)); + CHECK_APR_SUCCESS(apr_thread_mutex_create(&mutex, APR_THREAD_MUTEX_NESTED, pool)); + CHECK_APR_SUCCESS(apr_thread_cond_create(&condition, pool)); +} + +qpid::concurrent::APRMonitor::~APRMonitor(){ + CHECK_APR_SUCCESS(apr_thread_cond_destroy(condition)); + CHECK_APR_SUCCESS(apr_thread_mutex_destroy(mutex)); + apr_pool_destroy(pool); + APRBase::decrement(); +} + +void qpid::concurrent::APRMonitor::wait(){ + CHECK_APR_SUCCESS(apr_thread_cond_wait(condition, mutex)); +} + + +void qpid::concurrent::APRMonitor::wait(u_int64_t time){ + apr_status_t status = apr_thread_cond_timedwait(condition, mutex, time * 1000); + if(!status == APR_TIMEUP) CHECK_APR_SUCCESS(status); +} + +void qpid::concurrent::APRMonitor::notify(){ + CHECK_APR_SUCCESS(apr_thread_cond_signal(condition)); +} + +void qpid::concurrent::APRMonitor::notifyAll(){ + CHECK_APR_SUCCESS(apr_thread_cond_broadcast(condition)); +} + +void qpid::concurrent::APRMonitor::acquire(){ + CHECK_APR_SUCCESS(apr_thread_mutex_lock(mutex)); +} + +void qpid::concurrent::APRMonitor::release(){ + CHECK_APR_SUCCESS(apr_thread_mutex_unlock(mutex)); +} diff --git a/cpp/common/concurrent/src/APRThread.cpp b/cpp/common/concurrent/src/APRThread.cpp new file mode 100644 index 0000000000..4202fe81b6 --- /dev/null +++ b/cpp/common/concurrent/src/APRThread.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 "APRBase.h" +#include "APRThread.h" +#include "apr_portable.h" + +using namespace qpid::concurrent; + +void* APR_THREAD_FUNC ExecRunnable(apr_thread_t* thread, void *data){ + ((Runnable*) data)->run(); + CHECK_APR_SUCCESS(apr_thread_exit(thread, APR_SUCCESS)); + return NULL; +} + +APRThread::APRThread(apr_pool_t* _pool, Runnable* _runnable) : pool(_pool), runnable(_runnable){} + +APRThread::~APRThread(){ +} + +void APRThread::start(){ + CHECK_APR_SUCCESS(apr_thread_create(&runner, NULL, ExecRunnable,(void*) runnable, pool)); +} + +void APRThread::join(){ + apr_status_t status; + CHECK_APR_SUCCESS(apr_thread_join(&status, runner)); +} + +void APRThread::interrupt(){ + CHECK_APR_SUCCESS(apr_thread_exit(runner, APR_SUCCESS)); +} + +unsigned int qpid::concurrent::APRThread::currentThread(){ + return apr_os_thread_current(); +} diff --git a/cpp/common/concurrent/src/APRThreadFactory.cpp b/cpp/common/concurrent/src/APRThreadFactory.cpp new file mode 100644 index 0000000000..9ba68e9e56 --- /dev/null +++ b/cpp/common/concurrent/src/APRThreadFactory.cpp @@ -0,0 +1,35 @@ +/* + * + * 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 "APRThreadFactory.h" + +using namespace qpid::concurrent; + +APRThreadFactory::APRThreadFactory(){ + APRBase::increment(); + CHECK_APR_SUCCESS(apr_pool_create(&pool, NULL)); +} + +APRThreadFactory::~APRThreadFactory(){ + apr_pool_destroy(pool); + APRBase::decrement(); +} + +Thread* APRThreadFactory::create(Runnable* runnable){ + return new APRThread(pool, runnable); +} diff --git a/cpp/common/concurrent/src/APRThreadPool.cpp b/cpp/common/concurrent/src/APRThreadPool.cpp new file mode 100644 index 0000000000..e0fcb804e6 --- /dev/null +++ b/cpp/common/concurrent/src/APRThreadPool.cpp @@ -0,0 +1,85 @@ +/* + * + * 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 "APRThreadFactory.h" +#include "APRThreadPool.h" +#include "QpidError.h" +#include <iostream> + +using namespace qpid::concurrent; + +APRThreadPool::APRThreadPool(int _size) : size(_size), factory(new APRThreadFactory()), + deleteFactory(true), running(false){ + worker = new Worker(this); +} + +APRThreadPool::APRThreadPool(int _size, ThreadFactory* _factory) : size(_size), factory(_factory), + deleteFactory(false), running(false){ + worker = new Worker(this); +} + +APRThreadPool::~APRThreadPool(){ + if(deleteFactory) delete factory; +} + +void APRThreadPool::addTask(Runnable* task){ + lock.acquire(); + tasks.push(task); + lock.notifyAll(); + lock.release(); +} + +void APRThreadPool::runTask(){ + lock.acquire(); + while(tasks.empty()){ + lock.wait(); + } + Runnable* task = tasks.front(); + tasks.pop(); + lock.release(); + try{ + task->run(); + }catch(qpid::QpidError error){ + std::cout << "Error [" << error.code << "] " << error.msg << " (" << error.file << ":" << error.line << ")" << std::endl; + } +} + +void APRThreadPool::start(){ + if(!running){ + running = true; + for(int i = 0; i < size; i++){ + Thread* t = factory->create(worker); + t->start(); + threads.push_back(t); + } + } +} + +void APRThreadPool::stop(){ + if(!running){ + running = false; + lock.acquire(); + lock.notifyAll(); + lock.release(); + for(int i = 0; i < size; i++){ + threads[i]->join(); + delete threads[i]; + } + } +} + + diff --git a/cpp/common/error/inc/QpidError.h b/cpp/common/error/inc/QpidError.h new file mode 100644 index 0000000000..a739b506c7 --- /dev/null +++ b/cpp/common/error/inc/QpidError.h @@ -0,0 +1,47 @@ +/* + * + * 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> + +#ifndef __QpidError__ +#define __QpidError__ + +namespace qpid { + +class QpidError{ +public: + const int code; + const std::string msg; + const std::string file; + const int line; + + inline QpidError(int _code, const std::string& _msg, const std::string& _file, int _line) : code(_code), msg(_msg), file(_file), line(_line) {} + ~QpidError(){} + +}; + +#define THROW_QPID_ERROR(A, B) throw QpidError(A, B, __FILE__, __LINE__) + +} + +#define PROTOCOL_ERROR 10000 +#define APR_ERROR 20000 +#define FRAMING_ERROR 30000 +#define CLIENT_ERROR 40000 +#define INTERNAL_ERROR 50000 + +#endif diff --git a/cpp/common/error/inc/QpidErrorIO.h b/cpp/common/error/inc/QpidErrorIO.h new file mode 100644 index 0000000000..4f9bd3ce26 --- /dev/null +++ b/cpp/common/error/inc/QpidErrorIO.h @@ -0,0 +1,29 @@ +/* + * + * 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 "QpidError.h" +#include <ostream> + +std::ostream& operator <<(std::ostream& out, const QpidError& error) +{ + out << "Qpid Error [" << error.code << "] " << error.msg + << " (" << error.file << ":" << error.line << ")"; + return out; +} + + diff --git a/cpp/common/framing/Makefile b/cpp/common/framing/Makefile new file mode 100644 index 0000000000..1dfc286050 --- /dev/null +++ b/cpp/common/framing/Makefile @@ -0,0 +1,28 @@ +# +# 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. +# + +.PHONY: all clean test + +all: + @$(MAKE) -C generated all + +test: + @$(MAKE) -C test all + +clean : + @$(MAKE) -C generated clean + @$(MAKE) -C test clean + diff --git a/cpp/common/framing/generated/Makefile b/cpp/common/framing/generated/Makefile new file mode 100644 index 0000000000..12ec402760 --- /dev/null +++ b/cpp/common/framing/generated/Makefile @@ -0,0 +1,41 @@ +# +# 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. +# + +QPID_HOME = ../../../.. +include ${QPID_HOME}/cpp/options.mk + +STYLESHEET_DIR = stylesheets +JAVA = java +XSLTP = ${TOOLS_DIR}/saxon8.jar + +SPEC = ${SPEC_DIR}/amqp-8.0.xml +STYLESHEETS = $(wildcard stylesheets/*.xsl) + +GENERATED_SOURCES=amqp_methods.cpp # Seed generation + +.PHONY: all clean + +all: ${GENERATED_SOURCES} + +clean : + -@rm -f *.cpp *.h + +${GENERATED_SOURCES}: ${STYLESHEETS} ${SPEC} + ${JAVA} -jar ${XSLTP} -o results.out ${SPEC} ${STYLESHEET_DIR}/code_gen.xsl + ${JAVA} -jar ${XSLTP} -o results.out ${SPEC} ${STYLESHEET_DIR}/framing.xsl + +-include $(GENERATED_SOURCES:.cpp=.d) + diff --git a/cpp/common/framing/generated/stylesheets/amqp_client.xsl b/cpp/common/framing/generated/stylesheets/amqp_client.xsl new file mode 100644 index 0000000000..f0fa3d7890 --- /dev/null +++ b/cpp/common/framing/generated/stylesheets/amqp_client.xsl @@ -0,0 +1,155 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:amqp="http://amqp.org"> + + <xsl:import href="code_utils.xsl"/> + + <!-- + ================== + Template: client_h + ================== + Client header file. + --> + <xsl:template match="amqp" mode="client_h"> + <xsl:param name="domain-cpp-table"/> + <xsl:result-document href="AMQP_Client.h" format="textFormat"> + <xsl:value-of select="amqp:copyright()"/> + <xsl:text> +#ifndef _AMQP_Client_ +#define _AMQP_Client_ + +#include "AMQP_ServerOperations.h" +#include "FieldTable.h" +#include "OutputHandler.h" + +namespace qpid { +namespace framing { + +class AMQP_Client : virtual public AMQP_ServerOperations +{ + OutputHandler* out; + + public: + AMQP_Client(OutputHandler* _out); + virtual ~AMQP_Client() {}

</xsl:text> + <xsl:for-each select="class"> + <xsl:variable name="class" select="amqp:cpp-class-name(@name)"/> + <xsl:if test="doc"> + <xsl:text>
/**
===== Class: </xsl:text><xsl:value-of select="$class"/><xsl:text> =====
</xsl:text> + <xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>
*/
</xsl:text> + </xsl:if> + <xsl:text> class </xsl:text><xsl:value-of select="$class"/><xsl:text> : virtual public AMQP_ServerOperations::</xsl:text><xsl:value-of select="$class"/><xsl:text>Handler + { + OutputHandler* out; + + public: + /* Constructors and destructors */ + </xsl:text><xsl:value-of select="$class"/><xsl:text>(OutputHandler* _out); + virtual ~</xsl:text><xsl:value-of select="$class"/><xsl:text>(); + + /* Protocol methods */
</xsl:text> + <xsl:for-each select="method"> + <xsl:if test="chassis[@name='server']"> + <xsl:variable name="method" select="amqp:cpp-name(@name)"/> + <xsl:if test="doc"> + <xsl:text>
/**
----- Method: </xsl:text><xsl:value-of select="$class"/><xsl:text>.</xsl:text><xsl:value-of select="$method"/><xsl:text> -----
</xsl:text> + <xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>
*/
</xsl:text> + </xsl:if> + <xsl:for-each select="rule"> + <xsl:text>
/**
</xsl:text> + <xsl:text>Rule "</xsl:text><xsl:value-of select="@name"/><xsl:text>":
</xsl:text><xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>
*/
</xsl:text> + </xsl:for-each> + <xsl:text> virtual void </xsl:text><xsl:value-of select="$method"/> + <xsl:text>( u_int16_t channel</xsl:text><xsl:if test="field"><xsl:text>,
 </xsl:text> + <xsl:for-each select="field"> + <xsl:variable name="domain-cpp-type" select="amqp:cpp-lookup(@domain, $domain-cpp-table)"/> + <xsl:value-of select="concat($domain-cpp-type, amqp:cpp-arg-ref($domain-cpp-type), ' ', amqp:cpp-name(@name))"/> + <xsl:if test="position()!=last()"> + <xsl:text>,
 </xsl:text> + </xsl:if> + </xsl:for-each> + </xsl:if> + <xsl:text> );
</xsl:text> + </xsl:if> + </xsl:for-each> + <xsl:text> }; /* class </xsl:text><xsl:value-of select="$class"/><xsl:text> */
</xsl:text> + </xsl:for-each> + <xsl:text>}; /* class AMQP_Client */ + +} /* namespace framing */ +} /* namespace qpid */ + +#endif
</xsl:text> + </xsl:result-document> + </xsl:template> + + + <!-- + ==================== + Template: client_cpp + ==================== + Client body. + --> + <xsl:template match="amqp" mode="client_cpp"> + <xsl:param name="domain-cpp-table"/> + <xsl:result-document href="AMQP_Client.cpp" format="textFormat"> + <xsl:value-of select="amqp:copyright()"/> + <xsl:text> + +#include "AMQP_Client.h" + +namespace qpid { +namespace framing { + +AMQP_Client::AMQP_Client(OutputHandler* _out) : + out(_out) +{ +}

</xsl:text> + <xsl:for-each select="class"> + <xsl:variable name="class" select="amqp:cpp-class-name(@name)"/> + <xsl:text>
/* ++++++++++ Class: </xsl:text><xsl:value-of select="$class"/><xsl:text> ++++++++++ */ + +AMQP_Client::</xsl:text><xsl:value-of select="$class"/><xsl:text>::</xsl:text><xsl:value-of select="$class"/><xsl:text>(OutputHandler* _out) : + out(_out) +{ +} + +AMQP_Client::</xsl:text><xsl:value-of select="$class"/><xsl:text>::~</xsl:text><xsl:value-of select="$class"/><xsl:text>() {}

</xsl:text> + <xsl:for-each select="method"> + <xsl:if test="chassis[@name='server']"> + <xsl:text>void AMQP_Client::</xsl:text><xsl:value-of select="$class"/><xsl:text>::</xsl:text> + <xsl:value-of select="amqp:cpp-name(@name)"/><xsl:text>( u_int16_t channel</xsl:text><xsl:if test="field"> + <xsl:text>,
 </xsl:text> + <xsl:for-each select="field"> + <xsl:variable name="domain-cpp-type" select="amqp:cpp-lookup(@domain, $domain-cpp-table)"/> + <xsl:value-of select="concat($domain-cpp-type, amqp:cpp-arg-ref($domain-cpp-type), ' ', amqp:cpp-name(@name))"/> + <xsl:if test="position()!=last()"> + <xsl:text>,
 </xsl:text> + </xsl:if> + </xsl:for-each> + </xsl:if> + <xsl:text> ) +{ + out->send( new AMQFrame( channel, + new </xsl:text><xsl:value-of select="concat($class, amqp:field-name(@name), 'Body')"/><xsl:text>( </xsl:text> + <xsl:for-each select="field"> + <xsl:value-of select="amqp:cpp-name(@name)"/> + <xsl:if test="position()!=last()"> + <xsl:text>,
 </xsl:text> + </xsl:if> + </xsl:for-each> + <xsl:text> ) ) ); +}

</xsl:text> + </xsl:if> + </xsl:for-each> + </xsl:for-each> + <xsl:text> + +} /* namespace framing */ +} /* namespace qpid */
</xsl:text> + </xsl:result-document> + </xsl:template> + +</xsl:stylesheet> diff --git a/cpp/common/framing/generated/stylesheets/amqp_client_handler_impl.xsl b/cpp/common/framing/generated/stylesheets/amqp_client_handler_impl.xsl new file mode 100644 index 0000000000..aa095eaf79 --- /dev/null +++ b/cpp/common/framing/generated/stylesheets/amqp_client_handler_impl.xsl @@ -0,0 +1,187 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:amqp="http://amqp.org"> + + <xsl:import href="code_utils.xsl"/> + + <!-- + =============================== + Template: client_handler_impl_h + =============================== + Template to generate the AMQP_ServerHandlerImpl class header file. + --> + <xsl:template match="amqp" mode="client_handler_impl_h"> + <xsl:param name="domain-cpp-table"/> + <xsl:result-document href="AMQP_ClientHandlerImpl.h" format="textFormat"> + <xsl:value-of select="amqp:copyright()"/> + <xsl:text> +#ifndef _AMQP_ClientHandlerImpl_ +#define _AMQP_ClientHandlerImpl_ + +#include "AMQP_ClientOperations.h" +#include "FieldTable.h" + +namespace qpid { +namespace framing { + +class AMQP_ClientHandlerImpl : virtual public AMQP_ClientOperations +{
</xsl:text> + + <!-- List of pointers to each inner class instance --> + <xsl:for-each select="class"> + <xsl:variable name="class" select="concat(amqp:cpp-class-name(@name), 'Handler')"/> + <xsl:text> AMQP_ClientOperations::</xsl:text><xsl:value-of select="$class"/><xsl:text>* </xsl:text> + <xsl:value-of select="$class"/><xsl:text>Ptr;
</xsl:text> + </xsl:for-each> + <xsl:text> + public: + AMQP_ClientHandlerImpl(); + virtual ~AMQP_ClientHandlerImpl();

</xsl:text> + + <!-- List of functions to return pointer to each inner class instance --> + <xsl:for-each select="class"> + <xsl:variable name="class" select="concat(amqp:cpp-class-name(@name), 'Handler')"/> + <xsl:text> inline AMQP_ClientOperations::</xsl:text> + <xsl:value-of select="$class"/><xsl:text>* get</xsl:text><xsl:value-of select="$class"/> + <xsl:text>() { return </xsl:text><xsl:value-of select="$class"/><xsl:text>Ptr; }
</xsl:text> + </xsl:for-each> + <xsl:text>
</xsl:text> + + <!-- Inner classes --> + <xsl:for-each select="class"> + <xsl:variable name="class" select="concat(amqp:cpp-class-name(@name), 'Handler')"/> + + <!-- Inner class documentation & rules --> + <xsl:if test="doc"> + <xsl:text>
/**
</xsl:text> + <xsl:text>===== Class: </xsl:text><xsl:value-of select="$class"/><xsl:text>Impl =====
</xsl:text> + <xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>*/
</xsl:text> + </xsl:if> + + <!-- Inner class definition --> + <xsl:text> class </xsl:text><xsl:value-of select="$class"/> + <xsl:text>Impl : virtual public AMQP_ClientOperations::</xsl:text><xsl:value-of select="$class"/> + <xsl:text>
 { + public: + /* Constructors and destructors */ + </xsl:text><xsl:value-of select="$class"/><xsl:text>Impl(); + virtual ~</xsl:text><xsl:value-of select="$class"/><xsl:text>Impl(); + + /* Protocol methods */
</xsl:text> + + <!-- Inner class methods (only if the chassis is set to "client") --> + <xsl:for-each select="method"> + <xsl:if test="chassis[@name='client']"> + <xsl:variable name="method" select="amqp:cpp-name(@name)"/> + + <!-- Inner class method documentation & rules --> + <xsl:if test="doc"> + <xsl:text>
/**
</xsl:text> + <xsl:text>----- Method: </xsl:text><xsl:value-of select="$class"/> + <xsl:text>Impl.</xsl:text><xsl:value-of select="@name"/><xsl:text> -----
</xsl:text> + <xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>*/
</xsl:text> + </xsl:if> + <xsl:for-each select="rule"> + <xsl:text>
/**
</xsl:text> + <xsl:text>Rule "</xsl:text><xsl:value-of select="@name"/><xsl:text>":
</xsl:text> + <xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>*/
</xsl:text> + </xsl:for-each> + + <!-- Inner class method definition --> + <xsl:text>
 virtual void </xsl:text><xsl:value-of select="$method"/> + <xsl:text>( u_int16_t channel</xsl:text> + + <!-- Inner class method parameter definition --> + <xsl:if test="field"> + <xsl:text>,
 </xsl:text> + <xsl:for-each select="field"> + <xsl:variable name="domain-cpp-type" select="amqp:cpp-lookup(@domain, $domain-cpp-table)"/> + <xsl:value-of select="concat($domain-cpp-type, amqp:cpp-arg-ref($domain-cpp-type), ' ', amqp:cpp-name(@name))"/> + <xsl:if test="position()!=last()"> + <xsl:text>,
 </xsl:text> + </xsl:if> + </xsl:for-each> + </xsl:if> + <xsl:text> );
</xsl:text> + </xsl:if> + </xsl:for-each> + <xsl:text>
 }; /* class </xsl:text><xsl:value-of select="$class"/><xsl:text>Impl */
</xsl:text> + </xsl:for-each> + <xsl:text>
}; /* AMQP_ClientHandlerImpl */ + +} /* namespace framing */ +} /* namespace qpid */ + +#endif
</xsl:text> + </xsl:result-document> + </xsl:template> + + <!-- + ================================= + Template: client_handler_impl_cpp + ================================= + Template to generate the AMQP_ServerHandlerImpl class stubs. + --> + <xsl:template match="amqp" mode="client_handler_impl_cpp"> + <xsl:param name="domain-cpp-table"/> + <xsl:result-document href="AMQP_ClientHandlerImpl.cpp" format="textFormat"> + <xsl:value-of select="amqp:copyright()"/> + <xsl:text> +#include "AMQP_ClientHandlerImpl.h" + +namespace qpid { +namespace framing { + +AMQP_ClientHandlerImpl::AMQP_ClientHandlerImpl() :
 </xsl:text> + <xsl:for-each select="class"> + <xsl:variable name="class" select="amqp:cpp-class-name(@name)"/> + <xsl:value-of select="$class"/> + <xsl:text>HandlerPtr( new </xsl:text><xsl:value-of select="$class"/><xsl:text>HandlerImpl() )</xsl:text> + <xsl:if test="position()!=last()"> + <xsl:text>,
 </xsl:text> + </xsl:if> + </xsl:for-each> + <xsl:text> +{ +} + +AMQP_ClientHandlerImpl::~AMQP_ClientHandlerImpl() +{
</xsl:text> + <xsl:for-each select="class"> + <xsl:text> delete </xsl:text><xsl:value-of select="amqp:cpp-class-name(@name)"/><xsl:text>HandlerPtr;
</xsl:text> + </xsl:for-each>} + + <xsl:for-each select="class"> + <xsl:variable name="class" select="amqp:cpp-class-name(@name)"/> + <xsl:text>
/* ===== Class: </xsl:text><xsl:value-of select="$class"/><xsl:text>HandlerImpl ===== */

</xsl:text> + <xsl:text>AMQP_ClientHandlerImpl::</xsl:text><xsl:value-of select="$class"/><xsl:text>HandlerImpl::</xsl:text> + <xsl:value-of select="$class"/><xsl:text>HandlerImpl()
{
}

</xsl:text> + <xsl:text>AMQP_ClientHandlerImpl::</xsl:text><xsl:value-of select="$class"/><xsl:text>HandlerImpl::~</xsl:text> + <xsl:value-of select="$class"/><xsl:text>HandlerImpl()
{
}

</xsl:text> + <xsl:for-each select="method"> + <xsl:if test="chassis[@name='client']"> + <xsl:text>void AMQP_ClientHandlerImpl::</xsl:text><xsl:value-of select="$class"/><xsl:text>HandlerImpl::</xsl:text> + <xsl:value-of select="amqp:cpp-name(@name)"/><xsl:text>( u_int16_t channel</xsl:text> + <xsl:if test="field"> + <xsl:text>,
 </xsl:text> + <xsl:for-each select="field"> + <xsl:variable name="domain-cpp-type" select="amqp:cpp-lookup(@domain, $domain-cpp-table)"/> + <xsl:value-of select="concat($domain-cpp-type, amqp:cpp-arg-ref($domain-cpp-type), ' ', amqp:cpp-name(@name))"/> + <xsl:if test="position()!=last()"> + <xsl:text>,
 </xsl:text> + </xsl:if> + </xsl:for-each> + </xsl:if><xsl:text> )
{
}

</xsl:text> + </xsl:if> + </xsl:for-each> + </xsl:for-each> + <xsl:text> + +} /* namespace framing */ +} /* namespace qpid */

</xsl:text> + </xsl:result-document> + </xsl:template> + +</xsl:stylesheet> diff --git a/cpp/common/framing/generated/stylesheets/amqp_client_operations.xsl b/cpp/common/framing/generated/stylesheets/amqp_client_operations.xsl new file mode 100644 index 0000000000..234b7080ba --- /dev/null +++ b/cpp/common/framing/generated/stylesheets/amqp_client_operations.xsl @@ -0,0 +1,105 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:amqp="http://amqp.org"> + + <xsl:import href="code_utils.xsl"/> + + <!-- + ============================= + Template: client-operations-h + ============================= + Template to generate the AMQP_ClientHandler virtual class. This is the pure + virtual class from which the AMQP_Server and AMQP_ClientHandlerImpl classes + are derived. + --> + <xsl:template match="amqp" mode="client-operations-h"> + <xsl:param name="domain-cpp-table"/> + <xsl:result-document href="AMQP_ClientOperations.h" format="textFormat"> + <xsl:value-of select="amqp:copyright()"/> + <xsl:text> +#ifndef _AMQP_ClientOperations_ +#define _AMQP_ClientOperations_ + +#include "AMQP_Constants.h" +#include "FieldTable.h" + +namespace qpid { +namespace framing { + +class AMQP_ClientOperations +{ + public: + AMQP_ClientOperations() {} + virtual ~AMQP_ClientOperations() {} + inline u_int16_t getAmqpMajor() { return (u_int16_t)</xsl:text><xsl:value-of select="@major"/><xsl:text>; } + inline u_int16_t getAmqpMinor() { return (u_int16_t)</xsl:text><xsl:value-of select="@minor"/><xsl:text>; }

</xsl:text> + + <!-- Inner classes --> + <xsl:for-each select="class"> + <xsl:variable name="class" select="concat(amqp:cpp-class-name(@name), 'Handler')"/> + + <!-- Inner class documentation & rules --> + <xsl:if test="doc"> + <xsl:text>
/**
===== Class: </xsl:text><xsl:value-of select="$class"/><xsl:text> =====
</xsl:text> + <xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>*/
</xsl:text> + </xsl:if> + + <!-- Inner class definition --> + <xsl:text> class </xsl:text><xsl:value-of select="$class"/><xsl:text> + { + public: + /* Constructors and destructors */ + </xsl:text><xsl:value-of select="$class"/><xsl:text>() {} + virtual ~</xsl:text><xsl:value-of select="$class"/><xsl:text>() {} + + /* Protocol methods */
</xsl:text> + + <!-- Inner class methods (only if the chassis is set to "client") --> + <xsl:for-each select="method"> + <xsl:if test="chassis[@name='client']"> + <xsl:variable name="method" select="amqp:cpp-name(@name)"/> + + <!-- Inner class method documentation & rules --> + <xsl:if test="doc"> + <xsl:text>
/**
----- Method: </xsl:text><xsl:value-of select="$class"/><xsl:text>.</xsl:text> + <xsl:value-of select="@name"/><xsl:text> -----
</xsl:text> + <xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>*/
</xsl:text> + </xsl:if> + <xsl:for-each select="rule"> + <xsl:text>
/**
</xsl:text> + <xsl:text>Rule "</xsl:text><xsl:value-of select="@name"/><xsl:text>":
</xsl:text> + <xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>*/
</xsl:text> + </xsl:for-each> + + <!-- Inner class method definition --> + <xsl:text> virtual void </xsl:text><xsl:value-of select="$method"/> + <xsl:text>( u_int16_t channel</xsl:text> + + <!-- Inner class method parameter definition --> + <xsl:if test="field"> + <xsl:text>,
 </xsl:text> + <xsl:for-each select="field"> + <xsl:variable name="domain-cpp-type" select="amqp:cpp-lookup(@domain, $domain-cpp-table)"/> + <xsl:value-of select="concat($domain-cpp-type, amqp:cpp-arg-ref($domain-cpp-type), ' ', amqp:cpp-name(@name))"/> + <xsl:if test="position()!=last()"> + <xsl:text>,
 </xsl:text> + </xsl:if> + </xsl:for-each> + </xsl:if> + <xsl:text> ) = 0;
</xsl:text> + </xsl:if> + </xsl:for-each> + <xsl:text>
 }; /* class </xsl:text><xsl:value-of select="$class"/><xsl:text> */
</xsl:text> + </xsl:for-each> + <xsl:text>
}; /* class AMQP_ClientOperations */ + +} /* namespace framing */ +} /* namespace qpid */ + +#endif
</xsl:text> + </xsl:result-document> + </xsl:template> + +</xsl:stylesheet> diff --git a/cpp/common/framing/generated/stylesheets/amqp_consts.xsl b/cpp/common/framing/generated/stylesheets/amqp_consts.xsl new file mode 100644 index 0000000000..c1c927f941 --- /dev/null +++ b/cpp/common/framing/generated/stylesheets/amqp_consts.xsl @@ -0,0 +1,77 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:amqp="http://amqp.org"> + + <xsl:import href="code_utils.xsl"/> + <xsl:output method="text" indent="yes" name="textFormat"/> + + <xsl:template match="/"> + <xsl:apply-templates select="amqp" mode="domain-table"/> + <xsl:apply-templates select="amqp" mode="domain-consts"/> + </xsl:template> + + <!-- + ====================== + Template: domain-table + ====================== + Generates the domain name to C++ type lookup table + which is required for later generation. + Format: + <domains> + <domain doamin-name="dname1" cpp-type="type1"/> + <domain doamin-name="dname2" cpp-type="type2"/> + ... + </domains> + --> + <xsl:template match="amqp" mode="domain-table"> + <domains><xsl:text>
</xsl:text> + <xsl:for-each select="domain"> + <xsl:text> </xsl:text><domain> + <xsl:attribute name="domain-name"> + <xsl:value-of select="@name"/> + </xsl:attribute> + <xsl:attribute name="cpp-type"> + <xsl:value-of select="amqp:cpp-type(@type)"/> + </xsl:attribute> + </domain><xsl:text>
</xsl:text> + </xsl:for-each> + </domains> + </xsl:template> + + <!-- + ======================= + Template: domain-consts + ======================= + Generates a header file (AMQP_Constants.h) containing definitions of + all the <constant> declarations in the AMQP XML specification. + --> + <xsl:template match="amqp" mode="domain-consts"> + <xsl:result-document href="AMQP_Constants.h" format="textFormat"> + <xsl:value-of select="amqp:copyright()"/> + <xsl:text> +#ifndef _AMQP_Constants_ +#define _AMQP_Constants_ + +#include "amqp_types.h" + +namespace qpid { +namespace framing { + +/**** Constants ****/

</xsl:text> + <xsl:for-each select="constant"> + <xsl:if test="doc"> + <xsl:text>
/*
</xsl:text> + <xsl:value-of select="normalize-space(doc)"/> + <xsl:text>
*/
</xsl:text> + </xsl:if> + <xsl:text>const u_int16_t </xsl:text><xsl:value-of select="concat('AMQP_', upper-case(amqp:cpp-name(@name)), ' = ', @value)"/><xsl:text>;
</xsl:text> + </xsl:for-each> + <xsl:text> + +} /* namespace framing */ +} /* namespace qpid */ + +#endif
</xsl:text> + </xsl:result-document> + </xsl:template> + +</xsl:stylesheet> diff --git a/cpp/common/framing/generated/stylesheets/amqp_server.xsl b/cpp/common/framing/generated/stylesheets/amqp_server.xsl new file mode 100644 index 0000000000..4ad29a4b95 --- /dev/null +++ b/cpp/common/framing/generated/stylesheets/amqp_server.xsl @@ -0,0 +1,155 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:amqp="http://amqp.org"> + + <xsl:import href="code_utils.xsl"/> + + <!-- + ================== + Template: server_h + ================== + Server header file. + --> + <xsl:template match="amqp" mode="server_h"> + <xsl:param name="domain-cpp-table"/> + <xsl:result-document href="AMQP_Server.h" format="textFormat"> + <xsl:value-of select="amqp:copyright()"/> + <xsl:text> +#ifndef _AMQP_Server_ +#define _AMQP_Server_ + +#include "AMQP_ClientOperations.h" +#include "FieldTable.h" +#include "OutputHandler.h" + +namespace qpid { +namespace framing { + +class AMQP_Server : virtual public AMQP_ClientOperations +{ + OutputHandler* out; + + public: + AMQP_Server(OutputHandler* _out); + virtual ~AMQP_Server() {}

</xsl:text> + <xsl:for-each select="class"> + <xsl:variable name="class" select="amqp:cpp-class-name(@name)"/> + <xsl:if test="doc"> + <xsl:text>
/**
===== Class: </xsl:text><xsl:value-of select="$class"/><xsl:text> =====
</xsl:text> + <xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>
*/
</xsl:text> + </xsl:if> + <xsl:text> class </xsl:text><xsl:value-of select="$class"/><xsl:text> : virtual public AMQP_ClientOperations::</xsl:text><xsl:value-of select="$class"/><xsl:text>Handler + { + OutputHandler* out; + + public: + /* Constructors and destructors */ + </xsl:text><xsl:value-of select="$class"/><xsl:text>(OutputHandler* _out); + virtual ~</xsl:text><xsl:value-of select="$class"/><xsl:text>(); + + /* Protocol methods */
</xsl:text> + <xsl:for-each select="method"> + <xsl:if test="chassis[@name='client']"> + <xsl:variable name="method" select="amqp:cpp-name(@name)"/> + <xsl:if test="doc"> + <xsl:text>
/**
----- Method: </xsl:text><xsl:value-of select="$class"/><xsl:text>.</xsl:text><xsl:value-of select="$method"/><xsl:text> -----
</xsl:text> + <xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>
*/
</xsl:text> + </xsl:if> + <xsl:for-each select="rule"> + <xsl:text>
/**
</xsl:text> + <xsl:text>Rule "</xsl:text><xsl:value-of select="@name"/><xsl:text>":
</xsl:text><xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>
*/
</xsl:text> + </xsl:for-each> + <xsl:text> virtual void </xsl:text><xsl:value-of select="$method"/> + <xsl:text>( u_int16_t channel</xsl:text><xsl:if test="field"><xsl:text>,
 </xsl:text> + <xsl:for-each select="field"> + <xsl:variable name="domain-cpp-type" select="amqp:cpp-lookup(@domain, $domain-cpp-table)"/> + <xsl:value-of select="concat($domain-cpp-type, amqp:cpp-arg-ref($domain-cpp-type), ' ', amqp:cpp-name(@name))"/> + <xsl:if test="position()!=last()"> + <xsl:text>,
 </xsl:text> + </xsl:if> + </xsl:for-each> + </xsl:if> + <xsl:text> );
</xsl:text> + </xsl:if> + </xsl:for-each> + <xsl:text> }; /* class </xsl:text><xsl:value-of select="$class"/><xsl:text> */
</xsl:text> + </xsl:for-each> + <xsl:text>}; /* class AMQP_Server */ + +} /* namespace framing */ +} /* namespace qpid */ + +#endif
</xsl:text> + </xsl:result-document> + </xsl:template> + + + <!-- + ==================== + Template: server_cpp + ==================== + Server body. + --> + <xsl:template match="amqp" mode="server_cpp"> + <xsl:param name="domain-cpp-table"/> + <xsl:result-document href="AMQP_Server.cpp" format="textFormat"> + <xsl:value-of select="amqp:copyright()"/> + <xsl:text> + +#include "AMQP_Server.h" + +namespace qpid { +namespace framing { + +AMQP_Server::AMQP_Server(OutputHandler* _out) : + out(_out) +{ +}

</xsl:text> + <xsl:for-each select="class"> + <xsl:variable name="class" select="amqp:cpp-class-name(@name)"/> + <xsl:text>
/* ++++++++++ Class: </xsl:text><xsl:value-of select="$class"/><xsl:text> ++++++++++ */ + +AMQP_Server::</xsl:text><xsl:value-of select="$class"/><xsl:text>::</xsl:text><xsl:value-of select="$class"/><xsl:text>(OutputHandler* _out) : + out(_out) +{ +} + +AMQP_Server::</xsl:text><xsl:value-of select="$class"/><xsl:text>::~</xsl:text><xsl:value-of select="$class"/><xsl:text>() {}

</xsl:text> + <xsl:for-each select="method"> + <xsl:if test="chassis[@name='client']"> + <xsl:text>void AMQP_Server::</xsl:text><xsl:value-of select="$class"/><xsl:text>::</xsl:text> + <xsl:value-of select="amqp:cpp-name(@name)"/><xsl:text>( u_int16_t channel</xsl:text><xsl:if test="field"> + <xsl:text>,
 </xsl:text> + <xsl:for-each select="field"> + <xsl:variable name="domain-cpp-type" select="amqp:cpp-lookup(@domain, $domain-cpp-table)"/> + <xsl:value-of select="concat($domain-cpp-type, amqp:cpp-arg-ref($domain-cpp-type), ' ', amqp:cpp-name(@name))"/> + <xsl:if test="position()!=last()"> + <xsl:text>,
 </xsl:text> + </xsl:if> + </xsl:for-each> + </xsl:if> + <xsl:text> ) +{ + out->send( new AMQFrame( channel, + new </xsl:text><xsl:value-of select="concat($class, amqp:field-name(@name), 'Body')"/><xsl:text>( </xsl:text> + <xsl:for-each select="field"> + <xsl:value-of select="amqp:cpp-name(@name)"/> + <xsl:if test="position()!=last()"> + <xsl:text>,
 </xsl:text> + </xsl:if> + </xsl:for-each> + <xsl:text> ) ) ); +}

</xsl:text> + </xsl:if> + </xsl:for-each> + </xsl:for-each> + <xsl:text> + +} /* namespace framing */ +} /* namespace qpid */
</xsl:text> + </xsl:result-document> + </xsl:template> + +</xsl:stylesheet> diff --git a/cpp/common/framing/generated/stylesheets/amqp_server_handler_impl.xsl b/cpp/common/framing/generated/stylesheets/amqp_server_handler_impl.xsl new file mode 100644 index 0000000000..de879a5670 --- /dev/null +++ b/cpp/common/framing/generated/stylesheets/amqp_server_handler_impl.xsl @@ -0,0 +1,187 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:amqp="http://amqp.org"> + + <xsl:import href="code_utils.xsl"/> + + <!-- + =============================== + Template: server_handler_impl_h + =============================== + Template to generate the AMQP_ServerHandlerImpl class header file. + --> + <xsl:template match="amqp" mode="server_handler_impl_h"> + <xsl:param name="domain-cpp-table"/> + <xsl:result-document href="AMQP_ServerHandlerImpl.h" format="textFormat"> + <xsl:value-of select="amqp:copyright()"/> + <xsl:text> +#ifndef _AMQP_ServerHandlerImpl_ +#define _AMQP_ServerHandlerImpl_ + +#include "AMQP_ServerOperations.h" +#include "FieldTable.h" + +namespace qpid { +namespace framing { + +class AMQP_ServerHandlerImpl : virtual public AMQP_ServerOperations +{
</xsl:text> + + <!-- List of pointers to each inner class instance --> + <xsl:for-each select="class"> + <xsl:variable name="class" select="concat(amqp:cpp-class-name(@name), 'Handler')"/> + <xsl:text> AMQP_ServerOperations::</xsl:text><xsl:value-of select="$class"/><xsl:text>* </xsl:text> + <xsl:value-of select="$class"/><xsl:text>Ptr;
</xsl:text> + </xsl:for-each> + <xsl:text> + public: + AMQP_ServerHandlerImpl(); + virtual ~AMQP_ServerHandlerImpl();

</xsl:text> + + <!-- List of functions to return pointer to each inner class instance --> + <xsl:for-each select="class"> + <xsl:variable name="class" select="concat(amqp:cpp-class-name(@name), 'Handler')"/> + <xsl:text> virtual inline AMQP_ServerOperations::</xsl:text> + <xsl:value-of select="$class"/><xsl:text>* get</xsl:text><xsl:value-of select="$class"/> + <xsl:text>() { return </xsl:text><xsl:value-of select="$class"/><xsl:text>Ptr; }
</xsl:text> + </xsl:for-each> + <xsl:text>
</xsl:text> + + <!-- Inner classes --> + <xsl:for-each select="class"> + <xsl:variable name="class" select="concat(amqp:cpp-class-name(@name), 'Handler')"/> + + <!-- Inner class documentation & rules --> + <xsl:if test="doc"> + <xsl:text>
/**
</xsl:text> + <xsl:text>===== Class: </xsl:text><xsl:value-of select="$class"/><xsl:text>Impl =====
</xsl:text> + <xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>*/
</xsl:text> + </xsl:if> + + <!-- Inner class definition --> + <xsl:text> class </xsl:text><xsl:value-of select="$class"/> + <xsl:text>Impl : virtual public AMQP_ServerOperations::</xsl:text><xsl:value-of select="$class"/> + <xsl:text>
 { + public: + /* Constructors and destructors */ + </xsl:text><xsl:value-of select="$class"/><xsl:text>Impl(); + virtual ~</xsl:text><xsl:value-of select="$class"/><xsl:text>Impl(); + + /* Protocol methods */
</xsl:text> + + <!-- Inner class methods (only if the chassis is set to "server") --> + <xsl:for-each select="method"> + <xsl:if test="chassis[@name='server']"> + <xsl:variable name="method" select="amqp:cpp-name(@name)"/> + + <!-- Inner class method documentation & rules --> + <xsl:if test="doc"> + <xsl:text>
/**
</xsl:text> + <xsl:text>----- Method: </xsl:text><xsl:value-of select="$class"/> + <xsl:text>Impl.</xsl:text><xsl:value-of select="@name"/><xsl:text> -----
</xsl:text> + <xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>*/
</xsl:text> + </xsl:if> + <xsl:for-each select="rule"> + <xsl:text>
/**
</xsl:text> + <xsl:text>Rule "</xsl:text><xsl:value-of select="@name"/><xsl:text>":
</xsl:text> + <xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>*/
</xsl:text> + </xsl:for-each> + + <!-- Inner class method definition --> + <xsl:text>
 virtual void </xsl:text><xsl:value-of select="$method"/> + <xsl:text>( u_int16_t channel</xsl:text> + + <!-- Inner class method parameter definition --> + <xsl:if test="field"> + <xsl:text>,
 </xsl:text> + <xsl:for-each select="field"> + <xsl:variable name="domain-cpp-type" select="amqp:cpp-lookup(@domain, $domain-cpp-table)"/> + <xsl:value-of select="concat($domain-cpp-type, amqp:cpp-arg-ref($domain-cpp-type), ' ', amqp:cpp-name(@name))"/> + <xsl:if test="position()!=last()"> + <xsl:text>,
 </xsl:text> + </xsl:if> + </xsl:for-each> + </xsl:if> + <xsl:text> );
</xsl:text> + </xsl:if> + </xsl:for-each> + <xsl:text>
 }; /* class </xsl:text><xsl:value-of select="$class"/><xsl:text>Impl */
</xsl:text> + </xsl:for-each> + <xsl:text>
}; /* AMQP_ServerHandlerImpl */ + +} /* namespace framing */ +} /* namespace qpid */ + +#endif
</xsl:text> + </xsl:result-document> + </xsl:template> + + <!-- + ================================= + Template: server_handler_impl_cpp + ================================= + Template to generate the AMQP_ServerHandlerImpl class stubs. + --> + <xsl:template match="amqp" mode="server_handler_impl_cpp"> + <xsl:param name="domain-cpp-table"/> + <xsl:result-document href="AMQP_ServerHandlerImpl.cpp" format="textFormat"> + <xsl:value-of select="amqp:copyright()"/> + <xsl:text> +#include "AMQP_ServerHandlerImpl.h" + +namespace qpid { +namespace framing { + +AMQP_ServerHandlerImpl::AMQP_ServerHandlerImpl() :
 </xsl:text> + <xsl:for-each select="class"> + <xsl:variable name="class" select="amqp:cpp-class-name(@name)"/> + <xsl:value-of select="$class"/> + <xsl:text>HandlerPtr( new </xsl:text><xsl:value-of select="$class"/><xsl:text>HandlerImpl() )</xsl:text> + <xsl:if test="position()!=last()"> + <xsl:text>,
 </xsl:text> + </xsl:if> + </xsl:for-each> + <xsl:text> +{ +} + +AMQP_ServerHandlerImpl::~AMQP_ServerHandlerImpl() +{
</xsl:text> + <xsl:for-each select="class"> + <xsl:text> delete </xsl:text><xsl:value-of select="amqp:cpp-class-name(@name)"/><xsl:text>HandlerPtr;
</xsl:text> + </xsl:for-each>} + + <xsl:for-each select="class"> + <xsl:variable name="class" select="amqp:cpp-class-name(@name)"/> + <xsl:text>
/* ===== Class: </xsl:text><xsl:value-of select="$class"/><xsl:text>HandlerImpl ===== */

</xsl:text> + <xsl:text>AMQP_ServerHandlerImpl::</xsl:text><xsl:value-of select="$class"/><xsl:text>HandlerImpl::</xsl:text> + <xsl:value-of select="$class"/><xsl:text>HandlerImpl()
{
}

</xsl:text> + <xsl:text>AMQP_ServerHandlerImpl::</xsl:text><xsl:value-of select="$class"/><xsl:text>HandlerImpl::~</xsl:text> + <xsl:value-of select="$class"/><xsl:text>HandlerImpl()
{
}

</xsl:text> + <xsl:for-each select="method"> + <xsl:if test="chassis[@name='server']"> + <xsl:text>void AMQP_ServerHandlerImpl::</xsl:text><xsl:value-of select="$class"/><xsl:text>HandlerImpl::</xsl:text> + <xsl:value-of select="amqp:cpp-name(@name)"/><xsl:text>( u_int16_t channel</xsl:text> + <xsl:if test="field"> + <xsl:text>,
 </xsl:text> + <xsl:for-each select="field"> + <xsl:variable name="domain-cpp-type" select="amqp:cpp-lookup(@domain, $domain-cpp-table)"/> + <xsl:value-of select="concat($domain-cpp-type, amqp:cpp-arg-ref($domain-cpp-type), ' ', amqp:cpp-name(@name))"/> + <xsl:if test="position()!=last()"> + <xsl:text>,
 </xsl:text> + </xsl:if> + </xsl:for-each> + </xsl:if><xsl:text> )
{
}

</xsl:text> + </xsl:if> + </xsl:for-each> + </xsl:for-each> + <xsl:text> + +} /* namespace framing */ +} /* namespace qpid */

</xsl:text> + </xsl:result-document> + </xsl:template> + +</xsl:stylesheet> diff --git a/cpp/common/framing/generated/stylesheets/amqp_server_operations.xsl b/cpp/common/framing/generated/stylesheets/amqp_server_operations.xsl new file mode 100644 index 0000000000..b42242e8fe --- /dev/null +++ b/cpp/common/framing/generated/stylesheets/amqp_server_operations.xsl @@ -0,0 +1,113 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:amqp="http://amqp.org"> + + <xsl:import href="code_utils.xsl"/> + + <!-- + ============================= + Template: server-operations-h + ============================= + Template to generate the AMQP_ServerHandler virtual class. This is the pure + virtual class from which the AMQP_Client and AMQP_ServerHandlerImpl classes + are derived. + --> + <xsl:template match="amqp" mode="server-operations-h"> + <xsl:param name="domain-cpp-table"/> + <xsl:result-document href="AMQP_ServerOperations.h" format="textFormat"> + <xsl:value-of select="amqp:copyright()"/> + <xsl:text> +#ifndef _AMQP_ServerOperations_ +#define _AMQP_ServerOperations_ + +#include "AMQP_Constants.h" +#include "FieldTable.h" + +namespace qpid { +namespace framing { + +class AMQP_ServerOperations +{ + public: + AMQP_ServerOperations() {} + virtual ~AMQP_ServerOperations() {} + inline u_int16_t getAmqpMajor() { return (u_int16_t)</xsl:text><xsl:value-of select="@major"/><xsl:text>; } + inline u_int16_t getAmqpMinor() { return (u_int16_t)</xsl:text><xsl:value-of select="@minor"/><xsl:text>; }

</xsl:text> + + <!-- Inner classes --> + <xsl:for-each select="class"> + <xsl:variable name="class" select="concat(amqp:cpp-class-name(@name), 'Handler')"/> + + <!-- Inner class documentation & rules --> + <xsl:if test="doc"> + <xsl:text>
/**
===== Class: </xsl:text><xsl:value-of select="$class"/><xsl:text> =====
</xsl:text> + <xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>*/
</xsl:text> + </xsl:if> + + <!-- Inner class definition --> + <xsl:text> class </xsl:text><xsl:value-of select="$class"/><xsl:text> + { + public: + /* Constructors and destructors */ + </xsl:text><xsl:value-of select="$class"/><xsl:text>() {} + virtual ~</xsl:text><xsl:value-of select="$class"/><xsl:text>() {} + + /* Protocol methods */
</xsl:text> + + <!-- Inner class methods (only if the chassis is set to "server") --> + <xsl:for-each select="method"> + <xsl:if test="chassis[@name='server']"> + <xsl:variable name="method" select="amqp:cpp-name(@name)"/> + + <!-- Inner class method documentation & rules --> + <xsl:if test="doc"> + <xsl:text>
/**
----- Method: </xsl:text><xsl:value-of select="$class"/><xsl:text>.</xsl:text> + <xsl:value-of select="@name"/><xsl:text> -----
</xsl:text> + <xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>*/
</xsl:text> + </xsl:if> + <xsl:for-each select="rule">/** + <xsl:text>
/**
</xsl:text> + <xsl:text>Rule "</xsl:text><xsl:value-of select="@name"/><xsl:text>":
</xsl:text> + <xsl:value-of select="amqp:process-docs(doc)"/> + <xsl:text>*/
</xsl:text> + </xsl:for-each> + + <!-- Inner class method definition --> + <xsl:text> virtual void </xsl:text><xsl:value-of select="$method"/> + <xsl:text>( u_int16_t channel</xsl:text> + + <!-- Inner class method parameter definition --> + <xsl:if test="field"> + <xsl:text>,
 </xsl:text> + <xsl:for-each select="field"> + <xsl:variable name="domain-cpp-type" select="amqp:cpp-lookup(@domain, $domain-cpp-table)"/> + <xsl:value-of select="concat($domain-cpp-type, amqp:cpp-arg-ref($domain-cpp-type), ' ', amqp:cpp-name(@name))"/> + <xsl:if test="position()!=last()"> + <xsl:text>,
 </xsl:text> + </xsl:if> + </xsl:for-each> + </xsl:if> + <xsl:text> ) = 0;
</xsl:text> + </xsl:if> + </xsl:for-each> + <xsl:text> }; /* class </xsl:text><xsl:value-of select="$class"/><xsl:text> */
</xsl:text> + </xsl:for-each> + + <xsl:for-each select="class"> + <xsl:variable name="class" select="concat(amqp:cpp-class-name(@name), 'Handler')"/> + <xsl:text> virtual AMQP_ServerOperations::</xsl:text> + <xsl:value-of select="$class"/><xsl:text>* get</xsl:text><xsl:value-of select="$class"/> + <xsl:text>() = 0;</xsl:text> + </xsl:for-each> + + <xsl:text>}; /* class AMQP_ServerOperations */ + +} /* namespace framing */ +} /* namespace qpid */ + +#endif
</xsl:text> + </xsl:result-document> + </xsl:template> + +</xsl:stylesheet> diff --git a/cpp/common/framing/generated/stylesheets/code_gen.xsl b/cpp/common/framing/generated/stylesheets/code_gen.xsl new file mode 100644 index 0000000000..5e9f4ef8f0 --- /dev/null +++ b/cpp/common/framing/generated/stylesheets/code_gen.xsl @@ -0,0 +1,91 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:amqp="http://amqp.org"> + + <xsl:import href="convert_0.81.xsl"/> + <xsl:import href="amqp_consts.xsl"/> + <xsl:import href="amqp_server_operations.xsl"/> + <xsl:import href="amqp_client_operations.xsl"/> + <xsl:import href="amqp_server.xsl"/> + <xsl:import href="amqp_client.xsl"/> + <xsl:import href="amqp_server_handler_impl.xsl"/> + <xsl:import href="amqp_client_handler_impl.xsl"/> + + <xsl:output method="text" indent="yes" name="textFormat"/> + <xsl:key name="domain-lookup" match="domains/domain" use="@domain-name"/> + + <xsl:template match="/"> + + <!-- 0. Convert to 0.81 format --> + <!-- + NOTE: The XML specification change from 0.8 to 0.81 is primarily a change to + the XML itself, not the protocol it represents. However, at the time of this + commit, the 0.81 specification has not been approved by the AMQP working group, + so this converter from the 0.8 format to the 0.81 format has been included as + a temporary measure. When the 0.81 format becomes official, then this conversion + should be removed, and all of the templates below will revert to select=".". + + TODO: Remove this conversion when the new 0.81 spec is checked in. + --> + <xsl:variable name="format-v081"> + <xsl:apply-templates mode="do-amqp" select="amqp"/> + </xsl:variable> + <!-- == Uncomment this to view output for debugging == + <xsl:result-document href="convert_081.out"> + <xsl:copy-of select="$format-v081"/> + </xsl:result-document> + --> + + <!-- 1. Domain to C++ type lookup table --> + <xsl:variable name="domain-cpp-table"> + <xsl:apply-templates mode="domain-table" select="$format-v081"/> + </xsl:variable> + <!-- == Uncomment this to view output for debugging == + <xsl:result-document href="domain_cpp_table.out"> + <xsl:copy-of select="$domain-cpp-table"/> + </xsl:result-document> + --> + + <!-- 2. Constant declarations (AMQP_Constants.h) --> + <xsl:apply-templates mode="domain-consts" select="$format-v081"/> + + <!-- 3. Client and server handler pure virtual classes --> + <xsl:apply-templates mode="server-operations-h" select="$format-v081"> + <xsl:with-param name="domain-cpp-table" select="$domain-cpp-table"/> + </xsl:apply-templates> + <xsl:apply-templates mode="client-operations-h" select="$format-v081"> + <xsl:with-param name="domain-cpp-table" select="$domain-cpp-table"/> + </xsl:apply-templates> + + <!-- 4. Client and server output classes --> + <xsl:apply-templates mode="server_h" select="$format-v081"> + <xsl:with-param name="domain-cpp-table" select="$domain-cpp-table"/> + </xsl:apply-templates> + <xsl:apply-templates mode="client_h" select="$format-v081"> + <xsl:with-param name="domain-cpp-table" select="$domain-cpp-table"/> + </xsl:apply-templates> + <xsl:apply-templates mode="server_cpp" select="$format-v081"> + <xsl:with-param name="domain-cpp-table" select="$domain-cpp-table"/> + </xsl:apply-templates> + <xsl:apply-templates mode="client_cpp" select="$format-v081"> + <xsl:with-param name="domain-cpp-table" select="$domain-cpp-table"/> + </xsl:apply-templates> + + <!-- 5. Client and server handler stub classes --> + <xsl:apply-templates mode="server_handler_impl_h" select="$format-v081"> + <xsl:with-param name="domain-cpp-table" select="$domain-cpp-table"/> + </xsl:apply-templates> + <xsl:apply-templates mode="client_handler_impl_h" select="$format-v081"> + <xsl:with-param name="domain-cpp-table" select="$domain-cpp-table"/> + </xsl:apply-templates> + <!-- TODO: Find a way to only run the .cpp stub generator when required, as + running this will overwrite any stub code in existance! --> + <xsl:apply-templates mode="server_handler_impl_cpp" select="$format-v081"> + <xsl:with-param name="domain-cpp-table" select="$domain-cpp-table"/> + </xsl:apply-templates> + <xsl:apply-templates mode="client_handler_impl_cpp" select="$format-v081"> + <xsl:with-param name="domain-cpp-table" select="$domain-cpp-table"/> + </xsl:apply-templates> + + </xsl:template> + +</xsl:stylesheet> diff --git a/cpp/common/framing/generated/stylesheets/code_utils.xsl b/cpp/common/framing/generated/stylesheets/code_utils.xsl new file mode 100644 index 0000000000..f4a0f6e5ce --- /dev/null +++ b/cpp/common/framing/generated/stylesheets/code_utils.xsl @@ -0,0 +1,210 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:amqp="http://amqp.org"> + + <!-- + ======================== + Function: amqp:copyright + ======================== + Print out a standard Apache copyright notice and generated code warning. + --> + <xsl:function name="amqp:copyright">/** +* +* 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. +* +*/ + +/** +* +* NOTE: This file is generated directly from the AMQP XML specification. +* === DO NOT EDIT === +* +*/
</xsl:function> + + <!-- + ========================== + Function: amqp:upper-first + ========================== + Convert the first character of the parameter to upper-case + --> + <xsl:function name="amqp:upper-first"> + <xsl:param name="in"/> + <xsl:value-of select="concat(upper-case(substring($in, 1, 1)), substring($in, 2))"/> + </xsl:function> + + <!-- + ======================== + Function: amqp:cpp-name-1 + ======================== + Convert parameter "name" to a valid C++ identifier, finding spaces and '-'s + in the parameter name and replacing them with '_' chars. Also check for C++ + reserved words and prefix them with '_'. No capitalization is performed. + --> + <xsl:function name="amqp:cpp-name-1"> + <xsl:param name="name"/> + <xsl:choose> + <!-- C++ reserved words. --> + <xsl:when test="$name='delete'">delete_</xsl:when> + <xsl:when test="$name='return'">return_</xsl:when> + <!-- Change unsuitable C++ identifier characters. --> + <xsl:otherwise><xsl:value-of select="translate($name, ' -', '__')"/></xsl:otherwise> + </xsl:choose> + </xsl:function> + + <!-- + ======================= + Function: amqp:cpp-name + ======================= + Convert parameter "name" to a valid, camel cased C++ name. + --> + <xsl:function name="amqp:cpp-name"> + <xsl:param name="name"/> + <xsl:value-of select="amqp:cpp-name-1(amqp:camel-case($name))"/> + </xsl:function> + + <!-- + ============================= + Function: amqp:cpp-class-name + ============================= + Convert parameter "name" to a valid C++ identifier, finding spaces and '-'s + in the parameter name and replacing them with '_' chars. Also check for C++ + reserved words and prefix them with '_'. First letter only is capitalized. + --> + <xsl:function name="amqp:cpp-class-name"> + <xsl:param name="name"/> + <xsl:value-of select="amqp:upper-first(amqp:cpp-name($name))"/> + </xsl:function> + + <!-- + ========================= + Function: amqp:camel-case + ========================= + *** NOTE: Only works with *one* of either '-' or ' '. If a name contains 2 or + *** more of these characters, then this will break. + Convert parameter "name" to camel case, where words are separated by ' ' or '-' + --> + <xsl:function name="amqp:camel-case"> + <xsl:param name="name"/> + <xsl:choose> + <xsl:when test="contains($name, ' ')"> + <xsl:value-of select="concat(substring-before($name, ' '), amqp:upper-first(substring-after($name, ' ')))"/> + </xsl:when> + <xsl:when test="contains($name, '-')"> + <xsl:value-of select="concat(substring-before($name, '-'), amqp:upper-first(substring-after($name, '-')))"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="$name"/> + </xsl:otherwise> + </xsl:choose> + </xsl:function> + + <!-- + ========================= + Function: amqp:field-name + ========================= + Get a valid field name, processing spaces and '-'s where appropriate + --> + <xsl:function name="amqp:field-name"> + <xsl:param name="name"/> + <xsl:value-of select="amqp:upper-first(amqp:camel-case($name))"/> + </xsl:function> + + <!-- + ======================= + Function: amqp:cpp-type + ======================= + Map the set of simple AMQP types to C++ types. Also map the AMQP table + domain to appropriate C++ class. + --> + <xsl:function name="amqp:cpp-type"> + <xsl:param name="type"/> + <xsl:choose> + <!-- Simple AMQP domain types --> + <xsl:when test="$type='octet'">u_int8_t</xsl:when> + <xsl:when test="$type='short'">u_int16_t</xsl:when> + <xsl:when test="$type='shortstr'">string</xsl:when> + <xsl:when test="$type='longstr'">string</xsl:when> + <xsl:when test="$type='bit'">bool</xsl:when> + <xsl:when test="$type='long'">u_int32_t</xsl:when> + <xsl:when test="$type='longlong'">u_int64_t</xsl:when> + <xsl:when test="$type='timestamp'">u_int64_t</xsl:when> + <!-- AMQP structures --> + <xsl:when test="$type='table'">FieldTable</xsl:when> + <!-- Fallback: unknown type --> + <xsl:otherwise>unknown_type /* WARNING: undefined type */</xsl:otherwise> + </xsl:choose> + </xsl:function> + + <!-- + ========================== + Function: amqp:cpp-arg-ref + ========================== + Determines whether a C++ reference is required for an argument. + --> + <xsl:function name="amqp:cpp-arg-ref"> + <xsl:param name="type"/> + <xsl:choose> + <xsl:when test="$type='string'">&</xsl:when> + <xsl:when test="$type='FieldTable'">&</xsl:when> + </xsl:choose> + </xsl:function> + + <!-- + ========================= + Function: amqp:cpp-lookup + ========================= + Template and function for looking up the cpp type from the domain name. + The template runs on a lookup table XML generated by the "domain_table" + template in amqp_domaintypes.xsl. + --> + <xsl:template match="/" mode="cpp-lookup"> + <xsl:param name="domain-name"/> + <xsl:for-each select="key('domain-lookup', $domain-name)"> + <xsl:value-of select="@cpp-type"/> + </xsl:for-each> + </xsl:template> + + <xsl:function name="amqp:cpp-lookup"> + <xsl:param name="domain-name"/> + <xsl:param name="domain-cpp-table"/> + <xsl:apply-templates mode="cpp-lookup" select="$domain-cpp-table"> + <xsl:with-param name="domain-name" select="$domain-name"/> + </xsl:apply-templates> + </xsl:function> + + <!-- + ========================= + Function: amqp:cpp-lookup + ========================= + Template and function for processing the possibly multiple <doc> elements + within a node. + --> + <xsl:template match="doc" mode="process-doc-elts"> + <xsl:for-each select="."> + <xsl:choose> + <xsl:when test=".[@type='grammar']"><xsl:value-of select="."/></xsl:when> + <xsl:when test=".[@type='scenario']"><xsl:value-of select="concat('
Test Scenario: ', normalize-space(.))"/></xsl:when> + <xsl:otherwise><xsl:value-of select="normalize-space(.)"/></xsl:otherwise> + </xsl:choose> + </xsl:for-each> + </xsl:template> + + <xsl:function name="amqp:process-docs"> + <xsl:param name="doc-elts"/> + <xsl:apply-templates mode="process-doc-elts" select="$doc-elts"/> + </xsl:function> + + +</xsl:stylesheet> + diff --git a/cpp/common/framing/generated/stylesheets/convert_0.81.xsl b/cpp/common/framing/generated/stylesheets/convert_0.81.xsl new file mode 100644 index 0000000000..9924f165da --- /dev/null +++ b/cpp/common/framing/generated/stylesheets/convert_0.81.xsl @@ -0,0 +1,407 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:amqp="http://amqp.org"> + +<xsl:template match="/"> + <xsl:apply-templates select="/" mode="do-amqp"/> +</xsl:template> + +<!-- ====== + <amqp> + ====== --> +<xsl:template match="amqp" mode="do-amqp"> + +<!-- <xsl:text>
</xsl:text> --> +<xsl:element name= "amqp"> +<xsl:attribute name="major"><xsl:value-of select="@major"/></xsl:attribute> +<xsl:attribute name="minor"><xsl:value-of select="@minor"/></xsl:attribute> +<xsl:attribute name="port"><xsl:value-of select="@port"/></xsl:attribute> +<xsl:attribute name="comment"><xsl:value-of select="@comment"/></xsl:attribute> +<xsl:text>
</xsl:text> + +<!-- constant elements --> +<xsl:text>
</xsl:text> +<xsl:text> </xsl:text><xsl:comment> + ==================== + Constants + ==================== + </xsl:comment><xsl:text>
</xsl:text> +<xsl:text>
</xsl:text> +<xsl:apply-templates select="constant" mode="do-constant"> +<xsl:with-param name="indent" select="' '"/> +</xsl:apply-templates> + +<!-- domain elements --> +<xsl:text>
</xsl:text> +<xsl:text> </xsl:text><xsl:comment> + ==================== + Domains + ==================== + </xsl:comment><xsl:text>
</xsl:text> +<xsl:text>
</xsl:text> +<xsl:apply-templates select="domain" mode="do-domain"> +<xsl:with-param name="indent" select="' '"/> +</xsl:apply-templates> + +<!-- required elementary domain definition elements added into v0.81 --> +<xsl:text>
</xsl:text> +<xsl:text> </xsl:text><xsl:comment> Elementary domains </xsl:comment><xsl:text>
</xsl:text> +<xsl:text> </xsl:text><xsl:element name="domain"> + <xsl:attribute name="name">bit</xsl:attribute> + <xsl:attribute name="type">bit</xsl:attribute> + <xsl:attribute name="label">single bit</xsl:attribute> +</xsl:element><xsl:text>
</xsl:text> +<xsl:text> </xsl:text><xsl:element name="domain"> + <xsl:attribute name="name">octet</xsl:attribute> + <xsl:attribute name="type">octet</xsl:attribute> + <xsl:attribute name="label">single octet</xsl:attribute> +</xsl:element><xsl:text>
</xsl:text> +<xsl:text> </xsl:text><xsl:element name="domain"> + <xsl:attribute name="name">short</xsl:attribute> + <xsl:attribute name="type">short</xsl:attribute> + <xsl:attribute name="label">16-bit integer</xsl:attribute> +</xsl:element><xsl:text>
</xsl:text> +<xsl:text> </xsl:text><xsl:element name="domain"> + <xsl:attribute name="name">long</xsl:attribute> + <xsl:attribute name="type">long</xsl:attribute> + <xsl:attribute name="label">32-bit integer</xsl:attribute> +</xsl:element><xsl:text>
</xsl:text> +<xsl:text> </xsl:text><xsl:element name="domain"> + <xsl:attribute name="name">longlong</xsl:attribute> + <xsl:attribute name="type">longlong</xsl:attribute> + <xsl:attribute name="label">64-bit integer</xsl:attribute> +</xsl:element><xsl:text>
</xsl:text> +<xsl:text> </xsl:text><xsl:element name="domain"> + <xsl:attribute name="name">shortstr</xsl:attribute> + <xsl:attribute name="type">shortstr</xsl:attribute> + <xsl:attribute name="label">short string</xsl:attribute> +</xsl:element><xsl:text>
</xsl:text> +<xsl:text> </xsl:text><xsl:element name="domain"> + <xsl:attribute name="name">longstr</xsl:attribute> + <xsl:attribute name="type">longstr</xsl:attribute> + <xsl:attribute name="label">long string</xsl:attribute> +</xsl:element><xsl:text>
</xsl:text> +<xsl:text> </xsl:text><xsl:element name="domain"> + <xsl:attribute name="name">timestamp</xsl:attribute> + <xsl:attribute name="type">timestamp</xsl:attribute> + <xsl:attribute name="label">64-bit timestamp</xsl:attribute> +</xsl:element><xsl:text>
</xsl:text> +<xsl:text> </xsl:text><xsl:element name="domain"> + <xsl:attribute name="name">table</xsl:attribute> + <xsl:attribute name="type">table</xsl:attribute> + <xsl:attribute name="label">field table</xsl:attribute> +</xsl:element><xsl:text>
</xsl:text> + +<!-- class elements --> +<xsl:text>
</xsl:text> +<xsl:text> </xsl:text><xsl:comment> + ==================== + Classes + ==================== + </xsl:comment><xsl:text>
</xsl:text> +<xsl:apply-templates select="class" mode="do-class"> +<xsl:with-param name="indent" select="' '"/> +</xsl:apply-templates> + +</xsl:element><!-- amqp --> +<!-- <xsl:text>
</xsl:text> --> +</xsl:template> + +<!-- ========== + <constant> + ========== --> +<xsl:template match="constant" mode="do-constant"> +<xsl:param name="indent"/> +<xsl:variable name="constant" select="translate(@name, ' ', '-')"/> + +<xsl:value-of select="$indent"/><xsl:element name="constant"> +<xsl:attribute name="name"><xsl:value-of select="$constant"/></xsl:attribute> +<xsl:attribute name="value"><xsl:value-of select="@value"/></xsl:attribute> +<xsl:if test="@class"> +<xsl:attribute name="class"><xsl:value-of select="translate(@class, ' ', '-')"/></xsl:attribute> +</xsl:if> + +<!-- If there is content, place in child <doc> element --> +<xsl:if test="string-length(.) > 0"> +<xsl:text>
</xsl:text> +<xsl:value-of select="$indent"/><xsl:text> </xsl:text><xsl:element name="doc"><xsl:text>
</xsl:text> +<xsl:value-of select="$indent"/><xsl:text> </xsl:text><xsl:value-of select="normalize-space(.)"/><xsl:text>
</xsl:text> +<xsl:value-of select="$indent"/><xsl:text> </xsl:text></xsl:element><xsl:text>
</xsl:text> +<xsl:value-of select="$indent"/> +</xsl:if> + +</xsl:element><xsl:text>
</xsl:text> + +</xsl:template> + +<!-- ======== + <domain> + ======== --> +<xsl:template match="domain" mode="do-domain"> +<xsl:param name="indent"/> +<xsl:variable name="domain" select="translate(@name, ' ', '-')"/> + +<xsl:value-of select="$indent"/><xsl:element name="domain"> +<xsl:attribute name="name"><xsl:value-of select="$domain"/></xsl:attribute> +<xsl:attribute name="type"><xsl:value-of select="@type"/></xsl:attribute> +<xsl:if test="doc|assert|rule"><xsl:text>
</xsl:text></xsl:if> + +<!-- doc elements --> +<xsl:apply-templates select="doc" mode="do-doc"> +<xsl:with-param name="indent" select="concat($indent, ' ')"/> +</xsl:apply-templates> + +<!-- assert elements --> +<xsl:apply-templates select="assert" mode="do-assert"> +<xsl:with-param name="indent" select="concat($indent, ' ')"/> +</xsl:apply-templates> + +<!-- rule elements --> +<xsl:apply-templates select="rule" mode="do-rule"> +<xsl:with-param name="indent" select="concat($indent, ' ')"/> +<xsl:with-param name="label" select="$domain"/> +</xsl:apply-templates> + +<xsl:if test="doc|assert|rule"><xsl:value-of select="$indent"/></xsl:if></xsl:element><xsl:text>
</xsl:text> + +</xsl:template> + +<!-- ======== + <class> + ======== --> + +<xsl:template match="class" mode="do-class"> +<xsl:param name="indent"/> +<xsl:variable name="class" select="translate(@name, ' ', '-')"/> + +<!-- Ignore class test - removed from 0.81 --> +<xsl:if test="not($class = 'test')"> +<xsl:text>
</xsl:text><xsl:value-of select="$indent"/><xsl:comment><xsl:value-of select="concat(' == Class: ', $class, ' == ')"/></xsl:comment><xsl:text>
</xsl:text> +<xsl:value-of select="$indent"/><xsl:element name="class"> +<xsl:attribute name="name"><xsl:value-of select="$class"/></xsl:attribute> +<xsl:attribute name="handler"><xsl:value-of select="@handler"/></xsl:attribute> +<xsl:attribute name="index"><xsl:value-of select="@index"/></xsl:attribute> +<xsl:if test="doc|chassis|rule|field|method"><xsl:text>
</xsl:text></xsl:if> + +<!-- doc elements --> +<xsl:apply-templates select="doc" mode="do-doc"> +<xsl:with-param name="indent" select="concat($indent, ' ')"/> +<xsl:with-param name="label" select="$class"/> +</xsl:apply-templates> + +<!-- chassis elements --> +<xsl:apply-templates select="chassis" mode="do-chassis"> +<xsl:with-param name="indent" select="concat($indent, ' ')"/> +</xsl:apply-templates> + +<!-- rule elements --> +<xsl:apply-templates select="rule" mode="do-rule"> +<xsl:with-param name="indent" select="concat($indent, ' ')"/> +<xsl:with-param name="label" select="$class"/> +</xsl:apply-templates> + +<!-- field elements --> +<xsl:apply-templates select="field" mode="do-field"> +<xsl:with-param name="indent" select="concat($indent, ' ')"/> +<xsl:with-param name="label" select="$class"/> +</xsl:apply-templates> + +<!-- method elements --> +<xsl:apply-templates select="method" mode="do-method"> +<xsl:with-param name="indent" select="concat($indent, ' ')"/> +<xsl:with-param name="label" select="$class"/> +</xsl:apply-templates> + +<xsl:if test="doc|chassis|rule|field|method"><xsl:value-of select="$indent"/></xsl:if></xsl:element><xsl:text>
</xsl:text> +</xsl:if> +</xsl:template> + +<!-- ======== + <method> + ======== --> + +<xsl:template match="method" mode="do-method"> +<xsl:param name="indent"/> +<xsl:param name="label"/> +<xsl:variable name="method" select="translate(@name, ' ', '-')"/> + +<xsl:text>
</xsl:text><xsl:value-of select="$indent"/><xsl:comment><xsl:value-of select="concat(' == Method: ', $label, '.', $method, ' == ')"/></xsl:comment><xsl:text>
</xsl:text> +<xsl:value-of select="$indent"/><xsl:element name="method"> +<xsl:attribute name="name"><xsl:value-of select="$method"/></xsl:attribute> +<xsl:if test="@synchronous"><xsl:attribute name="synchronous"><xsl:value-of select="@synchronous"/></xsl:attribute></xsl:if> +<xsl:attribute name="index"><xsl:value-of select="@index"/></xsl:attribute> +<xsl:if test="doc|chassis|response|rule|field"><xsl:text>
</xsl:text></xsl:if> + +<!-- doc elements --> +<xsl:apply-templates select="doc" mode="do-doc"> +<xsl:with-param name="indent" select="concat($indent, ' ')"/> +<xsl:with-param name="label" select="concat($label, '.', $method)"/> +</xsl:apply-templates> + +<!-- chassis and response elements --> +<xsl:apply-templates select="chassis" mode="do-chassis"> +<xsl:with-param name="indent" select="concat($indent, ' ')"/> +</xsl:apply-templates> +<xsl:apply-templates select="response" mode="do-response"> +<xsl:with-param name="indent" select="concat($indent, ' ')"/> +</xsl:apply-templates> + +<!-- rule elements --> +<xsl:apply-templates select="rule" mode="do-rule"> +<xsl:with-param name="indent" select="concat($indent, ' ')"/> +<xsl:with-param name="label" select="concat($label, '.', $method)"/> +</xsl:apply-templates> + +<!-- field elements --> +<xsl:apply-templates select="field" mode="do-field"> +<xsl:with-param name="indent" select="concat($indent, ' ')"/> +<xsl:with-param name="label" select="concat($label, '.', $method)"/> +</xsl:apply-templates> + +<xsl:if test="doc|chassis|response|rule|field"><xsl:value-of select="$indent"/></xsl:if></xsl:element><xsl:text>
</xsl:text> + +</xsl:template> + +<!-- ======== + <field> + ======== --> + +<xsl:template match="field" mode="do-field"> +<xsl:param name="indent"/> +<xsl:param name="label"/> +<xsl:variable name="field" select="translate(@name, ' ', '-')"/> + +<xsl:value-of select="$indent"/><xsl:element name="field"> +<xsl:attribute name="name"><xsl:value-of select="$field"/></xsl:attribute> +<xsl:if test="@type"> +<xsl:attribute name="domain"><xsl:value-of select="translate(@type, ' ', '-')"/></xsl:attribute> +</xsl:if> +<xsl:if test="@domain"> +<xsl:attribute name="domain"><xsl:value-of select="translate(@domain, ' ', '-')"/></xsl:attribute> +</xsl:if> +<xsl:if test="doc|rule|assert"><xsl:text>
</xsl:text></xsl:if> + +<!-- doc elements --> +<xsl:apply-templates select="doc" mode="do-doc"> +<xsl:with-param name="indent" select="concat($indent, ' ')"/> +<xsl:with-param name="label" select="concat($label, '.', $field)"/> +</xsl:apply-templates> + +<!-- rule elements --> +<xsl:apply-templates select="rule" mode="do-rule"> +<xsl:with-param name="indent" select="concat($indent, ' ')"/> +<xsl:with-param name="label" select="concat($label, '.', $field)"/> +</xsl:apply-templates> + +<!-- assert elements --> +<xsl:apply-templates select="assert" mode="do-assert"> +<xsl:with-param name="indent" select="concat($indent, ' ')"/> +</xsl:apply-templates> + +<xsl:if test="doc|rule|assert"><xsl:value-of select="$indent"/></xsl:if></xsl:element><xsl:text>
</xsl:text> + +</xsl:template> + +<!-- ======== + <assert> + ======== --> +<xsl:template match="assert" mode="do-assert"> +<xsl:param name="indent"/> + +<xsl:value-of select="$indent"/><xsl:element name="assert"> +<xsl:attribute name="check"><xsl:value-of select="@check"/></xsl:attribute> +<xsl:if test="@value"><xsl:attribute name="value"><xsl:value-of select="@value"/></xsl:attribute></xsl:if> +<xsl:if test="@rule"><xsl:attribute name="rule"><xsl:value-of select="@rule"/></xsl:attribute></xsl:if> + +<xsl:apply-templates select="doc" mode="do-doc"> +<xsl:with-param name="indent" select="concat($indent, ' ')"/> +</xsl:apply-templates> + +</xsl:element><xsl:text>
</xsl:text> + +</xsl:template> + +<!-- ======== + <rule> + ======== --> +<xsl:template match="rule" mode="do-rule"> +<xsl:param name="indent"/> +<xsl:param name="label"/> + +<xsl:value-of select="$indent"/><xsl:element name="rule"> +<xsl:attribute name="name">rule_<xsl:value-of select="$label"/>_<xsl:number format="01"/></xsl:attribute> + +<!-- If there is content, place in child <doc> element --> + +<xsl:if test="string-length(.) > 0"> +<xsl:text>
</xsl:text> +<xsl:value-of select="$indent"/><xsl:text> </xsl:text><xsl:element name="doc"><xsl:text>
</xsl:text> +<xsl:value-of select="$indent"/><xsl:text> </xsl:text><xsl:value-of select="normalize-space(.)"/><xsl:text>
</xsl:text> +<xsl:value-of select="$indent"/><xsl:text> </xsl:text></xsl:element><xsl:text>
</xsl:text> +<xsl:value-of select="$indent"/> +</xsl:if> + +</xsl:element><xsl:text>
</xsl:text> + +</xsl:template> + +<!-- ========= + <chassis> + ========= --> +<xsl:template match="chassis" mode="do-chassis"> +<xsl:param name="indent"/> + +<xsl:value-of select="$indent"/><xsl:element name="chassis"> +<xsl:attribute name="name"><xsl:value-of select="@name"/></xsl:attribute> +<xsl:attribute name="implement"><xsl:value-of select="@implement"/></xsl:attribute> +</xsl:element><xsl:text>
</xsl:text> + +</xsl:template> + +<!-- ========== + <response> + ========== --> +<xsl:template match="response" mode="do-response"> +<xsl:param name="indent"/> + +<xsl:value-of select="$indent"/><xsl:element name="response"> +<xsl:attribute name="name"><xsl:value-of select="@name"/></xsl:attribute> +</xsl:element><xsl:text>
</xsl:text> + +</xsl:template> + +<!-- ===== + <doc> + ===== --> +<xsl:template match="doc" mode="do-doc"> +<xsl:param name="indent"/> +<xsl:param name="label"/> + +<!-- Handle cases of <doc name="rule>...</doc>: turn them into <rule><doc>...</doc></rule> --> +<xsl:if test="@name = 'rule'"> +<xsl:value-of select="$indent"/><xsl:element name="rule"> +<xsl:if test="@test"><xsl:attribute name="name"><xsl:value-of select="@test"/></xsl:attribute></xsl:if> +<xsl:if test="not(@test)"><xsl:attribute name="name">doc_rule_<xsl:value-of select="$label"/>_<xsl:number format="01"/></xsl:attribute></xsl:if> +<xsl:text>
</xsl:text> +<xsl:value-of select="concat($indent, ' ')"/><xsl:element name="doc"><xsl:text>
</xsl:text> +<xsl:value-of select="concat($indent, ' ')"/><xsl:text> </xsl:text><xsl:value-of select="normalize-space(.)"/><xsl:text>
</xsl:text> +<xsl:value-of select="concat($indent, ' ')"/></xsl:element><xsl:text>
</xsl:text> +<xsl:value-of select="$indent"/></xsl:element><xsl:text>
</xsl:text> +</xsl:if> + +<!-- Normal <doc>...</doc> elements --> +<xsl:if test="not(@name = 'rule')"> +<xsl:value-of select="$indent"/><xsl:element name="doc"> +<xsl:if test="@name = 'grammar'"> +<xsl:attribute name="type">grammar</xsl:attribute> +<xsl:value-of select="$indent"/><xsl:text> </xsl:text><xsl:value-of select="."/> +</xsl:if> +<xsl:if test="not(@name = 'grammar')"> +<xsl:text>
</xsl:text> +<xsl:value-of select="$indent"/><xsl:text> </xsl:text><xsl:value-of select="normalize-space(.)"/><xsl:text>
</xsl:text> +</xsl:if> +<xsl:value-of select="$indent"/></xsl:element><xsl:text>
</xsl:text> +</xsl:if> + +</xsl:template> + +</xsl:stylesheet> diff --git a/cpp/common/framing/generated/stylesheets/cpp.xsl b/cpp/common/framing/generated/stylesheets/cpp.xsl new file mode 100644 index 0000000000..ae66f65745 --- /dev/null +++ b/cpp/common/framing/generated/stylesheets/cpp.xsl @@ -0,0 +1,318 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:amqp="http://amqp.org"> + +<!-- this class contains the templates for generating C++ source code for a given framing model --> + +<xsl:import href="utils.xsl"/> +<xsl:output method="text" indent="yes" name="textFormat"/> + +<xsl:template match="/"> + <xsl:apply-templates mode="generate-multi" select="frames"/> + <xsl:apply-templates mode="method-list-header" select="frames"/> + <xsl:apply-templates mode="method-list-source" select="frames"/> + <xsl:apply-templates mode="method-interface" select="frames"/> +</xsl:template> + +<!-- processes all frames outputting the classes in a single stream --> +<xsl:template match="frames" mode="generate-single"> + <xsl:result-document href="amqp_methods.h" format="textFormat"> +#include "amqp_framing.h" + <xsl:for-each select="frame"> + <xsl:call-template name="generate-class"> + <xsl:with-param name="f" select="."/> + </xsl:call-template> + </xsl:for-each> + </xsl:result-document> +</xsl:template> + +<!-- generates seperate file for each class/frame --> +<xsl:template match="frame" mode="generate-multi"> + <xsl:variable name="uri" select="concat(@name, '.h')"/> + <xsl:result-document href="{$uri}" format="textFormat"> +#include "amqp_types.h" +#include "AMQP_ServerOperations.h" +#include "AMQMethodBody.h" +#include "Buffer.h" +#include "FieldTable.h" + +#ifndef _<xsl:value-of select="@name"/>_ +#define _<xsl:value-of select="@name"/>_ + +namespace qpid { +namespace framing { + + <xsl:call-template name="generate-class"> + <xsl:with-param name="f" select="."/> + </xsl:call-template> +} +} + +#endif + +</xsl:result-document> +</xsl:template> + + +<!-- main class generation template --> +<xsl:template name="generate-class"> + <xsl:param name="f"/> +/** + * This class is autogenerated, do not modify. [From <xsl:value-of select="$f/parent::frames/@protocol"/>] + */ +class <xsl:value-of select="$f/@name"/> : virtual public AMQMethodBody +{ + <xsl:for-each select="$f/field"> + <xsl:value-of select="@cpp-type"/> + <xsl:text> </xsl:text> + <xsl:value-of select="@name"/>; + </xsl:for-each> + +public: + typedef std::tr1::shared_ptr<<xsl:value-of select="$f/@name"/>> shared_ptr; + + virtual ~<xsl:value-of select="$f/@name"/>() {} + + <xsl:for-each select="$f/field"> + inline <xsl:value-of select="concat(@cpp-arg-type, ' get', amqp:upper-first(@name), '() { return ', @name)"/>; } + </xsl:for-each> + + + inline void print(std::ostream& out) const{ + out << "<xsl:value-of select="$f/@declaration_name"/>" + <xsl:for-each select="$f/field"> + <xsl:text> << ", </xsl:text> + <xsl:value-of select="@name"/>="<< + <xsl:value-of select="@name"/> + </xsl:for-each> + ; + } + + inline u_int16_t amqpClassId() const { + return <xsl:value-of select="$f/@class-id"/>; + } + + inline u_int16_t amqpMethodId() const { + return <xsl:value-of select="$f/@method-id"/>; + } + + inline u_int32_t bodySize() const { + <xsl:choose> + <xsl:when test="$f/field"> + return + <xsl:for-each select="$f/field"> + <xsl:if test="position() != 1">+ + </xsl:if> + <xsl:value-of select="amqp:field-length(.)"/> + </xsl:for-each> + ; + </xsl:when> + <xsl:otherwise>return 0;</xsl:otherwise> + </xsl:choose> + } + + <xsl:if test="@server='true'"> + inline void invoke(AMQP_ServerOperations& target, u_int16_t channel) { + <xsl:if test="field"> + <xsl:value-of select="concat('target.get', amqp:upper-first(parent::class/@name), 'Handler()->', @invocation_name, '(channel, ')"/> + <xsl:value-of select="$f/field/@name" separator=", "/>); + </xsl:if> + <xsl:if test="not(field)"> + <xsl:value-of select="concat('target.get', amqp:upper-first(parent::class/@name), 'Handler()->', @invocation_name, '(channel)')"/>; + </xsl:if> + } + </xsl:if> + + inline void encodeContent(Buffer& buffer) const + { + <xsl:if test="$f/field[@type='bit']"> + u_int8_t flags = 0; + <xsl:for-each select="$f/field[@type='bit']"> + <xsl:value-of select="concat('flags |= ', @name,' << (', @boolean-index, ' - 1)')"/>; + </xsl:for-each> + </xsl:if> + <xsl:for-each select="$f/field"> + <xsl:if test="@type != 'bit'"> + <xsl:value-of select="amqp:encoder(.)"/>; + </xsl:if> + <xsl:if test="@type = 'bit' and @boolean-index = 1"> + <xsl:text>buffer.putOctet(flags)</xsl:text>; + </xsl:if> + </xsl:for-each> + } + + inline void decodeContent(Buffer& buffer) + { + <xsl:if test="$f/field[@type='bit']"> + u_int8_t maxbit = <xsl:value-of select="$f/@bit-field-count"/>; + </xsl:if> + <xsl:for-each select="$f/field"> + <xsl:choose> + <xsl:when test="@type = 'bit' and @boolean-index = 1"> + <xsl:text>u_int8_t flags = buffer.getOctet()</xsl:text>; + <xsl:value-of select="amqp:decoder(.)"/>; + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="amqp:decoder(.)"/>; + </xsl:otherwise> + </xsl:choose> + </xsl:for-each> + } + + <xsl:if test="$f/field"> + <!-- only generate overloaded constructor if there are fields in this method --> + inline <xsl:value-of select="$f/@name"/>(<xsl:value-of select="$f/field/concat(@cpp-arg-type, ' _', @name)" separator=", "/>) : <xsl:value-of select="$f/field/concat(@name, '(_', @name, ')')" separator=", "/> + { + } + </xsl:if> + + inline <xsl:value-of select="$f/@name"/>() + { + } +}; + +</xsl:template> + +<xsl:template match="frames" mode="method-list-header"> +<xsl:result-document href="amqp_methods.h" format="textFormat"> +/** + * This file is autogenerated, do not modify. + */ + +#ifndef AMQ_METHODS_H +#define AMQ_METHODS_H + + <xsl:for-each select="class/frame"> +#include "<xsl:value-of select="@name"/>.h" + </xsl:for-each> + +namespace qpid { +namespace framing { + + <xsl:for-each select="class/frame"> +const <xsl:value-of select="concat(@name, ' ', @declaration_name)"/>; + </xsl:for-each> + +AMQMethodBody* createAMQMethodBody(u_int16_t classId, u_int16_t methodId); + +} +} + +#endif +</xsl:result-document> +</xsl:template> + +<xsl:template match="frames" mode="method-list-source"> + <xsl:result-document href="amqp_methods.cpp" format="textFormat"> +#include "amqp_methods.h" +#include "QpidError.h" + +namespace qpid { +namespace framing { +/** + * This method is autogenerated, do not modify. + */ +AMQMethodBody* createAMQMethodBody(u_int16_t classId, u_int16_t methodId){ + switch(classId * 1000 + methodId) + { + <xsl:for-each select="class/frame"> + <xsl:text>case </xsl:text> + <xsl:value-of select="@class-id"/> + <xsl:text> * 1000 + </xsl:text> + <xsl:value-of select="@method-id"/> + <xsl:text>: return new </xsl:text> + <xsl:value-of select="@name"/>(); + </xsl:for-each> + } + THROW_QPID_ERROR(FRAMING_ERROR, "Unknown method"); +} + +} +} +</xsl:result-document> +</xsl:template> + +<xsl:template match="frames" mode="generate-interface"> + <xsl:result-document href="AMQPServer.h" format="textFormat"> +#include "amqp_types.h" +#include "FieldTable.h" + +#ifndef _AMQPServer_ +#define _AMQPServer_ + +namespace qpid { +namespace framing { + +class AMQPServer +{ + public: + + <xsl:for-each select="class"> + class <xsl:value-of select="concat(amqp:upper-first(@name), 'Handler')"/>{ + public: + <xsl:for-each select="frame[@server='true']"> + <xsl:if test="field"> + virtual void <xsl:value-of select="@invocation_name"/>(u_int16_t channel, <xsl:value-of select="field/concat(@cpp-arg-type, ' ', @name)" separator=", "/>) = 0; + </xsl:if> + <xsl:if test="not(field)"> + virtual void <xsl:value-of select="@invocation_name"/>(u_int16_t channel) = 0; + </xsl:if> + </xsl:for-each> + virtual ~<xsl:value-of select="concat(amqp:upper-first(@name), 'Handler')"/>(){} + }; + + virtual <xsl:value-of select="concat(amqp:upper-first(@name), 'Handler* get', amqp:upper-first(@name), 'Handler')"/>() = 0; + + </xsl:for-each> + virtual ~AMQPServer(){} +}; + +} +} + +#endif +</xsl:result-document> + + <xsl:result-document href="AMQPClient.h" format="textFormat"> +#include "amqp_types.h" +#include "FieldTable.h" + +#ifndef _AMQPClient_ +#define _AMQPClient_ + +namespace qpid { +namespace framing { + +class AMQPClient +{ + public: + + <xsl:for-each select="class"> + class <xsl:value-of select="concat(amqp:upper-first(@name), 'Handler')"/>{ + public: + <xsl:for-each select="frame[@client='true']"> + <xsl:if test="field"> + virtual void <xsl:value-of select="@invocation_name"/>(u_int16_t channel, <xsl:value-of select="field/concat(@cpp-arg-type, ' ', @name)" separator=", "/>) = 0; + </xsl:if> + <xsl:if test="not(field)"> + virtual void <xsl:value-of select="@invocation_name"/>(u_int16_t channel) = 0; + </xsl:if> + </xsl:for-each> + virtual ~<xsl:value-of select="concat(amqp:upper-first(@name), 'Handler')"/>(){} + }; + + virtual <xsl:value-of select="concat(amqp:upper-first(@name), 'Handler* get', amqp:upper-first(@name), 'Handler')"/>() = 0; + + </xsl:for-each> + + virtual ~AMQPClient(){} +}; + +} +} + +#endif +</xsl:result-document> + +</xsl:template> + +</xsl:stylesheet> diff --git a/cpp/common/framing/generated/stylesheets/framing.xsl b/cpp/common/framing/generated/stylesheets/framing.xsl new file mode 100644 index 0000000000..c63e719a77 --- /dev/null +++ b/cpp/common/framing/generated/stylesheets/framing.xsl @@ -0,0 +1,49 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:amqp="http://amqp.org"> + +<xsl:import href="prepare1.xsl"/> +<xsl:import href="prepare2.xsl"/> +<xsl:import href="prepare3.xsl"/> +<xsl:import href="cpp.xsl"/> + +<xsl:output indent="yes"/> +<xsl:output method="text" indent="yes" name="textFormat"/> + +<xsl:template match="/"> + <xsl:variable name="prepare1"> + <xsl:apply-templates mode="prepare1" select="."/> + </xsl:variable> + + <xsl:variable name="prepare2"> + <xsl:apply-templates mode="prepare2" select="$prepare1"/> + </xsl:variable> + + <xsl:variable name="model"> + <xsl:apply-templates mode="prepare3" select="$prepare2"/> + </xsl:variable> + + <xsl:apply-templates mode="generate-multi" select="$model"/> + <xsl:apply-templates mode="method-list-header" select="$model"/> + <xsl:apply-templates mode="method-list-source" select="$model"/> + + <!-- these interfaces are now generated by the new scripts from kim --> + <!-- e.g. those of the form amqp-server/client-*.xsl --> + <!-- xsl:apply-templates mode="generate-interface" select="$model"/ --> + + <!-- dump out the intermediary files for debugging --> + <!-- + <xsl:result-document href="prepare1.out"> + <xsl:copy-of select="$prepare1"/> + </xsl:result-document> + + <xsl:result-document href="prepare2.out"> + <xsl:copy-of select="$prepare2"/> + </xsl:result-document> + + <xsl:result-document href="model.out"> + <xsl:copy-of select="$model"/> + </xsl:result-document> + --> +</xsl:template> + +</xsl:stylesheet> diff --git a/cpp/common/framing/generated/stylesheets/prepare1.xsl b/cpp/common/framing/generated/stylesheets/prepare1.xsl new file mode 100644 index 0000000000..2aeda89677 --- /dev/null +++ b/cpp/common/framing/generated/stylesheets/prepare1.xsl @@ -0,0 +1,104 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:amqp="http://amqp.org"> + +<xsl:import href="utils.xsl"/> + +<xsl:output indent="yes"/> +<xsl:param name="asl_base"/> + +<!-- pre-process, phase 1 --> + +<xsl:template match="/"> + <xsl:apply-templates select="protocol" mode="prepare1"/> +</xsl:template> + +<xsl:template match="amqp" mode="prepare1"> + <frames> + <xsl:attribute name="protocol"> + <xsl:value-of select="@comment"/> + <xsl:text> (</xsl:text> + <xsl:text>major=</xsl:text><xsl:value-of select="@major"/> + <xsl:text>, minor=</xsl:text><xsl:value-of select="@minor"/> + <xsl:text>)</xsl:text> + </xsl:attribute> + <xsl:apply-templates mode="prepare1" select="inherit"/> + <xsl:apply-templates mode="prepare1" select="include"/> + <xsl:apply-templates mode="prepare1" select="domain"/> + <xsl:apply-templates mode="prepare1" select="class"/> + </frames> +</xsl:template> + +<xsl:template match="include" mode="prepare1"> + <xsl:if test="@filename != 'asl_constants.asl'"> + <!-- skip asl_constants.asl, we don't need it and it is not well formed so causes error warnings --> + <xsl:apply-templates select="document(@filename)" mode="prepare1"/> + </xsl:if> +</xsl:template> + +<xsl:template match="inherit" mode="prepare1"> + <xsl:variable name="ibase" select="concat('file:///', $asl_base, '/', @name, '.asl')"/> + <xsl:choose> + <xsl:when test="document($ibase)"> + <xsl:apply-templates select="document($ibase)" mode="prepare1"/> + </xsl:when> + <xsl:otherwise> + <xsl:message> + Could not inherit from <xsl:value-of select="$ibase"/>; file not found. + </xsl:message> + </xsl:otherwise> + </xsl:choose> +</xsl:template> + +<xsl:template match="class[@index]" mode="prepare1"> +<xsl:if test="not(@name = 'test')"> + <class> + <xsl:attribute name="name"><xsl:value-of select="@name"/></xsl:attribute> + <xsl:apply-templates select="method" mode="prepare1"/> + </class> +</xsl:if> +</xsl:template> + +<xsl:template match="method" mode="prepare1"> + <xsl:if test="parent::class[@index]"><!-- there is a template class that has no index, which we want to skip --> + <frame> + <xsl:attribute name="name"><xsl:value-of select="amqp:class-name(parent::class/@name, @name)"/></xsl:attribute> + <xsl:attribute name="class-id"><xsl:value-of select="parent::class/@index"/></xsl:attribute> + <xsl:if test="@index"> + <xsl:attribute name="method-id"><xsl:value-of select="@index"/></xsl:attribute> + </xsl:if> + <xsl:if test="not(@index)"> + <xsl:attribute name="method-id"><xsl:number count="method"/></xsl:attribute> + </xsl:if> + <xsl:attribute name="invocation_name"> + <xsl:value-of select="amqp:keyword-check(amqp:field-name(@name))"/> + </xsl:attribute> + <xsl:attribute name="declaration_name"> + <xsl:value-of select="amqp:method-name(parent::class/@name, @name)"/> + </xsl:attribute> + <xsl:if test="chassis[@name='client']"> + <xsl:attribute name="client">true</xsl:attribute> + </xsl:if> + <xsl:if test="chassis[@name='server']"> + <xsl:attribute name="server">true</xsl:attribute> + </xsl:if> + <xsl:apply-templates select="field" mode="prepare1"/> + </frame> + </xsl:if> +</xsl:template> + +<xsl:template match="domain" mode="prepare1"> + <domain> + <name><xsl:value-of select="@name"/></name> + <type><xsl:value-of select="@type"/></type> + </domain> +</xsl:template> + +<xsl:template match="field" mode="prepare1"> + <field> + <xsl:copy-of select="@name"/> + <xsl:copy-of select="@type"/> + <xsl:copy-of select="@domain"/> + </field> +</xsl:template> + +</xsl:stylesheet> diff --git a/cpp/common/framing/generated/stylesheets/prepare2.xsl b/cpp/common/framing/generated/stylesheets/prepare2.xsl new file mode 100644 index 0000000000..331319de57 --- /dev/null +++ b/cpp/common/framing/generated/stylesheets/prepare2.xsl @@ -0,0 +1,54 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:amqp="http://amqp.org"> + +<xsl:import href="utils.xsl"/> + +<xsl:output indent="yes"/> + +<!-- pre-process, phase 2 --> + +<xsl:key name="domain-lookup" match="domain" use="name"/> + +<xsl:template match="/"> + <xsl:apply-templates mode="prepare2" select="frames"/> +</xsl:template> + +<xsl:template match="field[@domain]" mode="prepare2"> + <field> + <xsl:variable name="t1" select="key('domain-lookup', @domain)/type"/> + <xsl:attribute name="name"><xsl:value-of select="amqp:field-name(@name)"/></xsl:attribute> + <xsl:attribute name="type"><xsl:value-of select="$t1"/></xsl:attribute> + </field> +</xsl:template> + +<xsl:template match="field[@type]" mode="prepare2"> + <field> + <xsl:attribute name="name"><xsl:value-of select="amqp:field-name(@name)"/></xsl:attribute> + <xsl:attribute name="type"><xsl:value-of select="@type"/></xsl:attribute> + </field> +</xsl:template> + +<xsl:template match="frames" mode="prepare2"> + <frames> + <xsl:copy-of select="@protocol"/> + <xsl:apply-templates mode="prepare2"/> + </frames> +</xsl:template> + +<xsl:template match="class" mode="prepare2"> + <class> + <xsl:copy-of select="@*"/> + <xsl:apply-templates mode="prepare2"/> + </class> +</xsl:template> + +<xsl:template match="frame" mode="prepare2"> + <xsl:element name="{name()}"> + <xsl:copy-of select="@*"/> + <xsl:apply-templates mode="prepare2" select="field"/> + </xsl:element> +</xsl:template> + +<xsl:template match="domain" mode="prepare2"></xsl:template> + +</xsl:stylesheet> diff --git a/cpp/common/framing/generated/stylesheets/prepare3.xsl b/cpp/common/framing/generated/stylesheets/prepare3.xsl new file mode 100644 index 0000000000..27a4764e4f --- /dev/null +++ b/cpp/common/framing/generated/stylesheets/prepare3.xsl @@ -0,0 +1,54 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:amqp="http://amqp.org"> + +<xsl:import href="utils.xsl"/> + +<xsl:output indent="yes"/> + +<!-- final preparation of the model --> + +<xsl:template match="/"> + <xsl:apply-templates mode="prepare3"/> +</xsl:template> + +<xsl:template match="frames" mode="prepare3"> + <frames> + <xsl:copy-of select="@protocol"/> + <xsl:apply-templates mode="prepare3"/> + </frames> +</xsl:template> + +<xsl:template match="class" mode="prepare3"> + <class> + <xsl:copy-of select="@*"/> + <xsl:apply-templates mode="prepare3"/> + </class> +</xsl:template> + +<xsl:template match="frame" mode="prepare3"> + <xsl:element name="frame"> + <xsl:copy-of select="@*"/> + <xsl:if test="field[@type='bit']"> + <xsl:attribute name="has-bit-field">true</xsl:attribute> + <xsl:attribute name="bit-field-count"><xsl:value-of select="count(field[@type='bit'])"/></xsl:attribute> + </xsl:if> + <xsl:apply-templates mode="prepare3"/> + </xsl:element> +</xsl:template> + + +<xsl:template match="field" mode="prepare3"> + <field> + <xsl:attribute name="type"><xsl:value-of select="@type"/></xsl:attribute> + <!-- ensure the field name is processed to be a valid java name --> + <xsl:attribute name="name"><xsl:value-of select="amqp:field-name(@name)"/></xsl:attribute> + <!-- add some attributes to make code generation easier --> + <xsl:attribute name="cpp-type"><xsl:value-of select="amqp:cpp-type(@type)"/></xsl:attribute> + <xsl:attribute name="cpp-arg-type"><xsl:value-of select="amqp:cpp-arg-type(@type)"/></xsl:attribute> + <xsl:if test="@type='bit'"> + <xsl:attribute name="boolean-index"><xsl:number count="field[@type='bit']"/></xsl:attribute> + </xsl:if> + </field> +</xsl:template> + +</xsl:stylesheet> diff --git a/cpp/common/framing/generated/stylesheets/registry.xsl b/cpp/common/framing/generated/stylesheets/registry.xsl new file mode 100644 index 0000000000..a818a0a871 --- /dev/null +++ b/cpp/common/framing/generated/stylesheets/registry.xsl @@ -0,0 +1,12 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:amqp="http://amqp.org"> + +<xsl:import href="java.xsl"/> + +<xsl:output method="text" indent="yes" name="textFormat"/> + +<xsl:template match="/"> + <xsl:apply-templates mode="generate-registry" select="registries"/> +</xsl:template> + +</xsl:stylesheet> diff --git a/cpp/common/framing/generated/stylesheets/utils.xsl b/cpp/common/framing/generated/stylesheets/utils.xsl new file mode 100644 index 0000000000..829d38433e --- /dev/null +++ b/cpp/common/framing/generated/stylesheets/utils.xsl @@ -0,0 +1,194 @@ +<?xml version='1.0'?> +<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:amqp="http://amqp.org"> + +<!-- This file contains functions that are used in the generation of the java classes for framing --> + +<!-- retrieve the java type of a given amq type --> +<xsl:function name="amqp:cpp-type"> + <xsl:param name="t"/> + <xsl:choose> + <xsl:when test="$t='octet'">u_int8_t</xsl:when> + <xsl:when test="$t='short'">u_int16_t</xsl:when> + <xsl:when test="$t='shortstr'">string</xsl:when> + <xsl:when test="$t='longstr'">string</xsl:when> + <xsl:when test="$t='bit'">bool</xsl:when> + <xsl:when test="$t='long'">u_int32_t</xsl:when> + <xsl:when test="$t='longlong'">u_int64_t</xsl:when> + <xsl:when test="$t='table'">FieldTable</xsl:when> + <xsl:otherwise>Object /*WARNING: undefined type*/</xsl:otherwise> + </xsl:choose> +</xsl:function> +<xsl:function name="amqp:cpp-arg-type"> + <xsl:param name="t"/> + <xsl:choose> + <xsl:when test="$t='octet'">u_int8_t</xsl:when> + <xsl:when test="$t='short'">u_int16_t</xsl:when> + <xsl:when test="$t='shortstr'">string&</xsl:when> + <xsl:when test="$t='longstr'">string&</xsl:when> + <xsl:when test="$t='bit'">bool</xsl:when> + <xsl:when test="$t='long'">u_int32_t</xsl:when> + <xsl:when test="$t='longlong'">u_int64_t</xsl:when> + <xsl:when test="$t='table'">FieldTable&</xsl:when> + <xsl:otherwise>Object /*WARNING: undefined type*/</xsl:otherwise> + </xsl:choose> +</xsl:function> + +<!-- retrieve the code to get the field size of a given amq type --> +<xsl:function name="amqp:field-length"> + <xsl:param name="f"/> + <xsl:choose> + <xsl:when test="$f/@type='bit' and $f/@boolean-index=1"> + <xsl:value-of select="concat('1 /*', $f/@name, '*/')"/> + </xsl:when> + <xsl:when test="$f/@type='bit' and $f/@boolean-index > 1"> + <xsl:value-of select="concat('0 /*', $f/@name, '*/')"/> + </xsl:when> + <xsl:when test="$f/@type='char'"> + <xsl:value-of select="concat('1 /*', $f/@name, '*/')"/> + </xsl:when> + <xsl:when test="$f/@type='octet'"> + <xsl:value-of select="concat('1 /*', $f/@name, '*/')"/> + </xsl:when> + <xsl:when test="$f/@type='short'"> + <xsl:value-of select="concat('2 /*', $f/@name, '*/')"/> + </xsl:when> + <xsl:when test="$f/@type='long'"> + <xsl:value-of select="concat('4 /*', $f/@name, '*/')"/> + </xsl:when> + <xsl:when test="$f/@type='longlong'"> + <xsl:value-of select="concat('8 /*', $f/@name, '*/')"/> + </xsl:when> + <xsl:when test="$f/@type='shortstr'"> + <xsl:value-of select="concat('1 + ', $f/@name, '.length()')"/> + </xsl:when> + <xsl:when test="$f/@type='longstr'"> + <xsl:value-of select="concat('4 + ', $f/@name, '.length()')"/> + </xsl:when> + <xsl:when test="$f/@type='table'"> + <xsl:value-of select="concat($f/@name, '.size()')"/> + </xsl:when> + <xsl:otherwise><xsl:text>/* WARNING: COULD NOT DETERMINE FIELD SIZE */</xsl:text></xsl:otherwise> + </xsl:choose> +</xsl:function> + +<!-- retrieve the code to encode a field of a given amq type --> +<!-- Note: + This method will not provide an encoder for a bit field. + Bit fields should be encoded together separately. --> + +<xsl:function name="amqp:encoder"> + <xsl:param name="f"/> + <xsl:choose> + <xsl:when test="$f/@type='octet'"> + <xsl:value-of select="concat('buffer.putOctet(', $f/@name, ')')"/> + </xsl:when> + <xsl:when test="$f/@type='short'"> + <xsl:value-of select="concat('buffer.putShort(', $f/@name, ')')"/> + </xsl:when> + <xsl:when test="$f/@type='long'"> + <xsl:value-of select="concat('buffer.putLong(', $f/@name, ')')"/> + </xsl:when> + <xsl:when test="$f/@type='longlong'"> + <xsl:value-of select="concat('buffer.putLongLong(', $f/@name, ')')"/> + </xsl:when> + <xsl:when test="$f/@type='shortstr'"> + <xsl:value-of select="concat('buffer.putShortString(', $f/@name, ')')"/> + </xsl:when> + <xsl:when test="$f/@type='longstr'"> + <xsl:value-of select="concat('buffer.putLongString(', $f/@name, ')')"/> + </xsl:when> + <xsl:when test="$f/@type='table'"> + <xsl:value-of select="concat('buffer.putFieldTable(', $f/@name, ')')"/> + </xsl:when> + <xsl:otherwise><xsl:text>/* WARNING: COULD NOT DETERMINE ENCODER */</xsl:text></xsl:otherwise> + </xsl:choose> +</xsl:function> + +<!-- retrieve the code to decode a field of a given amq type --> +<xsl:function name="amqp:decoder"> + <xsl:param name="f"/> + <xsl:choose> + <xsl:when test="$f/@type='bit'"> + <xsl:value-of select="concat($f/@name, ' = (1 << (', $f/@boolean-index, ' - 1)) & flags;')"/> + </xsl:when> + <xsl:when test="$f/@type='octet'"> + <xsl:value-of select="concat($f/@name, ' = buffer.getOctet()')"/> + </xsl:when> + <xsl:when test="$f/@type='short'"> + <xsl:value-of select="concat($f/@name, ' = buffer.getShort()')"/> + </xsl:when> + <xsl:when test="$f/@type='long'"> + <xsl:value-of select="concat($f/@name, ' = buffer.getLong()')"/> + </xsl:when> + <xsl:when test="$f/@type='longlong'"> + <xsl:value-of select="concat($f/@name, ' = buffer.getLongLong()')"/> + </xsl:when> + <xsl:when test="$f/@type='shortstr'"> + <xsl:value-of select="concat('buffer.getShortString(', $f/@name), ')'"/> + </xsl:when> + <xsl:when test="$f/@type='longstr'"> + <xsl:value-of select="concat('buffer.getLongString(', $f/@name), ')'"/> + </xsl:when> + <xsl:when test="$f/@type='table'"> + <xsl:value-of select="concat('buffer.getFieldTable(', $f/@name, ')')"/> + </xsl:when> + <xsl:otherwise><xsl:text>/* WARNING: COULD NOT DETERMINE DECODER */</xsl:text></xsl:otherwise> + </xsl:choose> +</xsl:function> + +<!-- create the class name for a frame, based on class and method (passed in) --> +<xsl:function name="amqp:class-name"> + <xsl:param name="class"/> + <xsl:param name="method"/> + <xsl:value-of select="concat(amqp:upper-first($class),amqp:upper-first(amqp:field-name($method)), 'Body')"/> +</xsl:function> + +<!-- create the class name for a frame, based on class and method (passed in) --> +<xsl:function name="amqp:method-name"> + <xsl:param name="class"/> + <xsl:param name="method"/> + <xsl:value-of select="concat(translate($class, '- ', '__'), '_', translate($method, '- ', '__'))"/> +</xsl:function> + +<!-- get a valid field name, processing spaces and '-'s where appropriate --> +<xsl:function name="amqp:field-name"> + <xsl:param name="name"/> + <xsl:choose> + <xsl:when test="contains($name, ' ')"> + <xsl:value-of select="concat(substring-before($name, ' '), amqp:upper-first(substring-after($name, ' ')))"/> + </xsl:when> + <xsl:when test="contains($name, '-')"> + <xsl:value-of select="concat(substring-before($name, '-'), amqp:upper-first(substring-after($name, '-')))"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="$name"/> + </xsl:otherwise> + </xsl:choose> +</xsl:function> + +<!-- convert the first character of the input to upper-case --> +<xsl:function name="amqp:upper-first"> + <xsl:param name="in"/> + <xsl:value-of select="concat(upper-case(substring($in, 1, 1)), substring($in, 2))"/> +</xsl:function> + + +<xsl:function name="amqp:keyword-check"> + <xsl:param name="in"/> + <xsl:choose> + <xsl:when test="contains($in, 'delete')"> + <xsl:value-of select="concat($in, '_')"/> + </xsl:when> + <xsl:when test="contains($in, 'string')"> + <xsl:value-of select="concat($in, '_')"/> + </xsl:when> + <xsl:when test="contains($in, 'return')"> + <xsl:value-of select="concat($in, '_')"/> + </xsl:when> + <xsl:otherwise> + <xsl:value-of select="$in"/> + </xsl:otherwise> + </xsl:choose> +</xsl:function> + +</xsl:stylesheet> diff --git a/cpp/common/framing/inc/AMQBody.h b/cpp/common/framing/inc/AMQBody.h new file mode 100644 index 0000000000..d4b436c949 --- /dev/null +++ b/cpp/common/framing/inc/AMQBody.h @@ -0,0 +1,46 @@ +/* + * + * 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.h" +#include "amqp_types.h" +#include "Buffer.h" + +#ifndef _AMQBody_ +#define _AMQBody_ + +namespace qpid { + namespace framing { + + class AMQBody + { + public: + typedef std::tr1::shared_ptr<AMQBody> shared_ptr; + + virtual u_int32_t size() const = 0; + virtual u_int8_t type() const = 0; + virtual void encode(Buffer& buffer) const = 0; + virtual void decode(Buffer& buffer, u_int32_t size) = 0; + inline virtual ~AMQBody(){} + }; + + enum body_types {METHOD_BODY = 1, HEADER_BODY = 2, CONTENT_BODY = 3, HEARTBEAT_BODY = 8}; + + } +} + + +#endif diff --git a/cpp/common/framing/inc/AMQContentBody.h b/cpp/common/framing/inc/AMQContentBody.h new file mode 100644 index 0000000000..8e97c31edb --- /dev/null +++ b/cpp/common/framing/inc/AMQContentBody.h @@ -0,0 +1,49 @@ +/* + * + * 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 "amqp_types.h" +#include "AMQBody.h" +#include "Buffer.h" + +#ifndef _AMQContentBody_ +#define _AMQContentBody_ + +namespace qpid { +namespace framing { + +class AMQContentBody : virtual public AMQBody +{ + string data; + +public: + typedef std::tr1::shared_ptr<AMQContentBody> shared_ptr; + + AMQContentBody(); + AMQContentBody(string& data); + inline virtual ~AMQContentBody(){} + inline u_int8_t type() const { return CONTENT_BODY; }; + inline string& getData(){ return data; } + u_int32_t size() const; + void encode(Buffer& buffer) const; + void decode(Buffer& buffer, u_int32_t size); +}; + +} +} + + +#endif diff --git a/cpp/common/framing/inc/AMQDataBlock.h b/cpp/common/framing/inc/AMQDataBlock.h new file mode 100644 index 0000000000..6c47c78864 --- /dev/null +++ b/cpp/common/framing/inc/AMQDataBlock.h @@ -0,0 +1,39 @@ +/* + * + * 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 "Buffer.h" + +#ifndef _AMQDataBlock_ +#define _AMQDataBlock_ + +namespace qpid { +namespace framing { + +class AMQDataBlock +{ +public: + virtual ~AMQDataBlock() {} + virtual void encode(Buffer& buffer) = 0; + virtual bool decode(Buffer& buffer) = 0; + virtual u_int32_t size() const = 0; +}; + +} +} + + +#endif diff --git a/cpp/common/framing/inc/AMQFrame.h b/cpp/common/framing/inc/AMQFrame.h new file mode 100644 index 0000000000..5656d20377 --- /dev/null +++ b/cpp/common/framing/inc/AMQFrame.h @@ -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 "amqp_methods.h" +#include "amqp_types.h" +#include "AMQBody.h" +#include "AMQDataBlock.h" +#include "AMQMethodBody.h" +#include "AMQHeaderBody.h" +#include "AMQContentBody.h" +#include "AMQHeartbeatBody.h" +#include "Buffer.h" + +#ifndef _AMQFrame_ +#define _AMQFrame_ + +namespace qpid { + namespace framing { + + class AMQFrame : virtual public AMQDataBlock + { + u_int16_t channel; + u_int8_t type;//used if the body is decoded separately from the 'head' + AMQBody::shared_ptr body; + + public: + AMQFrame(); + AMQFrame(u_int16_t channel, AMQBody* body); + AMQFrame(u_int16_t channel, AMQBody::shared_ptr& body); + virtual ~AMQFrame(); + virtual void encode(Buffer& buffer); + virtual bool decode(Buffer& buffer); + virtual u_int32_t size() const; + u_int16_t getChannel(); + AMQBody::shared_ptr& getBody(); + + u_int32_t decodeHead(Buffer& buffer); + void decodeBody(Buffer& buffer, uint32_t size); + + friend std::ostream& operator<<(std::ostream& out, const AMQFrame& body); + }; + + } +} + + +#endif diff --git a/cpp/common/framing/inc/AMQHeaderBody.h b/cpp/common/framing/inc/AMQHeaderBody.h new file mode 100644 index 0000000000..369db8a9c8 --- /dev/null +++ b/cpp/common/framing/inc/AMQHeaderBody.h @@ -0,0 +1,55 @@ +/* + * + * 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 "amqp_types.h" +#include "AMQBody.h" +#include "Buffer.h" +#include "HeaderProperties.h" + +#ifndef _AMQHeaderBody_ +#define _AMQHeaderBody_ + +namespace qpid { +namespace framing { + +class AMQHeaderBody : virtual public AMQBody +{ + HeaderProperties* properties; + u_int16_t weight; + u_int64_t contentSize; + + void createProperties(int classId); +public: + typedef std::tr1::shared_ptr<AMQHeaderBody> shared_ptr; + + AMQHeaderBody(int classId); + AMQHeaderBody(); + inline u_int8_t type() const { return HEADER_BODY; } + HeaderProperties* getProperties(){ return properties; } + inline u_int64_t getContentSize() const { return contentSize; } + inline void setContentSize(u_int64_t size) { contentSize = size; } + virtual ~AMQHeaderBody(); + virtual u_int32_t size() const; + virtual void encode(Buffer& buffer) const; + virtual void decode(Buffer& buffer, u_int32_t size); +}; + +} +} + + +#endif diff --git a/cpp/common/framing/inc/AMQHeartbeatBody.h b/cpp/common/framing/inc/AMQHeartbeatBody.h new file mode 100644 index 0000000000..ca2def977a --- /dev/null +++ b/cpp/common/framing/inc/AMQHeartbeatBody.h @@ -0,0 +1,43 @@ +/* + * + * 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 "amqp_types.h" +#include "AMQBody.h" +#include "Buffer.h" + +#ifndef _AMQHeartbeatBody_ +#define _AMQHeartbeatBody_ + +namespace qpid { +namespace framing { + +class AMQHeartbeatBody : virtual public AMQBody +{ +public: + typedef std::tr1::shared_ptr<AMQHeartbeatBody> shared_ptr; + + virtual ~AMQHeartbeatBody() {} + inline u_int32_t size() const { return 0; } + inline u_int8_t type() const { return HEARTBEAT_BODY; } + inline void encode(Buffer& buffer) const {} + inline void decode(Buffer& buffer, u_int32_t size) {} +}; + +} +} + +#endif diff --git a/cpp/common/framing/inc/AMQMethodBody.h b/cpp/common/framing/inc/AMQMethodBody.h new file mode 100644 index 0000000000..59d5dd5212 --- /dev/null +++ b/cpp/common/framing/inc/AMQMethodBody.h @@ -0,0 +1,56 @@ +/* + * + * 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 <iostream> +#include "amqp_types.h" +#include "AMQBody.h" +#include "Buffer.h" +#include "AMQP_ServerOperations.h" + +#ifndef _AMQMethodBody_ +#define _AMQMethodBody_ + +namespace qpid { +namespace framing { + +class AMQMethodBody : virtual public AMQBody +{ +public: + typedef std::tr1::shared_ptr<AMQMethodBody> shared_ptr; + + inline u_int8_t type() const { return METHOD_BODY; } + inline u_int32_t size() const { return 4 + bodySize(); } + inline virtual ~AMQMethodBody(){} + virtual void print(std::ostream& out) const = 0; + virtual u_int16_t amqpMethodId() const = 0; + virtual u_int16_t amqpClassId() const = 0; + virtual void invoke(AMQP_ServerOperations& target, u_int16_t channel); + virtual void encodeContent(Buffer& buffer) const = 0; + virtual void decodeContent(Buffer& buffer) = 0; + virtual u_int32_t bodySize() const = 0; + void encode(Buffer& buffer) const; + void decode(Buffer& buffer, u_int32_t size); + bool match(AMQMethodBody* other) const; +}; + +std::ostream& operator<<(std::ostream& out, const AMQMethodBody& body); + +} +} + + +#endif diff --git a/cpp/common/framing/inc/BasicHeaderProperties.h b/cpp/common/framing/inc/BasicHeaderProperties.h new file mode 100644 index 0000000000..8688a37bf9 --- /dev/null +++ b/cpp/common/framing/inc/BasicHeaderProperties.h @@ -0,0 +1,93 @@ +/* + * + * 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 "amqp_types.h" +#include "amqp_methods.h" +#include "Buffer.h" +#include "HeaderProperties.h" + +#ifndef _BasicHeaderProperties_ +#define _BasicHeaderProperties_ + +namespace qpid { +namespace framing { + + //TODO: This could be easily generated from the spec + class BasicHeaderProperties : public HeaderProperties + { + string contentType; + string contentEncoding; + FieldTable headers; + u_int8_t deliveryMode; + u_int8_t priority; + string correlationId; + string replyTo; + string expiration; + string messageId; + u_int64_t timestamp; + string type; + string userId; + string appId; + string clusterId; + + u_int16_t getFlags() const; + + public: + BasicHeaderProperties(); + virtual ~BasicHeaderProperties(); + virtual u_int32_t size() const; + virtual void encode(Buffer& buffer) const; + virtual void decode(Buffer& buffer, u_int32_t size); + + inline virtual u_int8_t classId(){ return BASIC; } + + inline string& getContentType(){ return contentType; } + inline string& getContentEncoding(){ return contentEncoding; } + inline FieldTable& getHeaders(){ return headers; } + inline u_int8_t getDeliveryMode(){ return deliveryMode; } + inline u_int8_t getPriority(){ return priority; } + inline string& getCorrelationId(){return correlationId; } + inline string& getReplyTo(){ return replyTo; } + inline string& getExpiration(){ return expiration; } + inline string& getMessageId(){return messageId; } + inline u_int64_t getTimestamp(){ return timestamp; } + inline string& getType(){ return type; } + inline string& getUserId(){ return userId; } + inline string& getAppId(){ return appId; } + inline string& getClusterId(){ return clusterId; } + + void inline setContentType(string& type){ contentType = type; } + void inline setContentEncoding(string& encoding){ contentEncoding = encoding; } + void inline setHeaders(FieldTable& headers){ this->headers = headers; } + void inline setDeliveryMode(u_int8_t mode){ deliveryMode = mode; } + void inline setPriority(u_int8_t priority){ this->priority = priority; } + void inline setCorrelationId(string& correlationId){ this->correlationId = correlationId; } + void inline setReplyTo(string& replyTo){ this->replyTo = replyTo;} + void inline setExpiration(string& expiration){ this->expiration = expiration; } + void inline setMessageId(string& messageId){ this->messageId = messageId; } + void inline setTimestamp(u_int64_t timestamp){ this->timestamp = timestamp; } + void inline setType(string& type){ this->type = type; } + void inline setUserId(string& userId){ this->userId = userId; } + void inline setAppId(string& appId){this->appId = appId; } + void inline setClusterId(string& clusterId){ this->clusterId = clusterId; } + }; + +} +} + + +#endif diff --git a/cpp/common/framing/inc/BodyHandler.h b/cpp/common/framing/inc/BodyHandler.h new file mode 100644 index 0000000000..f92ae66804 --- /dev/null +++ b/cpp/common/framing/inc/BodyHandler.h @@ -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 <string> + +#ifndef _BodyHandler_ +#define _BodyHandler_ + +#include "AMQMethodBody.h" +#include "AMQHeaderBody.h" +#include "AMQContentBody.h" +#include "AMQHeartbeatBody.h" + +namespace qpid { +namespace framing { + + class BodyHandler{ + public: + virtual void handleMethod(AMQMethodBody::shared_ptr body) = 0; + virtual void handleHeader(AMQHeaderBody::shared_ptr body) = 0; + virtual void handleContent(AMQContentBody::shared_ptr body) = 0; + virtual void handleHeartbeat(AMQHeartbeatBody::shared_ptr body) = 0; + + void handleBody(AMQBody::shared_ptr& body); + }; + + class UnknownBodyType{ + public: + const u_int16_t type; + inline UnknownBodyType(u_int16_t _type) : type(_type){} + }; +} +} + + +#endif diff --git a/cpp/common/framing/inc/Buffer.h b/cpp/common/framing/inc/Buffer.h new file mode 100644 index 0000000000..1ff4611f1f --- /dev/null +++ b/cpp/common/framing/inc/Buffer.h @@ -0,0 +1,77 @@ +/* + * + * 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 "amqp_types.h" +#include "FieldTable.h" + +#ifndef _Buffer_ +#define _Buffer_ + +namespace qpid { +namespace framing { + +class Buffer +{ + const int size; + char* data; + int position; + int limit; + int r_position; + int r_limit; + +public: + + Buffer(int size); + ~Buffer(); + + void flip(); + void clear(); + void compact(); + void record(); + void restore(); + int available(); + char* start(); + void move(int bytes); + + void putOctet(u_int8_t i); + void putShort(u_int16_t i); + void putLong(u_int32_t i); + void putLongLong(u_int64_t i); + + u_int8_t getOctet(); + u_int16_t getShort(); + u_int32_t getLong(); + u_int64_t getLongLong(); + + void putShortString(const string& s); + void putLongString(const string& s); + void getShortString(string& s); + void getLongString(string& s); + + void putFieldTable(const FieldTable& t); + void getFieldTable(FieldTable& t); + + void putRawData(const string& s); + void getRawData(string& s, u_int32_t size); + +}; + +} +} + + +#endif diff --git a/cpp/common/framing/inc/FieldTable.h b/cpp/common/framing/inc/FieldTable.h new file mode 100644 index 0000000000..cf935d3284 --- /dev/null +++ b/cpp/common/framing/inc/FieldTable.h @@ -0,0 +1,68 @@ +/* + * + * 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 <iostream> +#include <vector> +#include "amqp_types.h" + +#ifndef _FieldTable_ +#define _FieldTable_ + +namespace qpid { +namespace framing { + + class NamedValue; + class Value; + class Buffer; + + class FieldTable + { + std::vector<NamedValue*> values; + NamedValue* find(const std::string& name) const; + + Value* getValue(const std::string& name) const; + void setValue(const std::string& name, Value* value); + + public: + ~FieldTable(); + u_int32_t size() const; + int count() 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, u_int64_t value); + void setTable(const std::string& name, const FieldTable& value); + //void setDecimal(string& name, xxx& value); + std::string getString(const std::string& name); + int getInt(const std::string& name); + u_int64_t getTimestamp(const std::string& name); + void getTable(const std::string& name, FieldTable& value); + //void getDecimal(string& name, xxx& value); + + void encode(Buffer& buffer) const; + void decode(Buffer& buffer); + + friend std::ostream& operator<<(std::ostream& out, const FieldTable& body); + }; + + class FieldNotFoundException{}; + class UnknownFieldName : public FieldNotFoundException{}; + class IncorrectFieldType : public FieldNotFoundException{}; +} +} + + +#endif diff --git a/cpp/common/framing/inc/HeaderProperties.h b/cpp/common/framing/inc/HeaderProperties.h new file mode 100644 index 0000000000..f84345c203 --- /dev/null +++ b/cpp/common/framing/inc/HeaderProperties.h @@ -0,0 +1,43 @@ +/* + * + * 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 "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 u_int8_t classId() = 0; + virtual u_int32_t size() const = 0; + virtual void encode(Buffer& buffer) const = 0; + virtual void decode(Buffer& buffer, u_int32_t size) = 0; + }; +} +} + + +#endif diff --git a/cpp/common/framing/inc/InitiationHandler.h b/cpp/common/framing/inc/InitiationHandler.h new file mode 100644 index 0000000000..2e8d1e652b --- /dev/null +++ b/cpp/common/framing/inc/InitiationHandler.h @@ -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 <string> + +#ifndef _InitiationHandler_ +#define _InitiationHandler_ + +#include "ProtocolInitiation.h" + +namespace qpid { +namespace framing { + + class InitiationHandler{ + public: + virtual void initiated(ProtocolInitiation* header) = 0; + }; + +} +} + + +#endif diff --git a/cpp/common/framing/inc/InputHandler.h b/cpp/common/framing/inc/InputHandler.h new file mode 100644 index 0000000000..2722cae0ed --- /dev/null +++ b/cpp/common/framing/inc/InputHandler.h @@ -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 <string> + +#ifndef _InputHandler_ +#define _InputHandler_ + +#include "AMQFrame.h" + +namespace qpid { +namespace framing { + + class InputHandler{ + public: + virtual void received(AMQFrame* frame) = 0; + }; + +} +} + + +#endif diff --git a/cpp/common/framing/inc/NamedValue.h b/cpp/common/framing/inc/NamedValue.h new file mode 100644 index 0000000000..729b5d08a7 --- /dev/null +++ b/cpp/common/framing/inc/NamedValue.h @@ -0,0 +1,49 @@ +/* + * + * 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 <iostream> +#include <vector> +#include "amqp_types.h" +#include "Value.h" + +#ifndef _NamedValue_ +#define _NamedValue_ + +namespace qpid { +namespace framing { + + class Buffer; + + class NamedValue{ + string name; + Value* value; + public: + NamedValue(); + NamedValue(const string& name, Value* value); + ~NamedValue(); + void encode(Buffer& buffer); + void decode(Buffer& buffer); + u_int32_t size() const; + inline const string& getName() const { return name; } + inline Value* getValue() const { return value; } + inline void setValue(Value* val) { value = val; } + }; +} +} + + +#endif diff --git a/cpp/common/framing/inc/OutputHandler.h b/cpp/common/framing/inc/OutputHandler.h new file mode 100644 index 0000000000..7fe63660c3 --- /dev/null +++ b/cpp/common/framing/inc/OutputHandler.h @@ -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 <string> + +#ifndef _OutputHandler_ +#define _OutputHandler_ + +#include "AMQFrame.h" + +namespace qpid { +namespace framing { + + class OutputHandler{ + public: + virtual void send(AMQFrame* frame) = 0; + }; + +} +} + + +#endif diff --git a/cpp/common/framing/inc/ProtocolInitiation.h b/cpp/common/framing/inc/ProtocolInitiation.h new file mode 100644 index 0000000000..ab9734e6b3 --- /dev/null +++ b/cpp/common/framing/inc/ProtocolInitiation.h @@ -0,0 +1,48 @@ +/* + * + * 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 "amqp_types.h" +#include "Buffer.h" +#include "AMQDataBlock.h" + +#ifndef _ProtocolInitiation_ +#define _ProtocolInitiation_ + +namespace qpid { +namespace framing { + +class ProtocolInitiation : virtual public AMQDataBlock +{ + u_int8_t pmajor; + u_int8_t pminor; + +public: + ProtocolInitiation(); + ProtocolInitiation(u_int8_t major, u_int8_t minor); + virtual ~ProtocolInitiation(); + virtual void encode(Buffer& buffer); + virtual bool decode(Buffer& buffer); + inline virtual u_int32_t size() const { return 8; } + inline u_int8_t getMajor(){ return pmajor; } + inline u_int8_t getMinor(){ return pminor; } +}; + +} +} + + +#endif diff --git a/cpp/common/framing/inc/Value.h b/cpp/common/framing/inc/Value.h new file mode 100644 index 0000000000..e3d2a2c1d6 --- /dev/null +++ b/cpp/common/framing/inc/Value.h @@ -0,0 +1,109 @@ +/* + * + * 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 <iostream> +#include <vector> +#include "amqp_types.h" +#include "FieldTable.h" + +#ifndef _Value_ +#define _Value_ + +namespace qpid { +namespace framing { + + class Buffer; + + class Value{ + public: + inline virtual ~Value(){} + virtual u_int32_t size() const = 0; + virtual char getType() const = 0; + virtual void encode(Buffer& buffer) = 0; + virtual void decode(Buffer& buffer) = 0; + }; + + class StringValue : public virtual Value{ + string value; + + public: + inline StringValue(const string& v) : value(v){} + inline StringValue(){} + inline string getValue(){ return value; } + ~StringValue(){} + inline virtual u_int32_t size() const { return 4 + value.length(); } + inline virtual char getType() const { return 'S'; } + virtual void encode(Buffer& buffer); + virtual void decode(Buffer& buffer); + }; + + class IntegerValue : public virtual Value{ + int value; + public: + inline IntegerValue(int v) : value(v){} + inline IntegerValue(){} + inline int getValue(){ return value; } + ~IntegerValue(){} + inline virtual u_int32_t size() const { return 4; } + inline virtual char getType() const { return 'I'; } + virtual void encode(Buffer& buffer); + virtual void decode(Buffer& buffer); + }; + + class TimeValue : public virtual Value{ + u_int64_t value; + public: + inline TimeValue(int v) : value(v){} + inline TimeValue(){} + inline u_int64_t getValue(){ return value; } + ~TimeValue(){} + inline virtual u_int32_t size() const { return 8; } + inline virtual char getType() const { return 'T'; } + virtual void encode(Buffer& buffer); + virtual void decode(Buffer& buffer); + }; + + class DecimalValue : public virtual Value{ + u_int8_t decimals; + u_int32_t value; + public: + inline DecimalValue(int v) : value(v){} + inline DecimalValue(){} + ~DecimalValue(){} + inline virtual u_int32_t size() const { return 5; } + inline virtual char getType() const { return 'D'; } + virtual void encode(Buffer& buffer); + virtual void decode(Buffer& buffer); + }; + + class FieldTableValue : public virtual Value{ + FieldTable value; + public: + inline FieldTableValue(const FieldTable& v) : value(v){} + inline FieldTableValue(){} + inline FieldTable getValue(){ return value; } + ~FieldTableValue(){} + inline virtual u_int32_t size() const { return 4 + value.size(); } + inline virtual char getType() const { return 'F'; } + virtual void encode(Buffer& buffer); + virtual void decode(Buffer& buffer); + }; +} +} + + +#endif diff --git a/cpp/common/framing/inc/amqp_framing.h b/cpp/common/framing/inc/amqp_framing.h new file mode 100644 index 0000000000..adb0045ee5 --- /dev/null +++ b/cpp/common/framing/inc/amqp_framing.h @@ -0,0 +1,31 @@ +/* + * + * 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 "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 "amqp_methods.h" +#include "InputHandler.h" +#include "OutputHandler.h" +#include "InitiationHandler.h" +#include "ProtocolInitiation.h" +#include "BasicHeaderProperties.h" diff --git a/cpp/common/framing/inc/amqp_types.h b/cpp/common/framing/inc/amqp_types.h new file mode 100644 index 0000000000..6f8ef0862a --- /dev/null +++ b/cpp/common/framing/inc/amqp_types.h @@ -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 <string> +#ifdef _WINDOWS +#include "windows.h" +typedef unsigned char u_int8_t; +typedef unsigned short u_int16_t; +typedef unsigned int u_int32_t; +typedef unsigned __int64 u_int64_t; +#endif +#ifndef _WINDOWS +#include "sys/types.h" +#endif + +#ifndef AMQP_TYPES_H +#define AMQP_TYPES_H + + +typedef std::string string; + +#endif diff --git a/cpp/common/framing/src/AMQContentBody.cpp b/cpp/common/framing/src/AMQContentBody.cpp new file mode 100644 index 0000000000..c8aadc8108 --- /dev/null +++ b/cpp/common/framing/src/AMQContentBody.cpp @@ -0,0 +1,35 @@ +/* + * + * 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 "AMQContentBody.h" + +qpid::framing::AMQContentBody::AMQContentBody(){ +} + +qpid::framing::AMQContentBody::AMQContentBody(string& _data) : data(_data){ +} + +u_int32_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, u_int32_t size){ + buffer.getRawData(data, size); +} + diff --git a/cpp/common/framing/src/AMQFrame.cpp b/cpp/common/framing/src/AMQFrame.cpp new file mode 100644 index 0000000000..70f71010ff --- /dev/null +++ b/cpp/common/framing/src/AMQFrame.cpp @@ -0,0 +1,147 @@ +/* + * + * 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 "AMQFrame.h" +#include "QpidError.h" + +using namespace qpid::framing; + +AMQFrame::AMQFrame(){} + +AMQFrame::AMQFrame(u_int16_t _channel, AMQBody* _body) : channel(_channel), body(_body){} + +AMQFrame::AMQFrame(u_int16_t _channel, AMQBody::shared_ptr& _body) : channel(_channel), body(_body){} + +AMQFrame::~AMQFrame(){ +} + +u_int16_t AMQFrame::getChannel(){ + return channel; +} + +AMQBody::shared_ptr& AMQFrame::getBody(){ + return body; +} + +void AMQFrame::encode(Buffer& buffer) +{ + buffer.putOctet(body->type()); + buffer.putShort(channel); + buffer.putLong(body->size()); + body->encode(buffer); + buffer.putOctet(0xCE); +} + +AMQBody::shared_ptr createMethodBody(Buffer& buffer){ + u_int16_t classId = buffer.getShort(); + u_int16_t methodId = buffer.getShort(); + AMQBody::shared_ptr body(createAMQMethodBody(classId, methodId)); + return body; +} + +u_int32_t AMQFrame::size() const{ + if(!body.get()) THROW_QPID_ERROR(INTERNAL_ERROR, "Attempt to get size of frame with no body set!"); + return 1/*type*/ + 2/*channel*/ + 4/*body size*/ + body->size() + 1/*0xCE*/; +} + +bool AMQFrame::decode(Buffer& buffer) +{ + if(buffer.available() < 7) return false; + buffer.record(); + u_int8_t type = buffer.getOctet(); + channel = buffer.getShort(); + u_int32_t size = buffer.getLong(); + if(buffer.available() < size + 1){ + buffer.restore(); + return false; + } + switch(type) + { + case METHOD_BODY: + body = createMethodBody(buffer); + break; + case HEADER_BODY: + body = AMQBody::shared_ptr(new AMQHeaderBody()); + break; + case CONTENT_BODY: + body = AMQBody::shared_ptr(new AMQContentBody()); + break; + case HEARTBEAT_BODY: + body = AMQBody::shared_ptr(new AMQHeartbeatBody()); + break; + default: + string msg("Unknown body type: "); + msg += type; + THROW_QPID_ERROR(FRAMING_ERROR, msg); + } + body->decode(buffer, size); + u_int8_t end = buffer.getOctet(); + if(end != 0xCE) THROW_QPID_ERROR(FRAMING_ERROR, "Frame end not found"); + return true; +} + +u_int32_t AMQFrame::decodeHead(Buffer& buffer){ + type = buffer.getOctet(); + channel = buffer.getShort(); + return buffer.getLong(); +} + +void AMQFrame::decodeBody(Buffer& buffer, uint32_t size) +{ + switch(type) + { + case METHOD_BODY: + body = createMethodBody(buffer); + break; + case HEADER_BODY: + body = AMQBody::shared_ptr(new AMQHeaderBody()); + break; + case CONTENT_BODY: + body = AMQBody::shared_ptr(new AMQContentBody()); + break; + case HEARTBEAT_BODY: + body = AMQBody::shared_ptr(new AMQHeartbeatBody()); + break; + default: + string msg("Unknown body type: "); + msg += type; + THROW_QPID_ERROR(FRAMING_ERROR, msg); + } + body->decode(buffer, size); +} + +std::ostream& qpid::framing::operator<<(std::ostream& out, const AMQFrame& t){ + out << "Frame[channel=" << t.channel << "; "; + if(t.body.get() == 0){ + out << "empty"; + }else if(t.body->type() == METHOD_BODY){ + (dynamic_cast<AMQMethodBody*>(t.body.get()))->print(out); + }else if(t.body->type() == HEADER_BODY){ + out << "header, content_size=" << + (dynamic_cast<AMQHeaderBody*>(t.body.get()))->getContentSize() + << " (" << t.body->size() << " bytes)"; + }else if(t.body->type() == CONTENT_BODY){ + out << "content (" << t.body->size() << " bytes)"; + }else if(t.body->type() == HEARTBEAT_BODY){ + out << "heartbeat"; + }else{ + out << "unknown type, " << t.body->type(); + } + out << "]"; + return out; +} + diff --git a/cpp/common/framing/src/AMQHeaderBody.cpp b/cpp/common/framing/src/AMQHeaderBody.cpp new file mode 100644 index 0000000000..4bf1626a8a --- /dev/null +++ b/cpp/common/framing/src/AMQHeaderBody.cpp @@ -0,0 +1,60 @@ +/* + * + * 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 "AMQHeaderBody.h" +#include "QpidError.h" +#include "BasicHeaderProperties.h" + +qpid::framing::AMQHeaderBody::AMQHeaderBody(int classId) : weight(0), contentSize(0){ + createProperties(classId); +} + +qpid::framing::AMQHeaderBody::AMQHeaderBody() : properties(0), weight(0), contentSize(0){ +} + +qpid::framing::AMQHeaderBody::~AMQHeaderBody(){ + delete properties; +} + +u_int32_t qpid::framing::AMQHeaderBody::size() const{ + return 12 + properties->size(); +} + +void qpid::framing::AMQHeaderBody::encode(Buffer& buffer) const { + buffer.putShort(properties->classId()); + buffer.putShort(weight); + buffer.putLongLong(contentSize); + properties->encode(buffer); +} + +void qpid::framing::AMQHeaderBody::decode(Buffer& buffer, u_int32_t size){ + u_int16_t classId = buffer.getShort(); + weight = buffer.getShort(); + contentSize = buffer.getLongLong(); + createProperties(classId); + properties->decode(buffer, size - 12); +} + +void qpid::framing::AMQHeaderBody::createProperties(int classId){ + switch(classId){ + case BASIC: + properties = new qpid::framing::BasicHeaderProperties(); + break; + default: + THROW_QPID_ERROR(FRAMING_ERROR, "Unknown header class"); + } +} diff --git a/cpp/common/framing/src/AMQMethodBody.cpp b/cpp/common/framing/src/AMQMethodBody.cpp new file mode 100644 index 0000000000..73862bb2bf --- /dev/null +++ b/cpp/common/framing/src/AMQMethodBody.cpp @@ -0,0 +1,43 @@ +/* + * + * 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 "AMQMethodBody.h" +#include "QpidError.h" + +void qpid::framing::AMQMethodBody::encode(Buffer& buffer) const{ + buffer.putShort(amqpClassId()); + buffer.putShort(amqpMethodId()); + encodeContent(buffer); +} + +void qpid::framing::AMQMethodBody::decode(Buffer& buffer, u_int32_t size){ + decodeContent(buffer); +} + +bool qpid::framing::AMQMethodBody::match(AMQMethodBody* other) const{ + return other != 0 && other->amqpClassId() == amqpClassId() && other->amqpMethodId() == amqpMethodId(); +} + +void qpid::framing::AMQMethodBody::invoke(AMQP_ServerOperations& target, u_int16_t channel){ + THROW_QPID_ERROR(PROTOCOL_ERROR, "Method not supported by AMQP Server."); +} + + +std::ostream& qpid::framing::operator<<(std::ostream& out, const AMQMethodBody& m){ + m.print(out); + return out; +} diff --git a/cpp/common/framing/src/BasicHeaderProperties.cpp b/cpp/common/framing/src/BasicHeaderProperties.cpp new file mode 100644 index 0000000000..4219d33a8f --- /dev/null +++ b/cpp/common/framing/src/BasicHeaderProperties.cpp @@ -0,0 +1,102 @@ +/* + * + * 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 "BasicHeaderProperties.h" + +//TODO: This could be easily generated from the spec + +qpid::framing::BasicHeaderProperties::BasicHeaderProperties() : deliveryMode(0), priority(0), timestamp(0){} +qpid::framing::BasicHeaderProperties::~BasicHeaderProperties(){} + +u_int32_t qpid::framing::BasicHeaderProperties::size() const{ + u_int32_t size = 2;//flags + if(contentType.length() > 0) size += contentType.length() + 1; + if(contentEncoding.length() > 0) size += contentEncoding.length() + 1; + if(headers.count() > 0) size += headers.size(); + if(deliveryMode != 0) size += 1; + if(priority != 0) size += 1; + if(correlationId.length() > 0) size += correlationId.length() + 1; + if(replyTo.length() > 0) size += replyTo.length() + 1; + if(expiration.length() > 0) size += expiration.length() + 1; + if(messageId.length() > 0) size += messageId.length() + 1; + if(timestamp != 0) size += 8; + if(type.length() > 0) size += type.length() + 1; + if(userId.length() > 0) size += userId.length() + 1; + if(appId.length() > 0) size += appId.length() + 1; + if(clusterId.length() > 0) size += clusterId.length() + 1; + + return size; +} + +void qpid::framing::BasicHeaderProperties::encode(qpid::framing::Buffer& buffer) const{ + u_int16_t flags = getFlags(); + buffer.putShort(flags); + + if(contentType.length() > 0) buffer.putShortString(contentType); + if(contentEncoding.length() > 0) buffer.putShortString(contentEncoding); + if(headers.count() > 0) buffer.putFieldTable(headers); + if(deliveryMode != 0) buffer.putOctet(deliveryMode); + if(priority != 0) buffer.putOctet(priority); + if(correlationId.length() > 0) buffer.putShortString(correlationId); + if(replyTo.length() > 0) buffer.putShortString(replyTo); + if(expiration.length() > 0) buffer.putShortString(expiration); + if(messageId.length() > 0) buffer.putShortString(messageId); + if(timestamp != 0) buffer.putLongLong(timestamp);; + if(type.length() > 0) buffer.putShortString(type); + if(userId.length() > 0) buffer.putShortString(userId); + if(appId.length() > 0) buffer.putShortString(appId); + if(clusterId.length() > 0) buffer.putShortString(clusterId); +} + +void qpid::framing::BasicHeaderProperties::decode(qpid::framing::Buffer& buffer, u_int32_t size){ + u_int16_t flags = buffer.getShort(); + int shift = 15; + if(flags & (1 << 15)) buffer.getShortString(contentType); + if(flags & (1 << 14)) buffer.getShortString(contentEncoding); + if(flags & (1 << 13)) buffer.getFieldTable(headers); + if(flags & (1 << 12)) deliveryMode = buffer.getOctet(); + if(flags & (1 << 11)) priority = buffer.getOctet(); + if(flags & (1 << 10)) buffer.getShortString(correlationId); + if(flags & (1 << 9)) buffer.getShortString(replyTo); + if(flags & (1 << 8)) buffer.getShortString(expiration); + if(flags & (1 << 7)) buffer.getShortString(messageId); + if(flags & (1 << 6)) timestamp = buffer.getLongLong(); + if(flags & (1 << 5)) buffer.getShortString(type); + if(flags & (1 << 4)) buffer.getShortString(userId); + if(flags & (1 << 3)) buffer.getShortString(appId); + if(flags & (1 << 2)) buffer.getShortString(clusterId); +} + +u_int16_t qpid::framing::BasicHeaderProperties::getFlags() const{ + u_int16_t flags(0); + int shift = 15; + if(contentType.length() > 0) flags |= (1 << 15); + if(contentEncoding.length() > 0) flags |= (1 << 14); + if(headers.count() > 0) flags |= (1 << 13); + if(deliveryMode != 0) flags |= (1 << 12); + if(priority != 0) flags |= (1 << 11); + if(correlationId.length() > 0) flags |= (1 << 10); + if(replyTo.length() > 0) flags |= (1 << 9); + if(expiration.length() > 0) flags |= (1 << 8); + if(messageId.length() > 0) flags |= (1 << 7); + if(timestamp != 0) flags |= (1 << 6); + if(type.length() > 0) flags |= (1 << 5); + if(userId.length() > 0) flags |= (1 << 4); + if(appId.length() > 0) flags |= (1 << 3); + if(clusterId.length() > 0) flags |= (1 << 2); + return flags; +} diff --git a/cpp/common/framing/src/BodyHandler.cpp b/cpp/common/framing/src/BodyHandler.cpp new file mode 100644 index 0000000000..4e4e2e02f7 --- /dev/null +++ b/cpp/common/framing/src/BodyHandler.cpp @@ -0,0 +1,49 @@ +/* + * + * 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.h" +#include "BodyHandler.h" + +using namespace qpid::framing; +using namespace std::tr1; + +void BodyHandler::handleBody(AMQBody::shared_ptr& body){ + + switch(body->type()) + { + + case METHOD_BODY: + handleMethod(dynamic_pointer_cast<AMQMethodBody, AMQBody>(body)); + break; + + case HEADER_BODY: + handleHeader(dynamic_pointer_cast<AMQHeaderBody, AMQBody>(body)); + break; + + case CONTENT_BODY: + handleContent(dynamic_pointer_cast<AMQContentBody, AMQBody>(body)); + break; + + case HEARTBEAT_BODY: + handleHeartbeat(dynamic_pointer_cast<AMQHeartbeatBody, AMQBody>(body)); + break; + + default: + throw UnknownBodyType(body->type()); + } + +} diff --git a/cpp/common/framing/src/Buffer.cpp b/cpp/common/framing/src/Buffer.cpp new file mode 100644 index 0000000000..5264491980 --- /dev/null +++ b/cpp/common/framing/src/Buffer.cpp @@ -0,0 +1,167 @@ +/* + * + * 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 "Buffer.h" + +qpid::framing::Buffer::Buffer(int _size) : size(_size), position(0), limit(_size){ + data = new char[size]; +} + +qpid::framing::Buffer::~Buffer(){ + delete[] data; +} + +void qpid::framing::Buffer::flip(){ + limit = position; + position = 0; +} + +void qpid::framing::Buffer::clear(){ + limit = size; + position = 0; +} + +void qpid::framing::Buffer::compact(){ + int p = limit - position; + //copy p chars from position to 0 + memmove(data, data + position, p); + limit = size; + position = p; +} + +void qpid::framing::Buffer::record(){ + r_position = position; + r_limit = limit; +} + +void qpid::framing::Buffer::restore(){ + position = r_position; + limit = r_limit; +} + +int qpid::framing::Buffer::available(){ + return limit - position; +} + +char* qpid::framing::Buffer::start(){ + return data + position; +} + +void qpid::framing::Buffer::move(int bytes){ + position += bytes; +} + +void qpid::framing::Buffer::putOctet(u_int8_t i){ + data[position++] = i; +} + +void qpid::framing::Buffer::putShort(u_int16_t i){ + u_int16_t b = i; + data[position++] = (u_int8_t) (0xFF & (b >> 8)); + data[position++] = (u_int8_t) (0xFF & b); +} + +void qpid::framing::Buffer::putLong(u_int32_t i){ + u_int32_t b = i; + data[position++] = (u_int8_t) (0xFF & (b >> 24)); + data[position++] = (u_int8_t) (0xFF & (b >> 16)); + data[position++] = (u_int8_t) (0xFF & (b >> 8)); + data[position++] = (u_int8_t) (0xFF & b); +} + +void qpid::framing::Buffer::putLongLong(u_int64_t i){ + u_int32_t hi = i >> 32; + u_int32_t lo = i; + putLong(hi); + putLong(lo); +} + +u_int8_t qpid::framing::Buffer::getOctet(){ + return (u_int8_t) data[position++]; +} + +u_int16_t qpid::framing::Buffer::getShort(){ + u_int16_t hi = (unsigned char) data[position++]; + hi = hi << 8; + hi |= (unsigned char) data[position++]; + return hi; +} + +u_int32_t qpid::framing::Buffer::getLong(){ + u_int32_t a = (unsigned char) data[position++]; + u_int32_t b = (unsigned char) data[position++]; + u_int32_t c = (unsigned char) data[position++]; + u_int32_t d = (unsigned char) data[position++]; + a = a << 24; + a |= b << 16; + a |= c << 8; + a |= d; + return a; +} + +u_int64_t qpid::framing::Buffer::getLongLong(){ + u_int64_t hi = getLong(); + u_int64_t lo = getLong(); + hi = hi << 32; + return hi | lo; +} + + +void qpid::framing::Buffer::putShortString(const string& s){ + u_int8_t size = s.length(); + putOctet(size); + s.copy(data + position, size); + position += size; +} + +void qpid::framing::Buffer::putLongString(const string& s){ + u_int32_t size = s.length(); + putLong(size); + s.copy(data + position, size); + position += size; +} + +void qpid::framing::Buffer::getShortString(string& s){ + u_int8_t size = getOctet(); + s.assign(data + position, size); + position += size; +} + +void qpid::framing::Buffer::getLongString(string& s){ + u_int32_t size = getLong(); + s.assign(data + position, size); + position += size; +} + +void qpid::framing::Buffer::putFieldTable(const FieldTable& t){ + t.encode(*this); +} + +void qpid::framing::Buffer::getFieldTable(FieldTable& t){ + t.decode(*this); +} + +void qpid::framing::Buffer::putRawData(const string& s){ + u_int32_t size = s.length(); + s.copy(data + position, size); + position += size; +} + +void qpid::framing::Buffer::getRawData(string& s, u_int32_t size){ + s.assign(data + position, size); + position += size; +} diff --git a/cpp/common/framing/src/FieldTable.cpp b/cpp/common/framing/src/FieldTable.cpp new file mode 100644 index 0000000000..048cefa83c --- /dev/null +++ b/cpp/common/framing/src/FieldTable.cpp @@ -0,0 +1,127 @@ +/* + * + * 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 "FieldTable.h" +#include "NamedValue.h" +#include "QpidError.h" +#include "Buffer.h" +#include "Value.h" + +qpid::framing::FieldTable::~FieldTable(){ + int count(values.size()); + for(int i = 0; i < count; i++){ + delete values[i]; + } +} + +u_int32_t qpid::framing::FieldTable::size() const { + u_int32_t size(4); + int count(values.size()); + for(int i = 0; i < count; i++){ + size += values[i]->size(); + } + return size; +} + +int qpid::framing::FieldTable::count() const { + return values.size(); +} + +std::ostream& qpid::framing::operator<<(std::ostream& out, const FieldTable& t){ + out << "field_table{}"; + return out; +} + +void qpid::framing::FieldTable::setString(const std::string& name, const std::string& value){ + setValue(name, new StringValue(value)); +} + +void qpid::framing::FieldTable::setInt(const std::string& name, int value){ + setValue(name, new IntegerValue(value)); +} + +void qpid::framing::FieldTable::setTimestamp(const std::string& name, u_int64_t value){ + setValue(name, new TimeValue(value)); +} + +void qpid::framing::FieldTable::setTable(const std::string& name, const FieldTable& value){ + setValue(name, new FieldTableValue(value)); +} + +std::string qpid::framing::FieldTable::getString(const std::string& name){ + StringValue* val = dynamic_cast<StringValue*>(getValue(name)); + return (val == 0 ? "" : val->getValue()); +} + +int qpid::framing::FieldTable::getInt(const std::string& name){ + IntegerValue* val = dynamic_cast<IntegerValue*>(getValue(name)); + return (val == 0 ? 0 : val->getValue()); +} + +u_int64_t qpid::framing::FieldTable::getTimestamp(const std::string& name){ + TimeValue* val = dynamic_cast<TimeValue*>(getValue(name)); + return (val == 0 ? 0 : val->getValue()); +} + +void qpid::framing::FieldTable::getTable(const std::string& name, FieldTable& value){ + FieldTableValue* val = dynamic_cast<FieldTableValue*>(getValue(name)); + if(val != 0) value = val->getValue(); +} + +qpid::framing::NamedValue* qpid::framing::FieldTable::find(const std::string& name) const{ + int count(values.size()); + for(int i = 0; i < count; i++){ + if(values[i]->getName() == name) return values[i]; + } + return 0; +} + +qpid::framing::Value* qpid::framing::FieldTable::getValue(const std::string& name) const{ + NamedValue* val = find(name); + return val == 0 ? 0 : val->getValue(); +} + +void qpid::framing::FieldTable::setValue(const std::string& name, Value* value){ + NamedValue* val = find(name); + if(val == 0){ + val = new NamedValue(name, value); + values.push_back(val); + }else{ + Value* old = val->getValue(); + if(old != 0) delete old; + val->setValue(value); + } +} + +void qpid::framing::FieldTable::encode(Buffer& buffer) const{ + buffer.putLong(size() - 4); + int count(values.size()); + for(int i = 0; i < count; i++){ + values[i]->encode(buffer); + } +} + +void qpid::framing::FieldTable::decode(Buffer& buffer){ + u_int32_t size = buffer.getLong(); + int leftover = buffer.available() - size; + + while(buffer.available() > leftover){ + NamedValue* value = new NamedValue(); + value->decode(buffer); + values.push_back(value); + } +} diff --git a/cpp/common/framing/src/NamedValue.cpp b/cpp/common/framing/src/NamedValue.cpp new file mode 100644 index 0000000000..e80aea433c --- /dev/null +++ b/cpp/common/framing/src/NamedValue.cpp @@ -0,0 +1,67 @@ +/* + * + * 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 "NamedValue.h" +#include "QpidError.h" +#include "Buffer.h" +#include "FieldTable.h" + +qpid::framing::NamedValue::NamedValue() : value(0){} + +qpid::framing::NamedValue::NamedValue(const string& n, Value* v) : name(n), value(v){} + +qpid::framing::NamedValue::~NamedValue(){ + if(value != 0){ + delete value; + } +} + +u_int32_t qpid::framing::NamedValue::size() const{ + return value ? 1/*size of name*/ + name.length() + 1/*type char*/ + value->size() : 0; +} + +void qpid::framing::NamedValue::encode(Buffer& buffer){ + buffer.putShortString(name); + u_int8_t type = value->getType(); + buffer.putOctet(type); + value->encode(buffer); +} + +void qpid::framing::NamedValue::decode(Buffer& buffer){ + buffer.getShortString(name); + u_int8_t type = buffer.getOctet(); + switch(type){ + case 'S': + value = new StringValue(); + break; + case 'I': + value = new IntegerValue(); + break; + case 'D': + value = new DecimalValue(); + break; + case 'T': + value = new TimeValue(); + break; + case 'F': + value = new FieldTableValue(); + break; + default: + THROW_QPID_ERROR(FRAMING_ERROR, "Unknown field table value type"); + } + value->decode(buffer); +} diff --git a/cpp/common/framing/src/ProtocolInitiation.cpp b/cpp/common/framing/src/ProtocolInitiation.cpp new file mode 100644 index 0000000000..6806d73b55 --- /dev/null +++ b/cpp/common/framing/src/ProtocolInitiation.cpp @@ -0,0 +1,53 @@ +/* + * + * 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 "ProtocolInitiation.h" + +qpid::framing::ProtocolInitiation::ProtocolInitiation(){} + +qpid::framing::ProtocolInitiation::ProtocolInitiation(u_int8_t _major, u_int8_t _minor) : pmajor(_major), pminor(_minor){} + +qpid::framing::ProtocolInitiation::~ProtocolInitiation(){} + +void qpid::framing::ProtocolInitiation::encode(Buffer& buffer){ + buffer.putOctet('A'); + buffer.putOctet('M'); + buffer.putOctet('Q'); + buffer.putOctet('P'); + buffer.putOctet(1);//class + buffer.putOctet(1);//instance + buffer.putOctet(pmajor); + buffer.putOctet(pminor); +} + +bool qpid::framing::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 + pmajor = buffer.getOctet(); + pminor = buffer.getOctet(); + return true; + }else{ + return false; + } +} + +//TODO: this should prbably be generated from the spec at some point to keep the version numbers up to date diff --git a/cpp/common/framing/src/Value.cpp b/cpp/common/framing/src/Value.cpp new file mode 100644 index 0000000000..240b086696 --- /dev/null +++ b/cpp/common/framing/src/Value.cpp @@ -0,0 +1,57 @@ +/* + * + * 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 "Value.h" +#include "Buffer.h" +#include "FieldTable.h" + +void qpid::framing::StringValue::encode(Buffer& buffer){ + buffer.putLongString(value); +} +void qpid::framing::StringValue::decode(Buffer& buffer){ + buffer.getLongString(value); +} + +void qpid::framing::IntegerValue::encode(Buffer& buffer){ + buffer.putLong((u_int32_t) value); +} +void qpid::framing::IntegerValue::decode(Buffer& buffer){ + value = buffer.getLong(); +} + +void qpid::framing::TimeValue::encode(Buffer& buffer){ + buffer.putLongLong(value); +} +void qpid::framing::TimeValue::decode(Buffer& buffer){ + value = buffer.getLongLong(); +} + +void qpid::framing::DecimalValue::encode(Buffer& buffer){ + buffer.putOctet(decimals); + buffer.putLong(value); +} +void qpid::framing::DecimalValue::decode(Buffer& buffer){ + decimals = buffer.getOctet(); + value = buffer.getLong(); +} + +void qpid::framing::FieldTableValue::encode(Buffer& buffer){ + buffer.putFieldTable(value); +} +void qpid::framing::FieldTableValue::decode(Buffer& buffer){ + buffer.getFieldTable(value); +} diff --git a/cpp/common/framing/test/BodyHandlerTest.cpp b/cpp/common/framing/test/BodyHandlerTest.cpp new file mode 100644 index 0000000000..94038d9a6c --- /dev/null +++ b/cpp/common/framing/test/BodyHandlerTest.cpp @@ -0,0 +1,107 @@ +/* + * + * 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 <iostream> +#include "amqp_framing.h" +#include <cppunit/TestCase.h> +#include <cppunit/TextTestRunner.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> + +using namespace qpid::framing; + +class BodyHandlerTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(BodyHandlerTest); + CPPUNIT_TEST(testMethod); + CPPUNIT_TEST(testHeader); + CPPUNIT_TEST(testContent); + CPPUNIT_TEST(testHeartbeat); + CPPUNIT_TEST_SUITE_END(); +private: + + class TestBodyHandler : public BodyHandler{ + AMQMethodBody* const method; + AMQHeaderBody* const header; + AMQContentBody* const content; + AMQHeartbeatBody* const heartbeat; + + public: + + TestBodyHandler(AMQMethodBody* _method) : method(_method), header(0), content(0), heartbeat(0){} + TestBodyHandler(AMQHeaderBody* _header) : method(0), header(_header), content(0), heartbeat(0){} + TestBodyHandler(AMQContentBody* _content) : method(0), header(0), content(_content), heartbeat(0){} + TestBodyHandler(AMQHeartbeatBody* _heartbeat) : method(0), header(0), content(0), heartbeat(_heartbeat){} + + virtual void handleMethod(AMQMethodBody::shared_ptr body){ + CPPUNIT_ASSERT(method); + CPPUNIT_ASSERT_EQUAL(method, body.get()); + } + virtual void handleHeader(AMQHeaderBody::shared_ptr body){ + CPPUNIT_ASSERT(header); + CPPUNIT_ASSERT_EQUAL(header, body.get()); + } + virtual void handleContent(AMQContentBody::shared_ptr body){ + CPPUNIT_ASSERT(content); + CPPUNIT_ASSERT_EQUAL(content, body.get()); + } + virtual void handleHeartbeat(AMQHeartbeatBody::shared_ptr body){ + CPPUNIT_ASSERT(heartbeat); + CPPUNIT_ASSERT_EQUAL(heartbeat, body.get()); + } + }; + +public: + + void testMethod() + { + AMQMethodBody* method = new QueueDeclareBody(); + AMQFrame frame(0, method); + TestBodyHandler handler(method); + handler.handleBody(frame.getBody()); + } + + void testHeader() + { + AMQHeaderBody* header = new AMQHeaderBody(); + AMQFrame frame(0, header); + TestBodyHandler handler(header); + handler.handleBody(frame.getBody()); + } + + void testContent() + { + AMQContentBody* content = new AMQContentBody(); + AMQFrame frame(0, content); + TestBodyHandler handler(content); + handler.handleBody(frame.getBody()); + } + + void testHeartbeat() + { + AMQHeartbeatBody* heartbeat = new AMQHeartbeatBody(); + AMQFrame frame(0, heartbeat); + TestBodyHandler handler(heartbeat); + handler.handleBody(frame.getBody()); + } +}; + + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(BodyHandlerTest); + diff --git a/cpp/common/framing/test/Makefile b/cpp/common/framing/test/Makefile new file mode 100644 index 0000000000..487b8d537b --- /dev/null +++ b/cpp/common/framing/test/Makefile @@ -0,0 +1,21 @@ +# +# 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. +# + +QPID_HOME = ../../../.. +LDLIBS=-lapr-1 -lcppunit $(COMMON_LIB) +INCLUDES=$(TEST_INCLUDES) -I ../generated +include ${QPID_HOME}/cpp/test_plugins.mk + diff --git a/cpp/common/framing/test/field_table_test.cpp b/cpp/common/framing/test/field_table_test.cpp new file mode 100644 index 0000000000..48332e05bc --- /dev/null +++ b/cpp/common/framing/test/field_table_test.cpp @@ -0,0 +1,55 @@ +/* + * + * 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 <iostream> +#include "amqp_framing.h" +#include <cppunit/TestCase.h> +#include <cppunit/TextTestRunner.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> + +using namespace qpid::framing; + +class FieldTableTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(FieldTableTest); + CPPUNIT_TEST(testMe); + CPPUNIT_TEST_SUITE_END(); + + public: + + void testMe() + { + FieldTable ft; + ft.setString("A", "BCDE"); + CPPUNIT_ASSERT_EQUAL(std::string("BCDE"), ft.getString("A")); + + Buffer buffer(100); + buffer.putFieldTable(ft); + buffer.flip(); + FieldTable ft2; + buffer.getFieldTable(ft2); + CPPUNIT_ASSERT_EQUAL(std::string("BCDE"), ft2.getString("A")); + + } +}; + + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(FieldTableTest); + diff --git a/cpp/common/framing/test/framing_test.cpp b/cpp/common/framing/test/framing_test.cpp new file mode 100644 index 0000000000..1978c2cbed --- /dev/null +++ b/cpp/common/framing/test/framing_test.cpp @@ -0,0 +1,147 @@ +/* + * + * 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 "amqp_framing.h" +#include "ConnectionRedirectBody.h" +#include <iostream> +#include <sstream> +#include <cppunit/TestCase.h> +#include <cppunit/TextTestRunner.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> +#include <typeinfo> + +using namespace qpid::framing; + +// TODO aconway 2006-09-12: Why do we need explicit qpid::framing:: below? + +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(ConnectionRedirectBody); + CPPUNIT_TEST(BasicConsumeOkBody); + CPPUNIT_TEST_SUITE_END(); + + private: + Buffer buffer; + + public: + + FramingTest() : buffer(100) {} + + void testBasicQosBody() + { + BasicQosBody in(0xCAFEBABE, 0xABBA, true); + in.encodeContent(buffer); + buffer.flip(); + BasicQosBody out; + out.decodeContent(buffer); + CPPUNIT_ASSERT_EQUAL(tostring(in), tostring(out)); + } + + void testConnectionSecureBody() + { + std::string s = "security credential"; + ConnectionSecureBody in(s); + in.encodeContent(buffer); + buffer.flip(); + ConnectionSecureBody out; + out.decodeContent(buffer); + CPPUNIT_ASSERT_EQUAL(tostring(in), tostring(out)); + } + + void testConnectionRedirectBody() + { + std::string a = "hostA"; + std::string b = "hostB"; + qpid::framing::ConnectionRedirectBody in(a, b); + in.encodeContent(buffer); + buffer.flip(); + qpid::framing::ConnectionRedirectBody out; + out.decodeContent(buffer); + CPPUNIT_ASSERT_EQUAL(tostring(in), tostring(out)); + } + + void testAccessRequestBody() + { + std::string s = "text"; + AccessRequestBody in(s, true, false, true, false, true); + in.encodeContent(buffer); + buffer.flip(); + AccessRequestBody out; + out.decodeContent(buffer); + CPPUNIT_ASSERT_EQUAL(tostring(in), tostring(out)); + } + + void testBasicConsumeBody() + { + std::string q = "queue"; + std::string t = "tag"; + BasicConsumeBody in(0, q, t, false, true, false, false); + in.encodeContent(buffer); + buffer.flip(); + BasicConsumeBody out; + out.decodeContent(buffer); + CPPUNIT_ASSERT_EQUAL(tostring(in), tostring(out)); + } + + + void ConnectionRedirectBody() + { + std::string a = "hostA"; + std::string b = "hostB"; + AMQFrame in(999, new qpid::framing::ConnectionRedirectBody(a, b)); + in.encode(buffer); + buffer.flip(); + AMQFrame out; + out.decode(buffer); + CPPUNIT_ASSERT_EQUAL(tostring(in), tostring(out)); + } + + void BasicConsumeOkBody() + { + std::string s = "hostA"; + AMQFrame in(999, new qpid::framing::BasicConsumeOkBody(s)); + in.encode(buffer); + buffer.flip(); + AMQFrame out; + for(int i = 0; i < 5; i++){ + out.decode(buffer); + CPPUNIT_ASSERT_EQUAL(tostring(in), tostring(out)); + } + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(FramingTest); + + + diff --git a/cpp/common/framing/test/header_test.cpp b/cpp/common/framing/test/header_test.cpp new file mode 100644 index 0000000000..0ff6d47d57 --- /dev/null +++ b/cpp/common/framing/test/header_test.cpp @@ -0,0 +1,144 @@ +/* + * + * 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 <iostream> +#include "amqp_framing.h" +#include <cppunit/TestCase.h> +#include <cppunit/TextTestRunner.h> +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/plugin/TestPlugIn.h> + +using namespace qpid::framing; + +class HeaderTest : public CppUnit::TestCase +{ + CPPUNIT_TEST_SUITE(HeaderTest); + CPPUNIT_TEST(testGenericProperties); + CPPUNIT_TEST(testAllSpecificProperties); + CPPUNIT_TEST(testSomeSpecificProperties); + CPPUNIT_TEST_SUITE_END(); + +public: + + // TODO aconway 2006-09-12: Need more detailed tests, + // need tests to assert something! + // + void testGenericProperties() + { + AMQHeaderBody body(BASIC); + dynamic_cast<BasicHeaderProperties*>(body.getProperties())->getHeaders().setString("A", "BCDE"); + Buffer buffer(100); + + body.encode(buffer); + buffer.flip(); + AMQHeaderBody body2; + body2.decode(buffer, body.size()); + BasicHeaderProperties* props = + dynamic_cast<BasicHeaderProperties*>(body2.getProperties()); + CPPUNIT_ASSERT_EQUAL(std::string("BCDE"), + props->getHeaders().getString("A")); + } + + void testAllSpecificProperties(){ + string contentType("text/html"); + string contentEncoding("UTF8"); + u_int8_t deliveryMode(2); + u_int8_t priority(3); + string correlationId("abc"); + string replyTo("no-address"); + string expiration("why is this a string?"); + string messageId("xyz"); + u_int64_t timestamp(0xabcd); + string type("eh?"); + string userId("guest"); + string appId("just testing"); + string clusterId("no clustering required"); + + AMQHeaderBody body(BASIC); + BasicHeaderProperties* properties = + dynamic_cast<BasicHeaderProperties*>(body.getProperties()); + properties->setContentType(contentType); + properties->getHeaders().setString("A", "BCDE"); + properties->setDeliveryMode(deliveryMode); + properties->setPriority(priority); + properties->setCorrelationId(correlationId); + properties->setReplyTo(replyTo); + properties->setExpiration(expiration); + properties->setMessageId(messageId); + properties->setTimestamp(timestamp); + properties->setType(type); + properties->setUserId(userId); + properties->setAppId(appId); + properties->setClusterId(clusterId); + + Buffer buffer(10000); + body.encode(buffer); + buffer.flip(); + AMQHeaderBody temp; + temp.decode(buffer, body.size()); + properties = dynamic_cast<BasicHeaderProperties*>(temp.getProperties()); + + CPPUNIT_ASSERT_EQUAL(contentType, properties->getContentType()); + CPPUNIT_ASSERT_EQUAL(std::string("BCDE"), properties->getHeaders().getString("A")); + CPPUNIT_ASSERT_EQUAL(deliveryMode, properties->getDeliveryMode()); + CPPUNIT_ASSERT_EQUAL(priority, properties->getPriority()); + CPPUNIT_ASSERT_EQUAL(correlationId, properties->getCorrelationId()); + CPPUNIT_ASSERT_EQUAL(replyTo, properties->getReplyTo()); + CPPUNIT_ASSERT_EQUAL(expiration, properties->getExpiration()); + CPPUNIT_ASSERT_EQUAL(messageId, properties->getMessageId()); + CPPUNIT_ASSERT_EQUAL(timestamp, properties->getTimestamp()); + CPPUNIT_ASSERT_EQUAL(type, properties->getType()); + CPPUNIT_ASSERT_EQUAL(userId, properties->getUserId()); + CPPUNIT_ASSERT_EQUAL(appId, properties->getAppId()); + CPPUNIT_ASSERT_EQUAL(clusterId, properties->getClusterId()); + } + + void testSomeSpecificProperties(){ + string contentType("application/octet-stream"); + u_int8_t deliveryMode(5); + u_int8_t priority(6); + string expiration("Z"); + u_int64_t timestamp(0xabe4a34a); + + AMQHeaderBody body(BASIC); + BasicHeaderProperties* properties = + dynamic_cast<BasicHeaderProperties*>(body.getProperties()); + properties->setContentType(contentType); + properties->setDeliveryMode(deliveryMode); + properties->setPriority(priority); + properties->setExpiration(expiration); + properties->setTimestamp(timestamp); + + Buffer buffer(100); + body.encode(buffer); + buffer.flip(); + AMQHeaderBody temp; + temp.decode(buffer, body.size()); + properties = dynamic_cast<BasicHeaderProperties*>(temp.getProperties()); + + CPPUNIT_ASSERT_EQUAL(contentType, properties->getContentType()); + CPPUNIT_ASSERT_EQUAL((int) deliveryMode, (int) properties->getDeliveryMode()); + CPPUNIT_ASSERT_EQUAL((int) priority, (int) properties->getPriority()); + CPPUNIT_ASSERT_EQUAL(expiration, properties->getExpiration()); + CPPUNIT_ASSERT_EQUAL(timestamp, properties->getTimestamp()); + } +}; + +// Make this test suite a plugin. +CPPUNIT_PLUGIN_IMPLEMENT(); +CPPUNIT_TEST_SUITE_REGISTRATION(HeaderTest); + diff --git a/cpp/common/io/Makefile b/cpp/common/io/Makefile new file mode 100644 index 0000000000..e94e802afa --- /dev/null +++ b/cpp/common/io/Makefile @@ -0,0 +1,35 @@ + # + # 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. + # + +QPID_HOME = ../../.. +include ${QPID_HOME}/cpp/options.mk + +# Compiler flags +CXXFLAGS = ${DEBUG} ${OPT} -MMD -I inc -I ../concurrent/inc -I ../error/inc -I ../framing/inc -I ../framing/generated -I ${APR_HOME}/include/apr-1/ + +SOURCES := $(wildcard src/*.cpp) +OBJECTS := $(subst .cpp,.o,$(SOURCES)) +DEPS := $(subst .cpp,.d,$(SOURCES)) + +.PHONY: all clean + +all: ${OBJECTS} + +-include $(DEPS) + +clean : + -@rm -f ${OBJECTS} src/*.d + diff --git a/cpp/common/io/inc/APRConnector.h b/cpp/common/io/inc/APRConnector.h new file mode 100644 index 0000000000..c292c4d7e0 --- /dev/null +++ b/cpp/common/io/inc/APRConnector.h @@ -0,0 +1,95 @@ +/* + * + * 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. + * + */ +#ifndef _APRConnector_ +#define _APRConnector_ + +#include "apr_network_io.h" +#include "apr_time.h" + +#include "InputHandler.h" +#include "OutputHandler.h" +#include "InitiationHandler.h" +#include "ProtocolInitiation.h" +#include "ShutdownHandler.h" +#include "Thread.h" +#include "ThreadFactory.h" +#include "Connector.h" +#include "APRMonitor.h" + +namespace qpid { +namespace io { + + class APRConnector : public virtual qpid::framing::OutputHandler, + public virtual Connector, + private virtual qpid::concurrent::Runnable + { + const bool debug; + const int receive_buffer_size; + const int send_buffer_size; + + bool closed; + + apr_time_t lastIn; + apr_time_t lastOut; + apr_interval_time_t timeout; + u_int32_t idleIn; + u_int32_t idleOut; + + TimeoutHandler* timeoutHandler; + ShutdownHandler* shutdownHandler; + qpid::framing::InputHandler* input; + qpid::framing::InitiationHandler* initialiser; + qpid::framing::OutputHandler* output; + + qpid::framing::Buffer inbuf; + qpid::framing::Buffer outbuf; + + qpid::concurrent::APRMonitor* writeLock; + qpid::concurrent::ThreadFactory* threadFactory; + qpid::concurrent::Thread* receiver; + + apr_pool_t* pool; + apr_socket_t* socket; + + void checkIdle(apr_status_t status); + void writeBlock(qpid::framing::AMQDataBlock* data); + void writeToSocket(char* data, int available); + void setSocketTimeout(); + + void run(); + + public: + APRConnector(bool debug = false, u_int32_t buffer_size = 1024); + virtual ~APRConnector(); + virtual void connect(const std::string& host, int port); + virtual void init(qpid::framing::ProtocolInitiation* header); + virtual void close(); + virtual void setInputHandler(qpid::framing::InputHandler* handler); + virtual void setTimeoutHandler(TimeoutHandler* handler); + virtual void setShutdownHandler(ShutdownHandler* handler); + virtual qpid::framing::OutputHandler* getOutputHandler(); + virtual void send(qpid::framing::AMQFrame* frame); + virtual void setReadTimeout(u_int16_t timeout); + virtual void setWriteTimeout(u_int16_t timeout); + }; + +} +} + + +#endif diff --git a/cpp/common/io/inc/APRIOProcessor.h b/cpp/common/io/inc/APRIOProcessor.h new file mode 100644 index 0000000000..de0d67a9c4 --- /dev/null +++ b/cpp/common/io/inc/APRIOProcessor.h @@ -0,0 +1,86 @@ +/* + * + * 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. + * + */ +#ifndef _APRIOProcessor_ +#define _APRIOProcessor_ + +#include "apr_poll.h" +#include <queue> +#include <iostream> +#include "APRMonitor.h" +#include "APRThread.h" +#include "IOSession.h" +#include "Runnable.h" + +namespace qpid { +namespace io { + + /** + * Manages non-blocking io through the APR polling + * routines. Interacts with the actual io tasks to be performed + * through the IOSession interface, an implementing instance of + * which must be set as the client_data of the apr_pollfd_t + * structures registered. + */ + class APRIOProcessor : private virtual qpid::concurrent::Runnable + { + const int size; + const apr_interval_time_t timeout; + apr_pollset_t* pollset; + int count; + qpid::concurrent::APRThread thread; + qpid::concurrent::APRMonitor lock; + volatile bool stopped; + + void poll(); + virtual void run(); + + public: + APRIOProcessor(apr_pool_t* pool, int size, int timeout); + /** + * Add the fd to the poll set. Relies on the client_data being + * an instance implementing IOSession, through which the write + * and read operations will be performed when readiness is + * indicated by the poll response. + */ + void add(apr_pollfd_t* const fd); + /** + * Remove the fd from the poll set. + */ + void remove(apr_pollfd_t* const fd); + /** + * Indicates whether the capacity of this processor has been + * reached (or whether it can still handle further fd's). + */ + bool full(); + /** + * Indicates whether there are any fd's registered. + */ + bool empty(); + /** + * Stop processing. + */ + void stop(); + + ~APRIOProcessor(); + }; + +} +} + + +#endif diff --git a/cpp/common/io/inc/APRSocket.h b/cpp/common/io/inc/APRSocket.h new file mode 100644 index 0000000000..610cf0e175 --- /dev/null +++ b/cpp/common/io/inc/APRSocket.h @@ -0,0 +1,45 @@ +/* + * + * 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. + * + */ +#ifndef _APRSocket_ +#define _APRSocket_ + +#include "apr_network_io.h" +#include "Buffer.h" + +namespace qpid { +namespace io { + + class APRSocket + { + apr_socket_t* const socket; + volatile bool closed; + public: + APRSocket(apr_socket_t* socket); + void read(qpid::framing::Buffer& b); + void write(qpid::framing::Buffer& b); + void close(); + bool isOpen(); + u_int8_t read(); + ~APRSocket(); + }; + +} +} + + +#endif diff --git a/cpp/common/io/inc/Acceptor.h b/cpp/common/io/inc/Acceptor.h new file mode 100644 index 0000000000..5c690c546f --- /dev/null +++ b/cpp/common/io/inc/Acceptor.h @@ -0,0 +1,38 @@ +/* + * + * 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. + * + */ +#ifndef _Acceptor_ +#define _Acceptor_ + +#include "SessionHandlerFactory.h" + + +namespace qpid { +namespace io { + + class Acceptor + { + public: + virtual void bind(int port, SessionHandlerFactory* factory) = 0; + virtual ~Acceptor(){} + }; + +} +} + + +#endif diff --git a/cpp/common/io/inc/BlockingAPRAcceptor.h b/cpp/common/io/inc/BlockingAPRAcceptor.h new file mode 100644 index 0000000000..b77371b02e --- /dev/null +++ b/cpp/common/io/inc/BlockingAPRAcceptor.h @@ -0,0 +1,62 @@ +/* + * + * 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. + * + */ +#ifndef _BlockingAPRAcceptor_ +#define _BlockingAPRAcceptor_ + +#include <vector> +#include "apr_network_io.h" +#include "apr_poll.h" +#include "apr_time.h" + +#include "Acceptor.h" +#include "APRMonitor.h" +#include "BlockingAPRSessionContext.h" +#include "Runnable.h" +#include "SessionContext.h" +#include "SessionHandlerFactory.h" +#include "Thread.h" +#include "ThreadFactory.h" +#include "ThreadPool.h" + +namespace qpid { +namespace io { + + class BlockingAPRAcceptor : public virtual Acceptor + { + typedef std::vector<BlockingAPRSessionContext*>::iterator iterator; + + const bool debug; + apr_pool_t* apr_pool; + qpid::concurrent::ThreadFactory* threadFactory; + std::vector<BlockingAPRSessionContext*> sessions; + apr_socket_t* socket; + const int connectionBacklog; + volatile bool running; + + public: + BlockingAPRAcceptor(bool debug = false, int connectionBacklog = 10); + virtual void bind(int port, SessionHandlerFactory* factory); + virtual ~BlockingAPRAcceptor(); + void closed(BlockingAPRSessionContext* session); + }; + +} +} + + +#endif diff --git a/cpp/common/io/inc/BlockingAPRSessionContext.h b/cpp/common/io/inc/BlockingAPRSessionContext.h new file mode 100644 index 0000000000..038ebd6662 --- /dev/null +++ b/cpp/common/io/inc/BlockingAPRSessionContext.h @@ -0,0 +1,94 @@ +/* + * + * 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. + * + */ +#ifndef _BlockingAPRSessionContext_ +#define _BlockingAPRSessionContext_ + +#include <queue> +#include <vector> + +#include "apr_network_io.h" +#include "apr_time.h" + +#include "AMQFrame.h" +#include "APRMonitor.h" +#include "Buffer.h" +#include "Runnable.h" +#include "SessionContext.h" +#include "SessionHandler.h" +#include "SessionHandlerFactory.h" +#include "ShutdownHandler.h" +#include "Thread.h" +#include "ThreadFactory.h" + +namespace qpid { +namespace io { + + class BlockingAPRAcceptor; + + class BlockingAPRSessionContext : public virtual SessionContext + { + class Reader : public virtual qpid::concurrent::Runnable{ + BlockingAPRSessionContext* parent; + public: + inline Reader(BlockingAPRSessionContext* p) : parent(p){} + inline virtual void run(){ parent->read(); } + inline virtual ~Reader(){} + }; + + class Writer : public virtual qpid::concurrent::Runnable{ + BlockingAPRSessionContext* parent; + public: + inline Writer(BlockingAPRSessionContext* p) : parent(p){} + inline virtual void run(){ parent->write(); } + inline virtual ~Writer(){} + }; + + apr_socket_t* socket; + const bool debug; + SessionHandler* handler; + BlockingAPRAcceptor* acceptor; + std::queue<qpid::framing::AMQFrame*> outframes; + qpid::framing::Buffer inbuf; + qpid::framing::Buffer outbuf; + qpid::concurrent::APRMonitor outlock; + Reader* reader; + Writer* writer; + qpid::concurrent::Thread* rThread; + qpid::concurrent::Thread* wThread; + + volatile bool closed; + + void read(); + void write(); + public: + BlockingAPRSessionContext(apr_socket_t* socket, + qpid::concurrent::ThreadFactory* factory, + BlockingAPRAcceptor* acceptor, + bool debug = false); + ~BlockingAPRSessionContext(); + virtual void send(qpid::framing::AMQFrame* frame); + virtual void close(); + void shutdown(); + void init(SessionHandler* handler); + }; + +} +} + + +#endif diff --git a/cpp/common/io/inc/Connector.h b/cpp/common/io/inc/Connector.h new file mode 100644 index 0000000000..52684329f1 --- /dev/null +++ b/cpp/common/io/inc/Connector.h @@ -0,0 +1,56 @@ +/* + * + * 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. + * + */ +#ifndef _Connector_ +#define _Connector_ + +#include "InputHandler.h" +#include "OutputHandler.h" +#include "InitiationHandler.h" +#include "ProtocolInitiation.h" +#include "ShutdownHandler.h" +#include "TimeoutHandler.h" + +namespace qpid { +namespace io { + + class Connector + { + public: + virtual void connect(const std::string& host, int port) = 0; + virtual void init(qpid::framing::ProtocolInitiation* header) = 0; + virtual void close() = 0; + virtual void setInputHandler(qpid::framing::InputHandler* handler) = 0; + virtual void setTimeoutHandler(TimeoutHandler* handler) = 0; + virtual void setShutdownHandler(ShutdownHandler* handler) = 0; + virtual qpid::framing::OutputHandler* getOutputHandler() = 0; + /** + * Set the timeout for reads, in secs. + */ + virtual void setReadTimeout(u_int16_t timeout) = 0; + /** + * Set the timeout for writes, in secs. + */ + virtual void setWriteTimeout(u_int16_t timeout) = 0; + virtual ~Connector(){} + }; + +} +} + + +#endif diff --git a/cpp/common/io/inc/ConnectorImpl.h b/cpp/common/io/inc/ConnectorImpl.h new file mode 100644 index 0000000000..242f3aed49 --- /dev/null +++ b/cpp/common/io/inc/ConnectorImpl.h @@ -0,0 +1,53 @@ +/* + * + * 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. + * + */ +#ifndef _APRConnectorImpl_ +#define _APRConnectorImpl_ + +#ifdef _USE_APR_IO_ +#include "APRConnector.h" +#else +#include "LConnector.h" +#endif + +namespace qpid { +namespace io { + +#ifdef _USE_APR_IO_ + class ConnectorImpl : public virtual APRConnector + { + + public: + ConnectorImpl(bool debug = false, u_int32_t buffer_size = 1024):APRConnector(debug,buffer_size){}; + virtual ~ConnectorImpl(){}; + }; +#else + class ConnectorImpl : public virtual LConnector + { + + public: + ConnectorImpl(bool debug = false, u_int32_t buffer_size = 1024):LConnector(debug, buffer_size){}; + virtual ~ConnectorImpl(){}; + }; + +#endif + +} +} + + +#endif diff --git a/cpp/common/io/inc/IOSession.h b/cpp/common/io/inc/IOSession.h new file mode 100644 index 0000000000..45e84d29b1 --- /dev/null +++ b/cpp/common/io/inc/IOSession.h @@ -0,0 +1,45 @@ +/* + * + * 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. + * + */ +#ifndef _IOSession_ +#define _IOSession_ + +namespace qpid { +namespace io { + + class IOSession + { + public: + virtual void read() = 0; + virtual void write() = 0; + virtual ~IOSession(){} + }; + + + class IOSessionHolder + { + IOSession* session; + public: + IOSessionHolder(IOSession* _session) : session(_session) {} + void read(){ session->read(); } + void write(){ session->write(); } + }; +} +} + + +#endif diff --git a/cpp/common/io/inc/LConnector.h b/cpp/common/io/inc/LConnector.h new file mode 100644 index 0000000000..59d95a6b57 --- /dev/null +++ b/cpp/common/io/inc/LConnector.h @@ -0,0 +1,48 @@ +/* + * + * 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. + * + */ +#ifndef _LConnector_ +#define _LConnector_ + + +#include "InputHandler.h" +#include "OutputHandler.h" +#include "InitiationHandler.h" +#include "ProtocolInitiation.h" +#include "Thread.h" +#include "ThreadFactory.h" +#include "Connector.h" + +namespace qpid { +namespace io { + + class LConnector : public virtual qpid::framing::OutputHandler, + public virtual Connector, + private virtual qpid::concurrent::Runnable + { + + public: + LConnector(bool debug = false, u_int32_t buffer_size = 1024){}; + virtual ~LConnector(){}; + + }; + +} +} + + +#endif diff --git a/cpp/common/io/inc/LFAcceptor.h b/cpp/common/io/inc/LFAcceptor.h new file mode 100644 index 0000000000..314f811827 --- /dev/null +++ b/cpp/common/io/inc/LFAcceptor.h @@ -0,0 +1,71 @@ +/* + * + * 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. + * + */ +#ifndef _LFAcceptor_ +#define _LFAcceptor_ + +#include <vector> +#include "apr_network_io.h" +#include "apr_poll.h" +#include "apr_time.h" + +#include "Acceptor.h" +#include "APRMonitor.h" +#include "APRThreadFactory.h" +#include "APRThreadPool.h" +#include "LFProcessor.h" +#include "LFSessionContext.h" +#include "Runnable.h" +#include "SessionContext.h" +#include "SessionHandlerFactory.h" +#include "Thread.h" + +namespace qpid { +namespace io { + + class LFAcceptor : public virtual Acceptor + { + class APRPool{ + public: + apr_pool_t* pool; + APRPool(); + ~APRPool(); + }; + + APRPool aprPool; + LFProcessor processor; + + const int max_connections_per_processor; + const bool debug; + const int connectionBacklog; + + volatile bool running; + + public: + LFAcceptor(bool debug = false, + int connectionBacklog = 10, + int worker_threads = 5, + int max_connections_per_processor = 500); + virtual void bind(int port, SessionHandlerFactory* factory); + virtual ~LFAcceptor(); + }; + +} +} + + +#endif diff --git a/cpp/common/io/inc/LFProcessor.h b/cpp/common/io/inc/LFProcessor.h new file mode 100644 index 0000000000..7d99d51943 --- /dev/null +++ b/cpp/common/io/inc/LFProcessor.h @@ -0,0 +1,116 @@ +/* + * + * 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. + * + */ +#ifndef _LFProcessor_ +#define _LFProcessor_ + +#include "apr_poll.h" +#include <iostream> +#include <vector> +#include "APRMonitor.h" +#include "APRThreadFactory.h" +#include "IOSession.h" +#include "Runnable.h" + +namespace qpid { +namespace io { + + class LFSessionContext; + + /** + * This class processes a poll set using the leaders-followers + * pattern for thread synchronization: the leader will poll and on + * the poll returning, it will remove a session, promote a + * follower to leadership, then process the session. + */ + class LFProcessor : private virtual qpid::concurrent::Runnable + { + typedef std::vector<LFSessionContext*>::iterator iterator; + + const int size; + const apr_interval_time_t timeout; + apr_pollset_t* pollset; + int signalledCount; + int current; + const apr_pollfd_t* signalledFDs; + int count; + const int workerCount; + qpid::concurrent::Thread** const workers; + qpid::concurrent::APRMonitor leadLock; + qpid::concurrent::APRMonitor countLock; + qpid::concurrent::APRThreadFactory factory; + std::vector<LFSessionContext*> sessions; + bool hasLeader; + volatile bool stopped; + + const apr_pollfd_t* getNextEvent(); + void waitToLead(); + void relinquishLead(); + void poll(); + virtual void run(); + + public: + LFProcessor(apr_pool_t* pool, int workers, int size, int timeout); + /** + * Add the fd to the poll set. Relies on the client_data being + * an instance of LFSessionContext. + */ + void add(const apr_pollfd_t* const fd); + /** + * Remove the fd from the poll set. + */ + void remove(const apr_pollfd_t* const fd); + /** + * Signal that the fd passed in, already part of the pollset, + * has had its flags altered. + */ + void update(const apr_pollfd_t* const fd); + /** + * Add an fd back to the poll set after deactivation. + */ + void reactivate(const apr_pollfd_t* const fd); + /** + * Temporarily remove the fd from the poll set. Called when processing + * is about to begin. + */ + void deactivate(const apr_pollfd_t* const fd); + /** + * Indicates whether the capacity of this processor has been + * reached (or whether it can still handle further fd's). + */ + bool full(); + /** + * Indicates whether there are any fd's registered. + */ + bool empty(); + /** + * Stop processing. + */ + void stop(); + /** + * Start processing. + */ + void start(); + + ~LFProcessor(); + }; + +} +} + + +#endif diff --git a/cpp/common/io/inc/LFSessionContext.h b/cpp/common/io/inc/LFSessionContext.h new file mode 100644 index 0000000000..ef6a0d07b0 --- /dev/null +++ b/cpp/common/io/inc/LFSessionContext.h @@ -0,0 +1,89 @@ +/* + * + * 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. + * + */ +#ifndef _LFSessionContext_ +#define _LFSessionContext_ + +#include <queue> + +#include "apr_network_io.h" +#include "apr_poll.h" +#include "apr_time.h" + +#include "AMQFrame.h" +#include "APRMonitor.h" +#include "APRSocket.h" +#include "Buffer.h" +#include "IOSession.h" +#include "LFProcessor.h" +#include "SessionContext.h" +#include "SessionHandler.h" + +namespace qpid { +namespace io { + + + class LFSessionContext : public virtual SessionContext, public virtual IOSession + { + const bool debug; + APRSocket socket; + bool initiated; + + qpid::framing::Buffer in; + qpid::framing::Buffer out; + + SessionHandler* handler; + LFProcessor* const processor; + + apr_pollfd_t fd; + + std::queue<qpid::framing::AMQFrame*> framesToWrite; + qpid::concurrent::APRMonitor writeLock; + + bool processing; + bool closing; + + //these are just for debug, as a crude way of detecting concurrent access + volatile unsigned int reading; + volatile unsigned int writing; + + static qpid::concurrent::APRMonitor logLock; + void log(const std::string& desc, qpid::framing::AMQFrame* const frame); + + public: + LFSessionContext(apr_pool_t* pool, apr_socket_t* socket, + LFProcessor* const processor, + bool debug = false); + ~LFSessionContext(); + virtual void send(qpid::framing::AMQFrame* frame); + virtual void close(); + virtual void read(); + virtual void write(); + void init(SessionHandler* handler); + void startProcessing(); + void stopProcessing(); + void handleClose(); + void shutdown(); + inline apr_pollfd_t* const getFd(){ return &fd; } + inline bool isClosed(){ return !socket.isOpen(); } + }; + +} +} + + +#endif diff --git a/cpp/common/io/inc/SessionContext.h b/cpp/common/io/inc/SessionContext.h new file mode 100644 index 0000000000..f223a70daa --- /dev/null +++ b/cpp/common/io/inc/SessionContext.h @@ -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. + * + */ +#ifndef _SessionContext_ +#define _SessionContext_ + +#include "OutputHandler.h" + +namespace qpid { +namespace io { + + class SessionContext : public virtual qpid::framing::OutputHandler + { + public: + virtual void close() = 0; + virtual ~SessionContext(){} + }; + +} +} + + +#endif diff --git a/cpp/common/io/inc/SessionHandler.h b/cpp/common/io/inc/SessionHandler.h new file mode 100644 index 0000000000..21a992ab65 --- /dev/null +++ b/cpp/common/io/inc/SessionHandler.h @@ -0,0 +1,42 @@ +/* + * + * 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. + * + */ +#ifndef _SessionHandler_ +#define _SessionHandler_ + +#include "InputHandler.h" +#include "InitiationHandler.h" +#include "ProtocolInitiation.h" +#include "TimeoutHandler.h" + +namespace qpid { +namespace io { + + class SessionHandler : public virtual qpid::framing::InitiationHandler, + public virtual qpid::framing::InputHandler, + public virtual TimeoutHandler + { + public: + virtual void closed() = 0; + virtual ~SessionHandler(){} + }; + +} +} + + +#endif diff --git a/cpp/common/io/inc/SessionHandlerFactory.h b/cpp/common/io/inc/SessionHandlerFactory.h new file mode 100644 index 0000000000..67d968b72e --- /dev/null +++ b/cpp/common/io/inc/SessionHandlerFactory.h @@ -0,0 +1,38 @@ +/* + * + * 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. + * + */ +#ifndef _SessionHandlerFactory_ +#define _SessionHandlerFactory_ + +#include "SessionContext.h" +#include "SessionHandler.h" + +namespace qpid { +namespace io { + + class SessionHandlerFactory + { + public: + virtual SessionHandler* create(SessionContext* ctxt) = 0; + virtual ~SessionHandlerFactory(){} + }; + +} +} + + +#endif diff --git a/cpp/common/io/inc/SessionManager.h b/cpp/common/io/inc/SessionManager.h new file mode 100644 index 0000000000..30c5208532 --- /dev/null +++ b/cpp/common/io/inc/SessionManager.h @@ -0,0 +1,40 @@ +/* + * + * 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. + * + */ +#ifndef _SessionManager_ +#define _SessionManager_ + +#include "SessionContext.h" +#include "SessionHandler.h" + +namespace qpid { +namespace io { + + class SessionManager + { + public: + virtual SessionHandler* init(SessionContext* ctxt) = 0; + virtual void close(SessionContext* ctxt) = 0; + virtual void updateInterest(SessionContext* ctxt, bool read, bool write) = 0; + virtual ~SessionManager(){} + }; + +} +} + + +#endif diff --git a/cpp/common/io/inc/ShutdownHandler.h b/cpp/common/io/inc/ShutdownHandler.h new file mode 100644 index 0000000000..186d9eeca4 --- /dev/null +++ b/cpp/common/io/inc/ShutdownHandler.h @@ -0,0 +1,34 @@ +/* + * + * 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. + * + */ +#ifndef _ShutdownHandler_ +#define _ShutdownHandler_ + +namespace qpid { +namespace io { + + class ShutdownHandler + { + public: + virtual void shutdown() = 0; + virtual ~ShutdownHandler(){} + }; + +} +} + +#endif diff --git a/cpp/common/io/inc/TimeoutHandler.h b/cpp/common/io/inc/TimeoutHandler.h new file mode 100644 index 0000000000..c92220fd6e --- /dev/null +++ b/cpp/common/io/inc/TimeoutHandler.h @@ -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. + * + */ +#ifndef _TimeoutHandler_ +#define _TimeoutHandler_ + +namespace qpid { +namespace io { + + class TimeoutHandler + { + public: + virtual void idleOut() = 0; + virtual void idleIn() = 0; + virtual ~TimeoutHandler(){} + }; + +} +} + + +#endif diff --git a/cpp/common/io/src/APRConnector.cpp b/cpp/common/io/src/APRConnector.cpp new file mode 100644 index 0000000000..0e022a8c73 --- /dev/null +++ b/cpp/common/io/src/APRConnector.cpp @@ -0,0 +1,198 @@ +/* + * + * 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 <iostream> +#include "APRBase.h" +#include "APRConnector.h" +#include "APRThreadFactory.h" +#include "QpidError.h" + +using namespace qpid::io; +using namespace qpid::concurrent; +using namespace qpid::framing; +using qpid::QpidError; + +APRConnector::APRConnector(bool _debug, u_int32_t buffer_size) : closed(true), debug(_debug), + idleIn(0), idleOut(0), timeout(0), + timeoutHandler(0), + shutdownHandler(0), + lastIn(0), lastOut(0), + receive_buffer_size(buffer_size), + send_buffer_size(buffer_size), + inbuf(receive_buffer_size), + outbuf(send_buffer_size){ + + APRBase::increment(); + + CHECK_APR_SUCCESS(apr_pool_create(&pool, NULL)); + CHECK_APR_SUCCESS(apr_socket_create(&socket, APR_INET, SOCK_STREAM, APR_PROTO_TCP, pool)); + + threadFactory = new APRThreadFactory(); + writeLock = new APRMonitor(); +} + +APRConnector::~APRConnector(){ + delete receiver; + delete writeLock; + delete threadFactory; + apr_pool_destroy(pool); + + APRBase::decrement(); +} + +void APRConnector::connect(const std::string& host, int port){ + apr_sockaddr_t* address; + CHECK_APR_SUCCESS(apr_sockaddr_info_get(&address, host.c_str(), APR_UNSPEC, port, APR_IPV4_ADDR_OK, pool)); + CHECK_APR_SUCCESS(apr_socket_connect(socket, address)); + closed = false; + + receiver = threadFactory->create(this); + receiver->start(); +} + +void APRConnector::init(ProtocolInitiation* header){ + writeBlock(header); + delete header; +} + +void APRConnector::close(){ + closed = true; + CHECK_APR_SUCCESS(apr_socket_close(socket)); + receiver->join(); +} + +void APRConnector::setInputHandler(InputHandler* handler){ + input = handler; +} + +void APRConnector::setShutdownHandler(ShutdownHandler* handler){ + shutdownHandler = handler; +} + +OutputHandler* APRConnector::getOutputHandler(){ + return this; +} + +void APRConnector::send(AMQFrame* frame){ + writeBlock(frame); + if(debug) std::cout << "SENT: " << *frame << std::endl; + delete frame; +} + +void APRConnector::writeBlock(AMQDataBlock* data){ + writeLock->acquire(); + data->encode(outbuf); + + //transfer data to wire + outbuf.flip(); + writeToSocket(outbuf.start(), outbuf.available()); + outbuf.clear(); + writeLock->release(); +} + +void APRConnector::writeToSocket(char* data, int available){ + apr_size_t bytes(available); + apr_size_t written(0); + while(written < available && !closed){ + apr_status_t status = apr_socket_send(socket, data + written, &bytes); + if(status == APR_TIMEUP){ + std::cout << "Write request timed out." << std::endl; + } + if(bytes == 0){ + std::cout << "Write request wrote 0 bytes." << std::endl; + } + lastOut = apr_time_as_msec(apr_time_now()); + written += bytes; + bytes = available - written; + } +} + +void APRConnector::checkIdle(apr_status_t status){ + if(timeoutHandler){ + apr_time_t now = apr_time_as_msec(apr_time_now()); + if(APR_STATUS_IS_TIMEUP(status)){ + if(idleIn && (now - lastIn > idleIn)){ + timeoutHandler->idleIn(); + } + }else if(APR_STATUS_IS_EOF(status)){ + closed = true; + CHECK_APR_SUCCESS(apr_socket_close(socket)); + if(shutdownHandler) shutdownHandler->shutdown(); + }else{ + lastIn = now; + } + if(idleOut && (now - lastOut > idleOut)){ + timeoutHandler->idleOut(); + } + } +} + +void APRConnector::setReadTimeout(u_int16_t t){ + idleIn = t * 1000;//t is in secs + if(idleIn && (!timeout || idleIn < timeout)){ + timeout = idleIn; + setSocketTimeout(); + } + +} + +void APRConnector::setWriteTimeout(u_int16_t t){ + idleOut = t * 1000;//t is in secs + if(idleOut && (!timeout || idleOut < timeout)){ + timeout = idleOut; + setSocketTimeout(); + } +} + +void APRConnector::setSocketTimeout(){ + //interval is in microseconds, timeout in milliseconds + //want the interval to be a bit shorter than the timeout, hence multiply + //by 800 rather than 1000. + apr_interval_time_t interval(timeout * 800); + apr_socket_timeout_set(socket, interval); +} + +void APRConnector::setTimeoutHandler(TimeoutHandler* handler){ + timeoutHandler = handler; +} + +void APRConnector::run(){ + try{ + while(!closed){ + apr_size_t bytes(inbuf.available()); + if(bytes < 1){ + THROW_QPID_ERROR(INTERNAL_ERROR, "Frame exceeds buffer size."); + } + checkIdle(apr_socket_recv(socket, inbuf.start(), &bytes)); + + if(bytes > 0){ + inbuf.move(bytes); + inbuf.flip();//position = 0, limit = total data read + + AMQFrame frame; + while(frame.decode(inbuf)){ + if(debug) std::cout << "RECV: " << frame << std::endl; + input->received(&frame); + } + //need to compact buffer to preserve any 'extra' data + inbuf.compact(); + } + } + }catch(QpidError error){ + std::cout << "Error [" << error.code << "] " << error.msg << " (" << error.file << ":" << error.line << ")" << std::endl; + } +} diff --git a/cpp/common/io/src/APRIOProcessor.cpp b/cpp/common/io/src/APRIOProcessor.cpp new file mode 100644 index 0000000000..d630f2f315 --- /dev/null +++ b/cpp/common/io/src/APRIOProcessor.cpp @@ -0,0 +1,100 @@ +/* + * + * 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 "APRIOProcessor.h" +#include "APRBase.h" +#include "QpidError.h" + +using namespace qpid::io; +using namespace qpid::concurrent; + +APRIOProcessor::APRIOProcessor(apr_pool_t* pool, int _size, int _timeout) : size(_size), + timeout(_timeout), + count(0), + thread(pool, this), + stopped(false){ + + CHECK_APR_SUCCESS(apr_pollset_create(&pollset, size, pool, APR_POLLSET_THREADSAFE)); + thread.start(); +} + +void APRIOProcessor::add(apr_pollfd_t* const fd){ + CHECK_APR_SUCCESS(apr_pollset_add(pollset, fd)); + lock.acquire(); + if(!count++) lock.notify(); + lock.release(); +} + +void APRIOProcessor::remove(apr_pollfd_t* const fd){ + CHECK_APR_SUCCESS(apr_pollset_remove(pollset, fd)); + lock.acquire(); + count--; + lock.release(); +} + +bool APRIOProcessor::full(){ + lock.acquire(); + bool full = count == size; + lock.release(); + return full; +} + +bool APRIOProcessor::empty(){ + lock.acquire(); + bool empty = count == 0; + lock.release(); + return empty; +} + +void APRIOProcessor::poll(){ + try{ + int signalledCount; + const apr_pollfd_t* signalledFDs; + apr_status_t status = apr_pollset_poll(pollset, timeout, &signalledCount, &signalledFDs); + if(status == APR_SUCCESS){ + for(int i = 0; i < signalledCount; i++){ + IOSessionHolder* session = reinterpret_cast<IOSessionHolder*>(signalledFDs[i].client_data); + if(signalledFDs[i].rtnevents & APR_POLLIN) session->read(); + if(signalledFDs[i].rtnevents & APR_POLLOUT) session->write(); + } + } + }catch(qpid::QpidError error){ + std::cout << "Error [" << error.code << "] " << error.msg << " (" << error.file << ":" << error.line << ")" << std::endl; + } + +} + +void APRIOProcessor::run(){ + while(!stopped){ + lock.acquire(); + while(count == 0) lock.wait(); + lock.release(); + poll(); + } +} + +void APRIOProcessor::stop(){ + lock.acquire(); + stopped = true; + lock.notify(); + lock.release(); +} + +APRIOProcessor::~APRIOProcessor(){ + CHECK_APR_SUCCESS(apr_pollset_destroy(pollset)); +} + diff --git a/cpp/common/io/src/APRSocket.cpp b/cpp/common/io/src/APRSocket.cpp new file mode 100644 index 0000000000..32861ea442 --- /dev/null +++ b/cpp/common/io/src/APRSocket.cpp @@ -0,0 +1,76 @@ +/* + * + * 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 "APRSocket.h" + +#include <iostream> + +using namespace qpid::io; +using namespace qpid::framing; +using namespace qpid::concurrent; + +APRSocket::APRSocket(apr_socket_t* _socket) : socket(_socket), closed(false){ + +} + +void APRSocket::read(qpid::framing::Buffer& buffer){ + apr_size_t bytes; + bytes = buffer.available(); + apr_status_t s = apr_socket_recv(socket, buffer.start(), &bytes); + buffer.move(bytes); + if(APR_STATUS_IS_TIMEUP(s)){ + //timed out + }else if(APR_STATUS_IS_EOF(s)){ + close(); + } +} + +void APRSocket::write(qpid::framing::Buffer& buffer){ + apr_size_t bytes; + do{ + bytes = buffer.available(); + apr_status_t s = apr_socket_send(socket, buffer.start(), &bytes); + buffer.move(bytes); + }while(bytes > 0); +} + +void APRSocket::close(){ + if(!closed){ + std::cout << "Closing socket " << socket << "@" << this << std::endl; + CHECK_APR_SUCCESS(apr_socket_close(socket)); + closed = true; + } +} + +bool APRSocket::isOpen(){ + return !closed; +} + +u_int8_t APRSocket::read(){ + char data[1]; + apr_size_t bytes = 1; + apr_status_t s = apr_socket_recv(socket, data, &bytes); + if(APR_STATUS_IS_EOF(s) || bytes == 0){ + return 0; + }else{ + return *data; + } +} + +APRSocket::~APRSocket(){ +} diff --git a/cpp/common/io/src/BlockingAPRAcceptor.cpp b/cpp/common/io/src/BlockingAPRAcceptor.cpp new file mode 100644 index 0000000000..380318bcfa --- /dev/null +++ b/cpp/common/io/src/BlockingAPRAcceptor.cpp @@ -0,0 +1,81 @@ +/* + * + * 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 <iostream> +#include "BlockingAPRAcceptor.h" +#include "APRBase.h" +#include "APRThreadFactory.h" + +using namespace qpid::concurrent; +using namespace qpid::framing; +using namespace qpid::io; + +BlockingAPRAcceptor::BlockingAPRAcceptor(bool _debug, int c) : connectionBacklog(c), + threadFactory(new APRThreadFactory()), + debug(_debug){ + + APRBase::increment(); + CHECK_APR_SUCCESS(apr_pool_create(&apr_pool, NULL)); +} + +void BlockingAPRAcceptor::bind(int port, SessionHandlerFactory* factory){ + apr_sockaddr_t* address; + CHECK_APR_SUCCESS(apr_sockaddr_info_get(&address, APR_ANYADDR, APR_UNSPEC, port, APR_IPV4_ADDR_OK, apr_pool)); + CHECK_APR_SUCCESS(apr_socket_create(&socket, APR_INET, SOCK_STREAM, APR_PROTO_TCP, apr_pool)); + CHECK_APR_SUCCESS(apr_socket_bind(socket, address)); + CHECK_APR_SUCCESS(apr_socket_listen(socket, connectionBacklog)); + running = true; + std::cout << "Listening on port " << port << "..." << std::endl; + while(running){ + apr_socket_t* client; + apr_status_t status = apr_socket_accept(&client, socket, apr_pool); + if(status == APR_SUCCESS){ + //configure socket: + CHECK_APR_SUCCESS(apr_socket_timeout_set(client, 1000000/* i.e. 1 sec*/)); + CHECK_APR_SUCCESS(apr_socket_opt_set(client, APR_TCP_NODELAY, 1)); + CHECK_APR_SUCCESS(apr_socket_opt_set(client, APR_SO_SNDBUF, 32768)); + CHECK_APR_SUCCESS(apr_socket_opt_set(client, APR_SO_RCVBUF, 32768)); + + BlockingAPRSessionContext* session = new BlockingAPRSessionContext(client, threadFactory, this, debug); + session->init(factory->create(session)); + sessions.push_back(session); + }else{ + running = false; + if(status != APR_EINTR){ + std::cout << "ERROR: " << get_desc(status) << std::endl; + } + } + } + for(iterator i = sessions.begin(); i < sessions.end(); i++){ + (*i)->shutdown(); + } + + CHECK_APR_SUCCESS(apr_socket_close(socket)); +} + +BlockingAPRAcceptor::~BlockingAPRAcceptor(){ + delete threadFactory; + apr_pool_destroy(apr_pool); + APRBase::decrement(); +} + + +void BlockingAPRAcceptor::closed(BlockingAPRSessionContext* session){ + sessions.erase(find(sessions.begin(), sessions.end(), session)); + delete this; +} + diff --git a/cpp/common/io/src/BlockingAPRSessionContext.cpp b/cpp/common/io/src/BlockingAPRSessionContext.cpp new file mode 100644 index 0000000000..99352c90d5 --- /dev/null +++ b/cpp/common/io/src/BlockingAPRSessionContext.cpp @@ -0,0 +1,177 @@ +/* + * + * 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 <iostream> +#include "BlockingAPRSessionContext.h" +#include "BlockingAPRAcceptor.h" +#include "APRBase.h" +#include "QpidError.h" + +using namespace qpid::concurrent; +using namespace qpid::framing; +using namespace qpid::io; + + +BlockingAPRSessionContext::BlockingAPRSessionContext(apr_socket_t* _socket, + ThreadFactory* factory, + BlockingAPRAcceptor* _acceptor, + bool _debug) + : socket(_socket), + debug(_debug), + inbuf(65536), + outbuf(65536), + handler(0), + acceptor(_acceptor), + closed(false){ + + reader = new Reader(this); + writer = new Writer(this); + + rThread = factory->create(reader); + wThread = factory->create(writer); +} + +BlockingAPRSessionContext::~BlockingAPRSessionContext(){ + delete reader; + delete writer; + + delete rThread; + delete wThread; + + delete handler; +} + +void BlockingAPRSessionContext::read(){ + try{ + bool initiated(false); + while(!closed){ + apr_size_t bytes(inbuf.available()); + if(bytes < 1){ + THROW_QPID_ERROR(INTERNAL_ERROR, "Frame exceeds buffer size."); + } + apr_status_t s = apr_socket_recv(socket, inbuf.start(), &bytes); + if(APR_STATUS_IS_TIMEUP(s)){ + //timed out, check closed on loop + }else if(APR_STATUS_IS_EOF(s) || bytes == 0){ + closed = true; + }else{ + inbuf.move(bytes); + inbuf.flip(); + + if(!initiated){ + ProtocolInitiation* init = new ProtocolInitiation(); + if(init->decode(inbuf)){ + handler->initiated(init); + if(debug) std::cout << "RECV: [" << &socket << "]: Initialised " << std::endl; + initiated = true; + } + }else{ + AMQFrame frame; + while(frame.decode(inbuf)){ + if(debug) std::cout << "RECV: [" << &socket << "]:" << frame << std::endl; + handler->received(&frame); + } + } + //need to compact buffer to preserve any 'extra' data + inbuf.compact(); + } + } + + //close socket + }catch(qpid::QpidError error){ + std::cout << "Error [" << error.code << "] " << error.msg << " (" << error.file << ":" << error.line << ")" << std::endl; + } +} + +void BlockingAPRSessionContext::write(){ + while(!closed){ + //get next frame + outlock.acquire(); + while(outframes.empty() && !closed){ + outlock.wait(); + } + if(!closed){ + AMQFrame* frame = outframes.front(); + outframes.pop(); + outlock.release(); + + //encode + frame->encode(outbuf); + if(debug) std::cout << "SENT [" << &socket << "]:" << *frame << std::endl; + delete frame; + outbuf.flip(); + + //write from outbuf to socket + char* data = outbuf.start(); + const int available = outbuf.available(); + int written = 0; + apr_size_t bytes = available; + while(available > written){ + apr_status_t s = apr_socket_send(socket, data + written, &bytes); + written += bytes; + bytes = available - written; + } + outbuf.clear(); + }else{ + outlock.release(); + } + } +} + +void BlockingAPRSessionContext::send(AMQFrame* frame){ + if(!closed){ + outlock.acquire(); + bool was_empty(outframes.empty()); + outframes.push(frame); + if(was_empty){ + outlock.notify(); + } + outlock.release(); + }else{ + std::cout << "WARNING: Session closed[" << &socket << "], dropping frame. " << &frame << std::endl; + } +} + +void BlockingAPRSessionContext::init(SessionHandler* handler){ + this->handler = handler; + //start the threads + rThread->start(); + wThread->start(); +} + +void BlockingAPRSessionContext::close(){ + closed = true; + wThread->join(); + CHECK_APR_SUCCESS(apr_socket_close(socket)); + if(debug) std::cout << "RECV: [" << &socket << "]: Closed " << std::endl; + handler->closed(); + acceptor->closed(this); + delete this; +} + +void BlockingAPRSessionContext::shutdown(){ + closed = true; + outlock.acquire(); + outlock.notify(); + outlock.release(); + + wThread->join(); + CHECK_APR_SUCCESS(apr_socket_close(socket)); + rThread->join(); + handler->closed(); + delete this; +} diff --git a/cpp/common/io/src/LFAcceptor.cpp b/cpp/common/io/src/LFAcceptor.cpp new file mode 100644 index 0000000000..6653e926db --- /dev/null +++ b/cpp/common/io/src/LFAcceptor.cpp @@ -0,0 +1,80 @@ +/* + * + * 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 "LFAcceptor.h" +#include "APRBase.h" + +using namespace qpid::concurrent; +using namespace qpid::io; + +LFAcceptor::LFAcceptor(bool _debug, int c, int worker_threads, int m) : processor(aprPool.pool, worker_threads, 1000, 5000000), + connectionBacklog(c), + max_connections_per_processor(m), + debug(_debug){ + +} + + +void LFAcceptor::bind(int port, SessionHandlerFactory* factory){ + apr_socket_t* socket; + apr_sockaddr_t* address; + CHECK_APR_SUCCESS(apr_sockaddr_info_get(&address, APR_ANYADDR, APR_UNSPEC, port, APR_IPV4_ADDR_OK, aprPool.pool)); + CHECK_APR_SUCCESS(apr_socket_create(&socket, APR_INET, SOCK_STREAM, APR_PROTO_TCP, aprPool.pool)); + CHECK_APR_SUCCESS(apr_socket_opt_set(socket, APR_SO_REUSEADDR, 1)); + CHECK_APR_SUCCESS(apr_socket_bind(socket, address)); + CHECK_APR_SUCCESS(apr_socket_listen(socket, connectionBacklog)); + running = true; + processor.start(); + + std::cout << "Listening on port " << port << "..." << std::endl; + while(running){ + apr_socket_t* client; + apr_status_t status = apr_socket_accept(&client, socket, aprPool.pool); + if(status == APR_SUCCESS){ + //make this socket non-blocking: + CHECK_APR_SUCCESS(apr_socket_timeout_set(client, 0)); + CHECK_APR_SUCCESS(apr_socket_opt_set(client, APR_SO_NONBLOCK, 1)); + CHECK_APR_SUCCESS(apr_socket_opt_set(client, APR_TCP_NODELAY, 1)); + CHECK_APR_SUCCESS(apr_socket_opt_set(client, APR_SO_SNDBUF, 32768)); + CHECK_APR_SUCCESS(apr_socket_opt_set(client, APR_SO_RCVBUF, 32768)); + LFSessionContext* session = new LFSessionContext(aprPool.pool, client, &processor, debug); + session->init(factory->create(session)); + }else{ + running = false; + if(status != APR_EINTR){ + std::cout << "ERROR: " << get_desc(status) << std::endl; + } + } + } + + processor.stop(); + CHECK_APR_SUCCESS(apr_socket_close(socket)); +} + + +LFAcceptor::~LFAcceptor(){ +} + +LFAcceptor::APRPool::APRPool(){ + APRBase::increment(); + CHECK_APR_SUCCESS(apr_pool_create(&pool, NULL)); +} + +LFAcceptor::APRPool::~APRPool(){ + apr_pool_destroy(pool); + APRBase::decrement(); +} diff --git a/cpp/common/io/src/LFProcessor.cpp b/cpp/common/io/src/LFProcessor.cpp new file mode 100644 index 0000000000..8ef3543b8f --- /dev/null +++ b/cpp/common/io/src/LFProcessor.cpp @@ -0,0 +1,191 @@ +/* + * + * 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 "LFProcessor.h" +#include "APRBase.h" +#include "LFSessionContext.h" +#include "QpidError.h" +#include <sstream> + +using namespace qpid::io; +using namespace qpid::concurrent; +using qpid::QpidError; + +LFProcessor::LFProcessor(apr_pool_t* pool, int _workers, int _size, int _timeout) : size(_size), + timeout(_timeout), + signalledCount(0), + current(0), + count(0), + hasLeader(false), + workerCount(_workers), + workers(new Thread*[_workers]), + stopped(false){ + + CHECK_APR_SUCCESS(apr_pollset_create(&pollset, size, pool, APR_POLLSET_THREADSAFE)); + //create & start the required number of threads + for(int i = 0; i < workerCount; i++){ + workers[i] = factory.create(this); + } +} + + +LFProcessor::~LFProcessor(){ + for(int i = 0; i < workerCount; i++){ + delete workers[i]; + } + delete[] workers; + CHECK_APR_SUCCESS(apr_pollset_destroy(pollset)); +} + +void LFProcessor::start(){ + for(int i = 0; i < workerCount; i++){ + workers[i]->start(); + } +} + +void LFProcessor::add(const apr_pollfd_t* const fd){ + CHECK_APR_SUCCESS(apr_pollset_add(pollset, fd)); + countLock.acquire(); + sessions.push_back(reinterpret_cast<LFSessionContext*>(fd->client_data)); + count++; + countLock.release(); +} + +void LFProcessor::remove(const apr_pollfd_t* const fd){ + CHECK_APR_SUCCESS(apr_pollset_remove(pollset, fd)); + countLock.acquire(); + sessions.erase(find(sessions.begin(), sessions.end(), reinterpret_cast<LFSessionContext*>(fd->client_data))); + count--; + countLock.release(); +} + +void LFProcessor::reactivate(const apr_pollfd_t* const fd){ + CHECK_APR_SUCCESS(apr_pollset_add(pollset, fd)); +} + +void LFProcessor::deactivate(const apr_pollfd_t* const fd){ + CHECK_APR_SUCCESS(apr_pollset_remove(pollset, fd)); +} + +void LFProcessor::update(const apr_pollfd_t* const fd){ + CHECK_APR_SUCCESS(apr_pollset_remove(pollset, fd)); + CHECK_APR_SUCCESS(apr_pollset_add(pollset, fd)); +} + +bool LFProcessor::full(){ + countLock.acquire(); + bool full = count == size; + countLock.release(); + return full; +} + +bool LFProcessor::empty(){ + countLock.acquire(); + bool empty = count == 0; + countLock.release(); + return empty; +} + +void LFProcessor::poll(){ + apr_status_t status; + do{ + current = 0; + if(!stopped){ + status = apr_pollset_poll(pollset, timeout, &signalledCount, &signalledFDs); + } + }while(status != APR_SUCCESS && !stopped); +} + +void LFProcessor::run(){ + try{ + while(!stopped){ + leadLock.acquire(); + waitToLead(); + if(!stopped){ + const apr_pollfd_t* evt = getNextEvent(); + if(evt){ + LFSessionContext* session = reinterpret_cast<LFSessionContext*>(evt->client_data); + session->startProcessing(); + + relinquishLead(); + leadLock.release(); + + //process event: + if(evt->rtnevents & APR_POLLIN) session->read(); + if(evt->rtnevents & APR_POLLOUT) session->write(); + + if(session->isClosed()){ + session->handleClose(); + countLock.acquire(); + sessions.erase(find(sessions.begin(), sessions.end(), session)); + count--; + countLock.release(); + }else{ + session->stopProcessing(); + } + + }else{ + leadLock.release(); + } + }else{ + leadLock.release(); + } + } + }catch(QpidError error){ + std::cout << "Error [" << error.code << "] " << error.msg << " (" << error.file << ":" << error.line << ")" << std::endl; + } +} + +void LFProcessor::waitToLead(){ + while(hasLeader && !stopped) leadLock.wait(); + hasLeader = !stopped; +} + +void LFProcessor::relinquishLead(){ + hasLeader = false; + leadLock.notify(); +} + +const apr_pollfd_t* LFProcessor::getNextEvent(){ + while(true){ + if(stopped){ + return 0; + }else if(current < signalledCount){ + //use result of previous poll if one is available + return signalledFDs + (current++); + }else{ + //else poll to get new events + poll(); + } + } +} + +void LFProcessor::stop(){ + stopped = true; + leadLock.acquire(); + leadLock.notifyAll(); + leadLock.release(); + + for(int i = 0; i < workerCount; i++){ + workers[i]->join(); + } + + for(iterator i = sessions.begin(); i < sessions.end(); i++){ + (*i)->shutdown(); + } +} + diff --git a/cpp/common/io/src/LFSessionContext.cpp b/cpp/common/io/src/LFSessionContext.cpp new file mode 100644 index 0000000000..d786cb5e98 --- /dev/null +++ b/cpp/common/io/src/LFSessionContext.cpp @@ -0,0 +1,187 @@ +/* + * + * 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 "LFSessionContext.h" +#include "APRBase.h" +#include "QpidError.h" +#include <assert.h> + +using namespace qpid::concurrent; +using namespace qpid::io; +using namespace qpid::framing; + +LFSessionContext::LFSessionContext(apr_pool_t* _pool, apr_socket_t* _socket, + LFProcessor* const _processor, + bool _debug) : socket(_socket), + processor(_processor), + initiated(false), + processing(false), + closing(false), + in(32768), + out(32768), + reading(0), + writing(0), + debug(_debug){ + + fd.p = _pool; + fd.desc_type = APR_POLL_SOCKET; + fd.reqevents = APR_POLLIN; + fd.client_data = this; + fd.desc.s = _socket; + + out.flip(); +} + +LFSessionContext::~LFSessionContext(){ + +} + +void LFSessionContext::read(){ + assert(!reading); // No concurrent read. + reading = APRThread::currentThread(); + + socket.read(in); + in.flip(); + if(initiated){ + AMQFrame frame; + while(frame.decode(in)){ + if(debug) log("RECV", &frame); + handler->received(&frame); + } + }else{ + ProtocolInitiation init; + if(init.decode(in)){ + handler->initiated(&init); + initiated = true; + if(debug) std::cout << "INIT [" << &socket << "]" << std::endl; + } + } + in.compact(); + + reading = 0; +} + +void LFSessionContext::write(){ + assert(!writing); // No concurrent writes. + writing = APRThread::currentThread(); + + bool done = isClosed(); + while(!done){ + if(out.available() > 0){ + socket.write(out); + if(out.available() > 0){ + writing = 0; + + //incomplete write, leave flags to receive notification of readiness to write + done = true;//finished processing for now, but write is still in progress + } + }else{ + //do we have any frames to write? + writeLock.acquire(); + if(!framesToWrite.empty()){ + out.clear(); + bool encoded(false); + AMQFrame* frame = framesToWrite.front(); + while(frame && out.available() >= frame->size()){ + encoded = true; + frame->encode(out); + if(debug) log("SENT", frame); + delete frame; + framesToWrite.pop(); + frame = framesToWrite.empty() ? 0 : framesToWrite.front(); + } + if(!encoded) THROW_QPID_ERROR(FRAMING_ERROR, "Could not write frame, too large for buffer."); + out.flip(); + }else{ + //reset flags, don't care about writability anymore + fd.reqevents = APR_POLLIN; + done = true; + + writing = 0; + + if(closing){ + socket.close(); + } + } + writeLock.release(); + } + } +} + +void LFSessionContext::send(AMQFrame* frame){ + writeLock.acquire(); + if(!closing){ + framesToWrite.push(frame); + if(!(fd.reqevents & APR_POLLOUT)){ + fd.reqevents |= APR_POLLOUT; + if(!processing){ + processor->update(&fd); + } + } + } + writeLock.release(); +} + +void LFSessionContext::startProcessing(){ + writeLock.acquire(); + processing = true; + processor->deactivate(&fd); + writeLock.release(); +} + +void LFSessionContext::stopProcessing(){ + writeLock.acquire(); + processor->reactivate(&fd); + processing = false; + writeLock.release(); +} + +void LFSessionContext::close(){ + closing = true; + writeLock.acquire(); + if(!processing){ + //allow pending frames to be written to socket + fd.reqevents = APR_POLLOUT; + processor->update(&fd); + } + writeLock.release(); +} + +void LFSessionContext::handleClose(){ + handler->closed(); + std::cout << "Session closed [" << &socket << "]" << std::endl; + delete handler; + delete this; +} + +void LFSessionContext::shutdown(){ + socket.close(); + handleClose(); +} + +void LFSessionContext::init(SessionHandler* handler){ + this->handler = handler; + processor->add(&fd); +} + +void LFSessionContext::log(const std::string& desc, AMQFrame* const frame){ + logLock.acquire(); + std::cout << desc << " [" << &socket << "]: " << *frame << std::endl; + logLock.release(); +} + +APRMonitor LFSessionContext::logLock; diff --git a/cpp/common/utils/inc/logger.h b/cpp/common/utils/inc/logger.h new file mode 100644 index 0000000000..8a57854476 --- /dev/null +++ b/cpp/common/utils/inc/logger.h @@ -0,0 +1,82 @@ +/* + * + * 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. + * + */ +/********************************************************************* +* +* NOTE: This is a lightweight logging class intended for debugging and +* verification purposes only. +* +* DO NOT USE FOR PRODUCT DEVELOPMENT - Rather, use an agreed upon +* established logging class (such as Apache's log4cxx) for product +* development purposes. +* +*********************************************************************/ + +#ifndef __LOGGER__ +#define __LOGGER__ + +#include <fstream> +#include <iostream> + +namespace qpid { +namespace utils { + +class Logger : public std::ofstream +{ + private: + bool echo_flag; + bool timestamp_flag; + bool eol_flag; + char buff[128]; // Buffer for writing formatted strings + + void write_timestamp(); + + public: + Logger(const char* filename, const bool append); + Logger(std::string& filename, const bool append); + ~Logger(); + + bool getEchoFlag() {return echo_flag;} + bool setEchoFlag(const bool _echo_flag) {echo_flag = _echo_flag;} + bool getTimestampFlag() {return timestamp_flag;} + bool setTimestampFlag(const bool _timestamp_flag) {timestamp_flag = _timestamp_flag;} + + void log(const char* message); + void log(const char* message, const bool echo); + void log(const char* message, const bool echo, const bool timestamp); + + Logger& operator<< (bool b); + Logger& operator<< (const short s); + Logger& operator<< (const unsigned short us); + Logger& operator<< (const int i); + Logger& operator<< (const unsigned int ui); + Logger& operator<< (const long l); + Logger& operator<< (const unsigned long ul); + Logger& operator<< (const long long l); + Logger& operator<< (const unsigned long long ul); + Logger& operator<< (const float f); + Logger& operator<< (const double d); + Logger& operator<< (const long double ld); + Logger& operator<< (const char* cstr); + Logger& operator<< (const std::string& str); +}; + +} +} + + +#endif diff --git a/cpp/common/utils/inc/memory.h b/cpp/common/utils/inc/memory.h new file mode 100644 index 0000000000..2d65877adb --- /dev/null +++ b/cpp/common/utils/inc/memory.h @@ -0,0 +1,17 @@ +#ifndef __UTIL_MEMORY__ +#define __UTIL_MEMORY__ + +#if __GNUC__ < 4 + #include "boost/shared_ptr.hpp" + namespace std { + namespace tr1 { + using boost::shared_ptr; + using boost::dynamic_pointer_cast; + using boost::static_pointer_cast; + } + } +#else + #include <tr1/memory> +#endif +#endif + diff --git a/cpp/common/utils/src/Makefile b/cpp/common/utils/src/Makefile new file mode 100644 index 0000000000..0185ab9975 --- /dev/null +++ b/cpp/common/utils/src/Makefile @@ -0,0 +1,40 @@ + # + # 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. + # +##### Options ##### +QPID_HOME = ../../../.. + +include ${QPID_HOME}/cpp/options.mk + +##### Compiler flags ##### +CXXFLAGS = -I ../inc -I ${APR_HOME}/include/apr-1/ + +##### Targets ##### +# Add additional source files to SOURCE LIST to include them in the build. +COMMON_SOURCE_LIST = logger.cpp + +COMMON_OBJ_LIST = $(COMMON_SOURCE_LIST:.cpp=.o) +LOGGER_TEST_EXE = logger_test + + +.PHONY: all clean + +all: $(LOGGER_TEST_EXE) + +$(LOGGER_TEST_EXE) : $(COMMON_OBJ_LIST) $(LOGGER_TEST_EXE).o + $(CXX) -o $@ $^ -l apr-1 -L /usr/local/apr/lib/ + +clean: + -@rm -f $(LOGGER_TEST_EXE) $(LOGGER_TEST_EXE).o $(COMMON_OBJ_LIST) test_log.txt *~ ../inc/*~ diff --git a/cpp/common/utils/src/logger.cpp b/cpp/common/utils/src/logger.cpp new file mode 100644 index 0000000000..603fa6574e --- /dev/null +++ b/cpp/common/utils/src/logger.cpp @@ -0,0 +1,209 @@ +/* + * + * 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. + * + */ +/********************************************************************* +* +* NOTE: This is a lightweight logging class intended for debugging and +* verification purposes only. +* +* DO NOT USE FOR PRODUCT DEVELOPMENT - Rather, use an agreed upon +* established logging class (such as Apache's log4cxx) for product +* development purposes. +* +*********************************************************************/ + +#include <iostream> +#include <ostream> +#include <string.h> +#include "apr_time.h" +#include "logger.h" + +namespace qpid { +namespace utils { + +Logger::Logger(const char* filename, const bool append): + std::ofstream(filename, append ? std::ios::app : std::ios::out) +{ + echo_flag = false; + timestamp_flag = true; + eol_flag = true; +} + +Logger::Logger(std::string& filename, const bool append): + std::ofstream(filename.c_str(), append ? std::ios::app : std::ios::out) +{ + echo_flag = false; + timestamp_flag = true; + eol_flag = true; +} + +Logger::~Logger() +{ + close(); +} + +void Logger::write_timestamp() +{ + int len; + apr_time_exp_t now; + apr_time_exp_lt(&now, apr_time_now()); + sprintf(buff, "%4d/%02d/%02d %02d:%02d:%02d.%06d : ", 1900+now.tm_year, now.tm_mon, + now.tm_mday, now.tm_hour, now.tm_min, now.tm_sec, now.tm_usec); + write(buff, strlen(buff)); +} + + +void Logger::log(const char* message) +{ + if (timestamp_flag && eol_flag) + { + eol_flag = false; + write_timestamp(); + } + write(message, strlen(message)); + if (echo_flag) + std::cout << message; + if (strchr(message, '\n')) + eol_flag = true; +} + +void Logger::log(const char* message, const bool echo) +{ + if (timestamp_flag && eol_flag) + { + eol_flag = false; + write_timestamp(); + } + write(message, strlen(message)); + if (echo) + std::cout << message; + if (strchr(message, '\n')) + eol_flag = true; +} + +void Logger::log(const char* message, const bool echo, const bool timestamp) +{ + if (timestamp && eol_flag) + { + eol_flag = false; + write_timestamp(); + } + write(message, strlen(message)); + if (echo) + std::cout << message; + if (strchr(message, '\n')) + eol_flag = true; +} + +Logger& Logger::operator<< (const bool b) +{ + log(b ? "true" : "false"); + return *this; +} + +Logger& Logger::operator<< (const short s) +{ + sprintf(buff, "%d", s); + log(buff); + return *this; +} + +Logger& Logger::operator<< (const unsigned short us) +{ + sprintf(buff, "%u", us); + log(buff); + return *this; +} + +Logger& Logger::operator<< (const int i) +{ + sprintf(buff, "%d", i); + log(buff); + return *this; +} + +Logger& Logger::operator<< (const unsigned int ui) +{ + sprintf(buff, "%u", ui); + log(buff); + return *this; +} + +Logger& Logger::operator<< (const long l) +{ + sprintf(buff, "%ld", l); + log(buff); + return *this; +} + +Logger& Logger::operator<< (const unsigned long ul) +{ + sprintf(buff, "%lu", ul); + log(buff); + return *this; +} + +Logger& Logger::operator<< (const long long l) +{ + sprintf(buff, "%ld", l); + log(buff); + return *this; +} + +Logger& Logger::operator<< (const unsigned long long ul) +{ + sprintf(buff, "%lu", ul); + log(buff); + return *this; +} + +Logger& Logger::operator<< (const float f) +{ + sprintf(buff, "%f", f); + log(buff); + return *this; +} + +Logger& Logger::operator<< (const double d) +{ + sprintf(buff, "%lf", d); + log(buff); + return *this; +} + +Logger& Logger::operator<< (const long double ld) +{ + sprintf(buff, "%Lf", ld); + log(buff); + return *this; +} + +Logger& Logger::operator<< (const char* cstr) +{ + log(cstr); + return *this; +} + +Logger& Logger::operator<< (const std::string& str) +{ + log(str.c_str()); + return *this; +} + +} +} + diff --git a/cpp/common/utils/src/logger_test.cpp b/cpp/common/utils/src/logger_test.cpp new file mode 100644 index 0000000000..1866af9fbb --- /dev/null +++ b/cpp/common/utils/src/logger_test.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 <iostream> +#include <string> +#include "logger.h" + +using namespace qpid::utils; + +void run_sequence(Logger& log) +{ + bool b = true; + short s = -5; + unsigned short us = 12; + int i = -2345; + unsigned int ui = 34567; + long l = -12345678; + unsigned long ul = 23456789; + long long ll = -1234567890; + unsigned long long ull = 1234567890; + float f = -123.45678; + double d = 123.45678901; + long double ld = 23456.789012345678; + char* cstr = "This is a test C string."; + char* cr = "\n"; + std::string str("This is a test std::string"); + log << "bool = " << b << cr; + log << "short = " << s << cr; + log << "unsigned sort = " << us << cr; + log << "int = " << i << cr; + log << "unsigned int = " << ui << cr; + log << "long = " << l << cr; + log << "unsigned long = " << ul << cr; + log << "long long = " << ll << cr; + log << "unsigned long long = " << ull << cr; + log << "float = " << f << cr; + log << "double = " << d << cr; + log << "long double = " << ld << cr; + log << "char* = " << cstr << cr; + log << "std::string = " << str << cr; + log << "String 1\n"; + log << "String 2\n" << "String 3 " << "String 4\n"; + log << "Literal bool = " << false << cr; + log << "Literal unsigned int = " << 15 << cr; + log << "Literal double = " << (double)15 << cr; +} + +int main(int argc, char** argv) +{ + Logger log("test_log.txt", false); + std::cout << "****** Initial state (echo off, timestamp on)" << std::endl; + run_sequence(log); + std::cout << std::endl << "****** (echo off, timestamp off)" << std::endl; + log.setTimestampFlag(false); + run_sequence(log); + std::cout << std::endl << "****** (echo on, timestamp on)" << std::endl; + log.setEchoFlag(true); + log.setTimestampFlag(true); + run_sequence(log); + std::cout << std::endl << "****** (echo on, timestamp off)" << std::endl; + log.setTimestampFlag(false); + run_sequence(log); + return 0; +} diff --git a/cpp/doxygen/Makefile b/cpp/doxygen/Makefile new file mode 100644 index 0000000000..187fe698a0 --- /dev/null +++ b/cpp/doxygen/Makefile @@ -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. + # + +.PHONY: all + +all: + doxygen doxygen.cfg + +clean: + rm -rf html diff --git a/cpp/doxygen/doxygen.cfg b/cpp/doxygen/doxygen.cfg new file mode 100644 index 0000000000..d31c1bb134 --- /dev/null +++ b/cpp/doxygen/doxygen.cfg @@ -0,0 +1,1238 @@ +# NB: requires doxygen and graphviz - install with yum. + +# 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 = NO + +# 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 = NO + +# 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 = 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 = 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 = 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 = NO + +# 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 = + +#--------------------------------------------------------------------------- +# 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 = .. + +# 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 = *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py + +# 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 = + +# 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 + +# 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 = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# 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/cpp/options.mk b/cpp/options.mk new file mode 100644 index 0000000000..a1dfd695a7 --- /dev/null +++ b/cpp/options.mk @@ -0,0 +1,54 @@ + #l + # 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. + # + +# Directories +SPEC_DIR = ${QPID_HOME}/specs +QPID_CPP_HOME = ${QPID_HOME}/cpp +COMMON_HOME = ${QPID_CPP_HOME}/common +TOOLS_DIR = ${QPID_CPP_HOME}/tools +LIB_DIR = ${QPID_CPP_HOME}/lib +BIN_DIR = ${QPID_CPP_HOME}/bin +APR_HOME= /usr +BOOST_HOME= /usr +CPPUNIT_HOME= /usr + +# Compile flags +DEBUG = -g +# _USE_APR_IO_ set when APR IO build is desired. +OPT = -D _USE_APR_IO_ #-O3 +APR_INCLUDES=-I ${APR_HOME}/include/apr-1/ +BOOST_INCLUDES=-I ${BOOST_HOME}/include/boost-1_33_1 +CPPUNIT_INCLUDES=-I ${CPPUNIT_HOME}/include +COMMON_INCLUDES = -I ${COMMON_HOME}/framing/inc -I ${COMMON_HOME}/framing/generated -I ${COMMON_HOME}/concurrent/inc -I ${COMMON_HOME}/io/inc -I ${COMMON_HOME}/error/inc -I $(COMMON_HOME)/utils/inc ${APR_INCLUDES} ${BOOST_INCLUDES} ${CPPUNIT_INCLUDES} +SRC_INCLUDES = $(COMMON_INCLUDES) -I inc +TEST_INCLUDES = $(COMMON_INCLUDES) -I ../inc +INCLUDES=$(SRC_INCLUDES) # Default to src +CXXFLAGS = $(DEBUG) $(OPT) -MMD -fpic $(INCLUDES) + +# TODO aconway 2006-09-12: This is not something we want in a release +# but it's useful for development. +RPATH= -Wl,-rpath,$(CURDIR)/$(LIB_DIR) + +# General link flags +LDFLAGS= -L $(LIB_DIR) -L ${APR_HOME}/lib -L ${BOOST_HOME}/lib -L ${CPPUNIT_HOME}/lib $(RPATH) + +# Libraries and executables. Use absolute paths so exes can find +# libs wherever they are run. TODO: Proper library management. +BROKER=$(BIN_DIR)/qpidd +BROKER_LIB=$(CURDIR)/$(LIB_DIR)/libqpid_broker.so.1.0 +COMMON_LIB=$(CURDIR)/$(LIB_DIR)/libqpid_common.so.1.0 +CLIENT_LIB=$(CURDIR)/$(LIB_DIR)/libqpid_client.so.1.0 + diff --git a/cpp/test_plugins.mk b/cpp/test_plugins.mk new file mode 100644 index 0000000000..abac186954 --- /dev/null +++ b/cpp/test_plugins.mk @@ -0,0 +1,42 @@ +# +# 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. +# + + +# +# Standard make fragment for building test plugins in a directory. +# + +include ${QPID_HOME}/cpp/options.mk + +SOURCES := $(wildcard *.cpp) +TESTS := $(SOURCES:.cpp=.so) +DEPS= $(SOURCES:.cpp=.d) + +INCLUDES = $(TEST_INCLUDES) + +.PHONY: all clean + +all: $(TESTS) + +clean: + -@rm -f $(TESTS) $(DEPS) + +# Rule to build test plugins from .cpp files. +%.so: %.cpp + $(CXX) -shared -o $@ $< $(CXXFLAGS) $(LDFLAGS) $(LDLIBS) + +# Dependencies +-include $(DEPS) diff --git a/cpp/tools/saxon8.jar b/cpp/tools/saxon8.jar Binary files differnew file mode 100644 index 0000000000..197ce75c5b --- /dev/null +++ b/cpp/tools/saxon8.jar |