summaryrefslogtreecommitdiff
path: root/java/broker
diff options
context:
space:
mode:
authorRafael H. Schloming <rhs@apache.org>2006-09-19 22:06:50 +0000
committerRafael H. Schloming <rhs@apache.org>2006-09-19 22:06:50 +0000
commit913489deb2ee9dbf44455de5f407ddaf4bd8c540 (patch)
tree7ea442d6867d0076f1c9ea4f4265664059e7aff5 /java/broker
downloadqpid-python-913489deb2ee9dbf44455de5f407ddaf4bd8c540.tar.gz
Import of qpid from etp:
URL: https://etp.108.redhat.com/svn/etp/trunk/blaze Repository Root: https://etp.108.redhat.com/svn/etp Repository UUID: 06e15bec-b515-0410-bef0-cc27a458cf48 Revision: 608 git-svn-id: https://svn.apache.org/repos/asf/incubator/qpid/trunk/qpid@447994 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'java/broker')
-rw-r--r--java/broker/README.txt40
-rw-r--r--java/broker/bin/qpid-server18
-rw-r--r--java/broker/bin/qpid-server.bat53
-rwxr-xr-xjava/broker/bin/run.bat28
-rwxr-xr-xjava/broker/bin/run.sh41
-rw-r--r--java/broker/bin/runAll18
-rw-r--r--java/broker/build-module.xml22
-rw-r--r--java/broker/build-old.xml265
-rw-r--r--java/broker/etc/config.xml90
-rw-r--r--java/broker/etc/log4j.xml46
-rw-r--r--java/broker/etc/passwd1
-rw-r--r--java/broker/etc/qpid-server.conf22
-rw-r--r--java/broker/etc/virtualhosts.xml25
-rw-r--r--java/broker/src/log4j.properties6
-rw-r--r--java/broker/src/org/apache/qpid/server/AMQChannel.java702
-rw-r--r--java/broker/src/org/apache/qpid/server/ConsumerTagNotUniqueException.java22
-rw-r--r--java/broker/src/org/apache/qpid/server/Main.java612
-rw-r--r--java/broker/src/org/apache/qpid/server/ManagedChannel.java64
-rw-r--r--java/broker/src/org/apache/qpid/server/RequiredDeliveryException.java109
-rw-r--r--java/broker/src/org/apache/qpid/server/configuration/Configurator.java102
-rw-r--r--java/broker/src/org/apache/qpid/server/configuration/VirtualHostConfiguration.java217
-rw-r--r--java/broker/src/org/apache/qpid/server/exchange/AbstractExchange.java134
-rw-r--r--java/broker/src/org/apache/qpid/server/exchange/DefaultExchangeFactory.java63
-rw-r--r--java/broker/src/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java92
-rw-r--r--java/broker/src/org/apache/qpid/server/exchange/DestNameExchange.java204
-rw-r--r--java/broker/src/org/apache/qpid/server/exchange/DestWildExchange.java210
-rw-r--r--java/broker/src/org/apache/qpid/server/exchange/Exchange.java47
-rw-r--r--java/broker/src/org/apache/qpid/server/exchange/ExchangeFactory.java28
-rw-r--r--java/broker/src/org/apache/qpid/server/exchange/ExchangeInUseException.java28
-rw-r--r--java/broker/src/org/apache/qpid/server/exchange/ExchangeRegistry.java38
-rw-r--r--java/broker/src/org/apache/qpid/server/exchange/HeadersBinding.java142
-rw-r--r--java/broker/src/org/apache/qpid/server/exchange/HeadersExchange.java226
-rw-r--r--java/broker/src/org/apache/qpid/server/exchange/Index.java85
-rw-r--r--java/broker/src/org/apache/qpid/server/exchange/ManagedExchange.java78
-rw-r--r--java/broker/src/org/apache/qpid/server/exchange/MessageRouter.java36
-rw-r--r--java/broker/src/org/apache/qpid/server/exchange/NoRouteException.java39
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/BasicAckMethodHandler.java52
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/BasicCancelMethodHandler.java58
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java90
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/BasicPublishMethodHandler.java77
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/BasicQosHandler.java46
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/BasicRecoverMethodHandler.java54
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/ChannelCloseHandler.java58
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/ChannelCloseOkHandler.java51
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/ChannelFlowHandler.java61
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/ChannelOpenHandler.java58
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java65
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/ConnectionCloseOkMethodHandler.java63
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java68
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java115
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java127
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/ConnectionTuneOkMethodHandler.java54
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/ExchangeDeclareHandler.java79
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/ExchangeDeleteHandler.java62
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/OnCurrentThreadExecutor.java31
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/QueueBindHandler.java94
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/QueueDeclareHandler.java124
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/QueueDeleteHandler.java84
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/TxCommitHandler.java55
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/TxRollbackHandler.java59
-rw-r--r--java/broker/src/org/apache/qpid/server/handler/TxSelectHandler.java50
-rw-r--r--java/broker/src/org/apache/qpid/server/jms/JmsConsumer.java107
-rw-r--r--java/broker/src/org/apache/qpid/server/management/AMQManagedObject.java65
-rw-r--r--java/broker/src/org/apache/qpid/server/management/DefaultManagedObject.java126
-rw-r--r--java/broker/src/org/apache/qpid/server/management/JMXManagedObjectRegistry.java66
-rw-r--r--java/broker/src/org/apache/qpid/server/management/Managable.java31
-rw-r--r--java/broker/src/org/apache/qpid/server/management/ManagedBroker.java78
-rw-r--r--java/broker/src/org/apache/qpid/server/management/ManagedObject.java53
-rw-r--r--java/broker/src/org/apache/qpid/server/management/ManagedObjectRegistry.java39
-rw-r--r--java/broker/src/org/apache/qpid/server/management/ManagementConfiguration.java27
-rw-r--r--java/broker/src/org/apache/qpid/server/management/NoopManagedObjectRegistry.java45
-rw-r--r--java/broker/src/org/apache/qpid/server/protocol/AMQMethodEvent.java62
-rw-r--r--java/broker/src/org/apache/qpid/server/protocol/AMQMethodListener.java52
-rw-r--r--java/broker/src/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java603
-rw-r--r--java/broker/src/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java217
-rw-r--r--java/broker/src/org/apache/qpid/server/protocol/AMQPProtocolProvider.java50
-rw-r--r--java/broker/src/org/apache/qpid/server/protocol/AMQProtocolSession.java122
-rw-r--r--java/broker/src/org/apache/qpid/server/protocol/ExchangeInitialiser.java38
-rw-r--r--java/broker/src/org/apache/qpid/server/protocol/HeartbeatConfig.java64
-rw-r--r--java/broker/src/org/apache/qpid/server/protocol/ManagedConnection.java92
-rw-r--r--java/broker/src/org/apache/qpid/server/protocol/ManagedSession.java33
-rw-r--r--java/broker/src/org/apache/qpid/server/queue/AMQMessage.java343
-rw-r--r--java/broker/src/org/apache/qpid/server/queue/AMQQueue.java654
-rw-r--r--java/broker/src/org/apache/qpid/server/queue/AsyncDeliveryConfig.java53
-rw-r--r--java/broker/src/org/apache/qpid/server/queue/DefaultQueueRegistry.java47
-rw-r--r--java/broker/src/org/apache/qpid/server/queue/DeliveryManager.java262
-rw-r--r--java/broker/src/org/apache/qpid/server/queue/ExchangeBindings.java109
-rw-r--r--java/broker/src/org/apache/qpid/server/queue/ManagedQueue.java177
-rw-r--r--java/broker/src/org/apache/qpid/server/queue/NoConsumersException.java47
-rw-r--r--java/broker/src/org/apache/qpid/server/queue/QueueRegistry.java30
-rw-r--r--java/broker/src/org/apache/qpid/server/queue/Subscription.java29
-rw-r--r--java/broker/src/org/apache/qpid/server/queue/SubscriptionFactory.java37
-rw-r--r--java/broker/src/org/apache/qpid/server/queue/SubscriptionImpl.java172
-rw-r--r--java/broker/src/org/apache/qpid/server/queue/SubscriptionManager.java28
-rw-r--r--java/broker/src/org/apache/qpid/server/queue/SubscriptionSet.java180
-rw-r--r--java/broker/src/org/apache/qpid/server/queue/WeightedSubscriptionManager.java23
-rw-r--r--java/broker/src/org/apache/qpid/server/registry/ApplicationRegistry.java108
-rw-r--r--java/broker/src/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java155
-rw-r--r--java/broker/src/org/apache/qpid/server/registry/IApplicationRegistry.java65
-rw-r--r--java/broker/src/org/apache/qpid/server/security/auth/AuthenticationManager.java30
-rw-r--r--java/broker/src/org/apache/qpid/server/security/auth/AuthenticationProviderInitialiser.java63
-rw-r--r--java/broker/src/org/apache/qpid/server/security/auth/AuthenticationResult.java40
-rw-r--r--java/broker/src/org/apache/qpid/server/security/auth/CRAMMD5Initialiser.java35
-rw-r--r--java/broker/src/org/apache/qpid/server/security/auth/JCAProvider.java43
-rw-r--r--java/broker/src/org/apache/qpid/server/security/auth/PasswordFilePrincipalDatabase.java130
-rw-r--r--java/broker/src/org/apache/qpid/server/security/auth/PrincipalDatabase.java42
-rw-r--r--java/broker/src/org/apache/qpid/server/security/auth/SASLAuthenticationManager.java224
-rw-r--r--java/broker/src/org/apache/qpid/server/security/auth/UsernamePasswordInitialiser.java99
-rw-r--r--java/broker/src/org/apache/qpid/server/security/auth/UsernamePrincipal.java39
-rw-r--r--java/broker/src/org/apache/qpid/server/security/auth/amqplain/AmqPlainInitialiser.java35
-rw-r--r--java/broker/src/org/apache/qpid/server/security/auth/amqplain/AmqPlainSaslServer.java120
-rw-r--r--java/broker/src/org/apache/qpid/server/security/auth/amqplain/AmqPlainSaslServerFactory.java56
-rw-r--r--java/broker/src/org/apache/qpid/server/security/auth/plain/PlainInitialiser.java35
-rw-r--r--java/broker/src/org/apache/qpid/server/security/auth/plain/PlainSaslServer.java141
-rw-r--r--java/broker/src/org/apache/qpid/server/security/auth/plain/PlainSaslServerFactory.java56
-rw-r--r--java/broker/src/org/apache/qpid/server/state/AMQState.java33
-rw-r--r--java/broker/src/org/apache/qpid/server/state/AMQStateManager.java219
-rw-r--r--java/broker/src/org/apache/qpid/server/state/IllegalStateTransitionException.java45
-rw-r--r--java/broker/src/org/apache/qpid/server/state/StateAwareMethodListener.java37
-rw-r--r--java/broker/src/org/apache/qpid/server/state/StateListener.java27
-rw-r--r--java/broker/src/org/apache/qpid/server/store/MemoryMessageStore.java137
-rw-r--r--java/broker/src/org/apache/qpid/server/store/MessageStore.java80
-rw-r--r--java/broker/src/org/apache/qpid/server/transport/ConnectorConfiguration.java93
-rw-r--r--java/broker/src/org/apache/qpid/server/transport/ThreadPoolFilter.java692
-rw-r--r--java/broker/src/org/apache/qpid/server/txn/TxnBuffer.java75
-rw-r--r--java/broker/src/org/apache/qpid/server/txn/TxnOp.java26
-rw-r--r--java/broker/src/org/apache/qpid/server/util/CircularBuffer.java123
-rw-r--r--java/broker/src/org/apache/qpid/server/util/LoggingProxy.java102
-rw-r--r--java/broker/test/build-module.xml32
-rw-r--r--java/broker/test/lib/README1
-rw-r--r--java/broker/test/lib/junit/junit-4.0.jarbin0 -> 105601 bytes
-rw-r--r--java/broker/test/lib/junit/junit.jarbin0 -> 121070 bytes
-rw-r--r--java/broker/test/src/org/apache/qpid/server/UnitTests.java39
-rw-r--r--java/broker/test/src/org/apache/qpid/server/configuration/TestPropertyUtils.java50
-rw-r--r--java/broker/test/src/org/apache/qpid/server/configuration/UnitTests.java32
-rw-r--r--java/broker/test/src/org/apache/qpid/server/exchange/AbstractHeadersExchangeTest.java212
-rw-r--r--java/broker/test/src/org/apache/qpid/server/exchange/HeadersBindingTest.java200
-rw-r--r--java/broker/test/src/org/apache/qpid/server/exchange/HeadersExchangePerformanceTest.java181
-rw-r--r--java/broker/test/src/org/apache/qpid/server/exchange/HeadersExchangeTest.java81
-rw-r--r--java/broker/test/src/org/apache/qpid/server/exchange/UnitTests.java32
-rw-r--r--java/broker/test/src/org/apache/qpid/server/protocol/MockIoSession.java288
-rw-r--r--java/broker/test/src/org/apache/qpid/server/protocol/TestProtocolInitiation.java212
-rw-r--r--java/broker/test/src/org/apache/qpid/server/protocol/UnitTests.java32
-rw-r--r--java/broker/test/src/org/apache/qpid/server/queue/AckTest.java243
-rw-r--r--java/broker/test/src/org/apache/qpid/server/queue/ConcurrencyTest.java261
-rw-r--r--java/broker/test/src/org/apache/qpid/server/queue/DeliveryManagerTest.java159
-rw-r--r--java/broker/test/src/org/apache/qpid/server/queue/MessageTestHelper.java49
-rw-r--r--java/broker/test/src/org/apache/qpid/server/queue/MockProtocolSession.java121
-rw-r--r--java/broker/test/src/org/apache/qpid/server/queue/QueueConcurrentPerfTest.java46
-rw-r--r--java/broker/test/src/org/apache/qpid/server/queue/QueuePerfTest.java255
-rw-r--r--java/broker/test/src/org/apache/qpid/server/queue/SendPerfTest.java171
-rw-r--r--java/broker/test/src/org/apache/qpid/server/queue/SubscriptionManagerTest.java105
-rw-r--r--java/broker/test/src/org/apache/qpid/server/queue/SubscriptionSetTest.java149
-rw-r--r--java/broker/test/src/org/apache/qpid/server/queue/TestSubscription.java84
-rw-r--r--java/broker/test/src/org/apache/qpid/server/queue/UnitTests.java38
-rw-r--r--java/broker/test/src/org/apache/qpid/server/store/SkeletonMessageStore.java99
-rw-r--r--java/broker/test/src/org/apache/qpid/server/store/TestReferenceCounting.java68
-rw-r--r--java/broker/test/src/org/apache/qpid/server/store/TestableMemoryMessageStore.java39
-rw-r--r--java/broker/test/src/org/apache/qpid/server/store/UnitTests.java34
-rw-r--r--java/broker/test/src/org/apache/qpid/server/util/AveragedRun.java63
-rw-r--r--java/broker/test/src/org/apache/qpid/server/util/ConcurrentTest.java76
-rw-r--r--java/broker/test/src/org/apache/qpid/server/util/LoggingProxyTest.java89
-rw-r--r--java/broker/test/src/org/apache/qpid/server/util/NullApplicationRegistry.java103
-rw-r--r--java/broker/test/src/org/apache/qpid/server/util/NullAuthenticationManager.java82
-rw-r--r--java/broker/test/src/org/apache/qpid/server/util/RunStats.java54
-rw-r--r--java/broker/test/src/org/apache/qpid/server/util/TimedRun.java49
-rw-r--r--java/broker/test/src/org/apache/qpid/server/util/UnitTests.java32
167 files changed, 16915 insertions, 0 deletions
diff --git a/java/broker/README.txt b/java/broker/README.txt
new file mode 100644
index 0000000000..d6519ab1d1
--- /dev/null
+++ b/java/broker/README.txt
@@ -0,0 +1,40 @@
+========================================================================
+This software uses berkeley db java edition, licensed as described
+below.
+
+========================================================================
+Copyright (c) 1990-2005
+ Sleepycat Software. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. Redistributions in any form must be accompanied by information on
+ how to obtain complete source code for the DB software and any
+ accompanying software that uses the DB software. The source code
+ must either be included in the distribution or be available for no
+ more than the cost of distribution plus a nominal fee, and must be
+ freely redistributable under reasonable conditions. For an
+ executable file, complete source code means the source code for all
+ modules it contains. It does not include source code for modules or
+ files that typically accompany the major components of the operating
+ system on which the executable file runs.
+
+THIS SOFTWARE IS PROVIDED BY SLEEPYCAT SOFTWARE ``AS IS'' AND ANY EXPRESS
+OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR
+NON-INFRINGEMENT, ARE DISCLAIMED. IN NO EVENT SHALL SLEEPYCAT SOFTWARE
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+THE POSSIBILITY OF SUCH DAMAGE.
+
+ \ No newline at end of file
diff --git a/java/broker/bin/qpid-server b/java/broker/bin/qpid-server
new file mode 100644
index 0000000000..353b2a2077
--- /dev/null
+++ b/java/broker/bin/qpid-server
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Copyright (c) 2006 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+. qpid-run org.apache.qpid.server.Main "$@"
diff --git a/java/broker/bin/qpid-server.bat b/java/broker/bin/qpid-server.bat
new file mode 100644
index 0000000000..1e2ca7c1ac
--- /dev/null
+++ b/java/broker/bin/qpid-server.bat
@@ -0,0 +1,53 @@
+@REM
+@REM Copyright (c) 2006 The Apache Software Foundation
+@REM
+@REM Licensed under the Apache License, Version 2.0 (the "License");
+@REM you may not use this file except in compliance with the License.
+@REM You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing, software
+@REM distributed under the License is distributed on an "AS IS" BASIS,
+@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@REM See the License for the specific language governing permissions and
+@REM limitations under the License.
+@REM
+
+@echo off
+REM Script to run the Qpid Java Broker
+
+rem Guess QPID_HOME if not defined
+set CURRENT_DIR=%cd%
+if not "%QPID_HOME%" == "" goto gotHome
+set QPID_HOME=%CURRENT_DIR%
+echo %QPID_HOME%
+if exist "%QPID_HOME%\bin\qpid-server.bat" goto okHome
+cd ..
+set QPID_HOME=%cd%
+cd %CURRENT_DIR%
+:gotHome
+if exist "%QPID_HOME%\bin\qpid-server.bat" goto okHome
+echo The QPID_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program
+goto end
+:okHome
+
+if not "%JAVA_HOME%" == "" goto gotJavaHome
+echo The JAVA_HOME environment variable is not defined
+echo This environment variable is needed to run this program
+goto exit
+:gotJavaHome
+if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome
+goto okJavaHome
+:noJavaHome
+echo The JAVA_HOME environment variable is not defined correctly
+echo This environment variable is needed to run this program.
+goto exit
+:okJavaHome
+
+set LAUNCH_JAR=%QPID_HOME%\lib\broker-launch.jar
+set MODULE_JARS=%QPID_HOME%\lib\bdbstore-launch.jar
+"%JAVA_HOME%"\bin\java -server -Xmx1024m -DQPID_HOME="%QPID_HOME%" -cp "%LAUNCH_JAR%;%MODULE_JARS%" org.apache.qpid.server.Main *
+
+:end
diff --git a/java/broker/bin/run.bat b/java/broker/bin/run.bat
new file mode 100755
index 0000000000..fde68364fd
--- /dev/null
+++ b/java/broker/bin/run.bat
@@ -0,0 +1,28 @@
+@REM
+@REM Copyright (c) 2006 The Apache Software Foundation
+@REM
+@REM Licensed under the Apache License, Version 2.0 (the "License");
+@REM you may not use this file except in compliance with the License.
+@REM You may obtain a copy of the License at
+@REM
+@REM http://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing, software
+@REM distributed under the License is distributed on an "AS IS" BASIS,
+@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@REM See the License for the specific language governing permissions and
+@REM limitations under the License.
+@REM
+
+@echo off
+set CORE_CLASSES=..\classes;..\testclasses
+
+set COMMONDIR=..\..\common
+
+set COMMONLIB=%COMMONDIR%\lib\slf4j\slf4j-simple.jar;%COMMONDIR%\lib\logging-log4j\log4j-1.2.9.jar;%COMMONDIR%\lib\mina\mina-core-0.9.2.jar
+
+set DIST=..\lib\bdb\je-3.0.12.jar;..\dist\amqpd.jar;..\..\client\dist\amqp-common.jar
+
+set CP=%CORE_CLASSES%;%DIST%;%COMMONLIB%
+
+"%JAVA_HOME%\bin\java" -Xmx512m -Damqj.logging.level=INFO -cp %CP% %*
diff --git a/java/broker/bin/run.sh b/java/broker/bin/run.sh
new file mode 100755
index 0000000000..06ad56b61c
--- /dev/null
+++ b/java/broker/bin/run.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+#
+# Copyright (c) 2006 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+
+COMMONDIR=../../common
+
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/slf4j/slf4j-simple.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/logging-log4j/log4j-1.2.13.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/mina/mina-core-0.9.5-SNAPSHOT.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/mina/mina-filter-ssl-0.9.5-SNAPSHOT.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-collections/commons-collections-3.1.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-cli/commons-cli-1.0.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-configuration/commons-configuration-1.2.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-logging/commons-logging-api.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-logging/commons-logging.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/commons-lang/commons-lang-2.1.jar
+COMMONLIB=$COMMONLIB:$COMMONDIR/lib/junit/junit-4.0.jar
+
+DIST=../lib/bdb/je-3.0.12.jar:../dist/amqpd-tests.jar:../dist/amqpd.jar:../../client/dist/amqp-common.jar
+
+CP=../intellijclasses:$DIST:$COMMONLIB
+
+if [ "$(uname -a | fgrep Cygwin)" != "" ]; then
+ CP=$(cygpath --mixed --path $CP)
+fi
+
+"$JAVA_HOME/bin/java" -Xmx512m -Dprepopulate=10 -Damqj.logging.level=INFO -cp $CP $*
diff --git a/java/broker/bin/runAll b/java/broker/bin/runAll
new file mode 100644
index 0000000000..0d6c6068e6
--- /dev/null
+++ b/java/broker/bin/runAll
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+doRun()
+{
+ class=$1
+ shift
+ echo
+ echo ================================================================================
+ echo Running $class
+ ./run.sh $class "$@"
+}
+
+# parameters are: clients messages iterations
+doRun org.apache.qpid.server.queue.SendPerfTest 10 1000 100
+
+doRun org.apache.qpid.server.queue.QueuePerfTest
+
+doRun org.apache.qpid.server.queue.QueueConcurrentPerfTest
diff --git a/java/broker/build-module.xml b/java/broker/build-module.xml
new file mode 100644
index 0000000000..4c3aa56396
--- /dev/null
+++ b/java/broker/build-module.xml
@@ -0,0 +1,22 @@
+<!--
+ -
+ - Copyright (c) 2006 The Apache Software Foundation
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+ -
+ -->
+<project name="AMQ Broker" default="build">
+ <property name="module.depends" value="common"/>
+ <property name="module.main" value="org.apache.qpid.server.Main"/>
+ <import file="../module.xml"/>
+</project>
diff --git a/java/broker/build-old.xml b/java/broker/build-old.xml
new file mode 100644
index 0000000000..0f910610ea
--- /dev/null
+++ b/java/broker/build-old.xml
@@ -0,0 +1,265 @@
+<?xml version="1.0"?>
+<!--
+ -
+ - Copyright (c) 2006 The Apache Software Foundation
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+ -
+ -->
+
+<!-- AMQPd build file -->
+
+<project name="qpid-broker" default="jar" basedir=".">
+
+ <!-- Global task properties -->
+ <property name="common.root" value="${basedir}/../common"/>
+ <property name="common.lib" value="${common.root}/lib"/>
+ <property name="common.src" value="${common.root}/src"/>
+ <property name="common.resources" value="${common.root}/resources"/>
+
+ <property name="common.generated.java.src" value="${common.root}/generated/java"/>
+
+ <property name="client.classes" value="${basedir}/../client/classes"/>
+ <property name="client.testclasses" value="${basedir}/../client/testclasses"/>
+
+ <property name="broker.root" value="${basedir}"/>
+ <property name="broker.lib" value="${basedir}/lib"/>
+ <property name="broker.src" value="${broker.root}/src"/>
+ <property name="broker.tests" value="${broker.root}/test"/>
+ <property name="broker.classes" value="${broker.root}/classes"/>
+ <property name="broker.testclasses" value="${broker.root}/testclasses"/>
+
+ <property name="broker.dist" value="${basedir}/dist"/>
+ <property name="dam.dist" value="${basedir}/damPackage"/>
+
+ <!-- Path structures used by several tasks -->
+ <path id="amqp.classpath">
+ <fileset dir="${broker.lib}">
+ <include name="**/*.jar"/>
+ </fileset>
+ <fileset dir="${common.lib}">
+ <include name="**/*.jar"/>
+ </fileset>
+ <pathelement path="${client.classes}"/>
+ <pathelement path="${client.testclasses}"/>
+ <pathelement path="${broker.classes}"/>
+ <pathelement path="${broker.testclasses}"/>
+ </path>
+
+ <!-- Task definitions -->
+
+ <target name="init" description="Initialise the build process">
+ <tstamp>
+ <format property="release" pattern="-dMMMyy" locale="en" timezone="GMT"/>
+ </tstamp>
+
+ <mkdir dir="${broker.classes}"/>
+ <mkdir dir="${broker.testclasses}"/>
+ <mkdir dir="${broker.dist}"/>
+ </target>
+
+ <!-- Remove all built files -->
+ <target name="clean" depends="init" description="Remove all built and generated files">
+ <delete dir="${broker.dist}"/>
+ <!--<delete dir="${generated.java.src}"/>-->
+ <delete dir="${broker.classes}"/>
+ <delete dir="${broker.testclasses}"/>
+ </target>
+
+ <!-- Generate framing source code from protocol specification -->
+ <target name="generate" description="Generates framing source code from protocol specification">
+ <ant dir="../common" target="generate"/>
+ </target>
+
+ <!-- Compile Java -->
+ <target name="compile" depends="init, generate" description="Compile all source files excluding tests">
+ <javac destdir="${broker.classes}" target="1.5" source="1.5" classpathref="amqp.classpath" debug="on">
+ <compilerarg value="-Xlint:unchecked" />
+ <compilerarg value="-Xlint:deprecation" />
+ <src path="${common.src}"/>
+ <src path="${broker.src}"/>
+ <src path="${common.generated.java.src}"/>
+ </javac>
+
+ <copy todir="${broker.classes}">
+ <!-- copy any non java src files into the build tree, e.g. log4j.properties -->
+ <fileset dir="${broker.src}">
+ <exclude name="**/*.java"/>
+ <exclude name="**/package.html"/>
+ </fileset>
+ <fileset dir="${common.src}">
+ <exclude name="**/*.java"/>
+ <exclude name="**/package.html"/>
+ </fileset>
+ <fileset dir="${common.resources}">
+ <exclude name="**/*.java"/>
+ <exclude name="**/package.html"/>
+ </fileset>
+ </copy>
+ </target>
+
+ <target name="compiletests" depends="compile" description="Compile all tests">
+ <javac destdir="${broker.testclasses}" target="1.5" source="1.5" classpathref="amqp.classpath" debug="on">
+ <compilerarg value="-Xlint:unchecked"/>
+ <compilerarg value="-Xlint:deprecation"/>
+ <src path="${broker.tests}"/>
+ </javac>
+
+ <copy todir="${broker.testclasses}">
+ <!-- copy any non java src files into the build tree, e.g. log4j.properties -->
+ <fileset dir="${broker.tests}">
+ <exclude name="**/*.java"/>
+ <exclude name="**/package.html"/>
+ </fileset>
+ </copy>
+ </target>
+
+ <target name="test" depends="compiletests">
+ <junit fork="yes" showoutput="true" haltonfailure="yes">
+ <test name="org.apache.qpid.server.UnitTests"/>
+ <formatter type="plain"/>
+ <classpath refid="amqp.classpath"/>
+ </junit>
+ </target>
+
+ <!-- Build jar archive -->
+ <target name="jar" depends="compile, compiletests" description="Create Jar files as the distribution">
+ <mkdir dir="${broker.dist}"/>
+ <jar basedir="${broker.classes}" jarfile="${broker.dist}/amqpd.jar"/>
+ <jar basedir="${broker.testclasses}" jarfile="${broker.dist}/amqpd-tests.jar"/>
+ </target>
+
+ <target name="release" depends="jar" description="Create a release package">
+ <!-- create a jar file that will contain classpath entries for all required jars -->
+ <jar jarfile="${broker.dist}/broker-launch.jar">
+ <manifest>
+ <attribute name="Main-Class" value="org.apache.qpid.server.Main"/>
+ <attribute name="Class-Path" value="lib/je-3.0.35.jar lib/slf4j-simple.jar lib/log4j-1.2.13.jar lib/mina-core-0.9.5-SNAPSHOT.jar lib/mina-filter-ssl-0.9.5-SNAPSHOT.jar lib/commons-cli-1.0.jar lib/commons-configuration-1.2.jar lib/commons-collections-3.1.jar lib/commons-logging-api.jar lib/commons-logging.jar lib/commons-lang-2.1.jar lib/amqpd.jar lib/amqp-common.jar"/>
+ </manifest>
+ </jar>
+ <!-- create a zip for releasing -->
+ <zip destfile="${broker.dist}/broker.zip">
+ <fileset dir="bin" includes="*.sh,*.bat"/>
+ <fileset file="${broker.dist}/broker-launch.jar" />
+ <zipfileset prefix="etc" dir="etc" includes="*"/>
+ <zipfileset prefix="lib" file="${broker.lib}/bdb/je-3.0.35.jar"/>
+ <zipfileset prefix="lib" file="${common.lib}/slf4j/slf4j-simple.jar"/>
+ <zipfileset prefix="lib" file="${common.lib}/logging-log4j/log4j-1.2.13.jar"/>
+ <zipfileset prefix="lib" file="${common.lib}/mina/mina-core-0.9.5-SNAPSHOT.jar"/>
+ <zipfileset prefix="lib" file="${common.lib}/mina/mina-filter-ssl-0.9.5-SNAPSHOT.jar"/>
+ <zipfileset prefix="lib" file="${common.lib}/commons-cli/commons-cli-1.0.jar"/>
+ <zipfileset prefix="lib" file="${common.lib}/commons-configuration/commons-configuration-1.2.jar"/>
+ <zipfileset prefix="lib" file="${common.lib}/commons-collections/commons-collections-3.1.jar"/>
+ <zipfileset prefix="lib" file="${common.lib}/commons-logging/commons-logging-api.jar"/>
+ <zipfileset prefix="lib" file="${common.lib}/commons-logging/commons-logging.jar"/>
+ <zipfileset prefix="lib" file="${common.lib}/commons-lang/commons-lang-2.1.jar"/>
+ <zipfileset prefix="lib" file="${broker.dist}/amqpd.jar"/>
+ <zipfileset prefix="lib" file="${broker.dist}/amqp-common.jar"/>
+ </zip>
+
+ <tar destfile="${broker.dist}/broker.tar.gz" compression="gzip">
+ <tarfileset dir="bin" includes="*.sh,*.bat"/>
+ <tarfileset file="${broker.dist}/broker-launch.jar" />
+ <tarfileset prefix="etc" dir="etc" includes="*"/>
+ <tarfileset prefix="lib" file="${broker.lib}/bdb/je-3.0.35.jar"/>
+ <tarfileset prefix="lib" file="${common.lib}/slf4j/slf4j-simple.jar"/>
+ <tarfileset prefix="lib" file="${common.lib}/logging-log4j/log4j-1.2.13.jar"/>
+ <tarfileset prefix="lib" file="${common.lib}/mina/mina-core-0.9.5-SNAPSHOT.jar"/>
+ <tarfileset prefix="lib" file="${common.lib}/mina/mina-filter-ssl-0.9.5-SNAPSHOT.jar"/>
+ <tarfileset prefix="lib" file="${common.lib}/commons-cli/commons-cli-1.0.jar"/>
+ <tarfileset prefix="lib" file="${common.lib}/commons-configuration/commons-configuration-1.2.jar"/>
+ <tarfileset prefix="lib" file="${common.lib}/commons-collections/commons-collections-3.1.jar"/>
+ <tarfileset prefix="lib" file="${common.lib}/commons-logging/commons-logging-api.jar"/>
+ <tarfileset prefix="lib" file="${common.lib}/commons-logging/commons-logging.jar"/>
+ <tarfileset prefix="lib" file="${common.lib}/commons-lang/commons-lang-2.1.jar"/>
+ <tarfileset prefix="lib" file="${broker.dist}/amqpd.jar"/>
+ <tarfileset prefix="lib" file="${broker.dist}/amqp-common.jar"/>
+ </tar>
+ </target>
+
+ <!-- @TODO finish this target MM !! -->
+ <target name="damit">
+
+ <property name="tarFileName" value="ams_amq.tar"/>
+ <delete failonerror="false" file="${tarFileName}"/>
+ <delete includeEmptyDirs="true" failonerror="false">
+ <fileset dir="${dam.dist}"/>
+ </delete>
+ <mkdir dir="${dam.dist}"/>
+ <copy todir="${dam.dist}">
+ <fileset dir="${basedir}/damPackage">
+ <exclude name="config/gps_domain/applications/gps.ear"/>
+ </fileset>
+ </copy>
+ <!-- Remove CR and EOF characters from shell scripts within the DAM package -->
+ <fixcrlf srcdir="${dam.dist}" eol="lf" eof="remove">
+ <include name="**/*.sh"/>
+ <include name="**/*.startup"/>
+ </fixcrlf>
+
+ <tar tarfile="${tarFileName}">
+ <!-- add everything _except_ shell scripts: rw_r__r__ -->
+ <tarfileset dir="${dam.dist}" mode="644">
+ <exclude name="**/*.sh"/>
+ <exclude name="**/*.startup"/>
+ </tarfileset>
+ <!-- add all shell scripts as executable: rwxr_xr_x -->
+ <tarfileset dir="${dam.dist}" mode="755">
+ <include name="**/*.sh"/>
+ <include name="**/*.startup"/>
+ </tarfileset>
+ </tar>
+
+ </target>
+
+ <target name="javadoc" depends="compile, compiletests" description="Generate javadoc for all broker classes">
+ <mkdir dir="${broker.dist}/docs/api"/>
+ <javadoc sourcepath="${broker.src}" destdir="${broker.dist}/docs/api"
+ packagenames="org.apache.qpid.*" classpathref="amqp.classpath" author="true"
+ version="true" windowTitle="AMQPd (Project Qpid) API" doctitle="AMQP Broker API"
+ footer="See &lt;a href=&quot;http://www.amqp.org&quot;&gt;www.amqp.org&lt;/a&gt; for more information."
+ use="true" verbose="false"/>
+ </target>
+
+ <target name="RunBroker" depends="compile" description="Run broker">
+ <java classpathref="amqp.classpath" fork="yes" classname="org.apache.qpid.server.Main">
+ <sysproperty key="amqj.logging.level" value="INFO"/>
+ <jvmarg value="-server"/>
+ </java>
+ </target>
+
+ <target name="profile" depends="compile" description="Run broker in Netbeans profiler">
+ <fail unless="netbeans.home">This target can only run inside the NetBeans IDE.</fail>
+ <nbprofiledirect>
+ <classpath refid="amqp.classpath"/>
+ </nbprofiledirect>
+
+ <java classpathref="amqp.classpath" fork="yes" classname="org.apache.qpid.server.Main"
+ dir="${profiler.session.working.dir}"
+ jvm="${profiler.info.jvm}">
+ <sysproperty key="amqj.logging.level" value="INFO"/>
+ <jvmarg value="${profiler.info.jvmargs.agent}"/>
+ <jvmarg line="${profiler.info.jvmargs}"/>
+ <jvmarg value="-server"/>
+ <jvmarg value="-Xmx512m"/>
+ <env key="Path" path="${profiler.info.agentpath}:${env.Path}"/>
+ </java>
+ </target>
+
+ <target name="profile-single" depends="compile" description="Profile File">
+ <fail unless="netbeans.home">This target can only run inside the NetBeans IDE.</fail>
+ <nbprofile classname="${profile.class}">
+ <classpath refid="amqp.classpath"/>
+ </nbprofile>
+ </target>
+</project>
diff --git a/java/broker/etc/config.xml b/java/broker/etc/config.xml
new file mode 100644
index 0000000000..531054dd86
--- /dev/null
+++ b/java/broker/etc/config.xml
@@ -0,0 +1,90 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ -
+ - Copyright (c) 2006 The Apache Software Foundation
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+ -
+ -->
+<broker>
+ <prefix>${QPID_HOME}</prefix>
+ <work>${prefix}/work</work>
+ <conf>${prefix}/etc</conf>
+ <connector>
+ <ssl>false</ssl>
+ <nonssl>true</nonssl>
+ <transport>nio</transport>
+ <port>5672</port>
+ <sslport>8672</sslport>
+ <socketReceiveBuffer>32768</socketReceiveBuffer>
+ <socketSendBuffer>32768</socketSendBuffer>
+ </connector>
+ <management>
+ <enabled>true</enabled>
+ </management>
+ <advanced>
+ <filterchain enableExecutorPool="true"/>
+ <enablePooledAllocator>false</enablePooledAllocator>
+ <enableDirectBuffers>false</enableDirectBuffers>
+ <framesize>65535</framesize>
+ </advanced>
+ <security>
+ <principal-databases>
+ <principal-database>
+ <name>passwordfile</name>
+ <class>org.apache.qpid.server.security.auth.PasswordFilePrincipalDatabase</class>
+ <attributes>
+ <attribute>
+ <name>passwordFile</name>
+ <value>${conf}/passwd</value>
+ </attribute>
+ </attributes>
+ </principal-database>
+ </principal-databases>
+ <sasl>
+ <mechanisms>
+ <mechanism>
+ <initialiser>
+ <class>org.apache.qpid.server.security.auth.CRAMMD5Initialiser</class>
+ <principal-database>passwordfile</principal-database>
+ </initialiser>
+ </mechanism>
+ <mechanism>
+ <initialiser>
+ <class>org.apache.qpid.server.security.auth.amqplain.AmqPlainInitialiser</class>
+ <principal-database>passwordfile</principal-database>
+ </initialiser>
+ </mechanism>-->
+ <mechanism>
+ <initialiser>
+ <class>org.apache.qpid.server.security.auth.plain.PlainInitialiser</class>
+ <principal-database>passwordfile</principal-database>
+ </initialiser>
+ </mechanism>
+ </mechanisms>
+ </sasl>
+ </security>
+ <heartbeat>
+ <delay>0</delay>
+ <timeoutFactor>2.0</timeoutFactor>
+ </heartbeat>
+ <queue>
+ <auto_register>true</auto_register>
+ </queue>
+ <store>
+ <!--<class>org.apache.qpid.server.store.MemoryMessageStore</class>-->
+ <class>org.apache.qpid.server.store.berkeleydb.BDBMessageStore</class>
+ <environment-path>${work}/bdb</environment-path>
+ </store>
+ <virtualhosts>${conf}/virtualhosts.xml</virtualhosts>
+</broker>
diff --git a/java/broker/etc/log4j.xml b/java/broker/etc/log4j.xml
new file mode 100644
index 0000000000..a4a154cca7
--- /dev/null
+++ b/java/broker/etc/log4j.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0"?>
+<!--
+ -
+ - Copyright (c) 2006 The Apache Software Foundation
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+ -
+ -->
+<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
+ <appender name="FileAppender" class="org.apache.log4j.FileAppender">
+ <param name="File" value="${QPID_HOME}/log/qpid.log"/>
+ <param name="Append" value="false"/>
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%t %-5p %c{2} - %m%n"/>
+ </layout>
+ </appender>
+
+ <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender">
+
+ <layout class="org.apache.log4j.PatternLayout">
+ <param name="ConversionPattern" value="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>
+ </layout>
+ </appender>
+
+ <!--<category name="org.apache.qpid.server.store">
+ <priority value="debug"/>
+ </category>-->
+
+ <root>
+ <priority value="info"/>
+ <appender-ref ref="STDOUT"/>
+ <appender-ref ref="FileAppender"/>
+ </root>
+</log4j:configuration>
diff --git a/java/broker/etc/passwd b/java/broker/etc/passwd
new file mode 100644
index 0000000000..9e06cab884
--- /dev/null
+++ b/java/broker/etc/passwd
@@ -0,0 +1 @@
+guest:guest
diff --git a/java/broker/etc/qpid-server.conf b/java/broker/etc/qpid-server.conf
new file mode 100644
index 0000000000..494dae6534
--- /dev/null
+++ b/java/broker/etc/qpid-server.conf
@@ -0,0 +1,22 @@
+#
+# Copyright (c) 2006 The Apache Software Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+QPID_LIBS=$QPID_HOME/lib/broker-launch.jar:$QPID_HOME/lib/bdbstore-launch.jar
+
+export JAVA=java \
+ JAVA_VM=-server \
+ JAVA_MEM=-Xmx1024m \
+ CLASSPATH=$QPID_LIBS
diff --git a/java/broker/etc/virtualhosts.xml b/java/broker/etc/virtualhosts.xml
new file mode 100644
index 0000000000..1f4f5e4d6b
--- /dev/null
+++ b/java/broker/etc/virtualhosts.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+ -
+ - Copyright (c) 2006 The Apache Software Foundation
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+ -
+ -->
+<virtualhosts>
+ <virtualhost>
+ <path>/development</path>
+ <bind>direct://amq.direct//queue</bind>
+ <bind>direct://amq.direct//ping</bind>
+ </virtualhost>
+</virtualhosts>
diff --git a/java/broker/src/log4j.properties b/java/broker/src/log4j.properties
new file mode 100644
index 0000000000..3ff6f0b581
--- /dev/null
+++ b/java/broker/src/log4j.properties
@@ -0,0 +1,6 @@
+log4j.rootCategory=${amqj.logging.level}, console
+
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+log4j.appender.console.Threshold=info
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern=%t %d %p [%c{4}] %m%n
diff --git a/java/broker/src/org/apache/qpid/server/AMQChannel.java b/java/broker/src/org/apache/qpid/server/AMQChannel.java
new file mode 100644
index 0000000000..8dc4626c46
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/AMQChannel.java
@@ -0,0 +1,702 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.BasicPublishBody;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.server.exchange.MessageRouter;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.txn.TxnBuffer;
+import org.apache.qpid.server.txn.TxnOp;
+import org.apache.qpid.server.management.Managable;
+import org.apache.qpid.server.management.ManagedObject;
+import org.apache.qpid.server.management.DefaultManagedObject;
+
+import javax.management.ObjectName;
+import javax.management.MalformedObjectNameException;
+import javax.management.JMException;
+import javax.management.MBeanException;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
+import java.util.Map;
+import java.util.TreeMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class AMQChannel implements Managable
+{
+ public static final int DEFAULT_PREFETCH = 5000;
+
+ private static final Logger _log = Logger.getLogger(AMQChannel.class);
+
+ private final int _channelId;
+
+ private final String _channelName;
+
+ private boolean _transactional;
+
+ private long _prefetchCount;
+
+ /**
+ * The delivery tag is unique per channel. This is pre-incremented before putting into the deliver frame so that
+ * value of this represents the <b>last</b> tag sent out
+ */
+ private long _deliveryTag;
+
+ /**
+ * A channel has a default queue (the last declared) that is used when no queue name is
+ * explictily set
+ */
+ private AMQQueue _defaultQueue;
+
+ /**
+ * This tag is unique per subscription to a queue. The server returns this in response to a
+ * basic.consume request.
+ */
+ private int _consumerTag = 0;
+
+ /**
+ * The current message - which may be partial in the sense that not all frames have been received yet -
+ * which has been received by this channel. As the frames are received the message gets updated and once all
+ * frames have been received the message can then be routed.
+ */
+ private AMQMessage _currentMessage;
+
+ /**
+ * Maps from consumer tag to queue instance. Allows us to unsubscribe from a queue.
+ */
+ private final Map<String, AMQQueue> _consumerTag2QueueMap = new TreeMap<String, AMQQueue>();
+
+ private final MessageStore _messageStore;
+
+ private final Object _unacknowledgedMessageMapLock = new Object();
+
+ private Map<Long, UnacknowledgedMessage> _unacknowledgedMessageMap = new LinkedHashMap<Long, UnacknowledgedMessage>(DEFAULT_PREFETCH);
+
+ private final AtomicBoolean _suspended = new AtomicBoolean(false);
+
+ private final MessageRouter _exchanges;
+
+ private final TxnBuffer _txnBuffer;
+
+ private final AMQChannelMBean _managedObject;
+
+ public ManagedObject getManagedObject()
+ {
+ return _managedObject;
+ }
+
+ /**
+ * MBean interface for the implementation AMQChannelMBean
+ */
+ public interface AMQChannelMBeanMBean extends ManagedChannel
+ {
+
+ }
+
+ /**
+ * AMQChannelMBean. It implements the management interface exposed for
+ * monitoring and managing the channel.
+ */
+ public final class AMQChannelMBean extends DefaultManagedObject implements AMQChannelMBeanMBean
+ {
+ public AMQChannelMBean()
+ {
+ super(ManagedChannel.class, ManagedChannel.TYPE);
+ }
+
+ public String getObjectInstanceName()
+ {
+ return _channelName;
+ }
+
+ public boolean isTransactional()
+ {
+ return _transactional;
+ }
+
+ public int getUnacknowledgedMessageCount()
+ {
+ return _unacknowledgedMessageMap.size();
+ }
+
+ public void commitTransactions() throws JMException
+ {
+ try
+ {
+ if (_transactional)
+ {
+ _txnBuffer.commit();
+ }
+ }
+ catch(AMQException ex)
+ {
+ throw new MBeanException(ex, ex.toString());
+ }
+ }
+
+ public void rollbackTransactions() throws JMException
+ {
+ if (_transactional)
+ {
+ synchronized (_txnBuffer)
+ {
+ try
+ {
+ _txnBuffer.rollback();
+ }
+ catch(AMQException ex)
+ {
+ throw new MBeanException(ex, ex.toString());
+ }
+ }
+ }
+ }
+
+ } // End of MBean class
+
+
+ public static class UnacknowledgedMessage
+ {
+ public final AMQMessage message;
+ public final String consumerTag;
+ public AMQQueue queue;
+
+ public UnacknowledgedMessage(AMQQueue queue, AMQMessage message, String consumerTag)
+ {
+ this.queue = queue;
+ this.message = message;
+ this.consumerTag = consumerTag;
+ }
+
+ private void discard() throws AMQException
+ {
+ if (queue != null)
+ {
+ message.dequeue(queue);
+ }
+ message.decrementReference();
+ }
+ }
+
+ public AMQChannel(int channelId, MessageStore messageStore, MessageRouter exchanges)
+ throws AMQException
+ {
+ _channelId = channelId;
+ _channelName = _channelId + "-" + this.hashCode();
+ _prefetchCount = DEFAULT_PREFETCH;
+ _messageStore = messageStore;
+ _exchanges = exchanges;
+ _txnBuffer = new TxnBuffer(_messageStore);
+
+ _managedObject = new AMQChannelMBean();
+ _managedObject.register();
+ }
+
+ public int getChannelId()
+ {
+ return _channelId;
+ }
+
+ public boolean isTransactional()
+ {
+ return _transactional;
+ }
+
+ public void setTransactional(boolean transactional)
+ {
+ _transactional = transactional;
+ }
+
+ public long getPrefetchCount()
+ {
+ return _prefetchCount;
+ }
+
+ public void setPrefetchCount(long prefetchCount)
+ {
+ _prefetchCount = prefetchCount;
+ }
+
+ public void setPublishFrame(BasicPublishBody publishBody, AMQProtocolSession publisher) throws AMQException
+ {
+ _currentMessage = new AMQMessage(_messageStore, publishBody);
+ _currentMessage.setPublisher(publisher);
+ }
+
+ public void publishContentHeader(ContentHeaderBody contentHeaderBody)
+ throws AMQException
+ {
+ if (_currentMessage == null)
+ {
+ throw new AMQException("Received content header without previously receiving a BasicDeliver frame");
+ }
+ else
+ {
+ _currentMessage.setContentHeaderBody(contentHeaderBody);
+ // check and route if header says body length is zero
+ if (contentHeaderBody.bodySize == 0)
+ {
+ routeCurrentMessage();
+ }
+ }
+ }
+
+ public void publishContentBody(ContentBody contentBody)
+ throws AMQException
+ {
+ if (_currentMessage == null)
+ {
+ throw new AMQException("Received content body without previously receiving a JmsPublishBody");
+ }
+ if (_currentMessage.getContentHeaderBody() == null)
+ {
+ throw new AMQException("Received content body without previously receiving a content header");
+ }
+
+ _currentMessage.addContentBodyFrame(contentBody);
+ if (_currentMessage.isAllContentReceived())
+ {
+ routeCurrentMessage();
+ }
+ }
+
+ protected void routeCurrentMessage() throws AMQException
+ {
+ if (_transactional)
+ {
+ //don't route this until commit
+ _txnBuffer.enlist(new Publish(_currentMessage));
+ _currentMessage = null;
+ }
+ else
+ {
+ _exchanges.routeContent(_currentMessage);
+ _currentMessage.decrementReference();
+ _currentMessage = null;
+ }
+ }
+
+ public long getNextDeliveryTag()
+ {
+ return ++_deliveryTag;
+ }
+
+ public int getNextConsumerTag()
+ {
+ return ++_consumerTag;
+ }
+
+ /**
+ * Subscribe to a queue. We register all subscriptions in the channel so that
+ * if the channel is closed we can clean up all subscriptions, even if the
+ * client does not explicitly unsubscribe from all queues.
+ *
+ * @param tag the tag chosen by the client (if null, server will generate one)
+ * @param queue the queue to subscribe to
+ * @param session the protocol session of the subscriber
+ * @return the consumer tag. This is returned to the subscriber and used in
+ * subsequent unsubscribe requests
+ * @throws ConsumerTagNotUniqueException if the tag is not unique
+ * @throws AMQException if something goes wrong
+ */
+ public String subscribeToQueue(String tag, AMQQueue queue, AMQProtocolSession session, boolean acks) throws AMQException, ConsumerTagNotUniqueException
+ {
+ if (tag == null)
+ {
+ tag = "sgen_" + getNextConsumerTag();
+ }
+ if (_consumerTag2QueueMap.containsKey(tag))
+ {
+ throw new ConsumerTagNotUniqueException();
+ }
+
+ queue.registerProtocolSession(session, _channelId, tag, acks);
+ _consumerTag2QueueMap.put(tag, queue);
+ return tag;
+ }
+
+
+ public void unsubscribeConsumer(AMQProtocolSession session, String consumerTag) throws AMQException
+ {
+ AMQQueue q = _consumerTag2QueueMap.remove(consumerTag);
+ if (q != null)
+ {
+ q.unregisterProtocolSession(session, _channelId, consumerTag);
+ }
+ else
+ {
+ throw new AMQException(_log, "Consumer tag " + consumerTag + " not known to channel " +
+ _channelId);
+ }
+ }
+
+ /**
+ * Called from the protocol session to close this channel and clean up.
+ *
+ * @throws AMQException if there is an error during closure
+ */
+ public void close(AMQProtocolSession session) throws AMQException
+ {
+ if (_transactional)
+ {
+ synchronized (_txnBuffer)
+ {
+ _txnBuffer.rollback();//releases messages
+ }
+ }
+ unsubscribeAllConsumers(session);
+ requeue();
+ _managedObject.unregister();
+ }
+
+ private void unsubscribeAllConsumers(AMQProtocolSession session) throws AMQException
+ {
+ _log.info("Unsubscribing all consumers on channel " + toString());
+ for (Map.Entry<String, AMQQueue> me : _consumerTag2QueueMap.entrySet())
+ {
+ me.getValue().unregisterProtocolSession(session, _channelId, me.getKey());
+ }
+ _consumerTag2QueueMap.clear();
+ }
+
+ /**
+ * Add a message to the channel-based list of unacknowledged messages
+ *
+ * @param message
+ * @param deliveryTag
+ * @param queue
+ */
+ public void addUnacknowledgedMessage(AMQMessage message, long deliveryTag, String consumerTag, AMQQueue queue)
+ {
+ synchronized (_unacknowledgedMessageMapLock)
+ {
+ _unacknowledgedMessageMap.put(deliveryTag, new UnacknowledgedMessage(queue, message, consumerTag));
+ checkSuspension();
+ }
+ }
+
+ /**
+ * Called to attempt re-enqueue all outstanding unacknowledged messages on the channel.
+ * May result in delivery to this same channel or to other subscribers.
+ */
+ public void requeue() throws AMQException
+ {
+ // we must create a new map since all the messages will get a new delivery tag when they are redelivered
+ Map<Long, UnacknowledgedMessage> currentList;
+ synchronized (_unacknowledgedMessageMapLock)
+ {
+ currentList = _unacknowledgedMessageMap;
+ _unacknowledgedMessageMap = new LinkedHashMap<Long, UnacknowledgedMessage>(DEFAULT_PREFETCH);
+ }
+
+ for (UnacknowledgedMessage unacked : currentList.values())
+ {
+ if (unacked.queue != null)
+ {
+ unacked.queue.deliver(unacked.message);
+ }
+ }
+ }
+
+ /**
+ * Called to resend all outstanding unacknowledged messages to this same channel.
+ */
+ public void resend(AMQProtocolSession session)
+ {
+ //messages go to this channel
+ synchronized (_unacknowledgedMessageMapLock)
+ {
+ for (Map.Entry<Long, UnacknowledgedMessage> entry : _unacknowledgedMessageMap.entrySet())
+ {
+ long deliveryTag = entry.getKey();
+ String consumerTag = entry.getValue().consumerTag;
+ AMQMessage msg = entry.getValue().message;
+
+ session.writeFrame(msg.getDataBlock(_channelId, consumerTag, deliveryTag));
+ }
+ }
+ }
+
+ /**
+ * Callback indicating that a queue has been deleted. We must update the structure of unacknowledged
+ * messages to remove the queue reference and also decrement any message reference counts, without
+ * actually removing the item sine we may get an ack for a delivery tag that was generated from the
+ * deleted queue.
+ *
+ * @param queue
+ */
+ public void queueDeleted(AMQQueue queue)
+ {
+ synchronized (_unacknowledgedMessageMapLock)
+ {
+ for (Map.Entry<Long, UnacknowledgedMessage> unacked : _unacknowledgedMessageMap.entrySet())
+ {
+ final UnacknowledgedMessage unackedMsg = unacked.getValue();
+ // we can compare the reference safely in this case
+ if (unackedMsg.queue == queue)
+ {
+ unackedMsg.queue = null;
+ try
+ {
+ unackedMsg.message.decrementReference();
+ }
+ catch (AMQException e)
+ {
+ _log.error("Error decrementing ref count on message " + unackedMsg.message.getMessageId() + ": " +
+ e, e);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Acknowledge one or more messages.
+ *
+ * @param deliveryTag the last delivery tag
+ * @param multiple if true will acknowledge all messages up to an including the delivery tag. if false only
+ * acknowledges the single message specified by the delivery tag
+ * @throws AMQException if the delivery tag is unknown (e.g. not outstanding) on this channel
+ */
+ public void acknowledgeMessage(long deliveryTag, boolean multiple) throws AMQException
+ {
+ if (_transactional)
+ {
+ //don't handle this until commit
+ _txnBuffer.enlist(new Ack(deliveryTag, multiple));
+ }
+ else
+ {
+ handleAcknowledgement(deliveryTag, multiple);
+ }
+ }
+
+ private void handleAcknowledgement(long deliveryTag, boolean multiple) throws AMQException
+ {
+ if (multiple)
+ {
+ LinkedList<UnacknowledgedMessage> acked = new LinkedList<UnacknowledgedMessage>();
+ synchronized (_unacknowledgedMessageMapLock)
+ {
+ if (deliveryTag == 0)
+ {
+ //Spec 2.1.6.11 ... If the multiple field is 1, and the delivery tag is zero, tells the server to acknowledge all outstanding mesages.
+ _log.info("Multiple ack on delivery tag 0. ACKing all messages. Current count:" + _unacknowledgedMessageMap.size());
+ acked = new LinkedList<UnacknowledgedMessage>(_unacknowledgedMessageMap.values());
+ _unacknowledgedMessageMap.clear();
+ }
+ else
+ {
+ if (!_unacknowledgedMessageMap.containsKey(deliveryTag))
+ {
+ throw new AMQException("Multiple ack on delivery tag " + deliveryTag + " not known for channel");
+ }
+ Iterator<Map.Entry<Long, UnacknowledgedMessage>> i = _unacknowledgedMessageMap.entrySet().iterator();
+ while (i.hasNext())
+ {
+ Map.Entry<Long, UnacknowledgedMessage> unacked = i.next();
+ i.remove();
+ acked.add(unacked.getValue());
+ if (unacked.getKey() == deliveryTag)
+ {
+ break;
+ }
+ }
+ }
+ }
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Received multiple ack for delivery tag " + deliveryTag + ". Removing " +
+ acked.size() + " items.");
+ }
+
+ for (UnacknowledgedMessage msg : acked)
+ {
+ msg.discard();
+ }
+
+ }
+ else
+ {
+ UnacknowledgedMessage msg;
+ synchronized (_unacknowledgedMessageMapLock)
+ {
+ msg = _unacknowledgedMessageMap.remove(deliveryTag);
+ }
+ if (msg == null)
+ {
+ throw new AMQException("Single ack on delivery tag " + deliveryTag + " not known for channel:" + _channelId);
+ }
+ msg.discard();
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Received non-multiple ack for messaging with delivery tag " + deliveryTag);
+ }
+ }
+
+ checkSuspension();
+ }
+
+ /**
+ * Used only for testing purposes.
+ *
+ * @return the map of unacknowledged messages
+ */
+ public Map<Long, UnacknowledgedMessage> getUnacknowledgedMessageMap()
+ {
+ return _unacknowledgedMessageMap;
+ }
+
+ private void checkSuspension()
+ {
+ boolean suspend;
+ //noinspection SynchronizeOnNonFinalField
+ synchronized (_unacknowledgedMessageMapLock)
+ {
+ suspend = _unacknowledgedMessageMap.size() >= _prefetchCount;
+ }
+ setSuspended(suspend);
+ }
+
+ public void setSuspended(boolean suspended)
+ {
+ boolean wasSuspended = _suspended.getAndSet(suspended);
+ if (wasSuspended != suspended)
+ {
+ if (wasSuspended)
+ {
+ _log.info("Unsuspending channel " + this);
+ //may need to deliver queued messages
+ for (AMQQueue q : _consumerTag2QueueMap.values())
+ {
+ q.deliverAsync();
+ }
+ }
+ else
+ {
+ _log.info("Suspending channel " + this);
+ }
+ }
+ }
+
+ public boolean isSuspended()
+ {
+ return _suspended.get();
+ }
+
+ public void commit() throws AMQException
+ {
+ _txnBuffer.commit();
+ }
+
+ public void rollback() throws AMQException
+ {
+ //need to protect rollback and close from each other...
+ synchronized (_txnBuffer)
+ {
+ _txnBuffer.rollback();
+ }
+ }
+
+ public String toString()
+ {
+ StringBuilder sb = new StringBuilder(30);
+ sb.append("Channel: id ").append(_channelId).append(", transaction mode: ").append(_transactional);
+ sb.append(", prefetch count: ").append(_prefetchCount);
+ return sb.toString();
+ }
+
+ public ObjectName getObjectName()
+ throws MalformedObjectNameException
+ {
+ StringBuilder sb = new StringBuilder(30);
+ sb.append("Channel:id=").append(_channelId);
+ sb.append(",transaction mode=").append(_transactional);
+ return new ObjectName(sb.toString());
+ }
+
+ public void setDefaultQueue(AMQQueue queue)
+ {
+ _defaultQueue = queue;
+ }
+
+ public AMQQueue getDefaultQueue()
+ {
+ return _defaultQueue;
+ }
+
+ private class Ack implements TxnOp
+ {
+ private final long _msgId;
+ private final boolean _multi;
+
+ Ack(long msgId, boolean multi)
+ {
+ _msgId = msgId;
+ _multi = multi;
+ }
+
+ public void commit() throws AMQException
+ {
+ handleAcknowledgement(_msgId, _multi);
+ }
+
+ public void rollback()
+ {
+ }
+ }
+
+ //TODO:
+ //implement a scheme whereby messages can be stored on disk
+ //until commit, then reloaded...
+ private class Publish implements TxnOp
+ {
+ private final AMQMessage _msg;
+
+ Publish(AMQMessage msg)
+ {
+ _msg = msg;
+ }
+
+ public void commit() throws AMQException
+ {
+ _exchanges.routeContent(_msg);
+ _msg.decrementReference();
+ }
+
+ public void rollback()
+ {
+ try
+ {
+ _msg.decrementReference();
+ }
+ catch (AMQException e)
+ {
+ _log.error("Error rolling back a publish request: " + e, e);
+ }
+ }
+ }
+
+}
diff --git a/java/broker/src/org/apache/qpid/server/ConsumerTagNotUniqueException.java b/java/broker/src/org/apache/qpid/server/ConsumerTagNotUniqueException.java
new file mode 100644
index 0000000000..6a7e54bc45
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/ConsumerTagNotUniqueException.java
@@ -0,0 +1,22 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server;
+
+public class ConsumerTagNotUniqueException extends Exception
+{
+}
diff --git a/java/broker/src/org/apache/qpid/server/Main.java b/java/broker/src/org/apache/qpid/server/Main.java
new file mode 100644
index 0000000000..dee6f76334
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/Main.java
@@ -0,0 +1,612 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server;
+
+import org.apache.qpid.framing.ProtocolVersionList;
+import org.apache.qpid.pool.ReadWriteThreadModel;
+import org.apache.qpid.server.protocol.AMQPFastProtocolHandler;
+import org.apache.qpid.server.protocol.AMQPProtocolProvider;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.ConfigurationFileApplicationRegistry;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+import org.apache.qpid.server.transport.ConnectorConfiguration;
+import org.apache.qpid.server.configuration.VirtualHostConfiguration;
+import org.apache.qpid.server.management.DefaultManagedObject;
+import org.apache.qpid.server.management.ManagedBroker;
+import org.apache.qpid.server.management.ManagedObject;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.exchange.ExchangeFactory;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.url.URLSyntaxException;
+import org.apache.commons.cli.*;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.log4j.BasicConfigurator;
+import org.apache.log4j.Logger;
+import org.apache.log4j.xml.DOMConfigurator;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IoAcceptor;
+import org.apache.mina.common.SimpleByteBufferAllocator;
+import org.apache.mina.transport.socket.nio.SocketAcceptorConfig;
+import org.apache.mina.transport.socket.nio.SocketSessionConfig;
+
+import javax.management.ObjectName;
+import javax.management.MalformedObjectNameException;
+import javax.management.JMException;
+import javax.management.MBeanException;
+import java.io.File;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.util.StringTokenizer;
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * Main entry point for AMQPD.
+ *
+ */
+public class Main implements ProtocolVersionList
+{
+ private static final Logger _logger = Logger.getLogger(Main.class);
+
+ private static final String DEFAULT_CONFIG_FILE = "etc/config.xml";
+
+ private static final String DEFAULT_LOG_CONFIG_FILENAME = "log4j.xml";
+
+ protected static class InitException extends Exception
+ {
+ InitException(String msg)
+ {
+ super(msg);
+ }
+ }
+
+ protected final Options options = new Options();
+ protected CommandLine commandLine;
+
+ protected Main(String[] args)
+ {
+ setOptions(options);
+ if (parseCommandline(args))
+ {
+ execute();
+ }
+ }
+
+ protected boolean parseCommandline(String[] args)
+ {
+ try
+ {
+ commandLine = new PosixParser().parse(options, args);
+ return true;
+ }
+ catch (ParseException e)
+ {
+ System.err.println("Error: " + e.getMessage());
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp("Qpid", options, true);
+ return false;
+ }
+ }
+
+ protected void setOptions(Options options)
+ {
+ Option help = new Option("h", "help", false, "print this message");
+ Option version = new Option("v", "version", false, "print the version information and exit");
+ Option configFile = OptionBuilder.withArgName("file").hasArg().withDescription("use given configuration file").
+ withLongOpt("config").create("c");
+ Option port = OptionBuilder.withArgName("port").hasArg().withDescription("listen on the specified port. Overrides any value in the config file").
+ withLongOpt("port").create("p");
+ Option bind = OptionBuilder.withArgName("bind").hasArg().withDescription("bind to the specified address. Overrides any value in the config file").
+ withLongOpt("bind").create("b");
+ Option logconfig = OptionBuilder.withArgName("logconfig").hasArg().withDescription("use the specified log4j xml configuration file. By " +
+ "default looks for a file named " + DEFAULT_LOG_CONFIG_FILENAME + " in the same directory as the configuration file").
+ withLongOpt("logconfig").create("l");
+ Option logwatchconfig = OptionBuilder.withArgName("logwatch").hasArg().withDescription("monitor the log file configuration file for changes. Units are seconds. " +
+ "Zero means do not check for changes.").withLongOpt("logwatch").create("w");
+
+ options.addOption(help);
+ options.addOption(version);
+ options.addOption(configFile);
+ options.addOption(logconfig);
+ options.addOption(logwatchconfig);
+ options.addOption(port);
+ options.addOption(bind);
+ }
+
+ protected void execute()
+ {
+ // note this understands either --help or -h. If an option only has a long name you can use that but if
+ // an option has a short name and a long name you must use the short name here.
+ if (commandLine.hasOption("h"))
+ {
+ HelpFormatter formatter = new HelpFormatter();
+ formatter.printHelp("Qpid", options, true);
+ }
+ else if (commandLine.hasOption("v"))
+ {
+ String ver = "Qpid 0.9.0.0";
+ String protocol = "AMQP version(s) [major.minor]: ";
+ for (int i=0; i<pv.length; i++)
+ {
+ if (i > 0)
+ protocol += ", ";
+ protocol += pv[i][PROTOCOL_MAJOR] + "." + pv[i][PROTOCOL_MINOR];
+ }
+ System.out.println(ver + " (" + protocol + ")");
+ }
+ else
+ {
+ try
+ {
+ startup();
+ }
+ catch (InitException e)
+ {
+ System.out.println(e.getMessage());
+ }
+ catch (ConfigurationException e)
+ {
+ System.out.println("Error configuring message broker: " + e);
+ e.printStackTrace();
+ }
+ catch (Exception e)
+ {
+ System.out.println("Error intialising message broker: " + e);
+ e.printStackTrace();
+ }
+ }
+ }
+
+
+ protected void startup() throws InitException, ConfigurationException, Exception
+ {
+ final String QpidHome = System.getProperty("QPID_HOME");
+ final File defaultConfigFile = new File(QpidHome, DEFAULT_CONFIG_FILE);
+ final File configFile = new File(commandLine.getOptionValue("c", defaultConfigFile.getPath()));
+ if (!configFile.exists())
+ {
+ String error = "File " + configFile + " could not be found. Check the file exists and is readable.";
+
+ if (QpidHome == null)
+ {
+ error = error + "\nNote: Qpid_HOME is not set.";
+ }
+
+ throw new InitException(error);
+ }
+ else
+ {
+ System.out.println("Using configuration file " + configFile.getAbsolutePath());
+ }
+
+ String logConfig = commandLine.getOptionValue("l");
+ String logWatchConfig = commandLine.getOptionValue("w", "0");
+ if (logConfig != null)
+ {
+ File logConfigFile = new File(logConfig);
+ configureLogging(logConfigFile, logWatchConfig);
+ }
+ else
+ {
+ File configFileDirectory = configFile.getParentFile();
+ File logConfigFile = new File(configFileDirectory, DEFAULT_LOG_CONFIG_FILENAME);
+ configureLogging(logConfigFile, logWatchConfig);
+ }
+
+ ApplicationRegistry.initialise(new ConfigurationFileApplicationRegistry(configFile));
+
+ _logger.info("Starting Qpid.AMQP broker");
+
+ ConnectorConfiguration connectorConfig = ApplicationRegistry.getInstance().
+ getConfiguredObject(ConnectorConfiguration.class);
+
+ ByteBuffer.setUseDirectBuffers(connectorConfig.enableDirectBuffers);
+
+ // the MINA default is currently to use the pooled allocator although this may change in future
+ // once more testing of the performance of the simple allocator has been done
+ if (!connectorConfig.enablePooledAllocator)
+ {
+ ByteBuffer.setAllocator(new SimpleByteBufferAllocator());
+ }
+
+ int port = connectorConfig.port;
+
+ String portStr = commandLine.getOptionValue("p");
+ if (portStr != null)
+ {
+ try
+ {
+ port = Integer.parseInt(portStr);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new InitException("Invalid port: " + portStr);
+ }
+ }
+
+ String VIRTUAL_HOSTS = "virtualhosts";
+
+ Object virtualHosts = ApplicationRegistry.getInstance().getConfiguration().getProperty(VIRTUAL_HOSTS);
+
+ if (virtualHosts != null)
+ {
+ if (virtualHosts instanceof Collection)
+ {
+ int totalVHosts = ((Collection) virtualHosts).size();
+ for (int vhost = 0; vhost < totalVHosts; vhost++)
+ {
+ setupVirtualHosts(configFile.getParent() , (String)((List)virtualHosts).get(vhost));
+ }
+ }
+ else
+ {
+ setupVirtualHosts(configFile.getParent() , (String)virtualHosts);
+ }
+ }
+ bind(port, connectorConfig);
+
+ createAndRegisterBrokerMBean();
+ }
+
+ protected void setupVirtualHosts(String configFileParent, String configFilePath) throws ConfigurationException, AMQException, URLSyntaxException
+ {
+ String configVar = "${conf}";
+
+ if (configFilePath.startsWith(configVar))
+ {
+ configFilePath = configFileParent + configFilePath.substring(configVar.length());
+ }
+
+ if (configFilePath.indexOf(".xml") != -1 )
+ {
+ VirtualHostConfiguration vHostConfig = new VirtualHostConfiguration(configFilePath);
+ vHostConfig.performBindings();
+ }
+ else
+ {
+ // the virtualhosts value is a path. Search it for XML files.
+
+ File virtualHostDir = new File(configFilePath);
+
+ String[] fileNames = virtualHostDir.list();
+
+ for (int each=0; each < fileNames.length; each++)
+ {
+ if (fileNames[each].endsWith(".xml"))
+ {
+ VirtualHostConfiguration vHostConfig = new VirtualHostConfiguration(configFilePath+"/"+fileNames[each]);
+ vHostConfig.performBindings();
+ }
+ }
+ }
+ }
+
+ protected void bind(int port, ConnectorConfiguration connectorConfig)
+ {
+ String bindAddr = commandLine.getOptionValue("b");
+ if (bindAddr == null)
+ {
+ bindAddr = connectorConfig.bindAddress;
+ }
+
+ try
+ {
+ //IoAcceptor acceptor = new SocketAcceptor(connectorConfig.processors);
+ IoAcceptor acceptor = connectorConfig.createAcceptor();
+ SocketAcceptorConfig sconfig = (SocketAcceptorConfig) acceptor.getDefaultConfig();
+ SocketSessionConfig sc = (SocketSessionConfig) sconfig.getSessionConfig();
+
+ sc.setReceiveBufferSize(connectorConfig.socketReceiveBufferSize);
+ sc.setSendBufferSize(connectorConfig.socketWriteBuferSize);
+ sc.setTcpNoDelay(connectorConfig.tcpNoDelay);
+
+ // if we do not use the executor pool threading model we get the default leader follower
+ // implementation provided by MINA
+ if (connectorConfig.enableExecutorPool)
+ {
+ sconfig.setThreadModel(new ReadWriteThreadModel());
+ }
+
+ if (connectorConfig.enableNonSSL)
+ {
+ AMQPFastProtocolHandler handler = new AMQPProtocolProvider().getHandler();
+ InetSocketAddress bindAddress;
+ if (bindAddr.equals("wildcard"))
+ {
+ bindAddress = new InetSocketAddress(port);
+ }
+ else
+ {
+ bindAddress = new InetSocketAddress(InetAddress.getByAddress(parseIP(bindAddr)), port);
+ }
+ acceptor.bind(bindAddress, handler, sconfig);
+ _logger.info("Qpid.AMQP listening on non-SSL address " + bindAddress);
+ }
+
+ if (connectorConfig.enableSSL)
+ {
+ AMQPFastProtocolHandler handler = new AMQPProtocolProvider().getHandler();
+ handler.setUseSSL(true);
+ try
+ {
+ acceptor.bind(new InetSocketAddress(connectorConfig.sslPort),
+ handler, sconfig);
+ _logger.info("Qpid.AMQP listening on SSL port " + connectorConfig.sslPort);
+ }
+ catch (IOException e)
+ {
+ _logger.error("Unable to listen on SSL port: " + e, e);
+ }
+ }
+ }
+ catch (Exception e)
+ {
+ _logger.error("Unable to bind service to registry: " + e, e);
+ }
+ }
+
+ public static void main(String[] args)
+ {
+
+ new Main(args);
+ }
+
+ private byte[] parseIP(String address) throws Exception
+ {
+ StringTokenizer tokenizer = new StringTokenizer(address, ".");
+ byte[] ip = new byte[4];
+ int index = 0;
+ while (tokenizer.hasMoreTokens())
+ {
+ String token = tokenizer.nextToken();
+ try
+ {
+ ip[index++] = Byte.parseByte(token);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new Exception("Error parsing IP address: " + address, e);
+ }
+ }
+ if (index != 4)
+ {
+ throw new Exception("Invalid IP address: " + address);
+ }
+ return ip;
+ }
+
+ private void configureLogging(File logConfigFile, String logWatchConfig)
+ {
+ int logWatchTime = 0;
+ try
+ {
+ logWatchTime = Integer.parseInt(logWatchConfig);
+ }
+ catch (NumberFormatException e)
+ {
+ System.err.println("Log watch configuration value of " + logWatchConfig + " is invalid. Must be " +
+ "a non-negative integer. Using default of zero (no watching configured");
+ }
+ if (logConfigFile.exists() && logConfigFile.canRead())
+ {
+ System.out.println("Configuring logger using configuration file " + logConfigFile.getAbsolutePath());
+ if (logWatchTime > 0)
+ {
+ System.out.println("log file " + logConfigFile.getAbsolutePath() + " will be checked for changes every " +
+ logWatchTime + " seconds");
+ // log4j expects the watch interval in milliseconds
+ DOMConfigurator.configureAndWatch(logConfigFile.getAbsolutePath(), logWatchTime * 1000);
+ }
+ else
+ {
+ DOMConfigurator.configure(logConfigFile.getAbsolutePath());
+ }
+ }
+ else
+ {
+ System.err.println("Logging configuration error: unable to read file " + logConfigFile.getAbsolutePath());
+ System.err.println("Using basic log4j configuration");
+ BasicConfigurator.configure();
+ }
+ }
+
+ private void createAndRegisterBrokerMBean()
+ throws AMQException
+ {
+ new AMQBrokerManager().register();
+ }
+
+ /**
+ * MBean interface for the implementation AMQBrokerManager.
+ */
+ public interface AMQBrokerManagerMBean extends ManagedBroker
+ {
+
+ }
+ /**
+ * AMQPBrokerMBean implements the broker management interface and exposes the
+ * Broker level management features like creating and deleting exchanges and queue.
+ */
+ private final class AMQBrokerManager extends DefaultManagedObject
+ implements AMQBrokerManagerMBean
+ {
+ private final QueueRegistry _queueRegistry;
+ private final ExchangeRegistry _exchangeRegistry;
+ private final ExchangeFactory _exchangeFactory;
+ private final MessageStore _messageStore;
+
+ protected AMQBrokerManager()
+ {
+ super(ManagedBroker.class, ManagedBroker.TYPE);
+
+ IApplicationRegistry appRegistry = ApplicationRegistry.getInstance();
+ _queueRegistry = appRegistry.getQueueRegistry();
+ _exchangeRegistry = appRegistry.getExchangeRegistry();
+ _exchangeFactory = ApplicationRegistry.getInstance().getExchangeFactory();
+ _messageStore = ApplicationRegistry.getInstance().getMessageStore();
+ }
+
+ public String getObjectInstanceName()
+ {
+ return this.getClass().getName();
+ }
+
+ /**
+ * Creates new exchange and registers it with the registry.
+ * @param exchangeName
+ * @param type
+ * @param durable
+ * @param autoDelete
+ * @throws JMException
+ */
+ public void createNewExchange(String exchangeName,
+ String type,
+ boolean durable,
+ boolean autoDelete)
+ throws JMException
+ {
+ try
+ {
+ synchronized(_exchangeRegistry)
+ {
+ Exchange exchange = _exchangeRegistry.getExchange(exchangeName);
+
+ if (exchange == null)
+ {
+ exchange = _exchangeFactory.createExchange(exchangeName,
+ type, //eg direct
+ durable,
+ autoDelete,
+ 0); //ticket no
+ _exchangeRegistry.registerExchange(exchange);
+ }
+ else
+ {
+ throw new JMException("The exchange \"" + exchangeName + "\" already exists.");
+ }
+ }
+ }
+ catch(AMQException ex)
+ {
+ _logger.error("Error in creating exchange " + exchangeName, ex);
+ throw new MBeanException(ex, ex.toString());
+ }
+ }
+
+ /**
+ * Unregisters the exchange from registry.
+ * @param exchangeName
+ * @throws JMException
+ */
+ public void unregisterExchange(String exchangeName)
+ throws JMException
+ {
+ boolean inUse = false;
+ // TODO
+ // Check if the exchange is in use.
+ // Check if there are queue-bindings with the exchnage and unregister
+ // when there are no bindings.
+ try
+ {
+ _exchangeRegistry.unregisterExchange(exchangeName, false);
+ }
+ catch(AMQException ex)
+ {
+ _logger.error("Error in unregistering exchange " + exchangeName, ex);
+ throw new MBeanException(ex, ex.toString());
+ }
+ }
+
+ /**
+ * Creates a new queue and registers it with the registry and puts it
+ * in persistance storage if durable queue.
+ * @param queueName
+ * @param durable
+ * @param owner
+ * @param autoDelete
+ * @throws JMException
+ */
+ public void createQueue(String queueName,
+ boolean durable,
+ String owner,
+ boolean autoDelete)
+ throws JMException
+ {
+ AMQQueue queue = _queueRegistry.getQueue(queueName);
+ if (queue == null)
+ {
+ try
+ {
+ queue = new AMQQueue(queueName, durable, owner, autoDelete, _queueRegistry);
+ if (queue.isDurable() && !queue.isAutoDelete())
+ {
+ _messageStore.createQueue(queue);
+ }
+ _queueRegistry.registerQueue(queue);
+ }
+ catch (AMQException ex)
+ {
+ _logger.error("Error in creating queue " + queueName, ex);
+ throw new MBeanException(ex, ex.toString());
+ }
+ }
+ else
+ {
+ throw new JMException("The queue \"" + queueName + "\" already exists.");
+ }
+ }
+
+ /**
+ * Deletes the queue from queue registry and persistant storage.
+ * @param queueName
+ * @throws JMException
+ */
+ public void deleteQueue(String queueName) throws JMException
+ {
+ AMQQueue queue = _queueRegistry.getQueue(queueName);
+ if (queue == null)
+ {
+ throw new JMException("The Queue " + queueName + " is not a registerd queue.");
+ }
+
+ try
+ {
+ queue.delete();
+ _messageStore.removeQueue(queueName);
+
+ }
+ catch (AMQException ex)
+ {
+ throw new MBeanException(ex, ex.toString());
+ }
+ }
+
+ public ObjectName getObjectName() throws MalformedObjectNameException
+ {
+ StringBuffer objectName = new StringBuffer(ManagedObject.DOMAIN);
+ objectName.append(":type=").append(getType());
+
+ return new ObjectName(objectName.toString());
+ }
+ } // End of MBean class
+}
diff --git a/java/broker/src/org/apache/qpid/server/ManagedChannel.java b/java/broker/src/org/apache/qpid/server/ManagedChannel.java
new file mode 100644
index 0000000000..815dfdcfbd
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/ManagedChannel.java
@@ -0,0 +1,64 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.qpid.server;
+
+import javax.management.JMException;
+import java.io.IOException;
+
+/**
+ * The managed interface exposed to allow management of channels.
+ * @author Bhupendra Bhardwaj
+ * @version 0.1
+ */
+public interface ManagedChannel
+{
+ static final String TYPE = "Channel";
+
+ /**
+ * Tells whether the channel is transactional.
+ * @return true if the channel is transactional.
+ * @throws IOException
+ */
+ boolean isTransactional() throws IOException;
+
+ /**
+ * Tells the number of unacknowledged messages in this channel.
+ * @return number of unacknowledged messages.
+ * @throws IOException
+ */
+ int getUnacknowledgedMessageCount() throws IOException;
+
+
+ //********** Operations *****************//
+
+ /**
+ * Commits the transactions if the channel is transactional.
+ * @throws IOException
+ * @throws JMException
+ */
+ void commitTransactions() throws IOException, JMException;
+
+ /**
+ * Rollsback the transactions if the channel is transactional.
+ * @throws IOException
+ * @throws JMException
+ */
+ void rollbackTransactions() throws IOException, JMException;
+
+}
diff --git a/java/broker/src/org/apache/qpid/server/RequiredDeliveryException.java b/java/broker/src/org/apache/qpid/server/RequiredDeliveryException.java
new file mode 100644
index 0000000000..6c4fb6b730
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/RequiredDeliveryException.java
@@ -0,0 +1,109 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server;
+
+import org.apache.qpid.framing.BasicPublishBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.CompositeAMQDataBlock;
+import org.apache.qpid.framing.BasicReturnBody;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.AMQMessage;
+
+import java.util.List;
+
+/**
+ * Signals that a required delivery could not be made. This could be bacuse of
+ * the immediate flag being set and the queue having no consumers, or the mandatory
+ * flag being set and the exchange having no valid bindings.
+ */
+public abstract class RequiredDeliveryException extends AMQException
+{
+ private final String _message;
+ private final BasicPublishBody _publishBody;
+ private final ContentHeaderBody _contentHeaderBody;
+ private final List<ContentBody> _contentBodies;
+
+ public RequiredDeliveryException(String message, AMQMessage payload)
+ {
+ super(message);
+ _message = message;
+ _publishBody = payload.getPublishBody();
+ _contentHeaderBody = payload.getContentHeaderBody();
+ _contentBodies = payload.getContentBodies();
+ }
+
+ public RequiredDeliveryException(String message,
+ BasicPublishBody publishBody,
+ ContentHeaderBody contentHeaderBody,
+ List<ContentBody> contentBodies)
+ {
+ super(message);
+ _message = message;
+ _publishBody = publishBody;
+ _contentHeaderBody = contentHeaderBody;
+ _contentBodies = contentBodies;
+ }
+
+ public BasicPublishBody getPublishBody()
+ {
+ return _publishBody;
+ }
+
+ public ContentHeaderBody getContentHeaderBody()
+ {
+ return _contentHeaderBody;
+ }
+
+ public List<ContentBody> getContentBodies()
+ {
+ return _contentBodies;
+ }
+
+ public CompositeAMQDataBlock getReturnMessage(int channel)
+ {
+ BasicReturnBody returnBody = new BasicReturnBody();
+ returnBody.exchange = _publishBody.exchange;
+ returnBody.replyCode = getReplyCode();
+ returnBody.replyText = _message;
+ returnBody.routingKey = _publishBody.routingKey;
+
+ AMQFrame[] allFrames = new AMQFrame[2 + _contentBodies.size()];
+
+ AMQFrame returnFrame = new AMQFrame();
+ returnFrame.bodyFrame = returnBody;
+ returnFrame.channel = channel;
+
+ allFrames[0] = returnFrame;
+ allFrames[1] = ContentHeaderBody.createAMQFrame(channel, _contentHeaderBody);
+ for (int i = 2; i < allFrames.length; i++)
+ {
+ allFrames[i] = ContentBody.createAMQFrame(channel, _contentBodies.get(i - 2));
+ }
+
+ return new CompositeAMQDataBlock(allFrames);
+ }
+
+ public int getErrorCode()
+ {
+ return getReplyCode();
+ }
+
+ public abstract int getReplyCode();
+}
diff --git a/java/broker/src/org/apache/qpid/server/configuration/Configurator.java b/java/broker/src/org/apache/qpid/server/configuration/Configurator.java
new file mode 100644
index 0000000000..e02b958941
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/configuration/Configurator.java
@@ -0,0 +1,102 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.configuration;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.log4j.Logger;
+import org.apache.qpid.configuration.Configured;
+import org.apache.qpid.configuration.PropertyUtils;
+import org.apache.qpid.configuration.PropertyException;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+import java.lang.reflect.Field;
+
+/**
+ * This class contains utilities for populating classes automatically from values pulled from configuration
+ * files.
+ */
+public class Configurator
+{
+ private static final Logger _logger = Logger.getLogger(Configurator.class);
+
+ /**
+ * Configure a given instance using the application configuration. Note that superclasses are <b>not</b>
+ * currently configured but this could easily be added if required.
+ * @param instance the instance to configure
+ */
+ public static void configure(Object instance)
+ {
+ final Configuration config = ApplicationRegistry.getInstance().getConfiguration();
+
+ for (Field f : instance.getClass().getDeclaredFields())
+ {
+ Configured annotation = f.getAnnotation(Configured.class);
+ if (annotation != null)
+ {
+ setValueInField(f, instance, config, annotation);
+ }
+ }
+ }
+
+ private static void setValueInField(Field f, Object instance, Configuration config, Configured annotation)
+ {
+ Class fieldClass = f.getType();
+ String configPath = annotation.path();
+ try
+ {
+ if (fieldClass == String.class)
+ {
+ String val = config.getString(configPath, annotation.defaultValue());
+ val = PropertyUtils.replaceProperties(val);
+ f.set(instance, val);
+ }
+ else if (fieldClass == int.class)
+ {
+ int val = config.getInt(configPath, Integer.parseInt(annotation.defaultValue()));
+ f.setInt(instance, val);
+ }
+ else if (fieldClass == long.class)
+ {
+ long val = config.getLong(configPath, Long.parseLong(annotation.defaultValue()));
+ f.setLong(instance, val);
+ }
+ else if (fieldClass == double.class)
+ {
+ double val = config.getDouble(configPath, Double.parseDouble(annotation.defaultValue()));
+ f.setDouble(instance, val);
+ }
+ else if (fieldClass == boolean.class)
+ {
+ boolean val = config.getBoolean(configPath, Boolean.parseBoolean(annotation.defaultValue()));
+ f.setBoolean(instance, val);
+ }
+ else
+ {
+ _logger.error("Unsupported field type " + fieldClass + " for " + f + " IGNORING configured value");
+ }
+ }
+ catch (PropertyException e)
+ {
+ _logger.error("Unable to expand property: " + e + " INGORING field " + f, e);
+ }
+ catch (IllegalAccessException e)
+ {
+ _logger.error("Unable to access field " + f + " IGNORING configured value");
+ }
+ }
+} \ No newline at end of file
diff --git a/java/broker/src/org/apache/qpid/server/configuration/VirtualHostConfiguration.java b/java/broker/src/org/apache/qpid/server/configuration/VirtualHostConfiguration.java
new file mode 100644
index 0000000000..a7ad50917c
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/configuration/VirtualHostConfiguration.java
@@ -0,0 +1,217 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.configuration;
+
+import org.apache.qpid.url.AMQBindingURL;
+import org.apache.qpid.url.URLSyntaxException;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.AMQException;
+import org.apache.log4j.Logger;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.XMLConfiguration;
+
+
+import java.util.Collection;
+
+public class VirtualHostConfiguration
+{
+ private static final Logger _logger = Logger.getLogger(VirtualHostConfiguration.class);
+
+ XMLConfiguration _config;
+
+ private static final String XML_VIRTUALHOST = "virtualhost";
+ private static final String XML_PATH = "path";
+ private static final String XML_BIND = "bind";
+ private static final String XML_VIRTUALHOST_PATH = "virtualhost.path";
+ private static final String XML_VIRTUALHOST_BIND = "virtualhost.bind";
+
+
+ public VirtualHostConfiguration(String configFile) throws ConfigurationException
+ {
+ _logger.info("Loading Config file:" + configFile);
+
+ _config = new XMLConfiguration(configFile);
+
+ if (_config.getProperty(XML_VIRTUALHOST_PATH) == null)
+ {
+ throw new ConfigurationException(
+ "Virtualhost Configuration document does not contain a valid virtualhost.");
+ }
+ }
+
+ public void performBindings() throws AMQException, ConfigurationException, URLSyntaxException
+ {
+ Object prop = _config.getProperty(XML_VIRTUALHOST_PATH);
+
+ if (prop instanceof Collection)
+ {
+ _logger.debug("Number of VirtualHosts: " + ((Collection) prop).size());
+
+ int virtualhosts = ((Collection) prop).size();
+ for (int vhost = 0; vhost < virtualhosts; vhost++)
+ {
+ loadVirtualHost(vhost);
+ }
+ }
+ else
+ {
+ loadVirtualHost(-1);
+ }
+ }
+
+ private void loadVirtualHost(int index) throws AMQException, ConfigurationException, URLSyntaxException
+ {
+ String path = XML_VIRTUALHOST;
+
+ if (index != -1)
+ {
+ path = path + "(" + index + ")";
+ }
+
+ Object prop = _config.getProperty(path + "." + XML_PATH);
+
+ if (prop == null)
+ {
+ prop = _config.getProperty(path + "." + XML_BIND);
+ String error = "Virtual Host not defined for binding";
+
+ if (prop != null)
+ {
+ if (prop instanceof Collection)
+ {
+ error += "s";
+ }
+
+ error += ": " + prop;
+ }
+
+ throw new ConfigurationException(error);
+ }
+
+ _logger.info("VirtualHost:'" + prop + "'");
+
+ prop = _config.getProperty(path + "." + XML_BIND);
+ if (prop instanceof Collection)
+ {
+ int bindings = ((Collection) prop).size();
+ _logger.debug("Number of Bindings: " + bindings);
+ for (int dest = 0; dest < bindings; dest++)
+ {
+ loadBinding(path, dest);
+ }
+ }
+ else
+ {
+ loadBinding(path, -1);
+ }
+ }
+
+ private void loadBinding(String rootpath, int index) throws AMQException, ConfigurationException, URLSyntaxException
+ {
+ String path = rootpath + "." + XML_BIND;
+ if (index != -1)
+ {
+ path = path + "(" + index + ")";
+ }
+
+ String bindingString = _config.getString(path);
+
+ AMQBindingURL binding = new AMQBindingURL(bindingString);
+
+ _logger.debug("Loaded Binding:" + binding);
+
+ try
+ {
+ bind(binding);
+ }
+ catch (AMQException amqe)
+ {
+ _logger.info("Unable to bind url: " + binding);
+ throw amqe;
+ }
+ }
+
+ private void bind(AMQBindingURL binding) throws AMQException, ConfigurationException
+ {
+
+ String queueName = binding.getQueueName();
+
+ // This will occur if the URL is a Topic
+ if (queueName == null)
+ {
+ //todo register valid topic
+ ///queueName = binding.getDestinationName();
+ throw new AMQException("Topics cannot be bound. TODO Register valid topic");
+ }
+
+ //Get references to Broker Registries
+ QueueRegistry queueRegistry = ApplicationRegistry.getInstance().getQueueRegistry();
+ MessageStore messageStore = ApplicationRegistry.getInstance().getMessageStore();
+ ExchangeRegistry exchangeRegistry = ApplicationRegistry.getInstance().getExchangeRegistry();
+
+ synchronized (queueRegistry)
+ {
+ AMQQueue queue = queueRegistry.getQueue(queueName);
+
+ if (queue == null)
+ {
+ _logger.info("Queue '" + binding.getQueueName() + "' does not exists. Creating.");
+
+ queue = new AMQQueue(queueName,
+ Boolean.parseBoolean(binding.getOption(AMQBindingURL.OPTION_DURABLE)),
+ null /* These queues will have no owner */,
+ false /* Therefore autodelete makes no sence */, queueRegistry);
+
+ if (queue.isDurable())
+ {
+ messageStore.createQueue(queue);
+ }
+
+ queueRegistry.registerQueue(queue);
+ }
+ else
+ {
+ _logger.info("Queue '" + binding.getQueueName() + "' already exists not creating.");
+ }
+
+ Exchange defaultExchange = exchangeRegistry.getExchange(binding.getExchangeName());
+ synchronized (defaultExchange)
+ {
+ if (defaultExchange == null)
+ {
+ throw new ConfigurationException("Attempt to bind queue to unknown exchange:" + binding);
+ }
+
+ defaultExchange.registerQueue(queue.getName(), queue, null);
+
+ if (binding.getRoutingKey() == null || binding.getRoutingKey().equals(""))
+ {
+ throw new ConfigurationException("Unknown binding not specified on url:" + binding);
+ }
+
+ queue.bind(binding.getRoutingKey(), defaultExchange);
+ }
+ _logger.info("Queue '" + queue.getName() + "' bound to exchange:" + binding.getExchangeName() + " RK:'" + binding.getRoutingKey() + "'");
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/exchange/AbstractExchange.java b/java/broker/src/org/apache/qpid/server/exchange/AbstractExchange.java
new file mode 100644
index 0000000000..e11de152d0
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/exchange/AbstractExchange.java
@@ -0,0 +1,134 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.management.DefaultManagedObject;
+import org.apache.qpid.server.management.Managable;
+import org.apache.qpid.server.management.ManagedObject;
+
+public abstract class AbstractExchange implements Exchange, Managable
+{
+ private String _name;
+
+ protected boolean _durable;
+
+ protected int _ticket;
+
+ protected ExchangeMBean _exchangeMbean;
+
+ /**
+ * Whether the exchange is automatically deleted once all queues have detached from it
+ */
+ protected boolean _autoDelete;
+
+ /**
+ * Abstract MBean class. This has some of the methods implemented from
+ * management intrerface for exchanges. Any implementaion of an
+ * Exchange MBean should extend this class.
+ */
+ protected abstract class ExchangeMBean extends DefaultManagedObject implements ManagedExchange
+ {
+ public ExchangeMBean()
+ {
+ super(ManagedExchange.class, ManagedExchange.TYPE);
+ }
+
+ public String getObjectInstanceName()
+ {
+ return _name;
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public int getTicketNo()
+ {
+ return _ticket;
+ }
+
+ public boolean isDurable()
+ {
+ return _durable;
+ }
+
+ public boolean isAutoDelete()
+ {
+ return _autoDelete;
+ }
+
+ } // End of MBean class
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ /**
+ * Concrete exchanges must implement this method in order to create the managed representation. This is
+ * called during initialisation (template method pattern).
+ * @return the MBean
+ */
+ protected abstract ExchangeMBean createMBean();
+
+ public void initialise(String name, boolean durable, int ticket, boolean autoDelete) throws AMQException
+ {
+ _name = name;
+ _durable = durable;
+ _autoDelete = autoDelete;
+ _ticket = ticket;
+ _exchangeMbean = createMBean();
+ _exchangeMbean.register();
+ }
+
+ public boolean isDurable()
+ {
+ return _durable;
+ }
+
+ public boolean isAutoDelete()
+ {
+ return _autoDelete;
+ }
+
+ public int getTicket()
+ {
+ return _ticket;
+ }
+
+ public void close() throws AMQException
+ {
+ if (_exchangeMbean != null)
+ {
+ _exchangeMbean.unregister();
+ }
+ }
+
+ public String toString()
+ {
+ return getClass().getName() + "[" + getName() +"]";
+ }
+
+ public ManagedObject getManagedObject()
+ {
+ return _exchangeMbean;
+ }
+
+}
diff --git a/java/broker/src/org/apache/qpid/server/exchange/DefaultExchangeFactory.java b/java/broker/src/org/apache/qpid/server/exchange/DefaultExchangeFactory.java
new file mode 100644
index 0000000000..990a868e72
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/exchange/DefaultExchangeFactory.java
@@ -0,0 +1,63 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class DefaultExchangeFactory implements ExchangeFactory
+{
+ private static final Logger _logger = Logger.getLogger(DefaultExchangeFactory.class);
+
+ private Map<String, Class<? extends Exchange>> _exchangeClassMap = new HashMap<String, Class<? extends Exchange>>();
+
+ public DefaultExchangeFactory()
+ {
+ _exchangeClassMap.put("direct", org.apache.qpid.server.exchange.DestNameExchange.class);
+ _exchangeClassMap.put("topic", org.apache.qpid.server.exchange.DestWildExchange.class);
+ _exchangeClassMap.put("headers", org.apache.qpid.server.exchange.HeadersExchange.class);
+ }
+
+ public Exchange createExchange(String exchange, String type, boolean durable, boolean autoDelete,
+ int ticket)
+ throws AMQException
+ {
+ Class<? extends Exchange> exchClass = _exchangeClassMap.get(type);
+ if (exchClass == null)
+ {
+ throw new AMQException(_logger, "Unknown exchange type: " + type);
+ }
+ try
+ {
+ Exchange e = exchClass.newInstance();
+ e.initialise(exchange, durable, ticket, autoDelete);
+ return e;
+ }
+ catch (InstantiationException e)
+ {
+ throw new AMQException(_logger, "Unable to create exchange: " + e, e);
+ }
+ catch (IllegalAccessException e)
+ {
+ throw new AMQException(_logger, "Unable to create exchange: " + e, e);
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java b/java/broker/src/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java
new file mode 100644
index 0000000000..06a3944367
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/exchange/DefaultExchangeRegistry.java
@@ -0,0 +1,92 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.protocol.ExchangeInitialiser;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.log4j.Logger;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+public class DefaultExchangeRegistry implements ExchangeRegistry
+{
+ private static final Logger _log = Logger.getLogger(DefaultExchangeRegistry.class);
+
+ /**
+ * Maps from exchange name to exchange instance
+ */
+ private ConcurrentMap<String, Exchange> _exchangeMap = new ConcurrentHashMap<String, Exchange>();
+
+ public DefaultExchangeRegistry(ExchangeFactory exchangeFactory)
+ {
+ //create 'standard' exchanges:
+ try
+ {
+ new ExchangeInitialiser().initialise(exchangeFactory, this);
+ }
+ catch(AMQException e)
+ {
+ _log.error("Failed to initialise exchanges: ", e);
+ }
+ }
+
+ public void registerExchange(Exchange exchange)
+ {
+ _exchangeMap.put(exchange.getName(), exchange);
+ }
+
+ public void unregisterExchange(String name, boolean inUse) throws AMQException
+ {
+ // TODO: check inUse argument
+ Exchange e = _exchangeMap.remove(name);
+ if (e != null)
+ {
+ e.close();
+ }
+ else
+ {
+ throw new AMQException("Unknown exchange " + name);
+ }
+ }
+
+ public Exchange getExchange(String name)
+ {
+ return _exchangeMap.get(name);
+ }
+
+ /**
+ * Routes content through exchanges, delivering it to 1 or more queues.
+ * @param payload
+ * @throws AMQException if something goes wrong delivering data
+ */
+ public void routeContent(AMQMessage payload) throws AMQException
+ {
+ final String exchange = payload.getPublishBody().exchange;
+ final Exchange exch = _exchangeMap.get(exchange);
+ // there is a small window of opportunity for the exchange to be deleted in between
+ // the JmsPublish being received (where the exchange is validated) and the final
+ // content body being received (which triggers this method)
+ if (exch == null)
+ {
+ throw new AMQException("Exchange '" + exchange + "' does not exist");
+ }
+ exch.route(payload);
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/exchange/DestNameExchange.java b/java/broker/src/org/apache/qpid/server/exchange/DestNameExchange.java
new file mode 100644
index 0000000000..7f1c7df224
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/exchange/DestNameExchange.java
@@ -0,0 +1,204 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.*;
+import org.apache.log4j.Logger;
+
+import javax.management.openmbean.*;
+import javax.management.MBeanException;
+import javax.management.JMException;
+import java.util.List;
+import java.util.Map;
+import java.util.ArrayList;
+
+public class DestNameExchange extends AbstractExchange
+{
+ private static final Logger _logger = Logger.getLogger(DestNameExchange.class);
+
+ /**
+ * Maps from queue name to queue instances
+ */
+ private final Index _index = new Index();
+
+ /**
+ * MBean class implementing the management interfaces.
+ */
+ private final class DestNameExchangeMBean extends ExchangeMBean
+ {
+ private String[] _bindingItemNames = {"BindingKey", "QueueNames"};
+ private String[] _bindingItemDescriptions = {"Binding key", "Queue Names"};
+ private String[] _bindingItemIndexNames = {"BindingKey"};
+ private OpenType[] _bindingItemTypes = new OpenType[2];
+
+ private CompositeType _bindingDataType = null;
+ private TabularType _bindinglistDataType = null;
+ private TabularDataSupport _bindingList = null;
+
+ public DestNameExchangeMBean()
+ {
+ super();
+ init();
+ }
+
+ /**
+ * initialises the OpenType objects.
+ */
+ private void init()
+ {
+ try
+ {
+ _bindingItemTypes[0] = SimpleType.STRING;
+ //_bindingItemTypes[1] = ArrayType.getArrayType(SimpleType.STRING);
+ _bindingItemTypes[1] = new ArrayType(1, SimpleType.STRING);
+
+ _bindingDataType = new CompositeType("QueueBinding",
+ "Queue and binding keye",
+ _bindingItemNames,
+ _bindingItemDescriptions,
+ _bindingItemTypes);
+ _bindinglistDataType = new TabularType("Bindings",
+ "List of queues and binding keys",
+ _bindingDataType,
+ _bindingItemIndexNames);
+ }
+ catch(OpenDataException ex)
+ {
+ //It should never occur.
+ _logger.error("OpenDataTypes could not be created.", ex);
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public TabularData viewBindings()
+ throws OpenDataException
+ {
+ Map<String, List<AMQQueue>> bindings = _index.getBindingsMap();
+ _bindingList = new TabularDataSupport(_bindinglistDataType);
+
+ for (Map.Entry<String, List<AMQQueue>> entry : bindings.entrySet())
+ {
+ String key = entry.getKey();
+ List<String> queueList = new ArrayList<String>();
+
+ List<AMQQueue> queues = entry.getValue();
+ for (AMQQueue q : queues)
+ {
+ queueList.add(q.getName());
+ }
+
+ Object[] bindingItemValues = {key, queueList.toArray(new String[0])};
+ CompositeData bindingData = new CompositeDataSupport(_bindingDataType,
+ _bindingItemNames,
+ bindingItemValues);
+ _bindingList.put(bindingData);
+ }
+
+ return _bindingList;
+ }
+
+ public void createBinding(String queueName, String binding)
+ throws JMException
+ {
+ AMQQueue queue = ApplicationRegistry.getInstance().getQueueRegistry().getQueue(queueName);
+
+ if (queue == null)
+ throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange.");
+
+ try
+ {
+ registerQueue(binding, queue, null);
+ queue.bind(binding, DestNameExchange.this);
+ }
+ catch (AMQException ex)
+ {
+ throw new MBeanException(ex);
+ }
+ }
+
+ }// End of MBean class
+
+
+ protected ExchangeMBean createMBean()
+ {
+ return new DestNameExchangeMBean();
+ }
+
+ public void registerQueue(String routingKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+ assert queue != null;
+ assert routingKey != null;
+ if(!_index.add(routingKey, queue))
+ {
+ _logger.debug("Queue " + queue + " is already registered with routing key " + routingKey);
+ }
+ else
+ {
+ _logger.debug("Binding queue " + queue + " with routing key " + routingKey
+ + " to exchange " + this);
+ }
+ }
+
+ public void deregisterQueue(String routingKey, AMQQueue queue) throws AMQException
+ {
+ assert queue != null;
+ assert routingKey != null;
+
+ if (!_index.remove(routingKey, queue))
+ {
+ throw new AMQException("Queue " + queue + " was not registered with exchange " + this.getName() +
+ " with routing key " + routingKey + ". No queue was registered with that routing key");
+ }
+ }
+
+ public void route(AMQMessage payload) throws AMQException
+ {
+ BasicPublishBody publishBody = payload.getPublishBody();
+
+ final String routingKey = publishBody.routingKey;
+ final List<AMQQueue> queues = _index.get(routingKey);
+ if (queues == null || queues.isEmpty())
+ {
+ String msg = "Routing key " + routingKey + " is not known to " + this;
+ if (publishBody.mandatory)
+ {
+ throw new NoRouteException(msg, payload);
+ }
+ else
+ {
+ _logger.warn(msg);
+ }
+ }
+ else
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Publishing message to queue " + queues);
+ }
+
+ for(AMQQueue q :queues)
+ {
+ q.deliver(payload);
+ }
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/exchange/DestWildExchange.java b/java/broker/src/org/apache/qpid/server/exchange/DestWildExchange.java
new file mode 100644
index 0000000000..16b35cf6fa
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/exchange/DestWildExchange.java
@@ -0,0 +1,210 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.BasicPublishBody;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+import javax.management.openmbean.*;
+import javax.management.JMException;
+import javax.management.MBeanException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+public class DestWildExchange extends AbstractExchange
+{
+ private static final Logger _logger = Logger.getLogger(DestWildExchange.class);
+
+ private ConcurrentHashMap<String, List<AMQQueue>> _routingKey2queues = new ConcurrentHashMap<String, List<AMQQueue>>();
+
+ /**
+ * DestWildExchangeMBean class implements the management interface for the
+ * Topic exchanges.
+ */
+ private final class DestWildExchangeMBean extends ExchangeMBean
+ {
+ private String[] _bindingItemNames = {"BindingKey", "QueueNames"};
+ private String[] _bindingItemDescriptions = {"Binding key", "Queue Names"};
+ private String[] _bindingItemIndexNames = {"BindingKey"};
+ private OpenType[] _bindingItemTypes = new OpenType[2];
+
+ private CompositeType _bindingDataType = null;
+ private TabularType _bindinglistDataType = null;
+ private TabularDataSupport _bindingList = null;
+
+ public DestWildExchangeMBean()
+ {
+ super();
+ init();
+ }
+
+ /**
+ * initialises the OpenType objects.
+ */
+ private void init()
+ {
+ try
+ {
+ _bindingItemTypes[0] = SimpleType.STRING;
+ _bindingItemTypes[1] = new ArrayType(1, SimpleType.STRING);
+
+ _bindingDataType = new CompositeType("QueueBinding",
+ "Queue and binding keye",
+ _bindingItemNames,
+ _bindingItemDescriptions,
+ _bindingItemTypes);
+ _bindinglistDataType = new TabularType("Bindings",
+ "List of queues and binding keys",
+ _bindingDataType,
+ _bindingItemIndexNames);
+ }
+ catch(OpenDataException ex)
+ {
+ //It should never occur.
+ _logger.error("OpenDataTypes could not be created.", ex);
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public TabularData viewBindings()
+ throws OpenDataException
+ {
+ _bindingList = new TabularDataSupport(_bindinglistDataType);
+
+ for (Map.Entry<String, List<AMQQueue>> entry : _routingKey2queues.entrySet())
+ {
+ String key = entry.getKey();
+ List<String> queueList = new ArrayList<String>();
+
+ List<AMQQueue> queues = entry.getValue();
+ for (AMQQueue q : queues)
+ {
+ queueList.add(q.getName());
+ }
+
+ Object[] bindingItemValues = {key, queueList.toArray(new String[0])};
+ CompositeData bindingData = new CompositeDataSupport(_bindingDataType,
+ _bindingItemNames,
+ bindingItemValues);
+ _bindingList.put(bindingData);
+ }
+
+ return _bindingList;
+ }
+
+ public void createBinding(String queueName, String binding)
+ throws JMException
+ {
+ AMQQueue queue = ApplicationRegistry.getInstance().getQueueRegistry().getQueue(queueName);
+
+ if (queue == null)
+ throw new JMException("Queue \"" + queueName + "\" is not registered with the exchange.");
+
+ try
+ {
+ registerQueue(binding, queue, null);
+ queue.bind(binding, DestWildExchange.this);
+ }
+ catch (AMQException ex)
+ {
+ throw new MBeanException(ex);
+ }
+ }
+
+ } // End of MBean class
+
+
+ public void registerQueue(String routingKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+ assert queue != null;
+ assert routingKey != null;
+ // we need to use putIfAbsent, which is an atomic operation, to avoid a race condition
+ List<AMQQueue> queueList = _routingKey2queues.putIfAbsent(routingKey, new CopyOnWriteArrayList<AMQQueue>());
+ // if we got null back, no previous value was associated with the specified routing key hence
+ // we need to read back the new value just put into the map
+ if (queueList == null)
+ {
+ queueList = _routingKey2queues.get(routingKey);
+ }
+ if (!queueList.contains(queue))
+ {
+ queueList.add(queue);
+ }
+ else if(_logger.isDebugEnabled())
+ {
+ _logger.debug("Queue " + queue + " is already registered with routing key " + routingKey);
+ }
+
+ }
+
+ public void route(AMQMessage payload) throws AMQException
+ {
+ BasicPublishBody publishBody = payload.getPublishBody();
+
+ final String routingKey = publishBody.routingKey;
+ List<AMQQueue> queues = _routingKey2queues.get(routingKey);
+ // if we have no registered queues we have nothing to do
+ // TODO: add support for the immediate flag
+ if (queues == null)
+ {
+ //todo Check for valid topic - mritchie
+ return;
+ }
+
+ for (AMQQueue q : queues)
+ {
+ // TODO: modify code generator to add clone() method then clone the deliver body
+ // without this addition we have a race condition - we will be modifying the body
+ // before the encoder has encoded the body for delivery
+ q.deliver(payload);
+ }
+ }
+
+ public void deregisterQueue(String routingKey, AMQQueue queue) throws AMQException
+ {
+ assert queue != null;
+ assert routingKey != null;
+
+ List<AMQQueue> queues = _routingKey2queues.get(routingKey);
+ if (queues == null)
+ {
+ throw new AMQException("Queue " + queue + " was not registered with exchange " + this.getName() +
+ " with routing key " + routingKey + ". No queue was registered with that routing key");
+
+ }
+ boolean removedQ = queues.remove(queue);
+ if (!removedQ)
+ {
+ throw new AMQException("Queue " + queue + " was not registered with exchange " + this.getName() +
+ " with routing key " + routingKey);
+ }
+ }
+
+ protected ExchangeMBean createMBean()
+ {
+ return new DestWildExchangeMBean();
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/exchange/Exchange.java b/java/broker/src/org/apache/qpid/server/exchange/Exchange.java
new file mode 100644
index 0000000000..cd75825601
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/exchange/Exchange.java
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.AMQMessage;
+
+public interface Exchange
+{
+ String getName();
+
+ void initialise(String name, boolean durable, int ticket, boolean autoDelete) throws AMQException;
+
+ boolean isDurable();
+
+ /**
+ * @return true if the exchange will be deleted after all queues have been detached
+ */
+ boolean isAutoDelete();
+
+ int getTicket();
+
+ void close() throws AMQException;
+
+ void registerQueue(String routingKey, AMQQueue queue, FieldTable args) throws AMQException;
+
+ void deregisterQueue(String routingKey, AMQQueue queue) throws AMQException;
+
+ void route(AMQMessage message) throws AMQException;
+}
diff --git a/java/broker/src/org/apache/qpid/server/exchange/ExchangeFactory.java b/java/broker/src/org/apache/qpid/server/exchange/ExchangeFactory.java
new file mode 100644
index 0000000000..36fd159f31
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/exchange/ExchangeFactory.java
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.AMQException;
+
+
+public interface ExchangeFactory
+{
+ Exchange createExchange(String exchange, String type, boolean durable, boolean autoDelete,
+ int ticket)
+ throws AMQException;
+}
diff --git a/java/broker/src/org/apache/qpid/server/exchange/ExchangeInUseException.java b/java/broker/src/org/apache/qpid/server/exchange/ExchangeInUseException.java
new file mode 100644
index 0000000000..4a6c735bee
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/exchange/ExchangeInUseException.java
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.AMQException;
+
+public class ExchangeInUseException extends AMQException
+{
+ public ExchangeInUseException(String exchangeName)
+ {
+ super("Exchange " + exchangeName + " is currently in use");
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/exchange/ExchangeRegistry.java b/java/broker/src/org/apache/qpid/server/exchange/ExchangeRegistry.java
new file mode 100644
index 0000000000..5b71cd7b0c
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/exchange/ExchangeRegistry.java
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.AMQMessage;
+
+
+public interface ExchangeRegistry extends MessageRouter
+{
+ void registerExchange(Exchange exchange);
+
+ /**
+ * Unregister an exchange
+ * @param name name of the exchange to delete
+ * @param inUse if true, do NOT delete the exchange if it is in use (has queues bound to it)
+ * @throws ExchangeInUseException when the exchange cannot be deleted because it is in use
+ * @throws AMQException
+ */
+ void unregisterExchange(String name, boolean inUse) throws ExchangeInUseException, AMQException;
+
+ Exchange getExchange(String name);
+}
diff --git a/java/broker/src/org/apache/qpid/server/exchange/HeadersBinding.java b/java/broker/src/org/apache/qpid/server/exchange/HeadersBinding.java
new file mode 100644
index 0000000000..d69e956b5f
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/exchange/HeadersBinding.java
@@ -0,0 +1,142 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.log4j.Logger;
+
+import java.util.Collections;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Defines binding and matching based on a set of headers.
+ */
+class HeadersBinding
+{
+ private static final Logger _logger = Logger.getLogger(HeadersBinding.class);
+
+ private final Map _mappings = new HashMap();
+ private final Set<Object> required = new HashSet<Object>();
+ private final Set<Map.Entry> matches = new HashSet<Map.Entry>();
+ private boolean matchAny;
+
+ /**
+ * Creates a binding for a set of mappings. Those mappings whose value is
+ * null or the empty string are assumed only to be required headers, with
+ * no constraint on the value. Those with a non-null value are assumed to
+ * define a required match of value.
+ * @param mappings the defined mappings this binding should use
+ */
+ HeadersBinding(Map mappings)
+ {
+ //noinspection unchecked
+ this(mappings == null ? new HashSet<Map.Entry>() : mappings.entrySet());
+ _mappings.putAll(mappings);
+ }
+
+ private HeadersBinding(Set<Map.Entry> entries)
+ {
+ for (Map.Entry e : entries)
+ {
+ if (isSpecial(e.getKey()))
+ {
+ processSpecial((String) e.getKey(), e.getValue());
+ }
+ else if (e.getValue() == null || e.getValue().equals(""))
+ {
+ required.add(e.getKey());
+ }
+ else
+ {
+ matches.add(e);
+ }
+ }
+ }
+
+ protected Map getMappings()
+ {
+ return _mappings;
+ }
+
+ /**
+ * Checks whether the supplied headers match the requirements of this binding
+ * @param headers the headers to check
+ * @return true if the headers define any required keys and match any required
+ * values
+ */
+ public boolean matches(Map headers)
+ {
+ if(headers == null)
+ {
+ return required.isEmpty() && matches.isEmpty();
+ }
+ else
+ {
+ return matchAny ? or(headers) : and(headers);
+ }
+ }
+
+ private boolean and(Map headers)
+ {
+ //need to match all the defined mapping rules:
+ return headers.keySet().containsAll(required)
+ && headers.entrySet().containsAll(matches);
+ }
+
+ private boolean or(Map headers)
+ {
+ //only need to match one mapping rule:
+ return !Collections.disjoint(headers.keySet(), required)
+ || !Collections.disjoint(headers.entrySet(), matches);
+ }
+
+ private void processSpecial(String key, Object value)
+ {
+ if("X-match".equalsIgnoreCase(key))
+ {
+ matchAny = isAny(value);
+ }
+ else
+ {
+ _logger.warn("Ignoring special header: " + key);
+ }
+ }
+
+ private boolean isAny(Object value)
+ {
+ if(value instanceof String)
+ {
+ if("any".equalsIgnoreCase((String) value)) return true;
+ if("all".equalsIgnoreCase((String) value)) return false;
+ }
+ _logger.warn("Ignoring unrecognised match type: " + value);
+ return false;//default to all
+ }
+
+ static boolean isSpecial(Object key)
+ {
+ return key instanceof String && isSpecial((String) key);
+ }
+
+ static boolean isSpecial(String key)
+ {
+ return key.startsWith("X-") || key.startsWith("x-");
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/exchange/HeadersExchange.java b/java/broker/src/org/apache/qpid/server/exchange/HeadersExchange.java
new file mode 100644
index 0000000000..83475f87f2
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/exchange/HeadersExchange.java
@@ -0,0 +1,226 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.AMQMessage;
+
+import javax.management.openmbean.*;
+import javax.management.ServiceNotFoundException;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * An exchange that binds queues based on a set of required headers and header values
+ * and routes messages to these queues by matching the headers of the message against
+ * those with which the queues were bound.
+ * <p/>
+ * <pre>
+ * The Headers Exchange
+ *
+ * Routes messages according to the value/presence of fields in the message header table.
+ * (Basic and JMS content has a content header field called "headers" that is a table of
+ * message header fields).
+ *
+ * class = "headers"
+ * routing key is not used
+ *
+ * Has the following binding arguments:
+ *
+ * the X-match field - if "all", does an AND match (used for GRM), if "any", does an OR match.
+ * other fields prefixed with "X-" are ignored (and generate a console warning message).
+ * a field with no value or empty value indicates a match on presence only.
+ * a field with a value indicates match on field presence and specific value.
+ *
+ * Standard instances:
+ *
+ * amq.match - pub/sub on field content/value
+ * </pre>
+ */
+public class HeadersExchange extends AbstractExchange
+{
+ private static final Logger _logger = Logger.getLogger(HeadersExchange.class);
+
+ private final List<Registration> _bindings = new CopyOnWriteArrayList<Registration>();
+
+ /**
+ * HeadersExchangeMBean class implements the management interface for the
+ * Header Exchanges.
+ */
+ private final class HeadersExchangeMBean extends ExchangeMBean
+ {
+ private String[] _bindingItemNames = {"Queue", "HeaderBinding"};
+ private String[] _bindingItemDescriptions = {"Queue Name", "Header attribute bindings"};
+ private String[] _bindingItemIndexNames = {"Queue"};
+ private OpenType[] _bindingItemTypes = new OpenType[2];
+
+ private CompositeType _bindingDataType = null;
+ private TabularType _bindinglistDataType = null;
+ private TabularDataSupport _bindingList = null;
+
+ public HeadersExchangeMBean()
+ {
+ super();
+ init();
+ }
+ /**
+ * initialises the OpenType objects.
+ */
+ private void init()
+ {
+ try
+ {
+ _bindingItemTypes[0] = SimpleType.STRING;
+ _bindingItemTypes[1] = new ArrayType(1, SimpleType.STRING);;
+
+ _bindingDataType = new CompositeType("QueueAndHeaderAttributesBinding",
+ "Queue and header attributes binding",
+ _bindingItemNames,
+ _bindingItemDescriptions,
+ _bindingItemTypes);
+ _bindinglistDataType = new TabularType("HeaderBindings",
+ "List of queues and related header attribute bindings",
+ _bindingDataType,
+ _bindingItemIndexNames);
+ }
+ catch(OpenDataException ex)
+ {
+ //It should never occur.
+ _logger.error("OpenDataTypes could not be created.", ex);
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public TabularData viewBindings()
+ throws OpenDataException
+ {
+ _bindingList = new TabularDataSupport(_bindinglistDataType);
+ for (Iterator<Registration> itr = _bindings.iterator(); itr.hasNext();)
+ {
+ Registration registration = itr.next();
+ String queueName = registration.queue.getName();
+
+ HeadersBinding headers = registration.binding;
+ Map<Object, Object> headerMappings = headers.getMappings();
+ List<String> mappingList = new ArrayList<String>();
+
+ for (Map.Entry<Object, Object> en : headerMappings.entrySet())
+ {
+ String key = en.getKey().toString();
+ String value = en.getValue().toString();
+
+ mappingList.add(key + "=" + value);
+ }
+
+ Object[] bindingItemValues = {queueName, mappingList.toArray(new String[0])};
+ CompositeData bindingData = new CompositeDataSupport(_bindingDataType,
+ _bindingItemNames,
+ bindingItemValues);
+ _bindingList.put(bindingData);
+ }
+
+ return _bindingList;
+ }
+
+ public void createBinding(String QueueName, String binding)
+ throws ServiceNotFoundException
+ {
+ throw new ServiceNotFoundException("This service is not supported by \"" + this.getName() + "\"");
+ }
+
+ } // End of MBean class
+
+ public void registerQueue(String routingKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+ _logger.debug("Exchange " + getName() + ": Binding " + queue.getName() + " with " + args);
+ _bindings.add(new Registration(new HeadersBinding(args), queue));
+ }
+
+ public void deregisterQueue(String routingKey, AMQQueue queue) throws AMQException
+ {
+ _logger.debug("Exchange " + getName() + ": Unbinding " + queue.getName());
+ _bindings.remove(new Registration(null, queue));
+ }
+
+ public void route(AMQMessage payload) throws AMQException
+ {
+ Map headers = getHeaders(payload.getContentHeaderBody());
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Exchange " + getName() + ": routing message with headers " + headers);
+ }
+ boolean delivered = false;
+ for (Registration e : _bindings)
+ {
+ if (e.binding.matches(headers))
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Exchange " + getName() + ": delivering message with headers " +
+ headers + " to " + e.queue.getName());
+ }
+ e.queue.deliver(payload);
+ delivered = true;
+ }
+ }
+ if (!delivered)
+ {
+ _logger.warn("Exchange " + getName() + ": message not routable.");
+ }
+ }
+
+ protected Map getHeaders(ContentHeaderBody contentHeaderFrame)
+ {
+ //what if the content type is not 'basic'? 'file' and 'stream' content classes also define headers,
+ //but these are not yet implemented.
+ return ((BasicContentHeaderProperties) contentHeaderFrame.properties).getHeaders();
+ }
+
+ protected ExchangeMBean createMBean()
+ {
+ return new HeadersExchangeMBean();
+ }
+
+ private static class Registration
+ {
+ private final HeadersBinding binding;
+ private final AMQQueue queue;
+
+ Registration(HeadersBinding binding, AMQQueue queue)
+ {
+ this.binding = binding;
+ this.queue = queue;
+ }
+
+ public int hashCode()
+ {
+ return queue.hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ return o instanceof Registration && ((Registration) o).queue.equals(queue);
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/exchange/Index.java b/java/broker/src/org/apache/qpid/server/exchange/Index.java
new file mode 100644
index 0000000000..9e88b6a68c
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/exchange/Index.java
@@ -0,0 +1,85 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.server.queue.AMQQueue;
+
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * An index of queues against routing key. Allows multiple queues to be stored
+ * against the same key. Used in the DestNameExchange.
+ */
+class Index
+{
+ private ConcurrentMap<String, List<AMQQueue>> _index
+ = new ConcurrentHashMap<String, List<AMQQueue>>();
+
+ boolean add(String key, AMQQueue queue)
+ {
+ List<AMQQueue> queues = _index.get(key);
+ if(queues == null)
+ {
+ queues = new CopyOnWriteArrayList<AMQQueue>();
+ //next call is atomic, so there is no race to create the list
+ List<AMQQueue> active = _index.putIfAbsent(key, queues);
+ if(active != null)
+ {
+ //someone added the new one in faster than we did, so use theirs
+ queues = active;
+ }
+ }
+ if(queues.contains(queue))
+ {
+ return false;
+ }
+ else
+ {
+ return queues.add(queue);
+ }
+ }
+
+ boolean remove(String key, AMQQueue queue)
+ {
+ List<AMQQueue> queues = _index.get(key);
+ if (queues != null)
+ {
+ boolean removed = queues.remove(queue);
+ if (queues.size() == 0)
+ {
+ _index.remove(key);
+ }
+ return removed;
+ }
+ return false;
+ }
+
+ List<AMQQueue> get(String key)
+ {
+ return _index.get(key);
+ }
+
+ Map<String, List<AMQQueue>> getBindingsMap()
+ {
+ return _index;
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/exchange/ManagedExchange.java b/java/broker/src/org/apache/qpid/server/exchange/ManagedExchange.java
new file mode 100644
index 0000000000..a434ecb443
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/exchange/ManagedExchange.java
@@ -0,0 +1,78 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import javax.management.openmbean.TabularData;
+import javax.management.JMException;
+import java.io.IOException;
+
+/**
+ * The management interface exposed to allow management of an Exchange.
+ * @author Robert J. Greig
+ * @author Bhupendra Bhardwaj
+ * @version 0.1
+ */
+public interface ManagedExchange
+{
+ static final String TYPE = "Exchange";
+
+ /**
+ * Returns the name of the managed exchange.
+ * @return the name of the exchange.
+ * @throws IOException
+ */
+ String getName() throws IOException;
+
+ /**
+ * Tells if the exchange is durable or not.
+ * @return true if the exchange is durable.
+ * @throws IOException
+ */
+ boolean isDurable() throws IOException;
+
+ /**
+ * Tells if the exchange is set for autodelete or not.
+ * @return true if the exchange is set as autodelete.
+ * @throws IOException
+ */
+ boolean isAutoDelete() throws IOException;
+
+ int getTicketNo() throws IOException;
+
+
+ // Operations
+
+ /**
+ * Returns all the bindings this exchange has with the queues.
+ * @return the bindings with the exchange.
+ * @throws IOException
+ * @throws JMException
+ */
+ TabularData viewBindings()
+ throws IOException, JMException;
+
+ /**
+ * Creates new binding with the given queue and binding.
+ * @param QueueName
+ * @param binding
+ * @throws JMException
+ */
+ void createBinding(String QueueName, String binding)
+ throws JMException;
+
+} \ No newline at end of file
diff --git a/java/broker/src/org/apache/qpid/server/exchange/MessageRouter.java b/java/broker/src/org/apache/qpid/server/exchange/MessageRouter.java
new file mode 100644
index 0000000000..2eef5f0676
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/exchange/MessageRouter.java
@@ -0,0 +1,36 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.AMQException;
+
+/**
+ * Separated out from the ExchangeRegistry interface to allow components
+ * that use only this part to have a dependency with a reduced footprint.
+ *
+ */
+public interface MessageRouter
+{
+ /**
+ * Routes content through exchanges, delivering it to 1 or more queues.
+ * @param message the message to be routed
+ * @throws org.apache.qpid.AMQException if something goes wrong delivering data
+ */
+ void routeContent(AMQMessage message) throws AMQException;
+}
diff --git a/java/broker/src/org/apache/qpid/server/exchange/NoRouteException.java b/java/broker/src/org/apache/qpid/server/exchange/NoRouteException.java
new file mode 100644
index 0000000000..6d5511d9c8
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/exchange/NoRouteException.java
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.protocol.AMQConstant;
+
+/**
+ * Thrown by an exchange if there is no way to route a message with the
+ * mandatory flag set.
+ */
+public class NoRouteException extends RequiredDeliveryException
+{
+ public NoRouteException(String msg, AMQMessage message)
+ {
+ super(msg, message);
+ }
+
+ public int getReplyCode()
+ {
+ return AMQConstant.NO_ROUTE.getCode();
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/BasicAckMethodHandler.java b/java/broker/src/org/apache/qpid/server/handler/BasicAckMethodHandler.java
new file mode 100644
index 0000000000..5e236f7da9
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/BasicAckMethodHandler.java
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.BasicAckBody;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.AMQChannel;
+
+public class BasicAckMethodHandler implements StateAwareMethodListener<BasicAckBody>
+{
+ private static final BasicAckMethodHandler _instance = new BasicAckMethodHandler();
+
+ public static BasicAckMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private BasicAckMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<BasicAckBody> evt) throws AMQException
+ {
+ BasicAckBody body = evt.getMethod();
+ final AMQChannel channel = protocolSession.getChannel(evt.getChannelId());
+ // this method throws an AMQException if the delivery tag is not known
+ channel.acknowledgeMessage(body.deliveryTag, body.multiple);
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/BasicCancelMethodHandler.java b/java/broker/src/org/apache/qpid/server/handler/BasicCancelMethodHandler.java
new file mode 100644
index 0000000000..52d6d9f0f1
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/BasicCancelMethodHandler.java
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.BasicCancelBody;
+import org.apache.qpid.framing.BasicCancelOkBody;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.AMQException;
+
+public class BasicCancelMethodHandler implements StateAwareMethodListener<BasicCancelBody>
+{
+ private static final BasicCancelMethodHandler _instance = new BasicCancelMethodHandler();
+
+ public static BasicCancelMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private BasicCancelMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<BasicCancelBody> evt) throws AMQException
+ {
+ final AMQChannel channel = protocolSession.getChannel(evt.getChannelId());
+ final BasicCancelBody body = evt.getMethod();
+ channel.unsubscribeConsumer(protocolSession, body.consumerTag);
+ if(!body.nowait)
+ {
+ final AMQFrame responseFrame = BasicCancelOkBody.createAMQFrame(evt.getChannelId(), body.consumerTag);
+ protocolSession.writeFrame(responseFrame);
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java b/java/broker/src/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java
new file mode 100644
index 0000000000..7da5863044
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/BasicConsumeMethodHandler.java
@@ -0,0 +1,90 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.framing.BasicConsumeBody;
+import org.apache.qpid.framing.BasicConsumeOkBody;
+import org.apache.qpid.framing.ConnectionCloseBody;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.ConsumerTagNotUniqueException;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.log4j.Logger;
+
+public class BasicConsumeMethodHandler implements StateAwareMethodListener<BasicConsumeBody>
+{
+ private static final Logger _log = Logger.getLogger(BasicConsumeMethodHandler.class);
+
+ private static final BasicConsumeMethodHandler _instance = new BasicConsumeMethodHandler();
+
+ public static BasicConsumeMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private BasicConsumeMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession session,
+ AMQMethodEvent<BasicConsumeBody> evt) throws AMQException
+ {
+ BasicConsumeBody body = evt.getMethod();
+ final int channelId = evt.getChannelId();
+
+ AMQChannel channel = session.getChannel(channelId);
+ if (channel == null)
+ {
+ _log.error("Channel " + channelId + " not found");
+ // TODO: either alert or error that the
+ }
+ else
+ {
+ AMQQueue queue = body.queue == null ? channel.getDefaultQueue() : queueRegistry.getQueue(body.queue);
+
+ if(queue == null)
+ {
+ _log.info("No queue for '" + body.queue + "'");
+ }
+ try
+ {
+ String consumerTag = channel.subscribeToQueue(body.consumerTag, queue, session, !body.noAck);
+ if(!body.nowait)
+ {
+ session.writeFrame(BasicConsumeOkBody.createAMQFrame(channelId, consumerTag));
+ }
+
+ //now allow queue to start async processing of any backlog of messages
+ queue.deliverAsync();
+ }
+ catch(ConsumerTagNotUniqueException e)
+ {
+ String msg = "Non-unique consumer tag, '" + body.consumerTag + "'";
+ session.writeFrame(ConnectionCloseBody.createAMQFrame(channelId, AMQConstant.NOT_ALLOWED.getCode(), msg, BasicConsumeBody.CLASS_ID, BasicConsumeBody.METHOD_ID));
+ }
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/BasicPublishMethodHandler.java b/java/broker/src/org/apache/qpid/server/handler/BasicPublishMethodHandler.java
new file mode 100644
index 0000000000..f2f660299d
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/BasicPublishMethodHandler.java
@@ -0,0 +1,77 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.BasicPublishBody;
+import org.apache.qpid.framing.ChannelCloseBody;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class BasicPublishMethodHandler implements StateAwareMethodListener<BasicPublishBody>
+{
+ private static final BasicPublishMethodHandler _instance = new BasicPublishMethodHandler();
+
+ public static BasicPublishMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private BasicPublishMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<BasicPublishBody> evt) throws AMQException
+ {
+ final BasicPublishBody body = evt.getMethod();
+
+ // TODO: check the delivery tag field details - is it unique across the broker or per subscriber?
+ if (body.exchange == null)
+ {
+ body.exchange = "amq.direct";
+ }
+ Exchange e = exchangeRegistry.getExchange(body.exchange);
+ // if the exchange does not exist we raise a channel exception
+ if (e == null)
+ {
+ protocolSession.closeChannel(evt.getChannelId());
+ // TODO: modify code gen to make getClazz and getMethod public methods rather than protected
+ // then we can remove the hardcoded 0,0
+ AMQFrame cf = ChannelCloseBody.createAMQFrame(evt.getChannelId(), 500, "Unknown exchange name", 0, 0);
+ protocolSession.writeFrame(cf);
+ }
+ else
+ {
+ // The partially populated BasicDeliver frame plus the received route body
+ // is stored in the channel. Once the final body frame has been received
+ // it is routed to the exchange.
+ AMQChannel channel = protocolSession.getChannel(evt.getChannelId());
+ channel.setPublishFrame(body, protocolSession);
+ }
+ }
+}
+
diff --git a/java/broker/src/org/apache/qpid/server/handler/BasicQosHandler.java b/java/broker/src/org/apache/qpid/server/handler/BasicQosHandler.java
new file mode 100644
index 0000000000..0d1c039207
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/BasicQosHandler.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.BasicQosBody;
+import org.apache.qpid.framing.BasicQosOkBody;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.AMQException;
+
+public class BasicQosHandler implements StateAwareMethodListener<BasicQosBody>
+{
+ private static final BasicQosHandler _instance = new BasicQosHandler();
+
+ public static BasicQosHandler getInstance()
+ {
+ return _instance;
+ }
+
+ public void methodReceived(AMQStateManager stateMgr, QueueRegistry queues, ExchangeRegistry exchanges,
+ AMQProtocolSession session, AMQMethodEvent<BasicQosBody> evt) throws AMQException
+ {
+ session.getChannel(evt.getChannelId()).setPrefetchCount(evt.getMethod().prefetchCount);
+ session.writeFrame(new AMQFrame(evt.getChannelId(), new BasicQosOkBody()));
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/BasicRecoverMethodHandler.java b/java/broker/src/org/apache/qpid/server/handler/BasicRecoverMethodHandler.java
new file mode 100644
index 0000000000..1dce283c9e
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/BasicRecoverMethodHandler.java
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.framing.BasicRecoverBody;
+import org.apache.qpid.AMQException;
+import org.apache.log4j.Logger;
+
+public class BasicRecoverMethodHandler implements StateAwareMethodListener<BasicRecoverBody>
+{
+ private static final Logger _logger = Logger.getLogger(BasicRecoverMethodHandler.class);
+
+ private static final BasicRecoverMethodHandler _instance = new BasicRecoverMethodHandler();
+
+ public static BasicRecoverMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<BasicRecoverBody> evt) throws AMQException
+ {
+ _logger.debug("Recover received on protocol session " + protocolSession + " and channel " + evt.getChannelId());
+ AMQChannel channel = protocolSession.getChannel(evt.getChannelId());
+ if (channel == null)
+ {
+ throw new AMQException("Unknown channel " + evt.getChannelId());
+ }
+ channel.resend(protocolSession);
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/ChannelCloseHandler.java b/java/broker/src/org/apache/qpid/server/handler/ChannelCloseHandler.java
new file mode 100644
index 0000000000..1b03f15d22
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/ChannelCloseHandler.java
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.ChannelCloseBody;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.ChannelCloseOkBody;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class ChannelCloseHandler implements StateAwareMethodListener<ChannelCloseBody>
+{
+ private static final Logger _logger = Logger.getLogger(ChannelCloseHandler.class);
+
+ private static ChannelCloseHandler _instance = new ChannelCloseHandler();
+
+ public static ChannelCloseHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ChannelCloseHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<ChannelCloseBody> evt) throws AMQException
+ {
+ ChannelCloseBody body = evt.getMethod();
+ _logger.info("Received channel close for id " + evt.getChannelId() + " citing class " + body.classId +
+ " and method " + body.methodId);
+ protocolSession.closeChannel(evt.getChannelId());
+ AMQFrame response = ChannelCloseOkBody.createAMQFrame(evt.getChannelId());
+ protocolSession.writeFrame(response);
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/ChannelCloseOkHandler.java b/java/broker/src/org/apache/qpid/server/handler/ChannelCloseOkHandler.java
new file mode 100644
index 0000000000..7731e56d4d
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/ChannelCloseOkHandler.java
@@ -0,0 +1,51 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.ChannelCloseOkBody;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.log4j.Logger;
+
+public class ChannelCloseOkHandler implements StateAwareMethodListener<ChannelCloseOkBody>
+{
+ private static final Logger _logger = Logger.getLogger(ChannelCloseOkHandler.class);
+
+ private static ChannelCloseOkHandler _instance = new ChannelCloseOkHandler();
+
+ public static ChannelCloseOkHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ChannelCloseOkHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<ChannelCloseOkBody> evt) throws AMQException
+ {
+ _logger.info("Received channel-close-ok for channel-id " + evt.getChannelId());
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/ChannelFlowHandler.java b/java/broker/src/org/apache/qpid/server/handler/ChannelFlowHandler.java
new file mode 100644
index 0000000000..fb26549fad
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/ChannelFlowHandler.java
@@ -0,0 +1,61 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.ChannelFlowBody;
+import org.apache.qpid.framing.ChannelFlowOkBody;
+import org.apache.qpid.framing.ChannelCloseBody;
+import org.apache.qpid.AMQException;
+
+public class ChannelFlowHandler implements StateAwareMethodListener<ChannelFlowBody>
+{
+ private static final Logger _logger = Logger.getLogger(ChannelFlowHandler.class);
+
+ private static ChannelFlowHandler _instance = new ChannelFlowHandler();
+
+ public static ChannelFlowHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ChannelFlowHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<ChannelFlowBody> evt) throws AMQException
+ {
+ ChannelFlowBody body = evt.getMethod();
+
+ AMQChannel channel = protocolSession.getChannel(evt.getChannelId());
+ channel.setSuspended(!body.active);
+ _logger.info("Channel.Flow for channel " + evt.getChannelId() + ", active=" + body.active);
+
+ AMQFrame response = ChannelFlowOkBody.createAMQFrame(evt.getChannelId(), body.active);
+ protocolSession.writeFrame(response);
+ }}
diff --git a/java/broker/src/org/apache/qpid/server/handler/ChannelOpenHandler.java b/java/broker/src/org/apache/qpid/server/handler/ChannelOpenHandler.java
new file mode 100644
index 0000000000..634cd70469
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/ChannelOpenHandler.java
@@ -0,0 +1,58 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.ChannelOpenBody;
+import org.apache.qpid.framing.ChannelOpenOkBody;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class ChannelOpenHandler implements StateAwareMethodListener<ChannelOpenBody>
+{
+ private static ChannelOpenHandler _instance = new ChannelOpenHandler();
+
+ public static ChannelOpenHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ChannelOpenHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<ChannelOpenBody> evt) throws AMQException
+ {
+ IApplicationRegistry registry = ApplicationRegistry.getInstance();
+ final AMQChannel channel = new AMQChannel(evt.getChannelId(), registry.getMessageStore(),
+ exchangeRegistry);
+ protocolSession.addChannel(channel);
+ AMQFrame response = ChannelOpenOkBody.createAMQFrame(evt.getChannelId());
+ protocolSession.writeFrame(response);
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java b/java/broker/src/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java
new file mode 100644
index 0000000000..f78d6f7276
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/ConnectionCloseMethodHandler.java
@@ -0,0 +1,65 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.framing.ConnectionCloseBody;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.ConnectionCloseOkBody;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.AMQException;
+import org.apache.log4j.Logger;
+
+public class ConnectionCloseMethodHandler implements StateAwareMethodListener<ConnectionCloseBody>
+{
+ private static final Logger _logger = Logger.getLogger(ConnectionCloseMethodHandler.class);
+
+ private static ConnectionCloseMethodHandler _instance = new ConnectionCloseMethodHandler();
+
+ public static ConnectionCloseMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ConnectionCloseMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<ConnectionCloseBody> evt) throws AMQException
+ {
+ final ConnectionCloseBody body = evt.getMethod();
+ _logger.info("ConnectionClose received with reply code/reply text " + body.replyCode + "/" +
+ body.replyText + " for " + protocolSession);
+ try
+ {
+ protocolSession.closeSession();
+ }
+ catch (Exception e)
+ {
+ _logger.error("Error closing protocol session: " + e, e);
+ }
+ final AMQFrame response = ConnectionCloseOkBody.createAMQFrame(evt.getChannelId());
+ protocolSession.writeFrame(response);
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/ConnectionCloseOkMethodHandler.java b/java/broker/src/org/apache/qpid/server/handler/ConnectionCloseOkMethodHandler.java
new file mode 100644
index 0000000000..f918158edb
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/ConnectionCloseOkMethodHandler.java
@@ -0,0 +1,63 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.ConnectionCloseOkBody;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.state.AMQState;
+import org.apache.log4j.Logger;
+
+public class ConnectionCloseOkMethodHandler implements StateAwareMethodListener<ConnectionCloseOkBody>
+{
+ private static final Logger _logger = Logger.getLogger(ConnectionCloseOkMethodHandler.class);
+
+ private static ConnectionCloseOkMethodHandler _instance = new ConnectionCloseOkMethodHandler();
+
+ public static ConnectionCloseOkMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ConnectionCloseOkMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<ConnectionCloseOkBody> evt) throws AMQException
+ {
+ //todo should this not do more than just log the method?
+ _logger.info("Received Connection-close-ok");
+
+ try
+ {
+ stateManager.changeState(AMQState.CONNECTION_CLOSED);
+ protocolSession.closeSession();
+ }
+ catch (Exception e)
+ {
+ _logger.error("Error closing protocol session: " + e, e);
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java b/java/broker/src/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java
new file mode 100644
index 0000000000..7bc28f9eb9
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/ConnectionOpenMethodHandler.java
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.ConnectionOpenBody;
+import org.apache.qpid.framing.ConnectionOpenOkBody;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQState;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class ConnectionOpenMethodHandler implements StateAwareMethodListener<ConnectionOpenBody>
+{
+ private static ConnectionOpenMethodHandler _instance = new ConnectionOpenMethodHandler();
+
+ public static ConnectionOpenMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ConnectionOpenMethodHandler()
+ {
+ }
+
+ private static String generateClientID()
+ {
+ return Long.toString(System.currentTimeMillis());
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<ConnectionOpenBody> evt) throws AMQException
+ {
+ ConnectionOpenBody body = evt.getMethod();
+ String contextKey = body.virtualHost;
+
+ //todo //FIXME The virtual host must be validated by the server for the connection to open-ok
+ // See Spec (0.8.2). Section 3.1.2 Virtual Hosts
+ if (contextKey == null)
+ {
+ contextKey = generateClientID();
+ }
+ protocolSession.setContextKey(contextKey);
+ AMQFrame response = ConnectionOpenOkBody.createAMQFrame((short)0, contextKey);
+ stateManager.changeState(AMQState.CONNECTION_OPEN);
+ protocolSession.writeFrame(response);
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java b/java/broker/src/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java
new file mode 100644
index 0000000000..1c0da4f658
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/ConnectionSecureOkMethodHandler.java
@@ -0,0 +1,115 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.AMQChannelException;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.protocol.HeartbeatConfig;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQState;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.security.auth.AuthenticationManager;
+import org.apache.qpid.server.security.auth.AuthenticationResult;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.log4j.Logger;
+
+import javax.security.sasl.SaslServer;
+import javax.security.sasl.SaslException;
+
+public class ConnectionSecureOkMethodHandler implements StateAwareMethodListener<ConnectionSecureOkBody>
+{
+ private static final Logger _logger = Logger.getLogger(ConnectionSecureOkMethodHandler.class);
+
+ private static ConnectionSecureOkMethodHandler _instance = new ConnectionSecureOkMethodHandler();
+
+ public static ConnectionSecureOkMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ConnectionSecureOkMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<ConnectionSecureOkBody> evt) throws AMQException
+ {
+ ConnectionSecureOkBody body = evt.getMethod();
+
+ AuthenticationManager authMgr = ApplicationRegistry.getInstance().getAuthenticationManager();
+ SaslServer ss = protocolSession.getSaslServer();
+ if (ss == null)
+ {
+ throw new AMQException("No SASL context set up in session");
+ }
+
+ AuthenticationResult authResult = authMgr.authenticate(ss, body.response);
+ switch (authResult.status)
+ {
+ case ERROR:
+ // Can't do this as we violate protocol. Need to send Close
+ // throw new AMQException(AMQConstant.NOT_ALLOWED.getCode(), AMQConstant.NOT_ALLOWED.getName());
+ _logger.info("Authentication failed");
+ stateManager.changeState(AMQState.CONNECTION_CLOSING);
+ AMQFrame close = ConnectionCloseBody.createAMQFrame(0, AMQConstant.NOT_ALLOWED.getCode(),
+ AMQConstant.NOT_ALLOWED.getName(),
+ ConnectionCloseBody.CLASS_ID,
+ ConnectionCloseBody.METHOD_ID);
+ protocolSession.writeFrame(close);
+ disposeSaslServer(protocolSession);
+ break;
+ case SUCCESS:
+ _logger.info("Connected as: " + ss.getAuthorizationID());
+ stateManager.changeState(AMQState.CONNECTION_NOT_TUNED);
+ AMQFrame tune = ConnectionTuneBody.createAMQFrame(0, Integer.MAX_VALUE,
+ ConnectionStartOkMethodHandler.getConfiguredFrameSize(),
+ HeartbeatConfig.getInstance().getDelay());
+ protocolSession.writeFrame(tune);
+ disposeSaslServer(protocolSession);
+ break;
+ case CONTINUE:
+ stateManager.changeState(AMQState.CONNECTION_NOT_AUTH);
+ AMQFrame challenge = ConnectionSecureBody.createAMQFrame(0, authResult.challenge);
+ protocolSession.writeFrame(challenge);
+ }
+ }
+
+ private void disposeSaslServer(AMQProtocolSession ps)
+ {
+ SaslServer ss = ps.getSaslServer();
+ if (ss != null)
+ {
+ ps.setSaslServer(null);
+ try
+ {
+ ss.dispose();
+ }
+ catch (SaslException e)
+ {
+ _logger.error("Error disposing of Sasl server: " + e);
+ }
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java b/java/broker/src/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java
new file mode 100644
index 0000000000..5715ce181b
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/ConnectionStartOkMethodHandler.java
@@ -0,0 +1,127 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.commons.configuration.Configuration;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.ConnectionSecureBody;
+import org.apache.qpid.framing.ConnectionStartOkBody;
+import org.apache.qpid.framing.ConnectionTuneBody;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.protocol.HeartbeatConfig;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.security.auth.AuthenticationManager;
+import org.apache.qpid.server.security.auth.AuthenticationResult;
+import org.apache.qpid.server.state.AMQState;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+
+public class ConnectionStartOkMethodHandler implements StateAwareMethodListener<ConnectionStartOkBody>
+{
+ private static final Logger _logger = Logger.getLogger(ConnectionStartOkMethodHandler.class);
+
+ private static ConnectionStartOkMethodHandler _instance = new ConnectionStartOkMethodHandler();
+
+ private static final int DEFAULT_FRAME_SIZE = 65536;
+
+ public static StateAwareMethodListener<ConnectionStartOkBody> getInstance()
+ {
+ return _instance;
+ }
+
+ private ConnectionStartOkMethodHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<ConnectionStartOkBody> evt) throws AMQException
+ {
+ final ConnectionStartOkBody body = evt.getMethod();
+ _logger.info("SASL Mechanism selected: " + body.mechanism);
+ _logger.info("Locale selected: " + body.locale);
+
+ AuthenticationManager authMgr = ApplicationRegistry.getInstance().getAuthenticationManager();
+
+ SaslServer ss = null;
+ try
+ {
+ ss = authMgr.createSaslServer(body.mechanism, protocolSession.getLocalFQDN());
+ protocolSession.setSaslServer(ss);
+
+ AuthenticationResult authResult = authMgr.authenticate(ss, body.response);
+
+ switch (authResult.status)
+ {
+ case ERROR:
+ throw new AMQException("Authentication failed");
+ case SUCCESS:
+ _logger.info("Connected as: " + ss.getAuthorizationID());
+ stateManager.changeState(AMQState.CONNECTION_NOT_TUNED);
+ AMQFrame tune = ConnectionTuneBody.createAMQFrame(0, Integer.MAX_VALUE, getConfiguredFrameSize(),
+ HeartbeatConfig.getInstance().getDelay());
+ protocolSession.writeFrame(tune);
+ break;
+ case CONTINUE:
+ stateManager.changeState(AMQState.CONNECTION_NOT_AUTH);
+ AMQFrame challenge = ConnectionSecureBody.createAMQFrame(0, authResult.challenge);
+ protocolSession.writeFrame(challenge);
+ }
+ }
+ catch (SaslException e)
+ {
+ disposeSaslServer(protocolSession);
+ throw new AMQException("SASL error: " + e, e);
+ }
+ }
+
+ private void disposeSaslServer(AMQProtocolSession ps)
+ {
+ SaslServer ss = ps.getSaslServer();
+ if (ss != null)
+ {
+ ps.setSaslServer(null);
+ try
+ {
+ ss.dispose();
+ }
+ catch (SaslException e)
+ {
+ _logger.error("Error disposing of Sasl server: " + e);
+ }
+ }
+ }
+
+ static int getConfiguredFrameSize()
+ {
+ final Configuration config = ApplicationRegistry.getInstance().getConfiguration();
+ final int framesize = config.getInt("advanced.framesize", DEFAULT_FRAME_SIZE);
+ _logger.info("Framesize set to " + framesize);
+ return framesize;
+ }
+}
+
diff --git a/java/broker/src/org/apache/qpid/server/handler/ConnectionTuneOkMethodHandler.java b/java/broker/src/org/apache/qpid/server/handler/ConnectionTuneOkMethodHandler.java
new file mode 100644
index 0000000000..05ca10fec5
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/ConnectionTuneOkMethodHandler.java
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.ConnectionTuneOkBody;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQState;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class ConnectionTuneOkMethodHandler implements StateAwareMethodListener<ConnectionTuneOkBody>
+{
+ private static final Logger _logger = Logger.getLogger(ConnectionTuneOkMethodHandler.class);
+
+ private static ConnectionTuneOkMethodHandler _instance = new ConnectionTuneOkMethodHandler();
+
+ public static ConnectionTuneOkMethodHandler getInstance()
+ {
+ return _instance;
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<ConnectionTuneOkBody> evt) throws AMQException
+ {
+ ConnectionTuneOkBody body = evt.getMethod();
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug(body);
+ }
+ stateManager.changeState(AMQState.CONNECTION_NOT_OPENED);
+ protocolSession.initHeartbeats(body.heartbeat);
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/ExchangeDeclareHandler.java b/java/broker/src/org/apache/qpid/server/handler/ExchangeDeclareHandler.java
new file mode 100644
index 0000000000..444a54a4f2
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/ExchangeDeclareHandler.java
@@ -0,0 +1,79 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.ExchangeDeclareBody;
+import org.apache.qpid.framing.ExchangeDeclareOkBody;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.exchange.ExchangeFactory;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+public class ExchangeDeclareHandler implements StateAwareMethodListener<ExchangeDeclareBody>
+{
+ private static final Logger _logger = Logger.getLogger(ExchangeDeclareHandler.class);
+
+ private static final ExchangeDeclareHandler _instance = new ExchangeDeclareHandler();
+
+ public static ExchangeDeclareHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private final ExchangeFactory exchangeFactory;
+
+ private ExchangeDeclareHandler()
+ {
+ exchangeFactory = ApplicationRegistry.getInstance().getExchangeFactory();
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<ExchangeDeclareBody> evt) throws AMQException
+ {
+ final ExchangeDeclareBody body = evt.getMethod();
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Request to declare exchange of type " + body.type + " with name " + body.exchange);
+ }
+ synchronized(exchangeRegistry)
+ {
+ Exchange exchange = exchangeRegistry.getExchange(body.exchange);
+
+ if (exchange == null)
+ {
+ exchange = exchangeFactory.createExchange(body.exchange, body.type, body.durable,
+ body.passive, body.ticket);
+ exchangeRegistry.registerExchange(exchange);
+ }
+ }
+ if(!body.nowait)
+ {
+ AMQFrame response = ExchangeDeclareOkBody.createAMQFrame(evt.getChannelId());
+ protocolSession.writeFrame(response);
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/ExchangeDeleteHandler.java b/java/broker/src/org/apache/qpid/server/handler/ExchangeDeleteHandler.java
new file mode 100644
index 0000000000..441e991872
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/ExchangeDeleteHandler.java
@@ -0,0 +1,62 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.ExchangeDeleteBody;
+import org.apache.qpid.framing.ExchangeDeleteOkBody;
+import org.apache.qpid.server.exchange.ExchangeInUseException;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class ExchangeDeleteHandler implements StateAwareMethodListener<ExchangeDeleteBody>
+{
+ private static final ExchangeDeleteHandler _instance = new ExchangeDeleteHandler();
+
+ public static ExchangeDeleteHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private ExchangeDeleteHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<ExchangeDeleteBody> evt) throws AMQException
+ {
+ ExchangeDeleteBody body = evt.getMethod();
+ try
+ {
+ exchangeRegistry.unregisterExchange(body.exchange, body.ifUnused);
+ AMQFrame response = ExchangeDeleteOkBody.createAMQFrame(evt.getChannelId());
+ protocolSession.writeFrame(response);
+ }
+ catch (ExchangeInUseException e)
+ {
+ // TODO: sort out consistent channel close mechanism that does all clean up etc.
+ }
+
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/OnCurrentThreadExecutor.java b/java/broker/src/org/apache/qpid/server/handler/OnCurrentThreadExecutor.java
new file mode 100644
index 0000000000..a689366a2f
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/OnCurrentThreadExecutor.java
@@ -0,0 +1,31 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import java.util.concurrent.Executor;
+
+/**
+ * An executor that executes the task on the current thread.
+ */
+public class OnCurrentThreadExecutor implements Executor
+{
+ public void execute(Runnable command)
+ {
+ command.run();
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/QueueBindHandler.java b/java/broker/src/org/apache/qpid/server/handler/QueueBindHandler.java
new file mode 100644
index 0000000000..98eec37a4a
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/QueueBindHandler.java
@@ -0,0 +1,94 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.protocol.AMQConstant;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.QueueBindBody;
+import org.apache.qpid.framing.QueueBindOkBody;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class QueueBindHandler implements StateAwareMethodListener<QueueBindBody>
+{
+ private static final Logger _log = Logger.getLogger(QueueBindHandler.class);
+
+ private static final QueueBindHandler _instance = new QueueBindHandler();
+
+ public static QueueBindHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private QueueBindHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<QueueBindBody> evt) throws AMQException
+ {
+ final QueueBindBody body = evt.getMethod();
+ final AMQQueue queue;
+ if (body.queue == null)
+ {
+ queue = protocolSession.getChannel(evt.getChannelId()).getDefaultQueue();
+ if (queue == null)
+ {
+ throw new AMQException("No default queue defined on channel and queue was null");
+ }
+ if (body.routingKey == null)
+ {
+ body.routingKey = queue.getName();
+ }
+ }
+ else
+ {
+ queue = queueRegistry.getQueue(body.queue);
+ }
+
+ if (queue == null)
+ {
+ throw body.getChannelException(AMQConstant.NOT_FOUND.getCode(), "Queue " + body.queue + " does not exist.");
+ }
+ final Exchange exch = exchangeRegistry.getExchange(body.exchange);
+ if (exch == null)
+ {
+ throw body.getChannelException(AMQConstant.NOT_FOUND.getCode(), "Exchange " + body.exchange + " does not exist.");
+ }
+ exch.registerQueue(body.routingKey, queue, body.arguments);
+ queue.bind(body.routingKey, exch);
+ if (_log.isInfoEnabled())
+ {
+ _log.info("Binding queue " + queue + " to exchange " + exch + " with routing key " + body.routingKey);
+ }
+ if (!body.nowait)
+ {
+ final AMQFrame response = QueueBindOkBody.createAMQFrame(evt.getChannelId());
+ protocolSession.writeFrame(response);
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/QueueDeclareHandler.java b/java/broker/src/org/apache/qpid/server/handler/QueueDeclareHandler.java
new file mode 100644
index 0000000000..d1eb387748
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/QueueDeclareHandler.java
@@ -0,0 +1,124 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.configuration.Configured;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.QueueDeclareBody;
+import org.apache.qpid.framing.QueueDeclareOkBody;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.configuration.Configurator;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+import java.text.MessageFormat;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class QueueDeclareHandler implements StateAwareMethodListener<QueueDeclareBody>
+{
+ private static final Logger _log = Logger.getLogger(QueueDeclareHandler.class);
+
+ private static final QueueDeclareHandler _instance = new QueueDeclareHandler();
+
+ public static QueueDeclareHandler getInstance()
+ {
+ return _instance;
+ }
+
+ @Configured(path = "queue.auto_register", defaultValue = "false")
+ public boolean autoRegister;
+
+ private final AtomicInteger _counter = new AtomicInteger();
+
+ private final MessageStore _store;
+
+ protected QueueDeclareHandler()
+ {
+ Configurator.configure(this);
+ _store = ApplicationRegistry.getInstance().getMessageStore();
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<QueueDeclareBody> evt) throws AMQException
+ {
+ QueueDeclareBody body = evt.getMethod();
+
+ // if we aren't given a queue name, we create one which we return to the client
+ if (body.queue == null)
+ {
+ body.queue = createName();
+ }
+ //TODO: do we need to check that the queue already exists with exactly the same "configuration"?
+
+ synchronized (queueRegistry)
+ {
+ AMQQueue queue;
+ if ((queue = queueRegistry.getQueue(body.queue)) == null)
+ {
+ queue = createQueue(body, queueRegistry, protocolSession);
+ if (queue.isDurable() && !queue.isAutoDelete())
+ {
+ _store.createQueue(queue);
+ }
+ queueRegistry.registerQueue(queue);
+ if (autoRegister)
+ {
+ Exchange defaultExchange = exchangeRegistry.getExchange("amq.direct");
+ defaultExchange.registerQueue(body.queue, queue, null);
+ queue.bind(body.queue, defaultExchange);
+ _log.info("Queue " + body.queue + " bound to default exchange");
+ }
+ }
+ //set this as the default queue on the channel:
+ protocolSession.getChannel(evt.getChannelId()).setDefaultQueue(queue);
+ }
+ if (!body.nowait)
+ {
+ AMQFrame response = QueueDeclareOkBody.createAMQFrame(evt.getChannelId(), body.queue, 0L, 0L);
+ _log.info("Queue " + body.queue + " declared successfully");
+ protocolSession.writeFrame(response);
+ }
+ }
+
+ protected String createName()
+ {
+ return "tmp_" + pad(_counter.incrementAndGet());
+ }
+
+ protected static String pad(int value)
+ {
+ return MessageFormat.format("{0,number,0000000000000}", value);
+ }
+
+ protected AMQQueue createQueue(QueueDeclareBody body, QueueRegistry registry, AMQProtocolSession session)
+ throws AMQException
+ {
+ String owner = body.exclusive ? session.getContextKey() : null;
+ return new AMQQueue(body.queue, body.durable, owner, body.autoDelete, registry);
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/QueueDeleteHandler.java b/java/broker/src/org/apache/qpid/server/handler/QueueDeleteHandler.java
new file mode 100644
index 0000000000..82c1d93065
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/QueueDeleteHandler.java
@@ -0,0 +1,84 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.framing.QueueDeleteBody;
+import org.apache.qpid.framing.QueueDeleteOkBody;
+import org.apache.qpid.AMQException;
+
+public class QueueDeleteHandler implements StateAwareMethodListener<QueueDeleteBody>
+{
+ private static final QueueDeleteHandler _instance = new QueueDeleteHandler();
+
+ public static QueueDeleteHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private final boolean _failIfNotFound;
+ private final MessageStore _store;
+
+ public QueueDeleteHandler()
+ {
+ this(true);
+ }
+
+ public QueueDeleteHandler(boolean failIfNotFound)
+ {
+ _failIfNotFound = failIfNotFound;
+ _store = ApplicationRegistry.getInstance().getMessageStore();
+
+ }
+
+ public void methodReceived(AMQStateManager stateMgr, QueueRegistry queues, ExchangeRegistry exchanges, AMQProtocolSession session, AMQMethodEvent<QueueDeleteBody> evt) throws AMQException
+ {
+ QueueDeleteBody body = evt.getMethod();
+ AMQQueue queue;
+ if(body.queue == null)
+ {
+ queue = session.getChannel(evt.getChannelId()).getDefaultQueue();
+ }
+ else
+ {
+ queue = queues.getQueue(body.queue);
+ }
+
+ if(queue == null)
+ {
+ if(_failIfNotFound)
+ {
+ throw body.getChannelException(404, "Queue " + body.queue + " does not exist.");
+ }
+ }
+ else
+ {
+ int purged = queue.delete(body.ifUnused, body.ifEmpty);
+ _store.removeQueue(queue.getName());
+ session.writeFrame(QueueDeleteOkBody.createAMQFrame(evt.getChannelId(), purged));
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/TxCommitHandler.java b/java/broker/src/org/apache/qpid/server/handler/TxCommitHandler.java
new file mode 100644
index 0000000000..ce18c94c2b
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/TxCommitHandler.java
@@ -0,0 +1,55 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.TxCommitBody;
+import org.apache.qpid.framing.TxCommitOkBody;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class TxCommitHandler implements StateAwareMethodListener<TxCommitBody>
+{
+ private static TxCommitHandler _instance = new TxCommitHandler();
+
+ public static TxCommitHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private TxCommitHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<TxCommitBody> evt) throws AMQException
+ {
+
+ try{
+ protocolSession.getChannel(evt.getChannelId()).commit();
+ protocolSession.writeFrame(TxCommitOkBody.createAMQFrame(evt.getChannelId()));
+ }catch(AMQException e){
+ throw evt.getMethod().getChannelException(e.getErrorCode(), "Failed to commit: " + e.getMessage());
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/TxRollbackHandler.java b/java/broker/src/org/apache/qpid/server/handler/TxRollbackHandler.java
new file mode 100644
index 0000000000..ff2d79fb95
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/TxRollbackHandler.java
@@ -0,0 +1,59 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.TxRollbackBody;
+import org.apache.qpid.framing.TxRollbackOkBody;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+import org.apache.qpid.server.AMQChannel;
+
+public class TxRollbackHandler implements StateAwareMethodListener<TxRollbackBody>
+{
+ private static TxRollbackHandler _instance = new TxRollbackHandler();
+
+ public static TxRollbackHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private TxRollbackHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<TxRollbackBody> evt) throws AMQException
+ {
+ try{
+ AMQChannel channel = protocolSession.getChannel(evt.getChannelId());
+ channel.rollback();
+ protocolSession.writeFrame(TxRollbackOkBody.createAMQFrame(evt.getChannelId()));
+ //Now resend all the unacknowledged messages back to the original subscribers.
+ //(Must be done after the TxnRollback-ok response).
+ channel.resend(protocolSession);
+ }catch(AMQException e){
+ throw evt.getMethod().getChannelException(e.getErrorCode(), "Failed to rollback: " + e.getMessage());
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/handler/TxSelectHandler.java b/java/broker/src/org/apache/qpid/server/handler/TxSelectHandler.java
new file mode 100644
index 0000000000..d55930489c
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/handler/TxSelectHandler.java
@@ -0,0 +1,50 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.handler;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.TxSelectBody;
+import org.apache.qpid.framing.TxSelectOkBody;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQStateManager;
+import org.apache.qpid.server.state.StateAwareMethodListener;
+
+public class TxSelectHandler implements StateAwareMethodListener<TxSelectBody>
+{
+ private static TxSelectHandler _instance = new TxSelectHandler();
+
+ public static TxSelectHandler getInstance()
+ {
+ return _instance;
+ }
+
+ private TxSelectHandler()
+ {
+ }
+
+ public void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<TxSelectBody> evt) throws AMQException
+ {
+ protocolSession.getChannel(evt.getChannelId()).setTransactional(true);
+ protocolSession.writeFrame(TxSelectOkBody.createAMQFrame(evt.getChannelId()));
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/jms/JmsConsumer.java b/java/broker/src/org/apache/qpid/server/jms/JmsConsumer.java
new file mode 100644
index 0000000000..da82d2166e
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/jms/JmsConsumer.java
@@ -0,0 +1,107 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.jms;
+
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.AMQException;
+
+public class JmsConsumer
+{
+ private int _prefetchValue;
+
+ private PrefetchUnits _prefetchUnits;
+
+ private boolean _noLocal;
+
+ private boolean _autoAck;
+
+ private boolean _exclusive;
+
+ private AMQProtocolSession _protocolSession;
+
+ public enum PrefetchUnits
+ {
+ OCTETS,
+ MESSAGES
+ }
+
+ public int getPrefetchValue()
+ {
+ return _prefetchValue;
+ }
+
+ public void setPrefetchValue(int prefetchValue)
+ {
+ _prefetchValue = prefetchValue;
+ }
+
+ public PrefetchUnits getPrefetchUnits()
+ {
+ return _prefetchUnits;
+ }
+
+ public void setPrefetchUnits(PrefetchUnits prefetchUnits)
+ {
+ _prefetchUnits = prefetchUnits;
+ }
+
+ public boolean isNoLocal()
+ {
+ return _noLocal;
+ }
+
+ public void setNoLocal(boolean noLocal)
+ {
+ _noLocal = noLocal;
+ }
+
+ public boolean isAutoAck()
+ {
+ return _autoAck;
+ }
+
+ public void setAutoAck(boolean autoAck)
+ {
+ _autoAck = autoAck;
+ }
+
+ public boolean isExclusive()
+ {
+ return _exclusive;
+ }
+
+ public void setExclusive(boolean exclusive)
+ {
+ _exclusive = exclusive;
+ }
+
+ public AMQProtocolSession getProtocolSession()
+ {
+ return _protocolSession;
+ }
+
+ public void setProtocolSession(AMQProtocolSession protocolSession)
+ {
+ _protocolSession = protocolSession;
+ }
+
+ public void deliverMessage() throws AMQException
+ {
+
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/management/AMQManagedObject.java b/java/broker/src/org/apache/qpid/server/management/AMQManagedObject.java
new file mode 100644
index 0000000000..fea955b93b
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/management/AMQManagedObject.java
@@ -0,0 +1,65 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.management;
+
+import javax.management.ListenerNotFoundException;
+import javax.management.NotificationBroadcaster;
+import javax.management.NotificationBroadcasterSupport;
+import javax.management.NotificationFilter;
+import javax.management.NotificationListener;
+
+/**
+ * This class provides additinal feature of Notification Broadcaster to the
+ * DefaultManagedObject.
+ * @author Bhupendra Bhardwaj
+ * @version 0.1
+ */
+public abstract class AMQManagedObject extends DefaultManagedObject
+ implements NotificationBroadcaster
+{
+ /**
+ * broadcaster support class
+ */
+ protected NotificationBroadcasterSupport _broadcaster = new NotificationBroadcasterSupport();
+
+ /**
+ * sequence number for notifications
+ */
+ protected long _notificationSequenceNumber = 0;
+
+ protected AMQManagedObject(Class<?> managementInterface, String typeName)
+ {
+ super(managementInterface, typeName);
+ }
+
+
+ // notification broadcaster implementation
+
+ public void addNotificationListener(NotificationListener listener,
+ NotificationFilter filter,
+ Object handback)
+ {
+ _broadcaster.addNotificationListener(listener, filter, handback);
+ }
+
+ public void removeNotificationListener(NotificationListener listener)
+ throws ListenerNotFoundException
+ {
+ _broadcaster.removeNotificationListener(listener);
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/management/DefaultManagedObject.java b/java/broker/src/org/apache/qpid/server/management/DefaultManagedObject.java
new file mode 100644
index 0000000000..bb8603f8b4
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/management/DefaultManagedObject.java
@@ -0,0 +1,126 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.management;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+import javax.management.ObjectName;
+import javax.management.MalformedObjectNameException;
+
+/**
+ * Provides implementation of the boilerplate ManagedObject interface. Most managed objects should find it useful
+ * to extend this class rather than implementing ManagedObject from scratch.
+ *
+ */
+public abstract class DefaultManagedObject implements ManagedObject
+{
+ private Class<?> _managementInterface;
+
+ private String _typeName;
+
+ protected DefaultManagedObject(Class<?> managementInterface, String typeName)
+ {
+ _managementInterface = managementInterface;
+ _typeName = typeName;
+ }
+
+ public String getType()
+ {
+ return _typeName;
+ }
+
+ public Class<?> getManagementInterface()
+ {
+ return _managementInterface;
+ }
+
+ public void register() throws AMQException
+ {
+ try
+ {
+ ApplicationRegistry.getInstance().getManagedObjectRegistry().registerObject(this);
+ }
+ catch (Exception e)
+ {
+ throw new AMQException("Error registering managed object " + this + ": " + e, e);
+ }
+ }
+
+ public void unregister() throws AMQException
+ {
+ try
+ {
+ ApplicationRegistry.getInstance().getManagedObjectRegistry().unregisterObject(this);
+ }
+ catch (Exception e)
+ {
+ throw new AMQException("Error unregistering managed object: " + this + ": " + e, e);
+ }
+ }
+
+ public String toString()
+ {
+ return getObjectInstanceName() + "[" + getType() + "]";
+ }
+
+ /**
+ * Created the ObjectName as per the JMX Specs
+ * @return ObjectName
+ * @throws MalformedObjectNameException
+ */
+ public ObjectName getObjectName()
+ throws MalformedObjectNameException
+ {
+ String name = jmxEncode(new StringBuffer(getObjectInstanceName()), 0).toString();
+ StringBuffer objectName = new StringBuffer(ManagedObject.DOMAIN);
+ objectName.append(":type=").append(getType());
+ objectName.append(",name=").append(name);
+
+ return new ObjectName(objectName.toString());
+ }
+
+ private static StringBuffer jmxEncode(StringBuffer jmxName, int attrPos)
+ {
+ for (int i = attrPos; i < jmxName.length(); i++)
+ {
+ if (jmxName.charAt(i) == ',')
+ {
+ jmxName.setCharAt(i, ';');
+ }
+ else if (jmxName.charAt(i) == ':')
+ {
+ jmxName.setCharAt(i, '-');
+ }
+ else if (jmxName.charAt(i) == '?' ||
+ jmxName.charAt(i) == '*' ||
+ jmxName.charAt(i) == '\\')
+ {
+ jmxName.insert(i, '\\');
+ i++;
+ }
+ else if (jmxName.charAt(i) == '\n')
+ {
+ jmxName.insert(i, '\\');
+ i++;
+ jmxName.setCharAt(i, 'n');
+ }
+ }
+ return jmxName;
+ }
+} \ No newline at end of file
diff --git a/java/broker/src/org/apache/qpid/server/management/JMXManagedObjectRegistry.java b/java/broker/src/org/apache/qpid/server/management/JMXManagedObjectRegistry.java
new file mode 100644
index 0000000000..d556973970
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/management/JMXManagedObjectRegistry.java
@@ -0,0 +1,66 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.management;
+
+import org.apache.log4j.Logger;
+
+import javax.management.JMException;
+import javax.management.MBeanServer;
+import javax.management.NotCompliantMBeanException;
+import javax.management.StandardMBean;
+import java.lang.management.ManagementFactory;
+
+public class JMXManagedObjectRegistry implements ManagedObjectRegistry
+{
+ private static final Logger _log = Logger.getLogger(JMXManagedObjectRegistry.class);
+
+ private final MBeanServer _mbeanServer;
+
+ public JMXManagedObjectRegistry()
+ {
+ _log.info("Initialising managed object registry using platform MBean server");
+ // we use the platform MBean server currently but this must be changed or at least be configuurable
+ _mbeanServer = ManagementFactory.getPlatformMBeanServer();
+ }
+
+ public void registerObject(ManagedObject managedObject) throws JMException
+ {
+ try
+ {
+ _mbeanServer.registerMBean(managedObject, managedObject.getObjectName());
+ }
+ catch(NotCompliantMBeanException ex)
+ {
+ // The following is a hack due to a silly change to StandardMBean in JDK 1.6
+ // They have added in generics to get compile time safety which reduces the
+ // flexibility
+ Object o = managedObject;
+ Class<Object> clazz = (Class<Object>) managedObject.getManagementInterface();
+ StandardMBean mbean = new StandardMBean(o, clazz);
+
+ _mbeanServer.registerMBean(mbean, managedObject.getObjectName());
+ }
+
+ }
+
+ public void unregisterObject(ManagedObject managedObject) throws JMException
+ {
+ _mbeanServer.unregisterMBean(managedObject.getObjectName());
+ }
+
+} \ No newline at end of file
diff --git a/java/broker/src/org/apache/qpid/server/management/Managable.java b/java/broker/src/org/apache/qpid/server/management/Managable.java
new file mode 100644
index 0000000000..e62e1c7f87
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/management/Managable.java
@@ -0,0 +1,31 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.management;
+
+/**
+ * Any object that can return a related MBean should implement this interface.
+ *
+ * This enables other classes to get the managed object, which in turn is useful when
+ * constructing relationships between managed objects without having to maintain
+ * separate data structures containing MBeans.
+ *
+ */
+public interface Managable
+{
+ ManagedObject getManagedObject();
+}
diff --git a/java/broker/src/org/apache/qpid/server/management/ManagedBroker.java b/java/broker/src/org/apache/qpid/server/management/ManagedBroker.java
new file mode 100644
index 0000000000..ecf6123c7d
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/management/ManagedBroker.java
@@ -0,0 +1,78 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.qpid.server.management;
+
+import javax.management.JMException;
+import java.io.IOException;
+
+/**
+ * The ManagedBroker is the management interface to expose management
+ * features of the Broker.
+ *
+ * @author Bhupendra Bhardwaj
+ * @version 0.1
+ */
+public interface ManagedBroker
+{
+ static final String TYPE = "BrokerManager";
+
+ /**
+ * Creates a new Exchange.
+ * @param name
+ * @param type
+ * @param durable
+ * @param passive
+ * @throws IOException
+ * @throws JMException
+ */
+ void createNewExchange(String name, String type, boolean durable, boolean passive)
+ throws IOException, JMException;
+
+ /**
+ * unregisters all the channels, queuebindings etc and unregisters
+ * this exchange from managed objects.
+ * @param exchange
+ * @throws IOException
+ * @throws JMException
+ */
+ void unregisterExchange(String exchange)
+ throws IOException, JMException;
+
+ /**
+ * Create a new Queue on the Broker server
+ * @param queueName
+ * @param durable
+ * @param owner
+ * @param autoDelete
+ * @throws IOException
+ * @throws JMException
+ */
+ void createQueue(String queueName, boolean durable, String owner, boolean autoDelete)
+ throws IOException, JMException;
+
+ /**
+ * Unregisters the Queue bindings, removes the subscriptions and unregisters
+ * from the managed objects.
+ * @param queueName
+ * @throws IOException
+ * @throws JMException
+ */
+ void deleteQueue(String queueName)
+ throws IOException, JMException;
+}
diff --git a/java/broker/src/org/apache/qpid/server/management/ManagedObject.java b/java/broker/src/org/apache/qpid/server/management/ManagedObject.java
new file mode 100644
index 0000000000..0643f84744
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/management/ManagedObject.java
@@ -0,0 +1,53 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.management;
+
+import org.apache.qpid.AMQException;
+
+import javax.management.ObjectName;
+import javax.management.MalformedObjectNameException;
+
+/**
+ * This should be implemented by all Managable objects.
+ */
+public interface ManagedObject
+{
+ static final String DOMAIN = "org.apache.qpid";
+
+ /**
+ * @return the name that uniquely identifies this object instance. It must be
+ * unique only among objects of this type at this level in the hierarchy so
+ * the uniqueness should not be too difficult to ensure.
+ */
+ String getObjectInstanceName();
+
+ String getType();
+
+ Class<?> getManagementInterface();
+
+ void register() throws AMQException;
+
+ void unregister() throws AMQException;
+
+ /**
+ * Returns the ObjectName required for the mbeanserver registration.
+ * @return ObjectName
+ * @throws MalformedObjectNameException
+ */
+ ObjectName getObjectName() throws MalformedObjectNameException;
+}
diff --git a/java/broker/src/org/apache/qpid/server/management/ManagedObjectRegistry.java b/java/broker/src/org/apache/qpid/server/management/ManagedObjectRegistry.java
new file mode 100644
index 0000000000..7270ec83b4
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/management/ManagedObjectRegistry.java
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.management;
+
+import javax.management.JMException;
+
+/**
+ * Handles the registration (and unregistration and so on) of managed objects.
+ *
+ * Managed objects are responsible for exposting attributes, operations and notifications. They will expose
+ * these outside the JVM therefore it is important not to use implementation objects directly as managed objects.
+ * Instead, creating inner classes and exposing those is an effective way of exposing internal state in a
+ * controlled way.
+ *
+ * Although we do not explictly use them while targetting Java 5, the enhanced MXBean approach in Java 6 will
+ * be the obvious choice for managed objects.
+ *
+ */
+public interface ManagedObjectRegistry
+{
+ void registerObject(ManagedObject managedObject) throws JMException;
+
+ void unregisterObject(ManagedObject managedObject) throws JMException;
+}
diff --git a/java/broker/src/org/apache/qpid/server/management/ManagementConfiguration.java b/java/broker/src/org/apache/qpid/server/management/ManagementConfiguration.java
new file mode 100644
index 0000000000..ec80009d17
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/management/ManagementConfiguration.java
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.management;
+
+import org.apache.qpid.configuration.Configured;
+
+public class ManagementConfiguration
+{
+ @Configured(path = "management.enabled",
+ defaultValue = "true")
+ public boolean enabled;
+}
diff --git a/java/broker/src/org/apache/qpid/server/management/NoopManagedObjectRegistry.java b/java/broker/src/org/apache/qpid/server/management/NoopManagedObjectRegistry.java
new file mode 100644
index 0000000000..3bf2c8d9ca
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/management/NoopManagedObjectRegistry.java
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.management;
+
+import org.apache.log4j.Logger;
+
+import javax.management.JMException;
+
+/**
+ * This managed object registry does not actually register MBeans. This can be used in tests when management is
+ * not required or when management has been disabled.
+ *
+ */
+public class NoopManagedObjectRegistry implements ManagedObjectRegistry
+{
+ private static final Logger _log = Logger.getLogger(NoopManagedObjectRegistry.class);
+
+ public NoopManagedObjectRegistry()
+ {
+ _log.info("Management is disabled");
+ }
+
+ public void registerObject(ManagedObject managedObject) throws JMException
+ {
+ }
+
+ public void unregisterObject(ManagedObject managedObject) throws JMException
+ {
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/protocol/AMQMethodEvent.java b/java/broker/src/org/apache/qpid/server/protocol/AMQMethodEvent.java
new file mode 100644
index 0000000000..0c8d049951
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/protocol/AMQMethodEvent.java
@@ -0,0 +1,62 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.qpid.framing.AMQMethodBody;
+
+/**
+ * An event that is passed to AMQMethodListeners describing a particular method.
+ * It supplies the:
+ * <ul><li>channel id</li>
+ * <li>protocol method</li>
+ * to listeners. This means that listeners do not need to be stateful.
+ *
+ * In the StateAwareMethodListener, other useful objects such as the protocol session
+ * are made available.
+ *
+ */
+public class AMQMethodEvent<M extends AMQMethodBody>
+{
+ private final M _method;
+
+ private final int _channelId;
+
+ public AMQMethodEvent(int channelId, M method)
+ {
+ _channelId = channelId;
+ _method = method;
+ }
+
+ public M getMethod()
+ {
+ return _method;
+ }
+
+ public int getChannelId()
+ {
+ return _channelId;
+ }
+
+ public String toString()
+ {
+ StringBuilder buf = new StringBuilder("Method event: ");
+ buf.append("\nChannel id: ").append(_channelId);
+ buf.append("\nMethod: ").append(_method);
+ return buf.toString();
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/protocol/AMQMethodListener.java b/java/broker/src/org/apache/qpid/server/protocol/AMQMethodListener.java
new file mode 100644
index 0000000000..e8d973cd91
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/protocol/AMQMethodListener.java
@@ -0,0 +1,52 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.framing.AMQMethodBody;
+
+/**
+ * Interface that allows classes to register for interest in protocol method frames.
+ *
+ */
+public interface AMQMethodListener
+{
+ /**
+ * Invoked when a method frame has been received
+ * @param evt the event that contains the method and channel
+ * @param protocolSession the protocol session associated with the event
+ * @return true if the handler has processed the method frame, false otherwise. Note
+ * that this does not prohibit the method event being delivered to subsequent listeners
+ * but can be used to determine if nobody has dealt with an incoming method frame.
+ * @throws AMQException if an error has occurred. This exception will be delivered
+ * to all registered listeners using the error() method (see below) allowing them to
+ * perform cleanup if necessary.
+ */
+ <B extends AMQMethodBody> boolean methodReceived(AMQMethodEvent<B> evt,
+ AMQProtocolSession protocolSession,
+ QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry) throws AMQException;
+
+ /**
+ * Callback when an error has occurred. Allows listeners to clean up.
+ * @param e
+ */
+ void error(AMQException e);
+}
diff --git a/java/broker/src/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java b/java/broker/src/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java
new file mode 100644
index 0000000000..80aa4756d2
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/protocol/AMQMinaProtocolSession.java
@@ -0,0 +1,603 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.log4j.Logger;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.transport.vmpipe.VmPipeAddress;
+import org.apache.qpid.AMQChannelException;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.codec.AMQCodecFactory;
+import org.apache.qpid.codec.AMQDecoder;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.management.DefaultManagedObject;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.state.AMQStateManager;
+
+import javax.security.sasl.SaslServer;
+import javax.management.ObjectName;
+import javax.management.MalformedObjectNameException;
+import javax.management.openmbean.*;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.net.InetSocketAddress;
+import java.net.SocketAddress;
+
+public class AMQMinaProtocolSession implements AMQProtocolSession, ProtocolVersionList
+{
+ private static final Logger _logger = Logger.getLogger(AMQProtocolSession.class);
+
+ private final IoSession _minaProtocolSession;
+
+ private String _contextKey;
+
+ private final Map<Integer, AMQChannel> _channelMap = new HashMap<Integer, AMQChannel>();
+
+ private final CopyOnWriteArraySet<AMQMethodListener> _frameListeners = new CopyOnWriteArraySet<AMQMethodListener>();
+
+ private final AMQStateManager _stateManager;
+
+ private final QueueRegistry _queueRegistry;
+
+ private final ExchangeRegistry _exchangeRegistry;
+
+ private AMQCodecFactory _codecFactory;
+
+ private AMQProtocolSessionMBean _managedObject;
+
+ private SaslServer _saslServer;
+
+ private Object _lastReceived;
+
+ private Object _lastSent;
+
+ private boolean _closed;
+
+ private long _maxNoOfChannels;
+
+ /* AMQP Version for this session */
+
+ private byte _major;
+ private byte _minor;
+
+ /**
+ * This class implements the management interface (is an MBean). In order to make more attributes, operations
+ * and notifications available over JMX simply augment the ManagedConnection interface and add the appropriate
+ * implementation here.
+ */
+ private final class AMQProtocolSessionMBean extends DefaultManagedObject implements ManagedConnection
+ {
+ /**
+ * Represents the channel attributes sent with channel data.
+ */
+ private String[] _channelAtttibuteNames = { "ChannelId",
+ "ChannelName",
+ "Transactional",
+ "DefaultQueue"};
+ private String[] _channelAttributeDescriptions = { "Channel Identifier",
+ "Channel Name",
+ "is Channel Transactional?",
+ "Default Queue Name" };
+ private OpenType[] _channelAttributeTypes = { SimpleType.INTEGER,
+ SimpleType.OBJECTNAME,
+ SimpleType.BOOLEAN,
+ SimpleType.STRING };
+ /**
+ * Channels in the list will be indexed according to channelId.
+ */
+ private String[] _indexNames = { "ChannelId" };
+
+ /**
+ * represents the data type for channel data.
+ */
+ private CompositeType _channelType = null;
+ /**
+ * Datatype for list of channelsType.
+ */
+ private TabularType _channelsType = null;
+
+ private TabularDataSupport _channelsList = null;
+
+ public AMQProtocolSessionMBean()
+ {
+ super(ManagedConnection.class, ManagedConnection.TYPE);
+ init();
+ }
+
+ /**
+ * initialises the CompositeTypes and TabularType attributes.
+ */
+ private void init()
+ {
+ try
+ {
+ _channelType = new CompositeType("channel",
+ "a Channel",
+ _channelAtttibuteNames,
+ _channelAttributeDescriptions,
+ _channelAttributeTypes);
+
+ _channelsType = new TabularType("channelsType",
+ "List of available channelsType",
+ _channelType,
+ _indexNames);
+ }
+ catch(OpenDataException ex)
+ {
+ // It should never occur.
+ _logger.error("OpenDataTypes could not be created.", ex);
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public Date getLastIoTime()
+ {
+ return new Date(_minaProtocolSession.getLastIoTime());
+ }
+
+ public String getRemoteAddress()
+ {
+ return _minaProtocolSession.getRemoteAddress().toString();
+ }
+
+ public long getWrittenBytes()
+ {
+ return _minaProtocolSession.getWrittenBytes();
+ }
+
+ public long getReadBytes()
+ {
+ return _minaProtocolSession.getReadBytes();
+ }
+
+ public long getMaximumNumberOfAllowedChannels()
+ {
+ return _maxNoOfChannels;
+ }
+
+ public void setMaximumNumberOfAllowedChannels(long value)
+ {
+ _maxNoOfChannels = value;
+ }
+
+ public String getObjectInstanceName()
+ {
+ String remote = getRemoteAddress();
+ return "anonymous".equals(remote) ? remote + hashCode() : remote;
+ }
+
+ /**
+ * Creates the list of channels in tabular form from the _channelMap.
+ * @return list of channels in tabular form.
+ * @throws OpenDataException
+ */
+ private TabularData getChannels()
+ throws OpenDataException
+ {
+ _channelsList = new TabularDataSupport(_channelsType);
+
+ for (Map.Entry<Integer, AMQChannel> entry : _channelMap.entrySet())
+ {
+ AMQChannel channel = entry.getValue();
+ ObjectName channelObjectName = null;
+
+ try
+ {
+ channelObjectName = channel.getObjectName();
+ }
+ catch (MalformedObjectNameException ex)
+ {
+ _logger.error("Unable to create object name: ", ex);
+ }
+
+ Object[] itemValues = {channel.getChannelId(),
+ channelObjectName,
+ channel.isTransactional(),
+ (channel.getDefaultQueue() != null) ? channel.getDefaultQueue().getName() : null};
+
+ CompositeData channelData = new CompositeDataSupport(_channelType,
+ _channelAtttibuteNames,
+ itemValues);
+
+ _channelsList.put(channelData);
+ }
+
+ return _channelsList;
+ }
+
+ public TabularData viewChannels()
+ throws OpenDataException
+ {
+ return getChannels();
+ }
+
+ public void closeChannel(int id)
+ throws Exception
+ {
+ try
+ {
+ AMQMinaProtocolSession.this.closeChannel(id);
+ }
+ catch (AMQException ex)
+ {
+ throw new Exception(ex.toString());
+ }
+ }
+
+ public void closeConnection()
+ throws Exception
+ {
+ try
+ {
+ AMQMinaProtocolSession.this.closeSession();
+ }
+ catch (AMQException ex)
+ {
+ throw new Exception(ex.toString());
+ }
+ }
+
+ }
+
+ public AMQMinaProtocolSession(IoSession session, QueueRegistry queueRegistry, ExchangeRegistry exchangeRegistry,
+ AMQCodecFactory codecFactory)
+ throws AMQException
+ {
+ this(session, queueRegistry, exchangeRegistry, codecFactory, new AMQStateManager());
+ }
+
+ public AMQMinaProtocolSession(IoSession session, QueueRegistry queueRegistry, ExchangeRegistry exchangeRegistry,
+ AMQCodecFactory codecFactory, AMQStateManager stateManager)
+ throws AMQException
+ {
+ _stateManager = stateManager;
+ _minaProtocolSession = session;
+ session.setAttachment(this);
+ _frameListeners.add(_stateManager);
+ _queueRegistry = queueRegistry;
+ _exchangeRegistry = exchangeRegistry;
+ _codecFactory = codecFactory;
+ _managedObject = new AMQProtocolSessionMBean();
+ _managedObject.register();
+ }
+
+ public static AMQProtocolSession getAMQProtocolSession(IoSession minaProtocolSession)
+ {
+ return (AMQProtocolSession) minaProtocolSession.getAttachment();
+ }
+
+ public void dataBlockReceived(AMQDataBlock message)
+ throws Exception
+ {
+ _lastReceived = message;
+ if (message instanceof ProtocolInitiation)
+ {
+ ProtocolInitiation pi = (ProtocolInitiation) message;
+ // this ensures the codec never checks for a PI message again
+ ((AMQDecoder)_codecFactory.getDecoder()).setExpectProtocolInitiation(false);
+ try {
+ pi.checkVersion(this); // Fails if not correct
+ // This sets the protocol version (and hence framing classes) for this session.
+ _major = pi.protocolMajor;
+ _minor = pi.protocolMinor;
+ String mechanisms = ApplicationRegistry.getInstance().getAuthenticationManager().getMechanisms();
+ String locales = "en_US";
+ AMQFrame response = ConnectionStartBody.createAMQFrame((short)0, pi.protocolMajor, pi.protocolMinor, null,
+ mechanisms.getBytes(), locales.getBytes());
+ _minaProtocolSession.write(response);
+ } catch (AMQException e) {
+ _logger.error("Received incorrect protocol initiation", e);
+ /* Find last protocol version in protocol version list. Make sure last protocol version
+ listed in the build file (build-module.xml) is the latest version which will be used
+ here. */
+ int i = pv.length - 1;
+ _minaProtocolSession.write(new ProtocolInitiation(pv[i][PROTOCOL_MAJOR], pv[i][PROTOCOL_MINOR]));
+ // TODO: Close connection (but how to wait until message is sent?)
+ }
+ }
+ else
+ {
+ AMQFrame frame = (AMQFrame) message;
+
+ if (frame.bodyFrame instanceof AMQMethodBody)
+ {
+ methodFrameReceived(frame);
+ }
+ else
+ {
+ try
+ {
+ contentFrameReceived(frame);
+ }
+ catch (RequiredDeliveryException e)
+ {
+ //need to return the message:
+ _logger.info("Returning message to " + this + " channel " + frame.channel
+ + ": " + e.getMessage());
+ writeFrame(e.getReturnMessage(frame.channel));
+ }
+ }
+ }
+ }
+
+ private void methodFrameReceived(AMQFrame frame)
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Method frame received: " + frame);
+ }
+ final AMQMethodEvent<AMQMethodBody> evt = new AMQMethodEvent<AMQMethodBody>(frame.channel,
+ (AMQMethodBody)frame.bodyFrame);
+ try
+ {
+ boolean wasAnyoneInterested = false;
+ for (AMQMethodListener listener : _frameListeners)
+ {
+ wasAnyoneInterested = listener.methodReceived(evt, this, _queueRegistry, _exchangeRegistry) ||
+ wasAnyoneInterested;
+ }
+ if (!wasAnyoneInterested)
+ {
+ throw new AMQException("AMQMethodEvent " + evt + " was not processed by any listener.");
+ }
+ }
+ catch (AMQChannelException e)
+ {
+ _logger.error("Closing channel due to: " + e.getMessage());
+ writeFrame(e.getCloseFrame(frame.channel));
+ }
+ catch (AMQException e)
+ {
+ for (AMQMethodListener listener : _frameListeners)
+ {
+ listener.error(e);
+ }
+ _minaProtocolSession.close();
+ }
+ }
+
+ private void contentFrameReceived(AMQFrame frame) throws AMQException
+ {
+ if (frame.bodyFrame instanceof ContentHeaderBody)
+ {
+ contentHeaderReceived(frame);
+ }
+ else if (frame.bodyFrame instanceof ContentBody)
+ {
+ contentBodyReceived(frame);
+ }
+ else if (frame.bodyFrame instanceof HeartbeatBody)
+ {
+ _logger.debug("Received heartbeat from client");
+ }
+ else
+ {
+ _logger.warn("Unrecognised frame " + frame.getClass().getName());
+ }
+ }
+
+ private void contentHeaderReceived(AMQFrame frame) throws AMQException
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Content header frame received: " + frame);
+ }
+ getChannel(frame.channel).publishContentHeader((ContentHeaderBody)frame.bodyFrame);
+ }
+
+ private void contentBodyReceived(AMQFrame frame) throws AMQException
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Content body frame received: " + frame);
+ }
+ getChannel(frame.channel).publishContentBody((ContentBody)frame.bodyFrame);
+ }
+
+ /**
+ * Convenience method that writes a frame to the protocol session. Equivalent
+ * to calling getProtocolSession().write().
+ *
+ * @param frame the frame to write
+ */
+ public void writeFrame(AMQDataBlock frame)
+ {
+ _lastSent = frame;
+ _minaProtocolSession.write(frame);
+ }
+
+ public String getContextKey()
+ {
+ return _contextKey;
+ }
+
+ public void setContextKey(String contextKey)
+ {
+ _contextKey = contextKey;
+ }
+
+ public AMQChannel getChannel(int channelId) throws AMQException
+ {
+ return _channelMap.get(channelId);
+ }
+
+ public void addChannel(AMQChannel channel)
+ {
+ _channelMap.put(channel.getChannelId(), channel);
+ }
+
+ /**
+ * Close a specific channel. This will remove any resources used by the channel, including:
+ * <ul><li>any queue subscriptions (this may in turn remove queues if they are auto delete</li>
+ * </ul>
+ * @param channelId id of the channel to close
+ * @throws AMQException if an error occurs closing the channel
+ * @throws IllegalArgumentException if the channel id is not valid
+ */
+ public void closeChannel(int channelId) throws AMQException
+ {
+ final AMQChannel channel = _channelMap.get(channelId);
+ if (channel == null)
+ {
+ throw new IllegalArgumentException("Unknown channel id");
+ }
+ else
+ {
+ try
+ {
+ channel.close(this);
+ }
+ finally
+ {
+ _channelMap.remove(channelId);
+ }
+ }
+ }
+
+ /**
+ * In our current implementation this is used by the clustering code.
+ * @param channelId
+ */
+ public void removeChannel(int channelId)
+ {
+ _channelMap.remove(channelId);
+ }
+
+ /**
+ * Initialise heartbeats on the session.
+ * @param delay delay in seconds (not ms)
+ */
+ public void initHeartbeats(int delay)
+ {
+ if(delay > 0)
+ {
+ _minaProtocolSession.setIdleTime(IdleStatus.WRITER_IDLE, delay);
+ _minaProtocolSession.setIdleTime(IdleStatus.READER_IDLE, HeartbeatConfig.getInstance().getTimeout(delay));
+ }
+ }
+
+ /**
+ * Closes all channels that were opened by this protocol session. This frees up all resources
+ * used by the channel.
+ * @throws AMQException if an error occurs while closing any channel
+ */
+ private void closeAllChannels() throws AMQException
+ {
+ for (AMQChannel channel : _channelMap.values())
+ {
+ channel.close(this);
+ }
+ }
+
+ /**
+ * This must be called when the session is _closed in order to free up any resources
+ * managed by the session.
+ */
+ public void closeSession() throws AMQException
+ {
+ if(!_closed)
+ {
+ _closed = true;
+ closeAllChannels();
+ if (_managedObject != null)
+ {
+ _managedObject.unregister();
+ }
+ }
+ }
+
+ public String toString()
+ {
+ return "AMQProtocolSession(" + _minaProtocolSession.getRemoteAddress() + ")";
+ }
+
+ public String dump()
+ {
+ return this + " last_sent=" + _lastSent + " last_received=" + _lastReceived;
+ }
+
+ /**
+ * @return an object that can be used to identity
+ */
+ public Object getKey()
+ {
+ return _minaProtocolSession.getRemoteAddress();
+ }
+
+ /**
+ * Get the fully qualified domain name of the local address to which this session is bound. Since some servers
+ * may be bound to multiple addresses this could vary depending on the acceptor this session was created from.
+ *
+ * @return a String FQDN
+ */
+ public String getLocalFQDN()
+ {
+ SocketAddress address = _minaProtocolSession.getLocalAddress();
+ // we use the vmpipe address in some tests hence the need for this rather ugly test. The host
+ // information is used by SASL primary.
+ if (address instanceof InetSocketAddress)
+ {
+ return ((InetSocketAddress)address).getHostName();
+ }
+ else if (address instanceof VmPipeAddress)
+ {
+ return "vmpipe:" + ((VmPipeAddress)address).getPort();
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unsupported socket address class: " + address);
+ }
+ }
+
+ public SaslServer getSaslServer()
+ {
+ return _saslServer;
+ }
+
+ public void setSaslServer(SaslServer saslServer)
+ {
+ _saslServer = saslServer;
+ }
+
+ /**
+ * Convenience methods for managing AMQP version.
+ * NOTE: Both major and minor will be set to 0 prior to protocol initiation.
+ */
+
+ public byte getAmqpMajor()
+ {
+ return _major;
+ }
+
+ public byte getAmqpMinor()
+ {
+ return _minor;
+ }
+
+ public boolean amqpVersionEquals(byte major, byte minor)
+ {
+ return _major == major && _minor == minor;
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java b/java/broker/src/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java
new file mode 100644
index 0000000000..a51dbd5d59
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/protocol/AMQPFastProtocolHandler.java
@@ -0,0 +1,217 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.codec.AMQCodecFactory;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.transport.ConnectorConfiguration;
+import org.apache.qpid.ssl.BogusSSLContextFactory;
+import org.apache.log4j.Logger;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.IdleStatus;
+import org.apache.mina.common.IoHandlerAdapter;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.filter.SSLFilter;
+import org.apache.mina.filter.codec.ProtocolCodecFilter;
+import org.apache.mina.util.SessionUtil;
+
+import java.io.IOException;
+
+
+/**
+ * The protocol handler handles "protocol events" for all connections. The state
+ * associated with an individual connection is accessed through the protocol session.
+ *
+ * We delegate all frame (message) processing to the AMQProtocolSession which wraps
+ * the state for the connection.
+ *
+ */
+public class AMQPFastProtocolHandler extends IoHandlerAdapter implements ProtocolVersionList
+{
+ private static final Logger _logger = Logger.getLogger(AMQPFastProtocolHandler.class);
+
+ /**
+ * The registry of all queues. This is passed to frame listeners when frame
+ * events occur.
+ */
+ private final QueueRegistry _queueRegistry;
+
+ /**
+ * The registry of all exchanges. This is passed to frame listeners when frame
+ * events occur.
+ */
+ private final ExchangeRegistry _exchangeRegistry;
+
+ private boolean _useSSL;
+
+ public AMQPFastProtocolHandler(QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry)
+ {
+ _queueRegistry = queueRegistry;
+ _exchangeRegistry = exchangeRegistry;
+
+ _logger.debug("AMQPFastProtocolHandler created");
+ }
+
+ protected AMQPFastProtocolHandler(AMQPFastProtocolHandler handler)
+ {
+ this(handler._queueRegistry, handler._exchangeRegistry);
+ }
+
+ public void sessionCreated(IoSession protocolSession) throws Exception
+ {
+ SessionUtil.initialize(protocolSession);
+ final AMQCodecFactory codecFactory = new AMQCodecFactory(true);
+
+ createSession(protocolSession, _queueRegistry, _exchangeRegistry, codecFactory);
+ _logger.info("Protocol session created");
+
+ final ProtocolCodecFilter pcf = new ProtocolCodecFilter(codecFactory);
+
+ ConnectorConfiguration connectorConfig = ApplicationRegistry.getInstance().
+ getConfiguredObject(ConnectorConfiguration.class);
+ if (connectorConfig.enableExecutorPool)
+ {
+ if (_useSSL)
+ {
+ protocolSession.getFilterChain().addAfter("AsynchronousReadFilter", "sslFilter",
+ new SSLFilter(BogusSSLContextFactory.getInstance(true)));
+ }
+ protocolSession.getFilterChain().addBefore("AsynchronousWriteFilter", "protocolFilter", pcf);
+ }
+ else
+ {
+ protocolSession.getFilterChain().addLast("protocolFilter", pcf);
+ }
+ }
+
+ /**
+ * Separated into its own, protected, method to allow easier reuse
+ */
+ protected void createSession(IoSession session, QueueRegistry queues, ExchangeRegistry exchanges, AMQCodecFactory codec) throws AMQException
+ {
+ new AMQMinaProtocolSession(session, queues, exchanges, codec);
+ }
+
+ public void sessionOpened(IoSession protocolSession) throws Exception
+ {
+ _logger.info("Session opened");
+ }
+
+ public void sessionClosed(IoSession protocolSession) throws Exception
+ {
+ _logger.info("Protocol Session closed");
+ final AMQProtocolSession amqProtocolSession = AMQMinaProtocolSession.getAMQProtocolSession(protocolSession);
+ amqProtocolSession.closeSession();
+ }
+
+ public void sessionIdle(IoSession session, IdleStatus status) throws Exception
+ {
+ _logger.debug("Protocol Session [" + this + "] idle: " + status);
+ if(IdleStatus.WRITER_IDLE.equals(status))
+ {
+ //write heartbeat frame:
+ session.write(HeartbeatBody.FRAME);
+ }
+ else if(IdleStatus.READER_IDLE.equals(status))
+ {
+ //failover:
+ throw new IOException("Timed out while waiting for heartbeat from peer.");
+ }
+
+ }
+
+ public void exceptionCaught(IoSession protocolSession, Throwable throwable) throws Exception
+ {
+ AMQProtocolSession session = AMQMinaProtocolSession.getAMQProtocolSession(protocolSession);
+ if (throwable instanceof AMQProtocolHeaderException)
+ {
+ /* Find last protocol version in protocol version list. Make sure last protocol version
+ listed in the build file (build-module.xml) is the latest version which will be returned
+ here. */
+ int i = pv.length - 1;
+ protocolSession.write(new ProtocolInitiation(pv[i][PROTOCOL_MAJOR], pv[i][PROTOCOL_MINOR]));
+ protocolSession.close();
+ _logger.error("Error in protocol initiation " + session + ": " + throwable.getMessage(), throwable);
+ }
+ else if(throwable instanceof IOException)
+ {
+ _logger.error("IOException caught in" + session + ", session closed implictly: " + throwable, throwable);
+ }
+ else
+ {
+ protocolSession.write(ConnectionCloseBody.createAMQFrame(0, 200, throwable.getMessage(), 0, 0));
+ _logger.error("Exception caught in" + session + ", closing session explictly: " + throwable, throwable);
+ protocolSession.close();
+ }
+ }
+
+ /**
+ * Invoked when a message is received on a particular protocol session. Note that a
+ * protocol session is directly tied to a particular physical connection.
+ * @param protocolSession the protocol session that received the message
+ * @param message the message itself (i.e. a decoded frame)
+ * @throws Exception if the message cannot be processed
+ */
+ public void messageReceived(IoSession protocolSession, Object message) throws Exception
+ {
+ final AMQProtocolSession amqProtocolSession = AMQMinaProtocolSession.getAMQProtocolSession(protocolSession);
+
+ if (message instanceof AMQDataBlock)
+ {
+ amqProtocolSession.dataBlockReceived((AMQDataBlock) message);
+ }
+ else if (message instanceof ByteBuffer)
+ {
+ throw new IllegalStateException("Handed undecoded ByteBuffer buf = " + message);
+ }
+ else
+ {
+ throw new IllegalStateException("Handed unhandled message. message.class = " + message.getClass() + " message = " + message);
+ }
+ }
+
+ /**
+ * Called after a message has been sent out on a particular protocol session
+ * @param protocolSession the protocol session (i.e. connection) on which this
+ * message was sent
+ * @param object the message (frame) that was encoded and sent
+ * @throws Exception if we want to indicate an error
+ */
+ public void messageSent(IoSession protocolSession, Object object) throws Exception
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Message sent: " + object);
+ }
+ }
+
+ public boolean isUseSSL()
+ {
+ return _useSSL;
+ }
+
+ public void setUseSSL(boolean useSSL)
+ {
+ _useSSL = useSSL;
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/protocol/AMQPProtocolProvider.java b/java/broker/src/org/apache/qpid/server/protocol/AMQPProtocolProvider.java
new file mode 100644
index 0000000000..0088db08bb
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/protocol/AMQPProtocolProvider.java
@@ -0,0 +1,50 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+
+/**
+ * The protocol provide's role is to encapsulate the initialisation of the protocol handler.
+ *
+ * The protocol handler (see AMQPFastProtocolHandler class) handles protocol events
+ * such as connection closing or a frame being received. It can either do this directly
+ * or pass off to the protocol session in the cases where state information is required to
+ * deal with the event.
+ *
+ */
+public class AMQPProtocolProvider
+{
+ /**
+ * Handler for protocol events
+ */
+ private AMQPFastProtocolHandler _handler;
+
+ public AMQPProtocolProvider()
+ {
+ IApplicationRegistry registry = ApplicationRegistry.getInstance();
+ _handler = new AMQPFastProtocolHandler(registry.getQueueRegistry(),
+ registry.getExchangeRegistry());
+ }
+
+ public AMQPFastProtocolHandler getHandler()
+ {
+ return _handler;
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/protocol/AMQProtocolSession.java b/java/broker/src/org/apache/qpid/server/protocol/AMQProtocolSession.java
new file mode 100644
index 0000000000..402ebc329d
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/protocol/AMQProtocolSession.java
@@ -0,0 +1,122 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.qpid.framing.AMQDataBlock;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.AMQException;
+
+import javax.security.sasl.SaslServer;
+
+
+public interface AMQProtocolSession
+{
+ /**
+ * Called when a protocol data block is received
+ * @param message the data block that has been received
+ * @throws Exception if processing the datablock fails
+ */
+ void dataBlockReceived(AMQDataBlock message) throws Exception;
+
+ /**
+ * Write a datablock, encoding where necessary (e.g. into a sequence of bytes)
+ * @param frame the frame to be encoded and written
+ */
+ void writeFrame(AMQDataBlock frame);
+
+ /**
+ * Get the context key associated with this session. Context key is described
+ * in the AMQ protocol specification (RFC 6).
+ * @return the context key
+ */
+ String getContextKey();
+
+ /**
+ * Set the context key associated with this session. Context key is described
+ * in the AMQ protocol specification (RFC 6).
+ * @param contextKey the context key
+ */
+ void setContextKey(String contextKey);
+
+ /**
+ * Get the channel for this session associated with the specified id. A channel
+ * id is unique per connection (i.e. per session).
+ * @param channelId the channel id which must be valid
+ * @return null if no channel exists, the channel otherwise
+ */
+ AMQChannel getChannel(int channelId) throws AMQException;
+
+ /**
+ * Associate a channel with this session.
+ * @param channel the channel to associate with this session. It is an error to
+ * associate the same channel with more than one session but this is not validated.
+ */
+ void addChannel(AMQChannel channel);
+
+ /**
+ * Close a specific channel. This will remove any resources used by the channel, including:
+ * <ul><li>any queue subscriptions (this may in turn remove queues if they are auto delete</li>
+ * </ul>
+ * @param channelId id of the channel to close
+ * @throws org.apache.qpid.AMQException if an error occurs closing the channel
+ * @throws IllegalArgumentException if the channel id is not valid
+ */
+ void closeChannel(int channelId) throws AMQException;
+
+ /**
+ * Remove a channel from the session but do not close it.
+ * @param channelId
+ */
+ void removeChannel(int channelId);
+
+ /**
+ * Initialise heartbeats on the session.
+ * @param delay delay in seconds (not ms)
+ */
+ void initHeartbeats(int delay);
+
+ /**
+ * This must be called when the session is _closed in order to free up any resources
+ * managed by the session.
+ */
+ void closeSession() throws AMQException;
+
+ /**
+ * @return a key that uniquely identifies this session
+ */
+ Object getKey();
+
+ /**
+ * Get the fully qualified domain name of the local address to which this session is bound. Since some servers
+ * may be bound to multiple addresses this could vary depending on the acceptor this session was created from.
+ *
+ * @return a String FQDN
+ */
+ String getLocalFQDN();
+
+ /**
+ * @return the sasl server that can perform authentication for this session.
+ */
+ SaslServer getSaslServer();
+
+ /**
+ * Set the sasl server that is to perform authentication for this session.
+ * @param saslServer
+ */
+ void setSaslServer(SaslServer saslServer);
+}
diff --git a/java/broker/src/org/apache/qpid/server/protocol/ExchangeInitialiser.java b/java/broker/src/org/apache/qpid/server/protocol/ExchangeInitialiser.java
new file mode 100644
index 0000000000..08c31ed3ff
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/protocol/ExchangeInitialiser.java
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.exchange.ExchangeDefaults;
+import org.apache.qpid.server.exchange.ExchangeFactory;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+
+public class ExchangeInitialiser
+{
+ public void initialise(ExchangeFactory factory, ExchangeRegistry registry) throws AMQException{
+ define(registry, factory, ExchangeDefaults.DIRECT_EXCHANGE_NAME, ExchangeDefaults.DIRECT_EXCHANGE_CLASS);
+ define(registry, factory, ExchangeDefaults.TOPIC_EXCHANGE_NAME, ExchangeDefaults.TOPIC_EXCHANGE_CLASS);
+ define(registry, factory, ExchangeDefaults.HEADERS_EXCHANGE_NAME, ExchangeDefaults.HEADERS_EXCHANGE_CLASS);
+ }
+
+ private void define(ExchangeRegistry r, ExchangeFactory f,
+ String name, String type) throws AMQException
+ {
+ r.registerExchange(f.createExchange(name, type, true, false, 0));
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/protocol/HeartbeatConfig.java b/java/broker/src/org/apache/qpid/server/protocol/HeartbeatConfig.java
new file mode 100644
index 0000000000..d7678185d4
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/protocol/HeartbeatConfig.java
@@ -0,0 +1,64 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.qpid.configuration.Configured;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+public class HeartbeatConfig
+{
+ @Configured(path = "heartbeat.delay", defaultValue = "5")
+ public int delay = 5;//in secs
+ @Configured(path = "heartbeat.timeoutFactor", defaultValue = "2.0")
+ public double timeoutFactor = 2;
+
+ public double getTimeoutFactor()
+ {
+ return timeoutFactor;
+ }
+
+ public void setTimeoutFactor(double timeoutFactor)
+ {
+ this.timeoutFactor = timeoutFactor;
+ }
+
+ public int getDelay()
+ {
+ return delay;
+ }
+
+ public void setDelay(int delay)
+ {
+ this.delay = delay;
+ }
+
+ int getTimeout(int writeDelay)
+ {
+ return (int) (timeoutFactor * writeDelay);
+ }
+
+ public static HeartbeatConfig getInstance()
+ {
+ return ApplicationRegistry.getInstance().getConfiguredObject(HeartbeatConfig.class);
+ }
+
+ public String toString()
+ {
+ return "HeartBeatConfig{delay = " + delay + " timeoutFactor = " + timeoutFactor + "}";
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/protocol/ManagedConnection.java b/java/broker/src/org/apache/qpid/server/protocol/ManagedConnection.java
new file mode 100644
index 0000000000..f3fd8bc7e2
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/protocol/ManagedConnection.java
@@ -0,0 +1,92 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.qpid.server.protocol;
+
+import org.apache.qpid.AMQException;
+
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.OpenDataException;
+import java.util.Date;
+
+/**
+ * The management interface exposed to allow management of Connections.
+ * @author Bhupendra Bhardwaj
+ * @version 0.1
+ */
+public interface ManagedConnection
+{
+ static final String TYPE = "Connection";
+
+ /**
+ * Tells the last time, the IO operation was done.
+ * @return last IO time.
+ */
+ Date getLastIoTime();
+
+ /**
+ * Tells the remote address of this connection.
+ * @return remote address
+ */
+ String getRemoteAddress();
+
+ /**
+ * Tells the total number of bytes written till now.
+ * @return number of bytes written.
+ */
+ long getWrittenBytes();
+
+ /**
+ * Tells the total number of bytes read till now.
+ * @return number of bytes read.
+ */
+ long getReadBytes();
+
+ /**
+ * Tells the maximum number of channels that can be opened using
+ * this connection. This is useful in setting notifications or
+ * taking required action is there are more channels being created.
+ * @return maximum number of channels allowed to be created.
+ */
+ long getMaximumNumberOfAllowedChannels();
+
+ /**
+ * Sets the maximum number of channels allowed to be created using
+ * this connection.
+ * @param value
+ */
+ void setMaximumNumberOfAllowedChannels(long value);
+
+ //********** Operations *****************//
+
+ /**
+ * Returns channel details of all the channels opened for this connection.
+ * @return channel details.
+ */
+ TabularData viewChannels() throws OpenDataException;
+
+ /**
+ * Closes all the channels and unregisters this connection from managed objects.
+ */
+ void closeConnection() throws Exception;
+
+ /**
+ * Unsubscribes the consumers and unregisters the channel from managed objects.
+ */
+ void closeChannel(int channelId) throws Exception;
+}
diff --git a/java/broker/src/org/apache/qpid/server/protocol/ManagedSession.java b/java/broker/src/org/apache/qpid/server/protocol/ManagedSession.java
new file mode 100644
index 0000000000..2a1a0b62c2
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/protocol/ManagedSession.java
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import java.util.Date;
+
+public interface ManagedSession
+{
+ static final String TYPE = "Connection";
+
+ Date getLastIoTime();
+
+ String getRemoteAddress();
+
+ long getWrittenBytes();
+
+ long getReadBytes();
+}
diff --git a/java/broker/src/org/apache/qpid/server/queue/AMQMessage.java b/java/broker/src/org/apache/qpid/server/queue/AMQMessage.java
new file mode 100644
index 0000000000..a4ff453720
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/queue/AMQMessage.java
@@ -0,0 +1,343 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.AMQException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * Combines the information that make up a deliverable message into a more manageable form.
+ */
+public class AMQMessage
+{
+ private final Set<Object> _tokens = new HashSet<Object>();
+
+ private AMQProtocolSession _publisher;
+
+ private final BasicPublishBody _publishBody;
+
+ private ContentHeaderBody _contentHeaderBody;
+
+ private List<ContentBody> _contentBodies;
+
+ private boolean _redelivered;
+
+ private final long _messageId;
+
+ private final AtomicInteger _referenceCount = new AtomicInteger(1);
+
+ /**
+ * Keeps a track of how many bytes we have received in body frames
+ */
+ private long _bodyLengthReceived = 0;
+
+ /**
+ * The message store in which this message is contained.
+ */
+ private transient final MessageStore _store;
+
+ public AMQMessage(MessageStore messageStore, BasicPublishBody publishBody)
+ {
+ _messageId = messageStore.getNewMessageId();
+ _publishBody = publishBody;
+ _store = messageStore;
+ _contentBodies = new LinkedList<ContentBody>();
+ }
+
+ public AMQMessage(MessageStore store, long messageId, BasicPublishBody publishBody,
+ ContentHeaderBody contentHeaderBody, List<ContentBody> contentBodies)
+ throws AMQException
+ {
+ _publishBody = publishBody;
+ _contentHeaderBody = contentHeaderBody;
+ _contentBodies = contentBodies;
+ _messageId = messageId;
+ _store = store;
+ storeMessage();
+ }
+
+ public AMQMessage(MessageStore store, BasicPublishBody publishBody,
+ ContentHeaderBody contentHeaderBody, List<ContentBody> contentBodies)
+ throws AMQException
+ {
+ this(store, store.getNewMessageId(), publishBody, contentHeaderBody, contentBodies);
+ }
+
+ protected AMQMessage(AMQMessage msg) throws AMQException
+ {
+ this(msg._store, msg._messageId, msg._publishBody, msg._contentHeaderBody, msg._contentBodies);
+ }
+
+ private void storeMessage() throws AMQException
+ {
+ if (isPersistent())
+ {
+ _store.put(this);
+ }
+ }
+
+ public CompositeAMQDataBlock getDataBlock(ByteBuffer encodedDeliverBody, int channel)
+ {
+ AMQFrame[] allFrames = new AMQFrame[1 + _contentBodies.size()];
+
+ allFrames[0] = ContentHeaderBody.createAMQFrame(channel, _contentHeaderBody);
+ for (int i = 1; i < allFrames.length; i++)
+ {
+ allFrames[i] = ContentBody.createAMQFrame(channel, _contentBodies.get(i - 1));
+ }
+ return new CompositeAMQDataBlock(encodedDeliverBody, allFrames);
+ }
+
+ public CompositeAMQDataBlock getDataBlock(int channel, String consumerTag, long deliveryTag)
+ {
+ AMQFrame[] allFrames = new AMQFrame[2 + _contentBodies.size()];
+
+ allFrames[0] = BasicDeliverBody.createAMQFrame(channel, consumerTag, deliveryTag, _redelivered,
+ getExchangeName(), getRoutingKey());
+ allFrames[1] = ContentHeaderBody.createAMQFrame(channel, _contentHeaderBody);
+ for (int i = 2; i < allFrames.length; i++)
+ {
+ allFrames[i] = ContentBody.createAMQFrame(channel, _contentBodies.get(i - 2));
+ }
+ return new CompositeAMQDataBlock(allFrames);
+ }
+
+ public List<AMQBody> getPayload()
+ {
+ List<AMQBody> payload = new ArrayList<AMQBody>(2 + _contentBodies.size());
+ payload.add(_publishBody);
+ payload.add(_contentHeaderBody);
+ payload.addAll(_contentBodies);
+ return payload;
+ }
+
+ public BasicPublishBody getPublishBody()
+ {
+ return _publishBody;
+ }
+
+ public ContentHeaderBody getContentHeaderBody()
+ {
+ return _contentHeaderBody;
+ }
+
+ public void setContentHeaderBody(ContentHeaderBody contentHeaderBody) throws AMQException
+ {
+ _contentHeaderBody = contentHeaderBody;
+ if (isAllContentReceived())
+ {
+ storeMessage();
+ }
+ }
+
+ public List<ContentBody> getContentBodies()
+ {
+ return _contentBodies;
+ }
+
+ public void setContentBodies(List<ContentBody> contentBodies)
+ {
+ _contentBodies = contentBodies;
+ }
+
+ public void addContentBodyFrame(ContentBody contentBody) throws AMQException
+ {
+ _contentBodies.add(contentBody);
+ _bodyLengthReceived += contentBody.getSize();
+ if (isAllContentReceived())
+ {
+ storeMessage();
+ }
+ }
+
+ public boolean isAllContentReceived()
+ {
+ return _bodyLengthReceived == _contentHeaderBody.bodySize;
+ }
+
+ public boolean isRedelivered()
+ {
+ return _redelivered;
+ }
+
+ String getExchangeName()
+ {
+ return _publishBody.exchange;
+ }
+
+ String getRoutingKey()
+ {
+ return _publishBody.routingKey;
+ }
+
+ boolean isImmediate()
+ {
+ return _publishBody.immediate;
+ }
+
+ NoConsumersException getNoConsumersException(String queue)
+ {
+ return new NoConsumersException(queue, _publishBody, _contentHeaderBody, _contentBodies);
+ }
+
+ void setRedelivered(boolean redelivered)
+ {
+ _redelivered = redelivered;
+ }
+
+ public long getMessageId()
+ {
+ return _messageId;
+ }
+
+ /**
+ * Threadsafe. Increment the reference count on the message.
+ */
+ public void incrementReference()
+ {
+ _referenceCount.incrementAndGet();
+ }
+
+ /**
+ * Threadsafe. This will decrement the reference count and when it reaches zero will remove the message from the
+ * message store.
+ */
+ public void decrementReference() throws AMQException
+ {
+ // note that the operation of decrementing the reference count and then removing the message does not
+ // have to be atomic since the ref count starts at 1 and the exchange itself decrements that after
+ // the message has been passed to all queues. i.e. we are
+ // not relying on the all the increments having taken place before the delivery manager decrements.
+ if (_referenceCount.decrementAndGet() == 0)
+ {
+ _store.removeMessage(_messageId);
+ }
+ }
+
+ public void setPublisher(AMQProtocolSession publisher)
+ {
+ _publisher = publisher;
+ }
+
+ public AMQProtocolSession getPublisher()
+ {
+ return _publisher;
+ }
+
+ public boolean checkToken(Object token)
+ {
+ if(_tokens.contains(token))
+ {
+ return true;
+ }
+ else
+ {
+ _tokens.add(token);
+ return false;
+ }
+ }
+
+ public void enqueue(AMQQueue queue) throws AMQException
+ {
+ //if the message is not persistent or the queue is not durable
+ //we will not need to recover the association and so do not
+ //need to record it
+ if(isPersistent() && queue.isDurable())
+ {
+ _store.enqueueMessage(queue.getName(), _messageId);
+ }
+ }
+
+ public void dequeue(AMQQueue queue) throws AMQException
+ {
+ //only record associations where both queue and message will survive
+ //a restart, so only need to remove association if this is the case
+ if(isPersistent() && queue.isDurable())
+ {
+ _store.dequeueMessage(queue.getName(), _messageId);
+ }
+ }
+
+ /**
+ * Used to requeue a message (on delivery where an acknowledgement is
+ * expected). This will move it to the end of the queue.
+ */
+ public void requeue(AMQQueue queue) throws AMQException
+ {
+ if(isPersistent() && queue.isDurable())
+ {
+ if(!_store.inTran())
+ {
+ //if not already in tran, want to be so this is atomic
+ _store.beginTran();
+ try
+ {
+ requeueImpl(queue);
+ _store.commitTran();
+ }
+ catch(AMQException e)
+ {
+ _store.abortTran();
+ }
+ }
+ else
+ {
+ //May already be in tran (e.g. if this is called during delivery
+ //resulting from a commit).
+ requeueImpl(queue);
+ }
+ }
+ }
+
+ private void requeueImpl(AMQQueue queue) throws AMQException
+ {
+ try
+ {
+ _store.dequeueMessage(queue.getName(), _messageId);
+ _store.enqueueMessage(queue.getName(), _messageId);
+ }
+ catch(AMQException e)
+ {
+ throw e;
+ }
+ catch(Throwable t)
+ {
+ throw new AMQException("Failure on requeue of message", t);
+ }
+ }
+
+ public boolean isPersistent() throws AMQException
+ {
+ if(_contentHeaderBody == null)
+ {
+ throw new AMQException("Cannot determine delivery mode of message. Content header not found.");
+ }
+ return _contentHeaderBody.properties instanceof BasicContentHeaderProperties
+ &&((BasicContentHeaderProperties) _contentHeaderBody.properties).getDeliveryMode() == 2;
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/queue/AMQQueue.java b/java/broker/src/org/apache/qpid/server/queue/AMQQueue.java
new file mode 100644
index 0000000000..8e744d5960
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/queue/AMQQueue.java
@@ -0,0 +1,654 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.log4j.Logger;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.management.Managable;
+import org.apache.qpid.server.management.ManagedObject;
+import org.apache.qpid.server.management.AMQManagedObject;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+
+import javax.management.openmbean.*;
+import javax.management.MBeanNotificationInfo;
+import javax.management.AttributeChangeNotification;
+import javax.management.Notification;
+import javax.management.JMException;
+import javax.management.MBeanException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+
+/**
+ * This is an AMQ Queue, and should not be confused with a JMS queue or any other abstraction like
+ * that. It is described fully in RFC 006.
+ */
+public class AMQQueue implements Managable
+{
+ private static final Logger _logger = Logger.getLogger(AMQQueue.class);
+
+ private final String _name;
+
+ /**
+ * null means shared
+ */
+ private final String _owner;
+
+ private final boolean _durable;
+
+ /**
+ * If true, this queue is deleted when the last subscriber is removed
+ */
+ private final boolean _autoDelete;
+
+ /**
+ * Holds subscribers to the queue.
+ */
+ private final SubscriptionSet _subscribers;
+
+ private final SubscriptionFactory _subscriptionFactory;
+
+ /**
+ * Manages message delivery.
+ */
+ private final DeliveryManager _deliveryMgr;
+
+ /**
+ * The queue registry with which this queue is registered.
+ */
+ private final QueueRegistry _queueRegistry;
+
+ /**
+ * Used to track bindings to exchanges so that on deletion they can easily
+ * be cancelled.
+ */
+ private final ExchangeBindings _bindings = new ExchangeBindings(this);
+
+ /**
+ * Executor on which asynchronous delivery will be carriedout where required
+ */
+ private final Executor _asyncDelivery;
+
+ private final AMQQueueMBean _managedObject;
+
+ /**
+ * max allowed size of a single message.
+ */
+ private long _maxAllowedMessageSize = 0;
+
+ /**
+ * max allowed number of messages on a queue.
+ */
+ private long _maxAllowedMessageCount = 0;
+
+ /**
+ * max allowed size in bytes for all the messages combined together in a queue.
+ */
+ private long _queueDepth = 0;
+
+ /**
+ * total messages received by the queue since startup.
+ */
+ private long _totalMessagesReceived = 0;
+
+ /**
+ * MBean interface for the implementation AMQQueueMBean.
+ * This is required for making the implementation a compliant MBean.
+ */
+ public interface AMQQueueMBeanMBean extends ManagedQueue
+ {
+
+ }
+ /**
+ * MBean class for AMQQueue. It implements all the management features exposed
+ * for an AMQQueue.
+ */
+ private final class AMQQueueMBean extends AMQManagedObject implements AMQQueueMBeanMBean
+ {
+ // AMQ message attribute names exposed.
+ private String[] _msgAttributeNames = { "MessageId",
+ "Redelivered",
+ "Content's size",
+ "Contents" };
+ // AMQ Message attribute descriptions.
+ private String[] _msgAttributeDescriptions = { "Message Id",
+ "Redelivered",
+ "Message content's size in bytes",
+ "Message content bodies" };
+ // AMQ message attribute types.
+ private OpenType[] _msgAttributeTypes = new OpenType[4];
+ // Messages will be indexed according to the messageId.
+ private String[] _msgAttributeIndex = { "MessageId"};
+ // Composite type for representing AMQ Message data.
+ private CompositeType _messageDataType = null;
+ // Datatype for representing AMQ messages list.
+ private TabularType _messagelistDataType = null;
+
+ private String[] _contentNames = {"SerialNumber", "ContentBody"};
+ private String[] _contentDesc = {"SerialNumber", "Message Content"};
+ private String[] _contentIndex = {"SerialNumber"};
+ private OpenType[] _contentType = new OpenType[2];
+ private CompositeType _contentBodyType = null;
+ private TabularType _contentBodyListType = null;
+
+ public AMQQueueMBean()
+ {
+ super(ManagedQueue.class, ManagedQueue.TYPE);
+ init();
+ }
+
+ private void init()
+ {
+ try
+ {
+ _contentType[0] = SimpleType.INTEGER;
+ _contentType[1] = new ArrayType(1, SimpleType.BYTE);
+ _contentBodyType = new CompositeType("Content",
+ "Message body content",
+ _contentNames,
+ _contentDesc,
+ _contentType);
+ _contentBodyListType = new TabularType("MessageContents",
+ "MessageContent",
+ _contentBodyType,
+ _contentIndex);
+
+ _msgAttributeTypes[0] = SimpleType.LONG;
+ _msgAttributeTypes[1] = SimpleType.BOOLEAN;
+ _msgAttributeTypes[2] = SimpleType.LONG;
+ _msgAttributeTypes[3] = _contentBodyListType;
+
+ _messageDataType = new CompositeType("Message",
+ "AMQ Message",
+ _msgAttributeNames,
+ _msgAttributeDescriptions,
+ _msgAttributeTypes);
+ _messagelistDataType = new TabularType("Messages",
+ "List of messages",
+ _messageDataType,
+ _msgAttributeIndex);
+ }
+ catch (OpenDataException ex)
+ {
+ _logger.error("OpenDataTypes could not be created.", ex);
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public String getObjectInstanceName()
+ {
+ return _name;
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public boolean isDurable()
+ {
+ return _durable;
+ }
+
+ public String getOwner()
+ {
+ return _owner;
+ }
+
+ public boolean isAutoDelete()
+ {
+ return _autoDelete;
+ }
+
+ public int getMessageCount()
+ {
+ return _deliveryMgr.getQueueMessageCount();
+ }
+
+ public long getMaximumMessageSize()
+ {
+ return _maxAllowedMessageSize;
+ }
+
+ public void setMaximumMessageSize(long value)
+ {
+ _maxAllowedMessageSize = value;
+ }
+
+ public int getConsumerCount()
+ {
+ return _subscribers.size();
+ }
+
+ public int getActiveConsumerCount()
+ {
+ return _subscribers.getWeight();
+ }
+
+ public long getReceivedMessageCount()
+ {
+ return _totalMessagesReceived;
+ }
+
+ public long getMaximumMessageCount()
+ {
+ return _maxAllowedMessageCount;
+ }
+
+ public void setMaximumMessageCount( long value)
+ {
+ _maxAllowedMessageCount = value;
+ }
+
+ public long getQueueDepth()
+ {
+ return _queueDepth;
+ }
+
+ public void setQueueDepth(long value)
+ {
+ _queueDepth = value;
+ }
+
+ // Operations
+
+ private void checkForNotification()
+ {
+ if (getMessageCount() >= getMaximumMessageCount())
+ {
+ Notification n = new Notification(
+ "Warning",
+ this,
+ ++_notificationSequenceNumber,
+ System.currentTimeMillis(),
+ "Queue has reached its size limit and is now full.");
+
+ _broadcaster.sendNotification(n);
+ }
+ }
+
+ public void deleteMessageFromTop() throws JMException
+ {
+ try
+ {
+ _deliveryMgr.removeAMessageFromTop();
+ }
+ catch(AMQException ex)
+ {
+ throw new MBeanException(ex, ex.toString());
+ }
+ }
+
+ public void clearQueue() throws JMException
+ {
+ try
+ {
+ _deliveryMgr.clearAllMessages();
+ }
+ catch (AMQException ex)
+ {
+ throw new MBeanException(ex, ex.toString());
+ }
+ }
+
+ /**
+ * Returns the messages stored in this queue in tabular form.
+ * @param beginIndex
+ * @param endIndex
+ * @return AMQ messages in tabular form.
+ * @throws JMException
+ */
+ public TabularData viewMessages(int beginIndex, int endIndex) throws JMException
+ {
+ if ((beginIndex > endIndex) || (beginIndex < 1))
+ {
+ throw new JMException("FromIndex = " + beginIndex + ", ToIndex = " + endIndex +
+ "\nFromIndex should be greater than 0 and less than ToIndex");
+ }
+
+ List<AMQMessage> list = _deliveryMgr.getMessages();
+
+ if (beginIndex > list.size())
+ {
+ throw new JMException("FromIndex = " + beginIndex + ". There are only " + list.size() + " messages in the queue");
+ }
+
+ endIndex = endIndex < list.size() ? endIndex : list.size();
+ TabularDataSupport _messageList = new TabularDataSupport(_messagelistDataType);
+
+ for (int i = beginIndex; i <= endIndex; i++)
+ {
+ AMQMessage msg = list.get(i - 1);
+ long msgId = msg.getMessageId();
+
+ List<ContentBody> cBodies = msg.getContentBodies();
+
+ TabularDataSupport _contentList = new TabularDataSupport(_contentBodyListType);
+ int contentSerialNo = 1;
+ long size = 0;
+
+ for (ContentBody body : cBodies)
+ {
+ if (body.getSize() != 0)
+ {
+ Byte[] byteArray = getByteArray(body.payload.slice().array());
+ size = size + byteArray.length;
+
+ Object[] contentValues = {contentSerialNo, byteArray};
+ CompositeData contentData = new CompositeDataSupport(_contentBodyType,
+ _contentNames,
+ contentValues);
+
+ _contentList.put(contentData);
+ }
+ }
+
+ Object[] itemValues = {msgId, true, size, _contentList};
+ CompositeData messageData = new CompositeDataSupport(_messageDataType,
+ _msgAttributeNames,
+ itemValues);
+ _messageList.put(messageData);
+ }
+
+ return _messageList;
+ }
+
+ /**
+ * A utility to convert byte[] to Byte[]. Required to create composite
+ * type for message contents.
+ * @param byteArray message content as byte[]
+ * @return Byte[]
+ */
+ private Byte[] getByteArray(byte[] byteArray)
+ {
+ int size = byteArray.length;
+ List<Byte> list = new ArrayList<Byte>();
+
+ for (int i = 0; i < size; i++)
+ {
+ list.add(byteArray[i]);
+ }
+
+ return list.toArray(new Byte[0]);
+ }
+
+ /**
+ * Creates all the notifications this MBean can send.
+ * @return Notifications broadcasted by this MBean.
+ */
+ public MBeanNotificationInfo[] getNotificationInfo()
+ {
+ String[] notificationTypes = new String[]
+ {AttributeChangeNotification.ATTRIBUTE_CHANGE};
+ String name = AttributeChangeNotification.class.getName();
+ String description = "An attribute of this MBean has changed";
+ MBeanNotificationInfo info1 = new MBeanNotificationInfo(notificationTypes,
+ name,
+ description);
+
+ return new MBeanNotificationInfo[] {info1};
+ }
+
+ } // End of AMQMBean class
+
+ public AMQQueue(String name, boolean durable, String owner,
+ boolean autoDelete, QueueRegistry queueRegistry)
+ throws AMQException
+ {
+ this(name, durable, owner, autoDelete, queueRegistry,
+ AsyncDeliveryConfig.getAsyncDeliveryExecutor(), new SubscriptionImpl.Factory());
+ }
+
+ public AMQQueue(String name, boolean durable, String owner,
+ boolean autoDelete, QueueRegistry queueRegistry, SubscriptionFactory subscriptionFactory)
+ throws AMQException
+ {
+ this(name, durable, owner, autoDelete, queueRegistry,
+ AsyncDeliveryConfig.getAsyncDeliveryExecutor(), subscriptionFactory);
+ }
+
+ public AMQQueue(String name, boolean durable, String owner,
+ boolean autoDelete, QueueRegistry queueRegistry, Executor asyncDelivery,
+ SubscriptionFactory subscriptionFactory)
+ throws AMQException
+ {
+
+ this(name, durable, owner, autoDelete, queueRegistry, asyncDelivery, new SubscriptionSet(), subscriptionFactory);
+ }
+
+ public AMQQueue(String name, boolean durable, String owner,
+ boolean autoDelete, QueueRegistry queueRegistry, Executor asyncDelivery)
+ throws AMQException
+ {
+
+ this(name, durable, owner, autoDelete, queueRegistry, asyncDelivery, new SubscriptionSet(),
+ new SubscriptionImpl.Factory());
+ }
+
+ protected AMQQueue(String name, boolean durable, String owner,
+ boolean autoDelete, QueueRegistry queueRegistry,
+ SubscriptionSet subscribers, SubscriptionFactory subscriptionFactory)
+ throws AMQException
+ {
+ this(name, durable, owner, autoDelete, queueRegistry,
+ AsyncDeliveryConfig.getAsyncDeliveryExecutor(), subscribers, subscriptionFactory);
+ }
+
+ protected AMQQueue(String name, boolean durable, String owner,
+ boolean autoDelete, QueueRegistry queueRegistry,
+ SubscriptionSet subscribers)
+ throws AMQException
+ {
+ this(name, durable, owner, autoDelete, queueRegistry,
+ AsyncDeliveryConfig.getAsyncDeliveryExecutor(), subscribers, new SubscriptionImpl.Factory());
+ }
+
+ protected AMQQueue(String name, boolean durable, String owner,
+ boolean autoDelete, QueueRegistry queueRegistry,
+ Executor asyncDelivery, SubscriptionSet subscribers, SubscriptionFactory subscriptionFactory)
+ throws AMQException
+ {
+ if (name == null)
+ {
+ throw new IllegalArgumentException("Queue name must not be null");
+ }
+ if (queueRegistry == null)
+ {
+ throw new IllegalArgumentException("Queue registry must not be null");
+ }
+ _name = name;
+ _durable = durable;
+ _owner = owner;
+ _autoDelete = autoDelete;
+ _queueRegistry = queueRegistry;
+ _asyncDelivery = asyncDelivery;
+ _managedObject = new AMQQueueMBean();
+ _managedObject.register();
+
+ _subscribers = subscribers;
+ _subscriptionFactory = subscriptionFactory;
+ _deliveryMgr = new DeliveryManager(_subscribers, this);
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+
+ public boolean isShared()
+ {
+ return _owner == null;
+ }
+
+ public boolean isDurable()
+ {
+ return _durable;
+ }
+
+ public String getOwner()
+ {
+ return _owner;
+ }
+
+ public boolean isAutoDelete()
+ {
+ return _autoDelete;
+ }
+
+ public int getMessageCount()
+ {
+ return _deliveryMgr.getQueueMessageCount();
+ }
+
+ public ManagedObject getManagedObject()
+ {
+ return _managedObject;
+ }
+
+ public void bind(String routingKey, Exchange exchange)
+ {
+ _bindings.addBinding(routingKey, exchange);
+ }
+
+ public void registerProtocolSession(AMQProtocolSession ps, int channel, String consumerTag, boolean acks)
+ throws AMQException
+ {
+ debug("Registering protocol session {0} with channel {1} and consumer tag {2} with {3}", ps, channel, consumerTag, this);
+
+ Subscription subscription = _subscriptionFactory.createSubscription(channel, ps, consumerTag, acks);
+ _subscribers.addSubscriber(subscription);
+ }
+
+ public void unregisterProtocolSession(AMQProtocolSession ps, int channel, String consumerTag) throws AMQException
+ {
+ debug("Unregistering protocol session {0} with channel {1} and consumer tag {2} from {3}", ps, channel, consumerTag,
+ this);
+
+ Subscription removedSubscription;
+ if ((removedSubscription = _subscribers.removeSubscriber(_subscriptionFactory.createSubscription(channel,
+ ps,
+ consumerTag)))
+ == null)
+ {
+ throw new AMQException("Protocol session with channel " + channel + " and consumer tag " + consumerTag +
+ " and protocol session key " + ps.getKey() + " not registered with queue " + this);
+ }
+
+ // if we are eligible for auto deletion, unregister from the queue registry
+ if (_autoDelete && _subscribers.isEmpty())
+ {
+ autodelete();
+ // we need to manually fire the event to the removed subscription (which was the last one left for this
+ // queue. This is because the delete method uses the subscription set which has just been cleared
+ removedSubscription.queueDeleted(this);
+ }
+ }
+
+ public int delete(boolean checkUnused, boolean checkEmpty) throws AMQException
+ {
+ if(checkUnused && !_subscribers.isEmpty())
+ {
+ _logger.info("Will not delete " + this + " as it is in use.");
+ return 0;
+ }
+ else if(checkEmpty && _deliveryMgr.getQueueMessageCount() > 0)
+ {
+ _logger.info("Will not delete " + this + " as it is not empty.");
+ return 0;
+ }
+ else
+ {
+ delete();
+ return _deliveryMgr.getQueueMessageCount();
+ }
+ }
+
+ public void delete() throws AMQException
+ {
+ _subscribers.queueDeleted(this);
+ _bindings.deregister();
+ _queueRegistry.unregisterQueue(_name);
+ _managedObject.unregister();
+ }
+
+ protected void autodelete() throws AMQException
+ {
+ debug("autodeleting {0}", this);
+ delete();
+ }
+
+ public void deliver(AMQMessage msg) throws AMQException
+ {
+ msg.enqueue(this);
+ _deliveryMgr.deliver(getName(), msg);
+ updateReceivedMessageCount();
+ }
+
+ public void deliverAsync()
+ {
+ _deliveryMgr.processAsync(_asyncDelivery);
+ }
+
+ protected SubscriptionManager getSubscribers()
+ {
+ return _subscribers;
+ }
+
+ protected void updateReceivedMessageCount()
+ {
+ _totalMessagesReceived++;
+ _managedObject.checkForNotification();
+ }
+
+ public boolean equals(Object o)
+ {
+ if (this == o)
+ {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass())
+ {
+ return false;
+ }
+
+ final AMQQueue amqQueue = (AMQQueue) o;
+
+ return (_name.equals(amqQueue._name));
+ }
+
+ public int hashCode()
+ {
+ return _name.hashCode();
+ }
+
+ public String toString()
+ {
+ return "Queue(" + _name + ")@" + System.identityHashCode(this);
+ }
+
+ private void debug(String msg, Object... args)
+ {
+ if(_logger.isDebugEnabled())
+ {
+ _logger.debug(MessageFormat.format(msg, args));
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/queue/AsyncDeliveryConfig.java b/java/broker/src/org/apache/qpid/server/queue/AsyncDeliveryConfig.java
new file mode 100644
index 0000000000..60788c1ccb
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/queue/AsyncDeliveryConfig.java
@@ -0,0 +1,53 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.configuration.Configured;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+public class AsyncDeliveryConfig
+{
+ private Executor _executor;
+
+ @Configured(path = "delivery.poolsize", defaultValue = "0")
+ public int poolSize;
+
+ public Executor getExecutor()
+ {
+ if (_executor == null)
+ {
+ if (poolSize > 0)
+ {
+ _executor = Executors.newFixedThreadPool(poolSize);
+ }
+ else
+ {
+ _executor = Executors.newCachedThreadPool();
+ }
+ }
+ return _executor;
+ }
+
+ public static Executor getAsyncDeliveryExecutor()
+ {
+ return ApplicationRegistry.getInstance().getConfiguredObject(AsyncDeliveryConfig.class).getExecutor();
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/queue/DefaultQueueRegistry.java b/java/broker/src/org/apache/qpid/server/queue/DefaultQueueRegistry.java
new file mode 100644
index 0000000000..a7dc98ec22
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/queue/DefaultQueueRegistry.java
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.AMQException;
+
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+public class DefaultQueueRegistry implements QueueRegistry
+{
+ private ConcurrentMap<String, AMQQueue> _queueMap = new ConcurrentHashMap<String, AMQQueue>();
+
+ public DefaultQueueRegistry()
+ {
+ }
+
+ public void registerQueue(AMQQueue queue) throws AMQException
+ {
+ _queueMap.put(queue.getName(), queue);
+ }
+
+ public void unregisterQueue(String name) throws AMQException
+ {
+ _queueMap.remove(name);
+ }
+
+ public AMQQueue getQueue(String name)
+ {
+ return _queueMap.get(name);
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/queue/DeliveryManager.java b/java/broker/src/org/apache/qpid/server/queue/DeliveryManager.java
new file mode 100644
index 0000000000..fbd952073e
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/queue/DeliveryManager.java
@@ -0,0 +1,262 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.AMQException;
+import org.apache.log4j.Logger;
+
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Manages delivery of messages on behalf of a queue
+ *
+ */
+class DeliveryManager
+{
+ private static final Logger _log = Logger.getLogger(DeliveryManager.class);
+
+ /**
+ * Holds any queued messages
+ */
+ private final Queue<AMQMessage> _messages = new LinkedList<AMQMessage>();
+ /**
+ * Ensures that only one asynchronous task is running for this manager at
+ * any time.
+ */
+ private final AtomicBoolean _processing = new AtomicBoolean();
+ /**
+ * The subscriptions on the queue to whom messages are delivered
+ */
+ private final SubscriptionManager _subscriptions;
+
+ /**
+ * An indication of the mode we are in. If this is true then messages are
+ * being queued up in _messages for asynchronous delivery. If it is false
+ * then messages can be delivered directly as they come in.
+ */
+ private boolean _queueing;
+
+ /**
+ * A reference to the queue we are delivering messages for. We need this to be able
+ * to pass the code that handles acknowledgements a handle on the queue.
+ */
+ private final AMQQueue _queue;
+
+ DeliveryManager(SubscriptionManager subscriptions, AMQQueue queue)
+ {
+ _subscriptions = subscriptions;
+ _queue = queue;
+ }
+
+ private synchronized boolean enqueue(AMQMessage msg)
+ {
+ if (_queueing)
+ {
+ _messages.offer(msg);
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ private synchronized void startQueueing(AMQMessage msg)
+ {
+ _queueing = true;
+ enqueue(msg);
+ }
+
+ /**
+ * Determines whether there are queued messages. Sets _queueing to false if
+ * there are no queued messages. This needs to be atomic.
+ *
+ * @return true if there are queued messages
+ */
+ private synchronized boolean hasQueuedMessages()
+ {
+ boolean empty = _messages.isEmpty();
+ if (empty)
+ {
+ _queueing = false;
+ }
+ return !empty;
+ }
+
+ public synchronized int getQueueMessageCount()
+ {
+ return _messages.size();
+ }
+
+ protected synchronized List<AMQMessage> getMessages()
+ {
+ return new ArrayList<AMQMessage>(_messages);
+ }
+
+ protected synchronized void removeAMessageFromTop() throws AMQException
+ {
+ AMQMessage msg = poll();
+ if (msg != null)
+ {
+ msg.dequeue(_queue);
+ }
+ }
+
+ protected synchronized void clearAllMessages() throws AMQException
+ {
+ AMQMessage msg = poll();
+ while (msg != null)
+ {
+ msg.dequeue(_queue);
+ msg = poll();
+ }
+ }
+
+ /**
+ * Only one thread should ever execute this method concurrently, but
+ * it can do so while other threads invoke deliver().
+ */
+ private void processQueue()
+ {
+ try
+ {
+ boolean hasSubscribers = _subscriptions.hasActiveSubscribers();
+ while (hasQueuedMessages() && hasSubscribers)
+ {
+ Subscription next = _subscriptions.nextSubscriber(peek());
+ //We don't synchronize access to subscribers so need to re-check
+ if (next != null)
+ {
+ try
+ {
+ next.send(poll(), _queue);
+ }
+ catch (AMQException e)
+ {
+ _log.error("Unable to deliver message: " + e, e);
+ }
+ }
+ else
+ {
+ hasSubscribers = false;
+ }
+ }
+ }
+ finally
+ {
+ _processing.set(false);
+ }
+ }
+
+ private synchronized AMQMessage peek()
+ {
+ return _messages.peek();
+ }
+
+ private synchronized AMQMessage poll()
+ {
+ return _messages.poll();
+ }
+
+ /**
+ * Requests that the delivery manager start processing the queue asynchronously
+ * if there is work that can be done (i.e. there are messages queued up and
+ * subscribers that can receive them.
+ * <p/>
+ * This should be called when subscribers are added, but only after the consume-ok
+ * message has been returned as message delivery may start immediately. It should also
+ * be called after unsuspending a client.
+ * <p/>
+ *
+ * @param executor the executor on which the delivery should take place
+ */
+ void processAsync(Executor executor)
+ {
+ if (hasQueuedMessages() && _subscriptions.hasActiveSubscribers())
+ {
+ //are we already running? if so, don't re-run
+ if (_processing.compareAndSet(false, true))
+ {
+ executor.execute(new Runner());
+ }
+ }
+ }
+
+ /**
+ * Handles message delivery. The delivery manager is always in one of two modes;
+ * it is either queueing messages for asynchronous delivery or delivering
+ * directly.
+ *
+ * @param name the name of the entity on whose behalf we are delivering the message
+ * @param msg the message to deliver
+ * @throws NoConsumersException if there are no active subscribers to deliver
+ * the message to
+ */
+ void deliver(String name, AMQMessage msg) throws AMQException
+ {
+ msg.incrementReference();
+ // first check whether we are queueing, and enqueue if we are
+ if (!enqueue(msg))
+ {
+ // not queueing so deliver message to 'next' subscriber
+ Subscription s = _subscriptions.nextSubscriber(msg);
+ if (s == null)
+ {
+ if (msg.isImmediate())
+ {
+ throw msg.getNoConsumersException(name);
+ }
+ else
+ {
+ // no subscribers yet so enter 'queueing' mode and queue this message
+ startQueueing(msg);
+ }
+ }
+ else
+ {
+ s.send(msg, _queue);
+ }
+ }
+
+ else
+ {
+ if (msg.isImmediate())
+ {
+ //todo check with spec to see if enqueing for immediate client delivery is ok.
+ Subscription s = _subscriptions.nextSubscriber(msg);
+ if (s == null)
+ {
+ throw msg.getNoConsumersException(name);
+ }
+ }
+ }
+ }
+
+ private class Runner implements Runnable
+ {
+ public void run()
+ {
+ processQueue();
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/queue/ExchangeBindings.java b/java/broker/src/org/apache/qpid/server/queue/ExchangeBindings.java
new file mode 100644
index 0000000000..424330bd11
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/queue/ExchangeBindings.java
@@ -0,0 +1,109 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.AMQException;
+
+import java.util.List;
+import java.util.HashSet;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * When a queue is deleted, it should be deregistered from any
+ * exchange it has been bound to. This class assists in this task,
+ * by keeping track of all bindings for a given queue.
+ */
+class ExchangeBindings
+{
+ static class ExchangeBinding
+ {
+ private final Exchange exchange;
+ private final String routingKey;
+
+ ExchangeBinding(String routingKey, Exchange exchange)
+ {
+ this.routingKey = routingKey;
+ this.exchange = exchange;
+ }
+
+ void unbind(AMQQueue queue) throws AMQException
+ {
+ exchange.deregisterQueue(routingKey, queue);
+ }
+
+ public Exchange getExchange()
+ {
+ return exchange;
+ }
+
+ public String getRoutingKey()
+ {
+ return routingKey;
+ }
+
+ public int hashCode()
+ {
+ return exchange.hashCode() + routingKey.hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ if (!(o instanceof ExchangeBinding)) return false;
+ ExchangeBinding eb = (ExchangeBinding) o;
+ return exchange.equals(eb.exchange) && routingKey.equals(eb.routingKey);
+ }
+ }
+
+ private final List<ExchangeBinding> _bindings = new CopyOnWriteArrayList<ExchangeBinding>();
+ private final AMQQueue _queue;
+
+ ExchangeBindings(AMQQueue queue)
+ {
+ _queue = queue;
+ }
+
+ /**
+ * Adds the specified binding to those being tracked.
+ * @param routingKey the routing key with which the queue whose bindings
+ * are being tracked by the instance has been bound to the exchange
+ * @param exchange the exchange bound to
+ */
+ void addBinding(String routingKey, Exchange exchange)
+ {
+ _bindings.add(new ExchangeBinding(routingKey, exchange));
+ }
+
+ /**
+ * Deregisters this queue from any exchange it has been bound to
+ */
+ void deregister() throws AMQException
+ {
+ //remove duplicates at this point
+ HashSet<ExchangeBinding> copy = new HashSet<ExchangeBinding>(_bindings);
+ for (ExchangeBinding b : copy)
+ {
+ b.unbind(_queue);
+ }
+ }
+
+ List<ExchangeBinding> getExchangeBindings()
+ {
+ return _bindings;
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/queue/ManagedQueue.java b/java/broker/src/org/apache/qpid/server/queue/ManagedQueue.java
new file mode 100644
index 0000000000..03a0a10337
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/queue/ManagedQueue.java
@@ -0,0 +1,177 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import javax.management.openmbean.TabularData;
+import javax.management.JMException;
+import java.io.IOException;
+
+/**
+ * The management interface exposed to allow management of a queue.
+ * @author Robert J. Greig
+ * @author Bhupendra Bhardwaj
+ * @version 0.1
+ */
+public interface ManagedQueue
+{
+ static final String TYPE = "Queue";
+
+ /**
+ * Returns the Name of the ManagedQueue.
+ * @return the name of the managedQueue.
+ * @throws IOException
+ */
+ String getName() throws IOException;
+
+ /**
+ * Tells whether this ManagedQueue is durable or not.
+ * @return true if this ManagedQueue is a durable queue.
+ * @throws IOException
+ */
+ boolean isDurable() throws IOException;
+
+ /**
+ * Tells the Owner of the ManagedQueue.
+ * @return the owner's name.
+ * @throws IOException
+ */
+ String getOwner() throws IOException;
+
+ /**
+ * Tells if the ManagedQueue is set to AutoDelete.
+ * @return true if the ManagedQueue is set to AutoDelete.
+ * @throws IOException
+ */
+ boolean isAutoDelete() throws IOException;
+
+ /**
+ * Gets the total number of messages on the queue, which are yet to be
+ * delivered to the consumer(s).
+ * @return number of undelivered message in the Queue.
+ * @throws IOException
+ */
+ int getMessageCount() throws IOException;
+
+ /**
+ * Returns the maximum size of a message (in bytes) allowed to be accepted by the
+ * ManagedQueue. This is useful in setting notifications or taking
+ * appropriate action, if the size of the message received is more than
+ * the allowed size.
+ * @return the maximum size of a message allowed to be aceepted by the
+ * ManagedQueue.
+ * @throws IOException
+ */
+ long getMaximumMessageSize() throws IOException;
+
+ /**
+ * Sets the maximum size of the message (in bytes) that is allowed to be
+ * accepted by the Queue.
+ * @param bytes maximum size of message.
+ * @throws IOException
+ */
+ void setMaximumMessageSize(long bytes) throws IOException;
+
+ /**
+ * Returns the total number of subscribers to the queue.
+ * @return the number of subscribers.
+ * @throws IOException
+ */
+ int getConsumerCount() throws IOException;
+
+ /**
+ * Returns the total number of active subscribers to the queue.
+ * @return the number of active subscribers
+ * @throws IOException
+ */
+ int getActiveConsumerCount() throws IOException;
+
+ /**
+ * Tells the total number of messages receieved by the queue since startup.
+ * @return total number of messages received.
+ * @throws IOException
+ */
+ long getReceivedMessageCount() throws IOException;
+
+ /**
+ * Tells the maximum number of messages that can be stored in the queue.
+ * This is useful in setting the notifications or taking required
+ * action is the number of message increase this limit.
+ * @return maximum muber of message allowed to be stored in the queue.
+ * @throws IOException
+ */
+ long getMaximumMessageCount() throws IOException;
+
+ /**
+ * Sets the maximum number of messages allowed to be stored in the queue.
+ * @param value the maximum number of messages allowed to be stored in the queue.
+ * @throws IOException
+ */
+ void setMaximumMessageCount(long value) throws IOException;
+
+ /**
+ * Tells the maximum size of all the messages combined together,
+ * that can be stored in the queue. This is useful for setting notifications
+ * or taking required action if the size of messages stored in the queue
+ * increases over this limit.
+ * @return maximum size of the all the messages allowed for the queue.
+ * @throws IOException
+ */
+ long getQueueDepth() throws IOException;
+
+ /**
+ * Sets the maximum size of all the messages together, that can be stored
+ * in the queue.
+ * @param value
+ * @throws IOException
+ */
+ void setQueueDepth(long value) throws IOException;
+
+
+
+ //********** Operations *****************//
+
+
+ /**
+ * Returns a subset of all the messages stored in the queue. The messages
+ * are returned based on the given index numbers.
+ * @param fromIndex
+ * @param toIndex
+ * @return
+ * @throws IOException
+ * @throws JMException
+ */
+ TabularData viewMessages(int fromIndex, int toIndex)
+ throws IOException, JMException;
+
+ /**
+ * Deletes the first message from top.
+ * @throws IOException
+ * @throws JMException
+ */
+ void deleteMessageFromTop()
+ throws IOException, JMException;
+
+ /**
+ * Clears the queue by deleting all the messages from the queue.
+ * @throws IOException
+ * @throws JMException
+ */
+ void clearQueue()
+ throws IOException, JMException;
+
+}
diff --git a/java/broker/src/org/apache/qpid/server/queue/NoConsumersException.java b/java/broker/src/org/apache/qpid/server/queue/NoConsumersException.java
new file mode 100644
index 0000000000..0616f75633
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/queue/NoConsumersException.java
@@ -0,0 +1,47 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.server.RequiredDeliveryException;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.BasicPublishBody;
+import org.apache.qpid.protocol.AMQConstant;
+
+import java.util.List;
+
+/**
+ * Signals that no consumers exist for a message at a given point in time.
+ * Used if a message has immediate=true and there are no consumers registered
+ * with the queue.
+ */
+public class NoConsumersException extends RequiredDeliveryException
+{
+ public NoConsumersException(String queue,
+ BasicPublishBody publishBody,
+ ContentHeaderBody contentHeaderBody,
+ List<ContentBody> contentBodies)
+ {
+ super("Immediate delivery to " + queue + " is not possible.", publishBody, contentHeaderBody, contentBodies);
+ }
+
+ public int getReplyCode()
+ {
+ return AMQConstant.NO_CONSUMERS.getCode();
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/queue/QueueRegistry.java b/java/broker/src/org/apache/qpid/server/queue/QueueRegistry.java
new file mode 100644
index 0000000000..bc5599fb20
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/queue/QueueRegistry.java
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.AMQException;
+
+
+public interface QueueRegistry
+{
+ void registerQueue(AMQQueue queue) throws AMQException;
+
+ void unregisterQueue(String name) throws AMQException;
+
+ AMQQueue getQueue(String name);
+}
diff --git a/java/broker/src/org/apache/qpid/server/queue/Subscription.java b/java/broker/src/org/apache/qpid/server/queue/Subscription.java
new file mode 100644
index 0000000000..d76e9ec105
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/queue/Subscription.java
@@ -0,0 +1,29 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.AMQException;
+
+public interface Subscription
+{
+ void send(AMQMessage msg, AMQQueue queue) throws AMQException;
+
+ boolean isSuspended();
+
+ void queueDeleted(AMQQueue queue);
+}
diff --git a/java/broker/src/org/apache/qpid/server/queue/SubscriptionFactory.java b/java/broker/src/org/apache/qpid/server/queue/SubscriptionFactory.java
new file mode 100644
index 0000000000..127b19b0e4
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/queue/SubscriptionFactory.java
@@ -0,0 +1,37 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.AMQException;
+
+/**
+ * Allows the customisation of the creation of a subscription. This is typically done within an AMQQueue. This
+ * factory primarily assists testing although in future more sophisticated subscribers may need a different
+ * subscription implementation.
+ *
+ * @see org.apache.qpid.server.queue.AMQQueue
+ */
+public interface SubscriptionFactory
+{
+ Subscription createSubscription(int channel, AMQProtocolSession protocolSession, String consumerTag, boolean acks)
+ throws AMQException;
+
+ Subscription createSubscription(int channel, AMQProtocolSession protocolSession,String consumerTag)
+ throws AMQException;
+}
diff --git a/java/broker/src/org/apache/qpid/server/queue/SubscriptionImpl.java b/java/broker/src/org/apache/qpid/server/queue/SubscriptionImpl.java
new file mode 100644
index 0000000000..a3c2fab4f4
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/queue/SubscriptionImpl.java
@@ -0,0 +1,172 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.log4j.Logger;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.qpid.framing.AMQDataBlock;
+import org.apache.qpid.framing.AMQFrame;
+import org.apache.qpid.framing.BasicDeliverBody;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.AMQException;
+
+/**
+ * Encapsulation of a supscription to a queue.
+ * <p/>
+ * Ties together the protocol session of a subscriber, the consumer tag that
+ * was given out by the broker and the channel id.
+ * <p/>
+ */
+public class SubscriptionImpl implements Subscription
+{
+ private static final Logger _logger = Logger.getLogger(SubscriptionImpl.class);
+
+ public final AMQChannel channel;
+
+ public final AMQProtocolSession protocolSession;
+
+ public final String consumerTag;
+
+ private final Object sessionKey;
+
+ /**
+ * True if messages need to be acknowledged
+ */
+ private final boolean _acks;
+
+ public static class Factory implements SubscriptionFactory
+ {
+ public SubscriptionImpl createSubscription(int channel, AMQProtocolSession protocolSession, String consumerTag, boolean acks)
+ throws AMQException
+ {
+ return new SubscriptionImpl(channel, protocolSession, consumerTag, acks);
+ }
+
+ public SubscriptionImpl createSubscription(int channel, AMQProtocolSession protocolSession, String consumerTag)
+ throws AMQException
+ {
+ return new SubscriptionImpl(channel, protocolSession, consumerTag);
+ }
+ }
+
+ public SubscriptionImpl(int channelId, AMQProtocolSession protocolSession,
+ String consumerTag, boolean acks)
+ throws AMQException
+ {
+ AMQChannel channel = protocolSession.getChannel(channelId);
+ if (channel == null) {
+ throw new NullPointerException("channel not found in protocol session");
+ }
+
+ this.channel = channel;
+ this.protocolSession = protocolSession;
+ this.consumerTag = consumerTag;
+ sessionKey = protocolSession.getKey();
+ _acks = acks;
+ }
+
+ public SubscriptionImpl(int channel, AMQProtocolSession protocolSession,
+ String consumerTag)
+ throws AMQException
+ {
+ this(channel, protocolSession, consumerTag, false);
+ }
+
+ public boolean equals(Object o)
+ {
+ return (o instanceof SubscriptionImpl) && equals((SubscriptionImpl) o);
+ }
+
+ /**
+ * Equality holds if the session matches and the channel and consumer tag are the same.
+ */
+ private boolean equals(SubscriptionImpl psc)
+ {
+ return sessionKey.equals(psc.sessionKey)
+ && psc.channel == channel
+ && psc.consumerTag.equals(consumerTag);
+ }
+
+ public int hashCode()
+ {
+ return sessionKey.hashCode();
+ }
+
+ public String toString()
+ {
+ return "[channel=" + channel + ", consumerTag=" + consumerTag + ", session=" + protocolSession.getKey() + "]";
+ }
+
+ public void send(AMQMessage msg, AMQQueue queue) throws AMQException
+ {
+ if (msg != null)
+ {
+ final long deliveryTag = channel.getNextDeliveryTag();
+ ByteBuffer deliver = createEncodedDeliverFrame(deliveryTag, msg.getRoutingKey(), msg.getExchangeName());
+ AMQDataBlock frame = msg.getDataBlock(deliver, channel.getChannelId());
+ if (_acks)
+ {
+ channel.addUnacknowledgedMessage(msg, deliveryTag, consumerTag, queue);
+ }
+ protocolSession.writeFrame(frame);
+ // if we do not need to wait for client acknowledgements we can decrement
+ // the reference count immediately
+ if (!_acks)
+ {
+ msg.decrementReference();
+ msg.dequeue(queue);
+ }
+ else
+ {
+ //move the msg to the back of the persistently recorded queue while
+ //witing for ack
+ msg.requeue(queue);
+ }
+ }
+ else
+ {
+ _logger.error("Attempt to send Null message", new NullPointerException());
+ }
+ }
+
+ public boolean isSuspended()
+ {
+ return channel.isSuspended();
+ }
+
+ /**
+ * Callback indicating that a queue has been deleted.
+ * @param queue
+ */
+ public void queueDeleted(AMQQueue queue)
+ {
+ channel.queueDeleted(queue);
+ }
+
+ private ByteBuffer createEncodedDeliverFrame(long deliveryTag, String routingKey, String exchange)
+ {
+ AMQFrame deliverFrame = BasicDeliverBody.createAMQFrame(channel.getChannelId(), consumerTag,
+ deliveryTag, false, exchange,
+ routingKey);
+ ByteBuffer buf = ByteBuffer.allocate((int) deliverFrame.getSize()); // XXX: Could cast be a problem?
+ deliverFrame.writePayload(buf);
+ buf.flip();
+ return buf;
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/queue/SubscriptionManager.java b/java/broker/src/org/apache/qpid/server/queue/SubscriptionManager.java
new file mode 100644
index 0000000000..185b0e4268
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/queue/SubscriptionManager.java
@@ -0,0 +1,28 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+/**
+ * Abstraction of actor that will determine the subscriber to whom
+ * a message will be sent.
+ */
+public interface SubscriptionManager
+{
+ public boolean hasActiveSubscribers();
+ public Subscription nextSubscriber(AMQMessage msg);
+}
diff --git a/java/broker/src/org/apache/qpid/server/queue/SubscriptionSet.java b/java/broker/src/org/apache/qpid/server/queue/SubscriptionSet.java
new file mode 100644
index 0000000000..3b53792234
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/queue/SubscriptionSet.java
@@ -0,0 +1,180 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.log4j.Logger;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Holds a set of subscriptions for a queue and manages the round
+ * robin-ing of deliver etc.
+ */
+class SubscriptionSet implements WeightedSubscriptionManager
+{
+ private static final Logger _log = Logger.getLogger(SubscriptionSet.class);
+
+ /**
+ * List of registered subscribers
+ */
+ private List<Subscription> _subscriptions = new CopyOnWriteArrayList<Subscription>();
+
+ /**
+ * Used to control the round robin delivery of content
+ */
+ private int _currentSubscriber;
+
+ /**
+ * Accessor for unit tests.
+ */
+ int getCurrentSubscriber()
+ {
+ return _currentSubscriber;
+ }
+
+ public void addSubscriber(Subscription subscription)
+ {
+ _subscriptions.add(subscription);
+ }
+
+ /**
+ * Remove the subscription, returning it if it was found
+ * @param subscription
+ * @return null if no match was found
+ */
+ public Subscription removeSubscriber(Subscription subscription)
+ {
+ boolean isRemoved = _subscriptions.remove(subscription); // TODO: possibly need O(1) operation here.
+ if (isRemoved)
+ {
+ return subscription;
+ }
+ else
+ {
+ debugDumpSubscription(subscription);
+ return null;
+ }
+ }
+
+ private void debugDumpSubscription(Subscription subscription)
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Subscription " + subscription + " not found. Dumping subscriptions:");
+ for (Subscription s : _subscriptions)
+ {
+ _log.debug("Subscription: " + s);
+ }
+ _log.debug("Subscription dump complete");
+ }
+ }
+
+ /**
+ * Return the next unsuspended subscription or null if not found.
+ *
+ * Performance note:
+ * This method can scan all items twice when looking for a subscription that is not
+ * suspended. The worst case occcurs when all subscriptions are suspended. However, it is does this
+ * without synchronisation and subscriptions may be added and removed concurrently. Also note that because of
+ * race conditions and when subscriptions are removed between calls to nextSubscriber, the
+ * IndexOutOfBoundsException also causes the scan to start at the beginning.
+ */
+ public Subscription nextSubscriber(AMQMessage msg)
+ {
+ if (_subscriptions.isEmpty())
+ {
+ return null;
+ }
+
+ try {
+ final Subscription result = nextSubscriber();
+ if (result == null) {
+ _currentSubscriber = 0;
+ return nextSubscriber();
+ } else {
+ return result;
+ }
+ } catch (IndexOutOfBoundsException e) {
+ _currentSubscriber = 0;
+ return nextSubscriber();
+ }
+ }
+
+ private Subscription nextSubscriber()
+ {
+ final ListIterator<Subscription> iterator = _subscriptions.listIterator(_currentSubscriber);
+ while (iterator.hasNext()) {
+ Subscription subscription = iterator.next();
+ ++_currentSubscriber;
+ subscriberScanned();
+ if (!subscription.isSuspended()) {
+ return subscription;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Overridden in test classes.
+ */
+ protected void subscriberScanned()
+ {
+ }
+
+ public boolean isEmpty()
+ {
+ return _subscriptions.isEmpty();
+ }
+
+ public boolean hasActiveSubscribers()
+ {
+ for (Subscription s : _subscriptions)
+ {
+ if (!s.isSuspended()) return true;
+ }
+ return false;
+ }
+
+ public int getWeight()
+ {
+ int count = 0;
+ for (Subscription s : _subscriptions)
+ {
+ if (!s.isSuspended()) count++;
+ }
+ return count;
+ }
+
+ /**
+ * Notification that a queue has been deleted. This is called so that the subscription can inform the
+ * channel, which in turn can update its list of unacknowledged messages.
+ * @param queue
+ */
+ public void queueDeleted(AMQQueue queue)
+ {
+ for (Subscription s : _subscriptions)
+ {
+ s.queueDeleted(queue);
+ }
+ }
+
+ int size() {
+ return _subscriptions.size();
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/queue/WeightedSubscriptionManager.java b/java/broker/src/org/apache/qpid/server/queue/WeightedSubscriptionManager.java
new file mode 100644
index 0000000000..adf6aefdfb
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/queue/WeightedSubscriptionManager.java
@@ -0,0 +1,23 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+public interface WeightedSubscriptionManager extends SubscriptionManager
+{
+ public int getWeight();
+}
diff --git a/java/broker/src/org/apache/qpid/server/registry/ApplicationRegistry.java b/java/broker/src/org/apache/qpid/server/registry/ApplicationRegistry.java
new file mode 100644
index 0000000000..4d1b0dbcfe
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/registry/ApplicationRegistry.java
@@ -0,0 +1,108 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.registry;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.configuration.Configurator;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An abstract application registry that provides access to configuration information and handles the
+ * construction and caching of configurable objects.
+ *
+ * Subclasses should handle the construction of the "registered objects" such as the exchange registry.
+ *
+ */
+public abstract class ApplicationRegistry implements IApplicationRegistry
+{
+ private static final Logger _logger = Logger.getLogger(ApplicationRegistry.class);
+
+ private static IApplicationRegistry _instance;
+
+ private final Map<Class<?>, Object> _configuredObjects = new HashMap<Class<?>, Object>();
+
+ protected final Configuration _configuration;
+
+ private static class ShutdownService implements Runnable
+ {
+ public void run()
+ {
+ _logger.info("Shutting down application registry...");
+ try
+ {
+ _instance.getMessageStore().close();
+ }
+ catch (Exception e)
+ {
+ _logger.error("Error shutting down message store: " + e, e);
+ }
+ }
+ }
+
+ public static void initialise(IApplicationRegistry instance) throws Exception
+ {
+ _instance = instance;
+ instance.initialise();
+ Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownService()));
+ }
+
+ protected ApplicationRegistry(Configuration configuration)
+ {
+ _configuration = configuration;
+ }
+
+ public static IApplicationRegistry getInstance()
+ {
+ if (_instance == null)
+ {
+ throw new RuntimeException("Application registry not initialised");
+ }
+ else
+ {
+ return _instance;
+ }
+ }
+
+ public Configuration getConfiguration()
+ {
+ return _configuration;
+ }
+
+ public <T> T getConfiguredObject(Class<T> instanceType)
+ {
+ T instance = (T) _configuredObjects.get(instanceType);
+ if (instance == null)
+ {
+ try
+ {
+ instance = instanceType.newInstance();
+ }
+ catch (Exception e)
+ {
+ _logger.error("Unable to instantiate configuration class " + instanceType + " - ensure it has a public default constructor");
+ throw new IllegalArgumentException("Unable to instantiate configuration class " + instanceType + " - ensure it has a public default constructor");
+ }
+ Configurator.configure(instance);
+ _configuredObjects.put(instanceType, instance);
+ }
+ return instance;
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java b/java/broker/src/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java
new file mode 100644
index 0000000000..db79ae8876
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/registry/ConfigurationFileApplicationRegistry.java
@@ -0,0 +1,155 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.registry;
+
+import org.apache.commons.configuration.CompositeConfiguration;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.ConfigurationException;
+import org.apache.commons.configuration.SystemConfiguration;
+import org.apache.commons.configuration.XMLConfiguration;
+import org.apache.qpid.server.exchange.DefaultExchangeFactory;
+import org.apache.qpid.server.exchange.DefaultExchangeRegistry;
+import org.apache.qpid.server.exchange.ExchangeFactory;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.management.JMXManagedObjectRegistry;
+import org.apache.qpid.server.management.ManagedObjectRegistry;
+import org.apache.qpid.server.management.ManagementConfiguration;
+import org.apache.qpid.server.management.NoopManagedObjectRegistry;
+import org.apache.qpid.server.queue.DefaultQueueRegistry;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.security.auth.AuthenticationManager;
+import org.apache.qpid.server.security.auth.SASLAuthenticationManager;
+import org.apache.qpid.server.store.MessageStore;
+
+import java.io.File;
+
+public class ConfigurationFileApplicationRegistry extends ApplicationRegistry
+{
+ private QueueRegistry _queueRegistry;
+
+ private ExchangeRegistry _exchangeRegistry;
+
+ private ExchangeFactory _exchangeFactory;
+
+ private ManagedObjectRegistry _managedObjectRegistry;
+
+ private AuthenticationManager _authenticationManager;
+
+ private MessageStore _messageStore;
+
+ public ConfigurationFileApplicationRegistry(File configurationURL) throws ConfigurationException
+ {
+ super(config(configurationURL));
+ }
+
+ // Our configuration class needs to make the interpolate method
+ // public so it can be called below from the config method.
+ private static class MyConfiguration extends CompositeConfiguration {
+ public String interpolate(String obj) {
+ return super.interpolate(obj);
+ }
+ }
+
+ private static final Configuration config(File url) throws ConfigurationException {
+ // We have to override the interpolate methods so that
+ // interpolation takes place accross the entirety of the
+ // composite configuration. Without doing this each
+ // configuration object only interpolates variables defined
+ // inside itself.
+ final MyConfiguration conf = new MyConfiguration();
+ conf.addConfiguration(new SystemConfiguration() {
+ protected String interpolate(String o) {
+ return conf.interpolate(o);
+ }
+ });
+ conf.addConfiguration(new XMLConfiguration(url) {
+ protected String interpolate(String o) {
+ return conf.interpolate(o);
+ }
+ });
+ return conf;
+ }
+
+ public void initialise() throws Exception
+ {
+ initialiseManagedObjectRegistry();
+ _queueRegistry = new DefaultQueueRegistry();
+ _exchangeFactory = new DefaultExchangeFactory();
+ _exchangeRegistry = new DefaultExchangeRegistry(_exchangeFactory);
+ _authenticationManager = new SASLAuthenticationManager();
+ initialiseMessageStore();
+ }
+
+ private void initialiseManagedObjectRegistry()
+ {
+ ManagementConfiguration config = getConfiguredObject(ManagementConfiguration.class);
+ if (config.enabled)
+ {
+ _managedObjectRegistry = new JMXManagedObjectRegistry();
+ }
+ else
+ {
+ _managedObjectRegistry = new NoopManagedObjectRegistry();
+ }
+ }
+
+ private void initialiseMessageStore() throws Exception
+ {
+ String messageStoreClass = _configuration.getString("store.class");
+ Class clazz = Class.forName(messageStoreClass);
+ Object o = clazz.newInstance();
+
+ if (!(o instanceof MessageStore))
+ {
+ throw new Exception("Message store class must implement " + MessageStore.class + ". Class " + clazz +
+ " does not.");
+ }
+ _messageStore = (MessageStore) o;
+ _messageStore.configure(getQueueRegistry(), "store", _configuration);
+ }
+
+ public QueueRegistry getQueueRegistry()
+ {
+ return _queueRegistry;
+ }
+
+ public ExchangeRegistry getExchangeRegistry()
+ {
+ return _exchangeRegistry;
+ }
+
+ public ExchangeFactory getExchangeFactory()
+ {
+ return _exchangeFactory;
+ }
+
+ public ManagedObjectRegistry getManagedObjectRegistry()
+ {
+ return _managedObjectRegistry;
+ }
+
+ public AuthenticationManager getAuthenticationManager()
+ {
+ return _authenticationManager;
+ }
+
+ public MessageStore getMessageStore()
+ {
+ return _messageStore;
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/registry/IApplicationRegistry.java b/java/broker/src/org/apache/qpid/server/registry/IApplicationRegistry.java
new file mode 100644
index 0000000000..0102f78424
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/registry/IApplicationRegistry.java
@@ -0,0 +1,65 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.registry;
+
+import org.apache.qpid.server.exchange.ExchangeFactory;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.management.ManagedObjectRegistry;
+import org.apache.qpid.server.security.auth.AuthenticationManager;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.commons.configuration.Configuration;
+
+public interface IApplicationRegistry
+{
+ /**
+ * Initialise the application registry. All initialisation must be done in this method so that any components
+ * that need access to the application registry itself for initialisation are able to use it. Attempting to
+ * initialise in the constructor will lead to failures since the registry reference will not have been set.
+ */
+ void initialise() throws Exception;
+
+ /**
+ * This gets access to a "configured object". A configured object has fields populated from a the configuration
+ * object (Commons Configuration) automatically, where it has the appropriate attributes defined on fields.
+ * Application registry implementations can choose the refresh strategy or caching approach.
+ * @param instanceType the type of object you want initialised. This must be unique - i.e. you can only
+ * have a single object of this type in the system.
+ * @return the configured object
+ */
+ <T> T getConfiguredObject(Class<T> instanceType);
+
+ /**
+ * Get the low level configuration. For use cases where the configured object approach is not required
+ * you can get the complete configuration information.
+ * @return a Commons Configuration instance
+ */
+ Configuration getConfiguration();
+
+ QueueRegistry getQueueRegistry();
+
+ ExchangeRegistry getExchangeRegistry();
+
+ ExchangeFactory getExchangeFactory();
+
+ ManagedObjectRegistry getManagedObjectRegistry();
+
+ AuthenticationManager getAuthenticationManager();
+
+ MessageStore getMessageStore();
+}
diff --git a/java/broker/src/org/apache/qpid/server/security/auth/AuthenticationManager.java b/java/broker/src/org/apache/qpid/server/security/auth/AuthenticationManager.java
new file mode 100644
index 0000000000..0adb7b98e2
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/security/auth/AuthenticationManager.java
@@ -0,0 +1,30 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth;
+
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+public interface AuthenticationManager
+{
+ String getMechanisms();
+
+ SaslServer createSaslServer(String mechanism, String localFQDN) throws SaslException;
+
+ AuthenticationResult authenticate(SaslServer server, byte[] response);
+}
diff --git a/java/broker/src/org/apache/qpid/server/security/auth/AuthenticationProviderInitialiser.java b/java/broker/src/org/apache/qpid/server/security/auth/AuthenticationProviderInitialiser.java
new file mode 100644
index 0000000000..71e3c81ae4
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/security/auth/AuthenticationProviderInitialiser.java
@@ -0,0 +1,63 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth;
+
+import org.apache.commons.configuration.Configuration;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.SaslServerFactory;
+import java.util.Map;
+
+public interface AuthenticationProviderInitialiser
+{
+ /**
+ * @return the mechanism's name. This will be used in the list of mechanism's advertised to the
+ * client.
+ */
+ String getMechanismName();
+
+ /**
+ * Initialise the authentication provider.
+ * @param baseConfigPath the path in the config file that points to any config options for this provider. Each
+ * provider can have its own set of configuration options
+ * @param configuration the Apache Commons Configuration instance used to configure this provider
+ * @param principalDatabases the set of principal databases that are available
+ */
+ void initialise(String baseConfigPath, Configuration configuration,
+ Map<String, PrincipalDatabase> principalDatabases) throws Exception;
+
+ /**
+ * @return the callback handler that should be used to process authentication requests for this mechanism. This will
+ * be called after initialise and will be stored by the authentication manager. The callback handler <b>must</b> be
+ * fully threadsafe.
+ */
+ CallbackHandler getCallbackHandler();
+
+ /**
+ * Get the properties that must be passed in to the Sasl.createSaslServer method.
+ * @return the properties, which may be null
+ */
+ Map<String, ?> getProperties();
+
+ /**
+ * Get the class that is the server factory. This is used for the JCA registration.
+ * @return null if no JCA registration is required, otherwise return the class
+ * that will be used in JCA registration
+ */
+ Class<? extends SaslServerFactory> getServerFactoryClassForJCARegistration();
+}
diff --git a/java/broker/src/org/apache/qpid/server/security/auth/AuthenticationResult.java b/java/broker/src/org/apache/qpid/server/security/auth/AuthenticationResult.java
new file mode 100644
index 0000000000..d7dcf2c973
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/security/auth/AuthenticationResult.java
@@ -0,0 +1,40 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth;
+
+public class AuthenticationResult
+{
+ public enum AuthenticationStatus
+ {
+ SUCCESS, CONTINUE, ERROR
+ }
+
+ public AuthenticationStatus status;
+ public byte[] challenge;
+
+ public AuthenticationResult(byte[] challenge, AuthenticationStatus status)
+ {
+ this.status = status;
+ this.challenge = challenge;
+ }
+
+ public AuthenticationResult(AuthenticationStatus status)
+ {
+ this.status = status;
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/security/auth/CRAMMD5Initialiser.java b/java/broker/src/org/apache/qpid/server/security/auth/CRAMMD5Initialiser.java
new file mode 100644
index 0000000000..bfd2ac1b9f
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/security/auth/CRAMMD5Initialiser.java
@@ -0,0 +1,35 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth;
+
+import javax.security.sasl.SaslServerFactory;
+
+public class CRAMMD5Initialiser extends UsernamePasswordInitialiser
+{
+ public String getMechanismName()
+ {
+ return "CRAM-MD5";
+ }
+
+ public Class<? extends SaslServerFactory> getServerFactoryClassForJCARegistration()
+ {
+ // since the CRAM-MD5 provider is registered as part of the JDK, we do not
+ // return the factory class here since we do not need to register it ourselves.
+ return null;
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/security/auth/JCAProvider.java b/java/broker/src/org/apache/qpid/server/security/auth/JCAProvider.java
new file mode 100644
index 0000000000..1477b33ebe
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/security/auth/JCAProvider.java
@@ -0,0 +1,43 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth;
+
+import javax.security.sasl.SaslServerFactory;
+import java.security.Provider;
+import java.security.Security;
+import java.util.Map;
+
+public final class JCAProvider extends Provider
+{
+ public JCAProvider(Map<String, Class<? extends SaslServerFactory>> providerMap)
+ {
+ super("AMQSASLProvider", 1.0, "A JCA provider that registers all " +
+ "AMQ SASL providers that want to be registered");
+ register(providerMap);
+ Security.addProvider(this);
+ }
+
+ private void register(Map<String, Class<? extends SaslServerFactory>> providerMap)
+ {
+ for (Map.Entry<String, Class<? extends SaslServerFactory>> me :
+ providerMap.entrySet())
+ {
+ put("SaslServerFactory." + me.getKey(), me.getValue().getName());
+ }
+ }
+} \ No newline at end of file
diff --git a/java/broker/src/org/apache/qpid/server/security/auth/PasswordFilePrincipalDatabase.java b/java/broker/src/org/apache/qpid/server/security/auth/PasswordFilePrincipalDatabase.java
new file mode 100644
index 0000000000..fb11b89367
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/security/auth/PasswordFilePrincipalDatabase.java
@@ -0,0 +1,130 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth;
+
+import org.apache.log4j.Logger;
+
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.AccountNotFoundException;
+import java.security.Principal;
+import java.io.*;
+import java.util.regex.Pattern;
+
+/**
+ * Represents a user database where the account information is stored in a simple flat file.
+ *
+ * The file is expected to be in the form:
+ * username:password
+ * username1:password1
+ * ...
+ * usernamen:passwordn
+ *
+ * where a carriage return separates each username/password pair. Passwords are assumed to be in
+ * plain text.
+ *
+ */
+public class PasswordFilePrincipalDatabase implements PrincipalDatabase
+{
+ private static final Logger _logger = Logger.getLogger(PasswordFilePrincipalDatabase.class);
+
+ private File _passwordFile;
+
+ private Pattern _regexp = Pattern.compile(":");
+
+ public PasswordFilePrincipalDatabase()
+ {
+ }
+
+ public void setPasswordFile(String passwordFile) throws FileNotFoundException
+ {
+ File f = new File(passwordFile);
+ _logger.info("PasswordFilePrincipalDatabase using file " + f.getAbsolutePath());
+ _passwordFile = f;
+ if (!f.exists())
+ {
+ throw new FileNotFoundException("Cannot find password file " + f);
+ }
+ if (!f.canRead())
+ {
+ throw new FileNotFoundException("Cannot read password file " + f +
+ ". Check permissions.");
+ }
+ }
+
+ public void setPassword(Principal principal, PasswordCallback callback) throws IOException,
+ AccountNotFoundException
+ {
+ if (_passwordFile == null)
+ {
+ throw new AccountNotFoundException("Unable to locate principal since no password file was specified during initialisation");
+ }
+ if (principal == null)
+ {
+ throw new IllegalArgumentException("principal must not be null");
+ }
+ char[] pwd = lookupPassword(principal.getName());
+ if (pwd != null)
+ {
+ callback.setPassword(pwd);
+ }
+ else
+ {
+ throw new AccountNotFoundException("No account found for principal " + principal);
+ }
+ }
+
+ /**
+ * Looks up the password for a specified user in the password file.
+ * Note this code is <b>not</b> secure since it creates strings of passwords. It should be modified
+ * to create only char arrays which get nulled out.
+ * @param name
+ * @return
+ * @throws IOException
+ */
+ private char[] lookupPassword(String name) throws IOException
+ {
+ BufferedReader reader = null;
+ try
+ {
+ reader = new BufferedReader(new FileReader(_passwordFile));
+ String line;
+
+ while ((line = reader.readLine()) != null)
+ {
+ String[] result = _regexp.split(line);
+ if (result == null || result.length < 2)
+ {
+ continue;
+ }
+
+ if (name.equals(result[0]))
+ {
+ return result[1].toCharArray();
+ }
+ }
+ return null;
+ }
+ finally
+ {
+ if (reader != null)
+ {
+ reader.close();
+ }
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/security/auth/PrincipalDatabase.java b/java/broker/src/org/apache/qpid/server/security/auth/PrincipalDatabase.java
new file mode 100644
index 0000000000..8add5455ee
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/security/auth/PrincipalDatabase.java
@@ -0,0 +1,42 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth;
+
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.login.AccountNotFoundException;
+import java.security.Principal;
+import java.io.IOException;
+
+/**
+ * Represents a "user database" which is really a way of storing principals (i.e. usernames) and
+ * passwords.
+ */
+public interface PrincipalDatabase
+{
+ /**
+ * Set the password for a given principal in the specified callback. This is used for certain
+ * SASL providers. The user database implementation should look up the password in any way it
+ * chooses and set it in the callback by calling its setPassword method.
+ * @param principal the principal
+ * @param callback the password callback that wants to receive the password
+ * @throws AccountNotFoundException if the account for specified principal could not be found
+ * @throws IOException if there was an error looking up the principal
+ */
+ void setPassword(Principal principal, PasswordCallback callback)
+ throws IOException, AccountNotFoundException;
+}
diff --git a/java/broker/src/org/apache/qpid/server/security/auth/SASLAuthenticationManager.java b/java/broker/src/org/apache/qpid/server/security/auth/SASLAuthenticationManager.java
new file mode 100644
index 0000000000..7d0b60d95e
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/security/auth/SASLAuthenticationManager.java
@@ -0,0 +1,224 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.log4j.Logger;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.configuration.PropertyUtils;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+import javax.security.sasl.SaslServerFactory;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+import java.security.Security;
+
+public class SASLAuthenticationManager implements AuthenticationManager
+{
+ private static final Logger _log = Logger.getLogger(SASLAuthenticationManager.class);
+
+ /**
+ * The list of mechanisms, in the order in which they are configured (i.e. preferred order)
+ */
+ private String _mechanisms;
+
+ /**
+ * Maps from the mechanism to the callback handler to use for handling those requests
+ */
+ private Map<String, CallbackHandler> _callbackHandlerMap = new HashMap<String, CallbackHandler>();
+
+ /**
+ * Maps from the mechanism to the properties used to initialise the server. See the method
+ * Sasl.createSaslServer for details of the use of these properties. This map is populated during initialisation
+ * of each provider.
+ */
+ private Map<String, Map<String, ?>> _serverCreationProperties = new HashMap<String, Map<String, ?>>();
+
+ public SASLAuthenticationManager() throws Exception
+ {
+ _log.info("Initialising SASL authentication manager");
+ Map<String, PrincipalDatabase> databases = initialisePrincipalDatabases();
+ initialiseAuthenticationMechanisms(databases);
+ }
+
+ private Map<String, PrincipalDatabase> initialisePrincipalDatabases() throws Exception
+ {
+ Configuration config = ApplicationRegistry.getInstance().getConfiguration();
+ List<String> databaseNames = config.getList("security.principal-databases.principal-database.name");
+ List<String> databaseClasses = config.getList("security.principal-databases.principal-database.class");
+ Map<String, PrincipalDatabase> databases = new HashMap<String, PrincipalDatabase>();
+ for (int i = 0; i < databaseNames.size(); i++)
+ {
+ Object o;
+ try
+ {
+ o = Class.forName(databaseClasses.get(i)).newInstance();
+ }
+ catch (Exception e)
+ {
+ throw new Exception("Error initialising principal database: " + e, e);
+ }
+
+ if (!(o instanceof PrincipalDatabase))
+ {
+ throw new Exception("Principal databases must implement the PrincipalDatabase interface");
+ }
+
+ initialisePrincipalDatabase((PrincipalDatabase) o, config, i);
+
+ String name = databaseNames.get(i);
+ if (name == null || name.length() == 0)
+ {
+ throw new Exception("Principal database names must have length greater than or equal to one character");
+ }
+ PrincipalDatabase pd = databases.get(name);
+ if (pd != null)
+ {
+ throw new Exception("Duplicate principal database name provided");
+ }
+ _log.info("Initialised principal database " + name + " successfully");
+ databases.put(name, (PrincipalDatabase) o);
+ }
+ return databases;
+ }
+
+ private void initialisePrincipalDatabase(PrincipalDatabase principalDatabase, Configuration config, int index)
+ throws Exception
+ {
+ String baseName = "security.principal-databases.principal-database(" + index + ").attributes.attribute.";
+ List<String> argumentNames = config.getList(baseName + "name");
+ List<String> argumentValues = config.getList(baseName + "value");
+ for (int i = 0; i < argumentNames.size(); i++)
+ {
+ String argName = argumentNames.get(i);
+ if (argName == null || argName.length() == 0)
+ {
+ throw new Exception("Argument names must have length >= 1 character");
+ }
+ if (Character.isLowerCase(argName.charAt(0)))
+ {
+ argName = Character.toUpperCase(argName.charAt(0)) + argName.substring(1);
+ }
+ String methodName = "set" + argName;
+ Method method = principalDatabase.getClass().getMethod(methodName, String.class);
+ if (method == null)
+ {
+ throw new Exception("No method " + methodName + " found in class " + principalDatabase.getClass() +
+ " hence unable to configure principal database. The method must be public and " +
+ "have a single String argument with a void return type");
+ }
+ method.invoke(principalDatabase, PropertyUtils.replaceProperties(argumentValues.get(i)));
+ }
+ }
+
+ private void initialiseAuthenticationMechanisms(Map<String, PrincipalDatabase> databases) throws Exception
+ {
+ Configuration config = ApplicationRegistry.getInstance().getConfiguration();
+ List<String> mechanisms = config.getList("security.sasl.mechanisms.mechanism.initialiser.class");
+
+ // Maps from the mechanism to the properties used to initialise the server. See the method
+ // Sasl.createSaslServer for details of the use of these properties. This map is populated during initialisation
+ // of each provider.
+ Map<String, Class<? extends SaslServerFactory>> providerMap = new TreeMap<String, Class<? extends SaslServerFactory>>();
+
+ for (int i = 0; i < mechanisms.size(); i++)
+ {
+ String baseName = "security.sasl.mechanisms.mechanism(" + i + ").initialiser";
+ String clazz = config.getString(baseName + ".class");
+ initialiseAuthenticationMechanism(baseName, clazz, databases, config, providerMap);
+ }
+ if (providerMap.size() > 0)
+ {
+ Security.addProvider(new JCAProvider(providerMap));
+ }
+ }
+
+ private void initialiseAuthenticationMechanism(String baseName, String clazz,
+ Map<String, PrincipalDatabase> databases,
+ Configuration configuration,
+ Map<String, Class<? extends SaslServerFactory>> providerMap)
+ throws Exception
+ {
+ Class initialiserClazz = Class.forName(clazz);
+ Object o = initialiserClazz.newInstance();
+ if (!(o instanceof AuthenticationProviderInitialiser))
+ {
+ throw new Exception("The class " + clazz + " must be an instance of " +
+ AuthenticationProviderInitialiser.class);
+ }
+ AuthenticationProviderInitialiser initialiser = (AuthenticationProviderInitialiser) o;
+ initialiser.initialise(baseName, configuration, databases);
+ String mechanism = initialiser.getMechanismName();
+ if (_mechanisms == null)
+ {
+ _mechanisms = mechanism;
+ }
+ else
+ {
+ // simple append should be fine since the number of mechanisms is small and this is a one time initialisation
+ _mechanisms = _mechanisms + " " + mechanism;
+ }
+ _callbackHandlerMap.put(mechanism, initialiser.getCallbackHandler());
+ _serverCreationProperties.put(mechanism, initialiser.getProperties());
+ Class<? extends SaslServerFactory> factory = initialiser.getServerFactoryClassForJCARegistration();
+ if (factory != null)
+ {
+ providerMap.put(mechanism, factory);
+ }
+ _log.info("Initialised " + mechanism + " SASL provider successfully");
+ }
+
+ public String getMechanisms()
+ {
+ return _mechanisms;
+ }
+
+ public SaslServer createSaslServer(String mechanism, String localFQDN) throws SaslException
+ {
+ return Sasl.createSaslServer(mechanism, "AMQP", localFQDN, _serverCreationProperties.get(mechanism),
+ _callbackHandlerMap.get(mechanism));
+ }
+
+ public AuthenticationResult authenticate(SaslServer server, byte[] response)
+ {
+ try
+ {
+ // Process response from the client
+ byte[] challenge = server.evaluateResponse(response != null ? response : new byte[0]);
+
+ if (server.isComplete())
+ {
+ return new AuthenticationResult(challenge, AuthenticationResult.AuthenticationStatus.SUCCESS);
+ }
+ else
+ {
+ return new AuthenticationResult(challenge, AuthenticationResult.AuthenticationStatus.CONTINUE);
+ }
+ }
+ catch (SaslException e)
+ {
+ return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.ERROR);
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/security/auth/UsernamePasswordInitialiser.java b/java/broker/src/org/apache/qpid/server/security/auth/UsernamePasswordInitialiser.java
new file mode 100644
index 0000000000..02a953f47c
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/security/auth/UsernamePasswordInitialiser.java
@@ -0,0 +1,99 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth;
+
+import org.apache.commons.configuration.Configuration;
+
+import javax.security.auth.callback.*;
+import javax.security.auth.login.AccountNotFoundException;
+import javax.security.sasl.AuthorizeCallback;
+import java.util.Map;
+import java.io.IOException;
+import java.security.Principal;
+
+public abstract class UsernamePasswordInitialiser implements AuthenticationProviderInitialiser
+{
+ private ServerCallbackHandler _callbackHandler;
+
+ private class ServerCallbackHandler implements CallbackHandler
+ {
+ private final PrincipalDatabase _principalDatabase;
+
+ protected ServerCallbackHandler(PrincipalDatabase database)
+ {
+ _principalDatabase = database;
+ }
+
+ public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException
+ {
+ Principal username = null;
+ for (Callback callback : callbacks)
+ {
+ if (callback instanceof NameCallback)
+ {
+ username = new UsernamePrincipal(((NameCallback)callback).getDefaultName());
+ }
+ else if (callback instanceof PasswordCallback)
+ {
+ try
+ {
+ _principalDatabase.setPassword(username, (PasswordCallback) callback);
+ }
+ catch (AccountNotFoundException e)
+ {
+ // very annoyingly the callback handler does not throw anything more appropriate than
+ // IOException
+ throw new IOException("Error looking up user " + e);
+ }
+ }
+ else if (callback instanceof AuthorizeCallback)
+ {
+ ((AuthorizeCallback)callback).setAuthorized(true);
+ }
+ else
+ {
+ throw new UnsupportedCallbackException(callback);
+ }
+ }
+ }
+ }
+
+ public void initialise(String baseConfigPath, Configuration configuration,
+ Map<String, PrincipalDatabase> principalDatabases) throws Exception
+ {
+ String principalDatabaseName = configuration.getString(baseConfigPath + ".principal-database");
+ PrincipalDatabase db = principalDatabases.get(principalDatabaseName);
+ if (db == null)
+ {
+ throw new Exception("Principal database " + principalDatabaseName + " not found. Ensure the name matches " +
+ "an entry in the configuration file");
+ }
+ _callbackHandler = new ServerCallbackHandler(db);
+ }
+
+ public CallbackHandler getCallbackHandler()
+ {
+ return _callbackHandler;
+ }
+
+ public Map<String, ?> getProperties()
+ {
+ // there are no properties required for the CRAM-MD5 implementation
+ return null;
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/security/auth/UsernamePrincipal.java b/java/broker/src/org/apache/qpid/server/security/auth/UsernamePrincipal.java
new file mode 100644
index 0000000000..1d425b8399
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/security/auth/UsernamePrincipal.java
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth;
+
+import java.security.Principal;
+
+/**
+ * A principal that is just a wrapper for a simple username.
+ *
+ */
+public class UsernamePrincipal implements Principal
+{
+ private String _name;
+
+ public UsernamePrincipal(String name)
+ {
+ _name = name;
+ }
+
+ public String getName()
+ {
+ return _name;
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/security/auth/amqplain/AmqPlainInitialiser.java b/java/broker/src/org/apache/qpid/server/security/auth/amqplain/AmqPlainInitialiser.java
new file mode 100644
index 0000000000..94406237a5
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/security/auth/amqplain/AmqPlainInitialiser.java
@@ -0,0 +1,35 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth.amqplain;
+
+import org.apache.qpid.server.security.auth.UsernamePasswordInitialiser;
+
+import javax.security.sasl.SaslServerFactory;
+
+public class AmqPlainInitialiser extends UsernamePasswordInitialiser
+{
+ public String getMechanismName()
+ {
+ return "AMQPLAIN";
+ }
+
+ public Class<? extends SaslServerFactory> getServerFactoryClassForJCARegistration()
+ {
+ return AmqPlainSaslServerFactory.class;
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/security/auth/amqplain/AmqPlainSaslServer.java b/java/broker/src/org/apache/qpid/server/security/auth/amqplain/AmqPlainSaslServer.java
new file mode 100644
index 0000000000..0f7981abe1
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/security/auth/amqplain/AmqPlainSaslServer.java
@@ -0,0 +1,120 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth.amqplain;
+
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.AMQFrameDecodingException;
+import org.apache.mina.common.ByteBuffer;
+
+import javax.security.sasl.SaslServer;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.AuthorizeCallback;
+import javax.security.auth.callback.*;
+import java.io.IOException;
+
+public class AmqPlainSaslServer implements SaslServer
+{
+ public static final String MECHANISM = "AMQPLAIN";
+
+ private CallbackHandler _cbh;
+
+ private String _authorizationId;
+
+ private boolean _complete = false;
+
+ public AmqPlainSaslServer(CallbackHandler cbh)
+ {
+ _cbh = cbh;
+ }
+
+ public String getMechanismName()
+ {
+ return MECHANISM;
+ }
+
+ public byte[] evaluateResponse(byte[] response) throws SaslException
+ {
+ try
+ {
+ final FieldTable ft = new FieldTable(ByteBuffer.wrap(response), response.length);
+ String username = (String) ft.get("LOGIN");
+ // we do not care about the prompt but it throws if null
+ NameCallback nameCb = new NameCallback("prompt", username);
+ // we do not care about the prompt but it throws if null
+ PasswordCallback passwordCb = new PasswordCallback("prompt", false);
+ // TODO: should not get pwd as a String but as a char array...
+ String pwd = (String) ft.get("PASSWORD");
+ passwordCb.setPassword(pwd.toCharArray());
+ AuthorizeCallback authzCb = new AuthorizeCallback(username, username);
+ Callback[] callbacks = new Callback[]{nameCb, passwordCb, authzCb};
+ _cbh.handle(callbacks);
+ _complete = true;
+ if (authzCb.isAuthorized())
+ {
+ _authorizationId = authzCb.getAuthenticationID();
+ return null;
+ }
+ else
+ {
+ throw new SaslException("Authentication failed");
+ }
+ }
+ catch (AMQFrameDecodingException e)
+ {
+ throw new SaslException("Unable to decode response: " + e, e);
+ }
+ catch (IOException e)
+ {
+ throw new SaslException("Error processing data: " + e, e);
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ throw new SaslException("Unable to obtain data from callback handler: " + e, e);
+ }
+ }
+
+ public boolean isComplete()
+ {
+ return _complete;
+ }
+
+ public String getAuthorizationID()
+ {
+ return _authorizationId;
+ }
+
+ public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException
+ {
+ throw new SaslException("Unsupported operation");
+ }
+
+ public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException
+ {
+ throw new SaslException("Unsupported operation");
+ }
+
+ public Object getNegotiatedProperty(String propName)
+ {
+ return null;
+ }
+
+ public void dispose() throws SaslException
+ {
+ _cbh = null;
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/security/auth/amqplain/AmqPlainSaslServerFactory.java b/java/broker/src/org/apache/qpid/server/security/auth/amqplain/AmqPlainSaslServerFactory.java
new file mode 100644
index 0000000000..c4e904f923
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/security/auth/amqplain/AmqPlainSaslServerFactory.java
@@ -0,0 +1,56 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth.amqplain;
+
+import javax.security.sasl.SaslServerFactory;
+import javax.security.sasl.SaslServer;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.Sasl;
+import javax.security.auth.callback.CallbackHandler;
+import java.util.Map;
+
+public class AmqPlainSaslServerFactory implements SaslServerFactory
+{
+ public SaslServer createSaslServer(String mechanism, String protocol, String serverName, Map props,
+ CallbackHandler cbh) throws SaslException
+ {
+ if (AmqPlainSaslServer.MECHANISM.equals(mechanism))
+ {
+ return new AmqPlainSaslServer(cbh);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public String[] getMechanismNames(Map props)
+ {
+ if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) ||
+ props.containsKey(Sasl.POLICY_NODICTIONARY) ||
+ props.containsKey(Sasl.POLICY_NOACTIVE))
+ {
+ // returned array must be non null according to interface documentation
+ return new String[0];
+ }
+ else
+ {
+ return new String[]{AmqPlainSaslServer.MECHANISM};
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/security/auth/plain/PlainInitialiser.java b/java/broker/src/org/apache/qpid/server/security/auth/plain/PlainInitialiser.java
new file mode 100644
index 0000000000..7b055a4f58
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/security/auth/plain/PlainInitialiser.java
@@ -0,0 +1,35 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth.plain;
+
+import org.apache.qpid.server.security.auth.UsernamePasswordInitialiser;
+
+import javax.security.sasl.SaslServerFactory;
+
+public class PlainInitialiser extends UsernamePasswordInitialiser
+{
+ public String getMechanismName()
+ {
+ return "PLAIN";
+ }
+
+ public Class<? extends SaslServerFactory> getServerFactoryClassForJCARegistration()
+ {
+ return PlainSaslServerFactory.class;
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/security/auth/plain/PlainSaslServer.java b/java/broker/src/org/apache/qpid/server/security/auth/plain/PlainSaslServer.java
new file mode 100644
index 0000000000..5a69ed02ba
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/security/auth/plain/PlainSaslServer.java
@@ -0,0 +1,141 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth.plain;
+
+import javax.security.auth.callback.*;
+import javax.security.sasl.AuthorizeCallback;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+import java.io.IOException;
+
+public class PlainSaslServer implements SaslServer
+{
+ public static final String MECHANISM = "PLAIN";
+
+ private CallbackHandler _cbh;
+
+ private String _authorizationId;
+
+ private boolean _complete = false;
+
+ public PlainSaslServer(CallbackHandler cbh)
+ {
+ _cbh = cbh;
+ }
+
+ public String getMechanismName()
+ {
+ return MECHANISM;
+ }
+
+ public byte[] evaluateResponse(byte[] response) throws SaslException
+ {
+ try
+ {
+ int authzidNullPosition = findNullPosition(response, 0);
+ if (authzidNullPosition < 0)
+ {
+ throw new SaslException("Invalid PLAIN encoding, authzid null terminator not found");
+ }
+ int authcidNullPosition = findNullPosition(response, authzidNullPosition + 1);
+ if (authcidNullPosition < 0)
+ {
+ throw new SaslException("Invalid PLAIN encoding, authcid null terminator not found");
+ }
+
+ // we do not currently support authcid in any meaningful way
+ String authcid = new String(response, 0, authzidNullPosition, "utf8");
+ String authzid = new String(response, authzidNullPosition + 1, authcidNullPosition - 1, "utf8");
+
+ // we do not care about the prompt but it throws if null
+ NameCallback nameCb = new NameCallback("prompt", authzid);
+ // we do not care about the prompt but it throws if null
+ PasswordCallback passwordCb = new PasswordCallback("prompt", false);
+ // TODO: should not get pwd as a String but as a char array...
+ int passwordLen = response.length - authcidNullPosition - 1;
+ String pwd = new String(response, authcidNullPosition + 1, passwordLen, "utf8");
+ passwordCb.setPassword(pwd.toCharArray());
+ AuthorizeCallback authzCb = new AuthorizeCallback(authzid, authzid);
+ Callback[] callbacks = new Callback[]{nameCb, passwordCb, authzCb};
+ _cbh.handle(callbacks);
+ _complete = true;
+ if (authzCb.isAuthorized())
+ {
+ _authorizationId = authzCb.getAuthenticationID();
+ return null;
+ }
+ else
+ {
+ throw new SaslException("Authentication failed");
+ }
+ }
+ catch (IOException e)
+ {
+ throw new SaslException("Error processing data: " + e, e);
+ }
+ catch (UnsupportedCallbackException e)
+ {
+ throw new SaslException("Unable to obtain data from callback handler: " + e, e);
+ }
+ }
+
+ private int findNullPosition(byte[] response, int startPosition)
+ {
+ int position = startPosition;
+ while (position < response.length)
+ {
+ if (response[position] == (byte) 0)
+ {
+ return position;
+ }
+ position++;
+ }
+ return -1;
+ }
+
+ public boolean isComplete()
+ {
+ return _complete;
+ }
+
+ public String getAuthorizationID()
+ {
+ return _authorizationId;
+ }
+
+ public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException
+ {
+ throw new SaslException("Unsupported operation");
+ }
+
+ public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException
+ {
+ throw new SaslException("Unsupported operation");
+ }
+
+ public Object getNegotiatedProperty(String propName)
+ {
+ return null;
+ }
+
+ public void dispose() throws SaslException
+ {
+ _cbh = null;
+ }
+
+}
diff --git a/java/broker/src/org/apache/qpid/server/security/auth/plain/PlainSaslServerFactory.java b/java/broker/src/org/apache/qpid/server/security/auth/plain/PlainSaslServerFactory.java
new file mode 100644
index 0000000000..754ecbde78
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/security/auth/plain/PlainSaslServerFactory.java
@@ -0,0 +1,56 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.security.auth.plain;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.sasl.Sasl;
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+import javax.security.sasl.SaslServerFactory;
+import java.util.Map;
+
+public class PlainSaslServerFactory implements SaslServerFactory
+{
+ public SaslServer createSaslServer(String mechanism, String protocol, String serverName, Map props,
+ CallbackHandler cbh) throws SaslException
+ {
+ if (PlainSaslServer.MECHANISM.equals(mechanism))
+ {
+ return new PlainSaslServer(cbh);
+ }
+ else
+ {
+ return null;
+ }
+ }
+
+ public String[] getMechanismNames(Map props)
+ {
+ if (props.containsKey(Sasl.POLICY_NOPLAINTEXT) ||
+ props.containsKey(Sasl.POLICY_NODICTIONARY) ||
+ props.containsKey(Sasl.POLICY_NOACTIVE))
+ {
+ // returned array must be non null according to interface documentation
+ return new String[0];
+ }
+ else
+ {
+ return new String[]{PlainSaslServer.MECHANISM};
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/state/AMQState.java b/java/broker/src/org/apache/qpid/server/state/AMQState.java
new file mode 100644
index 0000000000..46d46eb4c0
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/state/AMQState.java
@@ -0,0 +1,33 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.state;
+
+/**
+ * States used in the AMQ protocol. Used by the finite state machine to determine
+ * valid responses.
+ */
+public enum AMQState
+{
+ CONNECTION_NOT_STARTED,
+ CONNECTION_NOT_AUTH,
+ CONNECTION_NOT_TUNED,
+ CONNECTION_NOT_OPENED,
+ CONNECTION_OPEN,
+ CONNECTION_CLOSING,
+ CONNECTION_CLOSED
+}
diff --git a/java/broker/src/org/apache/qpid/server/state/AMQStateManager.java b/java/broker/src/org/apache/qpid/server/state/AMQStateManager.java
new file mode 100644
index 0000000000..54c8bcd9d5
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/state/AMQStateManager.java
@@ -0,0 +1,219 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.state;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.*;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.handler.*;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQMethodListener;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.log4j.Logger;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+/**
+ * The state manager is responsible for managing the state of the protocol session.
+ * <p/>
+ * For each AMQProtocolHandler there is a separate state manager.
+ *
+ */
+public class AMQStateManager implements AMQMethodListener
+{
+ private static final Logger _logger = Logger.getLogger(AMQStateManager.class);
+
+ /**
+ * The current state
+ */
+ private AMQState _currentState;
+
+ /**
+ * Maps from an AMQState instance to a Map from Class to StateTransitionHandler.
+ * The class must be a subclass of AMQFrame.
+ */
+ private final Map<AMQState, Map<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>> _state2HandlersMap =
+ new HashMap<AMQState, Map<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>>();
+
+ private CopyOnWriteArraySet<StateListener> _stateListeners = new CopyOnWriteArraySet<StateListener>();
+
+ public AMQStateManager()
+ {
+ this(AMQState.CONNECTION_NOT_STARTED, true);
+ }
+
+ protected AMQStateManager(AMQState initial, boolean register)
+ {
+ _currentState = initial;
+ if (register)
+ {
+ registerListeners();
+ }
+ }
+
+ protected void registerListeners()
+ {
+ Map<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>> frame2handlerMap =
+ new HashMap<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>();
+
+ // we need to register a map for the null (i.e. all state) handlers otherwise you get
+ // a stack overflow in the handler searching code when you present it with a frame for which
+ // no handlers are registered
+ //
+ _state2HandlersMap.put(null, frame2handlerMap);
+
+ frame2handlerMap = new HashMap<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>();
+ frame2handlerMap.put(ConnectionStartOkBody.class, ConnectionStartOkMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_NOT_STARTED, frame2handlerMap);
+
+ frame2handlerMap = new HashMap<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>();
+ frame2handlerMap.put(ConnectionSecureOkBody.class, ConnectionSecureOkMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_NOT_AUTH, frame2handlerMap);
+
+ frame2handlerMap = new HashMap<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>();
+ frame2handlerMap.put(ConnectionTuneOkBody.class, ConnectionTuneOkMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_NOT_TUNED, frame2handlerMap);
+
+ frame2handlerMap = new HashMap<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>();
+ frame2handlerMap.put(ConnectionOpenBody.class, ConnectionOpenMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_NOT_OPENED, frame2handlerMap);
+
+ //
+ // ConnectionOpen handlers
+ //
+ frame2handlerMap = new HashMap<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>();
+ frame2handlerMap.put(ChannelOpenBody.class, ChannelOpenHandler.getInstance());
+ frame2handlerMap.put(ChannelCloseBody.class, ChannelCloseHandler.getInstance());
+ frame2handlerMap.put(ChannelCloseOkBody.class, ChannelCloseOkHandler.getInstance());
+ frame2handlerMap.put(ConnectionCloseBody.class, ConnectionCloseMethodHandler.getInstance());
+ frame2handlerMap.put(ExchangeDeclareBody.class, ExchangeDeclareHandler.getInstance());
+ frame2handlerMap.put(ExchangeDeleteBody.class, ExchangeDeleteHandler.getInstance());
+ frame2handlerMap.put(BasicAckBody.class, BasicAckMethodHandler.getInstance());
+ frame2handlerMap.put(BasicRecoverBody.class, BasicRecoverMethodHandler.getInstance());
+ frame2handlerMap.put(BasicConsumeBody.class, BasicConsumeMethodHandler.getInstance());
+ frame2handlerMap.put(BasicCancelBody.class, BasicCancelMethodHandler.getInstance());
+ frame2handlerMap.put(BasicPublishBody.class, BasicPublishMethodHandler.getInstance());
+ frame2handlerMap.put(BasicQosBody.class, BasicQosHandler.getInstance());
+ frame2handlerMap.put(QueueBindBody.class, QueueBindHandler.getInstance());
+ frame2handlerMap.put(QueueDeclareBody.class, QueueDeclareHandler.getInstance());
+ frame2handlerMap.put(QueueDeleteBody.class, QueueDeleteHandler.getInstance());
+ frame2handlerMap.put(ChannelFlowBody.class, ChannelFlowHandler.getInstance());
+ frame2handlerMap.put(TxSelectBody.class, TxSelectHandler.getInstance());
+ frame2handlerMap.put(TxCommitBody.class, TxCommitHandler.getInstance());
+ frame2handlerMap.put(TxRollbackBody.class, TxRollbackHandler.getInstance());
+
+ _state2HandlersMap.put(AMQState.CONNECTION_OPEN, frame2handlerMap);
+
+ frame2handlerMap = new HashMap<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>();
+ frame2handlerMap.put(ConnectionCloseOkBody.class, ConnectionCloseOkMethodHandler.getInstance());
+ _state2HandlersMap.put(AMQState.CONNECTION_CLOSING, frame2handlerMap);
+
+ }
+
+ public AMQState getCurrentState()
+ {
+ return _currentState;
+ }
+
+ public void changeState(AMQState newState) throws AMQException
+ {
+ _logger.debug("State changing to " + newState + " from old state " + _currentState);
+ final AMQState oldState = _currentState;
+ _currentState = newState;
+
+ for (StateListener l : _stateListeners)
+ {
+ l.stateChanged(oldState, newState);
+ }
+ }
+
+ public void error(AMQException e)
+ {
+ _logger.error("State manager received error notification: " + e, e);
+ for (StateListener l : _stateListeners)
+ {
+ l.error(e);
+ }
+ }
+
+ public <B extends AMQMethodBody> boolean methodReceived(AMQMethodEvent<B> evt,
+ AMQProtocolSession protocolSession,
+ QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry) throws AMQException
+ {
+ StateAwareMethodListener<B> handler = findStateTransitionHandler(_currentState, evt.getMethod());
+ if (handler != null)
+ {
+ handler.methodReceived(this, queueRegistry, exchangeRegistry, protocolSession, evt);
+ return true;
+ }
+ return false;
+ }
+
+ protected <B extends AMQMethodBody> StateAwareMethodListener<B> findStateTransitionHandler(AMQState currentState,
+ B frame)
+ throws IllegalStateTransitionException
+ {
+ if (_logger.isDebugEnabled())
+ {
+ _logger.debug("Looking for state transition handler for frame " + frame.getClass());
+ }
+ final Map<Class<? extends AMQMethodBody>, StateAwareMethodListener<? extends AMQMethodBody>>
+ classToHandlerMap = _state2HandlersMap.get(currentState);
+
+ if (classToHandlerMap == null)
+ {
+ // if no specialised per state handler is registered look for a
+ // handler registered for "all" states
+ return findStateTransitionHandler(null, frame);
+ }
+ final StateAwareMethodListener<B> handler = (StateAwareMethodListener<B>) classToHandlerMap.get(frame.getClass());
+ if (handler == null)
+ {
+ if (currentState == null)
+ {
+ _logger.debug("No state transition handler defined for receiving frame " + frame);
+ return null;
+ }
+ else
+ {
+ // if no specialised per state handler is registered look for a
+ // handler registered for "all" states
+ return findStateTransitionHandler(null, frame);
+ }
+ }
+ else
+ {
+ return handler;
+ }
+ }
+
+ public void addStateListener(StateListener listener)
+ {
+ _logger.debug("Adding state listener");
+ _stateListeners.add(listener);
+ }
+
+ public void removeStateListener(StateListener listener)
+ {
+ _stateListeners.remove(listener);
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/state/IllegalStateTransitionException.java b/java/broker/src/org/apache/qpid/server/state/IllegalStateTransitionException.java
new file mode 100644
index 0000000000..c77cb4f833
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/state/IllegalStateTransitionException.java
@@ -0,0 +1,45 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.state;
+
+import org.apache.qpid.AMQException;
+
+public class IllegalStateTransitionException extends AMQException
+{
+ private AMQState _originalState;
+
+ private Class _frame;
+
+ public IllegalStateTransitionException(AMQState originalState, Class frame)
+ {
+ super("No valid state transition defined for receiving frame " + frame +
+ " from state " + originalState);
+ _originalState = originalState;
+ _frame = frame;
+ }
+
+ public AMQState getOriginalState()
+ {
+ return _originalState;
+ }
+
+ public Class getFrameClass()
+ {
+ return _frame;
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/state/StateAwareMethodListener.java b/java/broker/src/org/apache/qpid/server/state/StateAwareMethodListener.java
new file mode 100644
index 0000000000..9776151c28
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/state/StateAwareMethodListener.java
@@ -0,0 +1,37 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.state;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.protocol.AMQMethodEvent;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.framing.AMQMethodBody;
+
+/**
+ * A frame listener that is informed of the protocol state when invoked and has
+ * the opportunity to update state.
+ *
+ */
+public interface StateAwareMethodListener <B extends AMQMethodBody>
+{
+ void methodReceived(AMQStateManager stateManager, QueueRegistry queueRegistry,
+ ExchangeRegistry exchangeRegistry, AMQProtocolSession protocolSession,
+ AMQMethodEvent<B> evt) throws AMQException;
+}
diff --git a/java/broker/src/org/apache/qpid/server/state/StateListener.java b/java/broker/src/org/apache/qpid/server/state/StateListener.java
new file mode 100644
index 0000000000..d31e786ef1
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/state/StateListener.java
@@ -0,0 +1,27 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.state;
+
+import org.apache.qpid.AMQException;
+
+public interface StateListener
+{
+ void stateChanged(AMQState oldState, AMQState newState) throws AMQException;
+
+ void error(Throwable t);
+}
diff --git a/java/broker/src/org/apache/qpid/server/store/MemoryMessageStore.java b/java/broker/src/org/apache/qpid/server/store/MemoryMessageStore.java
new file mode 100644
index 0000000000..d15a035259
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/store/MemoryMessageStore.java
@@ -0,0 +1,137 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.store;
+
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.AMQException;
+import org.apache.log4j.Logger;
+import org.apache.commons.configuration.Configuration;
+
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.List;
+
+/**
+ * A simple message store that stores the messages in a threadsafe structure in memory.
+ *
+ */
+public class MemoryMessageStore implements MessageStore
+{
+ private static final Logger _log = Logger.getLogger(MemoryMessageStore.class);
+
+ private static final int DEFAULT_HASHTABLE_CAPACITY = 50000;
+
+ private static final String HASHTABLE_CAPACITY_CONFIG = "hashtable-capacity";
+
+ protected ConcurrentMap<Long, AMQMessage> _messageMap;
+
+ private final AtomicLong _messageId = new AtomicLong(1);
+
+ public void configure(String base, Configuration config)
+ {
+ int hashtableCapacity = config.getInt(base + "." + HASHTABLE_CAPACITY_CONFIG, DEFAULT_HASHTABLE_CAPACITY);
+ _log.info("Using capacity " + hashtableCapacity + " for hash table");
+ _messageMap = new ConcurrentHashMap<Long, AMQMessage>(hashtableCapacity);
+ }
+
+ public void configure(QueueRegistry queueRegistry, String base, Configuration config) throws Exception
+ {
+ configure(base, config);
+ }
+
+ public void close() throws Exception
+ {
+ if(_messageMap != null)
+ {
+ _messageMap.clear();
+ _messageMap = null;
+ }
+ }
+
+ public void put(AMQMessage msg)
+ {
+ _messageMap.put(msg.getMessageId(), msg);
+ }
+
+ public void removeMessage(long messageId)
+ {
+ if (_log.isDebugEnabled())
+ {
+ _log.debug("Removing message with id " + messageId);
+ }
+ _messageMap.remove(messageId);
+ }
+
+ public void createQueue(AMQQueue queue) throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void removeQueue(String name) throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void enqueueMessage(String name, long messageId) throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void dequeueMessage(String name, long messageId) throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void beginTran() throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void commitTran() throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void abortTran() throws AMQException
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean inTran()
+ {
+ return false;
+ }
+
+ public List<AMQQueue> createQueues() throws AMQException
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getNewMessageId()
+ {
+ return _messageId.getAndIncrement();
+ }
+
+ public AMQMessage getMessage(long messageId)
+ {
+ return _messageMap.get(messageId);
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/store/MessageStore.java b/java/broker/src/org/apache/qpid/server/store/MessageStore.java
new file mode 100644
index 0000000000..7655b26221
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/store/MessageStore.java
@@ -0,0 +1,80 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.store;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.QueueRegistry;
+
+import java.util.List;
+
+public interface MessageStore
+{
+ /**
+ * Called after instantiation in order to configure the message store. A particular implementation can define
+ * whatever parameters it wants.
+ * @param queueRegistry the registry of queues to be used by this store
+ * @param base the base element identifier from which all configuration items are relative. For example, if the base
+ * element is "store", the all elements used by concrete classes will be "store.foo" etc.
+ * @param config the apache commons configuration object
+ */
+ void configure(QueueRegistry queueRegistry, String base, Configuration config) throws Exception;
+
+ /**
+ * Called to close and cleanup any resources used by the message store.
+ * @throws Exception
+ */
+ void close() throws Exception;
+
+ void put(AMQMessage msg) throws AMQException;
+
+ void removeMessage(long messageId) throws AMQException;
+
+ void createQueue(AMQQueue queue) throws AMQException;
+
+ void removeQueue(String name) throws AMQException;
+
+ void enqueueMessage(String name, long messageId) throws AMQException;
+
+ void dequeueMessage(String name, long messageId) throws AMQException;
+
+ void beginTran() throws AMQException;
+
+ void commitTran() throws AMQException;
+
+ void abortTran() throws AMQException;
+
+ boolean inTran();
+
+ /**
+ * Recreate all queues that were persisted, including re-enqueuing of existing messages
+ * @return
+ * @throws AMQException
+ */
+ List<AMQQueue> createQueues() throws AMQException;
+
+ /**
+ * Return a valid, currently unused message id.
+ * @return a message id
+ */
+ long getNewMessageId();
+}
+
+
diff --git a/java/broker/src/org/apache/qpid/server/transport/ConnectorConfiguration.java b/java/broker/src/org/apache/qpid/server/transport/ConnectorConfiguration.java
new file mode 100644
index 0000000000..e9e9c8aca4
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/transport/ConnectorConfiguration.java
@@ -0,0 +1,93 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.transport;
+
+import org.apache.qpid.configuration.Configured;
+import org.apache.mina.common.IoAcceptor;
+
+public class ConnectorConfiguration
+{
+ public static final String DEFAULT_PORT = "5672";
+
+ public static final String SSL_PORT = "8672";
+
+ @Configured(path = "connector.processors",
+ defaultValue = "4")
+ public int processors;
+
+ @Configured(path = "connector.port",
+ defaultValue = DEFAULT_PORT)
+ public int port;
+
+ @Configured(path = "connector.bind",
+ defaultValue = "wildcard")
+ public String bindAddress;
+
+ @Configured(path = "connector.sslport",
+ defaultValue = SSL_PORT)
+ public int sslPort;
+
+ @Configured(path = "connector.socketReceiveBuffer",
+ defaultValue = "32767")
+ public int socketReceiveBufferSize;
+
+ @Configured(path = "connector.socketWriteBuffer",
+ defaultValue = "32767")
+ public int socketWriteBuferSize;
+
+ @Configured(path = "connector.tcpNoDelay",
+ defaultValue = "true")
+ public boolean tcpNoDelay;
+
+ @Configured(path = "advanced.filterchain[@enableExecutorPool]",
+ defaultValue = "false")
+ public boolean enableExecutorPool;
+
+ @Configured(path = "advanced.enablePooledAllocator",
+ defaultValue = "false")
+ public boolean enablePooledAllocator;
+
+ @Configured(path = "advanced.enableDirectBuffers",
+ defaultValue = "false")
+ public boolean enableDirectBuffers;
+
+ @Configured(path = "connector.ssl",
+ defaultValue = "false")
+ public boolean enableSSL;
+
+ @Configured(path = "connector.nonssl",
+ defaultValue = "true")
+ public boolean enableNonSSL;
+
+ @Configured(path = "advanced.useBlockingIo",
+ defaultValue = "false")
+ public boolean useBlockingIo;
+
+ public IoAcceptor createAcceptor()
+ {
+ if(useBlockingIo)
+ {
+ System.out.println("Using blocking io");
+ return new org.apache.qpid.bio.SocketAcceptor();
+ }
+ else
+ {
+ return new org.apache.mina.transport.socket.nio.SocketAcceptor(processors);
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/transport/ThreadPoolFilter.java b/java/broker/src/org/apache/qpid/server/transport/ThreadPoolFilter.java
new file mode 100644
index 0000000000..e3d87f808c
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/transport/ThreadPoolFilter.java
@@ -0,0 +1,692 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.transport;
+
+import org.apache.mina.common.*;
+import org.apache.mina.util.*;
+import org.apache.mina.util.Queue;
+import org.apache.mina.util.Stack;
+
+import java.util.*;
+
+/**
+ * A Thread-pooling filter. This filter forwards {@link IoHandler} events
+ * to its thread pool.
+ * <p/>
+ * This is an implementation of
+ * <a href="http://deuce.doc.wustl.edu/doc/pspdfs/lf.pdf">Leader/Followers
+ * thread pool</a> by Douglas C. Schmidt et al.
+ */
+public class ThreadPoolFilter extends IoFilterAdapter
+{
+ /**
+ * Default maximum size of thread pool (2G).
+ */
+ public static final int DEFAULT_MAXIMUM_POOL_SIZE = Integer.MAX_VALUE;
+
+ /**
+ * Default keep-alive time of thread pool (1 min).
+ */
+ public static final int DEFAULT_KEEP_ALIVE_TIME = 60 * 1000;
+
+ /**
+ * A queue which contains {@link Integer}s which represents reusable
+ * thread IDs. {@link Worker} first checks this queue and then
+ * uses {@link #threadId} when no reusable thread ID is available.
+ */
+ private static final Queue threadIdReuseQueue = new Queue();
+ private static int threadId = 0;
+
+ private static int acquireThreadId()
+ {
+ synchronized (threadIdReuseQueue)
+ {
+ Integer id = (Integer) threadIdReuseQueue.pop();
+ if (id == null)
+ {
+ return ++ threadId;
+ }
+ else
+ {
+ return id.intValue();
+ }
+ }
+ }
+
+ private static void releaseThreadId(int id)
+ {
+ synchronized (threadIdReuseQueue)
+ {
+ threadIdReuseQueue.push(new Integer(id));
+ }
+ }
+
+ private final String threadNamePrefix;
+ private final Map buffers = new IdentityHashMap();
+ private final BlockingQueue unfetchedSessionBuffers = new BlockingQueue();
+ private final Set allSessionBuffers = new IdentityHashSet();
+
+ private Worker leader;
+ private final Stack followers = new Stack();
+ private final Set allWorkers = new IdentityHashSet();
+
+ private int maximumPoolSize = DEFAULT_MAXIMUM_POOL_SIZE;
+ private int keepAliveTime = DEFAULT_KEEP_ALIVE_TIME;
+
+ private boolean shuttingDown;
+
+ private int poolSize;
+ private final Object poolSizeLock = new Object();
+
+ /**
+ * Creates a new instance of this filter with default thread pool settings.
+ */
+ public ThreadPoolFilter()
+ {
+ this("IoThreadPool");
+ }
+
+ /**
+ * Creates a new instance of this filter with the specified thread name prefix
+ * and other default settings.
+ *
+ * @param threadNamePrefix the prefix of the thread names this pool will create.
+ */
+ public ThreadPoolFilter(String threadNamePrefix)
+ {
+ if (threadNamePrefix == null)
+ {
+ throw new NullPointerException("threadNamePrefix");
+ }
+ threadNamePrefix = threadNamePrefix.trim();
+ if (threadNamePrefix.length() == 0)
+ {
+ throw new IllegalArgumentException("threadNamePrefix is empty.");
+ }
+ this.threadNamePrefix = threadNamePrefix;
+ }
+
+ public String getThreadNamePrefix()
+ {
+ return threadNamePrefix;
+ }
+
+ public int getPoolSize()
+ {
+ synchronized (poolSizeLock)
+ {
+ return poolSize;
+ }
+ }
+
+ public int getMaximumPoolSize()
+ {
+ return maximumPoolSize;
+ }
+
+ public int getKeepAliveTime()
+ {
+ return keepAliveTime;
+ }
+
+ public void setMaximumPoolSize(int maximumPoolSize)
+ {
+ if (maximumPoolSize <= 0)
+ {
+ throw new IllegalArgumentException();
+ }
+ this.maximumPoolSize = maximumPoolSize;
+ }
+
+ public void setKeepAliveTime(int keepAliveTime)
+ {
+ this.keepAliveTime = keepAliveTime;
+ }
+
+ public void init()
+ {
+ shuttingDown = false;
+ leader = new Worker();
+ leader.start();
+ leader.lead();
+ }
+
+ public void destroy()
+ {
+ shuttingDown = true;
+ int expectedPoolSize = 0;
+ while (getPoolSize() != expectedPoolSize)
+ {
+ List allWorkers;
+ synchronized (poolSizeLock)
+ {
+ allWorkers = new ArrayList(this.allWorkers);
+ }
+
+ // You may not interrupt the current thread.
+ if (allWorkers.remove(Thread.currentThread()))
+ {
+ expectedPoolSize = 1;
+ }
+
+ for (Iterator i = allWorkers.iterator(); i.hasNext();)
+ {
+ Worker worker = (Worker) i.next();
+ while (worker.isAlive())
+ {
+ worker.interrupt();
+ try
+ {
+ // This timeout will help us from
+ // infinite lock-up and interrupt workers again.
+ worker.join(100);
+ }
+ catch (InterruptedException e)
+ {
+ }
+ }
+ }
+ }
+
+ this.allSessionBuffers.clear();
+ this.unfetchedSessionBuffers.clear();
+ this.buffers.clear();
+ this.followers.clear();
+ this.leader = null;
+ }
+
+ private void increasePoolSize(Worker worker)
+ {
+ synchronized (poolSizeLock)
+ {
+ poolSize++;
+ allWorkers.add(worker);
+ }
+ }
+
+ private void decreasePoolSize(Worker worker)
+ {
+ synchronized (poolSizeLock)
+ {
+ poolSize--;
+ allWorkers.remove(worker);
+ }
+ }
+
+ private void fireEvent(NextFilter nextFilter, IoSession session,
+ EventType type, Object data)
+ {
+ final BlockingQueue unfetchedSessionBuffers = this.unfetchedSessionBuffers;
+ final Set allSessionBuffers = this.allSessionBuffers;
+ final Event event = new Event(type, nextFilter, data);
+
+ synchronized (unfetchedSessionBuffers)
+ {
+ final SessionBuffer buf = getSessionBuffer(session);
+ final Queue eventQueue = buf.eventQueue;
+
+ synchronized (buf)
+ {
+ eventQueue.push(event);
+ }
+
+ if (!allSessionBuffers.contains(buf))
+ {
+ allSessionBuffers.add(buf);
+ unfetchedSessionBuffers.push(buf);
+ }
+ }
+ }
+
+ /**
+ * Implement this method to fetch (or pop) a {@link SessionBuffer} from
+ * the given <tt>unfetchedSessionBuffers</tt>. The default implementation
+ * simply pops the buffer from it. You could prioritize the fetch order.
+ *
+ * @return A non-null {@link SessionBuffer}
+ */
+ protected SessionBuffer fetchSessionBuffer(Queue unfetchedSessionBuffers)
+ {
+ return (SessionBuffer) unfetchedSessionBuffers.pop();
+ }
+
+ private SessionBuffer getSessionBuffer(IoSession session)
+ {
+ final Map buffers = this.buffers;
+ SessionBuffer buf = (SessionBuffer) buffers.get(session);
+ if (buf == null)
+ {
+ synchronized (buffers)
+ {
+ buf = (SessionBuffer) buffers.get(session);
+ if (buf == null)
+ {
+ buf = new SessionBuffer(session);
+ buffers.put(session, buf);
+ }
+ }
+ }
+ return buf;
+ }
+
+ private void removeSessionBuffer(SessionBuffer buf)
+ {
+ final Map buffers = this.buffers;
+ final IoSession session = buf.session;
+ synchronized (buffers)
+ {
+ buffers.remove(session);
+ }
+ }
+
+ protected static class SessionBuffer
+ {
+ private final IoSession session;
+
+ private final Queue eventQueue = new Queue();
+
+ private SessionBuffer(IoSession session)
+ {
+ this.session = session;
+ }
+
+ public IoSession getSession()
+ {
+ return session;
+ }
+
+ public Queue getEventQueue()
+ {
+ return eventQueue;
+ }
+ }
+
+ private class Worker extends Thread
+ {
+ private final int id;
+ private final Object promotionLock = new Object();
+ private boolean dead;
+
+ private Worker()
+ {
+ int id = acquireThreadId();
+ this.id = id;
+ this.setName(threadNamePrefix + '-' + id);
+ increasePoolSize(this);
+ }
+
+ public boolean lead()
+ {
+ final Object promotionLock = this.promotionLock;
+ synchronized (promotionLock)
+ {
+ if (dead)
+ {
+ return false;
+ }
+
+ leader = this;
+ promotionLock.notify();
+ }
+
+ return true;
+ }
+
+ public void run()
+ {
+ for (; ;)
+ {
+ if (!waitForPromotion())
+ {
+ break;
+ }
+
+ SessionBuffer buf = fetchBuffer();
+ giveUpLead();
+ if (buf == null)
+ {
+ break;
+ }
+
+ processEvents(buf);
+ follow();
+ releaseBuffer(buf);
+ }
+
+ decreasePoolSize(this);
+ releaseThreadId(id);
+ }
+
+ private SessionBuffer fetchBuffer()
+ {
+ BlockingQueue unfetchedSessionBuffers = ThreadPoolFilter.this.unfetchedSessionBuffers;
+ synchronized (unfetchedSessionBuffers)
+ {
+ while (!shuttingDown)
+ {
+ try
+ {
+ unfetchedSessionBuffers.waitForNewItem();
+ }
+ catch (InterruptedException e)
+ {
+ continue;
+ }
+
+ return ThreadPoolFilter.this.fetchSessionBuffer(unfetchedSessionBuffers);
+ }
+ }
+
+ return null;
+ }
+
+ private void processEvents(SessionBuffer buf)
+ {
+ final IoSession session = buf.session;
+ final Queue eventQueue = buf.eventQueue;
+ for (; ;)
+ {
+ Event event;
+ synchronized (buf)
+ {
+ event = (Event) eventQueue.pop();
+ if (event == null)
+ {
+ break;
+ }
+ }
+ processEvent(event.getNextFilter(), session,
+ event.getType(), event.getData());
+ }
+ }
+
+ private void follow()
+ {
+ final Object promotionLock = this.promotionLock;
+ final Stack followers = ThreadPoolFilter.this.followers;
+ synchronized (promotionLock)
+ {
+ if (this != leader)
+ {
+ synchronized (followers)
+ {
+ followers.push(this);
+ }
+ }
+ }
+ }
+
+ private void releaseBuffer(SessionBuffer buf)
+ {
+ final BlockingQueue unfetchedSessionBuffers = ThreadPoolFilter.this.unfetchedSessionBuffers;
+ final Set allSessionBuffers = ThreadPoolFilter.this.allSessionBuffers;
+ final Queue eventQueue = buf.eventQueue;
+
+ synchronized (unfetchedSessionBuffers)
+ {
+ if (eventQueue.isEmpty())
+ {
+ allSessionBuffers.remove(buf);
+ removeSessionBuffer(buf);
+ }
+ else
+ {
+ unfetchedSessionBuffers.push(buf);
+ }
+ }
+ }
+
+ private boolean waitForPromotion()
+ {
+ final Object promotionLock = this.promotionLock;
+
+ long startTime = System.currentTimeMillis();
+ long currentTime = System.currentTimeMillis();
+
+ synchronized (promotionLock)
+ {
+ while (this != leader && !shuttingDown)
+ {
+ // Calculate remaining keep-alive time
+ int keepAliveTime = getKeepAliveTime();
+ if (keepAliveTime > 0)
+ {
+ keepAliveTime -= (currentTime - startTime);
+ }
+ else
+ {
+ keepAliveTime = Integer.MAX_VALUE;
+ }
+
+ // Break the loop if there's no remaining keep-alive time.
+ if (keepAliveTime <= 0)
+ {
+ break;
+ }
+
+ // Wait for promotion
+ try
+ {
+ promotionLock.wait(keepAliveTime);
+ }
+ catch (InterruptedException e)
+ {
+ }
+
+ // Update currentTime for the next iteration
+ currentTime = System.currentTimeMillis();
+ }
+
+ boolean timeToLead = this == leader && !shuttingDown;
+
+ if (!timeToLead)
+ {
+ // time to die
+ synchronized (followers)
+ {
+ followers.remove(this);
+ }
+
+ // Mark as dead explicitly when we've got promotionLock.
+ dead = true;
+ }
+
+ return timeToLead;
+ }
+ }
+
+ private void giveUpLead()
+ {
+ final Stack followers = ThreadPoolFilter.this.followers;
+ Worker worker;
+ do
+ {
+ synchronized (followers)
+ {
+ worker = (Worker) followers.pop();
+ }
+
+ if (worker == null)
+ {
+ // Increase the number of threads if we
+ // are not shutting down and we can increase the number.
+ if (!shuttingDown
+ && getPoolSize() < getMaximumPoolSize())
+ {
+ worker = new Worker();
+ worker.lead();
+ worker.start();
+ }
+
+ // This loop should end because:
+ // 1) lead() is called already,
+ // 2) or it is shutting down and there's no more threads left.
+ break;
+ }
+ }
+ while (!worker.lead());
+ }
+ }
+
+ protected static class EventType
+ {
+ public static final EventType OPENED = new EventType("OPENED");
+
+ public static final EventType CLOSED = new EventType("CLOSED");
+
+ public static final EventType READ = new EventType("READ");
+
+ public static final EventType WRITTEN = new EventType("WRITTEN");
+
+ public static final EventType RECEIVED = new EventType("RECEIVED");
+
+ public static final EventType SENT = new EventType("SENT");
+
+ public static final EventType IDLE = new EventType("IDLE");
+
+ public static final EventType EXCEPTION = new EventType("EXCEPTION");
+
+ private final String value;
+
+ private EventType(String value)
+ {
+ this.value = value;
+ }
+
+ public String toString()
+ {
+ return value;
+ }
+ }
+
+ protected static class Event
+ {
+ private final EventType type;
+ private final NextFilter nextFilter;
+ private final Object data;
+
+ public Event(EventType type, NextFilter nextFilter, Object data)
+ {
+ this.type = type;
+ this.nextFilter = nextFilter;
+ this.data = data;
+ }
+
+ public Object getData()
+ {
+ return data;
+ }
+
+
+ public NextFilter getNextFilter()
+ {
+ return nextFilter;
+ }
+
+
+ public EventType getType()
+ {
+ return type;
+ }
+ }
+
+ public void sessionCreated(NextFilter nextFilter, IoSession session)
+ {
+ nextFilter.sessionCreated(session);
+ }
+
+ public void sessionOpened(NextFilter nextFilter,
+ IoSession session)
+ {
+ fireEvent(nextFilter, session, EventType.OPENED, null);
+ }
+
+ public void sessionClosed(NextFilter nextFilter,
+ IoSession session)
+ {
+ fireEvent(nextFilter, session, EventType.CLOSED, null);
+ }
+
+ public void sessionIdle(NextFilter nextFilter,
+ IoSession session, IdleStatus status)
+ {
+ fireEvent(nextFilter, session, EventType.IDLE, status);
+ }
+
+ public void exceptionCaught(NextFilter nextFilter,
+ IoSession session, Throwable cause)
+ {
+ fireEvent(nextFilter, session, EventType.EXCEPTION, cause);
+ }
+
+ public void messageReceived(NextFilter nextFilter,
+ IoSession session, Object message)
+ {
+ ByteBufferUtil.acquireIfPossible(message);
+ fireEvent(nextFilter, session, EventType.RECEIVED, message);
+ }
+
+ public void messageSent(NextFilter nextFilter,
+ IoSession session, Object message)
+ {
+ ByteBufferUtil.acquireIfPossible(message);
+ fireEvent(nextFilter, session, EventType.SENT, message);
+ }
+
+ protected void processEvent(NextFilter nextFilter,
+ IoSession session, EventType type,
+ Object data)
+ {
+ if (type == EventType.RECEIVED)
+ {
+ nextFilter.messageReceived(session, data);
+ ByteBufferUtil.releaseIfPossible(data);
+ }
+ else if (type == EventType.SENT)
+ {
+ nextFilter.messageSent(session, data);
+ ByteBufferUtil.releaseIfPossible(data);
+ }
+ else if (type == EventType.EXCEPTION)
+ {
+ nextFilter.exceptionCaught(session, (Throwable) data);
+ }
+ else if (type == EventType.IDLE)
+ {
+ nextFilter.sessionIdle(session, (IdleStatus) data);
+ }
+ else if (type == EventType.OPENED)
+ {
+ nextFilter.sessionOpened(session);
+ }
+ else if (type == EventType.CLOSED)
+ {
+ nextFilter.sessionClosed(session);
+ }
+ }
+
+ public void filterWrite(NextFilter nextFilter, IoSession session, WriteRequest writeRequest)
+ {
+ nextFilter.filterWrite(session, writeRequest);
+ }
+
+ public void filterClose(NextFilter nextFilter, IoSession session) throws Exception
+ {
+ nextFilter.filterClose(session);
+ }
+} \ No newline at end of file
diff --git a/java/broker/src/org/apache/qpid/server/txn/TxnBuffer.java b/java/broker/src/org/apache/qpid/server/txn/TxnBuffer.java
new file mode 100644
index 0000000000..9a46afafe5
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/txn/TxnBuffer.java
@@ -0,0 +1,75 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.txn;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.store.MessageStore;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TxnBuffer
+{
+ private final MessageStore _store;
+ private final List<TxnOp> _ops = new ArrayList<TxnOp>();
+
+ public TxnBuffer(MessageStore store)
+ {
+ _store = store;
+ }
+
+ public void commit() throws AMQException
+ {
+ _store.beginTran();
+ boolean failed = true;
+ try
+ {
+ for(TxnOp op : _ops)
+ {
+ op.commit();
+ }
+ _ops.clear();
+ failed = false;
+ }
+ finally
+ {
+ if(failed)
+ {
+ _store.abortTran();
+ }
+ else
+ {
+ _store.commitTran();
+ }
+ }
+ }
+
+ public void rollback() throws AMQException
+ {
+ for(TxnOp op : _ops)
+ {
+ op.rollback();
+ }
+ _ops.clear();
+ }
+
+ public void enlist(TxnOp op)
+ {
+ _ops.add(op);
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/txn/TxnOp.java b/java/broker/src/org/apache/qpid/server/txn/TxnOp.java
new file mode 100644
index 0000000000..402e75c64b
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/txn/TxnOp.java
@@ -0,0 +1,26 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.txn;
+
+import org.apache.qpid.AMQException;
+
+public interface TxnOp
+{
+ public void commit() throws AMQException;
+ public void rollback();
+}
diff --git a/java/broker/src/org/apache/qpid/server/util/CircularBuffer.java b/java/broker/src/org/apache/qpid/server/util/CircularBuffer.java
new file mode 100644
index 0000000000..d2c10cb4d1
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/util/CircularBuffer.java
@@ -0,0 +1,123 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+import java.util.Iterator;
+
+public class CircularBuffer implements Iterable
+{
+ private final Object[] _log;
+ private int _size;
+ private int _index;
+
+ public CircularBuffer(int size)
+ {
+ _log = new Object[size];
+ }
+
+ public void add(Object o)
+ {
+ _log[_index++] = o;
+ _size = Math.min(_size+1, _log.length);
+ if(_index >= _log.length)
+ {
+ _index = 0;
+ }
+ }
+
+ public Object get(int i)
+ {
+ if(i >= _log.length)
+ {
+ throw new ArrayIndexOutOfBoundsException(i);
+ }
+ return _log[index(i)];
+ }
+
+ public int size() {
+ return _size;
+ }
+
+ public Iterator iterator()
+ {
+ return new Iterator()
+ {
+ private int i = 0;
+
+ public boolean hasNext()
+ {
+ return i < _size;
+ }
+
+ public Object next()
+ {
+ return get(i++);
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ public String toString()
+ {
+ StringBuilder s = new StringBuilder();
+ boolean first = true;
+ for(Object o : this)
+ {
+ if(!first)
+ {
+ s.append(", ");
+ }
+ else
+ {
+ first = false;
+ }
+ s.append(o);
+ }
+ return s.toString();
+ }
+
+ public void dump()
+ {
+ for(Object o : this)
+ {
+ System.out.println(o);
+ }
+ }
+
+ int index(int i)
+ {
+ return _size == _log.length ? (_index + i) % _log.length : i;
+ }
+
+ public static void main(String[] artgv)
+ {
+ String[] items = new String[]{
+ "A","B","C","D","E","F","G","H","I","J","K"
+ };
+ CircularBuffer buffer = new CircularBuffer(5);
+ for(String s : items)
+ {
+ buffer.add(s);
+ System.out.println(buffer);
+ }
+ }
+}
diff --git a/java/broker/src/org/apache/qpid/server/util/LoggingProxy.java b/java/broker/src/org/apache/qpid/server/util/LoggingProxy.java
new file mode 100644
index 0000000000..03c4896422
--- /dev/null
+++ b/java/broker/src/org/apache/qpid/server/util/LoggingProxy.java
@@ -0,0 +1,102 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+
+/**
+ * Dynamic proxy that records invocations in a fixed size circular buffer,
+ * dumping details on hitting an exception.
+ * <p>
+ * Useful in debugging.
+ * <p>
+ */
+public class LoggingProxy implements InvocationHandler
+{
+ private final Object _target;
+ private final CircularBuffer _log;
+
+ public LoggingProxy(Object target, int size)
+ {
+ _target = target;
+ _log = new CircularBuffer(size);
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
+ {
+ try
+ {
+ entered(method, args);
+ Object result = method.invoke(_target, args);
+ returned(method, result);
+ return result;
+ }
+ catch(InvocationTargetException e)
+ {
+ dump();
+ throw e.getTargetException();
+ }
+ }
+
+ void dump()
+ {
+ _log.dump();
+ }
+
+ CircularBuffer getBuffer()
+ {
+ return _log;
+ }
+
+ private synchronized void entered(Method method, Object[] args)
+ {
+ if (args == null)
+ {
+ _log.add(Thread.currentThread() + ": " + method.getName() + "() entered");
+ }
+ else
+ {
+ _log.add(Thread.currentThread() + ": " + method.getName() + "(" + Arrays.toString(args) + ") entered");
+ }
+ }
+
+ private synchronized void returned(Method method, Object result)
+ {
+ if (method.getReturnType() == Void.TYPE)
+ {
+ _log.add(Thread.currentThread() + ": " + method.getName() + "() returned");
+ }
+ else
+ {
+ _log.add(Thread.currentThread() + ": " + method.getName() + "() returned " + result);
+ }
+ }
+
+ public Object getProxy(Class... c)
+ {
+ return Proxy.newProxyInstance(_target.getClass().getClassLoader(), c, this);
+ }
+
+ public int getBufferSize() {
+ return _log.size();
+ }
+}
diff --git a/java/broker/test/build-module.xml b/java/broker/test/build-module.xml
new file mode 100644
index 0000000000..3128b77249
--- /dev/null
+++ b/java/broker/test/build-module.xml
@@ -0,0 +1,32 @@
+<!--
+ -
+ - Copyright (c) 2006 The Apache Software Foundation
+ -
+ - Licensed under the Apache License, Version 2.0 (the "License");
+ - you may not use this file except in compliance with the License.
+ - You may obtain a copy of the License at
+ -
+ - http://www.apache.org/licenses/LICENSE-2.0
+ -
+ - Unless required by applicable law or agreed to in writing, software
+ - distributed under the License is distributed on an "AS IS" BASIS,
+ - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ - See the License for the specific language governing permissions and
+ - limitations under the License.
+ -
+ -->
+<project name="Broker Tests" default="build">
+ <property name="module.depends" value="broker common"/>
+ <property name="module.dist" value="false"/>
+
+ <import file="../../module.xml"/>
+
+ <target name="test" depends="build">
+ <junit fork="yes" showoutput="true" haltonfailure="yes">
+ <test name="org.apache.qpid.server.UnitTests"/>
+ <formatter type="plain"/>
+ <classpath refid="module.class.path"/>
+ </junit>
+ </target>
+
+</project>
diff --git a/java/broker/test/lib/README b/java/broker/test/lib/README
new file mode 100644
index 0000000000..a75673935e
--- /dev/null
+++ b/java/broker/test/lib/README
@@ -0,0 +1 @@
+Junit copied here momentarily.
diff --git a/java/broker/test/lib/junit/junit-4.0.jar b/java/broker/test/lib/junit/junit-4.0.jar
new file mode 100644
index 0000000000..b20406924a
--- /dev/null
+++ b/java/broker/test/lib/junit/junit-4.0.jar
Binary files differ
diff --git a/java/broker/test/lib/junit/junit.jar b/java/broker/test/lib/junit/junit.jar
new file mode 100644
index 0000000000..674d71e89e
--- /dev/null
+++ b/java/broker/test/lib/junit/junit.jar
Binary files differ
diff --git a/java/broker/test/src/org/apache/qpid/server/UnitTests.java b/java/broker/test/src/org/apache/qpid/server/UnitTests.java
new file mode 100644
index 0000000000..377b1fb64e
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/UnitTests.java
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server;
+
+import junit.framework.JUnit4TestAdapter;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ org.apache.qpid.server.configuration.UnitTests.class,
+ org.apache.qpid.server.exchange.UnitTests.class,
+ org.apache.qpid.server.protocol.UnitTests.class,
+ org.apache.qpid.server.queue.UnitTests.class,
+ org.apache.qpid.server.store.UnitTests.class,
+ org.apache.qpid.server.util.UnitTests.class
+ })
+public class UnitTests
+{
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(UnitTests.class);
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/configuration/TestPropertyUtils.java b/java/broker/test/src/org/apache/qpid/server/configuration/TestPropertyUtils.java
new file mode 100644
index 0000000000..bd78d1c786
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/configuration/TestPropertyUtils.java
@@ -0,0 +1,50 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.configuration;
+
+import junit.framework.JUnit4TestAdapter;
+import org.apache.qpid.configuration.PropertyException;
+import org.apache.qpid.configuration.PropertyUtils;
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+// TODO: This belongs in the "common" module.
+public class TestPropertyUtils
+{
+ @Test
+ public void testSimpleExpansion() throws PropertyException
+ {
+ System.setProperty("banana", "fruity");
+ String expandedProperty = PropertyUtils.replaceProperties("${banana}");
+ assertEquals(expandedProperty, "fruity");
+ }
+
+ @Test
+ public void testDualExpansion() throws PropertyException
+ {
+ System.setProperty("banana", "fruity");
+ System.setProperty("concrete", "horrible");
+ String expandedProperty = PropertyUtils.replaceProperties("${banana}xyz${concrete}");
+ assertEquals(expandedProperty, "fruityxyzhorrible");
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(TestPropertyUtils.class);
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/configuration/UnitTests.java b/java/broker/test/src/org/apache/qpid/server/configuration/UnitTests.java
new file mode 100644
index 0000000000..4c70d7c4da
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/configuration/UnitTests.java
@@ -0,0 +1,32 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.configuration;
+
+import junit.framework.JUnit4TestAdapter;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({TestPropertyUtils.class})
+public class UnitTests
+{
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(UnitTests.class);
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/exchange/AbstractHeadersExchangeTest.java b/java/broker/test/src/org/apache/qpid/server/exchange/AbstractHeadersExchangeTest.java
new file mode 100644
index 0000000000..ac04c51e46
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/exchange/AbstractHeadersExchangeTest.java
@@ -0,0 +1,212 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.NoConsumersException;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.SkeletonMessageStore;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.framing.BasicPublishBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.framing.BasicContentHeaderProperties;
+import org.apache.qpid.AMQException;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.HashSet;
+
+public class AbstractHeadersExchangeTest
+{
+ private final HeadersExchange exchange = new HeadersExchange();
+ protected final Set<TestQueue> queues = new HashSet<TestQueue>();
+ private int count;
+
+ protected TestQueue bindDefault(String... bindings) throws AMQException
+ {
+ return bind("Queue" + (++count), bindings);
+ }
+
+ protected TestQueue bind(String queueName, String... bindings) throws AMQException
+ {
+ return bind(queueName, getHeaders(bindings));
+ }
+
+ protected TestQueue bind(String queue, FieldTable bindings) throws AMQException
+ {
+ return bind(new TestQueue(queue), bindings);
+ }
+
+ protected TestQueue bind(TestQueue queue, String... bindings) throws AMQException
+ {
+ return bind(queue, getHeaders(bindings));
+ }
+
+ protected TestQueue bind(TestQueue queue, FieldTable bindings) throws AMQException
+ {
+ queues.add(queue);
+ exchange.registerQueue(null, queue, bindings);
+ return queue;
+ }
+
+
+ protected void route(Message m) throws AMQException
+ {
+ m.route(exchange);
+ }
+
+ protected void routeAndTest(Message m, TestQueue... expected) throws AMQException
+ {
+ routeAndTest(m, Arrays.asList(expected));
+ }
+
+ protected void routeAndTest(Message m, List<TestQueue> expected) throws AMQException
+ {
+ route(m);
+ for (TestQueue q : queues)
+ {
+ if (expected.contains(q))
+ {
+ assertTrue("Expected " + m + " to be delivered to " + q, m.isInQueue(q));
+ //assert m.isInQueue(q) : "Expected " + m + " to be delivered to " + q;
+ }
+ else
+ {
+ assertFalse("Did not expect " + m + " to be delivered to " + q, m.isInQueue(q));
+ //assert !m.isInQueue(q) : "Did not expect " + m + " to be delivered to " + q;
+ }
+ }
+ }
+
+ static FieldTable getHeaders(String... entries)
+ {
+ FieldTable headers = new FieldTable();
+ for (String s : entries)
+ {
+ String[] parts = s.split("=", 2);
+ headers.put(parts[0], parts.length > 1 ? parts[1] : "");
+ }
+ return headers;
+ }
+
+ static BasicPublishBody getPublishRequest(String id)
+ {
+ BasicPublishBody request = new BasicPublishBody();
+ request.routingKey = id;
+ return request;
+ }
+
+ static ContentHeaderBody getContentHeader(FieldTable headers)
+ {
+ ContentHeaderBody header = new ContentHeaderBody();
+ header.properties = getProperties(headers);
+ return header;
+ }
+
+ static BasicContentHeaderProperties getProperties(FieldTable headers)
+ {
+ BasicContentHeaderProperties properties = new BasicContentHeaderProperties();
+ properties.setHeaders(headers);
+ return properties;
+ }
+
+ static class TestQueue extends AMQQueue
+ {
+ final List<HeadersExchangeTest.Message> messages = new ArrayList<HeadersExchangeTest.Message>();
+
+ public TestQueue(String name) throws AMQException
+ {
+ super(name, false, "test", true, ApplicationRegistry.getInstance().getQueueRegistry());
+ }
+
+ public void deliver(AMQMessage msg) throws AMQException
+ {
+ messages.add(new HeadersExchangeTest.Message(msg));
+ }
+ }
+
+ /**
+ * Just add some extra utility methods to AMQMessage to aid testing.
+ */
+ static class Message extends AMQMessage
+ {
+ private static MessageStore _messageStore = new SkeletonMessageStore();
+
+ Message(String id, String... headers) throws AMQException
+ {
+ this(id, getHeaders(headers));
+ }
+
+ Message(String id, FieldTable headers) throws AMQException
+ {
+ this(getPublishRequest(id), getContentHeader(headers), null);
+ }
+
+ private Message(BasicPublishBody publish, ContentHeaderBody header, List<ContentBody> bodies) throws AMQException
+ {
+ super(_messageStore, publish, header, bodies);
+ }
+
+ private Message(AMQMessage msg) throws AMQException
+ {
+ super(msg);
+ }
+
+ void route(Exchange exchange) throws AMQException
+ {
+ exchange.route(this);
+ }
+
+ boolean isInQueue(TestQueue queue)
+ {
+ return queue.messages.contains(this);
+ }
+
+ public int hashCode()
+ {
+ return getKey().hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ return o instanceof HeadersExchangeTest.Message && equals((HeadersExchangeTest.Message) o);
+ }
+
+ private boolean equals(HeadersExchangeTest.Message m)
+ {
+ return getKey().equals(m.getKey());
+ }
+
+ public String toString()
+ {
+ return getKey().toString();
+ }
+
+ private Object getKey()
+ {
+ return getPublishBody().routingKey;
+ }
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/exchange/HeadersBindingTest.java b/java/broker/test/src/org/apache/qpid/server/exchange/HeadersBindingTest.java
new file mode 100644
index 0000000000..7e33b1d711
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/exchange/HeadersBindingTest.java
@@ -0,0 +1,200 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.junit.Test;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+
+import java.util.Map;
+import java.util.HashMap;
+
+import junit.framework.JUnit4TestAdapter;
+
+/**
+ */
+public class HeadersBindingTest
+{
+ private Map<String, String> bindHeaders = new HashMap<String, String>();
+ private Map<String, String> matchHeaders = new HashMap<String, String>();
+
+ @Test public void default_1()
+ {
+ bindHeaders.put("A", "Value of A");
+
+ matchHeaders.put("A", "Value of A");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ @Test public void default_2()
+ {
+ bindHeaders.put("A", "Value of A");
+
+ matchHeaders.put("A", "Value of A");
+ matchHeaders.put("B", "Value of B");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ @Test public void default_3()
+ {
+ bindHeaders.put("A", "Value of A");
+
+ matchHeaders.put("A", "Altered value of A");
+
+ assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ @Test public void all_1()
+ {
+ bindHeaders.put("X-match", "all");
+ bindHeaders.put("A", "Value of A");
+
+ matchHeaders.put("A", "Value of A");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ @Test public void all_2()
+ {
+ bindHeaders.put("X-match", "all");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.put("A", "Value of A");
+
+ assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ @Test public void all_3()
+ {
+ bindHeaders.put("X-match", "all");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.put("A", "Value of A");
+ matchHeaders.put("B", "Value of B");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ @Test public void all_4()
+ {
+ bindHeaders.put("X-match", "all");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.put("A", "Value of A");
+ matchHeaders.put("B", "Value of B");
+ matchHeaders.put("C", "Value of C");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ @Test public void all_5()
+ {
+ bindHeaders.put("X-match", "all");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.put("A", "Value of A");
+ matchHeaders.put("B", "Altered value of B");
+ matchHeaders.put("C", "Value of C");
+
+ assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ @Test public void any_1()
+ {
+ bindHeaders.put("X-match", "any");
+ bindHeaders.put("A", "Value of A");
+
+ matchHeaders.put("A", "Value of A");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ @Test public void any_2()
+ {
+ bindHeaders.put("X-match", "any");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.put("A", "Value of A");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ @Test public void any_3()
+ {
+ bindHeaders.put("X-match", "any");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.put("A", "Value of A");
+ matchHeaders.put("B", "Value of B");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ @Test public void any_4()
+ {
+ bindHeaders.put("X-match", "any");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.put("A", "Value of A");
+ matchHeaders.put("B", "Value of B");
+ matchHeaders.put("C", "Value of C");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ @Test public void any_5()
+ {
+ bindHeaders.put("X-match", "any");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.put("A", "Value of A");
+ matchHeaders.put("B", "Altered value of B");
+ matchHeaders.put("C", "Value of C");
+
+ assertTrue(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+
+ @Test public void any_6()
+ {
+ bindHeaders.put("X-match", "any");
+ bindHeaders.put("A", "Value of A");
+ bindHeaders.put("B", "Value of B");
+
+ matchHeaders.put("A", "Altered value of A");
+ matchHeaders.put("B", "Altered value of B");
+ matchHeaders.put("C", "Value of C");
+
+ assertFalse(new HeadersBinding(bindHeaders).matches(matchHeaders));
+ }
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(HeadersBindingTest.class);
+ }
+
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/exchange/HeadersExchangePerformanceTest.java b/java/broker/test/src/org/apache/qpid/server/exchange/HeadersExchangePerformanceTest.java
new file mode 100644
index 0000000000..74cb082db7
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/exchange/HeadersExchangePerformanceTest.java
@@ -0,0 +1,181 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.queue.NoConsumersException;
+import org.apache.qpid.server.util.TimedRun;
+import org.apache.qpid.server.util.AveragedRun;
+import org.apache.qpid.framing.BasicPublishBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.ContentBody;
+
+import java.util.List;
+
+/**
+ * Want to vary the number of regsitrations, messages and matches and measure
+ * the corresponding variance in execution time.
+ * <p/>
+ * Each registration will contain the 'All' header, even registrations will
+ * contain the 'Even' header and odd headers will contain the 'Odd' header.
+ * In additions each regsitration will have a unique value for the 'Specific'
+ * header as well.
+ * <p/>
+ * Messages can then be routed to all registrations, to even- or odd- registrations
+ * or to a specific registration.
+ *
+ */
+public class HeadersExchangePerformanceTest extends AbstractHeadersExchangeTest
+{
+ private static enum Mode {ALL, ODD_OR_EVEN, SPECIFIC}
+
+ private final TestQueue[] queues;
+ private final Mode mode;
+
+ public HeadersExchangePerformanceTest(Mode mode, int registrations) throws AMQException
+ {
+ this.mode = mode;
+ queues = new TestQueue[registrations];
+ for (int i = 0; i < queues.length; i++)
+ {
+ switch(mode)
+ {
+ case ALL:
+ queues[i] = bind(new FastQueue("Queue" + i), "All");
+ break;
+ case ODD_OR_EVEN:
+ queues[i] = bind(new FastQueue("Queue" + i), "All", oddOrEven(i));
+ break;
+ case SPECIFIC:
+ queues[i] = bind(new FastQueue("Queue" + i), "All", oddOrEven(i), "Specific"+ i);
+ break;
+ }
+ }
+ }
+
+ void sendToAll(int count) throws AMQException
+ {
+ send(count, "All=True");
+ }
+
+ void sendToOdd(int count) throws AMQException
+ {
+ send(count, "All=True", "Odd=True");
+ }
+
+ void sendToEven(int count) throws AMQException
+ {
+ send(count, "All=True", "Even=True");
+ }
+
+ void sendToAllSpecifically(int count) throws AMQException
+ {
+ for (int i = 0; i < queues.length; i++)
+ {
+ sendToSpecific(count, i);
+ }
+ }
+
+ void sendToSpecific(int count, int index) throws AMQException
+ {
+ send(count, "All=True", oddOrEven(index) + "=True", "Specific=" + index);
+ }
+
+ private void send(int count, String... headers) throws AMQException
+ {
+ for (int i = 0; i < count; i++)
+ {
+ route(new Message("Message" + i, headers));
+ }
+ }
+
+ private static String oddOrEven(int i)
+ {
+ return (i % 2 == 0 ? "Even" : "Odd");
+ }
+
+ static class FastQueue extends TestQueue
+ {
+
+ public FastQueue(String name) throws AMQException
+ {
+ super(name);
+ }
+
+ public void deliver(BasicPublishBody publishBody, ContentHeaderBody contentHeaderBody, List<ContentBody> contentBodies) throws NoConsumersException
+ {
+ //just discard as we are not testing routing functionality here
+ }
+ }
+
+ static class Test extends TimedRun
+ {
+ private final Mode mode;
+ private final int registrations;
+ private final int count;
+ private HeadersExchangePerformanceTest test;
+
+ Test(Mode mode, int registrations, int count)
+ {
+ super(mode + ", registrations=" + registrations + ", count=" + count);
+ this.mode = mode;
+ this.registrations = registrations;
+ this.count = count;
+ }
+
+ protected void setup() throws Exception
+ {
+ test = new HeadersExchangePerformanceTest(mode, registrations);
+ run(100); //do a warm up run before times start
+ }
+
+ protected void teardown() throws Exception
+ {
+ test = null;
+ System.gc();
+ }
+
+ protected void run() throws Exception
+ {
+ run(count);
+ }
+
+ private void run(int count) throws Exception
+ {
+ switch(mode)
+ {
+ case ALL:
+ test.sendToAll(count);
+ break;
+ default:
+ System.out.println("Test for " + mode + " not yet implemented.");
+ }
+ }
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ int registrations = Integer.parseInt(argv[0]);
+ int messages = Integer.parseInt(argv[1]);
+ int iterations = Integer.parseInt(argv[2]);
+ TimedRun test = new Test(Mode.ALL, registrations, messages);
+ AveragedRun tests = new AveragedRun(test, iterations);
+ System.out.println(tests.call());
+ }
+}
+
diff --git a/java/broker/test/src/org/apache/qpid/server/exchange/HeadersExchangeTest.java b/java/broker/test/src/org/apache/qpid/server/exchange/HeadersExchangeTest.java
new file mode 100644
index 0000000000..86414ffae2
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/exchange/HeadersExchangeTest.java
@@ -0,0 +1,81 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import org.junit.Test;
+import org.junit.Before;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.util.NullApplicationRegistry;
+import junit.framework.JUnit4TestAdapter;
+
+public class HeadersExchangeTest extends AbstractHeadersExchangeTest
+{
+ @Before
+ public void init() throws Exception
+ {
+ ApplicationRegistry.initialise(new NullApplicationRegistry());
+ }
+
+ @Test
+ public void simple() throws AMQException
+ {
+ TestQueue q1 = bindDefault("F0000");
+ TestQueue q2 = bindDefault("F0000=Aardvark");
+ TestQueue q3 = bindDefault("F0001");
+ TestQueue q4 = bindDefault("F0001=Bear");
+ TestQueue q5 = bindDefault("F0000", "F0001");
+ TestQueue q6 = bindDefault("F0000=Aardvark", "F0001=Bear");
+ TestQueue q7 = bindDefault("F0000", "F0001=Bear");
+ TestQueue q8 = bindDefault("F0000=Aardvark", "F0001");
+ TestQueue q9 = bindDefault("F0000=Apple", "F0001=Banana");
+ TestQueue q10 = bindDefault("F0000=Apple", "F0001");
+
+ routeAndTest(new Message("Message1", "F0000"), q1);
+ routeAndTest(new Message("Message2", "F0000=Aardvark"), q1, q2);
+ routeAndTest(new Message("Message3", "F0000=Aardvark", "F0001"), q1, q2, q3, q5, q8);
+ routeAndTest(new Message("Message4", "F0000", "F0001=Bear"), q1, q3, q4, q5, q7);
+ routeAndTest(new Message("Message5", "F0000=Aardvark", "F0001=Bear"),
+ q1, q2, q3, q4, q5, q6, q7, q8);
+ routeAndTest(new Message("Message6", "F0002"));
+ }
+
+ @Test
+ public void any() throws AMQException
+ {
+ TestQueue q1 = bindDefault("F0000", "F0001", "X-match=any");
+ TestQueue q2 = bindDefault("F0000=Aardvark", "F0001=Bear", "X-match=any");
+ TestQueue q3 = bindDefault("F0000", "F0001=Bear", "X-match=any");
+ TestQueue q4 = bindDefault("F0000=Aardvark", "F0001", "X-match=any");
+ TestQueue q5 = bindDefault("F0000=Apple", "F0001=Banana", "X-match=any");
+ TestQueue q6 = bindDefault("F0000=Apple", "F0001", "X-match=any");
+
+ routeAndTest(new Message("Message1", "F0000"), q1, q3);
+ routeAndTest(new Message("Message2", "F0000=Aardvark"), q1, q2, q3, q4);
+ routeAndTest(new Message("Message3", "F0000=Aardvark", "F0001"), q1, q2, q3, q4, q6);
+ routeAndTest(new Message("Message4", "F0000", "F0001=Bear"), q1, q2, q3, q4, q6);
+ routeAndTest(new Message("Message5", "F0000=Aardvark", "F0001=Bear"), q1, q2, q3, q4, q6);
+ routeAndTest(new Message("Message6", "F0002"));
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(HeadersExchangeTest.class);
+ }
+
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/exchange/UnitTests.java b/java/broker/test/src/org/apache/qpid/server/exchange/UnitTests.java
new file mode 100644
index 0000000000..a3c6439b67
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/exchange/UnitTests.java
@@ -0,0 +1,32 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.exchange;
+
+import junit.framework.JUnit4TestAdapter;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({HeadersBindingTest.class, HeadersExchangeTest.class})
+public class UnitTests
+{
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(UnitTests.class);
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/protocol/MockIoSession.java b/java/broker/test/src/org/apache/qpid/server/protocol/MockIoSession.java
new file mode 100644
index 0000000000..101ba7dd36
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/protocol/MockIoSession.java
@@ -0,0 +1,288 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import org.apache.mina.common.*;
+import org.apache.mina.common.support.DefaultCloseFuture;
+import org.apache.mina.common.support.DefaultWriteFuture;
+
+import java.net.SocketAddress;
+import java.util.Set;
+
+public class MockIoSession implements IoSession
+{
+ private AMQProtocolSession _protocolSession;
+
+ /**
+ * Stores the last response written
+ */
+ private Object _lastWrittenObject;
+
+ private boolean _closing;
+
+ public MockIoSession()
+ {
+ }
+
+ public Object getLastWrittenObject()
+ {
+ return _lastWrittenObject;
+ }
+
+ public IoService getService()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public IoHandler getHandler()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public IoSessionConfig getConfig()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public IoFilterChain getFilterChain()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public WriteFuture write(Object message)
+ {
+ WriteFuture wf = new DefaultWriteFuture(null);
+ _lastWrittenObject = message;
+ return wf;
+ }
+
+ public CloseFuture close()
+ {
+ _closing = true;
+ CloseFuture cf = new DefaultCloseFuture(null);
+ cf.setClosed();
+ return cf;
+ }
+
+ public Object getAttachment()
+ {
+ return _protocolSession;
+ }
+
+ public Object setAttachment(Object attachment)
+ {
+ Object current = _protocolSession;
+ _protocolSession = (AMQProtocolSession) attachment;
+ return current;
+ }
+
+ public Object getAttribute(String key)
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Object setAttribute(String key, Object value)
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Object setAttribute(String key)
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Object removeAttribute(String key)
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean containsAttribute(String key)
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public Set getAttributeKeys()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public TransportType getTransportType()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isConnected()
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isClosing()
+ {
+ return _closing;
+ }
+
+ public CloseFuture getCloseFuture()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public SocketAddress getRemoteAddress()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public SocketAddress getLocalAddress()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public SocketAddress getServiceAddress()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getIdleTime(IdleStatus status)
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getIdleTimeInMillis(IdleStatus status)
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setIdleTime(IdleStatus status, int idleTime)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getWriteTimeout()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getWriteTimeoutInMillis()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setWriteTimeout(int writeTimeout)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public TrafficMask getTrafficMask()
+ {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void setTrafficMask(TrafficMask trafficMask)
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void suspendRead()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void suspendWrite()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void resumeRead()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public void resumeWrite()
+ {
+ //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getReadBytes()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getWrittenBytes()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getReadMessages()
+ {
+ return 0L;
+ }
+
+ public long getWrittenMessages()
+ {
+ return 0L;
+ }
+
+ public long getWrittenWriteRequests()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getScheduledWriteRequests()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getScheduledWriteBytes()
+ {
+ return 0; //TODO
+ }
+
+ public long getCreationTime()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getLastIoTime()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getLastReadTime()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getLastWriteTime()
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public boolean isIdle(IdleStatus status)
+ {
+ return false; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public int getIdleCount(IdleStatus status)
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ public long getLastIdleTime(IdleStatus status)
+ {
+ return 0; //To change body of implemented methods use File | Settings | File Templates.
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/protocol/TestProtocolInitiation.java b/java/broker/test/src/org/apache/qpid/server/protocol/TestProtocolInitiation.java
new file mode 100644
index 0000000000..34e1709a2d
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/protocol/TestProtocolInitiation.java
@@ -0,0 +1,212 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import junit.framework.Assert;
+import junit.framework.JUnit4TestAdapter;
+import org.apache.qpid.codec.AMQDecoder;
+import org.apache.qpid.codec.AMQEncoder;
+import org.apache.qpid.framing.*;
+import org.apache.mina.common.ByteBuffer;
+import org.apache.mina.common.WriteFuture;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+import org.apache.mina.filter.codec.ProtocolEncoderOutput;
+import org.apache.mina.filter.codec.support.SimpleProtocolDecoderOutput;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * This test suite tests the handling of protocol initiation frames and related issues.
+ */
+public class TestProtocolInitiation implements ProtocolVersionList
+{
+ private AMQPFastProtocolHandler _protocolHandler;
+
+ private MockIoSession _mockIoSession;
+
+ /**
+ * We need to use the object encoder mechanism so to allow us to retrieve the
+ * output (a bytebuffer) we define our own encoder output class. The encoder
+ * writes the encoded data to this class, from where we can retrieve it during
+ * the test run.
+ */
+ private class TestProtocolEncoderOutput implements ProtocolEncoderOutput
+ {
+ public ByteBuffer result;
+
+ public void write(ByteBuffer buf)
+ {
+ result = buf;
+ }
+
+ public void mergeAll()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ public WriteFuture flush()
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private class TestProtocolDecoderOutput implements ProtocolDecoderOutput
+ {
+ public Object result;
+
+ public void write(Object buf)
+ {
+ result = buf;
+ }
+
+ public void flush()
+ {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ @Before
+ public void createCommonObjects()
+ {
+ _mockIoSession = new MockIoSession();
+ _protocolHandler = new AMQPFastProtocolHandler(null, null);
+ }
+
+
+ /**
+ * Tests that the AMQDecoder handles invalid protocol classes
+ * @throws Exception
+ */
+ @Test(expected = AMQProtocolClassException.class)
+ public void testDecoderValidateProtocolClass() throws Exception
+ {
+ ProtocolInitiation pi = createValidProtocolInitiation();
+ pi.protocolClass = 2;
+ decodePI(pi);
+ }
+
+ /**
+ * Tests that the AMQDecoder handles invalid protocol instance numbers
+ * @throws Exception
+ */
+ @Test(expected = AMQProtocolInstanceException.class)
+ public void testDecoderValidatesProtocolInstance() throws Exception
+ {
+ ProtocolInitiation pi = createValidProtocolInitiation();
+ pi.protocolInstance = 2;
+ decodePI(pi);
+ }
+
+ /**
+ * Tests that the AMQDecoder handles invalid protocol major
+ * @throws Exception
+ */
+ @Test(expected = AMQProtocolVersionException.class)
+ public void testDecoderValidatesProtocolMajor() throws Exception
+ {
+ ProtocolInitiation pi = createValidProtocolInitiation();
+ pi.protocolMajor = 2;
+ decodePI(pi);
+ }
+
+ /**
+ * Tests that the AMQDecoder handles invalid protocol minor
+ * @throws Exception
+ */
+ @Test(expected = AMQProtocolVersionException.class)
+ public void testDecoderValidatesProtocolMinor() throws Exception
+ {
+ ProtocolInitiation pi = createValidProtocolInitiation();
+ pi.protocolMinor = 99;
+ decodePI(pi);
+ }
+
+ /**
+ * Tests that the AMQDecoder accepts a valid PI
+ * @throws Exception
+ */
+ @Test(expected = AMQProtocolHeaderException.class)
+ public void testDecoderValidatesHeader() throws Exception
+ {
+ ProtocolInitiation pi = createValidProtocolInitiation();
+ pi.header = new char[] {'P', 'Q', 'M', 'A' };
+ decodePI(pi);
+ }
+
+ /**
+ * Test that a valid header is passed by the decoder.
+ * @throws Exception
+ */
+ @Test
+ public void testDecoderAcceptsValidHeader() throws Exception
+ {
+ ProtocolInitiation pi = createValidProtocolInitiation();
+ decodePI(pi);
+ }
+
+ /**
+ * This test checks that an invalid protocol header results in the
+ * connection being closed.
+ */
+ @Test
+ public void testInvalidProtocolHeaderClosesConnection() throws Exception
+ {
+ AMQProtocolHeaderException pe = new AMQProtocolHeaderException("Test");
+ _protocolHandler.exceptionCaught(_mockIoSession, pe);
+ Assert.assertNotNull(_mockIoSession.getLastWrittenObject());
+ Object piResponse = _mockIoSession.getLastWrittenObject();
+ Assert.assertEquals(piResponse.getClass(), ProtocolInitiation.class);
+ ProtocolInitiation pi = (ProtocolInitiation) piResponse;
+ Assert.assertEquals("Protocol Initiation sent out was not the broker's expected header", pi,
+ createValidProtocolInitiation());
+ Assert.assertTrue("Session has not been closed", _mockIoSession.isClosing());
+ }
+
+ private ProtocolInitiation createValidProtocolInitiation()
+ {
+ /* Find last protocol version in protocol version list. Make sure last protocol version
+ listed in the build file (build-module.xml) is the latest version which will be used
+ here. */
+ int i = pv.length - 1;
+ return new ProtocolInitiation(pv[i][PROTOCOL_MAJOR], pv[i][PROTOCOL_MINOR]);
+ }
+
+ /**
+ * Helper that encodes a protocol initiation and attempts to decode it
+ * @param pi
+ * @throws Exception
+ */
+ private void decodePI(ProtocolInitiation pi) throws Exception
+ {
+ // we need to do this test at the level of the decoder since we initially only expect PI frames
+ // so the protocol handler is not set up to know whether it should be expecting a PI frame or
+ // a different type of frame
+ AMQDecoder decoder = new AMQDecoder(true);
+ AMQEncoder encoder = new AMQEncoder();
+ TestProtocolEncoderOutput peo = new TestProtocolEncoderOutput();
+ encoder.encode(_mockIoSession, pi, peo);
+ TestProtocolDecoderOutput pdo = new TestProtocolDecoderOutput();
+ decoder.decode(_mockIoSession, peo.result, pdo);
+ ((ProtocolInitiation) pdo.result).checkVersion(this);
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(TestProtocolInitiation.class);
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/protocol/UnitTests.java b/java/broker/test/src/org/apache/qpid/server/protocol/UnitTests.java
new file mode 100644
index 0000000000..09dc76d310
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/protocol/UnitTests.java
@@ -0,0 +1,32 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.protocol;
+
+import junit.framework.JUnit4TestAdapter;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({TestProtocolInitiation.class})
+public class UnitTests
+{
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(UnitTests.class);
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/queue/AckTest.java b/java/broker/test/src/org/apache/qpid/server/queue/AckTest.java
new file mode 100644
index 0000000000..904665949c
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/queue/AckTest.java
@@ -0,0 +1,243 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import junit.framework.JUnit4TestAdapter;
+import static org.junit.Assert.assertTrue;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.Ignore;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.BasicPublishBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.store.TestableMemoryMessageStore;
+import org.apache.qpid.server.util.NullApplicationRegistry;
+import org.apache.log4j.Logger;
+
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Tests that acknowledgements are handled correctly.
+ */
+public class AckTest
+{
+ private static final Logger _log = Logger.getLogger(AckTest.class);
+
+ private SubscriptionImpl _subscription;
+
+ private MockProtocolSession _protocolSession;
+
+ private TestableMemoryMessageStore _messageStore;
+
+ private AMQChannel _channel;
+
+ private SubscriptionSet _subscriptionManager;
+
+ private AMQQueue _queue;
+
+ public AckTest() throws Exception
+ {
+ ApplicationRegistry.initialise(new NullApplicationRegistry());
+ }
+
+ @Before
+ public void setup() throws Exception
+ {
+ _messageStore = new TestableMemoryMessageStore();
+ _channel = new AMQChannel(5, _messageStore, null/*dont need exchange registry*/);
+ _protocolSession = new MockProtocolSession(_messageStore);
+ _protocolSession.addChannel(_channel);
+ _subscriptionManager = new SubscriptionSet();
+ _queue = new AMQQueue("myQ", false, "guest", true, new DefaultQueueRegistry(), _subscriptionManager);
+ }
+
+ private void publishMessages(int count) throws AMQException
+ {
+ for (int i = 1; i <= count; i++)
+ {
+ BasicPublishBody publishBody = new BasicPublishBody();
+ publishBody.routingKey = "rk";
+ publishBody.exchange = "someExchange";
+ AMQMessage msg = new AMQMessage(_messageStore, publishBody);
+ msg.setContentHeaderBody(new ContentHeaderBody());
+ _subscription.send(msg, _queue);
+ }
+ }
+
+ /**
+ * Tests that the acknowledgements are correctly associated with a channel and
+ * order is preserved when acks are enabled
+ */
+ @Test @Ignore /* FIXME: broken at the moment */
+ public void ackChannelAssociationTest() throws AMQException
+ {
+ _subscription = new SubscriptionImpl(5, _protocolSession, "conTag", true);
+ final int msgCount = 10;
+ publishMessages(msgCount);
+
+ Map<Long, AMQChannel.UnacknowledgedMessage> map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == msgCount);
+
+ Iterator<Map.Entry<Long, AMQChannel.UnacknowledgedMessage>> it = map.entrySet().iterator();
+ for (int i = 1; i <= map.size(); i++)
+ {
+ Map.Entry<Long, AMQChannel.UnacknowledgedMessage> entry = it.next();
+ assertTrue(entry.getKey() == i);
+ AMQChannel.UnacknowledgedMessage unackedMsg = entry.getValue();
+ assertTrue(unackedMsg.queue == _queue);
+ }
+ assertTrue(_messageStore.getMessageMap().size() == msgCount);
+ }
+
+ /**
+ * Tests that in no-ack mode no messages are retained
+ */
+ @Test
+ public void testNoAckMode() throws AMQException
+ {
+ // false arg means no acks expected
+ _subscription = new SubscriptionImpl(5, _protocolSession, "conTag", false);
+ final int msgCount = 10;
+ publishMessages(msgCount);
+
+ Map<Long, AMQChannel.UnacknowledgedMessage> map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == 0);
+ assertTrue(_messageStore.getMessageMap().size() == 0);
+ }
+
+ /**
+ * Tests that a single acknowledgement is handled correctly (i.e multiple flag not
+ * set case)
+ */
+ @Test
+ public void singleAckReceivedTest() throws AMQException
+ {
+ _subscription = new SubscriptionImpl(5, _protocolSession, "conTag", true);
+ final int msgCount = 10;
+ publishMessages(msgCount);
+
+ _channel.acknowledgeMessage(5, false);
+ Map<Long, AMQChannel.UnacknowledgedMessage> map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == msgCount - 1);
+
+ Iterator<Map.Entry<Long, AMQChannel.UnacknowledgedMessage>> it = map.entrySet().iterator();
+ int i = 1;
+ while (i <= map.size())
+ {
+ Map.Entry<Long, AMQChannel.UnacknowledgedMessage> entry = it.next();
+ assertTrue(entry.getKey() == i);
+ AMQChannel.UnacknowledgedMessage unackedMsg = entry.getValue();
+ assertTrue(unackedMsg.queue == _queue);
+ // 5 is the delivery tag of the message that *should* be removed
+ if (++i == 5)
+ {
+ ++i;
+ }
+ }
+ }
+
+ /**
+ * Tests that a single acknowledgement is handled correctly (i.e multiple flag not
+ * set case)
+ */
+ @Test
+ public void multiAckReceivedTest() throws AMQException
+ {
+ _subscription = new SubscriptionImpl(5, _protocolSession, "conTag", true);
+ final int msgCount = 10;
+ publishMessages(msgCount);
+
+ _channel.acknowledgeMessage(5, true);
+ Map<Long, AMQChannel.UnacknowledgedMessage> map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == 5);
+
+ Iterator<Map.Entry<Long, AMQChannel.UnacknowledgedMessage>> it = map.entrySet().iterator();
+ int i = 1;
+ while (i <= map.size())
+ {
+ Map.Entry<Long, AMQChannel.UnacknowledgedMessage> entry = it.next();
+ assertTrue(entry.getKey() == i + 5);
+ AMQChannel.UnacknowledgedMessage unackedMsg = entry.getValue();
+ assertTrue(unackedMsg.queue == _queue);
+ ++i;
+ }
+ }
+
+ /**
+ * Tests that a multiple acknowledgement is handled correctly. When ack'ing all pending msgs.
+ *
+ */
+ @Test
+ public void multiAckAllReceivedTest() throws AMQException
+ {
+ _subscription = new SubscriptionImpl(5, _protocolSession, "conTag", true);
+ final int msgCount = 10;
+ publishMessages(msgCount);
+
+ _channel.acknowledgeMessage(0, true);
+ Map<Long, AMQChannel.UnacknowledgedMessage> map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == 0);
+
+ Iterator<Map.Entry<Long, AMQChannel.UnacknowledgedMessage>> it = map.entrySet().iterator();
+ int i = 1;
+ while (i <= map.size())
+ {
+ Map.Entry<Long, AMQChannel.UnacknowledgedMessage> entry = it.next();
+ assertTrue(entry.getKey() == i + 5);
+ AMQChannel.UnacknowledgedMessage unackedMsg = entry.getValue();
+ assertTrue(unackedMsg.queue == _queue);
+ ++i;
+ }
+ }
+
+
+
+ @Test
+ public void testPrefetch() throws AMQException
+ {
+ _subscription = new SubscriptionImpl(5, _protocolSession, "conTag", true);
+ _channel.setPrefetchCount(5);
+ final int msgCount = 5;
+ publishMessages(msgCount);
+
+ // at this point we should have sent out only 5 messages with a further 5 queued
+ // up in the channel which should be suspended
+ assertTrue(_subscription.isSuspended());
+ Map<Long, AMQChannel.UnacknowledgedMessage> map = _channel.getUnacknowledgedMessageMap();
+ assertTrue(map.size() == 5);
+ _channel.acknowledgeMessage(5, true);
+ assertTrue(!_subscription.isSuspended());
+ try
+ {
+ Thread.sleep(3000);
+ }
+ catch (InterruptedException e)
+ {
+ _log.error("Error: " + e, e);
+ }
+ assertTrue(map.size() == 0);
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(AckTest.class);
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/queue/ConcurrencyTest.java b/java/broker/test/src/org/apache/qpid/server/queue/ConcurrencyTest.java
new file mode 100644
index 0000000000..1cf11933fa
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/queue/ConcurrencyTest.java
@@ -0,0 +1,261 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import junit.framework.JUnit4TestAdapter;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import org.junit.Test;
+import org.junit.Assert;
+import org.apache.qpid.AMQException;
+import org.apache.qpid.server.handler.OnCurrentThreadExecutor;
+
+import java.util.*;
+import java.util.concurrent.Executor;
+
+/**
+ * Tests delivery in the face of concurrent incoming _messages, subscription alterations
+ * and attempts to asynchronously process queued _messages.
+ */
+public class ConcurrencyTest extends MessageTestHelper
+{
+ private final Random random = new Random();
+
+ private final int numMessages = 1000;
+
+ private final List<TestSubscription> _subscribers = new ArrayList<TestSubscription>();
+ private final Set<Subscription> _active = new HashSet<Subscription>();
+ private final List<AMQMessage> _messages = new ArrayList<AMQMessage>();
+ private int next = 0;//index to next message to send
+ private final List<AMQMessage> _received = Collections.synchronizedList(new ArrayList<AMQMessage>());
+ private final Executor _executor = new OnCurrentThreadExecutor();
+ private final List<Thread> _threads = new ArrayList<Thread>();
+
+ private final SubscriptionSet _subscriptionMgr = new SubscriptionSet();
+ private final DeliveryManager _deliveryMgr;
+
+ private boolean isComplete;
+ private boolean failed;
+
+ public ConcurrencyTest() throws Exception
+ {
+ _deliveryMgr = new DeliveryManager(_subscriptionMgr, new AMQQueue("myQ", false, "guest", false,
+ new DefaultQueueRegistry()));
+ }
+
+ @Test
+ public void concurrent1() throws InterruptedException, AMQException
+ {
+ initSubscriptions(10);
+ initMessages(numMessages);
+ initThreads(1, 4, 4, 4);
+ run();
+ check();
+ }
+
+ @Test
+ public void concurrent2() throws InterruptedException, AMQException
+ {
+ initSubscriptions(10);
+ initMessages(numMessages);
+ initThreads(4, 2, 2, 2);
+ run();
+ check();
+ }
+
+ void check()
+ {
+ assertFalse("Failed", failed);
+
+ _deliveryMgr.processAsync(_executor);
+
+ assertEquals("Did not recieve the correct number of messages", _messages.size(), _received.size());
+ for(int i = 0; i < _messages.size(); i++)
+ {
+ assertEquals("Wrong message at " + i, _messages.get(i), _received.get(i));
+ }
+ }
+
+ void initSubscriptions(int subscriptions)
+ {
+ for(int i = 0; i < subscriptions; i++)
+ {
+ _subscribers.add(new TestSubscription("Subscriber" + i, _received));
+ }
+ }
+
+ void initMessages(int messages) throws AMQException
+ {
+ for(int i = 0; i < messages; i++)
+ {
+ _messages.add(message());
+ }
+ }
+
+ void initThreads(int senders, int subscribers, int suspenders, int processors)
+ {
+ addThreads(senders, senders == 1 ? new Sender() : new OrderedSender());
+ addThreads(subscribers, new Subscriber());
+ addThreads(suspenders, new Suspender());
+ addThreads(processors, new Processor());
+ }
+
+ void addThreads(int count, Runnable runner)
+ {
+ for(int i = 0; i < count; i++)
+ {
+ _threads.add(new Thread(runner, runner.toString()));
+ }
+ }
+
+ void run() throws InterruptedException
+ {
+ for(Thread t : _threads)
+ {
+ t.start();
+ }
+
+ for(Thread t : _threads)
+ {
+ t.join();
+ }
+ }
+
+ private void toggle(Subscription s)
+ {
+ synchronized (_active)
+ {
+ if (_active.contains(s))
+ {
+ _active.remove(s);
+ Subscription result = _subscriptionMgr.removeSubscriber(s);
+ Assert.assertTrue("Removed subscription " + result + " but trying to remove subscription " + s,
+ result != null && result.equals(s));
+ }
+ else
+ {
+ _active.add(s);
+ _subscriptionMgr.addSubscriber(s);
+ }
+ }
+ }
+
+ private AMQMessage nextMessage()
+ {
+ synchronized (_messages)
+ {
+ if (next < _messages.size())
+ {
+ return _messages.get(next++);
+ }
+ else
+ {
+ if (_deliveryMgr.getQueueMessageCount() == 0) {
+ isComplete = true;
+ }
+ return null;
+ }
+ }
+ }
+
+ private boolean randomBoolean()
+ {
+ return random.nextBoolean();
+ }
+
+ private TestSubscription randomSubscriber()
+ {
+ return _subscribers.get(random.nextInt(_subscribers.size()));
+ }
+
+ private class Sender extends Runner
+ {
+ void doRun() throws Throwable
+ {
+ AMQMessage msg = nextMessage();
+ if (msg != null)
+ {
+ _deliveryMgr.deliver(toString(), msg);
+ }
+ }
+ }
+
+ private class OrderedSender extends Sender
+ {
+ synchronized void doRun() throws Throwable
+ {
+ super.doRun();
+ }
+ }
+
+ private class Suspender extends Runner
+ {
+ void doRun() throws Throwable
+ {
+ randomSubscriber().setSuspended(randomBoolean());
+ }
+ }
+
+ private class Subscriber extends Runner
+ {
+ void doRun() throws Throwable
+ {
+ toggle(randomSubscriber());
+ }
+ }
+
+ private class Processor extends Runner
+ {
+ void doRun() throws Throwable
+ {
+ _deliveryMgr.processAsync(_executor);
+ }
+ }
+
+ private abstract class Runner implements Runnable
+ {
+ public void run()
+ {
+ try
+ {
+ while (!stop())
+ {
+ doRun();
+ }
+ }
+ catch (Throwable t)
+ {
+ failed = true;
+ t.printStackTrace();
+ }
+ }
+
+ abstract void doRun() throws Throwable;
+
+ boolean stop()
+ {
+ return isComplete || failed;
+ }
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(ConcurrencyTest.class);
+ }
+
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/queue/DeliveryManagerTest.java b/java/broker/test/src/org/apache/qpid/server/queue/DeliveryManagerTest.java
new file mode 100644
index 0000000000..d00cd55fa1
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/queue/DeliveryManagerTest.java
@@ -0,0 +1,159 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.apache.qpid.server.handler.OnCurrentThreadExecutor;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.util.NullApplicationRegistry;
+import org.apache.qpid.AMQException;
+import junit.framework.JUnit4TestAdapter;
+
+public class DeliveryManagerTest extends MessageTestHelper
+{
+ private final SubscriptionSet _subscriptions = new SubscriptionSet();
+ private final DeliveryManager _mgr;
+
+ public DeliveryManagerTest() throws Exception
+ {
+ try
+ {
+ _mgr = new DeliveryManager(_subscriptions, new AMQQueue("myQ", false, "guest", false,
+ new DefaultQueueRegistry()));
+ }
+ catch(Throwable t)
+ {
+ t.printStackTrace();
+ throw new AMQException("Could not initialise delivery manager", t);
+ }
+ }
+
+ @Test
+ public void startInQueueingMode() throws AMQException
+ {
+ AMQMessage[] messages = new AMQMessage[10];
+ for(int i = 0; i < messages.length; i++)
+ {
+ messages[i] = message();
+ }
+ int batch = messages.length / 2;
+
+ for(int i = 0; i < batch; i++)
+ {
+ _mgr.deliver("Me", messages[i]);
+ }
+
+ TestSubscription s1 = new TestSubscription("1");
+ TestSubscription s2 = new TestSubscription("2");
+ _subscriptions.addSubscriber(s1);
+ _subscriptions.addSubscriber(s2);
+
+ for(int i = batch; i < messages.length; i++)
+ {
+ _mgr.deliver("Me", messages[i]);
+ }
+
+ assertTrue(s1.getMessages().isEmpty());
+ assertTrue(s2.getMessages().isEmpty());
+
+ _mgr.processAsync(new OnCurrentThreadExecutor());
+
+ assertEquals(messages.length / 2, s1.getMessages().size());
+ assertEquals(messages.length / 2, s2.getMessages().size());
+
+ for(int i = 0; i < messages.length; i++)
+ {
+ if(i % 2 == 0)
+ {
+ assertTrue(s1.getMessages().get(i / 2) == messages[i]);
+ }
+ else
+ {
+ assertTrue(s2.getMessages().get(i / 2) == messages[i]);
+ }
+ }
+ }
+
+ @Test
+ public void startInDirectMode() throws AMQException
+ {
+ AMQMessage[] messages = new AMQMessage[10];
+ for(int i = 0; i < messages.length; i++)
+ {
+ messages[i] = message();
+ }
+ int batch = messages.length / 2;
+
+ TestSubscription s1 = new TestSubscription("1");
+ _subscriptions.addSubscriber(s1);
+
+ for(int i = 0; i < batch; i++)
+ {
+ _mgr.deliver("Me", messages[i]);
+ }
+
+ assertEquals(batch, s1.getMessages().size());
+ for(int i = 0; i < batch; i++)
+ {
+ assertTrue(messages[i] == s1.getMessages().get(i));
+ }
+ s1.getMessages().clear();
+ assertEquals(0, s1.getMessages().size());
+
+ s1.setSuspended(true);
+ for(int i = batch; i < messages.length; i++)
+ {
+ _mgr.deliver("Me", messages[i]);
+ }
+
+ _mgr.processAsync(new OnCurrentThreadExecutor());
+ assertEquals(0, s1.getMessages().size());
+ s1.setSuspended(false);
+
+ _mgr.processAsync(new OnCurrentThreadExecutor());
+ assertEquals(messages.length - batch, s1.getMessages().size());
+
+ for(int i = batch; i < messages.length; i++)
+ {
+ assertTrue(messages[i] == s1.getMessages().get(i - batch));
+ }
+
+ }
+
+ @Test (expected=NoConsumersException.class)
+ public void noConsumers() throws AMQException
+ {
+ _mgr.deliver("Me", message(true));
+ }
+
+ @Test (expected=NoConsumersException.class)
+ public void noActiveConsumers() throws AMQException
+ {
+ TestSubscription s = new TestSubscription("A");
+ _subscriptions.addSubscriber(s);
+ s.setSuspended(true);
+ _mgr.deliver("Me", message(true));
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(DeliveryManagerTest.class);
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/queue/MessageTestHelper.java b/java/broker/test/src/org/apache/qpid/server/queue/MessageTestHelper.java
new file mode 100644
index 0000000000..f9baa77b65
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/queue/MessageTestHelper.java
@@ -0,0 +1,49 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.framing.BasicPublishBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.SkeletonMessageStore;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.util.NullApplicationRegistry;
+import org.apache.qpid.AMQException;
+
+class MessageTestHelper
+{
+ private final MessageStore _messageStore = new SkeletonMessageStore();
+
+ MessageTestHelper() throws Exception
+ {
+ ApplicationRegistry.initialise(new NullApplicationRegistry());
+ }
+
+ AMQMessage message() throws AMQException
+ {
+ return message(false);
+ }
+
+ AMQMessage message(boolean immediate) throws AMQException
+ {
+ BasicPublishBody publish = new BasicPublishBody();
+ publish.immediate = immediate;
+ return new AMQMessage(_messageStore, publish, new ContentHeaderBody(), null);
+ }
+
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/queue/MockProtocolSession.java b/java/broker/test/src/org/apache/qpid/server/queue/MockProtocolSession.java
new file mode 100644
index 0000000000..f26d6d64b3
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/queue/MockProtocolSession.java
@@ -0,0 +1,121 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.framing.AMQDataBlock;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.store.MessageStore;
+
+import javax.security.sasl.SaslServer;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A protocol session that can be used for testing purposes.
+ */
+public class MockProtocolSession implements AMQProtocolSession
+{
+ private MessageStore _messageStore;
+
+ private Map<Integer, AMQChannel> _channelMap = new HashMap<Integer, AMQChannel>();
+
+ public MockProtocolSession(MessageStore messageStore)
+ {
+ _messageStore = messageStore;
+ }
+
+ public void dataBlockReceived(AMQDataBlock message) throws Exception
+ {
+ }
+
+ public void writeFrame(AMQDataBlock frame)
+ {
+ }
+
+ public String getContextKey()
+ {
+ return null;
+ }
+
+ public void setContextKey(String contextKey)
+ {
+ }
+
+ public AMQChannel getChannel(int channelId)
+ {
+ AMQChannel channel = _channelMap.get(channelId);
+ if (channel == null)
+ {
+ throw new IllegalArgumentException("Invalid channel id: " + channelId);
+ }
+ else
+ {
+ return channel;
+ }
+ }
+
+ public void addChannel(AMQChannel channel)
+ {
+ if (channel == null)
+ {
+ throw new IllegalArgumentException("Channel must not be null");
+ }
+ else
+ {
+ _channelMap.put(channel.getChannelId(), channel);
+ }
+ }
+
+ public void closeChannel(int channelId) throws AMQException
+ {
+ }
+
+ public void removeChannel(int channelId)
+ {
+ _channelMap.remove(channelId);
+ }
+
+ public void initHeartbeats(int delay)
+ {
+ }
+
+ public void closeSession() throws AMQException
+ {
+ }
+
+ public Object getKey()
+ {
+ return null;
+ }
+
+ public String getLocalFQDN()
+ {
+ return null;
+ }
+
+ public SaslServer getSaslServer()
+ {
+ return null;
+ }
+
+ public void setSaslServer(SaslServer saslServer)
+ {
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/queue/QueueConcurrentPerfTest.java b/java/broker/test/src/org/apache/qpid/server/queue/QueueConcurrentPerfTest.java
new file mode 100644
index 0000000000..8ae8ebae79
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/queue/QueueConcurrentPerfTest.java
@@ -0,0 +1,46 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.server.util.AveragedRun;
+import org.apache.qpid.server.util.ConcurrentTest;
+
+public class QueueConcurrentPerfTest extends QueuePerfTest
+{
+ QueueConcurrentPerfTest(Factory factory, int queueCount, int messages)
+ {
+ super(factory, queueCount, messages);
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ Factory[] factories = new Factory[]{SYNCHRONIZED, CONCURRENT};
+ int iterations = 5;
+ String label = argv.length > 0 ? argv[0]: null;
+ System.out.println((label == null ? "" : "Label, ") + "Queue Type, No. of Queues, No. of Operations, Avg Time, Min Time, Max Time");
+ //vary number of queues:
+ for(Factory f : factories)
+ {
+ run(label, new AveragedRun(new ConcurrentTest(new QueuePerfTest(f, 100, 10000), iterations), 5));
+ run(label, new AveragedRun(new ConcurrentTest(new QueuePerfTest(f, 1000, 10000), iterations), 5));
+ run(label, new AveragedRun(new ConcurrentTest(new QueuePerfTest(f, 10000, 10000), iterations), 5));
+ run(label, new AveragedRun(new ConcurrentTest(new QueuePerfTest(f, 1000, 1000), iterations), 5));
+ run(label, new AveragedRun(new ConcurrentTest(new QueuePerfTest(f, 1000, 100000), iterations), 5));
+ }
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/queue/QueuePerfTest.java b/java/broker/test/src/org/apache/qpid/server/queue/QueuePerfTest.java
new file mode 100644
index 0000000000..36e4e90f35
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/queue/QueuePerfTest.java
@@ -0,0 +1,255 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.server.util.AveragedRun;
+import org.apache.qpid.server.util.TimedRun;
+import org.apache.qpid.server.util.RunStats;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+
+public class QueuePerfTest extends TimedRun
+{
+ private final Factory _factory;
+ private final int _queueCount;
+ private final int _messages;
+ private final String _msg = "";
+ private List<Queue<String>> _queues;
+
+ QueuePerfTest(Factory factory, int queueCount, int messages)
+ {
+ super(factory + ", " + queueCount + ", " + messages);
+ _factory = factory;
+ _queueCount = queueCount;
+ _messages = messages;
+ }
+
+ protected void setup() throws Exception
+ {
+ //init
+ int count = Integer.getInteger("prepopulate", 0);
+// System.err.println("Prepopulating with " + count + " items");
+ _queues = new ArrayList<Queue<String>>(_queueCount);
+ for (int i = 0; i < _queueCount; i++)
+ {
+ Queue<String> q = _factory.create();
+ for(int j = 0; j < count; ++j)
+ {
+ q.add("Item"+ j);
+ }
+ _queues.add(q);
+ }
+ System.gc();
+ }
+
+ protected void teardown() throws Exception
+ {
+ System.gc();
+ }
+
+ protected void run() throws Exception
+ {
+ //dispatch
+ for (int i = 0; i < _messages; i++)
+ {
+ for (Queue<String> q : _queues)
+ {
+ q.offer(_msg);
+ q.poll();
+ }
+ }
+ }
+
+ static interface Factory
+ {
+ Queue<String> create();
+ }
+
+ static Factory CONCURRENT = new Factory()
+ {
+ public Queue<String> create()
+ {
+ return new ConcurrentLinkedQueue<String>();
+ }
+
+ public String toString()
+ {
+ return "ConcurrentLinkedQueue";
+ }
+
+ };
+
+ static Factory SYNCHRONIZED = new Factory()
+ {
+ public Queue<String> create()
+ {
+ return new SynchronizedQueue<String>(new LinkedList<String>());
+ }
+
+
+ public String toString()
+ {
+ return "Synchronized LinkedList";
+ }
+ };
+
+ static Factory PLAIN = new Factory()
+ {
+ public Queue<String> create()
+ {
+ return new LinkedList<String>();
+ }
+
+ public String toString()
+ {
+ return "Plain LinkedList";
+ }
+ };
+
+ static class SynchronizedQueue<E> implements Queue<E>
+ {
+ private final Queue<E> queue;
+
+ SynchronizedQueue(Queue<E> queue)
+ {
+ this.queue = queue;
+ }
+
+ public synchronized E element()
+ {
+ return queue.element();
+ }
+
+ public synchronized boolean offer(E o)
+ {
+ return queue.offer(o);
+ }
+
+ public synchronized E peek()
+ {
+ return queue.peek();
+ }
+
+ public synchronized E poll()
+ {
+ return queue.poll();
+ }
+
+ public synchronized E remove()
+ {
+ return queue.remove();
+ }
+
+ public synchronized int size()
+ {
+ return queue.size();
+ }
+
+ public synchronized boolean isEmpty()
+ {
+ return queue.isEmpty();
+ }
+
+ public synchronized boolean contains(Object o)
+ {
+ return queue.contains(o);
+ }
+
+ public synchronized Iterator<E> iterator()
+ {
+ return queue.iterator();
+ }
+
+ public synchronized Object[] toArray()
+ {
+ return queue.toArray();
+ }
+
+ public synchronized <T>T[] toArray(T[] a)
+ {
+ return queue.toArray(a);
+ }
+
+ public synchronized boolean add(E o)
+ {
+ return queue.add(o);
+ }
+
+ public synchronized boolean remove(Object o)
+ {
+ return queue.remove(o);
+ }
+
+ public synchronized boolean containsAll(Collection<?> c)
+ {
+ return queue.containsAll(c);
+ }
+
+ public synchronized boolean addAll(Collection<? extends E> c)
+ {
+ return queue.addAll(c);
+ }
+
+ public synchronized boolean removeAll(Collection<?> c)
+ {
+ return queue.removeAll(c);
+ }
+
+ public synchronized boolean retainAll(Collection<?> c)
+ {
+ return queue.retainAll(c);
+ }
+
+ public synchronized void clear()
+ {
+ queue.clear();
+ }
+ }
+
+ static void run(String label, AveragedRun test) throws Exception
+ {
+ RunStats stats = test.call();
+ System.out.println((label == null ? "" : label + ", ") + test
+ + ", " + stats.getAverage() + ", " + stats.getMax() + ", " + stats.getMin());
+ }
+
+ public static void main(String[] argv) throws Exception
+ {
+ Factory[] factories = new Factory[]{PLAIN, SYNCHRONIZED, CONCURRENT};
+ int iterations = 5;
+ String label = argv.length > 0 ? argv[0]: null;
+ System.out.println((label == null ? "" : "Label, ") + "Queue Type, No. of Queues, No. of Operations, Avg Time, Min Time, Max Time");
+ //vary number of queues:
+
+ for(Factory f : factories)
+ {
+ run(label, new AveragedRun(new QueuePerfTest(f, 100, 10000), iterations));
+ run(label, new AveragedRun(new QueuePerfTest(f, 1000, 10000), iterations));
+ run(label, new AveragedRun(new QueuePerfTest(f, 10000, 10000), iterations));
+ run(label, new AveragedRun(new QueuePerfTest(f, 1000, 1000), iterations));
+ run(label, new AveragedRun(new QueuePerfTest(f, 1000, 100000), iterations));
+ }
+ }
+
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/queue/SendPerfTest.java b/java/broker/test/src/org/apache/qpid/server/queue/SendPerfTest.java
new file mode 100644
index 0000000000..eff65a9350
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/queue/SendPerfTest.java
@@ -0,0 +1,171 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import org.apache.qpid.AMQException;
+import org.apache.qpid.codec.AMQCodecFactory;
+import org.apache.qpid.framing.BasicPublishBody;
+import org.apache.qpid.framing.ContentBody;
+import org.apache.qpid.framing.ContentHeaderBody;
+import org.apache.qpid.framing.FieldTable;
+import org.apache.qpid.server.AMQChannel;
+import org.apache.qpid.server.exchange.AbstractExchange;
+import org.apache.qpid.server.exchange.Exchange;
+import org.apache.qpid.server.handler.OnCurrentThreadExecutor;
+import org.apache.qpid.server.protocol.AMQMinaProtocolSession;
+import org.apache.qpid.server.protocol.AMQProtocolSession;
+import org.apache.qpid.server.protocol.MockIoSession;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.registry.IApplicationRegistry;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.SkeletonMessageStore;
+import org.apache.qpid.server.util.AveragedRun;
+import org.apache.qpid.server.util.NullApplicationRegistry;
+import org.apache.qpid.server.util.TimedRun;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class SendPerfTest extends TimedRun
+{
+ private int _messages = 1000;
+ private int _clients = 10;
+ private List<AMQQueue> _queues;
+
+ public SendPerfTest(int clients, int messages)
+ {
+ super("SendPerfTest, msgs=" + messages + ", clients=" + clients);
+ _messages = messages;
+ _clients = clients;
+ }
+
+ protected void setup() throws Exception
+ {
+ _queues = initQueues(_clients);
+ System.gc();
+ }
+
+ protected void teardown() throws Exception
+ {
+ System.gc();
+ }
+
+ protected void run() throws Exception
+ {
+ deliver(_messages, _queues);
+ }
+
+ //have a dummy AMQProtocolSession that does nothing on the writeFrame()
+ //set up x number of queues
+ //create necessary bits and pieces to deliver a message
+ //deliver y messages to each queue
+
+ public static void main(String[] argv) throws Exception
+ {
+ ApplicationRegistry.initialise(new NullApplicationRegistry());
+ int clients = Integer.parseInt(argv[0]);
+ int messages = Integer.parseInt(argv[1]);
+ int iterations = Integer.parseInt(argv[2]);
+ AveragedRun test = new AveragedRun(new SendPerfTest(clients, messages), iterations);
+ test.run();
+ }
+
+ /**
+ * Delivers messages to a number of queues.
+ * @param count the number of messages to deliver
+ * @param queues the list of queues
+ * @throws NoConsumersException
+ */
+ static void deliver(int count, List<AMQQueue> queues) throws AMQException
+ {
+ BasicPublishBody publish = new BasicPublishBody();
+ publish.exchange = new NullExchange().getName();
+ ContentHeaderBody header = new ContentHeaderBody();
+ List<ContentBody> body = new ArrayList<ContentBody>();
+ MessageStore messageStore = new SkeletonMessageStore();
+ body.add(new ContentBody());
+ for (int i = 0; i < count; i++)
+ {
+ for (AMQQueue q : queues)
+ {
+ q.deliver(new AMQMessage(messageStore, i, publish, header, body));
+ }
+ }
+ }
+
+ static List<AMQQueue> initQueues(int number) throws AMQException
+ {
+ Exchange exchange = new NullExchange();
+ List<AMQQueue> queues = new ArrayList<AMQQueue>(number);
+ for (int i = 0; i < number; i++)
+ {
+ AMQQueue q = createQueue("Queue" + (i + 1));
+ q.bind("routingKey", exchange);
+ try
+ {
+ q.registerProtocolSession(createSession(), 1, "1", false);
+ }
+ catch (Exception e)
+ {
+ throw new AMQException("Error creating protocol session: " + e, e);
+ }
+ queues.add(q);
+ }
+ return queues;
+ }
+
+ static AMQQueue createQueue(String name) throws AMQException
+ {
+ return new AMQQueue(name, false, null, false, ApplicationRegistry.getInstance().getQueueRegistry(),
+ new OnCurrentThreadExecutor());
+ }
+
+ static AMQProtocolSession createSession() throws Exception
+ {
+ IApplicationRegistry reg = ApplicationRegistry.getInstance();
+ AMQCodecFactory codecFactory = new AMQCodecFactory(true);
+ AMQMinaProtocolSession result = new AMQMinaProtocolSession(new MockIoSession(), reg.getQueueRegistry(), reg.getExchangeRegistry(), codecFactory);
+ result.addChannel(new AMQChannel(1, null, null));
+ return result;
+ }
+
+ static class NullExchange extends AbstractExchange
+ {
+ public String getName()
+ {
+ return "NullExchange";
+ }
+
+ protected ExchangeMBean createMBean()
+ {
+ return null;
+ }
+
+ public void registerQueue(String routingKey, AMQQueue queue, FieldTable args) throws AMQException
+ {
+ }
+
+ public void deregisterQueue(String routingKey, AMQQueue queue) throws AMQException
+ {
+ }
+
+ public void route(AMQMessage payload) throws AMQException
+ {
+ }
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/queue/SubscriptionManagerTest.java b/java/broker/test/src/org/apache/qpid/server/queue/SubscriptionManagerTest.java
new file mode 100644
index 0000000000..7743db5078
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/queue/SubscriptionManagerTest.java
@@ -0,0 +1,105 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import junit.framework.JUnit4TestAdapter;
+
+public class SubscriptionManagerTest
+{
+ private final SubscriptionSet mgr = new SubscriptionSet();
+
+ @Test
+ public void basicSubscriptionManagement()
+ {
+ assertTrue(mgr.isEmpty());
+ assertFalse(mgr.hasActiveSubscribers());
+ TestSubscription s1 = new TestSubscription("S1");
+ mgr.addSubscriber(s1);
+ assertFalse(mgr.isEmpty());
+ assertTrue(mgr.hasActiveSubscribers());
+
+ TestSubscription s2 = new TestSubscription("S2");
+ mgr.addSubscriber(s2);
+
+ s2.setSuspended(true);
+ assertFalse(mgr.isEmpty());
+ assertTrue(mgr.hasActiveSubscribers());
+ assertTrue(s2.isSuspended());
+ assertFalse(s1.isSuspended());
+
+ s1.setSuspended(true);
+ assertFalse(mgr.hasActiveSubscribers());
+
+ mgr.removeSubscriber(new TestSubscription("S1"));
+ assertFalse(mgr.isEmpty());
+ mgr.removeSubscriber(new TestSubscription("S2"));
+ assertTrue(mgr.isEmpty());
+ }
+
+ @Test
+ public void roundRobin()
+ {
+ TestSubscription a = new TestSubscription("A");
+ TestSubscription b = new TestSubscription("B");
+ TestSubscription c = new TestSubscription("C");
+ TestSubscription d = new TestSubscription("D");
+ mgr.addSubscriber(a);
+ mgr.addSubscriber(b);
+ mgr.addSubscriber(c);
+ mgr.addSubscriber(d);
+
+ for (int i = 0; i < 3; i++)
+ {
+ assertEquals(a, mgr.nextSubscriber(null));
+ assertEquals(b, mgr.nextSubscriber(null));
+ assertEquals(c, mgr.nextSubscriber(null));
+ assertEquals(d, mgr.nextSubscriber(null));
+ }
+
+ c.setSuspended(true);
+
+ for (int i = 0; i < 3; i++)
+ {
+ assertEquals(a, mgr.nextSubscriber(null));
+ assertEquals(b, mgr.nextSubscriber(null));
+ assertEquals(d, mgr.nextSubscriber(null));
+ }
+
+ mgr.removeSubscriber(a);
+ d.setSuspended(true);
+ c.setSuspended(false);
+ Subscription e = new TestSubscription("D");
+ mgr.addSubscriber(e);
+
+ for (int i = 0; i < 3; i++)
+ {
+ assertEquals(b, mgr.nextSubscriber(null));
+ assertEquals(c, mgr.nextSubscriber(null));
+ assertEquals(e, mgr.nextSubscriber(null));
+ }
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(SubscriptionManagerTest.class);
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/queue/SubscriptionSetTest.java b/java/broker/test/src/org/apache/qpid/server/queue/SubscriptionSetTest.java
new file mode 100644
index 0000000000..b6e8f8b44d
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/queue/SubscriptionSetTest.java
@@ -0,0 +1,149 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import junit.framework.JUnit4TestAdapter;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import org.junit.Test;
+
+public class SubscriptionSetTest
+{
+ /**
+ * A SubscriptionSet that counts the number of items scanned.
+ */
+ static class TestSubscriptionSet extends SubscriptionSet
+ {
+ private int scanned = 0;
+
+ void resetScanned()
+ {
+ scanned = 0;
+ }
+
+ protected void subscriberScanned()
+ {
+ ++scanned;
+ }
+
+ int getScanned()
+ {
+ return scanned;
+ }
+ }
+
+ final TestSubscription sub1 = new TestSubscription("1");
+ final TestSubscription sub2 = new TestSubscription("2");
+ final TestSubscription sub3 = new TestSubscription("3");
+
+ final TestSubscription suspendedSub1 = new TestSubscription("sus1", true);
+ final TestSubscription suspendedSub2 = new TestSubscription("sus2", true);
+ final TestSubscription suspendedSub3 = new TestSubscription("sus3", true);
+
+ @Test
+ public void nextMessage()
+ {
+ SubscriptionSet ss = new SubscriptionSet();
+ assertNull(ss.nextSubscriber(null));
+ assertEquals(0, ss.getCurrentSubscriber());
+
+ ss.addSubscriber(sub1);
+ assertEquals(sub1, ss.nextSubscriber(null));
+ assertEquals(1, ss.getCurrentSubscriber());
+ assertEquals(sub1, ss.nextSubscriber(null));
+ assertEquals(1, ss.getCurrentSubscriber());
+
+ ss.addSubscriber(sub2);
+ ss.addSubscriber(sub3);
+
+ assertEquals(sub2, ss.nextSubscriber(null));
+ assertEquals(2, ss.getCurrentSubscriber());
+
+ assertEquals(sub3, ss.nextSubscriber(null));
+ assertEquals(3, ss.getCurrentSubscriber());
+ }
+
+ @Test
+ public void nextMessageWhenAllSuspended()
+ {
+ SubscriptionSet ss = createAllSuspendedSubscriptionSet();
+ assertNull(ss.nextSubscriber(null));
+ assertEquals(3, ss.getCurrentSubscriber());
+
+ assertNull(ss.nextSubscriber(null));
+ assertEquals(3, ss.getCurrentSubscriber());
+ }
+
+ private TestSubscriptionSet createAllSuspendedSubscriptionSet()
+ {
+ TestSubscriptionSet ss = new TestSubscriptionSet();
+ ss.addSubscriber(suspendedSub1);
+ ss.addSubscriber(suspendedSub2);
+ ss.addSubscriber(suspendedSub3);
+ return ss;
+ }
+
+ @Test
+ public void nextMessageAfterRemove()
+ {
+ SubscriptionSet ss = new SubscriptionSet();
+ ss.addSubscriber(suspendedSub1);
+ ss.addSubscriber(suspendedSub2);
+ ss.addSubscriber(sub3);
+ assertEquals(sub3, ss.nextSubscriber(null));
+ assertEquals(3, ss.getCurrentSubscriber());
+
+ assertEquals(suspendedSub1, ss.removeSubscriber(suspendedSub1));
+
+ assertEquals(sub3, ss.nextSubscriber(null)); // Current implementation handles OutOfBoundsException here.
+ assertEquals(2, ss.getCurrentSubscriber());
+ }
+
+ @Test
+ public void nextMessageOverScanning()
+ {
+ TestSubscriptionSet ss = new TestSubscriptionSet();
+ TestSubscription sub = new TestSubscription("test");
+ ss.addSubscriber(suspendedSub1);
+ ss.addSubscriber(sub);
+ ss.addSubscriber(suspendedSub3);
+ assertEquals(sub, ss.nextSubscriber(null));
+ assertEquals(2, ss.getCurrentSubscriber());
+ assertEquals(2, ss.getScanned());
+
+ ss.resetScanned();
+ sub.setSuspended(true);
+ assertNull(ss.nextSubscriber(null));
+ assertEquals(3, ss.getCurrentSubscriber());
+ // Current implementation overscans by one item here.
+ assertEquals(ss.size() + 1, ss.getScanned());
+ }
+
+ @Test
+ public void nextMessageOverscanWorstCase() {
+ TestSubscriptionSet ss = createAllSuspendedSubscriptionSet();
+ ss.nextSubscriber(null);
+ // Scans the subscriptions twice.
+ assertEquals(ss.size() * 2, ss.getScanned());
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(SubscriptionSetTest.class);
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/queue/TestSubscription.java b/java/broker/test/src/org/apache/qpid/server/queue/TestSubscription.java
new file mode 100644
index 0000000000..093d7e60f4
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/queue/TestSubscription.java
@@ -0,0 +1,84 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestSubscription implements Subscription
+{
+ private final List<AMQMessage> messages;
+ private final Object key;
+ private boolean isSuspended;
+
+ public TestSubscription(Object key)
+ {
+ this(key, new ArrayList<AMQMessage>());
+ }
+
+ public TestSubscription(final Object key, final boolean isSuspended)
+ {
+ this(key);
+ setSuspended(isSuspended);
+ }
+
+ TestSubscription(Object key, List<AMQMessage> messages)
+ {
+ this.key = key;
+ this.messages = messages;
+ }
+
+ List<AMQMessage> getMessages()
+ {
+ return messages;
+ }
+
+ public void send(AMQMessage msg, AMQQueue queue)
+ {
+ messages.add(msg);
+ }
+
+ public void setSuspended(boolean suspended)
+ {
+ isSuspended = suspended;
+ }
+
+ public boolean isSuspended()
+ {
+ return isSuspended;
+ }
+
+ public void queueDeleted(AMQQueue queue)
+ {
+ }
+
+ public int hashCode()
+ {
+ return key.hashCode();
+ }
+
+ public boolean equals(Object o)
+ {
+ return o instanceof TestSubscription && ((TestSubscription) o).key.equals(key);
+ }
+
+ public String toString()
+ {
+ return key.toString();
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/queue/UnitTests.java b/java/broker/test/src/org/apache/qpid/server/queue/UnitTests.java
new file mode 100644
index 0000000000..3a86773a15
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/queue/UnitTests.java
@@ -0,0 +1,38 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.queue;
+
+import junit.framework.JUnit4TestAdapter;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ AckTest.class,
+ DeliveryManagerTest.class,
+ SubscriptionManagerTest.class,
+ SubscriptionSetTest.class,
+ ConcurrencyTest.class}
+)
+public class UnitTests
+{
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(UnitTests.class);
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/store/SkeletonMessageStore.java b/java/broker/test/src/org/apache/qpid/server/store/SkeletonMessageStore.java
new file mode 100644
index 0000000000..82292d2503
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/store/SkeletonMessageStore.java
@@ -0,0 +1,99 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.store;
+
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.server.queue.AMQQueue;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.AMQException;
+import org.apache.commons.configuration.Configuration;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * A message store that does nothing. Designed to be used in tests that do not want to use any message store
+ * functionality.
+ */
+public class SkeletonMessageStore implements MessageStore
+{
+ private final AtomicLong _messageId = new AtomicLong(1);
+
+ public void configure(String base, Configuration config) throws Exception
+ {
+ }
+
+ public void configure(QueueRegistry queueRegistry, String base, Configuration config) throws Exception
+ {
+ }
+
+ public void close() throws Exception
+ {
+ }
+
+ public void put(AMQMessage msg)
+ {
+ }
+
+ public void removeMessage(long messageId)
+ {
+ }
+
+ public void createQueue(AMQQueue queue) throws AMQException
+ {
+ }
+
+ public void removeQueue(String name) throws AMQException
+ {
+ }
+
+ public void enqueueMessage(String name, long messageId) throws AMQException
+ {
+ }
+
+ public void dequeueMessage(String name, long messageId) throws AMQException
+ {
+ }
+
+ public void beginTran() throws AMQException
+ {
+ }
+
+ public boolean inTran()
+ {
+ return false;
+ }
+
+ public void commitTran() throws AMQException
+ {
+ }
+
+ public void abortTran() throws AMQException
+ {
+ }
+
+ public List<AMQQueue> createQueues() throws AMQException
+ {
+ return null;
+ }
+
+ public long getNewMessageId()
+ {
+ return _messageId.getAndIncrement();
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/store/TestReferenceCounting.java b/java/broker/test/src/org/apache/qpid/server/store/TestReferenceCounting.java
new file mode 100644
index 0000000000..d2e3ca6478
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/store/TestReferenceCounting.java
@@ -0,0 +1,68 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.store;
+
+import junit.framework.JUnit4TestAdapter;
+import org.junit.Test;
+import org.junit.Assert;
+import org.junit.Before;
+import org.apache.qpid.server.queue.AMQMessage;
+import org.apache.qpid.AMQException;
+
+/**
+ * Tests that reference counting works correctly with AMQMessage and the message store
+ */
+public class TestReferenceCounting
+{
+ private TestableMemoryMessageStore _store;
+
+ @Before
+ public void createCommonObjects()
+ {
+ _store = new TestableMemoryMessageStore();
+ }
+
+ /**
+ * Check that when the reference count is decremented the message removes itself from the store
+ */
+ @Test
+ public void testMessageGetsRemoved() throws AMQException
+ {
+ AMQMessage message = new AMQMessage(_store, null);
+ _store.put(message);
+ Assert.assertTrue(_store.getMessageMap().size() == 1);
+ message.decrementReference();
+ Assert.assertTrue(_store.getMessageMap().size() == 0);
+ }
+
+ @Test
+ public void testMessageRemains() throws AMQException
+ {
+ AMQMessage message = new AMQMessage(_store, null);
+ _store.put(message);
+ Assert.assertTrue(_store.getMessageMap().size() == 1);
+ message.incrementReference();
+ message.decrementReference();
+ Assert.assertTrue(_store.getMessageMap().size() == 1);
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(TestReferenceCounting.class);
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/store/TestableMemoryMessageStore.java b/java/broker/test/src/org/apache/qpid/server/store/TestableMemoryMessageStore.java
new file mode 100644
index 0000000000..1214cd2825
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/store/TestableMemoryMessageStore.java
@@ -0,0 +1,39 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.store;
+
+import org.apache.qpid.server.queue.AMQMessage;
+
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Adds some extra methods to the memory message store for testing purposes.
+ */
+public class TestableMemoryMessageStore extends MemoryMessageStore
+{
+ public TestableMemoryMessageStore()
+ {
+ _messageMap = new ConcurrentHashMap<Long, AMQMessage>();
+ }
+
+ public ConcurrentMap<Long, AMQMessage> getMessageMap()
+ {
+ return _messageMap;
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/store/UnitTests.java b/java/broker/test/src/org/apache/qpid/server/store/UnitTests.java
new file mode 100644
index 0000000000..a917d736a2
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/store/UnitTests.java
@@ -0,0 +1,34 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.store;
+
+import junit.framework.JUnit4TestAdapter;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+ TestReferenceCounting.class
+})
+public class UnitTests
+{
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(UnitTests.class);
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/util/AveragedRun.java b/java/broker/test/src/org/apache/qpid/server/util/AveragedRun.java
new file mode 100644
index 0000000000..3e4a1edac4
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/util/AveragedRun.java
@@ -0,0 +1,63 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+import org.apache.qpid.server.util.TimedRun;
+
+import java.util.concurrent.Callable;
+import java.util.Collection;
+
+public class AveragedRun implements Callable<RunStats>
+{
+ private final RunStats stats = new RunStats();
+ private final TimedRun test;
+ private final int iterations;
+
+ public AveragedRun(TimedRun test, int iterations)
+ {
+ this.test = test;
+ this.iterations = iterations;
+ }
+
+ public RunStats call() throws Exception
+ {
+ for (int i = 0; i < iterations; i++)
+ {
+ stats.record(test.call());
+ }
+ return stats;
+ }
+
+ public void run() throws Exception
+ {
+ System.out.println(test + ": " + call());
+ }
+
+ public String toString()
+ {
+ return test.toString();
+ }
+
+ static void run(Collection<AveragedRun> tests) throws Exception
+ {
+ for(AveragedRun test : tests)
+ {
+ test.run();
+ }
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/util/ConcurrentTest.java b/java/broker/test/src/org/apache/qpid/server/util/ConcurrentTest.java
new file mode 100644
index 0000000000..6c5185e254
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/util/ConcurrentTest.java
@@ -0,0 +1,76 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+public class ConcurrentTest extends TimedRun
+{
+ private final TimedRun _test;
+ private final Thread[] _threads;
+
+ public ConcurrentTest(TimedRun test, int threads)
+ {
+ super(test.toString());
+ _test = test;
+ _threads = new Thread[threads];
+ }
+
+ protected void setup() throws Exception
+ {
+ _test.setup();
+ for(int i = 0; i < _threads.length; i++)
+ {
+ _threads[i] = new Thread(new Runner());
+ }
+ }
+
+ protected void teardown() throws Exception
+ {
+ _test.teardown();
+ }
+
+ protected void run() throws Exception
+ {
+ for(Thread t : _threads)
+ {
+ t.start();
+ }
+ for(Thread t : _threads)
+ {
+ t.join();
+ }
+ }
+
+ private class Runner implements Runnable
+ {
+ private Exception error;
+
+ public void run()
+ {
+ try
+ {
+ _test.run();
+ }
+ catch(Exception e)
+ {
+ error = e;
+ e.printStackTrace();
+ }
+ }
+ }
+
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/util/LoggingProxyTest.java b/java/broker/test/src/org/apache/qpid/server/util/LoggingProxyTest.java
new file mode 100644
index 0000000000..15c9e1a59a
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/util/LoggingProxyTest.java
@@ -0,0 +1,89 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+import junit.framework.JUnit4TestAdapter;
+import org.junit.Assert;
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+public class LoggingProxyTest
+{
+ static interface IFoo {
+ void foo();
+ void foo(int i, Collection c);
+ String bar();
+ String bar(String s, List l);
+ }
+
+ static class Foo implements IFoo {
+ public void foo()
+ {
+ }
+
+ public void foo(int i, Collection c)
+ {
+ }
+
+ public String bar()
+ {
+ return null;
+ }
+
+ public String bar(String s, List l)
+ {
+ return "ha";
+ }
+ }
+
+ @Test
+ public void simple() {
+ LoggingProxy proxy = new LoggingProxy(new Foo(), 20);
+ IFoo foo = (IFoo)proxy.getProxy(IFoo.class);
+ foo.foo();
+ assertEquals(2, proxy.getBufferSize());
+ Assert.assertTrue(proxy.getBuffer().get(0).toString().matches(".*: foo\\(\\) entered$"));
+ Assert.assertTrue(proxy.getBuffer().get(1).toString().matches(".*: foo\\(\\) returned$"));
+
+ foo.foo(3, Arrays.asList(0, 1, 2));
+ assertEquals(4, proxy.getBufferSize());
+ Assert.assertTrue(proxy.getBuffer().get(2).toString().matches(".*: foo\\(\\[3, \\[0, 1, 2\\]\\]\\) entered$"));
+ Assert.assertTrue(proxy.getBuffer().get(3).toString().matches(".*: foo\\(\\) returned$"));
+
+ foo.bar();
+ assertEquals(6, proxy.getBufferSize());
+ Assert.assertTrue(proxy.getBuffer().get(4).toString().matches(".*: bar\\(\\) entered$"));
+ Assert.assertTrue(proxy.getBuffer().get(5).toString().matches(".*: bar\\(\\) returned null$"));
+
+ foo.bar("hello", Arrays.asList(1, 2, 3));
+ assertEquals(8, proxy.getBufferSize());
+ Assert.assertTrue(proxy.getBuffer().get(6).toString().matches(".*: bar\\(\\[hello, \\[1, 2, 3\\]\\]\\) entered$"));
+ Assert.assertTrue(proxy.getBuffer().get(7).toString().matches(".*: bar\\(\\) returned ha$"));
+
+ proxy.dump();
+ }
+
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(LoggingProxyTest.class);
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/util/NullApplicationRegistry.java b/java/broker/test/src/org/apache/qpid/server/util/NullApplicationRegistry.java
new file mode 100644
index 0000000000..3daf143561
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/util/NullApplicationRegistry.java
@@ -0,0 +1,103 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+import org.apache.qpid.server.exchange.DefaultExchangeFactory;
+import org.apache.qpid.server.exchange.DefaultExchangeRegistry;
+import org.apache.qpid.server.exchange.ExchangeFactory;
+import org.apache.qpid.server.exchange.ExchangeRegistry;
+import org.apache.qpid.server.management.ManagedObjectRegistry;
+import org.apache.qpid.server.management.NoopManagedObjectRegistry;
+import org.apache.qpid.server.queue.DefaultQueueRegistry;
+import org.apache.qpid.server.queue.QueueRegistry;
+import org.apache.qpid.server.registry.ApplicationRegistry;
+import org.apache.qpid.server.security.auth.AuthenticationManager;
+import org.apache.qpid.server.store.MessageStore;
+import org.apache.qpid.server.store.TestableMemoryMessageStore;
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.MapConfiguration;
+
+import java.util.HashMap;
+
+public class NullApplicationRegistry extends ApplicationRegistry
+{
+ private QueueRegistry _queueRegistry;
+
+ private ExchangeRegistry _exchangeRegistry;
+
+ private ExchangeFactory _exchangeFactory;
+
+ private ManagedObjectRegistry _managedObjectRegistry;
+
+ private AuthenticationManager _authenticationManager;
+
+ private MessageStore _messageStore;
+
+ public NullApplicationRegistry()
+ {
+ super(new MapConfiguration(new HashMap()));
+ }
+
+ public void initialise() throws Exception
+ {
+ _managedObjectRegistry = new NoopManagedObjectRegistry();
+ _queueRegistry = new DefaultQueueRegistry();
+ _exchangeFactory = new DefaultExchangeFactory();
+ _exchangeRegistry = new DefaultExchangeRegistry(_exchangeFactory);
+ _authenticationManager = new NullAuthenticationManager();
+ _messageStore = new TestableMemoryMessageStore();
+
+ _configuration.addProperty("heartbeat.delay", 10 * 60); // 10 minutes
+ }
+
+ public Configuration getConfiguration()
+ {
+ return _configuration;
+ }
+
+ public QueueRegistry getQueueRegistry()
+ {
+ return _queueRegistry;
+ }
+
+ public ExchangeRegistry getExchangeRegistry()
+ {
+ return _exchangeRegistry;
+ }
+
+ public ExchangeFactory getExchangeFactory()
+ {
+ return _exchangeFactory;
+ }
+
+ public ManagedObjectRegistry getManagedObjectRegistry()
+ {
+ return _managedObjectRegistry;
+ }
+
+ public AuthenticationManager getAuthenticationManager()
+ {
+ return _authenticationManager;
+ }
+
+ public MessageStore getMessageStore()
+ {
+ return _messageStore;
+ }
+}
+
diff --git a/java/broker/test/src/org/apache/qpid/server/util/NullAuthenticationManager.java b/java/broker/test/src/org/apache/qpid/server/util/NullAuthenticationManager.java
new file mode 100644
index 0000000000..cdde505451
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/util/NullAuthenticationManager.java
@@ -0,0 +1,82 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+import org.apache.qpid.server.security.auth.AuthenticationManager;
+import org.apache.qpid.server.security.auth.AuthenticationResult;
+
+import javax.security.sasl.SaslException;
+import javax.security.sasl.SaslServer;
+
+public class NullAuthenticationManager implements AuthenticationManager
+{
+ public String getMechanisms()
+ {
+ return "PLAIN";
+ }
+
+ public SaslServer createSaslServer(String mechanism, String localFQDN) throws SaslException
+ {
+ return new SaslServer()
+ {
+ public String getMechanismName()
+ {
+ return "PLAIN";
+ }
+
+ public byte[] evaluateResponse(byte[] response) throws SaslException
+ {
+ return new byte[0];
+ }
+
+ public boolean isComplete()
+ {
+ return true;
+ }
+
+ public String getAuthorizationID()
+ {
+ return "guest";
+ }
+
+ public byte[] unwrap(byte[] incoming, int offset, int len) throws SaslException
+ {
+ return new byte[0];
+ }
+
+ public byte[] wrap(byte[] outgoing, int offset, int len) throws SaslException
+ {
+ return new byte[0];
+ }
+
+ public Object getNegotiatedProperty(String propName)
+ {
+ return null;
+ }
+
+ public void dispose() throws SaslException
+ {
+ }
+ };
+ }
+
+ public AuthenticationResult authenticate(SaslServer server, byte[] response)
+ {
+ return new AuthenticationResult(AuthenticationResult.AuthenticationStatus.SUCCESS);
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/util/RunStats.java b/java/broker/test/src/org/apache/qpid/server/util/RunStats.java
new file mode 100644
index 0000000000..248622836d
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/util/RunStats.java
@@ -0,0 +1,54 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+public class RunStats
+{
+ private long min = Long.MAX_VALUE;
+ private long max;
+ private long total;
+ private int count;
+
+ public void record(long time)
+ {
+ max = Math.max(time, max);
+ min = Math.min(time, min);
+ total += time;
+ count++;
+ }
+
+ public long getMin()
+ {
+ return min;
+ }
+
+ public long getMax()
+ {
+ return max;
+ }
+
+ public long getAverage()
+ {
+ return total / count;
+ }
+
+ public String toString()
+ {
+ return "avg=" + getAverage() + ", min=" + min + ", max=" + max;
+ }
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/util/TimedRun.java b/java/broker/test/src/org/apache/qpid/server/util/TimedRun.java
new file mode 100644
index 0000000000..f779b7fbb6
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/util/TimedRun.java
@@ -0,0 +1,49 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+import java.util.concurrent.Callable;
+
+public abstract class TimedRun implements Callable<Long>
+{
+ private final String description;
+
+ public TimedRun(String description)
+ {
+ this.description = description;
+ }
+
+ public Long call() throws Exception
+ {
+ setup();
+ long start = System.currentTimeMillis();
+ run();
+ long stop = System.currentTimeMillis();
+ teardown();
+ return stop - start;
+ }
+
+ public String toString()
+ {
+ return description;
+ }
+
+ protected void setup() throws Exception{}
+ protected void teardown() throws Exception{}
+ protected abstract void run() throws Exception;
+}
diff --git a/java/broker/test/src/org/apache/qpid/server/util/UnitTests.java b/java/broker/test/src/org/apache/qpid/server/util/UnitTests.java
new file mode 100644
index 0000000000..d6cc471413
--- /dev/null
+++ b/java/broker/test/src/org/apache/qpid/server/util/UnitTests.java
@@ -0,0 +1,32 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.qpid.server.util;
+
+import junit.framework.JUnit4TestAdapter;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({LoggingProxyTest.class})
+public class UnitTests
+{
+ public static junit.framework.Test suite()
+ {
+ return new JUnit4TestAdapter(UnitTests.class);
+ }
+}