diff --git a/activemq-cpp/makefile b/activemq-cpp/makefile new file mode 100644 index 0000000000..3d7638beca --- /dev/null +++ b/activemq-cpp/makefile @@ -0,0 +1,142 @@ +SRCDIR = src +MAKESUPPORT_HOME = $(PWD) + +OFILES = \ + $(OUTDIR)/main/activemq/exceptions/ActiveMQException.o \ + \ + $(OUTDIR)/main/activemq/support/InitDirector.o \ + \ + $(OUTDIR)/main/activemq/transport/IOTransport.o \ + $(OUTDIR)/main/activemq/transport/TcpTransport.o \ + $(OUTDIR)/main/activemq/transport/ResponseCorrelator.o \ + $(OUTDIR)/main/activemq/transport/TransportFactoryMap.o \ + $(OUTDIR)/main/activemq/transport/IOTransportFactory.o \ + $(OUTDIR)/main/activemq/transport/TcpTransportFactory.o \ + \ + $(OUTDIR)/main/activemq/connector/ConnectorFactoryMap.o \ + \ + $(OUTDIR)/main/activemq/connector/stomp/StompConnector.o \ + $(OUTDIR)/main/activemq/connector/stomp/StompConnectorFactory.o \ + $(OUTDIR)/main/activemq/connector/stomp/StompCommandReader.o \ + $(OUTDIR)/main/activemq/connector/stomp/StompCommandWriter.o \ + $(OUTDIR)/main/activemq/connector/stomp/StompSessionManager.o \ + \ + $(OUTDIR)/main/activemq/connector/stomp/commands/CommandConstants.o \ + \ + $(OUTDIR)/main/activemq/connector/stomp/marshal/Marshaler.o \ + \ + $(OUTDIR)/main/activemq/core/ActiveMQConnectionFactory.o \ + $(OUTDIR)/main/activemq/core/ActiveMQConnection.o \ + $(OUTDIR)/main/activemq/core/ActiveMQSession.o \ + $(OUTDIR)/main/activemq/core/ActiveMQProducer.o \ + $(OUTDIR)/main/activemq/core/ActiveMQConsumer.o \ + $(OUTDIR)/main/activemq/core/ActiveMQTransaction.o \ + \ + $(OUTDIR)/main/activemq/io/EndianReader.o \ + $(OUTDIR)/main/activemq/io/EndianWriter.o \ + $(OUTDIR)/main/activemq/io/BufferedInputStream.o \ + $(OUTDIR)/main/activemq/io/BufferedOutputStream.o \ + $(OUTDIR)/main/activemq/io/ByteArrayInputStream.o \ + $(OUTDIR)/main/activemq/io/ByteArrayOutputStream.o \ + \ + $(OUTDIR)/main/activemq/logger/SimpleLogger.o \ + $(OUTDIR)/main/activemq/logger/LogWriter.o \ + $(OUTDIR)/main/activemq/logger/LogManager.o \ + $(OUTDIR)/main/activemq/logger/LoggerHierarchy.o \ + \ + $(OUTDIR)/main/activemq/network/ServerSocket.o \ + $(OUTDIR)/main/activemq/network/TcpSocket.o \ + $(OUTDIR)/main/activemq/network/BufferedSocket.o \ + $(OUTDIR)/main/activemq/network/SocketFactory.o \ + $(OUTDIR)/main/activemq/network/SocketInputStream.o \ + $(OUTDIR)/main/activemq/network/SocketOutputStream.o\ + \ + $(OUTDIR)/main/activemq/util/Guid.o \ + $(OUTDIR)/main/activemq/util/StringTokenizer.o \ + \ + $(OUTDIR)/main/activemq/concurrent/Thread.o \ + $(OUTDIR)/main/activemq/concurrent/Mutex.o \ + $(OUTDIR)/main/activemq/concurrent/ThreadPool.o \ + $(OUTDIR)/main/activemq/concurrent/PooledThread.o + +OTESTFILES = \ + $(OUTDIR)/test/main.o \ + \ + $(OUTDIR)/test/activemq/core/ActiveMQConnectionFactoryTest.o \ + $(OUTDIR)/test/activemq/core/ActiveMQConnectionTest.o \ + $(OUTDIR)/test/activemq/core/ActiveMQSessionTest.o \ + \ + $(OUTDIR)/test/activemq/concurrent/MutexTest.o \ + $(OUTDIR)/test/activemq/concurrent/ThreadPoolTest.o \ + $(OUTDIR)/test/activemq/concurrent/ThreadTest.o \ + \ + $(OUTDIR)/test/activemq/connector/stomp/StompConnectorTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/StompFrameTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/StompCommandReaderTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/StompCommandWriterTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/StompSessionManagerTest.o \ + \ + $(OUTDIR)/test/activemq/connector/stomp/commands/CommandConstantsTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/commands/AbortCommandTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/commands/AckCommandTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/commands/BeginCommandTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/commands/CommitCommandTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/commands/ConnectCommandTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/commands/ConnectedCommandTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/commands/DisconnectCommandTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/commands/ErrorCommandTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/commands/ReceiptCommandTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/commands/SubscribeCommandTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/commands/UnsubscribeCommandTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/commands/MessageCommandTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/commands/BytesMessageCommandTest.o \ + $(OUTDIR)/test/activemq/connector/stomp/commands/TextMessageCommandTest.o \ + \ + $(OUTDIR)/test/activemq/connector/stomp/marshal/MarshalerTest.o \ + \ + $(OUTDIR)/test/activemq/connector/ConnectorFactoryMapRegistrarTest.o \ + $(OUTDIR)/test/activemq/connector/ConnectorFactoryMapTest.o \ + \ + $(OUTDIR)/test/activemq/exceptions/ActiveMQExceptionTest.o \ + \ + $(OUTDIR)/test/activemq/io/BufferedInputStreamTest.o \ + $(OUTDIR)/test/activemq/io/BufferedOutputStreamTest.o \ + $(OUTDIR)/test/activemq/io/ByteArrayInputStreamTest.o \ + $(OUTDIR)/test/activemq/io/ByteArrayOutputStreamTest.o \ + $(OUTDIR)/test/activemq/io/EndianReaderTest.o \ + $(OUTDIR)/test/activemq/io/EndianWriterTest.o \ + \ + $(OUTDIR)/test/activemq/logger/LoggerTest.o \ + \ + $(OUTDIR)/test/activemq/network/SocketFactoryTest.o \ + $(OUTDIR)/test/activemq/network/SocketTest.o \ + \ + $(OUTDIR)/test/activemq/transport/DummyTransportFactory.o \ + $(OUTDIR)/test/activemq/transport/IOTransportTest.o \ + $(OUTDIR)/test/activemq/transport/ResponseCorrelatorTest.o \ + $(OUTDIR)/test/activemq/transport/TransportFactoryMapTest.o \ + $(OUTDIR)/test/activemq/transport/TransportFactoryMapRegistrarTest.o \ + \ + $(OUTDIR)/test/activemq/util/GuidTest.o \ + $(OUTDIR)/test/activemq/util/IntegerTest.o \ + $(OUTDIR)/test/activemq/util/LongTest.o \ + $(OUTDIR)/test/activemq/util/BooleanTest.o \ + $(OUTDIR)/test/activemq/util/QueueTest.o \ + $(OUTDIR)/test/activemq/util/StringTokenizerTest.o + +OINTEGRATIONFILES = \ + $(OUTDIR)/test-integration/main.o \ + \ + $(OUTDIR)/test-integration/integration/simple/SimpleTester.o \ + $(OUTDIR)/test-integration/integration/transactional/TransactionTester.o \ + $(OUTDIR)/test-integration/integration/common/AbstractTester.o \ + $(OUTDIR)/test-integration/integration/common/IntegrationCommon.o + +LIBFILE = $(OUTDIR)/activemq.a +TESTEXE = $(OUTDIR)/activemqTest +INTEGRATIONEXE = $(OUTDIR)/activemqIntegrationTests + +DEFINES = + +include $(MAKESUPPORT_HOME)/makefile.cfg + diff --git a/activemq-cpp/makefile-linux-debug.cfg b/activemq-cpp/makefile-linux-debug.cfg new file mode 100644 index 0000000000..6fd0f20500 --- /dev/null +++ b/activemq-cpp/makefile-linux-debug.cfg @@ -0,0 +1,18 @@ +# +# Compiler specific configuration +# + +OUTDIR = out +LIBRARY_NAME = activemq-cpp +LIBFILE = $(OUTDIR)/lib$(LIBRARY_NAME).a + +# +# GCC/G++ debug for Linux +# +CC = g++ -frtti -g -pthread -DDEBUG -D_DEBUG -D_REENTRANT +LD = g++ -g -frtti -pthread +CCFLAGS = -Wall +LDFLAGS = -L$(OUTDIR) -l$(LIBRARY_NAME) -lcppunit -ldl -luuid +ARFLAGS = + + diff --git a/activemq-cpp/makefile-linux-release.cfg b/activemq-cpp/makefile-linux-release.cfg new file mode 100644 index 0000000000..e6d1121274 --- /dev/null +++ b/activemq-cpp/makefile-linux-release.cfg @@ -0,0 +1,19 @@ +# +# Compiler specific configuration +# + +OUTDIR = out +LIBRARY_NAME = activemq-cpp +LIBFILE = $(OUTDIR)/lib$(LIBRARY_NAME).a + +# +# GCC/G++ release for Linux +# +CC = g++ -frtti -pthread -O3 -DNDEBUG -D_REENTRANT +LD = g++ -frtti -pthread +CCFLAGS = -Wall +LDFLAGS = -L$(OUTDIR) -l$(LIBRARY_NAME) -lcppunit -ldl -luuid +OUTDIR = out +ARFLAGS = + + diff --git a/activemq-cpp/makefile-windows-debug.cfg b/activemq-cpp/makefile-windows-debug.cfg new file mode 100644 index 0000000000..eefd366038 --- /dev/null +++ b/activemq-cpp/makefile-windows-debug.cfg @@ -0,0 +1,16 @@ +# +# Compiler specific configuration +# + +LIBFILE = $(OUTDIR)/libactivemq-cpp.a + +# +# GCC/G++ debug for Linux +# +CC = g++ -fexceptions -frtti -O0 -g3 -DDEBUG -D_DEBUG -D_REENTRANT -D_WIN32 -DWINVER=0x0502 -DWIN32_LEAN_AND_MEAN +LD = g++ -g3 -frtti +CCFLAGS = -Wall +LDFLAGS = -L$(OUTDIR) -lactivemq -lcppunit -lws2_32 -lrpcrt4 +OUTDIR = out +ARFLAGS = + diff --git a/activemq-cpp/makefile-windows-release.cfg b/activemq-cpp/makefile-windows-release.cfg new file mode 100644 index 0000000000..3dcd5f9b15 --- /dev/null +++ b/activemq-cpp/makefile-windows-release.cfg @@ -0,0 +1,17 @@ +# +# Compiler specific configuration +# + +LIBFILE = $(OUTDIR)/libactivemq-cpp.a + +# +# GCC/G++ release for Linux +# +CC = g++ -fexceptions -frtti -O3 -DNDEBUG -D_REENTRANT -D__WIN32 -DWIN32_LEAN_AND_MEAN +LD = g++ -frtti +CCFLAGS = -Wall +LDFLAGS = -L$(OUTDIR) -lactivemq -lcppunit -lws2_32 -lrpcrt4 +OUTDIR = out +ARFLAGS = + + diff --git a/activemq-cpp/makefile.cfg b/activemq-cpp/makefile.cfg new file mode 100644 index 0000000000..3522816a25 --- /dev/null +++ b/activemq-cpp/makefile.cfg @@ -0,0 +1,179 @@ +CURRENT = $(PWD) + +# --- select compiler for structure +# ---------------------------------------------------------- + +include $(MAKESUPPORT_HOME)/makefile-$(OSTYPE)-$(CONFIG).cfg + +# --- set generic commmands +# ---------------------------------------------------------- + +MV = mv +RM = rm -f +RMR = rm -rf +CP = cp -p +LN = ln -s +LS = ls +CAT = cat +MD = mkdir +AR = ar -r +PURIFY = purify +ECHO = echo +TOUCH = touch +CD = cd +STRIP = strip + +# --- set default targets and their handling procedures +# ---------------------------------------------------------- + +.SILENT: +.KEEP_STATE: + +.SUFFIXES: +.SUFFIXES: .cpp .CC + +.INIT: + $(ECHO) " -<>- Compiling "$(CURRENT) + +.DONE: + $(ECHO) " -<>- Done" + +.FAILED: + $(ECHO) " *** Compilation of $(CURRENT) Failed" + +$(OUTDIR)/main/%.o: $(SRCDIR)/main/%.cpp + $(ECHO) " - "$(CC) "'"$<"'" + $(CC) -o $@ $(DEFINES) $(CCFLAGS) -I$(SRCDIR)/main \ + $(LOCAL_INCLUDE) -c $< + +$(OUTDIR)/test/%.o: $(SRCDIR)/test/%.cpp + $(ECHO) " - "$(CC) "'"$<"'" + $(CC) -o $@ $(DEFINES) $(CCFLAGS) -I$(SRCDIR)/main -I$(SRCDIR)/test \ + $(LOCAL_INCLUDE) -c $< + +$(OUTDIR)/test-integration/%.o: $(SRCDIR)/test-integration/%.cpp + $(ECHO) " - "$(CC) "'"$<"'" + $(CC) -o $@ $(DEFINES) $(CCFLAGS) -I$(SRCDIR)/main -I$(SRCDIR)/test-integration \ + $(LOCAL_INCLUDE) -c $< + +# --- set generic targets and their handling procedures +# ---------------------------------------------------------- + +lib: \ + prepare \ + $(OFILES) \ + $(LIBFILE) \ + done + +test: \ + prepare_test \ + $(OTESTFILES) \ + $(TESTEXE) \ + done + +integration: \ + prepare_integration \ + $(OINTEGRATIONFILES) \ + $(INTEGRATIONEXE) \ + done + +all: lib test integration + +default: all + + +$(LIBFILE): $(OFILES) $(DEPLIBS) + $(ECHO) " - Creating static library file "$@ + $(AR) $(ARFLAGS) $@ $(OFILES) + +$(TESTEXE): $(OTESTFILES) + $(LD) -o $@ $(OTESTFILES) $(LDFLAGS) + $(ECHO) 'Finished building target: $@' + +$(INTEGRATIONEXE): $(OINTEGRATIONFILES) + $(LD) -o $@ $(OINTEGRATIONFILES) $(LDFLAGS) + $(ECHO) 'Finished building target: $@' + +clean: + $(ECHO) " - Cleaning up local directory "$(CURRENT) + $(ECHO) " - Removing object files" + $(RM) $(OFILES) + $(ECHO) " - Removing file "$(LIBFILE) + $(RM) -rf $(OUTDIR) + $(RM) $(LIBFILE) + $(RM) *~ *% + $(RM) #* + $(RM) core + $(RM) a.out + +prepare: + if test ! -d $(OUTDIR) ; \ + then \ + $(MD) $(OUTDIR); \ + $(MD) $(OUTDIR)/main; \ + $(MD) $(OUTDIR)/main/activemq; \ + $(MD) $(OUTDIR)/main/activemq/exceptions; \ + $(MD) $(OUTDIR)/main/activemq/commands; \ + $(MD) $(OUTDIR)/main/activemq/connector; \ + $(MD) $(OUTDIR)/main/activemq/connector/stomp; \ + $(MD) $(OUTDIR)/main/activemq/connector/stomp/commands; \ + $(MD) $(OUTDIR)/main/activemq/connector/stomp/marshal; \ + $(MD) $(OUTDIR)/main/activemq/core; \ + $(MD) $(OUTDIR)/main/activemq/io; \ + $(MD) $(OUTDIR)/main/activemq/logger; \ + $(MD) $(OUTDIR)/main/activemq/network; \ + $(MD) $(OUTDIR)/main/activemq/util; \ + $(MD) $(OUTDIR)/main/activemq/support; \ + $(MD) $(OUTDIR)/main/activemq/concurrent; \ + $(MD) $(OUTDIR)/main/activemq/transport; \ + fi + +prepare_test: + if test ! -d $(OUTDIR) ; \ + then \ + $(MD) $(OUTDIR); \ + fi + if test ! -d $(OUTDIR)/test ; \ + then \ + $(MD) $(OUTDIR)/test; \ + $(MD) $(OUTDIR)/test/activemq; \ + $(MD) $(OUTDIR)/test/activemq/exceptions; \ + $(MD) $(OUTDIR)/test/activemq/commands; \ + $(MD) $(OUTDIR)/test/activemq/connector; \ + $(MD) $(OUTDIR)/test/activemq/connector/stomp; \ + $(MD) $(OUTDIR)/test/activemq/connector/stomp/commands; \ + $(MD) $(OUTDIR)/test/activemq/connector/stomp/marshal; \ + $(MD) $(OUTDIR)/test/activemq/core; \ + $(MD) $(OUTDIR)/test/activemq/io; \ + $(MD) $(OUTDIR)/test/activemq/logger; \ + $(MD) $(OUTDIR)/test/activemq/network; \ + $(MD) $(OUTDIR)/test/activemq/util; \ + $(MD) $(OUTDIR)/test/activemq/concurrent; \ + $(MD) $(OUTDIR)/test/activemq/transport; \ + fi + +prepare_integration: + if test ! -d $(OUTDIR) ; \ + then \ + $(MD) $(OUTDIR); \ + fi + if test ! -d $(OUTDIR)/test-integration ; \ + then \ + $(MD) $(OUTDIR)/test-integration; \ + $(MD) $(OUTDIR)/test-integration/integration; \ + $(MD) $(OUTDIR)/test-integration/integration/common; \ + $(MD) $(OUTDIR)/test-integration/integration/simple; \ + $(MD) $(OUTDIR)/test-integration/integration/transactional; \ + $(MD) $(OUTDIR)/test-integration/integration/durable; \ + fi + +done: + $(ECHO) "Done." + + + + + + + + diff --git a/activemq-cpp/pom.xml b/activemq-cpp/pom.xml new file mode 100644 index 0000000000..d7c6c51688 --- /dev/null +++ b/activemq-cpp/pom.xml @@ -0,0 +1,228 @@ + + + 4.0.0 + + activemq + ActiveMQ :: CPP :: Parent + activemq-cpp-parent + pom + + + 1.0-SNAPSHOT + + + + + + + + org.codehaus.mojo + native-maven-plugin + 1.0-alpha-1-SNAPSHOT + true + + + true + + + + + ${compiler.provider} + ${env.factory.name} + + ${compiler.options} + + ${linker.executable} + + ${linker.options} + + + + + + + ../src/main + ../src/test + + + ../src/main/activemq/concurrent + + Mutex.cpp + PooledThread.cpp + Thread.cpp + ThreadPool.cpp + + + + ../src/main/activemq/connector + + ConnectorFactoryMap.cpp + + + + ../src/main/activemq/connector/stomp + + StompCommandReader.cpp + StompCommandWriter.cpp + StompConnector.cpp + StompConnectorFactory.cpp + StompSessionManager.cpp + + + + ../src/main/activemq/connector/stomp/commands + + CommandConstants.cpp + + + + ../src/main/activemq/connector/stomp/marshal + + Marshaler.cpp + + + + ../src/main/activemq/core + + ActiveMQConnection.cpp + ActiveMQConnectionFactory.cpp + ActiveMQConsumer.cpp + ActiveMQProducer.cpp + ActiveMQSession.cpp + ActiveMQTransaction.cpp + + + + ../src/main/activemq/exceptions + + ActiveMQException.cpp + + + + ../src/main/activemq/io + + BufferedInputStream.cpp + BufferedOutputStream.cpp + ByteArrayInputStream.cpp + ByteArrayOutputStream.cpp + EndianReader.cpp + EndianWriter.cpp + + + + ../src/main/activemq/logger + + Logger.cpp + LoggerHierarchy.cpp + LogManager.cpp + LogWriter.cpp + SimpleLogger.cpp + + + + ../src/main/activemq/network + + BufferedSocket.cpp + ServerSocket.cpp + SocketFactory.cpp + SocketInputStream.cpp + SocketOutputStream.cpp + TcpSocket.cpp + + + + ../src/main/activemq/support + + InitDirector.cpp + + + + ../src/main/activemq/transport + + IOTransport.cpp + IOTransportFactory.cpp + ResponseCorrelator.cpp + TcpTransport.cpp + TcpTransportFactory.cpp + TransportFactoryMap.cpp + + + + ../src/main/activemq/util + + Guid.cpp + StringTokenizer.cpp + + + + + + + + + + + + + + win32-msvc + + + windows + + + + win32-msvc + + + + + + win32-gcc + + win32-gcc + + + + + + unix + + + unix + + + + unix + + + + + + + + + Maven Snapshots + http://snapshots.maven.codehaus.org/maven2/ + + true + + + true + + + + + + Maven Snapshots + http://snapshots.maven.codehaus.org/maven2/ + + true + + + true + + + + diff --git a/activemq-cpp/readme.txt b/activemq-cpp/readme.txt new file mode 100644 index 0000000000..9efc91a46e --- /dev/null +++ b/activemq-cpp/readme.txt @@ -0,0 +1,109 @@ +-------------------------------------------------------------------------- +ActiveMQ CPP Library - Version 0.0.1 +-------------------------------------------------------------------------- + +This library provides a JMS like interface to an ActiveMQ broker in c++. + +Currently the Library only supports the Stomp protocol, future versions +will contain support for openwire. + +UNIT Tests +-------------------------------------------------------------------------- + +The package contains a complete set of cppunit tests. In order for you +to build an run the tests, you will need to download and install the +cppunit suite. + +http://cppunit.sourceforge.net/cppunit-wiki + +or on Fedora type + +yum install cppunit* + +Make sure that the path to the installed cpp unit library and includes is +visible in your current shell before you try building the tets. + +Integration Tests +-------------------------------------------------------------------------- + +The library also contains a set of tests that are run against a real AMQ +broker. Running these without a broker will result in failed tests. +The tests currently hardcode the broker url to be tcp://127.0.0.1:61613, +you can change this by changing the declaration in IntegrationCommon.cpp +in the test-integration src tree. + +Notes for Windows users +-------------------------------------------------------------------------- + +The builds support using the GNU compiler on Windows, we used the MinGW +package. There is an issues still outstanding with this in that the sockets +break for no reason when built this way. We therefore suggest that you +stick with using the MSVC compiler when on windows. + +There are a couple or things that you will need to setup to ensure that the +MSVC compile succeeds. + +* You need to download and install the Platform SDK if you don't have it + installed already. +* Ensure that the path to you MSVC install is set in the PATH env variable. + you can tests this buy typing cl.exe at the command line, if you get an + error complaining that its not found, then setup you PATH correctly. +* Set the INCLUDE env variable to include the path to your MSVC includes, + and the platform SDK includes. + + i.e. INCLUDE = D:\Program Files\Microsoft Visual Studio 8\VC\include;D:\Program Files\Microsoft Platform SDK\Include + +* Set the LIB env variable to include the path to your MSVC libs, and the + Platform SDK libs. + + i.e. LIB = D:\Program Files\Microsoft Visual Studio 8\VC\lib;D:\Program Files\Microsoft Platform SDK\Lib + +Maven Builds +-------------------------------------------------------------------------- + +The pacakge currently supports building the library only using maven. + +The Mojo Native plugin (from the MOJO maven plugins site) is required. + +http://mojo.codehaus.org/maven-native/native-maven-plugin/introduction.html + +On the windows platform is was necessay to download the source for this +plugin and build it locally, this shouldn't be necessary on non-windows +platforms, but if you have problems, try that first. + +You can get the latest source via subversion: + +svn co https://svn.codehaus.org/mojo/trunk/mojo/maven-native + +Once you have downloaded the source, install the plugin into your local +repository via: mvn install + +Using Maven with activemq-cpp + +* type mvn package + +This will build the library using the default target for the platform +you are on, which is release, and the gnu compiler for unix platforms, or +the MSVC compiler on windows platforms. + +Makefile Builds +-------------------------------------------------------------------------- + +The Makefile provided requires some env variable to be set + +OSTYPE: This is the OS you are on and is reflected in the names of the + makefiles. Currently your choices are Linux or Windows, both use + the GNU compiler. + +CONFIG: This is the build Mode you want to execute, i.e. debug or release. + +MAKESUPPORT_HOME: Path to the folder where the Makefiles are stored. + +There are three targets available in the Makefile, lib, test, and integration +whose output is fairly obvious. + +Using the Makefile: + +* type make to build all targets: lib, tests and integration +* type make < Target Name > to build only the target you need. +* type make clean to remove all of the object, library, and executable files. diff --git a/activemq-cpp/src/main/activemq/concurrent/Concurrent.h b/activemq-cpp/src/main/activemq/concurrent/Concurrent.h new file mode 100644 index 0000000000..6b46ca2349 --- /dev/null +++ b/activemq-cpp/src/main/activemq/concurrent/Concurrent.h @@ -0,0 +1,62 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONCURRENT_CONCURRENT_H_ +#define _ACTIVEMQ_CONCURRENT_CONCURRENT_H_ + +#include + +namespace activemq{ +namespace concurrent{ + +/** + * The synchronized macro defines a mechanism for snycronizing + * a scetion of code. The macro must be passed an object that + * implements the Syncronizable interface. + * + * The macro works by creating a for loop that will loop exactly + * once, creating a Lock object that is scoped to the loop. Once + * the loop conpletes and exits the Lock object goes out of scope + * releasing the lock on object W. For added safety the if else + * is used because not all compiles restrict the lifetime of + * loop variables to the loop, they will however restrict them + * to the scope of the else. + * + * The macro would be used as follows. + * + * X; + * + * somefunction() + * { + * syncronized(X) + * { + * // Do something that needs syncronizing. + * } + * } + */ + +#define WAIT_INFINITE 0xFFFFFFFF + +#define synchronized(W) \ + if(false){} \ + else \ + for(activemq::concurrent::Lock lock_W(W); \ + lock_W.isLocked(); lock_W.unlock()) + +}} + +#endif /*_ACTIVEMQ_CONCURRENT_CONCURRENT_H_*/ diff --git a/activemq-cpp/src/main/activemq/concurrent/Lock.h b/activemq-cpp/src/main/activemq/concurrent/Lock.h new file mode 100644 index 0000000000..2d104548dc --- /dev/null +++ b/activemq-cpp/src/main/activemq/concurrent/Lock.h @@ -0,0 +1,124 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONCURRENT_LOCK_H +#define ACTIVEMQ_CONCURRENT_LOCK_H + +// Includes. +#include + +namespace activemq{ +namespace concurrent{ + + /** + * A wrapper class around a given synchronization mechanism that + * provides automatic release upon destruction. + * @author Nathan Mittler + */ + class Lock + { + private: // Data + + /** + * Flag to indicate whether or not this object has locked the + * sync object. + */ + bool locked; + + /** + * The synchronizable object to lock/unlock. + */ + Synchronizable* syncObject; + + public: // Interface + + /** + * Constructor - initializes the object member and locks + * the object if desired. + * @param object The sync object to control + * @param intiallyLocked If true, the object will automatically + * be locked. + */ + Lock( Synchronizable* object, const bool intiallyLocked = true ) + { + try{ + syncObject = object; + locked = false; + + if( intiallyLocked ) + { + lock(); + } + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) + } + + /** + * Destructor - Unlocks the object if it is locked. + */ + virtual ~Lock() + { + try{ + if( locked ) + { + syncObject->unlock(); + } + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) + } + + /** + * Locks the object. + */ + void lock() + { + try{ + syncObject->lock(); + locked = true; + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) + } + + /** + * Unlocks the object. + */ + void unlock() + { + try{ + if(locked) + { + syncObject->unlock(); + locked = false; + } + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) + } + + /** + * Indicates whether or not the object is locked. + * @return true if the object is locked, otherwise false. + */ + bool isLocked() const{ return locked; } + }; + +}} + +#endif // ACTIVEMQ_CONCURRENT_LOCK_H diff --git a/activemq-cpp/src/main/activemq/concurrent/Mutex.cpp b/activemq-cpp/src/main/activemq/concurrent/Mutex.cpp new file mode 100644 index 0000000000..9d7e9b99f4 --- /dev/null +++ b/activemq-cpp/src/main/activemq/concurrent/Mutex.cpp @@ -0,0 +1,21 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 + +using namespace activemq::concurrent; + +//////////////////////////////////////////////////////////////////////////////// diff --git a/activemq-cpp/src/main/activemq/concurrent/Mutex.h b/activemq-cpp/src/main/activemq/concurrent/Mutex.h new file mode 100644 index 0000000000..1080c37db6 --- /dev/null +++ b/activemq-cpp/src/main/activemq/concurrent/Mutex.h @@ -0,0 +1,358 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONCURRENT_MUTEX_H +#define ACTIVEMQ_CONCURRENT_MUTEX_H + +// Includes. +#include +#include +#include +#include + +#if (defined(__unix__) || defined(unix) || defined(MACOSX)) && !defined(USG) + #ifndef unix + #define unix + #endif + + #include + #include +#endif + +#if defined(WIN32) || defined(__CYGWIN__) && !defined unix + + #include + + #if ( !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0400) + #if ( !defined(WINVER) || WINVER < 0x0400) + #pragma message ("Unsupported platform, Windows NT 4.0 or later required") + #endif + #endif + +#endif + +#include + +namespace activemq{ +namespace concurrent{ + + /** + * Creates a pthread_mutex_t object. The object is created + * such that successive locks from the same thread is allowed + * and will be successful. + * @see pthread_mutex_t + */ + class Mutex : public Synchronizable + { + private: // Data + + /** + * The mutex object. + */ + #ifdef unix + pthread_mutex_t mutex; + + std::list eventQ; + #else + CRITICAL_SECTION mutex; + + std::list eventQ; + #endif + + // Lock Status Members + int lock_count; + unsigned long lock_owner; + + public: + + /** + * Constructor - creates and initializes the mutex. + */ + Mutex() + { + #ifdef unix + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutex_init(&mutex, &attr); + pthread_mutexattr_destroy(&attr); + #else + InitializeCriticalSection(&mutex); + #endif + + lock_owner = 0; + lock_count = 0; + } + + /** + * Destructor - destroys the mutex object. + */ + virtual ~Mutex() + { + // Unlock the mutex. + unlock(); + + #ifdef unix + pthread_mutex_destroy(&mutex); + #else + DeleteCriticalSection(&mutex); + #endif + } + + /** + * Locks the object. + */ + virtual void lock() throw( exceptions::ActiveMQException ) + { + if(isLockOwner()) + { + lock_count++; + } + else + { + #ifdef unix + pthread_mutex_lock(&mutex); + #else + EnterCriticalSection(&mutex); + #endif + + lock_count = 1; + lock_owner = Thread::getId(); + } + } + + /** + * Unlocks the object. + */ + virtual void unlock() throw( exceptions::ActiveMQException ) + { + if(lock_owner == 0) + { + return; + } + + if(!isLockOwner()) + { + throw exceptions::ActiveMQException( + __FILE__, __LINE__, + "Mutex::unlock - Failed, not Lock Owner!" ); + } + + lock_count--; + + if(lock_count == 0) + { + lock_owner = 0; + + #ifdef unix + pthread_mutex_unlock(&mutex); + #else + LeaveCriticalSection(&mutex); + #endif + } + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. + */ + virtual void wait() throw( exceptions::ActiveMQException ) + { + // Delegate to the timed version + wait( WAIT_INFINITE ); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. This wait will timeout after the specified time + * interval. + */ + virtual void wait( unsigned long millisecs ) + throw( exceptions::ActiveMQException ) + { + if(!isLockOwner()) + { + throw exceptions::ActiveMQException( + __FILE__, __LINE__, + "Mutex::wait - Failed, not Lock Owner!"); + } + + // Save the current owner and Lock count as we are going to + // unlock and release for someone else to lock on potentially. + // When we come back and re-lock we want to restore to the + // state we were in before. + unsigned long lock_owner = this->lock_owner; + int lock_count = this->lock_count; + + this->lock_count = 0; + this->lock_owner = 0; + + #ifdef unix + + // Create this threads wait event + pthread_cond_t waitEvent; + pthread_cond_init(&waitEvent, NULL); + + // Store the event in the queue so that a notify can + // call it and wake up the thread. + eventQ.push_back(&waitEvent); + + int returnValue = 0; + if(millisecs != WAIT_INFINITE) + { + timeval now = {}; + gettimeofday(&now, NULL); + + timespec wait = {}; + wait.tv_sec = now.tv_sec + (millisecs / 1000); + wait.tv_nsec = (now.tv_usec * 1000) + ((millisecs % 1000) * 1000000); + + if(wait.tv_nsec > 1000000000) + { + wait.tv_sec++; + wait.tv_nsec -= 1000000000; + } + + returnValue = pthread_cond_timedwait(&waitEvent, &mutex, &wait); + } + else + { + returnValue = pthread_cond_wait(&waitEvent, &mutex); + } + + // If the wait did not succeed for any reason, remove it + // from the queue. + if( returnValue != 0 ){ + std::list::iterator iter = eventQ.begin(); + for( ; iter != eventQ.end(); ++iter ){ + if( *iter == &waitEvent ){ + eventQ.erase(iter); + break; + } + } + } + + // Destroy our wait event now, the notify method will have removed it + // from the event queue. + pthread_cond_destroy(&waitEvent); + + #else + + // Create the event to wait on + HANDLE waitEvent = CreateEvent( NULL, false, false, NULL ); + + if(waitEvent == NULL) + { + throw exceptions::ActiveMQException( + __FILE__, __LINE__, + "Mutex::Mutex - Failed Creating Event." ); + } + + eventQ.push_back( waitEvent ); + + // Release the Lock + LeaveCriticalSection( &mutex ); + + // Wait for a signal + WaitForSingleObject( waitEvent, millisecs ); + + // Reaquire the Lock + EnterCriticalSection( &mutex ); + + // Clean up the event, the notif methods will have + // already poped it from the queue. + CloseHandle( waitEvent ); + + #endif + + // restore the owner + this->lock_owner = lock_owner; + this->lock_count = lock_count; + } + + /** + * Signals a waiter on this object that it can now wake + * up and continue. + */ + virtual void notify() throw( exceptions::ActiveMQException ) + { + if( !isLockOwner() ) + { + throw exceptions::ActiveMQException( + __FILE__, __LINE__, + "Mutex::Notify - Failed, not Lock Owner!" ); + } + + if( !eventQ.empty() ) + { + #ifdef unix + pthread_cond_signal( eventQ.front() ); + eventQ.pop_front(); + #else + SetEvent( eventQ.front() ); + eventQ.pop_front(); + #endif + } + } + + /** + * Signals the waiters on this object that it can now wake + * up and continue. + */ + virtual void notifyAll() throw( exceptions::ActiveMQException ) + { + if(!isLockOwner()) + { + throw exceptions::ActiveMQException( + __FILE__, __LINE__, + "Mutex::NotifyAll - Failed, not Lock Owner!" ); + } + + #ifdef unix + + while(!eventQ.empty()) + { + pthread_cond_signal( eventQ.front() ); + eventQ.pop_front(); + } + + #else + + while(!eventQ.empty()) + { + SetEvent( eventQ.front() ); + eventQ.pop_front(); + } + + #endif + } + + private: + + /** + * Check if the calling thread is the Lock Owner + */ + bool isLockOwner() + { + return lock_owner == Thread::getId(); + } + + }; + +}} + +#endif // ACTIVEMQ_CONCURRENT_MUTEX_H diff --git a/activemq-cpp/src/main/activemq/concurrent/PooledThread.cpp b/activemq-cpp/src/main/activemq/concurrent/PooledThread.cpp new file mode 100644 index 0000000000..e11558cc3a --- /dev/null +++ b/activemq-cpp/src/main/activemq/concurrent/PooledThread.cpp @@ -0,0 +1,161 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 +#include +#include +#include + +#include + +using namespace activemq; +using namespace activemq::concurrent; + +//////////////////////////////////////////////////////////////////////////////// +LOGCMS_INITIALIZE(logger, PooledThread, "com.activemq.concurrent.PooledThread"); + +//////////////////////////////////////////////////////////////////////////////// +PooledThread::PooledThread(ThreadPool* pool) +{ + if(pool == NULL) + { + throw exceptions::IllegalArgumentException( __FILE__, __LINE__, + "PooledThread::PooledThread"); + } + + busy = false; + done = false; + + listener = NULL; + + // Store our Pool. + this->pool = pool; +} + +//////////////////////////////////////////////////////////////////////////////// +PooledThread::~PooledThread() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +void PooledThread::run(void) +{ + ThreadPool::Task task; + + try + { + while(!done) + { + //LOGCMS_DEBUG(logger, "PooledThread::run - Entering deQ"); + + // Blocks until there something to be done + task = pool->deQueueTask(); + + //LOGCMS_DEBUG(logger, "PooledThread::run - Exited deQ"); + + // Check if the Done Flag is set, in case it happened while we + // were waiting for a task + if(done) + { + break; + } + + // If we got here and the runnable was null then something + // bad must have happened. Throw an Exception and bail. + if(!task.first) + { + throw exceptions::ActiveMQException( __FILE__, __LINE__, + "PooledThread::run - Retrieive NULL task from Pool."); + } + + // Got some work to do, so set flag to busy + busy = true; + + // Inform a listener that we are going to start + if(listener) + { + /*LOGCMS_DEBUG(logger, + "PooledThread::run - Inform Listener we are starting");*/ + listener->onTaskStarted(this); + } + + // Perform the work + task.first->run(); + + /*LOGCMS_DEBUG(logger, + "PooledThread::run - Inform Task Listener we are done");*/ + + // Notify the Task listener that we are done + task.second->onTaskComplete(task.first); + + // Inform a listener that we are going to stop and wait + // for a new task + if(listener) + { + /*LOGCMS_DEBUG(logger, + "PooledThread::run - Inform Listener we are done");*/ + listener->onTaskCompleted(this); + } + + // Set flag to inactive, we will wait for work + busy = false; + } + } + catch(exceptions::ActiveMQException& ex) + { + ex.setMark( __FILE__, __LINE__ ); + + // Notify the Task owner + if(task.first && task.second) + { + task.second->onTaskException(task.first, ex); + } + + busy = false; + + // Notify the PooledThreadListener + if(listener) + { + listener->onTaskException(this, ex); + } + } + catch(...) + { + exceptions::ActiveMQException ex( + __FILE__, __LINE__, + "PooledThread::run - Caught Unknown Exception"); + + // Notify the Task owner + if(task.first && task.second) + { + task.second->onTaskException(task.first, ex); + } + + busy = false; + + // Notify the PooledThreadListener + if(listener) + { + listener->onTaskException(this, ex); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +void PooledThread::stop(void) throw ( cms::CMSException ) +{ + done = true; +} diff --git a/activemq-cpp/src/main/activemq/concurrent/PooledThread.h b/activemq-cpp/src/main/activemq/concurrent/PooledThread.h new file mode 100644 index 0000000000..d66f431888 --- /dev/null +++ b/activemq-cpp/src/main/activemq/concurrent/PooledThread.h @@ -0,0 +1,105 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONCURRENT_POOLEDTHREAD_H_ +#define _ACTIVEMQ_CONCURRENT_POOLEDTHREAD_H_ + +#include +#include +#include +#include + +#include +#include + +namespace activemq{ +namespace concurrent{ + + class ThreadPool; + + class PooledThread : public Thread, public cms::Stoppable + { + private: + + // Is this thread currently processing something + bool busy; + + // Boolean flag indicating thread should stop + bool done; + + // Listener for Task related events + PooledThreadListener* listener; + + // The thread pool this Pooled Thread is Servicing + ThreadPool* pool; + + // Logger Init + LOGCMS_DECLARE(logger); + + public: + + /** + * Constructor + */ + PooledThread(ThreadPool* pool); + + /** + * Destructor + */ + virtual ~PooledThread(void); + + /** + * Run Method for this object waits for something to be + * enqueued on the ThreadPool and then grabs it and calls + * its run method. + */ + virtual void run(void); + + /** + * Stops the Thread, thread will complete its task if currently + * running one, and then die. Does not block. + */ + virtual void stop(void) throw ( cms::CMSException ); + + /** + * Checks to see if the thread is busy, if busy it means + * that this thread has taken a task from the ThreadPool's + * queue and is processing it. + */ + virtual bool isBusy(void) { return busy; } + + /** + * Adds a listener to this PooledThread to be + * notified when this thread starts and completes a task. + */ + virtual void setPooledThreadListener(PooledThreadListener* listener) + { + this->listener = listener; + } + + /** + * Removes a listener for this PooledThread to be + * notified when this thread starts and completes a task. + */ + virtual PooledThreadListener* getPooledThreadListener(void) + { + return this->listener; + } + }; + +}} + +#endif /*_ACTIVEMQ_CONCURRENT_POOLEDTHREAD_H_*/ diff --git a/activemq-cpp/src/main/activemq/concurrent/PooledThreadListener.h b/activemq-cpp/src/main/activemq/concurrent/PooledThreadListener.h new file mode 100644 index 0000000000..da2739ea1b --- /dev/null +++ b/activemq-cpp/src/main/activemq/concurrent/PooledThreadListener.h @@ -0,0 +1,66 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONCURRENT_POOLEDTHREADLISTENER_H_ +#define _ACTIVEMQ_CONCURRENT_POOLEDTHREADLISTENER_H_ + +#include + +namespace activemq{ +namespace concurrent{ + + //forward declare + class PooledThread; + + class PooledThreadListener + { + public: + + /** + * Destructor + */ + virtual ~PooledThreadListener(void) {} + + /** + * Called by a pooled thread when it is about to begin + * executing a new task. + * @param Pointer to the Pooled Thread that is making this call + */ + virtual void onTaskStarted(PooledThread* thread) = 0; + + /** + * Called by a pooled thread when it has completed a task + * and is going back to waiting for another task to run + * @param Pointer the the Pooled Thread that is making this call. + */ + virtual void onTaskCompleted(PooledThread* thread) = 0; + + /** + * Called by a pooled thread when it has encountered an exception + * while running a user task, after receiving this notification + * the callee should assume that the PooledThread is now no longer + * running. + * @param Pointer to the Pooled Thread that is making this call + * @param The Exception that occured. + */ + virtual void onTaskException(PooledThread* thread, + exceptions::ActiveMQException& ex) = 0; + + }; + +}} + +#endif /*_ACTIVEMQ_CONCURRENT_POOLEDTHREADLISTENER_H_*/ diff --git a/activemq-cpp/src/main/activemq/concurrent/Runnable.h b/activemq-cpp/src/main/activemq/concurrent/Runnable.h new file mode 100644 index 0000000000..ee8f6a624a --- /dev/null +++ b/activemq-cpp/src/main/activemq/concurrent/Runnable.h @@ -0,0 +1,41 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONCURRENT_RUNNABLE_H_ +#define ACTIVEMQ_CONCURRENT_RUNNABLE_H_ + +namespace activemq{ +namespace concurrent{ + + /** + * Interface for a runnable object - defines a task + * that can be run by a thread. + */ + class Runnable{ + public: + + virtual ~Runnable(){} + + /** + * Run method - called by the Thread class in the context + * of the thread. + */ + virtual void run() = 0; + }; + +}} + +#endif /*ACTIVEMQ_CONCURRENT_RUNNABLE_H_*/ diff --git a/activemq-cpp/src/main/activemq/concurrent/Synchronizable.h b/activemq-cpp/src/main/activemq/concurrent/Synchronizable.h new file mode 100644 index 0000000000..5377f844bc --- /dev/null +++ b/activemq-cpp/src/main/activemq/concurrent/Synchronizable.h @@ -0,0 +1,87 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONCURRENT_SYNCHRONIZABLE_H +#define ACTIVEMQ_CONCURRENT_SYNCHRONIZABLE_H + +#include + +namespace activemq{ +namespace concurrent{ + + /** + * The interface for all synchronizable objects (that is, objects + * that can be locked and unlocked). + */ + class Synchronizable + { + public: // Abstract Interface + + virtual ~Synchronizable(){} + + /** + * Locks the object. + * @throws ActiveMQException + */ + virtual void lock() throw(exceptions::ActiveMQException) = 0; + + /** + * Unlocks the object. + * @throws ActiveMQException + */ + virtual void unlock() throw(exceptions::ActiveMQException) = 0; + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. + * @throws ActiveMQException + */ + virtual void wait() throw(exceptions::ActiveMQException) = 0; + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. This wait will timeout after the specified time + * interval. + * @param time in millisecsonds to wait, or WAIT_INIFINITE + * @throws ActiveMQException + */ + virtual void wait(unsigned long millisecs) + throw(exceptions::ActiveMQException) = 0; + + /** + * Signals a waiter on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + * @throws ActiveMQException + */ + virtual void notify() throw(exceptions::ActiveMQException) = 0; + + /** + * Signals the waiters on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + * @throws ActiveMQException + */ + virtual void notifyAll() throw(exceptions::ActiveMQException) = 0; + + }; + +}} + +#endif /*ACTIVEMQ_CONCURRENT_SYNCHRONIZABLE_H*/ diff --git a/activemq-cpp/src/main/activemq/concurrent/TaskListener.h b/activemq-cpp/src/main/activemq/concurrent/TaskListener.h new file mode 100644 index 0000000000..0366621819 --- /dev/null +++ b/activemq-cpp/src/main/activemq/concurrent/TaskListener.h @@ -0,0 +1,56 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONCURRENT_TASKLISTENER_H_ +#define _ACTIVEMQ_CONCURRENT_TASKLISTENER_H_ + +#include +#include + +namespace activemq{ +namespace concurrent{ + +class TaskListener +{ +public: + + /** + * Destructor + */ + virtual ~TaskListener() {} + + /** + * Called when a queued task has completed, the task that + * finished is passed along for user consumption + * @param Runnable Pointer to the task that finished + */ + virtual void onTaskComplete(Runnable* task) = 0; + + /** + * Called when a queued task has thrown an exception while + * being run. The Callee should assume that this was an + * unrecoverable exeption and that this task is now defunct. + * @param Runnable Pointer to the task + * @param The ActiveMQException that was thrown. + */ + virtual void onTaskException(Runnable* task, + exceptions::ActiveMQException& ex) = 0; + +}; + +}} + +#endif /*_ACTIVEMQ_CONCURRENT_TASKLISTENER_H_*/ diff --git a/activemq-cpp/src/main/activemq/concurrent/Thread.cpp b/activemq-cpp/src/main/activemq/concurrent/Thread.cpp new file mode 100644 index 0000000000..d5729ff638 --- /dev/null +++ b/activemq-cpp/src/main/activemq/concurrent/Thread.cpp @@ -0,0 +1,173 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "Thread.h" +#include + +#ifdef unix + #include // EINTR + extern int errno; +#else + #include // _endthreadex +#endif + +#include + +using namespace activemq; +using namespace activemq::concurrent; + +#ifdef unix +static struct ThreadStaticInitializer { + // Thread Attribute member + pthread_attr_t threadAttribute; + // Static Initializer: + ThreadStaticInitializer() { + pthread_attr_init (&threadAttribute); + pthread_attr_setdetachstate (&threadAttribute, PTHREAD_CREATE_JOINABLE); + } +} threadStaticInitializer; +#endif + +//////////////////////////////////////////////////////////////////////////////// +Thread::Thread() +{ + task = this; + started = false; + joined = false; +} + +//////////////////////////////////////////////////////////////////////////////// +Thread::Thread( Runnable* task ) +{ + this->task = task; + started = false; + joined = false; +} + +//////////////////////////////////////////////////////////////////////////////// +Thread::~Thread() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +void Thread::start() throw ( exceptions::ActiveMQException ) +{ + if (this->started) { + throw exceptions::ActiveMQException( __FILE__, __LINE__, + "Thread already started"); + } + +#ifdef unix + + pthread_attr_init (&attributes); + pthread_attr_setdetachstate (&attributes, PTHREAD_CREATE_JOINABLE); + int err = pthread_create ( + &this->threadHandle, + &attributes, + runCallback, + this); + if (err != 0) { + throw exceptions::ActiveMQException( __FILE__, __LINE__, + "Coud not start thread"); + } + +#else + + unsigned int threadId = 0; + this->threadHandle = + (HANDLE)_beginthreadex(NULL, 0, runCallback, this, 0, &threadId); + if (this->threadHandle == NULL) { + throw exceptions::ActiveMQException( __FILE__, __LINE__, + "Coud not start thread"); + } + +#endif + + // Mark the thread as started. + started = true; +} + +//////////////////////////////////////////////////////////////////////////////// +void Thread::join() throw( exceptions::ActiveMQException ) +{ + if (!this->started) { + throw exceptions::ActiveMQException( __FILE__, __LINE__, + "Thread::join() called without having called Thread::start()"); + } + if (!this->joined) { + +#ifdef unix + pthread_join(this->threadHandle, NULL); +#else + WaitForSingleObject (this->threadHandle, INFINITE); +#endif + + } + this->joined = true; +} + +//////////////////////////////////////////////////////////////////////////////// +void Thread::sleep(int millisecs) +{ +#ifdef unix + struct timespec rec, rem; + rec.tv_sec = millisecs / 1000; + rec.tv_nsec = (millisecs % 1000) * 1000000; + while( nanosleep( &rec, &rem ) == -1 ){ + if( errno != EINTR ){ + break; + } + } + +#else + Sleep (millisecs); +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +unsigned long Thread::getId(void) +{ + #ifdef unix + return (long)(pthread_self()); + #else + return GetCurrentThreadId(); + #endif +} + +//////////////////////////////////////////////////////////////////////////////// +#ifdef unix +void* +#else +unsigned int WINAPI +#endif +Thread::runCallback (void* param) +{ + // Get the instance. + Thread* thread = (Thread*)param; + + // Invoke run on the task. + thread->task->run(); + +#ifdef unix + return NULL; +#else + // Return 0 if no exception was threwn. Otherwise -1. + _endthreadex(0); // Needed when using threads and CRT in Windows. Otherwise memleak can appear. + return 0; +#endif +} + diff --git a/activemq-cpp/src/main/activemq/concurrent/Thread.h b/activemq-cpp/src/main/activemq/concurrent/Thread.h new file mode 100644 index 0000000000..f6f3cbf7bd --- /dev/null +++ b/activemq-cpp/src/main/activemq/concurrent/Thread.h @@ -0,0 +1,131 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONCURRENT_THREAD_H +#define ACTIVEMQ_CONCURRENT_THREAD_H + +#include +#include +#include +#include + +#if (defined(__unix__) || defined(unix) || defined(MACOSX)) && !defined(USG) + + #ifndef unix + #define unix + #endif + + #include +#else + #include +#endif + +namespace activemq{ +namespace concurrent{ + + /** + * Basic thread class - mimics the Java Thread. Derived classes may + * implement the run method, or this class can be used as is with + * a provided Runnable delegate. + */ + class Thread : public Runnable + { + private: + + /** + * The task to be run by this thread, defaults to + * this thread object. + */ + Runnable* task; + + #ifdef unix + pthread_attr_t attributes; + pthread_t threadHandle ; + #else + HANDLE threadHandle ; + #endif + + /** + * Started state of this thread. + */ + bool started; + + /** + * Indicates whether the thread has already been + * joined. + */ + bool joined; + + public: + + Thread(); + Thread( Runnable* task ); + virtual ~Thread(); + + /** + * Creates a system thread and starts it in a joinable mode. + * Upon creation, the + * run() method of either this object or the provided Runnable + * object will be invoked in the context of this thread. + * @exception runtime_error is thrown if the system could + * not start the thread. + */ + virtual void start() throw (exceptions::ActiveMQException); + + /** + * Wait til the thread exits. This is when the run() + * method has returned or has thrown an exception. + * If an exception was thrown in the run() method, + * join() will return the thrown exception. Otherwise + * (if run() returned normally), join() will + * return NULL. + */ + virtual void join() throw (exceptions::ActiveMQException); + + /** + * Default implementation of the run method - does nothing. + */ + virtual void run(){}; + + public: + + /** + * Halts execution of the calling thread for a specified no of millisec. + * + * Note that this method is a static method that applies to the + * calling thread and not to the thread object. + */ + static void sleep(int millisecs); + + /** + * Obtains the Thread Id of the current thread + * @return Thread Id + */ + static unsigned long getId(void); + + private: + + // Internal thread handling + #ifdef unix + static void* runCallback (void* param); + #else + static unsigned int WINAPI runCallback (void* param); + #endif + } ; + +}} + +#endif /*ACTIVEMQ_CONCURRENT_THREAD_H*/ diff --git a/activemq-cpp/src/main/activemq/concurrent/ThreadPool.cpp b/activemq-cpp/src/main/activemq/concurrent/ThreadPool.cpp new file mode 100644 index 0000000000..1699865681 --- /dev/null +++ b/activemq-cpp/src/main/activemq/concurrent/ThreadPool.cpp @@ -0,0 +1,344 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 +#include +#include + +#ifdef min +#undef min +#endif + +#include +#include + +using namespace std; +using namespace activemq; +using namespace activemq::concurrent; + +//////////////////////////////////////////////////////////////////////////////// +LOGCMS_INITIALIZE(logger, ThreadPool, "com.activemq.concurrent.ThreadPool"); +LOGCMS_INITIALIZE(marker, ThreadPool, "com.activemq.concurrent.ThreadPool.Marker"); + +//////////////////////////////////////////////////////////////////////////////// +ThreadPool ThreadPool::instance; + +//////////////////////////////////////////////////////////////////////////////// +ThreadPool::ThreadPool(void) +{ + maxThreads = DEFAULT_MAX_POOL_SIZE; + blockSize = DEFAULT_MAX_BLOCK_SIZE; + freeThreads = 0; + + shutdown = false; +} + +//////////////////////////////////////////////////////////////////////////////// +ThreadPool::~ThreadPool(void) +{ + try + { + std::vector::iterator itr = pool.begin(); + + // Stop all the threads + for(; itr != pool.end(); ++itr) + { + (*itr)->stop(); + } + + // Set the shutdown flag so that the DeQueue methods all quit + // when we interrupt them. + shutdown = true; + + synchronized(&queue) + { + // Signal the Queue so that all waiters are notified + queue.notifyAll(); + } + + // Wait for everyone to die + for(itr = pool.begin(); itr != pool.end(); ++itr) + { + (*itr)->join(); + } + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ThreadPool::queueTask(ThreadPool::Task task) + throw ( exceptions::ActiveMQException ) +{ + try + { + if(!task.first || !task.second) + { + throw exceptions::IllegalArgumentException( __FILE__, __LINE__, + "ThreadPool::QueueTask - Invalid args for Task"); + } + + //LOGCMS_DEBUG(logger, "ThreadPool::QueueTask - syncing on queue"); + + synchronized(&queue) + { + //LOGCMS_DEBUG(logger, "ThreadPool::QueueTask - sync'd, synching pool"); + + // If there's nobody open to do work, then create some more + // threads to handle the work. + if(freeThreads == 0) + { + AllocateThreads(blockSize); + } + + //LOGCMS_DEBUG(logger, "ThreadPool::QueueTask - pushing task"); + + // queue the new work. + queue.push(task); + + //LOGCMS_DEBUG(logger, "ThreadPool::QueueTask - calling notify"); + + // Inform waiters that we put some work on the queue. + queue.notify(); + } + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +ThreadPool::Task ThreadPool::deQueueTask(void) + throw ( exceptions::ActiveMQException ) +{ + try + { + //LOGCMS_DEBUG(logger, "ThreadPool::DeQueueTask - syncing on queue"); + + synchronized(&queue) + { + /*LOGCMS_DEBUG(logger, + "ThreadPool::DeQueueTask - sync'd checking queue empty");*/ + + // Wait for work, wait in a while loop since another thread could + // be waiting for a lock and get the work before we get woken up + // from our wait. + while(queue.empty() && !shutdown) + { + //LOGCMS_DEBUG(logger, "ThreadPool::DeQueueTask - Q empty, waiting"); + + queue.wait(); + + //LOGCMS_DEBUG(logger, "ThreadPool::DeQueueTask - done waiting"); + } + + // Don't give more work if we are closing down + if(shutdown) + { + return Task(); + } + + // check size again. + if(queue.empty()) + { + throw exceptions::ActiveMQException( __FILE__, __LINE__, + "ThreadPool::DeQueueUserWorkItem - Empty Taskn, not in shutdown."); + } + + //LOGCMS_DEBUG(logger, "ThreadPool::DeQueueTask - popping task"); + + // not empty so get the new work to do + return queue.pop(); + } + + return Task(); + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ThreadPool::reserve(unsigned long size) +{ + try{ + synchronized(&poolLock) + { + if(size < pool.size() || pool.size() == maxThreads) + { + return; + } + + // How many do we reserve + unsigned long allocCount = size - pool.size(); + + // Allocate the new Threads + AllocateThreads(allocCount); + } + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ThreadPool::setMaxThreads(unsigned long maxThreads) +{ + try + { + synchronized(&poolLock) + { + if(maxThreads == 0) + { + // Caller tried to do something stupid, ignore them. + return; + } + + this->maxThreads = maxThreads; + } + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ThreadPool::setBlockSize(unsigned long blockSize) +{ + try + { + if(blockSize <= 0) + { + // User tried something dumb, protect them from themselves + return; + } + + synchronized(&poolLock) + { + this->blockSize = blockSize; + } + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ThreadPool::AllocateThreads(unsigned long count) +{ + try + { + if(pool.size() >= maxThreads) + { + return; + } + + synchronized(&poolLock) + { + // Take the min of alloc size of maxThreads since we don't + // want anybody sneaking eaxtra threads in, greedy bastards. + count = std::min(count, maxThreads - pool.size()); + + // Each time we create a thread we increment the free Threads + // counter, but before we call start so that the Thread doesn't + // get ahead of us. + for(unsigned long i = 0; i < count; ++i) + { + pool.push_back(new PooledThread(this)); + pool.back()->setPooledThreadListener(this); + freeThreads++; + pool.back()->start(); + } + } + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ThreadPool::onTaskStarted(PooledThread* thread) +{ + try + { + synchronized(&poolLock) + { + freeThreads--; + + // Now that this callback has decremented the free threads coutner + // let check if there is any outstanding work to be done and no + // threads to handle it. This could happen if the QueueTask + // method was called successively without any of the PooledThreads + // having a chance to wake up and service the queue. This would + // cause the number of Task to exceed the number of free threads + // once the Threads got a chance to wake up and service the queue + if(freeThreads == 0 && !queue.empty()) + { + // Allocate a new block of threads + AllocateThreads(blockSize); + } + } + + //LOGCMS_DEBUG(logger, "ThreadPool::onTaskStarted:"); + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ThreadPool::onTaskCompleted(PooledThread* thread) +{ + try + { + synchronized(&poolLock) + { + freeThreads++; + } + + //LOGCMS_DEBUG(logger, "ThreadPool::onTaskCompleted: "); + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ThreadPool::onTaskException( + PooledThread* thread, + exceptions::ActiveMQException& ex) +{ + //LOGCMS_DEBUG(logger, "ThreadPool::onTaskException: "); + + try + { + synchronized(&poolLock) + { + // Delete the thread that had the exception and start a new + // one to take its place. + freeThreads--; + + std::vector::iterator itr = + std::find(pool.begin(), pool.end(), thread); + + if(itr != pool.end()) + { + pool.erase(itr); + } + + // Bye-Bye Thread Object + delete thread; + + // Now allocate a replacement + AllocateThreads(1); + } + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) +} + diff --git a/activemq-cpp/src/main/activemq/concurrent/ThreadPool.h b/activemq-cpp/src/main/activemq/concurrent/ThreadPool.h new file mode 100644 index 0000000000..cc5a4aaf02 --- /dev/null +++ b/activemq-cpp/src/main/activemq/concurrent/ThreadPool.h @@ -0,0 +1,239 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONCURRENT_THREADPOOL_H_ +#define _ACTIVEMQ_CONCURRENT_THREADPOOL_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace activemq{ +namespace concurrent{ + + /** + * Defines a Thread Pool object that implements the functionality + * of pooling threads to perform user tasks. The Thread Poll has + * max size that it will grow to. The thread pool allocates threads + * in blocks. When there are no waiting worker threads and a task + * is queued then a new batch is allocated. The user can specify + * the size of the blocks, otherwise a default value is used. + *

+ * When the user queues a task they must also queue a listner to + * be notified when the task has completed, this provides the user + * with a mechanism to know when a task object can be freed. + *

+ * To have the Thread Pool perform a task, the user enqueue's an + * object that implements the Runnable insterface and + * one of the worker threads will executing it in its thread context. + */ + class ThreadPool : public PooledThreadListener + { + public: + + // Constants + static const size_t DEFAULT_MAX_POOL_SIZE = 10; + static const size_t DEFAULT_MAX_BLOCK_SIZE = 3; + + // Types + typedef std::pair Task; + + private: + + // Vector of threads that this object has created for its pool. + std::vector< PooledThread* > pool; + + // Queue of Task that are in need of completion + util::Queue queue; + + // Max number of Threads this Pool can contian + unsigned long maxThreads; + + // Max number of tasks that can be allocated at a time + unsigned long blockSize; + + // boolean flag use to indocate that this object is shutting down. + bool shutdown; + + // Count of threads that are currently free to perfom some work. + unsigned long freeThreads; + + // Mutex for locking operations that affect the pool. + Mutex poolLock; + + // Logger Init + LOGCMS_DECLARE(logger); + LOGCMS_DECLARE(marker); + + private: // Statics + + // The singleton instance of this class + static ThreadPool instance; + + public: + + /** + * Constructor + */ + ThreadPool(void); + + /** + * Destructor + */ + virtual ~ThreadPool(void); + + /** + * Queue a task to be completed by one of the Pooled Threads. + * tasks are serviced as soon as a PooledThread + * is available to run it. + * @param object that derives from Runnable + * @throws ActiveMQException + */ + virtual void queueTask(Task task) + throw ( exceptions::ActiveMQException ); + + /** + * DeQueue a task to be completed by one of the Pooled Threads. + * A caller of this method will block until there is something + * in the tasks queue, therefore care must be taken when calling + * this function. Normally clients of ThreadPool don't use + * this, only the PooledThread objects owned by + * this ThreadPool. + * @return object that derives from Runnable + * @throws ActiveMQException + */ + virtual Task deQueueTask(void) + throw ( exceptions::ActiveMQException ); + + /** + * Returns the current number of Threads in the Pool, this is + * how many there are now, not how many are active or the max + * number that might exist. + * @return integer number of threads in existance. + */ + virtual unsigned long getPoolSize(void) const { return pool.size(); } + + /** + * Returns the current backlog of items in the tasks queue, this + * is how much work is still waiting to get done. + * @return number of outstanding tasks. + */ + virtual unsigned long getBacklog(void) const { return queue.size(); } + + /** + * Ensures that there is at least the specified number of Threads + * allocated to the pool. If the size is greater than the MAX + * number of threads in the pool, then only MAX threads are + * reservved. If the size is smaller than the number of threads + * currently in the pool, than nothing is done. + * @param number of threads to reserve. + */ + virtual void reserve(unsigned long size); + + /** + * Get the Max Number of Threads this Pool can contain + * @return max size + */ + virtual unsigned long getMaxThreads(void) const { return maxThreads; } + + /** + * Sets the Max number of threads this pool can contian. + * if this value is smaller than the current size of the + * pool nothing is done. + */ + virtual void setMaxThreads(unsigned long maxThreads); + + /** + * Gets the Max number of threads that can be allocated at a time + * when new threads are needed. + * @return max Thread Block Size + */ + virtual unsigned long getBlockSize(void) const { return blockSize; } + + /** + * Sets the Max number of Threads that can be allocated at a time + * when the Thread Pool determines that more Threads are needed. + * @param Max Thread Block Size + */ + virtual void setBlockSize(unsigned long blockSize); + + /** + * Returns the current number of available threads in the pool, threads + * that are performing a user task are considered unavailable. This value + * could change immeadiately after calling as Threads could finish right + * after and be available again. This is informational only. + * @return totoal free threads + */ + virtual unsigned long getFreeThreadCount(void) const { return freeThreads; } + + public: // PooledThreadListener Callbacks + + /** + * Called by a pooled thread when it is about to begin + * executing a new task. This will decrement the available + * threads counter so that this object knows when there are + * no more free threads and must create new ones. + * @param Pointer to the Pooled Thread that is making this call + */ + virtual void onTaskStarted(PooledThread* thread); + + /** + * Called by a pooled thread when it has completed a task + * and is going back to waiting for another task to run, + * this will increment the free threads counter. + * @param Pointer the the Pooled Thread that is making this call. + */ + virtual void onTaskCompleted(PooledThread* thread); + + /** + * Called by a pooled thread when it has encountered an exception + * while running a user task, after receiving this notification + * the callee should assume that the PooledThread is now no longer + * running. + * @param Pointer to the Pooled Thread that is making this call + * @param The Exception that occured. + */ + virtual void onTaskException(PooledThread* thread, + exceptions::ActiveMQException& ex); + + public: // Statics + + /** + * Return the one and only Thread Pool instance. + * @return The Thread Pool Pointer + */ + static ThreadPool* getInstance(void) { return &instance; } + + private: + + /** + * Allocates the requested ammount of Threads, won't exceed + * maxThreads. + * @param the number of threads to create + */ + void AllocateThreads(unsigned long count); + + }; + +}} + +#endif /*_ACTIVEMQ_CONCURRENT_THREADPOOL_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/Connector.h b/activemq-cpp/src/main/activemq/connector/Connector.h new file mode 100644 index 0000000000..4c1b8edae6 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/Connector.h @@ -0,0 +1,317 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_CONNECTOR_H_ +#define _ACTIVEMQ_CONNECTOR_CONNECTOR_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace connector{ + + // Forward declarations. + class Connector + : + public cms::Startable, + public cms::Closeable + { + public: // Connector Types + + enum AckType + { + DeliveredAck = 0, // Message delivered but not consumed + PoisonAck = 1, // Message could not be processed due to + // poison pill but discard anyway + ConsumedAck = 2 // Message consumed, discard + }; + + public: + + virtual ~Connector(void) {}; + + /** + * Gets the Client Id for this connection, if this + * connection has been closed, then this method returns "" + * @return Client Id String + */ + virtual std::string getClientId(void) const = 0; + + /** + * Gets a reference to the Transport that this connection + * is using. + * @param reference to a transport + * @throws InvalidStateException if the Transport is not set + */ + virtual transport::Transport& getTransport(void) const + throw (exceptions::InvalidStateException ) = 0; + + /** + * Creates a Session Info object for this connector + * @param Acknowledgement Mode of the Session + * @returns Session Info Object + * @throws ConnectorException + */ + virtual SessionInfo* createSession( + cms::Session::AcknowledgeMode ackMode) + throw( ConnectorException ) = 0; + + /** + * Create a Consumer for the given Session + * @param Destination to Subscribe to. + * @param Session Information. + * @return Consumer Information + * @throws ConnectorException + */ + virtual ConsumerInfo* createConsumer( + cms::Destination* destination, + SessionInfo* session, + const std::string& selector = "") + throw ( ConnectorException ) = 0; + + /** + * Create a Durable Consumer for the given Session + * @param Topic to Subscribe to. + * @param Session Information. + * @param name of the Durable Topic + * @param Selector + * @param if set, inhibits the delivery of messages + * published by its own connection + * @return Consumer Information + * @throws ConnectorException + */ + virtual ConsumerInfo* createDurableConsumer( + cms::Topic* topic, + SessionInfo* session, + const std::string& name, + const std::string& selector = "", + bool noLocal = false) + throw ( ConnectorException ) = 0; + + /** + * Create a Consumer for the given Session + * @param Destination to Subscribe to. + * @param Session Information. + * @return Producer Information + * @throws ConnectorException + */ + virtual ProducerInfo* createProducer( + cms::Destination* destination, + SessionInfo* session) + throw ( ConnectorException ) = 0; + + /** + * Creates a Topic given a name and session info + * @param Topic Name + * @param Session Information + * @return a newly created Topic Object + * @throws ConnectorException + */ + virtual cms::Topic* createTopic(const std::string& name, + SessionInfo* session) + throw ( ConnectorException ) = 0; + + /** + * Creates a Queue given a name and session info + * @param Queue Name + * @param Session Information + * @return a newly created Queue Object + * @throws ConnectorException + */ + virtual cms::Queue* createQueue(const std::string& name, + SessionInfo* session) + throw ( ConnectorException ) = 0; + + /** + * Creates a Temporary Topic given a name and session info + * @param Temporary Topic Name + * @param Session Information + * @return a newly created Temporary Topic Object + * @throws ConnectorException + */ + virtual cms::TemporaryTopic* createTemporaryTopic( + SessionInfo* session) + throw ( ConnectorException ) = 0; + + /** + * Creates a Temporary Queue given a name and session info + * @param Temporary Queue Name + * @param Session Information + * @return a newly created Temporary Queue Object + * @throws ConnectorException + */ + virtual cms::TemporaryQueue* createTemporaryQueue( + SessionInfo* session) + throw ( ConnectorException ) = 0; + + /** + * Sends a Message + * @param The Message to send. + * @param Producer Info for the sender of this message + * @throws ConnectorException + */ + virtual void send(cms::Message* message, ProducerInfo* producerInfo) + throw ( ConnectorException ) = 0; + + /** + * Sends a set of Messages + * @param List of Messages to send. + * @param Producer Info for the sender of this message + * @throws ConnectorException + */ + virtual void send(std::list& messages, + ProducerInfo* producerInfo) + throw ( ConnectorException ) = 0; + + /** + * Acknowledges a Message + * @param An ActiveMQMessage to Ack. + * @throws ConnectorException + */ + virtual void acknowledge(const SessionInfo* session, + const cms::Message* message, + AckType ackType = ConsumedAck) + throw ( ConnectorException ) = 0; + + /** + * Starts a new Transaction. + * @param Session Information + * @throws ConnectorException + */ + virtual TransactionInfo* startTransaction( + SessionInfo* session) + throw ( ConnectorException ) = 0; + + /** + * Commits a Transaction. + * @param The Transaction information + * @param Session Information + * @throws ConnectorException + */ + virtual void commit(TransactionInfo* transaction, + SessionInfo* session) + throw ( ConnectorException ) = 0; + + /** + * Rolls back a Transaction. + * @param The Transaction information + * @param Session Information + * @throws ConnectorException + */ + virtual void rollback(TransactionInfo* transaction, + SessionInfo* session) + throw ( ConnectorException ) = 0; + + /** + * Creates a new Message. + * @param Session Information + * @param Transaction Info for this Message + * @throws ConnectorException + */ + virtual cms::Message* createMessage( + SessionInfo* session, + TransactionInfo* transaction) + throw ( ConnectorException ) = 0; + + /** + * Creates a new BytesMessage. + * @param Session Information + * @param Transaction Info for this Message + * @throws ConnectorException + */ + virtual cms::BytesMessage* createBytesMessage( + SessionInfo* session, + TransactionInfo* transaction) + throw ( ConnectorException ) = 0; + + /** + * Creates a new TextMessage. + * @param Session Information + * @param Transaction Info for this Message + * @throws ConnectorException + */ + virtual cms::TextMessage* createTextMessage( + SessionInfo* session, + TransactionInfo* transaction) + throw ( ConnectorException ) = 0; + + /** + * Creates a new MapMessage. + * @param Session Information + * @param Transaction Info for this Message + * @throws ConnectorException + */ + virtual cms::MapMessage* createMapMessage( + SessionInfo* session, + TransactionInfo* transaction) + throw ( ConnectorException ) = 0; + + /** + * Unsubscribe from a givenDurable Subscription + * @param name of the Subscription + * @throws ConnectorException + */ + virtual void unsubscribe(const std::string& name) + throw ( ConnectorException ) = 0; + + /** + * Destroys the given connector resource. + * @param resource the resource to be destroyed. + * @throws ConnectorException + */ + virtual void destroyResource( ConnectorResource* resource ) + throw ( ConnectorException ) = 0; + + /** + * Sets the listener of consumer messages. + * @param listener the observer. + */ + virtual void setConsumerMessageListener( + ConsumerMessageListener* listener) = 0; + + /** + * Sets the Listner of exceptions for this connector + * @param ExceptionListener the observer. + */ + virtual void setExceptionListener( + cms::ExceptionListener* listener) = 0; + }; + +}} + +#endif /*_ACTIVEMQ_CONNECTOR_CONNECTOR_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/ConnectorException.h b/activemq-cpp/src/main/activemq/connector/ConnectorException.h new file mode 100644 index 0000000000..86f60a3a36 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/ConnectorException.h @@ -0,0 +1,64 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 CONNECTOREXCEPTION_H_ +#define CONNECTOREXCEPTION_H_ + +#include + +namespace activemq{ +namespace connector{ + + /* + * Signals that an Connector exception of some sort has occurred. + */ + class ConnectorException : public exceptions::ActiveMQException + { + public: + + ConnectorException() {} + ConnectorException( const exceptions::ActiveMQException& ex ){ + *(ActiveMQException*)this = ex; + } + ConnectorException( const ConnectorException& ex ){ + *(exceptions::ActiveMQException*)this = ex; + } + ConnectorException(const char* file, const int lineNumber, + const char* msg, ...) + { + va_list vargs ; + va_start(vargs, msg) ; + buildMessage(msg, vargs) ; + + // Set the first mark for this exception. + setMark( file, lineNumber ); + } + + /** + * Clones this exception. This is useful for cases where you need + * to preserve the type of the original exception as well as the message. + * All subclasses should override. + */ + virtual exceptions::ActiveMQException* clone() const{ + return new ConnectorException( *this ); + } + virtual ~ConnectorException() {} + + }; + +}} + +#endif /*CONNECTOREXCEPTION_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/ConnectorFactory.h b/activemq-cpp/src/main/activemq/connector/ConnectorFactory.h new file mode 100644 index 0000000000..629b9af572 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/ConnectorFactory.h @@ -0,0 +1,48 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 CONNECTORFACTORY_H_ +#define CONNECTORFACTORY_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ + + /** + * Interface class for all Connector Factory Classes + */ + class ConnectorFactory + { + public: + + virtual ~ConnectorFactory(void) {}; + + /** + * Creates a connector + * @param The Properties that the new connector is configured with + */ + virtual Connector* createConnector( + const activemq::util::Properties& properties, + activemq::transport::Transport* transport) = 0; + + }; + +}} + +#endif /*CONNECTORFACTORY_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/ConnectorFactoryMap.cpp b/activemq-cpp/src/main/activemq/connector/ConnectorFactoryMap.cpp new file mode 100644 index 0000000000..09cb417384 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/ConnectorFactoryMap.cpp @@ -0,0 +1,74 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 + +using namespace activemq; +using namespace activemq::connector; + +//////////////////////////////////////////////////////////////////////////////// +ConnectorFactoryMap* ConnectorFactoryMap::getInstance(void) +{ + // Static instance of this Map, create here so that one will + // always exist, the one and only Connector Map. + static ConnectorFactoryMap instance; + + return &instance; +} + +//////////////////////////////////////////////////////////////////////////////// +void ConnectorFactoryMap::registerConnectorFactory(const std::string& name, + ConnectorFactory* factory) +{ + factoryMap[name] = factory; +} + +//////////////////////////////////////////////////////////////////////////////// +void ConnectorFactoryMap::unregisterConnectorFactory(const std::string& name) +{ + factoryMap.erase(name); +} + +//////////////////////////////////////////////////////////////////////////////// +ConnectorFactory* ConnectorFactoryMap::lookup(const std::string& name) +{ + std::map::const_iterator itr = + factoryMap.find(name); + + if(itr != factoryMap.end()) + { + return itr->second; + } + + // Didn't find it, return nothing, not a single thing. + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +std::size_t ConnectorFactoryMap::getFactoryNames( + std::vector& factoryList) +{ + std::map::const_iterator itr = + factoryMap.begin(); + + for(; itr != factoryMap.end(); ++itr) + { + factoryList.insert(factoryList.end(), itr->first); + } + + return factoryMap.size(); +} diff --git a/activemq-cpp/src/main/activemq/connector/ConnectorFactoryMap.h b/activemq-cpp/src/main/activemq/connector/ConnectorFactoryMap.h new file mode 100644 index 0000000000..b3269abece --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/ConnectorFactoryMap.h @@ -0,0 +1,93 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 CONNECTORFACTORYMAP_H_ +#define CONNECTORFACTORYMAP_H_ + +#include +#include +#include + +#include +#include + +namespace activemq{ +namespace connector{ + + /** + * Lookup Map for Connector Factories. Use the Connector name to + * find the associated factory. This class does not take ownership + * of the stored factories, they must be deallocated somewhere. + */ + class ConnectorFactoryMap + { + public: + + /** + * Gets a singleton instance of this class. + */ + static ConnectorFactoryMap* getInstance(void); + + /** + * Registers a new Connector Factory with this map + * @param name to associate the factory with + * @param factory to store. + */ + void registerConnectorFactory(const std::string& name, + ConnectorFactory* factory); + + /** + * Unregisters a Connector Factory with this map + * @param name of the factory to remove + */ + void unregisterConnectorFactory(const std::string& name); + + /** + * Lookup the named factory in the Map + * @param the factory name to lookup + * @return the factory assciated with the name, or NULL + */ + ConnectorFactory* lookup(const std::string& name); + + /** + * Fetch a list of factory names that this Map contains + * @param vector object to receive the list + * @returns count of factories. + */ + std::size_t getFactoryNames(std::vector& factoryList); + + private: + + // Hidden Contrustor, prevents instantiation + ConnectorFactoryMap() {}; + + // Hidden Destructor. + virtual ~ConnectorFactoryMap() {}; + + // Hidden Copy Constructore + ConnectorFactoryMap(const ConnectorFactoryMap& factoryMap); + + // Hidden Assignment operator + ConnectorFactoryMap operator=(const ConnectorFactoryMap& factoryMap); + + // Map of Factories + std::map factoryMap; + + }; + +}} + +#endif /*CONNECTORFACTORYMAP_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/ConnectorFactoryMapRegistrar.h b/activemq-cpp/src/main/activemq/connector/ConnectorFactoryMapRegistrar.h new file mode 100644 index 0000000000..69b5663a49 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/ConnectorFactoryMapRegistrar.h @@ -0,0 +1,90 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 CONNECTORFACTORYMAPREGISTRAR_H_ +#define CONNECTORFACTORYMAPREGISTRAR_H_ + +#include + +#include + +namespace activemq{ +namespace connector{ + + /** + * Registers the passed in factory into the factory map, this class + * can manage the lifetime of the registered factory (default behaviour). + */ + class ConnectorFactoryMapRegistrar + { + public: + + /** + * Constructor for this class + * @param name of the factory to register + * @param the factory + * @param boolean indicating if this object manages the lifetime of + * the factory that is being registered. + */ + ConnectorFactoryMapRegistrar( const std::string& name, + ConnectorFactory* factory, + bool manageLifetime = true ) + { + // Register it in the map. + ConnectorFactoryMap::getInstance()-> + registerConnectorFactory(name, factory); + + // Store for later deletion + this->factory = factory; + this->manageLifetime = manageLifetime; + this->name = name; + } + + virtual ~ConnectorFactoryMapRegistrar(void) + { + try + { + // UnRegister it in the map. + ConnectorFactoryMap::getInstance()-> + unregisterConnectorFactory(name); + + if(manageLifetime) + { + delete factory; + } + } + catch(...) {} + } + + /** + * get a reference to the factory that this class is holding + * @return reference to a factory class + */ + virtual ConnectorFactory& getFactory(void) { + return *factory; + } + + private: + + std::string name; + ConnectorFactory* factory; + bool manageLifetime; + + }; + +}} + +#endif /*CONNECTORFACTORYMAPREGISTRAR_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/ConnectorResource.h b/activemq-cpp/src/main/activemq/connector/ConnectorResource.h new file mode 100644 index 0000000000..1278354299 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/ConnectorResource.h @@ -0,0 +1,25 @@ +#ifndef ACTIVEMQ_CONNECTOR_CONNECTORRESOURCE_H_ +#define ACTIVEMQ_CONNECTOR_CONNECTORRESOURCE_H_ + +namespace activemq{ +namespace connector{ + + /** + * An object who's lifetime is determined by + * the connector that created it. All ConnectorResources + * should be given back to the connector rather than + * deleting explicitly. + */ + class ConnectorResource + { + public: + + /** + * Destructor + */ + virtual ~ConnectorResource() {} + }; + +}} + +#endif /*ACTIVEMQ_CONNECTOR_CONNECTORRESOURCE_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/ConsumerInfo.h b/activemq-cpp/src/main/activemq/connector/ConsumerInfo.h new file mode 100644 index 0000000000..0963c57bde --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/ConsumerInfo.h @@ -0,0 +1,89 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_CONSUMERINFO_H_ +#define _ACTIVEMQ_CONNECTOR_CONSUMERINFO_H_ + +#include +#include +#include +#include + +namespace activemq{ +namespace connector{ + + class ConsumerInfo : public ConnectorResource + { + public: + + /** + * Destructor + */ + virtual ~ConsumerInfo(void) {} + + /** + * Gets this message consumer's message selector expression. + * @return This Consumer's selector expression or "". + */ + virtual const std::string& getMessageSelector(void) const = 0; + + /** + * Sets this message consumer's message selector expression. + * @param This Consumer's selector expression or "". + */ + virtual void setMessageSelector( const std::string& selector ) = 0; + + /** + * Gets the ID that is assigned to this consumer + * @return value of the Consumer Id. + */ + virtual unsigned int getConsumerId(void) const = 0; + + /** + * Sets the ID that is assigned to this consumer + * @return string value of the Consumer Id. + */ + virtual void setConsumerId( const unsigned int id ) = 0; + + /** + * Gets the Destination that this Consumer is subscribed on + * @return Destination + */ + virtual const cms::Destination& getDestination(void) const = 0; + + /** + * Sets the destination that this Consumer is listening on + * @param Destination + */ + virtual void setDestination( const cms::Destination& destination ) = 0; + + /** + * Gets the Session Info that this consumer is attached too + * @return SessionnInfo pointer + */ + virtual const SessionInfo* getSessionInfo(void) const = 0; + + /** + * Gets the Session Info that this consumer is attached too + * @return SessionnInfo pointer + */ + virtual void setSessionInfo( const SessionInfo* session ) = 0; + + }; + +}} + +#endif /*_ACTIVEMQ_CONNECTOR_CONSUMERINFO_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/ConsumerMessageListener.h b/activemq-cpp/src/main/activemq/connector/ConsumerMessageListener.h new file mode 100644 index 0000000000..6e534be5d2 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/ConsumerMessageListener.h @@ -0,0 +1,46 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_CONSUMERMESSAGELISTENER_H_ +#define _ACTIVEMQ_CONNECTOR_CONSUMERMESSAGELISTENER_H_ + +#include +#include + +namespace activemq{ +namespace connector{ + + /** + * An observer of messages that are targeted at a + * particular consumer. + */ + class ConsumerMessageListener{ + public: + + virtual ~ConsumerMessageListener(){} + + /** + * Called to dispatch a message to a particular consumer. + * @param consumer the target consumer of the dispatch. + * @param msg the message to be dispatched. + */ + virtual void onConsumerMessage( ConsumerInfo* consumer, + core::ActiveMQMessage* msg ) = 0; + }; + +}} + +#endif /*_ACTIVEMQ_CONNECTOR_CONSUMERMESSAGELISTENER_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/ProducerInfo.h b/activemq-cpp/src/main/activemq/connector/ProducerInfo.h new file mode 100644 index 0000000000..75c5c2c861 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/ProducerInfo.h @@ -0,0 +1,75 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_PRODUCERINFO_H_ +#define _ACTIVEMQ_CONNECTOR_PRODUCERINFO_H_ + +#include + +#include +#include + +namespace activemq{ +namespace connector{ + + class ProducerInfo : public ConnectorResource + { + public: + + virtual ~ProducerInfo(void) {} + + /** + * Retrieves the default destination that this producer + * sends its messages to. + * @return Destionation, owned by this object + */ + virtual const cms::Destination& getDestination(void) const = 0; + + /** + * Sets the Default Destination for this Producer + * @param reference to a destination, copied internally + */ + virtual void setDestination( const cms::Destination& dest ) = 0; + + /** + * Gets the ID that is assigned to this Producer + * @return value of the Producer Id. + */ + virtual unsigned int getProducerId(void) const = 0; + + /** + * Sets the ID that is assigned to this Producer + * @return string value of the Producer Id. + */ + virtual void setProducerId( const unsigned int id ) = 0; + + /** + * Gets the Session Info that this consumer is attached too + * @return SessionnInfo pointer + */ + virtual const SessionInfo* getSessionInfo(void) const = 0; + + /** + * Gets the Session Info that this consumer is attached too + * @return SessionnInfo pointer + */ + virtual void setSessionInfo( const SessionInfo* session ) = 0; + + }; + +}} + +#endif /*_ACTIVEMQ_CONNECTOR_PRODUCERINFO_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/SessionInfo.h b/activemq-cpp/src/main/activemq/connector/SessionInfo.h new file mode 100644 index 0000000000..92f64c989d --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/SessionInfo.h @@ -0,0 +1,93 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_SESSIONINFO_H_ +#define _ACTIVEMQ_CONNECTOR_SESSIONINFO_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ + + class SessionInfo : public ConnectorResource + { + public: + + /** + * Destructor + */ + virtual ~SessionInfo(void) {} + + /** + * Gets the Connection Id of the Connection that this consumer is + * using to receive its messages. + * @return string value of the connection id + */ + virtual const std::string& getConnectionId(void) const = 0; + + /** + * Sets the Connection Id of the Connection that this consumer is + * using to receive its messages. + * @param string value of the connection id + */ + virtual void setConnectionId( const std::string& id ) = 0; + + /** + * Gets the Sessions Id value + * @return id for this session + */ + virtual unsigned int getSessionId(void) const = 0; + + /** + * Sets the Session Id for this Session + * @param integral id value for this session + */ + virtual void setSessionId( const unsigned int id ) = 0; + + /** + * Sets the Ack Mode of this Session Info object + * @param Ack Mode + */ + virtual void setAckMode(cms::Session::AcknowledgeMode ackMode) = 0; + + /** + * Gets the Ack Mode of this Session + * @return Ack Mode + */ + virtual cms::Session::AcknowledgeMode getAckMode(void) const = 0; + + /** + * Gets the currently active transaction info, if this session is + * transacted, returns NULL when not transacted. You must call + * getAckMode and see if the session is transacted. + * @return Transaction Id of current Transaction + */ + virtual const TransactionInfo* getTransactionInfo(void) const = 0; + + /** + * Sets the current transaction info for this session, this is nit + * used when the session is not transacted. + * @param Transaction Id + */ + virtual void setTransactionInfo( const TransactionInfo* transaction ) = 0; + + }; + +}} + +#endif /*_ACTIVEMQ_CONNECTOR_SESSIONINFO_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/TransactionInfo.h b/activemq-cpp/src/main/activemq/connector/TransactionInfo.h new file mode 100644 index 0000000000..ef32021def --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/TransactionInfo.h @@ -0,0 +1,64 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_TRANSACTIONINFO_H_ +#define _ACTIVEMQ_CONNECTOR_TRANSACTIONINFO_H_ + +#include + +namespace activemq{ +namespace connector{ + + class SessionInfo; + + class TransactionInfo : public ConnectorResource + { + public: + + /** + * Destructor + */ + virtual ~TransactionInfo(void) {} + + /** + * Gets the Transction Id + * @return unsigned int Id + */ + virtual unsigned int getTransactionId(void) const = 0; + + /** + * Sets the Transction Id + * @param unsigned int Id + */ + virtual void setTransactionId( const unsigned int id ) = 0; + + /** + * Gets the Session Info that this transaction is attached too + * @return SessionnInfo pointer + */ + virtual const SessionInfo* getSessionInfo(void) const = 0; + + /** + * Gets the Session Info that this transaction is attached too + * @return SessionnInfo pointer + */ + virtual void setSessionInfo( const SessionInfo* session ) = 0; + + }; + +}} + +#endif /*_ACTIVEMQ_CONNECTOR_TRANSACTIONINFO_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompCommandListener.h b/activemq-cpp/src/main/activemq/connector/stomp/StompCommandListener.h new file mode 100644 index 0000000000..8d92ba0a04 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompCommandListener.h @@ -0,0 +1,50 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_STOMPCOMMANDLISTENER_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_STOMPCOMMANDLISTENER_H_ + +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + /** + * Interface class for object that with to register with the Stomp + * Connector in order to process a Command that was received. + */ + class StompCommandListener + { + public: + + virtual ~StompCommandListener(void) {} + + /** + * Process the Stomp Command + * @param command to process + * @throw ConnterException + */ + virtual void onStompCommand( commands::StompCommand* command ) + throw ( StompConnectorException ) = 0; + + }; + +}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_STOMPCOMMANDLISTENER_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompCommandReader.cpp b/activemq-cpp/src/main/activemq/connector/stomp/StompCommandReader.cpp new file mode 100644 index 0000000000..7a0314100a --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompCommandReader.cpp @@ -0,0 +1,325 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "StompCommandReader.h" + +#include +#include + +using namespace std; +using namespace activemq; +using namespace activemq::concurrent; +using namespace activemq::connector; +using namespace activemq::connector::stomp; +using namespace activemq::transport; +using namespace activemq::io; +using namespace activemq::exceptions; + +//////////////////////////////////////////////////////////////////////////////// +StompCommandReader::StompCommandReader(void) +{ + inputStream = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +StompCommandReader::StompCommandReader(InputStream* is) +{ + inputStream = is; +} + +//////////////////////////////////////////////////////////////////////////////// +Command* StompCommandReader::readCommand(void) + throw (CommandIOException) +{ + try + { + // Create a new Frame for reading to. + StompFrame* frame = new StompFrame(); + + // Read the command into the frame. + readStompCommand( *frame ); + + // Read the headers. + readStompHeaders( *frame ); + + // Read the body. + readStompBody( *frame ); + + // Return the Command, caller must delete it. + return marshaler.marshal( frame ); + } + AMQ_CATCH_RETHROW( CommandIOException ) + AMQ_CATCH_EXCEPTION_CONVERT( ActiveMQException, CommandIOException ) + AMQ_CATCHALL_THROW( CommandIOException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void StompCommandReader::readStompCommand( StompFrame& frame ) + throw ( StompConnectorException ) +{ + // Read the command; + int numChars = readStompHeaderLine(); + + if( numChars <= 0 ) + { + throw StompConnectorException( + __FILE__, __LINE__, + "StompCommandReader::readStompCommand: " + "Error on Read of Command Header" ); + } + + // Set the command in the frame - copy the memory. + frame.setCommand( reinterpret_cast(&buffer[0]) ); + + // Clean up the mess. + buffer.clear(); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompCommandReader::readStompHeaders( StompFrame& frame ) + throw (StompConnectorException) +{ + // Read the command; + bool endOfHeaders = false; + + while( !endOfHeaders ) + { + // Clean up the mess. + buffer.clear(); + + // Read in the next header line. + int numChars = readStompHeaderLine(); + + if( numChars == 0 ) + { + // should never get here + throw StompConnectorException( + __FILE__, __LINE__, + "StompCommandReader::readStompHeaders: no characters read" ); + } + + // Check for an empty line to demark the end of the header section. + // if its not the end then we have a header to process, so parse it. + if( numChars == 1 && buffer[0] == '\0' ) + { + endOfHeaders = true; + } + else + { + // Search through this line to separate the key/value pair. + for( size_t ix = 0; ix < buffer.size(); ++ix ) + { + // If found the key/value separator... + if( buffer[ix] == ':' ) + { + // Null-terminate the key. + buffer[ix] = '\0'; + + const char* key = reinterpret_cast(&buffer[0]); + const char* value = reinterpret_cast(&buffer[ix+1]); + + // Assign the header key/value pair. + frame.getProperties().setProperty(key, value); + + // Break out of the for loop. + break; + } + } + } + } + + // Clean up the mess. + buffer.clear(); +} + +//////////////////////////////////////////////////////////////////////////////// +int StompCommandReader::readStompHeaderLine(void) + throw (StompConnectorException) +{ + int count = 0; + + while( true ) + { + // Read the next char from the stream. + buffer.push_back( inputStream->read() ); + + // Increment the position pointer. + count++; + + // If we reached the line terminator, return the total number + // of characters read. + if( buffer[count-1] == '\n' ) + { + // Overwrite the line feed with a null character. + buffer[count-1] = '\0'; + + return count; + } + } + + // If we get here something bad must have happened. + throw StompConnectorException( + __FILE__, __LINE__, + "StompCommandReader::readStompHeaderLine: " + "Unrecoverable, error condition"); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompCommandReader::readStompBody( StompFrame& frame ) + throw ( StompConnectorException ) +{ + unsigned long content_length = 0; + + if(frame.getProperties().hasProperty( + commands::CommandConstants::toString( + commands::CommandConstants::HEADER_CONTENTLENGTH))) + { + char* stopped_string = NULL; + + string length = + frame.getProperties().getProperty( + commands::CommandConstants::toString( + commands::CommandConstants::HEADER_CONTENTLENGTH)); + + content_length = strtoul( + length.c_str(), + &stopped_string, + 10); + } + + if(content_length != 0) + { + // For this case its assumed that content length indicates how + // much to read. We reserve space in the buffer for it to + // minimize the number of reallocs that might occur. We are + // assuming that content length doesn't count the trailing null + // that indicates the end of frame. The reserve won't do anything + // if the buffer already has that much capacity. The resize call + // basically sets the end iterator to the correct location since + // this is a char vector and we already reserve enough space. + // Resize doesn't realloc the vector smaller if content_length + // is less than capacity of the buffer, it just move the end + // iterator. Reserve adds the benefit that the mem is set to + // zero. Over time as larger messages come in thsi will cause + // us to adapt to that size so that future messages that are + // around that size won't alloc any new memory. + + buffer.reserve( content_length ); + buffer.resize( content_length ); + + // Read the Content Length now + read( &buffer[0], content_length ); + + // Content Length read, now pop the end terminator off (\0\n). + if(inputStream->read() != '\0' || + inputStream->read() != '\n') + { + throw StompConnectorException( + __FILE__, __LINE__, + "StompCommandReader::readStompBody: " + "Read Content Length, and no trailing null"); + } + } + else + { + // Content length was either zero, or not set, so we read until the + // first null is encounted. + + while( true ) + { + char byte = inputStream->read(); + + buffer.push_back(byte); + + content_length++; + + if(byte != '\0') + { + continue; + } + + // We read up to the first NULL, now lets pop off the required + // newline to complete the packet. + if(inputStream->read() != '\n') + { + throw StompConnectorException( + __FILE__, __LINE__, + "StompCommandReader::readStompBody: " + "Read Body, and no trailing newline"); + } + + break; // Read null and newline we are done. + } + } + + if( content_length != 0 ) + { + char* cpyBody = new char[content_length]; + memcpy(cpyBody, &buffer[0], content_length); + + // Set the body contents in the frame - copy the memory + frame.setBody( cpyBody, content_length ); + } + + // Clean up the mess. + buffer.clear(); +} + +//////////////////////////////////////////////////////////////////////////////// +int StompCommandReader::read(unsigned char* buffer, int count) + throw(io::IOException) +{ + if( inputStream == NULL ) + { + throw IOException( + __FILE__, __LINE__, + "StompCommandReader::read(char*,int) - input stream is NULL" ); + } + + int head = 0; + + // We call the read(buffer, size) version asking for one + // byte, if this returns zero, then there wasn't anything + // on the stream to read, so we try again after a short + // pause in hopes that some more data will show up. + while( true ) + { + head += inputStream->read(&buffer[head], count - head); + + if(head == count) + { + return count; + } + + // Got here, so we wait a bit and try again. + Thread::sleep( 10 ); + } +} + +//////////////////////////////////////////////////////////////////////////////// +unsigned char StompCommandReader::readByte(void) throw(io::IOException) +{ + if( inputStream == NULL ) + { + throw IOException( + __FILE__, __LINE__, + "StompCommandReader::read(char*,int) - input stream is NULL" ); + } + + unsigned char c = 0; + inputStream->read(&c, 1); + return c; +} diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompCommandReader.h b/activemq-cpp/src/main/activemq/connector/stomp/StompCommandReader.h new file mode 100644 index 0000000000..e9b33105bd --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompCommandReader.h @@ -0,0 +1,146 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_STOMPCOMMANDREADER_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_STOMPCOMMANDREADER_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + class StompCommandReader : public transport::CommandReader + { + private: + + /** + * The target input stream. + */ + io::InputStream* inputStream; + + /** + * Vector Object used to buffer data + */ + std::vector buffer; + + /** + * Marshaler of Stomp Commands + */ + marshal::Marshaler marshaler; + + public: + + /** + * Deafult Constructor + */ + StompCommandReader( void ); + + /** + * Constructor. + * @param is the target input stream. + */ + StompCommandReader( io::InputStream* is ); + + /** + * Destructor + */ + virtual ~StompCommandReader(void) {} + + /** + * Reads a command from the given input stream. + * @return The next command available on the stream. + * @throws CommandIOException if a problem occurs during the read. + */ + virtual transport::Command* readCommand( void ) + throw ( transport::CommandIOException ); + + /** + * Sets the target input stream. + * @param Target Input Stream + */ + virtual void setInputStream(io::InputStream* is){ + inputStream = is; + } + + /** + * Gets the target input stream. + * @return Target Input Stream + */ + virtual io::InputStream* getInputStream( void ){ + return inputStream; + } + + /** + * Attempts to read an array of bytes from the stream. + * @param buffer The target byte buffer. + * @param count The number of bytes to read. + * @return The number of bytes read. + * @throws IOException thrown if an error occurs. + */ + virtual int read(unsigned char* buffer, int count) + throw( io::IOException ); + + /** + * Attempts to read a byte from the input stream + * @return The byte. + * @throws IOException thrown if an error occurs. + */ + virtual unsigned char readByte(void) throw( io::IOException ); + + private: + + /** + * Read the Stomp Command from the Frame + * @param reference to a Stomp Frame + * @throws StompConnectorException + */ + void readStompCommand( StompFrame& frame ) + throw ( StompConnectorException ); + + /** + * Read all the Stomp Headers for the incoming Frame + * @param Frame to place data into + * @throws StompConnectorException + */ + void readStompHeaders( StompFrame& frame ) + throw ( StompConnectorException ); + + /** + * Reads a Stomp Header line and stores it in the buffer object + * @return number of bytes read, zero if there was a problem. + * @throws StompConnectorException + */ + int readStompHeaderLine( void ) throw ( StompConnectorException ); + + /** + * Reads the Stomp Body from the Wire and store it in the frame. + * @param Stomp Frame to place data in + */ + void readStompBody( StompFrame& frame ) + throw ( StompConnectorException ); + + }; + +}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_STOMPCOMMANDREADER_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompCommandWriter.cpp b/activemq-cpp/src/main/activemq/connector/stomp/StompCommandWriter.cpp new file mode 100644 index 0000000000..829fb75601 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompCommandWriter.cpp @@ -0,0 +1,137 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "StompCommandWriter.h" + +#include +#include + +using namespace std; +using namespace activemq; +using namespace activemq::connector; +using namespace activemq::connector::stomp; +using namespace activemq::connector::stomp::commands; +using namespace activemq::transport; +using namespace activemq::io; +using namespace activemq::exceptions; + +//////////////////////////////////////////////////////////////////////////////// +StompCommandWriter::StompCommandWriter(void) +{ + outputStream = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +StompCommandWriter::StompCommandWriter(OutputStream* os) +{ + outputStream = os; +} + +//////////////////////////////////////////////////////////////////////////////// +void StompCommandWriter::writeCommand( const Command* command ) + throw ( transport::CommandIOException ) +{ + try + { + if( outputStream == NULL ) + { + throw CommandIOException( + __FILE__, __LINE__, + "StompCommandWriter::writeCommand - " + "output stream is NULL" ); + } + + const StompFrame& frame = marshaler.marshal( command ); + + // Write the command. + const string& cmdString = frame.getCommand(); + write( cmdString.c_str(), cmdString.length() ); + writeByte( '\n' ); + + // Write all the headers. + vector< pair > headers = frame.getProperties().toArray(); + for( unsigned int ix=0; ix < headers.size(); ++ix ) + { + string& name = headers[ix].first; + string& value = headers[ix].second; + + write( name.c_str(), name.length() ); + writeByte( ':' ); + write( value.c_str(), value.length() ); + writeByte( '\n' ); + } + + // Finish the header section with a form feed. + writeByte( '\n' ); + + // Write the body. + const char* body = frame.getBody(); + if( body != NULL ) + { + write( body, frame.getBodyLength() ); + } + + if( ( frame.getBodyLength() == 0 ) || + ( frame.getProperties().getProperty( + CommandConstants::toString( + CommandConstants::HEADER_CONTENTLENGTH ), "" ) != "" ) ) + { + writeByte( '\0' ); + } + + writeByte( '\n' ); + + // Flush the stream. + outputStream->flush(); + } + AMQ_CATCH_RETHROW( CommandIOException ) + AMQ_CATCH_EXCEPTION_CONVERT( ActiveMQException, CommandIOException ) + AMQ_CATCHALL_THROW( CommandIOException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void StompCommandWriter::write(const unsigned char* buffer, int count) + throw(IOException) +{ + if( outputStream == NULL ) + { + throw IOException( + __FILE__, __LINE__, + "StompCommandWriter::write(char*,int) - input stream is NULL" ); + } + + outputStream->write( buffer, count ); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompCommandWriter::writeByte(unsigned char v) throw(IOException) +{ + if( outputStream == NULL ) + { + throw IOException( + __FILE__, __LINE__, + "StompCommandWriter::write(char) - input stream is NULL" ); + } + + outputStream->write( v ); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompCommandWriter::write(const char* buffer, int count) + throw(io::IOException) +{ + write(reinterpret_cast(buffer), count); +} diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompCommandWriter.h b/activemq-cpp/src/main/activemq/connector/stomp/StompCommandWriter.h new file mode 100644 index 0000000000..9a01defef4 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompCommandWriter.h @@ -0,0 +1,118 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_STOMPCOMMANDWRITER_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_STOMPCOMMANDWRITER_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + class StompCommandWriter : public transport::CommandWriter + { + private: + + /** + * Target output stream. + */ + io::OutputStream* outputStream; + + /** + * Marshaler of Stomp Commands + */ + marshal::Marshaler marshaler; + + public: + + /** + * Default Constructor + */ + StompCommandWriter(void); + + /** + * Constructor. + * @param os the target output stream. + */ + StompCommandWriter( io::OutputStream* os ); + + /** + * Destructor + */ + virtual ~StompCommandWriter(void) {} + + /** + * Sets the target output stream. + */ + virtual void setOutputStream(io::OutputStream* os){ + outputStream = os; + } + + /** + * Gets the target output stream. + */ + virtual io::OutputStream* getOutputStream(void){ + return outputStream; + } + + /** + * Writes a command to the given output stream. + * @param command the command to write. + * @param os the target stream for the write. + * @throws CommandIOException if a problem occurs during the write. + */ + virtual void writeCommand( const transport::Command* command ) + throw ( transport::CommandIOException ); + + /** + * Writes a byte array to the output stream. + * @param buffer a byte array + * @param count the number of bytes in the array to write. + * @throws IOException thrown if an error occurs. + */ + virtual void write(const unsigned char* buffer, int count) + throw( io::IOException ); + + /** + * Writes a byte to the output stream. + * @param v The value to be written. + * @throws IOException thrown if an error occurs. + */ + virtual void writeByte(unsigned char v) throw( io::IOException ); + + private: + + /** + * Writes a char array to the output stream. + * @param buffer a char array + * @param count the number of bytes in the array to write. + * @throws IOException thrown if an error occurs. + */ + virtual void write(const char* buffer, int count) + throw( io::IOException ); + + }; + +}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_STOMPCOMMANDWRITER_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompConnector.cpp b/activemq-cpp/src/main/activemq/connector/stomp/StompConnector.cpp new file mode 100644 index 0000000000..c0b85f3e96 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompConnector.cpp @@ -0,0 +1,795 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace activemq; +using namespace activemq::connector; +using namespace activemq::util; +using namespace activemq::transport; +using namespace activemq::exceptions; +using namespace activemq::connector::stomp; +using namespace activemq::connector::stomp::commands; + +//////////////////////////////////////////////////////////////////////////////// +StompConnector::StompConnector( Transport* transport, + const util::Properties& properties ) + throw ( IllegalArgumentException ) +{ + if(transport == NULL) + { + throw IllegalArgumentException( + __FILE__, __LINE__, + "StompConnector::StompConnector - Transport cannot be NULL"); + } + + this->transport = transport; + this->state = DISCONNECTED; + this->exceptionListener = NULL; + this->messageListener = NULL; + this->sessionManager = NULL; + this->nextProducerId = 0; + this->nextTransactionId = 0; + this->properties.copy( &properties ); + + // Observe the transport for events. + this->transport->setCommandListener( this ); + this->transport->setTransportExceptionListener( this ); + + // Setup the reader and writer in the transport. + this->transport->setCommandReader( &reader ); + this->transport->setCommandWriter( &writer ); + + // Register ourself for those commands that we process + addCmdListener( CommandConstants::ERROR_CMD, this ); +} + +//////////////////////////////////////////////////////////////////////////////// +StompConnector::~StompConnector(void) +{ + try + { + close(); + + delete sessionManager; + } + AMQ_CATCH_NOTHROW( ActiveMQException ) + AMQ_CATCHALL_NOTHROW( ) +} + +//////////////////////////////////////////////////////////////////////////////// +unsigned int StompConnector::getNextProducerId(void) +{ + synchronized(&mutex) + { + return nextProducerId++; + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +unsigned int StompConnector::getNextTransactionId(void) +{ + synchronized(&mutex) + { + return nextTransactionId++; + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +void StompConnector::enforceConnected( void ) throw ( ConnectorException ) +{ + if( state != CONNECTED ) + { + throw StompConnectorException( + __FILE__, __LINE__, + "StompConnector::enforceConnected - Not Connected!" ); + } +} + +//////////////////////////////////////////////////////////////////////////////// +void StompConnector::addCmdListener( + commands::CommandConstants::CommandId commandId, + StompCommandListener* listener ) +{ + cmdListenerMap.insert( make_pair( commandId, listener ) ); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompConnector::removeCmdListener( + commands::CommandConstants::CommandId commandId ) +{ + cmdListenerMap.erase(commandId); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompConnector::start(void) throw( cms::CMSException ) +{ + try + { + synchronized( &mutex ) + { + if( state == CONNECTED ) + { + throw ActiveMQException( + __FILE__, __LINE__, + "StompConnector::start - already started" ); + } + + // Start the transport - this establishes the socket. + transport->start(); + + // Send the connect message to the broker. + connect(); + } + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompConnector::close(void) throw( cms::CMSException ){ + + try + { + synchronized( &mutex ) + { + if( state == this->CONNECTED ) + { + // Send the disconnect message to the broker. + disconnect(); + + // Close the transport. + transport->close(); + } + } + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompConnector::connect(void) +{ + try + { + // Mark this connector as started. + state = this->CONNECTING; + + // Send the connect command to the broker + ConnectCommand cmd; + + // Encode User Name and Password and Client ID + string login = getLogin(); + if( login.length() > 0 ){ + cmd.setLogin( login ); + } + string password = getPassword(); + if( password.length() > 0 ){ + cmd.setPassword( password ); + } + string clientId = getClientId(); + if( clientId.length() > 0 ){ + cmd.setClientId( clientId ); + } + + Response* response = transport->request( &cmd ); + + if( dynamic_cast< ExceptionResponse* >( response ) != NULL ) + { + throw StompConnectorException( + __FILE__, __LINE__, + "StompConnector::connect - Failed on Connect Request" ); + } + + ConnectedCommand* connected = + dynamic_cast< ConnectedCommand* >( response ); + + if( connected == NULL ) + { + throw StompConnectorException( + __FILE__, __LINE__, + "StompConnector::connect - " + "Response not a connected response" ); + } + + // Connected so we now create the SessionManager + sessionManager = new StompSessionManager( + connected->getSessionId(), transport ); + + // Give our message listener to the session manager it will + // notify all the interested clients + sessionManager->setConsumerMessageListener( messageListener ); + + // Add the Session Manager as the Command Listener for + // Message commands so that it can route them to the + // correct consumers. + addCmdListener( CommandConstants::MESSAGE, sessionManager ); + + // In Stomp, the client Id is the same as the session id that is + // returned in the Connected response + properties.setProperty( + commands::CommandConstants::toString( + commands::CommandConstants::HEADER_CLIENT_ID ), + connected->getSessionId() ); + + // Tag us in the Connected State now. + state = CONNECTED; + + // Clean up + delete response; + } + AMQ_CATCH_RETHROW( BrokerError ) + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void StompConnector::disconnect(void) +{ + try + { + // Mark state as no longer connected. + state = this->DISCONNECTED; + + // Send the disconnect command to the broker. + DisconnectCommand cmd; + transport->oneway( &cmd ); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ); +} + +//////////////////////////////////////////////////////////////////////////////// +SessionInfo* StompConnector::createSession( + cms::Session::AcknowledgeMode ackMode) + throw( ConnectorException ) +{ + try + { + enforceConnected(); + + return sessionManager->createSession( ackMode ); + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +ConsumerInfo* StompConnector::createConsumer( + cms::Destination* destination, + SessionInfo* session, + const std::string& selector) + throw ( ConnectorException ) +{ + try + { + enforceConnected(); + + return sessionManager->createConsumer( + destination, session, selector ); + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +ConsumerInfo* StompConnector::createDurableConsumer( + cms::Topic* topic, + SessionInfo* session, + const std::string& name, + const std::string& selector, + bool noLocal) + throw ( ConnectorException ) +{ + try + { + enforceConnected(); + + return sessionManager->createDurableConsumer( + topic, session, name, selector, noLocal ); + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +ProducerInfo* StompConnector::createProducer( + cms::Destination* destination, + SessionInfo* session) + throw ( ConnectorException ) +{ + try + { + enforceConnected(); + + ProducerInfo* producer = new StompProducerInfo(); + + producer->setDestination( *destination ); + producer->setProducerId( getNextProducerId() ); + producer->setSessionInfo( session ); + + return producer; + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +cms::Topic* StompConnector::createTopic(const std::string& name, + SessionInfo* session) + throw ( ConnectorException ) +{ + try + { + enforceConnected(); + + return new StompTopic(name); + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +cms::Queue* StompConnector::createQueue(const std::string& name, + SessionInfo* session) + throw ( ConnectorException ) +{ + try + { + enforceConnected(); + + return new StompQueue(name); + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +cms::TemporaryTopic* StompConnector::createTemporaryTopic( + SessionInfo* session) + throw ( ConnectorException ) +{ + try + { + throw UnsupportedOperationException( + __FILE__, __LINE__, + "StompConnector::createTemporaryTopic - No Stomp Support"); + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +cms::TemporaryQueue* StompConnector::createTemporaryQueue( + SessionInfo* session) + throw ( ConnectorException ) +{ + try + { + throw UnsupportedOperationException( + __FILE__, __LINE__, + "StompConnector::createTemporaryQueue - No Stomp Support"); + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompConnector::send(cms::Message* message, + ProducerInfo* producerInfo) + throw ( ConnectorException ) +{ + try + { + enforceConnected(); + + const SessionInfo* session = producerInfo->getSessionInfo(); + Command* command = dynamic_cast< transport::Command* >( message ); + + if( command == NULL ) + { + throw StompConnectorException( + __FILE__, __LINE__, + "StompConnector::send - " + "Message is not a valid stomp type."); + } + + if( session->getAckMode() == cms::Session::Transactional ) + { + StompCommand* stompCommand = + dynamic_cast< StompCommand* >( message ); + + if( stompCommand == NULL ) + { + throw StompConnectorException( + __FILE__, __LINE__, + "StompConnector::send - " + "Message is not a valid stomp type."); + } + + stompCommand->setTransactionId( + Integer::toString( + session->getTransactionInfo()->getTransactionId() ) ); + } + + // Send it + transport->oneway( command ); + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompConnector::send(std::list& messages, + ProducerInfo* producerInfo) + throw ( ConnectorException ) +{ + try + { + enforceConnected(); + + list::const_iterator itr = messages.begin(); + + for(; itr != messages.end(); ++itr) + { + this->send(*itr, producerInfo); + } + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompConnector::acknowledge( const SessionInfo* session, + const cms::Message* message, + AckType ackType = ConsumedAck ) + throw ( ConnectorException ) +{ + try + { + enforceConnected(); + + // Auto to Stomp means don't do anything, so we drop it here + // for client acknowledge we have to send and ack. + if( session->getAckMode() == cms::Session::ClientAcknowledge ) + { + AckCommand cmd; + + if( message->getCMSMessageId() == NULL ) + { + throw StompConnectorException( + __FILE__, __LINE__, + "StompConnector::send - " + "Message has no Message Id, cannot ack."); + } + + cmd.setMessageId( message->getCMSMessageId() ); + + if( session->getAckMode() == cms::Session::Transactional ) + { + cmd.setTransactionId( + Integer::toString( + session->getTransactionInfo()->getTransactionId() ) ); + } + + transport->oneway( &cmd ); + } + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +TransactionInfo* StompConnector::startTransaction( + SessionInfo* session) + throw ( ConnectorException ) +{ + try + { + enforceConnected(); + + TransactionInfo* transaction = new StompTransactionInfo(); + + transaction->setTransactionId( getNextTransactionId() ); + + session->setTransactionInfo( transaction ); + + BeginCommand cmd; + + cmd.setTransactionId( + Integer::toString( transaction->getTransactionId() ) ); + + transport->oneway( &cmd ); + + return transaction; + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompConnector::commit(TransactionInfo* transaction, + SessionInfo* session) + throw ( ConnectorException ) +{ + try + { + enforceConnected(); + + CommitCommand cmd; + + cmd.setTransactionId( + Integer::toString( transaction->getTransactionId() ) ); + + transport->oneway( &cmd ); + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompConnector::rollback(TransactionInfo* transaction, + SessionInfo* session) + throw ( ConnectorException ) +{ + try + { + enforceConnected(); + + AbortCommand cmd; + + cmd.setTransactionId( + Integer::toString( transaction->getTransactionId() ) ); + + transport->oneway( &cmd ); + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +cms::Message* StompConnector::createMessage( + SessionInfo* session, + TransactionInfo* transaction) + throw ( ConnectorException ) +{ + try + { + enforceConnected(); + + MessageCommand* cmd = new MessageCommand(); + + if( transaction != NULL ) + { + cmd->setTransactionId( + Integer::toString( transaction->getTransactionId() ) ); + } + + return cmd; + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +cms::BytesMessage* StompConnector::createBytesMessage( + SessionInfo* session, + TransactionInfo* transaction) + throw ( ConnectorException ) +{ + try + { + enforceConnected(); + + BytesMessageCommand* cmd = new BytesMessageCommand(); + + if( transaction != NULL ) + { + cmd->setTransactionId( + Integer::toString( transaction->getTransactionId() ) ); + } + + return cmd; + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +cms::TextMessage* StompConnector::createTextMessage( + SessionInfo* session, + TransactionInfo* transaction) + throw ( ConnectorException ) +{ + try + { + enforceConnected(); + + TextMessageCommand* cmd = new TextMessageCommand; + + if( transaction != NULL ) + { + cmd->setTransactionId( + Integer::toString( transaction->getTransactionId() ) ); + } + + return cmd; + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +cms::MapMessage* StompConnector::createMapMessage( + SessionInfo* session, + TransactionInfo* transaction) + throw ( ConnectorException ) +{ + try + { + throw UnsupportedOperationException( + __FILE__, __LINE__, + "StompConnector::createTemporaryQueue - No Stomp Support"); + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompConnector::unsubscribe(const std::string& name) + throw ( ConnectorException ) +{ + try + { + throw UnsupportedOperationException( + __FILE__, __LINE__, + "StompConnector::createTemporaryQueue - No Stomp Support"); + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompConnector::destroyResource( ConnectorResource* resource ) + throw ( ConnectorException ) +{ + try + { + ConsumerInfo* consumer = + dynamic_cast(resource); + SessionInfo* session = + dynamic_cast(resource); + + if( consumer != NULL) + { + sessionManager->removeConsumer( consumer ); + } + else if( session != NULL) + { + sessionManager->removeSession( session ); + } + + // No matter what we end it here. + delete resource; + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompConnector::onCommand( transport::Command* command ) +{ + try + { + StompCommand* stompCommand = dynamic_cast< StompCommand* >(command); + + if(stompCommand == NULL) + { + fire( ConnectorException( + __FILE__, __LINE__, + "StompConnector::onCommand - Recieved an unknown Command") ); + } + + CmdListenerMap::iterator itr = + cmdListenerMap.find( stompCommand->getStompCommandId() ); + + if( itr == cmdListenerMap.end() ) + { + fire( ConnectorException( + __FILE__, __LINE__, + "StompConnector::onCommand - " + "Recieved command with no listener") ); + + // This isn't going an farther, so delete it. + delete command; + + return; // we are done + } + + // Hand off + itr->second->onStompCommand( stompCommand ); + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompConnector::onTransportException( + transport::Transport* source, + const exceptions::ActiveMQException& ex ) +{ + try + { + // Inform the user. + fire( ex ); + + // Close down. + close(); + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ); +} + +//////////////////////////////////////////////////////////////////////////////// +void StompConnector::onStompCommand( commands::StompCommand* command ) + throw ( StompConnectorException ) +{ + try + { + ErrorCommand* error = + dynamic_cast(command); + + if(error != NULL) + { + fire( StompConnectorException( + __FILE__, __LINE__, + (string( "StompConnector::onStompCommand - " ) + + error->getErrorMessage() ).c_str() ) ); + + // Shutdown + close(); + } + } + AMQ_CATCH_RETHROW( StompConnectorException ) + AMQ_CATCHALL_THROW( StompConnectorException ); +} diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompConnector.h b/activemq-cpp/src/main/activemq/connector/stomp/StompConnector.h new file mode 100644 index 0000000000..f30adf1576 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompConnector.h @@ -0,0 +1,533 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONNECTOR_STOMP_STOMPCONNECTOR_H_ +#define ACTIVEMQ_CONNECTOR_STOMP_STOMPCONNECTOR_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + /** + * The connector implementation for the STOMP protocol. + */ + class StompConnector + : + public Connector, + public transport::CommandListener, + public transport::TransportExceptionListener, + public StompCommandListener + { + private: + + // Flags the state we are in for connection to broker. + enum connectionState + { + DISCONNECTED, + CONNECTING, + CONNECTED + }; + + // Maps Command Ids to listener that are interested + typedef std::map< commands::CommandConstants::CommandId, + StompCommandListener*> CmdListenerMap; + + private: + + /** + * The transport for sending/receiving commands on the wire. + */ + transport::Transport* transport; + + /** + * Flag to indicate the start state of the connector. + */ + connectionState state; + + /** + * Sync object. + */ + concurrent::Mutex mutex; + + /** + * Observer of messages directed at a particular + * consumer. + */ + ConsumerMessageListener* messageListener; + + /** + * Observer of connector exceptions. + */ + cms::ExceptionListener* exceptionListener; + + /** + * This Connector's Command Reader + */ + StompCommandReader reader; + + /** + * This Connector's Command Writer + */ + StompCommandWriter writer; + + /** + * Map to hold StompCommandListeners + */ + CmdListenerMap cmdListenerMap; + + /** + * Session Manager object that will be allocated when we connect + */ + StompSessionManager* sessionManager; + + /** + * Next avaliable Producer Id + */ + unsigned int nextProducerId; + + /** + * Next avaliable Transaction Id + */ + unsigned int nextTransactionId; + + /** + * Properties for the connector. + */ + util::SimpleProperties properties; + + private: + + /** + * Sends the connect message to the broker and + * waits for the response. + */ + void connect(void); + + /** + * Sends a oneway disconnect message to the broker. + */ + void disconnect(void); + + /** + * Fires a consumer message to the observer. + */ + void fire( ConsumerInfo* consumer, core::ActiveMQMessage* msg ){ + try{ + if( messageListener != NULL ){ + messageListener->onConsumerMessage( + consumer, + msg ); + } + }catch( ... ){/* do nothing*/} + } + + /** + * Fires an exception event to the observing object. + */ + void fire( const exceptions::ActiveMQException& ex ){ + try{ + if( exceptionListener != NULL ){ + exceptionListener->onException( ex ); + } + }catch( ... ){/* do nothing*/} + } + + public: + + /** + * Constructor for the stomp connector. + * @param transport the transport object for sending/receiving + * commands on the wire. + * @param props properties for configuring the connector. + */ + StompConnector( transport::Transport* transport, + const util::Properties& properties ) + throw ( exceptions::IllegalArgumentException ); + + virtual ~StompConnector(void); + + /** + * Starts the service. + * @throws CMSException + */ + virtual void start(void) throw( cms::CMSException ); + + /** + * Closes this object and deallocates the appropriate resources. + * @throws CMSException + */ + virtual void close(void) throw( cms::CMSException ); + + /** + * Gets the Client Id for this connection, if this + * connection has been closed, then this method returns "" + * @return Client Id String + */ + virtual std::string getClientId(void) const { + return properties.getProperty( + commands::CommandConstants::toString( + commands::CommandConstants::HEADER_CLIENT_ID ), "" ); + } + + virtual std::string getLogin(void) const { + return properties.getProperty( + commands::CommandConstants::toString( + commands::CommandConstants::HEADER_LOGIN ), "" ); + } + + virtual std::string getPassword(void) const { + return properties.getProperty( + commands::CommandConstants::toString( + commands::CommandConstants::HEADER_PASSWORD ), "" ); + } + + /** + * Gets a reference to the Transport that this connection + * is using. + * @param reference to a transport + * @throws InvalidStateException if the Transport is not set + */ + virtual transport::Transport& getTransport(void) const + throw (exceptions::InvalidStateException ) { + + if( transport == NULL ) { + throw exceptions::InvalidStateException( + __FILE__, __LINE__, + "StompConnector::getTransport - " + "Invalid State, No Transport."); + } + + return *transport; + } + + /** + * Creates a Session Info object for this connector + * @param Acknowledgement Mode of the Session + * @returns Session Info Object + * @throws ConnectorException + */ + virtual SessionInfo* createSession( + cms::Session::AcknowledgeMode ackMode) + throw( ConnectorException ); + + /** + * Create a Consumer for the given Session + * @param Destination to Subscribe to. + * @param Session Information. + * @return Consumer Information + * @throws ConnectorException + */ + virtual ConsumerInfo* createConsumer( + cms::Destination* destination, + SessionInfo* session, + const std::string& selector = "") + throw ( ConnectorException ); + + /** + * Create a Durable Consumer for the given Session + * @param Topic to Subscribe to. + * @param Session Information. + * @param name of the Durable Topic + * @param Selector + * @param if set, inhibits the delivery of messages + * published by its own connection + * @return Consumer Information + * @throws ConnectorException + */ + virtual ConsumerInfo* createDurableConsumer( + cms::Topic* topic, + SessionInfo* session, + const std::string& name, + const std::string& selector = "", + bool noLocal = false) + throw ( ConnectorException ); + + /** + * Create a Consumer for the given Session + * @param Destination to Subscribe to. + * @param Session Information. + * @return Producer Information + * @throws ConnectorException + */ + virtual ProducerInfo* createProducer( + cms::Destination* destination, + SessionInfo* session) + throw ( ConnectorException ); + + /** + * Creates a Topic given a name and session info + * @param Topic Name + * @param Session Information + * @return a newly created Topic Object + * @throws ConnectorException + */ + virtual cms::Topic* createTopic( const std::string& name, + SessionInfo* session ) + throw ( ConnectorException ); + + /** + * Creates a Queue given a name and session info + * @param Queue Name + * @param Session Information + * @return a newly created Queue Object + * @throws ConnectorException + */ + virtual cms::Queue* createQueue( const std::string& name, + SessionInfo* session ) + throw ( ConnectorException ); + + /** + * Creates a Temporary Topic given a name and session info + * @param Temporary Topic Name + * @param Session Information + * @return a newly created Temporary Topic Object + * @throws ConnectorException + */ + virtual cms::TemporaryTopic* createTemporaryTopic( + SessionInfo* session) + throw ( ConnectorException ); + + /** + * Creates a Temporary Queue given a name and session info + * @param Temporary Queue Name + * @param Session Information + * @return a newly created Temporary Queue Object + * @throws ConnectorException + */ + virtual cms::TemporaryQueue* createTemporaryQueue( + SessionInfo* session) + throw ( ConnectorException ); + + /** + * Sends a Message + * @param The Message to send. + * @param Producer Info for the sender of this message + * @throws ConnectorException + */ + virtual void send( cms::Message* message, ProducerInfo* producerInfo ) + throw ( ConnectorException ); + + /** + * Sends a set of Messages + * @param List of Messages to send. + * @param Producer Info for the sender of this message + * @throws ConnectorException + */ + virtual void send( std::list& messages, + ProducerInfo* producerInfo ) + throw ( ConnectorException ); + + /** + * Acknowledges a Message + * @param An ActiveMQMessage to Ack. + * @throws ConnectorException + */ + virtual void acknowledge( const SessionInfo* session, + const cms::Message* message, + AckType ackType) + throw ( ConnectorException ); + + /** + * Starts a new Transaction. + * @param Session Information + * @throws ConnectorException + */ + virtual TransactionInfo* startTransaction( + SessionInfo* session) + throw ( ConnectorException ); + + /** + * Commits a Transaction. + * @param The Transaction information + * @param Session Information + * @throws ConnectorException + */ + virtual void commit(TransactionInfo* transaction, + SessionInfo* session) + throw ( ConnectorException ); + + /** + * Rolls back a Transaction. + * @param The Transaction information + * @param Session Information + * @throws ConnectorException + */ + virtual void rollback(TransactionInfo* transaction, + SessionInfo* session) + throw ( ConnectorException ); + + /** + * Creates a new Message. + * @param Session Information + * @param Transaction Info for this Message + * @throws ConnectorException + */ + virtual cms::Message* createMessage( + SessionInfo* session, + TransactionInfo* transaction) + throw ( ConnectorException ); + + /** + * Creates a new BytesMessage. + * @param Session Information + * @param Transaction Info for this Message + * @throws ConnectorException + */ + virtual cms::BytesMessage* createBytesMessage( + SessionInfo* session, + TransactionInfo* transaction) + throw ( ConnectorException ); + + /** + * Creates a new TextMessage. + * @param Session Information + * @param Transaction Info for this Message + * @throws ConnectorException + */ + virtual cms::TextMessage* createTextMessage( + SessionInfo* session, + TransactionInfo* transaction) + throw ( ConnectorException ); + + /** + * Creates a new MapMessage. + * @param Session Information + * @param Transaction Info for this Message + * @throws ConnectorException + */ + virtual cms::MapMessage* createMapMessage( + SessionInfo* session, + TransactionInfo* transaction) + throw ( ConnectorException ); + + /** + * Unsubscribe from a givenDurable Subscription + * @param name of the Subscription + * @throws ConnectorException + */ + virtual void unsubscribe( const std::string& name ) + throw ( ConnectorException ); + + /** + * Destroys the given connector resource. + * @param resource the resource to be destroyed. + * @throws ConnectorException + */ + virtual void destroyResource( ConnectorResource* resource ) + throw ( ConnectorException ); + + /** + * Sets the listener of consumer messages. + * @param listener the observer. + */ + virtual void setConsumerMessageListener( + ConsumerMessageListener* listener) + { + this->messageListener = listener; + + if(sessionManager != NULL) + { + sessionManager->setConsumerMessageListener( listener ); + } + } + + /** + * Sets the Listner of exceptions for this connector + * @param ExceptionListener the observer. + */ + virtual void setExceptionListener( + cms::ExceptionListener* listener) + { + this->exceptionListener = listener; + } + + public: // transport::CommandListener + + /** + * Event handler for the receipt of a non-response command from the + * transport. + * @param command the received command object. + */ + virtual void onCommand( transport::Command* command ); + + public: // TransportExceptionListener + + /** + * Event handler for an exception from a command transport. + * @param source The source of the exception + * @param ex The exception. + */ + virtual void onTransportException( + transport::Transport* source, + const exceptions::ActiveMQException& ex ); + + public: // StompCommandListener + + /** + * Process the Stomp Command + * @param command to process + * @throw ConnterException + */ + virtual void onStompCommand( commands::StompCommand* command ) + throw ( StompConnectorException ); + + public: + + /** + * Registers a Command Listener using the CommandId specified + * if there is already a listener for that command it will be + * removed. + * @param CommandId to process + * @param pointer to the listener to call + */ + virtual void addCmdListener( + commands::CommandConstants::CommandId commandId, + StompCommandListener* listener ); + + /** + * UnRegisters a Command Listener using the CommandId specified + * @param CommandId of the listener to remove. + */ + virtual void removeCmdListener( + commands::CommandConstants::CommandId commandId ); + + private: + + unsigned int getNextProducerId( void ); + unsigned int getNextTransactionId( void ); + + // Check for Connected State and Throw an exception if not. + void enforceConnected( void ) throw ( ConnectorException ); + + }; + +}}} + +#endif /*ACTIVEMQ_CONNECTOR_STOMP_STOMPCONNECTOR_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompConnectorException.h b/activemq-cpp/src/main/activemq/connector/stomp/StompConnectorException.h new file mode 100644 index 0000000000..195a869dc2 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompConnectorException.h @@ -0,0 +1,65 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONNECTOR_STOMP_STOMPCONNECTOREXCEPTION_H_ +#define ACTIVEMQ_CONNECTOR_STOMP_STOMPCONNECTOREXCEPTION_H_ + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + /* + * Signals that an Connector exception of some sort has occurred. + */ + class StompConnectorException : public connector::ConnectorException + { + public: + + StompConnectorException() {} + StompConnectorException( const exceptions::ActiveMQException& ex ){ + *(ActiveMQException*)this = ex; + } + StompConnectorException( const StompConnectorException& ex ){ + *(exceptions::ActiveMQException*)this = ex; + } + StompConnectorException(const char* file, const int lineNumber, + const char* msg, ...) + { + va_list vargs ; + va_start(vargs, msg) ; + buildMessage(msg, vargs) ; + + // Set the first mark for this exception. + setMark( file, lineNumber ); + } + + /** + * Clones this exception. This is useful for cases where you need + * to preserve the type of the original exception as well as the message. + * All subclasses should override. + */ + virtual exceptions::ActiveMQException* clone() const{ + return new StompConnectorException( *this ); + } + virtual ~StompConnectorException() {} + + }; + +}}} + +#endif /*ACTIVEMQ_CONNECTOR_STOMP_STOMPCONNECTOREXCEPTION_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompConnectorFactory.cpp b/activemq-cpp/src/main/activemq/connector/stomp/StompConnectorFactory.cpp new file mode 100644 index 0000000000..fb13e8f041 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompConnectorFactory.cpp @@ -0,0 +1,49 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 +#include +#include +#include + +using namespace activemq; +using namespace activemq::util; +using namespace activemq::transport; +using namespace activemq::connector; +using namespace activemq::connector::stomp; + +//////////////////////////////////////////////////////////////////////////////// + +//////////////////////////////////////////////////////////////////////////////// +Connector* StompConnectorFactory::createConnector( + const activemq::util::Properties& properties, + activemq::transport::Transport* transport) +{ + return dynamic_cast( + new StompConnector(transport, properties)); +} + +//////////////////////////////////////////////////////////////////////////////// +ConnectorFactory& StompConnectorFactory::getInstance(void) +{ + // Create a static instance of the registrar and return a reference to + // its internal instance of this class. + static ConnectorFactoryMapRegistrar registrar( + "stomp", new StompConnectorFactory()); + + return registrar.getFactory(); +} diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompConnectorFactory.h b/activemq-cpp/src/main/activemq/connector/stomp/StompConnectorFactory.h new file mode 100644 index 0000000000..d961e99b51 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompConnectorFactory.h @@ -0,0 +1,54 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONNECTOR_STOMP_STOMPCONNECTORFACTORY_H_ +#define ACTIVEMQ_CONNECTOR_STOMP_STOMPCONNECTORFACTORY_H_ + +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + class StompConnectorFactory : public connector::ConnectorFactory + { + private: + + + public: + + virtual ~StompConnectorFactory(void) {} + + /** + * Creates a StompConnector + * @param The Properties that the new connector is configured with + */ + virtual Connector* createConnector( + const activemq::util::Properties& properties, + activemq::transport::Transport* transport); + + /** + * Returns an instance of this Factory by reference + * @return StompConnectorFactory reference + */ + static ConnectorFactory& getInstance(void); + + }; + +}}} + +#endif /*ACTIVEMQ_CONNECTOR_STOMP_STOMPCONNECTORFACTORY_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompConsumerInfo.h b/activemq-cpp/src/main/activemq/connector/stomp/StompConsumerInfo.h new file mode 100644 index 0000000000..c362390dc8 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompConsumerInfo.h @@ -0,0 +1,118 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_STOMPCONSUMERINFO_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_STOMPCONSUMERINFO_H_ + +namespace activemq{ +namespace connector{ +namespace stomp{ + + class StompConsumerInfo : public ConsumerInfo + { + private: + + // Message Selector for this Consumer + std::string selector; + + // Consumer Id + unsigned int consumerId; + + // Destination + cms::Destination* destination; + + // Session Info - We do not own this + const SessionInfo* session; + + public: + + StompConsumerInfo(void) { + selector = ""; + consumerId = 0; + destination = NULL; + } + virtual ~StompConsumerInfo(void) { delete destination; } + + /** + * Gets this message consumer's message selector expression. + * @return This Consumer's selector expression or "". + */ + virtual const std::string& getMessageSelector(void) const { + return selector; + } + + /** + * Sets this message consumer's message selector expression. + * @param This Consumer's selector expression or "". + */ + virtual void setMessageSelector( const std::string& selector ) { + this->selector = selector; + } + + /** + * Gets the ID that is assigned to this consumer + * @return value of the Consumer Id. + */ + virtual unsigned int getConsumerId(void) const { + return consumerId; + } + + /** + * Sets the ID that is assigned to this consumer + * @return string value of the Consumer Id. + */ + virtual void setConsumerId( const unsigned int id ) { + this->consumerId = id; + } + + /** + * Gets the Destination that this Consumer is subscribed on + * @return Destination + */ + virtual const cms::Destination& getDestination(void) const { + return *destination; + } + + /** + * Sets the destination that this Consumer is listening on + * @param Destination + */ + virtual void setDestination( const cms::Destination& destination ) { + this->destination = destination.clone(); + } + + /** + * Gets the Session Info that this consumer is attached too + * @return SessionnInfo pointer + */ + virtual const SessionInfo* getSessionInfo(void) const { + return session; + } + + /** + * Gets the Session Info that this consumer is attached too + * @return SessionnInfo pointer + */ + virtual void setSessionInfo( const SessionInfo* session ) { + this->session = session; + } + + }; + +}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_STOMPCONSUMERINFO_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompDestination.h b/activemq-cpp/src/main/activemq/connector/stomp/StompDestination.h new file mode 100644 index 0000000000..12014272a2 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompDestination.h @@ -0,0 +1,106 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_STOMPDESTINATION_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_STOMPDESTINATION_H_ + +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + /** + * Templatized Destination Class that bundles all the common aspects + * of a Stomp Destination into one class. The template arguement is + * one of Topic, Queue, TemporaryTopic, or TemporaryQueue. + */ + template + class StompDestination : public T + { + private: + + // Destination type + cms::Destination::DestinationType destType; + + // Name of the Destination + std::string name; + + public: + + StompDestination(void) {} + + StompDestination( const std::string& name, + cms::Destination::DestinationType type ) + { + this->name = name; + this->destType = type; + } + + virtual ~StompDestination(void) {} + + /** + * Retrieves the name of this destination, plus the stomp + * destination decorator + * @return name + */ + virtual std::string toProviderString(void) const { + return getPrefix() + name; + } + + /** + * Retrieve the Destination Type for this Destination + * @return The Destination Type + */ + virtual cms::Destination::DestinationType getDestinationType(void) const { + return destType; + } + + /** + * Converts the Destination Name into a String minus the + * stomp decorator + * @return string name + */ + virtual std::string toString(void) const { + return name; + } + + /** + * Copies the contents of the given Destinastion object to this one. + * @param source The source Destination object. + */ + virtual void copy( const cms::Destination& source ) { + this->destType = source.getDestinationType(); + this->name = source.toString(); + } + + protected: + + /** + * Retrieves the proper Stomp Prefix for the specified type + * of Destination + * @return string prefix + */ + virtual std::string getPrefix(void) const = 0; + + }; + +}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_STOMPDESTINATION_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompFrame.h b/activemq-cpp/src/main/activemq/connector/stomp/StompFrame.h new file mode 100644 index 0000000000..89d9cb4d43 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompFrame.h @@ -0,0 +1,127 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONNECTOR_STOMP_STOMPFRAMEWRAPPER_H_ +#define ACTIVEMQ_CONNECTOR_STOMP_STOMPFRAMEWRAPPER_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + /** + * A Stomp-level message frame that encloses all messages + * to and from the broker. + */ + class StompFrame{ + public: + + /** + * Default constructor. + */ + StompFrame(void){ + body = NULL; + bodyLength = 0; + } + + /** + * Destruction - frees the memory pool. + */ + virtual ~StompFrame(void) { delete body; } + + /** + * Clonse this message exactly, returns a new instance that the + * caller is required to delete. + * @return new copy of this message + */ + virtual StompFrame* clone(void) const { + StompFrame* frame = new StompFrame(); + frame->command = command; + frame->properties = properties; + char* cpyBody = new char[bodyLength]; + memcpy(cpyBody, body, bodyLength); + frame->setBody(cpyBody, bodyLength); + return frame; + } + + /** + * Sets the command for this stomp frame. + * @param command The command to be set. + */ + void setCommand( const std::string& cmd ){ + this->command = cmd; + } + + /** + * Accessor for this frame's command field. + */ + const std::string& getCommand(void) const{ + return command; + } + + /** + * Gets access to the header properties for this frame. + */ + util::Properties& getProperties(void){ return properties; } + const util::Properties& getProperties(void) const { + return properties; + } + + /** + * Accessor for the body data of this frame. + * @return char pointer to body data + */ + const char* getBody(void) const{ + return body; + } + + /** + * Return the number of bytes contained in this frames body + * @return Body bytes length. + */ + int getBodyLength(void) const{ return bodyLength; } + + /** + * Sets the body data of this frame as a byte sequence. + * @param bytes The byte buffer to be set in the body. + * @param numBytes The number of bytes in the buffer. + */ + void setBody( const char* bytes, const int numBytes ){ + body = bytes; + bodyLength = numBytes; + } + + private: + + // String Name of this command. + std::string command; + + // Properties of the Stomp Message + util::SimpleProperties properties; + + // Byte data of Body. + const char* body; + int bodyLength; + + }; + +}}} + +#endif /*ACTIVEMQ_CONNECTOR_STOMP_STOMPFRAMEWRAPPER_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompProducerInfo.h b/activemq-cpp/src/main/activemq/connector/stomp/StompProducerInfo.h new file mode 100644 index 0000000000..b40c922ab2 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompProducerInfo.h @@ -0,0 +1,99 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_STOMPPRODUCERINFO_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_STOMPPRODUCERINFO_H_ + +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + class StompProducerInfo : public ProducerInfo + { + private: + + // Producer Id + unsigned int producerId; + + // Default Destination + cms::Destination* dest; + + // Session that this producer is attached to - we do not own this + const SessionInfo* session; + + public: + + StompProducerInfo(void) { dest = NULL; } + virtual ~StompProducerInfo(void) { delete dest; } + + /** + * Retrieves the default destination that this producer + * sends its messages to. + * @return Destionation, owned by this object + */ + virtual const cms::Destination& getDestination(void) const { + return *dest; + } + + /** + * Sets the Default Destination for this Producer + * @param reference to a destination, copied internally + */ + virtual void setDestination( const cms::Destination& dest ) { + this->dest = dest.clone(); + } + + /** + * Gets the ID that is assigned to this Producer + * @return value of the Producer Id. + */ + virtual unsigned int getProducerId(void) const { + return producerId; + } + + /** + * Sets the ID that is assigned to this Producer + * @return string value of the Producer Id. + */ + virtual void setProducerId( const unsigned int id ) { + this->producerId = id; + } + + /** + * Gets the Session Info that this consumer is attached too + * @return SessionnInfo pointer + */ + virtual const SessionInfo* getSessionInfo(void) const { + return session; + } + + /** + * Gets the Session Info that this consumer is attached too + * @return SessionnInfo pointer + */ + virtual void setSessionInfo( const SessionInfo* session ) { + this->session = session; + } + + }; + +}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_STOMPPRODUCERINFO_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompQueue.h b/activemq-cpp/src/main/activemq/connector/stomp/StompQueue.h new file mode 100644 index 0000000000..c100286899 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompQueue.h @@ -0,0 +1,74 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_STOMPQUEUE_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_STOMPQUEUE_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + class StompQueue : public StompDestination + { + public: + + StompQueue(void) : StompDestination< cms::Queue >() {} + + StompQueue(const std::string& name) : + StompDestination< cms::Queue >( name, cms::Destination::QUEUE ) + {} + + virtual ~StompQueue(void) {} + + /** + * Gets the name of this queue. + * @return The queue name. + */ + virtual std::string getQueueName(void) const + throw( cms::CMSException ) { + return toString(); + } + + /** + * Creates a new instance of this destination type that is a + * copy of this one, and returns it. + * @returns cloned copy of this object + */ + virtual cms::Destination* clone(void) const { + return new StompQueue( toString() ); + } + + protected: + + /** + * Retrieves the proper Stomp Prefix for the specified type + * of Destination + * @return string prefix + */ + virtual std::string getPrefix(void) const { + return commands::CommandConstants::queuePrefix; + } + + }; + +}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_STOMPQUEUE_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompSelector.h b/activemq-cpp/src/main/activemq/connector/stomp/StompSelector.h new file mode 100644 index 0000000000..b457ac1e4c --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompSelector.h @@ -0,0 +1,31 @@ +#ifndef ACTIVEMQ_CONNECTOR_STOMP_STOMPSELECTOR_H_ +#define ACTIVEMQ_CONNECTOR_STOMP_STOMPSELECTOR_H_ + +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + /** + * Since the stomp protocol doesn't have a consumer-based selector + * mechanism, we have to do the selector logic on the client + * side. This class provides the selector algorithm that is + * needed to determine if a given message is to be selected for + * a given consumer's selector string. + */ + class StompSelector{ + public: + + static bool isSelected( const std::string& selector, + cms::Message* msg ) + { + return true; + } + + }; + +}}} + +#endif /*ACTIVEMQ_CONNECTOR_STOMP_STOMPSELECTOR_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompSessionInfo.h b/activemq-cpp/src/main/activemq/connector/stomp/StompSessionInfo.h new file mode 100644 index 0000000000..162de52197 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompSessionInfo.h @@ -0,0 +1,134 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_STOMPSESSIONINFO_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_STOMPSESSIONINFO_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + class StompSessionInfo : public connector::SessionInfo + { + private: + + // Acknowledge Mode of this Session + cms::Session::AcknowledgeMode ackMode; + + // The id of the connection to the broker + // (given to us by the broker) + std::string connectionId; + + // The unique session id + unsigned int sessionId; + + // Info for this sessions current transaction + const TransactionInfo* transaction; + + public: + + /** + * Constructor + */ + StompSessionInfo(void) + { + sessionId = 0; + ackMode = cms::Session::AutoAcknowledge; + } + + /** + * Destructor + */ + virtual ~StompSessionInfo(void) {} + + /** + * Gets the Connection Id of the Connection that this consumer is + * using to receive its messages. + * @return string value of the connection id + */ + virtual const std::string& getConnectionId(void) const{ + return connectionId; + } + + /** + * Sets the Connection Id of the Connection that this consumer is + * using to receive its messages. + * @param string value of the connection id + */ + virtual void setConnectionId( const std::string& id ){ + connectionId = id; + } + + /** + * Gets the Sessions Id value + * @return id for this session + */ + virtual unsigned int getSessionId(void) const { + return sessionId; + } + + /** + * Sets the Session Id for this Session + * @param integral id value for this session + */ + virtual void setSessionId( const unsigned int id ) { + this->sessionId = id; + } + + /** + * Sets the Ack Mode of this Session Info object + * @param Ack Mode + */ + virtual void setAckMode(cms::Session::AcknowledgeMode ackMode) { + this->ackMode = ackMode; + } + + /** + * Gets the Ack Mode of this Session + * @return Ack Mode + */ + virtual cms::Session::AcknowledgeMode getAckMode(void) const { + return ackMode; + } + + /** + * Gets the currently active transaction info, if this session is + * transacted, returns NULL when not transacted. You must call + * getAckMode and see if the session is transacted. + * @return Transaction Id of current Transaction + */ + virtual const TransactionInfo* getTransactionInfo(void) const { + return transaction; + } + + /** + * Sets the current transaction info for this session, this is nit + * used when the session is not transacted. + * @param Transaction Id + */ + virtual void setTransactionInfo( const TransactionInfo* transaction ) { + this->transaction = transaction; + } + + }; + +}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_STOMPSESSIONINFO_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompSessionManager.cpp b/activemq-cpp/src/main/activemq/connector/stomp/StompSessionManager.cpp new file mode 100644 index 0000000000..d7fabe6ff1 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompSessionManager.cpp @@ -0,0 +1,326 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "StompSessionManager.h" + +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::exceptions; +using namespace activemq::transport; +using namespace activemq::connector; +using namespace activemq::connector::stomp; +using namespace activemq::connector::stomp::commands; + +//////////////////////////////////////////////////////////////////////////////// +StompSessionManager::StompSessionManager( const std::string& connectionId, + Transport* transport ) +{ + if( transport == NULL ) + { + throw NullPointerException( + __FILE__, __LINE__, + "StompSessionManager::StompSessionManager" ); + } + + this->transport = transport; + this->connectionId = connectionId; + this->nextSessionId = 0; + this->nextConsumerId = 0; + this->messageListener = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +StompSessionManager::~StompSessionManager(void) +{ + // NOTE - I am not cleaning out the ConsumerInfo objects in the + // map becaise it is really the job of the consumer ot remove itself + // when it is destructed. If it doesn't then we would have problems, + // but if it does, but it's deleted after this object then we would + // still have problems. +} + +//////////////////////////////////////////////////////////////////////////////// +unsigned int StompSessionManager::getNextSessionId(void) +{ + synchronized(&mutex) + { + return nextSessionId++; + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +unsigned int StompSessionManager::getNextConsumerId(void) +{ + synchronized(&mutex) + { + return nextConsumerId++; + } + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +connector::SessionInfo* StompSessionManager::createSession( + cms::Session::AcknowledgeMode ackMode) + throw ( exceptions::ActiveMQException ) +{ + try + { + SessionInfo* session = new StompSessionInfo(); + + // Init data + session->setAckMode(ackMode); + session->setConnectionId( connectionId ); + session->setSessionId( getNextSessionId() ); + + return session; + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void StompSessionManager::removeSession( + connector::SessionInfo* session ) + throw ( exceptions::ActiveMQException ) +{ + // NO-op +} + +//////////////////////////////////////////////////////////////////////////////// +connector::ConsumerInfo* StompSessionManager::createConsumer( + cms::Destination* destination, + SessionInfo* session, + const std::string& selector) + throw( ConnectorException ) +{ + try + { + // Delegate to the createDurableConsumer method, just pas the + // appropriate params so that a regular consumer is created on + // the broker side. + return createDurableConsumer( + destination, session, "", selector, false ); + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ) +} + +//////////////////////////////////////////////////////////////////////////////// +connector::ConsumerInfo* StompSessionManager::createDurableConsumer( + cms::Destination* destination, + SessionInfo* session, + const std::string& name, + const std::string& selector, + bool noLocal ) + throw ( ConnectorException ) +{ + try + { + synchronized(&mutex) + { + // Find the right mapping to consumers + ConsumerMap& consumerMap = + destinationMap[ destination->toString() ]; + + // We only need to send a sub request if there are no active + // consumers on this destination. + if( consumerMap.empty() ) + { + // Send the request to the Broker + SubscribeCommand cmd; + + if( session->getAckMode() == cms::Session::ClientAcknowledge ) + { + cmd.setAckMode( CommandConstants::ACK_CLIENT ); + } + cmd.setDestination( destination->toProviderString() ); + cmd.setNoLocal( noLocal ); + + if( name != "" ) + { + cmd.setSubscriptionName( name ); + } + + // The Selector is set on the first subscribe on this dest, + // and if another consumer is created on this destination + // that specifies a selector it will be ignored. While + // this is not ideal, is the only way to handle the fact + // that activemq stomp doesn't support multiple sessions. + if( selector != "" ) + { + cmd.setMessageSelector( selector ); + } + + // Fire the message + transport->oneway( &cmd ); + } + + // Initialize a new Consumer info Message + ConsumerInfo* consumer = new StompConsumerInfo(); + + consumer->setConsumerId( getNextConsumerId() ); + consumer->setDestination( *destination ); + consumer->setMessageSelector( selector ); + consumer->setSessionInfo( session ); + + // Store this consumer for later message dispatching. + consumerMap.insert( + make_pair( consumer->getConsumerId(), consumer ) ); + + return consumer; + } + + return NULL; + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void StompSessionManager::removeConsumer( + connector::ConsumerInfo* consumer) + throw( ConnectorException ) +{ + try + { + synchronized(&mutex) + { + DestinationMap::iterator itr = + destinationMap.find( consumer->getDestination().toString() ); + + if( itr == destinationMap.end() ) + { + // Already removed from the map + return; + } + + ConsumerMap& consumers = itr->second; + + // Remove from the map. + consumers.erase( consumer->getConsumerId() ); + + // If there are no more on this destination then we unsubscribe + if( consumers.empty() ) + { + UnsubscribeCommand cmd; + + cmd.setDestination( + consumer->getDestination().toProviderString() ); + + // Send the message + transport->oneway( &cmd ); + } + } + } + AMQ_CATCH_RETHROW( ConnectorException ) + AMQ_CATCHALL_THROW( ConnectorException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void StompSessionManager::onStompCommand( commands::StompCommand* command ) + throw ( StompConnectorException ) +{ + try + { + cms::Message* message = dynamic_cast< cms::Message*>( command ); + + if( message == NULL ) + { + throw StompConnectorException( + __FILE__, __LINE__, + "StompSessionManager::onStompCommand - Invalid Command" ); + } + + if( messageListener == NULL ) + { + throw StompConnectorException( + __FILE__, __LINE__, + "StompSessionManager::onStompCommand - " + "No Message Listener Registered." ); + } + + synchronized(&mutex) + { + DestinationMap::iterator itr = + destinationMap.find( message->getCMSDestination().toString() ); + + if( itr == destinationMap.end() ) + { + throw StompConnectorException( + __FILE__, __LINE__, + "StompSessionManager::onStompCommand - " + "Received a Message that doesn't have a listener" ); + } + + // If we only have 1 consumer, we don't need to clone the original + // message. + if(itr->second.size() == 1) + { + ConsumerInfo* consumerInfo = itr->second.begin()->second; + + if( StompSelector::isSelected( + consumerInfo->getMessageSelector(), + message ) ) + { + ActiveMQMessage* msg = + dynamic_cast< ActiveMQMessage* >( message ); + messageListener->onConsumerMessage( consumerInfo, msg ); + } + + return; + } + + // We have more than one consumer of this message - we have to + // clone the message for each consumer so they don't destroy each other's + // message. + ConsumerMap::iterator c_itr = itr->second.begin(); + + for(; c_itr != itr->second.end(); ++c_itr ) + { + ConsumerInfo* consumerInfo = c_itr->second; + + if( StompSelector::isSelected( + consumerInfo->getMessageSelector(), + message ) ) + { + ActiveMQMessage* msg = + dynamic_cast< ActiveMQMessage* >( message->clone() ); + messageListener->onConsumerMessage( consumerInfo, msg ); + } + } + + // We got here which means that we sent copies, so remove + // the original. + delete command; + } + } + AMQ_CATCH_RETHROW( StompConnectorException ) + AMQ_CATCH_EXCEPTION_CONVERT( ActiveMQException, StompConnectorException ) + AMQ_CATCHALL_THROW( StompConnectorException ) +} diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompSessionManager.h b/activemq-cpp/src/main/activemq/connector/stomp/StompSessionManager.h new file mode 100644 index 0000000000..17b41d50b2 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompSessionManager.h @@ -0,0 +1,185 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_STOMPSESSIONMANAGER_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_STOMPSESSIONMANAGER_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + /** + * The Stomp Session Manager is responsible for managing multiple + * Client Sessions. The management involves routing messages to + * sessions. If more than one ActiveMQConsumer is created that is + * listening to the same Topic or Queue, then the messages that are + * received must be delivered to each of those sessions, and copied + * so that a transactional session can manage the lifetime of the + * message. + */ + class StompSessionManager : public StompCommandListener + { + private: + + // Map Types + typedef std::map ConsumerMap; + typedef std::map DestinationMap; + + private: + + // Next id to be used for a Session Id + unsigned int nextSessionId; + + // Next id to be used for a Consumer Id + unsigned int nextConsumerId; + + // Mutex to protect ids. + concurrent::Mutex mutex; + + // Mapping of a Session to all the consumer's + DestinationMap destinationMap; + + // Transport that we use to find a transport for sending + // commands + transport::Transport* transport; + + // Consumer Message listener, we notify this whenever we receive + // a new StompMessage type command. + ConsumerMessageListener* messageListener; + + // The global connection id + std::string connectionId; + + public: + + StompSessionManager( const std::string& connectionId, + transport::Transport* transport ); + virtual ~StompSessionManager(void); + + /** + * Creates a new Session and returns a SessionInfo object whose + * lifetime is the property of the caller. + * @param the ackMode of the session. + * @return new SessionInfo object + */ + virtual connector::SessionInfo* createSession( + cms::Session::AcknowledgeMode ackMode) + throw ( exceptions::ActiveMQException ); + + /** + * removes the specified Session from the Manager, all data that + * is associated with session consumers is now lost. The session + * is not deleted here, it is the owner's responsibility. + * @param the session info for the session to remove. + */ + virtual void removeSession( connector::SessionInfo* session ) + throw ( exceptions::ActiveMQException ); + + /** + * Creates a new consumer to the specified session, will subscribe + * to the destination if another consumer hasn't already been + * subbed to that destination. The returned consumer is the + * owned by the caller and not deleted by this class. + * @param destination to subscribe to + * @param session to associate with + * @param selector string + * @return new ConsumerInfo object. + */ + virtual connector::ConsumerInfo* createConsumer( + cms::Destination* destination, + SessionInfo* session, + const std::string& selector) + throw( ConnectorException ); + + /** + * Creates a new durable consumer to the specified session, will + * subscribe to the destination if another consumer hasn't already + * been subbed to that destination. The returned consumer is the + * owned by the caller and not deleted by this class. + * @param Topic to subscribe to + * @param session to associate with + * @param Subscription Name + * @param selector string + * @param Should we be notified of messages we send. + * @return new ConsumerInfo object. + */ + virtual connector::ConsumerInfo* createDurableConsumer( + cms::Destination* destination, + SessionInfo* session, + const std::string& name, + const std::string& selector, + bool noLocal ) + throw ( ConnectorException ); + + /** + * Removes the Consumer from the session, will unsubscrive if the + * consumer is the only one listeneing on this destination. The + * Consumer is not deleted, just unassociated from the Manager + * caller is responsible for managing the lifetime. + * @param the ConsumerInfo for the consumer to remove + * @throws ConnectorException + */ + virtual void removeConsumer( connector::ConsumerInfo* consumer ) + throw( ConnectorException ); + + /** + * Sets the listener of consumer messages. + * @param listener the observer. + */ + virtual void setConsumerMessageListener( + ConsumerMessageListener* listener ) + { + this->messageListener = listener; + } + + public: // StompCommand Listener + + /** + * Process the Stomp Command + * @param command to process + * @throw ConnterException + */ + virtual void onStompCommand( commands::StompCommand* command ) + throw ( StompConnectorException ); + + protected: + + /** + * Gets the Next Session Id + * @return unique session id + */ + virtual unsigned int getNextSessionId(void); + + /** + * Gets the Next Session Id + * @return unique session id + */ + virtual unsigned int getNextConsumerId(void); + + }; + +}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_STOMPSESSIONMANAGER_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompTopic.h b/activemq-cpp/src/main/activemq/connector/stomp/StompTopic.h new file mode 100644 index 0000000000..b4b6241cd3 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompTopic.h @@ -0,0 +1,74 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_STOMPTOPIC_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_STOMPTOPIC_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + class StompTopic : public StompDestination + { + public: + + StompTopic(void) : StompDestination() {} + + StompTopic(const std::string& name) : + StompDestination< cms::Topic >( name, cms::Destination::TOPIC ) + {} + + virtual ~StompTopic(void) {} + + /** + * Gets the name of this queue. + * @return The queue name. + */ + virtual std::string getTopicName(void) const + throw( cms::CMSException ) { + return toString(); + } + + /** + * Creates a new instance of this destination type that is a + * copy of this one, and returns it. + * @returns cloned copy of this object + */ + virtual cms::Destination* clone(void) const { + return new StompTopic( toString() ); + } + + protected: + + /** + * Retrieves the proper Stomp Prefix for the specified type + * of Destination + * @return string prefix + */ + virtual std::string getPrefix(void) const { + return commands::CommandConstants::topicPrefix; + } + + }; + +}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_STOMPTOPIC_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/StompTransactionInfo.h b/activemq-cpp/src/main/activemq/connector/stomp/StompTransactionInfo.h new file mode 100644 index 0000000000..8ef73a08e8 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/StompTransactionInfo.h @@ -0,0 +1,88 @@ +/* +* Copyright 2006 The Apache Software Foundation or its licensors, as +* applicable. +* +* 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 ACTIVEMQ_CONNECTOR_STOMPTRANSACTIONINFO_H_ +#define ACTIVEMQ_CONNECTOR_STOMPTRANSACTIONINFO_H_ + +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + class StompTransactionInfo : public connector::TransactionInfo + { + private: + + // Transaction Id + unsigned int transactionId; + + // Session Info - We do not own this + const SessionInfo* session; + + public: + + /** + * TransactionInfo Constructor + */ + StompTransactionInfo(void) { + transactionId = 0; + session = NULL; + } + + /** + * Destructor + */ + virtual ~StompTransactionInfo(void) {} + + /** + * Gets the Transction Id + * @return unsigned int Id + */ + virtual unsigned int getTransactionId(void) const { + return transactionId; + } + + /** + * Sets the Transction Id + * @param unsigned int Id + */ + virtual void setTransactionId( const unsigned int id ) { + this->transactionId = id; + } + + /** + * Gets the Session Info that this Transction is attached too + * @return SessionnInfo pointer + */ + virtual const SessionInfo* getSessionInfo(void) const { + return session; + } + + /** + * Gets the Session Info that this Transction is attached too + * @return SessionnInfo pointer + */ + virtual void setSessionInfo( const SessionInfo* session ) { + this->session = session; + } + + }; + +}}} + +#endif /*ACTIVEMQ_CONNECTOR_STOMPTRANSACTIONINFO_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/AbortCommand.h b/activemq-cpp/src/main/activemq/connector/stomp/commands/AbortCommand.h new file mode 100644 index 0000000000..b40b62eabe --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/AbortCommand.h @@ -0,0 +1,85 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_ABORTCOMMAND_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_ABORTCOMMAND_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + /** + * Represents the Stomp Abort Command which rolls back a + * transaction in progress. + */ + class AbortCommand : public AbstractCommand< transport::Command > + { + public: + + AbortCommand(void) : + AbstractCommand() { + initialize( getFrame() ); + } + AbortCommand( StompFrame* frame ) : + AbstractCommand(frame) { + validate( getFrame() ); + } + virtual ~AbortCommand(void) {} + + protected: + + /** + * Inheritors are required to override this method to init the + * frame with data appropriate for the command type. + * @param Frame to init + */ + virtual void initialize( StompFrame& frame ) + { + frame.setCommand( CommandConstants::toString( + CommandConstants::ABORT ) ); + } + + /** + * Inheritors are required to override this method to validate + * the passed stomp frame before it is marshalled or unmarshaled + * @param Frame to validate + * @returns true if frame is valid + */ + virtual bool validate( const StompFrame& frame ) const + { + if((frame.getCommand() == + CommandConstants::toString( CommandConstants::ABORT ) ) && + (frame.getProperties().hasProperty( + CommandConstants::toString( + CommandConstants::HEADER_TRANSACTIONID ) ) ) ) + { + return true; + } + + return false; + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_ABORTCOMMAND_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/AbstractCommand.h b/activemq-cpp/src/main/activemq/connector/stomp/commands/AbstractCommand.h new file mode 100644 index 0000000000..6a606259f9 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/AbstractCommand.h @@ -0,0 +1,280 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_ABSTRACTCOMMAND_H_ +#define ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_ABSTRACTCOMMAND_H_ + +#include +#include +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + /** + * Interface for all Stomp commands. Commands wrap + * around a stomp frame and provide their own marshalling + * to and from frames. Stomp frame objects are dumb and have + * a generic interface that becomes cumbersome to use directly. + * Commands help to abstract the stomp frame by providing a + * more user-friendly interface to the frame content. + */ + + template + class AbstractCommand + : + public StompCommand, + public T + { + protected: + + // Frame that contains the actual message + StompFrame* frame; + + protected: + + StompFrame& getFrame(void) { + if( frame == NULL ){ + throw exceptions::NullPointerException( + __FILE__, __LINE__, + "AbstractCommand::getFrame - Frame not initialized"); + } + + return *frame; + } + + const StompFrame& getFrame(void) const { + if( frame == NULL ){ + throw exceptions::NullPointerException( + __FILE__, __LINE__, + "AbstractCommand::getFrame - Frame not initialized"); + } + + return *frame; + } + + void destroyFrame(void) + { + if( frame != NULL ){ + delete frame; + frame = NULL; + } + } + + const char* getPropertyValue( const std::string& name ) const{ + return getFrame().getProperties().getProperty( name ); + } + + const std::string getPropertyValue( + const std::string& name, + const std::string& defReturn ) const { + return getFrame().getProperties().getProperty( + name, defReturn ); + } + + void setPropertyValue( const std::string& name, const std::string& value ){ + getFrame().getProperties().setProperty( name, value ); + } + + /** + * Inheritors are required to override this method to init the + * frame with data appropriate for the command type. + * @param Frame to init + */ + virtual void initialize( StompFrame& frame ) = 0; + + /** + * Inheritors are required to override this method to validate + * the passed stomp frame before it is marshalled or unmarshaled + * @param Frame to validate + * @returns true if frame is valid + */ + virtual bool validate( const StompFrame& frame ) const = 0; + + public: + + AbstractCommand(void){ + frame = new StompFrame; + } + AbstractCommand(StompFrame* frame){ + this->frame = frame; + } + virtual ~AbstractCommand(void){ + destroyFrame(); + } + + /** + * Sets the Command Id of this Message + * @param Command Id + */ + virtual void setCommandId( const unsigned int id ){ + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_REQUESTID), + util::Integer::toString( id ) ); + } + + /** + * Gets the Command Id of this Message + * @return Command Id + */ + virtual unsigned int getCommandId(void) const { + return util::Integer::parseInt( + getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_REQUESTID ), + "0" ) ); + } + + /** + * Set if this Message requires a Response + * @param true if response is required + */ + virtual void setResponseRequired( const bool required ) { + } + + /** + * Is a Response required for this Command + * @return true if a response is required. + */ + virtual bool isResponseRequired(void) const { + return frame->getProperties().hasProperty( + CommandConstants::toString( + CommandConstants::HEADER_REQUESTID) ); + } + + /** + * Gets the Correlation Id that is associated with this message + * @return the Correlation Id + */ + virtual unsigned int getCorrelationId(void) const { + return util::Integer::parseInt( + getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_RESPONSEID ), + "0" ) ); + } + + /** + * Sets the Correlation Id if this Command + * @param Id + */ + virtual void setCorrelationId( const unsigned int corrId ) { + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_RESPONSEID), + util::Integer::toString( corrId ) ); + } + + /** + * Get the Transaction Id of this Command + * @return the Id of the Transaction + */ + virtual const char* getTransactionId(void) const{ + return getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_TRANSACTIONID) ); + } + + /** + * Set the Transaction Id of this Command + * @param the Id of the Transaction + */ + virtual void setTransactionId( const std::string& id ){ + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_TRANSACTIONID), + id ); + } + + /** + * Retrieve the Stomp Command Id for this message. + * @return Stomp CommandId enum + */ + virtual CommandConstants::CommandId getStompCommandId(void) const { + return CommandConstants::toCommandId( + getFrame().getCommand() ); + } + + /** + * Marshals the command to a stomp frame. + * @returns the stomp frame representation of this + * command. + * @throws MarshalException if the command is not + * in a state that can be marshaled. + */ + virtual const StompFrame& marshal(void) const + throw (marshal::MarshalException) + { + if( frame == NULL || !validate( *frame ) ){ + throw marshal::MarshalException( + __FILE__, __LINE__, + "AbstractCommand::marshal() - frame invalid" ); + } + + return getFrame(); + } + + protected: + + /** + * Fetch the number of bytes in the Stomp Frame Body + * @return number of bytes + */ + virtual unsigned long getNumBytes(void) const{ + return getFrame().getBodyLength(); + } + + /** + * Returns a char array of bytes that are contained in the message + * @param pointer to array of bytes. + */ + virtual const char* getBytes(void) const{ + return getFrame().getBody(); + } + + /** + * Set the bytes that are to be sent in the body of this message + * the content length flag indicates if the Content Length header + * should be set. + * @param bytes to store + * @param number of bytes to pull from the bytes buffer + * @param true if the content length header should be set + */ + virtual void setBytes( const char* bytes, + const unsigned long numBytes, + const bool setContentLength = true ) + { + char* copy = new char[numBytes]; + memcpy( copy, bytes, numBytes ); + getFrame().setBody( copy, numBytes ); + if( setContentLength ) + { + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_CONTENTLENGTH), + util::Long::toString( numBytes ) ); + } + } + }; + +}}}} + +#endif /*ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_ABSTRACTCOMMAND_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/AckCommand.h b/activemq-cpp/src/main/activemq/connector/stomp/commands/AckCommand.h new file mode 100644 index 0000000000..d24027510a --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/AckCommand.h @@ -0,0 +1,113 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNCETOR_STOMP_COMMANDS_ACKCOMMAND_H_ +#define _ACTIVEMQ_CONNCETOR_STOMP_COMMANDS_ACKCOMMAND_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + /** + * Stomp Command that Represents Acknowledgement of a message + * receive. The Ack Command has one required attribute, message + * Id. For each message sent to the client from the broker, the + * message will not be considered consumed until an Ack is sent. + * Optionally a Transaction Id can be set that indicates that the + * message acknowledgement should be part of a named transaction. + */ + class AckCommand : public AbstractCommand< transport::Command > + { + public: + + AckCommand(void) : + AbstractCommand() { + initialize( getFrame() ); + } + AckCommand( StompFrame* frame ) : + AbstractCommand(frame) { + validate( getFrame() ); + } + virtual ~AckCommand(void) {} + + /** + * Get the Message Id of this Command + * @return the Id of the Message + */ + virtual const char* getMessageId(void) const{ + return getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_MESSAGEID) ); + } + + /** + * Set the Message Id that this Ack is associated with + * @param the Message Id + */ + virtual void setMessageId(const std::string& messageId){ + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_MESSAGEID), + messageId ); + } + + protected: + + /** + * Inheritors are required to override this method to init the + * frame with data appropriate for the command type. + * @param Frame to init + */ + virtual void initialize( StompFrame& frame ) + { + frame.setCommand( CommandConstants::toString( + CommandConstants::ACK ) ); + } + + /** + * Inheritors are required to override this method to validate + * the passed stomp frame before it is marshalled or unmarshaled + * @param Frame to validate + * @returns true if frame is valid + */ + virtual bool validate( const StompFrame& frame ) const + { + if((frame.getCommand() == + CommandConstants::toString( CommandConstants::ACK )) && + (frame.getProperties().hasProperty( + CommandConstants::toString( + CommandConstants::HEADER_TRANSACTIONID ) ) && + (frame.getProperties().hasProperty( + CommandConstants::toString( + CommandConstants::HEADER_MESSAGEID ) ) ) ) ); + { + return true; + } + + return false; + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNCETOR_STOMP_COMMANDS_ACKCOMMAND_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/BeginCommand.h b/activemq-cpp/src/main/activemq/connector/stomp/commands/BeginCommand.h new file mode 100644 index 0000000000..5d25061461 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/BeginCommand.h @@ -0,0 +1,90 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_BEGINCOMMAND_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_BEGINCOMMAND_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + /** + * Begins a Transaction. Transactions in this case apply to + * sending and acknowledging -- any messages sent or acknowledged + * during a transaction will be handled atomically based on the + * transaction. + * + * A transaction Identifier is required and this id will be used + * for all sends, commits, aborts, or acks. + */ + class BeginCommand : public AbstractCommand< transport::Command > + { + public: + + BeginCommand(void) : + AbstractCommand() { + initialize( getFrame() ); + } + BeginCommand( StompFrame* frame ) : + AbstractCommand(frame) { + validate( getFrame() ); + } + virtual ~BeginCommand(void) {} + + protected: + + /** + * Inheritors are required to override this method to init the + * frame with data appropriate for the command type. + * @param Frame to init + */ + virtual void initialize( StompFrame& frame ) + { + frame.setCommand( CommandConstants::toString( + CommandConstants::BEGIN ) ); + } + + /** + * Inheritors are required to override this method to validate + * the passed stomp frame before it is marshalled or unmarshaled + * @param Frame to validate + * @returns true if frame is valid + */ + virtual bool validate( const StompFrame& frame ) const + { + if((frame.getCommand() == + CommandConstants::toString( CommandConstants::BEGIN )) && + (frame.getProperties().hasProperty( + CommandConstants::toString( + CommandConstants::HEADER_TRANSACTIONID ) ) ) ) + { + return true; + } + + return false; + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_BEGINCOMMAND_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/BytesMessageCommand.h b/activemq-cpp/src/main/activemq/connector/stomp/commands/BytesMessageCommand.h new file mode 100644 index 0000000000..32eb293435 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/BytesMessageCommand.h @@ -0,0 +1,96 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_BYTESMESSAGECOMMAND_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_BYTESMESSAGECOMMAND_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + /** + * Implements the interface for a cms::BytesMessage. Uses the template + * class StompMessage to implement all cms::Message type functionality + * and implements the BytesMessage interface here. + */ + class BytesMessageCommand : public StompMessage< cms::BytesMessage > + { + public: + + BytesMessageCommand(void) : + StompMessage< cms::BytesMessage >() { + initialize( getFrame() ); + } + BytesMessageCommand( StompFrame* frame ) : + StompMessage< cms::BytesMessage >(frame) { + validate( getFrame() ); + } + virtual ~BytesMessageCommand(void) {} + + /** + * Clonse this message exactly, returns a new instance that the + * caller is required to delete. + * @return new copy of this message + */ + virtual cms::Message* clone(void) const { + StompFrame* frame = getFrame().clone(); + + return new BytesMessageCommand( frame ); + } + + /** + * sets the bytes given to the message body. + * @param Byte Buffer to copy + * @param Number of bytes in Buffer to copy + * @throws CMSException + */ + virtual void setBodyBytes( const unsigned char* buffer, + const unsigned long numBytes ) + throw( cms::CMSException ) { + this->setBytes( + reinterpret_cast( buffer ), numBytes ); + } + + /** + * Gets the bytes that are contained in this message, user should + * copy this data into a user allocated buffer. Call + * getBodyLength to determine the number of bytes + * to expect. + * @return const pointer to a byte buffer + */ + virtual const unsigned char* getBodyBytes(void) const { + return reinterpret_cast( this->getBytes() ); + } + + /** + * Returns the number of bytes contained in the body of this message. + * @return number of bytes. + */ + virtual unsigned long getBodyLength(void) const { + return this->getNumBytes(); + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_BYTESMESSAGECOMMAND_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/CommandConstants.cpp b/activemq-cpp/src/main/activemq/connector/stomp/commands/CommandConstants.cpp new file mode 100644 index 0000000000..baa45bee5c --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/CommandConstants.cpp @@ -0,0 +1,144 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "CommandConstants.h" +#include + +#include +#include + +using namespace std; +using namespace activemq; +using namespace activemq::exceptions; +using namespace activemq::connector::stomp; +using namespace activemq::connector::stomp::commands; + +//////////////////////////////////////////////////////////////////////////////// +const char* CommandConstants::queuePrefix = "/queue/"; +const char* CommandConstants::topicPrefix = "/topic/"; + +//////////////////////////////////////////////////////////////////////////////// +string CommandConstants::StaticInitializer::stompHeaders[NUM_STOMP_HEADERS]; +string CommandConstants::StaticInitializer::commands[NUM_COMMANDS]; +string CommandConstants::StaticInitializer::ackModes[NUM_ACK_MODES]; +string CommandConstants::StaticInitializer::msgTypes[NUM_MSG_TYPES]; +map CommandConstants::StaticInitializer::stompHeaderMap; +map CommandConstants::StaticInitializer::commandMap; +map CommandConstants::StaticInitializer::ackModeMap; +map CommandConstants::StaticInitializer::msgTypeMap; +CommandConstants::StaticInitializer CommandConstants::staticInits; + +//////////////////////////////////////////////////////////////////////////////// +CommandConstants::StaticInitializer::StaticInitializer(){ + + stompHeaders[HEADER_DESTINATION] = "destination"; + stompHeaders[HEADER_TRANSACTIONID] = "transaction"; + stompHeaders[HEADER_CONTENTLENGTH] = "content-length"; + stompHeaders[HEADER_SESSIONID] = "session"; + stompHeaders[HEADER_RECEIPTID] = "receipt-id"; + stompHeaders[HEADER_RECEIPT_REQUIRED] = "receipt"; + stompHeaders[HEADER_MESSAGEID] = "message-id"; + stompHeaders[HEADER_ACK] = "ack"; + stompHeaders[HEADER_LOGIN] = "login"; + stompHeaders[HEADER_PASSWORD] = "passcode"; + stompHeaders[HEADER_CLIENT_ID] = "client-id"; + stompHeaders[HEADER_MESSAGE] = "message"; + stompHeaders[HEADER_CORRELATIONID] = "correlation-id"; + stompHeaders[HEADER_REQUESTID] = "request-id"; + stompHeaders[HEADER_RESPONSEID] = "response-id"; + stompHeaders[HEADER_EXPIRES] = "expires"; + stompHeaders[HEADER_PERSISTANT] = "persistent"; + stompHeaders[HEADER_PRIORITY] = "priority"; + stompHeaders[HEADER_REPLYTO] = "reply-to"; + stompHeaders[HEADER_TYPE] = "type"; + stompHeaders[HEADER_AMQMSGTYPE] = "amq-msg-type"; + stompHeaders[HEADER_JMSXGROUPID] = "JMSXGroupID"; + stompHeaders[HEADER_JMSXGROUPSEQNO] = "JMSXGroupSeq"; + stompHeaders[HEADER_SELECTOR] = "selector"; + stompHeaders[HEADER_DISPATCH_ASYNC] = "activemq.dispatchAsync"; + stompHeaders[HEADER_EXCLUSIVE] = "activemq.exclusive"; + stompHeaders[HEADER_MAXPENDINGMSGLIMIT] = "activemq.maximumPendingMessageLimit"; + stompHeaders[HEADER_NOLOCAL] = "activemq.noLocal"; + stompHeaders[HEADER_PREFETCHSIZE] = "activemq.prefetchSize"; + stompHeaders[HEADER_PRIORITY] = "activemq.priority"; + stompHeaders[HEADER_RETROACTIVE] = "activemq.retroactive"; + stompHeaders[HEADER_SUBSCRIPTIONNAME] = "activemq.subscriptionName"; + stompHeaders[HEADER_TIMESTAMP] = "timestamp"; + stompHeaders[HEADER_REDELIVERED] = "redelivered"; + stompHeaders[HEADER_REDELIVERYCOUNT] = "redelivery_count"; + stompHeaders[HEADER_SELECTOR] = "selector"; + stompHeaders[HEADER_ID] = "id"; + stompHeaders[HEADER_SUBSCRIPTION] = "subscription"; + commands[CONNECT] = "CONNECT"; + commands[CONNECTED] = "CONNECTED"; + commands[DISCONNECT] = "DISCONNECT"; + commands[SUBSCRIBE] = "SUBSCRIBE"; + commands[UNSUBSCRIBE] = "UNSUBSCRIBE"; + commands[MESSAGE] = "MESSAGE"; + commands[SEND] = "SEND"; + commands[BEGIN] = "BEGIN"; + commands[COMMIT] = "COMMIT"; + commands[ABORT] = "ABORT"; + commands[ACK] = "ACK"; + commands[ERROR_CMD] = "ERROR"; + commands[RECEIPT] = "RECEIPT"; + ackModes[ACK_CLIENT] = "client"; + ackModes[ACK_AUTO] = "auto"; + msgTypes[TEXT] = "text"; + msgTypes[BYTES] = "bytes"; + + for( int ix=0; ix +#include + +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class CommandConstants{ + public: + + enum CommandId{ + CONNECT, + CONNECTED, + DISCONNECT, + SUBSCRIBE, + UNSUBSCRIBE, + MESSAGE, + SEND, + BEGIN, + COMMIT, + ABORT, + ACK, + ERROR_CMD, + RECEIPT, + NUM_COMMANDS + }; + + enum StompHeader{ + HEADER_DESTINATION, + HEADER_TRANSACTIONID, + HEADER_CONTENTLENGTH, + HEADER_SESSIONID, + HEADER_RECEIPT_REQUIRED, + HEADER_RECEIPTID, + HEADER_MESSAGEID, + HEADER_ACK, + HEADER_LOGIN, + HEADER_PASSWORD, + HEADER_CLIENT_ID, + HEADER_MESSAGE, + HEADER_CORRELATIONID, + HEADER_REQUESTID, + HEADER_RESPONSEID, + HEADER_EXPIRES, + HEADER_PERSISTANT, + HEADER_REPLYTO, + HEADER_TYPE, + HEADER_AMQMSGTYPE, + HEADER_JMSXGROUPID, + HEADER_JMSXGROUPSEQNO, + HEADER_DISPATCH_ASYNC, + HEADER_EXCLUSIVE, + HEADER_MAXPENDINGMSGLIMIT, + HEADER_NOLOCAL, + HEADER_PREFETCHSIZE, + HEADER_PRIORITY, + HEADER_RETROACTIVE, + HEADER_SUBSCRIPTIONNAME, + HEADER_TIMESTAMP, + HEADER_REDELIVERED, + HEADER_REDELIVERYCOUNT, + HEADER_SELECTOR, + HEADER_ID, + HEADER_SUBSCRIPTION, + NUM_STOMP_HEADERS + }; + + enum AckMode{ + ACK_CLIENT, + ACK_AUTO, + NUM_ACK_MODES + }; + + enum MessageType + { + TEXT, + BYTES, + NUM_MSG_TYPES + }; + + static const char* queuePrefix; + static const char* topicPrefix; + + static const std::string& toString( const CommandId cmd ){ + return StaticInitializer::commands[cmd]; + } + + static CommandId toCommandId( const std::string& cmd ){ + std::map::iterator iter = + StaticInitializer::commandMap.find(cmd); + + if( iter == StaticInitializer::commandMap.end() ){ + return NUM_COMMANDS; + } + + return iter->second; + } + + static std::string toString( const StompHeader header ){ + return StaticInitializer::stompHeaders[header]; + } + + static StompHeader toStompHeader( const std::string& header ){ + + std::map::iterator iter = + StaticInitializer::stompHeaderMap.find(header); + + if( iter == StaticInitializer::stompHeaderMap.end() ){ + return NUM_STOMP_HEADERS; + } + + return iter->second; + } + + static std::string toString( const AckMode mode ){ + return StaticInitializer::ackModes[mode]; + } + + static AckMode toAckMode( const std::string& mode ){ + std::map::iterator iter = + StaticInitializer::ackModeMap.find(mode); + + if( iter == StaticInitializer::ackModeMap.end() ){ + return NUM_ACK_MODES; + } + + return iter->second; + } + + static std::string toString( const MessageType type ){ + return StaticInitializer::msgTypes[type]; + } + + static MessageType toMessageType( const std::string& type ){ + std::map::iterator iter = + StaticInitializer::msgTypeMap.find(type); + + if( iter == StaticInitializer::msgTypeMap.end() ){ + return NUM_MSG_TYPES; + } + + return iter->second; + } + + static cms::Destination* toDestination( const std::string& dest ) + throw ( exceptions::IllegalArgumentException ); + + class StaticInitializer{ + public: + StaticInitializer(); + virtual ~StaticInitializer(){} + + static std::string stompHeaders[NUM_STOMP_HEADERS]; + static std::string commands[NUM_COMMANDS]; + static std::string ackModes[NUM_ACK_MODES]; + static std::string msgTypes[NUM_MSG_TYPES]; + static std::map stompHeaderMap; + static std::map commandMap; + static std::map ackModeMap; + static std::map msgTypeMap; + }; + + private: + + static StaticInitializer staticInits; + }; + +}}}} + +#endif /*ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_COMMANDCONSTANTS_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/CommitCommand.h b/activemq-cpp/src/main/activemq/connector/stomp/commands/CommitCommand.h new file mode 100644 index 0000000000..a13329ca4c --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/CommitCommand.h @@ -0,0 +1,85 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_COMMITCOMMAND_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_COMMITCOMMAND_H_ + +#include +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + /** + * Commits a Transaction. + */ + class CommitCommand : public AbstractCommand< transport::Command > + { + public: + + CommitCommand(void) : + AbstractCommand() { + initialize( getFrame() ); + } + CommitCommand( StompFrame* frame ) : + AbstractCommand(frame) { + validate( getFrame() ); + } + virtual ~CommitCommand(void) {} + + protected: + + /** + * Inheritors are required to override this method to init the + * frame with data appropriate for the command type. + * @param Frame to init + */ + virtual void initialize( StompFrame& frame ) + { + frame.setCommand( CommandConstants::toString( + CommandConstants::COMMIT ) ); + } + + /** + * Inheritors are required to override this method to validate + * the passed stomp frame before it is marshalled or unmarshaled + * @param Frame to validate + * @returns true if frame is valid + */ + virtual bool validate( const StompFrame& frame ) const + { + if((frame.getCommand() == + CommandConstants::toString( CommandConstants::COMMIT )) && + (frame.getProperties().hasProperty( + CommandConstants::toString( + CommandConstants::HEADER_TRANSACTIONID ) ) ) ) + { + return true; + } + + return false; + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_COMMITCOMMAND_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/ConnectCommand.h b/activemq-cpp/src/main/activemq/connector/stomp/commands/ConnectCommand.h new file mode 100644 index 0000000000..a082f7e1b7 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/ConnectCommand.h @@ -0,0 +1,144 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_CONNECTCOMMAND_H_ +#define ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_CONNECTCOMMAND_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + /** + * Message sent to the broker to connect. + */ + class ConnectCommand : public AbstractCommand< transport::Command > + { + public: + + ConnectCommand(void) : + AbstractCommand() { + initialize( getFrame() ); + } + ConnectCommand( StompFrame* frame ) : + AbstractCommand(frame) { + validate( getFrame() ); + } + virtual ~ConnectCommand(void) {}; + + /** + * Get the login + * @return char* to login, can be "" + */ + virtual const char* getLogin(void) const { + return getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_LOGIN) ); + } + + /** + * Set the login + * @param password string value + */ + virtual void setLogin( const std::string& login ){ + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_LOGIN) , + login ); + } + + /** + * Get the password + * @return char* to password, can be "" + */ + virtual const char* getPassword(void) const{ + return getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_PASSWORD) ); + } + + /** + * Set the password + * @param passwrod string value + */ + virtual void setPassword( const std::string& password ){ + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_PASSWORD) , + password ); + } + + /** + * Get the Client Id + * @return char* to client Id, can be "" + */ + virtual const char* getClientId(void) const{ + return getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_CLIENT_ID) ); + } + + /** + * Set the Client Id + * @param client id string value + */ + virtual void setClientId( const std::string& clientId ){ + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_CLIENT_ID) , + clientId ); + } + + protected: + + /** + * Inheritors are required to override this method to init the + * frame with data appropriate for the command type. + * @param Frame to init + */ + virtual void initialize( StompFrame& frame ) + { + frame.setCommand( CommandConstants::toString( + CommandConstants::CONNECT ) ); + } + + /** + * Inheritors are required to override this method to validate + * the passed stomp frame before it is marshalled or unmarshaled + * @param Frame to validate + * @returns true if frame is valid + */ + virtual bool validate( const StompFrame& frame ) const + { + if(frame.getCommand() == + CommandConstants::toString( CommandConstants::CONNECT ) ) + { + return true; + } + + return false; + } + + }; + +}}}} + +#endif /*ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_CONNECTCOMMAND_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/ConnectedCommand.h b/activemq-cpp/src/main/activemq/connector/stomp/commands/ConnectedCommand.h new file mode 100644 index 0000000000..cd015b358d --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/ConnectedCommand.h @@ -0,0 +1,101 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_COMMAND_STOMP_COMMANDS_CONNECTEDCOMMAND_H_ +#define ACTIVEMQ_COMMAND_STOMP_COMMANDS_CONNECTEDCOMMAND_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + /** + * The stomp command returned from the broker indicating + * a connection has been established. + */ + class ConnectedCommand : public AbstractCommand< transport::Response > + { + public: + + ConnectedCommand(void) : + AbstractCommand< transport::Response >() { + initialize( getFrame() ); + } + ConnectedCommand( StompFrame* frame ) : + AbstractCommand< transport::Response >( frame ) { + validate( getFrame() ); + } + virtual ~ConnectedCommand(void) {} + + /** + * Get the Session Id + */ + virtual const char* getSessionId() const { + return getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_SESSIONID) ); + } + + /** + * Set the Session Id + */ + virtual void setSessionId( const std::string& session ) { + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_SESSIONID), + session ); + } + + protected: + + /** + * Inheritors are required to override this method to init the + * frame with data appropriate for the command type. + * @param Frame to init + */ + virtual void initialize( StompFrame& frame ) + { + frame.setCommand( CommandConstants::toString( + CommandConstants::CONNECTED ) ); + } + + /** + * Inheritors are required to override this method to validate + * the passed stomp frame before it is marshalled or unmarshaled + * @param Frame to validate + * @returns true if frame is valid + */ + virtual bool validate( const StompFrame& frame ) const + { + if(frame.getCommand() == + CommandConstants::toString( CommandConstants::CONNECTED ) ) + { + return true; + } + + return false; + } + + }; + +}}}} + +#endif /*ACTIVEMQ_COMMAND_STOMP_COMMANDS_CONNECTEDCOMMAND_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/DisconnectCommand.h b/activemq-cpp/src/main/activemq/connector/stomp/commands/DisconnectCommand.h new file mode 100644 index 0000000000..ac39270bc8 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/DisconnectCommand.h @@ -0,0 +1,84 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_DISCONNECTCOMMAND_H_ +#define ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_DISCONNECTCOMMAND_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + /** + * Sent to the broker to disconnect gracefully before closing + * the transport. + */ + class DisconnectCommand : public AbstractCommand< transport::Command > + { + public: + + DisconnectCommand(void) : + AbstractCommand() { + initialize( getFrame() ); + } + DisconnectCommand( StompFrame* frame ) : + AbstractCommand(frame) { + validate( getFrame() ); + } + virtual ~DisconnectCommand(void){}; + + protected: + + /** + * Inheritors are required to override this method to init the + * frame with data appropriate for the command type. + * @param Frame to init + */ + virtual void initialize( StompFrame& frame ) + { + frame.setCommand( CommandConstants::toString( + CommandConstants::DISCONNECT ) ); + } + + /** + * Inheritors are required to override this method to validate + * the passed stomp frame before it is marshalled or unmarshaled + * @param Frame to validate + * @returns true if frame is valid + */ + virtual bool validate( const StompFrame& frame ) const + { + if(frame.getCommand() == + CommandConstants::toString( CommandConstants::DISCONNECT ) ) + { + return true; + } + + return false; + } + + }; + +}}}} + +#endif /*ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_DISCONNECTCOMMAND_H_*/ + + diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/ErrorCommand.h b/activemq-cpp/src/main/activemq/connector/stomp/commands/ErrorCommand.h new file mode 100644 index 0000000000..7ce0aa3ee9 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/ErrorCommand.h @@ -0,0 +1,120 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_ERRORCOMMAND_H_ +#define ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_ERRORCOMMAND_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + /** + * Message sent from the broker when an error + * occurs. + */ + class ErrorCommand : public AbstractCommand< transport::Command > + { + public: + + ErrorCommand(void) : + AbstractCommand() { + initialize( getFrame() ); + } + ErrorCommand( StompFrame* frame ) : + AbstractCommand(frame) { + validate( getFrame() ); + } + virtual ~ErrorCommand(void) {}; + + /** + * Get the error message + */ + virtual const char* getErrorMessage(void) const { + return getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_MESSAGE) ); + } + + /** + * Set the error message + */ + virtual void setErrorMessage( const std::string& title ) { + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_MESSAGE), + title ); + } + + /** + * Set the Text associated with this Error + * @param Error Message + */ + virtual void setErrorDetails( const std::string& text ) { + setBytes( text.c_str(), text.length() + 1 ); + } + + /** + * Get the Text associated with this Error + * @return Error Message + */ + virtual const char* getErrorDetails(void) const { + return getBytes(); + } + + protected: + + /** + * Inheritors are required to override this method to init the + * frame with data appropriate for the command type. + * @param Frame to init + */ + virtual void initialize( StompFrame& frame ) + { + frame.setCommand( CommandConstants::toString( + CommandConstants::ERROR_CMD ) ); + } + + /** + * Inheritors are required to override this method to validate + * the passed stomp frame before it is marshalled or unmarshaled + * @param Frame to validate + * @returns true if frame is valid + */ + virtual bool validate( const StompFrame& frame ) const + { + if((frame.getCommand() == + CommandConstants::toString( CommandConstants::ERROR_CMD ) ) && + (frame.getProperties().hasProperty( + CommandConstants::toString( + CommandConstants::HEADER_MESSAGE ) ) ) ) + { + return true; + } + + return false; + } + + }; + +}}}} + +#endif /*ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_ERRORCOMMAND_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/MessageCommand.h b/activemq-cpp/src/main/activemq/connector/stomp/commands/MessageCommand.h new file mode 100644 index 0000000000..5265b21d01 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/MessageCommand.h @@ -0,0 +1,63 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_MESSAGECOMMAND_H_ +#define ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_MESSAGECOMMAND_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + /** + * Message command which represents a ActiveMQMessage with no body + * can be sent or recieved. + */ + class MessageCommand : public StompMessage< cms::Message > + { + public: + + MessageCommand(void) : + StompMessage< cms::Message >() { + initialize( getFrame() ); + } + MessageCommand( StompFrame* frame ) : + StompMessage< cms::Message >( frame ) { + validate( getFrame() ); + } + virtual ~MessageCommand(void) {} + + /** + * Clonse this message exactly, returns a new instance that the + * caller is required to delete. + * @return new copy of this message + */ + virtual cms::Message* clone(void) const { + StompFrame* frame = getFrame().clone(); + + return new MessageCommand( frame ); + } + + }; + +}}}} + +#endif /*ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_MESSAGECOMMAND_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/ReceiptCommand.h b/activemq-cpp/src/main/activemq/connector/stomp/commands/ReceiptCommand.h new file mode 100644 index 0000000000..e2faa298dc --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/ReceiptCommand.h @@ -0,0 +1,104 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_RECEIPTCOMMAND_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_RECEIPTCOMMAND_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + /** + * Message sent from the Broker when a receipt is requested + * for messages that are sent. + */ + class ReceiptCommand : public AbstractCommand< transport::Response > + { + public: + + ReceiptCommand(void) : + AbstractCommand() { + initialize( getFrame() ); + } + ReceiptCommand( StompFrame* frame ) : + AbstractCommand(frame) { + validate( getFrame() ); + } + virtual ~ReceiptCommand(void) {} + + /** + * Get the receipt id + */ + virtual const char* getReceiptId(void) const{ + return getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_RECEIPTID) ); + } + + /** + * Set the receipt id + */ + virtual void setReceiptId( const std::string& id ){ + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_RECEIPTID), + id ); + } + + protected: + + /** + * Inheritors are required to override this method to init the + * frame with data appropriate for the command type. + * @param Frame to init + */ + virtual void initialize( StompFrame& frame ) + { + frame.setCommand( CommandConstants::toString( + CommandConstants::RECEIPT ) ); + } + + /** + * Inheritors are required to override this method to validate + * the passed stomp frame before it is marshalled or unmarshaled + * @param Frame to validate + * @returns true if frame is valid + */ + virtual bool validate( const StompFrame& frame ) const + { + if((frame.getCommand() == + CommandConstants::toString( CommandConstants::RECEIPT )) && + (frame.getProperties().hasProperty( + CommandConstants::toString( + CommandConstants::HEADER_RECEIPTID ) ) ) ) + { + return true; + } + + return false; + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_RECEIPTCOMMAND_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/StompCommand.h b/activemq-cpp/src/main/activemq/connector/stomp/commands/StompCommand.h new file mode 100644 index 0000000000..1580d123a9 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/StompCommand.h @@ -0,0 +1,111 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_STOMPCOMMAND_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_STOMPCOMMAND_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class StompCommand : public marshal::Marshalable + { + protected: + + /** + * Inheritors are required to override this method to init the + * frame with data appropriate for the command type. + * @param Frame to init + */ + virtual void initialize( StompFrame& frame ) = 0; + + /** + * Inheritors are required to override this method to validate + * the passed stomp frame before it is marshalled or unmarshaled + * @param Frame to validate + * @returns true if frame is valid + */ + virtual bool validate( const StompFrame& frame ) const = 0; + + public: + + virtual ~StompCommand(void) {} + + /** + * Sets the Command Id of this Message + * @param Command Id + */ + virtual void setCommandId( const unsigned int id ) = 0; + + /** + * Gets the Command Id of this Message + * @return Command Id + */ + virtual unsigned int getCommandId(void) const = 0; + + /** + * Set if this Message requires a Response + * @param true if response is required + */ + virtual void setResponseRequired( const bool required ) = 0; + + /** + * Is a Response required for this Command + * @return true if a response is required. + */ + virtual bool isResponseRequired(void) const = 0; + + /** + * Gets the Correlation Id that is associated with this message + * @return the Correlation Id + */ + virtual unsigned int getCorrelationId(void) const = 0; + + /** + * Sets the Correlation Id if this Command + * @param Id + */ + virtual void setCorrelationId( const unsigned int corrId ) = 0; + + /** + * Get the Transaction Id of this Command + * @return the Id of the Transaction + */ + virtual const char* getTransactionId(void) const = 0; + + /** + * Set the Transaction Id of this Command + * @param the Id of the Transaction + */ + virtual void setTransactionId( const std::string& id ) = 0; + + /** + * Retrieve the Stomp Command Id for this message. + * @return Stomp CommandId enum + */ + virtual CommandConstants::CommandId getStompCommandId(void) const = 0; + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_STOMPCOMMAND_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/StompMessage.h b/activemq-cpp/src/main/activemq/connector/stomp/commands/StompMessage.h new file mode 100644 index 0000000000..6d95e05767 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/StompMessage.h @@ -0,0 +1,400 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_STOMPMESSAGE_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_STOMPMESSAGE_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + /** + * Base class for Stomp Commands that represent the Active MQ message + * types. This class is templated and expects the Template type to be + * a cms::Message type, Message, TextMessage etc. This class will + * implement all the general cms:Message methods + * + * This class implement AbsractCommand and the + * ActiveMQMessage interface. + */ + template + class StompMessage : + public AbstractCommand< transport::Command >, + public T, + public core::ActiveMQMessage + { + private: + + // Core API defined Acknowedge Handler. + core::ActiveMQAckHandler* ackHandler; + + // Cached Destination + cms::Destination* dest; + + public: + + StompMessage(void) : + AbstractCommand< transport::Command >(), + ackHandler( NULL ) { dest = new StompTopic(); } + StompMessage( StompFrame* frame ) : + AbstractCommand< transport::Command >( frame ), + ackHandler( NULL ) + { + dest = CommandConstants::toDestination( + getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_DESTINATION ), "" ) ); + } + + virtual ~StompMessage(void) { delete dest; } + + /** + * Gets the properties map for this command. + * @return Reference to a Properties object + */ + virtual util::Properties& getProperties(void){ + return getFrame().getProperties(); + } + virtual const util::Properties& getProperties(void) const{ + return getFrame().getProperties(); + } + + /** + * Get the Correlation Id for this message + * @return string representation of the correlation Id + */ + virtual const char* getCMSCorrelationId(void) const { + return getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_CORRELATIONID ) ); + } + + /** + * Sets the Correlation Id used by this message + * @param String representing the correlation id. + */ + virtual void setCMSCorrelationId(const std::string& correlationId) { + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_CORRELATIONID ) , + correlationId ); + } + + /** + * Acknowledges all consumed messages of the session + * of this consumed message. + */ + virtual void acknowledge(void) const throw( cms::CMSException ) { + if(ackHandler != NULL) ackHandler->acknowledgeMessage(this); + } + + /** + * Sets the DeliveryMode for this message + * @return DeliveryMode enumerated value. + */ + virtual cms::Message::DeliveryMode getCMSDeliveryMode(void) const { + if(!getFrame().getProperties().hasProperty( + CommandConstants::toString( + CommandConstants::HEADER_PERSISTANT ) ) ) { + return cms::Message::PERSISTANT; + } + + return (cms::Message::DeliveryMode)( + util::Integer::parseInt( getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_PERSISTANT ) ) ) ); + } + + /** + * Sets the DeliveryMode for this message + * @param DeliveryMode enumerated value. + */ + virtual void setCMSDeliveryMode(cms::Message::DeliveryMode mode) { + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_PERSISTANT ) , + util::Integer::toString((int)mode) ); + } + + /** + * Gets the Destination for this Message + * @return Destination object + */ + virtual const cms::Destination& getCMSDestination(void) const{ + return *dest; + } + + /** + * Sets the Destination for this message + * @param Destination Object + */ + virtual void setCMSDestination(const cms::Destination& destination) { + dest->copy( destination ); + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_DESTINATION ), + dest->toProviderString() ); + } + + /** + * Gets the Expiration Time for this Message + * @return time value + */ + virtual long getCMSExpiration(void) const { + return util::Long::parseLong( getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_EXPIRES ), "0" ) ); + } + + /** + * Sets the Expiration Time for this message + * @param time value + */ + virtual void setCMSExpiration(long expireTime) { + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_EXPIRES) , + util::Long::toString( expireTime ) ); + } + + /** + * Gets the CMS Message Id for this Message + * @return time value + */ + virtual const char* getCMSMessageId(void) const { + return getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_MESSAGEID ) ); + } + + /** + * Sets the CMS Message Id for this message + * @param time value + */ + virtual void setCMSMessageId(const std::string& id) { + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_MESSAGEID ), + id ); + } + + /** + * Gets the Priority Value for this Message + * @return priority value + */ + virtual int getCMSPriority(void) const { + return util::Integer::parseInt( getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_PRIORITY ), "0" ) ); + } + + /** + * Sets the Priority Value for this message + * @param priority value + */ + virtual void setCMSPriority(int priority) { + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_PRIORITY), + util::Integer::toString( priority ) ); + } + + /** + * Gets the Redelivered Flag for this Message + * @return redelivered value + */ + virtual bool getCMSRedelivered(void) const { + return util::Boolean::parseBoolean( getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_REDELIVERED ), + "false" ) ); + } + + /** + * Sets the Redelivered Flag for this message + * @param redelivered value + */ + virtual void setCMSRedelivered(bool redelivered) { + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_REDELIVERED ), + util::Boolean::toString( redelivered ) ); + } + + /** + * Gets the CMS Reply To Address for this Message + * @return Reply To Value + */ + virtual const char* getCMSReplyTo(void) const { + return getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_REPLYTO ) ); + } + + /** + * Sets the CMS Reply To Address for this message + * @param Reply To value + */ + virtual void setCMSReplyTo(const std::string& id) { + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_REPLYTO ), + id ); + } + + /** + * Gets the Time Stamp for this Message + * @return time stamp value + */ + virtual long getCMSTimeStamp(void) const { + return util::Long::parseLong( getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_TIMESTAMP ), "0" ) ); + } + + /** + * Sets the Time Stamp for this message + * @param time stamp value + */ + virtual void setCMSTimeStamp(long timeStamp) { + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_TIMESTAMP ), + util::Long::toString( timeStamp ) ); + } + + /** + * Gets the CMS Message Type for this Message + * @return type value + */ + virtual const char* getCMSMessageType(void) const { + return getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_TYPE ) ); + } + + /** + * Sets the CMS Message Type for this message + * @param type value + */ + virtual void setCMSMessageType(const std::string& type) { + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_TYPE ), + type ); + } + + public: // ActiveMQMessage + + /** + * Sets the Acknowledgement Handler that this Message will use + * when the Acknowledge method is called. + * @param ActiveMQAckHandler + */ + virtual void setAckHandler(core::ActiveMQAckHandler* handler) { + this->ackHandler = handler; + } + + /** + * Gets the number of times this message has been redelivered. + * @return redelivery count + */ + virtual int getRedeliveryCount(void) const { + return util::Integer::parseInt( getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_REDELIVERYCOUNT ), + "0" ) ); + } + + /** + * Sets the count of the number of times this message has been + * redelivered + * @param redelivery count + */ + virtual void setRedeliveryCount(int count) { + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_REDELIVERYCOUNT ), + util::Integer::toString( count ) ); + } + + protected: + + /** + * Inheritors are required to override this method to init the + * frame with data appropriate for the command type. + * @param Frame to init + */ + virtual void initialize( StompFrame& frame ) + { + frame.setCommand( CommandConstants::toString( + CommandConstants::SEND ) ); + } + + /** + * Inheritors are required to override this method to validate + * the passed stomp frame before it is marshalled or unmarshaled + * @param Frame to validate + * @returns true if frame is valid + */ + virtual bool validate( const StompFrame& frame ) const + { + if(frame.getCommand() == + CommandConstants::toString( CommandConstants::SEND ) ) + { + if(frame.getProperties().hasProperty( + CommandConstants::toString( + CommandConstants::HEADER_DESTINATION ) ) ) + { + return true; + } + } + else if( frame.getCommand() == + CommandConstants::toString( CommandConstants::MESSAGE ) ) + { + if(frame.getProperties().hasProperty( + CommandConstants::toString( + CommandConstants::HEADER_DESTINATION ) ) && + frame.getProperties().hasProperty( + CommandConstants::toString( + CommandConstants::HEADER_MESSAGEID ) ) ) + { + return true; + } + } + + return false; + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_STOMPMESSAGE_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/SubscribeCommand.h b/activemq-cpp/src/main/activemq/connector/stomp/commands/SubscribeCommand.h new file mode 100644 index 0000000000..09301af9c0 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/SubscribeCommand.h @@ -0,0 +1,205 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_SUBSCRIBECOMMAND_H_ +#define ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_SUBSCRIBECOMMAND_H_ + +#include +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + /** + * Command sent to the broker to subscribe to a topic + * or queue. + */ + class SubscribeCommand : public AbstractCommand< transport::Command > + { + public: + + SubscribeCommand(void) : + AbstractCommand() { + initialize( getFrame() ); + } + SubscribeCommand( StompFrame* frame ) : + AbstractCommand< transport::Command >( frame ) { + validate( getFrame() ); + } + virtual ~SubscribeCommand(void) {} + + /** + * Get the destination + * @returns the destination Name String + */ + virtual const char* getDestination(void) const{ + return getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_DESTINATION) ); + } + + /** + * Set the destination + * @param the destination Name String + */ + virtual void setDestination( const std::string& dest ){ + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_DESTINATION), + dest ); + } + + /** + * Set the Ack Mode of this Subscription + * @param mode setting. + */ + virtual void setAckMode( const CommandConstants::AckMode mode ){ + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_ACK), + CommandConstants::toString( mode ) ); + } + + /** + * Get the Ack Mode of this Subscription + * @return mode setting. + */ + virtual CommandConstants::AckMode getAckMode(void) const{ + return CommandConstants::toAckMode( + getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_ACK) ) ); + } + + /** + * Sets the Message Selector that is associated with this + * subscribe request + * @param selector string + */ + virtual void setMessageSelector( const std::string& selector ) { + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_SELECTOR), + selector ); + } + + /** + * Gets the Message Selector that is associated with this + * subscribe request + * @returns the selector string + */ + virtual const char* getMessageSelector(void) const{ + return getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_SELECTOR) ); + } + + /** + * Sets the Subscription Name that is associated with this + * subscribe request + * @param Subscription Name + */ + virtual void setSubscriptionName( const std::string& name ) { + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_SUBSCRIPTIONNAME), + name ); + } + + /** + * Gets the Subscription Name that is associated with this + * subscribe request + * @returns the Subscription Name + */ + virtual const char* getSubscriptionName(void) const{ + return getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_SUBSCRIPTIONNAME) ); + } + + /** + * Gets hether or not locally sent messages should be ignored for + * subscriptions. Set to true to filter out locally sent messages + * @return NoLocal value + */ + virtual bool getNoLocal(void) const { + return util::Boolean::parseBoolean( getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_NOLOCAL ), + "false" ) ); + } + + /** + * Gets hether or not locally sent messages should be ignored for + * subscriptions. Set to true to filter out locally sent messages + * @param NoLocal value + */ + virtual void setNoLocal( bool noLocal ) { + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_NOLOCAL ), + util::Boolean::toString( noLocal ) ); + } + + protected: + + /** + * Inheritors are required to override this method to init the + * frame with data appropriate for the command type. + * @param Frame to init + */ + virtual void initialize( StompFrame& frame ) + { + frame.setCommand( CommandConstants::toString( + CommandConstants::SUBSCRIBE ) ); + + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_ACK), + CommandConstants::toString( + CommandConstants::ACK_AUTO ) ); + } + + /** + * Inheritors are required to override this method to validate + * the passed stomp frame before it is marshalled or unmarshaled + * @param Frame to validate + * @returns true if frame is valid + */ + virtual bool validate( const StompFrame& frame ) const + { + if((frame.getCommand() == + CommandConstants::toString( CommandConstants::SUBSCRIBE )) && + (frame.getProperties().hasProperty( + CommandConstants::toString( + CommandConstants::HEADER_DESTINATION ) ) ) ) + { + return true; + } + + return false; + } + + }; + +}}}} + +#endif /*ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_SUBSCRIBECOMMAND_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/TextMessageCommand.h b/activemq-cpp/src/main/activemq/connector/stomp/commands/TextMessageCommand.h new file mode 100644 index 0000000000..320285deb8 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/TextMessageCommand.h @@ -0,0 +1,75 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_TEXTMESSAGECOMMAND_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_TEXTMESSAGECOMMAND_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class TextMessageCommand : public StompMessage< cms::TextMessage > + { + public: + + TextMessageCommand(void) : + StompMessage< cms::TextMessage >() { + initialize( getFrame() ); + } + TextMessageCommand( StompFrame* frame ) : + StompMessage< cms::TextMessage >( frame ) { + validate( getFrame() ); + } + virtual ~TextMessageCommand(void) {} + + /** + * Clonse this message exactly, returns a new instance that the + * caller is required to delete. + * @return new copy of this message + */ + virtual cms::Message* clone(void) const { + StompFrame* frame = getFrame().clone(); + + return new TextMessageCommand( frame ); + } + + /** + * Gets the message character buffer. + * @return The message character buffer. + */ + virtual const char* getText(void) const throw( cms::CMSException ) { + return getBytes(); + } + + /** + * Sets the message contents. + * @param msg The message buffer. + */ + virtual void setText( const char* msg ) throw( cms::CMSException ) { + setBytes( msg, strlen(msg) + 1, false ); + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_TEXTMESSAGECOMMAND_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/commands/UnsubscribeCommand.h b/activemq-cpp/src/main/activemq/connector/stomp/commands/UnsubscribeCommand.h new file mode 100644 index 0000000000..52402cb7a8 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/commands/UnsubscribeCommand.h @@ -0,0 +1,104 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_UNSUBSCRIBECOMMAND_H_ +#define ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_UNSUBSCRIBECOMMAND_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + /** + * Command sent to the broker to unsubscribe to a + * topic or queue. + */ + class UnsubscribeCommand : public AbstractCommand< transport::Command > + { + public: + + UnsubscribeCommand(void) : + AbstractCommand< transport::Command >() { + initialize( getFrame() ); + } + UnsubscribeCommand( StompFrame* frame ) : + AbstractCommand< transport::Command >( frame ) { + validate( getFrame() ); + } + virtual ~UnsubscribeCommand(void) {}; + + /** + * Get the destination + */ + virtual const char* getDestination(void) const{ + return getPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_DESTINATION) ); + } + + /** + * Set the destination + */ + virtual void setDestination( const std::string& dest ){ + setPropertyValue( + CommandConstants::toString( + CommandConstants::HEADER_DESTINATION) , + dest ); + } + + protected: + + /** + * Inheritors are required to override this method to init the + * frame with data appropriate for the command type. + * @param Frame to init + */ + virtual void initialize( StompFrame& frame ) + { + frame.setCommand( CommandConstants::toString( + CommandConstants::UNSUBSCRIBE ) ); + } + + /** + * Inheritors are required to override this method to validate + * the passed stomp frame before it is marshalled or unmarshaled + * @param Frame to validate + * @returns true if frame is valid + */ + virtual bool validate( const StompFrame& frame ) const + { + if((frame.getCommand() == + CommandConstants::toString( CommandConstants::UNSUBSCRIBE )) && + (frame.getProperties().hasProperty( + CommandConstants::toString( + CommandConstants::HEADER_DESTINATION ) ) ) ) + { + return true; + } + + return false; + } + + }; + +}}}} + +#endif /*ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_UNSUBSCRIBECOMMAND_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/marshal/MarshalException.h b/activemq-cpp/src/main/activemq/connector/stomp/marshal/MarshalException.h new file mode 100644 index 0000000000..605cfc18ed --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/marshal/MarshalException.h @@ -0,0 +1,66 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONNECTOR_STOMP_MARSHALL_MARSHALEXCEPTION_H_ +#define ACTIVEMQ_CONNECTOR_STOMP_MARSHALL_MARSHALEXCEPTION_H_ + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace marshal{ + + /* + * Signals that an problem occurred during marshalling. + */ + class MarshalException : public exceptions::ActiveMQException + { + public: + + MarshalException() {} + MarshalException( const exceptions::ActiveMQException& ex ){ + *(ActiveMQException*)this = ex; + } + MarshalException( const MarshalException& ex ){ + *(exceptions::ActiveMQException*)this = ex; + } + MarshalException(const char* file, const int lineNumber, + const char* msg, ...) + { + va_list vargs ; + va_start(vargs, msg) ; + buildMessage(msg, vargs) ; + + // Set the first mark for this exception. + setMark( file, lineNumber ); + } + + /** + * Clones this exception. This is useful for cases where you need + * to preserve the type of the original exception as well as the message. + * All subclasses should override. + */ + virtual exceptions::ActiveMQException* clone() const{ + return new MarshalException( *this ); + } + virtual ~MarshalException() {} + + }; + +}}}} + +#endif /*ACTIVEMQ_CONNECTOR_STOMP_MARSHAL_MARSHALLEXCEPTION_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/marshal/Marshalable.h b/activemq-cpp/src/main/activemq/connector/stomp/marshal/Marshalable.h new file mode 100644 index 0000000000..317a61cc64 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/marshal/Marshalable.h @@ -0,0 +1,49 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CONNECTOR_STOMP_MARSHAL_MARSHALABLE_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_MARSHAL_MARSHALABLE_H_ + +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace marshal{ + + class Marshalable + { + public: + + virtual ~Marshalable(void) {} + + /** + * Marshals the command to a stomp frame. + * @returns the stomp frame representation of this + * command. + * @throws MarshalException if the command is not + * in a state that can be marshalled. + */ + virtual const StompFrame& marshal(void) const + throw ( marshal::MarshalException ) = 0; + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_MARSHAL_MARSHALABLE_H_*/ diff --git a/activemq-cpp/src/main/activemq/connector/stomp/marshal/Marshaler.cpp b/activemq-cpp/src/main/activemq/connector/stomp/marshal/Marshaler.cpp new file mode 100644 index 0000000000..af6b30a390 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/marshal/Marshaler.cpp @@ -0,0 +1,115 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 + +#include +#include +#include +#include +#include + +// Commands we can receive +#include +#include +#include + +// Message Commands we can receive +#include +#include +#include + +using namespace activemq; +using namespace activemq::exceptions; +using namespace activemq::transport; +using namespace activemq::connector::stomp; +using namespace activemq::connector::stomp::commands; +using namespace activemq::connector::stomp::marshal; + +//////////////////////////////////////////////////////////////////////////////// +transport::Command* Marshaler::marshal( StompFrame* frame ) + throw ( MarshalException ) +{ + try + { + CommandConstants::CommandId commandId = + CommandConstants::toCommandId(frame->getCommand().c_str()); + transport::Command* command = NULL; + + if(commandId == CommandConstants::CONNECTED){ + command = new ConnectedCommand( frame ); + } + else if(commandId == CommandConstants::ERROR_CMD){ + command = new ErrorCommand( frame ); + } + else if(commandId == CommandConstants::RECEIPT){ + command = new ReceiptCommand( frame ); + } + else if(commandId == CommandConstants::MESSAGE){ + if( !frame->getProperties().hasProperty( + CommandConstants::toString( + CommandConstants::HEADER_CONTENTLENGTH ) ) ) + { + command = new TextMessageCommand( frame ); + } + else + { + command = new BytesMessageCommand( frame ); + } + } + + // We either got a command or a response, but if we got neither + // then complain, something went wrong. + if(command == NULL) + { + throw MarshalException( + __FILE__, __LINE__, + "Marshaler::marshal - No Command Created from frame"); + } + + return command; + } + AMQ_CATCH_RETHROW( MarshalException ) + AMQ_CATCH_EXCEPTION_CONVERT( ActiveMQException, MarshalException ) + AMQ_CATCHALL_THROW( MarshalException ) +} + +//////////////////////////////////////////////////////////////////////////////// +const StompFrame& Marshaler::marshal( const transport::Command* command ) + throw ( MarshalException ) +{ + try + { + const Marshalable* marshalable = + dynamic_cast(command); + + // Easy, just get the frame from the command + if(marshalable != NULL) + { + return marshalable->marshal(); + } + else + { + throw MarshalException( + __FILE__, __LINE__, + "Marshaler::marshal - Invalid Command Type!"); + } + } + AMQ_CATCH_RETHROW( MarshalException ) + AMQ_CATCH_EXCEPTION_CONVERT( ActiveMQException, MarshalException ) + AMQ_CATCHALL_THROW( MarshalException ) +} diff --git a/activemq-cpp/src/main/activemq/connector/stomp/marshal/Marshaler.h b/activemq-cpp/src/main/activemq/connector/stomp/marshal/Marshaler.h new file mode 100644 index 0000000000..2328cff068 --- /dev/null +++ b/activemq-cpp/src/main/activemq/connector/stomp/marshal/Marshaler.h @@ -0,0 +1,67 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_CONNECTOR_STOMP_MARSHALER_H_ +#define ACTIVEMQ_CONNECTOR_STOMP_MARSHALER_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace marshal{ + + /** + * Interface for all marshallers between Commands and + * stomp frames. + */ + class Marshaler + { + public: + + Marshaler(void) {} + virtual ~Marshaler(void) {} + + /** + * Marshall a Stomp Frame to a Stomp Command, the frame is now + * owned by this Command, caller should not use the frame again. + * @param Frame to Marshall + * @return Newly Marshaled Stomp Message + * @throws MarshalException + */ + virtual transport::Command* marshal( StompFrame* frame ) + throw ( MarshalException ); + + /** + * Marshal a Stomp Command to a Stom Frame, if the command that + * is past is not castable to a Stomp Command an Exception will + * be thrown + * @param The Stomp Command to Marshal + * @return newly Marshaled Stomp Frame + * @throws MarshalException + */ + virtual const StompFrame& marshal( + const transport::Command* command ) + throw ( MarshalException ); + + }; + +}}}} + +#endif /*ACTIVEMQ_CONNECTOR_STOMP_MARSHALER_H_*/ diff --git a/activemq-cpp/src/main/activemq/core/ActiveMQAckHandler.h b/activemq-cpp/src/main/activemq/core/ActiveMQAckHandler.h new file mode 100644 index 0000000000..58c3b385e3 --- /dev/null +++ b/activemq-cpp/src/main/activemq/core/ActiveMQAckHandler.h @@ -0,0 +1,49 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CORE_ACTIVEMQACKHANDLER_H_ +#define _ACTIVEMQ_CORE_ACTIVEMQACKHANDLER_H_ + +#include + +namespace activemq{ +namespace core{ + + class ActiveMQMessage; + + /** + * Interface class that is used to give CMS Messages an interface to + * Ack themselves with. + */ + class ActiveMQAckHandler + { + public: + + virtual ~ActiveMQAckHandler(void) {}; + + /** + * Method called to acknowledge the message passed + * @param Message to Acknowlegde + * @throw CMSException + */ + virtual void acknowledgeMessage( const ActiveMQMessage* message ) + throw ( cms::CMSException ) = 0; + + }; + +}} + +#endif /*_ACTIVEMQ_CORE_ACTIVEMQACKHANDLER_H_*/ diff --git a/activemq-cpp/src/main/activemq/core/ActiveMQConnection.cpp b/activemq-cpp/src/main/activemq/core/ActiveMQConnection.cpp new file mode 100644 index 0000000000..ffe5240a67 --- /dev/null +++ b/activemq-cpp/src/main/activemq/core/ActiveMQConnection.cpp @@ -0,0 +1,201 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "ActiveMQConnection.h" + +#include +#include +#include +#include + +using namespace cms; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::connector; +using namespace activemq::exceptions; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +ActiveMQConnection::ActiveMQConnection(ActiveMQConnectionData* connectionData) +{ + this->connectionData = connectionData; + this->started = false; + this->exceptionListener = NULL; + + // We want to be the sink for all messages from the Connector + connectionData->getConnector()->setConsumerMessageListener( this ); +} + +//////////////////////////////////////////////////////////////////////////////// +ActiveMQConnection::~ActiveMQConnection(void) +{ + try + { + close(); + } + AMQ_CATCH_NOTHROW( ActiveMQException ) + AMQ_CATCHALL_NOTHROW( ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::Session* ActiveMQConnection::createSession(void) + throw ( cms::CMSException ) +{ + try + { + return this->createSession( Session::AutoAcknowledge ); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::Session* ActiveMQConnection::createSession( + cms::Session::AcknowledgeMode ackMode ) + throw ( cms::CMSException ) +{ + try + { + return new ActiveMQSession( + connectionData->getConnector()->createSession( ackMode ), + connectionData->getProperties(), + this ); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +std::string ActiveMQConnection::getClientId(void) const +{ + return connectionData->getConnector()->getClientId(); +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQConnection::close(void) throw ( cms::CMSException ) +{ + try + { + // Once current deliveries are done this stops the delivery + // of any new messages. + started = false; + + // Destroy the connection data + delete connectionData; + connectionData = NULL; + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQConnection::start(void) throw ( cms::CMSException ) +{ + // This starts or restarts the delivery of all incomming messages + // messages delivered while this connection is stopped are dropped + // and not acknowledged. + started = true; +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQConnection::stop(void) throw ( cms::CMSException ) +{ + // Once current deliveries are done this stops the delivery of any + // new messages. + started = false; +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQConnection::addMessageListener( const unsigned int consumerId, + ActiveMQMessageListener* listener ) +{ + // Place in Map + synchronized(&mutex) + { + consumers[consumerId] = listener; + } +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQConnection::removeMessageListener( const unsigned int consumerId ) +{ + // Remove from Map + synchronized(&mutex) + { + consumers.erase( consumerId ); + } +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQConnection::onConsumerMessage( connector::ConsumerInfo* consumer, + core::ActiveMQMessage* message ) +{ + try + { + if( connectionData == NULL) + { + NullPointerException ex( + __FILE__, __LINE__, + "ActiveMQConnection::onConsumerMessage - " + "Connection Data Null, could be closed." ); + + fire( ex ); + + return; + } + + // When not started we drop incomming messages + if( !started ) + { + // Indicate to Broker that we received the message, but it + // was not consumed. + connectionData->getConnector()->acknowledge( + consumer->getSessionInfo(), + (Message*)message, + Connector::DeliveredAck ); + + // Delete the message here + delete message; + + return; + } + + // Started, so lock map and dispatch the message. + synchronized(&mutex) + { + if(consumers.find(consumer->getConsumerId()) != consumers.end()) + { + consumers[consumer->getConsumerId()]-> + onActiveMQMessage( message ); + } + } + } + catch( exceptions::ActiveMQException& ex ) + { + ex.setMark( __FILE__, __LINE__ ); + fire( ex ); + } + catch( ... ) + { + exceptions::ActiveMQException ex( + __FILE__, __LINE__, + "IOTransport::run - caught unknown exception" ); + fire( ex ); + } + +} + diff --git a/activemq-cpp/src/main/activemq/core/ActiveMQConnection.h b/activemq-cpp/src/main/activemq/core/ActiveMQConnection.h new file mode 100644 index 0000000000..48449f7819 --- /dev/null +++ b/activemq-cpp/src/main/activemq/core/ActiveMQConnection.h @@ -0,0 +1,179 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CORE_ACTIVEMQCONNECTION_H_ +#define _ACTIVEMQ_CORE_ACTIVEMQCONNECTION_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace activemq{ +namespace core{ + + class cms::Session; + class ActiveMQConsumer; + + class ActiveMQConnection : + public cms::Connection, + public connector::ConsumerMessageListener + { + private: + + // the registered exception listener + cms::ExceptionListener* exceptionListener; + + // All the data that is used to connect this Connection + ActiveMQConnectionData* connectionData; + + // Indicates if this Connection is started + bool started; + + // Map of Consumer Ids to ActiveMQMessageListeners + std::map consumers; + + // Mutex to lock the Consumers Map + concurrent::Mutex mutex; + + public: + + /** + * Constructor + */ + ActiveMQConnection(ActiveMQConnectionData* connectionData); + + /** + * Destructor + */ + virtual ~ActiveMQConnection(void); + + public: // Connection Interface Methods + + /** + * Creates a new Session to work for this Connection + */ + virtual cms::Session* createSession(void) throw ( cms::CMSException ); + + /** + * Creates a new Session to work for this Connection using the + * specified acknowledgment mode + * @param the Acknowledgement Mode to use. + */ + virtual cms::Session* createSession(cms::Session::AcknowledgeMode ackMode) + throw ( cms::CMSException ); + + /** + * Get the Client Id for this session + * @return string version of Client Id + */ + virtual std::string getClientId(void) const; + + /** + * Retrieves the Connection Data object for this object. + * @return pointer to a connection data object. + */ + virtual ActiveMQConnectionData* getConnectionData(void){ + return connectionData; + } + + /** + * Gets the registered Exception Listener for this connection + * @return pointer to an exception listnener or NULL + */ + virtual cms::ExceptionListener* getExceptionListener(void) const{ + return exceptionListener; }; + + /** + * Sets the registed Exception Listener for this connection + * @param pointer to and ExceptionListener + */ + virtual void setExceptionListener(cms::ExceptionListener* listener){ + exceptionListener = listener; }; + + /** + * Close the currently open connection + * @throws CMSException + */ + virtual void close(void) throw ( cms::CMSException ); + + /** + * Starts or (restarts) a connections delivery of incoming messages + * @throws CMSException + */ + virtual void start(void) throw ( cms::CMSException ); + + /** + * Stop the flow of incoming messages + * @throws CMSException + */ + virtual void stop(void) throw ( cms::CMSException ); + + public: // ActiveMQConnection Methods + + /** + * Adds the ActiveMQMessageListener to the Mapping of Consumer Id's + * to listeners, all message to that id will be routed to the given + * listener + * @param Consumer Id String + * @param ActiveMQMessageListener Pointer + */ + virtual void addMessageListener(const unsigned int consumerId, + ActiveMQMessageListener* listener); + + /** + * Remove the Listener for the specified Consumer Id + * @param Consumer Id string + */ + virtual void removeMessageListener(const unsigned int consumerId); + + private: + + /** + * Notify the excpetion listener + */ + void fire( exceptions::ActiveMQException& ex ) + { + if( exceptionListener != NULL ) + { + try + { + exceptionListener->onException( ex ); + } + catch(...){} + } + } + + /** + * Called to dispatch a message to a particular consumer. + * @param consumer the target consumer of the dispatch. + * @param msg the message to be dispatched. + */ + virtual void onConsumerMessage( connector::ConsumerInfo* consumer, + core::ActiveMQMessage* message ); + + }; + +}} + +#endif /*ACTIVEMQCONNECTION_H_*/ diff --git a/activemq-cpp/src/main/activemq/core/ActiveMQConnectionData.h b/activemq-cpp/src/main/activemq/core/ActiveMQConnectionData.h new file mode 100644 index 0000000000..cffbfa84b9 --- /dev/null +++ b/activemq-cpp/src/main/activemq/core/ActiveMQConnectionData.h @@ -0,0 +1,120 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CORE_ACTIVEMQCONNECTIONDATA_H_ +#define _ACTIVEMQ_CORE_ACTIVEMQCONNECTIONDATA_H_ + +#include +#include +#include +#include + +namespace activemq{ +namespace core{ + + /** + * Container of the Data that is needed when creating a new connection + * object. Each ActiveMQConnection owns one of these objects. This + * object knows how to clean up the Connection Dependencies correctly + */ + class ActiveMQConnectionData + { + private: + + // Connector Object + connector::Connector* connector; + + // Transport we are using + transport::Transport* transport; + + // Properties used to configure this connection. + util::Properties* properties; + + public: + + /** + * Creates a new Connection Data object, passing it the data that + * it will manage for the parent connection object. + * @param A connector instance + * @param A Socket instance + * @param A Transport instance + * @throw IllegalArgumentException if an element is NULL + */ + ActiveMQConnectionData( connector::Connector* connector, + transport::Transport* transport, + util::Properties* properties ) + { + if( connector == NULL || + transport == NULL || + properties == NULL ) + { + throw exceptions::IllegalArgumentException( + __FILE__, __LINE__, + "ActiveMQConnectionData::ActiveMQConnectionData - " + "Required Parameter was NULL."); + } + + this->connector = connector; + this->transport = transport; + this->properties = properties; + } + + virtual ~ActiveMQConnectionData(void) + { + try + { + connector->close(); + delete connector; + + transport->close(); + delete transport; + + delete properties; + } + AMQ_CATCH_NOTHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_NOTHROW( ) + } + + /** + * Get the Connector that this Connection Data object holds + * @return Connector Pointer + */ + virtual connector::Connector* getConnector(void){ + return connector; + } + + /** + * Get the Connector that this Connection Data object holds + * @return Connector Pointer + */ + virtual transport::Transport* getTransport(void){ + return transport; + } + + /** + * Gets a reference to the properties that were used to configure + * this Connection. + * @return Properties object reference. + */ + virtual const util::Properties& getProperties(void) const { + return *properties; + } + + }; + +}} + +#endif /*_ACTIVEMQ_CORE_ACTIVEMQCONNECTIONDATA_H_*/ diff --git a/activemq-cpp/src/main/activemq/core/ActiveMQConnectionFactory.cpp b/activemq-cpp/src/main/activemq/core/ActiveMQConnectionFactory.cpp new file mode 100644 index 0000000000..4cf6699138 --- /dev/null +++ b/activemq-cpp/src/main/activemq/core/ActiveMQConnectionFactory.cpp @@ -0,0 +1,245 @@ +/* +* Copyright 2006 The Apache Software Foundation or its licensors, as +* applicable. +* +* 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 "ActiveMQConnectionFactory.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::util; +using namespace activemq::connector; +using namespace activemq::exceptions; +using namespace activemq::network; +using namespace activemq::transport; + +//////////////////////////////////////////////////////////////////////////////// +ActiveMQConnectionFactory::ActiveMQConnectionFactory(void) +{ + brokerURL = "tcp://localhost:61616"; + + this->username = ""; + this->password = ""; + this->clientId = ""; +} + +//////////////////////////////////////////////////////////////////////////////// +ActiveMQConnectionFactory::ActiveMQConnectionFactory(const std::string& url, + const std::string& username, + const std::string& password, + const std::string& clientId) +{ + brokerURL = url; + + this->username = username; + this->password = password; + this->clientId = clientId; +} + +//////////////////////////////////////////////////////////////////////////////// +cms::Connection* ActiveMQConnectionFactory::createConnection(void) +throw ( cms::CMSException ) +{ + return createConnection(username, password); +} + +//////////////////////////////////////////////////////////////////////////////// +cms::Connection* ActiveMQConnectionFactory::createConnection( + const std::string& username, + const std::string& password, + const std::string& clientId ) + throw ( cms::CMSException ) +{ + // Declared here so that they can be deleted in the catch block + SimpleProperties* properties = NULL; + Transport* transport = NULL; + Connector* connector = NULL; + ActiveMQConnectionData* connectionData = NULL; + ActiveMQConnection* connection = NULL; + + this->username = username; + this->password = password; + this->clientId = clientId; + + try + { + properties = new SimpleProperties; + + // if no Client Id specified, create one + if( this->clientId == "" ) + { + this->clientId = Guid::createGUIDString(); + } + + // Store login data in the properties + properties->setProperty( "username", this->username ); + properties->setProperty( "password", this->password ); + properties->setProperty( "clientId", this->clientId ); + + // Parse out the properties from the URI + parseURL( brokerURL, *properties ); + + // Create the Transport that the Connector will use. + string factoryName = + properties->getProperty( "transport", "tcp" ); + TransportFactory* factory = + TransportFactoryMap::getInstance().lookup( factoryName ); + if( factory == NULL ){ + throw ActiveMQException( + __FILE__, __LINE__, + "ActiveMQConnectionFactory::createConnection - " + "unknown transport factory"); + } + + // Create the transport. + transport = factory->createTransport( *properties ); + if( transport == NULL ){ + throw ActiveMQException( + __FILE__, __LINE__, + "ActiveMQConnectionFactory::createConnection - " + "failed creating new Transport"); + } + + // What wire format are we using, defaults to Stomp + std::string wireFormat = + properties->getProperty( "wireFormat", "stomp" ); + + // Now try and find a factory to create the Connector + ConnectorFactory* connectorfactory = + ConnectorFactoryMap::getInstance()->lookup( wireFormat ); + + if( connectorfactory == NULL ) + { + throw NullPointerException( + __FILE__, __LINE__, + "ActiveMQConnectionFactory::createConnection - " + "Connector for Wire Format not registered in Map"); + } + + // Create the Connector. + connector = connectorfactory->createConnector( *properties, transport ); + + if(connector == NULL) + { + throw NullPointerException( + __FILE__, __LINE__, + "ActiveMQConnectionFactory::createConnection - " + "Failed to Create the Connector"); + } + + // Start the Connector + connector->start(); + + // Create Holder and store the data for the Connection + connectionData = new ActiveMQConnectionData( + connector, transport, properties ); + + // Create and Return the new connection object. + connection = new ActiveMQConnection( connectionData ); + + return connection; + } + catch( exceptions::ActiveMQException& ex ) + { + ex.setMark( __FILE__, __LINE__ ); + + delete connection; + delete connector; + delete transport; + delete properties; + + throw ex; + } + catch( ... ) + { + exceptions::ActiveMQException ex( + __FILE__, __LINE__, + "ActiveMQConnectionFactory::create - " + "caught unknown exception" ); + + delete connection; + delete connector; + delete transport; + delete properties; + + throw ex; + } +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQConnectionFactory::parseURL(const std::string& URI, + Properties& properties) + throw ( exceptions::IllegalArgumentException ) +{ + try + { + StringTokenizer tokenizer(URI, ":/"); + + std::vector tokens; + + // Require that there be three tokens at the least, these are + // transport, url, port. + if(tokenizer.countTokens() < 3) + { + throw exceptions::IllegalArgumentException( + __FILE__, __LINE__, + (string("ActiveMQConnectionFactory::parseURL - " + "Marlformed URI: ") + URI).c_str()); + } + + // First element should be the Transport Type, following that is the + // URL and any params. + properties.setProperty( "transport", tokenizer.nextToken() ); + + // Parse URL and Port as one item, optional params follow the ? + // and then each param set is delimited with & we extract first + // three chars as they are the left over :// + properties.setProperty( "uri", tokenizer.nextToken("&?").substr(3) ); + + // Now get all the optional parameters and store them as properties + int count = tokenizer.toArray(tokens); + + for(int i = 0; i < count; ++i) + { + tokenizer.reset(tokens[i], "="); + + if(tokenizer.countTokens() != 2) + { + throw exceptions::IllegalArgumentException( + __FILE__, __LINE__, + (string("ActiveMQConnectionFactory::parseURL - " + "Marlformed Parameter = ") + tokens[i]).c_str()); + } + + // Store this param as a property + properties.setProperty(tokenizer.nextToken(), tokenizer.nextToken()); + } + } + AMQ_CATCH_RETHROW( IllegalArgumentException ) + AMQ_CATCH_EXCEPTION_CONVERT( ActiveMQException, IllegalArgumentException ) + AMQ_CATCHALL_THROW( IllegalArgumentException ) +} diff --git a/activemq-cpp/src/main/activemq/core/ActiveMQConnectionFactory.h b/activemq-cpp/src/main/activemq/core/ActiveMQConnectionFactory.h new file mode 100644 index 0000000000..51accac2a2 --- /dev/null +++ b/activemq-cpp/src/main/activemq/core/ActiveMQConnectionFactory.h @@ -0,0 +1,178 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CORE_ACTIVEMQCONNECTIONFACTORY_H_ +#define _ACTIVEMQ_CORE_ACTIVEMQCONNECTIONFACTORY_H_ + +#include +#include + +#include + +namespace activemq{ +namespace core{ + + class util::Properties; + + class ActiveMQConnectionFactory : public cms::ConnectionFactory + { + private: + + // The user name this factory will use to connect + std::string username; + + // The password this factory will use to connect + std::string password; + + // The client id to assign to the connection created + std::string clientId; + + // The URL of the Broker, the default is: + // "tcp://localhost:61616" + std::string brokerURL; + + public: + + /** + * Constructor + */ + ActiveMQConnectionFactory(void); + + /** + * Constructor + * @param the URL of the Broker we are connecting to. + * @param username to authenticate with, defaults to "" + * @param password to authenticate with, defaults to "" + * @param client Id to assign to connection, defaults to "" + */ + ActiveMQConnectionFactory(const std::string& url, + const std::string& username = "", + const std::string& password = "", + const std::string& clientId = ""); + + /** + * Destructor + */ + virtual ~ActiveMQConnectionFactory(void) {} + + /** + * Creates a connection with the default user identity. The + * connection is created in stopped mode. No messages will be + * delivered until the Connection.start method is explicitly + * called. + * @throws CMSException + */ + virtual cms::Connection* createConnection(void) throw ( cms::CMSException ); + + /** + * Creates a connection with the specified user identity. The + * connection is created in stopped mode. No messages will be + * delivered until the Connection.start method is explicitly called. + * @throw CMSException. + */ + virtual cms::Connection* createConnection(const std::string& username, + const std::string& password, + const std::string& clientId = "") + throw ( cms::CMSException ); + + /** + * Sets the username that should be used when creating a new connection + * @param username string + */ + virtual void setUsername(const std::string& username){ + this->username = username; + } + + /** + * Gets the username that this factory will use when creating a new + * connection instance. + * @return username string, "" for default credentials + */ + virtual const std::string& getUsername(void) const { + return username; + } + + /** + * Sets the password that should be used when creating a new connection + * @param password string + */ + virtual void setPassword(const std::string& password){ + this->password = password; + } + + /** + * Gets the password that this factory will use when creating a new + * connection instance. + * @return password string, "" for default credentials + */ + virtual const std::string& getPassword(void) const { + return password; + } + + /** + * Sets the Broker URL that should be used when creating a new + * connection instance + * @param brokerURL string + */ + virtual void setBrokerURL(const std::string& brokerURL){ + this->brokerURL = brokerURL; + } + + /** + * Gets the Broker URL that this factory will use when creating a new + * connection instance. + * @return brokerURL string + */ + virtual const std::string& getBrokerURL(void) const { + return brokerURL; + } + + /** + * Sets the Client Id that should be used when creating a new + * connection instance + * @param clientId string + */ + virtual void setClientId(const std::string& clientId){ + this->clientId = clientId; + } + + /** + * Gets the Client Id that this factory will use when creating a new + * connection instance. + * @return clientId string + */ + virtual const std::string& getClientId(void) const { + return clientId; + } + + protected: + + /** + * Parses the properties out of the provided Broker URI and sets + * them in the passed Properties Object. + * @param a Broker URI to parse + * @param a Properties object to set the parsed values in + * @throws IllegalArgumentException if the passed URI is invalid + */ + virtual void parseURL(const std::string& URI, + util::Properties& properties) + throw ( exceptions::IllegalArgumentException ); + + }; + +}} + +#endif /*_ACTIVEMQ_CORE_ACTIVEMQCONNECTIONFACTORY_H_*/ diff --git a/activemq-cpp/src/main/activemq/core/ActiveMQConsumer.cpp b/activemq-cpp/src/main/activemq/core/ActiveMQConsumer.cpp new file mode 100644 index 0000000000..638effb568 --- /dev/null +++ b/activemq-cpp/src/main/activemq/core/ActiveMQConsumer.cpp @@ -0,0 +1,443 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "ActiveMQConsumer.h" + +#include +#include +#include +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::exceptions; +using namespace activemq::concurrent; + +//////////////////////////////////////////////////////////////////////////////// +ActiveMQConsumer::ActiveMQConsumer(connector::ConsumerInfo* consumerInfo, + ActiveMQSession* session) +{ + if(session == NULL || consumerInfo == NULL) + { + throw NullPointerException( + __FILE__, __LINE__, + "ActiveMQConsumer::ActiveMQConsumer - Init with NULL Session"); + } + + // Init Producer Data + this->session = session; + this->consumerInfo = consumerInfo; + this->listenerThread = NULL; + this->listener = NULL; + this->shutdown = false; +} + +//////////////////////////////////////////////////////////////////////////////// +ActiveMQConsumer::~ActiveMQConsumer(void) +{ + try + { + // Dispose of the Consumer Info, this should stop us from getting + // any more messages. + session->onDestroySessionResource(this); + + // Stop the asynchronous message processin thread if it's + // running. + stopThread(); + + // Purge all the pending messages + purgeMessages(); + } + AMQ_CATCH_NOTHROW( ActiveMQException ) + AMQ_CATCHALL_NOTHROW( ) +} + +//////////////////////////////////////////////////////////////////////////////// +std::string ActiveMQConsumer::getMessageSelector(void) const + throw ( cms::CMSException ) +{ + try + { + // Fetch the Selector + return consumerInfo->getMessageSelector(); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::Message* ActiveMQConsumer::receive(void) throw ( cms::CMSException ) +{ + try + { + synchronized(&msgQueue) + { + // Check for empty in case of spurious wakeup, or race to + // queue lock. + while(!shutdown && msgQueue.empty()) + { + msgQueue.wait(); + } + + // This will only happen when this object is being + // destroyed in another thread context - kind of + // scary. + if( shutdown ){ + throw ActiveMQException( __FILE__, __LINE__, + "Consumer is being destroyed in another thread" ); + } + + // Fetch the Message then copy it so it can be handed off + // to the user. + cms::Message* message = msgQueue.pop(); + cms::Message* result = message->clone(); + + // The Message is cleaned up here if the Session is not + // transacted, otherwise we let the transaction clean up + // this message as it will have already been ack'd and + // stored for later redelivery. + destroyMessage( message ); + + return result; + } + + return NULL; + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::Message* ActiveMQConsumer::receive(int millisecs) + throw ( cms::CMSException ) +{ + try + { + synchronized(&msgQueue) + { + // Check for empty, and wait if its not + if( msgQueue.empty() ){ + msgQueue.wait(millisecs); + + // if its still empty...bail + if( msgQueue.empty() ) { + return NULL; + } + } + + // Fetch the Message then copy it so it can be handed off + // to the user. + cms::Message* message = msgQueue.pop(); + cms::Message* result = message->clone(); + + // The Message is cleaned up here if the Session is not + // transacted, otherwise we let the transaction clean up + // this message as it will have already been ack'd and + // stored for later redelivery. + destroyMessage( message ); + + return result; + } + + return NULL; + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::Message* ActiveMQConsumer::receiveNoWait(void) + throw ( cms::CMSException ) +{ + try + { + synchronized(&msgQueue) + { + if(!msgQueue.empty()) + { + // Fetch the Message then copy it so it can be handed off + // to the user. + cms::Message* message = msgQueue.pop(); + cms::Message* result = message->clone(); + + // The Message is cleaned up here if the Session is not + // transacted, otherwise we let the transaction clean up + // this message as it will have already been ack'd and + // stored for later redelivery. + destroyMessage( message ); + + return result; + } + } + + return NULL; + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQConsumer::setMessageListener(cms::MessageListener* listener) +{ + try + { + synchronized(&listenerLock) + { + this->listener = listener; + } + + // Start the thread if it isn't already running. + // If it is already running, this method will wake the thread up + // to notify it that there is a message listener, so that it may + // get rid of backed up messages. + startThread(); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQConsumer::acknowledgeMessage( const ActiveMQMessage* message ) + throw ( cms::CMSException ) +{ + try + { + // Delegate the Ack to the Session, we cast away copnstness since + // in a transactional session we might need to redeliver this + // message and update its data. + session->acknowledge(this, const_cast< ActiveMQMessage*>( message ) ); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQConsumer::run(void) +{ + try + { + while(!shutdown) + { + Message* message = NULL; + + synchronized(&msgQueue) + { + + // Gaurd against spurious wakeup or race to sync lock + // also if the listner has been unregistered we don't + // have anyone to notify, so we wait till a new one is + // registered, and then we will deliver the backlog + while(msgQueue.empty() || listener == NULL) + { + if( shutdown ) + { + break; + } + msgQueue.wait(); + } + + // don't want to process messages if we are shutting down. + if(shutdown) + { + return; + } + + // Dispatch the message + message = msgQueue.pop(); + } + + // Notify the listener + notifyListener( message ); + + // The Message is cleaned up here if the Session is not + // transacted, otherwise we let the transaction clean up + // this message as it will have already been ack'd and + // stored for later redelivery. + destroyMessage( message ); + } + } + catch( ... ) + { + cms::ExceptionListener* listener = session->getExceptionListener(); + + if(listener != NULL) + { + listener->onException( ActiveMQException( + __FILE__, __LINE__, + "ActiveMQConsumer::run - " + "MessageListener threw an unknown Exception, recovering...")); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQConsumer::dispatch(ActiveMQMessage* message) + throw ( cms::CMSException ) +{ + try + { + // If the Session is in ClientAcknowledge mode, then we set the + // handler in the message to this object and send it out. Otherwise + // we ack it here for all the other Modes. + if(session->getAcknowledgeMode() == Session::ClientAcknowledge) + { + // Register ourself so that we can handle the Message's + // acknowledge method. + message->setAckHandler(this); + } + else + { + session->acknowledge(this, message); + } + + // No listener, so we queue it + synchronized(&msgQueue) + { + msgQueue.push( dynamic_cast< cms::Message* >( message ) ); + msgQueue.notifyAll(); + } + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQConsumer::purgeMessages(void) +{ + try + { + synchronized(&msgQueue) + { + while(!msgQueue.empty()) + { + // destroy these messages if this is not a transacted + // session, if it is then the tranasction will clean + // the messages up. + destroyMessage( msgQueue.pop() ); + } + } + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQConsumer::onActiveMQMessage( ActiveMQMessage* message ) + throw ( ActiveMQException ) +{ + try + { + if( message == NULL ) + { + throw ActiveMQException( + __FILE__, __LINE__, + "ActiveMQConsumer::onActiveMQMessage - Passed a Null Message"); + } + + this->dispatch( message ); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQConsumer::notifyListener( Message* message ){ + + try + { + MessageListener* listener = NULL; + synchronized(&listenerLock) + { + listener = getMessageListener(); + } + if(listener != NULL) + { + listener->onMessage(*message); + } + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQConsumer::destroyMessage( Message* message ){ + + try + { + /** + * Only destroy the message if the session is NOT transacted. If + * it is, the session will take care of it. + */ + if( message != NULL && !session->isTransacted() ) + { + delete message; + } + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQConsumer::startThread(){ + + try + { + // Start the thread, if it's not already started. + if(listenerThread == NULL) + { + listenerThread = new Thread(this); + listenerThread->start(); + } + + // notify the Queue so that any pending messages get delivered + synchronized(&msgQueue) + { + msgQueue.notifyAll(); + } + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQConsumer::stopThread(){ + + try + { + shutdown = true; + + // if the thread is running signal it to quit and then + // wait for run to return so thread can die + if(listenerThread != NULL) + { + synchronized( &msgQueue ) + { + // Force a wakeup if run is in a wait. + msgQueue.notifyAll(); + } + + // Wait for it to die and then delete it. + listenerThread->join(); + delete listenerThread; + listenerThread = NULL; + } + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + diff --git a/activemq-cpp/src/main/activemq/core/ActiveMQConsumer.h b/activemq-cpp/src/main/activemq/core/ActiveMQConsumer.h new file mode 100644 index 0000000000..05e76a0c05 --- /dev/null +++ b/activemq-cpp/src/main/activemq/core/ActiveMQConsumer.h @@ -0,0 +1,227 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CORE_ACTIVEMQCONSUMER_H_ +#define _ACTIVEMQ_CORE_ACTIVEMQCONSUMER_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace core{ + + class ActiveMQSession; + + class ActiveMQConsumer : + public cms::MessageConsumer, + public ActiveMQAckHandler, + public concurrent::Runnable, + public ActiveMQMessageListener, + public ActiveMQSessionResource + { + private: + + // The session that owns this Consumer + ActiveMQSession* session; + + // The Consumer info for this Consumer + connector::ConsumerInfo* consumerInfo; + + // The Message Listener for this Consumer + cms::MessageListener* listener; + + // Lock to protect us from dispatching to a dead listener + concurrent::Mutex listenerLock; + + // Message Queue + util::Queue msgQueue; + + // Thread to notif a listener if one is added + concurrent::Thread* listenerThread; + + // Boolean to indicate that the listener Thread is shutting + // down and the run method should return. + bool shutdown; + + public: + + /** + * Constructor + */ + ActiveMQConsumer(connector::ConsumerInfo* consumerInfo, + ActiveMQSession* session); + + /** + * Destructor + */ + virtual ~ActiveMQConsumer(void); + + public: // Interface Implementation + + /** + * Synchronously Receive a Message + * @return new message + * @throws CMSException + */ + virtual cms::Message* receive(void) throw ( cms::CMSException ); + + /** + * Synchronously Receive a Message, time out after defined interval. + * Returns null if nothing read. + * @return new message + * @throws CMSException + */ + virtual cms::Message* receive(int millisecs) throw ( cms::CMSException ); + + /** + * Receive a Message, does not wait if there isn't a new message + * to read, returns NULL if nothing read. + * @return new message + * @throws CMSException + */ + virtual cms::Message* receiveNoWait(void) throw ( cms::CMSException ); + + /** + * Sets the MessageListener that this class will send notifs on + * @param MessageListener interface pointer + */ + virtual void setMessageListener(cms::MessageListener* listener); + + /** + * Gets the MessageListener that this class will send notifs on + * @param MessageListener interface pointer + */ + virtual cms::MessageListener* getMessageListener(void) const { + return this->listener; + } + + /** + * Gets this message consumer's message selector expression. + * @return This Consumer's selector expression or "". + * @throws cms::CMSException + */ + virtual std::string getMessageSelector(void) const + throw ( cms::CMSException ); + + /** + * Method called to acknowledge the message passed + * @param Message to Acknowlegde + * @throw CMSException + */ + virtual void acknowledgeMessage( const ActiveMQMessage* message ) + throw ( cms::CMSException ); + + /** + * Run method that is called from the Thread class when this object + * is registered with a Thread and started. This function reads from + * the message queue and dispatches calls to the MessageConsumer that + * is registered with this class. + * + * It is a error for a MessageListener to throw an exception in their + * onMessage method, but if it does happen this function will get any + * registered exception listener from the session and notify it. + */ + virtual void run(void); + + public: // ActiveMQMessageListener Methods + + /** + * Called asynchronously when a new message is received, the message + * that is passed is now the property of the callee, and the caller + * will disavowe all knowledge of the message, i.e Callee must delete. + * @param Message object pointer + */ + virtual void onActiveMQMessage( ActiveMQMessage* message ) + throw ( exceptions::ActiveMQException ); + + public: // ActiveMQSessionResource + + /** + * Retrieve the Connector resource that is associated with + * this Session resource. + * @return pointer to a Connector Resource, can be NULL + */ + virtual connector::ConnectorResource* getConnectorResource(void) { + return consumerInfo; + } + + public: // ActiveMQConsumer Methods + + /** + * Called to dispatch a message to this consumer, this is usually + * called from the context of another thread. This will enqueue a + * message on the Consumers Queue, or notify a listener if one is + * currently registered. + * @param cms::Message pointer to the message to dispatch + * @throw cms::CMSException + */ + virtual void dispatch(ActiveMQMessage* message) + throw ( cms::CMSException ); + + /** + * Get the Consumer information for this consumer + * @return Pointer to a Consumer Info Object + */ + virtual connector::ConsumerInfo* getConsumerInfo(void) { + return consumerInfo; + } + + protected: + + /** + * Purges all messages currently in the queue. This can be as a + * result of a rollback, or of the consumer being shutdown. + */ + virtual void purgeMessages(void); + + /** + * Destroys the message if the session is transacted, otherwise + * does nothing. + */ + virtual void destroyMessage( cms::Message* message ); + + /** + * Notifies the listener of a message. + */ + void notifyListener( cms::Message* message ); + + /** + * Starts the message processing thread to receive messages + * asynchronously. This thread is started when setMessageListener + * is invoked, which means that the caller is choosing to use this + * consumer asynchronously instead of synchronously (receive). + */ + void startThread(); + + /** + * Stops the asynchronous message processing thread if it's started. + */ + void stopThread(); + }; + +}} + +#endif /*_ACTIVEMQ_CORE_ACTIVEMQCONSUMER_H_*/ diff --git a/activemq-cpp/src/main/activemq/core/ActiveMQMessage.h b/activemq-cpp/src/main/activemq/core/ActiveMQMessage.h new file mode 100644 index 0000000000..d8de904508 --- /dev/null +++ b/activemq-cpp/src/main/activemq/core/ActiveMQMessage.h @@ -0,0 +1,68 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CORE_ACTIVEMQMESSAGE_H_ +#define _ACTIVEMQ_CORE_ACTIVEMQMESSAGE_H_ + +#include + +namespace activemq{ +namespace core{ + + class ActiveMQAckHandler; + + /** + * Interface for all ActiveMQ Messages that will pass through the core + * API layer. This interface defines a method that the API uses to set + * an Acknowledgement handler that will be called by the message when + * a user calls the acknowledge method of the Message + * interface. This is only done when the Session that this message + * passes through is in Client Acknowledge mode. + */ + class ActiveMQMessage + { + public: + + /** + * Destructor + */ + virtual ~ActiveMQMessage(void) {} + + /** + * Sets the Acknowledgement Handler that this Message will use + * when the Acknowledge method is called. + * @param ActiveMQAckHandler + */ + virtual void setAckHandler(ActiveMQAckHandler* handler) = 0; + + /** + * Gets the number of times this message has been redelivered. + * @return redelivery count + */ + virtual int getRedeliveryCount(void) const = 0; + + /** + * Sets the count of the number of times this message has been + * redelivered + * @param redelivery count + */ + virtual void setRedeliveryCount(int count) = 0; + + }; + +}} + +#endif /*_ACTIVEMQ_CORE_ACTIVEMQMESSAGE_H_*/ diff --git a/activemq-cpp/src/main/activemq/core/ActiveMQMessageListener.h b/activemq-cpp/src/main/activemq/core/ActiveMQMessageListener.h new file mode 100644 index 0000000000..534c9155b9 --- /dev/null +++ b/activemq-cpp/src/main/activemq/core/ActiveMQMessageListener.h @@ -0,0 +1,47 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CORE_ACTIVEMQMESSAGELISTENER_H_ +#define _ACTIVEMQ_CORE_ACTIVEMQMESSAGELISTENER_H_ + +#include + +namespace activemq{ +namespace core{ + + class ActiveMQMessage; + + class ActiveMQMessageListener + { + public: + + virtual ~ActiveMQMessageListener(void) {} + + /** + * Called asynchronously when a new message is received, the message + * that is passed is now the property of the callee, and the caller + * will disavowe all knowledge of the message, i.e Callee must delete. + * @param Message object pointer + */ + virtual void onActiveMQMessage( ActiveMQMessage* message ) + throw ( exceptions::ActiveMQException ) = 0; + + }; + +}} + +#endif /*_ACTIVEMQ_CORE_ACTIVEMQMESSAGELISTENER_H_*/ diff --git a/activemq-cpp/src/main/activemq/core/ActiveMQProducer.cpp b/activemq-cpp/src/main/activemq/core/ActiveMQProducer.cpp new file mode 100644 index 0000000000..9803ea5194 --- /dev/null +++ b/activemq-cpp/src/main/activemq/core/ActiveMQProducer.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "ActiveMQProducer.h" + +#include +#include + +using namespace std; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::connector; +using namespace activemq::exceptions; + +//////////////////////////////////////////////////////////////////////////////// +ActiveMQProducer::ActiveMQProducer(connector::ProducerInfo* producerInfo, + ActiveMQSession* session) +{ + if(session == NULL || producerInfo == NULL) + { + throw NullPointerException( + __FILE__, __LINE__, + "ActiveMQProducer::ActiveMQProducer - Init with NULL Session"); + } + + // Init Producer Data + this->session = session; + this->producerInfo = producerInfo; + + // Default the Delivery options + deliveryMode = cms::Message::PERSISTANT; + disableMsgId = false; + disableTimestamps = false; + priority = 4; + timeToLive = 0; +} + +//////////////////////////////////////////////////////////////////////////////// +ActiveMQProducer::~ActiveMQProducer(void) +{ + try + { + // Dispose of the ProducerInfo + session->onDestroySessionResource(this); + } + AMQ_CATCH_NOTHROW( ActiveMQException ) + AMQ_CATCHALL_NOTHROW( ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQProducer::send(cms::Message& message) + throw ( cms::CMSException ) +{ + try + { + send(producerInfo->getDestination(), message); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQProducer::send(const cms::Destination& destination, + cms::Message& message) throw ( cms::CMSException ) +{ + try + { + // configure the message + message.setCMSDestination(destination); + message.setCMSDeliveryMode(deliveryMode); + message.setCMSPriority(priority); + message.setCMSExpiration(timeToLive); + + session->send(&message, this); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} diff --git a/activemq-cpp/src/main/activemq/core/ActiveMQProducer.h b/activemq-cpp/src/main/activemq/core/ActiveMQProducer.h new file mode 100644 index 0000000000..7c18235b06 --- /dev/null +++ b/activemq-cpp/src/main/activemq/core/ActiveMQProducer.h @@ -0,0 +1,191 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CORE_ACTIVEMQPRODUCER_H_ +#define _ACTIVEMQ_CORE_ACTIVEMQPRODUCER_H_ + +#include +#include +#include + +#include +#include + +namespace activemq{ +namespace core{ + + class ActiveMQSession; + + class ActiveMQProducer : public cms::MessageProducer, + public ActiveMQSessionResource + { + private: + + // Delivery Mode of this Producer + cms::Message::DeliveryMode deliveryMode; + + // Disable the Message Id + bool disableMsgId; + + // Disable sending timestamps + bool disableTimestamps; + + // Priority Level to send at + int priority; + + // Time to live setting for message + int timeToLive; + + // Session that this producer sends to. + ActiveMQSession* session; + + // This Producers protocal specific info object + connector::ProducerInfo* producerInfo; + + public: + + /** + * Constructor + */ + ActiveMQProducer( connector::ProducerInfo* producerInfo, + ActiveMQSession* session ); + + /** + * Destructor + */ + virtual ~ActiveMQProducer(void); + + /** + * Sends the message to the default producer destination. + * @param a Message Object Pointer + * @throws CMSException + */ + virtual void send( cms::Message& message ) throw ( cms::CMSException ); + + /** + * Sends the message to the designated destination. + * @param a Message Object Pointer + * @throws CMSException + */ + virtual void send( const cms::Destination& destination, + cms::Message& message) throw ( cms::CMSException ); + + /** + * Sets the delivery mode for this Producer + * @param The DeliveryMode + */ + virtual void setDeliveryMode(cms::Message::DeliveryMode mode) { + deliveryMode = mode; + } + + /** + * Gets the delivery mode for this Producer + * @return The DeliveryMode + */ + virtual cms::Message::DeliveryMode getDeliveryMode(void) const { + return deliveryMode; + } + + /** + * Sets if Message Ids are disbled for this Producer + * @param boolean indicating enable / disable (true / false) + */ + virtual void setDisableMessageId( bool value ) { + disableMsgId = value; + } + + /** + * Sets if Message Ids are disbled for this Producer + * @param boolean indicating enable / disable (true / false) + */ + virtual bool getDisableMessageId(void) const { + return disableMsgId; + } + + /** + * Sets if Message Time Stamps are disbled for this Producer + * @param boolean indicating enable / disable (true / false) + */ + virtual void setDisableMessageTimeStamp( bool value ) { + disableTimestamps = value; + } + + /** + * Sets if Message Time Stamps are disbled for this Producer + * @param boolean indicating enable / disable (true / false) + */ + virtual bool getDisableMessageTimeStamp(void) const { + return disableTimestamps; + } + + /** + * Sets the Priority that this Producers sends messages at + * @param int value for Priority level + */ + virtual void setPriority( int priority ) { + this->priority = priority; + } + + /** + * Gets the Priority level that this producer sends messages at + * @return int based priority level + */ + virtual int getPriority(void) const { + return priority; + } + + /** + * Sets the Time to Live that this Producers sends messages with + * @param int value for time to live + */ + virtual void setTimeToLive( int time ) { + timeToLive = time; + } + + /** + * Gets the Time to Live that this producer sends messages with + * @return int based Time to Live + */ + virtual int getTimeToLive(void) const { + return timeToLive; + } + + public: // ActiveMQSessionResource + + /** + * Retrieve the Connector resource that is associated with + * this Session resource. + * @return pointer to a Connector Resource, can be NULL + */ + virtual connector::ConnectorResource* getConnectorResource(void) { + return producerInfo; + } + + public: + + /** + * Retrives this object ProducerInfo pointer + * @return ProducerInfo pointer + */ + virtual connector::ProducerInfo* getProducerInfo(void){ + return producerInfo; + } + + }; + +}} + +#endif /*_ACTIVEMQ_CORE_ACTIVEMQPRODUCER_H_*/ diff --git a/activemq-cpp/src/main/activemq/core/ActiveMQSession.cpp b/activemq-cpp/src/main/activemq/core/ActiveMQSession.cpp new file mode 100644 index 0000000000..23868e92ef --- /dev/null +++ b/activemq-cpp/src/main/activemq/core/ActiveMQSession.cpp @@ -0,0 +1,545 @@ +/* +* Copyright 2006 The Apache Software Foundation or its licensors, as +* applicable. +* +* 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 "ActiveMQSession.h" + +#include +#include + +#include +#include +#include +#include +#include + +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::util; +using namespace activemq::connector; +using namespace activemq::exceptions; + +//////////////////////////////////////////////////////////////////////////////// +ActiveMQSession::ActiveMQSession( SessionInfo* sessionInfo, + const Properties& properties, + ActiveMQConnection* connection) +{ + if(sessionInfo == NULL || connection == NULL) + { + throw NullPointerException( + __FILE__, __LINE__, + "ActiveMQSession::ActiveMQSession - Init with NULL data"); + } + + this->sessionInfo = sessionInfo; + this->transaction = NULL; + this->connection = connection; + this->closed = false; + + // Create a Transaction object only if the session is transactional + if(isTransacted()) + { + transaction = + new ActiveMQTransaction(connection, this, properties ); + } +} + +//////////////////////////////////////////////////////////////////////////////// +ActiveMQSession::~ActiveMQSession(void) +{ + try + { + // Destroy this session's resources + close(); + } + AMQ_CATCH_NOTHROW( ActiveMQException ) + AMQ_CATCHALL_NOTHROW( ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQSession::close(void) throw ( cms::CMSException ) +{ + if(closed) + { + return; + } + + try + { + // Destry the Transaction + delete transaction; + + // Destroy this sessions resources + connection->getConnectionData()-> + getConnector()->destroyResource(sessionInfo); + + // mark as done + closed = true; + } + AMQ_CATCH_NOTHROW( ActiveMQException ) + AMQ_CATCHALL_NOTHROW( ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQSession::commit(void) throw ( cms::CMSException ) +{ + try + { + if(closed || !isTransacted()) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQSession::commit - This Session Can't Commit"); + } + + // Commit the Transaction + transaction->commit(); + } + AMQ_CATCH_NOTHROW( ActiveMQException ) + AMQ_CATCHALL_NOTHROW( ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQSession::rollback(void) throw ( cms::CMSException ) +{ + try + { + if(closed || !isTransacted()) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQSession::rollback - This Session Can't Rollback"); + } + + // Rollback the Transaction + transaction->rollback(); + } + AMQ_CATCH_NOTHROW( ActiveMQException ) + AMQ_CATCHALL_NOTHROW( ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::MessageConsumer* ActiveMQSession::createConsumer( + cms::Destination& destination) + throw ( cms::CMSException ) +{ + try + { + if(closed) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQSession::createConsumer - Session Already Closed"); + } + + return createConsumer(destination, ""); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::MessageConsumer* ActiveMQSession::createConsumer( + cms::Destination& destination, + const std::string& selector) + throw ( cms::CMSException ) +{ + try + { + if(closed) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQSession::createConsumer - Session Already Closed"); + } + + ActiveMQConsumer* consumer = new ActiveMQConsumer( + connection->getConnectionData()->getConnector()-> + createConsumer(&destination, sessionInfo, selector), this); + + connection->addMessageListener( + consumer->getConsumerInfo()->getConsumerId(), consumer ); + + return consumer; + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::MessageConsumer* ActiveMQSession::createDurableConsumer( + cms::Topic& destination, + const std::string& name, + const std::string& selector, + bool noLocal ) + throw ( cms::CMSException ) +{ + try + { + if(closed) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQSession::createProducer - Session Already Closed"); + } + + ActiveMQConsumer* consumer = new ActiveMQConsumer( + connection->getConnectionData()->getConnector()-> + createDurableConsumer( &destination, sessionInfo, name, selector, noLocal ), this); + + connection->addMessageListener( + consumer->getConsumerInfo()->getConsumerId(), consumer ); + + return consumer; + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::MessageProducer* ActiveMQSession::createProducer( + cms::Destination& destination) + throw ( cms::CMSException ) +{ + try + { + if(closed) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQSession::createProducer - Session Already Closed"); + } + + return new ActiveMQProducer( + connection->getConnectionData()->getConnector()-> + createProducer(&destination, sessionInfo), this); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::Queue* ActiveMQSession::createQueue(const std::string& queueName) + throw ( cms::CMSException ) +{ + try + { + if(closed) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQSession::createQueue - Session Already Closed"); + } + + return connection->getConnectionData()-> + getConnector()->createQueue(queueName, sessionInfo); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::Topic* ActiveMQSession::createTopic(const std::string& topicName) + throw ( cms::CMSException ) +{ + try + { + if(closed) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQSession::createTopic - Session Already Closed"); + } + + return connection->getConnectionData()-> + getConnector()->createTopic(topicName, sessionInfo); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::TemporaryQueue* ActiveMQSession::createTemporaryQueue(void) + throw ( cms::CMSException ) +{ + try + { + if(closed) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQSession::createTemporaryQueue - Session Already Closed"); + } + + return connection->getConnectionData()-> + getConnector()->createTemporaryQueue(sessionInfo); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::TemporaryTopic* ActiveMQSession::createTemporaryTopic(void) + throw ( cms::CMSException ) +{ + try + { + if(closed) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQSession::createTemporaryTopic - Session Already Closed"); + } + + return connection->getConnectionData()-> + getConnector()->createTemporaryTopic(sessionInfo); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::Message* ActiveMQSession::createMessage(void) + throw ( cms::CMSException ) +{ + try + { + if(closed) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQSession::createMessage - Session Already Closed"); + } + + return connection->getConnectionData()-> + getConnector()->createMessage( sessionInfo, transaction ); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::BytesMessage* ActiveMQSession::createBytesMessage(void) + throw ( cms::CMSException) +{ + try + { + if(closed) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQSession::createBytesMessage - Session Already Closed"); + } + + return connection->getConnectionData()-> + getConnector()->createBytesMessage( sessionInfo, transaction ); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::BytesMessage* ActiveMQSession::createBytesMessage( + const unsigned char* bytes, + unsigned long bytesSize) + throw ( cms::CMSException) +{ + try + { + BytesMessage* msg = createBytesMessage(); + + msg->setBodyBytes(bytes, bytesSize); + + return msg; + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::TextMessage* ActiveMQSession::createTextMessage(void) + throw ( cms::CMSException ) +{ + try + { + if(closed) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQSession::createTextMessage - Session Already Closed"); + } + + return connection->getConnectionData()-> + getConnector()->createTextMessage( sessionInfo, transaction ); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::TextMessage* ActiveMQSession::createTextMessage(const std::string& text) + throw ( cms::CMSException ) +{ + try + { + TextMessage* msg = createTextMessage(); + + msg->setText(text.c_str()); + + return msg; + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::MapMessage* ActiveMQSession::createMapMessage(void) + throw ( cms::CMSException ) +{ + try + { + if(closed) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQSession::createMapMessage - Session Already Closed"); + } + + return connection-> + getConnectionData()-> + getConnector()->createMapMessage( sessionInfo, transaction ); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::Session::AcknowledgeMode ActiveMQSession::getAcknowledgeMode(void) const +{ + return sessionInfo != NULL ? + sessionInfo->getAckMode() : Session::AutoAcknowledge; +} + +//////////////////////////////////////////////////////////////////////////////// +bool ActiveMQSession::isTransacted(void) const +{ + return sessionInfo != NULL ? + sessionInfo->getAckMode() == Session::Transactional : false; +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQSession::acknowledge(ActiveMQConsumer* consumer, + ActiveMQMessage* message) + throw ( cms::CMSException ) +{ + try + { + if( closed ) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQSession::acknowledgeMessage - Session Already Closed"); + } + + // Stores the Message and its consumer in the tranasction, if the + // session is a transactional one. + if(isTransacted()) + { + transaction->addToTransaction( message, consumer ); + } + + // Delegate to connector to ack this message. + return connection->getConnectionData()-> + getConnector()->acknowledge( + sessionInfo, dynamic_cast< cms::Message* >( message ) ); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQSession::send(cms::Message* message, ActiveMQProducer* producer) + throw ( cms::CMSException ) +{ + try + { + if(closed) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQSession::onProducerClose - Session Already Closed"); + } + + // Send via the connection + connection->getConnectionData()-> + getConnector()->send( message, producer->getProducerInfo() ); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQSession::onDestroySessionResource( + ActiveMQSessionResource* resource ) + throw ( cms::CMSException ) +{ + try + { + if(closed) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQSession::onProducerClose - Session Already Closed"); + } + + ActiveMQConsumer* consumer = + dynamic_cast< ActiveMQConsumer*>( resource ); + + if( consumer != NULL ) + { + // Remove this Consumer from the Connection + connection->removeMessageListener( + consumer->getConsumerInfo()->getConsumerId()); + + // Remove this consumer from the Transaction if we are + // transactional + if( transaction != NULL ) + { + transaction->removeFromTransaction(consumer); + } + } + + // Free its resources. + connection->getConnectionData()-> + getConnector()->destroyResource( resource->getConnectorResource() ); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +cms::ExceptionListener* ActiveMQSession::getExceptionListener(void) +{ + if(connection != NULL) + { + return connection->getExceptionListener(); + } + + return NULL; +} diff --git a/activemq-cpp/src/main/activemq/core/ActiveMQSession.h b/activemq-cpp/src/main/activemq/core/ActiveMQSession.h new file mode 100644 index 0000000000..d7cadbe595 --- /dev/null +++ b/activemq-cpp/src/main/activemq/core/ActiveMQSession.h @@ -0,0 +1,271 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CORE_ACTIVEMQSESSION_H_ +#define _ACTIVEMQ_CORE_ACTIVEMQSESSION_H_ + +#include +#include +#include +#include + +namespace activemq{ +namespace core{ + + class ActiveMQTransaction; + class ActiveMQConnection; + class ActiveMQConsumer; + class ActiveMQMessage; + class ActiveMQProducer; + class ActiveMQConsumer; + + class ActiveMQSession : public cms::Session + { + private: + + // SessionInfo for this Session + connector::SessionInfo* sessionInfo; + + // Transaction Management object + ActiveMQTransaction* transaction; + + // Connection + ActiveMQConnection* connection; + + // Bool to indicate if this session was closed. + bool closed; + + public: + + /** + * Constructor + */ + ActiveMQSession( connector::SessionInfo* sessionInfo, + const util::Properties& properties, + ActiveMQConnection* connection); + + /** + * Destructor + */ + virtual ~ActiveMQSession(void); + + public: // Implements Mehtods + + /** + * Closes the Session + * @throw CMSException + */ + virtual void close(void) throw ( cms::CMSException ); + + /** + * Commits all messages done in this transaction and releases any + * locks currently held. + * @throws CMSException + */ + virtual void commit(void) throw ( cms::CMSException ); + + /** + * Rollsback all messages done in this transaction and releases any + * locks currently held. + * @throws CMSException + */ + virtual void rollback(void) throw ( cms::CMSException ); + + /** + * Creates a MessageConsumer for the specified destination. + * @param the Destination that this consumer receiving messages for. + * @throws CMSException + */ + virtual cms::MessageConsumer* createConsumer(cms::Destination& destination) + throw ( cms::CMSException ); + + /** + * Creates a MessageConsumer for the specified destination, using a + * message selector. + * @param the Destination that this consumer receiving messages for. + * @throws CMSException + */ + virtual cms::MessageConsumer* createConsumer(cms::Destination& destination, + const std::string& selector) + throw ( cms::CMSException ); + + /** + * Creates a durable subscriber to the specified topic, using a + * message selector + * @param the topic to subscribe to + * @param name used to identify the subscription + * @param only messages matching the selector are received + * @throws CMSException + */ + virtual cms::MessageConsumer* createDurableConsumer( + cms::Topic& destination, + const std::string& name, + const std::string& selector, + bool noLocal = false) + throw ( cms::CMSException ); + + /** + * Creates a MessageProducer to send messages to the specified + * destination. + * @param the Destination to publish on + * @throws CMSException + */ + virtual cms::MessageProducer* createProducer(cms::Destination& destination) + throw ( cms::CMSException ); + + /** + * Creates a queue identity given a Queue name. + * @param the name of the new Queue + * @throws CMSException + */ + virtual cms::Queue* createQueue(const std::string& queueName) + throw ( cms::CMSException ); + + /** + * Creates a topic identity given a Queue name. + * @param the name of the new Topic + * @throws CMSException + */ + virtual cms::Topic* createTopic(const std::string& topicName) + throw ( cms::CMSException ); + + /** + * Creates a TemporaryQueue object. + * @throws CMSException + */ + virtual cms::TemporaryQueue* createTemporaryQueue(void) + throw ( cms::CMSException ); + + /** + * Creates a TemporaryTopic object. + * @throws CMSException + */ + virtual cms::TemporaryTopic* createTemporaryTopic(void) + throw ( cms::CMSException ); + + /** + * Creates a new Message + * @throws CMSException + */ + virtual cms::Message* createMessage(void) + throw ( cms::CMSException ); + + /** + * Creates a BytesMessage + * @throws CMSException + */ + virtual cms::BytesMessage* createBytesMessage(void) + throw ( cms::CMSException); + + /** + * Creates a BytesMessage and sets the paylod to the passed value + * @param an array of bytes to set in the message + * @param the size of the bytes array, or number of bytes to use + * @throws CMSException + */ + virtual cms::BytesMessage* createBytesMessage(const unsigned char* bytes, + unsigned long bytesSize) + throw ( cms::CMSException); + + /** + * Creates a new TextMessage + * @throws CMSException + */ + virtual cms::TextMessage* createTextMessage(void) + throw ( cms::CMSException ); + + /** + * Creates a new TextMessage and set the text to the value given + * @param the initial text for the message + * @throws CMSException + */ + virtual cms::TextMessage* createTextMessage(const std::string& text) + throw ( cms::CMSException ); + + /** + * Creates a new TextMessage + * @throws CMSException + */ + virtual cms::MapMessage* createMapMessage(void) + throw ( cms::CMSException ); + + /** + * Returns the acknowledgement mode of the session. + * @return the Sessions Acknowledge Mode + */ + virtual cms::Session::AcknowledgeMode getAcknowledgeMode(void) const; + + /** + * Gets if the Sessions is a Transacted Session + * @return transacted true - false. + */ + virtual bool isTransacted(void) const; + + public: // ActiveMQSession specific Methods + + /** + * Sends a message from the Producer specified + * @param cms::Message pointer + * @param Producer Information + * @throws CMSException + */ + virtual void send(cms::Message* message, ActiveMQProducer* producer) + throw ( cms::CMSException ); + + /** + * When a ActiveMQ core object is closed or destroyed it should call + * back and let the session know that it is going away, this allows + * the session to clean up any associated resources. This method + * destroy's the data that is associated with a Producer object + * @param The Producer that is being destoryed + * @throw CMSException + */ + virtual void onDestroySessionResource( ActiveMQSessionResource* resource ) + throw ( cms::CMSException ); + + /** + * Called to acknowledge the receipt of a message. + * @param The consumer that received the message + * @param The Message to acknowledge. + * @throws CMSException + */ + virtual void acknowledge(ActiveMQConsumer* consumer, + ActiveMQMessage* message) + throw ( cms::CMSException); + + /** + * This method gets any registered exception listener of this sessions + * connection and returns it. Mainly intended for use by the objects + * that this session creates so that they can notify the client of + * exceptions that occur in the context of another thread. + * @returns cms::ExceptionListener pointer or NULL + */ + virtual cms::ExceptionListener* getExceptionListener(void); + + /** + * Gets the Session Information object for this session, if the + * session is closed than this returns null + * @return SessionInfo Pointer + */ + virtual connector::SessionInfo* getSessionInfo(void) { + return sessionInfo; + } + + }; + +}} + +#endif /*_ACTIVEMQ_CORE_ACTIVEMQSESSION_H_*/ diff --git a/activemq-cpp/src/main/activemq/core/ActiveMQSessionResource.h b/activemq-cpp/src/main/activemq/core/ActiveMQSessionResource.h new file mode 100644 index 0000000000..75a58bec7e --- /dev/null +++ b/activemq-cpp/src/main/activemq/core/ActiveMQSessionResource.h @@ -0,0 +1,42 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CORE_ACTIVEMQSESSIONRESOURCE_H_ +#define _ACTIVEMQ_CORE_ACTIVEMQSESSIONRESOURCE_H_ + +#include + +namespace activemq{ +namespace core{ + + class ActiveMQSessionResource + { + public: + + virtual ~ActiveMQSessionResource(void) {} + + /** + * Retrieve the Connector resource that is associated with + * this Session resource. + * @return pointer to a Connector Resource, can be NULL + */ + virtual connector::ConnectorResource* getConnectorResource(void) = 0; + + }; + +}} + +#endif /*_ACTIVEMQ_CORE_ACTIVEMQSESSIONRESOURCE_H_*/ diff --git a/activemq-cpp/src/main/activemq/core/ActiveMQTransaction.cpp b/activemq-cpp/src/main/activemq/core/ActiveMQTransaction.cpp new file mode 100644 index 0000000000..a20ed5f4d3 --- /dev/null +++ b/activemq-cpp/src/main/activemq/core/ActiveMQTransaction.cpp @@ -0,0 +1,343 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "ActiveMQTransaction.h" + +#include +#include +#include +#include +#include +#include + +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::util; +using namespace activemq::connector; +using namespace activemq::concurrent; +using namespace activemq::exceptions; + +//////////////////////////////////////////////////////////////////////////////// +ActiveMQTransaction::ActiveMQTransaction( ActiveMQConnection* connection, + ActiveMQSession* session, + const Properties& properties ) +{ + try + { + if(connection == NULL || session == NULL) + { + throw NullPointerException( + __FILE__, __LINE__, + "ActiveMQTransaction::ActiveMQTransaction - " + "Initialized with a NULL connection data"); + } + + // Store State Data + this->connection = connection; + this->session = session; + this->taskCount = 0; + + // convert from property Strings to int. + redeliveryDelay = Integer::parseInt( + properties.getProperty("transaction.redeliveryDelay", "25") ); + maxRedeliveries = Integer::parseInt( + properties.getProperty("transaction.maxRedeliveryCount", "5") ); + + // Start a new Transaction + transactionInfo = connection->getConnectionData()-> + getConnector()->startTransaction( session->getSessionInfo() ); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +ActiveMQTransaction::~ActiveMQTransaction(void) +{ + try + { + // Inform the connector we are rolling back before we close so that + // the provider knows we didn't complete this transaction + connection->getConnectionData()->getConnector()-> + rollback(transactionInfo, session->getSessionInfo()); + + // Clean up + clearTransaction(); + + // Must allow all the tasks to complete before we destruct otherwise + // the callbacks will cause an exception. + synchronized(&tasksDone) + { + while(taskCount != 0) + { + tasksDone.wait(1000); + + // TODO - Log Here to get some indication if we are stuck + } + } + } + AMQ_CATCH_NOTHROW( ActiveMQException ) + AMQ_CATCHALL_NOTHROW( ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQTransaction::clearTransaction(void) +{ + try + { + if(transactionInfo != NULL) + { + // Dispose of the ProducerInfo + connection->getConnectionData()-> + getConnector()->destroyResource(transactionInfo); + } + + synchronized(&rollbackLock) + { + // If there are any messages that are being transacted, then + // they die once and for all here. + RollbackMap::iterator itr = rollbackMap.begin(); + + for(; itr != rollbackMap.end(); ++itr) + { + MessageList::iterator msgItr = itr->second.begin(); + + for(; msgItr != itr->second.end(); ++msgItr) + { + delete *msgItr; + } + } + + rollbackMap.clear(); + } + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQTransaction::addToTransaction( ActiveMQMessage* message, + ActiveMQMessageListener* listener ) +{ + synchronized(&rollbackLock) + { + // Store in the Multi Map + rollbackMap[listener].push_back(message); + } +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQTransaction::removeFromTransaction( + ActiveMQMessageListener* listener ) +{ + try + { + // Delete all the messages, then remove the consumer's entry from + // the Rollback Map. + synchronized(&rollbackLock) + { + RollbackMap::iterator rb_itr = rollbackMap.find( listener ); + + if( rb_itr == rollbackMap.end() ) + { + return; + } + + MessageList::iterator itr = rb_itr->second.begin(); + + for(; itr != rollbackMap[listener].end(); ++itr) + { + delete *itr; + } + + // Erase the entry from the map + rollbackMap.erase(listener); + } + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQTransaction::commit(void) throw ( exceptions::ActiveMQException ) +{ + try + { + if(this->transactionInfo == NULL) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQTransaction::begin - " + "Commit called before transaction was started."); + } + + // Commit the current Transaction + connection->getConnectionData()->getConnector()-> + commit( transactionInfo, session->getSessionInfo() ); + + // Clean out the Transaction + clearTransaction(); + + // Start a new Transaction + transactionInfo = connection->getConnectionData()-> + getConnector()->startTransaction( session->getSessionInfo() ); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQTransaction::rollback(void) throw ( exceptions::ActiveMQException ) +{ + try + { + if(this->transactionInfo == NULL) + { + throw InvalidStateException( + __FILE__, __LINE__, + "ActiveMQTransaction::rollback - " + "Rollback called before transaction was started."); + } + + // Rollback the Transaction + connection->getConnectionData()->getConnector()-> + rollback( transactionInfo, session->getSessionInfo() ); + + // Dispose of the ProducerInfo + connection->getConnectionData()-> + getConnector()->destroyResource(transactionInfo); + + // Start a new Transaction + transactionInfo = connection->getConnectionData()-> + getConnector()->startTransaction( session->getSessionInfo() ); + + // Create a task for each consumer and copy its message list out + // to the Rollback task so we can clear the list for new messages + // that might come in next. + // NOTE - This could be turned into a Thread so that the connection + // doesn't have to wait on this method to complete an release its + // mutex so it can dispatch new messages. That would however requre + // copying the whole map over to the thread. + synchronized(&rollbackLock) + { + RollbackMap::iterator itr = rollbackMap.begin(); + + for(; itr != rollbackMap.end(); ++itr) + { + ThreadPool::getInstance()->queueTask(make_pair( + new RollbackTask( itr->first, + connection, + session, + itr->second, + maxRedeliveries, + redeliveryDelay) , this)); + + // Count the tasks started. + taskCount++; + + } + + // Clear the map. Ownership of the messages is now handed off + // to the rollback tasks. + rollbackMap.clear(); + } + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQTransaction::onTaskComplete( Runnable* task ) +{ + try + { + // Delete the task + delete task; + + taskCount--; + + if(taskCount == 0) + { + synchronized(&tasksDone) + { + tasksDone.notifyAll(); + } + } + } + AMQ_CATCH_NOTHROW( ActiveMQException ) + AMQ_CATCHALL_NOTHROW( ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQTransaction::onTaskException( Runnable* task, + exceptions::ActiveMQException& ex ) +{ + try + { + // Delegate + onTaskComplete(task); + + // Route the Error + ExceptionListener* listener = connection->getExceptionListener(); + + if(listener != NULL) + { + listener->onException( ex ); + } + } + AMQ_CATCH_NOTHROW( ActiveMQException ) + AMQ_CATCHALL_NOTHROW( ) +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQTransaction::RollbackTask::run(void) +{ + try + { + MessageList::iterator itr = messages.begin(); + + for(; itr != messages.end(); ++itr) + { + (*itr)->setRedeliveryCount((*itr)->getRedeliveryCount() + 1); + + // Redeliver Messages at some point in the future + Thread::sleep(redeliveryDelay); + + if((*itr)->getRedeliveryCount() >= maxRedeliveries) + { + // Poison Ack the Message, we give up processing this one + connection->getConnectionData()->getConnector()-> + acknowledge( + session->getSessionInfo(), + dynamic_cast< Message* >(*itr), + Connector::PoisonAck ); + + // Won't redeliver this so we kill it here. + delete *itr; + + return; + } + + listener->onActiveMQMessage(*itr); + } + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} diff --git a/activemq-cpp/src/main/activemq/core/ActiveMQTransaction.h b/activemq-cpp/src/main/activemq/core/ActiveMQTransaction.h new file mode 100644 index 0000000000..5864c2f088 --- /dev/null +++ b/activemq-cpp/src/main/activemq/core/ActiveMQTransaction.h @@ -0,0 +1,283 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_CORE_ACTIVEMQTRANSACTION_H_ +#define _ACTIVEMQ_CORE_ACTIVEMQTRANSACTION_H_ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace core{ + + class ActiveMQConnection; + class ActiveMQSession; + class ActiveMQMessage; + class ActiveMQMessageListener; + + /** + * Transaction Management class, hold messages that are to be redelivered + * upon a request to rollback. The Tranasction represents an always + * running transaction, when it is committed or rolled back it silently + * creates a new transaction for the next set of messages. The only + * way to permanently end this tranaction is to delete it. + * + * Configuration options + * + * transaction.redeliveryDelay + * Wait time between the redelivery of each message + * + * transaction.maxRedeliveryCount + * Max number of times a message can be redelivered, if the session is + * rolled back more than this many time, the message is dropped. + */ + class ActiveMQTransaction : public concurrent::TaskListener, + public connector::TransactionInfo, + public ActiveMQSessionResource + { + private: + + // List type for holding messages + typedef std::list< ActiveMQMessage* > MessageList; + + // Mapping of MessageListener Ids to Lists of Messages that are + // redelivered on a Rollback + typedef std::map< ActiveMQMessageListener*, MessageList > RollbackMap; + + private: + + // Connection this Transaction is associated with + ActiveMQConnection* connection; + + // Session this Transaction is associated with + ActiveMQSession* session; + + // Transaction Info for the current Transaction + connector::TransactionInfo* transactionInfo; + + // Map of ActiveMQMessageListener to Messages to Rollback + RollbackMap rollbackMap; + + // Lock object to protect the rollback Map + concurrent::Mutex rollbackLock; + + // Max number of redeliveries before we quit + int maxRedeliveries; + + // Wait time between sends of message on a rollback + int redeliveryDelay; + + // Mutex that is signaled when all tasks complete. + concurrent::Mutex tasksDone; + + // Count of Tasks that are outstanding + int taskCount; + + public: + + /** + * Constructor + */ + ActiveMQTransaction( ActiveMQConnection* connection, + ActiveMQSession* session, + const util::Properties& properties ); + + /** + * Destructor + */ + virtual ~ActiveMQTransaction(void); + + /** + * Adds the Message as a part of the Transaction for the specified + * ActiveMQConsumer. + * @param ActiveMQMessage + * @param ActiveMQMessageListener + */ + virtual void addToTransaction( ActiveMQMessage* message, + ActiveMQMessageListener* listener ); + + /** + * Removes the ActiveMQMessageListener and all of its transacted + * messages from the Transaction, this is usually only done when + * a ActiveMQMessageListener is destroyed. + * @param consumer who is to be removed. + */ + virtual void removeFromTransaction( ActiveMQMessageListener* listener ); + + /** + * Commit the current Transaction + * @throw CMSException + */ + virtual void commit(void) throw ( exceptions::ActiveMQException ); + + /** + * Rollback the current Transaction + * @throw CMSException + */ + virtual void rollback(void) throw ( exceptions::ActiveMQException ); + + /** + * Get the Transaction Information object for the current + * Transaction, returns NULL if no transaction is running + * @return TransactionInfo + */ + virtual connector::TransactionInfo* getTransactionInfo(void) const { + return transactionInfo; + } + + public: // TransactionInfo Interface + + /** + * Gets the Transction Id + * @return unsigned int Id + */ + virtual unsigned int getTransactionId(void) const { + return transactionInfo->getTransactionId(); + } + + /** + * Sets the Transction Id + * @param unsigned int Id + */ + virtual void setTransactionId( const unsigned int id ) { + transactionInfo->setTransactionId( id ); + } + + /** + * Gets the Session Info that this transaction is attached too + * @return SessionnInfo pointer + */ + virtual const connector::SessionInfo* getSessionInfo(void) const { + return transactionInfo->getSessionInfo(); + } + + /** + * Gets the Session Info that this transaction is attached too + * @return SessionnInfo pointer + */ + virtual void setSessionInfo( const connector::SessionInfo* session ) { + transactionInfo->setSessionInfo( session ); + } + + protected: // Task Listener Interface + + /** + * Called when a queued task has completed, the task that + * finished is passed along for user consumption. The task is + * deleted and the count of outstanding tasks is reduced. + * @param Runnable Pointer to the task that finished + */ + virtual void onTaskComplete( concurrent::Runnable* task ); + + /** + * Called when a queued task has thrown an exception while + * being run. The Callee should assume that this was an + * unrecoverable exeption and that this task is now defunct. + * Deletes the Task and notifies the connection that the + * exception has occurred. Reduce the outstanding task count. + * @param Runnable Pointer to the task + * @param The ActiveMQException that was thrown. + */ + virtual void onTaskException( concurrent::Runnable* task, + exceptions::ActiveMQException& ex ); + + public: // ActiveMQSessionResource + + /** + * Retrieve the Connector resource that is associated with + * this Session resource. + * @return pointer to a Connector Resource, can be NULL + */ + virtual connector::ConnectorResource* getConnectorResource(void) { + return transactionInfo; + } + + protected: + + /** + * Clean out all Messages from the Rollback Map, deleting the + * messages as it goes. Destroys the Transaction Info object as + * well. + * @throw ActiveMQException + */ + virtual void clearTransaction(void); + + private: + + // Internal class that is used to redeliver one consumers worth + // of messages from this transaction. + class RollbackTask : public concurrent::Runnable + { + private: + + // Wait time before redelivery in millisecs + int redeliveryDelay; + + // Max number of time to redeliver this message + int maxRedeliveries; + + // Messages to Redeliver + MessageList messages; + + // Consumer we are redelivering to + ActiveMQMessageListener* listener; + + // Connection to use for sending message acks + ActiveMQConnection* connection; + + // Session for this Transaction + ActiveMQSession* session; + + public: + + RollbackTask( ActiveMQMessageListener* listener, + ActiveMQConnection* connection, + ActiveMQSession* session, + MessageList& messages, + int maxRedeliveries, + int redeliveryDelay ){ + + // Store State Data. + this->messages = messages; + this->listener = listener; + this->redeliveryDelay = redeliveryDelay; + this->maxRedeliveries = maxRedeliveries; + this->session = session; + this->connection = connection; + } + + // Dispatches the Messages to the Consumer. + virtual void run(void); + + }; + + }; + +}} + +#endif /*_ACTIVEMQ_CORE_ACTIVEMQTRANSACTION_H_*/ diff --git a/activemq-cpp/src/main/activemq/exceptions/ActiveMQException.cpp b/activemq-cpp/src/main/activemq/exceptions/ActiveMQException.cpp new file mode 100644 index 0000000000..69c1c70101 --- /dev/null +++ b/activemq-cpp/src/main/activemq/exceptions/ActiveMQException.cpp @@ -0,0 +1,70 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 +#include "ActiveMQException.h" +#include + +using namespace activemq; +using namespace activemq::exceptions; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQException::buildMessage(const char* format, va_list& vargs) +{ + // Allocate buffer with a guess of it's size + int size = 128; + + // Format string + while( true ){ + + // Allocate a buffer of the specified size. + char* buffer = new char[size]; + + int written = vsnprintf(buffer, size, format, vargs); + if (written > -1 && written < size-1) { + + // Guessed size was enough. Assign the string. + message.assign (buffer, written); + break; + } + + // Our buffer wasn't big enough - destroy the old buffer, + // double the size and try again. + delete [] buffer; + size *= 2; + } + + activemq::logger::SimpleLogger logger("com.yadda1"); + logger.log( message ); +} + +//////////////////////////////////////////////////////////////////////////////// +void ActiveMQException::setMark( const char* file, const int lineNumber ){ + + // Add this mark to the end of the stack trace. + stackTrace.push_back( std::make_pair( (std::string)file, (int)lineNumber ) ); + + ostringstream stream; + stream << "\tFILE: " << stackTrace[stackTrace.size()-1].first; + stream << ", LINE: " << stackTrace[stackTrace.size()-1].second; + + activemq::logger::SimpleLogger logger("com.yadda2"); + logger.log( stream.str() ); +} + + + diff --git a/activemq-cpp/src/main/activemq/exceptions/ActiveMQException.h b/activemq-cpp/src/main/activemq/exceptions/ActiveMQException.h new file mode 100644 index 0000000000..b615d0a57e --- /dev/null +++ b/activemq-cpp/src/main/activemq/exceptions/ActiveMQException.h @@ -0,0 +1,178 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_EXCEPTIONS_ACTIVEMQEXCEPTION_H +#define ACTIVEMQ_EXCEPTIONS_ACTIVEMQEXCEPTION_H + +#include +#include +#include +#include + +namespace activemq{ +namespace exceptions{ + + /* + * Base class for all exceptions. + */ + class ActiveMQException : public cms::CMSException + { + private: + + /** + * The cause of this exception. + */ + std::string message; + + /** + * The stack trace. + */ + std::vector< std::pair< std::string, int> > stackTrace; + + public: + + /** + * Default Constructor + */ + ActiveMQException(void) {} + + /** + * Copy Constructor + */ + ActiveMQException( const ActiveMQException& ex ){ + *this = ex; + } + + /** + * Constructor - Initializes the file name and line number where + * this message occured. Sets the message to report, using an + * optional list of arguments to parse into the message + * @param file name where exception occurs + * @param line number where the exception occurred. + * @param message to report + * @param list of primitives that are formatted into the message + */ + ActiveMQException(const char* file, const int lineNumber, + const char* msg, ...) + { + va_list vargs ; + va_start(vargs, msg) ; + buildMessage(msg, vargs) ; + + // Set the first mark for this exception. + setMark( file, lineNumber ); + } + + /** + * Destructor + */ + virtual ~ActiveMQException(){} + + /** + * Gets the message for this exception. + */ + virtual const char* getMessage() const{ return message.c_str(); } + + /** + * Sets the cause for this exception. + * @param msg the format string for the msg. + */ + virtual void setMessage( const char* msg, ... ){ + va_list vargs ; + va_start(vargs, msg) ; + buildMessage(msg, vargs) ; + } + + /** + * Adds a file/line number to the stack trace. + * @param file The name of the file calling this method (use __FILE__). + * @param lineNumber The line number in the calling file (use __LINE__). + */ + virtual void setMark( const char* file, const int lineNumber ); + + /** + * Clones this exception. This is useful for cases where you need + * to preserve the type of the original exception as well as the message. + * All subclasses should override. + */ + virtual ActiveMQException* clone() const{ + return new ActiveMQException( *this ); + } + + /** + * Provides the stack trace for every point where + * this exception was caught, marked, and rethrown. The first + * item in the returned vector is the first point where the mark + * was set (e.g. where the exception was created). + * @return the stack trace. + */ + virtual std::vector< std::pair< std::string, int> > getStackTrace() const{ + return stackTrace; + } + + /** + * Prints the stack trace to std::err + */ + virtual void printStackTrace() const{ + printStackTrace( std::cerr ); + } + + /** + * Prints the stack trace to the given output stream. + * @param stream the target output stream. + */ + virtual void printStackTrace( std::ostream& stream ) const{ + stream << getStackTraceString(); + } + + /** + * Gets the stack trace as one contiguous string. + */ + virtual std::string getStackTraceString() const{ + + // Create the output stream. + std::ostringstream stream; + + // Write the message and each stack entry. + stream << message << std::endl; + for( unsigned int ix=0; ixmessage = ex.message; + this->stackTrace = ex.stackTrace; + return *this; + } + + protected: + + virtual void buildMessage(const char* format, va_list& vargs); + + }; + +}} + +#endif /*ACTIVEMQ_EXCEPTIONS_ACTIVEMQEXCEPTION_H*/ diff --git a/activemq-cpp/src/main/activemq/exceptions/ExceptionDefines.h b/activemq-cpp/src/main/activemq/exceptions/ExceptionDefines.h new file mode 100644 index 0000000000..d442f02099 --- /dev/null +++ b/activemq-cpp/src/main/activemq/exceptions/ExceptionDefines.h @@ -0,0 +1,78 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_EXCEPTIONS_EXCEPTIONDEFINES_H_ +#define _ACTIVEMQ_EXCEPTIONS_EXCEPTIONDEFINES_H_ + +/** + * Macro for catching and rethrowing an exception of + * a given type. + * @param type The type of the exception to throw + * (e.g. ActiveMQException ). + */ +#define AMQ_CATCH_RETHROW( type ) \ + catch( type& ex ){ \ + ex.setMark( __FILE__, __LINE__ ); \ + throw ex; \ + } + +/** + * Macro for catching an exception of one type and then rethrowing + * as another type. + * @param sourceType the type of the exception to be caught. + * @param targetType the type of the exception to be thrown. + */ +#define AMQ_CATCH_EXCEPTION_CONVERT( sourceType, targetType ) \ + catch( sourceType& ex ){ \ + targetType target( ex ); \ + target.setMark( __FILE__, __LINE__ ); \ + throw target; \ + } + +/** + * A catch-all that throws a known exception. + * @param type the type of exception to be thrown. + */ +#define AMQ_CATCHALL_THROW( type ) \ + catch( ... ){ \ + type ex( __FILE__, __LINE__, \ + "caught unknown exception" ); \ + throw ex; \ + } + +/** + * A catch-all that does not throw an exception, one use would + * be to catch any exception in a destructor and mark it, but not + * throw so that cleanup would continue as normal. + */ +#define AMQ_CATCHALL_NOTHROW( ) \ + catch( ... ){ \ + exceptions::ActiveMQException ex( __FILE__, __LINE__, \ + "caught unknown exception, not rethrowing" ); \ + } + +/** + * Macro for catching and rethrowing an exception of + * a given type. + * @param type The type of the exception to throw + * (e.g. ActiveMQException ). + */ +#define AMQ_CATCH_NOTHROW( type ) \ + catch( type& ex ){ \ + ex.setMark( __FILE__, __LINE__ ); \ + } + +#endif /*_ACTIVEMQ_EXCEPTIONS_EXCEPTIONDEFINES_H_*/ diff --git a/activemq-cpp/src/main/activemq/exceptions/IllegalArgumentException.h b/activemq-cpp/src/main/activemq/exceptions/IllegalArgumentException.h new file mode 100644 index 0000000000..8d11fdc66e --- /dev/null +++ b/activemq-cpp/src/main/activemq/exceptions/IllegalArgumentException.h @@ -0,0 +1,90 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_EXCEPTIONS_ILLEGALARGUMENTEXCEPTION_H_ +#define ACTIVEMQ_EXCEPTIONS_ILLEGALARGUMENTEXCEPTION_H_ + +#include + +namespace activemq{ +namespace exceptions{ + + /* + * Thrown when an illegal argument was passed into a method. + */ + class IllegalArgumentException : public ActiveMQException + { + public: + + /** + * Default Constructor + */ + IllegalArgumentException(){}; + + /** + * Conversion Constructor from some other ActiveMQException + * @param An exception that should become this type of Exception + */ + IllegalArgumentException( const ActiveMQException& ex ){ + *(ActiveMQException*)this = ex; + } + + /** + * Copy Constructor + */ + IllegalArgumentException( const IllegalArgumentException& ex ){ + *(ActiveMQException*)this = ex; + } + + /** + * Constructor - Initializes the file name and line number where + * this message occured. Sets the message to report, using an + * optional list of arguments to parse into the message + * @param file name where exception occurs + * @param line number where the exception occurred. + * @param message to report + * @param list of primitives that are formatted into the message + */ + IllegalArgumentException(const char* file, const int lineNumber, + const char* msg, ...) + { + va_list vargs ; + va_start(vargs, msg) ; + buildMessage(msg, vargs) ; + + // Set the first mark for this exception. + setMark( file, lineNumber ); + } + + /** + * Clones this exception. This is useful for cases where you need + * to preserve the type of the original exception as well as the message. + * All subclasses should override. + */ + virtual ActiveMQException* clone() const{ + return new IllegalArgumentException( *this ); + } + + /** + * Destructor + */ + virtual ~IllegalArgumentException(){} + + }; + +}} + +#endif /*ACTIVEMQ_EXCEPTIONS_ILLEGALARGUMENTEXCEPTION_H_*/ diff --git a/activemq-cpp/src/main/activemq/exceptions/IllegalMonitorStateException.h b/activemq-cpp/src/main/activemq/exceptions/IllegalMonitorStateException.h new file mode 100644 index 0000000000..262309a28b --- /dev/null +++ b/activemq-cpp/src/main/activemq/exceptions/IllegalMonitorStateException.h @@ -0,0 +1,92 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_EXCEPTIONS_ILLEGALMONITORSTATEEXCEPTION_H_ +#define ACTIVEMQ_EXCEPTIONS_ILLEGALMONITORSTATEEXCEPTION_H_ + +#include + +namespace activemq{ +namespace exceptions{ + + /* + * Thrown when an error occurs from calling a method from syncronizable + * and the caller doesn't hold a lock on the object. + */ + class IllegalMonitorStateException : public ActiveMQException + { + public: + + /** + * Default Constructor + */ + IllegalMonitorStateException(void) {}; + + /** + * Conversion Constructor from some other ActiveMQException + * @param An exception that should become this type of Exception + */ + IllegalMonitorStateException(const ActiveMQException& ex){ + *(ActiveMQException*)this = ex; + } + + /** + * Copy Constructor + */ + IllegalMonitorStateException(const IllegalMonitorStateException& ex){ + *(ActiveMQException*)this = ex; + } + + /** + * Constructor - Initializes the file name and line number where + * this message occured. Sets the message to report, using an + * optional list of arguments to parse into the message + * @param file name where exception occurs + * @param line number where the exception occurred. + * @param message to report + * @param list of primitives that are formatted into the message + */ + IllegalMonitorStateException(const char* file, + const int lineNumber, + const char* msg, ...) + { + va_list vargs; + va_start(vargs, msg); + buildMessage(msg, vargs); + + // Set the first mark for this exception. + setMark(file, lineNumber); + } + + /** + * Clones this exception. This is useful for cases where you need + * to preserve the type of the original exception as well as the message. + * All subclasses should override. + */ + virtual ActiveMQException* clone(void) const{ + return new IllegalMonitorStateException(*this); + } + + /** + * Destructor + */ + virtual ~IllegalMonitorStateException(void) {} + + }; + +}} + +#endif /*ACTIVEMQ_EXCEPTIONS_ILLEGALMONITORSTATEEXCEPTION_H_*/ diff --git a/activemq-cpp/src/main/activemq/exceptions/InterruptedException.h b/activemq-cpp/src/main/activemq/exceptions/InterruptedException.h new file mode 100644 index 0000000000..df483fff4a --- /dev/null +++ b/activemq-cpp/src/main/activemq/exceptions/InterruptedException.h @@ -0,0 +1,91 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_EXCEPTIONS_INTERRUPTEDENTEXCEPTION_H_ +#define ACTIVEMQ_EXCEPTIONS_INTERRUPTEDENTEXCEPTION_H_ + +#include + +namespace activemq{ +namespace exceptions{ + + /* + * Thrown when an Thread is interrupted during a wait. + */ + class InterruptedException : public ActiveMQException + { + public: + + /** + * Default Constructor + */ + InterruptedException(void) {}; + + /** + * Conversion Constructor from some other ActiveMQException + * @param An exception that should become this type of Exception + */ + InterruptedException(const ActiveMQException& ex){ + *(ActiveMQException*)this = ex; + } + + /** + * Copy Constructor + */ + InterruptedException(const InterruptedException& ex){ + *(ActiveMQException*)this = ex; + } + + /** + * Constructor - Initializes the file name and line number where + * this message occured. Sets the message to report, using an + * optional list of arguments to parse into the message + * @param file name where exception occurs + * @param line number where the exception occurred. + * @param message to report + * @param list of primitives that are formatted into the message + */ + InterruptedException(const char* file, + const int lineNumber, + const char* msg, ...) + { + va_list vargs; + va_start(vargs, msg); + buildMessage(msg, vargs); + + // Set the first mark for this exception. + setMark(file, lineNumber); + } + + /** + * Clones this exception. This is useful for cases where you need + * to preserve the type of the original exception as well as the message. + * All subclasses should override. + */ + virtual ActiveMQException* clone(void) const{ + return new InterruptedException(*this); + } + + /** + * Destructor + */ + virtual ~InterruptedException(void) {} + + }; + +}} + +#endif /*ACTIVEMQ_EXCEPTIONS_INTERRUPTEDENTEXCEPTION_H_*/ diff --git a/activemq-cpp/src/main/activemq/exceptions/InvalidStateException.h b/activemq-cpp/src/main/activemq/exceptions/InvalidStateException.h new file mode 100644 index 0000000000..f335e9287d --- /dev/null +++ b/activemq-cpp/src/main/activemq/exceptions/InvalidStateException.h @@ -0,0 +1,92 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_EXCEPTIONS_INVALIDSTATEEXCEPTION_H_ +#define _ACTIVEMQ_EXCEPTIONS_INVALIDSTATEEXCEPTION_H_ + +#include + +namespace activemq{ +namespace exceptions{ + + /* + * Thrown when an operation is requested, but the state of the object + * servicing the request is not correct for that request. + */ + class InvalidStateException : public ActiveMQException + { + public: + + /** + * Default Constructor + */ + InvalidStateException(void) {} + + /** + * Conversion Constructor from some other ActiveMQException + * @param An exception that should become this type of Exception + */ + InvalidStateException(const ActiveMQException& ex){ + *(ActiveMQException*)this = ex; + } + + /** + * Copy Constructor + */ + InvalidStateException(const InvalidStateException& ex){ + *(ActiveMQException*)this = ex; + } + + /** + * Constructor - Initializes the file name and line number where + * this message occured. Sets the message to report, using an + * optional list of arguments to parse into the message + * @param file name where exception occurs + * @param line number where the exception occurred. + * @param message to report + * @param list of primitives that are formatted into the message + */ + InvalidStateException(const char* file, + const int lineNumber, + const char* msg, ...) + { + va_list vargs; + va_start(vargs, msg); + buildMessage(msg, vargs); + + // Set the first mark for this exception. + setMark(file, lineNumber); + } + + /** + * Clones this exception. This is useful for cases where you need + * to preserve the type of the original exception as well as the message. + * All subclasses should override. + */ + virtual ActiveMQException* clone(void) const{ + return new InvalidStateException(*this); + } + + /** + * Destructor + */ + virtual ~InvalidStateException(void) {} + + }; + +}} + +#endif /*_ACTIVEMQ_EXCEPTIONS_INVALIDSTATEEXCEPTION_H_*/ diff --git a/activemq-cpp/src/main/activemq/exceptions/NoSuchElementException.h b/activemq-cpp/src/main/activemq/exceptions/NoSuchElementException.h new file mode 100644 index 0000000000..1eab12574e --- /dev/null +++ b/activemq-cpp/src/main/activemq/exceptions/NoSuchElementException.h @@ -0,0 +1,92 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_EXCEPTIONS_NOSUCHELEMENTEXCEPTION_H_ +#define ACTIVEMQ_EXCEPTIONS_NOSUCHELEMENTEXCEPTION_H_ + +#include + +namespace activemq{ +namespace exceptions{ + + /* + * Thrown from an operation that attempts to access some element that does + * not exist. + */ + class NoSuchElementException : public ActiveMQException + { + public: + + /** + * Default Constructor + */ + NoSuchElementException(void) {}; + + /** + * Conversion Constructor from some other ActiveMQException + * @param An exception that should become this type of Exception + */ + NoSuchElementException(const ActiveMQException& ex){ + *(ActiveMQException*)this = ex; + } + + /** + * Copy Constructor + */ + NoSuchElementException(const NoSuchElementException& ex){ + *(ActiveMQException*)this = ex; + } + + /** + * Constructor - Initializes the file name and line number where + * this message occured. Sets the message to report, using an + * optional list of arguments to parse into the message + * @param file name where exception occurs + * @param line number where the exception occurred. + * @param message to report + * @param list of primitives that are formatted into the message + */ + NoSuchElementException(const char* file, + const int lineNumber, + const char* msg, ...) + { + va_list vargs; + va_start(vargs, msg); + buildMessage(msg, vargs); + + // Set the first mark for this exception. + setMark(file, lineNumber); + } + + /** + * Clones this exception. This is useful for cases where you need + * to preserve the type of the original exception as well as the message. + * All subclasses should override. + */ + virtual ActiveMQException* clone(void) const{ + return new NoSuchElementException(*this); + } + + /** + * Destructor + */ + virtual ~NoSuchElementException(void) {} + + }; + +}} + +#endif /*ACTIVEMQ_EXCEPTIONS_NOSUCHELEMENTEXCEPTION_H_*/ diff --git a/activemq-cpp/src/main/activemq/exceptions/NullPointerException.h b/activemq-cpp/src/main/activemq/exceptions/NullPointerException.h new file mode 100644 index 0000000000..562d1de4ce --- /dev/null +++ b/activemq-cpp/src/main/activemq/exceptions/NullPointerException.h @@ -0,0 +1,91 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_EXCEPTIONS_NULLPOINTERENTEXCEPTION_H_ +#define ACTIVEMQ_EXCEPTIONS_NULLPOINTERENTEXCEPTION_H_ + +#include + +namespace activemq{ +namespace exceptions{ + + /* + * Thrown when an error occurs that involves a pointer being NULL + */ + class NullPointerException : public ActiveMQException + { + public: + + /** + * Default Constructor + */ + NullPointerException(void) {}; + + /** + * Conversion Constructor from some other ActiveMQException + * @param An exception that should become this type of Exception + */ + NullPointerException(const ActiveMQException& ex){ + *(ActiveMQException*)this = ex; + } + + /** + * Copy Constructor + */ + NullPointerException(const NullPointerException& ex){ + *(ActiveMQException*)this = ex; + } + + /** + * Constructor - Initializes the file name and line number where + * this message occured. Sets the message to report, using an + * optional list of arguments to parse into the message + * @param file name where exception occurs + * @param line number where the exception occurred. + * @param message to report + * @param list of primitives that are formatted into the message + */ + NullPointerException(const char* file, + const int lineNumber, + const char* msg, ...) + { + va_list vargs; + va_start(vargs, msg); + buildMessage(msg, vargs); + + // Set the first mark for this exception. + setMark(file, lineNumber); + } + + /** + * Clones this exception. This is useful for cases where you need + * to preserve the type of the original exception as well as the message. + * All subclasses should override. + */ + virtual ActiveMQException* clone(void) const{ + return new NullPointerException(*this); + } + + /** + * Destructor + */ + virtual ~NullPointerException(void) {} + + }; + +}} + +#endif /*ACTIVEMQ_EXCEPTIONS_NULLPOINTERENTEXCEPTION_H_*/ diff --git a/activemq-cpp/src/main/activemq/exceptions/RuntimeException.h b/activemq-cpp/src/main/activemq/exceptions/RuntimeException.h new file mode 100644 index 0000000000..1f7339668e --- /dev/null +++ b/activemq-cpp/src/main/activemq/exceptions/RuntimeException.h @@ -0,0 +1,93 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_EXCEPTIONS_RUNTIMEENTEXCEPTION_H_ +#define ACTIVEMQ_EXCEPTIONS_RUNTIMEENTEXCEPTION_H_ + +#include + +namespace activemq{ +namespace exceptions{ + + /* + * Thrown when an error occurs that involves something in the run time + * This could be a memory allocation exception or some other generally + * unrecoverable exception. + */ + class RuntimeException : public ActiveMQException + { + public: + + /** + * Default Constructor + */ + RuntimeException(void) {}; + + /** + * Conversion Constructor from some other ActiveMQException + * @param An exception that should become this type of Exception + */ + RuntimeException(const ActiveMQException& ex){ + *(ActiveMQException*)this = ex; + } + + /** + * Copy Constructor + */ + RuntimeException(const RuntimeException& ex){ + *(ActiveMQException*)this = ex; + } + + /** + * Constructor - Initializes the file name and line number where + * this message occured. Sets the message to report, using an + * optional list of arguments to parse into the message + * @param file name where exception occurs + * @param line number where the exception occurred. + * @param message to report + * @param list of primitives that are formatted into the message + */ + RuntimeException(const char* file, + const int lineNumber, + const char* msg, ...) + { + va_list vargs; + va_start(vargs, msg); + buildMessage(msg, vargs); + + // Set the first mark for this exception. + setMark(file, lineNumber); + } + + /** + * Clones this exception. This is useful for cases where you need + * to preserve the type of the original exception as well as the message. + * All subclasses should override. + */ + virtual ActiveMQException* clone(void) const{ + return new RuntimeException(*this); + } + + /** + * Destructor + */ + virtual ~RuntimeException(void) {} + + }; + +}} + +#endif /*ACTIVEMQ_EXCEPTIONS_RUNTIMEENTEXCEPTION_H_*/ diff --git a/activemq-cpp/src/main/activemq/exceptions/UnsupportedOperationException.h b/activemq-cpp/src/main/activemq/exceptions/UnsupportedOperationException.h new file mode 100644 index 0000000000..934abd9c72 --- /dev/null +++ b/activemq-cpp/src/main/activemq/exceptions/UnsupportedOperationException.h @@ -0,0 +1,90 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_EXCEPTIONS_UNSUPPORTEDOPERATIONEXCEPTION_H_ +#define ACTIVEMQ_EXCEPTIONS_UNSUPPORTEDOPERATIONEXCEPTION_H_ + +#include + +namespace activemq{ +namespace exceptions{ + + /* + * Thrown when an unsupported method is called. + */ + class UnsupportedOperationException : public ActiveMQException + { + public: + + /** + * Default Constructor + */ + UnsupportedOperationException(void) {}; + + /** + * Conversion Constructor from some other ActiveMQException + * @param An exception that should become this type of Exception + */ + UnsupportedOperationException( const ActiveMQException& ex ){ + *(ActiveMQException*)this = ex; + } + + /** + * Copy Constructor + */ + UnsupportedOperationException( const UnsupportedOperationException& ex ){ + *(ActiveMQException*)this = ex; + } + + /** + * Constructor - Initializes the file name and line number where + * this message occured. Sets the message to report, using an + * optional list of arguments to parse into the message + * @param file name where exception occurs + * @param line number where the exception occurred. + * @param message to report + * @param list of primitives that are formatted into the message + */ + UnsupportedOperationException(const char* file, const int lineNumber, + const char* msg, ...) + { + va_list vargs ; + va_start(vargs, msg) ; + buildMessage(msg, vargs) ; + + // Set the first mark for this exception. + setMark( file, lineNumber ); + } + + /** + * Clones this exception. This is useful for cases where you need + * to preserve the type of the original exception as well as the message. + * All subclasses should override. + */ + virtual ActiveMQException* clone() const{ + return new UnsupportedOperationException( *this ); + } + + /** + * Destructor + */ + virtual ~UnsupportedOperationException(){} + + }; + +}} + +#endif /*ACTIVEMQ_EXCEPTIONS_UNSUPPORTEDOPERATIONEXCEPTION_H_*/ diff --git a/activemq-cpp/src/main/activemq/io/BufferedInputStream.cpp b/activemq-cpp/src/main/activemq/io/BufferedInputStream.cpp new file mode 100644 index 0000000000..4fd13c1e7f --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/BufferedInputStream.cpp @@ -0,0 +1,145 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "BufferedInputStream.h" +#include + +using namespace activemq::io; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +BufferedInputStream::BufferedInputStream( InputStream* stream ) +{ + // Default to a 1k buffer. + init( stream, 1024 ); +} + +//////////////////////////////////////////////////////////////////////////////// +BufferedInputStream::BufferedInputStream( InputStream* stream, + const int bufferSize ) +{ + init( stream, bufferSize ); +} + +//////////////////////////////////////////////////////////////////////////////// +BufferedInputStream::~BufferedInputStream() +{ + // Destroy the buffer. + if( buffer != NULL ){ + delete [] buffer; + buffer = NULL; + } +} + +//////////////////////////////////////////////////////////////////////////////// +void BufferedInputStream::init( InputStream* stream, const int bufferSize ){ + + this->stream = stream; + this->bufferSize = bufferSize; + + // Create the buffer and initialize the head and tail positions. + buffer = new unsigned char[bufferSize]; + head = 0; + tail = 0; +} + +//////////////////////////////////////////////////////////////////////////////// +void BufferedInputStream::close() throw(cms::CMSException){ + + // Close the delegate stream. + stream->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +unsigned char BufferedInputStream::read() throw (IOException){ + + // If we don't have any data buffered yet - read as much as we can. + if( tail == head ){ + bufferData(); + } + + // Get the next character. + char returnValue = buffer[head++]; + + // If the buffer is now empty - reset it to the beginning of the buffer. + if( tail == head ){ + tail = head = 0; + } + + return returnValue; +} + +//////////////////////////////////////////////////////////////////////////////// +int BufferedInputStream::read( unsigned char* buffer, + const int bufferSize ) throw (IOException){ + + // If we still haven't filled the output buffer AND there is data + // on the input stream to be read, read a buffer's + // worth from the stream. + int totalRead = 0; + while( totalRead < bufferSize ){ + + // Get the remaining bytes to copy. + int bytesToCopy = min( tail-head, (bufferSize-totalRead) ); + + // Copy the data to the output buffer. + memcpy( buffer+totalRead, this->buffer+head, bytesToCopy ); + + // Increment the total bytes read. + totalRead += bytesToCopy; + + // Increment the head position. If the buffer is now empty, + // reset the positions and buffer more data. + head += bytesToCopy; + if( head == tail ){ + + // Reset the buffer indicies. + head = tail = 0; + + // If there is no more data currently available on the + // input stream, stop the loop. + if( stream->available() == 0 ){ + break; + } + + // Buffer as much data as we can. + bufferData(); + } + } + + // Return the total number of bytes read. + return totalRead; +} + +//////////////////////////////////////////////////////////////////////////////// +void BufferedInputStream::bufferData() throw (IOException){ + + if( tail == bufferSize ){ + throw IOException( __FILE__, __LINE__, + "BufferedInputStream::bufferData - buffer full" ); + } + + // Read in as many bytes as we can. + int bytesRead = stream->read( buffer+tail, bufferSize-tail ); + if( bytesRead == 0 ){ + throw IOException( __FILE__, __LINE__, + "BufferedInputStream::read() - failed reading bytes from stream"); + } + + // Increment the tail to the new end position. + tail += bytesRead; +} diff --git a/activemq-cpp/src/main/activemq/io/BufferedInputStream.h b/activemq-cpp/src/main/activemq/io/BufferedInputStream.h new file mode 100644 index 0000000000..fca24f0e79 --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/BufferedInputStream.h @@ -0,0 +1,191 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_IO_BUFFEREDINPUTSTREAM_H_ +#define ACTIVEMQ_IO_BUFFEREDINPUTSTREAM_H_ + +#include +#include + +namespace activemq{ +namespace io{ + + /** + * A wrapper around another input stream that performs + * a buffered read, where it reads more data than it needs + * in order to reduce the number of io operations on the + * input stream. + */ + class BufferedInputStream : public InputStream + { + private: + + /** + * The target input stream. + */ + InputStream* stream; + + /** + * The internal buffer. + */ + unsigned char* buffer; + + /** + * The buffer size. + */ + int bufferSize; + + /** + * The current head of the buffer. + */ + int head; + + /** + * The current tail of the buffer. + */ + int tail; + + public: + + /** + * Constructor + * @param stream The target input stream. + */ + BufferedInputStream( InputStream* stream ); + + /** + * Constructor + * @param stream the target input stream + * @param bufferSize the size for the internal buffer. + */ + BufferedInputStream( InputStream* stream, const int bufferSize ); + + /** + * Destructor. + */ + virtual ~BufferedInputStream(); + + /** + * Locks the object. + */ + virtual void lock() throw(exceptions::ActiveMQException){ + assert( stream != NULL ); + stream->lock(); + } + + /** + * Unlocks the object. + */ + virtual void unlock() throw(exceptions::ActiveMQException){ + assert( stream != NULL ); + stream->unlock(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. + */ + virtual void wait() throw(exceptions::ActiveMQException){ + assert( stream != NULL ); + stream->wait(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. This wait will timeout after the specified time + * interval. + * @param time in millisecsonds to wait, or WAIT_INIFINITE + * @throws ActiveMQException + */ + virtual void wait(unsigned long millisecs) + throw(exceptions::ActiveMQException) { + + assert( stream != NULL ); + stream->wait(millisecs); + } + + /** + * Signals a waiter on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + */ + virtual void notify() throw(exceptions::ActiveMQException){ + assert( stream != NULL ); + stream->notify(); + } + + /** + * Signals the waiters on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + */ + virtual void notifyAll() throw(exceptions::ActiveMQException){ + assert( stream != NULL ); + stream->notifyAll(); + } + + /** + * Indcates the number of bytes avaialable. + * @return the sum of the amount of data avalable + * in the buffer and the data available on the target + * input stream. + */ + virtual int available() const{ + return (tail-head)+stream->available(); + } + + /** + * Reads a single byte from the buffer. + * @return The next byte. + * @throws IOException thrown if an error occurs. + */ + virtual unsigned char read() throw (IOException); + + /** + * Reads an array of bytes from the buffer. + * @param buffer (out) the target buffer. + * @param bufferSize the size of the output buffer. + * @return The number of bytes read. + * @throws IOException thrown if an error occurs. + */ + virtual int read( unsigned char* buffer, const int bufferSize ) throw (IOException); + + /** + * Closes the target input stream. + */ + virtual void close(void) throw(cms::CMSException); + + private: + + /** + * Initializes the internal structures. + */ + void init( InputStream* stream, const int bufferSize ); + + /** + * Populates the buffer with as much data as possible + * from the target input stream. + */ + void bufferData(void) throw (IOException); + + }; + +}} + +#endif /*ACTIVEMQ_IO_BUFFEREDINPUTSTREAM_H_*/ diff --git a/activemq-cpp/src/main/activemq/io/BufferedOutputStream.cpp b/activemq-cpp/src/main/activemq/io/BufferedOutputStream.cpp new file mode 100644 index 0000000000..3a19076ed1 --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/BufferedOutputStream.cpp @@ -0,0 +1,121 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "BufferedOutputStream.h" +#include + +using namespace activemq::io; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +BufferedOutputStream::BufferedOutputStream( OutputStream* stream ) +{ + // Default to 1k buffer. + init( stream, 1024 ); +} + +//////////////////////////////////////////////////////////////////////////////// +BufferedOutputStream::BufferedOutputStream( OutputStream* stream, + const int bufSize ) +{ + init( stream, bufSize ); +} + +//////////////////////////////////////////////////////////////////////////////// +BufferedOutputStream::~BufferedOutputStream() +{ + // Destroy the buffer. + if( buffer != NULL ){ + delete [] buffer; + buffer = NULL; + } +} + +//////////////////////////////////////////////////////////////////////////////// +void BufferedOutputStream::init( OutputStream* stream, const int bufSize ){ + + this->stream = stream; + this->bufferSize = bufSize; + + buffer = new unsigned char[bufSize]; + head = tail = 0; +} + +//////////////////////////////////////////////////////////////////////////////// +void BufferedOutputStream::close() throw(cms::CMSException){ + + // Flush this stream. + flush(); + + // Close the delegate stream. + stream->close(); +} + +//////////////////////////////////////////////////////////////////////////////// +void BufferedOutputStream::emptyBuffer() throw (IOException){ + + if( head != tail ){ + stream->write( buffer+head, tail-head ); + } + head = tail = 0; +} + +//////////////////////////////////////////////////////////////////////////////// +void BufferedOutputStream::flush() throw (IOException){ + + // Empty the contents of the buffer to the output stream. + emptyBuffer(); + + // Flush the output stream. + stream->flush(); +} + +//////////////////////////////////////////////////////////////////////////////// +void BufferedOutputStream::write( const unsigned char c ) throw (IOException){ + + if( tail >= bufferSize ){ + emptyBuffer(); + } + + buffer[tail++] = c; +} + +//////////////////////////////////////////////////////////////////////////////// +void BufferedOutputStream::write( const unsigned char* buffer, const int len ) + throw (IOException) +{ + // Iterate until all the data is written. + for( int pos=0; pos < len; ){ + + if( tail >= bufferSize ){ + emptyBuffer(); + } + + // Get the number of bytes left to write. + int bytesToWrite = min( bufferSize-tail, len-pos ); + + // Copy the data. + memcpy( this->buffer+tail, buffer+pos, bytesToWrite ); + + // Increase the tail position. + tail += bytesToWrite; + + // Decrease the number of bytes to write. + pos += bytesToWrite; + } +} + diff --git a/activemq-cpp/src/main/activemq/io/BufferedOutputStream.h b/activemq-cpp/src/main/activemq/io/BufferedOutputStream.h new file mode 100644 index 0000000000..61525e0d66 --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/BufferedOutputStream.h @@ -0,0 +1,181 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_IO_BUFFEREDOUTPUTSTREAM_H_ +#define ACTIVEMQ_IO_BUFFEREDOUTPUTSTREAM_H_ + +#include +#include + +namespace activemq{ +namespace io{ + + /** + * Wrapper around another output stream that buffers + * output before writing to the target output stream. + */ + class BufferedOutputStream : public OutputStream + { + private: + + /** + * The target output stream. + */ + OutputStream* stream; + + /** + * The internal buffer. + */ + unsigned char* buffer; + + /** + * The size of the internal buffer. + */ + int bufferSize; + + /** + * The current head of the buffer. + */ + int head; + + /** + * The current tail of the buffer. + */ + int tail; + + public: + + /** + * Constructor. + * @param stream the target output stream. + */ + BufferedOutputStream( OutputStream* stream ); + + /** + * Constructor + * @param stream the target output stream. + * @param bufSize the size for the internal buffer. + */ + BufferedOutputStream( OutputStream* stream, const int bufSize ); + + /** + * Destructor + */ + virtual ~BufferedOutputStream(); + + /** + * Locks the object. + */ + virtual void lock() throw(exceptions::ActiveMQException){ + assert( stream != NULL ); + stream->lock(); + } + + /** + * Unlocks the object. + */ + virtual void unlock() throw(exceptions::ActiveMQException){ + assert( stream != NULL ); + stream->unlock(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. + */ + virtual void wait() throw(exceptions::ActiveMQException){ + assert( stream != NULL ); + stream->wait(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. This wait will timeout after the specified time + * interval. + * @param time in millisecsonds to wait, or WAIT_INIFINITE + * @throws ActiveMQException + */ + virtual void wait(unsigned long millisecs) + throw(exceptions::ActiveMQException) { + + assert( stream != NULL ); + stream->wait(millisecs); + } + + /** + * Signals a waiter on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + */ + virtual void notify() throw(exceptions::ActiveMQException){ + assert( stream != NULL ); + stream->notify(); + } + + /** + * Signals the waiters on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + */ + virtual void notifyAll() throw(exceptions::ActiveMQException){ + assert( stream != NULL ); + stream->notifyAll(); + } + + /** + * Writes a single byte to the output stream. + * @param c the byte. + * @throws IOException thrown if an error occurs. + */ + virtual void write( const unsigned char c ) throw (IOException); + + /** + * Writes an array of bytes to the output stream. + * @param buffer The array of bytes to write. + * @param len The number of bytes from the buffer to be written. + * @throws IOException thrown if an error occurs. + */ + virtual void write( const unsigned char* buffer, const int len ) throw (IOException); + + /** + * Invokes flush on the target output stream. + */ + virtual void flush() throw (IOException); + + /** + * Invokes close on the target output stream. + */ + void close() throw(cms::CMSException); + + private: + + /** + * Initializes the internal structures. + */ + void init( OutputStream* stream, const int bufSize ); + + /** + * Writes the contents of the buffer to the output stream. + */ + void emptyBuffer() throw (IOException); + }; + +}} + +#endif /*ACTIVEMQ_IO_BUFFEREDOUTPUTSTREAM_H_*/ diff --git a/activemq-cpp/src/main/activemq/io/ByteArrayInputStream.cpp b/activemq-cpp/src/main/activemq/io/ByteArrayInputStream.cpp new file mode 100644 index 0000000000..014b495bc8 --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/ByteArrayInputStream.cpp @@ -0,0 +1,96 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "ByteArrayInputStream.h" +#include + +using namespace activemq::io; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +ByteArrayInputStream::ByteArrayInputStream() +{ + pos = buffer.end(); +} + +//////////////////////////////////////////////////////////////////////////////// +ByteArrayInputStream::ByteArrayInputStream( const unsigned char* buffer, + int bufferSize ) +{ + setByteArray( buffer, bufferSize ); +} + +//////////////////////////////////////////////////////////////////////////////// +ByteArrayInputStream::~ByteArrayInputStream(void) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +void ByteArrayInputStream::setByteArray( const unsigned char* buffer, + int bufferSize ) +{ + // Remove old data + this->buffer.clear(); + + // Copy data to internal buffer. + for( int ix = 0; ix < bufferSize; ++ix ) + { + this->buffer.push_back(buffer[ix]); + } + + // Begin at the Beginning. + pos = this->buffer.begin(); +} + +//////////////////////////////////////////////////////////////////////////////// +void ByteArrayInputStream::close() throw(cms::CMSException){ + + // Close the delegate stream. + buffer.clear(); +} + +//////////////////////////////////////////////////////////////////////////////// +unsigned char ByteArrayInputStream::read() throw (IOException) +{ + if(pos != buffer.end()) + { + return *(pos++); + } + + throw IOException( __FILE__, __LINE__, + "ByteArrayInputStream::read: Out of Data"); +} + +//////////////////////////////////////////////////////////////////////////////// +int ByteArrayInputStream::read( unsigned char* buffer, + const int bufferSize ) + throw (IOException) +{ + int ix = 0; + + for( ; ix < bufferSize; ++ix, ++pos) + { + if(pos == this->buffer.end()) + { + break; + } + + buffer[ix] = *(pos); + } + + return ix; +} diff --git a/activemq-cpp/src/main/activemq/io/ByteArrayInputStream.h b/activemq-cpp/src/main/activemq/io/ByteArrayInputStream.h new file mode 100644 index 0000000000..2f74d59e3b --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/ByteArrayInputStream.h @@ -0,0 +1,173 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_IO_BYTEARRAYINPUTSTREAM_H_ +#define _ACTIVEMQ_IO_BYTEARRAYINPUTSTREAM_H_ + +#include +#include +#include +#include + +namespace activemq{ +namespace io{ + + class ByteArrayInputStream : public InputStream + { + private: + + /** + * The Array of Bytes to read from. + */ + std::vector buffer; + + /** + * iterator to current position in buffer. + */ + std::vector::const_iterator pos; + + /** + * Synchronization object. + */ + concurrent::Mutex mutex; + + public: + + /** + * Constructor + */ + ByteArrayInputStream(void); + + /** + * Constructor + * @param initial byte array to use to read from + * @param the size of the buffer + */ + ByteArrayInputStream( const unsigned char* buffer, + int bufferSize ); + + /** + * Destructor + */ + virtual ~ByteArrayInputStream(void); + + /** + * Sets the data that this reader uses, replaces any existing + * data and resets to beginning of the buffer. + * @param initial byte array to use to read from + * @param the size of the buffer + */ + virtual void setByteArray( const unsigned char* buffer, + int bufferSize ); + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. + * @throws ActiveMQException + */ + virtual void lock() throw(exceptions::ActiveMQException){ + mutex.lock(); + } + + /** + * Unlocks the object. + * @throws ActiveMQException + */ + virtual void unlock() throw(exceptions::ActiveMQException){ + mutex.unlock(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. + * @throws ActiveMQException + */ + virtual void wait() throw(exceptions::ActiveMQException){ + mutex.wait(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. This wait will timeout after the specified time + * interval. + * @param time in millisecsonds to wait, or WAIT_INIFINITE + * @throws ActiveMQException + */ + virtual void wait(unsigned long millisecs) throw(exceptions::ActiveMQException){ + mutex.wait(millisecs); + } + + /** + * Signals a waiter on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + * @throws ActiveMQException + */ + virtual void notify() throw(exceptions::ActiveMQException){ + mutex.notify(); + } + + /** + * Signals the waiters on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + * @throws ActiveMQException + */ + virtual void notifyAll() throw(exceptions::ActiveMQException){ + mutex.notifyAll(); + } + + /** + * Indcates the number of bytes avaialable. + * @return the sum of the amount of data avalable + * in the buffer and the data available on the target + * input stream. + */ + virtual int available() const{ + return distance(pos, buffer.end()); + } + + /** + * Reads a single byte from the buffer. + * @return The next byte. + * @throws IOException thrown if an error occurs. + */ + virtual unsigned char read() throw (IOException); + + /** + * Reads an array of bytes from the buffer. + * @param buffer (out) the target buffer. + * @param bufferSize the size of the output buffer. + * @return The number of bytes read. + * @throws IOException thrown if an error occurs. + */ + virtual int read( unsigned char* buffer, const int bufferSize ) + throw (IOException); + + /** + * Closes the target input stream. + */ + virtual void close() throw(cms::CMSException); + + }; + +}} + +#endif /*_ACTIVEMQ_IO_BYTEARRAYINPUTSTREAM_H_*/ diff --git a/activemq-cpp/src/main/activemq/io/ByteArrayOutputStream.cpp b/activemq-cpp/src/main/activemq/io/ByteArrayOutputStream.cpp new file mode 100644 index 0000000000..e05925e7b1 --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/ByteArrayOutputStream.cpp @@ -0,0 +1,62 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "ByteArrayOutputStream.h" +#include + +using namespace activemq::io; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +void ByteArrayOutputStream::close() throw(cms::CMSException) +{ + // Clear the Buffer + flush(); +} + +//////////////////////////////////////////////////////////////////////////////// +void ByteArrayOutputStream::flush() throw (IOException) +{ + // No Op +} + +//////////////////////////////////////////////////////////////////////////////// +void ByteArrayOutputStream::clear() throw (IOException) +{ + // Empty the contents of the buffer to the output stream. + buffer.clear(); +} + +//////////////////////////////////////////////////////////////////////////////// +void ByteArrayOutputStream::write( const unsigned char c ) + throw (IOException) +{ + buffer.push_back( c ); +} + +//////////////////////////////////////////////////////////////////////////////// +void ByteArrayOutputStream::write( const unsigned char* buffer, + const int len ) + throw (IOException) +{ + // Iterate until all the data is written. + for( int ix = 0; ix < len; ++ix) + { + this->buffer.push_back( buffer[ix] ); + } +} + diff --git a/activemq-cpp/src/main/activemq/io/ByteArrayOutputStream.h b/activemq-cpp/src/main/activemq/io/ByteArrayOutputStream.h new file mode 100644 index 0000000000..185422a379 --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/ByteArrayOutputStream.h @@ -0,0 +1,170 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_IO_BYTEARRAYOUTPUTSTREAM_H_ +#define _ACTIVEMQ_IO_BYTEARRAYOUTPUTSTREAM_H_ + +#include +#include +#include + +namespace activemq{ +namespace io{ + + class ByteArrayOutputStream : public OutputStream + { + private: + + /** + * The Array of Bytes to read from. + */ + std::vector buffer; + + /** + * Synchronization object. + */ + concurrent::Mutex mutex; + + public: + + /** + * Constructor + */ + ByteArrayOutputStream(void) {}; + + /** + * Destructor + */ + virtual ~ByteArrayOutputStream(void) {}; + + /** + * Get a snapshot of the data + * @return pointer to the data + */ + virtual const unsigned char* getByteArray(void) const + { + return &buffer[0]; + } + + /** + * Get the Size of the Internal Buffer + */ + virtual int getByteArraySize(void) const + { + return buffer.size(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. + * @throws ActiveMQException + */ + virtual void lock() throw(exceptions::ActiveMQException){ + mutex.lock(); + } + + /** + * Unlocks the object. + * @throws ActiveMQException + */ + virtual void unlock() throw(exceptions::ActiveMQException){ + mutex.unlock(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. + * @throws ActiveMQException + */ + virtual void wait() throw(exceptions::ActiveMQException){ + mutex.wait(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. This wait will timeout after the specified time + * interval. + * @param time in millisecsonds to wait, or WAIT_INIFINITE + * @throws ActiveMQException + */ + virtual void wait(unsigned long millisecs) throw(exceptions::ActiveMQException){ + mutex.wait(millisecs); + } + + /** + * Signals a waiter on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + * @throws ActiveMQException + */ + virtual void notify() throw(exceptions::ActiveMQException){ + mutex.notify(); + } + + /** + * Signals the waiters on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + * @throws ActiveMQException + */ + virtual void notifyAll() throw(exceptions::ActiveMQException){ + mutex.notifyAll(); + } + + /** + * Writes a single byte to the output stream. + * @param c the byte. + * @throws IOException thrown if an error occurs. + */ + virtual void write( const unsigned char c ) + throw (IOException); + + /** + * Writes an array of bytes to the output stream. + * @param buffer The array of bytes to write. + * @param len The number of bytes from the buffer to be written. + * @throws IOException thrown if an error occurs. + */ + virtual void write( const unsigned char* buffer, const int len ) + throw (IOException); + + /** + * Invokes flush on the target output stream, has no affect. + * @throws IOException + */ + virtual void flush( void ) throw (IOException); + + /** + * Clear current Stream contents + * @throws IOException + */ + virtual void clear( void ) throw (IOException); + + /** + * Invokes close on the target output stream. + * @throws CMSException + */ + void close( void ) throw(cms::CMSException); + + }; + +}} + +#endif /*_ACTIVEMQ_IO_BYTEARRAYOUTPUTSTREAM_H_*/ diff --git a/activemq-cpp/src/main/activemq/io/EndianReader.cpp b/activemq-cpp/src/main/activemq/io/EndianReader.cpp new file mode 100644 index 0000000000..42b4c0568e --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/EndianReader.cpp @@ -0,0 +1,129 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "EndianReader.h" +#include "../util/Endian.h" + +using namespace activemq::io; +using namespace activemq::util; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +EndianReader::EndianReader() +{ + inputStream = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +EndianReader::EndianReader( InputStream* is ) +{ + inputStream = is; +} + +//////////////////////////////////////////////////////////////////////////////// +EndianReader::~EndianReader() +{ + // no-op +} + +//////////////////////////////////////////////////////////////////////////////// +unsigned char EndianReader::readByte() throw(IOException) +{ + unsigned char value ; + + // Read a single byte + read(&value, sizeof(unsigned char)) ; + return value ; +} + +//////////////////////////////////////////////////////////////////////////////// +double EndianReader::readDouble() throw(IOException) +{ + double value ; + + // Read a double and convert from big endian to little endian if necessary + read((unsigned char*)&value, sizeof(double) ) ; + return Endian::byteSwap(value) ; +} + +//////////////////////////////////////////////////////////////////////////////// +float EndianReader::readFloat() throw(IOException) +{ + float value ; + + // Read a float and convert from big endian to little endian if necessary + read((unsigned char*)&value, sizeof(float)) ; + return Endian::byteSwap(value) ; +} + +//////////////////////////////////////////////////////////////////////////////// +uint16_t EndianReader::readUInt16() throw(IOException) +{ + uint16_t value; + + // Read a short and byteswap + read((unsigned char*)&value, sizeof(value) ); + return Endian::byteSwap(value); +} + +//////////////////////////////////////////////////////////////////////////////// +uint32_t EndianReader::readUInt32() throw(IOException) +{ + uint32_t value; + + // Read an int and convert from big endian to little endian if necessary + read((unsigned char*)&value, sizeof(value)) ; + return Endian::byteSwap(value) ; +} + +//////////////////////////////////////////////////////////////////////////////// +uint64_t EndianReader::readUInt64() throw(IOException) +{ + uint64_t value ; + + // Read a long long and convert from big endian to little endian if necessary + read((unsigned char*)&value, sizeof(value)) ; + return Endian::byteSwap(value) ; +} + +//////////////////////////////////////////////////////////////////////////////// +/*string EndianReader::readString() throw(IOException) +{ + // Read length of string + short length = readShort() ; + char* buffer = new char[length+1] ; + + // Read string bytes + read((unsigned char*)buffer, length) ; + *(buffer+length) = '\0' ; + + // Create string class + string value; + value.assign(buffer) ; + + return value ; +}*/ + +//////////////////////////////////////////////////////////////////////////////// +int EndianReader::read(unsigned char* buffer, int count) throw(IOException){ + + if( inputStream == NULL ){ + throw IOException( __FILE__, __LINE__, + "EndianReader::read(char*,int) - input stream is NULL" ); + } + + return inputStream->read( buffer, count ); +} diff --git a/activemq-cpp/src/main/activemq/io/EndianReader.h b/activemq-cpp/src/main/activemq/io/EndianReader.h new file mode 100644 index 0000000000..c58e8d6878 --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/EndianReader.h @@ -0,0 +1,132 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_IO_ENDIANREADER_H +#define ACTIVEMQ_IO_ENDIANREADER_H + +#include +#include + +namespace activemq{ +namespace io{ + + /* + * The BinaryReader class reads primitive C++ data types from an + * underlying input stream in a Java compatible way. Strings are + * read as raw bytes, no character decoding is performed. + * + * All numeric data types are assumed to be available in big + * endian (network byte order) and are converted automatically + * to little endian if needed by the platform. + * + * Should any error occur an IOException will be thrown. + */ + class EndianReader : public Reader + { + private: + + /** + * The target input stream. + */ + InputStream* inputStream; + + public: + + /** + * Constructor. + */ + EndianReader(); + + /** + * Constructor. + * @param is the target input stream. + */ + EndianReader( InputStream* is ); + + /** + * Destructor. + */ + virtual ~EndianReader(); + + /** + * Sets the target input stream. + */ + virtual void setInputStream( InputStream* is ){ + inputStream = is; + } + + /** + * Gets the target input stream. + */ + virtual InputStream* getInputStream(){ + return inputStream; + } + + /** + * Attempts to read an array of bytes from the stream. + * @param buffer The target byte buffer. + * @param count The number of bytes to read. + * @return The number of bytes read. + * @throws IOException thrown if an error occurs. + */ + virtual int read(unsigned char* buffer, int count) throw(IOException); + + /** + * Attempts to read a byte from the input stream + * @return The byte. + * @throws IOException thrown if an error occurs. + */ + virtual unsigned char readByte() throw(IOException); + + /** + * Attempts to read a double from the input stream + * @return The byte. + * @throws IOException thrown if an error occurs. + */ + virtual double readDouble() throw(IOException); + + /** + * Attempts to read a float from the input stream + * @return The byte. + * @throws IOException thrown if an error occurs. + */ + virtual float readFloat() throw(IOException); + + /** + * Attempts to read a short from the input stream + * @return The byte. + * @throws IOException thrown if an error occurs. + */ + virtual uint16_t readUInt16() throw(IOException); + + /** + * Attempts to read an int from the input stream + * @return The byte. + * @throws IOException thrown if an error occurs. + */ + virtual uint32_t readUInt32() throw(IOException); + + /** + * Attempts to read a long long from the input stream + * @return The byte. + * @throws IOException thrown if an error occurs. + */ + virtual uint64_t readUInt64() throw(IOException); + }; + +}} + +#endif /*ACTIVEMQ_IO_ENDIANREADER_H*/ diff --git a/activemq-cpp/src/main/activemq/io/EndianWriter.cpp b/activemq-cpp/src/main/activemq/io/EndianWriter.cpp new file mode 100644 index 0000000000..d8b6629386 --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/EndianWriter.cpp @@ -0,0 +1,114 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "EndianWriter.h" +#include + +using namespace activemq::io; +using namespace activemq::util; + +//////////////////////////////////////////////////////////////////////////////// +EndianWriter::EndianWriter() +{ + outputStream = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +EndianWriter::EndianWriter( OutputStream* os ) +{ + outputStream = os; +} + +//////////////////////////////////////////////////////////////////////////////// +EndianWriter::~EndianWriter() +{ + // no-op +} + +//////////////////////////////////////////////////////////////////////////////// +void EndianWriter::writeByte(unsigned char value) throw(IOException) +{ + // Write a single byte + write(&value, sizeof(unsigned char)); +} + +//////////////////////////////////////////////////////////////////////////////// +void EndianWriter::writeDouble(double v) throw(IOException) +{ + // Write a double, byteswap if necessary + double value = Endian::byteSwap(v); + write((unsigned char*)&value, sizeof(value)); +} + +//////////////////////////////////////////////////////////////////////////////// +void EndianWriter::writeFloat(float v) throw(IOException) +{ + // Write a float, byteswap if necessary + float value = Endian::byteSwap(v); + write((unsigned char*)&value, sizeof(value)); +} + +//////////////////////////////////////////////////////////////////////////////// +void EndianWriter::writeUInt16(uint16_t v) throw(IOException) +{ + // Write a short, byteswap if necessary + uint16_t value = Endian::byteSwap(v) ; + write((unsigned char*)&value, sizeof(value)); +} + +//////////////////////////////////////////////////////////////////////////////// +void EndianWriter::writeUInt32(uint32_t v) throw(IOException) +{ + // Write an int, byteswap if necessary + uint32_t value = Endian::byteSwap(v); + write((unsigned char*)&value, sizeof(value)); +} + +//////////////////////////////////////////////////////////////////////////////// +void EndianWriter::writeUInt64(uint64_t v) throw(IOException) +{ + // Write a long long, byteswap if necessary + uint64_t value = Endian::byteSwap(v); + write((unsigned char*)&value, sizeof(value)); +} + +//////////////////////////////////////////////////////////////////////////////// +/*void EndianWriter::writeString(const std::string& value) throw(IOException) +{ + // Assert argument + if( value.length() > USHRT_MAX ){ + throw IOException("String length exceeds maximum length") ; + } + + // Write length of string + short length = (short)value.length() ; + writeShort( length ) ; + + // Write string contents + write((unsigned char*)value.c_str(), length) ; +}*/ + +//////////////////////////////////////////////////////////////////////////////// +void EndianWriter::write(const unsigned char* buffer, int count) throw(IOException){ + + if( outputStream == NULL ){ + throw IOException( __FILE__, __LINE__, + "EndianWriter::write(char*,int) - input stream is NULL" ); + } + + outputStream->write( buffer, count ); +} diff --git a/activemq-cpp/src/main/activemq/io/EndianWriter.h b/activemq-cpp/src/main/activemq/io/EndianWriter.h new file mode 100644 index 0000000000..8096c4e103 --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/EndianWriter.h @@ -0,0 +1,132 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_IO_ENDIANWRITER_H +#define ACTIVEMQ_IO_ENDIANWRITER_H + +#include +#include +#include + +namespace activemq{ +namespace io{ + + /* + * The BinaryWriter class writes primitive C++ data types to an + * underlying output stream in a Java compatible way. Strings + * are written as raw bytes, no character encoding is performed. + * + * All numeric data types are written in big endian (network byte + * order) and if the platform is little endian they are converted + * automatically. + * + * Should any error occur an IOException will be thrown. + */ + class EndianWriter : public Writer + { + private: + + /** + * Target output stream. + */ + OutputStream* outputStream; + + public: + + /** + * Constructor. + */ + EndianWriter(); + + /** + * Constructor. + * @param os the target output stream. + */ + EndianWriter( OutputStream* os ); + + /** + * Destructor. + */ + virtual ~EndianWriter(); + + /** + * Sets the target output stream. + */ + virtual void setOutputStream( OutputStream* os ){ + outputStream = os; + } + + /** + * Gets the target output stream. + */ + virtual OutputStream* getOutputStream(){ + return outputStream; + } + + /** + * Writes a byte array to the target output stream. + * @param buffer a byte array. + * @param count the number of bytes to write. + * @throws IOException thrown if an error occurs. + */ + virtual void write(const unsigned char* buffer, int count) throw(IOException); + + /** + * Writes a byte to the target output stream. + * @param v the value to be written + * @throws IOException thrown if an error occurs. + */ + virtual void writeByte(unsigned char v) throw(IOException); + + /** + * Writes a double to the target output stream. + * @param v the value to be written + * @throws IOException thrown if an error occurs. + */ + virtual void writeDouble(double v) throw(IOException); + + /** + * Writes a float to the target output stream. + * @param v the value to be written + * @throws IOException thrown if an error occurs. + */ + virtual void writeFloat(float v) throw(IOException); + + /** + * Writes a short to the target output stream. + * @param v the value to be written + * @throws IOException thrown if an error occurs. + */ + virtual void writeUInt16(uint16_t v) throw(IOException); + + /** + * Writes an int to the target output stream. + * @param v the value to be written + * @throws IOException thrown if an error occurs. + */ + virtual void writeUInt32(uint32_t v) throw(IOException); + + /** + * Writes a long long to the target output stream. + * @param v the value to be written + * @throws IOException thrown if an error occurs. + */ + virtual void writeUInt64(uint64_t v) throw(IOException); + }; + +}} + +#endif /*ACTIVEMQ_IO_ENDIANWRITER_H*/ diff --git a/activemq-cpp/src/main/activemq/io/IOException.h b/activemq-cpp/src/main/activemq/io/IOException.h new file mode 100644 index 0000000000..24d16a5832 --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/IOException.h @@ -0,0 +1,64 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_IO_IOEXCEPTION_H +#define ACTIVEMQ_IO_IOEXCEPTION_H + +#include + +namespace activemq{ +namespace io{ + + /* + * Signals that an I/O exception of some sort has occurred. + */ + class IOException : public exceptions::ActiveMQException + { + public: + IOException(){} + IOException( const exceptions::ActiveMQException& ex ){ + *(exceptions::ActiveMQException*)this = ex; + } + IOException( const IOException& ex ){ + *(exceptions::ActiveMQException*)this = ex; + } + IOException(const char* file, const int lineNumber, + const char* msg, ...) + { + va_list vargs ; + va_start(vargs, msg) ; + buildMessage(msg, vargs) ; + + // Set the first mark for this exception. + setMark( file, lineNumber ); + } + + /** + * Clones this exception. This is useful for cases where you need + * to preserve the type of the original exception as well as the message. + * All subclasses should override. + */ + virtual exceptions::ActiveMQException* clone() const{ + return new IOException( *this ); + } + + virtual ~IOException(){} + + }; + +}} + +#endif /*ACTIVEMQ_IO_IOEXCEPTION_H*/ diff --git a/activemq-cpp/src/main/activemq/io/InputStream.h b/activemq-cpp/src/main/activemq/io/InputStream.h new file mode 100644 index 0000000000..bdcec585bc --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/InputStream.h @@ -0,0 +1,66 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_IO_INPUTSTREAM_H_ +#define ACTIVEMQ_IO_INPUTSTREAM_H_ + +#include +#include +#include + +namespace activemq{ +namespace io{ + + /** + * Base interface for an input stream. + */ + class InputStream + : + public cms::Closeable, + public concurrent::Synchronizable + { + + public: + + virtual ~InputStream(){} + + /** + * Indcates the number of bytes avaialable. + * @return the number of bytes available on this input stream. + */ + virtual int available() const = 0; + + /** + * Reads a single byte from the buffer. + * @return The next byte. + * @throws IOException thrown if an error occurs. + */ + virtual unsigned char read() throw (IOException) = 0; + + /** + * Reads an array of bytes from the buffer. + * @param buffer (out) the target buffer. + * @param bufferSize the size of the output buffer. + * @return The number of bytes read. + * @throws IOException thrown if an error occurs. + */ + virtual int read( unsigned char* buffer, const int bufferSize ) throw (IOException) = 0; + }; + +}} + +#endif /*ACTIVEMQ_IO_INPUTSTREAM_H_*/ diff --git a/activemq-cpp/src/main/activemq/io/OutputStream.h b/activemq-cpp/src/main/activemq/io/OutputStream.h new file mode 100644 index 0000000000..c168fa3800 --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/OutputStream.h @@ -0,0 +1,63 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_IO_OUTPUTSTREAM_H +#define ACTIVEMQ_IO_OUTPUTSTREAM_H + +#include +#include +#include + +namespace activemq{ +namespace io{ + + /** + * Base interface for an output stream. + */ + class OutputStream + : + public cms::Closeable, + public concurrent::Synchronizable + { + public: + + virtual ~OutputStream(){} + + /** + * Writes a single byte to the output stream. + * @param c the byte. + * @throws IOException thrown if an error occurs. + */ + virtual void write( const unsigned char c ) throw (IOException) = 0; + + /** + * Writes an array of bytes to the output stream. + * @param buffer The array of bytes to write. + * @param len The number of bytes from the buffer to be written. + * @throws IOException thrown if an error occurs. + */ + virtual void write( const unsigned char* buffer, const int len ) throw (IOException) = 0; + + /** + * Flushes any pending writes in this output stream. + */ + virtual void flush() throw (IOException) = 0; + }; + +}} + +#endif /*ACTIVEMQ_IO_OUTPUTSTREAM_H*/ diff --git a/activemq-cpp/src/main/activemq/io/Reader.h b/activemq-cpp/src/main/activemq/io/Reader.h new file mode 100644 index 0000000000..a968ce9f12 --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/Reader.h @@ -0,0 +1,66 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_IO_READER_H +#define ACTIVEMQ_IO_READER_H + +#include +#include +#include + +namespace activemq{ +namespace io{ + + /* + * Reader interface that wraps around an input stream and provides + * an interface for extracting the data from the input stream. + */ + class Reader + { + public: + + virtual ~Reader(){}; + + /** + * Sets the target input stream. + */ + virtual void setInputStream( InputStream* is ) = 0; + + /** + * Gets the target input stream. + */ + virtual InputStream* getInputStream() = 0; + + /** + * Attempts to read an array of bytes from the stream. + * @param buffer The target byte buffer. + * @param count The number of bytes to read. + * @return The number of bytes read. + * @throws IOException thrown if an error occurs. + */ + virtual int read(unsigned char* buffer, int count) throw(IOException) = 0; + + /** + * Attempts to read a byte from the input stream + * @return The byte. + * @throws IOException thrown if an error occurs. + */ + virtual unsigned char readByte() throw(IOException) = 0; + } ; + +}} + +#endif /*ACTIVEMQ_IO_READER_H*/ diff --git a/activemq-cpp/src/main/activemq/io/StandardErrorOutputStream.h b/activemq-cpp/src/main/activemq/io/StandardErrorOutputStream.h new file mode 100644 index 0000000000..7ac59dab00 --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/StandardErrorOutputStream.h @@ -0,0 +1,153 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_IO_STANDARDERROROUTPUTSTREAM_H_ +#define _ACTIVEMQ_IO_STANDARDERROROUTPUTSTREAM_H_ + +#include +#include + +#include + +namespace activemq{ +namespace io{ + + class StandardErrorOutputStream : public OutputStream + { + private: + + /** + * Synchronization object. + */ + concurrent::Mutex mutex; + + public: + + /** + * Constructor + */ + StandardErrorOutputStream(void) {} + + /** + * Destructor + */ + virtual ~StandardErrorOutputStream(void) {} + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. + * @throws ActiveMQException + */ + virtual void lock() throw(exceptions::ActiveMQException){ + mutex.lock(); + } + + /** + * Unlocks the object. + * @throws ActiveMQException + */ + virtual void unlock() throw(exceptions::ActiveMQException){ + mutex.unlock(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. + * @throws ActiveMQException + */ + virtual void wait() throw(exceptions::ActiveMQException){ + mutex.wait(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. This wait will timeout after the specified time + * interval. + * @param time in millisecsonds to wait, or WAIT_INIFINITE + * @throws ActiveMQException + */ + virtual void wait(unsigned long millisecs) throw(exceptions::ActiveMQException){ + mutex.wait(millisecs); + } + + /** + * Signals a waiter on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + * @throws ActiveMQException + */ + virtual void notify() throw(exceptions::ActiveMQException){ + mutex.notify(); + } + + /** + * Signals the waiters on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + * @throws ActiveMQException + */ + virtual void notifyAll() throw(exceptions::ActiveMQException){ + mutex.notifyAll(); + } + + /** + * Writes a single byte to the output stream. + * @param c the byte. + * @throws IOException thrown if an error occurs. + */ + virtual void write( const unsigned char c ) + throw (IOException) + { + std::cerr << c; + } + + /** + * Writes an array of bytes to the output stream. + * @param buffer The array of bytes to write. + * @param len The number of bytes from the buffer to be written. + * @throws IOException thrown if an error occurs. + */ + virtual void write( const unsigned char* buffer, const int len ) + throw (IOException) + { + for(int i = 0; i < len; ++i) + { + std::cerr << buffer[i]; + } + } + + /** + * Invokes flush on the target output stream. + */ + virtual void flush() throw (IOException){ + std::cerr.flush(); + } + + /** + * Invokes close on the target output stream. + */ + void close() throw(cms::CMSException){ + std::cerr.flush(); + } + + }; + +} + +#endif /*_ACTIVEMQ_IO_STANDARDERROROUTPUTSTREAM_H_*/ diff --git a/activemq-cpp/src/main/activemq/io/Writer.h b/activemq-cpp/src/main/activemq/io/Writer.h new file mode 100644 index 0000000000..abddb5f077 --- /dev/null +++ b/activemq-cpp/src/main/activemq/io/Writer.h @@ -0,0 +1,65 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_IO_WRITER_H +#define ACTIVEMQ_IO_WRITER_H + +#include +#include +#include + +namespace activemq{ +namespace io{ + + /* + * Writer interface for an object that wraps around an output + * stream + */ + class Writer + { + public: + + virtual ~Writer(){}; + + /** + * Sets the target output stream. + */ + virtual void setOutputStream( OutputStream* os ) = 0; + + /** + * Gets the target output stream. + */ + virtual OutputStream* getOutputStream() = 0; + + /** + * Writes a byte array to the output stream. + * @param buffer a byte array + * @param count the number of bytes in the array to write. + * @throws IOException thrown if an error occurs. + */ + virtual void write(const unsigned char* buffer, int count) throw(IOException) = 0; + + /** + * Writes a byte to the output stream. + * @param v The value to be written. + * @throws IOException thrown if an error occurs. + */ + virtual void writeByte(unsigned char v) throw(IOException) = 0; + }; + +}} + +#endif /*ACTIVEMQ_IO_WRITER_H*/ diff --git a/activemq-cpp/src/main/activemq/logger/ConsoleHandler.h b/activemq-cpp/src/main/activemq/logger/ConsoleHandler.h new file mode 100644 index 0000000000..7941c9ed94 --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/ConsoleHandler.h @@ -0,0 +1,87 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_LOGGER_CONSOLEHANDLER_H_ +#define _ACTIVEMQ_LOGGER_CONSOLEHANDLER_H_ + +#include +#include + +namespace activemq{ +namespace logger{ + + /** + * This Handler publishes log records to System.err. By default the + * SimpleFormatter is used to generate brief summaries. + * + * Configuration: By default each ConsoleHandler is initialized using + * the following LogManager configuration properties. If properties are + * not defined (or have invalid values) then the specified default + * values are used. + * + * ConsoleHandler.level specifies the default level for the Handler + * (defaults to Level.INFO). + * ConsoleHandler.filter specifies the name of a Filter class to use + * (defaults to no Filter). + * ConsoleHandler.formatter specifies the name of a Formatter class to + * use (defaults to SimpleFormatter). + */ + class ConsoleHandler + { + private: + + // The Standard Error Stream to log to + io::StandardErrorOutputStream stream; + + // The default Simple Formatter + SimpleFormatter formatter; + + public: + + /** + * Constructor + */ + ConsoleHandler(void) : StreamHandler(&stream, &formatter) + { + // Defaults level to Info + setLevel(Level.INFO); + } + + /** + * Destructor + */ + virtual ~ConsoleHandler(void) {} + + /** + * Close the current output stream. + *

+ * Override the StreamHandler close to flush the Std Err stream + * but doesn't close. + * @throw CMSException + */ + virtual void close(void) throw ( cms::CMSException ) + { + if(getOutputStream()) + { + getOutputStream->flush(); + } + } + + }; + +}} + +#endif /*_ACTIVEMQ_LOGGER_CONSOLEHANDLER_H_*/ diff --git a/activemq-cpp/src/main/activemq/logger/Filter.h b/activemq-cpp/src/main/activemq/logger/Filter.h new file mode 100644 index 0000000000..abb0758d57 --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/Filter.h @@ -0,0 +1,53 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_LOGGER_FILTER_H_ +#define _ACTIVEMQ_LOGGER_FILTER_H_ + +#include + +namespace activemq{ +namespace logger{ + + /** + * A Filter can be used to provide fine grain control over what is + * logged, beyond the control provided by log levels. + * + * Each Logger and each Handler can have a filter associated with it. + * The Logger or Handler will call the isLoggable method to check if a + * given LogRecord should be published. If isLoggable returns false, + * the LogRecord will be discarded. + */ + class Filter + { + public: + + /** + * Destructor + */ + virtual ~Filter(void) {} + + /** + * Check if a given log record should be published. + * @param the LogRecord to check. + */ + virtual bool isLoggable(const LogRecord& record) const = 0; + + }; + +}} + +#endif /*_ACTIVEMQ_LOGGER_FILTER_H_*/ diff --git a/activemq-cpp/src/main/activemq/logger/Formatter.h b/activemq-cpp/src/main/activemq/logger/Formatter.h new file mode 100644 index 0000000000..ac7b6e2c4c --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/Formatter.h @@ -0,0 +1,74 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_LOGGER_FORMATTER_H_ +#define _ACTIVEMQ_LOGGER_FORMATTER_H_ + +namespace activemq{ +namespace logger{ + + /** + * A Formatter provides support for formatting LogRecords. + * + * Typically each logging Handler will have a Formatter associated with + * it. The Formatter takes a LogRecord and converts it to a string. + * + * Some formatters (such as the XMLFormatter) need to wrap head and + * tail strings around a set of formatted records. The getHeader and + * getTail methods can be used to obtain these strings. + */ + class Formatter + { + public: + + /** + * Destrcutor + */ + virtual ~Formatter(void) {} + + /** + * Format the given log record and return the formatted string. + * @param The Log Record to Format + */ + virtual std::string format(const LogRecord& record) const = 0; + + /** + * Format the message string from a log record. + * @param The Log Record to Format + */ + virtual std::string formatMessage(const LogRecord& record) const = 0; + + /** + * Return the header string for a set of formatted records. In the + * default implementation this method should return empty string + * @param the target handler, can be null + * @return the head string + */ + virtual std::string getHead(const Handler* handler) = 0; + + /** + * Return the tail string for a set of formatted records. In the + * default implementation this method should return empty string + * @param the target handler, can be null + * @return the tail string + */ + virtual std::string getTail(const Handler* handler) = 0; + + }; + +}} + +#endif /*_ACTIVEMQ_LOGGER_FORMATTER_H_*/ diff --git a/activemq-cpp/src/main/activemq/logger/Handler.h b/activemq-cpp/src/main/activemq/logger/Handler.h new file mode 100644 index 0000000000..3cdc565647 --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/Handler.h @@ -0,0 +1,125 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_LOGGER_HANDLER_H_ +#define _ACTIVEMQ_LOGGER_HANDLER_H_ + +#include +#include + +namespace activemq{ +namespace logger{ + + class Filter; + class Formatter; + + /** + * A Handler object takes log messages from a Logger and exports them. + * It might for example, write them to a console or write them to a file, + * or send them to a network logging service, or forward them to an OS + * log, or whatever. + * + * A Handler can be disabled by doing a setLevel(Level.OFF) and can be + * re-enabled by doing a setLevel with an appropriate level. + * + * Handler classes typically use LogManager properties to set default + * values for the Handler's Filter, Formatter, and Level. See the + * specific documentation for each concrete Handler class. + */ + class Handler : public cms::Closeable + { + public: + + /** + * Destructor + */ + virtual ~Handler(void) {} + + /** + * Flush the Handler's output, clears any buffers. + */ + virtual void flush(void) = 0; + + /** + * Publish the Log Record to this Handler + * @param The Log Record to Publish + */ + virtual void publish(const LogRecord& record) = 0; + + /** + * Check if this Handler would actually log a given LogRecord. + *

+ * This method checks if the LogRecord has an appropriate Level and + * whether it satisfies any Filter. It also may make other Handler + * specific checks that might prevent a handler from logging the + * LogRecord. + * @param LogRecord to check + */ + virtual void isLoggable(const LogRecord& record) = 0; + + /** + * Sets the Filter that this Handler uses to filter Log Records + *

+ * For each call of publish the Handler will call this Filter (if it + * is non-null) to check if the LogRecord should be published or + * discarded. + * @param Filter derived instance + */ + virtual void setFilter(const Filter* filter) = 0; + + /** + * Gets the Filter that this Handler uses to filter Log Records + * @param Filter derived instance + */ + virtual const Filter* getFilter(void) = 0; + + /** + * Set the log level specifying which message levels will be logged + * by this Handler. + *

+ * The intention is to allow developers to turn on voluminous logging, + * but to limit the messages that are sent to certain Handlers. + * @param Level enumeration value + */ + virtual void setLevel(Level value) = 0; + + /** + * Get the log level specifying which message levels will be logged + * by this Handler. + * @param Level enumeration value + */ + virtual Level getLevel(void) = 0; + + /** + * Sets the Formatter used by this Handler + *

+ * Some Handlers may not use Formatters, in which case the + * Formatter will be remembered, but not used. + * @param Filter derived instance + */ + virtual void setFormatter(const Formatter* formatter) = 0; + + /** + * Gets the Formatter used by this Handler + * @param Filter derived instance + */ + virtual const Formatter* getFormatter(void) = 0; + + }; + +}} + +#endif /*_ACTIVEMQ_LOGGER_HANDLER_H_*/ diff --git a/activemq-cpp/src/main/activemq/logger/LogManager.cpp b/activemq-cpp/src/main/activemq/logger/LogManager.cpp new file mode 100644 index 0000000000..cfdefc53d4 --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/LogManager.cpp @@ -0,0 +1,129 @@ +/* +* Copyright 2006 The Apache Software Foundation or its licensors, as +* applicable. +* +* 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 "LogManager.h" + +#include +#include + +#include + +using namespace activemq; +using namespace activemq::logger; +using namespace activemq::util; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +concurrent::Mutex LogManager::mutex; +LogManager* LogManager::instance = NULL; +unsigned int LogManager::refCount = 0; + +//////////////////////////////////////////////////////////////////////////////// +LogManager::~LogManager( void ) +{ + // TODO - Delete all the loggers. +} + +//////////////////////////////////////////////////////////////////////////////// +void LogManager::setProperties( const Properties* properties ) +{ + // Copy the properties + this->properties.copy(properties); + + // Update the configuration of the loggers. + // TODO +} + +//////////////////////////////////////////////////////////////////////////////// +void LogManager::addPropertyChangeListener( + PropertyChangeListener* listener ) +{ + if(find(listeners.begin(), listeners.end(), listener) == listeners.end()) + { + listeners.push_back(listener); + } +} + +//////////////////////////////////////////////////////////////////////////////// +void LogManager::removePropertyChangeListener( + PropertyChangeListener* listener ) +{ + listeners.remove(listener); +} + +//////////////////////////////////////////////////////////////////////////////// +Logger* LogManager::getLogger( const std::string& name ) +{ + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +int LogManager::getLoggerNames( const std::vector& names ) +{ + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +LogManager* LogManager::getInstance( void ) +{ + synchronized( &mutex ) + { + if( instance == NULL ) + { + instance = new LogManager(); + } + + refCount++; + + return instance; + } + + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +void LogManager::returnInstance( void ) +{ + synchronized( &mutex ) + { + if( refCount == 0 ) + { + return ; + } + + refCount--; + + if( refCount == 0 ) + { + delete instance; + instance = NULL; + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +void LogManager::destroy( void ) +{ + if( instance != NULL ) + { + synchronized( &mutex ) + { + delete instance; + instance = NULL; + refCount = 0; + } + } +} diff --git a/activemq-cpp/src/main/activemq/logger/LogManager.h b/activemq-cpp/src/main/activemq/logger/LogManager.h new file mode 100644 index 0000000000..ae0a3d44a2 --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/LogManager.h @@ -0,0 +1,252 @@ +/* +* Copyright 2006 The Apache Software Foundation or its licensors, as +* applicable. +* +* 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 _ACTIVEMQ_LOGGER_LOGMANAGER_H_ +#define _ACTIVEMQ_LOGGER_LOGMANAGER_H_ + +#include +#include +#include +#include + +#include +#include + +namespace activemq{ +namespace logger{ + + class Logger; + class PropertyChangeListener; + + /** + * There is a single global LogManager object that is used to maintain + * a set of shared state about Loggers and log services. + * + * This LogManager object: + * + * * Manages a hierarchical namespace of Logger objects. All named + * Loggers are stored in this namespace. + * * Manages a set of logging control properties. These are simple + * key-value pairs that can be used by Handlers and other logging + * objects to configure themselves. + * + * The global LogManager object can be retrieved using + * LogManager.getLogManager(). The LogManager object is created during + * class initialization and cannot subsequently be changed. + * + * ***TODO**** + * By default, the LogManager reads its initial configuration from a + * properties file "lib/logging.properties" in the JRE directory. If + * you edit that property file you can change the default logging + * configuration for all uses of that JRE. + * + * In addition, the LogManager uses two optional system properties that + * allow more control over reading the initial configuration: + * + * * "decaf.logger.config.class" + * * "decaf.logger.config.file" + * + * These two properties may be set via the Preferences API, or as + * command line property definitions to the "java" command, or as + * system property definitions passed to JNI_CreateJavaVM. + * + * If the "java.util.logging.config.class" property is set, then the + * property value is treated as a class name. The given class will be + * loaded, an object will be instantiated, and that object's constructor + * is responsible for reading in the initial configuration. (That object + * may use other system properties to control its configuration.) The + * alternate configuration class can use readConfiguration(InputStream) + * to define properties in the LogManager. + * + * If "java.util.logging.config.class" property is not set, then the + * "java.util.logging.config.file" system property can be used to specify + * a properties file (in java.util.Properties format). The initial + * logging configuration will be read from this file. + * + * If neither of these properties is defined then, as described above, + * the LogManager will read its initial configuration from a properties + * file "lib/logging.properties" in the JRE directory. + * + * The properties for loggers and Handlers will have names starting with + * the dot-separated name for the handler or logger. + * ***TODO**** + * + * The global logging properties may include: + * + * * A property "handlers". This defines a whitespace separated + * list of class names for handler classes to load and register as + * handlers on the root Logger (the Logger named ""). Each class + * name must be for a Handler class which has a default constructor. + * Note that these Handlers may be created lazily, when they are + * first used. + * * A property ".handlers". This defines a whitespace or + * comma separated list of class names for handlers classes to load + * and register as handlers to the specified logger. Each class name + * must be for a Handler class which has a default constructor. Note + * that these Handlers may be created lazily, when they are first + * used. + * * A property ".useParentHandlers". This defines a boolean + * value. By default every logger calls its parent in addition to + * handling the logging message itself, this often result in + * messages being handled by the root logger as well. When setting + * this property to false a Handler needs to be configured for this + * logger otherwise no logging messages are delivered. + * * A property "config". This property is intended to allow arbitrary + * configuration code to be run. The property defines a whitespace + * separated list of class names. A new instance will be created for + * each named class. The default constructor of each class may + * execute arbitrary code to update the logging configuration, such + * as setting logger levels, adding handlers, adding filters, etc. + * + * Loggers are organized into a naming hierarchy based on their dot + * separated names. Thus "a.b.c" is a child of "a.b", but "a.b1" and + * a.b2" are peers. + * + * All properties whose names end with ".level" are assumed to define + * log levels for Loggers. Thus "foo.level" defines a log level for + * the logger called "foo" and (recursively) for any of its children + * in the naming hierarchy. Log Levels are applied in the order they + * are defined in the properties file. Thus level settings for child + * nodes in the tree should come after settings for their parents. The + * property name ".level" can be used to set the level for the root of + * the tree. + * + * All methods on the LogManager object are multi-thread safe. + */ + class LogManager + { + private: + + // Change listener on this class's Properties + std::list listeners; + + // Properties of the Log Manager + util::SimpleProperties properties; + + public: + + /** + * Destructor + */ + virtual ~LogManager(); + + /** + * Sets the Properties this LogManager should use to configure + * its loggers. Once set a properties change event is fired. + * @param Properties Pointer to read the configuration from + */ + virtual void setProperties( const util::Properties* properties ); + + /** + * Gets a reference to the Logging Properties used by this + * logger. + * @returns The Logger Properties Pointer + */ + virtual const util::Properties& getProperties( void ) const { + return properties; + } + + /** + * Gets the value of a named property of this LogManager + * @param Name of the Property to retrieve + * @return the value of the property + */ + virtual std::string getProperty( const std::string& name ) { + return properties.getProperty( name ); + } + + /** + * Adds a change listener for LogManager Properties, adding the same + * instance of a change event listener does nothing. + * @param PropertyChangeListener + */ + virtual void addPropertyChangeListener( + PropertyChangeListener* listener ); + + /** + * Removes a properties change listener from the LogManager. + */ + virtual void removePropertyChangeListener( + PropertyChangeListener* listener ); + + /** + * Retrieves or creates a new Logger using the name specified + * a new logger inherits the configuration of the logger's + * parent if there is no configuration data for the logger. + * @param The name of the Logger. + */ + virtual Logger* getLogger( const std::string& name ); + + /** + * Gets a list of known Logger Names from this Manager + * @param STL Vector to hold string logger names + * @return count of how many loggers were inserted + */ + virtual int getLoggerNames( const std::vector& names ); + + public: // Static Singleton Methods. + + /** + * Get the singleton instance + * @return Pointer to an instance of the Log Manager + */ + static LogManager* getInstance( void ); + + /** + * Returns a Checked out instance of this Manager + */ + static void returnInstance( void ); + + /** + * Forcefully Delete the Instance of this LogManager + * even if there are outstanding references. + */ + static void destroy( void ); + + protected: + + /** + * Constructor, hidden to protect against direct instantiation + */ + LogManager( void ) + {} + + /** + * Copy Constructo + */ + LogManager( const LogManager& manager ); + + /** + * Assignment operator + */ + void operator=( const LogManager& manager ); + + private: + + // Static mutex to protect the singleton methods + static concurrent::Mutex mutex; + + // Static pointer to the one and only instance. + static LogManager* instance; + + // Static counter for number of outstanding references + static unsigned int refCount; + + }; + +}} + +#endif /*_ACTIVEMQ_LOGGER_LOGMANAGER_H_*/ diff --git a/activemq-cpp/src/main/activemq/logger/LogRecord.h b/activemq-cpp/src/main/activemq/logger/LogRecord.h new file mode 100644 index 0000000000..02cac6c3ff --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/LogRecord.h @@ -0,0 +1,180 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_LOGGER_LOGRECORD_H_ +#define _ACTIVEMQ_LOGGER_LOGRECORD_H_ + +#include + +#include + +namespace activemq{ +namespace logger{ + + class LogRecord + { + private: + + // Level of this Record + Level level; + + // Name of the source Logger + std::string loggerName; + + // Name of the File that originated the Log + std::string sourceFile; + + // Line in the source file where log occured + unsigned long sourceLine; + + // The message to Log. + std::string message; + + // The function Name where the log occured + std::string functionName; + + // Time in Mills since UTC that this Record was logged + unsigned long timeStamp; + + // Thread Id of the Thread that logged this Record + unsigned long threadId; + + public: + + /** + * Constructor + */ + LogRecord() {} + + /** + * Destructor + */ + virtual ~LogRecord() {} + + /** + * Get Level of this log record + * @return Level enumeration value. + */ + Level getLevel(void) const { return level; }; + + /** + * Set the Level of this Log Record + * @param Level Enumeration Value + */ + void setLevel(Level value) { level = value; }; + + /** + * Gets the Source Logger's Name + * @return the source loggers name + */ + const std::string& getLoggerName(void) const { return loggerName; }; + + /** + * Sets the Source Logger's Name + * @param the source loggers name + */ + void setLoggerName(const std::string& loggerName) { + this->loggerName = loggerName; + }; + + /** + * Gets the Source Log File name + * @return the source loggers name + */ + const std::string& getSourceFile(void) const { return sourceFile; }; + + /** + * Sets the Source Log File Name + * @param the source loggers name + */ + void setSourceFile(const std::string& loggerName) { + this->sourceFile = sourceFile; + }; + + /** + * Gets the Source Log line number + * @return the source loggers line number + */ + unsigned long getSourceLine(void) const { return sourceLine; }; + + /** + * Sets the Source Log line number + * @param the source logger's line number + */ + void setSourceLine(long sourceLine) { + this->sourceLine = sourceLine; + }; + + /** + * Gets the Message to be Logged + * @return the source logger's message + */ + const std::string& getMessage(void) const { return message; }; + + /** + * Sets the Message to be Logged + * @param the source loggers message + */ + void setMessage(const std::string& message) { + this->message = message; + }; + + /** + * Gets the name of the function where this log was logged + * @return the source logger's message + */ + const std::string& getSourceFunction(void) const { return functionName; }; + + /** + * Sets the name of the function where this log was logged + * @param the source loggers message + */ + void setSourceFunction(const std::string& functionName) { + this->functionName = functionName; + }; + + /** + * Gets the time in mills that this message was logged. + * @return UTC time in milliseconds + */ + unsigned long getTimestamp(void) const { return timeStamp; }; + + /** + * Sets the time in mills that this message was logged. + * @param UTC Time in Milliseconds. + */ + void setTimestamp(long timeStamp) { + this->timeStamp = timeStamp; + }; + + /** + * Gets the Thread Id where this Log was created + * @return the source loggers line number + */ + unsigned long getTreadId(void) const { return threadId; }; + + /** + * Sets the Thread Id where this Log was created + * @param the source logger's line number + */ + void setTreadId(long threadId) { + this->threadId = threadId; + }; + }; + +}} + +#endif /*_ACTIVEMQ_LOGGER_LOGRECORD_H_*/ diff --git a/activemq-cpp/src/main/activemq/logger/LogWriter.cpp b/activemq-cpp/src/main/activemq/logger/LogWriter.cpp new file mode 100644 index 0000000000..32fd33d9ed --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/LogWriter.cpp @@ -0,0 +1,72 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "LogWriter.h" + +#include +#include +#include +#include + +using namespace activemq; +using namespace activemq::logger; +using namespace activemq::concurrent; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +concurrent::Mutex LogWriter::mutex; + +//////////////////////////////////////////////////////////////////////////////// +LogWriter::LogWriter(void) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +LogWriter::~LogWriter(void) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +void LogWriter::log(const std::string& file, + const int line, + const std::string& prefix, + const std::string& message) +{ + synchronized(&mutex) + { + cout << prefix << " " + << message << " - tid: " << Thread::getId() << endl; + } +} + +//////////////////////////////////////////////////////////////////////////////// +void LogWriter::log(const std::string& message) +{ + synchronized(&mutex) + { + cout << message << " - tid: " << Thread::getId() << endl; + } +} + +//////////////////////////////////////////////////////////////////////////////// +LogWriter& LogWriter::getInstance(void) +{ + // This one instance + static LogWriter instance; + + return instance; +} + diff --git a/activemq-cpp/src/main/activemq/logger/LogWriter.h b/activemq-cpp/src/main/activemq/logger/LogWriter.h new file mode 100644 index 0000000000..fe4e739691 --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/LogWriter.h @@ -0,0 +1,81 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_LOGGER_LOGWRITER_H_ +#define _ACTIVEMQ_LOGGER_LOGWRITER_H_ + +//#pragma comment(linker,"/include:activemq::logger::LogWriter::mutex") + +#include + +namespace activemq{ +namespace logger{ + + class LogWriter + { + public: + + /** + * Constructor + */ + LogWriter(void); + + /** + * Destructor + */ + virtual ~LogWriter(void); + + /** + * Writes a message to the output destination + */ + virtual void log(const std::string& file, + const int line, + const std::string& prefix, + const std::string& message); + + /** + * Writes a message to the output destination + */ + virtual void log(const std::string& message); + + public: // Static methods + + /** + * Get the singleton instance + */ + static LogWriter& getInstance(void); + + /** + * Returns a Checked out instance of this Writer + */ + static void returnInstance(void); + + /** + * Forcefully Delete the Instance of this LogWriter + * even if there are outstanding references. + */ + static void destroy(void); + + private: + + // Syncronization mutex + static concurrent::Mutex mutex; + + }; + +}} + +#endif /*_ACTIVEMQ_LOGGER_LOGWRITER_H_*/ diff --git a/activemq-cpp/src/main/activemq/logger/Logger.cpp b/activemq-cpp/src/main/activemq/logger/Logger.cpp new file mode 100644 index 0000000000..49501d65c5 --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/Logger.cpp @@ -0,0 +1,169 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "Logger.h" + +#include +#include + +using namespace std; +using namespace activemq; +using namespace activemq::logger; +using namespace activemq::exceptions; + +//////////////////////////////////////////////////////////////////////////////// +Logger::Logger(const std::string& name, Logger* parent) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +Logger::~Logger(void) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +void Logger::addHandler(Handler* handler) throw ( IllegalArgumentException ) +{ + if(handler == NULL) + { + IllegalArgumentException( + __FILE__, __LINE__, + "Logger::addHandler - HAndler cannot be null"); + } + + if(find(handlers.begin(), handlers.end(), handler) != handlers.end()) + { + handlers.push_back(handler); + } +} + + +//////////////////////////////////////////////////////////////////////////////// +void Logger::removeHandler(Handler* handler) +{ + list::iterator itr = + find(handlers.begin(), handlers.end(), handler); + + if(itr != handlers.end()) + { + delete *itr; + handlers.erase(itr); + } +} + +//////////////////////////////////////////////////////////////////////////////// +void Logger::setFilter(Filter* filter) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +bool Logger::isLoggable(Level level) const +{ + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +void Logger::entry(const std::string& blockName, + const std::string& file, + const int line) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +void Logger::exit(const std::string& blockName, + const std::string& file, + const int line) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +void Logger::debug(const std::string& file, + const int line, + const std::string fnctionName, + const std::string& message) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +void Logger::info(const std::string& file, + const int line, + const std::string fnctionName, + const std::string& message) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +void Logger::error(const std::string& file, + const int line, + const std::string fnctionName, + const std::string& message) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +void Logger::warn(const std::string& file, + const int line, + const std::string fnctionName, + const std::string& message) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +void Logger::fatal(const std::string& file, + const int line, + const std::string fnctionName, + const std::string& message) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +void Logger::log(Level level, const std::string& message) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +void Logger::log(Level level, + const std::string& file, + const int line, + const std::string& message, + cms::CMSException& ex) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +void Logger::log(Level level, + const std::string& file, + const int line, + const std::string& message, ...) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +void Logger::log(LogRecord& record) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +Logger* Logger::getLogger(const std::string& name) +{ + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +Logger* Logger::getAnonymousLogger(void) +{ + return NULL; +} diff --git a/activemq-cpp/src/main/activemq/logger/Logger.h b/activemq-cpp/src/main/activemq/logger/Logger.h new file mode 100644 index 0000000000..0b4a63ecd3 --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/Logger.h @@ -0,0 +1,425 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_LOGGER_LOGGER_H_ +#define _ACTIVEMQ_LOGGER_LOGGER_H_ + +#include +#include +#include + +#include +#include +#include + +namespace activemq{ +namespace logger{ + + class Handler; + class Filter; + + class Logger + { + private: + + // The name of this Logger + std::string name; + + // The Parent of this Logger + Logger* parent; + + // list of Handlers owned by this logger + std::list handlers; + + // Filter used by this Logger + Filter* filter; + + // The Log Level of this Logger + Level level; + + // Using Parent Handlers? + bool useParentHandlers; + + public: + + /** + * Creates a new instance of the Logger with the given name + * and assign it the given parent logger. + *

+ * The logger will be initially configured with a null Level + * and with useParentHandlers true. + * @param name - A name for the logger. This should be a + * dot-separated name and should normally be based on the package + * name or class name of the subsystem, such as java.net or + * javax.swing. It may be null for anonymous Loggers. + */ + Logger(const std::string& name, Logger* parent); + + /** + * Destructor + */ + virtual ~Logger(void); + + /** + * Gets the name of this Logger + * + * @return logger name + */ + virtual const std::string& getName(void) const { + return name; + } + + /** + * Add a log Handler to receive logging messages. + *

+ * By default, Loggers also send their output to their parent logger. + * Typically the root Logger is configured with a set of Handlers + * that essentially act as default handlers for all loggers. + * + * @param A Logging Handler + * #throws IllegalArgumentException + */ + virtual void addHandler(Handler* handler) + throw ( exceptions::IllegalArgumentException ); + + /** + * Removes the specified Handler and destroys it + *

+ * Returns silently if the given Handler is not found. + * + * @param The Handler to remove + */ + virtual void removeHandler(Handler* handler); + + /** + * Gets a vector containing all the handlers that this class + * has been assigned to use. + */ + virtual const std::list& getHandlers(void) const; + + /** + * Set a filter to control output on this Logger. + *

+ * After passing the initial "level" check, the Logger will call + * this Filter to check if a log record should really be published. + *

+ * The caller releases ownership of this filter to this logger + * + * @param Filter to use, can be null + */ + virtual void setFilter(Filter* filter); + + /** + * Gets the Filter object that this class is using. + * @return the Filter in use, can be null + */ + virtual const Filter* getFilter(void) const { + return filter; + } + + /** + * Get the log Level that has been specified for this Logger. The + * result may be the Null level, which means that this logger's + * effective level will be inherited from its parent. + */ + virtual Level getLevel(void) const { + return level; + } + + /** + * Set the log level specifying which message levels will be logged + * by this logger. Message levels lower than this value will be + * discarded. The level value Level.OFF can be used to turn off + * logging. + *

+ * If the new level is the Null Level, it means that this node + * should inherit its level from its nearest ancestor with a + * specific (non-null) level value. + * + * @param new Level value + */ + virtual void setLevel(Level level) { + this->level = level; + } + + /** + * Discover whether or not this logger is sending its output to + * its parent logger. + * + * @return true if using Parent Handlers + */ + virtual bool getUseParentHandlers(void) const { + return useParentHandlers; + } + + /** + * pecify whether or not this logger should send its output to it's + * parent Logger. This means that any LogRecords will also be + * written to the parent's Handlers, and potentially to its parent, + * recursively up the namespace. + * + * @param True is output is to be writen to the parent + */ + virtual void setUseParentHandlers(bool value) { + this->useParentHandlers = value; + } + + /** + * Logs an Block Enter message + *

+ * This is a convenience method that is used to tag a block enter, a + * log record with the class name function name and the string + * Entering is logged at the DEBUG log level. + * @param source block name + * @param source file name + * @param source line name + */ + virtual void entry(const std::string& blockName, + const std::string& file, + const int line); + + /** + * Logs an Block Exit message + *

+ * This is a convenience method that is used to tag a block exit, a + * log record with the class name function name and the string + * Exiting is logged at the DEBUG log level. + * @param source block name + * @param source file name + * @param source line name + */ + virtual void exit(const std::string& blockName, + const std::string& file, + const int line); + + /** + * Log a Debug Level Log + *

+ * If the logger is currently enabled for the DEBUG message level + * then the given message is forwarded to all the registered output + * Handler objects. + * + * @param file name where the log was generated + * @param line number where the log was generated + * @param name of the function that logged this + * @param the message to log + */ + virtual void debug(const std::string& file, + const int line, + const std::string fnctionName, + const std::string& message); + + /** + * Log a info Level Log + *

+ * If the logger is currently enabled for the info message level + * then the given message is forwarded to all the registered output + * Handler objects. + * + * @param file name where the log was generated + * @param line number where the log was generated + * @param name of the function that logged this + * @param the message to log + */ + virtual void info(const std::string& file, + const int line, + const std::string fnctionName, + const std::string& message); + + /** + * Log a warn Level Log + *

+ * If the logger is currently enabled for the warn message level + * then the given message is forwarded to all the registered output + * Handler objects. + * + * @param file name where the log was generated + * @param line number where the log was generated + * @param name of the function that logged this + * @param the message to log + */ + virtual void warn(const std::string& file, + const int line, + const std::string fnctionName, + const std::string& message); + + /** + * Log a error Level Log + *

+ * If the logger is currently enabled for the error message level + * then the given message is forwarded to all the registered output + * Handler objects. + * + * @param file name where the log was generated + * @param line number where the log was generated + * @param name of the function that logged this + * @param the message to log + */ + virtual void error(const std::string& file, + const int line, + const std::string fnctionName, + const std::string& message); + + /** + * Log a fatal Level Log + *

+ * If the logger is currently enabled for the fatal message level + * then the given message is forwarded to all the registered output + * Handler objects. + * + * @param file name where the log was generated + * @param line number where the log was generated + * @param name of the function that logged this + * @param the message to log + */ + virtual void fatal(const std::string& file, + const int line, + const std::string fnctionName, + const std::string& message); + + /** + * Log a Throw Message + *

+ * If the logger is currently enabled for the Throwing message level + * then the given message is forwarded to all the registered output + * Handler objects. + * + * @param file name where the log was generated + * @param line number where the log was generated + * @param name of the function that logged this + * @param the message to log + */ + virtual void throwing(const std::string& file, + const int line, + const std::string fnctionName, + const std::string& message); + + /** + * Check if a message of the given level would actually be logged + * by this logger. This check is based on the Loggers effective + * level, which may be inherited from its parent. + * + * @param level - a message logging level + * returns true if the given message level is currently being logged. + */ + virtual bool isLoggable(Level level) const; + + /** + * Log a LogRecord. + * + * All the other logging methods in this class call through this + * method to actually perform any logging. Subclasses can override + * this single method to capture all log activity. + * + * @param record - the LogRecord to be published + */ + virtual void log(LogRecord& record); + + /** + * Log a message, with no arguments. + *

+ * If the logger is currently enabled for the given message level + * then the given message is forwarded to all the registered output + * Handler objects + * + * @param the Level to log at + * @param the message to log + */ + virtual void log(Level level, const std::string& message); + + /** + * Log a message, with the list of params that is formatted into + * the message string. + *

+ * If the logger is currently enabled for the given message level + * then the given message is forwarded to all the registered output + * Handler objects + * + * @param the Level to log at + * @param the message to log + * @param variable length arguement to format the message string. + */ + virtual void log(Level level, + const std::string& file, + const int line, + const std::string& message, ...); + + /** + * Log a message, with associated Throwable information. + * + * If the logger is currently enabled for the given message level + * then the given arguments are stored in a LogRecord which is + * forwarded to all registered output handlers. + * + * Note that the thrown argument is stored in the LogRecord thrown + * property, rather than the LogRecord parameters property. Thus is + * it processed specially by output Formatters and is not treated + * as a formatting parameter to the LogRecord message property. + * + * @param the Level to log at + * @param File that the message was logged in + * @param line number where the message was logged at. + * @param Exception to log + */ + virtual void log(Level level, + const std::string& file, + const int line, + const std::string& message, + cms::CMSException& ex); + + public: + + /** + * Creates an anonymous logger + *

+ * The newly created Logger is not registered in the LogManager + * namespace. There will be no access checks on updates to the + * logger. + * Even although the new logger is anonymous, it is configured to + * have the root logger ("") as its parent. This means that by + * default it inherits its effective level and handlers from the + * root logger. + *

+ * The caller is responsible for destroying the returned logger. + * + * @return Newly created anonymous logger + */ + static Logger* getAnonymousLogger(void); + + /** + * Find or create a logger for a named subsystem. If a logger has + * already been created with the given name it is returned. + * Otherwise a new logger is created. + *

+ * If a new logger is created its log level will be configured based + * on the LogManager and it will configured to also send logging + * output to its parent loggers Handlers. It will be registered in + * the LogManager global namespace. + * + * @param name - A name for the logger. This should be a + * dot-separated name and should normally be based on the package + * name or class name of the subsystem, such as cms or + * activemq.core.ActiveMQConnection + * + * @return a suitable logger. + */ + static Logger* getLogger(const std::string& name); + + }; + +}} + +#endif /*_ACTIVEMQ_LOGGER_LOGGER_H_*/ diff --git a/activemq-cpp/src/main/activemq/logger/LoggerCommon.h b/activemq-cpp/src/main/activemq/logger/LoggerCommon.h new file mode 100644 index 0000000000..314f84ba4e --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/LoggerCommon.h @@ -0,0 +1,45 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_LOGGER_LOGGERCOMMON_H_ +#define _ACTIVEMQ_LOGGER_LOGGERCOMMON_H_ + +namespace activemq{ +namespace logger{ + +#ifdef DEBUG +#undef DEBUG +#endif + + /** + * Defines an enumeration for logging levels + */ + enum Level + { + Off, + Null, + Markblock, + Debug, + Info, + Warn, + Error, + Fatal, + Throwing + }; + +}} + +#endif /*_ACTIVEMQ_LOGGER_LOGGERCOMMON_H_ */ diff --git a/activemq-cpp/src/main/activemq/logger/LoggerDefines.h b/activemq-cpp/src/main/activemq/logger/LoggerDefines.h new file mode 100644 index 0000000000..f857e3b2bc --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/LoggerDefines.h @@ -0,0 +1,55 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_LOGGER_LOGGERDEFINES_H_ +#define _ACTIVEMQ_LOGGER_LOGGERDEFINES_H_ + +#include +#include + +#define LOGCMS_DECLARE(loggerName) \ + static activemq::logger::SimpleLogger loggerName; + +#define LOGCMS_INITIALIZE(loggerName, className, loggerFamily) \ + activemq::logger::SimpleLogger className::loggerName(loggerFamily); + +#define LOGCMS_DECLARE_LOCAL(loggerName) \ + activemq::logger::Logger loggerName; + +#define LOGCMS_DEBUG(logger, message) \ + logger.debug(__FILE__, __LINE__, message); + +#define LOGCMS_DEBUG_1(logger, message, value); \ + { \ + std::ostringstream ostream; \ + ostream << message << value; \ + logger.debug(__FILE__, __LINE__, ostream.str()); \ + } + +#define LOGCMS_INFO(logger, message) \ + logger.info(__FILE__, __LINE__, message); + +#define LOGCMS_ERROR(logger, message) \ + logger.error(__FILE__, __LINE__, message); + +#define LOGCMS_WARN(logger, message) \ + logger.warn(__FILE__, __LINE__, message); + +#define LOGCMS_FATAL(logger, message) \ + logger.fatal(__FILE__, __LINE__, message); + + +#endif diff --git a/activemq-cpp/src/main/activemq/logger/LoggerHierarchy.cpp b/activemq-cpp/src/main/activemq/logger/LoggerHierarchy.cpp new file mode 100644 index 0000000000..eba17b2136 --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/LoggerHierarchy.cpp @@ -0,0 +1,32 @@ +/* +* Copyright 2006 The Apache Software Foundation or its licensors, as +* applicable. +* +* 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 "LoggerHierarchy.h" + +using namespace activemq; +using namespace activemq::logger; + +//////////////////////////////////////////////////////////////////////////////// +LoggerHierarchy::LoggerHierarchy(void) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +LoggerHierarchy::~LoggerHierarchy(void) +{ +} + diff --git a/activemq-cpp/src/main/activemq/logger/LoggerHierarchy.h b/activemq-cpp/src/main/activemq/logger/LoggerHierarchy.h new file mode 100644 index 0000000000..9623b973ea --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/LoggerHierarchy.h @@ -0,0 +1,41 @@ +/* +* Copyright 2006 The Apache Software Foundation or its licensors, as +* applicable. +* +* 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 _ACTIVEMQ_LOGGER_LOGGERHIERARCHY_H_ +#define _ACTIVEMQ_LOGGER_LOGGERHIERARCHY_H_ + +namespace activemq{ +namespace logger{ + + class LoggerHierarchy + { + public: + + /** + * Default Constructor + */ + LoggerHierarchy(void); + + /** + * Destructor + */ + virtual ~LoggerHierarchy(void); + + }; + +}} + +#endif /*_ACTIVEMQ_LOGGER_LOGGERHIERARCHY_H_*/ diff --git a/activemq-cpp/src/main/activemq/logger/MarkBlockLogger.h b/activemq-cpp/src/main/activemq/logger/MarkBlockLogger.h new file mode 100644 index 0000000000..e6b556c881 --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/MarkBlockLogger.h @@ -0,0 +1,71 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_LOGGER_MARKBLOCKLOGGER_H_ +#define _ACTIVEMQ_LOGGER_MARKBLOCKLOGGER_H_ + +#include + +namespace activemq{ +namespace logger{ + + /** + * Defines a class that can be used to mark the entry and exit from + * scoped blocks. + *

+ * Create an instance of this class at the start of a scoped block, + * passing it the logger to use and the name of the block. The block + * entry and exit will be marked using the scope name, logger to the + * logger at the MARKBLOCK log level. + */ + class MarkBlockLogger + { + private: + + // Pointer to the Logger to use for Logging + Logger* logger; + + // Block Name to Log + std::string blockName; + + public: + + /** + * Constructor - Marks Block entry + * @param Logger to use + * @param Block name + */ + MarkBlockLogger(Logger* logger, const std::string& blockName) + { + this->logger = logger; + this->blockName = blockName; + + logger.mark(blockName + " - Entered"); + } + + /** + * Destructor - Marks Block Exit + */ + virtual ~MarkBlockLogger(void) + { + logger->mark(blockName + " - Exited"); + } + + }; + +}} + +#endif /*_ACTIVEMQ_LOGGER_MARKBLOCKLOGGER_H_*/ diff --git a/activemq-cpp/src/main/activemq/logger/PropertiesChangeListener.h b/activemq-cpp/src/main/activemq/logger/PropertiesChangeListener.h new file mode 100644 index 0000000000..201cff9cf4 --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/PropertiesChangeListener.h @@ -0,0 +1,50 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_LOGGER_PROPERTIESCHANGELISTENER_H_ +#define _ACTIVEMQ_LOGGER_PROPERTIESCHANGELISTENER_H_ + +namespace activemq{ +namespace logger{ + + /** + * Defines the interface that classes can use to listen for change + * events on Properties. + */ + class PropertiesChangeListener + { + public: + + /** + * Destructor + */ + virtual ~PropertiesChangeListener() {} + + /** + * Change Event, called when a property is changed + * @param Name of the Property + * @param Old Value of the Property + * @param New Value of the Property + */ + virtual void onPropertyChanged(const std::string& name, + const std::string& oldValue, + const std::string& newValue) = 0; + + }; + +}} + +#endif /*_ACTIVEMQ_LOGGER_PROPERTIESCHANGELISTENER_H_*/ diff --git a/activemq-cpp/src/main/activemq/logger/SimpleFormatter.h b/activemq-cpp/src/main/activemq/logger/SimpleFormatter.h new file mode 100644 index 0000000000..de344f1015 --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/SimpleFormatter.h @@ -0,0 +1,87 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_LOGGER_SIMPLEFORMATTER_H_ +#define _ACTIVEMQ_LOGGER_SIMPLEFORMATTER_H_ + +#include + +namespace activemq{ +namespace logger{ + + /** + * Print a brief summary of the LogRecord in a human readable format. + * The summary will typically be 1 or 2 lines. + */ + class SimpleFormatter : public Formatter + { + public: + + /** + * Constructor + */ + SimpleFormatter(void) {} + + /** + * Destructor + */ + virtual ~SimpleFormatter(void) {} + + /** + * Format the given log record and return the formatted string. + * @param The Log Record to Format + */ + virtual std::string format(const LogRecord& record) const + { + return ""; + } + + /** + * Format the message string from a log record. + * @param The Log Record to Format + */ + virtual std::string formatMessage(const LogRecord& record) const + { + return record.getMessage(); + } + + /** + * Return the header string for a set of formatted records. In the + * default implementation this method should return empty string + * @param the target handler, can be null + * @return empty string + */ + virtual std::string getHead(const Handler* handler) + { + return ""; + } + + /** + * Return the tail string for a set of formatted records. In the + * default implementation this method should return empty string + * @param the target handler, can be null + * @return empty string + */ + virtual std::string getTail(const Handler* handler) + { + return ""; + } + + }; + +}} + +#endif /*_ACTIVEMQ_LOGGER_SIMPLEFORMATTER_H_*/ diff --git a/activemq-cpp/src/main/activemq/logger/SimpleLogger.cpp b/activemq-cpp/src/main/activemq/logger/SimpleLogger.cpp new file mode 100644 index 0000000000..01d4f8303e --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/SimpleLogger.cpp @@ -0,0 +1,88 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "SimpleLogger.h" + +#include +#include + +using namespace activemq; +using namespace activemq::logger; +using namespace activemq::concurrent; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +SimpleLogger::SimpleLogger(const std::string& name) +{ + this->name = name; +} + +//////////////////////////////////////////////////////////////////////////////// +SimpleLogger::~SimpleLogger() +{} + +//////////////////////////////////////////////////////////////////////////////// +void SimpleLogger::mark(const std::string& message) +{ + LogWriter::getInstance().log("", 0, "", message); +} + +//////////////////////////////////////////////////////////////////////////////// +void SimpleLogger::debug(const std::string& file, + const int line, + const std::string& message) +{ + LogWriter::getInstance().log(file, line, "DEBUG:", message); +} + +//////////////////////////////////////////////////////////////////////////////// +void SimpleLogger::info(const std::string& file, + const int line, + const std::string& message) +{ + LogWriter::getInstance().log(file, line, "INFO:", message); +} + +//////////////////////////////////////////////////////////////////////////////// +void SimpleLogger::warn(const std::string& file, + const int line, + const std::string& message) +{ + LogWriter::getInstance().log(file, line, "WARNING:", message); +} + +//////////////////////////////////////////////////////////////////////////////// +void SimpleLogger::error(const std::string& file, + const int line, + const std::string& message) +{ + LogWriter::getInstance().log(file, line, "ERROR:", message); +} + +//////////////////////////////////////////////////////////////////////////////// +void SimpleLogger::fatal(const std::string& file, + const int line, + const std::string& message) +{ + LogWriter::getInstance().log(file, line, "FATAL:", message); +} + +//////////////////////////////////////////////////////////////////////////////// +void SimpleLogger::log(const std::string& message) +{ + LogWriter::getInstance().log(message); +} + diff --git a/activemq-cpp/src/main/activemq/logger/SimpleLogger.h b/activemq-cpp/src/main/activemq/logger/SimpleLogger.h new file mode 100644 index 0000000000..1dcb151679 --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/SimpleLogger.h @@ -0,0 +1,93 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_LOGGER_SIMPLELOGGER_H_ +#define _ACTIVEMQ_LOGGER_SIMPLELOGGER_H_ + +#include + +namespace activemq{ +namespace logger{ + + class SimpleLogger + { + public: + + /** + * Constructor + */ + SimpleLogger( const std::string& name ); + + /** + * Destructor + */ + virtual ~SimpleLogger(); + + /** + * Log a Mark Block Level Log + */ + virtual void mark(const std::string& message); + + /** + * Log a Debug Level Log + */ + virtual void debug(const std::string& file, + const int line, + const std::string& message); + + /** + * Log a Informational Level Log + */ + virtual void info(const std::string& file, + const int line, + const std::string& message); + + /** + * Log a Warning Level Log + */ + virtual void warn(const std::string& file, + const int line, + const std::string& message); + + /** + * Log a Error Level Log + */ + virtual void error(const std::string& file, + const int line, + const std::string& message); + + /** + * Log a Fatal Level Log + */ + virtual void fatal(const std::string& file, + const int line, + const std::string& message); + + /** + * No-frills log. + */ + virtual void log(const std::string& message); + + private: + + // Name of this Logger + std::string name; + + }; + +}} + +#endif /*_ACTIVEMQ_LOGGER_SIMPLELOGGER_H_*/ diff --git a/activemq-cpp/src/main/activemq/logger/StreamHandler.h b/activemq-cpp/src/main/activemq/logger/StreamHandler.h new file mode 100644 index 0000000000..76e9840bcd --- /dev/null +++ b/activemq-cpp/src/main/activemq/logger/StreamHandler.h @@ -0,0 +1,228 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_LOGGER_STREAMHANDLER_H_ +#define _ACTIVEMQ_LOGGER_STREAMHANDLER_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace logger{ + + class StreamHandler : public Handler + { + private: + + // OutputStream to write to + io::OutputStream* stream; + + // Formats this Handlers output + Formatter* formatter; + + // Filter object for Log Filtering + Filter* filter; + + public: + + /** + * Create a StreamHandler, with no current output stream. + */ + StreamHandler(void) + { + stream = NULL; + formatter = NULL; + filter = NULL; + + level = Level::FATAL; // We take everything by default + } + + /** + * Create a StreamHandler, with no current output stream. + */ + StreamHandler(io::OutputStream* stream, Formatter* formatter) + { + this->stream = stream; + this->formatter = formatter; + this->filter = NULL; + + level = Level::Fatal; // We take everything by default + } + + /** + * Destructor + */ + virtual ~StreamHandler(void) + { + try + { + close(); + } + AMQ_CATCH_NOTHROW(exceptions::ActiveMQException) + AMQ_CATCALL_NOTHROW() + } + + /** + * Close the current output stream. + *

+ * The close method will perform a flush and then close the Handler. + * After close has been called this Handler should no longer be used. + * Method calls may either be silently ignored or may throw runtime + * exceptions. + * @throw CMSException + */ + virtual void close(void) throw ( cms::CMSException ) + { + if(stream) + { + stream.flush(); + stream.close(); + } + } + + /** + * Flush the Handler's output, clears any buffers. + */ + virtual void flush(void) + { + if(stream) + { + stream->flush(); + } + } + + /** + * Publish the Log Record to this Handler + * @param The Log Record to Publish + */ + virtual void publish(const LogRecord& record) + { + try + { + if(!stream) + { + throw exceptions::NullPointerException( + __FILE__, __LINE__, + "StreamHandler::publish - Stream not set."); + } + + // Check if we should log this record + if(isLoggable(record)) + { + std::string log = formatter->format(record); + + synchronized(stream) + { + // Write the data to the stream + stream->write(log.c_str(), log.length()); + } + } + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) + } + + /** + * Check if this Handler would actually log a given LogRecord. + *

+ * + * @param LogRecord to check + */ + virtual void isLoggable(const LogRecord& record) + { + if(filter) + { + // Allow for some filtering to occurr + return filter->isLoggable(record)); + } + + // By default we want everything that is greater than or + // equal to the set level of this Handler. + return record.level >= level; + } + + /** + * Sets the Filter that this Handler uses to filter Log Records + * @param Filter derived instance + */ + virtual void setFilter(const Filter* filter){ + this->filter = filter; + } + + /** + * Gets the Filter that this Handler uses to filter Log Records + * @param Filter derived instance + */ + virtual const Filter* getFilter(void){ + return filter; + } + + /** + * Set the log level specifying which message levels will be logged + * by this Handler. + *

+ * The intention is to allow developers to turn on voluminous logging, + * but to limit the messages that are sent to certain Handlers. + * @param Level enumeration value + */ + virtual void setLevel(Level level){ + this->level = level; + } + + /** + * Get the log level specifying which message levels will be logged + * by this Handler. + * @param Level enumeration value + */ + virtual Level getLevel(void){ + return level; + } + + /** + * Sets the Formatter used by this Handler + * @param Filter derived instance + */ + virtual void setFormatter(const Formatter* formatter){ + this->formatter = formatter; + } + + /** + * Gets the Formatter used by this Handler + * @param Filter derived instance + */ + virtual const Formatter* getFormatter(void){ + return formatter; + } + + /** + * Gets the output Stream that this Handler is using + * @return OuputStream pointer + */ + virtual io::OutputStream* getOutputStream(void) const( + return stream; + } + + }; + +}} + +#endif /*_ACTIVEMQ_LOGGER_STREAMHANDLER_H_*/ diff --git a/activemq-cpp/src/main/activemq/network/BufferedSocket.cpp b/activemq-cpp/src/main/activemq/network/BufferedSocket.cpp new file mode 100644 index 0000000000..4b1586a57e --- /dev/null +++ b/activemq-cpp/src/main/activemq/network/BufferedSocket.cpp @@ -0,0 +1,121 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "BufferedSocket.h" + +#include + +using namespace activemq; +using namespace activemq::network; +using namespace activemq::io; +using namespace activemq::exceptions; + +//////////////////////////////////////////////////////////////////////////////// +BufferedSocket::BufferedSocket(Socket* socket, + unsigned int inputBufferSize, + unsigned int outputBufferSize, + bool own) +{ + if(socket == NULL) + { + throw IllegalArgumentException( + __FILE__, __LINE__, + "BufferedSocket::BufferedSocket - Constructed with NULL Socket"); + } + + this->socket = socket; + this->inputBufferSize = inputBufferSize; + this->outputBufferSize = outputBufferSize; + this->own = own; +} + +//////////////////////////////////////////////////////////////////////////////// +BufferedSocket::~BufferedSocket(void) +{ + try + { + if(outputStream) + { + // Ensure all data is written + outputStream->flush(); + } + + // Close the socket + socket->close(); + + // if we own it, delete it. + if(own) + { + delete socket; + } + + // Clean up our streams. + delete inputStream; + delete outputStream; + } + AMQ_CATCH_NOTHROW( ActiveMQException ) + AMQ_CATCHALL_NOTHROW( ) +} + +//////////////////////////////////////////////////////////////////////////////// +void BufferedSocket::connect( const char* host, const int port ) + throw( SocketException ) +{ + try + { + if( socket->isConnected() ) + { + throw SocketException( __FILE__, __LINE__, + "BufferedSocket::connect() - socket already connected" ); + } + + // Connect the socket. + socket->connect( host, port ); + + // Now create the buffered streams that wrap around the socket. + inputStream = new BufferedInputStream( + socket->getInputStream(), inputBufferSize ); + outputStream = new BufferedOutputStream( + socket->getOutputStream(), outputBufferSize ); + } + AMQ_CATCH_RETHROW( SocketException ) + AMQ_CATCH_EXCEPTION_CONVERT( ActiveMQException, SocketException ) + AMQ_CATCHALL_THROW( SocketException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void BufferedSocket::close(void) throw( cms::CMSException ) +{ + try + { + // Ensure all data writen + outputStream->flush(); + + // Close the Socket + socket->close(); + + // Remove old stream, recreate if reconnected + delete inputStream; + delete outputStream; + + inputStream = NULL; + outputStream = NULL; + } + AMQ_CATCH_RETHROW( SocketException ) + AMQ_CATCH_EXCEPTION_CONVERT( ActiveMQException, SocketException ) + AMQ_CATCHALL_THROW( SocketException ) +} diff --git a/activemq-cpp/src/main/activemq/network/BufferedSocket.h b/activemq-cpp/src/main/activemq/network/BufferedSocket.h new file mode 100644 index 0000000000..4e71303ac1 --- /dev/null +++ b/activemq-cpp/src/main/activemq/network/BufferedSocket.h @@ -0,0 +1,220 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_NETWORK_BUFFEREDSOCKET_H_ +#define _ACTIVEMQ_NETWORK_BUFFEREDSOCKET_H_ + +#include +#include +#include +#include + +namespace activemq{ +namespace network{ + + /** + * Buffered Socket class that wraps a Socket derived + * object and provides Buffered input and Output Streams to improce + * the efficiency of the reads and writes. + */ + class BufferedSocket : public Socket + { + private: + + // Socket that this class wraps to provide buffering + Socket* socket; + + // Indicates if the lifetime of the Socket is controlled by this + // class. If true Socket is deleted at destruction. + bool own; + + // Buffered Input stream to wrap the Socket input stream + io::BufferedInputStream* inputStream; + + // Buffered Output stream to wrap the Socket input stream + io::BufferedOutputStream* outputStream; + + // Sizes for the Buffered Streams + unsigned int inputBufferSize; + unsigned int outputBufferSize; + + public: + + /** + * Constructor + */ + BufferedSocket(Socket* socket, + unsigned int inputBufferSize = 1000, + unsigned int outputBufferSize = 1000, + bool own = true); + + /** + * Destructor + */ + virtual ~BufferedSocket(void); + + /** + * Connects to the specified destination. Closes this socket if + * connected to another destination. + * @param host The host of the server to connect to. + * @param port The port of the server to connect to. + * @throws IOException Thrown if a failure occurred in the connect. + */ + virtual void connect( const char* host, const int port ) + throw(SocketException); + + /** + * Closes this object and deallocates the appropriate resources. + * @throws CMSException + */ + virtual void close() throw( cms::CMSException ); + + /** + * Indicates whether or not this socket is connected to a destination. + */ + virtual bool isConnected() const{ + return socket->isConnected(); + } + + /** + * Gets the InputStream for this socket. + * @return The InputStream for this socket. NULL if not connected. + */ + virtual io::InputStream* getInputStream(){ + return inputStream; + } + + /** + * Gets the OutputStream for this socket. + * @return the OutputStream for this socket. NULL if not connected. + */ + virtual io::OutputStream* getOutputStream(){ + return outputStream; + } + + /** + * Gets the linger time. + * @return The linger time in seconds. + * @throws SocketException if the operation fails. + */ + virtual int getSoLinger() const throw(SocketException){ + return socket->getSoLinger(); + } + + /** + * Sets the linger time. + * @param linger The linger time in seconds. If 0, linger is off. + * @throws SocketException if the operation fails. + */ + virtual void setSoLinger( const int linger ) throw(SocketException){ + socket->setSoLinger(linger); + } + + /** + * Gets the keep alive flag. + * @return True if keep alive is enabled. + * @throws SocketException if the operation fails. + */ + virtual bool getKeepAlive() const throw(SocketException){ + return socket->getKeepAlive(); + } + + /** + * Enables/disables the keep alive flag. + * @param keepAlive If true, enables the flag. + * @throws SocketException if the operation fails. + */ + virtual void setKeepAlive( const bool keepAlive ) throw(SocketException){ + socket->setKeepAlive(keepAlive); + } + + /** + * Gets the receive buffer size. + * @return the receive buffer size in bytes. + * @throws SocketException if the operation fails. + */ + virtual int getReceiveBufferSize() const throw(SocketException){ + return socket->getReceiveBufferSize(); + } + + /** + * Sets the recieve buffer size. + * @param size Number of bytes to set the receive buffer to. + * @throws SocketException if the operation fails. + */ + virtual void setReceiveBufferSize( const int size ) throw(SocketException){ + socket->setReceiveBufferSize(size); + } + + /** + * Gets the reuse address flag. + * @return True if the address can be reused. + * @throws SocketException if the operation fails. + */ + virtual bool getReuseAddress() const throw(SocketException){ + return socket->getReuseAddress(); + } + + /** + * Sets the reuse address flag. + * @param reuse If true, sets the flag. + * @throws SocketException if the operation fails. + */ + virtual void setReuseAddress( const bool reuse ) throw(SocketException){ + socket->setReuseAddress(reuse); + } + + /** + * Gets the send buffer size. + * @return the size in bytes of the send buffer. + * @throws SocketException if the operation fails. + */ + virtual int getSendBufferSize() const throw(SocketException){ + return socket->getSendBufferSize(); + } + + /** + * Sets the send buffer size. + * @param size The number of bytes to set the send buffer to. + * @throws SocketException if the operation fails. + */ + virtual void setSendBufferSize( const int size ) throw(SocketException){ + socket->setSendBufferSize(size); + } + + /** + * Gets the timeout for socket operations. + * @return The timeout in milliseconds for socket operations. + * @throws SocketException Thrown if unable to retrieve the information. + */ + virtual int getSoTimeout() const throw(SocketException){ + return socket->getSoTimeout(); + } + + /** + * Sets the timeout for socket operations. + * @param timeout The timeout in milliseconds for socket operations.

+ * @throws SocketException Thrown if unable to set the information. + */ + virtual void setSoTimeout( const int timeout ) throw(SocketException){ + socket->setSoTimeout(timeout); + } + + }; + +}} + +#endif /*_ACTIVEMQ_NETWORK_BUFFEREDSOCKET_H_*/ diff --git a/activemq-cpp/src/main/activemq/network/ServerSocket.cpp b/activemq-cpp/src/main/activemq/network/ServerSocket.cpp new file mode 100644 index 0000000000..2471b65941 --- /dev/null +++ b/activemq-cpp/src/main/activemq/network/ServerSocket.cpp @@ -0,0 +1,204 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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. + */ + +#if defined(unix) && !defined(__CYGWIN__) + #include + #include + #include + #include + #include + #include + #include + extern int errno; +#else + #include + #include + #include + #define stat _stat + #ifdef errno + #undef errno + #endif + int errno; +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "ServerSocket.h" +#include +#include + +using namespace activemq::network; + +#if !defined( unix ) || defined( __CYGWIN__ ) + + // Static socket initializer needed for winsock + + ServerSocket::StaticServerSocketInitializer::StaticServerSocketInitializer () { + socketInitError = NULL; + const WORD version_needed = MAKEWORD(2,2); // lo-order byte: major version + WSAData temp; + if (WSAStartup(version_needed, &temp)){ + clear(); + socketInitError = new SocketException ( __FILE__, __LINE__, + "winsock.dll was not found"); + } + } + ServerSocket::StaticServerSocketInitializer::~StaticServerSocketInitializer () { + clear(); + WSACleanup(); + } + + // Create static instance of the socket initializer. + ServerSocket::StaticServerSocketInitializer + ServerSocket::staticSocketInitializer; + +#endif + + +//////////////////////////////////////////////////////////////////////////////// +ServerSocket::ServerSocket() +{ + socketHandle = Socket::INVALID_SOCKET_HANDLE; + +#if !defined( unix ) || defined( __CYGWIN__ ) + if (ServerSocket::staticSocketInitializer.getSocketInitError() != NULL) { + throw *ServerSocket::staticSocketInitializer.getSocketInitError(); + } +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +ServerSocket::~ServerSocket() +{ + // No shutdown, just close - dont want blocking destructor. + close(); +} + +//////////////////////////////////////////////////////////////////////////////// +void ServerSocket::bind (const char* host, int port) throw (SocketException) +{ + bind (host, port, SOMAXCONN); +} + +//////////////////////////////////////////////////////////////////////////////// +void ServerSocket::bind (const char* host, int port, int backlog) throw (SocketException) +{ + if (isBound()) { + throw SocketException ( __FILE__, __LINE__, + "ServerSocket::bind - Socket already bound" ); + } + + // Create the socket. + socketHandle = ::socket(AF_INET, SOCK_STREAM, 0); + if (socketHandle < 0) { + socketHandle = Socket::INVALID_SOCKET_HANDLE; + throw SocketException( __FILE__, __LINE__, ::strerror( errno )); + } + + // Verify the port value. + if (port <= 0 || port > 65535) { + throw SocketException( __FILE__, __LINE__, + "ServerSocket::bind - Port out of range: %d", port ); + } + + + sockaddr_in bind_addr; + bind_addr.sin_family = AF_INET; + bind_addr.sin_port = htons((short)port); + bind_addr.sin_addr.s_addr = 0; // To be set later down... + memset(&bind_addr.sin_zero, 0, sizeof(bind_addr.sin_zero)); + + // Resolve name + ::addrinfo hints; + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = PF_INET; + struct addrinfo *res_ptr = NULL; + int status = ::getaddrinfo(host, NULL, &hints, &res_ptr); + if( status != 0 || res_ptr == NULL) { + throw SocketException( __FILE__, __LINE__, ::strerror( errno ) ); + } + assert(res_ptr->ai_addr->sa_family == AF_INET); + // Porting: On both 32bit and 64 bit systems that we compile to soo far, sin_addr is a 32 bit value, not an unsigned long. + assert(sizeof(((sockaddr_in*)res_ptr->ai_addr)->sin_addr.s_addr) == 4); + bind_addr.sin_addr.s_addr = ((sockaddr_in*)res_ptr->ai_addr)->sin_addr.s_addr; + freeaddrinfo(res_ptr); + + // Set the socket to reuse the address. + int value = 1; + ::setsockopt(socketHandle, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(int) ); + + status = ::bind(socketHandle, + (sockaddr *)&bind_addr, sizeof(bind_addr)); + + if( status < 0 ){ + close(); + throw SocketException ( __FILE__, __LINE__, + "ServerSocket::bind - %s", ::strerror( errno ) ); + } + status = ::listen(socketHandle, backlog); + if (status < 0) { + close(); + throw SocketException( __FILE__, __LINE__, ::strerror( errno ) ); + } +} + +//////////////////////////////////////////////////////////////////////////////// +void ServerSocket::close() throw (cms::CMSException){ + + if (isBound()) { + + #if defined(unix) && !defined(__CYGWIN__) + ::close(socketHandle); + #else + ::closesocket(socketHandle); + #endif + + socketHandle = Socket::INVALID_SOCKET_HANDLE; + } +} + +//////////////////////////////////////////////////////////////////////////////// +bool ServerSocket::isBound() const { + return this->socketHandle != Socket::INVALID_SOCKET_HANDLE; +} + +//////////////////////////////////////////////////////////////////////////////// +Socket* ServerSocket::accept () throw (SocketException) +{ + struct sockaddr_in temp; + +#if defined( unix ) && !defined( __CYGWIN__ ) + socklen_t temp_len = sizeof (sockaddr_in); +#else + int temp_len = sizeof (sockaddr_in); +#endif + + SocketHandle ss_socket_handle = + ::accept(socketHandle, (struct sockaddr*)&temp, &temp_len); + if (ss_socket_handle < 0) { + throw SocketException( __FILE__, __LINE__, + "ServerSocket::accept- %s", ::strerror( errno ) ); + } + + return new TcpSocket(ss_socket_handle); +} + diff --git a/activemq-cpp/src/main/activemq/network/ServerSocket.h b/activemq-cpp/src/main/activemq/network/ServerSocket.h new file mode 100644 index 0000000000..3fef56b553 --- /dev/null +++ b/activemq-cpp/src/main/activemq/network/ServerSocket.h @@ -0,0 +1,119 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_NETWORK_SERVERSOCKETIMPL_H +#define ACTIVEMQ_NETWORK_SERVERSOCKETIMPL_H + +#include +#include + +namespace activemq{ +namespace network{ + + /** + * A server socket class (for testing purposes). + */ + class ServerSocket + { + public: + + typedef Socket::SocketHandle SocketHandle; + + private: + + SocketHandle socketHandle; + + public: + + /** + * Constructor. + * Creates a non-bound server socket. + */ + ServerSocket(); + + /** + * Destructor. + * Releases socket handle if close() hasn't been called. + */ + virtual ~ServerSocket(); + + public: + + /** + * Bind and listen to given IP/dns and port. + * @param host IP address or host name. + * @param port TCP port between 1..655535 + */ + virtual void bind (const char* host, int port) throw (SocketException); + + /** + * Bind and listen to given IP/dns and port. + * @param host IP address or host name. + * @param port TCP port between 1..655535 + * @param backlog Size of listen backlog. + */ + virtual void bind (const char* host, int port, int backlog) throw (SocketException); + + /** + * Blocks until a client connects to the bound socket. + * @return new socket. Never returns NULL. + */ + virtual Socket* accept () throw (SocketException); + + /** + * Closes the server socket. + */ + virtual void close() throw(cms::CMSException); + + /** + * @return true of the server socket is bound. + */ + virtual bool isBound() const; + + protected: + + #if !defined( unix ) || defined( __CYGWIN__ ) + + // WINDOWS needs initialization of winsock + class StaticServerSocketInitializer { + private: + + SocketException* socketInitError; + + void clear(){ + if( socketInitError != NULL ){ + delete socketInitError; + } + socketInitError = NULL; + } + + public: + SocketException* getSocketInitError () { + return socketInitError; + } + StaticServerSocketInitializer(); + virtual ~StaticServerSocketInitializer (); + + }; + static StaticServerSocketInitializer staticSocketInitializer; + #endif + + }; + +}} + +#endif // ACTIVEMQ_NETWORK_SERVERSOCKETIMPL_H + diff --git a/activemq-cpp/src/main/activemq/network/Socket.h b/activemq-cpp/src/main/activemq/network/Socket.h new file mode 100644 index 0000000000..1614b593ba --- /dev/null +++ b/activemq-cpp/src/main/activemq/network/Socket.h @@ -0,0 +1,170 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_NETWORK_SOCKET_H_ +#define _ACTIVEMQ_NETWORK_SOCKET_H_ + +#include +#include +#include +#include + +#if !defined( unix ) || defined( __CYGWIN__ ) +#include // SOCKET +#endif + +namespace activemq{ +namespace network{ + + class Socket : public cms::Closeable + { + public: + + // Define the SocketHandle type. + #if defined( unix ) && !defined( __CYGWIN__ ) + typedef int SocketHandle; + #else + typedef SOCKET SocketHandle; + #endif + + /** + * Defines a constant for an invalid socket handle. + */ + static const SocketHandle INVALID_SOCKET_HANDLE = (SocketHandle) -1; + + public: + + /** + * Destructor + */ + virtual ~Socket(void) {} + + /** + * Connects to the specified destination. Closes this socket if + * connected to another destination. + * @param host The host of the server to connect to. + * @param port The port of the server to connect to. + * @throws IOException Thrown if a failure occurred in the connect. + */ + virtual void connect( const char* host, const int port ) + throw(SocketException) = 0; + + /** + * Indicates whether or not this socket is connected to a destination. + */ + virtual bool isConnected() const = 0; + + /** + * Gets the InputStream for this socket. + * @return The InputStream for this socket. NULL if not connected. + */ + virtual io::InputStream* getInputStream() = 0; + + /** + * Gets the OutputStream for this socket. + * @return the OutputStream for this socket. NULL if not connected. + */ + virtual io::OutputStream* getOutputStream() = 0; + + /** + * Gets the linger time. + * @return The linger time in seconds. + * @throws SocketException if the operation fails. + */ + virtual int getSoLinger() const throw(SocketException) = 0; + + /** + * Sets the linger time. + * @param linger The linger time in seconds. If 0, linger is off. + * @throws SocketException if the operation fails. + */ + virtual void setSoLinger( const int linger ) throw(SocketException) = 0; + + /** + * Gets the keep alive flag. + * @return True if keep alive is enabled. + * @throws SocketException if the operation fails. + */ + virtual bool getKeepAlive() const throw(SocketException) = 0; + + /** + * Enables/disables the keep alive flag. + * @param keepAlive If true, enables the flag. + * @throws SocketException if the operation fails. + */ + virtual void setKeepAlive( const bool keepAlive ) throw(SocketException) = 0; + + /** + * Gets the receive buffer size. + * @return the receive buffer size in bytes. + * @throws SocketException if the operation fails. + */ + virtual int getReceiveBufferSize() const throw(SocketException) = 0; + + /** + * Sets the recieve buffer size. + * @param size Number of bytes to set the receive buffer to. + * @throws SocketException if the operation fails. + */ + virtual void setReceiveBufferSize( const int size ) throw(SocketException) = 0; + + /** + * Gets the reuse address flag. + * @return True if the address can be reused. + * @throws SocketException if the operation fails. + */ + virtual bool getReuseAddress() const throw(SocketException) = 0; + + /** + * Sets the reuse address flag. + * @param reuse If true, sets the flag. + * @throws SocketException if the operation fails. + */ + virtual void setReuseAddress( const bool reuse ) throw(SocketException) = 0; + + /** + * Gets the send buffer size. + * @return the size in bytes of the send buffer. + * @throws SocketException if the operation fails. + */ + virtual int getSendBufferSize() const throw(SocketException) = 0; + + /** + * Sets the send buffer size. + * @param size The number of bytes to set the send buffer to. + * @throws SocketException if the operation fails. + */ + virtual void setSendBufferSize( const int size ) throw(SocketException) = 0; + + /** + * Gets the timeout for socket operations. + * @return The timeout in milliseconds for socket operations. + * @throws SocketException Thrown if unable to retrieve the information. + */ + virtual int getSoTimeout() const throw(SocketException) = 0; + + /** + * Sets the timeout for socket operations. + * @param timeout The timeout in milliseconds for socket operations.

+ * @throws SocketException Thrown if unable to set the information. + */ + virtual void setSoTimeout( const int timeout ) throw(SocketException) = 0; + + }; + +}} + +#endif /*_ACTIVEMQ_NETWORK_BASESOCKET_H_*/ diff --git a/activemq-cpp/src/main/activemq/network/SocketException.h b/activemq-cpp/src/main/activemq/network/SocketException.h new file mode 100644 index 0000000000..9096ce5d3d --- /dev/null +++ b/activemq-cpp/src/main/activemq/network/SocketException.h @@ -0,0 +1,66 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_NETWORK_SOCKETEXCEPTION_H +#define ACTIVEMQ_NETWORK_SOCKETEXCEPTION_H + +#include + +namespace activemq{ +namespace network{ + + /** + * Exception for errors when manipulating sockets. + */ + class SocketException : public io::IOException + { + public: + + SocketException(){} + SocketException( const ActiveMQException& ex ){ + *(ActiveMQException*)this = ex; + } + SocketException( const SocketException& ex ){ + *(ActiveMQException*)this = ex; + } + SocketException(const char* file, const int lineNumber, + const char* msg, ...) + { + va_list vargs ; + va_start(vargs, msg) ; + buildMessage(msg, vargs) ; + + // Set the first mark for this exception. + setMark( file, lineNumber ); + } + + /** + * Clones this exception. This is useful for cases where you need + * to preserve the type of the original exception as well as the message. + * All subclasses should override. + */ + virtual ActiveMQException* clone() const{ + return new SocketException( *this ); + } + + virtual ~SocketException(){} + } ; + +}} + + +#endif // ACTIVEMQ_NETWORK_SOCKETEXCEPTION_H + diff --git a/activemq-cpp/src/main/activemq/network/SocketFactory.cpp b/activemq-cpp/src/main/activemq/network/SocketFactory.cpp new file mode 100644 index 0000000000..3580e3b49b --- /dev/null +++ b/activemq-cpp/src/main/activemq/network/SocketFactory.cpp @@ -0,0 +1,117 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 +#include +#include +#include + +using namespace std; +using namespace activemq; +using namespace activemq::util; +using namespace activemq::network; +using namespace activemq::exceptions; + +//////////////////////////////////////////////////////////////////////////////// +Socket* SocketFactory::createSocket(const Properties& properties) + throw ( SocketException ) +{ + try + { + const char* uri = properties.getProperty( "uri" ); + if( uri == NULL ) + { + throw SocketException( __FILE__, __LINE__, + "SocketTransport::start() - uri not provided" ); + } + + string dummy = uri; + + // Extract the port. + unsigned int portIx = dummy.find( ':' ); + if( portIx == string::npos ) + { + throw SocketException( __FILE__, __LINE__, + "SocketTransport::start() - uri malformed - port not specified: %s", uri); + } + string host = dummy.substr( 0, portIx ); + string portString = dummy.substr( portIx + 1 ); + int port; + if( sscanf( portString.c_str(), "%d", &port) != 1 ) + { + throw SocketException( __FILE__, __LINE__, + "SocketTransport::start() - unable to extract port from uri: %s", uri); + } + + // Get the read buffer size. + int inputBufferSize = 10000; + dummy = properties.getProperty( "inputBufferSize", "10000" ); + sscanf( dummy.c_str(), "%d", &inputBufferSize ); + + // Get the write buffer size. + int outputBufferSize = 10000; + dummy = properties.getProperty( "outputBufferSize", "10000" ); + sscanf( dummy.c_str(), "%d", &outputBufferSize ); + + // Get the linger flag. + int soLinger = 0; + dummy = properties.getProperty( "soLinger", "0" ); + sscanf( dummy.c_str(), "%d", &soLinger ); + + // Get the keepAlive flag. + bool soKeepAlive = + properties.getProperty( "soKeepAlive", "false" ) == "true"; + + // Get the socket receive buffer size. + int soReceiveBufferSize = 2000000; + dummy = properties.getProperty( "soReceiveBufferSize", "2000000" ); + sscanf( dummy.c_str(), "%d", &soReceiveBufferSize ); + + // Get the socket send buffer size. + int soSendBufferSize = 2000000; + dummy = properties.getProperty( "soSendBufferSize", "2000000" ); + sscanf( dummy.c_str(), "%d", &soSendBufferSize ); + + // Get the socket send buffer size. + int soTimeout = 10000; + dummy = properties.getProperty( "soTimeout", "10000" ); + sscanf( dummy.c_str(), "%d", &soTimeout ); + + // Now that we have all the elements that we wanted - let's do it! + // Create a TCP Socket and then Wrap it in a buffered socket + // so that users get the benefit of buffered reads and writes. + // The buffered socket will own the TcpSocket instance, and will + // clean it up when it is cleaned up. + TcpSocket* tcpSocket = new TcpSocket(); + BufferedSocket* socket = + new BufferedSocket(tcpSocket, inputBufferSize, outputBufferSize); + + // Connect the socket. + socket->connect( host.c_str(), port ); + + // Set the socket options. + socket->setSoLinger( soLinger ); + socket->setKeepAlive( soKeepAlive ); + socket->setReceiveBufferSize( soReceiveBufferSize ); + socket->setSendBufferSize( soSendBufferSize ); + socket->setSoTimeout( soTimeout ); + + return socket; + } + AMQ_CATCH_RETHROW( SocketException ) + AMQ_CATCH_EXCEPTION_CONVERT( ActiveMQException, SocketException ) + AMQ_CATCHALL_THROW( SocketException ) +} diff --git a/activemq-cpp/src/main/activemq/network/SocketFactory.h b/activemq-cpp/src/main/activemq/network/SocketFactory.h new file mode 100644 index 0000000000..f68589e5c9 --- /dev/null +++ b/activemq-cpp/src/main/activemq/network/SocketFactory.h @@ -0,0 +1,68 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_NETWORK_SOCKETFACTORY_H_ +#define _ACTIVEMQ_NETWORK_SOCKETFACTORY_H_ + +#include +#include + +namespace activemq{ +namespace network{ + + class Socket; + + /** + * Socket Factory implementation for use in Creating Sockets + *

+ *

+ * Property Options:

+ * Name Value

+ * -------------------------------------

+ * uri The uri for the transport connection. Must be provided.

+ * inputBufferSize size in bytes of the buffered input stream buffer. Defaults to 10000.

+ * outputBufferSize size in bytes of the buffered output stream buffer. Defaults to 10000.

+ * soLinger linger time for the socket (in microseconds). Defaults to 0.

+ * soKeepAlive keep alive flag for the socket (true/false). Defaults to false.

+ * soReceiveBufferSize The size of the socket receive buffer (in bytes). Defaults to 2MB.

+ * soSendBufferSize The size of the socket send buffer (in bytes). Defaults to 2MB.

+ * soTimeout The timeout of socket IO operations (in microseconds). Defaults to 10000

+ * + * @see Socket + */ + class SocketFactory + { + public: + + /** + * Destructor + */ + virtual ~SocketFactory(); + + /** + * Creates and returns a Socket dervied Object based on the values + * defined in the Properties Object that is passed in. + * @param a IProperties pointer. + * @throws SocketException. + */ + static Socket* createSocket(const util::Properties& properties) + throw ( SocketException ); + + }; + +}} + +#endif /*_ACTIVEMQ_NETWORK_SOCKETFACTORY_H_*/ diff --git a/activemq-cpp/src/main/activemq/network/SocketInputStream.cpp b/activemq-cpp/src/main/activemq/network/SocketInputStream.cpp new file mode 100644 index 0000000000..d7b122844f --- /dev/null +++ b/activemq-cpp/src/main/activemq/network/SocketInputStream.cpp @@ -0,0 +1,175 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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. + */ + +#if defined(unix) && !defined(__CYGWIN__) + #include + #include + #include + extern int errno; +#else + #include +#endif + +#include +#include +#include +#include + +using namespace activemq; +using namespace activemq::network; +using namespace activemq::io; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +SocketInputStream::SocketInputStream( network::Socket::SocketHandle socket ) +{ + this->socket = socket; + debug = false; +} + +//////////////////////////////////////////////////////////////////////////////// +SocketInputStream::~SocketInputStream() +{ +} + +//////////////////////////////////////////////////////////////////////////////// +int SocketInputStream::available() const{ + + +#if defined(unix) && !defined(__CYGWIN__) + + // Poll the socket for input. + pollfd fd; + fd.fd = socket; + fd.events = POLLIN; + fd.revents = POLLIN; + int status = poll( &fd, 1, 1 ); + if( status > 0 ){ + return 1; + } + +#else + + // Poll instantaneously to see if there is data on the socket. + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 100; + + fd_set pollSet; + FD_ZERO( &pollSet ); + FD_SET( 0, &pollSet ); + pollSet.fd_array[0] = socket; + if( ::select( 1, &pollSet, NULL, NULL, &timeout) > 0 ){ + return 1; + } + +#endif + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// +unsigned char SocketInputStream::read() throw (IOException){ + + unsigned char c; + int len = read( &c, 1 ); + if( len != sizeof(c) ){ + throw IOException( __FILE__, __LINE__, + "activemq::io::SocketInputStream::read - failed reading a byte"); + } + + return c; +} + +//////////////////////////////////////////////////////////////////////////////// +int SocketInputStream::read( unsigned char* buffer, const int bufferSize ) throw (IOException){ + + int bytesAvailable = available(); + + while( true ) + { + int len = ::recv(socket, (char*)buffer, bufferSize, 0); + + // Check for typical error conditions. + if( len < 0 ) + { + #if defined(unix) && !defined(__CYGWIN__) + + // If the socket was temporarily unavailable - just try again. + if( errno == EAGAIN ){ + continue; + } + + // Create the error string. + char* errorString = ::strerror(errno); + + #else + + // If the socket was temporarily unavailable - just try again. + int errorCode = ::WSAGetLastError(); + if( errorCode == WSAEWOULDBLOCK ){ + continue; + } + + // Create the error string. + static const int errorStringSize = 512; + char errorString[errorStringSize]; + memset( errorString, 0, errorStringSize ); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, + 0, + errorCode, + 0, + errorString, + errorStringSize - 1, + NULL); + + #endif + + // Otherwise, this was a bad error - throw an exception. + throw IOException( __FILE__, __LINE__, + "activemq::io::SocketInputStream::read - %s", errorString ); + } + + // No error, but no data - check for a broken socket. + if( len == 0 ) + { + // If the poll showed data, but we failed to read any, + // the socket is broken. + if( bytesAvailable > 0 ){ + throw IOException( __FILE__, __LINE__, + "activemq::io::SocketInputStream::read - The connection is broken" ); + } + + // Socket is not broken, just had no data. + return 0; + } + + if( debug ){ + printf("SocketInputStream:read(), numbytes:%d -", len); + for( int ix=0; ix 20 ) + printf("%c", buffer[ix] ); + else + printf("[%d]", buffer[ix] ); + } + printf("\n"); + } + + // Data was read successfully - return the bytes read. + return len; + } +} diff --git a/activemq-cpp/src/main/activemq/network/SocketInputStream.h b/activemq-cpp/src/main/activemq/network/SocketInputStream.h new file mode 100644 index 0000000000..97cd7c7e09 --- /dev/null +++ b/activemq-cpp/src/main/activemq/network/SocketInputStream.h @@ -0,0 +1,144 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_NETWORK_SOCKETINPUTSTREAM_H_ +#define ACTIVEMQ_NETWORK_SOCKETINPUTSTREAM_H_ + +#include +#include +#include + +namespace activemq{ +namespace network{ + + /** + * Input stream for performing reads on a socket. + */ + class SocketInputStream : public io::InputStream + { + private: + + // The socket handle. + Socket::SocketHandle socket; + concurrent::Mutex mutex; + bool debug; + + public: + + /** + * Constructor. + * @param socket the socket handle. + */ + SocketInputStream( Socket::SocketHandle socket ); + + /** + * Destructor. + */ + virtual ~SocketInputStream(); + + virtual void setDebug( const bool debug ){ + this->debug = debug; + } + + /** + * Locks the object. + */ + virtual void lock() throw(exceptions::ActiveMQException){ + mutex.lock(); + } + + /** + * Unlocks the object. + */ + virtual void unlock() throw(exceptions::ActiveMQException){ + mutex.unlock(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. + */ + virtual void wait() throw(exceptions::ActiveMQException){ + mutex.wait(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. This wait will timeout after the specified time + * interval. + * @param time in millisecsonds to wait, or WAIT_INIFINITE + * @throws ActiveMQException + */ + virtual void wait(unsigned long millisecs) + throw(exceptions::ActiveMQException) { + + mutex.wait(millisecs); + } + + /** + * Signals a waiter on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + */ + virtual void notify() throw(exceptions::ActiveMQException){ + mutex.notify(); + } + + /** + * Signals the waiters on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + */ + virtual void notifyAll() throw(exceptions::ActiveMQException){ + mutex.notifyAll(); + } + + /** + * Polls instantaneously to see if data is available on + * the socket. + * @return 1 if data is currently available on the socket, otherwise 0. + */ + virtual int available() const; + + /** + * Reads a single byte from the buffer. + * @return The next byte. + * @throws IOException thrown if an error occurs. + */ + virtual unsigned char read() throw (io::IOException); + + /** + * Reads an array of bytes from the buffer. + * @param buffer (out) the target buffer. + * @param bufferSize the size of the output buffer. + * @return The number of bytes read. + * @throws IOException thrown if an error occurs. + */ + virtual int read( unsigned char* buffer, const int bufferSize ) throw (io::IOException); + + /** + * Close - does nothing. It is the responsibility of the owner + * of the socket object to close it. + */ + virtual void close() throw(cms::CMSException){} + }; + +}} + +#endif /*ACTIVEMQ_NETWORK_SOCKETINPUTSTREAM_H_*/ diff --git a/activemq-cpp/src/main/activemq/network/SocketOutputStream.cpp b/activemq-cpp/src/main/activemq/network/SocketOutputStream.cpp new file mode 100644 index 0000000000..a0dc3959c9 --- /dev/null +++ b/activemq-cpp/src/main/activemq/network/SocketOutputStream.cpp @@ -0,0 +1,91 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "SocketOutputStream.h" + +#if defined( unix ) && !defined( __CYGWIN__ ) + #include + extern int errno; +#else + #include +#endif + +#include +#include + +#if defined( __APPLE__ ) +#define SOCKET_NOSIGNAL SO_NOSIGPIPE +#elif defined( unix ) && !defined( __CYGWIN__ ) && !defined( sun ) +#define SOCKET_NOSIGNAL MSG_NOSIGNAL +#else +#define SOCKET_NOSIGNAL 0 +#endif + +using namespace activemq::network; +using namespace activemq::io; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +SocketOutputStream::SocketOutputStream( Socket::SocketHandle socket ) +{ + this->socket = socket; + this->debug = false; +} + +//////////////////////////////////////////////////////////////////////////////// +SocketOutputStream::~SocketOutputStream(void) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +void SocketOutputStream::write( const unsigned char c ) throw (IOException) +{ + write( &c, 1 ); +} + +//////////////////////////////////////////////////////////////////////////////// +void SocketOutputStream::write( const unsigned char* buffer, const int len ) + throw (IOException) +{ + int remaining = len; + int sendOpts = SOCKET_NOSIGNAL; + + if( debug ){ + printf("SocketOutputStream:write(), numbytes:%d -", len); + for( int ix=0; ix 20 ){ + printf("%c", c ); + } + else printf("[%d]", c ); + } + printf("\n" ); + } + + while( remaining > 0 ) + { + int length = ::send( socket, (const char*)buffer, remaining, sendOpts ); + if( length < 0 ){ + throw IOException( __FILE__, __LINE__, + "activemq::io::SocketOutputStream::write - %s", ::strerror(errno) ); + } + + buffer+=length; + remaining -= length; + } +} + diff --git a/activemq-cpp/src/main/activemq/network/SocketOutputStream.h b/activemq-cpp/src/main/activemq/network/SocketOutputStream.h new file mode 100644 index 0000000000..1e8b917c46 --- /dev/null +++ b/activemq-cpp/src/main/activemq/network/SocketOutputStream.h @@ -0,0 +1,142 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_NETWORK_SOCKETOUTPUTSTREAM_H_ +#define ACTIVEMQ_NETWORK_SOCKETOUTPUTSTREAM_H_ + +#include +#include +#include + +namespace activemq{ +namespace network{ + + /** + * Output stream for performing write operations + * on a socket. + */ + class SocketOutputStream : public io::OutputStream + { + private: + + // The socket. + Socket::SocketHandle socket; + concurrent::Mutex mutex; + bool debug; + + public: + + /** + * Constructor. + * @param socket the socket handle. + */ + SocketOutputStream( Socket::SocketHandle socket ); + + /** + * Destructor. + */ + virtual ~SocketOutputStream(); + + virtual void setDebug( const bool debug ){ + this->debug = debug; + } + + /** + * Locks the object. + */ + virtual void lock() throw(exceptions::ActiveMQException){ + mutex.lock(); + } + + /** + * Unlocks the object. + */ + virtual void unlock() throw(exceptions::ActiveMQException){ + mutex.unlock(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. + */ + virtual void wait() throw(exceptions::ActiveMQException){ + mutex.wait(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. This wait will timeout after the specified time + * interval. + * @param time in millisecsonds to wait, or WAIT_INIFINITE + * @throws ActiveMQException + */ + virtual void wait(unsigned long millisecs) + throw(exceptions::ActiveMQException) { + + mutex.wait(millisecs); + } + + /** + * Signals a waiter on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + */ + virtual void notify() throw(exceptions::ActiveMQException){ + mutex.notify(); + } + + /** + * Signals the waiters on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + */ + virtual void notifyAll() throw(exceptions::ActiveMQException){ + mutex.notifyAll(); + } + + /** + * Writes a single byte to the output stream. + * @param c the byte. + * @throws IOException thrown if an error occurs. + */ + virtual void write( const unsigned char c ) throw (io::IOException); + + /** + * Writes an array of bytes to the output stream. + * @param buffer The array of bytes to write. + * @param len The number of bytes from the buffer to be written. + * @throws IOException thrown if an error occurs. + */ + virtual void write( const unsigned char* buffer, const int len ) throw (io::IOException); + + /** + * Flush - does nothing. + */ + virtual void flush() throw (io::IOException){}; + + /** + * Close - does nothing. It is the responsibility of the owner + * of the socket object to close it. + */ + virtual void close() throw(cms::CMSException){} + }; + +}} + +#endif /*ACTIVEMQ_NETWORK_SOCKETOUTPUTSTREAM_H_*/ diff --git a/activemq-cpp/src/main/activemq/network/TcpSocket.cpp b/activemq-cpp/src/main/activemq/network/TcpSocket.cpp new file mode 100644 index 0000000000..2313822c21 --- /dev/null +++ b/activemq-cpp/src/main/activemq/network/TcpSocket.cpp @@ -0,0 +1,322 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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. + */ + +#if defined(unix) && !defined(__CYGWIN__) + #include + #include + #include + #include + #include + #include + #include + extern int errno; +#else + #include + #include + #include + #define stat _stat +#endif + +#include +#include +#include +#include +#include +#include +#include "TcpSocket.h" +#include "SocketInputStream.h" +#include "SocketOutputStream.h" +#include + +using namespace activemq::network; +using namespace activemq::io; + + +#if !defined( unix ) || defined( __CYGWIN__ ) + + // Static socket initializer needed for winsock + + TcpSocket::StaticSocketInitializer::StaticSocketInitializer () { + socketInitError = NULL; + const WORD version_needed = MAKEWORD(2,2); // lo-order byte: major version + WSAData temp; + if (WSAStartup(version_needed, &temp)){ + clear(); + socketInitError = new SocketException ( __FILE__, __LINE__, + "winsock.dll was not found"); + } + } + TcpSocket::StaticSocketInitializer::~StaticSocketInitializer () { + clear(); + WSACleanup(); + } + + // Create static instance of the socket initializer. + TcpSocket::StaticSocketInitializer TcpSocket::staticSocketInitializer; + +#endif + +//////////////////////////////////////////////////////////////////////////////// +TcpSocket::TcpSocket() { + + socketHandle = INVALID_SOCKET_HANDLE; + inputStream = NULL; + outputStream = NULL; + +#if !defined( unix ) || defined( __CYGWIN__ ) + if (staticSocketInitializer.getSocketInitError() != NULL) { + throw *staticSocketInitializer.getSocketInitError(); + } +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +TcpSocket::TcpSocket(Socket::SocketHandle socketHandle){ + this->socketHandle = socketHandle; + + inputStream = new SocketInputStream( socketHandle ); + outputStream = new SocketOutputStream( socketHandle ); +} + +//////////////////////////////////////////////////////////////////////////////// +TcpSocket::~TcpSocket() +{ + // No shutdown, just close - dont want blocking destructor. + close(); +} + +//////////////////////////////////////////////////////////////////////////////// +InputStream* TcpSocket::getInputStream(){ + return inputStream; +} + +//////////////////////////////////////////////////////////////////////////////// +OutputStream* TcpSocket::getOutputStream(){ + return outputStream; +} + +//////////////////////////////////////////////////////////////////////////////// +void TcpSocket::connect(const char* host, int port) throw (SocketException) +{ + if( isConnected() ) { + throw SocketException( __FILE__, __LINE__, + "Socket::connect - Socket already connected. host: %s, port: %d", host, port ); + } + + // Create the socket. + socketHandle = ::socket(AF_INET, SOCK_STREAM, 0); + if( socketHandle < 0 ) { + socketHandle = INVALID_SOCKET_HANDLE; + throw SocketException( __FILE__, __LINE__, ::strerror( errno ) ); + } + + // Check port value. + if (port <= 0 || port > 65535) { + close(); + throw SocketException ( __FILE__, __LINE__, + "Socket::connect- Port out of range: %d", port ); + } + + sockaddr_in target_addr; + target_addr.sin_family = AF_INET; + target_addr.sin_port = htons((short)port); + target_addr.sin_addr.s_addr = 0; // To be set later down... + memset(&target_addr.sin_zero, 0, sizeof(target_addr.sin_zero)); + + // Resolve name + addrinfo hints; + memset(&hints, 0, sizeof(addrinfo)); + hints.ai_family = PF_INET; + struct addrinfo *res_ptr = NULL; + + int status = ::getaddrinfo(host, NULL, &hints, &res_ptr); + if( status != 0 || res_ptr == NULL){ + throw SocketException( __FILE__, __LINE__, + "Socket::connect - %s", ::strerror( errno ) ); + } + + assert(res_ptr->ai_addr->sa_family == AF_INET); + // Porting: On both 32bit and 64 bit systems that we compile to soo far, sin_addr + // is a 32 bit value, not an unsigned long. + assert(sizeof(((sockaddr_in*)res_ptr->ai_addr)->sin_addr.s_addr) == 4); + target_addr.sin_addr.s_addr = ((sockaddr_in*)res_ptr->ai_addr)->sin_addr.s_addr; + freeaddrinfo(res_ptr); + + // Attempt the connection to the server. + status = ::connect(socketHandle, + (const sockaddr *)&target_addr, + sizeof(target_addr)); + + if( status < 0 ){ + close(); + throw SocketException( __FILE__, __LINE__, + "Socket::connect - %s", ::strerror( errno ) ); + } + + // Create an input/output stream for this socket. + inputStream = new SocketInputStream( socketHandle ); + outputStream = new SocketOutputStream( socketHandle ); +} + +//////////////////////////////////////////////////////////////////////////////// +void TcpSocket::close() throw( cms::CMSException ) +{ + // Destroy the input stream. + if( inputStream != NULL ){ + delete inputStream; + inputStream = NULL; + } + + // Destroy the output stream. + if( outputStream != NULL ){ + delete outputStream; + outputStream = NULL; + } + + if( isConnected() ) + { + + ::shutdown(socketHandle, 2); + + #if defined(unix) && !defined(__CYGWIN__) + ::close(socketHandle); + #else + ::closesocket(socketHandle); + #endif + + socketHandle = INVALID_SOCKET_HANDLE; + } +} + +//////////////////////////////////////////////////////////////////////////////// +int TcpSocket::getSoLinger() const throw(SocketException){ + + linger value; + socklen_t length = sizeof(value); + ::getsockopt(socketHandle, SOL_SOCKET, SO_LINGER, (char*)&value, &length ); + + return value.l_onoff? value.l_linger : 0; +} + +//////////////////////////////////////////////////////////////////////////////// +void TcpSocket::setSoLinger( const int dolinger ) throw(SocketException){ + + linger value; + value.l_onoff = dolinger != 0; + value.l_linger = dolinger; + ::setsockopt(socketHandle, SOL_SOCKET, SO_LINGER, (char*)&value, sizeof(value) ); +} + +//////////////////////////////////////////////////////////////////////////////// +bool TcpSocket::getKeepAlive() const throw(SocketException){ + + int value; + socklen_t length = sizeof(int); + ::getsockopt(socketHandle, SOL_SOCKET, SO_KEEPALIVE, (char*)&value, &length ); + return value != 0; +} + +//////////////////////////////////////////////////////////////////////////////// +void TcpSocket::setKeepAlive( const bool keepAlive ) throw(SocketException){ + + int value = keepAlive? 1 : 0; + ::setsockopt(socketHandle, SOL_SOCKET, SO_KEEPALIVE, (char*)&value, sizeof(int) ); +} + +//////////////////////////////////////////////////////////////////////////////// +int TcpSocket::getReceiveBufferSize() const throw(SocketException){ + + int value; + socklen_t length = sizeof(int); + ::getsockopt(socketHandle, SOL_SOCKET, SO_RCVBUF, (char*)&value, &length ); + return value; +} + +//////////////////////////////////////////////////////////////////////////////// +void TcpSocket::setReceiveBufferSize( const int size ) throw(SocketException){ + + ::setsockopt(socketHandle, SOL_SOCKET, SO_RCVBUF, (char*)&size, sizeof(int) ); +} + +//////////////////////////////////////////////////////////////////////////////// +bool TcpSocket::getReuseAddress() const throw(SocketException){ + + int value; + socklen_t length = sizeof(int); + ::getsockopt(socketHandle, SOL_SOCKET, SO_REUSEADDR, (char*)&value, &length ); + return value != 0; +} + +//////////////////////////////////////////////////////////////////////////////// +void TcpSocket::setReuseAddress( const bool reuse ) throw(SocketException){ + + int value = reuse? 1 : 0; + ::setsockopt(socketHandle, SOL_SOCKET, SO_REUSEADDR, (char*)&value, sizeof(int) ); +} + +//////////////////////////////////////////////////////////////////////////////// +int TcpSocket::getSendBufferSize() const throw(SocketException){ + + int value; + socklen_t length = sizeof(int); + ::getsockopt(socketHandle, SOL_SOCKET, SO_SNDBUF, (char*)&value, &length ); + return value; +} + +//////////////////////////////////////////////////////////////////////////////// +void TcpSocket::setSendBufferSize( const int size ) throw(SocketException){ + + ::setsockopt(socketHandle, SOL_SOCKET, SO_SNDBUF, (char*)&size, sizeof(int) ); +} + +//////////////////////////////////////////////////////////////////////////////// +void TcpSocket::setSoTimeout ( const int millisecs ) throw (SocketException) +{ +#if defined( unix ) && !defined( __CYGWIN__ ) + timeval timot; + timot.tv_sec = millisecs / 1000; + timot.tv_usec = (millisecs % 1000) * 1000; +#else + int timot = millisecs; +#endif + + ::setsockopt(socketHandle, SOL_SOCKET, SO_RCVTIMEO, (const char*) &timot, sizeof (timot)); + ::setsockopt(socketHandle, SOL_SOCKET, SO_SNDTIMEO, (const char*) &timot, sizeof (timot)); +} + +//////////////////////////////////////////////////////////////////////////////// +int TcpSocket::getSoTimeout() const throw(SocketException) +{ +#if defined( unix ) && !defined( __CYGWIN__ ) + timeval timot; + timot.tv_sec = 0; + timot.tv_usec = 0; + socklen_t size = sizeof(timot); +#else + int timot = 0; + int size = sizeof(timot); +#endif + + ::getsockopt(socketHandle, SOL_SOCKET, SO_RCVTIMEO, (char*) &timot, &size); + +#if defined( unix ) && !defined( __CYGWIN__ ) + return (timot.tv_sec * 1000) + (timot.tv_usec / 1000); +#else + return timot; +#endif +} + diff --git a/activemq-cpp/src/main/activemq/network/TcpSocket.h b/activemq-cpp/src/main/activemq/network/TcpSocket.h new file mode 100644 index 0000000000..69ff27dd0b --- /dev/null +++ b/activemq-cpp/src/main/activemq/network/TcpSocket.h @@ -0,0 +1,234 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_NETWORK_SOCKET_H +#define ACTIVEMQ_NETWORK_SOCKET_H + +#include +#include +#include +#include + +namespace activemq{ +namespace network{ + + // Forward declarations + class SocketInputStream; + class SocketOutputStream; + + /** + * Platform-independent implementation of the socket interface. + */ + class TcpSocket : public Socket + { + private: + + /** + * The handle for this socket. + */ + SocketHandle socketHandle; + + /** + * The input stream for reading this socket. + */ + SocketInputStream* inputStream; + + /** + * The output stream for writing to this socket. + */ + SocketOutputStream* outputStream; + + public: + + /** + * Construct a non-connected socket. + */ + TcpSocket(); + + /** + * Construct a connected or bound socket based on given + * socket handle. + */ + TcpSocket(SocketHandle socketHandle); + + /** + * Destruct. + * Releases the socket handle but not + * gracefully shut down the connection. + */ + virtual ~TcpSocket(); + + /** + * Gets the handle for the socket. + */ + SocketHandle getSocketHandle () { + return socketHandle; + } + + /** + * Connects to the specified destination. Closes this socket if + * connected to another destination. + * @param host The host of the server to connect to. + * @param port The port of the server to connect to. + * @throws IOException Thrown if a failure occurred in the connect. + */ + virtual void connect( const char* host, const int port ) throw(SocketException); + + /** + * Indicates whether or not this socket is connected to a destination. + */ + virtual bool isConnected() const{ + return socketHandle != INVALID_SOCKET_HANDLE; + } + + /** + * Gets the InputStream for this socket. + * @return The InputStream for this socket. NULL if not connected. + */ + virtual io::InputStream* getInputStream(); + + /** + * Gets the OutputStream for this socket. + * @return the OutputStream for this socket. NULL if not connected. + */ + virtual io::OutputStream* getOutputStream(); + + /** + * Gets the linger time. + * @return The linger time in seconds. + * @throws SocketException if the operation fails. + */ + virtual int getSoLinger() const throw(SocketException); + + /** + * Sets the linger time. + * @param linger The linger time in seconds. If 0, linger is off. + * @throws SocketException if the operation fails. + */ + virtual void setSoLinger( const int linger ) throw(SocketException); + + /** + * Gets the keep alive flag. + * @return True if keep alive is enabled. + * @throws SocketException if the operation fails. + */ + virtual bool getKeepAlive() const throw(SocketException); + + /** + * Enables/disables the keep alive flag. + * @param keepAlive If true, enables the flag. + * @throws SocketException if the operation fails. + */ + virtual void setKeepAlive( const bool keepAlive ) throw(SocketException); + + /** + * Gets the receive buffer size. + * @return the receive buffer size in bytes. + * @throws SocketException if the operation fails. + */ + virtual int getReceiveBufferSize() const throw(SocketException); + + /** + * Sets the recieve buffer size. + * @param size Number of bytes to set the receive buffer to. + * @throws SocketException if the operation fails. + */ + virtual void setReceiveBufferSize( const int size ) throw(SocketException); + + /** + * Gets the reuse address flag. + * @return True if the address can be reused. + * @throws SocketException if the operation fails. + */ + virtual bool getReuseAddress() const throw(SocketException); + + /** + * Sets the reuse address flag. + * @param reuse If true, sets the flag. + * @throws SocketException if the operation fails. + */ + virtual void setReuseAddress( const bool reuse ) throw(SocketException); + + /** + * Gets the send buffer size. + * @return the size in bytes of the send buffer. + * @throws SocketException if the operation fails. + */ + virtual int getSendBufferSize() const throw(SocketException); + + /** + * Sets the send buffer size. + * @param size The number of bytes to set the send buffer to. + * @throws SocketException if the operation fails. + */ + virtual void setSendBufferSize( const int size ) throw(SocketException); + + /** + * Gets the timeout for socket operations. + * @return The timeout in milliseconds for socket operations. + * @throws SocketException Thrown if unable to retrieve the information. + */ + virtual int getSoTimeout() const throw(SocketException); + + /** + * Sets the timeout for socket operations. + * @param timeout The timeout in milliseconds for socket operations.

+ * @throws SocketException Thrown if unable to set the information. + */ + virtual void setSoTimeout( const int timeout ) throw(SocketException); + + /** + * Closes this object and deallocates the appropriate resources. + * @throws CMSException + */ + virtual void close() throw( cms::CMSException ); + + protected: + + #if !defined( unix ) || defined( __CYGWIN__ ) + + // WINDOWS needs initialization of winsock + class StaticSocketInitializer { + private: + + SocketException* socketInitError; + + void clear(){ + if( socketInitError != NULL ){ + delete socketInitError; + } + socketInitError = NULL; + } + + public: + + SocketException* getSocketInitError () { + return socketInitError; + } + + StaticSocketInitializer(); + virtual ~StaticSocketInitializer (); + + }; + + static StaticSocketInitializer staticSocketInitializer; + #endif + + }; + +}} + +#endif /*ACTIVEMQ_NETWORK_SOCKET_H*/ diff --git a/activemq-cpp/src/main/activemq/support/InitDirector.cpp b/activemq-cpp/src/main/activemq/support/InitDirector.cpp new file mode 100644 index 0000000000..e020c0329f --- /dev/null +++ b/activemq-cpp/src/main/activemq/support/InitDirector.cpp @@ -0,0 +1,51 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "InitDirector.h" + +#include +#include +#include +#include + +using namespace activemq; +using namespace activemq::support; + +int InitDirector::refCount; + +//////////////////////////////////////////////////////////////////////////////// +InitDirector::InitDirector(void) +{ + if( refCount == 0 ) + { + logger::LogWriter::getInstance(); + connector::stomp::StompConnectorFactory::getInstance(); + transport::TcpTransportFactory::getInstance(); + transport::IOTransportFactory::getInstance(); + } + + refCount++; +} + +//////////////////////////////////////////////////////////////////////////////// +InitDirector::~InitDirector(void) +{ + refCount--; + + if( refCount == 0 ) + { + } +} diff --git a/activemq-cpp/src/main/activemq/support/InitDirector.h b/activemq-cpp/src/main/activemq/support/InitDirector.h new file mode 100644 index 0000000000..28fb1c12fc --- /dev/null +++ b/activemq-cpp/src/main/activemq/support/InitDirector.h @@ -0,0 +1,46 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_SUPPORT_INITDIRECTOR_H_ +#define _ACTIVEMQ_SUPPORT_INITDIRECTOR_H_ + +namespace activemq{ +namespace support{ + + /* + * Create a static instance of this class to init all static data + * in order in this library. + * Each package that needs initalization should create a set of + * functions that control init and cleanup. Each should be called + * by this class init in the constructor and cleanup in the + * destructor + */ + class InitDirector + { + private: + + static int refCount; + + public: + + InitDirector(void); + virtual ~InitDirector(void); + + }; + +}} + +#endif /*_ACTIVEMQ_SUPPORT_INITDIRECTOR_H_*/ diff --git a/activemq-cpp/src/main/activemq/support/LibraryInit.h b/activemq-cpp/src/main/activemq/support/LibraryInit.h new file mode 100644 index 0000000000..083a043a8f --- /dev/null +++ b/activemq-cpp/src/main/activemq/support/LibraryInit.h @@ -0,0 +1,28 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_SUPPORT_LIBRARY_INIT_H +#define _ACTIVEMQ_SUPPORT_LIBRARY_INIT_H + +#include + +// Hide in a no name namespace, avoid any collisions +namespace { + static activemq::support::InitDirector initDirector; +} + +#endif /*_ACTIVEMQ_SUPPORT_LIBRARY_INIT_H*/ diff --git a/activemq-cpp/src/main/activemq/transport/BrokerError.h b/activemq-cpp/src/main/activemq/transport/BrokerError.h new file mode 100644 index 0000000000..85541f3e08 --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/BrokerError.h @@ -0,0 +1,67 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_BROKERERROR_H_ +#define ACTIVEMQ_TRANSPORT_BROKERERROR_H_ + +#include +#include + +namespace activemq{ +namespace transport{ + + /** + * A distributed exception that implies that an error occurred at + * the broker. + */ + class BrokerError : public exceptions::ActiveMQException{ + public: + + BrokerError(){}; + BrokerError( const exceptions::ActiveMQException& ex ){ + *(exceptions::ActiveMQException*)this = ex; + } + BrokerError( const BrokerError& ex ){ + *(exceptions::ActiveMQException*)this = ex; + } + BrokerError(const char* file, const int lineNumber, + const char* msg, ...) + { + va_list vargs ; + va_start(vargs, msg) ; + buildMessage(msg, vargs) ; + + // Set the first mark for this exception. + setMark( file, lineNumber ); + } + + /** + * Clones this exception. This is useful for cases where you need + * to preserve the type of the original exception as well as the message. + * All subclasses should override. + */ + virtual exceptions::ActiveMQException* clone() const{ + return new BrokerError( *this ); + } + + virtual ~BrokerError(){} + + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_BROKERERROR_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/Command.h b/activemq-cpp/src/main/activemq/transport/Command.h new file mode 100644 index 0000000000..4989c5c9d8 --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/Command.h @@ -0,0 +1,57 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_COMMAND_H_ +#define ACTIVEMQ_TRANSPORT_COMMAND_H_ + +namespace activemq{ +namespace transport{ + + class Command{ + public: + + virtual ~Command(void){} + + /** + * Sets the Command Id of this Message + * @param Command Id + */ + virtual void setCommandId( const unsigned int id ) = 0; + + /** + * Gets the Command Id of this Message + * @return Command Id + */ + virtual unsigned int getCommandId() const = 0; + + /** + * Set if this Message requires a Response + * @param true if response is required + */ + virtual void setResponseRequired( const bool required ) = 0; + + /** + * Is a Response required for this Command + * @return true if a response is required. + */ + virtual bool isResponseRequired() const = 0; + + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_COMMAND_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/CommandIOException.h b/activemq-cpp/src/main/activemq/transport/CommandIOException.h new file mode 100644 index 0000000000..4473f96159 --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/CommandIOException.h @@ -0,0 +1,62 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_COMMANDIOEXCEPTION_H_ +#define ACTIVEMQ_TRANSPORT_COMMANDIOEXCEPTION_H_ + +#include +#include + +namespace activemq{ +namespace transport{ + + class CommandIOException : public io::IOException{ + public: + + CommandIOException(){}; + CommandIOException( const exceptions::ActiveMQException& ex ){ + *(exceptions::ActiveMQException*)this = ex; + } + CommandIOException( const CommandIOException& ex ){ + *(exceptions::ActiveMQException*)this = ex; + } + CommandIOException(const char* file, const int lineNumber, + const char* msg, ...) + { + va_list vargs ; + va_start(vargs, msg) ; + buildMessage(msg, vargs) ; + + // Set the first mark for this exception. + setMark( file, lineNumber ); + } + + /** + * Clones this exception. This is useful for cases where you need + * to preserve the type of the original exception as well as the message. + * All subclasses should override. + */ + virtual exceptions::ActiveMQException* clone() const{ + return new CommandIOException( *this ); + } + + virtual ~CommandIOException(){} + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_COMMANDIOEXCEPTION_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/CommandListener.h b/activemq-cpp/src/main/activemq/transport/CommandListener.h new file mode 100644 index 0000000000..b3a79ec588 --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/CommandListener.h @@ -0,0 +1,44 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_COMMANDLISTENER_H_ +#define ACTIVEMQ_TRANSPORT_COMMANDLISTENER_H_ + +#include + +namespace activemq{ +namespace transport{ + + /** + * Interface for an observer of broker commands. + */ + class CommandListener{ + public: + + virtual ~CommandListener(void){} + + /** + * Event handler for the receipt of a command. + * @param command the received command object. + */ + virtual void onCommand( Command* command ) = 0; + + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_COMMANDLISTENER_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/CommandReader.h b/activemq-cpp/src/main/activemq/transport/CommandReader.h new file mode 100644 index 0000000000..c38ead9056 --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/CommandReader.h @@ -0,0 +1,50 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_COMMANDREADER_H_ +#define ACTIVEMQ_TRANSPORT_COMMANDREADER_H_ + +#include +#include +#include + +namespace activemq{ +namespace transport{ + + /** + * Interface for an object responsible for reading a command + * from an input stream. + */ + class CommandReader : public io::Reader + { + public: + + virtual ~CommandReader(void){} + + /** + * Reads a command from the given input stream. + * @return The next command available on the stream. + * @throws CommandIOException if a problem occurs during the read. + */ + virtual Command* readCommand( void ) + throw ( CommandIOException ) = 0; + + }; + +}} + +#endif /*ACTIVEMQ_COMMANDS_COMMANDREADER_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/CommandWriter.h b/activemq-cpp/src/main/activemq/transport/CommandWriter.h new file mode 100644 index 0000000000..f78de24211 --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/CommandWriter.h @@ -0,0 +1,51 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_COMMANDWRITER_H_ +#define ACTIVEMQ_TRANSPORT_COMMANDWRITER_H_ + +#include +#include +#include +#include + +namespace activemq{ +namespace transport{ + + /** + * Interface for an object responsible for writing a command + * to an output stream. + */ + class CommandWriter : public io::Writer + { + public: + + virtual ~CommandWriter(void) {} + + /** + * Writes a command to the given output stream. + * @param command the command to write. + * @throws CommandIOException if a problem occurs during the write. + */ + virtual void writeCommand( const Command* command ) + throw ( CommandIOException ) = 0; + + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_COMMANDWRITER_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/ExceptionResponse.h b/activemq-cpp/src/main/activemq/transport/ExceptionResponse.h new file mode 100644 index 0000000000..17be16fe9a --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/ExceptionResponse.h @@ -0,0 +1,45 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_EXCEPTIONRESPONSE_H_ +#define ACTIVEMQ_TRANSPORT_EXCEPTIONRESPONSE_H_ + +#include +#include + +namespace activemq{ +namespace transport{ + + /** + * A response object that indicates an error occurred at the + * broker. + */ + class ExceptionResponse : public Response{ + public: + + virtual ~ExceptionResponse(){} + + /** + * Gets the error from the broker. + */ + virtual const BrokerError* getException() const = 0; + + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_EXCEPTIONRESPONSE_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/FutureResponse.h b/activemq-cpp/src/main/activemq/transport/FutureResponse.h new file mode 100644 index 0000000000..a1eb4998f6 --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/FutureResponse.h @@ -0,0 +1,125 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_FUTURERESPONSE_H_ +#define ACTIVEMQ_TRANSPORT_FUTURERESPONSE_H_ + +#include +#include +#include + +#include + +namespace activemq{ +namespace transport{ + + /** + * A container that holds a response object. Since this + * object is Synchronizable, callers can wait on this object + * and when a response comes in, notify can be called to + * inform those waiting that the response is now available. + */ + class FutureResponse : public concurrent::Synchronizable{ + private: + + Response* response; + concurrent::Mutex mutex; + + public: + + FutureResponse(){ + response = NULL; + } + + virtual ~FutureResponse(){} + + /** + * Locks the object. + */ + virtual void lock() throw(exceptions::ActiveMQException){ + mutex.lock(); + } + + /** + * Unlocks the object. + */ + virtual void unlock() throw(exceptions::ActiveMQException){ + mutex.unlock(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. + */ + virtual void wait() throw(exceptions::ActiveMQException){ + mutex.wait(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. This wait will timeout after the specified time + * interval. + * @param time in millisecsonds to wait, or WAIT_INIFINITE + * @throws ActiveMQException + */ + virtual void wait(unsigned long millisecs) + throw(exceptions::ActiveMQException) + { + mutex.wait( millisecs ); + } + + /** + * Signals a waiter on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + */ + virtual void notify() throw(exceptions::ActiveMQException){ + mutex.notify(); + } + + /** + * Signals the waiters on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + */ + virtual void notifyAll() throw(exceptions::ActiveMQException){ + mutex.notifyAll(); + } + + /** + * Getters for the response property. + */ + virtual const Response* getResponse() const{ + return response; + } + virtual Response* getResponse(){ + return response; + } + + /** + * Setter for the response property. + */ + virtual void setResponse( Response* response ){ + this->response = response; + } + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_FUTURERESPONSE_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/IOTransport.cpp b/activemq-cpp/src/main/activemq/transport/IOTransport.cpp new file mode 100644 index 0000000000..e490904cc4 --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/IOTransport.cpp @@ -0,0 +1,200 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "IOTransport.h" +#include "CommandReader.h" +#include "CommandWriter.h" + +#include +#include + +using namespace activemq; +using namespace activemq::transport; +using namespace activemq::concurrent; + +//////////////////////////////////////////////////////////////////////////////// +IOTransport::IOTransport(){ + + listener = NULL; + reader = NULL; + writer = NULL; + exceptionListener = NULL; + inputStream = NULL; + outputStream = NULL; + closed = false; + thread = NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +IOTransport::~IOTransport(){ + + close(); +} + +//////////////////////////////////////////////////////////////////////////////// +void IOTransport::oneway( Command* command ) + throw(CommandIOException, exceptions::UnsupportedOperationException) +{ + // Make sure the thread has been started. + if( thread == NULL ){ + throw CommandIOException( + __FILE__, __LINE__, + "IOTransport::oneway() - transport is not started" ); + } + + // Make sure the command object is valid. + if( command == NULL ){ + throw CommandIOException( + __FILE__, __LINE__, + "IOTransport::oneway() - attempting to write NULL command" ); + } + + // Make sure we have an output strema to write to. + if( outputStream == NULL ){ + throw CommandIOException( + __FILE__, __LINE__, + "IOTransport::oneway() - invalid output stream" ); + } + + synchronized( outputStream ){ + // Write the command to the output stream. + writer->writeCommand( command ); + } +} + +//////////////////////////////////////////////////////////////////////////////// +void IOTransport::start() throw( cms::CMSException ){ + + // Can't restart a closed transport. + if( closed ){ + throw CommandIOException( __FILE__, __LINE__, "IOTransport::start() - transport is already closed - cannot restart" ); + } + + // If it's already started, do nothing. + if( thread != NULL ){ + return; + } + + // Make sure all variables that we need have been set. + if( inputStream == NULL || outputStream == NULL || + reader == NULL || writer == NULL ){ + throw CommandIOException( + __FILE__, __LINE__, + "IOTransport::start() - " + "IO sreams and reader/writer must be set before calling start" ); + } + + // Init the Command Reader and Writer with the Streams + reader->setInputStream( inputStream ); + writer->setOutputStream( outputStream ); + + // Start the polling thread. + thread = new Thread( this ); + thread->start(); +} + +//////////////////////////////////////////////////////////////////////////////// +void IOTransport::close() throw( cms::CMSException ){ + + try{ + // Mark this transport as closed. + closed = true; + + // Wait for the thread to die. + if( thread != NULL ){ + thread->join(); + delete thread; + thread = NULL; + } + + /** + * Close the input stream. + */ + if( inputStream != NULL ){ + + synchronized( inputStream ){ + inputStream->close(); + inputStream = NULL; + } + } + + /** + * Close the output stream. + */ + if( outputStream != NULL ){ + + synchronized( outputStream ){ + outputStream->close(); + outputStream = NULL; + } + } + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +void IOTransport::run(){ + + try{ + + while( !closed ){ + + int available = 0; + synchronized( inputStream ){ + available = inputStream->available(); + } + + if( available > 0 ){ + + Command* command = NULL; + + synchronized( inputStream ){ + // Read the next command from the input stream. + command = reader->readCommand(); + } + + // Notify the listener. + fire( command ); + } + else{ + + // Sleep for a short time and try again. + Thread::sleep( 1 ); + } + } + + }catch( exceptions::ActiveMQException& ex ){ + + ex.setMark( __FILE__, __LINE__ ); + + if( !closed ) { + fire( ex ); + } + } + catch( ... ){ + + if( !closed ) { + exceptions::ActiveMQException ex( + __FILE__, __LINE__, + "IOTransport::run - caught unknown exception" ); + + fire( ex ); + } + } +} + diff --git a/activemq-cpp/src/main/activemq/transport/IOTransport.h b/activemq-cpp/src/main/activemq/transport/IOTransport.h new file mode 100644 index 0000000000..9b12e7f66e --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/IOTransport.h @@ -0,0 +1,225 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_IOTRANSPORT_H_ +#define ACTIVEMQ_TRANSPORT_IOTRANSPORT_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace transport{ + + /** + * Implementation of the Transport interface that performs + * marshalling of commands to IO streams. This class does not + * implement the request method, it only handles oneway messages. + * A thread polls on the input stream for in-coming commands. When + * a command is received, the command listener is notified. The + * polling thread is not started until the start method is called. + * The close method will close the associated streams. Close can + * be called explicitly by the user, but is also called in the + * destructor. Once this object has been closed, it cannot be + * restarted. + */ + class IOTransport + : + public Transport, + public concurrent::Runnable + { + private: + + /** + * Listener to incoming commands. + */ + CommandListener* listener; + + /** + * Reads commands from the input stream. + */ + CommandReader* reader; + + /** + * Writes commands to the output stream. + */ + CommandWriter* writer; + + /** + * Listener of exceptions from this transport. + */ + TransportExceptionListener* exceptionListener; + + /** + * The input stream for incoming commands. + */ + io::InputStream* inputStream; + + /** + * The output stream for out-going commands. + */ + io::OutputStream* outputStream; + + /** + * The polling thread. + */ + concurrent::Thread* thread; + + /** + * Flag marking this transport as closed. + */ + bool closed; + + private: + + /** + * Notify the excpetion listener + */ + void fire( exceptions::ActiveMQException& ex ){ + + if( exceptionListener != NULL ){ + + try{ + exceptionListener->onTransportException( this, ex ); + }catch( ... ){} + } + } + + /** + * Notify the command listener. + */ + void fire( Command* command ){ + + try{ + if( listener != NULL ){ + listener->onCommand( command ); + } + }catch( ... ){} + } + + public: + + /** + * Constructor. + */ + IOTransport(); + + /** + * Destructor - calls close(). + */ + virtual ~IOTransport(); + + /** + * Sends a one-way command. Does not wait for any response from the + * broker. + * @param command the command to be sent. + * @throws CommandIOException if an exception occurs during writing of + * the command. + * @throws UnsupportedOperationException if this method is not implemented + * by this transport. + */ + virtual void oneway( Command* command ) throw(CommandIOException, exceptions::UnsupportedOperationException); + + /** + * Not supported by this class - throws an exception. + * @throws UnsupportedOperationException. + */ + virtual Response* request( Command* command ) throw(CommandIOException, exceptions::UnsupportedOperationException){ + throw exceptions::UnsupportedOperationException( __FILE__, __LINE__, "IOTransport::request() - unsupported operation" ); + } + + /** + * Assigns the command listener for non-response commands. + * @param listener the listener. + */ + virtual void setCommandListener( CommandListener* listener ){ + this->listener = listener; + } + + /** + * Sets the command reader. + * @param reader the object that will be used for reading command objects. + */ + virtual void setCommandReader( CommandReader* reader ){ + this->reader = reader; + } + + /** + * Sets the command writer. + * @param writer the object that will be used for writing command objects. + */ + virtual void setCommandWriter( CommandWriter* writer ){ + this->writer = writer; + } + + /** + * Sets the observer of asynchronous exceptions from this transport. + * @param listener the listener of transport exceptions. + */ + virtual void setTransportExceptionListener( TransportExceptionListener* listener ){ + this->exceptionListener = listener; + } + + /** + * Sets the input stream for in-coming commands. + * @param is The input stream. + */ + virtual void setInputStream( io::InputStream* is ){ + this->inputStream = is; + } + + /** + * Sets the output stream for out-going commands. + * @param os The output stream. + */ + virtual void setOutputStream( io::OutputStream* os ){ + this->outputStream = os; + } + + /** + * Starts this transport object and creates the thread for + * polling on the input stream for commands. If this object + * has been closed, throws an exception. Before calling start, + * the caller must set the IO streams and the reader and writer + * objects. + * @throws CMSException if an error occurs or if this transport + * has already been closed. + */ + virtual void start() throw( cms::CMSException ); + + /** + * Stops the polling thread and closes the streams. This can + * be called explicitly, but is also called in the destructor. Once + * this object has been closed, it cannot be restarted. + * @throws CMSException if errors occur. + */ + virtual void close() throw( cms::CMSException ); + + /** + * Runs the polling thread. + */ + virtual void run(); + + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_IOTRANSPORT_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/IOTransportFactory.cpp b/activemq-cpp/src/main/activemq/transport/IOTransportFactory.cpp new file mode 100644 index 0000000000..bceb8c9a91 --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/IOTransportFactory.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "IOTransportFactory.h" + +using namespace activemq::transport; + +//////////////////////////////////////////////////////////////////////////////// +TransportFactory& IOTransportFactory::getInstance(void) +{ + // Create the one and only instance of the registrar + static TransportFactoryMapRegistrar registrar( + "io", new IOTransportFactory()); + + return registrar.getFactory(); +} diff --git a/activemq-cpp/src/main/activemq/transport/IOTransportFactory.h b/activemq-cpp/src/main/activemq/transport/IOTransportFactory.h new file mode 100644 index 0000000000..a2ef98549c --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/IOTransportFactory.h @@ -0,0 +1,61 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_IOTRANSPORTFACTORY_H_ +#define ACTIVEMQ_TRANSPORT_IOTRANSPORTFACTORY_H_ + +#include +#include +#include + +namespace activemq{ +namespace transport{ + + /** + * Manufactures IOTransports, which are objects that + * read from input streams and write to output streams. + */ + class IOTransportFactory : public TransportFactory{ + private: + + static TransportFactoryMapRegistrar registrar; + + public: + + virtual ~IOTransportFactory(){} + + /** + * Creates a Transport instance. + * @param properties The properties for the transport. + */ + virtual Transport* createTransport( + const activemq::util::Properties& properties ) + { + return new IOTransport(); + } + + /** + * Returns a reference to this TransportFactory + * @returns TransportFactory Reference + */ + static TransportFactory& getInstance(void); + + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_IOTRANSPORTFACTORY_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/Response.h b/activemq-cpp/src/main/activemq/transport/Response.h new file mode 100644 index 0000000000..9f1d01d3bc --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/Response.h @@ -0,0 +1,47 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_RESPONSE_H_ +#define ACTIVEMQ_TRANSPORT_RESPONSE_H_ + +#include + +namespace activemq{ +namespace transport{ + + class Response : public Command{ + public: + + virtual ~Response(void) {} + + /** + * Gets the Correlation Id that is associated with this message + * @return the Correlation Id + */ + virtual unsigned int getCorrelationId() const = 0; + + /** + * Sets the Correlation Id if this Command + * @param Id + */ + virtual void setCorrelationId( const unsigned int corrId ) = 0; + + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_RESPONSE_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/ResponseCorrelator.cpp b/activemq-cpp/src/main/activemq/transport/ResponseCorrelator.cpp new file mode 100644 index 0000000000..05d21fa91b --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/ResponseCorrelator.cpp @@ -0,0 +1 @@ +#include "ResponseCorrelator.h" diff --git a/activemq-cpp/src/main/activemq/transport/ResponseCorrelator.h b/activemq-cpp/src/main/activemq/transport/ResponseCorrelator.h new file mode 100644 index 0000000000..5346cbf6a3 --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/ResponseCorrelator.h @@ -0,0 +1,368 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_RESPONSECORRELATOR_H_ +#define ACTIVEMQ_TRANSPORT_RESPONSECORRELATOR_H_ + +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace transport{ + + /** + * This type of transport filter is responsible for correlating + * asynchronous responses with requests. Non-response messages + * are simply sent directly to the CommandListener. It owns + * the transport that it + */ + class ResponseCorrelator : public TransportFilter + { + private: + + /** + * The next command id for sent commands. + */ + unsigned int nextCommandId; + + /** + * Map of request ids to future response objects. + */ + std::map requestMap; + + /** + * Maximum amount of time in milliseconds to wait for a response. + */ + unsigned long maxResponseWaitTime; + + /** + * Sync object for accessing the next command id variable. + */ + concurrent::Mutex commandIdMutex; + + /** + * Sync object for accessing the request map. + */ + concurrent::Mutex mapMutex; + + /** + * Flag to indicate the closed state. + */ + bool closed; + + private: + + /** + * Returns the next available command id. + */ + unsigned int getNextCommandId() throw (exceptions::ActiveMQException){ + + try{ + synchronized( &commandIdMutex ){ + return ++nextCommandId; + } + + // Should never get here, but some compilers aren't + // smart enough to figure out we'll never get here. + return 0; + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) + } + + public: + + /** + * Constructor. + */ + ResponseCorrelator( Transport* next, const bool own = true ) + : + TransportFilter( next, own ) + { + nextCommandId = 0; + + // Default max response wait time to 3 seconds. + maxResponseWaitTime = 3000; + + // Start in the closed state. + closed = true; + } + + /** + * Destructor - calls close(). + */ + virtual ~ResponseCorrelator(){ + + // Close the transport and destroy it. + close(); + + // Don't do anything with the future responses - + // they should be cleaned up by each requester. + } + + /** + * Gets the maximum wait time for a response in milliseconds. + */ + virtual unsigned long getMaxResponseWaitTime() const{ + return maxResponseWaitTime; + } + + /** + * Sets the maximum wait time for a response in milliseconds. + */ + virtual void setMaxResponseWaitTime( const unsigned long milliseconds ){ + maxResponseWaitTime = milliseconds; + } + + /** + * Sends a one-way command. Does not wait for any response from the + * broker. + * @param command the command to be sent. + * @throws CommandIOException if an exception occurs during writing of + * the command. + * @throws UnsupportedOperationException if this method is not implemented + * by this transport. + */ + virtual void oneway( Command* command ) + throw(CommandIOException, exceptions::UnsupportedOperationException) + { + + try{ + command->setCommandId( getNextCommandId() ); + command->setResponseRequired( false ); + + if( closed || next == NULL ){ + throw CommandIOException( __FILE__, __LINE__, + "transport already closed" ); + } + + next->oneway( command ); + } + AMQ_CATCH_RETHROW( exceptions::UnsupportedOperationException ) + AMQ_CATCH_RETHROW( CommandIOException ) + AMQ_CATCH_EXCEPTION_CONVERT( exceptions::ActiveMQException, CommandIOException ) + AMQ_CATCHALL_THROW( CommandIOException ) + } + + /** + * Sends the given request to the server and waits for the response. + * @param command The request to send. + * @return the response from the server. This may be of type ExceptionResponse + * in the case of a distributed error that occurs at the broker. + * @throws CommandIOException if an error occurs with the request. + */ + virtual Response* request( Command* command ) + throw(CommandIOException, exceptions::UnsupportedOperationException) + { + + try{ + command->setCommandId( getNextCommandId() ); + command->setResponseRequired( true ); + + // Add a future response object to the map indexed by this + // command id. + FutureResponse* futureResponse = + new FutureResponse(); + + synchronized( &mapMutex ){ + requestMap[command->getCommandId()] = futureResponse; + } + + // Wait to be notified of the response via the futureResponse + // object. + Response* response = NULL; + synchronized( futureResponse ){ + + // Send the request. + next->oneway( command ); + + // Wait for the response to come in. + futureResponse->wait( maxResponseWaitTime ); + + // Get the response. + response = futureResponse->getResponse(); + } + + // Perform cleanup on the map. + synchronized( &mapMutex ){ + + // We've done our waiting - get this thing out + // of the map. + requestMap.erase( command->getCommandId() ); + + // Destroy the futureResponse. It is safe to + // do this now because the other thread only + // accesses the futureResponse within a lock on + // the map. + delete futureResponse; + futureResponse = NULL; + } + + if( response == NULL ){ + + throw CommandIOException( __FILE__, __LINE__, + "response from futureResponse was invalid" ); + } + + return response; + } + AMQ_CATCH_RETHROW( exceptions::UnsupportedOperationException ) + AMQ_CATCH_RETHROW( CommandIOException ) + AMQ_CATCH_EXCEPTION_CONVERT( exceptions::ActiveMQException, CommandIOException ) + AMQ_CATCHALL_THROW( CommandIOException ) + } + + /** + * This is called in the context of the nested transport's + * reading thread. In the case of a response object, + * updates the request map and notifies those waiting on the + * response. Non-response messages are just delegated to + * the command listener. + * @param command the received from the nested transport. + */ + virtual void onCommand( Command* command ){ + + // Let's see if the incoming command is a response. + Response* response = + dynamic_cast(command); + + if( response == NULL ){ + + // It's a non-response - just notify the listener. + fire( command ); + return; + } + + // It is a response - let's correlate ... + synchronized( &mapMutex ){ + + // Look the future request up based on the correlation id. + std::map::iterator iter = + requestMap.find( response->getCorrelationId() ); + if( iter == requestMap.end() ){ + + // This is not terrible - just log it. + printf("ResponseCorrelator::onCommand() - received unknown response for request: %d\n", + response->getCorrelationId() ); + return; + } + + // Get the future response (if it's in the map, it's not NULL). + FutureResponse* futureResponse = iter->second; + + // If it's an exception response, notify the exception listener. + ExceptionResponse* exResp = + dynamic_cast(response); + if( exResp != NULL ){ + const BrokerError* error = exResp->getException(); + fire( *error ); + } + + synchronized( futureResponse ){ + + // Set the response property in the future response. + futureResponse->setResponse( response ); + + // Notify all waiting for this response. + futureResponse->notifyAll(); + } + } + } + + /** + * Assigns the command listener for non-response commands. + * @param listener the listener. + */ + virtual void setCommandListener( CommandListener* listener ){ + this->commandlistener = listener; + } + + /** + * Sets the observer of asynchronous exceptions from this transport. + * @param listener the listener of transport exceptions. + */ + virtual void setTransportExceptionListener( + TransportExceptionListener* listener ) + { + this->exceptionListener = listener; + } + + /** + * Starts this transport object and creates the thread for + * polling on the input stream for commands. If this object + * has been closed, throws an exception. Before calling start, + * the caller must set the IO streams and the reader and writer + * objects. + * @throws CMSException if an error occurs or if this transport + * has already been closed. + */ + virtual void start() throw( cms::CMSException ){ + + /** + * We're already started. + */ + if( !closed ){ + return; + } + + if( commandlistener == NULL ){ + throw exceptions::ActiveMQException( __FILE__, __LINE__, + "commandListener is invalid" ); + } + + if( exceptionListener == NULL ){ + throw exceptions::ActiveMQException( __FILE__, __LINE__, + "exceptionListener is invalid" ); + } + + if( next == NULL ){ + throw exceptions::ActiveMQException( __FILE__, __LINE__, + "next transport is NULL" ); + } + + // Start the delegate transport object. + next->start(); + + // Mark it as open. + closed = false; + } + + /** + * Stops the polling thread and closes the streams. This can + * be called explicitly, but is also called in the destructor. Once + * this object has been closed, it cannot be restarted. + * @throws CMSException if errors occur. + */ + virtual void close() throw( cms::CMSException ){ + + if( !closed && next != NULL ){ + next->close(); + } + + closed = true; + } + + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_RESPONSECORRELATOR_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/TcpTransport.cpp b/activemq-cpp/src/main/activemq/transport/TcpTransport.cpp new file mode 100644 index 0000000000..8614dfe59b --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/TcpTransport.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "TcpTransport.h" + +#include +#include +#include +#include + +using namespace std; +using namespace activemq; +using namespace activemq::transport; +using namespace activemq::network; +using namespace activemq::exceptions; + +//////////////////////////////////////////////////////////////////////////////// +TcpTransport::TcpTransport( const activemq::util::Properties& properties, + Transport* next, + const bool own ) + : TransportFilter( next, own ) +{ + try + { + // Create the IO device we will be communicating over the + // wire with. This may need to change if we add more types + // of sockets, such as SSL. + socket = SocketFactory::createSocket(properties); + + // Cast it to an IO transport so we can wire up the socket + // input and output streams. + IOTransport* ioTransport = dynamic_cast( next ); + if( ioTransport == NULL ){ + throw ActiveMQException( + __FILE__, __LINE__, + "TcpTransport::TcpTransport - " + "transport must be of type IOTransport"); + } + + // Give the IOTransport the streams from out TCP socket. + ioTransport->setInputStream( socket->getInputStream() ); + ioTransport->setOutputStream( socket->getOutputStream() ); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +//////////////////////////////////////////////////////////////////////////////// +TcpTransport::~TcpTransport(void) +{ + try + { + socket->close(); + delete socket; + } + AMQ_CATCH_NOTHROW( ActiveMQException ) + AMQ_CATCHALL_NOTHROW( ) +} + diff --git a/activemq-cpp/src/main/activemq/transport/TcpTransport.h b/activemq-cpp/src/main/activemq/transport/TcpTransport.h new file mode 100644 index 0000000000..da97f905cb --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/TcpTransport.h @@ -0,0 +1,54 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_TRANSPORT_TCPTRANSPORT_H_ +#define _ACTIVEMQ_TRANSPORT_TCPTRANSPORT_H_ + +#include +#include +#include + +namespace activemq{ +namespace transport{ + + /** + * Implements a TCP/IP based transport filter, this transport + * is meant to wrap an instance of an IOTransport. The lower + * level transport should take care of manaing stream reads + * and writes. + */ + class TcpTransport : public TransportFilter + { + private: + + /** + * Socket that this Transport Communicates with + */ + network::Socket* socket; + + public: + + TcpTransport( const activemq::util::Properties& properties, + Transport* next, + const bool own = true ); + virtual ~TcpTransport(void); + + }; + +}} + +#endif /*_ACTIVEMQ_TRANSPORT_TCPTRANSPORT_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/TcpTransportFactory.cpp b/activemq-cpp/src/main/activemq/transport/TcpTransportFactory.cpp new file mode 100644 index 0000000000..e367001ac2 --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/TcpTransportFactory.cpp @@ -0,0 +1,66 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "TcpTransportFactory.h" + +#include +#include + +using namespace activemq; +using namespace activemq::transport; +using namespace activemq::exceptions; + +//////////////////////////////////////////////////////////////////////////////// +TransportFactory& TcpTransportFactory::getInstance(void) +{ + // Create the one and only instance of the registrar + static TransportFactoryMapRegistrar registrar( + "tcp", new TcpTransportFactory()); + + return registrar.getFactory(); +} + +//////////////////////////////////////////////////////////////////////////////// +Transport* TcpTransportFactory::createTransport( + const activemq::util::Properties& properties ) + throw ( ActiveMQException ) +{ + try + { + TransportFactory* factory = + TransportFactoryMap::getInstance().lookup( "io" ); + + if( factory == NULL ){ + throw ActiveMQException( + __FILE__, __LINE__, + "TcpTransport::createTransport - " + "unknown transport factory"); + } + + Transport* transport = new TcpTransport( + properties, factory->createTransport( properties ) ); + + // Create a response correlator. This will wrap around our + // transport and manage its lifecycle - we don't need the + // internal transport anymore, so we can reuse its pointer. + transport = new ResponseCorrelator( transport ); + + return transport; + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} diff --git a/activemq-cpp/src/main/activemq/transport/TcpTransportFactory.h b/activemq-cpp/src/main/activemq/transport/TcpTransportFactory.h new file mode 100644 index 0000000000..5d20fc999c --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/TcpTransportFactory.h @@ -0,0 +1,54 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_TRANSPORT_TCPTRANSPORTFACTORY_H_ +#define _ACTIVEMQ_TRANSPORT_TCPTRANSPORTFACTORY_H_ + +#include +#include +#include +#include + +namespace activemq{ +namespace transport{ + + class TcpTransportFactory : public TransportFactory + { + public: + + virtual ~TcpTransportFactory(void) {} + + /** + * Creates a Transport instance. + * @param properties The properties for the transport. + * @throws ActiveMQException + */ + virtual Transport* createTransport( + const activemq::util::Properties& properties ) + throw ( exceptions::ActiveMQException ); + + /** + * Returns a reference to this TransportFactory + * @returns TransportFactory Reference + */ + static TransportFactory& getInstance(void); + + }; + +}} + +#endif /*_ACTIVEMQ_TRANSPORT_TCPTRANSPORTFACTORY_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/Transport.h b/activemq-cpp/src/main/activemq/transport/Transport.h new file mode 100644 index 0000000000..78d982f3bd --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/Transport.h @@ -0,0 +1,108 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_TRANSPORT_H_ +#define ACTIVEMQ_TRANSPORT_TRANSPORT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace transport{ + + // Forward declarations. + class CommandListener; + class CommandReader; + class CommandWriter; + class TransportExceptionListener; + + /** + * Interface for a transport layer for command objects. Callers can + * send oneway messages or make synchronous requests. Non-response + * messages will be delivered to the specified listener object upon + * receipt. + */ + class Transport + : + public cms::Startable, + public cms::Closeable + { + public: + + virtual ~Transport(){} + + /** + * Sends a one-way command. Does not wait for any response from the + * broker. + * @param command the command to be sent. + * @throws CommandIOException if an exception occurs during writing of + * the command. + * @throws UnsupportedOperationException if this method is not implemented + * by this transport. + */ + virtual void oneway( Command* command ) + throw(CommandIOException, + exceptions::UnsupportedOperationException) = 0; + + /** + * Sends the given command to the broker and then waits for the response. + * @param command the command to be sent. + * @return the response from the broker. + * @throws CommandIOException if an exception occurs during the read of the + * command. + * @throws UnsupportedOperationException if this method is not implemented + * by this transport. + */ + virtual Response* request( Command* command ) + throw(CommandIOException, + exceptions::UnsupportedOperationException) = 0; + + /** + * Assigns the command listener for non-response commands. + * @param listener the listener. + */ + virtual void setCommandListener( CommandListener* listener ) = 0; + + /** + * Sets the command reader. + * @param reader the object that will be used for reading command objects. + */ + virtual void setCommandReader( CommandReader* reader ) = 0; + + /** + * Sets the command writer. + * @param writer the object that will be used for writing command objects. + */ + virtual void setCommandWriter( CommandWriter* writer ) = 0; + + /** + * Sets the observer of asynchronous exceptions from this transport. + * @param listener the listener of transport exceptions. + */ + virtual void setTransportExceptionListener( + TransportExceptionListener* listener ) = 0; + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_TRANSPORT_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/TransportExceptionListener.h b/activemq-cpp/src/main/activemq/transport/TransportExceptionListener.h new file mode 100644 index 0000000000..fc551c53f9 --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/TransportExceptionListener.h @@ -0,0 +1,49 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_TRANSPORTEXCEPTIONLISTENER_H_ +#define ACTIVEMQ_TRANSPORT_TRANSPORTEXCEPTIONLISTENER_H_ + +#include + +namespace activemq{ +namespace transport{ + + // Forward declarations. + class Transport; + + /** + * A listener of asynchronous exceptions from a command transport object. + */ + class TransportExceptionListener{ + public: + + virtual ~TransportExceptionListener(){} + + /** + * Event handler for an exception from a command transport. + * @param source The source of the exception + * @param ex The exception. + */ + virtual void onTransportException( + Transport* source, + const exceptions::ActiveMQException& ex ) = 0; + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_TRANSPORTEXCEPTIONLISTENER_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/TransportFactory.h b/activemq-cpp/src/main/activemq/transport/TransportFactory.h new file mode 100644 index 0000000000..638dbe9857 --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/TransportFactory.h @@ -0,0 +1,47 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_TRANSPORTFACTORY_H_ +#define ACTIVEMQ_TRANSPORT_TRANSPORTFACTORY_H_ + +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace transport{ + + class TransportFactory{ + public: + + virtual ~TransportFactory(void){} + + /** + * Creates a Transport instance. + * @param Properties Object that will hold transport values + */ + virtual Transport* createTransport( + const activemq::util::Properties& properties ) = 0; + + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_TRANSPORTFACTORY_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/TransportFactoryMap.cpp b/activemq-cpp/src/main/activemq/transport/TransportFactoryMap.cpp new file mode 100644 index 0000000000..446dad6042 --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/TransportFactoryMap.cpp @@ -0,0 +1,71 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "TransportFactoryMap.h" + +using namespace activemq::transport; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +TransportFactoryMap& TransportFactoryMap::getInstance(void) +{ + // Static instance of this Map, create here so that one will + // always exist, the one and only Connector Map. + static TransportFactoryMap instance; + + return instance; +} + +//////////////////////////////////////////////////////////////////////////////// +void TransportFactoryMap::registerTransportFactory( const string& name, + TransportFactory* factory ) +{ + factoryMap[name] = factory; +} + +//////////////////////////////////////////////////////////////////////////////// +void TransportFactoryMap::unregisterTransportFactory( const string& name ){ + factoryMap.erase(name); +} + +//////////////////////////////////////////////////////////////////////////////// +TransportFactory* TransportFactoryMap::lookup( const string& name ){ + + map::const_iterator itr = + factoryMap.find(name); + + if(itr != factoryMap.end()) + { + return itr->second; + } + + // Didn't find it, return nothing, not a single thing. + return NULL; +} + +//////////////////////////////////////////////////////////////////////////////// +size_t TransportFactoryMap::getFactoryNames(vector& factoryList){ + map::const_iterator itr = + factoryMap.begin(); + + for(; itr != factoryMap.end(); ++itr) + { + factoryList.insert(factoryList.end(), itr->first); + } + + return factoryMap.size(); +} diff --git a/activemq-cpp/src/main/activemq/transport/TransportFactoryMap.h b/activemq-cpp/src/main/activemq/transport/TransportFactoryMap.h new file mode 100644 index 0000000000..efd35113df --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/TransportFactoryMap.h @@ -0,0 +1,95 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_TRANSPORTFACTORYMAP_H_ +#define ACTIVEMQ_TRANSPORT_TRANSPORTFACTORYMAP_H_ + +#include +#include +#include + +namespace activemq{ +namespace transport{ + + /** + * The TransportFactoryMap contains keys that map to specific versions + * of the TransportFactory class which create a particular type of + * Transport. + */ + class TransportFactoryMap{ + + private: + + // Map of Factories + std::map factoryMap; + + private: + + // Hidden Contrustor, prevents instantiation + TransportFactoryMap() {}; + + // Hidden Destructor. + virtual ~TransportFactoryMap() {}; + + // Hidden Copy Constructore + TransportFactoryMap(const TransportFactoryMap& factoryMap){}; + + // Hidden Assignment operator + TransportFactoryMap& operator=(const TransportFactoryMap& factoryMap){ + return *this; + } + + public: + + /** + * Gets a singleton instance of this class. + */ + static TransportFactoryMap& getInstance(void); + + /** + * Registers a new Transport Factory with this map + * @param name to associate the factory with + * @param factory to store. + */ + void registerTransportFactory( const std::string& name, + TransportFactory* factory ); + + /** + * Unregisters a Transport Factory with this map + * @param name of the factory to remove + */ + void unregisterTransportFactory( const std::string& name ); + + /** + * Lookup the named factory in the Map + * @param the factory name to lookup + * @return the factory assciated with the name, or NULL + */ + TransportFactory* lookup( const std::string& name ); + + /** + * Fetch a list of factory names that this Map contains + * @param vector object to receive the list + * @returns count of factories. + */ + std::size_t getFactoryNames(std::vector& factoryList); + + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_TRANSPORTFACTORYMAP_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/TransportFactoryMapRegistrar.h b/activemq-cpp/src/main/activemq/transport/TransportFactoryMapRegistrar.h new file mode 100644 index 0000000000..ceca160cd4 --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/TransportFactoryMapRegistrar.h @@ -0,0 +1,90 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_TRANSPORTFACTORYMAPREGISTRAR_H_ +#define ACTIVEMQ_TRANSPORT_TRANSPORTFACTORYMAPREGISTRAR_H_ + +#include + +namespace activemq{ +namespace transport{ + + /** + * Registers the passed in factory into the factory map, this class + * can manage the lifetime of the registered factory (default behaviour). + */ + class TransportFactoryMapRegistrar + { + public: + + /** + * Constructor for this class + * @param name of the factory to register + * @param the factory + * @param boolean indicating if this object manages the lifetime of + * the factory that is being registered. + */ + TransportFactoryMapRegistrar(const std::string& name, + TransportFactory* factory, + bool manageLifetime = true) + { + // Register it in the map. + TransportFactoryMap::getInstance(). + registerTransportFactory(name, factory); + + // Store for later deletion + this->factory = factory; + this->manageLifetime = manageLifetime; + this->name = name; + } + + virtual ~TransportFactoryMapRegistrar(void) + { + try + { + // UnRegister it in the map. + TransportFactoryMap::getInstance(). + unregisterTransportFactory(name); + + if(manageLifetime) + { + delete factory; + } + } + catch(...) {} + } + + /** + * Return a reference to the factory object that is contained in this + * registrar. + * @return TransportFactory reference + */ + virtual TransportFactory& getFactory(void) { + return *factory; + } + + private: + + std::string name; + TransportFactory* factory; + bool manageLifetime; + + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_TRANSPORTFACTORYMAPREGISTRAR_H_*/ diff --git a/activemq-cpp/src/main/activemq/transport/TransportFilter.h b/activemq-cpp/src/main/activemq/transport/TransportFilter.h new file mode 100644 index 0000000000..daa24b3672 --- /dev/null +++ b/activemq-cpp/src/main/activemq/transport/TransportFilter.h @@ -0,0 +1,230 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_TRANSPORTFILTER_H_ +#define ACTIVEMQ_TRANSPORT_TRANSPORTFILTER_H_ + +#include +#include +#include +#include + +namespace activemq{ +namespace transport{ + + /** + * A filter on the transport layer. Transport + * filters implement the Transport interface and + * optionally delegate calls to another Transport object. + */ + class TransportFilter + : + public Transport, + public CommandListener, + public TransportExceptionListener + { + protected: + + /** + * The transport that this filter wraps around. + */ + Transport* next; + + /** + * Flag to indicate whether this object controls + * the lifetime of the next transport object. + */ + bool own; + + /** + * Listener to incoming commands. + */ + CommandListener* commandlistener; + + /** + * Listener of exceptions from this transport. + */ + TransportExceptionListener* exceptionListener; + + protected: + + /** + * Notify the excpetion listener + */ + void fire( const exceptions::ActiveMQException& ex ){ + + if( exceptionListener != NULL ){ + + try{ + exceptionListener->onTransportException( this, ex ); + }catch( ... ){} + } + } + + /** + * Notify the command listener. + */ + void fire( Command* command ){ + + try{ + if( commandlistener != NULL ){ + commandlistener->onCommand( command ); + } + }catch( ... ){} + } + + public: + + /** + * Constructor. + */ + TransportFilter( Transport* next, const bool own = true ){ + + this->next = next; + this->own = own; + + commandlistener = NULL; + exceptionListener = NULL; + + // Observe the nested transport for events. + next->setCommandListener( this ); + next->setTransportExceptionListener( this ); + } + + /** + * Destructor - calls close(). + */ + virtual ~TransportFilter(){ + + if( own ){ + delete next; + next = NULL; + } + + } + + /** + * Event handler for the receipt of a command. + * @param command the received command object. + */ + virtual void onCommand( Command* command ){ + fire( command ); + } + + /** + * Event handler for an exception from a command transport. + * @param source The source of the exception + * @param ex The exception. + */ + virtual void onTransportException( Transport* source, const exceptions::ActiveMQException& ex ){ + fire( ex ); + } + + /** + * Sends a one-way command. Does not wait for any response from the + * broker. + * @param command the command to be sent. + * @throws CommandIOException if an exception occurs during writing of + * the command. + * @throws UnsupportedOperationException if this method is not implemented + * by this transport. + */ + virtual void oneway( Command* command ) throw(CommandIOException, exceptions::UnsupportedOperationException){ + next->oneway( command ); + } + + /** + * Not supported by this class - throws an exception. + * @throws UnsupportedOperationException. + */ + virtual Response* request( Command* command ) throw(CommandIOException, exceptions::UnsupportedOperationException){ + return next->request( command ); + } + + /** + * Assigns the command listener for non-response commands. + * @param listener the listener. + */ + virtual void setCommandListener( CommandListener* listener ){ + this->commandlistener = listener; + } + + /** + * Sets the command reader. + * @param reader the object that will be used for reading command objects. + */ + virtual void setCommandReader( CommandReader* reader ){ + next->setCommandReader( reader ); + } + + /** + * Sets the command writer. + * @param writer the object that will be used for writing command objects. + */ + virtual void setCommandWriter( CommandWriter* writer ){ + next->setCommandWriter( writer ); + } + + /** + * Sets the observer of asynchronous exceptions from this transport. + * @param listener the listener of transport exceptions. + */ + virtual void setTransportExceptionListener( TransportExceptionListener* listener ){ + this->exceptionListener = listener; + } + + /** + * Starts this transport object and creates the thread for + * polling on the input stream for commands. If this object + * has been closed, throws an exception. Before calling start, + * the caller must set the IO streams and the reader and writer + * objects. + * @throws CMSException if an error occurs or if this transport + * has already been closed. + */ + virtual void start() throw( cms::CMSException ){ + + if( commandlistener == NULL ){ + throw exceptions::ActiveMQException( __FILE__, __LINE__, + "commandListener is invalid" ); + } + + if( exceptionListener == NULL ){ + throw exceptions::ActiveMQException( __FILE__, __LINE__, + "exceptionListener is invalid" ); + } + + // Start the delegate transport object. + next->start(); + } + + /** + * Stops the polling thread and closes the streams. This can + * be called explicitly, but is also called in the destructor. Once + * this object has been closed, it cannot be restarted. + * @throws CMSException if errors occur. + */ + virtual void close() throw( cms::CMSException ){ + + next->close(); + } + + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_TRANSPORTFILTER_H_*/ diff --git a/activemq-cpp/src/main/activemq/util/Boolean.h b/activemq-cpp/src/main/activemq/util/Boolean.h new file mode 100644 index 0000000000..678e44e387 --- /dev/null +++ b/activemq-cpp/src/main/activemq/util/Boolean.h @@ -0,0 +1,61 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_UTIL_BOOLEAN_H_ +#define _ACTIVEMQ_UTIL_BOOLEAN_H_ + +#include + +namespace activemq{ +namespace util{ + + class Boolean : Number + { + public: + + Boolean(void) {} + virtual ~Boolean(void) {} + + /** + * Parses the String passed and extracts an bool. + * @param String to parse + * @return bool value + */ + static int parseBoolean(const std::string& value){ + bool ret = 0; + std::istringstream istream(value); + istream.clear(); + istream >> std::boolalpha >> ret; + return ret; + } + + /** + * Converts the bool to a String representation + * @param bool to convert + * @return string representation + */ + static std::string toString(bool value){ + std::ostringstream ostream; + ostream << std::boolalpha << value; + return ostream.str(); + } + + }; + +}} + +#endif /*BOOLEAN_H_*/ diff --git a/activemq-cpp/src/main/activemq/util/Endian.h b/activemq-cpp/src/main/activemq/util/Endian.h new file mode 100644 index 0000000000..0afb40d1c2 --- /dev/null +++ b/activemq-cpp/src/main/activemq/util/Endian.h @@ -0,0 +1,199 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_UTIL_ENDIAN_H +#define ACTIVEMQ_UTIL_ENDIAN_H + +#ifdef unix +#include +#else +#include +#endif + +// First try - check __BYTE_ORDER macro +#if !defined IFR_IS_BIG_ENDIAN && !defined IFR_IS_LITTLE_ENDIAN && !defined IFR_IS_DPD_ENDIAN +# ifdef unix +# include // defines __BYTE_ORDER (or sometimes __LITTLE_ENDIAN or __BIG_ENDIAN or __PDP_ENDIAN) +# endif +# if defined (__GLIBC__) +# include // Can also define __BYTE_ORDER +# endif +# ifdef __BYTE_ORDER +# if __BYTE_ORDER == __LITTLE_ENDIAN +# define IFR_IS_LITTLE_ENDIAN +# elif __BYTE_ORDER == __BIG_ENDIAN +# define IFR_IS_BIG_ENDIAN +# elif __BYTE_ORDER == __PDP_ENDIAN +# define IFR_IS_PDP_ENDIAN +# endif +# endif +#endif + +// Second try - check __LITTLE_ENDIAN or __BIG_ENDIAN +#if !defined IFR_IS_BIG_ENDIAN && !defined IFR_IS_LITTLE_ENDIAN && !defined IFR_IS_DPD_ENDIAN +# if defined __LITTLE_ENDIAN +# define IFR_IS_LITTLE_ENDIAN +# elif defined __BIG_ENDIAN +# define IFR_IS_BIG_ENDIAN +# elif defined __PDP_ENDIAN +# define IFR_IS_PDP_ENDIAN +# endif +#endif + +// Last try - find out from well-known processor types using little endian +#if !defined IFR_IS_BIG_ENDIAN && !defined IFR_IS_LITTLE_ENDIAN && !defined IFR_IS_DPD_ENDIAN +# if defined (i386) || defined (__i386__) \ + || defined (_M_IX86) || defined (vax) \ + || defined (__alpha) || defined (__alpha__) \ + || defined (__x86_64__) || defined (__ia64) \ + || defined (__ia64__) || defined (__amd64__) \ + || defined (_M_IX86) || defined (_M_IA64) \ + || defined (_M_ALPHA) +# define IFR_IS_LITTLE_ENDIAN +# else +# if defined (__sparc) || defined(__sparc__) \ + || defined(_POWER) || defined(__powerpc__) \ + || defined(__ppc__) || defined(__hppa) \ + || defined(_MIPSEB) || defined(_POWER) \ + || defined(__s390__) +# define IFR_IS_BIG_ENDIAN +# endif +# endif +#endif + +// Show error if we still don't know endianess +#if !defined IFR_IS_BIG_ENDIAN && !defined IFR_IS_LITTLE_ENDIAN && !defined IFR_IS_DPD_ENDIAN +#error "Could not determine endianess of your processor type" +#endif + +// Use these if the compiler does not support _intXX +#ifdef NEEDS_INT_DEFINED +#define _int16 short +#define _int32 int +#define _int64 long long +#endif + +// Check for uintXX types +#ifndef uint8_t +#define uint8_t unsigned char +#endif +#ifndef uint16_t +#define uint16_t unsigned short +#endif +#ifndef uint32_t +#define uint32_t unsigned int +#endif +#ifndef uint64_t +#define uint64_t unsigned long long +#endif + +// Macros and helpers for endian conversion +namespace activemq{ +namespace util{ + +/*#ifdef IFR_IS_BIGENDIAN +inline unsigned int htoni (unsigned int i) { return i; } +inline unsigned long long htonll (unsigned long long ll) { return ll; } +inline float htonf (float f) { return f; } +inline double htond (double d) { return d; } +inline unsigned int ntohi (unsigned int i) { return i; } +inline unsigned long long ntohll (unsigned long long ll) { return ll; } +inline float ntohf (float f) { return f; } +inline double ntohd (double d) { return d; } +#else // !IFR_IS_BIGENDIAN + +inline unsigned int htoni (unsigned int i) { + return ( i << 8 ) & 0xFF00 | + ( i >> 8 ) & 0x00FF; +} +inline unsigned long long htonll (unsigned long long ll) { + return + ( ll << 56 ) & 0xFF00000000000000ULL | + ( ll << 40 ) & 0x00FF000000000000ULL | + ( ll << 24 ) & 0x0000FF0000000000ULL | + ( ll << 8 ) & 0x000000FF00000000ULL | + ( ll >> 8 ) & 0x00000000FF000000ULL | + ( ll >> 24 ) & 0x0000000000FF0000ULL | + ( ll >> 40 ) & 0x000000000000FF00ULL | + ( ll >> 56 ) & 0x00000000000000FFULL; +} + + +inline float htonf (float f) { + unsigned int i = htonl( *(unsigned int *)&f ) ; + return *(float *)&i ; +} +inline double htond (double d) { + unsigned long long ll = htonll( *(unsigned long long *)&d ) ; + return *(double *)&ll ; +} +inline unsigned int ntohi (unsigned int i) { return htoni (i); } +inline unsigned long long ntohll (unsigned long long ll) { return htonll (ll); } +inline float ntohf (float f) { return htonf (f); } +inline double ntohd (double d) { return htond (d); } +*/ + class Endian{ + public: + + static void byteSwap(unsigned char* data, int dataLength) { + + #ifdef IFR_IS_BIGENDIAN + return; + #endif + + for (int i = 0; i + +using namespace activemq::util; +using namespace activemq::exceptions; +using namespace std; + +//////////////////////////////////////////////////////////////////////////////// +Guid::Guid(void) +{ + // Clear internal uuid, would pass isNull + #if defined( unix ) && !defined( __CYGWIN__ ) + memset(&uuid, 0, sizeof(uuid_t)); + #else + ::UuidCreateNil(&uuid); + #endif +} + +//////////////////////////////////////////////////////////////////////////////// +Guid::Guid(const Guid& source) +{ + // Set this uuid to that of the source + *this = source; +} + +//////////////////////////////////////////////////////////////////////////////// +Guid::Guid(const std::string& source) + throw ( IllegalArgumentException ) +{ + if(source == "") + { + throw IllegalArgumentException( + __FILE__, __LINE__, + "GUID::fromBytes - Source was Empty"); + } + + // Set this uuid to that of the source + *this = source; +} + +//////////////////////////////////////////////////////////////////////////////// +Guid::~Guid(void) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +bool Guid::isNull(void) const +{ + #if defined( unix ) && !defined( __CYGWIN__ ) + // Check the uuid APIs is null method + return uuid_is_null(*(const_cast(&uuid))) == 1 ? true : false; + #else + RPC_STATUS status; + + BOOL result = ::UuidIsNil( const_cast( &uuid ), &status ); + + return (result == TRUE) ? true : false; + #endif +} + +//////////////////////////////////////////////////////////////////////////////// +void Guid::setNull(void) +{ + #if defined( unix ) && !defined( __CYGWIN__ ) + // use the uuid function to clear + uuid_clear(uuid); + #else + ::UuidCreateNil(&uuid); + #endif +} + +//////////////////////////////////////////////////////////////////////////////// +Guid& Guid::createGUID(void) throw( RuntimeException ) +{ + #if defined( unix ) && !defined( __CYGWIN__ ) + // Use the uuid_generate method to create a new GUID + uuid_generate(uuid); + #else + // Create a uuid with the Co Create GUID + RPC_STATUS lhResult = ::UuidCreate( &uuid ); + + if ( lhResult == RPC_S_UUID_NO_ADDRESS ) + { + throw RuntimeException( + __FILE__, __LINE__, + "GUIG::createGUID - Failed Creating GUID"); + } + #endif + + return *this; +} + +//////////////////////////////////////////////////////////////////////////////// +std::string Guid::toString(void) const throw( RuntimeException ) +{ + std::string uuid_str = ""; + + #if defined( unix ) && !defined( __CYGWIN__ ) + // Create storage for the string buffer + char buffer[36] = {0}; + + // parse the uuid to the string + uuid_unparse(*(const_cast(&uuid)), buffer); + + // Store it in a string + uuid_str = buffer; + #else + // Convert the GUID object to a string. + unsigned char* guidStr = 0; + + RPC_STATUS result = ::UuidToString( + const_cast(&uuid), + &guidStr); + + if(result == RPC_S_OUT_OF_MEMORY) + { + throw RuntimeException( + __FILE__, __LINE__, + "GUIG::createGUID - Failed Creating GUID"); + } + + uuid_str = (char*)guidStr; + + // Dispose of the GUID string. + ::RpcStringFree(&guidStr); + #endif + + return uuid_str; +} + +//////////////////////////////////////////////////////////////////////////////// +Guid::operator std::string() const +{ + return toString(); +} + +//////////////////////////////////////////////////////////////////////////////// +const unsigned char* Guid::toBytes(void) const +{ + unsigned char* buffer = new unsigned char[getRawBytesSize()]; + + // copy our buffer + #if defined( unix ) && !defined( __CYGWIN__ ) + uuid_copy(buffer, *(const_cast(&uuid))); + #else + memcpy(buffer, &uuid, getRawBytesSize()); + #endif + + return &buffer[0]; +} + +//////////////////////////////////////////////////////////////////////////////// +Guid& Guid::fromBytes(const unsigned char* bytes) + throw ( IllegalArgumentException ) +{ + if(bytes == NULL) + { + throw IllegalArgumentException( + __FILE__, __LINE__, + "GUID::fromBytes - bytes pointer was NULL"); + } + + // Copy the data + #if defined( unix ) && !defined( __CYGWIN__ ) + memcpy(uuid, bytes, getRawBytesSize()); + #else + memcpy(&uuid, bytes, getRawBytesSize()); + #endif + + return *this; +} + +//////////////////////////////////////////////////////////////////////////////// +int Guid::getRawBytesSize(void) const +{ + #if defined( unix ) && !defined( __CYGWIN__ ) + return sizeof(uuid_t); + #else + return sizeof(::GUID); + #endif +} + +//////////////////////////////////////////////////////////////////////////////// +Guid::operator const unsigned char*() const +{ + #if defined( unix ) && !defined( __CYGWIN__ ) + return &uuid[0]; + #else + return reinterpret_cast(&uuid); + #endif +} + +//////////////////////////////////////////////////////////////////////////////// +Guid& Guid::operator=(const Guid& source) + throw ( IllegalArgumentException ) +{ + #if defined( unix ) && !defined( __CYGWIN__ ) + // Use the uuid method to copy + uuid_copy(uuid, *(const_cast(&source.uuid))); + #else + // Use mem copy + memcpy(&uuid, &source.uuid, getRawBytesSize()); + #endif + + return *this; +} + +//////////////////////////////////////////////////////////////////////////////// +Guid& Guid::operator=(const std::string& source) + throw ( IllegalArgumentException ) +{ + #if defined( unix ) && !defined( __CYGWIN__ ) + // Parse a uuid from the passed in string + uuid_parse( const_cast(source.c_str()), uuid ); + #else + if ( source.empty() ) + { + ::UuidCreateNil( &uuid ); + } + else + { + RPC_STATUS hResult = + ::UuidFromString( (unsigned char*)source.c_str(), &uuid ); + + if ( hResult == RPC_S_INVALID_STRING_UUID ) + { + throw IllegalArgumentException( + __FILE__, __LINE__, + "GUID::fromBytes - Invalid GUID String"); + } + } + #endif + + return *this; +} + +//////////////////////////////////////////////////////////////////////////////// +bool Guid::operator==(const Guid& source) const +{ + #if defined( unix ) && !defined( __CYGWIN__ ) + // uuid_compare returns 0 for equal + return uuid_compare( + *(const_cast(&uuid)), + *(const_cast(&source.uuid))) == 0 ? true : false; + #else + RPC_STATUS status; + + BOOL result = ::UuidEqual( + const_cast( &uuid ), + const_cast( &source.uuid ), + &status ); + + return ( result == TRUE ) ? true : false; + #endif +} + +//////////////////////////////////////////////////////////////////////////////// +bool Guid::operator==(const std::string& source) const +{ + return *this == Guid(source); +} + +//////////////////////////////////////////////////////////////////////////////// +bool Guid::operator!=(const Guid& source) const +{ + return !(*this == source); +} + +//////////////////////////////////////////////////////////////////////////////// +bool Guid::operator!=(const std::string& source) const +{ + return !(*this == source); +} + +//////////////////////////////////////////////////////////////////////////////// +bool Guid::operator<(const Guid& source) const +{ + #if defined( unix ) && !defined( __CYGWIN__ ) + // uuid_compare returns 0 for equal + return uuid_compare( + *(const_cast(&uuid)), + *(const_cast(&source.uuid))) < 0 ? true : false; + #else + RPC_STATUS status; + + int result = ::UuidCompare( + const_cast( &uuid ), + const_cast( &source.uuid ), + &status ); + + return ( result < 0 ) ? true : false; + #endif +} + +//////////////////////////////////////////////////////////////////////////////// +bool Guid::operator<(const std::string& source) const +{ + return *this < Guid(source); +} + +//////////////////////////////////////////////////////////////////////////////// +bool Guid::operator<=(const Guid& source) const +{ + #if defined( unix ) && !defined( __CYGWIN__ ) + // uuid_compare returns 0 for equal + return uuid_compare( + *(const_cast(&uuid)), + *(const_cast(&source.uuid))) <= 0 ? true : false; + #else + RPC_STATUS status; + + int result = ::UuidCompare( + const_cast( &uuid ), + const_cast( &source.uuid ), + &status ); + + return ( result <= 0 ) ? true : false; + #endif +} + +//////////////////////////////////////////////////////////////////////////////// +bool Guid::operator<=(const std::string& source) const +{ + return *this <= Guid(source); +} + +//////////////////////////////////////////////////////////////////////////////// +bool Guid::operator>(const Guid& source) const +{ + #if defined( unix ) && !defined( __CYGWIN__ ) + // uuid_compare returns 0 for equal + return uuid_compare( + *(const_cast(&uuid)), + *(const_cast(&source.uuid))) > 0 ? true : false; + #else + RPC_STATUS status; + + int result = ::UuidCompare( + const_cast( &uuid ), + const_cast( &source.uuid ), + &status ); + + return ( result > 0 ) ? true : false; + #endif +} + +//////////////////////////////////////////////////////////////////////////////// +bool Guid::operator>(const std::string& source) const +{ + return *this > Guid(source); +} + +//////////////////////////////////////////////////////////////////////////////// +bool Guid::operator>=(const Guid& source) const +{ + #if defined( unix ) && !defined( __CYGWIN__ ) + // uuid_compare returns 0 for equal + return uuid_compare( + *(const_cast(&uuid)), + *(const_cast(&source.uuid))) >= 0 ? true : false; + #else + RPC_STATUS status; + + int result = ::UuidCompare( + const_cast(&uuid), + const_cast(&source.uuid), + &status); + + return (result >= 0) ? true : false; + #endif +} + +//////////////////////////////////////////////////////////////////////////////// +bool Guid::operator>=(const std::string& source) const +{ + return *this >= Guid(source); +} + +//////////////////////////////////////////////////////////////////////////////// +std::string Guid::createGUIDString(void) +{ + return Guid().createGUID().toString(); +} + +//////////////////////////////////////////////////////////////////////////////// +const unsigned char* createGUIDBytes(void) +{ + return Guid().createGUID().toBytes(); +} diff --git a/activemq-cpp/src/main/activemq/util/Guid.h b/activemq-cpp/src/main/activemq/util/Guid.h new file mode 100644 index 0000000000..6513a54316 --- /dev/null +++ b/activemq-cpp/src/main/activemq/util/Guid.h @@ -0,0 +1,200 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_UTIL_GUID_H +#define ACTIVEMQ_UTIL_GUID_H + +#if defined( unix ) && !defined( __CYGWIN__ ) + #include +#elif defined(_WIN32) || defined( __CYGWIN__ ) + #include + #include +#else // defined MACOSX + #include "uuid.h" +#endif + +#include +#include + +#include + +namespace activemq{ +namespace util{ + + class Guid + { + public: + + /** + * Constructor + */ + Guid(void); + + /** + * Copy Constructor + */ + Guid(const Guid& source); + + /** + * Constructor - Create a GUID from a String + */ + Guid(const std::string& source) + throw ( exceptions::IllegalArgumentException ); + + /** + * Destructor + */ + virtual ~Guid(void); + + /** + * Determines if this GUID is null, if so it can be initialized with a + * call to createGUID. + * @return true for Null GUID, false otherwise. + */ + bool isNull(void) const; + + /** + * Clears the GUID's current value and sets it to a NULL GUID value + * will now pass isNull. + */ + void setNull(void); + + /** + * Generate a new GUID which will overwrite any current GUID value + * @return Reference to this object that now has a new GUID + */ + Guid& createGUID(void) throw( exceptions::RuntimeException ); + + /** + * Converts the GUID to a string and returns that string + * @return a string with this GUID's stringified value + */ + std::string toString(void) const throw( exceptions::RuntimeException ); + + /** + * Converts the GUID to a byte array and return a pointer to the + * new array, called takes ownership and must delete this array + * when done. + * @return a byte array with the GUID byte value, size = 16 + */ + const unsigned char* toBytes(void) const; + + /** + * Initializes this GUID with the GUID specified in the bytes parameter + * @return reference to this object. + */ + Guid& fromBytes(const unsigned char* bytes) + throw ( exceptions::IllegalArgumentException ); + + /** + * Returns the Size in Bytes of the Raw bytes representation of the + * GUID. + * @return size of the Raw bytes representation + */ + int getRawBytesSize(void) const; + + /** + * string type cast operator + * @returns string representation of this GUID + */ + operator std::string() const; + + /** + * byte array cast operator, caller does not own this memeory + * @returns byte array with the GUID byte value representation + */ + operator const unsigned char*() const; + + /** + * Assignment operators + * @return Reference to this GUID object + */ + Guid& operator=(const Guid& source) + throw ( exceptions::IllegalArgumentException ); + Guid& operator=(const std::string& source) + throw ( exceptions::IllegalArgumentException ); + + /** + * Equality Comparison Operators + * @return true for equal. false otherwise + */ + bool operator==(const Guid& source) const; + bool operator==(const std::string& source) const; + + /** + * Inequality Comparison Operators + * @return true for equal. false otherwise + */ + bool operator!=(const Guid& source) const; + bool operator!=(const std::string& source) const; + + /** + * Less than operators + * @return true for equal. false otherwise + */ + bool operator<(const Guid& source) const; + bool operator<(const std::string& source) const; + + /** + * Less than or equal to operators + * @return true for equal. false otherwise + */ + bool operator<=(const Guid& source) const; + bool operator<=(const std::string& source) const; + + /** + * Greater than operators + * @return true for equal. false otherwise + */ + bool operator>(const Guid& source) const; + bool operator>(const std::string& source) const; + + /** + * Greater than or equal to operators + * @return true for equal. false otherwise + */ + bool operator>=(const Guid& source) const; + bool operator>=(const std::string& source) const; + + public: + + /** + * Static Guid Creation Method, creates a GUID and returns it as a string + * @return Guid string. + */ + static std::string createGUIDString(void); + + /** + * Static Guid Create Method, create a GUID and returns the byte representation + * of the new GUID. + * @return Guid bytes array, size is 16 + */ + static const unsigned char* createGUIDBytes(void); + + private: + + // the uuid that this object represents. + #ifdef unix + uuid_t uuid; + #else + ::GUID uuid; + #endif + + }; + +}} + +#endif /*ACTIVEMQ_UTIL_GUID_H*/ diff --git a/activemq-cpp/src/main/activemq/util/Integer.h b/activemq-cpp/src/main/activemq/util/Integer.h new file mode 100644 index 0000000000..2b342539cc --- /dev/null +++ b/activemq-cpp/src/main/activemq/util/Integer.h @@ -0,0 +1,61 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_UTIL_INTEGER_H_ +#define _ACTIVEMQ_UTIL_INTEGER_H_ + +#include + +namespace activemq{ +namespace util{ + + class Integer : public Number + { + public: + + Integer(void) {} + virtual ~Integer(void) {} + + /** + * Parses the String passed and extracts an int. + * @param String to parse + * @return int value + */ + static int parseInt(const std::string& value){ + int ret = 0; + std::istringstream istream(value); + istream.clear(); + istream >> ret; + return ret; + } + + /** + * Converts the int to a String representation + * @param int to convert + * @return string representation + */ + static std::string toString(int value){ + std::ostringstream ostream; + ostream << value; + return ostream.str(); + } + + }; + +}} + +#endif /*_ACTIVEMQ_UTIL_INTEGER_H_*/ diff --git a/activemq-cpp/src/main/activemq/util/Long.h b/activemq-cpp/src/main/activemq/util/Long.h new file mode 100644 index 0000000000..6512301403 --- /dev/null +++ b/activemq-cpp/src/main/activemq/util/Long.h @@ -0,0 +1,60 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_UTIL_LONG_H_ +#define _ACTIVEMQ_UTIL_LONG_H_ + +#include + +namespace activemq{ +namespace util{ + + class Long : public Number + { + public: + + Long(void) {} + virtual ~Long(void) {} + + /** + * Parses the String passed and extracts an long. + * @param String to parse + * @return long value + */ + static long parseLong(const std::string& value){ + long ret = 0; + std::istringstream istream(value); + istream.clear(); + istream >> ret; + return ret; + } + + /** + * Converts the long to a String representation + * @param long to convert + * @return string representation + */ + static std::string toString(long value){ + std::ostringstream ostream; + ostream << value; + return ostream.str(); + } + }; + +}} + +#endif /*_ACTIVEMQ_UTIL_LONG_H_*/ diff --git a/activemq-cpp/src/main/activemq/util/Number.h b/activemq-cpp/src/main/activemq/util/Number.h new file mode 100644 index 0000000000..3d35e23efd --- /dev/null +++ b/activemq-cpp/src/main/activemq/util/Number.h @@ -0,0 +1,43 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_UTIL_NUMBER_H_ +#define _ACTIVEMQ_UTIL_NUMBER_H_ + +#include + +namespace activemq{ +namespace util{ + + /** + * The abstract class Number is the superclass of classes Byte, Double, + * Float, Integer, Long, and Short. + * + * Subclasses of Number must provide methods to convert the represented + * numeric value to byte, double, float, int, long, and short. + */ + class Number + { + public: + + virtual ~Number(void) {} + + }; + +}} + +#endif /*_ACTIVEMQ_UTIL_NUMBER_H_*/ diff --git a/activemq-cpp/src/main/activemq/util/Properties.h b/activemq-cpp/src/main/activemq/util/Properties.h new file mode 100644 index 0000000000..59466380a6 --- /dev/null +++ b/activemq-cpp/src/main/activemq/util/Properties.h @@ -0,0 +1,98 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_UTIL_PROPERTIES_H_ +#define ACTIVEMQ_UTIL_PROPERTIES_H_ + +#include +#include +#include + +namespace activemq{ +namespace util{ + + /** + * Interface for a Java-like properties object. This is essentially + * a map of key-value string pairs. + */ + class Properties{ + public: + + virtual ~Properties(){} + + /** + * Looks up the value for the given property. + * @param name The name of the property to be looked up. + * @return the value of the property with the given name, if it + * exists. If it does not exist, returns NULL. + */ + virtual const char* getProperty( const std::string& name ) const = 0; + + /** + * Looks up the value for the given property. + * @param name the name of the property to be looked up. + * @param defaultValue The value to be returned if the given + * property does not exist. + * @return The value of the property specified by name, if it + * exists, otherwise the defaultValue. + */ + virtual std::string getProperty( const std::string& name, + const std::string& defaultValue ) const = 0; + + /** + * Sets the value for a given property. If the property already + * exists, overwrites the value. + * @param name The name of the value to be written. + * @param value The value to be written. + */ + virtual void setProperty( const std::string& name, + const std::string& value ) = 0; + + /** + * Check to see if the Property exists in the set + * @return true if property exists, false otherwise. + */ + virtual bool hasProperty( const std::string& name ) const = 0; + + /** + * Method that serializes the contents of the property map to + * an arryay. + * @return list of pairs where the first is the name and the second + * is the value. + */ + virtual std::vector< std::pair > toArray() const = 0; + + /** + * Copies the contents of the given properties object to this one. + * @param source The source properties object. + */ + virtual void copy( const Properties* source ) = 0; + + /** + * Clones this object. + * @returns a replica of this object. + */ + virtual Properties* clone() const = 0; + + /** + * Clears all properties from the map. + */ + virtual void clear() = 0; + }; + +}} + +#endif /*ACTIVEMQ_UTIL_PROPERTIES_H_*/ diff --git a/activemq-cpp/src/main/activemq/util/Queue.h b/activemq-cpp/src/main/activemq/util/Queue.h new file mode 100644 index 0000000000..780ac1e120 --- /dev/null +++ b/activemq-cpp/src/main/activemq/util/Queue.h @@ -0,0 +1,308 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_UTIL_QUEUE_H +#define ACTIVEMQ_UTIL_QUEUE_H + +#include +#include +#include + +namespace activemq{ +namespace util{ + + /** + * The Queue class accepts messages with an psuh(m) command + * where m is the message to be queued. It destructively + * returns the message with pop(). pop() returns messages in + * the order they were enqueued. + * + * Queue is implemented with an instance of the STL queue object. + * The interface is essentially the same as that of the STL queue + * except that the pop method actually reaturns a reference to the + * element popped. This frees the app from having to call the + * front method before calling pop. + * + * Queue sq; // make a queue to hold string messages + * sq.push(s); // enqueues a message m + * string s = sq.pop(); // dequeues a message + * + * = DESIGN CONSIDERATIONS + * + * The Queue class inherits from the Synchronizable interface and + * provides methods for locking and unlocking this queue as well as + * waiting on this queue. In a multi-threaded app this can allow + * for multiple threads to be reading from and writing to the same + * Queue. + * + * Clients should consider that in a multiple threaded app it is + * possible that items could be placed on the queue faster than + * you are taking them off, so protection should be placed in your + * polling loop to ensure that you don't get stuck there. + */ + + template class Queue : public concurrent::Synchronizable + { + public: + + /** + * Constructor + */ + Queue(void); + + /** + * Destructor + */ + virtual ~Queue(void); + + /** + * Returns a Reference to the element at the head of the queue + * @return reference to a queue type object or (safe) + */ + T& front(void); + + /** + * Returns a Reference to the element at the head of the queue + * @return reference to a queue type object or (safe) + */ + const T& front(void) const; + + /** + * Returns a Reference to the element at the tail of the queue + * @return reference to a queue type object or (safe) + */ + T& back(void); + + /** + * Returns a Reference to the element at the tail of the queue + * @return reference to a queue type object or (safe) + */ + const T& back(void) const; + + /** + * Places a new Object at the Tail of the queue + * @param Queue Object Type reference. + */ + void push(const T &t); + + /** + * Removes and returns the element that is at the Head of the queue + * @return reference to a queue type object or (safe) + */ + T pop(void); + + /** + * Gets the Number of elements currently in the Queue + * @return Queue Size + */ + size_t size(void) const; + + /** + * Checks if this Queue is currently empty + * @return boolean indicating queue emptiness + */ + bool empty(void) const; + + /** + * Locks the object. + */ + virtual void lock() throw(exceptions::ActiveMQException){ + mutex.lock(); + } + + /** + * Unlocks the object. + */ + virtual void unlock() throw(exceptions::ActiveMQException){ + mutex.unlock(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. + */ + virtual void wait() throw(exceptions::ActiveMQException){ + mutex.wait(); + } + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. This wait will timeout after the specified time + * interval. + * @param time in millisecsonds to wait, or WAIT_INIFINITE + * @throws ActiveMQException + */ + virtual void wait(unsigned long millisecs) + throw(exceptions::ActiveMQException) { + + mutex.wait(millisecs); + } + + /** + * Signals a waiter on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + */ + virtual void notify() throw(exceptions::ActiveMQException){ + mutex.notify(); + } + + /** + * Signals the waiters on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + */ + virtual void notifyAll() throw(exceptions::ActiveMQException){ + mutex.notifyAll(); + } + + public: // Statics + + /** + * Fetch a reference to the safe value this object will return + * when there is nothing to fetch from the queue. + * @return Reference to this Queues safe object + */ + static const T& getSafeValue(void) { return safe; } + + private: + + // The real queue + std::queue queue; + + // Object used for sync + concurrent::Mutex mutex; + + // Safe value used when pop, front or back are + // called and the queue is empty. + static T safe; + + }; + + //-----{ Static Init }-----------------------------------------------------// + template + T Queue::safe; + + //-----{ Retrieve current length of Queue }--------------------------------// + + template inline size_t Queue::size() const + { + return queue.size(); + } + + //-----{ Retrieve whether Queue is empty or not }--------------------------// + + template inline bool Queue::empty(void) const + { + return queue.empty(); + } + + //-----{ Defulat Constructor }---------------------------------------------// + + template + Queue::Queue() + { + } + + //-----{ Default Destructor }----------------------------------------------// + + template Queue::~Queue() + { + } + + //-----{ Add Elements to Back of Queue }-----------------------------------// + + template + void Queue::push(const T &t) + { + queue.push(t); + } + + //-----{ Remove Elements from Front of Queue }-----------------------------// + + template + T Queue::pop(void) + { + if(queue.empty()) + { + return safe; + } + + // Pop the element into a temp, since we need to remain locked. + // this means getting front and then popping. + T temp = queue.front(); + queue.pop(); + + return temp; + } + + //-----{ Returnreference to element at front of Queue }--------------------// + + template + T& Queue::front(void) + { + if(queue.empty()) + { + return safe; + } + + return queue.front(); + } + + //-----{ Returnreference to element at front of Queue }--------------------// + + template + const T& Queue::front(void) const + { + if(queue.empty()) + { + return safe; + } + + return queue.front(); + } + + //-----{ Returnreference to element at back of Queue }---------------------// + + template + T& Queue::back(void) + { + if(queue.empty()) + { + return safe; + } + + return queue.back(); + } + + //-----{ Returnreference to element at back of Queue }---------------------// + + template + const T& Queue::back(void) const + { + if(queue.empty()) + { + return safe; + } + + return queue.back(); + } + +}} + +#endif /* ACTIVEMQ_UTIL_QUEUE_H */ diff --git a/activemq-cpp/src/main/activemq/util/SimpleProperties.h b/activemq-cpp/src/main/activemq/util/SimpleProperties.h new file mode 100644 index 0000000000..cf210e9d7e --- /dev/null +++ b/activemq-cpp/src/main/activemq/util/SimpleProperties.h @@ -0,0 +1,161 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_UTIL_SIMPLEPROPERTIES_H_ +#define ACTIVEMQ_UTIL_SIMPLEPROPERTIES_H_ + +#include +#include +#include + +namespace activemq{ +namespace util{ + + /** + * Basic implementation of the Properties interface. + */ + class SimpleProperties : public Properties{ + private: + + std::map< std::string, std::string > properties; + + public: + + virtual ~SimpleProperties(){} + + /** + * Looks up the value for the given property. + * @param name The name of the property to be looked up. + * @return the value of the property with the given name, if it + * exists. If it does not exist, returns NULL. + */ + virtual const char* getProperty( const std::string& name ) const{ + + std::map< std::string, std::string >::const_iterator iter = + properties.find( name ); + if( iter == properties.end() ){ + return NULL; + } + + return iter->second.c_str(); + } + + /** + * Looks up the value for the given property. + * @param name the name of the property to be looked up. + * @param defaultValue The value to be returned if the given + * property does not exist. + * @return The value of the property specified by name, if it + * exists, otherwise the defaultValue. + */ + virtual std::string getProperty( const std::string& name, + const std::string& defaultValue ) const { + + std::map< std::string, std::string >::const_iterator iter = + properties.find( name ); + if( iter == properties.end() ){ + return defaultValue; + } + + return iter->second; + } + + /** + * Sets the value for a given property. If the property already + * exists, overwrites the value. + * @param name The name of the value to be written. + * @param value The value to be written. + */ + virtual void setProperty( const std::string& name, + const std::string& value ){ + properties[name] = value; + } + + /** + * Check to see if the Property exists in the set + * @return true if property exists, false otherwise. + */ + virtual bool hasProperty( const std::string& name ) const + { + if(properties.find(name) != properties.end()) + { + return true; + } + + return false; + } + + /** + * Method that serializes the contents of the property map to + * an arryay. + * @return list of pairs where the first is the name and the second + * is the value. + */ + virtual std::vector< std::pair > toArray() const{ + + // Create a vector big enough to hold all the elements in the map. + std::vector< std::pair > vec( properties.size() ); + + // Get an iterator at the beginning of the map. + std::map< std::string, std::string >::const_iterator iter = properties.begin(); + + // Copy all of the elements from the map to the vector. + for( int ix=0; iter != properties.end(); ++iter, ++ix ){ + vec[ix] = *iter; + } + + return vec; + } + + /** + * Copies the contents of the given properties object to this one. + * @param source The source properties object. + */ + virtual void copy( const Properties* source ){ + + clear(); + + std::vector< std::pair< std::string, std::string > > vec = + source->toArray(); + for( unsigned int ix=0; ixproperties = properties; + + return props; + } + + /** + * Clears all properties from the map. + */ + virtual void clear(){ + properties.clear(); + } + }; + +}} + +#endif /*ACTIVEMQ_UTIL_SIMPLEPROPERTIES_H_*/ diff --git a/activemq-cpp/src/main/activemq/util/StringTokenizer.cpp b/activemq-cpp/src/main/activemq/util/StringTokenizer.cpp new file mode 100644 index 0000000000..723359b286 --- /dev/null +++ b/activemq-cpp/src/main/activemq/util/StringTokenizer.cpp @@ -0,0 +1,171 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "StringTokenizer.h" + +using namespace std; +using namespace activemq; +using namespace activemq::util; +using namespace activemq::exceptions; + +//////////////////////////////////////////////////////////////////////////////// +StringTokenizer::StringTokenizer(const std::string& str, + const std::string& delim, + bool returnDelims) +{ + // store off the data + this->str = str; + this->delim = delim; + this->returnDelims = returnDelims; + + // Start and the beginning + pos = 0; +} + +//////////////////////////////////////////////////////////////////////////////// +StringTokenizer::~StringTokenizer(void) +{ +} + +//////////////////////////////////////////////////////////////////////////////// +int StringTokenizer::countTokens(void) const +{ + int count = 0; + string::size_type localPos = pos; + string::size_type lastPos = pos; + + while(localPos != string::npos) + { + if(returnDelims && str.find_first_of(delim, localPos) == localPos) + { + count += 1; + localPos += 1; + + continue; + } + + // Find first token by spanning the fist non-delimiter, to the + // next delimiter, skipping any delimiters that are at the curret + // location. + lastPos = str.find_first_not_of(delim, localPos); + localPos = str.find_first_of(delim, lastPos); + + if(lastPos != string::npos) + { + count++; + } + } + + return count; +} + +//////////////////////////////////////////////////////////////////////////////// +bool StringTokenizer::hasMoreTokens(void) const +{ + string::size_type nextpos = + returnDelims ? str.find_first_of(delim, pos) : + str.find_first_not_of(delim, pos); + + return (nextpos != string::npos); +} + +//////////////////////////////////////////////////////////////////////////////// +std::string StringTokenizer::nextToken(void) + throw ( exceptions::NoSuchElementException ) +{ + if(pos == string::npos) + { + throw NoSuchElementException( + __FILE__, __LINE__, + "StringTokenizer::nextToken - No more Tokens available"); + } + + if(returnDelims) + { + // if char at current pos is a delim return it and advance pos + if(str.find_first_of(delim, pos) == pos) + { + return str.substr(pos++, 1); + } + } + + // Skip delimiters at beginning. + string::size_type lastPos = str.find_first_not_of(delim, pos); + + // Find the next delimiter in the string, the charactors in between + // will be the token to return. If this returns string::npos then + // there are no more delimiters in the string. + pos = str.find_first_of(delim, lastPos); + + if(string::npos != lastPos) + { + // Found a token, count it, if the pos of the next delim is npos + // then we set length to copy to npos so that all the remianing + // portion of the string is copied, otherwise we set it to the + return str.substr(lastPos, + pos == string::npos ? pos : pos-lastPos); + } + else + { + throw NoSuchElementException( + __FILE__, __LINE__, + "StringTokenizer::nextToken - No more Tokens available"); + } +} + +//////////////////////////////////////////////////////////////////////////////// +std::string StringTokenizer::nextToken(const std::string& delim) + throw ( exceptions::NoSuchElementException ) +{ + this->delim = delim; + + return nextToken(); +} + +//////////////////////////////////////////////////////////////////////////////// +unsigned int StringTokenizer::toArray(std::vector& array) +{ + int count = 0; + + while(hasMoreTokens()) + { + array.push_back(nextToken()); + count++; + } + + return count; +} + +//////////////////////////////////////////////////////////////////////////////// +void StringTokenizer::reset(const std::string& str, + const std::string& delim, + bool returnDelims) +{ + if(str != "") + { + this->str = str; + } + + if(delim != "") + { + this->delim = delim; + } + + this->returnDelims = returnDelims; + + // Begin at the Beginning + this->pos = 0; +} diff --git a/activemq-cpp/src/main/activemq/util/StringTokenizer.h b/activemq-cpp/src/main/activemq/util/StringTokenizer.h new file mode 100644 index 0000000000..8c76c01b36 --- /dev/null +++ b/activemq-cpp/src/main/activemq/util/StringTokenizer.h @@ -0,0 +1,139 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _ACTIVEMQ_UTIL_STRINGTOKENIZER_H_ +#define _ACTIVEMQ_UTIL_STRINGTOKENIZER_H_ + +#include +#include + +namespace activemq{ +namespace util{ + + class StringTokenizer + { + private: + + // String to tokenize + std::string str; + + // The delimiter string + std::string delim; + + // The current pos in the string + std::string::size_type pos; + + // Are we returning delimiters + bool returnDelims; + + public: + + /** + * Constructs a string tokenizer for the specified string. All + * characters in the delim argument are the delimiters for separating + * tokens. + * + * If the returnDelims flag is true, then the delimiter characters are + * also returned as tokens. Each delimiter is returned as a string of + * length one. If the flag is false, the delimiter characters are + * skipped and only serve as separators between tokens. + * + * Note that if delim is "", this constructor does not throw an + * exception. However, trying to invoke other methods on the resulting + * StringTokenizer may result in an Exception. + * @param string to tokenize + * @param String containing the delimiters + * @param boolean indicating if the delimiters are returned as tokens + */ + StringTokenizer(const std::string& str, + const std::string& delim = " \t\n\r\f", + bool returnDelims = false); + + /** + * Destructor + */ + virtual ~StringTokenizer(void); + + /** + * Calculates the number of times that this tokenizer's nextToken + * method can be called before it generates an exception. The current + * position is not advanced. + * @return Count of remaining tokens + */ + virtual int countTokens(void) const; + + /** + * Tests if there are more tokens available from this tokenizer's + * string. + * @return true if there are more tokens remaining + */ + virtual bool hasMoreTokens(void) const; + + /** + * Returns the next token from this string tokenizer. + * @return string value of next token + * @thorws NoSuchElementException + */ + virtual std::string nextToken(void) + throw ( exceptions::NoSuchElementException ); + + /** + * Returns the next token in this string tokenizer's string. First, + * the set of characters considered to be delimiters by this + * StringTokenizer object is changed to be the characters in the + * string delim. Then the next token in the string after the current + * position is returned. The current position is advanced beyond the + * recognized token. The new delimiter set remains the default after + * this call. + * @param string containing the new set of delimiters + * @return next string in the token list + * @throw NoSuchElementException + */ + virtual std::string nextToken(const std::string& delim) + throw ( exceptions::NoSuchElementException ); + + /** + * Grab all remaining tokens in the String and return them + * in the vector that is passed in by reference. + * @param vector to place token strings in + * @return number of string placed into the vector + */ + virtual unsigned int toArray(std::vector& array); + + /** + * Resets the Tokenizer's position in the String to the Beginning + * calls to countToken and nextToken now start back at the beginning. + * This allows this object to be reused, the caller need not create + * a new instance every time a String needs tokenizing. + * If set the string param will reset the string that this Tokenizer + * is working on. If set to "" no change is made. + * If set the delim param will reset the string that this Tokenizer + * is using to tokenizer the string. If set to "", no change is made + * If set the return Delims will set if this Tokenizer will return + * delimiters as tokens. Defaults to false. + * @param New String to tokenize or "", defaults to "" + * @param New Delimiter String to use or "", defaults to "" + * @param Should the Tokenizer return delimiters as Tokens, default false + */ + virtual void reset(const std::string& str = "", + const std::string& delim = "", + bool returnDelims = false); + + }; + +}} + +#endif /*_ACTIVEMQ_UTIL_STRINGTOKENIZER_H_*/ diff --git a/activemq-cpp/src/main/cms/BytesMessage.h b/activemq-cpp/src/main/cms/BytesMessage.h new file mode 100644 index 0000000000..e9fab5677d --- /dev/null +++ b/activemq-cpp/src/main/cms/BytesMessage.h @@ -0,0 +1,62 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _CMS_BYTESMESSAGE_H_ +#define _CMS_BYTESMESSAGE_H_ + +#include + +namespace cms{ + + class BytesMessage : public Message{ + + public: + + /** + * Destructor + */ + virtual ~BytesMessage(){} + + /** + * sets the bytes given to the message body. + * @param Byte Buffer to copy + * @param Number of bytes in Buffer to copy + * @throws CMSException + */ + virtual void setBodyBytes( + const unsigned char* buffer, const unsigned long numBytes ) + throw( CMSException ) = 0; + + /** + * Gets the bytes that are contained in this message, user should + * copy this data into a user allocated buffer. Call + * getBodyLength to determine the number of bytes + * to expect. + * @return const pointer to a byte buffer + */ + virtual const unsigned char* getBodyBytes(void) const = 0; + + /** + * Returns the number of bytes contained in the body of this message. + * @return number of bytes. + */ + virtual unsigned long getBodyLength(void) const = 0; + + }; +} + +#endif /*_CMS_BYTESMESSAGE_H_*/ diff --git a/activemq-cpp/src/main/cms/CMSException.h b/activemq-cpp/src/main/cms/CMSException.h new file mode 100644 index 0000000000..351bbe899e --- /dev/null +++ b/activemq-cpp/src/main/cms/CMSException.h @@ -0,0 +1,66 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 CMS_CMSEXCEPTION_H +#define CMS_CMSEXCEPTION_H + +// Includes +#include +#include +#include + +namespace cms{ + + /** + * This class represents an error that has occurred in + * cms. + */ + class CMSException{ + + public: + + /** + * Destruction + */ + virtual ~CMSException(){} + + /** + * Gets the cause of the error. + */ + virtual const char* getMessage() const = 0; + + /** + * Provides the stack trace for every point where + * this exception was caught, marked, and rethrown. + */ + virtual std::vector< std::pair< std::string, int> > getStackTrace() const = 0; + + /** + * Prints the stack trace to std::err + */ + virtual void printStackTrace() const = 0; + + /** + * Prints the stack trace to the given output stream. + * @param stream the target output stream. + */ + virtual void printStackTrace( std::ostream& stream ) const = 0; + }; + +} + +#endif /*CMS_CMSEXCEPTION_H*/ diff --git a/activemq-cpp/src/main/cms/Closeable.h b/activemq-cpp/src/main/cms/Closeable.h new file mode 100644 index 0000000000..10f7b259af --- /dev/null +++ b/activemq-cpp/src/main/cms/Closeable.h @@ -0,0 +1,41 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 CMS_CLOSEABLE_H +#define CMS_CLOSEABLE_H + +#include + +namespace cms{ + + /** + * Interface for a class that implements the close method. + */ + class Closeable{ + + public: + + virtual ~Closeable(void){} + + /** + * Closes this object and deallocates the appropriate resources. + */ + virtual void close() throw( CMSException ) = 0; + }; +} + +#endif /*CMS_CLOSEABLE_H*/ diff --git a/activemq-cpp/src/main/cms/Connection.h b/activemq-cpp/src/main/cms/Connection.h new file mode 100644 index 0000000000..66fa7bd609 --- /dev/null +++ b/activemq-cpp/src/main/cms/Connection.h @@ -0,0 +1,76 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _CMS_CONNECTION_H_ +#define _CMS_CONNECTION_H_ + +#include +#include +#include +#include + +namespace cms +{ + class ExceptionListener; + + class Connection : + public Startable, + public Stoppable, + public Closeable + { + public: + + /** + * Destructor + */ + virtual ~Connection(void) {} + + /** + * Creates a new Session to work for this Connection + */ + virtual Session* createSession(void) throw ( CMSException ) = 0; + + /** + * Creates a new Session to work for this Connection using the + * specified acknowledgment mode + * @param the Acknowledgement Mode to use. + */ + virtual Session* createSession(Session::AcknowledgeMode ackMode) + throw ( CMSException ) = 0; + + /** + * Get the Client Id for this session + */ + virtual std::string getClientId(void) const = 0; + + /** + * Gets the registered Exception Listener for this connection + * @return pointer to an exception listnener or NULL + */ + virtual ExceptionListener* getExceptionListener(void) const = 0; + + /** + * Sets the registed Exception Listener for this connection + * @param pointer to and ExceptionListener + */ + virtual void setExceptionListener(ExceptionListener* listener) = 0; + + }; + +} + +#endif /*_CMS_CONNECTION_H_*/ diff --git a/activemq-cpp/src/main/cms/ConnectionFactory.h b/activemq-cpp/src/main/cms/ConnectionFactory.h new file mode 100644 index 0000000000..33361b7987 --- /dev/null +++ b/activemq-cpp/src/main/cms/ConnectionFactory.h @@ -0,0 +1,61 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _CMS_CONNECTIONFACTORY_H_ +#define _CMS_CONNECTIONFACTORY_H_ + +#include +#include + +#include + +namespace cms +{ + + class ConnectionFactory + { + public: + + /** + * Destructor + */ + virtual ~ConnectionFactory(void) {} + + /** + * Creates a connection with the default user identity. The + * connection is created in stopped mode. No messages will be + * delivered until the Connection.start method is explicitly + * called. + * @throws CMSException + */ + virtual Connection* createConnection(void) throw ( CMSException ) = 0; + + /** + * Creates a connection with the specified user identity. The + * connection is created in stopped mode. No messages will be + * delivered until the Connection.start method is explicitly called. + * @throw CMSException. + */ + virtual Connection* createConnection(const std::string& username, + const std::string& password, + const std::string& clientId) + throw ( CMSException ) = 0; + + }; + +} + +#endif /*_CMS_CONNECTIONFACTORY_H_*/ diff --git a/activemq-cpp/src/main/cms/Destination.h b/activemq-cpp/src/main/cms/Destination.h new file mode 100644 index 0000000000..5d2659436a --- /dev/null +++ b/activemq-cpp/src/main/cms/Destination.h @@ -0,0 +1,82 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _CMS_DESTINATION_H_ +#define _CMS_DESTINATION_H_ + +#include + +namespace cms{ + + /** + * A Destination object encapsulates a provider-specific address. + */ + class Destination{ + public: + + enum DestinationType + { + TOPIC, + QUEUE, + TEMPORARY_TOPIC, + TEMPORARY_QUEUE + }; + + public: + + /** + * Destructor + */ + virtual ~Destination(void){} + + /** + * Retrieve the Destination Type for this Destination + * @return The Destination Type + */ + virtual DestinationType getDestinationType(void) const = 0; + + /** + * Converts the Destination Name into a String + * @return string name + */ + virtual std::string toString(void) const = 0; + + /** + * Converts the Destination to a String value representing the + * Provider specific name fot this destination, which is not + * necessarily equal to the User Supplied name of the Destination + * @return Provider specific Name + */ + virtual std::string toProviderString(void) const = 0; + + /** + * Creates a new instance of this destination type that is a + * copy of this one, and returns it. + * @returns cloned copy of this object + */ + virtual cms::Destination* clone(void) const = 0; + + /** + * Copies the contents of the given Destinastion object to this one. + * @param source The source Destination object. + */ + virtual void copy( const cms::Destination& source ) = 0; + + }; +} + +#endif /*_CMS_DESTINATION_H_*/ diff --git a/activemq-cpp/src/main/cms/ExceptionListener.h b/activemq-cpp/src/main/cms/ExceptionListener.h new file mode 100644 index 0000000000..190c09df61 --- /dev/null +++ b/activemq-cpp/src/main/cms/ExceptionListener.h @@ -0,0 +1,44 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _CMS_EXCEPTIONLISTENER_H_ +#define _CMS_EXCEPTIONLISTENER_H_ + +#include + +namespace cms{ + + class ExceptionListener + { + public: + + /** + * Destructor + */ + virtual ~ExceptionListener(void) {} + + /** + * Called when an exception occurs. + * @param Exception Object that occurred. + */ + virtual void onException(const cms::CMSException& ex) = 0; + + }; + +} + +#endif /*_CMS_EXCEPTIONLISTENER_H_*/ diff --git a/activemq-cpp/src/main/cms/MapMessage.h b/activemq-cpp/src/main/cms/MapMessage.h new file mode 100644 index 0000000000..502bf28b62 --- /dev/null +++ b/activemq-cpp/src/main/cms/MapMessage.h @@ -0,0 +1,38 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _CMS_MAPMESSAGE_H_ +#define _CMS_MAPMESSAGE_H_ + +#include + +namespace cms +{ + + class MapMessage : public Message + { + public: + + /** + * Destructor + */ + virtual ~MapMessage(void) {} + + }; + +} + +#endif /*_CMS_MAPMESSAGE_H_*/ diff --git a/activemq-cpp/src/main/cms/Message.h b/activemq-cpp/src/main/cms/Message.h new file mode 100644 index 0000000000..caefcab96a --- /dev/null +++ b/activemq-cpp/src/main/cms/Message.h @@ -0,0 +1,190 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _CMS_MESSAGE_H_ +#define _CMS_MESSAGE_H_ + +#include + +#include +#include + +namespace cms{ + + /** + * Root of all messages. + */ + class Message{ + public: + + /** + * Enumeration value for Message Delivery Mode + */ + enum DeliveryMode + { + PERSISTANT, + NONPERSISTANT + }; + + public: + + virtual ~Message(void){} + + /** + * Clonse this message exactly, returns a new instance that the + * caller is required to delete. + * @return new copy of this message + */ + virtual Message* clone(void) const = 0; + + /** + * Acknowledges all consumed messages of the session + * of this consumed message. + */ + virtual void acknowledge(void) const throw( CMSException ) = 0; + + /** + * Retrieves a reference to the properties object owned + * by this message + * @return A Properties Object reference + */ + virtual activemq::util::Properties& getProperties(void) = 0; + virtual const activemq::util::Properties& getProperties(void) const = 0; + + /** + * Get the Correlation Id for this message + * @return string representation of the correlation Id + */ + virtual const char* getCMSCorrelationId(void) const = 0; + + /** + * Sets the Correlation Id used by this message + * @param String representing the correlation id. + */ + virtual void setCMSCorrelationId(const std::string& correlationId) = 0; + + /** + * Sets the DeliveryMode for this message + * @return DeliveryMode enumerated value. + */ + virtual DeliveryMode getCMSDeliveryMode(void) const = 0; + + /** + * Sets the DeliveryMode for this message + * @param DeliveryMode enumerated value. + */ + virtual void setCMSDeliveryMode(DeliveryMode mode) = 0; + + /** + * Gets the Destination for this Message, returns a + * @return Destination object + */ + virtual const Destination& getCMSDestination(void) const = 0; + + /** + * Sets the Destination for this message + * @param Destination Object + */ + virtual void setCMSDestination(const Destination& destination) = 0; + + /** + * Gets the Expiration Time for this Message + * @return time value + */ + virtual long getCMSExpiration(void) const = 0; + + /** + * Sets the Expiration Time for this message + * @param time value + */ + virtual void setCMSExpiration(long expireTime) = 0; + + /** + * Gets the CMS Message Id for this Message + * @return time value + */ + virtual const char* getCMSMessageId(void) const = 0; + + /** + * Sets the CMS Message Id for this message + * @param time value + */ + virtual void setCMSMessageId(const std::string& id) = 0; + + /** + * Gets the Priority Value for this Message + * @return priority value + */ + virtual int getCMSPriority(void) const = 0; + + /** + * Sets the Priority Value for this message + * @param priority value + */ + virtual void setCMSPriority(int priority) = 0; + + /** + * Gets the Redelivered Flag for this Message + * @return redelivered value + */ + virtual bool getCMSRedelivered(void) const = 0; + + /** + * Sets the Redelivered Flag for this message + * @param redelivered value + */ + virtual void setCMSRedelivered(bool redelivered) = 0; + + /** + * Gets the CMS Reply To Address for this Message + * @return Reply To Value + */ + virtual const char* getCMSReplyTo(void) const = 0; + + /** + * Sets the CMS Reply To Address for this message + * @param Reply To value + */ + virtual void setCMSReplyTo(const std::string& id) = 0; + + /** + * Gets the Time Stamp for this Message + * @return time stamp value + */ + virtual long getCMSTimeStamp(void) const = 0; + + /** + * Sets the Time Stamp for this message + * @param time stamp value + */ + virtual void setCMSTimeStamp(long timeStamp) = 0; + + /** + * Gets the CMS Message Type for this Message + * @return type value + */ + virtual const char* getCMSMessageType(void) const = 0; + + /** + * Sets the CMS Message Type for this message + * @param type value + */ + virtual void setCMSMessageType(const std::string& type) = 0; + }; +} + +#endif /*_CMS_MESSAGE_H_*/ diff --git a/activemq-cpp/src/main/cms/MessageConsumer.h b/activemq-cpp/src/main/cms/MessageConsumer.h new file mode 100644 index 0000000000..2e333ede48 --- /dev/null +++ b/activemq-cpp/src/main/cms/MessageConsumer.h @@ -0,0 +1,83 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _CMS_MESSAGECONSUMER_H_ +#define _CMS_MESSAGECONSUMER_H_ + +#include +#include + +namespace cms +{ + + class MessageConsumer + { + public: + + /** + * Destructor + */ + virtual ~MessageConsumer(void) {} + + /** + * Synchronously Receive a Message + * @return new message + * @throws CMSException + */ + virtual Message* receive(void) throw ( CMSException ) = 0; + + /** + * Synchronously Receive a Message, time out after defined interval. + * Returns null if nothing read. + * @return new message + * @throws CMSException + */ + virtual Message* receive(int millisecs) throw ( CMSException ) = 0; + + /** + * Receive a Message, does not wait if there isn't a new message + * to read, returns NULL if nothing read. + * @return new message + * @throws CMSException + */ + virtual Message* receiveNoWait(void) throw ( CMSException ) = 0; + + /** + * Sets the MessageListener that this class will send notifs on + * @param MessageListener interface pointer + */ + virtual void setMessageListener(MessageListener* listener) = 0; + + /** + * Gets the MessageListener that this class will send notifs on + * @param MessageListener interface pointer + */ + virtual MessageListener* getMessageListener(void) const = 0; + + /** + * Gets this message consumer's message selector expression. + * @return This Consumer's selector expression or "". + * @throws cms::CMSException + */ + virtual std::string getMessageSelector(void) const + throw ( cms::CMSException ) = 0; + + }; + +} + +#endif /*_CMS_MESSAGECONSUMER_H_*/ diff --git a/activemq-cpp/src/main/cms/MessageListener.h b/activemq-cpp/src/main/cms/MessageListener.h new file mode 100644 index 0000000000..dcbc5894c7 --- /dev/null +++ b/activemq-cpp/src/main/cms/MessageListener.h @@ -0,0 +1,52 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _CMS_MESSAGELISTENER_H_ +#define _CMS_MESSAGELISTENER_H_ + +//#include + +namespace cms{ + + class Message; + + class MessageListener{ + public: + + virtual ~MessageListener(void){} + + /** + * Called asynchronously when a new message is received, the message + * reference can be to any othe Message types. a dynamic cast is used + * to find out what type of message this is. The lifetime of this + * object is only garunteed to be for life of the onMessage function + * after this returns the message may no longer exists. User should + * copy the data or clone the message if they wish to keep something + * around about this message. + * + * It is considered a programming error for this method to throw and + * exception. + * + * @param Message object reference + */ + virtual void onMessage( const Message& message ) = 0; + + }; + +} + +#endif /*_CMS_MESSAGELISTENER_H_*/ diff --git a/activemq-cpp/src/main/cms/MessageProducer.h b/activemq-cpp/src/main/cms/MessageProducer.h new file mode 100644 index 0000000000..34452f0708 --- /dev/null +++ b/activemq-cpp/src/main/cms/MessageProducer.h @@ -0,0 +1,120 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _CMS_MESSAGEPRODUCER_H_ +#define _CMS_MESSAGEPRODUCER_H_ + +#include +#include +#include + +namespace cms +{ + /** + * defines the MEssageProducer interface that is used + * by all MessageProducer derivations. This class defines the JMS + * spec'd interface for a MessageProducer. + */ + class MessageProducer + { + public: + + /** + * Destructor + */ + virtual ~MessageProducer(void) {} + + /** + * Sends the message to the default producer destination. + * @param a Message Object Pointer + * @throws CMSException + */ + virtual void send(Message& message) throw ( CMSException ) = 0; + + /** + * Sends the message to the designated destination. + * @param a Message Object Pointer + * @throws CMSException + */ + virtual void send(const Destination& destination, + Message& message) throw ( CMSException ) = 0; + + /** + * Sets the delivery mode for this Producer + * @param The DeliveryMode + */ + virtual void setDeliveryMode(Message::DeliveryMode mode) = 0; + + /** + * Gets the delivery mode for this Producer + * @return The DeliveryMode + */ + virtual Message::DeliveryMode getDeliveryMode(void) const = 0; + + /** + * Sets if Message Ids are disbled for this Producer + * @param boolean indicating enable / disable (true / false) + */ + virtual void setDisableMessageId(bool value) = 0; + + /** + * Sets if Message Ids are disbled for this Producer + * @param boolean indicating enable / disable (true / false) + */ + virtual bool getDisableMessageId(void) const = 0; + + /** + * Sets if Message Time Stamps are disbled for this Producer + * @param boolean indicating enable / disable (true / false) + */ + virtual void setDisableMessageTimeStamp(bool value) = 0; + + /** + * Sets if Message Time Stamps are disbled for this Producer + * @param boolean indicating enable / disable (true / false) + */ + virtual bool getDisableMessageTimeStamp(void) const = 0; + + /** + * Sets the Priority that this Producers sends messages at + * @param int value for Priority level + */ + virtual void setPriority(int priority) = 0; + + /** + * Gets the Priority level that this producer sends messages at + * @return int based priority level + */ + virtual int getPriority(void) const = 0; + + /** + * Sets the Time to Live that this Producers sends messages with + * @param int value for time to live + */ + virtual void setTimeToLive(int time) = 0; + + /** + * Gets the Time to Live that this producer sends messages with + * @return int based Time to Live + */ + virtual int getTimeToLive(void) const = 0; + + }; + +} + +#endif /*_CMS_MESSAGEPRODUCER_H_*/ diff --git a/activemq-cpp/src/main/cms/Queue.h b/activemq-cpp/src/main/cms/Queue.h new file mode 100644 index 0000000000..8bc86c7948 --- /dev/null +++ b/activemq-cpp/src/main/cms/Queue.h @@ -0,0 +1,46 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _CMS_QUEUE_H_ +#define _CMS_QUEUE_H_ + +#include +#include + +namespace cms{ + + /** + * An interface encapsulating a provider-specific queue name. + */ + class Queue : public Destination{ + + public: + + virtual ~Queue(void){} + + /** + * Gets the name of this queue. + * @return The queue name. + */ + virtual std::string getQueueName() const + throw( CMSException ) = 0; + + }; + +} + +#endif /*_CMS_QUEUE_H_*/ diff --git a/activemq-cpp/src/main/cms/Session.h b/activemq-cpp/src/main/cms/Session.h new file mode 100644 index 0000000000..ce806d389c --- /dev/null +++ b/activemq-cpp/src/main/cms/Session.h @@ -0,0 +1,232 @@ +/* +* Copyright 2006 The Apache Software Foundation or its licensors, as +* applicable. +* +* 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 _CMS_SESSION_H_ +#define _CMS_SESSION_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace cms +{ + + class Session : public Closeable + { + public: + + enum AcknowledgeMode + { + /** + * With this acknowledgment mode, the session automatically + * acknowledges a client's receipt of a message either when + * the session has successfully returned from a call to receive + * or when the message listener the session has called to + * process the message successfully returns. + */ + AutoAcknowledge, + + /** + * With this acknowledgment mode, the session automatically + * acknowledges a client's receipt of a message either when + * the session has successfully returned from a call to receive + * or when the message listener the session has called to + * process the message successfully returns. Acknowlegements + * may be delayed in this mode to increase performance at + * the cost of the message being redelivered this client fails. + */ + DupsOkAcknowledge, + + /** + * With this acknowledgment mode, the client acknowledges a + * consumed message by calling the message's acknowledge method. + */ + ClientAcknowledge, + + /** + * Messages will be consumed when the transaction commits. + */ + Transactional + }; + + public: + + /** + * Destructor + */ + virtual ~Session(void) {} + + /** + * Commits all messages done in this transaction and releases any + * locks currently held. + * @throws CMSException + */ + virtual void commit(void) throw ( CMSException ) = 0; + + /** + * Rollsback all messages done in this transaction and releases any + * locks currently held. + * @throws CMSException + */ + virtual void rollback(void) throw ( CMSException ) = 0; + + /** + * Creates a MessageConsumer for the specified destination. + * @param the Destination that this consumer receiving messages for. + * @throws CMSException + */ + virtual MessageConsumer* createConsumer( + Destination& destination ) + throw ( CMSException ) = 0; + + /** + * Creates a MessageConsumer for the specified destination, using a + * message selector. + * @param the Destination that this consumer receiving messages for. + * @throws CMSException + */ + virtual MessageConsumer* createConsumer( + Destination& destination, + const std::string& selector ) + throw ( CMSException ) = 0; + + /** + * Creates a durable subscriber to the specified topic, using a + * message selector + * @param the topic to subscribe to + * @param name used to identify the subscription + * @param only messages matching the selector are received + * @throws CMSException + */ + virtual MessageConsumer* createDurableConsumer( + Topic& destination, + const std::string& name, + const std::string& selector, + bool noLocal = false ) + throw ( CMSException ) = 0; + + /** + * Creates a MessageProducer to send messages to the specified + * destination. + * @param the Destination to publish on + * @throws CMSException + */ + virtual MessageProducer* createProducer( Destination& destination ) + throw ( CMSException ) = 0; + + /** + * Creates a queue identity given a Queue name. + * @param the name of the new Queue + * @throws CMSException + */ + virtual Queue* createQueue( const std::string& queueName ) + throw ( CMSException ) = 0; + + /** + * Creates a topic identity given a Queue name. + * @param the name of the new Topic + * @throws CMSException + */ + virtual Topic* createTopic( const std::string& topicName ) + throw ( CMSException ) = 0; + + /** + * Creates a TemporaryQueue object. + * @throws CMSException + */ + virtual TemporaryQueue* createTemporaryQueue(void) + throw ( CMSException ) = 0; + + /** + * Creates a TemporaryTopic object. + * @throws CMSException + */ + virtual TemporaryTopic* createTemporaryTopic(void) + throw ( CMSException ) = 0; + + /** + * Creates a new Message + * @throws CMSException + */ + virtual Message* createMessage(void) + throw ( CMSException ) = 0; + + /** + * Creates a BytesMessage + * @throws CMSException + */ + virtual BytesMessage* createBytesMessage(void) + throw ( CMSException) = 0; + + /** + * Creates a BytesMessage and sets the paylod to the passed value + * @param an array of bytes to set in the message + * @param the size of the bytes array, or number of bytes to use + * @throws CMSException + */ + virtual BytesMessage* createBytesMessage( + const unsigned char* bytes, + unsigned long bytesSize ) + throw ( CMSException) = 0; + + /** + * Creates a new TextMessage + * @throws CMSException + */ + virtual TextMessage* createTextMessage(void) + throw ( CMSException ) = 0; + + /** + * Creates a new TextMessage and set the text to the value given + * @param the initial text for the message + * @throws CMSException + */ + virtual TextMessage* createTextMessage( const std::string& text ) + throw ( CMSException ) = 0; + + /** + * Creates a new MapMessage + * @throws CMSException + */ + virtual MapMessage* createMapMessage(void) + throw ( CMSException ) = 0; + + /** + * Returns the acknowledgement mode of the session. + * @return the Sessions Acknowledge Mode + */ + virtual AcknowledgeMode getAcknowledgeMode(void) const = 0; + + /** + * Gets if the Sessions is a Transacted Session + * @return transacted true - false. + */ + virtual bool isTransacted(void) const = 0; + + }; + +} + +#endif /*_CMS_SESSION_H_*/ diff --git a/activemq-cpp/src/main/cms/Startable.h b/activemq-cpp/src/main/cms/Startable.h new file mode 100644 index 0000000000..01c6110441 --- /dev/null +++ b/activemq-cpp/src/main/cms/Startable.h @@ -0,0 +1,41 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 CMS_STARTABLE_H +#define CMS_STARTABLE_H + +#include + +namespace cms{ + + /** + * Interface for a class that implements the start method. + */ + class Startable{ + + public: + + virtual ~Startable(){} + + /** + * Starts the service. + */ + virtual void start() throw( CMSException ) = 0; + }; +} + +#endif /*CMS_STARTABLE_H*/ diff --git a/activemq-cpp/src/main/cms/Stoppable.h b/activemq-cpp/src/main/cms/Stoppable.h new file mode 100644 index 0000000000..9ba4f3e8c6 --- /dev/null +++ b/activemq-cpp/src/main/cms/Stoppable.h @@ -0,0 +1,41 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 CMS_STOPPABLE_H +#define CMS_STOPPABLE_H + +#include + +namespace cms{ + + /** + * Interface for a class that implements the stop method. + */ + class Stoppable{ + + public: + + virtual ~Stoppable(){} + + /** + * Stops this service. + */ + virtual void stop() throw( CMSException ) = 0; + }; +} + +#endif /*CMS_STOPPABLE_H*/ diff --git a/activemq-cpp/src/main/cms/TemporaryQueue.h b/activemq-cpp/src/main/cms/TemporaryQueue.h new file mode 100644 index 0000000000..60e490c96e --- /dev/null +++ b/activemq-cpp/src/main/cms/TemporaryQueue.h @@ -0,0 +1,46 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _CMS_TEMPORARYQUEUE_H_ +#define _CMS_TEMPORARYQUEUE_H_ + +#include +#include + +namespace cms{ + + /** + * An interface encapsulating a provider-specific queue name. + */ + class TemporaryQueue : public Destination + { + public: + + virtual ~TemporaryQueue(void) {} + + /** + * Gets the name of this queue. + * @return The queue name. + */ + virtual const char* getQueueName(void) const + throw( CMSException ) = 0; + + }; + +} + +#endif /*_CMS_TEMPORARYQUEUE_H_*/ diff --git a/activemq-cpp/src/main/cms/TemporaryTopic.h b/activemq-cpp/src/main/cms/TemporaryTopic.h new file mode 100644 index 0000000000..7c81215f37 --- /dev/null +++ b/activemq-cpp/src/main/cms/TemporaryTopic.h @@ -0,0 +1,46 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _CMS_TEMPORARYTOPIC_H_ +#define _CMS_TEMPORARYTOPIC_H_ + +#include +#include + +namespace cms{ + + /** + * An interface encapsulating a provider-specific topic name. + */ + class TemporaryTopic : public Destination + { + public: + + virtual ~TemporaryTopic(void) {} + + /** + * Gets the name of this topic. + * @return The topic name. + */ + virtual const char* getTopicName(void) + const throw( CMSException ) = 0; + + }; + +} + +#endif /*_CMS_TEMPORARYTOPIC_H_*/ diff --git a/activemq-cpp/src/main/cms/TextMessage.h b/activemq-cpp/src/main/cms/TextMessage.h new file mode 100644 index 0000000000..378cb7484d --- /dev/null +++ b/activemq-cpp/src/main/cms/TextMessage.h @@ -0,0 +1,49 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _CMS_TEXTMESSAGE_H_ +#define _CMS_TEXTMESSAGE_H_ + +#include +#include + +namespace cms{ + + /** + * Interface for a text message. + */ + class TextMessage : public Message{ + + public: + + virtual ~TextMessage(){} + + /** + * Gets the message character buffer. + * @return The message character buffer. + */ + virtual const char* getText() const throw( CMSException ) = 0; + + /** + * Sets the message contents. + * @param msg The message buffer. + */ + virtual void setText( const char* msg ) throw( CMSException ) = 0; + }; +} + +#endif /*_CMS_TEXTMESSAGE_H_*/ diff --git a/activemq-cpp/src/main/cms/Topic.h b/activemq-cpp/src/main/cms/Topic.h new file mode 100644 index 0000000000..5a882eef6c --- /dev/null +++ b/activemq-cpp/src/main/cms/Topic.h @@ -0,0 +1,46 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 _CMS_TOPIC_ +#define _CMS_TOPIC_ + +#include +#include + +namespace cms{ + + /** + * An interface encapsulating a provider-specific topic name. + */ + class Topic : public Destination{ + + public: + + virtual ~Topic(void) {} + + /** + * Gets the name of this topic. + * @return The topic name. + */ + virtual std::string getTopicName(void) + const throw( CMSException ) = 0; + + }; + +} + +#endif /*_CMS_TOPIC_*/ diff --git a/activemq-cpp/src/test-integration/integration/common/AbstractTester.cpp b/activemq-cpp/src/test-integration/integration/common/AbstractTester.cpp new file mode 100644 index 0000000000..ac3351ad70 --- /dev/null +++ b/activemq-cpp/src/test-integration/integration/common/AbstractTester.cpp @@ -0,0 +1,227 @@ +#include "AbstractTester.h" + +#include + +#include + +#include +#include +#include +#include + +#include +#include + +#include + +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::util; +using namespace activemq::exceptions; +using namespace activemq::concurrent; + +using namespace integration; +using namespace integration::common; + +AbstractTester::AbstractTester( cms::Session::AcknowledgeMode ackMode ) +{ + try + { + string url = IntegrationCommon::defaultURL; + numReceived = 0; + + // Create a Factory + connectionFactory = new ActiveMQConnectionFactory( url ); + + // Now create the connection + connection = connectionFactory->createConnection( + "", "", Guid().createGUIDString() ); + + // Set ourself as a recipient of Exceptions + connection->setExceptionListener( this ); + connection->start(); + + // Create a Session + session = connection->createSession( ackMode ); + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +AbstractTester::~AbstractTester() +{ + try + { + session->close(); + connection->close(); + + delete session; + delete connection; + delete connectionFactory; + } + AMQ_CATCH_NOTHROW( ActiveMQException ) + AMQ_CATCHALL_NOTHROW( ) +} + +void AbstractTester::doSleep(void) +{ + Thread::sleep( IntegrationCommon::defaultDelay ); +} + +unsigned int AbstractTester::produceTextMessages( + cms::MessageProducer& producer, + unsigned int count ) +{ + try + { + // Send some text messages. + ostringstream stream; + string text = "this is a test text message: id = "; + + cms::TextMessage* textMsg = + session->createTextMessage(); + + unsigned int realCount = 0; + + for( unsigned int ix=0; ixsetText( stream.str().c_str() ); + stream.str(""); + producer.send( *textMsg ); + doSleep(); + ++realCount; + } + + delete textMsg; + + return realCount; + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + +unsigned int AbstractTester::produceBytesMessages( + cms::MessageProducer& producer, + unsigned int count ) +{ + try + { + unsigned char buf[10]; + memset( buf, 0, 10 ); + buf[0] = 0; + buf[1] = 1; + buf[2] = 2; + buf[3] = 3; + buf[4] = 0; + buf[5] = 4; + buf[6] = 5; + buf[7] = 6; + + cms::BytesMessage* bytesMsg = + session->createBytesMessage(); + bytesMsg->setBodyBytes( buf, 10 ); + + unsigned int realCount = 0; + for( unsigned int ix=0; ix(message); + + std::string text = txtMsg.getText(); + +// printf("received text msg: %s\n", txtMsg.getText() ); + + numReceived++; + + // Signal that we got one + synchronized( &mutex ) + { + mutex.notifyAll(); + } + + return; + } + catch( std::bad_cast& ex ) + {} + + try + { + // Got a bytes msg. + const cms::BytesMessage& bytesMsg = + dynamic_cast(message); + + const unsigned char* bytes = bytesMsg.getBodyBytes(); + + string transcode( (const char*)bytes, bytesMsg.getBodyLength() ); + + //printf("received bytes msg: " ); + //int numBytes = bytesMsg.getBodyLength(); + //for( int ix=0; ix + +#include +#include +#include +#include + +namespace integration{ +namespace common{ + + class AbstractTester : public Tester + { + public: + + AbstractTester( cms::Session::AcknowledgeMode ackMode = + cms::Session::AutoAcknowledge ); + virtual ~AbstractTester(); + + virtual void doSleep(void); + + virtual unsigned int produceTextMessages( + cms::MessageProducer& producer, + unsigned int count ); + virtual unsigned int produceBytesMessages( + cms::MessageProducer& producer, + unsigned int count ); + + virtual void waitForMessages( unsigned int count ); + + virtual void onException( const cms::CMSException& error ); + virtual void onMessage( const cms::Message& message ); + + public: + + cms::ConnectionFactory* connectionFactory; + cms::Connection* connection; + cms::Session* session; + + unsigned int numReceived; + activemq::concurrent::Mutex mutex; + + }; + +}} + +#endif /*_INTEGRATION_COMMON_ABSTRACTTESTER_H_*/ diff --git a/activemq-cpp/src/test-integration/integration/common/IntegrationCommon.cpp b/activemq-cpp/src/test-integration/integration/common/IntegrationCommon.cpp new file mode 100644 index 0000000000..5f406b2799 --- /dev/null +++ b/activemq-cpp/src/test-integration/integration/common/IntegrationCommon.cpp @@ -0,0 +1,8 @@ +#include "IntegrationCommon.h" + +using namespace integration; +using namespace integration::common; + +const std::string IntegrationCommon::defaultURL = "tcp://127.0.0.1:61613"; +const int IntegrationCommon::defaultDelay = 5; +const unsigned int IntegrationCommon::defaultMsgCount = 1000; diff --git a/activemq-cpp/src/test-integration/integration/common/IntegrationCommon.h b/activemq-cpp/src/test-integration/integration/common/IntegrationCommon.h new file mode 100644 index 0000000000..1b07e4adc8 --- /dev/null +++ b/activemq-cpp/src/test-integration/integration/common/IntegrationCommon.h @@ -0,0 +1,23 @@ +#ifndef _INTEGRATION_COMMON_INTEGRATIONCOMMON_H_ +#define _INTEGRATION_COMMON_INTEGRATIONCOMMON_H_ + +#include + +namespace integration{ +namespace common{ + + class IntegrationCommon + { + public: + + virtual ~IntegrationCommon(); + + static const std::string defaultURL; + static const int defaultDelay; + static const unsigned int defaultMsgCount; + + }; + +}} + +#endif /*_INTEGRATION_COMMON_INTEGRATIONCOMMON_H_*/ diff --git a/activemq-cpp/src/test-integration/integration/common/Tester.h b/activemq-cpp/src/test-integration/integration/common/Tester.h new file mode 100644 index 0000000000..f1371ef995 --- /dev/null +++ b/activemq-cpp/src/test-integration/integration/common/Tester.h @@ -0,0 +1,24 @@ +#ifndef _INTEGRATION_COMMON_TESTER_H_ +#define _INTEGRATION_COMMON_TESTER_H_ + +#include +#include + + +namespace integration{ +namespace common{ + + class Tester : public cms::ExceptionListener, + public cms::MessageListener + { + public: + + virtual ~Tester() {} + + virtual void test(void) = 0; + + }; + +}} + +#endif /*_INTEGRATION_COMMON_TESTER_H_*/ diff --git a/activemq-cpp/src/test-integration/integration/durable/DurableTester.cpp b/activemq-cpp/src/test-integration/integration/durable/DurableTester.cpp new file mode 100644 index 0000000000..b78f3404a7 --- /dev/null +++ b/activemq-cpp/src/test-integration/integration/durable/DurableTester.cpp @@ -0,0 +1,120 @@ +#include "DurableTester.h" +#include + +CPPUNIT_TEST_SUITE_REGISTRATION( integration::durable::DurableTester ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace activemq::connector::stomp; +using namespace activemq::transport; +using namespace activemq::util; +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::util; +using namespace activemq::connector; +using namespace activemq::exceptions; +using namespace activemq::network; +using namespace activemq::transport; +using namespace activemq::concurrent; + +using namespace integration; +using namespace integration::durable; +using namespace integration::common; + +DurableTester::DurableTester() : AbstractTester() +{} + +DurableTester::~DurableTester() +{} + +void DurableTester::test() +{ + try + { + cout << "Starting activemqcms durable test (sending " + << IntegrationCommon::defaultMsgCount + << " messages per type and sleeping " + << IntegrationCommon::defaultDelay + << " milli-seconds) ...\n" + << endl; + + std::string subName = Guid().createGUID(); + + // Create CMS Object for Comms + cms::Topic* topic = session->createTopic("mytopic"); + cms::MessageConsumer* consumer = + session->createDurableConsumer( *topic, subName, "" ); + consumer->setMessageListener( this ); + cms::MessageProducer* producer = + session->createProducer( *topic ); + + unsigned int sent; + + // Send some text messages + sent = this->produceTextMessages( *producer, 3 ); + + // Wait for all messages + waitForMessages( sent ); + + printf("received: %d\n", numReceived ); + CPPUNIT_ASSERT( numReceived == sent ); + + // Nuke the consumer + delete consumer; + + // Send some text messages + sent += this->produceTextMessages( *producer, 3 ); + + consumer = session->createDurableConsumer( *topic, subName, "" ); + + // Send some text messages + sent += this->produceTextMessages( *producer, 3 ); + + // Wait for all remaining messages + waitForMessages( sent - numReceived ); + + printf("received: %d\n", numReceived ); + // CPPUNIT_ASSERT( numReceived == sent ); + + printf("Shutting Down\n" ); + delete producer; + delete consumer; + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} diff --git a/activemq-cpp/src/test-integration/integration/durable/DurableTester.h b/activemq-cpp/src/test-integration/integration/durable/DurableTester.h new file mode 100644 index 0000000000..8b6a419f63 --- /dev/null +++ b/activemq-cpp/src/test-integration/integration/durable/DurableTester.h @@ -0,0 +1,32 @@ +#ifndef _INTEGRATION_TRANSACTIONAL_DURABLETESTER_H_ +#define _INTEGRATION_TRANSACTIONAL_DURABLETESTER_H_ + +#include + +#include +#include + +namespace integration{ +namespace durable{ + + class DurableTester : public CppUnit::TestFixture, + public common::AbstractTester + { + CPPUNIT_TEST_SUITE( DurableTester ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + DurableTester(); + virtual ~DurableTester(); + + virtual void test(void); + + private: + + }; + +}} + +#endif /*_INTEGRATION_TRANSACTIONAL_DURABLETESTER_H_*/ diff --git a/activemq-cpp/src/test-integration/integration/simple/SimpleTester.cpp b/activemq-cpp/src/test-integration/integration/simple/SimpleTester.cpp new file mode 100644 index 0000000000..c5f67f43e2 --- /dev/null +++ b/activemq-cpp/src/test-integration/integration/simple/SimpleTester.cpp @@ -0,0 +1,109 @@ +#include "SimpleTester.h" +#include + +CPPUNIT_TEST_SUITE_REGISTRATION( integration::simple::SimpleTester ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace activemq::connector::stomp; +using namespace activemq::transport; +using namespace activemq::util; +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::util; +using namespace activemq::connector; +using namespace activemq::exceptions; +using namespace activemq::network; +using namespace activemq::transport; +using namespace activemq::concurrent; + +using namespace integration; +using namespace integration::simple; +using namespace integration::common; + +SimpleTester::SimpleTester() : AbstractTester() +{ + numReceived = 0; +} + +SimpleTester::~SimpleTester() +{ +} + +void SimpleTester::test() +{ + try + { + cout << "Starting activemqcms test (sending " + << IntegrationCommon::defaultMsgCount + << " messages per type and sleeping " + << IntegrationCommon::defaultDelay + << " milli-seconds) ...\n" + << endl; + + // Create CMS Object for Comms + cms::Topic* topic = session->createTopic("mytopic"); + cms::MessageConsumer* consumer = + session->createConsumer( *topic ); + consumer->setMessageListener( this ); + cms::MessageProducer* producer = + session->createProducer( *topic ); + + // Send some text messages + this->produceTextMessages( + *producer, IntegrationCommon::defaultMsgCount ); + + // Send some bytes messages. + this->produceTextMessages( + *producer, IntegrationCommon::defaultMsgCount ); + + // Wait for the messages to get here + waitForMessages( IntegrationCommon::defaultMsgCount * 2 ); + + printf("received: %d\n", numReceived ); + CPPUNIT_ASSERT( + numReceived == IntegrationCommon::defaultMsgCount * 2 ); + + printf("Shutting Down\n" ); + delete producer; + delete consumer; + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + diff --git a/activemq-cpp/src/test-integration/integration/simple/SimpleTester.h b/activemq-cpp/src/test-integration/integration/simple/SimpleTester.h new file mode 100644 index 0000000000..202ee1f4c0 --- /dev/null +++ b/activemq-cpp/src/test-integration/integration/simple/SimpleTester.h @@ -0,0 +1,30 @@ +#ifndef _INTEGRATION_SIMPLE_SIMPLETESTER_H_ +#define _INTEGRATION_SIMPLE_SIMPLETESTER_H_ + +#include +#include + +#include + +namespace integration{ +namespace simple{ + + class SimpleTester : public CppUnit::TestFixture, + public common::AbstractTester + { + CPPUNIT_TEST_SUITE( SimpleTester ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + SimpleTester(); + virtual ~SimpleTester(); + + virtual void test(void); + + }; + +}} + +#endif /*_INTEGRATION_SIMPLE_SIMPLETESTER_H_*/ diff --git a/activemq-cpp/src/test-integration/integration/transactional/TransactionTester.cpp b/activemq-cpp/src/test-integration/integration/transactional/TransactionTester.cpp new file mode 100644 index 0000000000..905d82cade --- /dev/null +++ b/activemq-cpp/src/test-integration/integration/transactional/TransactionTester.cpp @@ -0,0 +1,125 @@ +#include "TransactionTester.h" +#include + +CPPUNIT_TEST_SUITE_REGISTRATION( integration::transactional::TransactionTester ); + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace activemq::connector::stomp; +using namespace activemq::transport; +using namespace activemq::util; +using namespace std; +using namespace cms; +using namespace activemq; +using namespace activemq::core; +using namespace activemq::util; +using namespace activemq::connector; +using namespace activemq::exceptions; +using namespace activemq::network; +using namespace activemq::transport; +using namespace activemq::concurrent; + +using namespace integration; +using namespace integration::transactional; +using namespace integration::common; + +TransactionTester::TransactionTester() : AbstractTester( cms::Session::Transactional ) +{} + +TransactionTester::~TransactionTester() +{} + +void TransactionTester::test() +{ + try + { + cout << "Starting activemqcms transactional test (sending " + << IntegrationCommon::defaultMsgCount + << " messages per type and sleeping " + << IntegrationCommon::defaultDelay + << " milli-seconds) ...\n" + << endl; + + // Create CMS Object for Comms + cms::Topic* topic = session->createTopic("mytopic"); + cms::MessageConsumer* consumer = + session->createConsumer( *topic ); + consumer->setMessageListener( this ); + cms::MessageProducer* producer = + session->createProducer( *topic ); + + // Send some text messages + this->produceTextMessages( + *producer, IntegrationCommon::defaultMsgCount ); + + session->commit(); + + // Send some bytes messages. + this->produceTextMessages( + *producer, IntegrationCommon::defaultMsgCount ); + + session->commit(); + + // Wait till we get all the messages + waitForMessages( IntegrationCommon::defaultMsgCount * 2 ); + + printf("received: %d\n", numReceived ); + CPPUNIT_ASSERT( + numReceived == IntegrationCommon::defaultMsgCount * 2 ); + + numReceived = 0; + + // Send some text messages + this->produceTextMessages( + *producer, IntegrationCommon::defaultMsgCount ); + + session->rollback(); + + // Wait till we get all the messages + waitForMessages( IntegrationCommon::defaultMsgCount * 2 ); + + printf("received: %d\n", numReceived ); + CPPUNIT_ASSERT( + numReceived == IntegrationCommon::defaultMsgCount ); + + printf("Shutting Down\n" ); + delete producer; + delete consumer; + } + AMQ_CATCH_RETHROW( ActiveMQException ) + AMQ_CATCHALL_THROW( ActiveMQException ) +} + diff --git a/activemq-cpp/src/test-integration/integration/transactional/TransactionTester.h b/activemq-cpp/src/test-integration/integration/transactional/TransactionTester.h new file mode 100644 index 0000000000..e488290ae5 --- /dev/null +++ b/activemq-cpp/src/test-integration/integration/transactional/TransactionTester.h @@ -0,0 +1,32 @@ +#ifndef _INTEGRATION_TRANSACTIONAL_TRANSACTIONTESTER_H_ +#define _INTEGRATION_TRANSACTIONAL_TRANSACTIONTESTER_H_ + +#include + +#include +#include + +namespace integration{ +namespace transactional{ + + class TransactionTester : public CppUnit::TestFixture, + public common::AbstractTester + { + CPPUNIT_TEST_SUITE( TransactionTester ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + TransactionTester(); + virtual ~TransactionTester(); + + virtual void test(void); + + private: + + }; + +}} + +#endif /*_INTEGRATION_TRANSACTIONAL_TRANSACTIONTESTER_H_*/ diff --git a/activemq-cpp/src/test-integration/main.cpp b/activemq-cpp/src/test-integration/main.cpp new file mode 100644 index 0000000000..eba2919ed9 --- /dev/null +++ b/activemq-cpp/src/test-integration/main.cpp @@ -0,0 +1,19 @@ +#include +#include +#include +#include + +int main( int argc, char **argv) +{ + CppUnit::TextUi::TestRunner runner; + CppUnit::TestFactoryRegistry ®istry = CppUnit::TestFactoryRegistry::getRegistry(); + runner.addTest( registry.makeTest() ); + + // Shows a message as each test starts + CppUnit::BriefTestProgressListener listener; + runner.eventManager().addListener( &listener ); + + bool wasSuccessful = runner.run( "", false ); + return !wasSuccessful; +} + diff --git a/activemq-cpp/src/test/activemq/concurrent/MutexTest.cpp b/activemq-cpp/src/test/activemq/concurrent/MutexTest.cpp new file mode 100644 index 0000000000..4b5a906d29 --- /dev/null +++ b/activemq-cpp/src/test/activemq/concurrent/MutexTest.cpp @@ -0,0 +1,4 @@ +#include "MutexTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::concurrent::MutexTest ); + diff --git a/activemq-cpp/src/test/activemq/concurrent/MutexTest.h b/activemq-cpp/src/test/activemq/concurrent/MutexTest.h new file mode 100644 index 0000000000..55b7dd2b90 --- /dev/null +++ b/activemq-cpp/src/test/activemq/concurrent/MutexTest.h @@ -0,0 +1,593 @@ +#ifndef ACTIVEMQ_CONCURRENT_MUTEXTEST_H_ +#define ACTIVEMQ_CONCURRENT_MUTEXTEST_H_ + +#include +#include + +#include +#include +#include +#include + +namespace activemq{ +namespace concurrent{ + + class MutexTest : public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE( MutexTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST( testWait ); + CPPUNIT_TEST( testTimedWait ); + CPPUNIT_TEST( testNotify ); + CPPUNIT_TEST( testNotifyAll ); + CPPUNIT_TEST( testRecursiveLock ); + CPPUNIT_TEST( testDoubleLock ); + CPPUNIT_TEST_SUITE_END(); + + public: + + class MyThread + : + public Thread, + public Synchronizable{ + + private: + + Mutex mutex; + + public: + + int value; + MyThread(){ value = 0;} + virtual ~MyThread(){} + + virtual void lock() throw(exceptions::ActiveMQException){ + mutex.lock(); + } + virtual void unlock() throw(exceptions::ActiveMQException){ + mutex.unlock(); + } + virtual void wait() throw(exceptions::ActiveMQException){ + mutex.wait(); + } + virtual void wait(unsigned long millisecs) throw(exceptions::ActiveMQException){ + mutex.wait( millisecs ); + } + virtual void notify() throw(exceptions::ActiveMQException){ + mutex.notify(); + } + virtual void notifyAll() throw(exceptions::ActiveMQException){ + mutex.notifyAll(); + } + + virtual void run(){ + + { + Lock lock (this); + + value = value * 25; + } + } + + }; + + class MyWaitingThread + : + public Thread, + public Synchronizable{ + + private: + + Mutex mutex; + + public: + + int value; + MyWaitingThread(){ value = 0;} + virtual ~MyWaitingThread(){} + virtual void lock() throw(exceptions::ActiveMQException){ + mutex.lock(); + } + virtual void unlock() throw(exceptions::ActiveMQException){ + mutex.unlock(); + } + virtual void wait() throw(exceptions::ActiveMQException){ + mutex.wait(); + } + virtual void wait(unsigned long millisecs) throw(exceptions::ActiveMQException){ + mutex.wait( millisecs ); + } + virtual void notify() throw(exceptions::ActiveMQException){ + mutex.notify(); + } + virtual void notifyAll() throw(exceptions::ActiveMQException){ + mutex.notifyAll(); + } + + virtual void run(){ + + try + { + synchronized(this) + { + this->wait(); + + std::cout.flush(); + + value = value * 25; + } + } + catch(exceptions::ActiveMQException& ex) + { + ex.setMark( __FILE__, __LINE__ ); + } + } + }; + + class MyTimedWaitingThread + : + public Thread, + public Synchronizable{ + + private: + + Mutex mutex; + + public: + + int value; + MyTimedWaitingThread(){ value = 0;} + virtual ~MyTimedWaitingThread(){} + virtual void lock() throw(exceptions::ActiveMQException){ + mutex.lock(); + } + virtual void unlock() throw(exceptions::ActiveMQException){ + mutex.unlock(); + } + virtual void wait() throw(exceptions::ActiveMQException){ + mutex.wait(); + } + virtual void wait(unsigned long millisecs) throw(exceptions::ActiveMQException){ + mutex.wait( millisecs ); + } + virtual void notify() throw(exceptions::ActiveMQException){ + mutex.notify(); + } + virtual void notifyAll() throw(exceptions::ActiveMQException){ + mutex.notifyAll(); + } + + virtual void run(){ + + try + { + synchronized(this) + { + this->wait(2000); + + value = 666; + } + } + catch(exceptions::ActiveMQException& ex) + { + ex.setMark( __FILE__, __LINE__ ); + } + } + }; + + class MyNotifiedThread + : + public Thread, + public Synchronizable{ + + public: + + bool done; + Mutex* mutex; + + public: + + int value; + MyNotifiedThread(Mutex* mutex){ this->mutex = mutex; done = false; } + virtual ~MyNotifiedThread(){} + virtual void lock() throw(exceptions::ActiveMQException){ + mutex->lock(); + } + virtual void unlock() throw(exceptions::ActiveMQException){ + mutex->unlock(); + } + virtual void wait() throw(exceptions::ActiveMQException){ + mutex->wait(); + } + virtual void wait(unsigned long millisecs) throw(exceptions::ActiveMQException){ + mutex->wait( millisecs ); + } + virtual void notify() throw(exceptions::ActiveMQException){ + mutex->notify(); + } + virtual void notifyAll() throw(exceptions::ActiveMQException){ + mutex->notifyAll(); + } + + virtual void run(){ + + try + { + done = false; + synchronized(this) + { + this->wait(); + done = true; + } + } + catch(exceptions::ActiveMQException& ex) + { + ex.setMark( __FILE__, __LINE__ ); + } + } + }; + + class MyRecursiveLockThread + : + public Thread, + public Synchronizable{ + + public: + + bool done; + Mutex* mutex; + + public: + + int value; + MyRecursiveLockThread(Mutex* mutex){ this->mutex = mutex; done = false; } + virtual ~MyRecursiveLockThread(){} + virtual void lock() throw(exceptions::ActiveMQException){ + mutex->lock(); + } + virtual void unlock() throw(exceptions::ActiveMQException){ + mutex->unlock(); + } + virtual void wait() throw(exceptions::ActiveMQException){ + mutex->wait(); + } + virtual void wait(unsigned long millisecs) throw(exceptions::ActiveMQException){ + mutex->wait( millisecs ); + } + virtual void notify() throw(exceptions::ActiveMQException){ + mutex->notify(); + } + virtual void notifyAll() throw(exceptions::ActiveMQException){ + mutex->notifyAll(); + } + + virtual void run(){ + + try + { + done = false; + synchronized(this) + { + synchronized(this) + { + this->wait(); + done = true; + } + } + } + catch(exceptions::ActiveMQException& ex) + { + ex.setMark( __FILE__, __LINE__ ); + } + } + }; + + class MyDoubleLockThread + : + public Thread + { + + public: + + bool done; + Mutex* mutex1; + Mutex* mutex2; + + public: + + int value; + MyDoubleLockThread(Mutex* mutex1, Mutex* mutex2) + { + this->mutex1 = mutex1; + this->mutex2 = mutex2; + done = false; + } + + virtual ~MyDoubleLockThread(){} + + virtual void run(){ + + try + { + done = false; + synchronized(mutex1) + { + synchronized(mutex2) + { + mutex2->wait(); + done = true; + } + } + } + catch(exceptions::ActiveMQException& ex) + { + ex.setMark( __FILE__, __LINE__ ); + } + } + }; + + public: + + virtual void setUp(){}; + virtual void tearDown(){}; + + void testTimedWait(){ + + try + { + MyTimedWaitingThread test; + time_t startTime = time( NULL ); + test.start(); + test.join(); + time_t endTime = time( NULL ); + + long delta = endTime - startTime; + + CPPUNIT_ASSERT( delta >= 1 && delta <= 3 ); + } + catch(exceptions::ActiveMQException& ex) + { + std::cout << ex.getMessage() << std::endl; + } + } + + void testWait(){ + + try + { + MyWaitingThread test; + test.start(); + + Thread::sleep(1000); + + synchronized(&test) + { + for( int ix=0; ix<100; ix++ ){ + test.value += 1; + } + + test.notify(); + } + + test.join(); + + CPPUNIT_ASSERT( test.value == 2500 ); + + } + catch(exceptions::ActiveMQException& ex) + { + ex.setMark( __FILE__, __LINE__ ); + } + } + + void test() + { + MyThread test; + test.lock(); + + test.start(); + + for( int ix=0; ix<100; ix++ ){ + test.value += 1; + } + + test.unlock(); + test.join(); + + CPPUNIT_ASSERT( test.value == 2500 ); + } + + void testNotify() + { + try{ + Mutex mutex; + const int numThreads = 30; + MyNotifiedThread* threads[numThreads]; + + // Create and start all the threads. + for( int ix=0; ixstart(); + } + + // Sleep so all the threads can get to the wait. + Thread::sleep( 1000 ); + + synchronized(&mutex) + { + mutex.notify(); + } + + Thread::sleep( 1000 ); + + int counter = 0; + for( int ix=0; ixdone ){ + counter++; + } + } + + // Make sure only 1 thread was notified. + CPPUNIT_ASSERT( counter == 1 ); + + synchronized(&mutex) + { + // Notify all threads. + for( int ix=0; ixdone ){ + numComplete++; + } + } + CPPUNIT_ASSERT( numComplete == numThreads ); + + synchronized( &mutex ) + { + mutex.wait( 5 ); + } + + synchronized( &mutex ) + { + mutex.notifyAll(); + } + + }catch( exceptions::ActiveMQException& ex ){ + ex.setMark( __FILE__, __LINE__ ); + } + } + + void testNotifyAll() + { + try{ + Mutex mutex; + + const int numThreads = 100; + MyNotifiedThread* threads[numThreads]; + + // Create and start all the threads. + for( int ix=0; ixstart(); + } + + // Sleep so all the threads can get to the wait. + Thread::sleep( 1000 ); + + for( int ix=0; ixdone == true ){ + printf("threads[%d] is done prematurely\n", ix ); + } + CPPUNIT_ASSERT( threads[ix]->done == false ); + } + + // Notify all threads. + synchronized( &mutex ){ + mutex.notifyAll(); + } + + // Sleep to give the threads time to wake up. + Thread::sleep( 1000 ); + + int numComplete = 0; + for( int ix=0; ixdone ){ + numComplete++; + } + } + printf("numComplete: %d, numThreads: %d\n", numComplete, numThreads ); + CPPUNIT_ASSERT( numComplete == numThreads ); + + }catch( exceptions::ActiveMQException& ex ){ + ex.setMark( __FILE__, __LINE__ ); + } + } + + void testRecursiveLock() + { + try{ + Mutex mutex; + + const int numThreads = 30; + MyRecursiveLockThread* threads[numThreads]; + + // Create and start all the threads. + for( int ix=0; ixstart(); + } + + // Sleep so all the threads can get to the wait. + Thread::sleep( 1000 ); + + for( int ix=0; ixdone == true ){ + std::cout << "threads[" << ix + << "] is done prematurely\n"; + } + CPPUNIT_ASSERT( threads[ix]->done == false ); + } + + // Notify all threads. + synchronized( &mutex ) + { + synchronized( &mutex ) + { + mutex.notifyAll(); + } + } + + // Sleep to give the threads time to wake up. + Thread::sleep( 1000 ); + + for( int ix=0; ixdone != true ){ + std::cout<< "threads[" << ix << "] is not done\n"; + } + CPPUNIT_ASSERT( threads[ix]->done == true ); + } + }catch( exceptions::ActiveMQException& ex ){ + ex.setMark( __FILE__, __LINE__ ); + } + } + + void testDoubleLock() + { + try{ + Mutex mutex1; + Mutex mutex2; + + MyDoubleLockThread thread(&mutex1, &mutex2); + + thread.start(); + + // Let the thread get both locks + Thread::sleep( 200 ); + + // Lock mutex 2, thread is waiting on it + synchronized(&mutex2) + { + mutex2.notify(); + } + + // Let the thread die + thread.join(); + + CPPUNIT_ASSERT( thread.done ); + }catch( exceptions::ActiveMQException& ex ){ + ex.setMark( __FILE__, __LINE__ ); + } + } + }; + +}} + +#endif /*ACTIVEMQ_CONCURRENT_MUTEXTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/concurrent/ThreadPoolTest.cpp b/activemq-cpp/src/test/activemq/concurrent/ThreadPoolTest.cpp new file mode 100644 index 0000000000..fd2e9026d8 --- /dev/null +++ b/activemq-cpp/src/test/activemq/concurrent/ThreadPoolTest.cpp @@ -0,0 +1,3 @@ +#include "ThreadPoolTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::concurrent::ThreadPoolTest ); diff --git a/activemq-cpp/src/test/activemq/concurrent/ThreadPoolTest.h b/activemq-cpp/src/test/activemq/concurrent/ThreadPoolTest.h new file mode 100644 index 0000000000..55f3c968cb --- /dev/null +++ b/activemq-cpp/src/test/activemq/concurrent/ThreadPoolTest.h @@ -0,0 +1,233 @@ +#ifndef THREADPOOLTEST_H_ +#define THREADPOOLTEST_H_ + +#include +#include + +#include +#include +#include +#include +#include + +namespace activemq{ +namespace concurrent{ + + class ThreadPoolTest : + public CppUnit::TestFixture, + public TaskListener + { + CPPUNIT_TEST_SUITE( ThreadPoolTest ); + CPPUNIT_TEST( test1 ); + CPPUNIT_TEST( test2 ); + CPPUNIT_TEST_SUITE_END(); + + int tasksToComplete; + int complete; + Mutex mutex; + Mutex completeMutex; + bool caughtEx; + + public: + + ThreadPoolTest() + { + complete = 0; + tasksToComplete = 0; + caughtEx = false; + } + + virtual ~ThreadPoolTest() {}; + + virtual void onTaskComplete(Runnable* task) + { + try{ + synchronized(&mutex) + { + complete++; + + if(tasksToComplete == complete) + { + mutex.notifyAll(); + } + } + }catch( exceptions::ActiveMQException& ex ){ + ex.setMark( __FILE__, __LINE__ ); + } + } + + virtual void onTaskException(Runnable* task, exceptions::ActiveMQException& ex) + { + caughtEx = true; + } + + public: + + class MyTask : public Runnable + { + public: + + int value; + + MyTask(int x) + { + value = x; + } + + virtual ~MyTask() {}; + + virtual void run(void) + { + value += 100; + } + }; + + class MyWaitingTask : public Runnable + { + public: + + Mutex* mutex; + Mutex* complete; + + MyWaitingTask(Mutex* mutex, Mutex* complete) + { + this->mutex = mutex; + this->complete = complete; + } + + virtual ~MyWaitingTask() {}; + + virtual void run(void) + { + try + { + synchronized(mutex) + { + mutex->wait(); + } + + synchronized(complete) + { + complete->notify(); + } + } + catch( exceptions::ActiveMQException& ex ) + { + ex.setMark( __FILE__, __LINE__ ); + } + } + }; + + public: + + void test2() + { + try + { + ThreadPool pool; + Mutex myMutex; + + CPPUNIT_ASSERT( pool.getMaxThreads() == ThreadPool::DEFAULT_MAX_POOL_SIZE ); + CPPUNIT_ASSERT( pool.getBlockSize() == ThreadPool::DEFAULT_MAX_BLOCK_SIZE ); + pool.setMaxThreads(3); + pool.setBlockSize(1); + CPPUNIT_ASSERT( pool.getMaxThreads() == 3 ); + CPPUNIT_ASSERT( pool.getBlockSize() == 1 ); + CPPUNIT_ASSERT( pool.getPoolSize() == 0 ); + pool.reserve( 4 ); + CPPUNIT_ASSERT( pool.getPoolSize() == 3 ); + CPPUNIT_ASSERT( pool.getFreeThreadCount() == 3 ); + + MyWaitingTask task1(&myMutex, &completeMutex); + MyWaitingTask task2(&myMutex, &completeMutex); + MyWaitingTask task3(&myMutex, &completeMutex); + MyWaitingTask task4(&myMutex, &completeMutex); + + complete = 0; + tasksToComplete = 4; + + pool.queueTask(ThreadPool::Task(&task1, this)); + pool.queueTask(ThreadPool::Task(&task2, this)); + pool.queueTask(ThreadPool::Task(&task3, this)); + pool.queueTask(ThreadPool::Task(&task4, this)); + + Thread::sleep( 1000 ); + + CPPUNIT_ASSERT( pool.getFreeThreadCount() == 0 ); + CPPUNIT_ASSERT( pool.getBacklog() == 1 ); + + int count = 0; + while(complete != tasksToComplete && count < 100) + { + synchronized(&myMutex) + { + myMutex.notifyAll(); + } + + synchronized(&completeMutex) + { + completeMutex.wait(1000); + } + + count++; + } + + CPPUNIT_ASSERT( complete == tasksToComplete ); + CPPUNIT_ASSERT( caughtEx == false ); + } + catch( exceptions::ActiveMQException& ex ) + { + ex.setMark( __FILE__, __LINE__ ); + } + } + + void test1() + { + MyTask task1(1); + MyTask task2(2); + MyTask task3(3); + + complete = 0; + tasksToComplete = 3; + + ThreadPool* pool = ThreadPool::getInstance(); + + // Can't check this here since one of the other tests might + // have used the global thread pool. + // CPPUNIT_ASSERT( pool->getPoolSize() == 0 ); + + pool->queueTask(ThreadPool::Task(&task1, this)); + pool->queueTask(ThreadPool::Task(&task2, this)); + pool->queueTask(ThreadPool::Task(&task3, this)); + + Thread::sleep(500); + + CPPUNIT_ASSERT( complete == tasksToComplete ); + + CPPUNIT_ASSERT( task1.value == 101 ); + CPPUNIT_ASSERT( task2.value == 102 ); + CPPUNIT_ASSERT( task3.value == 103 ); + + CPPUNIT_ASSERT( pool->getPoolSize() > 0 ); + CPPUNIT_ASSERT( pool->getBacklog() == 0 ); + + CPPUNIT_ASSERT( pool->getMaxThreads() == ThreadPool::DEFAULT_MAX_POOL_SIZE ); + CPPUNIT_ASSERT( pool->getBlockSize() == ThreadPool::DEFAULT_MAX_BLOCK_SIZE ); + + pool->setMaxThreads(50); + pool->setBlockSize(50); + + CPPUNIT_ASSERT( pool->getMaxThreads() == 50 ); + CPPUNIT_ASSERT( pool->getBlockSize() == 50 ); + + Thread::sleep(500); + + CPPUNIT_ASSERT( pool->getFreeThreadCount() == pool->getPoolSize() ); + CPPUNIT_ASSERT( caughtEx == false ); + + } + }; + +}} + +#endif /*THREADPOOLTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/concurrent/ThreadTest.cpp b/activemq-cpp/src/test/activemq/concurrent/ThreadTest.cpp new file mode 100644 index 0000000000..6ce906c718 --- /dev/null +++ b/activemq-cpp/src/test/activemq/concurrent/ThreadTest.cpp @@ -0,0 +1,4 @@ +#include "ThreadTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::concurrent::ThreadTest ); + diff --git a/activemq-cpp/src/test/activemq/concurrent/ThreadTest.h b/activemq-cpp/src/test/activemq/concurrent/ThreadTest.h new file mode 100644 index 0000000000..5c358a0ccc --- /dev/null +++ b/activemq-cpp/src/test/activemq/concurrent/ThreadTest.h @@ -0,0 +1,130 @@ +#ifndef ACTIVEMQ_CONCURRENT_THREADTEST_H_ +#define ACTIVEMQ_CONCURRENT_THREADTEST_H_ + +#include +#include + +#include +#include + +namespace activemq{ +namespace concurrent{ + + class ThreadTest : public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE( ThreadTest ); + CPPUNIT_TEST( testDelegate ); + CPPUNIT_TEST( testDerived ); + CPPUNIT_TEST( testJoin ); + CPPUNIT_TEST_SUITE_END(); + + public: + + class Delegate : public Runnable{ + private: + + int stuff; + + public: + + Delegate(){ stuff = 0; } + virtual ~Delegate(){} + + int getStuff(){ + return stuff; + } + + virtual void run(){ + stuff = 1; + } + + }; + + class Derived : public Thread{ + private: + + int stuff; + + public: + + Derived(){ stuff = 0; } + virtual ~Derived(){} + + int getStuff(){ + return stuff; + } + + virtual void run(){ + stuff = 1; + } + + }; + + class JoinTest : public Thread{ + public: + + JoinTest(){} + virtual ~JoinTest(){} + + virtual void run(){ + + // Sleep for 2 seconds. + Thread::sleep( 2000 ); + } + + }; + + public: + + virtual void setUp(){}; + virtual void tearDown(){}; + void testDelegate(){ + + Delegate test; + int initialValue = test.getStuff(); + + Thread thread( &test ); + thread.start(); + thread.join(); + + int finalValue = test.getStuff(); + + // The values should be different - this proves + // that the runnable was run. + CPPUNIT_ASSERT( finalValue != initialValue ); + } + + void testDerived(){ + + Derived test; + int initialValue = test.getStuff(); + + test.start(); + test.join(); + + int finalValue = test.getStuff(); + + // The values should be different - this proves + // that the runnable was run. + CPPUNIT_ASSERT( finalValue != initialValue ); + } + + void testJoin(){ + + JoinTest test; + + time_t startTime = time( NULL ); + test.start(); + test.join(); + time_t endTime = time( NULL ); + + long delta = endTime - startTime; + + // Should be about 5 seconds that elapsed. + CPPUNIT_ASSERT( delta >= 1 && delta <= 3 ); + } + }; + +}} + +#endif /*ACTIVEMQ_CONCURRENT_THREADTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/ConnectorFactoryMapRegistrarTest.cpp b/activemq-cpp/src/test/activemq/connector/ConnectorFactoryMapRegistrarTest.cpp new file mode 100644 index 0000000000..a7872fa973 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/ConnectorFactoryMapRegistrarTest.cpp @@ -0,0 +1,3 @@ +#include "ConnectorFactoryMapRegistrarTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::ConnectorFactoryMapRegistrarTest ); diff --git a/activemq-cpp/src/test/activemq/connector/ConnectorFactoryMapRegistrarTest.h b/activemq-cpp/src/test/activemq/connector/ConnectorFactoryMapRegistrarTest.h new file mode 100644 index 0000000000..b9a60f7941 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/ConnectorFactoryMapRegistrarTest.h @@ -0,0 +1,44 @@ +#ifndef ACTIVEMQ_CONNECTOR_CONNECTORFACTORYMAPREGISTRARTEST_H_ +#define ACTIVEMQ_CONNECTOR_CONNECTORFACTORYMAPREGISTRARTEST_H_ + +#include +#include + +#include +#include + +namespace activemq{ +namespace connector{ + + class ConnectorFactoryMapRegistrarTest : public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE( ConnectorFactoryMapRegistrarTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + class TestConnectoryFactory : public ConnectorFactory + { + public: + + virtual Connector* createConnector( + const activemq::util::Properties& properties, + activemq::transport::Transport* transport) { return NULL; }; + }; + + void test(){ + + { + ConnectorFactoryMapRegistrar registrar("Test", new TestConnectoryFactory()); + + CPPUNIT_ASSERT( ConnectorFactoryMap::getInstance()->lookup("Test") != NULL); + } + + CPPUNIT_ASSERT( ConnectorFactoryMap::getInstance()->lookup( "Test" ) == NULL ); + } + }; + +}} + +#endif /*ACTIVEMQ_CONNECTOR_CONNECTORFACTORYMAPREGISTRARTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/ConnectorFactoryMapTest.cpp b/activemq-cpp/src/test/activemq/connector/ConnectorFactoryMapTest.cpp new file mode 100644 index 0000000000..927c0b1ea7 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/ConnectorFactoryMapTest.cpp @@ -0,0 +1,4 @@ +#include "ConnectorFactoryMapTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::ConnectorFactoryMapTest ); + diff --git a/activemq-cpp/src/test/activemq/connector/ConnectorFactoryMapTest.h b/activemq-cpp/src/test/activemq/connector/ConnectorFactoryMapTest.h new file mode 100644 index 0000000000..2fa4110af7 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/ConnectorFactoryMapTest.h @@ -0,0 +1,123 @@ +#ifndef ACTIVEMQ_CONNECTOR_CONNECTORFACTORYMAPTEST_H_ +#define ACTIVEMQ_CONNECTOR_CONNECTORFACTORYMAPTEST_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace connector{ + + class ConnectorFactoryMapTest : public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE( ConnectorFactoryMapTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + class testConnector : public Connector + { + public: + + virtual SessionInfo* createSessionInfo(void) throw( ConnectorException ) + { return NULL; }; + + virtual cms::Topic* createTopic(const std::string& name, SessionInfo* session) + throw ( ConnectorException ) + { return NULL; }; + virtual cms::Queue* createQueue(const std::string& name, SessionInfo* session) + throw ( ConnectorException ) + { return NULL; }; + + virtual cms::TemporaryTopic* createTemporaryTopic(const std::string& name, + SessionInfo* session) + throw ( ConnectorException ) + { return NULL; }; + + virtual cms::TemporaryQueue* createTemporaryQueue(const std::string& name, + SessionInfo* session) + throw ( ConnectorException ) + { return NULL; }; + + virtual void Send(cms::Message* message) throw ( ConnectorException ) {}; + virtual void Send(std::list& messages) + throw ( ConnectorException ) {}; + virtual void Acknowledge(cms::Message* message) throw ( ConnectorException ) {}; + virtual TransactionInfo* startTransaction(SessionInfo* session) + throw ( ConnectorException ) { return NULL; }; + virtual void commit(TransactionInfo* transaction, SessionInfo* session) + throw ( ConnectorException ) {}; + virtual void rollback(TransactionInfo* transaction, SessionInfo* session) + throw ( ConnectorException ) {}; + + virtual cms::BytesMessage* createByteMessage(SessionInfo* session, + TransactionInfo* transaction) + throw ( ConnectorException ) + { return NULL; }; + virtual cms::TextMessage* createTextMessage(SessionInfo* session, + TransactionInfo* transaction) + throw ( ConnectorException ) + { return NULL; }; + virtual void subscribe(cms::Destination* destination, SessionInfo* session) + throw ( ConnectorException ) {}; + virtual void unsubscribe(cms::Destination* destination, SessionInfo* session) + throw ( ConnectorException ) {}; + virtual void addMessageListener(cms::MessageListener* listener) {}; + virtual void removeMessageListener(cms::MessageListener* listener) {}; + virtual void addExceptionListener(cms::ExceptionListener* listener) {}; + virtual void removeExceptionListener(cms::ExceptionListener* listener) {}; + + }; + + public: + + class TestConnectoryFactory : public ConnectorFactory + { + public: + + virtual Connector* createConnector( + const activemq::util::Properties& properties, + activemq::transport::Transport* transport) { return NULL; }; + }; + + void test(){ + + ConnectorFactoryMap* factMap = ConnectorFactoryMap::getInstance(); + CPPUNIT_ASSERT( factMap != NULL ); + + TestConnectoryFactory testFactory; + + factMap->registerConnectorFactory( "test", &testFactory ); + + CPPUNIT_ASSERT( factMap->lookup( "test" ) == &testFactory ); + + std::vector names; + CPPUNIT_ASSERT( factMap->getFactoryNames( names ) >= 1 ); + + bool found = false; + for( unsigned int i = 0; i < names.size(); ++i ) + { + if( names[i] == "test" ) + { + found = true; + break; + } + } + CPPUNIT_ASSERT( found ); + + factMap->unregisterConnectorFactory( "test" ); + CPPUNIT_ASSERT( factMap->lookup( "test" ) == NULL ); + } + }; + +}} + +#endif /*ACTIVEMQ_CONNECTOR_CONNECTORFACTORYMAPTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/StompCommandReaderTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/StompCommandReaderTest.cpp new file mode 100644 index 0000000000..786d1ed633 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/StompCommandReaderTest.cpp @@ -0,0 +1,3 @@ +#include "StompCommandReaderTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::StompCommandReaderTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/StompCommandReaderTest.h b/activemq-cpp/src/test/activemq/connector/stomp/StompCommandReaderTest.h new file mode 100644 index 0000000000..18b03520f9 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/StompCommandReaderTest.h @@ -0,0 +1,108 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_STOMPCOMMANDREADERTEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_STOMPCOMMANDREADERTEST_H_ + +#include +#include + +#include +#include +#include +#include +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + class StompCommandReaderTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( StompCommandReaderTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + StompCommandReaderTest() {} + virtual ~StompCommandReaderTest() {} + + void test( void ) + { + io::ByteArrayInputStream biStream; + + StompCommandReader reader( &biStream ); + + const char* connectedStr = + "CONNECTED\nsession:test\n\n\0\n"; + const char* textStr = + "MESSAGE\n" + "destination:/topic/a\n" + "message-id:123\n" + "sampleProperty:testvalue\n\n" + "testMessage\0\n"; + const char* bytesStr = + "MESSAGE\n" // 8 + "destination:/topic/a\n" // 21 + "message-id:123\n" // 15 + "content-length:9\n" // 17 + "sampleProperty:testvalue\n\n" // 26 + "123456789\0\n"; // 11 + + biStream.setByteArray( + (const unsigned char*)connectedStr, 27 ); + + transport::Command* command = reader.readCommand(); + + CPPUNIT_ASSERT( command != NULL ); + + commands::ConnectedCommand* connected = + dynamic_cast< commands::ConnectedCommand* >( command ); + + CPPUNIT_ASSERT( connected != NULL ); + + CPPUNIT_ASSERT( connected->getSessionId() != NULL ); + std::string sessionId = connected->getSessionId(); + CPPUNIT_ASSERT( sessionId == "test" ); + + biStream.setByteArray( + (const unsigned char*)textStr, 83 ); + + command = reader.readCommand(); + + CPPUNIT_ASSERT( command != NULL ); + + commands::TextMessageCommand* textMessage = + dynamic_cast< commands::TextMessageCommand* >( command ); + + CPPUNIT_ASSERT( textMessage != NULL ); + + CPPUNIT_ASSERT( textMessage->getText() != NULL ); + std::string text = textMessage->getText(); + CPPUNIT_ASSERT( text == "testMessage" ); + + biStream.setByteArray( + (const unsigned char*)bytesStr, 98 ); + + command = reader.readCommand(); + + CPPUNIT_ASSERT( command != NULL ); + + commands::BytesMessageCommand* bytesMessage = + dynamic_cast< commands::BytesMessageCommand* >( command ); + + CPPUNIT_ASSERT( bytesMessage != NULL ); + + CPPUNIT_ASSERT( bytesMessage->getBodyBytes() != NULL ); + std::string bytesText( + (const char*)bytesMessage->getBodyBytes(), + (int)bytesMessage->getBodyLength() ); + CPPUNIT_ASSERT( bytesText == "123456789" ); + + } + + }; + +}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_STOMPCOMMANDREADERTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/StompCommandWriterTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/StompCommandWriterTest.cpp new file mode 100644 index 0000000000..06249e0893 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/StompCommandWriterTest.cpp @@ -0,0 +1,3 @@ +#include "StompCommandWriterTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::StompCommandWriterTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/StompCommandWriterTest.h b/activemq-cpp/src/test/activemq/connector/stomp/StompCommandWriterTest.h new file mode 100644 index 0000000000..f205a85672 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/StompCommandWriterTest.h @@ -0,0 +1,102 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_STOMPCOMMANDWRITERTEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_STOMPCOMMANDWRITERTEST_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + class StompCommandWriterTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( StompCommandWriterTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + StompCommandWriterTest() {} + virtual ~StompCommandWriterTest() {} + + void test( void ) + { + io::ByteArrayOutputStream boStream; + + StompCommandWriter writer( &boStream ); + + const char* result = + "CONNECTED\nsession:test\n\n\0\n" // 26 = 26 + "SEND\n" // 5 + "destination:/topic/a\n" // 21 + "message-id:123\n" // 15 + "sampleProperty:testvalue\n\n" // 26 + "testMessage\0\n" // 13 = 80 + "SEND\n" // 5 + "content-length:9\n" // 17 + "destination:/topic/a\n" // 21 + "message-id:123\n" // 15 + "sampleProperty:testvalue\n\n" // 26 + "123456789\0\n"; // 11 = 95 + // 201 + commands::ConnectedCommand connectedCommand; + commands::TextMessageCommand textCommand; + commands::BytesMessageCommand bytesCommand; + + // Sync to expected output + connectedCommand.setSessionId( "test" ); + + // Sync to expected output + textCommand.setCMSDestination( StompTopic("a") ); + textCommand.setCMSMessageId( "123" ); + textCommand.getProperties().setProperty( + "sampleProperty", "testvalue" ); + textCommand.setText( "testMessage" ); + + // Sync to expected output + bytesCommand.setCMSDestination( StompTopic("a") ); + bytesCommand.setCMSMessageId( "123" ); + bytesCommand.getProperties().setProperty( + "sampleProperty", "testvalue" ); + bytesCommand.setBodyBytes( + (const unsigned char*)"123456789", 9 ); + + writer.writeCommand( &connectedCommand ); + writer.writeCommand( &textCommand ); + writer.writeCommand( &bytesCommand ); + + const unsigned char* alloc = boStream.getByteArray(); + + //for( int i = 0; i < 201; ++i ) + //{ + // std::cout << result[i] << " == " << alloc[i] << std::endl; + //} + + CPPUNIT_ASSERT( boStream.getByteArraySize() == 201 ); + + for( int i = 0; i < 201; ++i ) + { + CPPUNIT_ASSERT( result[i] == alloc[i] ); + } + + // Use STL Compare + CPPUNIT_ASSERT( + std::equal( &result[0], &result[200], + boStream.getByteArray() ) ); + } + + }; + +}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_STOMPCOMMANDWRITERTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/StompConnectorTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/StompConnectorTest.cpp new file mode 100644 index 0000000000..7f8321b4c8 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/StompConnectorTest.cpp @@ -0,0 +1,3 @@ +#include "StompConnectorTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::StompConnectorTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/StompConnectorTest.h b/activemq-cpp/src/test/activemq/connector/stomp/StompConnectorTest.h new file mode 100644 index 0000000000..5a9bf578cb --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/StompConnectorTest.h @@ -0,0 +1,319 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_STOMPCONNECTORTEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_STOMPCONNECTORTEST_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + class StompConnectorTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( StompConnectorTest ); + CPPUNIT_TEST( testSessions ); + CPPUNIT_TEST( testConsumers ); + CPPUNIT_TEST( testProducers ); + CPPUNIT_TEST( testCommand ); + CPPUNIT_TEST( testSendingCommands ); + CPPUNIT_TEST_SUITE_END(); + + public: + + StompConnectorTest() {} + virtual ~StompConnectorTest() {} + + class MyCommandListener : public transport::CommandListener{ + public: + + transport::Command* cmd; + + public: + + MyCommandListener(){ + cmd = NULL; + } + virtual ~MyCommandListener(){} + + virtual void onCommand( transport::Command* command ){ + cmd = command; + } + }; + + class MyMessageListener : public ConsumerMessageListener{ + public: + + std::vector consumers; + + public: + virtual ~MyMessageListener(){} + + virtual void onConsumerMessage( ConsumerInfo* consumer, + core::ActiveMQMessage* msg ) + { + consumers.push_back( consumer ); + } + }; + + void testSessions() + { + std::string connectionId = "testConnectionId"; + StompResponseBuilder responseBuilder("testConnectionId"); + transport::DummyTransport transport( &responseBuilder ); + util::SimpleProperties properties; + StompConnector connector( &transport, properties ); + connector.start(); + + SessionInfo* info1 = connector.createSession( cms::Session::AutoAcknowledge ); + CPPUNIT_ASSERT( info1->getAckMode() == cms::Session::AutoAcknowledge ); + CPPUNIT_ASSERT( info1->getConnectionId() == connectionId ); + + SessionInfo* info2 = connector.createSession( cms::Session::DupsOkAcknowledge ); + CPPUNIT_ASSERT( info2->getAckMode() == cms::Session::DupsOkAcknowledge ); + CPPUNIT_ASSERT( info2->getConnectionId() == connectionId ); + + SessionInfo* info3 = connector.createSession( cms::Session::ClientAcknowledge ); + CPPUNIT_ASSERT( info3->getAckMode() == cms::Session::ClientAcknowledge ); + CPPUNIT_ASSERT( info3->getConnectionId() == connectionId ); + + SessionInfo* info4 = connector.createSession( cms::Session::Transactional ); + CPPUNIT_ASSERT( info4->getAckMode() == cms::Session::Transactional ); + CPPUNIT_ASSERT( info4->getConnectionId() == connectionId ); + + connector.destroyResource( info1 ); + connector.destroyResource( info2 ); + connector.destroyResource( info3 ); + connector.destroyResource( info4 ); + + } + + void testConsumers() + { + std::string connectionId = "testConnectionId"; + StompResponseBuilder responseBuilder("testConnectionId"); + transport::DummyTransport transport( &responseBuilder ); + util::SimpleProperties properties; + StompConnector connector( &transport, properties ); + connector.start(); + + SessionInfo* info1 = connector.createSession( cms::Session::AutoAcknowledge ); + std::string sel1 = ""; + StompTopic dest1( "dummy.topic.1" ); + ConsumerInfo* cinfo1 = connector.createConsumer( &dest1, info1, sel1 ); + CPPUNIT_ASSERT( cinfo1->getSessionInfo() == info1 ); + CPPUNIT_ASSERT( cinfo1->getDestination().toString() == dest1.toString() ); + CPPUNIT_ASSERT( cinfo1->getMessageSelector() == sel1 ); + + SessionInfo* info2 = connector.createSession( cms::Session::DupsOkAcknowledge ); + std::string sel2 = "mysel2"; + StompTopic dest2( "dummy.topic.2" ); + ConsumerInfo* cinfo2 = connector.createConsumer( &dest2, info2, sel2 ); + CPPUNIT_ASSERT( cinfo2->getSessionInfo() == info2 ); + CPPUNIT_ASSERT( cinfo2->getDestination().toString() == dest2.toString() ); + CPPUNIT_ASSERT( cinfo2->getMessageSelector() == sel2 ); + + SessionInfo* info3 = connector.createSession( cms::Session::ClientAcknowledge ); + std::string sel3 = "mysel3"; + StompQueue dest3( "dummy.queue.1" ); + ConsumerInfo* cinfo3 = connector.createConsumer( &dest3, info3, sel3 ); + CPPUNIT_ASSERT( cinfo3->getSessionInfo() == info3 ); + CPPUNIT_ASSERT( cinfo3->getDestination().toString() == dest3.toString() ); + CPPUNIT_ASSERT( cinfo3->getMessageSelector() == sel3 ); + + SessionInfo* info4 = connector.createSession( cms::Session::Transactional ); + std::string sel4 = ""; + StompTopic dest4( "dummy.queue.2" ); + ConsumerInfo* cinfo4 = connector.createConsumer( &dest4, info4, sel4 ); + CPPUNIT_ASSERT( cinfo4->getSessionInfo() == info4 ); + CPPUNIT_ASSERT( cinfo4->getDestination().toString() == dest4.toString() ); + CPPUNIT_ASSERT( cinfo4->getMessageSelector() == sel4 ); + + connector.destroyResource( cinfo1 ); + connector.destroyResource( cinfo2 ); + connector.destroyResource( cinfo3 ); + connector.destroyResource( cinfo4 ); + + connector.destroyResource( info1 ); + connector.destroyResource( info2 ); + connector.destroyResource( info3 ); + connector.destroyResource( info4 ); + } + + void testProducers() + { + std::string connectionId = "testConnectionId"; + StompResponseBuilder responseBuilder("testConnectionId"); + transport::DummyTransport transport( &responseBuilder ); + util::SimpleProperties properties; + StompConnector connector( &transport, properties ); + connector.start(); + + SessionInfo* info1 = connector.createSession( cms::Session::AutoAcknowledge ); + StompTopic dest1( "dummy.topic.1" ); + ProducerInfo* pinfo1 = connector.createProducer( &dest1, info1 ); + CPPUNIT_ASSERT( pinfo1->getSessionInfo() == info1 ); + CPPUNIT_ASSERT( pinfo1->getDestination().toString() == dest1.toString() ); + + SessionInfo* info2 = connector.createSession( cms::Session::DupsOkAcknowledge ); + StompTopic dest2( "dummy.topic.2" ); + ProducerInfo* pinfo2 = connector.createProducer( &dest2, info2 ); + CPPUNIT_ASSERT( pinfo2->getSessionInfo() == info2 ); + CPPUNIT_ASSERT( pinfo2->getDestination().toString() == dest2.toString() ); + + SessionInfo* info3 = connector.createSession( cms::Session::ClientAcknowledge ); + StompQueue dest3( "dummy.queue.1" ); + ProducerInfo* pinfo3 = connector.createProducer( &dest3, info3 ); + CPPUNIT_ASSERT( pinfo3->getSessionInfo() == info3 ); + CPPUNIT_ASSERT( pinfo3->getDestination().toString() == dest3.toString() ); + + SessionInfo* info4 = connector.createSession( cms::Session::Transactional ); + StompTopic dest4( "dummy.queue.2" ); + ProducerInfo* pinfo4 = connector.createProducer( &dest4, info4 ); + CPPUNIT_ASSERT( pinfo4->getSessionInfo() == info4 ); + CPPUNIT_ASSERT( pinfo4->getDestination().toString() == dest4.toString() ); + + connector.destroyResource( pinfo1 ); + connector.destroyResource( pinfo2 ); + connector.destroyResource( pinfo3 ); + connector.destroyResource( pinfo4 ); + + connector.destroyResource( info1 ); + connector.destroyResource( info2 ); + connector.destroyResource( info3 ); + connector.destroyResource( info4 ); + } + + void testCommand() + { + std::string connectionId = "testConnectionId"; + StompResponseBuilder responseBuilder("testConnectionId"); + transport::DummyTransport transport( &responseBuilder ); + util::SimpleProperties properties; + StompConnector connector( &transport, properties ); + connector.start(); + + StompTopic dest1( "dummy.topic" ); + StompTopic dest2( "dummy.topic2" ); + + SessionInfo* info1 = connector.createSession( cms::Session::AutoAcknowledge ); + ConsumerInfo* cinfo1 = connector.createConsumer( &dest1, info1, "" ); + + SessionInfo* info2 = connector.createSession( cms::Session::DupsOkAcknowledge ); + ConsumerInfo* cinfo2 = connector.createConsumer( &dest1, info2, "" ); + + SessionInfo* info3 = connector.createSession( cms::Session::ClientAcknowledge ); + ConsumerInfo* cinfo3 = connector.createConsumer( &dest2, info3, "" ); + + SessionInfo* info4 = connector.createSession( cms::Session::Transactional ); + ConsumerInfo* cinfo4 = connector.createConsumer( &dest2, info4, "" ); + + MyMessageListener listener; + connector.setConsumerMessageListener( &listener ); + + StompFrame* frame = new StompFrame(); + frame->setCommand( "MESSAGE" ); + frame->getProperties().setProperty( + "destination", dest1.toProviderString() ); + const char* buffer = strdup("hello world"); + frame->setBody( buffer, 12 ); + + commands::TextMessageCommand* msg = + new commands::TextMessageCommand( frame ); + transport.fireCommand( msg ); + + CPPUNIT_ASSERT( listener.consumers.size() == 2 ); + for( unsigned int ix=0; ixsetCommand( "MESSAGE" ); + frame->getProperties().setProperty( + "destination", dest2.toProviderString() ); + buffer = strdup("hello world"); + frame->setBody( buffer, 12 ); + + msg = new commands::TextMessageCommand( frame ); + transport.fireCommand( msg ); + + CPPUNIT_ASSERT( listener.consumers.size() == 2 ); + for( unsigned int ix=0; ixstart(); + + StompTopic dest1( "dummy.topic.1" ); + + MyCommandListener cmdListener; + transport.setOutgoingCommandListener( &cmdListener ); + + SessionInfo* info1 = connector->createSession( cms::Session::AutoAcknowledge ); + ConsumerInfo* cinfo1 = connector->createConsumer( &dest1, info1, "" ); + CPPUNIT_ASSERT( cmdListener.cmd != NULL ); + + cmdListener.cmd = NULL; + + SessionInfo* info2 = connector->createSession( cms::Session::DupsOkAcknowledge ); + ConsumerInfo* cinfo2 = connector->createConsumer( &dest1, info2, "" ); + CPPUNIT_ASSERT( cmdListener.cmd == NULL ); + + cmdListener.cmd = NULL; + + connector->destroyResource( cinfo1 ); + CPPUNIT_ASSERT( cmdListener.cmd == NULL ); + + cmdListener.cmd = NULL; + + connector->destroyResource( cinfo2 ); + CPPUNIT_ASSERT( cmdListener.cmd != NULL ); + + connector->destroyResource( info1 ); + connector->destroyResource( info2 ); + + delete connector; + CPPUNIT_ASSERT( cmdListener.cmd != NULL ); + } + + }; + +}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_STOMPCONNECTORTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/StompFrameTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/StompFrameTest.cpp new file mode 100644 index 0000000000..cd10e18dbf --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/StompFrameTest.cpp @@ -0,0 +1,3 @@ +#include "StompFrameTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::StompFrameTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/StompFrameTest.h b/activemq-cpp/src/test/activemq/connector/stomp/StompFrameTest.h new file mode 100644 index 0000000000..55ec7946e8 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/StompFrameTest.h @@ -0,0 +1,52 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_STOMPFRAMETEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_STOMPFRAMETEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + class StompFrameTest : public CppUnit::TestFixture + { + + CPPUNIT_TEST_SUITE( StompFrameTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + virtual ~StompFrameTest() {} + + void test() + { + StompFrame frame; + + CPPUNIT_ASSERT( frame.getCommand() == "" ); + frame.setCommand("test"); + CPPUNIT_ASSERT( frame.getCommand() == "test" ); + + frame.getProperties().setProperty("key", "value"); + + std::string result = frame.getProperties().getProperty("key"); + + CPPUNIT_ASSERT( result == "value" ); + + CPPUNIT_ASSERT( frame.getBody() == NULL ); + CPPUNIT_ASSERT( frame.getBodyLength() == 0 ); + + frame.setBody( strdup("ABC"), 4 ); + + CPPUNIT_ASSERT( frame.getBody() != NULL ); + CPPUNIT_ASSERT( frame.getBodyLength() == 4 ); + CPPUNIT_ASSERT( std::string(frame.getBody()) == "ABC" ); + } + + }; + +}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_STOMPFRAMETEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/StompResponseBuilder.h b/activemq-cpp/src/test/activemq/connector/stomp/StompResponseBuilder.h new file mode 100644 index 0000000000..e5cd129dfe --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/StompResponseBuilder.h @@ -0,0 +1,45 @@ +#ifndef ACTIVEMQ_CONNECTOR_STOMP_STOMPRESPONSEBUILDER_H_ +#define ACTIVEMQ_CONNECTOR_STOMP_STOMPRESPONSEBUILDER_H_ + +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + class StompResponseBuilder : public transport::DummyTransport::ResponseBuilder{ + + private: + + std::string sessionId; + + public: + + StompResponseBuilder( const std::string& sessionId ){ + this->sessionId = sessionId; + } + + virtual ~StompResponseBuilder(){} + + virtual transport::Response* buildResponse( const transport::Command* cmd ){ + + const commands::ConnectCommand* connectCommand = + dynamic_cast(cmd); + + if( connectCommand != NULL ){ + commands::ConnectedCommand* resp = new commands::ConnectedCommand(); + resp->setCorrelationId( connectCommand->getCommandId() ); + resp->setSessionId( sessionId ); + return resp; + } + + throw transport::CommandIOException( __FILE__, __LINE__, + "StompResponseBuilder - unrecognized command" ); + } + }; + +}}} + +#endif /*ACTIVEMQ_CONNECTOR_STOMP_STOMPRESPONSEBUILDER_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/StompSessionManagerTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/StompSessionManagerTest.cpp new file mode 100644 index 0000000000..11680f1d93 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/StompSessionManagerTest.cpp @@ -0,0 +1,3 @@ +#include "StompSessionManagerTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::StompSessionManagerTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/StompSessionManagerTest.h b/activemq-cpp/src/test/activemq/connector/stomp/StompSessionManagerTest.h new file mode 100644 index 0000000000..baea196801 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/StompSessionManagerTest.h @@ -0,0 +1,252 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_STOMPSESSIONMANAGERTEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_STOMPSESSIONMANAGERTEST_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ + + class StompSessionManagerTest : public CppUnit::TestFixture + { + + CPPUNIT_TEST_SUITE( StompSessionManagerTest ); + CPPUNIT_TEST( testSessions ); + CPPUNIT_TEST( testConsumers ); + CPPUNIT_TEST( testCommand ); + CPPUNIT_TEST( testSendingCommands ); + CPPUNIT_TEST_SUITE_END(); + + public: + + class MyCommandListener : public transport::CommandListener{ + public: + + transport::Command* cmd; + + public: + + MyCommandListener(){ + cmd = NULL; + } + virtual ~MyCommandListener(){} + + virtual void onCommand( transport::Command* command ){ + cmd = command; + } + }; + + class MyMessageListener : public ConsumerMessageListener{ + public: + + std::vector consumers; + + public: + virtual ~MyMessageListener(){} + + virtual void onConsumerMessage( ConsumerInfo* consumer, + core::ActiveMQMessage* msg ) + { + consumers.push_back( consumer ); + } + }; + + virtual ~StompSessionManagerTest() {} + + void testSessions() + { + std::string connectionId = "testConnectionId"; + StompResponseBuilder responseBuilder("testSessionId"); + transport::DummyTransport transport( &responseBuilder ); + StompSessionManager manager( connectionId, &transport ); + + SessionInfo* info1 = manager.createSession( cms::Session::AutoAcknowledge ); + CPPUNIT_ASSERT( info1->getAckMode() == cms::Session::AutoAcknowledge ); + CPPUNIT_ASSERT( info1->getConnectionId() == connectionId ); + + SessionInfo* info2 = manager.createSession( cms::Session::DupsOkAcknowledge ); + CPPUNIT_ASSERT( info2->getAckMode() == cms::Session::DupsOkAcknowledge ); + CPPUNIT_ASSERT( info2->getConnectionId() == connectionId ); + + SessionInfo* info3 = manager.createSession( cms::Session::ClientAcknowledge ); + CPPUNIT_ASSERT( info3->getAckMode() == cms::Session::ClientAcknowledge ); + CPPUNIT_ASSERT( info3->getConnectionId() == connectionId ); + + SessionInfo* info4 = manager.createSession( cms::Session::Transactional ); + CPPUNIT_ASSERT( info4->getAckMode() == cms::Session::Transactional ); + CPPUNIT_ASSERT( info4->getConnectionId() == connectionId ); + + delete info1; + delete info2; + delete info3; + delete info4; + } + + void testConsumers() + { + std::string connectionId = "testConnectionId"; + StompResponseBuilder responseBuilder("testSessionId"); + transport::DummyTransport transport( &responseBuilder ); + StompSessionManager manager( connectionId, &transport ); + + SessionInfo* info1 = manager.createSession( cms::Session::AutoAcknowledge ); + std::string sel1 = ""; + StompTopic dest1( "dummy.topic.1" ); + ConsumerInfo* cinfo1 = manager.createConsumer( &dest1, info1, sel1 ); + CPPUNIT_ASSERT( cinfo1->getSessionInfo() == info1 ); + CPPUNIT_ASSERT( cinfo1->getDestination().toString() == dest1.toString() ); + CPPUNIT_ASSERT( cinfo1->getMessageSelector() == sel1 ); + + SessionInfo* info2 = manager.createSession( cms::Session::DupsOkAcknowledge ); + std::string sel2 = "mysel2"; + StompTopic dest2( "dummy.topic.2" ); + ConsumerInfo* cinfo2 = manager.createConsumer( &dest2, info2, sel2 ); + CPPUNIT_ASSERT( cinfo2->getSessionInfo() == info2 ); + CPPUNIT_ASSERT( cinfo2->getDestination().toString() == dest2.toString() ); + CPPUNIT_ASSERT( cinfo2->getMessageSelector() == sel2 ); + + SessionInfo* info3 = manager.createSession( cms::Session::ClientAcknowledge ); + std::string sel3 = "mysel3"; + StompQueue dest3( "dummy.queue.1" ); + ConsumerInfo* cinfo3 = manager.createConsumer( &dest3, info3, sel3 ); + CPPUNIT_ASSERT( cinfo3->getSessionInfo() == info3 ); + CPPUNIT_ASSERT( cinfo3->getDestination().toString() == dest3.toString() ); + CPPUNIT_ASSERT( cinfo3->getMessageSelector() == sel3 ); + + SessionInfo* info4 = manager.createSession( cms::Session::Transactional ); + std::string sel4 = ""; + StompTopic dest4( "dummy.queue.2" ); + ConsumerInfo* cinfo4 = manager.createConsumer( &dest4, info4, sel4 ); + CPPUNIT_ASSERT( cinfo4->getSessionInfo() == info4 ); + CPPUNIT_ASSERT( cinfo4->getDestination().toString() == dest4.toString() ); + CPPUNIT_ASSERT( cinfo4->getMessageSelector() == sel4 ); + + delete info1; + delete info2; + delete info3; + delete info4; + + delete cinfo1; + delete cinfo2; + delete cinfo3; + delete cinfo4; + } + + void testCommand() + { + std::string connectionId = "testConnectionId"; + StompResponseBuilder responseBuilder("testSessionId"); + transport::DummyTransport transport( &responseBuilder ); + StompSessionManager manager( connectionId, &transport ); + + StompTopic dest1( "dummy.topic" ); + StompTopic dest2( "dummy.topic2" ); + + SessionInfo* info1 = manager.createSession( cms::Session::AutoAcknowledge ); + ConsumerInfo* cinfo1 = manager.createConsumer( &dest1, info1, "" ); + + SessionInfo* info2 = manager.createSession( cms::Session::DupsOkAcknowledge ); + ConsumerInfo* cinfo2 = manager.createConsumer( &dest1, info2, "" ); + + SessionInfo* info3 = manager.createSession( cms::Session::ClientAcknowledge ); + ConsumerInfo* cinfo3 = manager.createConsumer( &dest2, info3, "" ); + + SessionInfo* info4 = manager.createSession( cms::Session::Transactional ); + ConsumerInfo* cinfo4 = manager.createConsumer( &dest2, info4, "" ); + + MyMessageListener listener; + manager.setConsumerMessageListener( &listener ); + + commands::TextMessageCommand* msg = new commands::TextMessageCommand(); + msg->setCMSDestination( dest1 ); + msg->setText( "hello world" ); + manager.onStompCommand( msg ); + + CPPUNIT_ASSERT( listener.consumers.size() == 2 ); + for( unsigned int ix=0; ixsetCMSDestination( dest2 ); + msg->setText( "hello world" ); + manager.onStompCommand( msg ); + + CPPUNIT_ASSERT( listener.consumers.size() == 2 ); + for( unsigned int ix=0; ix +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class AbortCommandTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( AbortCommandTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + AbortCommandTest() {} + virtual ~AbortCommandTest() {} + + void test(void) + { + AbortCommand cmd; + + CPPUNIT_ASSERT( cmd.getStompCommandId() == + CommandConstants::ABORT ); + + CPPUNIT_ASSERT( cmd.isResponseRequired() == false ); + cmd.setResponseRequired( true ); + cmd.setCommandId( 123 ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == true ); + CPPUNIT_ASSERT( cmd.getCommandId() == 123 ); + cmd.setCorrelationId( 99 ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == 99 ); + CPPUNIT_ASSERT( cmd.getTransactionId() == NULL ); + cmd.setTransactionId( "ID:123456" ); + CPPUNIT_ASSERT( std::string( cmd.getTransactionId() ) == + "ID:123456" ); + + StompFrame* frame = cmd.marshal().clone(); + + CPPUNIT_ASSERT( frame != NULL ); + + AbortCommand cmd1( frame ); + + CPPUNIT_ASSERT( cmd.getCommandId() == cmd1.getCommandId() ); + CPPUNIT_ASSERT( cmd.getStompCommandId() == cmd1.getStompCommandId() ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == cmd1.isResponseRequired() ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == cmd1.getCorrelationId() ); + CPPUNIT_ASSERT( std::string(cmd.getTransactionId()) == cmd1.getTransactionId() ); + + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_ABORTCOMMANDTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/AckCommandTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/commands/AckCommandTest.cpp new file mode 100644 index 0000000000..3d3999a924 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/AckCommandTest.cpp @@ -0,0 +1,3 @@ +#include "AckCommandTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::commands::AckCommandTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/AckCommandTest.h b/activemq-cpp/src/test/activemq/connector/stomp/commands/AckCommandTest.h new file mode 100644 index 0000000000..40d326bd12 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/AckCommandTest.h @@ -0,0 +1,65 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_ACKCOMMANDTEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_ACKCOMMANDTEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class AckCommandTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( AckCommandTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + AckCommandTest() {} + virtual ~AckCommandTest() {} + + void test(void) + { + AckCommand cmd; + + CPPUNIT_ASSERT( cmd.getStompCommandId() == + CommandConstants::ACK ); + + CPPUNIT_ASSERT( cmd.isResponseRequired() == false ); + cmd.setResponseRequired( true ); + cmd.setCommandId( 123 ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == true ); + CPPUNIT_ASSERT( cmd.getCommandId() == 123 ); + cmd.setCorrelationId( 99 ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == 99 ); + CPPUNIT_ASSERT( cmd.getTransactionId() == NULL ); + cmd.setTransactionId( "ID:123456" ); + CPPUNIT_ASSERT( std::string( cmd.getTransactionId() ) == + "ID:123456" ); + CPPUNIT_ASSERT( cmd.getMessageId() == NULL ); + cmd.setMessageId( "ID:123456789" ); + CPPUNIT_ASSERT( std::string( cmd.getMessageId() ) == "ID:123456789" ); + + StompFrame* frame = cmd.marshal().clone(); + + CPPUNIT_ASSERT( frame != NULL ); + + AckCommand cmd1( frame ); + + CPPUNIT_ASSERT( cmd.getCommandId() == cmd1.getCommandId() ); + CPPUNIT_ASSERT( cmd.getStompCommandId() == cmd1.getStompCommandId() ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == cmd1.isResponseRequired() ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == cmd1.getCorrelationId() ); + CPPUNIT_ASSERT( std::string(cmd.getMessageId()) == cmd1.getMessageId() ); + CPPUNIT_ASSERT( std::string(cmd.getTransactionId()) == cmd1.getTransactionId() ); + + } + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_ACKCOMMANDTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/BeginCommandTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/commands/BeginCommandTest.cpp new file mode 100644 index 0000000000..0714d20855 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/BeginCommandTest.cpp @@ -0,0 +1,3 @@ +#include "BeginCommandTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::commands::BeginCommandTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/BeginCommandTest.h b/activemq-cpp/src/test/activemq/connector/stomp/commands/BeginCommandTest.h new file mode 100644 index 0000000000..b6bbe2f036 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/BeginCommandTest.h @@ -0,0 +1,62 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_BEGINCOMMANDTEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_BEGINCOMMANDTEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class BeginCommandTest: public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( BeginCommandTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + BeginCommandTest() {} + virtual ~BeginCommandTest() {} + + void test(void) + { + BeginCommand cmd; + + CPPUNIT_ASSERT( cmd.getStompCommandId() == + CommandConstants::BEGIN ); + + CPPUNIT_ASSERT( cmd.isResponseRequired() == false ); + cmd.setResponseRequired( true ); + cmd.setCommandId( 123 ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == true ); + CPPUNIT_ASSERT( cmd.getCommandId() == 123 ); + cmd.setCorrelationId( 99 ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == 99 ); + CPPUNIT_ASSERT( cmd.getTransactionId() == NULL ); + cmd.setTransactionId( "ID:123456" ); + CPPUNIT_ASSERT( std::string( cmd.getTransactionId() ) == + "ID:123456" ); + + StompFrame* frame = cmd.marshal().clone(); + + CPPUNIT_ASSERT( frame != NULL ); + + BeginCommand cmd1( frame ); + + CPPUNIT_ASSERT( cmd.getCommandId() == cmd1.getCommandId() ); + CPPUNIT_ASSERT( cmd.getStompCommandId() == cmd1.getStompCommandId() ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == cmd1.isResponseRequired() ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == cmd1.getCorrelationId() ); + CPPUNIT_ASSERT( std::string(cmd.getTransactionId()) == cmd1.getTransactionId() ); + + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_BEGINCOMMANDTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/BytesMessageCommandTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/commands/BytesMessageCommandTest.cpp new file mode 100644 index 0000000000..faa5515552 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/BytesMessageCommandTest.cpp @@ -0,0 +1,3 @@ +#include "BytesMessageCommandTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::commands::BytesMessageCommandTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/BytesMessageCommandTest.h b/activemq-cpp/src/test/activemq/connector/stomp/commands/BytesMessageCommandTest.h new file mode 100644 index 0000000000..1d39e7ce9e --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/BytesMessageCommandTest.h @@ -0,0 +1,188 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_BYTESMESSAGECOMMANDTEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_BYTESMESSAGECOMMANDTEST_H_ + +#include +#include +#include +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class BytesMessageCommandTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( BytesMessageCommandTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + protected: + + class TestAckHandler : public core::ActiveMQAckHandler + { + public: + + TestAckHandler(void) { wasAcked = false; } + virtual ~TestAckHandler(void) {} + + virtual void acknowledgeMessage( const core::ActiveMQMessage* message) + throw ( cms::CMSException ) + { + wasAcked = true; + } + + public: + + bool wasAcked; + + }; + + public: + + BytesMessageCommandTest() {} + virtual ~BytesMessageCommandTest() {} + + void test(void) + { + TestAckHandler ackHandler; + BytesMessageCommand cmd; + + CPPUNIT_ASSERT( cmd.getStompCommandId() == + CommandConstants::SEND ); + + CPPUNIT_ASSERT( cmd.isResponseRequired() == false ); + cmd.setResponseRequired( true ); + cmd.setCommandId( 123 ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == true ); + CPPUNIT_ASSERT( cmd.getCommandId() == 123 ); + cmd.setCorrelationId( 99 ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == 99 ); + CPPUNIT_ASSERT( cmd.getTransactionId() == NULL ); + cmd.setTransactionId( "ID:123456" ); + CPPUNIT_ASSERT( std::string( cmd.getTransactionId() ) == + "ID:123456" ); + StompTopic topic("testTopic"); + cmd.setCMSDestination( topic ); + CPPUNIT_ASSERT( cmd.getCMSDestination().toString() == + "testTopic" ); + + StompFrame* frame = cmd.marshal().clone(); + + CPPUNIT_ASSERT( frame != NULL ); + + BytesMessageCommand cmd1( frame ); + + CPPUNIT_ASSERT( cmd.getCommandId() == cmd1.getCommandId() ); + CPPUNIT_ASSERT( cmd.getStompCommandId() == cmd1.getStompCommandId() ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == cmd1.isResponseRequired() ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == cmd1.getCorrelationId() ); + CPPUNIT_ASSERT( std::string(cmd.getTransactionId()) == cmd1.getTransactionId() ); + + cmd.setAckHandler( &ackHandler ); + cmd.acknowledge(); + CPPUNIT_ASSERT( ackHandler.wasAcked == true ); + + CPPUNIT_ASSERT( + cmd.getProperties().hasProperty( "test" ) == false ); + cmd.getProperties().setProperty( "test", "value" ); + CPPUNIT_ASSERT( + cmd.getProperties().hasProperty( "test" ) == true ); + CPPUNIT_ASSERT( + std::string( cmd.getProperties().getProperty( "test" ) ) == "value" ); + + CPPUNIT_ASSERT( cmd.getCMSCorrelationId() == NULL ); + cmd.setCMSCorrelationId( "ID:1234567" ); + CPPUNIT_ASSERT( std::string( cmd.getCMSCorrelationId() ) == + "ID:1234567" ); + CPPUNIT_ASSERT( cmd.getCMSDeliveryMode() == + cms::Message::PERSISTANT ); + cmd.setCMSDeliveryMode( cms::Message::NONPERSISTANT ); + CPPUNIT_ASSERT( cmd.getCMSDeliveryMode() == + cms::Message::NONPERSISTANT ); + cmd.setCMSDestination( topic ); + CPPUNIT_ASSERT( cmd.getCMSDestination().toString() == + "testTopic" ); + CPPUNIT_ASSERT( cmd.getCMSExpiration() == 0 ); + cmd.setCMSExpiration( 123 ); + CPPUNIT_ASSERT( cmd.getCMSExpiration() == 123 ); + CPPUNIT_ASSERT( cmd.getCMSMessageId() == NULL ); + cmd.setCMSMessageId( "ID:1234567" ); + CPPUNIT_ASSERT( std::string( cmd.getCMSMessageId() ) == + "ID:1234567" ); + CPPUNIT_ASSERT( cmd.getCMSPriority() == 0 ); + cmd.setCMSPriority( 5 ); + CPPUNIT_ASSERT( cmd.getCMSPriority() == 5 ); + CPPUNIT_ASSERT( cmd.getCMSRedelivered() == false ); + cmd.setCMSRedelivered( true ); + CPPUNIT_ASSERT( cmd.getCMSRedelivered() == true ); + CPPUNIT_ASSERT( cmd.getCMSReplyTo() == NULL ); + cmd.setCMSReplyTo( "topic" ); + CPPUNIT_ASSERT( std::string( cmd.getCMSReplyTo() ) == + "topic" ); + CPPUNIT_ASSERT( cmd.getCMSTimeStamp() == 0 ); + cmd.setCMSTimeStamp( 123 ); + CPPUNIT_ASSERT( cmd.getCMSTimeStamp() == 123 ); + CPPUNIT_ASSERT( cmd.getCMSMessageType() == NULL ); + cmd.setCMSMessageType( "test" ); + CPPUNIT_ASSERT( std::string( cmd.getCMSMessageType() ) == + "test" ); + CPPUNIT_ASSERT( cmd.getRedeliveryCount() == 0 ); + cmd.setRedeliveryCount( 123 ); + CPPUNIT_ASSERT( cmd.getRedeliveryCount() == 123 ); + + const char* bodyBytes = "TESTBODYBYTES\0"; + CPPUNIT_ASSERT( cmd.getBodyLength() == 0 ); + cmd.setBodyBytes( (const unsigned char*)bodyBytes, + strlen( bodyBytes ) + 1 ); + CPPUNIT_ASSERT( cmd.getBodyLength() == + (unsigned int)strlen( bodyBytes ) + 1 ); + CPPUNIT_ASSERT( std::string( (const char*)cmd.getBodyBytes() ) == + bodyBytes ); + + cms::Message* cmd2 = cmd.clone(); + + CPPUNIT_ASSERT( cmd.getCMSPriority() == cmd2->getCMSPriority() ); + CPPUNIT_ASSERT( cmd.getCMSTimeStamp() == cmd2->getCMSTimeStamp() ); + CPPUNIT_ASSERT( cmd.getCMSExpiration() == cmd2->getCMSExpiration() ); + CPPUNIT_ASSERT( cmd.getCMSDeliveryMode() == cmd2->getCMSDeliveryMode() ); + CPPUNIT_ASSERT( std::string(cmd.getCMSCorrelationId()) == cmd2->getCMSCorrelationId() ); + CPPUNIT_ASSERT( std::string(cmd.getCMSReplyTo()) == cmd2->getCMSReplyTo() ); + CPPUNIT_ASSERT( std::string(cmd.getCMSMessageType()) == cmd2->getCMSMessageType() ); + CPPUNIT_ASSERT( std::string(cmd.getCMSMessageId()) == cmd2->getCMSMessageId() ); + + core::ActiveMQMessage* message = + dynamic_cast< core::ActiveMQMessage* >( cmd2 ); + + CPPUNIT_ASSERT( message != NULL ); + CPPUNIT_ASSERT( cmd.getRedeliveryCount() == + message->getRedeliveryCount() ); + + StompCommand* cmd4 = + dynamic_cast< StompCommand* >( cmd2 ); + + CPPUNIT_ASSERT( cmd4 != NULL ); + CPPUNIT_ASSERT( cmd.getCommandId() == cmd4->getCommandId() ); + CPPUNIT_ASSERT( cmd.getStompCommandId() == cmd4->getStompCommandId() ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == cmd4->isResponseRequired() ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == cmd4->getCorrelationId() ); + CPPUNIT_ASSERT( std::string(cmd.getTransactionId()) == + cmd4->getTransactionId() ); + + BytesMessageCommand* cmd5 = + dynamic_cast< BytesMessageCommand* >( cmd2 ); + + CPPUNIT_ASSERT( cmd5 != NULL ); + CPPUNIT_ASSERT( std::string( (const char*)cmd.getBodyBytes() ) == + (const char*)cmd5->getBodyBytes() ); + + delete cmd2; + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_BYTESMESSAGECOMMANDTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/CommandConstantsTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/commands/CommandConstantsTest.cpp new file mode 100644 index 0000000000..e1edef4a6c --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/CommandConstantsTest.cpp @@ -0,0 +1,3 @@ +#include "CommandConstantsTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::commands::CommandConstantsTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/CommandConstantsTest.h b/activemq-cpp/src/test/activemq/connector/stomp/commands/CommandConstantsTest.h new file mode 100644 index 0000000000..3805e569c2 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/CommandConstantsTest.h @@ -0,0 +1,81 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_COMMANDCONSTANTSTEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_COMMANDCONSTANTSTEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class CommandConstantsTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( CommandConstantsTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + CommandConstantsTest() {} + virtual ~CommandConstantsTest() {} + + void test(void) + { + int index = 0; + + for(; index < CommandConstants::NUM_COMMANDS; ++index ) + { + CommandConstants::CommandId cmdId = + (CommandConstants::CommandId)index; + std::string cmd = CommandConstants::toString( cmdId ); + + CPPUNIT_ASSERT( cmd != ""); + CPPUNIT_ASSERT( + cmdId == CommandConstants::toCommandId( cmd ) ); + } + + CPPUNIT_ASSERT( index != 0 ); + + index = 0; + + for(; index < CommandConstants::NUM_STOMP_HEADERS; ++index ) + { + CommandConstants::StompHeader hdrId = + (CommandConstants::StompHeader)index; + std::string hdr = CommandConstants::toString( hdrId ); + + CPPUNIT_ASSERT( hdr != ""); + CPPUNIT_ASSERT( + hdrId == CommandConstants::toStompHeader( hdr ) ); + } + + CPPUNIT_ASSERT( index != 0 ); + + index = 0; + + for(; index < CommandConstants::NUM_ACK_MODES; ++index ) + { + CommandConstants::AckMode ackMode = + (CommandConstants::AckMode)index; + std::string ackStr = CommandConstants::toString( ackMode ); + + CPPUNIT_ASSERT( ackStr != ""); + CPPUNIT_ASSERT( + ackMode == CommandConstants::toAckMode( ackStr ) ); + } + + CPPUNIT_ASSERT( index != 0 ); + + CPPUNIT_ASSERT( CommandConstants::queuePrefix != NULL ); + CPPUNIT_ASSERT( CommandConstants::topicPrefix != NULL ); + + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_COMMANDCONSTANTSTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/CommitCommandTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/commands/CommitCommandTest.cpp new file mode 100644 index 0000000000..eb0d1ef0c5 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/CommitCommandTest.cpp @@ -0,0 +1,3 @@ +#include "CommitCommandTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::commands::CommitCommandTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/CommitCommandTest.h b/activemq-cpp/src/test/activemq/connector/stomp/commands/CommitCommandTest.h new file mode 100644 index 0000000000..d1a66cc6b8 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/CommitCommandTest.h @@ -0,0 +1,62 @@ +#ifndef COMMITCOMMANDTEST_H_ +#define COMMITCOMMANDTEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class CommitCommandTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( CommitCommandTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + CommitCommandTest() {} + virtual ~CommitCommandTest() {} + + void test(void) + { + CommitCommand cmd; + + CPPUNIT_ASSERT( cmd.getStompCommandId() == + CommandConstants::COMMIT ); + + CPPUNIT_ASSERT( cmd.isResponseRequired() == false ); + cmd.setResponseRequired( true ); + cmd.setCommandId( 123 ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == true ); + CPPUNIT_ASSERT( cmd.getCommandId() == 123 ); + cmd.setCorrelationId( 99 ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == 99 ); + CPPUNIT_ASSERT( cmd.getTransactionId() == NULL ); + cmd.setTransactionId( "ID:123456" ); + CPPUNIT_ASSERT( std::string( cmd.getTransactionId() ) == + "ID:123456" ); + + StompFrame* frame = cmd.marshal().clone(); + + CPPUNIT_ASSERT( frame != NULL ); + + CommitCommand cmd1( frame ); + + CPPUNIT_ASSERT( cmd.getCommandId() == cmd1.getCommandId() ); + CPPUNIT_ASSERT( cmd.getStompCommandId() == cmd1.getStompCommandId() ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == cmd1.isResponseRequired() ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == cmd1.getCorrelationId() ); + CPPUNIT_ASSERT( std::string(cmd.getTransactionId()) == cmd1.getTransactionId() ); + + } + + }; + +}}}} + +#endif /*COMMITCOMMANDTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/ConnectCommandTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/commands/ConnectCommandTest.cpp new file mode 100644 index 0000000000..aef8012c9f --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/ConnectCommandTest.cpp @@ -0,0 +1,3 @@ +#include "ConnectCommandTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::commands::ConnectCommandTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/ConnectCommandTest.h b/activemq-cpp/src/test/activemq/connector/stomp/commands/ConnectCommandTest.h new file mode 100644 index 0000000000..f88f668429 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/ConnectCommandTest.h @@ -0,0 +1,62 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_CONNECTCOMMANDTEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_CONNECTCOMMANDTEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class ConnectCommandTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( ConnectCommandTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + ConnectCommandTest() {} + virtual ~ConnectCommandTest() {} + + void test(void) + { + ConnectCommand cmd; + + CPPUNIT_ASSERT( cmd.getStompCommandId() == + CommandConstants::CONNECT ); + + CPPUNIT_ASSERT( cmd.isResponseRequired() == false ); + cmd.setResponseRequired( true ); + cmd.setCommandId( 123 ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == true ); + CPPUNIT_ASSERT( cmd.getCommandId() == 123 ); + cmd.setCorrelationId( 99 ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == 99 ); + CPPUNIT_ASSERT( cmd.getTransactionId() == NULL ); + cmd.setTransactionId( "ID:123456" ); + CPPUNIT_ASSERT( std::string( cmd.getTransactionId() ) == + "ID:123456" ); + + StompFrame* frame = cmd.marshal().clone(); + + CPPUNIT_ASSERT( frame != NULL ); + + ConnectCommand cmd1( frame ); + + CPPUNIT_ASSERT( cmd.getCommandId() == cmd1.getCommandId() ); + CPPUNIT_ASSERT( cmd.getStompCommandId() == cmd1.getStompCommandId() ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == cmd1.isResponseRequired() ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == cmd1.getCorrelationId() ); + CPPUNIT_ASSERT( std::string(cmd.getTransactionId()) == cmd1.getTransactionId() ); + + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_CONNECTCOMMANDTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/ConnectedCommandTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/commands/ConnectedCommandTest.cpp new file mode 100644 index 0000000000..2a374273eb --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/ConnectedCommandTest.cpp @@ -0,0 +1,3 @@ +#include "ConnectedCommandTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::commands::ConnectedCommandTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/ConnectedCommandTest.h b/activemq-cpp/src/test/activemq/connector/stomp/commands/ConnectedCommandTest.h new file mode 100644 index 0000000000..ec21057eea --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/ConnectedCommandTest.h @@ -0,0 +1,62 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_CONNECTEDCOMMANDTEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_CONNECTEDCOMMANDTEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class ConnectedCommandTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( ConnectedCommandTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + ConnectedCommandTest() {} + virtual ~ConnectedCommandTest() {} + + void test(void) + { + ConnectedCommand cmd; + + CPPUNIT_ASSERT( cmd.getStompCommandId() == + CommandConstants::CONNECTED ); + + CPPUNIT_ASSERT( cmd.isResponseRequired() == false ); + cmd.setResponseRequired( true ); + cmd.setCommandId( 123 ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == true ); + CPPUNIT_ASSERT( cmd.getCommandId() == 123 ); + cmd.setCorrelationId( 99 ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == 99 ); + CPPUNIT_ASSERT( cmd.getTransactionId() == NULL ); + cmd.setTransactionId( "ID:123456" ); + CPPUNIT_ASSERT( std::string( cmd.getTransactionId() ) == + "ID:123456" ); + + StompFrame* frame = cmd.marshal().clone(); + + CPPUNIT_ASSERT( frame != NULL ); + + ConnectedCommand cmd1( frame ); + + CPPUNIT_ASSERT( cmd.getCommandId() == cmd1.getCommandId() ); + CPPUNIT_ASSERT( cmd.getStompCommandId() == cmd1.getStompCommandId() ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == cmd1.isResponseRequired() ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == cmd1.getCorrelationId() ); + CPPUNIT_ASSERT( std::string(cmd.getTransactionId()) == cmd1.getTransactionId() ); + + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_CONNECTEDCOMMANDTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/DisconnectCommandTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/commands/DisconnectCommandTest.cpp new file mode 100644 index 0000000000..20d134d893 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/DisconnectCommandTest.cpp @@ -0,0 +1,3 @@ +#include "DisconnectCommandTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::commands::DisconnectCommandTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/DisconnectCommandTest.h b/activemq-cpp/src/test/activemq/connector/stomp/commands/DisconnectCommandTest.h new file mode 100644 index 0000000000..a6f9be89a1 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/DisconnectCommandTest.h @@ -0,0 +1,62 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_DISCONNECTCOMMANDTEXT_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_DISCONNECTCOMMANDTEXT_H_ + +#include +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class DisconnectCommandTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( DisconnectCommandTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + DisconnectCommandTest() {} + virtual ~DisconnectCommandTest() {} + + void test(void) + { + DisconnectCommand cmd; + + CPPUNIT_ASSERT( cmd.getStompCommandId() == + CommandConstants::DISCONNECT ); + + CPPUNIT_ASSERT( cmd.isResponseRequired() == false ); + cmd.setResponseRequired( true ); + cmd.setCommandId( 123 ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == true ); + CPPUNIT_ASSERT( cmd.getCommandId() == 123 ); + cmd.setCorrelationId( 99 ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == 99 ); + CPPUNIT_ASSERT( cmd.getTransactionId() == NULL ); + cmd.setTransactionId( "ID:123456" ); + CPPUNIT_ASSERT( std::string( cmd.getTransactionId() ) == + "ID:123456" ); + + StompFrame* frame = cmd.marshal().clone(); + + CPPUNIT_ASSERT( frame != NULL ); + + DisconnectCommand cmd1( frame ); + + CPPUNIT_ASSERT( cmd.getCommandId() == cmd1.getCommandId() ); + CPPUNIT_ASSERT( cmd.getStompCommandId() == cmd1.getStompCommandId() ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == cmd1.isResponseRequired() ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == cmd1.getCorrelationId() ); + CPPUNIT_ASSERT( std::string(cmd.getTransactionId()) == cmd1.getTransactionId() ); + + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_DISCONNECTCOMMANDTEXT_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/ErrorCommandTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/commands/ErrorCommandTest.cpp new file mode 100644 index 0000000000..394aa46154 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/ErrorCommandTest.cpp @@ -0,0 +1,3 @@ +#include "ErrorCommandTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::commands::ErrorCommandTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/ErrorCommandTest.h b/activemq-cpp/src/test/activemq/connector/stomp/commands/ErrorCommandTest.h new file mode 100644 index 0000000000..b00504780e --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/ErrorCommandTest.h @@ -0,0 +1,70 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_ERRORCOMMANDTEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_ERRORCOMMANDTEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class ErrorCommandTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( ErrorCommandTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + ErrorCommandTest() {} + virtual ~ErrorCommandTest() {} + + void test(void) + { + ErrorCommand cmd; + + CPPUNIT_ASSERT( cmd.getStompCommandId() == + CommandConstants::ERROR_CMD ); + + CPPUNIT_ASSERT( cmd.isResponseRequired() == false ); + cmd.setResponseRequired( true ); + cmd.setCommandId( 123 ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == true ); + CPPUNIT_ASSERT( cmd.getCommandId() == 123 ); + cmd.setCorrelationId( 99 ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == 99 ); + CPPUNIT_ASSERT( cmd.getTransactionId() == NULL ); + cmd.setTransactionId( "ID:123456" ); + CPPUNIT_ASSERT( std::string( cmd.getTransactionId() ) == + "ID:123456" ); + CPPUNIT_ASSERT( cmd.getErrorMessage() == NULL ); + cmd.setErrorMessage( "Error" ); + CPPUNIT_ASSERT( std::string( cmd.getErrorMessage() ) == "Error" ); + CPPUNIT_ASSERT( cmd.getErrorDetails() == NULL ); + cmd.setErrorDetails( "ErrorD" ); + CPPUNIT_ASSERT( std::string( cmd.getErrorDetails() ) == "ErrorD" ); + + StompFrame* frame = cmd.marshal().clone(); + + CPPUNIT_ASSERT( frame != NULL ); + + ErrorCommand cmd1( frame ); + + CPPUNIT_ASSERT( cmd.getCommandId() == cmd1.getCommandId() ); + CPPUNIT_ASSERT( cmd.getStompCommandId() == cmd1.getStompCommandId() ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == cmd1.isResponseRequired() ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == cmd1.getCorrelationId() ); + CPPUNIT_ASSERT( std::string(cmd.getTransactionId()) == cmd1.getTransactionId() ); + CPPUNIT_ASSERT( std::string(cmd.getErrorMessage()) == cmd1.getErrorMessage() ); + CPPUNIT_ASSERT( std::string(cmd.getErrorDetails()) == cmd1.getErrorDetails() ); + + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_ERRORCOMMANDTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/MessageCommandTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/commands/MessageCommandTest.cpp new file mode 100644 index 0000000000..61eb578098 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/MessageCommandTest.cpp @@ -0,0 +1,3 @@ +#include "MessageCommandTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::commands::MessageCommandTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/MessageCommandTest.h b/activemq-cpp/src/test/activemq/connector/stomp/commands/MessageCommandTest.h new file mode 100644 index 0000000000..8a21f66673 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/MessageCommandTest.h @@ -0,0 +1,171 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_MESSAGECOMMANDTEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_MESSAGECOMMANDTEST_H_ + +#include +#include + +#include +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class MessageCommandTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( MessageCommandTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + protected: + + class TestAckHandler : public core::ActiveMQAckHandler + { + public: + + TestAckHandler(void) { wasAcked = false; } + virtual ~TestAckHandler(void) {} + + virtual void acknowledgeMessage( const core::ActiveMQMessage* message) + throw ( cms::CMSException ) + { + wasAcked = true; + } + + public: + + bool wasAcked; + + }; + + public: + + MessageCommandTest() {} + virtual ~MessageCommandTest() {} + + void test(void) + { + TestAckHandler ackHandler; + MessageCommand cmd; + + CPPUNIT_ASSERT( cmd.getStompCommandId() == + CommandConstants::SEND ); + + CPPUNIT_ASSERT( cmd.isResponseRequired() == false ); + cmd.setResponseRequired( true ); + cmd.setCommandId( 123 ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == true ); + CPPUNIT_ASSERT( cmd.getCommandId() == 123 ); + cmd.setCorrelationId( 99 ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == 99 ); + CPPUNIT_ASSERT( cmd.getTransactionId() == NULL ); + cmd.setTransactionId( "ID:123456" ); + CPPUNIT_ASSERT( std::string( cmd.getTransactionId() ) == + "ID:123456" ); + StompTopic topic("testTopic"); + cmd.setCMSDestination( topic ); + + StompFrame* frame = cmd.marshal().clone(); + + CPPUNIT_ASSERT( frame != NULL ); + + MessageCommand cmd1( frame ); + + CPPUNIT_ASSERT( cmd.getCommandId() == cmd1.getCommandId() ); + CPPUNIT_ASSERT( cmd.getStompCommandId() == CommandConstants::SEND ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == cmd1.isResponseRequired() ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == cmd1.getCorrelationId() ); + CPPUNIT_ASSERT( std::string(cmd.getTransactionId()) == cmd1.getTransactionId() ); + + cmd.setAckHandler( &ackHandler ); + cmd.acknowledge(); + CPPUNIT_ASSERT( ackHandler.wasAcked == true ); + + CPPUNIT_ASSERT( + cmd.getProperties().hasProperty( "test" ) == false ); + cmd.getProperties().setProperty( "test", "value" ); + CPPUNIT_ASSERT( + cmd.getProperties().hasProperty( "test" ) == true ); + CPPUNIT_ASSERT( + std::string( cmd.getProperties().getProperty( "test" ) ) == + "value" ); + + CPPUNIT_ASSERT( cmd.getCMSCorrelationId() == NULL ); + cmd.setCMSCorrelationId( "ID:1234567" ); + CPPUNIT_ASSERT( std::string( cmd.getCMSCorrelationId() ) == + "ID:1234567" ); + CPPUNIT_ASSERT( cmd.getCMSDeliveryMode() == + cms::Message::PERSISTANT ); + cmd.setCMSDeliveryMode( cms::Message::NONPERSISTANT ); + CPPUNIT_ASSERT( cmd.getCMSDeliveryMode() == + cms::Message::NONPERSISTANT ); + CPPUNIT_ASSERT( cmd.getCMSDestination().toString() == + "testTopic" ); + CPPUNIT_ASSERT( cmd.getCMSExpiration() == 0 ); + cmd.setCMSExpiration( 123 ); + CPPUNIT_ASSERT( cmd.getCMSExpiration() == 123 ); + CPPUNIT_ASSERT( cmd.getCMSMessageId() == NULL ); + cmd.setCMSMessageId( "ID:1234567" ); + CPPUNIT_ASSERT( std::string( cmd.getCMSMessageId() ) == + "ID:1234567" ); + CPPUNIT_ASSERT( cmd.getCMSPriority() == 0 ); + cmd.setCMSPriority( 5 ); + CPPUNIT_ASSERT( cmd.getCMSPriority() == 5 ); + CPPUNIT_ASSERT( cmd.getCMSRedelivered() == false ); + cmd.setCMSRedelivered( true ); + CPPUNIT_ASSERT( cmd.getCMSRedelivered() == true ); + CPPUNIT_ASSERT( cmd.getCMSReplyTo() == NULL ); + cmd.setCMSReplyTo( "topic" ); + CPPUNIT_ASSERT( std::string( cmd.getCMSReplyTo() ) == + "topic" ); + CPPUNIT_ASSERT( cmd.getCMSTimeStamp() == 0 ); + cmd.setCMSTimeStamp( 123 ); + CPPUNIT_ASSERT( cmd.getCMSTimeStamp() == 123 ); + CPPUNIT_ASSERT( cmd.getCMSMessageType() == NULL ); + cmd.setCMSMessageType( "test" ); + CPPUNIT_ASSERT( std::string( cmd.getCMSMessageType() ) == + "test" ); + CPPUNIT_ASSERT( cmd.getRedeliveryCount() == 0 ); + cmd.setRedeliveryCount( 123 ); + CPPUNIT_ASSERT( cmd.getRedeliveryCount() == 123 ); + + cms::Message* cmd2 = cmd.clone(); + + CPPUNIT_ASSERT( cmd.getCMSPriority() == cmd2->getCMSPriority() ); + CPPUNIT_ASSERT( cmd.getCMSTimeStamp() == cmd2->getCMSTimeStamp() ); + CPPUNIT_ASSERT( cmd.getCMSExpiration() == cmd2->getCMSExpiration() ); + CPPUNIT_ASSERT( cmd.getCMSDeliveryMode() == cmd2->getCMSDeliveryMode() ); + CPPUNIT_ASSERT( std::string(cmd.getCMSCorrelationId()) == cmd2->getCMSCorrelationId() ); + CPPUNIT_ASSERT( std::string(cmd.getCMSReplyTo()) == cmd2->getCMSReplyTo() ); + CPPUNIT_ASSERT( std::string(cmd.getCMSMessageType()) == cmd2->getCMSMessageType() ); + CPPUNIT_ASSERT( std::string(cmd.getCMSMessageId()) == cmd2->getCMSMessageId() ); + + core::ActiveMQMessage* message = + dynamic_cast< core::ActiveMQMessage* >( cmd2 ); + + CPPUNIT_ASSERT( message != NULL ); + CPPUNIT_ASSERT( cmd.getRedeliveryCount() == + message->getRedeliveryCount() ); + + StompCommand* cmd4 = + dynamic_cast< StompCommand* >( cmd2 ); + + CPPUNIT_ASSERT( cmd4 != NULL ); + CPPUNIT_ASSERT( cmd.getCommandId() == cmd4->getCommandId() ); + CPPUNIT_ASSERT( cmd.getStompCommandId() == cmd4->getStompCommandId() ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == cmd4->isResponseRequired() ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == cmd4->getCorrelationId() ); + CPPUNIT_ASSERT( std::string(cmd.getTransactionId()) == + cmd4->getTransactionId() ); + + delete cmd2; + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_MESSAGECOMMANDTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/ReceiptCommandTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/commands/ReceiptCommandTest.cpp new file mode 100644 index 0000000000..4b6907d4d3 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/ReceiptCommandTest.cpp @@ -0,0 +1,3 @@ +#include "ReceiptCommandTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::commands::ReceiptCommandTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/ReceiptCommandTest.h b/activemq-cpp/src/test/activemq/connector/stomp/commands/ReceiptCommandTest.h new file mode 100644 index 0000000000..5c662e3324 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/ReceiptCommandTest.h @@ -0,0 +1,69 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_COMMAND_RECEIPTCOMMANDTEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMAND_RECEIPTCOMMANDTEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class ReceiptCommandTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( ReceiptCommandTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + ReceiptCommandTest() {} + virtual ~ReceiptCommandTest() {} + + void test(void) + { + ReceiptCommand cmd; + + CPPUNIT_ASSERT( cmd.getStompCommandId() == + CommandConstants::RECEIPT ); + + CPPUNIT_ASSERT( cmd.isResponseRequired() == false ); + cmd.setResponseRequired( true ); + cmd.setCommandId( 123 ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == true ); + CPPUNIT_ASSERT( cmd.getCommandId() == 123 ); + cmd.setCorrelationId( 99 ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == 99 ); + CPPUNIT_ASSERT( cmd.getTransactionId() == NULL ); + cmd.setTransactionId( "ID:123456" ); + CPPUNIT_ASSERT( std::string( cmd.getTransactionId() ) == + "ID:123456" ); + CPPUNIT_ASSERT( cmd.getReceiptId() == NULL ); + cmd.setReceiptId( "456987" ); + CPPUNIT_ASSERT( std::string( cmd.getReceiptId() ) == + "456987" ); + + StompFrame* frame = cmd.marshal().clone(); + + CPPUNIT_ASSERT( frame != NULL ); + + ReceiptCommand cmd1( frame ); + + CPPUNIT_ASSERT( cmd.getCommandId() == cmd1.getCommandId() ); + CPPUNIT_ASSERT( cmd.getStompCommandId() == cmd1.getStompCommandId() ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == cmd1.isResponseRequired() ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == cmd1.getCorrelationId() ); + CPPUNIT_ASSERT( std::string( cmd.getTransactionId() ) == + cmd1.getTransactionId() ); + CPPUNIT_ASSERT( std::string( cmd.getReceiptId() ) == + cmd1.getReceiptId() ); + + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMAND_RECEIPTCOMMANDTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/SubscribeCommandTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/commands/SubscribeCommandTest.cpp new file mode 100644 index 0000000000..a1b757b7f2 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/SubscribeCommandTest.cpp @@ -0,0 +1,3 @@ +#include "SubscribeCommandTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::commands::SubscribeCommandTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/SubscribeCommandTest.h b/activemq-cpp/src/test/activemq/connector/stomp/commands/SubscribeCommandTest.h new file mode 100644 index 0000000000..ac92f5a756 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/SubscribeCommandTest.h @@ -0,0 +1,91 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_COMMAND_SUBSCRIBECOMMANDTEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMAND_SUBSCRIBECOMMANDTEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class SubscribeCommandTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( SubscribeCommandTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + SubscribeCommandTest() {} + virtual ~SubscribeCommandTest() {} + + void test(void) + { + SubscribeCommand cmd; + + CPPUNIT_ASSERT( cmd.getStompCommandId() == + CommandConstants::SUBSCRIBE ); + + CPPUNIT_ASSERT( cmd.isResponseRequired() == false ); + cmd.setResponseRequired( true ); + cmd.setCommandId( 123 ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == true ); + CPPUNIT_ASSERT( cmd.getCommandId() == 123 ); + cmd.setCorrelationId( 99 ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == 99 ); + CPPUNIT_ASSERT( cmd.getTransactionId() == NULL ); + cmd.setTransactionId( "ID:123456" ); + CPPUNIT_ASSERT( std::string( cmd.getTransactionId() ) == + "ID:123456" ); + CPPUNIT_ASSERT( cmd.getDestination() == NULL ); + cmd.setDestination( "456987" ); + CPPUNIT_ASSERT( std::string( cmd.getDestination() ) == + "456987" ); + CPPUNIT_ASSERT( cmd.getAckMode() == + CommandConstants::ACK_AUTO ); + cmd.setAckMode( CommandConstants::ACK_CLIENT ); + CPPUNIT_ASSERT( cmd.getAckMode() == + CommandConstants::ACK_CLIENT ); + CPPUNIT_ASSERT( cmd.getMessageSelector() == NULL ); + cmd.setMessageSelector( "Selector" ); + CPPUNIT_ASSERT( std::string( cmd.getMessageSelector() ) == + "Selector" ); + CPPUNIT_ASSERT( cmd.getSubscriptionName() == NULL ); + cmd.setSubscriptionName( "subscription" ); + CPPUNIT_ASSERT( std::string( cmd.getSubscriptionName() ) == + "subscription" ); + CPPUNIT_ASSERT( cmd.getNoLocal() == false ); + cmd.setNoLocal( true ); + CPPUNIT_ASSERT( cmd.getNoLocal() == true ); + + StompFrame* frame = cmd.marshal().clone(); + + CPPUNIT_ASSERT( frame != NULL ); + + SubscribeCommand cmd1( frame ); + + CPPUNIT_ASSERT( cmd.getCommandId() == cmd1.getCommandId() ); + CPPUNIT_ASSERT( cmd.getStompCommandId() == cmd1.getStompCommandId() ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == cmd1.isResponseRequired() ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == cmd1.getCorrelationId() ); + CPPUNIT_ASSERT( std::string(cmd.getTransactionId()) == + cmd1.getTransactionId() ); + CPPUNIT_ASSERT( std::string( cmd.getDestination() ) == + cmd1.getDestination() ); + CPPUNIT_ASSERT( cmd.getAckMode() == cmd1.getAckMode() ); + CPPUNIT_ASSERT( std::string( cmd.getMessageSelector() ) == + cmd1.getMessageSelector() ); + CPPUNIT_ASSERT( std::string( cmd.getSubscriptionName() ) == + cmd1.getSubscriptionName() ); + CPPUNIT_ASSERT( cmd.getNoLocal() == cmd1.getNoLocal() ); + + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMAND_SUBSCRIBECOMMANDTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/TextMessageCommandTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/commands/TextMessageCommandTest.cpp new file mode 100644 index 0000000000..42bd60ca13 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/TextMessageCommandTest.cpp @@ -0,0 +1,3 @@ +#include "TextMessageCommandTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::commands::TextMessageCommandTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/TextMessageCommandTest.h b/activemq-cpp/src/test/activemq/connector/stomp/commands/TextMessageCommandTest.h new file mode 100644 index 0000000000..43c57ea871 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/TextMessageCommandTest.h @@ -0,0 +1,180 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_TEXTMESSAGECOMMANDTEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_TEXTMESSAGECOMMANDTEST_H_ + +#include +#include +#include +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class TextMessageCommandTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( TextMessageCommandTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + protected: + + class TestAckHandler : public core::ActiveMQAckHandler + { + public: + + TestAckHandler(void) { wasAcked = false; } + virtual ~TestAckHandler(void) {} + + virtual void acknowledgeMessage( const core::ActiveMQMessage* message) + throw ( cms::CMSException ) + { + wasAcked = true; + } + + public: + + bool wasAcked; + + }; + + public: + + TextMessageCommandTest() {} + virtual ~TextMessageCommandTest() {} + + void test(void) + { + TestAckHandler ackHandler; + TextMessageCommand cmd; + + CPPUNIT_ASSERT( cmd.getStompCommandId() == + CommandConstants::SEND ); + + CPPUNIT_ASSERT( cmd.isResponseRequired() == false ); + cmd.setResponseRequired( true ); + cmd.setCommandId( 123 ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == true ); + CPPUNIT_ASSERT( cmd.getCommandId() == 123 ); + cmd.setCorrelationId( 99 ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == 99 ); + CPPUNIT_ASSERT( cmd.getTransactionId() == NULL ); + cmd.setTransactionId( "ID:123456" ); + CPPUNIT_ASSERT( std::string( cmd.getTransactionId() ) == + "ID:123456" ); + StompTopic topic("testTopic"); + cmd.setCMSDestination( topic ); + + StompFrame* frame = cmd.marshal().clone(); + + CPPUNIT_ASSERT( frame != NULL ); + + TextMessageCommand cmd1( frame ); + + CPPUNIT_ASSERT( cmd.getCommandId() == cmd1.getCommandId() ); + CPPUNIT_ASSERT( cmd.getStompCommandId() == cmd1.getStompCommandId() ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == cmd1.isResponseRequired() ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == cmd1.getCorrelationId() ); + CPPUNIT_ASSERT( std::string(cmd.getTransactionId()) == cmd1.getTransactionId() ); + + cmd.setAckHandler( &ackHandler ); + cmd.acknowledge(); + CPPUNIT_ASSERT( ackHandler.wasAcked == true ); + + CPPUNIT_ASSERT( + cmd.getProperties().hasProperty( "test" ) == false ); + cmd.getProperties().setProperty( "test", "value" ); + CPPUNIT_ASSERT( + cmd.getProperties().hasProperty( "test" ) == true ); + CPPUNIT_ASSERT( + std::string( cmd.getProperties().getProperty( "test" ) ) == + "value" ); + + CPPUNIT_ASSERT( cmd.getCMSCorrelationId() == NULL ); + cmd.setCMSCorrelationId( "ID:1234567" ); + CPPUNIT_ASSERT( std::string( cmd.getCMSCorrelationId() ) == + "ID:1234567" ); + CPPUNIT_ASSERT( cmd.getCMSDeliveryMode() == + cms::Message::PERSISTANT ); + cmd.setCMSDeliveryMode( cms::Message::NONPERSISTANT ); + CPPUNIT_ASSERT( cmd.getCMSDeliveryMode() == + cms::Message::NONPERSISTANT ); + CPPUNIT_ASSERT( cmd.getCMSDestination().toString() == + "testTopic" ); + CPPUNIT_ASSERT( cmd.getCMSExpiration() == 0 ); + cmd.setCMSExpiration( 123 ); + CPPUNIT_ASSERT( cmd.getCMSExpiration() == 123 ); + CPPUNIT_ASSERT( cmd.getCMSMessageId() == NULL ); + cmd.setCMSMessageId( "ID:1234567" ); + CPPUNIT_ASSERT( std::string( cmd.getCMSMessageId() ) == + "ID:1234567" ); + CPPUNIT_ASSERT( cmd.getCMSPriority() == 0 ); + cmd.setCMSPriority( 5 ); + CPPUNIT_ASSERT( cmd.getCMSPriority() == 5 ); + CPPUNIT_ASSERT( cmd.getCMSRedelivered() == false ); + cmd.setCMSRedelivered( true ); + CPPUNIT_ASSERT( cmd.getCMSRedelivered() == true ); + CPPUNIT_ASSERT( cmd.getCMSReplyTo() == NULL ); + cmd.setCMSReplyTo( "topic" ); + CPPUNIT_ASSERT( std::string( cmd.getCMSReplyTo() ) == + "topic" ); + CPPUNIT_ASSERT( cmd.getCMSTimeStamp() == 0 ); + cmd.setCMSTimeStamp( 123 ); + CPPUNIT_ASSERT( cmd.getCMSTimeStamp() == 123 ); + CPPUNIT_ASSERT( cmd.getCMSMessageType() == NULL ); + cmd.setCMSMessageType( "text" ); + CPPUNIT_ASSERT( std::string( cmd.getCMSMessageType() ) == + "text" ); + CPPUNIT_ASSERT( cmd.getRedeliveryCount() == 0 ); + cmd.setRedeliveryCount( 123 ); + CPPUNIT_ASSERT( cmd.getRedeliveryCount() == 123 ); + + CPPUNIT_ASSERT( cmd.getText() == NULL ); + cmd.setText( "TESTMESSAGE" ); + CPPUNIT_ASSERT( std::string( cmd.getText() ) == "TESTMESSAGE" ); + + cms::Message* cmd2 = cmd.clone(); + + CPPUNIT_ASSERT( cmd.getCMSPriority() == cmd2->getCMSPriority() ); + CPPUNIT_ASSERT( cmd.getCMSTimeStamp() == cmd2->getCMSTimeStamp() ); + CPPUNIT_ASSERT( cmd.getCMSExpiration() == cmd2->getCMSExpiration() ); + CPPUNIT_ASSERT( cmd.getCMSDeliveryMode() == cmd2->getCMSDeliveryMode() ); + CPPUNIT_ASSERT( std::string(cmd.getCMSCorrelationId()) == cmd2->getCMSCorrelationId() ); + CPPUNIT_ASSERT( std::string(cmd.getCMSReplyTo()) == cmd2->getCMSReplyTo() ); + CPPUNIT_ASSERT( std::string(cmd.getCMSMessageType()) == cmd2->getCMSMessageType() ); + CPPUNIT_ASSERT( std::string(cmd.getCMSMessageId()) == cmd2->getCMSMessageId() ); + + core::ActiveMQMessage* message = + dynamic_cast< core::ActiveMQMessage* >( cmd2 ); + + CPPUNIT_ASSERT( message != NULL ); + CPPUNIT_ASSERT( cmd.getRedeliveryCount() == + message->getRedeliveryCount() ); + + StompCommand* cmd4 = + dynamic_cast< StompCommand* >( cmd2 ); + + CPPUNIT_ASSERT( cmd4 != NULL ); + CPPUNIT_ASSERT( cmd.getCommandId() == cmd4->getCommandId() ); + CPPUNIT_ASSERT( cmd.getStompCommandId() == cmd4->getStompCommandId() ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == cmd4->isResponseRequired() ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == cmd4->getCorrelationId() ); + CPPUNIT_ASSERT( std::string( cmd.getTransactionId() ) == + cmd4->getTransactionId() ); + + TextMessageCommand* cmd5 = + dynamic_cast< TextMessageCommand* >(message); + + CPPUNIT_ASSERT( cmd5 != NULL ); + CPPUNIT_ASSERT( std::string( cmd.getText() ) == cmd5->getText() ); + + delete cmd2; + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMANDS_TEXTMESSAGECOMMANDTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/UnsubscribeCommandTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/commands/UnsubscribeCommandTest.cpp new file mode 100644 index 0000000000..c172a7ff25 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/UnsubscribeCommandTest.cpp @@ -0,0 +1,3 @@ +#include "UnsubscribeCommandTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::commands::UnsubscribeCommandTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/commands/UnsubscribeCommandTest.h b/activemq-cpp/src/test/activemq/connector/stomp/commands/UnsubscribeCommandTest.h new file mode 100644 index 0000000000..1efba1af34 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/commands/UnsubscribeCommandTest.h @@ -0,0 +1,67 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_COMMAND_UNSUBSCRIBECOMMANDTEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_COMMAND_UNSUBSCRIBECOMMANDTEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace commands{ + + class UnsubscribeCommandTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( UnsubscribeCommandTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + UnsubscribeCommandTest() {} + virtual ~UnsubscribeCommandTest() {} + + void test(void) + { + UnsubscribeCommand cmd; + + CPPUNIT_ASSERT( cmd.getStompCommandId() == + CommandConstants::UNSUBSCRIBE ); + + CPPUNIT_ASSERT( cmd.isResponseRequired() == false ); + cmd.setResponseRequired( true ); + cmd.setCommandId( 123 ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == true ); + CPPUNIT_ASSERT( cmd.getCommandId() == 123 ); + cmd.setCorrelationId( 99 ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == 99 ); + CPPUNIT_ASSERT( cmd.getTransactionId() == NULL ); + cmd.setTransactionId( "ID:123456" ); + CPPUNIT_ASSERT( std::string( cmd.getTransactionId() ) == + "ID:123456" ); + CPPUNIT_ASSERT( cmd.getDestination() == NULL ); + cmd.setDestination( "456987" ); + CPPUNIT_ASSERT( std::string( cmd.getDestination() ) == + "456987" ); + + StompFrame* frame = cmd.marshal().clone(); + + CPPUNIT_ASSERT( frame != NULL ); + + UnsubscribeCommand cmd1( frame ); + + CPPUNIT_ASSERT( cmd.getCommandId() == cmd1.getCommandId() ); + CPPUNIT_ASSERT( cmd.getStompCommandId() == cmd1.getStompCommandId() ); + CPPUNIT_ASSERT( cmd.isResponseRequired() == cmd1.isResponseRequired() ); + CPPUNIT_ASSERT( cmd.getCorrelationId() == cmd1.getCorrelationId() ); + CPPUNIT_ASSERT( std::string( cmd.getTransactionId() ) == + cmd1.getTransactionId() ); + CPPUNIT_ASSERT( std::string( cmd.getDestination() ) == + cmd1.getDestination() ); + + } + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_COMMAND_UNSUBSCRIBECOMMANDTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/connector/stomp/marshal/MarshalerTest.cpp b/activemq-cpp/src/test/activemq/connector/stomp/marshal/MarshalerTest.cpp new file mode 100644 index 0000000000..cf1a6654db --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/marshal/MarshalerTest.cpp @@ -0,0 +1,3 @@ +#include "MarshalerTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::connector::stomp::marshal::MarshalerTest ); diff --git a/activemq-cpp/src/test/activemq/connector/stomp/marshal/MarshalerTest.h b/activemq-cpp/src/test/activemq/connector/stomp/marshal/MarshalerTest.h new file mode 100644 index 0000000000..0ab705afe6 --- /dev/null +++ b/activemq-cpp/src/test/activemq/connector/stomp/marshal/MarshalerTest.h @@ -0,0 +1,104 @@ +#ifndef _ACTIVEMQ_CONNECTOR_STOMP_MARSHAL_MARSHALERTEST_H_ +#define _ACTIVEMQ_CONNECTOR_STOMP_MARSHAL_MARSHALERTEST_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace connector{ +namespace stomp{ +namespace marshal{ + + class MarshalerTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( MarshalerTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + MarshalerTest() {} + virtual ~MarshalerTest() {} + + void test( void ) + { + Marshaler marshaler; + + commands::ConnectedCommand connectedCommand; + commands::TextMessageCommand textCommand; + commands::BytesMessageCommand bytesCommand; + + // Sync to expected output + connectedCommand.setSessionId( "test" ); + + // Sync to expected output + textCommand.setCMSDestination( StompTopic("a") ); + textCommand.setCMSMessageId( "123" ); + textCommand.getProperties().setProperty( + "sampleProperty", "testvalue" ); + textCommand.setText( "testMessage" ); + + // Sync to expected output + bytesCommand.setCMSDestination( StompTopic("a") ); + bytesCommand.setCMSMessageId( "123" ); + bytesCommand.getProperties().setProperty( + "sampleProperty", "testvalue" ); + bytesCommand.setBodyBytes( + (const unsigned char*)"123456789\0", 10 ); + + StompFrame* connectedFrame = + marshaler.marshal( &connectedCommand ).clone(); + StompFrame* textFrame = + marshaler.marshal( &textCommand ).clone(); + StompFrame* bytesFrame = + marshaler.marshal( &bytesCommand ).clone(); + + CPPUNIT_ASSERT( connectedFrame != NULL ); + CPPUNIT_ASSERT( textFrame != NULL ); + CPPUNIT_ASSERT( bytesFrame != NULL ); + + commands::ConnectedCommand connectedCommand1( connectedFrame ); + commands::TextMessageCommand textCommand1( textFrame ); + commands::BytesMessageCommand bytesCommand1( bytesFrame ); + + // Connected Tests + CPPUNIT_ASSERT( connectedCommand.getCommandId() == + connectedCommand1.getCommandId() ); + CPPUNIT_ASSERT( connectedCommand.getStompCommandId() == + connectedCommand1.getStompCommandId() ); + CPPUNIT_ASSERT( connectedCommand.isResponseRequired() == + connectedCommand1.isResponseRequired() ); + CPPUNIT_ASSERT( connectedCommand.getCorrelationId() == + connectedCommand1.getCorrelationId() ); + + // TextMessage Tests + CPPUNIT_ASSERT( textCommand.getCommandId() == + textCommand1.getCommandId() ); + CPPUNIT_ASSERT( textCommand.getStompCommandId() == + textCommand1.getStompCommandId() ); + CPPUNIT_ASSERT( std::string( textCommand.getText() ) == + textCommand1.getText() ); + + // BytesMessage Tests + CPPUNIT_ASSERT( bytesCommand.getCommandId() == + bytesCommand1.getCommandId() ); + CPPUNIT_ASSERT( bytesCommand.getStompCommandId() == + bytesCommand1.getStompCommandId() ); + CPPUNIT_ASSERT( std::string( (const char*)bytesCommand.getBodyBytes() ) == + (const char*)bytesCommand1.getBodyBytes() ); + + + } + + }; + +}}}} + +#endif /*_ACTIVEMQ_CONNECTOR_STOMP_MARSHAL_MARSHALERTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/core/ActiveMQConnectionFactoryTest.cpp b/activemq-cpp/src/test/activemq/core/ActiveMQConnectionFactoryTest.cpp new file mode 100644 index 0000000000..acc08f759f --- /dev/null +++ b/activemq-cpp/src/test/activemq/core/ActiveMQConnectionFactoryTest.cpp @@ -0,0 +1,3 @@ +#include "ActiveMQConnectionFactoryTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::core::ActiveMQConnectionFactoryTest ); diff --git a/activemq-cpp/src/test/activemq/core/ActiveMQConnectionFactoryTest.h b/activemq-cpp/src/test/activemq/core/ActiveMQConnectionFactoryTest.h new file mode 100644 index 0000000000..a6bcbf5489 --- /dev/null +++ b/activemq-cpp/src/test/activemq/core/ActiveMQConnectionFactoryTest.h @@ -0,0 +1,63 @@ +#ifndef _ACTIVEMQ_CORE_ACTIVEMQCONNECTIONFACTORYTEST_H_ +#define _ACTIVEMQ_CORE_ACTIVEMQCONNECTIONFACTORYTEST_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace core{ + + class ActiveMQConnectionFactoryTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( ActiveMQConnectionFactoryTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + ActiveMQConnectionFactoryTest() {} + virtual ~ActiveMQConnectionFactoryTest() {} + + void test() + { + try + { + transport::TransportFactoryMapRegistrar registrar( + "dummy", new transport::DummyTransportFactory() ); + + std::string URI = + "dummy://127.0.0.1:23232&wireFormat=stomp"; + + ActiveMQConnectionFactory connectionFactory(URI); + + cms::Connection* connection = + + connectionFactory.createConnection(); + + CPPUNIT_ASSERT( connection != NULL ); + + delete connection; + + return; + } + AMQ_CATCH_NOTHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_NOTHROW( ) + + CPPUNIT_ASSERT( false ); + } + + }; + +}} + +#endif /*_ACTIVEMQ_CORE_ACTIVEMQCONNECTIONFACTORYTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/core/ActiveMQConnectionTest.cpp b/activemq-cpp/src/test/activemq/core/ActiveMQConnectionTest.cpp new file mode 100644 index 0000000000..7c0437e768 --- /dev/null +++ b/activemq-cpp/src/test/activemq/core/ActiveMQConnectionTest.cpp @@ -0,0 +1,4 @@ +#include "ActiveMQConnectionTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::core::ActiveMQConnectionTest ); + diff --git a/activemq-cpp/src/test/activemq/core/ActiveMQConnectionTest.h b/activemq-cpp/src/test/activemq/core/ActiveMQConnectionTest.h new file mode 100644 index 0000000000..d8515924c5 --- /dev/null +++ b/activemq-cpp/src/test/activemq/core/ActiveMQConnectionTest.h @@ -0,0 +1,243 @@ +#ifndef _ACTIVEMQ_CORE_ACTIVEMQCONNECTIONTEST_H_ +#define _ACTIVEMQ_CORE_ACTIVEMQCONNECTIONTEST_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace core{ + + class ActiveMQConnectionTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( ActiveMQConnectionTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + ActiveMQConnectionTest() {}; + virtual ~ActiveMQConnectionTest() {} + + class MyCommandListener : public transport::CommandListener{ + public: + + transport::Command* cmd; + + public: + + MyCommandListener(){ + cmd = NULL; + } + virtual ~MyCommandListener(){} + + virtual void onCommand( transport::Command* command ){ + cmd = command; + } + }; + + class MyMessageListener : + public connector::ConsumerMessageListener + { + public: + + std::vector consumers; + + public: + virtual ~MyMessageListener(){} + + virtual void onConsumerMessage( + connector::ConsumerInfo* consumer, + core::ActiveMQMessage* msg ) + { + consumers.push_back( consumer ); + } + }; + + class MyExceptionListener : public cms::ExceptionListener{ + public: + + bool caughtOne; + + public: + + MyExceptionListener(){ caughtOne = false; } + virtual ~MyExceptionListener(){} + + virtual void onException(const cms::CMSException& ex){ + caughtOne = true; + } + }; + + class MyActiveMQMessageListener : public ActiveMQMessageListener + { + public: + + std::vector messages; + + public: + virtual ~MyActiveMQMessageListener(){} + + virtual void onActiveMQMessage( ActiveMQMessage* message ) + throw ( exceptions::ActiveMQException ) + { + messages.push_back( message ); + } + }; + + void test() + { + try + { + transport::TransportFactoryMapRegistrar registrar( + "dummy", new transport::DummyTransportFactory() ); + + MyMessageListener listener; + MyExceptionListener exListener; + MyCommandListener cmdListener; + MyActiveMQMessageListener msgListener; + std::string connectionId = "testConnectionId"; + util::SimpleProperties* properties = + new util::SimpleProperties(); + transport::Transport* transport = NULL; + + transport::TransportFactory* factory = + transport::TransportFactoryMap::getInstance().lookup( + "dummy" ); + if( factory == NULL ){ + CPPUNIT_ASSERT( false ); + } + + // Create the transport. + transport = factory->createTransport( *properties ); + if( transport == NULL ){ + CPPUNIT_ASSERT( false ); + } + + transport::DummyTransport* dTransport = + dynamic_cast< transport::DummyTransport*>( transport ); + + CPPUNIT_ASSERT( dTransport != NULL ); + + dTransport->setCommandListener( &cmdListener ); + + connector::stomp::StompConnector* connector = + new connector::stomp::StompConnector( + transport, *properties ); + + connector->start(); + + ActiveMQConnection connection( + new ActiveMQConnectionData( + connector, transport, properties) ); + + connection.setExceptionListener( &exListener ); + + cms::Session* session1 = connection.createSession(); + cms::Session* session2 = connection.createSession(); + cms::Session* session3 = connection.createSession(); + + CPPUNIT_ASSERT( session1 != NULL ); + CPPUNIT_ASSERT( session2 != NULL ); + CPPUNIT_ASSERT( session3 != NULL ); + + connector::stomp::StompSessionInfo session; + connector::stomp::StompConsumerInfo consumer; + + session.setSessionId( 1 ); + session.setConnectionId( "TEST:123" ); + session.setAckMode( cms::Session::AutoAcknowledge ); + + consumer.setConsumerId( 1 ); + consumer.setSessionInfo( &session ); + consumer.setDestination( + connector::stomp::StompTopic( "test" ) ); + + connection.addMessageListener( 1, &msgListener ); + + connector::stomp::commands::TextMessageCommand* cmd = + new connector::stomp::commands::TextMessageCommand; + + cmd->setCMSDestination( + connector::stomp::StompTopic( "test" ) ); + + connector::ConsumerMessageListener* consumerListener = + dynamic_cast< connector::ConsumerMessageListener* >( + &connection ); + + connection.start(); + + CPPUNIT_ASSERT( consumerListener != NULL ); + + consumerListener->onConsumerMessage( &consumer, cmd ); + + CPPUNIT_ASSERT( msgListener.messages.size() == 1 ); + + connection.removeMessageListener( 1 ); + + msgListener.messages.clear(); + consumerListener->onConsumerMessage( &consumer, cmd ); + + CPPUNIT_ASSERT( msgListener.messages.size() == 0 ); + + connection.addMessageListener( 1, &msgListener ); + + connection.stop(); + consumerListener->onConsumerMessage( &consumer, cmd ); + connection.start(); + CPPUNIT_ASSERT( msgListener.messages.size() == 0 ); + + cmd = new connector::stomp::commands::TextMessageCommand; + + cmd->setCMSDestination( + connector::stomp::StompTopic( "test" ) ); + + consumerListener->onConsumerMessage( &consumer, cmd ); + CPPUNIT_ASSERT( msgListener.messages.size() == 1 ); + + connection.removeMessageListener( 1 ); + msgListener.messages.clear(); + + connection.close(); + + consumerListener->onConsumerMessage( &consumer, cmd ); + CPPUNIT_ASSERT( exListener.caughtOne == true ); + + delete cmd; + } + catch(...) + { + bool exceptionThrown = false; + + CPPUNIT_ASSERT( exceptionThrown ); + } + } + + }; + +}} + +#endif /*_ACTIVEMQ_CORE_ACTIVEMQCONNECTIONTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/core/ActiveMQSessionTest.cpp b/activemq-cpp/src/test/activemq/core/ActiveMQSessionTest.cpp new file mode 100644 index 0000000000..60aecc6df2 --- /dev/null +++ b/activemq-cpp/src/test/activemq/core/ActiveMQSessionTest.cpp @@ -0,0 +1,3 @@ +#include "ActiveMQSessionTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::core::ActiveMQSessionTest ); diff --git a/activemq-cpp/src/test/activemq/core/ActiveMQSessionTest.h b/activemq-cpp/src/test/activemq/core/ActiveMQSessionTest.h new file mode 100644 index 0000000000..61b9d4bcc8 --- /dev/null +++ b/activemq-cpp/src/test/activemq/core/ActiveMQSessionTest.h @@ -0,0 +1,558 @@ +#ifndef _ACTIVEMQ_CORE_ACTIVEMQSESSIONTEST_H_ +#define _ACTIVEMQ_CORE_ACTIVEMQSESSIONTEST_H_ + +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace core{ + + class ActiveMQSessionTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( ActiveMQSessionTest ); + CPPUNIT_TEST( testAutoAcking ); + CPPUNIT_TEST( testClientAck ); + CPPUNIT_TEST( testTransactional ); + CPPUNIT_TEST_SUITE_END(); + + private: + + class MyCommandListener : public transport::CommandListener{ + public: + + transport::Command* cmd; + + public: + + MyCommandListener(){ + cmd = NULL; + } + virtual ~MyCommandListener(){} + + virtual void onCommand( transport::Command* command ){ + cmd = command; + } + }; + + class MyExceptionListener : public cms::ExceptionListener{ + public: + + bool caughtOne; + + public: + + MyExceptionListener(){ caughtOne = false; } + virtual ~MyExceptionListener(){} + + virtual void onException(const cms::CMSException& ex){ + caughtOne = true; + } + }; + + class MyCMSMessageListener : public cms::MessageListener + { + public: + + std::vector messages; + concurrent::Mutex mutex; + bool ack; + + public: + + MyCMSMessageListener( bool ack = false ){ + this->ack = ack; + } + + virtual ~MyCMSMessageListener(){ + clear(); + } + + virtual void setAck( bool ack ){ + this->ack = ack; + } + + virtual void clear() { + std::vector::iterator itr = + messages.begin(); + + for( ; itr != messages.end(); ++itr ) + { + delete *itr; + } + + messages.clear(); + } + + virtual void onMessage( const cms::Message& message ) + { + synchronized( &mutex ) + { + if( ack ){ + message.acknowledge(); + } + + messages.push_back( message.clone() ); + + mutex.notifyAll(); + } + } + }; + + ActiveMQConnection* connection; + transport::DummyTransport* dTransport; + MyExceptionListener exListener; + MyCommandListener cmdListener; + + public: // CPPUNIT Method Overrides. + + void setUp() + { + try + { + transport::TransportFactoryMapRegistrar registrar( + "dummy", new transport::DummyTransportFactory() ); + + ActiveMQConnectionFactory factory("dummy://127.0.0.1:12345"); + + connection = dynamic_cast< ActiveMQConnection*>( + factory.createConnection() ); + + // Get the Transport and make sure we got a dummy Transport + // then add our command listener, so we can verify that when + // we send a message it hits the wire. + dTransport = dynamic_cast< transport::DummyTransport*>( + connection->getConnectionData()->getTransport() ); + CPPUNIT_ASSERT( dTransport != NULL ); + dTransport->setOutgoingCommandListener( &cmdListener ); + + connection->setExceptionListener( &exListener ); + connection->start(); + } + catch(...) + { + bool exceptionThrown = false; + + CPPUNIT_ASSERT( exceptionThrown ); + } + } + + void tearDown() + { + delete connection; + } + + void injectTextMessage( const std::string message, + const cms::Destination& destination ) + { + connector::stomp::StompFrame* frame = + new connector::stomp::StompFrame(); + frame->setCommand( "MESSAGE" ); + frame->getProperties().setProperty( + "destination", destination.toProviderString() ); + const char* buffer = strdup( message.c_str() ); + frame->setBody( buffer, 12 ); + + connector::stomp::commands::TextMessageCommand* msg = + new connector::stomp::commands::TextMessageCommand( frame ); + + // Init Message + msg->setText( message.c_str() ); + msg->setCMSDestination( destination ); + msg->setCMSMessageId( "Id: 123456" ); + + // Send the Message + CPPUNIT_ASSERT( dTransport != NULL ); + + dTransport->fireCommand( msg ); + } + + public: + + ActiveMQSessionTest(void) {} + virtual ~ActiveMQSessionTest(void) {} + + void testAutoAcking() + { + MyCMSMessageListener msgListener1; + MyCMSMessageListener msgListener2; + + CPPUNIT_ASSERT( connection != NULL ); + + // Create an Auto Ack Session + cms::Session* session = connection->createSession(); + + // Create a Topic + cms::Topic* topic1 = session->createTopic( "TestTopic1"); + cms::Topic* topic2 = session->createTopic( "TestTopic2"); + + CPPUNIT_ASSERT( topic1 != NULL ); + CPPUNIT_ASSERT( topic2 != NULL ); + + // Create a consumer + cms::MessageConsumer* consumer1 = + session->createConsumer( *topic1 ); + cms::MessageConsumer* consumer2 = + session->createConsumer( *topic2 ); + + CPPUNIT_ASSERT( consumer1 != NULL ); + CPPUNIT_ASSERT( consumer2 != NULL ); + + CPPUNIT_ASSERT( consumer1->getMessageSelector() == "" ); + CPPUNIT_ASSERT( consumer2->getMessageSelector() == "" ); + + CPPUNIT_ASSERT( consumer1->receiveNoWait() == NULL ); + CPPUNIT_ASSERT( consumer1->receive( 5 ) == NULL ); + CPPUNIT_ASSERT( consumer2->receiveNoWait() == NULL ); + CPPUNIT_ASSERT( consumer2->receive( 5 ) == NULL ); + + consumer1->setMessageListener( &msgListener1 ); + consumer2->setMessageListener( &msgListener2 ); + + injectTextMessage( "This is a Test 1" , *topic1 ); + + synchronized( &msgListener1.mutex ) + { + if( msgListener1.messages.size() == 0 ) + { + msgListener1.mutex.wait( 3000 ); + } + } + + CPPUNIT_ASSERT( msgListener1.messages.size() == 1 ); + + injectTextMessage( "This is a Test 2" , *topic2 ); + + synchronized( &msgListener2.mutex ) + { + if( msgListener2.messages.size() == 0 ) + { + msgListener2.mutex.wait( 3000 ); + } + } + + CPPUNIT_ASSERT( msgListener2.messages.size() == 1 ); + + cms::TextMessage* msg1 = + dynamic_cast< cms::TextMessage* >( + msgListener1.messages[0] ); + cms::TextMessage* msg2 = + dynamic_cast< cms::TextMessage* >( + msgListener2.messages[0] ); + + CPPUNIT_ASSERT( msg1 != NULL ); + CPPUNIT_ASSERT( msg2 != NULL ); + + std::string text1 = msg1->getText(); + std::string text2 = msg2->getText(); + + CPPUNIT_ASSERT( text1 == "This is a Test 1" ); + CPPUNIT_ASSERT( text2 == "This is a Test 2" ); + + delete consumer1; + delete consumer2; + + delete session; + } + + void testClientAck() + { + MyCMSMessageListener msgListener1( true ); + MyCMSMessageListener msgListener2( true ); + + CPPUNIT_ASSERT( connection != NULL ); + + // Create an Auto Ack Session + cms::Session* session = connection->createSession( + cms::Session::ClientAcknowledge ); + + // Create a Topic + cms::Topic* topic1 = session->createTopic( "TestTopic1"); + cms::Topic* topic2 = session->createTopic( "TestTopic2"); + + CPPUNIT_ASSERT( topic1 != NULL ); + CPPUNIT_ASSERT( topic2 != NULL ); + + // Create a consumer + cms::MessageConsumer* consumer1 = + session->createConsumer( *topic1 ); + cms::MessageConsumer* consumer2 = + session->createConsumer( *topic2 ); + + CPPUNIT_ASSERT( consumer1 != NULL ); + CPPUNIT_ASSERT( consumer2 != NULL ); + + CPPUNIT_ASSERT( consumer1->getMessageSelector() == "" ); + CPPUNIT_ASSERT( consumer2->getMessageSelector() == "" ); + + CPPUNIT_ASSERT( consumer1->receiveNoWait() == NULL ); + CPPUNIT_ASSERT( consumer1->receive( 5 ) == NULL ); + CPPUNIT_ASSERT( consumer2->receiveNoWait() == NULL ); + CPPUNIT_ASSERT( consumer2->receive( 5 ) == NULL ); + + consumer1->setMessageListener( &msgListener1 ); + consumer2->setMessageListener( &msgListener2 ); + + injectTextMessage( "This is a Test 1" , *topic1 ); + + synchronized( &msgListener1.mutex ) + { + if( msgListener1.messages.size() == 0 ) + { + msgListener1.mutex.wait( 3000 ); + } + } + + CPPUNIT_ASSERT( msgListener1.messages.size() == 1 ); + + msgListener1.messages[0]->acknowledge(); + + injectTextMessage( "This is a Test 2" , *topic2 ); + + synchronized( &msgListener2.mutex ) + { + if( msgListener2.messages.size() == 0 ) + { + msgListener2.mutex.wait( 3000 ); + } + } + + CPPUNIT_ASSERT( msgListener2.messages.size() == 1 ); + + msgListener2.messages[0]->acknowledge(); + + cms::TextMessage* msg1 = + dynamic_cast< cms::TextMessage* >( + msgListener1.messages[0] ); + cms::TextMessage* msg2 = + dynamic_cast< cms::TextMessage* >( + msgListener2.messages[0] ); + + CPPUNIT_ASSERT( msg1 != NULL ); + CPPUNIT_ASSERT( msg2 != NULL ); + + std::string text1 = msg1->getText(); + std::string text2 = msg2->getText(); + + CPPUNIT_ASSERT( text1 == "This is a Test 1" ); + CPPUNIT_ASSERT( text2 == "This is a Test 2" ); + + delete consumer1; + delete consumer2; + + delete session; + } + + void testTransactional() + { + MyCMSMessageListener msgListener1; + MyCMSMessageListener msgListener2; + + CPPUNIT_ASSERT( connection != NULL ); + + // Create an Auto Ack Session + cms::Session* session = connection->createSession( + cms::Session::Transactional ); + + // Create a Topic + cms::Topic* topic1 = session->createTopic( "TestTopic1"); + cms::Topic* topic2 = session->createTopic( "TestTopic2"); + + CPPUNIT_ASSERT( topic1 != NULL ); + CPPUNIT_ASSERT( topic2 != NULL ); + + // Create a consumer + cms::MessageConsumer* consumer1 = + session->createConsumer( *topic1 ); + cms::MessageConsumer* consumer2 = + session->createConsumer( *topic2 ); + + CPPUNIT_ASSERT( consumer1 != NULL ); + CPPUNIT_ASSERT( consumer2 != NULL ); + + CPPUNIT_ASSERT( consumer1->getMessageSelector() == "" ); + CPPUNIT_ASSERT( consumer2->getMessageSelector() == "" ); + + CPPUNIT_ASSERT( consumer1->receiveNoWait() == NULL ); + CPPUNIT_ASSERT( consumer1->receive( 5 ) == NULL ); + CPPUNIT_ASSERT( consumer2->receiveNoWait() == NULL ); + CPPUNIT_ASSERT( consumer2->receive( 5 ) == NULL ); + + consumer1->setMessageListener( &msgListener1 ); + consumer2->setMessageListener( &msgListener2 ); + + injectTextMessage( "This is a Test 1" , *topic1 ); + + synchronized( &msgListener1.mutex ) + { + if( msgListener1.messages.size() == 0 ) + { + msgListener1.mutex.wait( 3000 ); + } + } + + CPPUNIT_ASSERT( msgListener1.messages.size() == 1 ); + + session->commit(); + + injectTextMessage( "This is a Test 2" , *topic2 ); + + synchronized( &msgListener2.mutex ) + { + if( msgListener2.messages.size() == 0 ) + { + msgListener2.mutex.wait( 3000 ); + } + } + + CPPUNIT_ASSERT( msgListener2.messages.size() == 1 ); + + session->commit(); + + cms::TextMessage* msg1 = + dynamic_cast< cms::TextMessage* >( + msgListener1.messages[0] ); + cms::TextMessage* msg2 = + dynamic_cast< cms::TextMessage* >( + msgListener2.messages[0] ); + + CPPUNIT_ASSERT( msg1 != NULL ); + CPPUNIT_ASSERT( msg2 != NULL ); + + std::string text1 = msg1->getText(); + std::string text2 = msg2->getText(); + + CPPUNIT_ASSERT( text1 == "This is a Test 1" ); + CPPUNIT_ASSERT( text2 == "This is a Test 2" ); + + msgListener1.clear(); + msgListener2.clear(); + + const unsigned int msgCount = 50; + + for( unsigned int i = 0; i < msgCount; ++i ) + { + std::ostringstream stream; + + stream << "This is test message #" << i << std::ends; + + injectTextMessage( stream.str() , *topic1 ); + } + + for( unsigned int i = 0; i < msgCount; ++i ) + { + std::ostringstream stream; + + stream << "This is test message #" << i << std::ends; + + injectTextMessage( stream.str() , *topic2 ); + } + + synchronized( &msgListener1.mutex ) + { + const unsigned int interval = msgCount + 10; + unsigned int count = 0; + + while( msgListener1.messages.size() != msgCount && + count < interval ) + { + msgListener1.mutex.wait( 3000 ); + + ++count; + } + } + + CPPUNIT_ASSERT( msgListener1.messages.size() == msgCount ); + + synchronized( &msgListener2.mutex ) + { + const int interval = msgCount + 10; + int count = 0; + + while( msgListener2.messages.size() != msgCount && + count < interval ) + { + msgListener2.mutex.wait( 3000 ); + + ++count; + } + } + + CPPUNIT_ASSERT( msgListener2.messages.size() == msgCount ); + + msgListener1.clear(); + msgListener2.clear(); + + session->rollback(); + + synchronized( &msgListener1.mutex ) + { + const int interval = msgCount + 10; + int count = 0; + + while( msgListener1.messages.size() != msgCount && + count < interval ) + { + msgListener1.mutex.wait( 3000 ); + + ++count; + } + } + + CPPUNIT_ASSERT( msgListener1.messages.size() == msgCount ); + + synchronized( &msgListener2.mutex ) + { + const int interval = msgCount + 10; + int count = 0; + + while( msgListener2.messages.size() != msgCount && + count < interval ) + { + msgListener2.mutex.wait( 3000 ); + + ++count; + } + } + + CPPUNIT_ASSERT( msgListener2.messages.size() == msgCount ); + + delete consumer1; + delete consumer2; + + delete session; + } + + }; + +}} + +#endif /*_ACTIVEMQ_CORE_ACTIVEMQSESSIONTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/exceptions/ActiveMQExceptionTest.cpp b/activemq-cpp/src/test/activemq/exceptions/ActiveMQExceptionTest.cpp new file mode 100644 index 0000000000..1af1947bfb --- /dev/null +++ b/activemq-cpp/src/test/activemq/exceptions/ActiveMQExceptionTest.cpp @@ -0,0 +1,4 @@ +#include "ActiveMQExceptionTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::exceptions::ActiveMQExceptionTest ); + diff --git a/activemq-cpp/src/test/activemq/exceptions/ActiveMQExceptionTest.h b/activemq-cpp/src/test/activemq/exceptions/ActiveMQExceptionTest.h new file mode 100644 index 0000000000..17a6779bb5 --- /dev/null +++ b/activemq-cpp/src/test/activemq/exceptions/ActiveMQExceptionTest.h @@ -0,0 +1,39 @@ +#ifndef ACTIVEMQ_EXCEPTIONS_ACTIVEMQEXCEPTIONTEST_H_ +#define ACTIVEMQ_EXCEPTIONS_ACTIVEMQEXCEPTIONTEST_H_ + +#include +#include + +#include +#include + +namespace activemq{ +namespace exceptions{ + + class ActiveMQExceptionTest : public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE( ActiveMQExceptionTest ); + CPPUNIT_TEST( testMessage0 ); + CPPUNIT_TEST( testMessage3 ); + CPPUNIT_TEST_SUITE_END(); + + public: + + virtual void setUp(){}; + virtual void tearDown(){}; + void testMessage0(){ + char* text = "This is a test"; + ActiveMQException ex( __FILE__, __LINE__, text ); + CPPUNIT_ASSERT( strcmp( ex.getMessage(), text ) == 0 ); + } + + void testMessage3(){ + ActiveMQException ex( __FILE__, __LINE__, + "This is a test %d %d %d", 1, 100, 1000 ); + CPPUNIT_ASSERT( strcmp( ex.getMessage(), "This is a test 1 100 1000" ) == 0 ); + } + }; + +}} + +#endif /*ACTIVEMQ_EXCEPTIONS_ACTIVEMQEXCEPTIONTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/io/BufferedInputStreamTest.cpp b/activemq-cpp/src/test/activemq/io/BufferedInputStreamTest.cpp new file mode 100644 index 0000000000..b889c3f895 --- /dev/null +++ b/activemq-cpp/src/test/activemq/io/BufferedInputStreamTest.cpp @@ -0,0 +1,4 @@ +#include "BufferedInputStreamTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::io::BufferedInputStreamTest ); + diff --git a/activemq-cpp/src/test/activemq/io/BufferedInputStreamTest.h b/activemq-cpp/src/test/activemq/io/BufferedInputStreamTest.h new file mode 100644 index 0000000000..4cc712f1c2 --- /dev/null +++ b/activemq-cpp/src/test/activemq/io/BufferedInputStreamTest.h @@ -0,0 +1,163 @@ +#ifndef ACTIVEMQ_IO_BUFFEREDINPUTSTREAMTEST_H_ +#define ACTIVEMQ_IO_BUFFEREDINPUTSTREAMTEST_H_ + +#include +#include + +#include + + +namespace activemq{ +namespace io{ + + class BufferedInputStreamTest : public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE( BufferedInputStreamTest ); + CPPUNIT_TEST( testSmallerBuffer ); + CPPUNIT_TEST( testBiggerBuffer ); + CPPUNIT_TEST_SUITE_END(); + + public: + + class MyInputStream : public InputStream{ + private: + std::string data; + unsigned int pos; + public: + + MyInputStream( const std::string& data ){ + this->data = data; + pos = 0; + } + virtual ~MyInputStream(){} + + virtual int available() const{ + int len = data.length(); + return len - (int)pos; + } + virtual unsigned char read() throw (IOException){ + if( pos >= data.length() ){ + throw IOException(); + } + + return data.c_str()[pos++]; + } + virtual int read( unsigned char* buffer, const int bufferSize ) throw (IOException){ + unsigned int numToRead = std::min( bufferSize, available() ); + + const char* str = data.c_str(); + for( unsigned int ix=0; ix +#include + +#include +#include + +namespace activemq{ +namespace io{ + + class BufferedOutputStreamTest : public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE( BufferedOutputStreamTest ); + CPPUNIT_TEST( testSmallerBuffer ); + CPPUNIT_TEST( testBiggerBuffer ); + CPPUNIT_TEST_SUITE_END(); + + public: + + class MyOutputStream : public OutputStream{ + private: + char buffer[100]; + unsigned int pos; + public: + + MyOutputStream(){ + pos = 0; + memset( buffer, 0, 100 ); + } + virtual ~MyOutputStream(){} + + const char* getBuffer() const{ return buffer; } + + virtual void write( const unsigned char c ) throw (IOException){ + if( pos >= 100 ){ + throw IOException(); + } + + buffer[pos++] = c; + } + + virtual void write( const unsigned char* buffer, const int len ) throw (IOException){ + + if( (pos + len) > 100 ){ + throw IOException(); + } + + memcpy( this->buffer + pos, buffer, len ); + + pos += len; + } + + virtual void flush() throw (IOException){ + } + + virtual void close() throw(cms::CMSException){ + // do nothing. + } + + virtual void lock() throw(exceptions::ActiveMQException){ + } + virtual void unlock() throw(exceptions::ActiveMQException){ + } + virtual void wait() throw(exceptions::ActiveMQException){ + } + virtual void wait(unsigned long millisecs) throw(exceptions::ActiveMQException){ + } + virtual void notify() throw(exceptions::ActiveMQException){ + } + virtual void notifyAll() throw(exceptions::ActiveMQException){ + } + }; + + public: + + virtual void setUp(){}; + virtual void tearDown(){}; + void testSmallerBuffer(){ + + MyOutputStream myStream; + BufferedOutputStream bufStream( &myStream, 1 ); + + const char* buffer = myStream.getBuffer(); + + bufStream.write( (unsigned char)'T' ); + // Should not be written yet. + CPPUNIT_ASSERT( strcmp( buffer, "" ) == 0 ); + + bufStream.write( (unsigned char)'E' ); + // This time the T should have been written. + CPPUNIT_ASSERT( strcmp( buffer, "T" ) == 0 ); + + bufStream.write( (unsigned char*)"ST", 2 ); + // This time the ES should have been written. + CPPUNIT_ASSERT( strcmp( buffer, "TES" ) == 0 ); + + bufStream.flush(); + CPPUNIT_ASSERT( strcmp( buffer, "TEST" ) == 0 ); + } + + void testBiggerBuffer(){ + + MyOutputStream myStream; + BufferedOutputStream bufStream( &myStream, 10 ); + + const char* buffer = myStream.getBuffer(); + + bufStream.write( (unsigned char*)"TEST", 4 ); + + // Should not be written yet. + CPPUNIT_ASSERT( strcmp( buffer, "" ) == 0 ); + + bufStream.flush(); + CPPUNIT_ASSERT( strcmp( buffer, "TEST" ) == 0 ); + + bufStream.write( (unsigned char*)"TEST", 4 ); + bufStream.write( (unsigned char*)"12345678910", 11); + + CPPUNIT_ASSERT( strcmp( buffer, "TESTTEST123456" ) == 0 ); + + bufStream.flush(); + CPPUNIT_ASSERT( strcmp( buffer, "TESTTEST12345678910" ) == 0 ); + + } + }; + +}} + +#endif /*ACTIVEMQ_IO_BUFFEREDOUTPUTSTREAMTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/io/ByteArrayInputStreamTest.cpp b/activemq-cpp/src/test/activemq/io/ByteArrayInputStreamTest.cpp new file mode 100644 index 0000000000..4c5de14ae2 --- /dev/null +++ b/activemq-cpp/src/test/activemq/io/ByteArrayInputStreamTest.cpp @@ -0,0 +1,3 @@ +#include "ByteArrayInputStreamTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::io::ByteArrayInputStreamTest ); diff --git a/activemq-cpp/src/test/activemq/io/ByteArrayInputStreamTest.h b/activemq-cpp/src/test/activemq/io/ByteArrayInputStreamTest.h new file mode 100644 index 0000000000..240bed0eb0 --- /dev/null +++ b/activemq-cpp/src/test/activemq/io/ByteArrayInputStreamTest.h @@ -0,0 +1,73 @@ +#ifndef ACTIVEMQ_IO_BYTEARRAYINPUTSTREAMTEST_H_ +#define ACTIVEMQ_IO_BYTEARRAYINPUTSTREAMTEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace io{ + + class ByteArrayInputStreamTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( ByteArrayInputStreamTest ); + CPPUNIT_TEST( testStream ); + CPPUNIT_TEST_SUITE_END(); + + public: + + ByteArrayInputStreamTest() {} + + virtual ~ByteArrayInputStreamTest() {} + + void testStream() + { + std::vector testBuffer; + + testBuffer.push_back('t'); + testBuffer.push_back('e'); + testBuffer.push_back('s'); + testBuffer.push_back('t'); + + ByteArrayInputStream stream_a(&testBuffer[0], testBuffer.size()); + + CPPUNIT_ASSERT( stream_a.available() == 4 ); + + char a = stream_a.read(); + char b = stream_a.read(); + char c = stream_a.read(); + char d = stream_a.read(); + + CPPUNIT_ASSERT( a == 't' && b == 'e' && c == 's' && d == 't' ); + CPPUNIT_ASSERT( stream_a.available() == 0 ); + + testBuffer.push_back('e'); + + stream_a.setByteArray(&testBuffer[0], testBuffer.size()); + + CPPUNIT_ASSERT( stream_a.available() == 5 ); + + unsigned char* buffer = new unsigned char[6]; + + buffer[5] = '\0'; + + CPPUNIT_ASSERT( stream_a.read(buffer, 5) == 5 ); + CPPUNIT_ASSERT( std::string((const char*)buffer) == std::string("teste") ); + CPPUNIT_ASSERT( stream_a.available() == 0 ); + + stream_a.setByteArray(&testBuffer[0], testBuffer.size()); + + memset(buffer, 0, 6); + + CPPUNIT_ASSERT( stream_a.read(buffer, 3) == 3 ); + CPPUNIT_ASSERT( stream_a.read(&buffer[3], 5) == 2 ); + CPPUNIT_ASSERT( std::string((const char*)buffer) == std::string("teste") ); + + delete buffer; + } + }; + +}} + +#endif /*ACTIVEMQ_IO_BYTEARRAYINPUTSTREAMTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/io/ByteArrayOutputStreamTest.cpp b/activemq-cpp/src/test/activemq/io/ByteArrayOutputStreamTest.cpp new file mode 100644 index 0000000000..1f4133f9d5 --- /dev/null +++ b/activemq-cpp/src/test/activemq/io/ByteArrayOutputStreamTest.cpp @@ -0,0 +1,3 @@ +#include "ByteArrayOutputStreamTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::io::ByteArrayOutputStreamTest ); diff --git a/activemq-cpp/src/test/activemq/io/ByteArrayOutputStreamTest.h b/activemq-cpp/src/test/activemq/io/ByteArrayOutputStreamTest.h new file mode 100644 index 0000000000..a391d5413e --- /dev/null +++ b/activemq-cpp/src/test/activemq/io/ByteArrayOutputStreamTest.h @@ -0,0 +1,59 @@ +#ifndef ACTIVEMQ_IO_BYTEARRAYOUTPUTSTREAMTEST_H_ +#define ACTIVEMQ_IO_BYTEARRAYOUTPUTSTREAMTEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace io{ + + class ByteArrayOutputStreamTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( ByteArrayOutputStreamTest ); + CPPUNIT_TEST( testStream ); + CPPUNIT_TEST_SUITE_END(); + + public: + + ByteArrayOutputStreamTest() {} + + virtual ~ByteArrayOutputStreamTest() {} + + void testStream() + { + ByteArrayOutputStream stream_a; + + stream_a.write('a'); + stream_a.write(60); + stream_a.write('c'); + + CPPUNIT_ASSERT( stream_a.getByteArraySize() == 3 ); + + stream_a.clear(); + + CPPUNIT_ASSERT( stream_a.getByteArraySize() == 0 ); + + stream_a.write((const unsigned char*)("abc"), 3); + + CPPUNIT_ASSERT( stream_a.getByteArraySize() == 3 ); + + stream_a.clear(); + + CPPUNIT_ASSERT( stream_a.getByteArraySize() == 0 ); + + stream_a.write((const unsigned char*)("abc"), 3); + + unsigned char buffer[4]; + + memset(buffer, 0, 4); + memcpy(buffer, stream_a.getByteArray(), stream_a.getByteArraySize()); + + CPPUNIT_ASSERT( std::string((const char*)buffer) == std::string("abc") ); + } + }; + +}} + +#endif /*ACTIVEMQ_IO_BYTEARRAYOUTPUTSTREAMTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/io/EndianReaderTest.cpp b/activemq-cpp/src/test/activemq/io/EndianReaderTest.cpp new file mode 100644 index 0000000000..df063af5d0 --- /dev/null +++ b/activemq-cpp/src/test/activemq/io/EndianReaderTest.cpp @@ -0,0 +1,4 @@ +#include "EndianReaderTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::io::EndianReaderTest ); + diff --git a/activemq-cpp/src/test/activemq/io/EndianReaderTest.h b/activemq-cpp/src/test/activemq/io/EndianReaderTest.h new file mode 100644 index 0000000000..f15dcc2a2b --- /dev/null +++ b/activemq-cpp/src/test/activemq/io/EndianReaderTest.h @@ -0,0 +1,167 @@ +#ifndef ACTIVEMQ_IO_ENDIANREADERTEST_H_ +#define ACTIVEMQ_IO_ENDIANREADERTEST_H_ + +#include +#include + +#include +#include +#include +#include + +#ifdef min +#undef min +#endif + +#include + +namespace activemq{ +namespace io{ + + class EndianReaderTest : public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE( EndianReaderTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + class MyInputStream : public InputStream{ + private: + unsigned char* buffer; + int len; + unsigned int pos; + public: + + MyInputStream( unsigned char* buffer, int len ){ + this->buffer = buffer; + this->len = len; + pos = 0; + } + virtual ~MyInputStream(){} + + virtual int available() const{ + return len - (int)pos; + } + virtual unsigned char read() throw (IOException){ + if( (int)pos >= len ){ + throw IOException(); + } + + return buffer[pos++]; + } + virtual int read( unsigned char* buffer, const int bufferSize ) throw (IOException){ + unsigned int numToRead = std::min( bufferSize, available() ); + + for( unsigned int ix=0; ixbuffer[pos+ix]; + } + + pos += numToRead; + + return numToRead; + } + + virtual void close() throw(cms::CMSException){ + // do nothing. + } + + virtual void lock() throw(exceptions::ActiveMQException){ + } + virtual void unlock() throw(exceptions::ActiveMQException){ + } + virtual void wait() throw(exceptions::ActiveMQException){ + } + virtual void wait(unsigned long millisecs) throw(exceptions::ActiveMQException){ + } + virtual void notify() throw(exceptions::ActiveMQException){ + } + virtual void notifyAll() throw(exceptions::ActiveMQException){ + } + }; + + public: + + virtual void setUp(){}; + virtual void tearDown(){}; + void test(){ + + unsigned char buffer[1000]; + int ix = 0; + + unsigned char byteVal = (unsigned char)'T'; + uint16_t shortVal = 5; + uint32_t intVal = 10000; + uint64_t longVal = 1000000000; + float floatVal = 10.0f; + double doubleVal = 100.0; + unsigned char arrayVal[3] = { + 'a', 'b', 'c' + }; + + int size = sizeof(char); + memcpy( (char*)(buffer+ix), (char*)&byteVal, size ); + ix += size; + + size = sizeof(uint16_t); + uint16_t tempShort = util::Endian::byteSwap(shortVal); + memcpy( (char*)(buffer+ix), (char*)&tempShort, size ); + ix += size; + + size = sizeof(uint32_t); + uint32_t tempInt = util::Endian::byteSwap(intVal); + memcpy( (char*)(buffer+ix), (char*)&tempInt, size ); + ix += size; + + size = sizeof(uint64_t); + uint64_t tempLong = util::Endian::byteSwap(longVal); + memcpy( (char*)(buffer+ix), (char*)&tempLong, size ); + ix += size; + + size = sizeof(float); + float tempFloat = util::Endian::byteSwap(floatVal); + memcpy( (char*)(buffer+ix), (char*)&tempFloat, size ); + ix += size; + + size = sizeof(double); + double tempDouble = util::Endian::byteSwap(doubleVal); + memcpy( (char*)(buffer+ix), (char*)&tempDouble, size ); + ix += size; + + size = 3; + memcpy( (char*)(buffer+ix), (char*)&arrayVal, size ); + ix += size; + + // Create the stream with the buffer we just wrote to. + MyInputStream myStream( buffer, 1000 ); + EndianReader reader( &myStream ); + + byteVal = reader.readByte(); + CPPUNIT_ASSERT( byteVal == (unsigned char)'T' ); + + shortVal = reader.readUInt16(); + CPPUNIT_ASSERT( shortVal == 5 ); + + intVal = reader.readUInt32(); + CPPUNIT_ASSERT( intVal == 10000 ); + + longVal = reader.readUInt64(); + CPPUNIT_ASSERT( longVal == 1000000000 ); + + floatVal = reader.readFloat(); + CPPUNIT_ASSERT( floatVal == 10.0f ); + + doubleVal = reader.readDouble(); + CPPUNIT_ASSERT( doubleVal == 100.0 ); + + reader.read( arrayVal, 3 ); + CPPUNIT_ASSERT( arrayVal[0] == 'a' ); + CPPUNIT_ASSERT( arrayVal[1] == 'b' ); + CPPUNIT_ASSERT( arrayVal[2] == 'c' ); + } + + }; + +}} + +#endif /*ACTIVEMQ_IO_ENDIANREADERTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/io/EndianWriterTest.cpp b/activemq-cpp/src/test/activemq/io/EndianWriterTest.cpp new file mode 100644 index 0000000000..661f72aa65 --- /dev/null +++ b/activemq-cpp/src/test/activemq/io/EndianWriterTest.cpp @@ -0,0 +1,4 @@ +#include "EndianWriterTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::io::EndianWriterTest ); + diff --git a/activemq-cpp/src/test/activemq/io/EndianWriterTest.h b/activemq-cpp/src/test/activemq/io/EndianWriterTest.h new file mode 100644 index 0000000000..c241e8c986 --- /dev/null +++ b/activemq-cpp/src/test/activemq/io/EndianWriterTest.h @@ -0,0 +1,137 @@ +#ifndef ACTIVEMQ_IO_ENDIANWRITERTEST_H_ +#define ACTIVEMQ_IO_ENDIANWRITERTEST_H_ + +#include +#include + +#include +#include + +namespace activemq{ +namespace io{ + + class EndianWriterTest : public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE( EndianWriterTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + class MyOutputStream : public OutputStream{ + private: + + unsigned char buffer[1000]; + unsigned int pos; + public: + + MyOutputStream(){ + pos = 0; + memset( buffer, 0, 1000 ); + } + virtual ~MyOutputStream(){} + + const unsigned char* getBuffer() const{ return buffer; } + + virtual void write( const unsigned char c ) throw (IOException){ + if( pos >= 1000 ){ + throw IOException(); + } + + buffer[pos++] = c; + } + + virtual void write( const unsigned char* buffer, const int len ) throw (IOException){ + + if( (pos + len) > 1000 ){ + throw IOException(); + } + + memcpy( this->buffer + pos, buffer, len ); + + pos += len; + } + + virtual void flush() throw (IOException){ + } + + virtual void close() throw(cms::CMSException){ + // do nothing. + } + + virtual void lock() throw(exceptions::ActiveMQException){ + } + virtual void unlock() throw(exceptions::ActiveMQException){ + } + virtual void wait() throw(exceptions::ActiveMQException){ + } + virtual void wait(unsigned long millisecs) throw(exceptions::ActiveMQException){ + } + virtual void notify() throw(exceptions::ActiveMQException){ + } + virtual void notifyAll() throw(exceptions::ActiveMQException){ + } + }; + + public: + + virtual void setUp(){}; + virtual void tearDown(){}; + void test(){ + + unsigned char byteVal = (unsigned char)'T'; + uint16_t shortVal = 5; + uint32_t intVal = 10000; + uint64_t longVal = 1000000000; + float floatVal = 10.0f; + double doubleVal = 100.0; + unsigned char arrayVal[3] = { + 'a', 'b', 'c' + }; + + // Create the stream with the buffer we just wrote to. + MyOutputStream myStream; + EndianWriter writer( &myStream ); + + writer.writeByte( byteVal ); + writer.writeUInt16( shortVal ); + writer.writeUInt32( intVal ); + writer.writeUInt64( longVal ); + writer.writeFloat( floatVal ); + writer.writeDouble( doubleVal ); + writer.write( arrayVal, 3 ); + + + const unsigned char* buffer = myStream.getBuffer(); + int ix = 0; + + unsigned char tempByte = buffer[ix]; + CPPUNIT_ASSERT( tempByte == byteVal ); + ix += sizeof( tempByte ); + + uint16_t tempShort = util::Endian::byteSwap( *(uint16_t*)(buffer+ix) ); + CPPUNIT_ASSERT( tempShort == shortVal ); + ix += sizeof( tempShort ); + + uint32_t tempInt = util::Endian::byteSwap( *(uint32_t*)(buffer+ix) ); + CPPUNIT_ASSERT( tempInt == intVal ); + ix += sizeof( tempInt ); + + uint64_t tempLong = util::Endian::byteSwap( *(uint64_t*)(buffer+ix) ); + CPPUNIT_ASSERT( tempLong == longVal ); + ix += sizeof( tempLong ); + + float tempFloat = util::Endian::byteSwap( *(float*)(buffer+ix) ); + CPPUNIT_ASSERT( tempFloat == floatVal ); + ix += sizeof( tempFloat ); + + double tempDouble = util::Endian::byteSwap( *(double*)(buffer+ix) ); + CPPUNIT_ASSERT( tempDouble == doubleVal ); + ix += sizeof( tempDouble ); + } + + }; + +}} + +#endif /*ACTIVEMQ_IO_ENDIANWRITERTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/logger/LoggerTest.cpp b/activemq-cpp/src/test/activemq/logger/LoggerTest.cpp new file mode 100644 index 0000000000..c2ade236d7 --- /dev/null +++ b/activemq-cpp/src/test/activemq/logger/LoggerTest.cpp @@ -0,0 +1,4 @@ +#include "LoggerTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::logger::LoggerTest ); + diff --git a/activemq-cpp/src/test/activemq/logger/LoggerTest.h b/activemq-cpp/src/test/activemq/logger/LoggerTest.h new file mode 100644 index 0000000000..ee4376ccad --- /dev/null +++ b/activemq-cpp/src/test/activemq/logger/LoggerTest.h @@ -0,0 +1,41 @@ +#ifndef LOGGERTEST_H_ +#define LOGGERTEST_H_ + +#include +#include +#include + +namespace activemq{ +namespace logger{ + + class LoggerTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( LoggerTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + private: + + LOGCMS_DECLARE(testLogger); + + public: + + virtual ~LoggerTest() {} + + void test(void) + { + LOGCMS_DEBUG(testLogger, "Test Debug"); + LOGCMS_INFO(testLogger, "Test Info"); + LOGCMS_ERROR(testLogger, "Test Error"); + LOGCMS_WARN(testLogger, "Test Warn"); + LOGCMS_FATAL(testLogger, "Test Fatal"); + + CPPUNIT_ASSERT( true ); + } + }; + + LOGCMS_INITIALIZE(testLogger, LoggerTest, "com.activemq.logger.LoggerTest"); + +}} + +#endif /*LOGGERTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/network/SocketFactoryTest.cpp b/activemq-cpp/src/test/activemq/network/SocketFactoryTest.cpp new file mode 100644 index 0000000000..24ff31e4cf --- /dev/null +++ b/activemq-cpp/src/test/activemq/network/SocketFactoryTest.cpp @@ -0,0 +1,3 @@ +#include "SocketFactoryTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::network::SocketFactoryTest ); diff --git a/activemq-cpp/src/test/activemq/network/SocketFactoryTest.h b/activemq-cpp/src/test/activemq/network/SocketFactoryTest.h new file mode 100644 index 0000000000..105d60cd3f --- /dev/null +++ b/activemq-cpp/src/test/activemq/network/SocketFactoryTest.h @@ -0,0 +1,196 @@ +#ifndef _ACTIVEMQ_NETWORK_SOCKETFACTORYTEST_H_ +#define _ACTIVEMQ_NETWORK_SOCKETFACTORYTEST_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace activemq{ +namespace network{ + + class SocketFactoryTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( SocketFactoryTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + static const int port = 23232; + + class MyServerThread : public concurrent::Thread{ + private: + + bool done; + int numClients; + std::string lastMessage; + + public: + + concurrent::Mutex mutex; + + public: + MyServerThread(){ + done = false; + numClients = 0; + } + virtual ~MyServerThread(){ + stop(); + } + + std::string getLastMessage(){ + return lastMessage; + } + + int getNumClients(){ + return numClients; + } + + virtual void stop(){ + done = true; + } + + virtual void run(){ + try{ + unsigned char buf[1000]; + + ServerSocket server; + server.bind( "127.0.0.1", port ); + + network::Socket* socket = server.accept(); + server.close(); + + socket->setSoTimeout( 10 ); + socket->setSoLinger( false ); + + synchronized(&mutex) + { + numClients++; + + mutex.notifyAll(); + } + + while( !done && socket != NULL ){ + + io::InputStream* stream = socket->getInputStream(); + if( stream->available() > 0 ){ + + memset( buf, 0, 1000 ); + try{ + stream->read( buf, 1000 ); + + lastMessage = (char*)buf; + + if( strcmp( (char*)buf, "reply" ) == 0 ){ + io::OutputStream* output = socket->getOutputStream(); + output->write( (unsigned char*)"hello", strlen("hello" ) ); + } + + }catch( io::IOException& ex ){ + done = true; + } + + }else{ + Thread::sleep( 10 ); + } + } + + socket->close(); + delete socket; + + numClients--; + + synchronized(&mutex) + { + mutex.notifyAll(); + } + + }catch( io::IOException& ex ){ + printf("%s\n", ex.getMessage() ); + CPPUNIT_ASSERT( false ); + }catch( ... ){ + CPPUNIT_ASSERT( false ); + } + } + + }; + + public: + + SocketFactoryTest() {} + virtual ~SocketFactoryTest() {} + + void test(void) + { + try + { + MyServerThread serverThread; + serverThread.start(); + + concurrent::Thread::sleep( 40 ); + + util::SimpleProperties properties; + + std::ostringstream ostream; + + ostream << "127.0.0.1:" << port; + + properties.setProperty("uri", ostream.str()); + properties.setProperty("soLinger", "false"); + properties.setProperty("soTimeout", "5"); + + Socket* client = SocketFactory::createSocket(properties); + + BufferedSocket* buffSocket = dynamic_cast(client); + + CPPUNIT_ASSERT( buffSocket != NULL ); + + synchronized(&serverThread.mutex) + { + if(serverThread.getNumClients() != 1) + { + serverThread.mutex.wait(1000); + } + } + + CPPUNIT_ASSERT( client->isConnected() ); + + CPPUNIT_ASSERT( serverThread.getNumClients() == 1 ); + + client->close(); + + synchronized(&serverThread.mutex) + { + if(serverThread.getNumClients() != 0) + { + serverThread.mutex.wait(1000); + } + } + + CPPUNIT_ASSERT( serverThread.getNumClients() == 0 ); + + serverThread.stop(); + serverThread.join(); + + delete client; + } + catch(exceptions::ActiveMQException ex) + { + CPPUNIT_ASSERT( false ); + } + } + + }; + +}} + +#endif /*_ACTIVEMQ_NETWORK_SOCKETFACTORYTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/network/SocketTest.cpp b/activemq-cpp/src/test/activemq/network/SocketTest.cpp new file mode 100644 index 0000000000..ce6f1872f7 --- /dev/null +++ b/activemq-cpp/src/test/activemq/network/SocketTest.cpp @@ -0,0 +1,3 @@ +#include "SocketTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::network::SocketTest ); diff --git a/activemq-cpp/src/test/activemq/network/SocketTest.h b/activemq-cpp/src/test/activemq/network/SocketTest.h new file mode 100644 index 0000000000..2cc488bdfc --- /dev/null +++ b/activemq-cpp/src/test/activemq/network/SocketTest.h @@ -0,0 +1,296 @@ +#ifndef ACTIVEMQ_IO_SOCKETTEST_H_ +#define ACTIVEMQ_IO_SOCKETTEST_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace network{ + + class SocketTest : public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE( SocketTest ); + CPPUNIT_TEST( testConnect ); + CPPUNIT_TEST( testTx ); + CPPUNIT_TEST( testTrx ); + CPPUNIT_TEST_SUITE_END(); + + public: + + static const int port = 23232; + + class MyServerThread : public concurrent::Thread{ + private: + + bool done; + int numClients; + std::string lastMessage; + + public: + + concurrent::Mutex mutex; + + public: + + MyServerThread(){ + done = false; + numClients = 0; + } + + virtual ~MyServerThread(){ + stop(); + } + + std::string getLastMessage(){ + return lastMessage; + } + + int getNumClients(){ + return numClients; + } + + virtual void stop(){ + done = true; + } + + virtual void run(){ + try{ + unsigned char buf[1000]; + + ServerSocket server; + server.bind( "127.0.0.1", port ); + + Socket* socket = server.accept(); + server.close(); + + socket->setSoTimeout( 10 ); + socket->setSoLinger( false ); + numClients++; + + synchronized(&mutex) + { + mutex.notifyAll(); + } + + while( !done && socket != NULL ){ + + io::InputStream* stream = socket->getInputStream(); + if( stream->available() > 0 ){ + + memset( buf, 0, 1000 ); + try{ + stream->read( buf, 1000 ); + + lastMessage = (char*)buf; + + if( strcmp( (char*)buf, "reply" ) == 0 ){ + io::OutputStream* output = socket->getOutputStream(); + output->write( (unsigned char*)"hello", strlen("hello" ) ); + + synchronized(&mutex) + { + mutex.notifyAll(); + } + } + + }catch( io::IOException& ex ){ + done = true; + } + + }else{ + Thread::sleep( 10 ); + } + } + + socket->close(); + delete socket; + + numClients--; + + synchronized(&mutex) + { + mutex.notifyAll(); + } + + }catch( io::IOException& ex ){ + printf("%s\n", ex.getMessage() ); + CPPUNIT_ASSERT( false ); + }catch( ... ){ + CPPUNIT_ASSERT( false ); + } + } + + }; + + public: + + virtual void setUp(){}; + virtual void tearDown(){ + }; + void testConnect(){ + + try{ + + MyServerThread serverThread; + serverThread.start(); + + concurrent::Thread::sleep( 40 ); + + TcpSocket client; + + client.connect("127.0.0.1", port); + client.setSoTimeout( 5 ); + client.setSoLinger( false ); + + synchronized(&serverThread.mutex) + { + if(serverThread.getNumClients() != 1) + { + serverThread.mutex.wait(1000); + } + } + + CPPUNIT_ASSERT( serverThread.getNumClients() == 1 ); + + client.close(); + + synchronized(&serverThread.mutex) + { + if(serverThread.getNumClients() != 0) + { + serverThread.mutex.wait(1000); + } + } + + CPPUNIT_ASSERT( serverThread.getNumClients() == 0 ); + + serverThread.stop(); + serverThread.join(); + + }catch( io::IOException& ex ){ + const char* error = ex.getMessage(); + printf( "%s\n", error ); + } + } + + void testTx(){ + + try{ + + MyServerThread serverThread; + serverThread.start(); + + concurrent::Thread::sleep( 10 ); + + TcpSocket client; + + client.connect("127.0.0.1", port); + client.setSoTimeout( 5 ); + client.setSoLinger( false ); + + synchronized(&serverThread.mutex) + { + if(serverThread.getNumClients() != 1) + { + serverThread.mutex.wait(1000); + } + } + + CPPUNIT_ASSERT( serverThread.getNumClients() == 1 ); + + io::OutputStream* stream = client.getOutputStream(); + + std::string msg = "don't reply"; + stream->write( (unsigned char*)msg.c_str(), msg.length() ); + + concurrent::Thread::sleep( 10 ); + + CPPUNIT_ASSERT( serverThread.getLastMessage() == msg ); + + client.close(); + + synchronized(&serverThread.mutex) + { + if(serverThread.getNumClients() != 0) + { + serverThread.mutex.wait(1000); + } + } + + CPPUNIT_ASSERT( serverThread.getNumClients() == 0 ); + + serverThread.stop(); + serverThread.join(); + + }catch( io::IOException& ex ){ + const char* error = ex.getMessage(); + printf( "%s\n", error ); + } + } + + void testTrx(){ + + try{ + + MyServerThread serverThread; + serverThread.start(); + + concurrent::Thread::sleep( 10 ); + + TcpSocket client; + + client.connect("127.0.0.1", port); + client.setSoTimeout( 5 ); + client.setSoLinger(false); + + synchronized(&serverThread.mutex) + { + if(serverThread.getNumClients() != 1) + { + serverThread.mutex.wait(1000); + } + } + + CPPUNIT_ASSERT( serverThread.getNumClients() == 1 ); + + io::OutputStream* stream = client.getOutputStream(); + + std::string msg = "reply"; + stream->write( (unsigned char*)msg.c_str(), msg.length() ); + + synchronized(&serverThread.mutex) + { + serverThread.mutex.wait(300); + } + + unsigned char buf[500]; + memset( buf, 0, 500 ); + io::InputStream* istream = client.getInputStream(); + int numRead = istream->read( buf, 500 ); + CPPUNIT_ASSERT( numRead == 5 ); + CPPUNIT_ASSERT( strcmp( (char*)buf, "hello" ) == 0 ); + + client.close(); + + serverThread.stop(); + serverThread.join(); + + }catch( io::IOException& ex ){ + const char* error = ex.getMessage(); + printf( "%s\n", error ); + } + } + + }; + +}} + +#endif /*ACTIVEMQ_IO_SOCKETTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/transport/DummyTransport.h b/activemq-cpp/src/test/activemq/transport/DummyTransport.h new file mode 100644 index 0000000000..1448a2ebfc --- /dev/null +++ b/activemq-cpp/src/test/activemq/transport/DummyTransport.h @@ -0,0 +1,230 @@ +#ifndef ACTIVEMQ_TANSPORT_DUMMYTRANSPORT_H_ +#define ACTIVEMQ_TANSPORT_DUMMYTRANSPORT_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace transport{ + + class DummyTransport : public Transport{ + + public: + + class ResponseBuilder{ + public: + virtual ~ResponseBuilder(){} + + virtual Response* buildResponse( const Command* cmd ) = 0; + }; + + class InternalCommandListener : + public CommandListener, + public concurrent::Thread + { + private: + + DummyTransport* transport; + ResponseBuilder* responseBuilder; + concurrent::Mutex mutex; + Command* command; + bool done; + + public: + + InternalCommandListener(void) { + command = NULL; + transport = NULL; + responseBuilder = NULL; + done = false; + + this->start(); + } + + virtual ~InternalCommandListener() { + done = true; + synchronized( &mutex ) + { + mutex.notifyAll(); + } + this->join(); + } + + void setTransport( DummyTransport* transport ){ + this->transport = transport; + } + + void setResponseBuilder( ResponseBuilder* responseBuilder ) { + this->responseBuilder = responseBuilder; + } + + virtual void onCommand( Command* command ) + { + synchronized( &mutex ) + { + this->command = command; + + mutex.notifyAll(); + } + } + + void run(void) + { + try + { + synchronized( &mutex ) + { + while( !done ) + { + mutex.wait(); + + if( command == NULL ) + { + continue; + } + + concurrent::Thread::sleep( 100 ); + + if( responseBuilder != NULL && + transport != NULL ) + { + transport->fireCommand( + responseBuilder->buildResponse( + command ) ); + + command = NULL; + + return; + } + } + } + } + AMQ_CATCHALL_NOTHROW() + } + }; + + private: + + ResponseBuilder* responseBuilder; + CommandListener* commandListener; + CommandListener* outgoingCommandListener; + TransportExceptionListener* exceptionListener; + unsigned int nextCommandId; + concurrent::Mutex commandIdMutex; + bool own; + InternalCommandListener defaultListener; + + public: + + DummyTransport( ResponseBuilder* responseBuilder , + bool own = false, + bool useDefOutgoing = false ){ + + this->responseBuilder = NULL; + this->commandListener = NULL; + this->outgoingCommandListener = NULL; + this->exceptionListener = NULL; + this->responseBuilder = responseBuilder; + this->own = own; + this->nextCommandId = 0; + if( useDefOutgoing ) + { + this->outgoingCommandListener = &defaultListener; + this->defaultListener.setTransport( this ); + this->defaultListener.setResponseBuilder( responseBuilder ); + } + } + + virtual ~DummyTransport(){ + if( own ){ + delete responseBuilder; + } + } + + void setResponseBuilder( ResponseBuilder* responseBuilder ){ + this->responseBuilder = responseBuilder; + } + + unsigned int getNextCommandId() throw (exceptions::ActiveMQException){ + + try{ + synchronized( &commandIdMutex ){ + return ++nextCommandId; + } + + // Should never get here, but some compilers aren't + // smart enough to figure out we'll never get here. + return 0; + } + AMQ_CATCHALL_THROW( transport::CommandIOException ) + } + + virtual void oneway( Command* command ) + throw(CommandIOException, exceptions::UnsupportedOperationException) + { + if( outgoingCommandListener != NULL ){ + + command->setCommandId( getNextCommandId() ); + command->setResponseRequired( false ); + outgoingCommandListener->onCommand( command ); + return; + } + } + + virtual Response* request( Command* command ) + throw(CommandIOException, + exceptions::UnsupportedOperationException) + { + if( responseBuilder != NULL ){ + command->setCommandId( getNextCommandId() ); + command->setResponseRequired( true ); + return responseBuilder->buildResponse( command ); + } + + throw CommandIOException( __FILE__, __LINE__, + "no response builder available" ); + } + + virtual void setCommandListener( CommandListener* listener ){ + this->commandListener = listener; + } + + virtual void setOutgoingCommandListener( CommandListener* listener ){ + outgoingCommandListener = listener; + } + + virtual void setCommandReader( CommandReader* reader ){} + + virtual void setCommandWriter( CommandWriter* writer ){} + + virtual void setTransportExceptionListener( + TransportExceptionListener* listener ) + { + this->exceptionListener = listener; + } + + virtual void fireCommand( Command* cmd ){ + if( commandListener != NULL ){ + commandListener->onCommand( cmd ); + } + } + + virtual void fireException( const exceptions::ActiveMQException& ex ){ + if( exceptionListener != NULL ){ + exceptionListener->onTransportException( this, ex ); + } + } + + virtual void start() throw (cms::CMSException){} + virtual void close() throw (cms::CMSException){} + }; + +}} + +#endif /*ACTIVEMQ_TANSPORT_DUMMYTRANSPORT_H_*/ diff --git a/activemq-cpp/src/test/activemq/transport/DummyTransportFactory.cpp b/activemq-cpp/src/test/activemq/transport/DummyTransportFactory.cpp new file mode 100644 index 0000000000..c002c90d0d --- /dev/null +++ b/activemq-cpp/src/test/activemq/transport/DummyTransportFactory.cpp @@ -0,0 +1,27 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 "DummyTransportFactory.h" +#include + +using namespace activemq; +using namespace activemq::transport; + +//////////////////////////////////////////////////////////////////////////////// +//TransportFactoryMapRegistrar DummyTransportFactory::registrar( +// "dummy", new DummyTransportFactory()); +// diff --git a/activemq-cpp/src/test/activemq/transport/DummyTransportFactory.h b/activemq-cpp/src/test/activemq/transport/DummyTransportFactory.h new file mode 100644 index 0000000000..e0d7bbad75 --- /dev/null +++ b/activemq-cpp/src/test/activemq/transport/DummyTransportFactory.h @@ -0,0 +1,69 @@ +/* + * Copyright 2006 The Apache Software Foundation or its licensors, as + * applicable. + * + * 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 ACTIVEMQ_TRANSPORT_DUMMYTRANSPORTFACTORY_H_ +#define ACTIVEMQ_TRANSPORT_DUMMYTRANSPORTFACTORY_H_ + +#include +#include +#include +#include +#include + +namespace activemq{ +namespace transport{ + + /** + * Manufactures DummyTransports, which are objects that + * read from input streams and write to output streams. + */ + class DummyTransportFactory : public TransportFactory{ + private: + + // static TransportFactoryMapRegistrar registrar; + + public: + + virtual ~DummyTransportFactory(){} + + /** + * Creates a Transport instance. + * @param properties The properties for the transport. + */ + virtual Transport* createTransport( + const activemq::util::Properties& properties ) + { + std::string wireFormat = + properties.getProperty( "wireFormat", "stomp" ); + + DummyTransport::ResponseBuilder* builder = NULL; + + if( wireFormat == "stomp" ) + { + builder = new connector::stomp::StompResponseBuilder( + properties.getProperty( + "transport.sessionId", "testSessionId" ) ); + } + + return new DummyTransport( builder, true ); + } + + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_DUMMYTRANSPORTFACTORY_H_*/ diff --git a/activemq-cpp/src/test/activemq/transport/IOTransportTest.cpp b/activemq-cpp/src/test/activemq/transport/IOTransportTest.cpp new file mode 100644 index 0000000000..cb978d26b7 --- /dev/null +++ b/activemq-cpp/src/test/activemq/transport/IOTransportTest.cpp @@ -0,0 +1,4 @@ +#include "IOTransportTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::transport::IOTransportTest ); + diff --git a/activemq-cpp/src/test/activemq/transport/IOTransportTest.h b/activemq-cpp/src/test/activemq/transport/IOTransportTest.h new file mode 100644 index 0000000000..44f64f4219 --- /dev/null +++ b/activemq-cpp/src/test/activemq/transport/IOTransportTest.h @@ -0,0 +1,328 @@ +#ifndef ACTIVEMQ_COMMANDS_IOTRANSPORTTEST_H_ +#define ACTIVEMQ_COMMANDS_IOTRANSPORTTEST_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace transport{ + + class IOTransportTest : public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE( IOTransportTest ); + CPPUNIT_TEST( testStartClose ); + CPPUNIT_TEST( testRead ); + CPPUNIT_TEST( testWrite ); + CPPUNIT_TEST( testException ); + CPPUNIT_TEST_SUITE_END(); + + public: + + class MyCommand : public Command{ + public: + MyCommand(){ c = 0; } + virtual ~MyCommand(){} + + char c; + + virtual void setCommandId( const unsigned int id ){} + virtual unsigned int getCommandId() const{ return 0; } + + virtual void setResponseRequired( const bool required ){} + virtual bool isResponseRequired() const{ return false; } + }; + + class MyCommandListener : public CommandListener{ + public: + MyCommandListener(){} + virtual ~MyCommandListener(){} + + std::string str; + virtual void onCommand( Command* command ){ + const MyCommand* cmd = dynamic_cast(command); + str += cmd->c; + } + }; + + class MyCommandReader : public CommandReader{ + private: + + /** + * The target input stream. + */ + io::InputStream* inputStream; + + public: + MyCommandReader(){ throwException = false; } + virtual ~MyCommandReader(){} + + bool throwException; + + virtual void setInputStream(io::InputStream* is){ + inputStream = is; + } + + virtual io::InputStream* getInputStream(void){ + return inputStream; + } + + virtual Command* readCommand( void ) throw (CommandIOException){ + + try{ + if( throwException ){ + throw CommandIOException(); + } + + synchronized( inputStream ){ + MyCommand* command = new MyCommand(); + command->c = inputStream->read(); + return command; + } + + assert(false); + return NULL; + }catch( exceptions::ActiveMQException& ex ){ + CommandIOException cx( ex ); + cx.setMark( __FILE__, __LINE__ ); + throw cx; + } + } + + virtual int read(unsigned char* buffer, int count) + throw( io::IOException ) { + return 0; + } + + virtual unsigned char readByte() throw(io::IOException) { + return 0; + } + }; + + class MyCommandWriter : public CommandWriter{ + private: + + /** + * Target output stream. + */ + io::OutputStream* outputStream; + + public: + virtual ~MyCommandWriter(){} + + virtual void setOutputStream(io::OutputStream* os){ + outputStream = os; + } + + virtual io::OutputStream* getOutputStream(void){ + return outputStream; + } + + virtual void writeCommand( const Command* command ) + throw (CommandIOException) + { + try{ + synchronized( outputStream ){ + + const MyCommand* m = + dynamic_cast(command); + outputStream->write( m->c ); + } + }catch( exceptions::ActiveMQException& ex ){ + CommandIOException cx( ex ); + cx.setMark( __FILE__, __LINE__ ); + throw cx; + } + } + + virtual void write(const unsigned char* buffer, int count) + throw(io::IOException) {} + + virtual void writeByte(unsigned char v) throw(io::IOException) {} + }; + + class MyExceptionListener : public TransportExceptionListener{ + public: + + Transport* transport; + concurrent::Mutex mutex; + + MyExceptionListener(){ + transport = NULL; + } + virtual ~MyExceptionListener(){} + + virtual void onTransportException( Transport* source, const exceptions::ActiveMQException& ex ){ + transport = source; + + synchronized(&mutex) + { + mutex.notify(); + } + } + }; + + public: + + virtual void setUp(){}; + virtual void tearDown(){}; + + // This will just test that we can start and stop the + // transport without any exceptions. + void testStartClose(){ + + io::ByteArrayInputStream is; + io::ByteArrayOutputStream os; + MyCommandListener listener; + MyCommandReader reader; + MyCommandWriter writer; + MyExceptionListener exListener; + IOTransport transport; + transport.setCommandListener( &listener ); + transport.setCommandReader( &reader ); + transport.setCommandWriter( &writer ); + transport.setInputStream( &is ); + transport.setOutputStream( &os ); + transport.setTransportExceptionListener( &exListener ); + + transport.start(); + + concurrent::Thread::sleep( 50 ); + + transport.close(); + } + + void testRead(){ + + io::ByteArrayInputStream is; + io::ByteArrayOutputStream os; + MyCommandListener listener; + MyCommandReader reader; + MyCommandWriter writer; + MyExceptionListener exListener; + IOTransport transport; + transport.setCommandListener( &listener ); + transport.setCommandReader( &reader ); + transport.setCommandWriter( &writer ); + transport.setInputStream( &is ); + transport.setOutputStream( &os ); + transport.setTransportExceptionListener( &exListener ); + + transport.start(); + + concurrent::Thread::sleep( 10 ); + + unsigned char buffer[10] = { '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' }; + try{ + synchronized( &is ){ + is.setByteArray( buffer, 10 ); + } + }catch( exceptions::ActiveMQException& ex ){ + ex.setMark( __FILE__, __LINE__ ); + } + + concurrent::Thread::sleep( 100 ); + + CPPUNIT_ASSERT( listener.str == "1234567890" ); + + transport.close(); + } + + void testWrite(){ + + io::ByteArrayInputStream is; + io::ByteArrayOutputStream os; + MyCommandListener listener; + MyCommandReader reader; + MyCommandWriter writer; + MyExceptionListener exListener; + IOTransport transport; + transport.setCommandListener( &listener ); + transport.setCommandReader( &reader ); + transport.setCommandWriter( &writer ); + transport.setInputStream( &is ); + transport.setOutputStream( &os ); + transport.setTransportExceptionListener( &exListener ); + + transport.start(); + + MyCommand cmd; + cmd.c = '1'; + transport.oneway( &cmd ); + cmd.c = '2'; + transport.oneway( &cmd ); + cmd.c = '3'; + transport.oneway( &cmd ); + cmd.c = '4'; + transport.oneway( &cmd ); + cmd.c = '5'; + transport.oneway( &cmd ); + + const unsigned char* bytes = os.getByteArray(); + int size = os.getByteArraySize(); + CPPUNIT_ASSERT( size >= 5 ); + CPPUNIT_ASSERT( bytes[0] == '1' ); + CPPUNIT_ASSERT( bytes[1] == '2' ); + CPPUNIT_ASSERT( bytes[2] == '3' ); + CPPUNIT_ASSERT( bytes[3] == '4' ); + CPPUNIT_ASSERT( bytes[4] == '5' ); + + transport.close(); + } + + void testException(){ + + io::ByteArrayInputStream is; + io::ByteArrayOutputStream os; + MyCommandListener listener; + MyCommandReader reader; + MyCommandWriter writer; + MyExceptionListener exListener; + IOTransport transport; + transport.setCommandListener( &listener ); + transport.setCommandReader( &reader ); + reader.throwException = true; + transport.setCommandWriter( &writer ); + transport.setInputStream( &is ); + transport.setOutputStream( &os ); + transport.setTransportExceptionListener( &exListener ); + + unsigned char buffer[1] = { '1' }; + try{ + synchronized( &is ){ + is.setByteArray( buffer, 1); + } + }catch( exceptions::ActiveMQException& ex ){ + ex.setMark(__FILE__, __LINE__ ); + } + + transport.start(); + + synchronized(&exListener.mutex) + { + if(exListener.transport != &transport) + { + exListener.mutex.wait(1000); + } + } + + CPPUNIT_ASSERT( exListener.transport == &transport ); + + transport.close(); + } + }; + +}} + +#endif /*ACTIVEMQ_COMMANDS_IOTRANSPORTTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/transport/ResponseCorrelatorTest.cpp b/activemq-cpp/src/test/activemq/transport/ResponseCorrelatorTest.cpp new file mode 100644 index 0000000000..5f5d16751b --- /dev/null +++ b/activemq-cpp/src/test/activemq/transport/ResponseCorrelatorTest.cpp @@ -0,0 +1,4 @@ +#include "ResponseCorrelatorTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::transport::ResponseCorrelatorTest ); + diff --git a/activemq-cpp/src/test/activemq/transport/ResponseCorrelatorTest.h b/activemq-cpp/src/test/activemq/transport/ResponseCorrelatorTest.h new file mode 100644 index 0000000000..5aeb553a6b --- /dev/null +++ b/activemq-cpp/src/test/activemq/transport/ResponseCorrelatorTest.h @@ -0,0 +1,625 @@ +#ifndef ACTIVEMQ_COMMANDS_RESPONSECORRELATORTEST_H_ +#define ACTIVEMQ_COMMANDS_RESPONSECORRELATORTEST_H_ + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace activemq{ +namespace transport{ + + class ResponseCorrelatorTest : public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE( ResponseCorrelatorTest ); + CPPUNIT_TEST( testBasics ); + CPPUNIT_TEST( testOneway ); + CPPUNIT_TEST( testExceptionResponse ); + CPPUNIT_TEST( testTransportException ); + CPPUNIT_TEST( testMultiRequests ); + CPPUNIT_TEST_SUITE_END(); + + public: + + class MyCommand : public Command{ + private: + + unsigned int commandId; + bool responseRequired; + + public: + + virtual void setCommandId( const unsigned int id ){ + commandId = id; + } + virtual unsigned int getCommandId() const{ + return commandId; + } + + virtual void setResponseRequired( const bool required ){ + responseRequired = required; + } + virtual bool isResponseRequired() const{ + return responseRequired; + } + }; + + class MyResponse : public Response{ + private: + + unsigned int commandId; + bool responseRequired; + unsigned int corrId; + + public: + + virtual void setCommandId( const unsigned int id ){ + commandId = id; + } + virtual unsigned int getCommandId() const{ + return commandId; + } + + virtual void setResponseRequired( const bool required ){ + responseRequired = required; + } + virtual bool isResponseRequired() const{ + return responseRequired; + } + + virtual unsigned int getCorrelationId() const{ + return corrId; + } + virtual void setCorrelationId( const unsigned int corrId ){ + this->corrId = corrId; + } + }; + + class MyExceptionResponse : public ExceptionResponse{ + public: + + unsigned int commandId; + bool responseRequired; + unsigned int corrId; + BrokerError error; + + public: + + virtual void setCommandId( const unsigned int id ){ + commandId = id; + } + virtual unsigned int getCommandId() const{ + return commandId; + } + + virtual void setResponseRequired( const bool required ){ + responseRequired = required; + } + virtual bool isResponseRequired() const{ + return responseRequired; + } + + virtual unsigned int getCorrelationId() const{ + return corrId; + } + virtual void setCorrelationId( const unsigned int corrId ){ + this->corrId = corrId; + } + virtual const BrokerError* getException() const{ + return &error; + } + }; + + class MyTransport + : + public Transport, + public concurrent::Runnable{ + public: + CommandReader* reader; + CommandWriter* writer; + CommandListener* listener; + TransportExceptionListener* exListener; + concurrent::Thread* thread; + concurrent::Mutex mutex; + concurrent::Mutex startedMutex; + bool done; + std::queue requests; + + public: + + MyTransport(){ + reader = NULL; + writer = NULL; + listener = NULL; + exListener = NULL; + thread = NULL; + done = false; + } + + virtual ~MyTransport(){ + + close(); + } + + virtual void oneway( Command* command ) + throw(CommandIOException, exceptions::UnsupportedOperationException) + { + synchronized( &mutex ){ + requests.push( command ); + mutex.notifyAll(); + } + } + + virtual Response* request( Command* command ) + throw(CommandIOException, exceptions::UnsupportedOperationException) + { + throw exceptions::UnsupportedOperationException( + __FILE__, + __LINE__, + "stuff" ); + } + + virtual void setCommandListener( CommandListener* listener ){ + this->listener = listener; + } + + virtual void setCommandReader( CommandReader* reader ){ + this->reader = reader; + } + + virtual void setCommandWriter( CommandWriter* writer ){ + this->writer = writer; + } + + virtual void setTransportExceptionListener( + TransportExceptionListener* listener ) + { + this->exListener = listener; + } + + virtual void start() throw( cms::CMSException ){ + close(); + + done = false; + + thread = new concurrent::Thread( this ); + thread->start(); + } + + virtual void close() throw( cms::CMSException ){ + + done = true; + + if( thread != NULL ){ + synchronized( &mutex ){ + mutex.notifyAll(); + } + thread->join(); + delete thread; + thread = NULL; + } + } + + virtual Response* createResponse( Command* command ){ + + MyResponse* resp = new MyResponse(); + resp->setCorrelationId( command->getCommandId() ); + resp->setResponseRequired( false ); + return resp; + } + + virtual void run(){ + + try{ + + synchronized(&startedMutex) + { + startedMutex.notifyAll(); + } + + synchronized( &mutex ){ + + while( !done ){ + + if( requests.empty() ){ + mutex.wait(); + }else{ + + Command* cmd = requests.front(); + requests.pop(); + + // Only send a response if one is required. + Response* resp = NULL; + if( cmd->isResponseRequired() ){ + resp = createResponse( cmd ); + } + + mutex.unlock(); + + // Send both the response and the original + // command back to the correlator. + if( listener != NULL ){ + if( resp != NULL ){ + listener->onCommand( resp ); + } + listener->onCommand( cmd ); + } + + mutex.lock(); + } + } + } + }catch( exceptions::ActiveMQException& ex ){ + if( exListener ){ + exListener->onTransportException( this, ex ); + } + } + catch( ... ){ + if( exListener ){ + exceptions::ActiveMQException ex( __FILE__, __LINE__, "stuff" ); + exListener->onTransportException( this, ex ); + } + } + } + }; + + class MyExceptionResponseTransport : public MyTransport{ + public: + + MyExceptionResponseTransport(){} + virtual ~MyExceptionResponseTransport(){} + + virtual Response* createResponse( Command* command ){ + + MyExceptionResponse* resp = new MyExceptionResponse(); + resp->setCorrelationId( command->getCommandId() ); + resp->setResponseRequired( false ); + resp->error = BrokerError( __FILE__, __LINE__, + "some bad broker stuff" ); + return resp; + } + }; + + class MyBrokenTransport : public MyTransport{ + public: + + MyBrokenTransport(){} + virtual ~MyBrokenTransport(){} + + virtual Response* createResponse( Command* command ){ + throw exceptions::ActiveMQException( __FILE__, __LINE__, + "bad stuff" ); + } + }; + + class MyListener + : + public CommandListener, + public TransportExceptionListener{ + + public: + + int exCount; + std::set commands; + concurrent::Mutex mutex; + + public: + + MyListener(){ + exCount = 0; + } + virtual ~MyListener(){} + virtual void onCommand( Command* command ){ + + synchronized( &mutex ){ + commands.insert( command->getCommandId() ); + + mutex.notify(); + } + } + + virtual void onTransportException( + Transport* source, + const exceptions::ActiveMQException& ex ) + { + synchronized( &mutex ){ + exCount++; + } + } + }; + + class RequestThread : public concurrent::Thread{ + public: + + Transport* transport; + MyCommand cmd; + Response* resp; + public: + + RequestThread(){ + transport = NULL; + resp = NULL; + } + virtual ~RequestThread(){ + join(); + + if( resp != NULL ){ + delete resp; + resp = NULL; + } + } + + void setTransport( Transport* transport ){ + this->transport = transport; + } + + void run(){ + + try{ + resp = transport->request(&cmd); + }catch( ... ){ + CPPUNIT_ASSERT( false ); + } + } + }; + + public: + + virtual void setUp(){}; + virtual void tearDown(){}; + + void testBasics(){ + + try{ + + MyListener listener; + MyTransport transport; + ResponseCorrelator correlator( &transport, false ); + correlator.setCommandListener( &listener ); + correlator.setTransportExceptionListener( &listener ); + CPPUNIT_ASSERT( transport.listener == &correlator ); + CPPUNIT_ASSERT( transport.exListener == &correlator ); + + // Give the thread a little time to get up and running. + synchronized(&transport.startedMutex) + { + // Start the transport. + correlator.start(); + transport.startedMutex.wait(); + } + + // Send one request. + MyCommand cmd; + Response* resp = correlator.request( &cmd ); + CPPUNIT_ASSERT( resp != NULL ); + CPPUNIT_ASSERT( dynamic_cast(resp) == NULL ); + CPPUNIT_ASSERT( resp->getCorrelationId() == cmd.getCommandId() ); + + // Wait to get the message back asynchronously. + concurrent::Thread::sleep( 100 ); + + // Since our transport relays our original command back at us as a + // non-response message, check to make sure we received it and that + // it is the original command. + CPPUNIT_ASSERT( listener.commands.size() == 1 ); + CPPUNIT_ASSERT( listener.exCount == 0 ); + + correlator.close(); + + // Destroy the response. + delete resp; + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) + } + + void testOneway(){ + + try{ + + MyListener listener; + MyTransport transport; + ResponseCorrelator correlator( &transport, false ); + correlator.setCommandListener( &listener ); + correlator.setTransportExceptionListener( &listener ); + CPPUNIT_ASSERT( transport.listener == &correlator ); + CPPUNIT_ASSERT( transport.exListener == &correlator ); + + // Give the thread a little time to get up and running. + synchronized(&transport.startedMutex) + { + // Start the transport. + correlator.start(); + + transport.startedMutex.wait(); + } + + // Send many oneway request (we'll get them back asynchronously). + const unsigned int numCommands = 1000; + MyCommand commands[numCommands]; + for( unsigned int ix=0; ix(resp) != NULL ); + CPPUNIT_ASSERT( resp->getCorrelationId() == cmd.getCommandId() ); + + // Wait to make sure we get the exception. + concurrent::Thread::sleep( 100 ); + + // Since our transport relays our original command back at us as a + // non-response message, check to make sure we received it and that + // it is the original command. + CPPUNIT_ASSERT( listener.commands.size() == 1 ); + CPPUNIT_ASSERT( listener.exCount == 1 ); + + correlator.close(); + + // Destroy the response. + delete resp; + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) + } + + void testTransportException(){ + + try{ + + MyListener listener; + MyBrokenTransport transport; + ResponseCorrelator correlator( &transport, false ); + correlator.setCommandListener( &listener ); + correlator.setTransportExceptionListener( &listener ); + CPPUNIT_ASSERT( transport.listener == &correlator ); + CPPUNIT_ASSERT( transport.exListener == &correlator ); + + // Give the thread a little time to get up and running. + synchronized(&transport.startedMutex) + { + // Start the transport. + correlator.start(); + + transport.startedMutex.wait(); + } + + // Send one request. + MyCommand cmd; + try{ + correlator.request( &cmd ); + CPPUNIT_ASSERT(false); + }catch( CommandIOException& ex ){ + // Expected. + } + + // Wait to make sure we get the asynchronous message back. + concurrent::Thread::sleep( 100 ); + + // Since our transport relays our original command back at us as a + // non-response message, check to make sure we received it and that + // it is the original command. + CPPUNIT_ASSERT( listener.commands.size() == 0 ); + CPPUNIT_ASSERT( listener.exCount == 1 ); + + correlator.close(); + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) + } + + void testMultiRequests(){ + + try{ + + MyListener listener; + MyTransport transport; + ResponseCorrelator correlator( &transport, false ); + correlator.setCommandListener( &listener ); + correlator.setTransportExceptionListener( &listener ); + CPPUNIT_ASSERT( transport.listener == &correlator ); + CPPUNIT_ASSERT( transport.exListener == &correlator ); + + // Start the transport. + correlator.start(); + + // Make sure the start command got down to the thread. + CPPUNIT_ASSERT( transport.thread != NULL ); + + // Give the thread a little time to get up and running. + synchronized(&transport.startedMutex) + { + transport.startedMutex.wait(500); + } + + // Start all the requester threads. + const unsigned int numRequests = 100; + RequestThread requesters[numRequests]; + for( unsigned int ix=0; ixgetCorrelationId() ); + } + + concurrent::Thread::sleep( 25 ); + synchronized( &listener.mutex ) + { + unsigned int count = 0; + + while( listener.commands.size() != numRequests ) + { + listener.mutex.wait( 45 ); + + ++count; + + if( count == numRequests ) { + break; + } + } + } + + // Since our transport relays our original command back at us as a + // non-response message, check to make sure we received it and that + // it is the original command. + CPPUNIT_ASSERT( listener.commands.size() == numRequests ); + CPPUNIT_ASSERT( listener.exCount == 0 ); + + correlator.close(); + } + AMQ_CATCH_RETHROW( exceptions::ActiveMQException ) + AMQ_CATCHALL_THROW( exceptions::ActiveMQException ) + } + }; + +}} + +#endif /*ACTIVEMQ_COMMANDS_RESPONSECORRELATORTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/transport/TransportFactoryMapRegistrarTest.cpp b/activemq-cpp/src/test/activemq/transport/TransportFactoryMapRegistrarTest.cpp new file mode 100644 index 0000000000..db727067a8 --- /dev/null +++ b/activemq-cpp/src/test/activemq/transport/TransportFactoryMapRegistrarTest.cpp @@ -0,0 +1,3 @@ +#include "TransportFactoryMapRegistrarTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::transport::TransportFactoryMapRegistrarTest ); diff --git a/activemq-cpp/src/test/activemq/transport/TransportFactoryMapRegistrarTest.h b/activemq-cpp/src/test/activemq/transport/TransportFactoryMapRegistrarTest.h new file mode 100644 index 0000000000..099fc738a4 --- /dev/null +++ b/activemq-cpp/src/test/activemq/transport/TransportFactoryMapRegistrarTest.h @@ -0,0 +1,43 @@ +#ifndef ACTIVEMQ_TRANSPORT_CONNECTORFACTORYMAPREGISTRARTEST_H_ +#define ACTIVEMQ_TRANSPORT_CONNECTORFACTORYMAPREGISTRARTEST_H_ + +#include +#include + +#include +#include + +namespace activemq{ +namespace transport{ + + class TransportFactoryMapRegistrarTest : public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE( TransportFactoryMapRegistrarTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + class TestTransportFactory : public TransportFactory + { + public: + + virtual Transport* createTransport( + const activemq::util::Properties& properties ) { return NULL; }; + }; + + void test(){ + + { + TransportFactoryMapRegistrar registrar("Test", new TestTransportFactory()); + + CPPUNIT_ASSERT( TransportFactoryMap::getInstance().lookup("Test") != NULL); + } + + CPPUNIT_ASSERT( TransportFactoryMap::getInstance().lookup( "Test" ) == NULL ); + } + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_CONNECTORFACTORYMAPREGISTRARTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/transport/TransportFactoryMapTest.cpp b/activemq-cpp/src/test/activemq/transport/TransportFactoryMapTest.cpp new file mode 100644 index 0000000000..7c46e3dab5 --- /dev/null +++ b/activemq-cpp/src/test/activemq/transport/TransportFactoryMapTest.cpp @@ -0,0 +1,4 @@ +#include "TransportFactoryMapTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::transport::TransportFactoryMapTest ); + diff --git a/activemq-cpp/src/test/activemq/transport/TransportFactoryMapTest.h b/activemq-cpp/src/test/activemq/transport/TransportFactoryMapTest.h new file mode 100644 index 0000000000..af265e114b --- /dev/null +++ b/activemq-cpp/src/test/activemq/transport/TransportFactoryMapTest.h @@ -0,0 +1,60 @@ +#ifndef ACTIVEMQ_TRANSPORT_TRANSPORTFACTORYMAPTEST_H_ +#define ACTIVEMQ_TRANSPORT_TRANSPORTFACTORYMAPTEST_H_ + +#include +#include + +#include +#include + +namespace activemq{ +namespace transport{ + + class TransportFactoryMapTest : public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE( TransportFactoryMapTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + class TestTransportFactory : public TransportFactory + { + public: + + virtual Transport* createTransport( + const activemq::util::Properties& properties) { return NULL; }; + }; + + void test(){ + + TransportFactoryMap& factMap = + TransportFactoryMap::getInstance(); + TestTransportFactory testFactory; + + factMap.registerTransportFactory( "test", &testFactory ); + + CPPUNIT_ASSERT( factMap.lookup( "test" ) == &testFactory ); + + std::vector names; + CPPUNIT_ASSERT( factMap.getFactoryNames( names ) >= 1 ); + + bool found = false; + for( unsigned int i = 0; i < names.size(); ++i ) + { + if( names[i] == "test" ) + { + found = true; + break; + } + } + CPPUNIT_ASSERT( found ); + + factMap.unregisterTransportFactory( "test" ); + CPPUNIT_ASSERT( factMap.lookup( "test" ) == NULL ); + } + }; + +}} + +#endif /*ACTIVEMQ_TRANSPORT_TRANSPORTFACTORYMAPTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/util/BooleanTest.cpp b/activemq-cpp/src/test/activemq/util/BooleanTest.cpp new file mode 100644 index 0000000000..536fc193c9 --- /dev/null +++ b/activemq-cpp/src/test/activemq/util/BooleanTest.cpp @@ -0,0 +1,3 @@ +#include "BooleanTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::util::BooleanTest ); diff --git a/activemq-cpp/src/test/activemq/util/BooleanTest.h b/activemq-cpp/src/test/activemq/util/BooleanTest.h new file mode 100644 index 0000000000..247129ecae --- /dev/null +++ b/activemq-cpp/src/test/activemq/util/BooleanTest.h @@ -0,0 +1,47 @@ +#ifndef _ACTIVEMQ_UTIL_BOOLEANTEST_H_ +#define _ACTIVEMQ_UTIL_BOOLEANTEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace util{ + + class BooleanTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( BooleanTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + BooleanTest() {} + virtual ~BooleanTest() {} + + virtual void test(void) + { + bool x = Boolean::parseBoolean("false"); + bool y = Boolean::parseBoolean("true"); + bool z = Boolean::parseBoolean("false"); + + CPPUNIT_ASSERT( x == false ); + CPPUNIT_ASSERT( y == true ); + CPPUNIT_ASSERT( z == false ); + + std::string x1 = Boolean::toString( x ); + std::string y1 = Boolean::toString( y ); + std::string z1 = Boolean::toString( z ); + + CPPUNIT_ASSERT( x1 == "false" ); + CPPUNIT_ASSERT( y1 == "true" ); + CPPUNIT_ASSERT( z1 == "false" ); + + } + + }; + +}} + +#endif /*_ACTIVEMQ_UTIL_BOOLEANTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/util/GuidTest.cpp b/activemq-cpp/src/test/activemq/util/GuidTest.cpp new file mode 100644 index 0000000000..3cef3678f5 --- /dev/null +++ b/activemq-cpp/src/test/activemq/util/GuidTest.cpp @@ -0,0 +1,3 @@ +#include "GuidTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::util::GuidTest ); diff --git a/activemq-cpp/src/test/activemq/util/GuidTest.h b/activemq-cpp/src/test/activemq/util/GuidTest.h new file mode 100644 index 0000000000..20f53eb7a6 --- /dev/null +++ b/activemq-cpp/src/test/activemq/util/GuidTest.h @@ -0,0 +1,78 @@ +#ifndef _ACTIVEMQ_UTIL_GUIDTEST_H_ +#define _ACTIVEMQ_UTIL_GUIDTEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace util{ + + class GuidTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( GuidTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + virtual ~GuidTest() {} + + void test(void) + { + util::Guid guid; + + guid.createGUID(); + + CPPUNIT_ASSERT( guid.toString() == (std::string)guid ); + + Guid copy = guid; + + CPPUNIT_ASSERT( guid == copy ); + CPPUNIT_ASSERT( !(guid < copy) ); + CPPUNIT_ASSERT( guid <= copy ); + CPPUNIT_ASSERT( !(guid > copy) ); + CPPUNIT_ASSERT( guid >= copy ); + + std::string less = "0f2bd21c-9fee-4067-d739-c4d84a5d7f62"; + std::string more = "1f2bd21c-9fee-4067-d739-c4d84a5d7f62"; + + CPPUNIT_ASSERT( less < more ); + CPPUNIT_ASSERT( less <= more ); + CPPUNIT_ASSERT( !(less > more) ); + CPPUNIT_ASSERT( !(less >= more) ); + + less = more; + + CPPUNIT_ASSERT( less == more ); + + const unsigned char* bytes = guid.toBytes(); + + Guid bytesGUID; + bytesGUID.fromBytes(bytes); + + CPPUNIT_ASSERT( guid == bytesGUID ); + + delete bytes; + + Guid bytesGUID2; + bytesGUID2.fromBytes((const unsigned char*)guid); + + CPPUNIT_ASSERT( guid == bytesGUID2 ); + + Guid stringGUID(guid.toString()); + + CPPUNIT_ASSERT( stringGUID == guid ); + + Guid stringGUID2(guid.toString().c_str()); + + CPPUNIT_ASSERT( stringGUID2 == guid ); + CPPUNIT_ASSERT( !(stringGUID2 != guid) ); + + } + }; + +}} + +#endif /*_ACTIVEMQ_UTIL_GUIDTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/util/IntegerTest.cpp b/activemq-cpp/src/test/activemq/util/IntegerTest.cpp new file mode 100644 index 0000000000..c289df9093 --- /dev/null +++ b/activemq-cpp/src/test/activemq/util/IntegerTest.cpp @@ -0,0 +1,4 @@ +#include "IntegerTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::util::IntegerTest ); + diff --git a/activemq-cpp/src/test/activemq/util/IntegerTest.h b/activemq-cpp/src/test/activemq/util/IntegerTest.h new file mode 100644 index 0000000000..1ed2417a10 --- /dev/null +++ b/activemq-cpp/src/test/activemq/util/IntegerTest.h @@ -0,0 +1,47 @@ +#ifndef INTEGERTEST_H_ +#define INTEGERTEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace util{ + + class IntegerTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( IntegerTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + IntegerTest(void) {} + virtual ~IntegerTest(void) {} + + virtual void test(void) + { + int x = Integer::parseInt("12"); + int y = Integer::parseInt("12.1"); + int z = Integer::parseInt("42 24"); + + CPPUNIT_ASSERT( x == 12 ); + CPPUNIT_ASSERT( y == 12 ); + CPPUNIT_ASSERT( z == 42 ); + + std::string x1 = Integer::toString( x ); + std::string y1 = Integer::toString( y ); + std::string z1 = Integer::toString( z ); + + CPPUNIT_ASSERT( x1 == "12" ); + CPPUNIT_ASSERT( y1 == "12" ); + CPPUNIT_ASSERT( z1 == "42" ); + + } + + }; + +}} + +#endif /*INTEGERTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/util/LongTest.cpp b/activemq-cpp/src/test/activemq/util/LongTest.cpp new file mode 100644 index 0000000000..d063b1eac7 --- /dev/null +++ b/activemq-cpp/src/test/activemq/util/LongTest.cpp @@ -0,0 +1,3 @@ +#include "LongTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::util::LongTest ); diff --git a/activemq-cpp/src/test/activemq/util/LongTest.h b/activemq-cpp/src/test/activemq/util/LongTest.h new file mode 100644 index 0000000000..691fae5518 --- /dev/null +++ b/activemq-cpp/src/test/activemq/util/LongTest.h @@ -0,0 +1,47 @@ +#ifndef _ACTIVEMQ_UTIL_LONGTEST_H_ +#define _ACTIVEMQ_UTIL_LONGTEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace util{ + + class LongTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( LongTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + LongTest() {} + virtual ~LongTest() {} + + virtual void test(void) + { + long x = Long::parseLong("12"); + long y = Long::parseLong("12.1"); + long z = Long::parseLong("42 24"); + + CPPUNIT_ASSERT( x == 12 ); + CPPUNIT_ASSERT( y == 12 ); + CPPUNIT_ASSERT( z == 42 ); + + std::string x1 = Long::toString( x ); + std::string y1 = Long::toString( y ); + std::string z1 = Long::toString( z ); + + CPPUNIT_ASSERT( x1 == "12" ); + CPPUNIT_ASSERT( y1 == "12" ); + CPPUNIT_ASSERT( z1 == "42" ); + + } + + }; + +}} + +#endif /*_ACTIVEMQ_UTIL_LONGTEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/util/QueueTest.cpp b/activemq-cpp/src/test/activemq/util/QueueTest.cpp new file mode 100644 index 0000000000..268d930a44 --- /dev/null +++ b/activemq-cpp/src/test/activemq/util/QueueTest.cpp @@ -0,0 +1,3 @@ +#include "QueueTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::util::QueueTest ); diff --git a/activemq-cpp/src/test/activemq/util/QueueTest.h b/activemq-cpp/src/test/activemq/util/QueueTest.h new file mode 100644 index 0000000000..0a64e627c2 --- /dev/null +++ b/activemq-cpp/src/test/activemq/util/QueueTest.h @@ -0,0 +1,50 @@ +#ifndef _ACTIVEMQ_UTIL_QUEUETEST_H_ +#define _ACTIVEMQ_UTIL_QUEUETEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace util{ + + class QueueTest : public CppUnit::TestFixture { + + CPPUNIT_TEST_SUITE( QueueTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + + public: + + virtual ~QueueTest() {} + + void test() + { + Queue q; + + CPPUNIT_ASSERT( q.empty() == true ); + CPPUNIT_ASSERT( q.size() == 0 ); + + q.push('a'); + + CPPUNIT_ASSERT( q.front() == 'a' ); + + q.pop(); + + CPPUNIT_ASSERT( q.empty() == true ); + + q.push('b'); + q.push('c'); + + CPPUNIT_ASSERT( q.size() == 2 ); + + CPPUNIT_ASSERT( q.front() == 'b' ); + CPPUNIT_ASSERT( q.back() == 'c' ); + + } + }; + +}} + +#endif /*_ACTIVEMQ_UTIL_QUEUETEST_H_*/ diff --git a/activemq-cpp/src/test/activemq/util/StringTokenizerTest.cpp b/activemq-cpp/src/test/activemq/util/StringTokenizerTest.cpp new file mode 100644 index 0000000000..6ab023b96b --- /dev/null +++ b/activemq-cpp/src/test/activemq/util/StringTokenizerTest.cpp @@ -0,0 +1,3 @@ +#include "StringTokenizerTest.h" + +CPPUNIT_TEST_SUITE_REGISTRATION( activemq::util::StringTokenizerTest ); diff --git a/activemq-cpp/src/test/activemq/util/StringTokenizerTest.h b/activemq-cpp/src/test/activemq/util/StringTokenizerTest.h new file mode 100644 index 0000000000..393b48af47 --- /dev/null +++ b/activemq-cpp/src/test/activemq/util/StringTokenizerTest.h @@ -0,0 +1,99 @@ +#ifndef _ACTIVEMQ_UTIL_STRINGTOKENIZERTEST_H_ +#define _ACTIVEMQ_UTIL_STRINGTOKENIZERTEST_H_ + +#include +#include + +#include + +namespace activemq{ +namespace util{ + + class StringTokenizerTest : public CppUnit::TestFixture + { + CPPUNIT_TEST_SUITE( StringTokenizerTest ); + CPPUNIT_TEST( test ); + CPPUNIT_TEST_SUITE_END(); + public: + + virtual ~StringTokenizerTest() {} + + void test() + { + StringTokenizer tokenizer("stomp://127.0.0.1:23232", "://"); + CPPUNIT_ASSERT( tokenizer.countTokens() == 3 ); + CPPUNIT_ASSERT( tokenizer.nextToken() == "stomp" ); + CPPUNIT_ASSERT( tokenizer.nextToken() == "127.0.0.1" ); + CPPUNIT_ASSERT( tokenizer.nextToken() == "23232" ); + + StringTokenizer tokenizer1("::://stomp://127.0.0.1:23232:", ":/"); + CPPUNIT_ASSERT( tokenizer1.countTokens() == 3 ); + CPPUNIT_ASSERT( tokenizer1.nextToken() == "stomp" ); + CPPUNIT_ASSERT( tokenizer1.nextToken() == "127.0.0.1" ); + CPPUNIT_ASSERT( tokenizer1.nextToken() == "23232" ); + + StringTokenizer tokenizer2("test"); + CPPUNIT_ASSERT( tokenizer2.countTokens() == 1 ); + CPPUNIT_ASSERT( tokenizer2.hasMoreTokens() == true ); + CPPUNIT_ASSERT( tokenizer2.nextToken() == "test" ); + CPPUNIT_ASSERT( tokenizer2.hasMoreTokens() == false ); + + StringTokenizer tokenizer3(":", ":"); + CPPUNIT_ASSERT( tokenizer3.countTokens() == 0 ); + CPPUNIT_ASSERT( tokenizer3.hasMoreTokens() == false ); + CPPUNIT_ASSERT( tokenizer3.nextToken(" ") == ":" ); + + try + { + tokenizer3.nextToken(); + + CPPUNIT_ASSERT( false ); + } + catch(exceptions::NoSuchElementException ex) + { + CPPUNIT_ASSERT( true ); + } + + StringTokenizer tokenizer4("the quick brown fox"); + CPPUNIT_ASSERT( tokenizer4.countTokens() == 4 ); + CPPUNIT_ASSERT( tokenizer4.hasMoreTokens() == true ); + CPPUNIT_ASSERT( tokenizer4.nextToken() == "the" ); + CPPUNIT_ASSERT( tokenizer4.nextToken() == "quick" ); + CPPUNIT_ASSERT( tokenizer4.nextToken() == "brown" ); + CPPUNIT_ASSERT( tokenizer4.nextToken() == "fox" ); + CPPUNIT_ASSERT( tokenizer4.countTokens() == 0 ); + CPPUNIT_ASSERT( tokenizer4.hasMoreTokens() == false ); + + StringTokenizer tokenizer5("the:quick:brown:fox", ":", true); + CPPUNIT_ASSERT( tokenizer5.countTokens() == 7 ); + CPPUNIT_ASSERT( tokenizer5.hasMoreTokens() == true ); + CPPUNIT_ASSERT( tokenizer5.nextToken() == "the" ); + CPPUNIT_ASSERT( tokenizer5.nextToken() == ":" ); + CPPUNIT_ASSERT( tokenizer5.nextToken() == "quick" ); + CPPUNIT_ASSERT( tokenizer5.nextToken() == ":" ); + CPPUNIT_ASSERT( tokenizer5.nextToken() == "brown" ); + CPPUNIT_ASSERT( tokenizer5.nextToken() == ":" ); + CPPUNIT_ASSERT( tokenizer5.nextToken() == "fox" ); + CPPUNIT_ASSERT( tokenizer5.countTokens() == 0 ); + CPPUNIT_ASSERT( tokenizer5.hasMoreTokens() == false ); + + std::vector myArray; + StringTokenizer tokenizer6("the:quick:brown:fox", ":"); + CPPUNIT_ASSERT( tokenizer6.countTokens() == 4 ); + CPPUNIT_ASSERT( tokenizer6.toArray(myArray) == 4 ); + CPPUNIT_ASSERT( tokenizer6.countTokens() == 0 ); + tokenizer6.reset(); + CPPUNIT_ASSERT( tokenizer6.countTokens() == 4 ); + tokenizer6.reset("the:quick:brown:fox", "$"); + CPPUNIT_ASSERT( tokenizer6.countTokens() == 1 ); + tokenizer6.reset("this$is$a$test"); + CPPUNIT_ASSERT( tokenizer6.countTokens() == 4 ); + tokenizer6.reset("this$is$a$test", "$", true); + CPPUNIT_ASSERT( tokenizer6.countTokens() == 7 ); + + } + }; + +}} + +#endif /*_ACTIVEMQ_UTIL_STRINGTOKENIZERTEST_H_*/ diff --git a/activemq-cpp/src/test/main.cpp b/activemq-cpp/src/test/main.cpp new file mode 100644 index 0000000000..63d0b8d4a9 --- /dev/null +++ b/activemq-cpp/src/test/main.cpp @@ -0,0 +1,19 @@ +#include +#include +#include +#include + +int main( int argc, char **argv) +{ + CppUnit::TextUi::TestRunner runner; + CppUnit::TestFactoryRegistry ®istry = CppUnit::TestFactoryRegistry::getRegistry(); + runner.addTest( registry.makeTest() ); + + // Shows a message as each test starts + CppUnit::BriefTestProgressListener listener; + runner.eventManager().addListener( &listener ); + + bool wasSuccessful = runner.run( "", false ); + return !wasSuccessful; +} + diff --git a/activemq-cpp/todo.txt b/activemq-cpp/todo.txt new file mode 100644 index 0000000000..413836745b --- /dev/null +++ b/activemq-cpp/todo.txt @@ -0,0 +1,36 @@ +Client Side: + +* Work out platform independent build system +* Complete Logging API +* Add Destination URL like parameter processing +* investigate the 999 (1000) messages bug in the broker + + +* integration test against real AMQ broker (DONE) +* finish unit testing of core api (DONE) +* refactoring of core API (DONE) +* Resolve static initialization when used as a library. (DONE) +* Add Message Cloning to the Commands tests. (DONE) +* enforce connected state in stomp connector (DONE) +* Add setting username & password to connect command (DONE) +* Dummy Transport that acts like a broker (DONE) +* Update Session Manager to use Transport not Connector (DONE) +* Add Transport Factory Lookup Method (DONE) +* Connector Interfaces Cleanup (DONE) + +Server Side: + +* Implement Connected as a response (DONE) +* Implement use of JMSType in all messages (DONE) +* Add Content Length to all outgoing messages (DONE) + +Nice to Haves: + +* Add Connection Id support to Stomp Transport on Broker +* Add Consumer Id support to Stomp Transport on Broker +* implement selector algorithm - always pass null selector to broker, + current implementation is limited to using the selector on the + first consumer that is subscribed to a Topic. +* Add Durable Subscriptions to Stomp Connector. (DONE) + + diff --git a/activemq-cpp/unix/pom.xml b/activemq-cpp/unix/pom.xml new file mode 100644 index 0000000000..ab4e83f37f --- /dev/null +++ b/activemq-cpp/unix/pom.xml @@ -0,0 +1,78 @@ + + + 4.0.0 + + + activemq + activemq-cpp-parent + 1.0-SNAPSHOT + ../pom.xml + + + libactivemq-cpp + ActiveMQ :: CPP :: UNIX + The C++ client library for interfacing with an ActiveMQ broker + a + + + ${artifactId} + + + + + org.codehaus.mojo + native-maven-plugin + 1.0-alpha-1-SNAPSHOT + true + + + + + generic + + ${compiler.options} + + ar + + -r + + + + + + + + + + + + release + + true + + + + -frtti -pthread -O3 -DNDEBUG -D_REENTRANT + + + + + + + debug + + + debug + + + + + -frtti -g -pthread -DDEBUG -D_DEBUG -D_REENTRANT + + + + + + + diff --git a/activemq-cpp/win32-gcc/pom.xml b/activemq-cpp/win32-gcc/pom.xml new file mode 100644 index 0000000000..737e696b8b --- /dev/null +++ b/activemq-cpp/win32-gcc/pom.xml @@ -0,0 +1,78 @@ + + + 4.0.0 + + + activemq + activemq-cpp-parent + 1.0-SNAPSHOT + ../pom.xml + + + libactivemq-cpp + ActiveMQ :: CPP :: WIN32 :: GCC + The C++ client library for interfacing with an ActiveMQ broker + a + + + ${artifactId} + + + + + org.codehaus.mojo + native-maven-plugin + 1.0-alpha-1-SNAPSHOT + true + + + + + generic + + ${compiler.options} + + ar + + -r + + + + + + + + + + + + release + + true + + + + -frtti -pthread -O3 -DNDEBUG -D_REENTRANT -D_WIN32 -DWINVER=0x0502 -DWIN32_LEAN_AND_MEAN + + + + + + + debug + + + debug + + + + + -frtti -g -pthread -DDEBUG -D_DEBUG -D_REENTRANT -D_WIN32 -DWINVER=0x0502 -DWIN32_LEAN_AND_MEAN + + + + + + + diff --git a/activemq-cpp/win32-msvc/pom.xml b/activemq-cpp/win32-msvc/pom.xml new file mode 100644 index 0000000000..58c7a9ea67 --- /dev/null +++ b/activemq-cpp/win32-msvc/pom.xml @@ -0,0 +1,80 @@ + + + 4.0.0 + + + activemq + activemq-cpp-parent + 1.0-SNAPSHOT + ../pom.xml + + + activemq-cpp + ActiveMQ :: CPP :: WIN32 :: MSVC + The C++ client library for interfacing with an ActiveMQ broker + lib + + + ${artifactId} + + + + + org.codehaus.mojo + native-maven-plugin + 1.0-alpha-1-SNAPSHOT + true + + + + + msvc + org.codehaus.mojo.natives.msvc.MSVC2005x86EnvFactory + + ${compiler.options} + + lib + + + + + + + + + + + + + + release + + true + + + + /INCREMENTAL:NO /O2 /D WIN32 /D NDEBUG /D _LIB /D WIN32_LEAN_AND_MEAN /D _CRT_SECURE_NO_DEPRECATE /FD /EHsc /MD /Fo"Release\\" /Fd"Release\vc80.pdb" /W2 /nologo /c /Wp64 /Zi /TP + + + + + + + debug + + + debug + + + + + /INCREMENTAL:NO /Od /D WIN32 /D _DEBUG /D _LIB /D WIN32_LEAN_AND_MEAN /D _CRT_SECURE_NO_DEPRECATE /Gm /EHsc /RTC1 /MDd /Fo"Debug\\" /Fd"Debug\vc80.pdb" /W2 /nologo /c /Wp64 /ZI /TP + + + + + + + +